反射详细笔记

本文深入探讨了Java反射机制,包括其概念、优缺点、使用场景和原理。详细介绍了如何通过反射创建和访问对象,以及如何通过setAccessible()方法进行性能优化。此外,还展示了反射在框架设计中的重要性,以及它能完成的任务,如动态类型判断、对象实例化、方法调用等。最后,文章讨论了类加载过程,包括加载、验证、准备、解析和初始化五个阶段。
摘要由CSDN通过智能技术生成

  • 反射是框架设计的灵魂

  • (使用的前提条件:必须先得到代表的字节码的Class,Class类用于表示.class文件(字节码))

一、反射介绍

1、介绍

反射:Reflection

2、什么是反射

  • 反射就是把Java类中的各种成分映射成一个个的Java对象

3、反射的优缺点

优点:可以动态的创建和使用对象(也就是框架底层核心),使用灵活,没有反射机制,框架技术就是去底层支撑

缺点:使用反射基本是解释执行,执行速度有影响。

但是反射机制可以调优

  • Method 和 Field、Constructor对象都有setAccessible()方法

  • setAccessible作用是启动和禁用访问安全检查开关

  • 参数值为true标志 反射的对象在使用时取消访问检查,提高反射的效率。参数值为false则表示反射的对象执行访问检查

性能比较
    /**
 * @author lys
 * @version 1.0
 * 测试反射调用的性能,和优化方案
 */
​
    public class Reflction02 {
        public static void main(String[] args) throws ClassNotFoundException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchMethodException {
            m1();
            m2();
            m3();
        }
        //传统方法来调用hi
        public static void m1(){
            Cat cat = new Cat();
            long start = System.currentTimeMillis();
            for (int i = 0; i < 99999; i++) {
                cat.hi();
            }
            long end = System.currentTimeMillis();
            System.out.println("m1 耗时:" + (end-start));
​
        }
​
​
​
        //利用反射机制调用hi
​
        public static void m2() throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
            Class<?> cls = Class.forName("com.hspedu.Cat");
            Object o = cls.newInstance();
            Method hi = cls.getMethod("hi");
            long start = System.currentTimeMillis();
            for (int i = 0; i < 99999; i++) {
                hi.invoke(o);
            }
            long end = System.currentTimeMillis();
            System.out.println("m2 耗时:" + (end-start));
​
        }
​
        //反射机制 + 关闭访问检查
        public static void m3() throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
            Class<?> cls = Class.forName("com.hspedu.Cat");
            Object o = cls.newInstance();
            Method hi = cls.getMethod("hi");
            hi.setAccessible(true);  //true 表示关闭访问检查
            long start = System.currentTimeMillis();
            for (int i = 0; i < 99999; i++) {
                hi.invoke(o);
            }
            long end = System.currentTimeMillis();
            System.out.println("m3 耗时:" + (end-start));
​
        }
    }


4、Java反射原理图

 

5、Java反射机制可以完成的事情

  • 的·在运行时判断任意一个对象所属的类

  • 在运行时构造任意一个类的对象

  • 在运行时得到任意一个类所具有的成员变量和方法

  • 在运行时代用任意一个对象的成员变量和方法

  • 生产动态代理

二、反射基础知识

1、基础反射

 

Cat类
    public class Cat {
​
        private String name = "招财猫";
        public int age = 10;
​
        public Cat() {
​
        }
​
        public Cat(String name){
            this.name = name;
        }
​
        public void hi(){
            System.out.println("hi" + name);
        }
​
        public void cry(){
            System.out.println(name + "喵喵叫..");
        }
​
    }


public class Reflction01 {
​
    public static void main(String[] args) throws Exception {
​
        Properties properties = new Properties();
        properties.load(new FileInputStream("src\\re.properties"));
        String classfullpath = properties.get("classfullpath").toString();
        String methodname = properties.get("method").toString();
​
        //使用反射
        //1、加载类,返回Class类型的对象cls
        Class cls = Class.forName(classfullpath);
        
        //2、通过 cls 得到你加载的类 com.hspedu.Cat 的对象实例
        Object o = cls.newInstance();
        System.out.println("o的运行类型=" + o.getClass());//运行类型
        
        //3、通过 cls 得到你加载的类 com.hspedu.Cat 的 methodName“hi” 的方法对象
        Method method1 = cls.getMethod(methodname);
        System.out.println(method1.getName());
        
        //4、通过method1 调用方法:即通过方法对象
        System.out.println("=================");
        method1.invoke(o);
​
        //java.lang.reflect.Field: 代表诶的成员变量,Field对象表示某个类的成员变量
        //Field不能得到私有属性(private)
        Field nameField = cls.getField("age");
        System.out.println(nameField.get(o));
​
        //java.lang.reflect.Constructor: 代表类的构造方法,Constructor对象表示构造器
        Constructor constructor = cls.getConstructor();
        System.out.println(constructor);
​
        Constructor constructor1 = cls.getConstructor(String.class);//有参构造器 String类型的
        System.out.println(constructor1);
​
    }
}

