Java反射

Sl0th Lv4

JAVA反射

类加载器

类加载/类初始化

三个步骤

  • 类的加载: 将class文件读入内存,并为之创建一个java.lang.Class对象

    任何类被使用时,系统都会为之建立一个java.lang.Class对象

    系统中所有的类都是java.lang.Class的实例。

  • 类的连接

    • 验证阶段:检验被加载类是否有正确的内部结构,并和其他类协调一致
    • 准备阶段:负责为类的类变量分配内存,并设置默认初始化值
    • 解析阶段:将类的二进制数据中的符号引用替换为直接引用
  • 类的初始化:主要是对类变量初始化

    • 假如该类还没被加载和连接,则先加载并连接该类
    • 假如该类的直接父类还没被初始化,先初始化其直接父类(会向上递归,java中最先初始化的是Object类)
    • 假如类中有初始化语句,则系统依次执行这些初始化语句

类的初始化时机:

  • 创建类的实例
  • 调用类的类方法
  • 访问类或接口的类变量,或为该类变量赋值
  • 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
  • 初始化某个类的子类
  • 直接使用java.exe命令来运行某个主类

类加载器

作用

  • 将.class文件加载到内存中,并为之生成对应的java.lang.Class对象

java虚拟机的类加载机制

image-20220715194607363
image-20220715194607363

内置类加载器

Bootstrap class loader–>Platform class loader–>System class loader

父到子的顺序

image-20220715195009312
image-20220715195009312

反射

概述

指在运行时获取一个类的变量和方法信息,然后通过获取到的信息来创建对象,调用方法的一种机制。由于这种动态性,程序不用在编译器就完成确定,在运行起仍可以拓展

获取Class对象

  • 使用类的class属性,e.g. Student.class
  • 调用类的getClass()方法(这是Object类的方法,所有对象都有该方法)
  • Class类中的静态方法,forName(String className),该方法要传入字符串参数,该字符串是某个类的全路径,也就是完整包名的路径
1
2
3
4
5
6
7
8
9
Class<Student> c1=Student.class;
System.out.println(c1);
Class<Student> c2=Student.class;
System.out.println(c1==c2);//true,一个类在内存中只有一个字节码对象
Student s=new Student();
Class<? extends Student> c3=s.getClass();
System.out.println(c1==c3);//true
Class<?> c4=Class.forName("com.itheima.Student");//forName要抛出异常ClassNotFoundException
System.out.println(c1==c4);//true

反射构造方法

image-20220715214817907
image-20220715214817907

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//反射构造方法并使用
Class<?> c=Class.forName("com.itheima.Student");
//得到所有public权限构造函数(私有默认的不能)
Constructor<?>[] cons=c.getConstructors();
for(Constructor con:cons){
System.out.println(con);
}
Constructor<?>[] cons2=c.getDeclaredConstructors();//得到所有权限的构造函数
for(Constructor con:cons2)
{
System.out.println(con);
}
//获得指定构造函数
//参数
Constructor<?> con=c.getConstructor();//得到单个构造方法
//提供单个构造函数信息和访问权限
Object obj=con.newInstance();//根据构造方法创建对象
System.out.println(obj);

基本数据类型也可以通过.class方法得到class对象,getConstructor方法需要传入的参数都是class类型,并且是当前想要访问的构造方法的形参

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//反射练习
Class<?> c=Class.forName("com.itheima.Student");
//public Student(String name, int age,String address);
Constructor<?> con = c.getConstructor(String.class, int.class, String.class);
Object obj = con.newInstance("sqhy", 4781, "339");
System.out.println(obj);
//访问私有构造函数
//实现 Student s=new Student("sqhy");System.out.println(s);
Class<?> c1=Class.forName("com.itheima.Student");
//private Student(String name);
Constructor<?> con1=c1.getDeclaredConstructor(String.class);
//暴力反射
//public void setAccessible (boolean flag):值为true时,取消访问检查
con1.setAccessible(true);
Object obj1=con1.newInstance("sqhy");
System.out.println(obj1);
  • getDeclaredConstructor可获取所有访问权限的构造方法
  • getConstructor只能得到public权限的构造方法

遍历数组简便写法

1
2
3
for(Field filed: fields){
System.out.println(filed);
}

通过反射得到成员变量

