【自己复习用】Java反射从零到精通(附代码)

一、通过需求引出反射

在这里插入图片描述

二、反射机制图

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

三、反射的优点和缺点

在这里插入图片描述

1. 反射机制优化

在这里插入图片描述

/**
 * @author 86176
 * 反射专题:可以看到反射用时比传统方法用时长,因为反射基本是解释执行
 * 关闭访问检查机制可以减短一些运行时长
 */
public class Reflection2 {
    public static void main (String[] args) throws Exception {
        m1();
        m2();
        m3();
    }
    static void m1(){
        long start = System.currentTimeMillis();
        Cat cat = new Cat();
        for (int i=0;i<900000000;i++){
            cat.hi();
        }
        long end = System.currentTimeMillis();
        System.out.println("传统方法用时:"+(end-start));
    }
    static void m2() throws Exception {
        long start = System.currentTimeMillis();
        Class aClass = Class.forName("onlineClass.reflection.Cat");
        Method hi = aClass.getMethod("hi");
        Object cat = aClass.newInstance();
        for (int i=0;i<900000000;i++){
            hi.invoke(cat);
        }
        long end = System.currentTimeMillis();
        System.out.println("反射方法用时:"+(end-start));
    }
    static void m3() throws Exception {
        long start = System.currentTimeMillis();
        Class aClass = Class.forName("onlineClass.reflection.Cat");
        Method hi = aClass.getMethod("hi");
        hi.setAccessible(true);
        Object cat = aClass.newInstance();
        for (int i=0;i<900000000;i++){
            hi.invoke(cat);
        }
        long end = System.currentTimeMillis();

        System.out.println("反射方法用时,(关闭检查机制:"+(end-start));
    }
}

四、Class类分析

1. class类的类图

在这里插入图片描述
在这里插入图片描述

2. Class类的常用方法

在这里插入图片描述
在这里插入图片描述
每个类会生成一个class字节码文件,称之为元数据

在这里插入图片描述

五、获取class类对象的六种方式

在这里插入图片描述
六种方式可以根据阶段来理解

  1. 编码阶段获取        :Class.forName()
  2. 运行阶段获取(已有实例对象):对象.getClass()
  3. Class类阶段         :类.class
  4. 类加载器得到Class对象:ClassLoader.loadClass(classPath)
  5. java基本类型得到(例int,double):类.class
  6. 基本数据类型对应包装类:类.TYPE
        String classAllPath = "onlineClass.reflection.Cat";

        //第一种:通过Class.forName静态方法获得(已知类的全路径下使用,框架用得比较多
        Class cls1 = Class.forName(classAllPath);

        //第二种:类.class获得
        Class cls2 = Cat.class;

        //第三种:已经实例化对象的情况下,得到class
        Cat cat = new Cat();
        Class cls3 = cat.getClass();

        //第四种:通过类加载器得到
        Cat cat1 = new Cat();
        ClassLoader classLoader = cat1.getClass().getClassLoader();
        Class cls4 = classLoader.loadClass(classAllPath);

        //第五种:java基本数据类型获得
        Class<Integer> cls5 = int.class;

        //第六种:基本数据类型对应包装类,可以通过类.TYPE获得
        Class<Integer> cls6 = Integer.TYPE;

        System.out.println(cls1.hashCode());
        System.out.println(cls2.hashCode());
        System.out.println(cls3.hashCode());
        System.out.println(cls4.hashCode());
        System.out.println(cls5.hashCode());
        System.out.println(cls6.hashCode());
        //可以发现cla1,cls2,cls3,cls4的hashCode都是一样的,
        // 原因是内存里面,不论怎么处理在堆中一个类只能有一个类对象
        /*
        运行结果:
	        460141958
			460141958
			460141958
			460141958
			1163157884
			1163157884
        */

六、有哪些类型有Class对象

在这里插入图片描述

七、动态和静态加载

在这里插入图片描述

