博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

JavaEE - 15反射

Posted on 2020-12-09 11:45  Kingdomer  阅读(104)  评论(0编辑  收藏  举报

JavaEE - 15反射机制

(1)Java Reflection反射机制概述

  • Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。
  • 加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的结构信息。
  • 我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以形象称为:反射
  • 正常方式: 引入需要的"包类"名称 --> 通过new实例化 --> 取得实例化对象
  • 反射方式: 实例化对象  --> getClass()方法 --> 得到完整的"包类"名称

(1.1)Java反射机制提供的功能

  • 在运行时判断任意一个对象所属的类
  • 在运行时构造任意一个类的对象
  • 在运行时判断任意一个类所具有的成员变量和方法
  • 在运行时获取泛型信息
  • 在运行时调用任意一个对象的成员变量和方法
  • 在运行时处理注解
  • 生成动态代理

(1.2)反射相关的主要API

  • java.lang.Class:              代表一个类
  • java.lang.reflect.Method:     代表类的方法
  • java.lang.reflect.Field:      代表类的成员变量
  • java.lang.reflect.Constructor: 代表类的构造器
  • ......
    @Test
    public void test2() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
        Class clazz = Person.class;
        // 通过反射创建Person类的对象
        Constructor cons = clazz.getConstructor(String.class, int.class);
        Object obj = cons.newInstance("Tom", 12);
        System.out.println(obj.toString());    //Person{name='Tom', age=12, sex=null}
        // 通过反射,调用对象指定的属性、方法
        Person p = (Person) obj;
        Field sex = clazz.getDeclaredField("sex");
        sex.set(p,"man");
        System.out.println(p.toString());     //Person{name='Tom', age=12, sex=man}
        // 调用public方法
        Method m = clazz.getDeclaredMethod("show");  //name: Tom,age: 10,sex: man
        m.invoke(p);
// 调用私有的构造器 Constructor cons1 = clazz.getDeclaredConstructor(String.class); cons1.setAccessible(true); Person p1 = (Person) cons1.newInstance("Jerry"); System.out.println(p1); // Person{name='Jerry', age=0, sex=null} // 调用私有属性 Field age = clazz.getDeclaredField("age"); age.setAccessible(true); // Class ReflectionTest can not access a member of class Person with modifiers "private" age.set(p,10); System.out.println(p.toString()); // Person{name='Tom', age=10, sex=man} // 调用私有方法 Method showNation = clazz.getDeclaredMethod("showNation",String.class); showNation.setAccessible(true); showNation.invoke(p1, "美国"); //美国 }

 

(2)获取Class实例

  • 哪些类型可以有Class对象
    • class: 外部类, 成员(成员内部类、静态内部类),局部内部类, 匿名内部类
    • interface:接口/ []:数组/ enum: 枚举/ annotation:注解/ primitive type:基本数据类型/ void
    @Test
    public void test3(){
        Class c1 = Object.class;
        Class c2 = Comparable.class;
        Class c3 = String[].class;
        Class c4 = int[][].class;
        Class c5 = ElementType.class;
        Class c6 = Override.class;
        Class c7 = int.class;
        Class c8 = void.class;
        Class c9 = Class.class;

        int[] a = new int[10];
        int[] b = new int[100];
        Class c10 = a.getClass();
        Class c11 = b.getClass();
        System.out.println(c10 == c11); // true
    }

获取Class的实例的方式

    @Test
    public void test4() throws ClassNotFoundException {
        // 方式一: 调用运行时类的属性: .class
        Class clazz1 = Person.class;
        System.out.println(clazz1);  // class Person
        // 方式二: 通过运行时类的对象,调用getClass()
        Person p = new Person();
        Class clazz2 = p.getClass();
        System.out.println(clazz2);  // class Person
        // 方式三: 调用Class的静态方法: forName(String classPath)
        Class clazz3 = Class.forName("Person");
        System.out.println(clazz3);  // class Person
        System.out.println(clazz1 == clazz2); // true
        // 方式四: 使用类的加载器: ClassLoader
        ClassLoader classLoader = ReflectionTest.class.getClassLoader();
        Class clazz4 = classLoader.loadClass("Person");
        System.out.println(clazz4);  // class Person
    }

 

 

(3)类的加载与ClassLoader

类的加载过程
程序经过javac.exe命令以后,会生成一个或多个字节码文件(.class结尾)。
接着使用java.exe命令对某个字节码文件进行解释运行。相当于将某个字节码文件加载到内存中。此过程称为类的加载。
加载到内存中的类,称为运行时类。此运行时类,作为Class的一个实例。
Class的实例对应着一个运行时类。
加载到内存中的运行时类,会缓存一定的时间。在此时间之内,可以通过不同的方式来获取此运行时类。
 
类加载器作用
 
类加载器ClassLoader
   @Test
    public void test1(){
        // 对于自定义类,使用系统类加载器进行加载
        ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();
        System.out.println(classLoader); //sun.misc.Launcher$AppClassLoader@18b4aac2
        // 调用系统类加载器的getParent(): 获取扩展类加载器
        ClassLoader classLoader1 = classLoader.getParent();
        System.out.println(classLoader1);  // sun.misc.Launcher$ExtClassLoader@279f2327
        // 调用扩展类加载器的getParent():无法获取引导类加载器
        // 引导类加载器主要负责加载java的核心类库,无法加载自定义类的。
        ClassLoader classLoader2 = classLoader1.getParent();
        System.out.println(classLoader2);  // null
    }

 

(4)创建运行时类的对象

  • 通过反射创建对应的运行时类的对象
  • newInstance(): 调用此方法,创建对应的运行时类的对象。内部调用了运行时类的空参的构造器。
  • 要想此方法正常的创建运行时类的对象,要求:
  • 运行时类必须提供空参的构造器。
  • 空参构造器的访问权限满足。通常设置为public。
  • 在java Bean中要求提供一个public的空参构造器。原因:
  • 便于通过反射,创建运行时类的对象
  • 便于子类继承此运行时类时,默认调用super()时,保证父类有此构造器
 
    @Test
    public void test1() throws IllegalAccessException, InstantiationException {
        Class<Person> clazz = Person.class;
        Person obj = clazz.newInstance();
        System.out.println(obj);    //Person{name='null', age=0, sex=null}
    }

 

    public Object getInstance(String classPath) throws ClassNotFoundException {
        Class clazz = Class.forName(classPath);
        return clazz;
    }

    @Test
    public void test2(){
        int num = new Random().nextInt(3);
        String classPath = "";
        switch (num){
            case 0:
                classPath = "java.util.Date";
                break;
            case 1:
                classPath = "java.sql.Date";
                break;
            case 2:
                classPath = "com.bearpx.exer.Person";
                break;
        }
        try{
            Object obj = getInstance(classPath);
            System.out.println(obj);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

 

 

(5)获取运行时类的完整结构

(5.1)属性

        Class clazz = Person.class;
        // getFields: 获取当前运行时类及其父类中声明为public访问权限的属性
        Field[] fields = clazz.getFields();
        for(Field f : fields){
            System.out.println(f);  // public int Person.id
        }
        // getDeclaredFields: 获取当前运行时类中声明的所有属性。(不包含父类中声明的属性)
        Field[] declaredFields = clazz.getDeclaredFields();
        for(Field f : declaredFields){
            System.out.println(f);  // private java.lang.String Person.name   java.lang.String Person.sex
        }

(5.2)属性的 权限修饰符/数据类型/变量名

        Field[] declaredFields = clazz.getDeclaredFields();
        for(Field f : declaredFields){
            // 权限修饰符
            int modifier = f.getModifiers();
            System.out.print(Modifier.toString(modifier) + "\t");
            // 数据类型
            Class type = f.getType();
            System.out.print(type.getName() + "\t");
            // 变量名
            String fName = f.getName();
            System.out.print(fName);
            System.out.println();
        }

(5.3)方法

        Class clazz = Person.class;
        // getMethods():  获取当前运行时类及其所有父类中声明为public权限的方法
        Method[] methods = clazz.getMethods();
        for(Method m : methods){
            System.out.println(m); //public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
        }
        // getDeclaredMethods(): 获取当前运行时类中声明的所有方法。(不包含父类中声明的方法)
        Method[] declaredMethods = clazz.getDeclaredMethods();
        for(Method m : declaredMethods){
            System.out.println(m);
        }

(5.4)方法的 权限修饰符 / 返回值类型 / 方法名(参数类型1 形参名1...) throws XxxException{}

 for(Method m : declaredMethods){
            //获取方法声明的注解
            Annotation[] annos = m.getAnnotations();
            for(Annotation a : annos){
                System.out.println(a);
            }
            // 权限修饰符
            System.out.print(Modifier.toString(m.getModifiers()) + "\t");
            // 返回值类型
            System.out.print(m.getReturnType().getName() + "\t");
            // 方法名
            System.out.print(m.getName());
            System.out.print("(");
            // 形参列表
            Class[] parameterTypes = m.getParameterTypes();
            if(!(parameterTypes == null && parameterTypes.length == 0)){
                for (int i = 0; i < parameterTypes.length; i++){
                    if(i == parameterTypes.length - 1){
                        System.out.print(parameterTypes[i].getName() + " args_" + i);
                        break;
                    }
                    System.out.print(parameterTypes[i].getName() + " args_" + i + ",");
                }
            }
            System.out.print(")");

            // 抛出的异常
            Class[] exceptionType = m.getExceptionTypes();
            if(exceptionType.length > 0){
                System.out.print(" throws ");
                for(int i = 0 ; i < exceptionType.length; i++){
                    if(i == exceptionType.length - 1){
                        System.out.print(exceptionType[i].getName());
                        break;
                    }
                    System.out.print(exceptionType[i].getName() + ",");
                }
            }
            System.out.println();
        }

(5.5)构造器

        // getConstructors(): 获取当前运行时类中声明为public的构造器
        Constructor[] constructors = clazz.getConstructors();
        for(Constructor c : constructors){
            System.out.println(c);
        }
        // getDeclaredConstructors(): 获取当前运行时类中声明的所有构造器
        Constructor[] declaredConstructors = clazz.getDeclaredConstructors();
        for(Constructor c : declaredConstructors){
            System.out.println(c);
        }
 
 

(6)调用运行时类的指定结构

(6.1)获取运行时类的父类

    @Test
    public void test1(){
        Class clazz = Student.class;
        //获取运行时类的父类
        Class superClass = clazz.getSuperclass();
        System.out.println(superClass);  //class Person
    }

(6.2)获取运行时类的带泛型的父类; 获取泛型类型

    @Test
    public void test2(){
        // public class PersonDao extends Dao<Person> {}
        Class clazz = PersonDao.class;

        // 获取运行时类的带泛型的父类
        Type genericSuperclass = clazz.getGenericSuperclass();
        System.out.println(genericSuperclass);  //  Dao<Person>
        ParameterizedType paramType = (ParameterizedType) genericSuperclass;

        // 获取泛型类型
        Type[] actualTypeArguments = paramType.getActualTypeArguments();
        System.out.println(actualTypeArguments[0].getTypeName());       // Person
        System.out.println(((Class)actualTypeArguments[0]).getName());  // Person
    }

(6.3)获取运行时类的接口;获取运行时类的父类实现的接口

    @Test
    public void test3(){
        Class clazz = Person.class;

        Class[] interfaces = clazz.getInterfaces();
        for(Class c : interfaces){
            System.out.println(c);
        }
        System.out.println("----------------");
        Class[] interfaces1 = clazz.getSuperclass().getInterfaces();
        for (Class c : interfaces){
            System.out.println(c);
        }
    }

(6.4)获取运行时类的包

    @Test
    public void test4(){
        Class clazz = com.bearpx.exer.Person.class;
        Package pack = clazz.getPackage();
        System.out.println(pack); // package com.bearpx.exer
    }

(6.5)获取运行时类的注解

    @Test
    public void test5(){
        Class clazz = Person.class;
        Annotation[] annotations = clazz.getAnnotations();
        for(Annotation a : annotations){
            System.out.println(a);         //@MyAnnotation(value=ho)
        }
    }

(6.6)获取运行时类中指定结构: 属性、方法、构造器

    @Test
    public void test5() throws NoSuchFieldException, IllegalAccessException, InstantiationException {
        Class clazz = Person.class;
        // 创建运行时类的对象
        Person p = (Person) clazz.newInstance();

        // 获取指定的属性: 要求属性声明为public
        Field id = clazz.getField("id");
        // 设置当前属性的值: set()  指明设置哪个对象的属性,  将此属性值设置为多少
        id.set(p, 1001);
        // 获取当前属性的值: get()  指明获取哪个对象的当前属性值
        int uid = (int) id.get(p);
        System.out.println(uid);

        // 获取指定变量名的属性,私有属性
        Field name = clazz.getDeclaredField("name");
        name.setAccessible(true);
        name.set(p,"zhangsan");
        System.out.println(name.get(p)); // zhangsan
    }

方法

    @Test
    public void test6() throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        Class clazz = Person.class;
        Person p = (Person) clazz.newInstance();
        // 获取指定的某个方法: getDeclaredMethod() 指定获取的方法名称  指明获取的方法的形参列表
        Method showNation = clazz.getDeclaredMethod("showNation", String.class);
        // 保证方法可访问
        showNation.setAccessible(true);
        // invoke(): 方法的调用者 给方法形参赋值的实参
        // invoke()的返回值即为对应类中调用方法的返回值
        Object returnValue = showNation.invoke(p,"中国");
        System.out.println(returnValue);  // 我在中国

        // 静态方法
        Method staticNation = clazz.getDeclaredMethod("staticNation");
        staticNation.setAccessible(true);
        // 如果调用的方法没有返回值, 此invoke() 返回null
//        Object object = staticNation.invoke(Person.class);
        Object object = staticNation.invoke(null); // 传null  也可以
        System.out.println(object); // 中国
    }

构造器 

    @Test
    public void test7() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Class clazz = Person.class;
        // 获取指定的构造器: getDeclaredConstructor(): 参数: 指明构造器的参数列表
        Constructor constructor = clazz.getDeclaredConstructor(String.class);
        constructor.setAccessible(true);
        Person p = (Person) constructor.newInstance("Tom");
        System.out.println(p); // Person{name='Tom', age=0, sex=null}
    }