image-20220716012613059
image-20220716012613059

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//反射获取成员变量
Class<?> c = Class.forName("com.itheima.Student");
//getFields方法,获取该Class对象表示的类或接口的所有可访问的public字段
//getDeclaredFields方法,获取该Class对象表示的类或接口的所有字段
Field[] fields = c.getFields();
for(Field filed: fields){
System.out.println(filed);//只得到public的变量
}
Field[] fields1 = c.getDeclaredFields();
for(Field f:fields1)
{
System.out.println(f);//得到所有成员变量
}
//根据指定名称
Field ad = c.getField("address");//要抛出异常
Constructor<?> con = c.getConstructor();
Object obj=con.newInstance();
ad.set(obj,"339");//给obj的成员变量ad赋值为"339"
System.out.println(obj);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//练习
Class<?> c=Class.forName("com.itheima.Student");
Constructor<?> con = c.getConstructor();
Object obj=con.newInstance();
//name是私有成员
Field namef=c.getDeclaredField("name");
namef.setAccessible(true);//取消访问检查
namef.set(obj,"sqhy");
System.out.println(obj);
Field agef = c.getDeclaredField("age");
agef.setAccessible(true);
agef.set(obj,4781);
System.out.println(obj);
Field addressf = c.getDeclaredField("address");
addressf.setAccessible(true);
addressf.set(obj,"339");
System.out.println(obj);

反射获取成员方法

image-20220716014440272
image-20220716014440272

注:getDeclaredMethods方法只有本类的所有方法,没有由类或接口声明的对象以及从超类或超级接口继承的类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//反射获取成员方法
Class<?> c = Class.forName("com.itheima.Student");
Method[] methods = c.getMethods();
for(Method m:methods) {
System.out.println(m);//所有public方法,以及由类或接口声明的对象以及从超类或超级接口继承的类
}
Method[] methods1 = c.getDeclaredMethods();
for(Method m:methods1) {
System.out.println(m);//只有本类的所有方法,没有由类或接口声明的对象以及从超类或超级接口继承的类
}
System.out.println("---------");
//获取单个
Method m = c.getMethod("method1");
//获取无参构造方法创建对象并调用method1方法
Constructor<?> con = c.getConstructor();
Object obj = con.newInstance();
//Object invoke(Object obj,Object...args) 在具有指定参数的指定对象上调用方法
//Object 返回值类型
//obj调用方法的对象
//args 方法所需的参数
m.invoke(obj);//method1没有参数,成功调用类method1方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//练习
Class<?> c = Class.forName("com.itheima.Student");
Constructor<?> con = c.getConstructor();
Object obj = con.newInstance();
Method m1 = c.getMethod("method1");
m1.invoke(obj);//obj.method1()
Method m2 = c.getMethod("method2", String.class);
m2.invoke(obj,"sqhy");//obj.method2()
//调用有返回值的方法
Method m3 = c.getMethod("method3", String.class, int.class);
Object o = m3.invoke(obj, "sqhy", 4781);
System.out.println(o);
//私有方法
Method m4 = c.getDeclaredMethod("function");
m4.setAccessible(true);
m4.invoke(obj);

练习

  • 在ArrayList中插入一个字符串数据–越过泛型检查

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    ArrayList<Integer> array=new ArrayList<Integer>();
    array.add(10);
    array.add(20);
    //array.add("asa");//报错
    System.out.println(array);//[10, 20]
    Class<? extends ArrayList> c = array.getClass();
    //获取到原始方法,从而绕过泛型检查
    Method m = c.getMethod("add", Object.class);
    m.invoke(array,"hello");
    m.invoke(array,"sqhy");
    m.invoke(array,"zql");
    System.out.println(array);
  • 运行配置文件运行类的方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    /* 配置文件
    class.txt
    className=xxx
    methodName=xxx
    */
    /* className=com.itheima.S
    methodName=study
    */
    //加载数据
    Properties prop=new Properties();
    FileReader fr=new FileReader("./class.txt");//相对路径,这里.表示总文件夹(src的上一级)
    prop.load(fr);
    fr.close();
    //通过键名获取值
    String className = prop.getProperty("className");
    String methodName = prop.getProperty("methodName");
    //通过反射使用
    Class<?> c = Class.forName(className);//com.itheima.S
    Constructor<?> con = c.getConstructor();
    Object obj = con.newInstance();
    Method m = c.getMethod(methodName);//study
    m.invoke(obj);
  • 标题: Java反射
  • 作者: Sl0th
  • 创建于 : 2022-08-23 22:50:09
  • 更新于 : 2024-11-11 18:23:06
  • 链接: http://sl0th.top/2022/08/23/Java反射/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
评论