Java反射深入剖析【韩顺平老师版】

一、快速入门

  • 应用场景

    通过配置文件,创建类,并调用方法

    • 配置文件 ref.properties
    className = reflection.Cat
    method = sleep
    
    Properties properties = new Properties();
    ClassPathResource classPathResource = new ClassPathResource("ref.properties");
    InputStream inputStream = classPathResource.getInputStream();
    properties.load(inputStream);
    
    String className = properties.getProperty("className");
    String method = properties.getProperty("method");
    
    Class<?> aClass = Class.forName(className);
    
    Cat cat = (Cat)aClass.newInstance();
    
    Method method1 = aClass.getMethod(method);
    
    method1.invoke(cat);
    
    • 步骤

      • 读取配置文件

      • 加载类

        Class<?> aClass = Class.forName(className);

      • 得到类对象

        Cat cat = (Cat)aClass.newInstance();

      • 得到类方法

        在反射中,方法也可视为对象

        Method method1 = aClass.getMethod(method);

      • 执行类方法

        方法对象.invoke(类对象)

        method1.invoke(cat);

二、反射机制

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

反射相关的主要类:

在反射中,类的成员变量,方法,构造器 都视为 对象

  • java.lang.Class

    表示一个类

    Class 对象表示某个类加载后在堆中的对象

    Class<?> aClass = Class.forName("com.ajun.Cat");//加载类(全限定名)
    Cat cat = (Cat)aClass.newInstance();//得到类对象
    
  • java.lang.reflect.Method

    表示类的方法

    Method method = aClass.getMethod("sleep");//获取方法对象 (方法名:sleep)
    method.invoke(cat); //执行方法    【方法对象.invoke(类对象)】
    
  • java.lang.reflect.Field

    表示类的成员变量

    //只可以得到public类型的变量
    Field name = aClass.getField("name");//获取成员变量的对象 (成员变量:name)
    name.get(cat); //得到成员变量的值   【成员变量对象.get(类对象)】
    
  • java.lang.reflect.Constructor

    表示类的构造器

    Constructor constructor = aClass.getConstructor();//获取无参构造器对象
    Constructor constructor1 = aClass.getConstructor(String.class);//获取有参构造器对象。需要传入的参数为:构造器参数类型calss,如String类型就是 String.class
    

三、反射优化

  • 反射优点

    可以动态地创建和使用对象(也是框架底层核心),使用灵活,没有反射机制,框架就失去了底层支撑

  • 反射缺点

    反射基本上是解释执行,对速度有影响

  • 优化

    关闭安全检查开关(优化效果有限)

    Method、Field、Constructor 都实现了 AccessibleObject 接口,有 setAccessible(boolean flag) 方法

    setAccessible(true):关闭安全检查;默认是打开的。可以对私有属性、方法等实施爆破(突破私有限制),配合getDeclaredxxx()使用

四、class类分析

  • Class 也是类,继承自 Object。是一种特殊的类,Class 类的实例对象对应着 其它类

    Class<?> aClass = Class.forName(“com.ajun.Cat”);

    aClass 就是 Class 类的一个实例对象;对应着 Cat 类。

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

    aClass 是通过 Class.forName(“”) 得到的

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

在这里插入图片描述

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

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

    Method、Field、Constructor等

  • Class 对象是存放在里的

  • 类的字节码二进制数据,是放在方法区的 (元空间)

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

五、Class 类常用方法