2、Class类分析

(1) 基本介绍

  • Class也是类,因此也继承Object类

  • Class类对象不是new出来的,而是系统创建的

  • 对于某个类的Class类对象,在内存中只有一份,因为类只加载一次

  • 每个类的实例都会记得自己是由哪个 Class 实例所生成

  • 通过Class可以完整地得到一个类的完整结构,通过一系列API

  • Class对象是存放在堆的

  • 类的字节码二进制数据,是放在方法区的,有的地方称为类的元数据(包括 方法代码,变量名,方法名,访问权限等等)

3、Class常用方法

public class Car {
​
    public String brand ="宝马";
    public int price = 100;
    public String color = "白色";
​
    @Override
    public String toString() {
        return "Car{" +
            "brand='" + brand + '\'' +
            ", price=" + price +
            ", color='" + color + '\'' +
            '}';
    }
}
​


/**
 * @author lys
 * @version 1.0
 * 演示Class类的常用方法
 */
​
public class Class02 {
​
    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchFieldException {
​
        String classAllPath = "com.hspedu.Car";
        //1、获取到Car类 对应的 Class对象
        //<?> 表示不确定的Java类型
        Class<?> cls = Class.forName(classAllPath);
        //2、输出cls
        System.out.println(cls);            //显示cls对象,是哪个类的Class对象 com.hspedu.Car
        System.out.println(cls.getClass());//输出cls运行类型 java.lang.Class
        //3、得到包名
        System.out.println(cls.getPackage().getName());
        //4、得到全类名
        System.out.println(cls.getName());
        //5、通过cls创建对象实例
        Car car = (Car) cls.newInstance();
        System.out.println(car);
        //6、通过反射获取属性
        Field brand = cls.getField("brand");
        System.out.println(brand.get(car));
        //7、通过反射给属性赋值
        brand.set(car,"奔驰");
        System.out.println(brand.get(car));
        //8、获取所有的字段属性
        System.out.println("=========================");
        Field[] fields = cls.getFields();
        for (Field f : fields){
            System.out.println(f.getName());
        }
    }
​
}
​

4、获取Class对象的六种方式

主要就四种

 

/**
 * @author lys
 * @version 1.0
 * 演示得到Class对象的各种方式(6种)
 */
​
public class GetClass_ {
​
    public static void main(String[] args) throws ClassNotFoundException {
        //1、Class.forName
        String classAllPath = "com.hspedu.Car";
        Class<?> cls1 = Class.forName(classAllPath);
        System.out.println(cls1);
​
        //2、类名.class , 应用场景:用于参数传递
        Class cls2 = Car.class;
        System.out.println(cls2);
​
        //3、对象.getClass ,应用场景:有对象实例
        Car car = new Car();
        Class cls3 = car.getClass();
        System.out.println(cls3);
​
        //4、通过类的加载器【4种】来获取到类的Class对象
        //(1)先得到类加载器 car
        ClassLoader classLoader = car.getClass().getClassLoader();
        //(2)通过类加载器得到Class对象
        Class cls4 = classLoader.loadClass(classAllPath);
        System.out.println(cls4);
​
        //5、基本数据(int、char、boolean、float、double、byte、long、short)按如下方式得到Class类对象
        Class<Integer> integerClass = int.class;
        System.out.println(integerClass);
​
        //6、基本数据类型对应的包装类,可以通过 .TYPE 得到Class类对象
        Class<Integer> type = Integer.TYPE;
        System.out.println(type);
​
        //自动拆箱和自动装箱
        System.out.println(integerClass.hashCode());
        System.out.println(type.hashCode());
​
    }
​
}

5、那些类型有Class对象

  1. 外部类,成员内部类,静态内部类,局部内部类,匿名内部类

  2. interface:接口

  3. 数组

  4. enum:枚举

  5. annotation:注解

  6. 基本数据类型

  7. void

  • 实例:AllTypeClass

/**
 * @author lys
 * @version 1.0
 * 演示那些类型有Class对象
 */
​
public class AllTypeClass {
​
    public static void main(String[] args) {
        Class<String> cls1 = String.class;//外部类
        Class<Serializable> cls2 = Serializable.class;//接口
        Class<Integer[]> cls3 = Integer[].class;//数组
        Class<float[][]> cls4 = float[][].class;//二维数组
        Class<Deprecated> cls5 = Deprecated.class;//注解
        //枚举
        Class<Thread.State> cls6 = Thread.State.class;
        Class<Long> cls7 = long.class;//基本数据类型
        Class<Void> cls8 = void.class;//void数据类型
        Class<Class> cls9 = Class.class;
    }
}

三、类加载

1、基础介绍

  • 基本说明

    反射机制是 java实现动态语言的关键,也就是通过反射实现类动态加载

  1. 静态加载:编译时加载相关的类,如果没有则报错,依赖性太强

  2. 动态加载:运行时加载需要的类,如果运行时不用该类,则不报错,降低了依赖性

  3. 举例说明

  • 类加载时机

  1. 当创建对象是(new) //静态加载

  2. 当子类被加载时,父类也被加载 //静态加载

  3. 调用类中的静态成员时 //静态加载

  4. 通过反射 //动态加载

