Java:注解和反射

Posted opendragonhuang

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java:注解和反射相关的知识,希望对你有一定的参考价值。

1. 注解

 

1.1 注解的定义

注解就是源代码的元数据,通熟的讲就是代码中的标签。注解就有如下的特点:

  1. 注解是一个附属品,依赖于其他元素(包、类、方法、属性等等)存在。

  2. 注解本身没有作用,在恰当的时候由外部程序进行解析才会发生作用。

 

1.2 注解的分类

  1. 按来源分

    • JDK 自带注解,例如:@Override, @Deprecated, @SuppressWornings 。

    • 第三方注解。

    • 自定义注解。

  2. 按生命周期划分

    • SOURCE:只存在于源代码中,编译成 class 文件就不存在了。

    • Class:存在于源代码中和 class 文件中。

    • RUNTIME:注解保留到运行时。

 

1.3 元注解

元注解指的是用于修饰注解的注解,包括如下几个:

  1. @Retention:指明 Annotation 的生命周期,传入的值是一个枚举类型,可选值为:

    • RetentionPolicy.SOURCE

    • RetentionPolicy.CLASS

    • RetentionPolicy.RUNTIME

  2. @Target:指明 Annotation 可以修饰程序哪些元素,传入的值为ElemetType[] 类型,值可为:

    • ElementType.CONSTRUCTOR :构造器

    • ElementType.FIELD:属性

    • ElementType.LOCAL_VARIABLE:局部变量

    • ElementType.METHOD:方法

    • ElementType.PACKAGE:包

    • ElementType.PARAMETER:参数

    • ElementType.TYPE:类、接口(包括注解类型和 enum 声明)

  3. @Documented:使用此修饰的注解将会被 javadoc 工具提取成文档,使用此注解,其 @Retention 必须被设置为 RetentionPolicy.RUNTIME

  4. @Inherited:具有继承性。

 

1.4 自定义注解

自定义注解需要注意的问题:

  1. 使用 @interface 关键字定义。

  2. 自动继承 java.lang.annotation.Annotation 接口。

  3. 配置参数的类型只能是八大基本类型、String、Class、enum、Annotation 和对应的数组类型。

  4. 配置参数声明的语法格式如下([] 表示可省略):

    类型 变量名() [default 默认值];
  5. 如果只有一个配置参数,其参数名必须为 value。

  6. 如果定义的注解含有配置参数,那在使用注解时,必须指定参数值,指定形式为:“参数名=参数值”。如果只有一个参数,直接写参数值即可,定义中指定了默认值的参数可以不指定值,但没有的一定要指定值

  7. 没有成员的注解为标记,包含成员的称为元数据

 

1.5 注解的解析

参考代码:

(1)Info.java

 1 package com.hkl;
 2 ?
 3 import java.lang.annotation.ElementType;
 4 import java.lang.annotation.Retention;
 5 import java.lang.annotation.RetentionPolicy;
 6 import java.lang.annotation.Target;
 7 ?
 8 /**
 9  * 注解
10  */
11 @Retention(RetentionPolicy.RUNTIME)
12 @Target(ElementType.TYPE, ElementType.METHOD)
13 public @interface Info 
14     String info();
15     String birthday();
16     int age() default 0;
17 
18 ?
19 @Retention(RetentionPolicy.RUNTIME)
20 @Target(ElementType.TYPE, ElementType.METHOD)
21 @interface Desc
22     String value();
23 

 

 

(2)App.java

 1 package com.hkl;
 2 ?
 3 import java.lang.reflect.Method;
 4 ?
 5 /**
 6  * Hello world!
 7  *
 8  */
 9 @Info(info = "hkl", birthday = "2019/7/20")
10 @Desc("这是一个类")
11 public class App 
12 
13     @Info(info = "hkl", birthday = "2019/7/20", age = 22)
14     @Desc("这是一个方法")
15     public static void main( String[] args )
16     
17         // 解析注解
18         try 
19             Class clazz = Class.forName("com.hkl.App");
20 ?
21             // 获取类修饰的注解
22             System.out.println("---------类中的注解---------");
23             if(clazz.isAnnotationPresent(Info.class))
24                 Info classInfo = (Info) clazz.getAnnotation(Info.class);
25                 System.out.println(classInfo.info());
26                 System.out.println(classInfo.birthday());
27                 System.out.println(classInfo.age());
28             
29 ?
30             if(clazz.isAnnotationPresent(Desc.class))
31                 Desc classDesc = (Desc)clazz.getAnnotation(Desc.class);
32                 System.out.println(classDesc.value());
33             
34 ?
35             // 获取方法修饰的注解
36             Method[] methods = clazz.getMethods();
37 ?
38             System.out.println("---------方法中的注解解析---------");
39             for(Method method : methods)
40                 if(method.isAnnotationPresent(Desc.class))
41                     Desc methodDesc = (Desc)method.getAnnotation(Desc.class);
42                     System.out.println(methodDesc.value());
43                 
44 ?
45                 if(method.isAnnotationPresent(Info.class))
46                     Info methodInfo = (Info)method.getAnnotation(Info.class);
47                     System.out.println(methodInfo.info());
48                     System.out.println(methodInfo.birthday());
49                     System.out.println(methodInfo.age());
50                 
51 ?
52             
53          catch (ClassNotFoundException e) 
54             e.printStackTrace();
55         
56     
57 

 