实例代码:
在这里插入图片描述

  • 静态加载:如上图所示,其实程序不一定会用到Dog类(若key为2就用不到),但是在编译阶段直接报错,所以是静态加载
  • 动态加载:反射就是动态加载,如果没有到需要用到该方法的时候就不会报错

八、类加载流程图

在这里插入图片描述
在这里插入图片描述

九、类加载的五个阶段

        /**
         * 类加载的执行顺序:
         * 1.加载:把class字节码文件装载到内存中,并且生成一个class对象
         * 2.连接:
         *     2.1.验证:验证字节码文件是否符合要求,开头有魔数cafebabe,并且不危及虚拟机安全
         *     2.2.准备:JVM会在准备阶段把静态变量分配内存并初始化,如0,null,false等(此时num=0)
         *     2.3.解析:JVM将常量池中的符号应用替换成直接引用的过程
         * 3.初始化:到此阶段真正开始执行类中定义的java代码,此过程是执行<clinit()>方法的过程
         *      clinit方法会合并静态代码块和静态变量,并执行,所以num=300并没有执行
         *
         */

1.加载阶段

在这里插入图片描述

2.连接阶段

2.1.连接阶段-验证

在这里插入图片描述
可以看到Dog.class确实为cafebabe开头的文件

2.2.连接阶段-准备

在这里插入图片描述
在这里插入图片描述

2.3.连接阶段-解析

在这里插入图片描述

3.初始化

在这里插入图片描述
在这里插入图片描述
这里讲到,就是因为第3点,每个类在堆中才只有独一份

/**
 * @author huochai
 * @date 2022/10/8 21:03
 * P13:类加载的顺序
 */
public class ClassLoad03 {
    public static void main(String[] args) {
        System.out.println(B.num);
        /**
         * 类加载,执行顺序:
         * 1.加载:把class字节码文件装载到内存中,并且生成一个class对象
         * 2.连接:
         *     2.1.验证:验证字节码文件是否符合要求,开头有魔数cafebabe,并且不危及虚拟机安全
         *     2.2.准备:JVM会在准备阶段把静态变量分配内存并初始化,如0,null,false等(此时num=0)
         *     2.3.解析:JVM将常量池中的符号应用替换成直接引用的过程
         * 3.初始化:到此阶段真正开始执行类中定义的java代码,此过程是执行<clinit()>方法的过程
         *      clinit方法会合并静态代码块和静态变量,并执行,所以num=300并没有执行
         *
         */
    }
}

class B{
    static {
        System.out.println("B类被加载");
        num = 300;
    }
    static int num = 100;

    public B(){
        System.out.println("B()构造方法被执行");
    }
}

十一、反射获取类的结构信息

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

十二、反射爆破创建实例

在这里插入图片描述
在这里插入图片描述


/**
 * @author huochai
 * @date 2022/10/8 22:40
 * P16:反射爆破创建实例对象
 *  例1:通过无参public构造方法实例化对象
 *  例2:通过有参Public构造方法实例化对象
 *  例3:通过有参非public构造方法实例化对象
 * 本集重点:setAccessible()关闭安全性检查,俗称爆破
 */
public class ReflectionCreateInstance {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        /**
         * 例1:直接用newInstance创建
         */
        Class<?> cls1 = Class.forName("onlineClass.reflection.User");
        Object o = cls1.newInstance();
        System.out.println(o);

        /**
         * 例2:先获取有参构造器再通过构造器的newInstance方法实例化对象
         */
        Class<?> cls2 = Class.forName("onlineClass.reflection.User");
        Constructor<?> constructor = cls2.getConstructor(String.class);
        Object huochai = constructor.newInstance("huochai");
        System.out.println(huochai);

        /**
         * 例3:和例2的步骤类似,先获取类对象的构造方法(这一步要使用getDeclaredConstructor方法,因为获取的是私有构造方法)
         * 然后再通过构造方法的newInstance方法实例化,
         * 不过在这之前要先把安全性检查关闭,这样私有构造方法就也可以实例化对象
         */
        Class<?> cls3 = Class.forName("onlineClass.reflection.User");
        Constructor<?> declaredConstructor = cls3.getDeclaredConstructor(String.class, int.class);
        //设置爆破开启(安全性检查关闭)
        declaredConstructor.setAccessible(true);
        Object huochai1 = declaredConstructor.newInstance("huochai", 99999);
        System.out.println(huochai1);
    }
}

