zoukankan      html  css  js  c++  java
  • 反射

    首先要了解什么是“动态类型语言”:类型的检查是在运行时检查的,程序在运行时可以改变程序的结构和类型。常见的语言:javascript、Python、Ruby,而静态性语言是在程序编译的阶段,对程序进行检查。相对的来说有"动态的类型语言",就会有"静态性语言"的存在的,常见的"静态性语言"有 java、c、c++等,这些静态性语言都是在程序编译的时候检查程序。

    静态类型的语言的特点:

    优点:结构非常的规范,便于调试,类型安全。

    缺点:为此需要写更多的类型相关代码,导致不便于阅读、不清晰明了。

    动态类型的语言的特点:

    优点:方便阅读,不需要写非常多的类型相关的代码。

    缺点:自然就是不方便调试,命名不规范时会造成读不懂,不利于理解等。

    静态语言的开发效率可由IDE工具提高,而动态语言对程序员个人要求更高。

    反射可以更好的帮助我们理解java中所说的"一切皆对象"的论述,正因为java中含有反射的技术,所以可以认为java语言是"准动态性的语言",我们可以利用反射机制、字节码操作获得类似动态语言的特性

    从而让编程的时候更加灵活!

    反射机制:

       原先我们想调用一个类中的方法的时候,那么我们最初的做法是,先创建一个对象,然后使用对象.方法名()来调用方法,当然如果方法是静态的方法的话,那么我们可以使用类名.方法名来调用;我们知道,java源代码经过javac变成.class的字节码,然后被类加载器加载,加载完成后在堆内存的方法区产生一个Class的对象(一个类中对应一个对象),这个对象对应类中的完整的结构,这个对象就像一面镜子,我们通过这个镜子可以看见这个类的完整的结构,这就是反射。

    反射是在程序运行的时候,通过获取某个类的Class类的对象,进而可以操作此类的所有的属性和方法。那么问题就转换成如何获得Class的对象。

    获取Class的对象的方式:

    1.类名.class :前提是知道该类的类名。

    2.对象.getClass:前提是知道该类的对象。

    3.Class对象.forName("类的全路径 = 包名 + 类名");一般用于加载配置文件,来获取相应的class的对象(比如说好多框架的原理)

    4.使用类的加载器:ClassLoader.loadClass("类的全类名");

    我们知道类的加载器将字节码的文件加载进来,一个类只对应一个class,那么这个类中的成员也有相对应的class,这也就对应了,万物皆对象的概念,正如 jdkApi所说:

    Class的实例表示正在运行的 Java 应用程序中的类和接口。

    枚举是一种类,enum

    注释(指的是注解,是给计算机看的)是一种接口。annocation

    每个数组([])属于被映射为 Class 对象的一个类,所有具有相同元素类型和维数的数组都共享该 Class 对象。

    基本的 Java 类型(primitive type)(booleanbytecharshortintlongfloatdouble)和关键字 void 也表示为 Class 对象。

    Class 没有公共构造方法。

    Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的。

    那么可以将各个类型 的Class的对象,打印一下,看看都是什么,代码如下

    基本数据类型的Class的对象仍然是他们本身(void的class对象也是他本身):

    public static void main(String[] args) {
            //基本数据类型:
            Class bytes = byte.class;
            System.out.println(bytes);
            
            Class shorts = short.class;
            System.out.println(shorts);
            
            Class ints = int.class;
            System.out.println(ints);
            
            Class longs = long.class;
            System.out.println(longs);
            
            Class floats = float.class;
            System.out.println(floats);
            
            Class doubles = double.class;
            System.out.println(doubles);
            
            Class bolleans = boolean.class;
            System.out.println(bolleans);
            
            Class chars = char.class;
            System.out.println(chars);
        }

    结果:

    数组:只要是数据类型相同,维度相同,那么他们是存在同一个数据的class中的。

    class中对应的情况:

    java.lang.Class:代表一个类

    java.lang.reflect.Method:代表类的方法

    java.lang.reflect.Field:代表类的成员变量

    java.lang.reflect.Constructor:代表类的构造方法

    Class.getPackage()获取包

    getName():全限定名

    动态的构造对象分为两种情况:

    1.含有无参的构造函数:可以直接使用Class的对象来调用newInstance();创建对象。

    /*
         * 动态的获取对象的属性:
         */
        @Test
        public void getObj() throws Exception {
            //1.构造方法非私有的。
            Class clazz = Student.class;
            Object instance = clazz.newInstance();
            System.out.println(instance);//Student [name=null, age=0]
            //2.给属性赋值:
            //获取某个属性在字节码文件 中Field类型的对象
            Field name = clazz.getDeclaredField("name");
            //由于属性被私有化了,那么需要打开访问的权限才可以访问
            name.setAccessible(true);
            //设置属性name的值为"张三"
            name.set(instance, "张三");
            System.out.println(name.get(instance));//获取哪个对象的实例
            //3.设置id的值:与上面的设置属性是同理的。
            Field fieldId = clazz.getDeclaredField("age");
            fieldId.setAccessible(true);
            fieldId.set(instance, 1);
            System.out.println(fieldId.get(instance));
        }

    实体类:

    package com.atguigu.reflect.bean;
    
    public class Student {
    
        private String name;
        private int age;
    
        public Student() {
        }
    
        public Student(String name, int age) {
            super();
            this.name = name;
            this.age = age;
        }
    
        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;
        }
    
        @Override
        public String toString() {
            return "Student [name=" + name + ", age=" + age + "]";
        }
        
        /**
         * 获取学生的信息
         * @return
         */
        public String getInfo() {
            return "名称:" + name + ",年龄:" + age;
        }
        
    
    }

    2.如果没有无参数的构造函数:就不能直接使用字节码的对象来调用newInstance();来直接创建对象;可以创建Constructor的对象,然后再去newInstance()

    /*
         * 动态的获取成员的方法。
         * 在没有无参数的构造函数
         */
        @Test
        public void getObj_3() throws Exception {
            //1.同样也是先获取该类的Class的对象
            Class clazz = Student.class;
            //2.实例化相应的对象(要求对象有默认的无参构造)
            //Object instance = clazz.newInstance();
            //如果没有无参数构造函数的话,那么:需要使用有参构造
            Constructor constructor = clazz.getConstructor(String.class,int.class);
            Object instance = constructor.newInstance("张三丰",12);
            //3.使用Class的对象去获取相应的Method
            //想要确定一个方法:1.方法名;2.参数列表
            Method method = clazz.getDeclaredMethod("getInfo");
            //方法被某个对象调用。
            Object object = method.invoke(instance);
            //因为设计者并不知道,具体的什么方法会被执行,所以设置的是任意的类型,
            //所以想使用具体的那个方法的话,那么只有是将其向下转型处理
            String s = (String) object;
            System.out.println(s);
        }

    实体类:

    package com.atguigu.reflect.bean;
    
    public class Student {
    
        private String name;
        private int age;
    
        /*public Student() {
        }*/
    
        public Student(String name, int age) {
            super();
            this.name = name;
            this.age = age;
        }
    
        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;
        }
    
        @Override
        public String toString() {
            return "Student [name=" + name + ", age=" + age + "]";
        }
        
        /**
         * 获取学生的信息
         * @return
         */
        public String getInfo() {
            return "名称:" + name + ",年龄:" + age;
        }
        
    
    }

    通过反射来获取泛型

    Java采用泛型擦除的机制来引入泛型。Java中的泛型仅仅是给编译器javac使用的,确保数据的安全性和免去强制类型转换的麻烦。但是,一旦编译完成,所有和泛型有关的类型全部擦除。所以原先直接获取不到泛型信息。为了通过反射操作这些类型以迎合实际开发的需要,Java5.0就新增了ParameterizedType,GenericArrayType,TypeVariable和WildcardType几种类型来代表不能被归一到Class中的类型但是又和原始类型齐名的类型

    • GenericArrayType:表示一种元素类型是参数化类型或者类型变量的数组类型,比如:T[],ArrayList<T>[]
    • TypeVariable:是各种类型变量的公共父接口。比如:T,E

    • Type[] getBounds()可以继续获取上限,如果类型变量指定了上限例如T extends Person就返回上限Person,如果未指定就是Object
    • ParameterizedType:表示一种参数化的类型,比如:Comparable<Cat>,ArrayList<?>,ArrayList<? extends/super  T/Cat>

    • Type[]  getActualTypeArguments()返回表示此类型实际类型参数的 Type 对象的数组。

       WildcardType:代表一种通配符类型表达式,(Wildcard通配符)比如?,? extends/super  Person/T

      Type[]  getUpperBounds()上限,如果有的话,没有就默认是Object

      Type[]  getLowerBounds()下限,如果有的话,没有就返回长度为0的数组

    泛型类和泛型接口:

    Type getGenericSuperclass():如果父类有泛型,那么返回ParameterizedType参数化类型,即extends后面的父类写法

    例如:com.reflection.type.DAOImpl<T, P>保留

          com.reflection.type.DAOImpl<User, Integer>

          com.reflection.type.DAOImpl 擦除

     如果此 Class 表示 Object 类、接口、基本类型或 void,则返回 null。如果此对象表示一个数组类,则返回表示 Object 类的 Class 对象。

    2)Type[] getGenericInterfaces():同上

    3)TypeVariable[] clazz.getTypeParameters():返回本Class对象对应类型的泛型的类型变量

      例如:ArrayList类的E,如果有E extends 父类,可以通过TypeVariable的getBounds()方法得到上限。如果没有指定上限,默认上限是Object

    通过反射读取注解配置参数的值:

    package com.atguigu.reflect.annocation;
    
    import java.lang.annotation.Annotation;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    import java.lang.reflect.Method;
    
    import org.junit.Test;
    
    /**
     * 所用的知识: a:自定义注解 b:使用反射读取注解中的值
     * 
     * @author DHB
     *
     */
    public class AnnocationTest {
        public static void main(String[] args) {
    
        }
    
        /**
         * 获取方法上的注解
         * @throws InstantiationException
         * @throws IllegalAccessException
         * @throws SecurityException 
         * @throws NoSuchMethodException 
         */
        @Test
        public void test_2() throws InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException {
            // 获取一个类上的注解:
            Class clazz = UseAnnocation.class;
            Method method = clazz.getMethod("test");
            // 获取相应的注解的对象:
            Annotation annotation = method.getDeclaredAnnotation(MyAnnocation.class);
            //向下转型:有风险
            MyAnnocation my = (MyAnnocation) annotation;
            System.out.println(my.value());
        }
        
        
        /**
         * 获取类上的注解
         * @throws InstantiationException
         * @throws IllegalAccessException
         */
        @Test
        public void test() throws InstantiationException, IllegalAccessException {
            // 获取一个类上的注解:
            Class clazz = UseAnnocation.class;
            // 获取相应的注解的对象:
            Annotation annotation = clazz.getAnnotation(MyAnnocation.class);
            // 获取的对象的时候,一般需要对其进行向下转型。
            MyAnnocation m = (MyAnnocation) annotation;
            System.out.println(m.value());
        }
    
    }
    
    // 自定义一个注解:定义其作用的位置(类上、方法上)
    @Target({ ElementType.TYPE, ElementType.METHOD })
    @Retention(RetentionPolicy.RUNTIME) // 指定runtime的生命周期,反射访问的到
    @interface MyAnnocation {
        // 声明配置参数
        String value() default "atguigu";
    }
    
    // 定义一个类去使用注解:
    @MyAnnocation("生命在于静止....")
    class UseAnnocation {
        @MyAnnocation
        public void test() {
            System.out.println("hello world!!!");
        }
    }

    反射的应用之一:动态的代理。

    动态代理的演示将会在后续的码出。

      

  • 相关阅读:
    FCN 分割网络详解
    ResNet 结构理解
    使用 Estimator 构建卷积神经网络
    Object Detection Review
    MP 及OMP算法解析
    Docker 使用及常用命令
    采用std::thread 替换 openmp
    模型优化,汇总
    图像几何变换
    多线程下C#如何保证线程安全?
  • 原文地址:https://www.cnblogs.com/donghaibin/p/9159383.html
Copyright © 2011-2022 走看看