反射

类的加载

/*
      类的加载 -- 参考 类的加载过程
      		当程序要使用某个类时,如果该类还未被加载到内存中,
            则系统会通过类的加载,类的连接,类的初始化三步来实现对这个类进行初始化。
      类的加载 
      		就是指将class文件(字节码文件)读入内存,并为之创建一个Class对象。
      		任何类被使用时系统都会建立一个java.lang.Class对象。
            读取class文件时,加载了(执行了)static
      类的连接
      		验证:是否有正确的内部结构,并和其他类协调一致
      		准备:负责为类的静态成员分配内存,并设置默认初始化值
      		解析:将类的二进制数据中的符号引用(方法区中被加载的方法)替换为直接引用(指针、方法标记)
      类的初始化 
      		就是我们以前讲过的初始化步骤
*/
/*
      类加载的注意事项和使用

      注意事项:初始化
      	1.当类还未被加载和连接时,程序先加载并连接该类
      	2.该类的直接父类未被初始化,先初始化直接父类
      	3.假如类中有初始化语句(非static修饰)依次执行初始化语句
      	执行步骤2时,同样遵循1-3

      使用
      	创建类的实例
      	操作类或相关的类变量
     	调用类方法
     	初始化子类
      	直接使用java.exe运行某个主类
      	用反射方式强制创建类或接口对应的Class对象
*/

理解

/*
    在Java代码中,类型的加载、连接与初始化过程都是在程序运行期间完成的

    类型:可以理解为一个class
    加载:查找并加载类的二进制数据,最常见的情况是将已经编译完成的类的class文件从磁盘加载到内存中
    连接:确定类型与类型之间的关系,对于字节码的相关处理
    验证:确保被加载的类的正确性
    准备:为类的静态变量分配内存,并将其初始化为默认值。但是在到达初始化之前,类变量都没有初始化为真正的初始值
    解析:在类型的常量池中寻找类、接口、字段和方法的符号引用,把这些符号引用转换为直接引用的过程
    初始化:为类的静态变量赋予正确的初始值
    使用:比如创建对象,调用类的方法等
    卸载:类从内存中销毁
    理解:public static int number = 666;

    上面这段代码,在类加载的连接阶段,为对象number分配内存,并初始化为0;然后再初始化阶段在赋予正确的初始值:666
*/

类加载器

/*
    Java加载类时,都是通过类加载器来进行加载
    	作用:
    		负责将class文件加载到内存中,并生成対应的Class对象
    	机制:
    		全盘负责:
            	当类加载器负责加载某一个class文件时,与之相关的
    			class文件也由该类加载器负责,除非显示由另一个加载器负责
    		父类委托:
            	加载某一个class文件时,先让父类的加载器试图加载该
    			class文件,只有在父类无法加载该class文件时,才尝试从自己的类
    			路径加载
    		缓存机制:
            	会把所有加载的class文件生成的Class对象进行缓存
    			当程序需使用某一个Class对象时,类加载器首先去缓存中搜索,
    			缓存中不存在时,才会将class文件加载到内存中,并生成对应的
    			Class对象
*/

反射

/*
    反射就是在运行状态(测试类-main方法所在类)中,对于任意一个类,都能够知道这个类的所
    有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;并且能改变它的属性。
    而这也是Java被视为动态(或准动态,为啥要说是准动态,因为一般而言的动态语言定义是程序运行时,
    允许改变程序结构或变量类型,这种语言称为动态语言)语言的一个关键性质。
    
    要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.
    所以先要获取到每一个字节码文件对应的Class类型的对象.
*/
/*
Java反射机制原理探究
Class类:Java程序在编译完成后,会把所有class文件中所包含的类的基本元信息装载到JVM内存中,
以Class类的形式保存,每一个Class类对象代表一个具体类的基本元信息。我们的反射就是在Class类的基础上进行的,
Class类对象存储着类的所有相关信息,就像镜子,故称“反射”。
Field:即类或对象的域,就是属性值
Method:类或对象的方法
Constructor:类或对象的构造器,使用它可以构造出相应的类对象
反射基本原理
整体流程
调用反射的总体流程如下:
准备阶段:编译期装载所有的类,将每个类的元信息保存至Class类对象中,每一个类对应一个Class对象
获取Class对象:调用x.class/x.getClass()/Class.forName() 获取x的Class对象clz(这些方法的底层都是native方法,
是在JVM底层编写好的,涉及到了JVM底层,就先不进行探究了)
进行实际反射操作:通过clz对象获取Field/Method/Constructor对象进行进一步操作
整体过程中,需要注意的是进行实际反射操作的这个阶段,我们需要关注的点有:
我们是如何通过Class获取到Field/Method/Construcor的?
获取到的Field是如何具有对象属性值的?
获取到的Method是如何调用的?
*/

