美文网首页JavaJAVA
Java 注解和反射

Java 注解和反射

作者: 犹梦渔樵 | 来源:发表于2019-07-22 12:17 被阅读0次

    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 注解的解析

    Info.java:

    package com.hkl;
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    /**
     * 注解
     */
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.TYPE, ElementType.METHOD})
    public @interface Info {
        String info();
        String birthday();
        int age() default 0;
    }
    
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.TYPE, ElementType.METHOD})
    @interface Desc{
        String value();
    }
    

    App.java:

    package com.hkl;
    
    import java.lang.reflect.Method;
    
    /**
     * Hello world!
     *
     */
    @Info(info = "hkl", birthday = "2019/7/20")
    @Desc("这是一个类")
    public class App 
    {
        @Info(info = "hkl", birthday = "2019/7/20", age = 22)
        @Desc("这是一个方法")
        public static void main( String[] args )
        {
            // 解析注解
            try {
                Class clazz = Class.forName("com.hkl.App");
    
                // 获取类修饰的注解
                System.out.println("---------类中的注解---------");
                if(clazz.isAnnotationPresent(Info.class)){
                    Info classInfo = (Info) clazz.getAnnotation(Info.class);
                    System.out.println(classInfo.info());
                    System.out.println(classInfo.birthday());
                    System.out.println(classInfo.age());
                }
    
                if(clazz.isAnnotationPresent(Desc.class)){
                    Desc classDesc = (Desc)clazz.getAnnotation(Desc.class);
                    System.out.println(classDesc.value());
                }
    
                // 获取方法修饰的注解
                Method[] methods = clazz.getMethods();
    
                System.out.println("---------方法中的注解解析---------");
                for(Method method : methods){
                    if(method.isAnnotationPresent(Desc.class)){
                        Desc methodDesc = (Desc)method.getAnnotation(Desc.class);
                        System.out.println(methodDesc.value());
                    }
    
                    if(method.isAnnotationPresent(Info.class)){
                        Info methodInfo = (Info)method.getAnnotation(Info.class);
                        System.out.println(methodInfo.info());
                        System.out.println(methodInfo.birthday());
                        System.out.println(methodInfo.age());
                    }
    
                }
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
    }
    
    

    2. 反射

    2.1 什么反射

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

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

    2.2 获取 Class 对象的常用方式

    (1)类的 class 属性

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

    Class<Person> cl1 = Person.class;
    

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

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

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

    try {
        Class cl3 = Class.forName("com.llm.hkl.Person");
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
    

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

    ClassLoader cl = Person.class.getClassLoader();
    try {
        Class cl4 = cl.loadClass("com.llm.hkl.Person");
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
    

    2.3 反射的基本使用

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

    1. 无 Declared:只能获取到 public 修饰的,包括当前类和所有父类。
    2. 有 Declared:获取到当前类所有的(含有 private),但不包括其父类。

    Person 类:

    public class Person {
        private String name;
        private int age;
        public String habbit;
    
        public Person() {
        }
    
        public Person(String name, int age) {
            this.name = name;
            this.age = age;
        }
    
        private Person(String name, int age, String habbit) {
            this.name = name;
            this.age = age;
            this.habbit = habbit;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
        public String getHabbit() {
            return habbit;
        }
    
        public void setHabbit(String habbit) {
            this.habbit = habbit;
        }
    
        private String say(String str){
            String str1 = name+"说:"+str;
            System.out.println(str1);
            return str1;
        }
    
        public void eat(String food){
            System.out.println(name+"吃"+food);
        }
    
        @Override
        public String toString() {
            return "["+name+","+age+","+habbit+"]";
        }
    }
    
    

    测试方法:

    public class PersonTest {
        @Test
        public void ReflexTest() throws Exception{
            System.out.println("---------- new 方式创建对象 ----------");
            // 普通方式创建 Person 对象
            Person p1 = new Person("hkl", 22);
    
            // 直接设置属性
            p1.habbit = "编程";
            // 调用方法
            System.out.println(p1);
    
            //无法直接设置私有属性和调用私有方法
            //p1.name = "hkl";
            //p1.say(""Hello);
    
            //反射方式创建对象
            System.out.println("---------- 反射方式创建对象 ----------");
            Class<Person> clazz = Person.class;
    
            // 调用无参构造器参加对象
            Constructor<Person> constructor1 = clazz.getConstructor();
            Person p2 = constructor1.newInstance();
            System.out.println(p2);
    
            // 通过有参构造器
            Constructor<Person> constructor2 = clazz.getConstructor(String.class, int.class);
            Person p3 = constructor2.newInstance("hkl", 22);
            System.out.println(p3);
    
            // 通过私有的构造器
            Constructor<Person> constructor3 = clazz.getDeclaredConstructor(String.class, int.class, String.class);
            constructor3.setAccessible(true);
            Person p4 = constructor3.newInstance("hkl", 22, "编程");
            System.out.println(p4);
    
            //通过反射设置公有属性
            Field personFeildHabbit = clazz.getDeclaredField("habbit");
            personFeildHabbit.set(p2, "编程");
    
            //通过反射设置私有属性
            Field personFeildAge = clazz.getDeclaredField("age");
            personFeildAge.setAccessible(true);
            personFeildAge.set(p2, 18);
    
            //通过反射调用方法
            Method personMethodToString = clazz.getDeclaredMethod("toString");
            // 方法的返回值为调用方法的返回值
            String str = (String)personMethodToString.invoke(p2);
            System.out.println(str);
    
            // 通过反射调用私有方法
            Method personMethodSay = clazz.getDeclaredMethod("say", String.class);
            personMethodSay.setAccessible(true);
            String str2 = (String)personMethodSay.invoke(p2, "Hello");
            System.out.println(str2);
        }
    }
    

    2.5 设计模式:代理模式

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

    2.5.1 静态代理

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

    静态代理的实现:

    1. 代理类和原始对象实现相同的接口。
    2. 代理类保持原始对象的引用。
    3. 代理类里调用原始对象的方法。

    ClothFactory 接口:

    package com.hkl.proxy;
    
    
    public interface ClothFactory {
        String producer();
    }
    

    ClothFactoryProxy 类:

    package com.hkl.proxy;
    
    public class ClothFactoryProxy implements ClothFactory{
        private ClothFactory clothFactory;
    
        public ClothFactoryProxy(ClothFactory clothFactory) {
            this.clothFactory = clothFactory;
        }
    
        @Override
        public String producer() {
            System.out.println("代理对象做一些准备");
            clothFactory.producer();
            System.out.println("代理对象做一些收尾工作");
    
            return null;
        }
    }
    

    NikeFactory 类:

    package com.hkl.proxy;
    
    public class NikeFactory implements ClothFactory{
    
        public NikeFactory() {
        }
    
        @Override
        public String producer() {
            System.out.println("Nike 正在生产衣服");
            return null;
        }
    }
    

    测试方法:

    @Test
    public void staticProxyTest(){
        // 创建被代理对象
        NikeFactory nikeFactory = new NikeFactory();
    
        // 创建代理对象
        ClothFactoryProxy clothFactoryProxy = new ClothFactoryProxy(nikeFactory);
    
        // 通过代理对象调用被代理对象的方法
        clothFactoryProxy.producer();
    }
    

    2.5.2 动态代理模式

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

    Humen :接口

    package com.llm.proxy;
    public interface Human {
        String belief();
        void eat(String food);
    }
    

    SuperMan:类

    package com.hkl.proxy;
    
    public class SuperMan implements Human {
        @Override
        public String belief() {
            return "我相信,我能行!";
        }
    
        @Override
        public void eat(String food) {
            System.out.println("正在吃"+food);
        }
    }
    

    动态代理类:

    package com.hkl.proxy;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    
    public class DynamicProxy {
        // 获取代理对象
        public static Object getInstance(Object obj){
            MyInvocation h = new MyInvocation();
            h.bind(obj);
            // 动态的创建对象
            return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), h);
        }
    }
    
    class MyInvocation implements InvocationHandler{
        private Object obj;
    
        /**
         * 绑定被代理对象
         * @param obj
         */
        public void bind(Object obj){
            this.obj = obj;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            // 代理对象调用被代理对象的方法
            Object ret = method.invoke(obj, args);
            return ret;
        }
    }
    

    测试方法:

    @Test
    public void dynamicProxyTest(){
        SuperMan sm = new SuperMan();
        Human h = (Human)DynamicProxy.getInstance(sm);
        System.out.println(h.belief());;
        h.eat("麻辣烫");
    
        NikeFactory nikeFactory = new NikeFactory();
        ClothFactory clothFactory = (ClothFactory) DynamicProxy.getInstance(nikeFactory);
        clothFactory.producer();
    }
    

    相关文章

      网友评论

        本文标题:Java 注解和反射

        本文链接:https://www.haomeiwen.com/subject/zyiilctx.html