2. 反射

 

2.1 什么反射

反射指的是程序在运行期间借助反射 API 取得任何类的内部信息,并通过这些内部信息去操作对应对象的内部属性和方法。

任何一个类,在第一次使用时,就会被 JVM 加载到堆内存的方法区中。JVM 加载类成功后,就会在方法区中产生一个对应的 Class 对象(一个类只要一个 Class 对象),这个 Class 对象包含被加载类的全部结构信息。

 

2.2 获取 Class 对象的常用方式

(1)类的 class 属性

每一个类,都有一个 class 静态属性,这个静态属性就是类对应的 Class 对象。

1 Class<Person> cl1 = Person.class; 

 

(2)Object 对象 的 getClass() 方法

1 Person p1 = new Person();
2 Class<Person> cl2 = (Class<Person>) p1.getClass(); 

 

(3)通过 Class 类的 forName() 方法(最常用)

1 try 
2     Class cl3 = Class.forName("com.llm.hkl.Person");
3  catch (ClassNotFoundException e) 
4     e.printStackTrace();
5 

 

(4)通过 ClassLoader 类(不常用)

1 ClassLoader cl = Person.class.getClassLoader();
2 try 
3     Class cl4 = cl.loadClass("com.llm.hkl.Person");
4  catch (ClassNotFoundException e) 
5     e.printStackTrace();
6 

 

2.3 反射的基本使用

反射的基本使用包括创建对象,设置属性和调用方法。Class 对象中大多数 get 方法有 Declared 和无 Declared,他们的区别是:

  1. 无 Declared:只能获取到 public 修饰的,包括当前类和所有父类。

  2. 有 Declared:获取到当前类所有的(含有 private),但不包括其父类。

 

Person 类:

 1 public class Person 
 2     private String name;
 3     private int age;
 4     public String habbit;
 5 ?
 6     public Person() 
 7     
 8 ?
 9     public Person(String name, int age) 
10         this.name = name;
11         this.age = age;
12     
13 ?
14     private Person(String name, int age, String habbit) 
15         this.name = name;
16         this.age = age;
17         this.habbit = habbit;
18     
19 ?
20     public String getName() 
21         return name;
22     
23 ?
24     public void setName(String name) 
25         this.name = name;
26     
27 ?
28     public int getAge() 
29         return age;
30     
31 ?
32     public void setAge(int age) 
33         this.age = age;
34     
35 ?
36     public String getHabbit() 
37         return habbit;
38     
39 ?
40     public void setHabbit(String habbit) 
41         this.habbit = habbit;
42     
43 ?
44     private String say(String str)
45         String str1 = name+"说:"+str;
46         System.out.println(str1);
47         return str1;
48     
49 ?
50     public void eat(String food)
51         System.out.println(name+"吃"+food);
52     
53 ?
54     @Override
55     public String toString() 
56         return "["+name+","+age+","+habbit+"]";
57     
58 

测试方法:
 1 public class PersonTest 
 2     @Test
 3     public void ReflexTest() throws Exception
 4         System.out.println("---------- new 方式创建对象 ----------");
 5         // 普通方式创建 Person 对象
 6         Person p1 = new Person("hkl", 22);
 7 ?
 8         // 直接设置属性
 9         p1.habbit = "编程";
10         // 调用方法
11         System.out.println(p1);
12 ?
13         //无法直接设置私有属性和调用私有方法
14         //p1.name = "hkl";
15         //p1.say(""Hello);
16 ?
17         //反射方式创建对象
18         System.out.println("---------- 反射方式创建对象 ----------");
19         Class<Person> clazz = Person.class;
20 ?
21         // 调用无参构造器参加对象
22         Constructor<Person> constructor1 = clazz.getConstructor();
23         Person p2 = constructor1.newInstance();
24         System.out.println(p2);
25 ?
26         // 通过有参构造器
27         Constructor<Person> constructor2 = clazz.getConstructor(String.class, int.class);
28         Person p3 = constructor2.newInstance("hkl", 22);
29         System.out.println(p3);
30 ?
31         // 通过私有的构造器
32         Constructor<Person> constructor3 = clazz.getDeclaredConstructor(String.class, int.class, String.class);
33         constructor3.setAccessible(true);
34         Person p4 = constructor3.newInstance("hkl", 22, "编程");
35         System.out.println(p4);
36 ?
37         //通过反射设置公有属性
38         Field personFeildHabbit = clazz.getDeclaredField("habbit");
39         personFeildHabbit.set(p2, "编程");
40 ?
41         //通过反射设置私有属性
42         Field personFeildAge = clazz.getDeclaredField("age");
43         personFeildAge.setAccessible(true);
44         personFeildAge.set(p2, 18);
45 ?
46         //通过反射调用方法
47         Method personMethodToString = clazz.getDeclaredMethod("toString");
48         // 方法的返回值为调用方法的返回值
49         String str = (String)personMethodToString.invoke(p2);
50         System.out.println(str);
51 ?
52         // 通过反射调用私有方法
53         Method personMethodSay = clazz.getDeclaredMethod("say", String.class);
54         personMethodSay.setAccessible(true);
55         String str2 = (String)personMethodSay.invoke(p2, "Hello");
56         System.out.println(str2);
57     
58 

 