反射能做什么?

/*
    我们知道反射机制允许程序在运行时取得任何一个已知名称的class的内部信息,
    包括其modifiers(修饰符),fields(属性),methods(方法)等,并可于运行时改变fields内容或调用methods。
    那么我们便可以更灵活的编写代码,代码可以在运行时装配,无需在组件之间进行源代码链接,降低代码的耦合度;
    还有动态代理的实现等等;但是需要注意的是反射使用不当会造成很高的资源消耗!!
*/

通过反射获取源文件对应的Class对象

public class Student {
    private String name;
    private int age;
}

/**
 * 在运行状态(main所在的类),可以获取任意一个类,当中的属性和方法
 * 获取任意一个类,类的加载(加载的是class文件)加载成了一个与之对应的Class对象
 * 可以通过当前Class对象获取class文件(源文件)的代码
 * 反射可以通过代码创建对象
 */

public class Test {
    public static void main(String[] args) throws Exception {

        //反射获取源文件对应的Class对象(常用:方式1、3)
      
        //1.方式一 类名.class属性
        //类的加载 默认会生成一个与之对应的Class对象
        //Class<Student> studentClass = Student.class;
      
        // 2.方式2 对象的实例.getClass();
        //Object类中:
        //类<?> getClass() 返回此 Object的运行时类。 
        //Student student = new Student();
        //Class<? extends Student> aClass = student.getClass();
      
        //方式3 通过Class类中的forName()方法
        //static 类<?> forName(String className) 
        //返回与给定字符串名称的类或接口相关联的类对象。 
        //className:类的路径从包名开始
        Class<?> aClass = Class.forName("com.shujia.recl01.Student");
        System.out.println(aClass);
      
        // 通过三种方式获取类的Class对象,可以通过Class对象看到类中的代码(对象没有创建)
        //Class类中:
        //public T newInstance() 创建由此类对象表示的类的新实例。
        Student o = (Student) aClass.newInstance();
        //通过反射创建类的对象的实例
        System.out.println(o);
    }
}

反射与new创建实例的区别

通过反射获取成员变量

public class F {
    public String demo;
    private String test;
}

public class Student extends F{
    public String id;
    private String name;
    private int age;
    public String sex;
}

/*

通过反射获取成员变量

    Field[] getFields();获取所有公共成员变量的数组
    Field[] getDeclaredFields();获取所有成员变量的数组
    Field getField(String name);返回单个公共成员变量的对象
    Field getDeclaredField(String name);返回单个成员变量的对象
    
*/