在这里插入图片描述

  • forName()

    返回指定类名 className 的 Class 对象

    static Class<?> forName(String className)
    
    String classAllPath = "com.ajun.Cat";
    Class<?> cls = Class.forName(classAllPath);
    
    System.out.println(cls);//class com.ajun.Cat  cls对象所属类型
    System.out.println(cls.getClass());//class java.lang.Class  cls的运行类型
    
  • getClass()

    获取运行时对象的类

    继承自Object对象的方法

  • getPackage()

    返回对象所在的包。类型为 Package

    Package getPackage()
    
  • getName()

    返回名称 (字符串)

    String getName()
    
    System.out.println(cls.getPackage());//package com.ajun  包(package)
    System.out.println(cls.getPackage().getName());//com.ajun 包名(字符串)
    System.out.println(cls.getName());//com.ajun.Cat 全限定类名(字符串)
    
  • newInstance()

    创建对象实例

    Cat cat = (Cat)cls.newInstance();
    System.out.println(cat);//cat.toString()
    
  • getField()

    获取类字段属性 (public型)

    Field getField(String name)
    
    Field name = cls.getField("name");//字段属性 (Field表示字段类)
    System.out.println(name);//public java.lang.String com.ajun.Cat.name
    System.out.println(name.get(cat));//得到字段值 【字段属性对象.get(类实例)】
    
    • 给属性赋值
    name.set(cat,"喵喵");//【字段属性对象.set(类实例,新属性值)】
    System.out.println(name.get(cat));//喵喵
    
  • getFields()

    获取类的所有字段属性 (public型)

    Field[] getFields()
    
    Field[] fields = cls.getFields();
    for (Field field : fields) {
        System.out.println(field.getName());
    }
    
  • getMethod()

    获取类方法

    Method getMethod(String name, Class<?>... parameterTypes)
    
    Method sleep = cls.getMethod("sleep");//获取方法 (Method表示方法的类)
    System.out.println(sleep);//public void com.ajun.Cat.sleep()
    sleep.invoke(cat);//执行类方法 【方法类对象.invoke(类实例)】 
    

六、获取class的六种方式

1、Class.forName()

String classAllPath = "com.ajun.Cat";
Class<?> cls = Class.forName(classAllPath);

多用于配置文件,读取类全路径,加载类

可以在编译阶段使用

2、类.class

Class<Cat> catClass = Cat.class;

多用于参数传递

可以在加载阶段使用

3、对象.getClass()

Cat cat1 = new Cat("abc");
Class aClass = cat1.getClass();

有实例对象

可以在运行阶段使用

4、类加载器对象.loadClass()

String classAllPath1 = "com.ajun.Cat";
ClassLoader classLoader = cat.getClass().getClassLoader();

Class<?> aClass1 = classLoader.loadClass(classAllPath1);

5、基本数据类型.class

Class<Integer> integerClass = int.class;
Class<Boolean> booleanClass = boolean.class;
Class<Character> characterClass = char.class;

6、基本数据类型包装类.TYPE

Class<Integer> type = Integer.TYPE;
Class<Boolean> type1 = Boolean.TYPE;
Class<Character> type2 = Character.TYPE;

七、哪些类有class对象

在这里插入图片描述

八、类加载

1、分类

  • 静态加载

    编译时加载相关的类

    如果类不存在或找不到,则报错。依赖性强。

  • 动态加载

    运行时加载需要的类

    如果运行时不用该类,即使类不存在,也不报错。降低了依赖性。

2、加载时机

加载时机加载类型
创建对象时(new)静态加载
当子类被加载时,父类也加载静态加载
调用类中的静态成员时加载静态加载
反射加载动态加载

3、类加载步骤

3.1、概述

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

加载阶段链接阶段 都是由JVM完成的,程序员无法干预,而初始化阶段程序员可以干预(程序员可以定义静态成员)

3.2、加载阶段

将类的class文件读入内存,并为之创建一个java.lang.Class对象。此过程由类加载器完成。

在这里插入图片描述

3.3、连接阶段

将类的二进制数据合并到JRE中

1、验证

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

2、准备

在这里插入图片描述

//属性-成员变量-字段 三个叫法一样
/**
* n1 是实例属性,不是静态变量,和类没有关系,因此在准备阶段,是不会分配内存
* n2 是静态变量,分配内存,赋默认值 0,而不是20
* n3 是常量在编译期间,则会直接被编译器优化,然后给分配了内存并赋了初值30,在准备阶段赋与给定值
*/
public int n1 = 10; //实例变量
public static int n2 = 20; //静态变量
public static final int n3 = 30; //常量

在这里插入图片描述

3、解析

在这里插入图片描述

  • 符号引用:即一个字符串,这个字符串给出了能够唯一性识别一个方法,一个变量,一个类的相关信息
  • 直接引用:可以理解为一个内存地址,或者一个偏移量。比如类方法,类变量的直接引用是指向方法区的指针。在解析阶段,虚拟机会把所有的类名,方法名,字段名这些符号引用替换为具体的内存地址或偏移量,也就是直接引用

如:调用方法hello(),这个方法的地址是1234567,那么hello就是符号引用,1234567就是直接引用。

3.4、初始化阶段

JVM负责对类进行初始化。这里主要是指静态成员

clinit:class init

在这里插入图片描述

加载类的安全机制

