java里的java自定义注解的作用都有哪些

Open and Share
Java获取类、方法、属性上的注解
一、获取类上的注解
Java获取类上的注解有下面3个方法:
Class.getAnnotations() 获取所有的注解,包括自己声明的以及继承的
Class.getAnnotation(Class& A & annotationClass) 获取指定的注解,该注解可以是自己声明的,也可以是继承的
Class.getDeclaredAnnotations() 获取自己声明的注解
下面,我们来演示一下3个方法的使用。
首先,我们定义两个注解ParentAnnotation、SubAnnotation
@Retention(RetentionPolicy.RUNTIME)
@Target(value={ElementType.TYPE})
@Documented
@Inherited
public @interface ParentAnnotation {
@Target(value={ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SubAnnotation {
接下来,我们定义两个类,Parent、Sub,分别标注ParentAnnotation 注解和SubAnnotation注解
@ParentAnnotation
public class Parent {
@SubAnnotation
public class Sub extends Parent{
一切准备OK后,就开始测试了。
public class AnnotationTest {
public static void main(String[] args) {
Annotation[] allAnnos = Sub.class.getAnnotations()
Annotation[] deAnnos = Sub.class.getDeclaredAnnotations()
Annotation subAnnotation = Sub.class.getAnnotation(SubAnnotation.class)
Annotation parentAnnotation = Sub.class.getAnnotation(ParentAnnotation.class)
printAnnotation("all",allAnnos)
printAnnotation("declare",deAnnos)
printAnnotation("sub",subAnnotation)
printAnnotation("parent",parentAnnotation)
private static void printAnnotation(String msg,Annotation... annotations){
System.out.println("=============="+msg+"======================")
if(annotations == null){
System.out.println("Annotation is null")
for (Annotation annotation : annotations) {
System.out.println(annotation)
System.out.println()
执行结果:
==============all======================
@com.ghs.test.annotation.ParentAnnotation()
@com.ghs.test.annotation.SubAnnotation()
==============declare======================
@com.ghs.test.annotation.SubAnnotation()
==============sub======================
@com.ghs.test.annotation.SubAnnotation()
==============parent======================
@com.ghs.test.annotation.ParentAnnotation()
尝试着将ParentAnnotation中的@Inherited去掉,结果如下:
==============all======================
@com.ghs.test.annotation.SubAnnotation()
==============declare======================
@com.ghs.test.annotation.SubAnnotation()
==============sub======================
@com.ghs.test.annotation.SubAnnotation()
==============parent======================
再试着将Sub类中的SubAnnotation去掉,结果如下:
==============all======================
==============declare======================
==============sub======================
==============parent======================
经过几番小小的测试,我们基本上可以得出下面几条结论:
注解只有标注了@Inherited才能被子类继承
当某个类没有标注任何注解时,getAnnotations()和getDeclaredAnnotations()返回空数组
当某个注解查询不到时,getAnnotation(Class& A & annotationType)方法返回null
二、获取方法上的注解
修改上面的ParentAnnotation与SubAnnotation,使其可以标注在方法上
@Target(value={ElementType.TYPE, ElementType.METHOD})
在Sub、Parent中分别添加一个test()方法,如下:
@ParentAnnotation
public class Parent {
@ParentAnnotation
public void test(){
@SubAnnotation
public class Sub extends Parent{
@SubAnnotation
public void test(){
一切准备就绪,就可以进行测试了。
private static void testMethodAnnotation() {
Method[] methods = Sub.class.getMethods();
for (Method method : methods) {
if(method.getName().equals("test")){
Annotation[] allMAnnos = method.getAnnotations();
Annotation[] deMAnnos = method.getDeclaredAnnotations();
Annotation subMAnno = method.getAnnotation(SubAnnotation.class);
Annotation parentMAnno = method.getAnnotation(ParentAnnotation.class);
printAnnotation("allMAnnos",allMAnnos);
printAnnotation("deMAnnos",deMAnnos);
printAnnotation("subMAnno",subMAnno);
printAnnotation("parentMAnno",parentMAnno);
执行结果如下:
==============allMAnnos======================
@com.ghs.test.annotation.SubAnnotation()
==============deMAnnos======================
@com.ghs.test.annotation.SubAnnotation()
==============subMAnno======================
@com.ghs.test.annotation.SubAnnotation()
==============parentMAnno======================
尝试着删除Sub中的test方法,再次进行测试,结果如下:
==============allMAnnos======================
@com.ghs.test.annotation.ParentAnnotation()
==============deMAnnos======================
@com.ghs.test.annotation.ParentAnnotation()
==============subMAnno======================
==============parentMAnno======================
@com.ghs.test.annotation.ParentAnnotation()
经过两轮测试,可以得出以下结论:
子类重写的方法,注解无法被继承
针对方法而言,getAnnotations()与getDeclaredAnnotations()返回的结果似乎永远都是一样的。
附:针对此结论,如有不同的想法,还望不吝赐教
三、获取属性上的注解
修改上面的ParentAnnotation与SubAnnotation,使其可以标注在属性上
@Target(value={ElementType.TYPE, ElementType.METHOD,ElementTypeFIELD})
在Sub、Parent中分别添加一个name属性,如下:
@ParentAnnotation
public class Parent {
@ParentAnnotation
@ParentAnnotation
public void test(){
@SubAnnotation
public class Sub extends Parent{
@SubAnnotation
@SubAnnotation
public void test(){
下面开始测试:
private static void testFieldAnnotation() {
Field[] fields = Sub.class.getFields()
for (Field field : fields) {
Annotation[] allFAnnos= field.getAnnotations()
Annotation[] deFAnnos = field.getDeclaredAnnotations()
Annotation subFAnno = field.getAnnotation(SubAnnotation.class)
Annotation parentFAnno = field.getAnnotation(ParentAnnotation.class)
printAnnotation("allFAnnos",allFAnnos)
printAnnotation("deFAnnos",deFAnnos)
printAnnotation("subFAnno",subFAnno)
printAnnotation("parentFAnno",parentFAnno)
System.out.println("**************************************************\n")
执行结果如下:
==============allFAnnos======================
@com.ghs.test.annotation.SubAnnotation()
==============deFAnnos======================
@com.ghs.test.annotation.SubAnnotation()
==============subFAnno======================
@com.ghs.test.annotation.SubAnnotation()
==============parentFAnno======================
**************************************************
==============allFAnnos======================
@com.ghs.test.annotation.ParentAnnotation()
==============deFAnnos======================
@com.ghs.test.annotation.ParentAnnotation()
==============subFAnno======================
==============parentFAnno======================
@com.ghs.test.annotation.ParentAnnotation()
**************************************************
经过测试,我们可以得出下面的几个结论:
父类的属性和子类的属性互补干涉
针对属性而言,getAnnotations()与getDeclaredAnnotations()方法返回的结果似乎都是一样的
附:针对此结论,如有不同的想法,还望不吝赐教
没有更多推荐了,
加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!我叫马里奥
一、啥是@ 注解
  首先看名字,注解&&跟注释长得挺像。注解跟注释很类似,类比思考一下,注释是干什么用的?
  * 注释是一种存在于源代码中的信息,用于开发过程中给程序员提示和帮助。
  注解也是类似的东西,他也带来信息,不过跟注释不一样的地方在于,注解可以保留到编译之后的二进制代码中,而注释则在编译阶段就被抛弃了。
  由于注解拥有可以保留到二进制代码中的特征,因此可以通过注解为框架带来元信息。
  举个例子:最常用的注解之一@Controller 将这个注解标注到Controller类上,在springMVC(一种java mvc框架)构建ApplicationContext环境的时候,就会发现,哦这是一个控制器,这个控制器要映射到url上。这就相当于在类上面添加了一个能保留到二进制代码中的//这是一个控制器类&这么一段注释。
二、注解的语法
定义注解时,需要一些元注解(meta-annotation),如@Target和@Retention
@Target用来定义注解将应用于什么地方(如一个方法或者一个域)
@Retention用来定义注解在哪一个级别可用,在源代码中(source),类文件中(class)或者运行时(runtime)
在注解中,一般都会包含一些元素以表示某些值。当分析处理注解时,程序可以利用这些值。没有元素的注解称为标记注解(marker annotation)
四种元注解,元注解专职负责注解其他的注解,所以这四种注解的Target值都是ElementType.ANNOTATION_TYPE
表示该注解可以用在什么地方,由ElementType枚举定义&CONSTRUCTOR:构造器的声明&FIELD:域声明(包括enum实例)&LOCAL_VARIABLE:局部变量声明&METHOD:方法声明&PACKAGE:包声明PARAMETER:参数声明TYPE:类、接口(包括注解类型)或enum声明&ANNOTATION_TYPE:注解声明(应用于另一个注解上)TYPE_PARAMETER:类型参数声明(1.8新加入)&TYPE_USE:类型使用声明(1.8新加入)PS:当注解未指定Target值时,此注解可以使用任何元素之上,就是上面的类型
@Retention
表示需要在什么级别保存该注解信息,由RetentionPolicy枚举定义&SOURCE:注解将被编译器丢弃(该类型的注解信息只会保留在源码里,源码经过编译后,注解信息会被丢弃,不会保留在编译好的class文件里)CLASS:注解在class文件中可用,但会被VM丢弃(该类型的注解信息会保留在源码里和class文件里,在执行的时候,不会加载到虚拟机(JVM)中)RUNTIME:VM将在运行期也保留注解信息,因此可以通过反射机制读取注解的信息(源码、class文件和执行的时候都有注解的信息)PS:当注解未定义Retention值时,默认值是CLASS
@Documented
表示注解会被包含在javaapi文档中
@Inherited
允许子类继承父类的注解
注解元素可用的类型如下:
所有基本类型(int,float,boolean,byte,double,char,long,short)
Annotation
以上类型的数组 如果使用了其他类型,那编译器就会报错。也不允许使用任何包装类型。注解也可以作为元素的类型,也就是注解可以嵌套。 元素的修饰符,只能用public或default。
默认值限制
&&&&编译器对元素的默认值有些过分挑剔。首先,元素不能有不确定的值。也就是说,元素必须要么具有默认值,要么在使用注解时提供元素的值。
&&&&其次,对于非基本类型的元素,无论是在源代码中声明,还是在注解接口中定义默认值,都不能以null作为值。这就是限制,这就造成处理器很难表现一个元素的存在或缺失状态,因为每个注解的声明中,所有的元素都存在,并且都具有相应的值。为了绕开这个限制,只能定义一些特殊的值,例如空字符串或负数,表示某个元素不存在。
@Target(ElementType.Method)
@Retention(RetentionPolicy.RUNTIME)
public @interface MockNull {
& & public int id() default -1;
& & public String description() default "";
三、注解的使用
说过了注解是什么东西,注解怎么定义,该说说注解如何使用了。
首先声明一点,注解都是给框架用的,说到框架使用,很容易联想到反射。
&A extends&&&A
(&A&&annotationClass)&&&&&&&&&&&如果存在该元素的指定类型的注释,则返回这些注释,否则返回 null。
()&&&&&&&&&&&返回此元素上存在的所有注释。
&Filed类等可以拥有注解的反射类型都有类似可以获取注解的方法
&T extends&&&T
(&T&&annotationClass)&&&&&&&&&&&如果存在该元素的指定类型的注释,则返回这些注释,否则返回 null。
注解在java中属于一种比较高级而且麻烦的技术,非框架编写者基本不会用到这种技术。但是了解注解是个什么东西很重要,因为注解在框架的快捷配置中起了决定性的作用,也让no xml党们拥有了一个脱离xml配置的机会。了解注解可以让我们不止知其然还知其所以然,在使用框架的时候不至于迷迷糊糊的就@了个圈在上面。
阅读排行榜java基础加强--注解的一个例子
//1:定义一个person实例
package cn.itcast.
public class Person {
&&& private
&&& private int
&&& public
String getName() {
&&& return
&&& public void
setName(String name) {
&&& this.name =
&&& public int
getAge() {
&&& return
&&& public void
setAge(int age) {
&&& this.age =
----------------------------------------------------------------------------------------
//2:定义一个注解
package cn.itcast.annotation2;
import java.lang.annotation.R
import java.lang.annotation.RetentionP
@Retention(RetentionPolicy.RUNTIME)
public @interface Inject {
&&& String
-------------------------------------------------------------------------------
//3:引用这个注解
package cn.itcast.annotation2;
import cn.itcast.domain.P
public class PersonDao {
@Inject(name="yyyy",age=12) private P
&&& public
Person getPerson() {
&&& return
@Inject(name="xxx",age=12)
&&& public void
setPerson(Person person) {
&&& this.person
-----------------------------------------------------------------------------
//4:解析注解,封装信息到person中
package cn.itcast.annotation2;
import java.beans.BeanI
import java.beans.IntrospectionE
import java.beans.I
import java.beans.PropertyD
import java.lang.reflect.F
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.M
import org.junit.T
import cn.itcast.domain.P
public class PersonDaoTest {
&&& public void
test1() throws IntrospectionException, InstantiationException,
IllegalAccessException, SecurityException, NoSuchFieldException,
IllegalArgumentException, InvocationTargetException{
&&& PersonDao
dao = new PersonDao();
//1.得到personDao的所有属性
&&& BeanInfo
info = Introspector.getBeanInfo(dao.getClass());
PropertyDescriptor pds[] = info.getPropertyDescriptors();
for(PropertyDescriptor pd :pds){
//2.得到每一个属性的set方法
&&& Method
method = pd.getWriteMethod();& //set...
//判断属性相应的set方法是否存在
if(method==null){
//3.看set方法上是否有inject注解
&&& Inject
inject = method.getAnnotation(Inject.class);
if(inject==null){
//4.如果有inject注解,则用注解信息,创建一个属性需要的对象,注入到相应的属性上
//创建属性需要的对象
&&& Object bean
= pd.getPropertyType().newInstance();
//用注解中配置的信息填充上面这个对象
annotation2Bean(inject,bean);
method.invoke(dao, bean);
&&& Person p =
dao.getPerson();
System.out.println(p.getName());
System.out.println(p.getAge());
//把注解中封装的信息,注入到bean中
&&& private void
annotation2Bean(Inject inject, Object bean) throws
IllegalArgumentException, IllegalAccessException,
InvocationTargetException, IntrospectionException {
&&& Method
methods[] = inject.getClass().getMethods();&
//获得注解的每一个方法.
&&& for(Method
method : methods){
&&& String
propertyName = method.getName();& //age name
PropertyDescriptor pd& = new
PropertyDescriptor(propertyName,bean.getClass());&
//属性描述器的构造函数
//因为没有beaninfo,所以只能自己new一个属性描述器了
&&& Object
propertyValue = method.invoke(inject,
//获取注解的属性值& 牛逼,这样就能拿到这个注解的值,我确实像不明白,可能就是固定用法
&&& Method
setMethod = pd.getWriteMethod();& //set
setMethod.invoke(bean, propertyValue);&
//把注解的属性值赋给bean的相应属性
&&& }catch
(Exception e) {
&&& public void
test2() throws InstantiationException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException,
IntrospectionException{
&&& PersonDao
dao = new PersonDao();
//得到dao的所有字段
&&& Field fs[] =
dao.getClass().getDeclaredFields();
&&& for(Field f:
//看哪个字段上有注解
&&& Inject
inject = f.getAnnotation(Inject.class);
if(inject==null){
//如果字段上有注解
//看这个字段上需要什么对象
&&& Object bean
= f.getType().newInstance();
//用注解的信息,创建一个它需要的对象,注入进去
annotation2Bean(inject,bean);
f.setAccessible(true);
&&& f.set(dao,
System.out.println(dao.getPerson().getName());
已投稿到:
以上网友发言只代表其个人观点,不代表新浪网的观点或立场。关于在java接口的方法上面打注解还是在接口的实现类的方法打注解的疑惑
[问题点数:40分,结帖人qqZuok]
本版专家分:244
CSDN今日推荐
本版专家分:21681
2016年10月优秀小版主
2016年10月 Java大版内专家分月排行榜第一2016年9月 Java大版内专家分月排行榜第一2016年8月 Java大版内专家分月排行榜第一2014年4月 Java大版内专家分月排行榜第一
2016年11月 Java大版内专家分月排行榜第二
本版专家分:51103
2013年3月 Java大版内专家分月排行榜第三2013年2月 Java大版内专家分月排行榜第三2012年11月 Java大版内专家分月排行榜第三2008年3月 Java大版内专家分月排行榜第三
本版专家分:10
本版专家分:399
匿名用户不能发表回复!|
CSDN今日推荐JAVA反射与注解 | 戴定康的博客
前言现在在我们构建自己或公司的项目中,或多或少都会依赖几个流行比较屌的第三方库,比如:、、、等,如果你没用过,那你需要找时间补一下啦;有时在使用后我们会好奇他们到底是怎么做到这种简洁、高效、松耦合等诸多优点的,当然这里我不探讨它们具体怎么实现的 (可以看看我之前写的) ,而关心的是它们都用到同样的技术那就是本篇所讲的反射和注解,并实现的依赖注入。
阅读本篇文章有助于你更好的理解这些大形框架的原理和复习Java的知识点。为什么要把反射放在前面讲呢,实际上是因为我们学习注解的时候需要用到反射机制,所以,先学习反射有助于理解后面的知识。
主要是指程序可以访问,检测和修改它本身状态或行为的一种能力,并能根据自身行为的状态和结果,调整或修改应用所描述行为的状态和相关的语义。
反射机制是什么
面试有可能会问到,这句话不管你能不能理解,但是你只要记住就可以了
反射机制就是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
用一句话总结就是反射可以实现在运行时可以知道任意一个类的属性和方法。
反射机制能做什么反射机制主要提供了以下功能:
在运行时判断任意一个对象所属的类;
在运行时构造任意一个类的对象;
在运行时判断任意一个类所具有的成员变量和方法;
在运行时调用任意一个对象的方法;
生成(ps:这个知识点也很重要,后续会为大家讲到)
Java 反射机制的应用场景
逆向代码 ,例如反编译
与注解相结合的框架 例如Retrofit
单纯的反射机制应用框架 例如EventBus
动态生成类框架 例如Gson
反射机制的优点与缺点为什么要用反射机制?直接创建对象不就可以了吗,这就涉及到了动态与静态的概念
静态编译:在编译时确定类型,绑定对象,即通过。
动态编译:运行时确定类型,绑定对象。动态编译最大限度发挥了java的灵活性,体现了多态的应用,有以降低类之间的藕合性。
可以实现动态创建对象和编译,体现出很大的灵活性,特别是在J2EE的开发中它的灵活性就表现的十分明显。比如,一个大型的软件,不可能一次就把把它设计的很完美,当这个程序编译后,发布了,当发现需要更新某些功能时,我们不可能要用户把以前的卸载,再重新安装新的版本,假如这样的话,这个软件肯定是没有多少人用的。采用静态的话,需要把整个程序重新编译一次才可以实现功能的更新,而采用反射机制的话,它就可以不用卸载,只需要在运行时才动态的创建和编译,就可以实现该功能。
对性能有影响。使用反射基本上是一种解释操作,我们可以告诉JVM,我们希望做什么并且它满足我们的要求。这类操作总是慢于只直接执行相同的操作。
理解Class类和类类型想要了解反射首先理解一下Class类,它是反射实现的基础。类是java.lang.Class类的实例对象,而Class是所有类的类(There is a class named Class)对于普通的对象,我们一般都会这样创建和表示:
1Code code1 = new Code();
上面说了,所有的类都是Class的对象,那么如何表示呢,可不可以通过如下方式呢:
1Class c = new Class();
但是我们查看Class的源码时,是这样写的:
123private
Class(ClassLoader loader) {
classLoader = }
可以看到构造器是私有的,只有JVM可以创建Class的对象,因此不可以像普通类一样new一个Class对象,虽然我们不能new一个Class对象,但是却可以通过已有的类得到一个Class对象,共有三种方式,如下:
123Class c1 = Code. 这说明任何一个类都有一个隐含的静态成员变量class,这种方式是通过获取类的静态成员变量class得到的Class c2 = code1.getClass(); code1是Code的一个对象,这种方式是通过一个类的对象的getClass()方法获得的 Class c3 = Class.forName("com.trigl.reflect.Code"); 这种方法是Class类调用forName方法,通过一个类的全量限定名获得
这里,c1、c2、c3都是Class的对象,他们是完全一样的,而且有个学名,叫做Code的类类型(class type)。这里就让人奇怪了,前面不是说Code是Class的对象吗,而c1、c2、c3也是Class的对象,那么Code和c1、c2、c3不就一样了吗?为什么还叫Code什么类类型?这里不要纠结于它们是否相同,只要理解类类型是干什么的就好了,顾名思义,类类型就是类的类型,也就是描述一个类是什么,都有哪些东西,所以我们可以通过类类型知道一个类的属性和方法,并且可以调用一个类的属性和方法,这就是反射的基础。
举个简单例子代码:
12345678910111213141516public class ReflectDemo {
public static void main(String[] args) throws ClassNotFoundException {
Class class1=ReflectDemo.
System.out.println(class1.getName());
ReflectDemo demo2= new ReflectDemo();
Class c2 = demo2.getClass();
System.out.println(c2.getName());
Class class3 = Class.forName("com.tengj.reflect.ReflectDemo");
System.out.println(class3.getName());
}}
执行结果:
123com.tengj.reflect.ReflectDemocom.tengj.reflect.ReflectDemocom.tengj.reflect.ReflectDemo
Java反射相关操作
在这里先看一下sun为我们提供了那些反射机制中的类:java.lang.Cjava.lang.reflect.C java.lang.reflect.Fjava.lang.reflect.Mjava.lang.reflect.M
前面我们知道了怎么获取Class,那么我们可以通过这个Class干什么呢?总结如下:
获取成员方法Method
获取成员变量Field
获取构造函数Constructor
下面来具体介绍
获取成员方法信息
两个参数分别是方法名和方法参数类的类类型列表。
12345678public Method getDeclaredMethod(String name, Class&?&... parameterTypes) public Method getMethod(String name, Class&?&... parameterTypes) Method[] methods = class1.getDeclaredMethods();Method[] allMethods = class1.getMethods();Method method = class1.getMethod("info", String.class);Method declaredMethod = class1.getDeclaredMethod("info", String.class);
举个例子:
例如类A有如下一个方法:
123public void fun(String name,int age) {
System.out.println("我叫"+name+",今年"+age+"岁");
现在知道A有一个对象a,那么就可以通过:
1234Class c = Class.forName("com.tengj.reflect.Person");
Object o = c.newInstance();
Method method = c.getMethod("fun", String.class, int.class);method.invoke(o, "tengj", 10);
完整代码如下:
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849public class Person {
private int
private String msg="hello wrold"; public String getName() {
public void setName(String name) {
this.name =
public int getAge() {
public void setAge(int age) {
this.age =
public Person() {
private Person(String name) {
this.name =
System.out.println(name);
public void fun() {
System.out.println("fun");
public void fun(String name,int age) {
System.out.println("我叫"+name+",今年"+age+"岁");
}}public class ReflectDemo {
public static void main(String[] args){
try {
Class c = Class.forName("com.tengj.reflect.Person");
Object o = c.newInstance();
Method method = c.getMethod("fun", String.class, int.class);
method.invoke(o, "tengj", 10);
} catch (Exception e) {
e.printStackTrace();
}}
执行结果:
我叫tengj,今年10岁
怎样,是不是感觉很厉害,我们只要知道这个类的路径全称就能玩弄它于鼓掌之间。
有时候我们想获取类中所有成员方法的信息,要怎么办。可以通过以下几步来实现:
1.获取所有方法的数组:
1234Class c = Class.forName("com.tengj.reflect.Person");Method[] methods = c.getDeclaredMethods(); 或者:Method[] methods = c.getMethods();
2.然后循环这个数组就得到每个方法了:
1for (Method method : methods)
完整代码如下:person类跟上面一样,这里以及后面就不贴出来了,只贴关键代码
1234567891011121314public class ReflectDemo {
public static void main(String[] args){
try {
Class c = Class.forName("com.tengj.reflect.Person");
Method[] methods = c.getDeclaredMethods();
for(Method m:methods){
methodName= m.getName();
System.out.println(methodName);
} catch (Exception e) {
e.printStackTrace();
}}
执行结果:
这里如果把c.getDeclaredMethods();改成c.getMethods();执行结果如下,多了很多方法,以为把Object里面的方法也打印出来了,因为Object是所有类的父类:
获取成员变量信息
想一想成员变量中都包括什么:成员变量类型+成员变量名
类的成员变量也是一个对象,它是java.lang.reflect.Field的一个对象,所以我们通过java.lang.reflect.Field里面封装的方法来获取这些信息。
单独获取某个成员变量,通过Class类的以下方法实现:
参数是成员变量的名字
12345678public Field getDeclaredField(String name) public Field getField(String name) Field[] allFields = class1.getDeclaredFields();Field[] publicFields = class1.getFields();Field ageField = class1.getDeclaredField("age");Field desField = class1.getField("des");
举个例子:
例如一个类A有如下成员变量:
1private int
如果A有一个对象a,那么就可以这样得到其成员变量:
12Class c = a.getClass();Field field = c.getDeclaredField("n");
完整代码如下:
123456789101112131415public class ReflectDemo {
public static void main(String[] args){
try {
Class c = Class.forName("com.tengj.reflect.Person");
Field field = c.getDeclaredField("msg");
Object o = c.newInstance();
field.setAccessible(true);
Object msg = field.get(o);
System.out.println(msg);
} catch (Exception e) {
e.printStackTrace();
}}
执行结果:
hello wrold
同样,如果想要获取所有成员变量的信息,可以通过以下几步
1.获取所有成员变量的数组:
1Field[] fields = c.getDeclaredFields();
2.遍历变量数组,获得某个成员变量field
1for (Field field : fields)
完整代码:
12345678910111213public class ReflectDemo {
public static void main(String[] args){
try {
Class c = Class.forName("com.tengj.reflect.Person");
Field[] fields = c.getDeclaredFields();
for(Field field :fields){
System.out.println(field.getName());
} catch (Exception e) {
e.printStackTrace();
}}
执行结果:
获取构造函数
最后再想一想构造函数中都包括什么:构造函数参数同上,类的成构造函数也是一个对象,它是java.lang.reflect.Constructor的一个对象,所以我们通过java.lang.reflect.Constructor里面封装的方法来获取这些信息。
单独获取某个构造函数,通过Class类的以下方法实现:
这个参数为构造函数参数类的类类型列表
12345678public Constructor&T& getDeclaredConstructor(Class&?&... parameterTypes) public Constructor&T& getConstructor(Class&?&... parameterTypes) Constructor&?&[] allConstructors = class1.getDeclaredConstructors();Constructor&?&[] publicConstructors = class1.getConstructors();Constructor&?& constructor = class1.getDeclaredConstructor(String.class);Constructor publicConstructor = class1.getConstructor(String.class);
举个例子:
例如类A有如下一个构造函数:
123public A(String a, int b) {
那么就可以通过:
1Constructor constructor = a.getDeclaredConstructor(String.class, int.class);
来获取这个构造函数。
完整代码:
12345678910111213public class ReflectDemo {
public static void main(String[] args){
try {
Class c = Class.forName("com.tengj.reflect.Person");
Constructor constructor = c.getDeclaredConstructor(String.class);
constructor.setAccessible(true);
constructor.newInstance("tengj");
} catch (Exception e) {
e.printStackTrace();
}}
执行结果:
注意:Class的newInstance方法,只能创建只包含无参数的构造函数的类,如果某类只有带参数的构造函数,那么就要使用另外一种方式:
1fromClass.getDeclaredConstructor(String.class).newInstance("tengj");
获取所有的构造函数,可以通过以下步骤实现:
1.获取该类的所有构造函数,放在一个数组中:
1Constructor[] constructors = c.getDeclaredConstructors();
2.遍历构造函数数组,获得某个构造函数constructor:
1for (Constructor constructor : constructors)
完整代码:
1234567891011public class ReflectDemo {
public static void main(String[] args){
Constructor[] constructors = c.getDeclaredConstructors();
for(Constructor constructor:constructors){
System.out.println(constructor);
} catch (Exception e) {
e.printStackTrace();
}}
执行结果:
public com.tengj.reflect.Person()
public com.tengj.reflect.Person(java.lang.String)
注解需要用到的
1234Annotation[] annotations = (Annotation[]) class1.getAnnotations();Annotation annotation = (Annotation) class1.getAnnotation(Deprecated.class);Type genericSuperclass = class1.getGenericSuperclass();Type Type[] interfaceTypes = class1.getGenericInterfaces();
获取class对象的信息
12345678910111213141516boolean isPrimitive = class1.isPrimitive();boolean isArray = class1.isArray(); boolean isAnnotation = class1.isAnnotation();boolean isInterface = class1.isInterface();boolean isEnum = class1.isEnum();boolean isAnonymousClass = class1.isAnonymousClass();boolean isAnnotationPresent = class1.isAnnotationPresent(Deprecated.class);String className = class1.getName();Package aPackage = class1.getPackage();String simpleName = class1.getSimpleName();int modifiers = class1.getModifiers();Class&?&[] declaredClasses = class1.getDeclaredClasses();Class&?& declaringClass = class1.getDeclaringClass();getSuperclass():获取某类的父类
getInterfaces():获取某类实现的接口
通过反射了解集合泛型的本质
扩展的知识点,了解就可以了。后续会为大家写一篇关于泛型的文章。
首先下结论:
Java中集合的泛型,是防止错误输入的,只在编译阶段有效,绕过编译到了运行期就无效了。
下面通过一个实例来验证:
123456789101112131415161718192021222324252627282930313233343536373839404142public class GenericEssence {
public static void main(String[] args) {
List list1 = new ArrayList();
List&String& list2 = new ArrayList&String&();
list2.add("hello");
System.out.println("list2的长度是:" + list2.size());
Class c1 = list1.getClass();
Class c2 = list2.getClass();
System.out.println(c1 == c2);
try {
Method m = c2.getMethod("add", Object.class);
m.invoke(list2, 20);
System.out.println("list2的长度是:" + list2.size());
} catch (Exception e) {
e.printStackTrace();
}}
执行结果:
list2的长度是:1
list2的长度是:2
有助于理解上述所讲的知识点
JAVA注解概念及作用
注解即元数据,就是源代码的元数据
注解在代码中添加信息提供了一种形式化的方法,可以在后续中更方便的 使用这些数据
Annotation是一种应用于类、方法、参数、变量、构造器及包声明中的特殊修饰符。它是一种由JSR-175标准选择用来描述元数据的一种工具。
跟踪代码依赖性,实现替代配置文件功能,减少配置。如Spring中的一些注解
在编译时进行格式检查,如@Override等
每当你创建描述符性质的类或者接口时,一旦其中包含重复性的工作,就可以考虑使用注解来简化与自动化该过程。
什么是java注解?在java语法中,使用@符号作为开头,并在@后面紧跟注解名。被运用于类,接口,方法和字段之上,例如:
1234@Overridevoid myMethod() { ......}
这其中@Override就是注解。这个注解的作用也就是告诉编译器,myMethod()方法覆写了父类中的myMethod()方法。
java中内置的注解java中有三个内置的注解:
@Override:表示当前的方法定义将覆盖超类中的方法,如果出现错误,编译器就会报错。
@Deprecated:如果使用此注解,编译器会出现警告信息。
@SuppressWarnings:忽略编译器的警告信息。
本文不在阐述三种内置注解的使用情节和方法,感兴趣的请看
自定义注解的时候用到的,也就是自定义注解的注解;(这句话我自己说的,不知道对不对)
元注解的作用就是负责注解其他注解。Java5.0定义了4个标准的meta-annotation类型,它们被用来提供对其它 annotation类型作说明。
Java5.0定义的4个元注解:
@Retention
@Documented
@Inherited
java8加了两个新注解,后续我会讲到。
这些类型和它们所支持的类在java.lang.annotation包中可以找到。
@Target说明了Annotation所修饰的对象范围:Annotation可被用于 packages、types(类、接口、枚举、Annotation类型)、类型成员(方法、构造方法、成员变量、枚举值)、方法参数和本地变量(如循环变量、catch参数)。在Annotation类型的声明中使用了target可更加明晰其修饰的目标。
作用:用于描述注解的使用范围(即:被描述的注解可以用在什么地方)
取值(ElementType)有:
CONSTRUCTOR
用于描述构造器
用于描述域
LOCAL_VARIABLE
用于描述局部变量
用于描述方法
用于描述包
用于描述参数
用于描述类、接口(包括注解类型) 或enum声明
比如说这个注解表示只能在方法中使用:
12345678910111213@Target({ElementType.METHOD})public @interface MyCustomAnnotation {}public class MyClass {
@MyCustomAnnotation
public void myMethod()
}}
@Retention
@Retention定义了该Annotation被保留的时间长短:某些Annotation仅出现在源代码中,而被编译器丢弃;而另一些却被编译在class文件中;编译在class文件中的Annotation可能会被虚拟机忽略,而另一些在class被装载时将被读取(请注意并不影响class的执行,因为Annotation与class在使用上是被分离的)。使用这个meta-Annotation可以对 Annotation的“生命周期”限制。
作用:表示需要在什么级别保存该注释信息,用于描述注解的生命周期(即:被描述的注解在什么范围内有效)
取值(RetentionPoicy)有:
在源文件中有效(即源文件保留)
仅出现在源代码中,而被编译器丢弃
在class文件中有效(即class保留)
被编译在class文件中
在运行时有效(即运行时保留)
编译在class文件中
使用示例:
12345678@Target(value = {ElementType.FIELD})@Retention(value = RetentionPolicy.RUNTIME)public @interface Column {
String name();}
@Documented
@Documented用于描述其它类型的annotation应该被作为被标注的程序成员的公共API,因此可以被例如javadoc此类的工具文档化。Documented是一个标记注解,没有成员。
作用:将注解包含在javadoc中
123java.lang.annotation.Documented@Documentedpublic @interface MyCustomAnnotation {
@Inherited
是一个标记注解
阐述了某个被标注的类型是被继承的
使用了@Inherited修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类@Inherited annotation类型是被标注过的class的子类所继承。类并不从实现的接口继承annotation,方法不从它所重载的方法继承annotation
当@Inherited annotation类型标注的annotation的Retention是RetentionPolicy.RUNTIME,则反射API增强了这种继承性。如果我们使用java.lang.reflect去查询一个@Inherited annotation类型的annotation时,反射代码检查将展开工作:检查class和其父类,直到发现指定的annotation类型被发现,或者到达类继承结构的顶层。
作用:允许子类继承父类中的注解
示例,这里的MyParentClass 使用的注解标注了@Inherited,所以子类可以继承这个注解信息:
1234java.lang.annotation.Inherited@Inheritedpublic @interface MyCustomAnnotation {}
1234@MyCustomAnnotationpublic class MyParentClass {
... }
123public class MyChildClass extends MyParentClass {
... }
自定义注解格式123public @interface 注解名{
定义体}
注解参数的可支持数据类型:
所有基本数据类型(int,float,double,boolean,byte,char,long,short)
String 类型
Annotation类型
以上所有类型的数组
修饰符只能是public 或默认(default)
参数成员只能用基本类型byte,short,int,long,float,double,boolean八种基本类型和String,Enum,Class,annotations及这些类型的数组
如果只有一个参数成员,最好将名称设为”value”
注解元素必须有确定的值,可以在注解中定义默认值,也可以使用注解时指定,非基本类型的值不可为null,常使用空字符串或0作默认值
在表现一个元素存在或缺失的状态时,定义一下特殊值来表示,如空字符串或负值
示例:1234567891011121314151617181920 ddk @Target(ElementType.FIELD)@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface TestAnnotation {
public int id() default -1;
public String name() default "";}
注解处理器类库
java.lang.reflect.AnnotatedElement
Java使用Annotation接口来代表程序元素前面的注解,该接口是所有Annotation类型的父接口。除此之外,Java在java.lang.reflect 包下新增了AnnotatedElement接口,该接口代表程序中可以接受注解的程序元素,该接口主要有如下几个实现类:
 Class:类定义
 Constructor:构造器定义
 Field:累的成员变量定义
 Method:类的方法定义
 Package:类的包定义
java.lang.reflect 包下主要包含一些实现反射功能的工具类,实际上,java.lang.reflect 包所有提供的反射API扩充了读取运行时Annotation信息的能力。当一个Annotation类型被定义为运行时的Annotation后,该注解才能是运行时可见,当class文件被装载时被保存在class文件中的Annotation才会被虚拟机读取。
AnnotatedElement 接口是所有程序元素(Class、Method和Constructor)的父接口,所以程序通过反射获取了某个类的AnnotatedElement对象之后,程序就可以调用该对象的如下四个个方法来访问Annotation信息:
方法1: T getAnnotation(Class annotationClass): 返回改程序元素上存在的、指定类型的注解,如果该类型注解不存在,则返回null。
方法2:Annotation[] getAnnotations():返回该程序元素上存在的所有注解。
方法3:boolean is AnnotationPresent(Class&?extends Annotation& annotationClass):判断该程序元素上是否包含指定类型的注解,存在则返回true,否则返回false.
方法4:Annotation[] getDeclaredAnnotations():返回直接存在于此元素上的所有注释。与此接口中的其他方法不同,该方法将忽略继承的注释。(如果没有注释直接存在于此元素上,则返回长度为零的一个数组。)该方法的调用者可以随意修改返回的数组;这不会对其他调用者返回的数组产生任何影响。
注解处理器示例:123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156 peida@Target(ElementType.FIELD)@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface FruitName {
String value() default "";} peida@Target(ElementType.FIELD)@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface FruitColor {
public enum Color{ BULE,RED,GREEN};
Color fruitColor() default Color.GREEN;} peida@Target(ElementType.FIELD)@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface FruitProvider {
public int id() default -1;
public String name() default "";
public String address() default "";}public class Apple {
@FruitName("Apple")
private String appleN
@FruitColor(fruitColor=Color.RED)
private String appleC
@FruitProvider(id=1,name="陕西红富士集团",address="陕西省西安市延安路89号红富士大厦")
private String appleP
public void setAppleColor(String appleColor) {
this.appleColor = appleC
public String getAppleColor() {
return appleC
public void setAppleName(String appleName) {
this.appleName = appleN
public String getAppleName() {
return appleN
public void setAppleProvider(String appleProvider) {
this.appleProvider = appleP
public String getAppleProvider() {
return appleP
public void displayName(){
System.out.println("水果的名字是:苹果");
}}public class FruitInfoUtil {
public static void getFruitInfo(Class&?& clazz){
String strFruitName=" 水果名称:";
String strFruitColor=" 水果颜色:";
String strFruitProvicer="供应商信息:";
Field[] fields = clazz.getDeclaredFields();
for(Field field :fields){
if(field.isAnnotationPresent(FruitName.class)){
FruitName fruitName = (FruitName) field.getAnnotation(FruitName.class);
strFruitName=strFruitName+fruitName.value();
System.out.println(strFruitName);
else if(field.isAnnotationPresent(FruitColor.class)){
FruitColor fruitColor= (FruitColor) field.getAnnotation(FruitColor.class);
strFruitColor=strFruitColor+fruitColor.fruitColor().toString();
System.out.println(strFruitColor);
else if(field.isAnnotationPresent(FruitProvider.class)){
FruitProvider fruitProvider= (FruitProvider) field.getAnnotation(FruitProvider.class);
strFruitProvicer=" 供应商编号:"+fruitProvider.id()+" 供应商名称:"+fruitProvider.name()+" 供应商地址:"+fruitProvider.address();
System.out.println(strFruitProvicer);
}}public class FruitRun {
public static void main(String[] args) {
FruitInfoUtil.getFruitInfo(Apple.class);
}}==================================== 水果名称:Apple 水果颜色:RED 供应商编号:1 供应商名称:陕西红富士集团 供应商地址:陕西省西安市延安路89号红富士大厦
Java 8 中注解新特性
@Repeatable 元注解,表示被修饰的注解可以用在同一个声明式或者类型加上多个相同的注解(包含不同的属性值)
@Native 元注解,本地方法
java8 中Annotation 可以被用在任何使用 Type 的地方
123456789101112 String myString = new @NotNull String();myString = (@NonNull String)class MyList&T& implements @ReadOnly List&@ReadOnly T&{...}public void validateValues() throws @Critical ValidationFailedException{...}
欢迎您扫一扫上面的微信,加我为好友!

我要回帖

更多关于 java自定义注解的作用 的文章

 

随机推荐