public class Test {
    public static void main(String[] args) throws Exception{
         //获取源文件对应的Class对象
          Class<?> aClass = Class.forName("com.shujia.recl02.Student");
//        Field[] getFields();获取所有公共成员变量的数组(可以获取父类中的公共属性)
      
//        Field[] fields = aClass.getFields();
//        for (Field field : fields) {
//            System.out.println(field);
//        }

      
//        Field[] getDeclaredFields();
//        获取所有成员变量的数组(不能获取父类中的属性)
      
//        Field[] declaredFields = aClass.getDeclaredFields();
//        for (Field declaredField : declaredFields) {
//            System.out.println(declaredField);
//        }
      
      
//        Field getField(String name);
//        返回单个公共成员变量的对象(可以获取父类中的公共属性)
      
//        Field id = aClass.getField("id");
//        System.out.println(id);
//        Field name = aClass.getField("name");
//        System.out.println(name); //获取不到私有的
          Field demo = aClass.getField("demo");
          System.out.println(demo);//可以获取到父类中的公共成员变量
      
      
//        Field getDeclaredField(String name);
//        返回单个成员变量的对象(不能获取父类中的属性)
          Field name = aClass.getDeclaredField("name");
          System.out.println(name);//可以获取到自己的私有成员变量
          Field demo = aClass.getDeclaredField("demo");
          System.out.println(demo);//不可以获取到父类的公共成员变量
    }
}

通过反射修改成员变量的值

public class Student {
    private String name;
    public String age;

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age='" + age + '\'' +
                '}';
    }
}

/*
	Field类中:
        通过反射赋值
        void set(Object obj,Object value):给obj对象的成员赋值value
        
    Accessable属性是继承自AccessibleObject类.功能是启用或禁用安全检查
        绕过类型安全检测机制
        setAccessible(true);
*/

import java.lang.reflect.Field;

public class Test {
    public static void main(String[] args) throws Exception{
        //获取源文件对应的Class对象
        Class<?> aClass = Class.forName("com.shujia.field.Student");
        //通过反射创建实例
        Student student = (Student) aClass.newInstance();
        //获取成员
        Field name = aClass.getDeclaredField("name");
        System.out.println(name);
        //通过反射赋值
        //void set(Object obj,Object value):给obj对象的成员赋值value
        //name.set(student,"test");//此时会报错--私有的成员变量不能赋值
        //System.out.println(student);
      
        //私有的成员不能赋值,那么公共的呢?
        //获取成员
        Field age = aClass.getDeclaredField("age");
        System.out.println(age);
        age.set(student,"test");//此时却可以
        System.out.println(student);
      
        //我既然能获取私有的成员变量,为啥不能赋值呢?
        //想要赋值,就要在赋值之前,绕过类型安全检测机制
        name.setAccessible(true);//绕过类型安全检测机制
        name.set(student,"test");//之后就可以赋值了
        System.out.println(student);
    }
}

反射获取成员方法

public class User {
    public void test1(){
        System.out.println("test1");
    }
    public void test2(String name){
        System.out.println("test2---1");
    }
    public void test2(int name){
        System.out.println("test2---2");
    }
    private void test3(){
        System.out.println("test3");
    }
    private void test4(Object name){
        System.out.println("test4");
    }
}

import java.lang.reflect.Method;
import java.util.ArrayList;

/*
	获取所有方法
		方法[] getMethods() 
        返回包含一个数组 方法对象反射由此表示的类或接口的所有公共方法 类对象,
        包括那些由类或接口和那些从超类和超接口继承的声明。  
        
		方法[] getDeclaredMethods() 
		返回包含一个数组 方法对象反射的类或接口的所有声明的方法,通过此表示 类对象,
        包括公共,保护,默认(包)访问和私有方法,但不包括继承的方法。 
	获取单个方法
		方法 getMethod(String name, 类<?>... parameterTypes) 
		返回一个 方法对象,它反映此表示的类或接口的指定公共成员方法 类对象 
        
		方法 getDeclaredMethod(String name, 类<?>... parameterTypes) 
		返回一个 方法对象,它反映此表示的类或接口的指定声明的方法 类对象。 
*/