class User{
    String name;
    int balance ;
    public User(){
    }

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

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

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

十三、反射爆破操作属性

在这里插入图片描述

/**
 * @author huochai
 * @date 2022/10/9 18:36
 * P17:访问对象中的属性
 *
 */
public class ReflectionAccessProperty {
    public static void main(String[] args) throws Exception {
        Class<?> aClass = Class.forName("onlineClass.reflection.Person");

        //方法1:public类型的属性直接通过get获得
        Object o = aClass.newInstance();
        Field name = aClass.getField("name");
        name.set(o,"huochai");
        System.out.println(name.get(o));

        //方法2:非public类型的属性通过getDeclaredField获得
        Field age = aClass.getDeclaredField("age");
        System.out.println(age.get(o));

        //方法3:static修饰的属性可写为get(null),因为静态属性和具体实例对象无关,只和类加载有关
        Field age2 = aClass.getDeclaredField("age");
        System.out.println(age2.get(null));

        //方法4:private修饰的属性需要关闭安全检查机制(setAccessible)才可获取
        Field money = aClass.getDeclaredField("money");
        money.setAccessible(true);
        System.out.println(money.get(o));
    }
}

class Person{
    public String name;
    static int age;
    private int money;
}

十四、反射爆破操作方法

在这里插入图片描述


/**
 * @author huochai
 * @date 2022/10/10 15:37
 */
public class ReflectionAccessMethod {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException {
        
        //1.如用private修饰则需要getDeclaredMethod得到全部方法
        //2.如用private修饰则需要关闭安全检查setAccessible(true)
        //3.如是静态方法则可以不写具体实例对象
        Class<?> aClass = Class.forName("onlineClass.reflection.Person");
        Method say = aClass.getDeclaredMethod("say", String.class, int.class, int.class);
        say.setAccessible(true);
        Object o = aClass.newInstance();
        String str = (String) say.invoke(null,"huochai",1,23);
        System.out.println(str);
    }
}

十五、反射课后练习

课后练习1:

在这里插入图片描述

/**
 * @author huochai
 * @date 2022/10/10 16:06
 *  创建PrivateTest的类,利用Class类得到私有的name属性,
 *  修改私有的name属性值,并调用getName()的方法打印name属性值
 */
public class HomeWork1 {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        Class<PrivateTest> privateTestClass = PrivateTest.class;
        PrivateTest privateTest = privateTestClass.newInstance();
        Field name = privateTestClass.getDeclaredField("name");
        name.setAccessible(true);
        name.set(privateTest,"haha");
        Method getName = privateTestClass.getMethod("getName");
        System.out.println(getName.invoke(privateTest));
    }
}

class PrivateTest{
    private String name = "hellokitty";
    public String getName(){
        return name;
    }
}

课后练习2:

在这里插入图片描述


/**
 * @author huochai
 * @date 2022/10/10 16:13
 *   1. 利用Class类的forName方法得到File类的class对象
 *   2. 在控制台打印File类的所有构造器
 *   3. 通过newInstance的方法创建File对象,并创建E:\myNew.txt
 */
public class HomeWork2 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Class<?> fileCls = Class.forName("java.io.File");
        Constructor<?>[] declaredConstructors = fileCls.getDeclaredConstructors();
        for (Constructor<?> declaredConstructor : declaredConstructors) {
            System.out.println(declaredConstructor);
        }
        Constructor<?> constructor = fileCls.getConstructor(String.class);
        Object o = constructor.newInstance("C:\\temp\\myNew.txt");
        Method createNewFile = fileCls.getMethod("createNewFile");
        createNewFile.invoke(o);
        System.out.println("文件创建成功!");
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值