public class ClassLoad_ {
​
    public static void main(String[] args) throws Exception {
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入key");
​
        String key = scanner.next();
        switch(key){
            case "1":
                Doog dog = new Doog(); //静态加载
                dog.cry();
                break;
            case "2":
                Class<?> cls = Class.forName("com.hspedu.classload.Person");//动态加载
                Object o = cls.newInstance();
                Method hi = cls.getMethod("cry");
                hi.invoke(o);
                break;
            default:
                System.out.println("do nothing ....");
        }
    }
}
class Doog{
    public void cry(){
        System.out.println("小狗汪汪叫");
    }
}
class Person{
    public void cry(){
        System.out.println("小狗汪汪叫");
    }
}

2、类加载过程图

 

 

3、类加载五个阶段


1、第一个阶段

加载阶段

JVM 在该阶段的主要目的是将字节码从不同的数据源(可能是 class 文件、也可能是 jar 包,甚至网络)转化为二进制字节流加载到内存中,并生成一个代表该类的 java.lang.Class 对象

 

连接阶段-验证

  1. 目的是为了确保 Class 文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全

  2. 包括:文件格式验证(是否以魔数 oxcafebabe开头)、元数据验证、字节码验证和符号应用验证

  3. 可以考虑使用 -Xverify:none 参数来关闭大部分的类验证措施,缩短虚拟机类加载的时间。

连接阶段-验证

  • JVM 会在该阶段对静态变量,分配内存并默认初始化(对应数据类型的默认初始值,如0、0L、null、false等)。这些变量所使用的内存都将在方法区中进行分配

连接阶段-解析

  • 虚拟机将常量池内的符号引用替换为直接引用的过程

 

2、类加载-初始化(Initialization)

  1. 到初始化阶段,才真正开始执行类中定义的Java程序代码,此阶段是执行<clinit>()方法的过程

  2. <clinit>()方法是由编译器按语句在源文件中出现的顺序,依次自动收集类中的所有静态变量的赋值动作和静态代码块中的语句,进行合并。

    clinit<
        System.out.println("B 静态代码块被执行");
    	num = 300;
    	num = 100;
        >
            
    如果
            new B();  //就会导致类加载
    		B.num;   //如果直接使用类的静态属性,也会导致类加载
    		
        

  3. 虚拟机会保证一个类的<clinit>() 方法在多线程环境中被正确地加锁、同步、如果多个线程同时去初始化一个类,那么只会有一个线程去执行这个类的<clinit>()方法,其他线程都需要阻塞等待,直到活动线程执行<clinit>()方法完毕.

 

四、获取类的结构信息

 

 

 

 

五、反射爆破

1、反射爆破获取Class对象

/**
 * @author lys
 * @version 1.0
 * 演示通过反射机制创建实例
 */

public class ReflectCreateInstane {

    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {

        //1. 先获取到User类的Class对象
        Class<?> userClass = Class.forName("com.hspedu.reflection.ReflectCreateInstane");
        //2. 通过public的无参构造器创建实例
        Object o1 = userClass.newInstance();
        System.out.println(o1);
        //3. 通过public的有参构造器创建实例
        Constructor<?> constructor = userClass.getConstructor(String.class);
        Object o2 = constructor.newInstance("hanp");
        System.out.println(o2);
        //4. 通过非public的有参构造器创建实例
        Constructor<?> declaredConstructor = userClass.getDeclaredConstructor(int.class, String.class);
        declaredConstructor.setAccessible(true);//爆破, 解除安全检查开关
        Object o3 = declaredConstructor.newInstance(20, "nasidf");
        System.out.println(o3);
    }
}


class User{
    private int age = 10;
    private String name = "nihao";

    public User(){

    }

    public User(String name){
        this.name = name;
    }

    private User(int age,String name){
        this.age = age;
        this.name = name;

    }

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

2、反射爆破操作属性

/**
 * @author lys
 * @version 1.0
 * 演示反射操作属性
 */

public class ReflecAccessProperty {

    public static void main(String[] args) throws Exception {
        Class<?> cls = Class.forName("com.hspedu.reflection.Student");
        Object o = cls.newInstance();
        Field age = cls.getField("age");
        age.set(o,11);
        System.out.println(age.get(o));

        //爆破
        Field name = cls.getDeclaredField("name");
        name.setAccessible(true);   //爆破,可以操作private属性
        name.set(o,"nihao");   //因为name是static属性的,因此o可以写成null
        System.out.println(o);
        System.out.println(name.get(o));
        System.out.println(name.get(null));

    }


}

class Student{
    public int age;
    private static String name;
    public Student(){}

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

3、方法原理(反射爆破)//爆破可以访问private属性 但是需要关闭(true)安全检查

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值