public class Test {
    public static void main(String[] args) throws Exception{
        Class<?> aClass = Class.forName("com.shujia.method.User");
        User user = (User)aClass.newInstance();
        //获取所有的公共方法包括继承自父类的
//        Method[] methods = aClass.getMethods();
//        for (Method method : methods) {
//            System.out.println(method);
//        }
        //获取当前类所有方法
//        Method[] methods = aClass.getDeclaredMethods();
//        for (Method method : methods) {
//            System.out.println(method);
//        }
      
//		Method[] getDeclaredMethod(String name,Class<?>... parameterTypes);
//     	返回单个成员方法的对象,(方法的名字,参数类型的Class对象)
        Method test1 = aClass.getDeclaredMethod("test2",String.class);
        System.out.println(test1);
      
        Method test2 = aClass.getDeclaredMethod("test2",int.class);
        System.out.println(test2);
      
        //需求:对test4进行传值
        Method test3 = aClass.getDeclaredMethod("test4",Object.class);
        System.out.println(test3);
        test3.setAccessible(true);//绕过类型安全检测机制
        //Method类中
        //Object invoke(Object obj, Object... args) 
        //在具有指定参数的 方法对象上调用此 方法对象表示的底层方法。 
        test3.invoke(user,"10");
    }
}

通过反射获取构造方法

public class User {
    public User(){
        System.out.println("无参");
    }
    public User(String name,String age){
        System.out.println("有参");
    }
    private User(String name){
        System.out.println("私有");
    }

    @Override
    public String toString() {
        return super.toString();
    }
}

import java.lang.reflect.Constructor;

/*
获取构造方法
		Constructor<T> getConstructor(类<?>... parameterTypes) 
			返回一个 Constructor对象,该对象反映 Constructor对象表示的类的指定的公共 类函数。
            
		Constructor<?>[] getConstructors() 
			返回包含一个数组 Constructor对象反射由此表示的类的所有公共构造 类对象。 
            
        Constructor<T> getDeclaredConstructor(类<?>... parameterTypes) 
			返回一个 Constructor对象,该对象反映 Constructor对象表示的类或接口的指定 类函数 

        Constructor<?>[] getDeclaredConstructors() 
			返回一个反映 Constructor对象表示的类声明的所有 Constructor对象的数组 类 。 
            
创建对象
		T newInstance() 
			创建由此 类对象表示的类的新实例。 
		con.newInstance(“zhangsan", 20);
*/

public class Test {
    public static void main(String[] args) throws Exception{
        Class<?> aClass = Class.forName("com.shujia.cur.User");
        //之前创建对象
//        Object o = aClass.newInstance();// 默认执行无参构造
      
//        //获取当前类所有构造方法      
//        Constructor<?>[] constructors = aClass.getDeclaredConstructors();
//        for (Constructor<?> constructor : constructors) {
//            System.out.println(constructor);
//        }
        //获取当前类构造方法 --想要有参的构造方法就在()里加上参数类型的Class对象
        //不加就是无参的
        Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(String.class);
        System.out.println(declaredConstructor);
        declaredConstructor.setAccessible(true);//绕过安全检测机制
        Object test = declaredConstructor.newInstance("test");//这里执行有参构造

//        Constructor<?> declaredConstructor = aClass.getDeclaredConstructor();
//        System.out.println(declaredConstructor);
//        Object test = declaredConstructor.newInstance();//这里执行无参构造
          //这种方式与上面的 Object o = aClass.newInstance() 没有区别
    }
}

通过反射绕过泛型的安全检测机制案例

import java.lang.reflect.Method;
import java.util.ArrayList;

public class Test {
    public static void main(String[] args) throws Exception{
        ArrayList<Integer> strings = new ArrayList<>();//这里的add(String e)方法已经被泛型限定了
        strings.add(123);
        strings.add(456);
        // 通过反射获取strings对象的Class对象
        // 获取ArrayList中所有代码其中add(Object e)方法没有被泛型限定
        Class<? extends ArrayList> aClass = strings.getClass();
        //通过反射获取add方法
        Method add = aClass.getDeclaredMethod("add", Object.class);
        //添加元素
        add.invoke(strings,"789abc");
        System.out.println(strings);
    }
}
posted @ 2022-01-02 22:33  赤兔胭脂小吕布  阅读(20)  评论(0编辑  收藏  举报