2.5 设计模式:代理模式

使用一个代理对象将原始对象包装起来, 然后用该代理对象取代原始对象。任何对原始对象的调用都要通过代理对象,代理对象决定是否以及何时将方法调用转到原始对象上。

2.5.1 静态代理

代理类和原始对象在编译期间就确定下来了,不利于程序的扩展,且每一个代理只能为一个接口服务,这就会在开发的过程中产生多大的代理类。

静态代理的实现:

  1. 代理类和原始对象实现相同的接口。

  2. 代理类保持原始对象的引用。

  3. 代理类里调用原始对象的方法。

 

ClothFactory 接口:

1 package com.hkl.proxy;
2 ?
3 ?
4 public interface ClothFactory 
5     String producer();
6 

 

ClothFactoryProxy 类:

 

 1 package com.hkl.proxy;
 2 ?
 3 public class ClothFactoryProxy implements ClothFactory
 4     private ClothFactory clothFactory;
 5 ?
 6     public ClothFactoryProxy(ClothFactory clothFactory) 
 7         this.clothFactory = clothFactory;
 8     
 9 ?
10     @Override
11     public String producer() 
12         System.out.println("代理对象做一些准备");
13         clothFactory.producer();
14         System.out.println("代理对象做一些收尾工作");
15 ?
16         return null;
17     
18 

 

NikeFactory 类:

 1 package com.hkl.proxy;
 2 ?
 3 public class NikeFactory implements ClothFactory
 4 ?
 5     public NikeFactory() 
 6     
 7 ?
 8     @Override
 9     public String producer() 
10         System.out.println("Nike 正在生产衣服");
11         return null;
12     
13 

 

测试方法:

 1 @Test
 2 public void staticProxyTest()
 3     // 创建被代理对象
 4     NikeFactory nikeFactory = new NikeFactory();
 5 ?
 6     // 创建代理对象
 7     ClothFactoryProxy clothFactoryProxy = new ClothFactoryProxy(nikeFactory);
 8 ?
 9     // 通过代理对象调用被代理对象的方法
10     clothFactoryProxy.producer();
11 

 

2.5.2 动态代理模式

动态代理是通过 Java 的反射机制实现的。通过动态代理,只需一个代理对象就可以代理所有的对象。

 

Humen :接口

1 package com.llm.proxy;
2 public interface Human 
3     String belief();
4     void eat(String food);
5 

 

SuperMan:类

 1 package com.hkl.proxy;
 2 ?
 3 public class SuperMan implements Human 
 4     @Override
 5     public String belief() 
 6         return "我相信,我能行!";
 7     
 8 ?
 9     @Override
10     public void eat(String food) 
11         System.out.println("正在吃"+food);
12     
13 

 

动态代理类:

 1 package com.hkl.proxy;
 2 ?
 3 import java.lang.reflect.InvocationHandler;
 4 import java.lang.reflect.Method;
 5 import java.lang.reflect.Proxy;
 6 ?
 7 ?
 8 public class DynamicProxy 
 9     // 获取代理对象
10     public static Object getInstance(Object obj)
11         MyInvocation h = new MyInvocation();
12         h.bind(obj);
13         // 动态的创建对象
14         return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), h);
15     
16 
17 ?
18 class MyInvocation implements InvocationHandler
19     private Object obj;
20 ?
21     /**
22      * 绑定被代理对象
23      * @param obj
24      */
25     public void bind(Object obj)
26         this.obj = obj;
27     
28 ?
29     @Override
30     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable 
31         // 代理对象调用被代理对象的方法
32         Object ret = method.invoke(obj, args);
33         return ret;
34     
35 

 

测试方法:

 1 @Test
 2 public void dynamicProxyTest()
 3     SuperMan sm = new SuperMan();
 4     Human h = (Human)DynamicProxy.getInstance(sm);
 5     System.out.println(h.belief());;
 6     h.eat("麻辣烫");
 7 ?
 8     NikeFactory nikeFactory = new NikeFactory();
 9     ClothFactory clothFactory = (ClothFactory) DynamicProxy.getInstance(nikeFactory);
10     clothFactory.producer();
11 

 

 

以上是关于Java:注解和反射的主要内容,如果未能解决你的问题,请参考以下文章

Java反射和注解基础

java反射机制 怎样获取到类上面的注解

Java学习注解和反射超详细笔记

[记录] java反射+注解

注解和反射反射获取泛型反射获取注解

Java中的注解和反射