在这里插入图片描述

这是设计模式中,用静态内部类实现单例模式的原理

九、获取类的结构信息

1、Class 类

Class类方法返回说明
getName()String获取全类名
getSimpleName()String获取简单类名
getField()Field获取某个public属性(含父类)
getFields()Field[]获取所有public属性(含父类)
getDeclaredField()Field获取本类的某个属性 (四种类型都可以)
(public、protected、private、默认
getDeclaredFields()Field[]获取所有本类属性(四种类型)
getMethod()Method获取某个public方法(含父类)
getMethods()Methods[]获取所有public方法(含父类)
getDeclaredMethod()Method获取本类的某个方法 (四种类型都可以)
getDeclaredMethods()Methods[]获取所有本类方法(四种类型)
getConstructor()Constructor获取本类的某个public构造器
getConstructors()Constructor[]获取本类的所有public构造器
getDeclaredConstructors()Constructor[]获取本类的所有构造器(四种类型)
getPackage()Package获取包信息
getSuperClass()Class获取父类信息
getInterfaces()Class[]获取接口信息
getAnnotations()Annotation[]获取注解信息

2、Field 类

在这里插入图片描述

3、Method 类

在这里插入图片描述

4、Constructor 类

在这里插入图片描述

十、创建对象

在这里插入图片描述

创建之前需要先获得类的Class对象

  • 实体类
package com.ajun;

/**
 * @author ajun
 * Date 2021/6/26
 * @version 1.0
 */
@SuppressWarnings({"all"})
public class User {
    public String name = "ajun";
    private int age = 30;

    //无参构造器
    public User() {
    }

    //有参构造器
    public User(String name) {
        this.name = name;
    }

    //private构造器
    private User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
Class<?> aClass = Class.forName("com.ajun.User");//先得到类的Class对象

1、调用 public 无参构造器

Object u1 = aClass.newInstance();

2、调用 public 有参构造器

//先获得构造器对象
Constructor<?> constructor = aClass.getConstructor(String.class);
//调用构造器对象的 newInstance() 方法,并赋值
Object u2 = constructor.newInstance("aaa");

3、调用非 public 构造器

无参有参类似于1、2

//先获得构造器对象。通过 getDeclaredConstructor() 可以得到任何类型的构造器,包括私有的(private) 
Constructor<?> constructor1 = aClass.getDeclaredConstructor(String.class, int.class);
//对构造器实施爆破(突破私有限制)
constructor1.setAccessible(true);
//调用构造器对象的 newInstance() 方法,并赋值
Object u3 = constructor1.newInstance("bbb", 50);

十一、操作类中属性

在这里插入图片描述

  • Class 对象
Class<?> aClass = Class.forName("com.ajun.User");//先得到类的Class对象

1、public 属性

//得到 public 属性类
Field name = aClass.getField("name");
//得到 User 类的 name 属性值,参数为 User 对象
String nameValue = name.get(u1);
//设置 name 值。参数一:User对象;参数二:name 值
name.set(u1,"adafafaf");

如果属性是 static 的,在设置时类对象可以是 null

name.set(null,“aafafl”);

2、非 public 属性

//先获得属性对象。通过 getDeclaredField() 可以得到任何类型的属性,包括私有的(private)
Field age = aClass.getDeclaredField("age");
//对属性对象实施爆破(突破私有限制)
age.setAccessible(true);
age.set(u1,18);
int ageValue = age.get(u1);

十二、获取方法及使用

在这里插入图片描述

  • Class 对象
Class<?> aClass = Class.forName("com.ajun.User");//先得到类的Class对象

1、public 方法

//public 静态
public static String m1(String str){
    return "方法1"+str;
}
//获得方法类。参数一:方法名称;参数二:方法参数类型
Method m1 = aClass.getMethod("m1",String.class);
//执行方法。参数一:类对象;参数二:方法实参
m1.invoke(u1,"测试");
//静态方法的类对象可以为 null
m1.invoke(null,"测试");

2、非 public 方法

//私有
private String m2(){
    return "方法2";
}
//先获得方法对象。通过 getDeclaredMethod() 可以得到任何类型的方法,包括私有的(private)
Method m2 = aClass.getDeclaredMethod("m2");
//对方法对象实施爆破(突破私有限制)
m2.setAccessible(true);
//执行方法
m2.invoke(u1);
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

土味儿~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值