Java反射

本文详细介绍了Java的反射机制,包括类加载器的工作原理,类加载的时机和过程,以及不同类型的类加载器。接着,文章讲解了双亲委派模型,并展示了类加载器中重要的方法。在反射部分,阐述了反射的概述,获取Class对象的三种方式,以及如何通过反射获取和操作构造方法、成员变量和成员方法。文章提供了丰富的代码示例,帮助读者理解并实践Java反射技术。
摘要由CSDN通过智能技术生成


前言

Java的反射(reflection)机制是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法。这种动态获取程序信息以及动态调用对象的功能称为Java语言的反射机制。反射被视为动态语言的关键。


1. 类加载器?

1.1 类加载器

了解Java反射机制前先了解一下类加载器相关的知识。java文件 经过编译 -> Class文件(字节码文件) 最后虚拟机执行的就是字节码文件, 但是字节码文件又不会自己跑到虚拟机当中, 这时候就得需要一个”人“,把字节码文件放到虚拟机当中,而这个人就是 类加载器。
在这里插入图片描述

1.2 类加载的过程

类加载时机(何时加载字节码文件):

  • 创建类的实例(对象)
  • 调用类的类方法(静态方法)
  • 访问类或者接口的类变量(静态变量),或者为该类赋值
  • 使用反射方式来强制的创建某个类或接口对应的java.lang.Class对象
  • 初始化某一个类的子类(当你需要创建一个类的对象,那么也- - 要把这个类的父类 的字节码文件也加载到内存当中。
  • 直接使用java.exe命令来运行某个主类
    这么多情况,心好累,记不住啊!
    在这里插入图片描述

总结:用到就加载,不用不加载

类加载过程

  1. 加载
  • 通过一个类的全限定名(包名+类名)来获取定义此类的二进制字节流 —》 通过包名+类名,获取这个类,准备用流进行传输
  • 将这个字节流所代表的的静态存储结构转化为运行时的存储结构 —》 把这个类加载到内存当中
  • 在内存中会生成一个代表这个类的java.lang.Class对象,任何类被使用时,系统都会为之建立一个java.lang.Class对象 —》加载完毕创建一个Class对象
    在这里插入图片描述
    在这里插入图片描述
  1. 链接
  • 验证
    在这里插入图片描述

  • 准备
    在这里插入图片描述

  • 解析
    在这里插入图片描述
    解析过程拆解:
    name的类型是引用类型(String),String是其他的类的名字,最初加载String类的时候不知道String类在哪里,这时的String使用符号(ex:&&&)替代。在解析的过程中,就会找实际的String类在哪里,并把临时的符号变成实际的String引用
    在这里插入图片描述

  1. 初始化
    在这里插入图片描述

1.3 加载器的分类

  • 分类
    启动类加载器(Bootstrap ClassLoader):虚拟机内置的类加载器
    平台类加载器(Platform Classloader):负责加载JDK中一些特殊的模块
    系统类加载器(System Classloader):负责加载用户类路径上所指定的类库
  • 类加载器的继承关系
    System的父加载器为Platform
    Platform的父加载器为Bootstrap
  • 代码演示
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
// 获取系统类加载器的父类加载器,平台类加载器
ClassLoader clasLoader1 = systemClassLoader.getParent();
//获取平台类加载器的父类加载器, 启动类加载器
ClassLoader classLoader2 = clasLoader1.getParent();
System.out.println("系统类加载器" + systemClassLoader);
System.out.println("平台类加载器" + clasLoader1);
System.out.println("启动类加载器" + classLoader2);

1.4 双亲委派模型

  • 介绍
    如果一个类加载器收到了类加载请求,它并不会自己先去加载,而是把这个请求委托给父类的加载器去执行,如果父类加载器还存在其父类加载器,则进一步向上委托,依次递归,请求最终将到达顶层的启动类加载器,如果父类加载器可以完成类加载任务,就成功返回,倘若父类加载器无法完成此加载任务,子加载器才会尝试自己去加载,这就是双亲委派模式
    逻辑上的继承(不是extend)。
    在这里插入图片描述

1.5 类加载器中两个重要的方法:

  • 方法介绍
方法名说明
public static ClassLoader getSystemClassLoader()获取系统类加载器
public InputStream getResourceAsStream(String name)加载某一个资源文件
  • 代码演示
//获得系统类加载器
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
// 利用类加载器去加载一个指定的文件
// 参数:文件的路径
// 返回值:字节流
// 这里直接填写prop.properties,相对的在src下边
InputStream resourceAsStream = systemClassLoader.getResourceAsStream("prop.properties");
Properties properties = new Properties();
    properties.load(resourceAsStream);
    System.out.println(properties);
    resourceAsStream.close();

2. 反射

2.1 反射的概述

Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类所有的属性与方法;对于任意一个对象,都能够调用它的任意属性和方法(利用反射调用它类中的属性和方法时,无视修饰符)。
这种动态获取信息以及动态调用对象方法的功能称为Java语言的反射机制(先获取配置文件中的信息,动态获取信息并创建对象和调用方法)

2.2 获取Class对象的三种方式

  • 思考
    如何使用反射去调用一个类中的方法
    利用class对象来创建
    反射方式:创建对象
    反射方式:调用方法
  • 三种方式分类
    类名.class属性
    对象名.getClass()方法
    Class.forName(全类名)方法
    在这里插入图片描述
  • 代码演示
    student类
public class Student {
private String name;
private int age;
public Student() {
}

public Student(String name, int age) {
    this.name = name;
    this.age = age;
}

public String getName() {
    return name;
}

public void setName(String name) {
    this.name = name;
}

public int getAge() {
    return age;
}

public void setAge(int age) {
    this.age = age;
}

public void study(){
    System.out.println("学生在学习");
}

@Override
public String toString() {
    return "Student{" +
            "name='" + name + '\'' +
            ", age=" + age +
            '}';
}
}
  • main方法
Class<?> clazz = Class.forName("全类名");
Class<Student> clazz2 = Student.class;
Student s = new Student();
//每一个对象都对应着一个字节码文件,所以getClass方法是定义在Object中的
Class clazz3 = s.getClass();
System.out.println(clazz == clazz2);//true
System.out.println(clazz3 == clazz2);//true

2.3 获取Class类对象中的内容

Java当中什么都可以看成一个对象,只要是对象,就可以用类去描述。从class类中获取的成员变量就可以把它认为是成员变量对象(Field),以此类推…

如何学习?
大概步骤(以获取构造方法为例)
-获取class对象
-获得Constructor对象
-创建对象
明白几个问题
-获取对象不就是为了创建对象么?
-获取属性不就是为了给他赋值和取值么?
-获取方法不就是为了调用么?
学习
-把整个流程以及为什么用反射了解清楚,方法忘了就去api手册查,重复多了自然也就记住了
总结
-先获取整体(Class对象),在获取部分(成员属性,构造方法,成员男方法),然后操作部分

如图所示:Class对象加载到内存当中,我们可以去获取想要的信息
在这里插入图片描述

2.4 反射获取构造方法

2.4.1 Class类获取构造方法对象的方法

方法名说明
Constructor<?>[] getConstructors()返回所有公共构造方法对象的数组
Constructor<?>[] getDeclaredConstructors()返回所有构造方法对象的数组
Constructor getConstructor(Class<?>… parameterTypes)返回单个公共构造方法对象
Constructor getDeclaredConstructor(Class<?>… parameterTypes)返回单个构造方法对象
  • 代码演示
    Student.java
public class Student {
    private String name;
    private int age;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public void study() {
        System.out.println("我的名字是:" + name);
        System.out.println("学生在打篮球");
    }
    private Student(String name) {
        System.out.println("name的值为:" + name + "age的值为:" + age);
        System.out.println("private ... student ... 有参构造方法");
    }
    public Student() {
        System.out.println("public ... student ... 无参构造方法");
    }
    public Student(String name, int age) {
        System.out.println("name的值为:" + name + "age的值为:" + age);
        System.out.println("public ... student ... 有参构造方法");
    }
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

main

private static void method4() throws ClassNotFoundException, NoSuchMethodException {
    Class<?> clazz = Class.forName("com.gosling.reflect.Entity.Student");
    Constructor declaredConstructor = clazz.getDeclaredConstructor(String.class);
    System.out.println(declaredConstructor);
}
private static void method3() throws ClassNotFoundException, NoSuchMethodException {
    Class<?> clazz = Class.forName("com.gosling.reflect.Entity.Student");
    //小括号中一定要和构造方法的形参保持一致
    Constructor<?> constructor = clazz.getConstructor();
    System.out.println(constructor);
    Constructor<?> constructor2 = clazz.getConstructor(String.class, int.class);
    System.out.println(constructor2);
    //会报错,没有形参为int的构造方法
    Constructor<?> constructor3 = clazz.getConstructor(int.class);
    System.out.println(constructor3);
}
private static void method2() throws ClassNotFoundException {
    Class<?> clazz = Class.forName("com.gosling.reflect.Entity.Student");
    Constructor<?>[] declaredConstructors = clazz.getDeclaredConstructors();
    for (Constructor<?> declaredConstructor : declaredConstructors) {
        System.out.println(declaredConstructor);
    }
}
private static void method1() throws ClassNotFoundException {
    Class<?> clazz = Class.forName("com.gosling.reflect.Entity.Student");
    Constructor<?>[] constructors = clazz.getConstructors();
    for (Constructor<?> constructor : constructors) {
        System.out.println(constructor);
    }
}

2.4.2 通过Constructor对象中的方法创建对象

  • 方法介绍
方法名说明
T newInstance(Object…initargs)根据指定的构造方法创建对象
setAccessible(boolean flag)设置为true,表示取消访问检查
  • 代码演示
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
//        method1();
//        method2();
//        method3();
        //获取一个私有的构造方法,并创建对象
        //1.获取class对象
        Class<?> clazz = Class.forName("com.gosling.reflect.Entity.Student");
        //2.获取一个私有化的构造方法
        Constructor constructor = clazz.getDeclaredConstructor(String.class);
        //被private修饰的成员,不能直接使用
        //如果使用反射前行获取并使用,需要临时取消访问检查
        constructor.setAccessible(true);
        //3.直接创建对象
        Student student = (Student) constructor.newInstance("李白");
        System.out.println(student);
    }
private static void method3() throws ClassNotFoundException, InstantiationException, IllegalAccessException {
     //简写格式
     Class<?> clazz = Class.forName("com.gosling.reflect.Entity.Student");
     Student student = (Student) clazz.newInstance();//这个方法过时了
 }
private static void method2() throws ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
     Class<?> clazz = Class.forName("com.gosling.reflect.Entity.Student");
     Constructor<?> constructor = clazz.getConstructor();
     Student student = (Student) constructor.newInstance();
     System.out.println(student);
 }
private static void method1() throws ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
     // 获取class对象
     Class<?> clazz = Class.forName("com.gosling.reflect.Entity.Student");
     //获取构造方法对象
     Constructor<?> constructor = clazz.getConstructor(String.class, int.class);
     //利用newInstance方法,创建Student对象
     Student student = (Student) constructor.newInstance("李白", 61);
     System.out.println(student);
 }

2.4.3 小结

  • 获取class对象
    三种方式: Class.forName(“全类名”), 类名.class, 对象名.getClass()
  • 获取里面的构造方法对象
    getConstructor (Class<?>... parameterTypes)getDeclaredConstructor (Class<?>… parameterTypes)
  • 如果是public的,直接创建对象
    newInstance(Object… initargs)
  • 如果是非public的,需要临时取消检查,然后再创建对象
    setAccessible(boolean) 暴力反射

2.5 反射获取成员变量

2.5.1 Class类获取成员变量对象的方法

  • 方法介绍
方法名说明
Field[] getFields()返回所有公共(public)成员变量对象的数组
Field[] getDeclaredFields()返回所有成员变量对象的数组
Field getField(String name)返回单个公共成员变量对象
Field getDeclaredField(String name)返回单个成员变量对象
  • 代码演示
private static void method4() throws ClassNotFoundException, NoSuchFieldException {
    //1.获取class对象
    Class<?> clazz = Class.forName("com.gosling.reflect.Entity.Person");
    //2.获取name这个成员变量
    Field field = clazz.getDeclaredField("money");
    //3.打印
    System.out.println(field);
}

private static void method3() throws ClassNotFoundException, NoSuchFieldException {
    //想要获取的成员变量必须是真是存在的,且是public的
    //1.获取class对象
    Class<?> clazz = Class.forName("com.gosling.reflect.Entity.Person");
    //2.获取name这个成员变量
    Field field = clazz.getField("money");
    //3.打印
    System.out.println(field);
}

private static void method2() throws ClassNotFoundException {
    //1.获取class对象
    Class<?> clazz = Class.forName("com.gosling.reflect.Entity.Person");
    //2.获取Field对象
    Field[] fields = clazz.getDeclaredFields();
    //3.遍历
    for (Field field : fields) {
        System.out.println(field);
    }
}

private static void method1() throws ClassNotFoundException {
    //1.获取class对象
    Class<?> clazz = Class.forName("com.gosling.reflect.Entity.Person");
    //2.获取Field对象
    Field[] fields = clazz.getFields();
    //3.遍历
    for (Field field : fields) {
        System.out.println(field);
    }
}

2.5.2 给成员变量赋值

  • 方法介绍
方法名说明
void set(Object obj, Object value)赋值
Object get(Object obj)获取值
  • 代码演示
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, InstantiationException, IllegalAccessException {
//        method1();
        //1.获取class对象
        Class clazz = Class.forName("com.gosling.reflect.Entity.Person");
        //2.获取成员变量的Field对象
        Field field = clazz.getDeclaredField("money");
        //3.取消访问检查
        field.setAccessible(true);
        //4.调用get方法来取值
        //4.1创建一个对象
        Person person = (Person) clazz.newInstance();
        //4.2获取指定对象的money的值
        Object o = field.get(person);
        //5.打印
        System.out.println(o);

    }
    private static void method1() throws ClassNotFoundException, NoSuchFieldException, InstantiationException, IllegalAccessException {
        //1.获取class对象
        Class clazz = Class.forName("com.gosling.reflect.Entity.Person");
        // 2.获取name这个Field对象
        Field field = clazz.getField("name");
        //3.利用set方法进行赋值
        //3.1先创建一个Student对象
        Person person = (Person) clazz.newInstance();
        //3.2有了对象才可以给指定对象赋值
        field.set(person,"李白");
        System.out.println(person);
    }

2.6 反射获取成员方法并使用

2.6.1 Class类获取成员方法对象的方法

  • 方法分类
方法名
Method[] getMethods()返回所有公共成员方法对象的数组,包括继承的
Method[] getDeclaredMethods()返回所有成员方法对象的数组,不包括继承的
Method getMethod(String name, Class<?>… parameterTypes)返回单个公共成员方法对象
Method getDeclaredMethod(String name, Class<?>… parameterTypes)返回单个成员方法对象
  • 代码演示
private static void method5() throws ClassNotFoundException, NoSuchMethodException {
    //1.获取class对象
    Class clazz = Class.forName("com.gosling.reflect.Entity.Person");
    //2.获取私有的方法
    Method look = clazz.getDeclaredMethod("look");
    System.out.println(look);
}

private static void method4() throws ClassNotFoundException, NoSuchMethodException {
    //1.获取class对象
    Class clazz = Class.forName("com.gosling.reflect.Entity.Person");
    //2.获取有形参的方法
    Method method1 = clazz.getMethod("method2", String.class);
    System.out.println(method1);
}

private static void method3() throws ClassNotFoundException, NoSuchMethodException {
    //1.获取class对象
    Class clazz = Class.forName("com.gosling.reflect.Entity.Person");
    //2.获取无参数的方法
    Method method1 = clazz.getMethod("method1");
    System.out.println(method1);
}


private static void method2() throws ClassNotFoundException {
    //1.获取class对象
    Class clazz = Class.forName("com.gosling.reflect.Entity.Person");
    //2.获取成员方法对象
    Method[] methods = clazz.getDeclaredMethods();// 返回所有成员方法的数组,不包含继承的
    for (Method method : methods) {
        System.out.println(method);
    }
}

private static void method1() throws ClassNotFoundException {
    //1.获取class对象
    Class clazz = Class.forName("com.gosling.reflect.Entity.Person");
    //2.获取成员方法对象
    Method[] methods = clazz.getMethods();// 继承父类的方法也在里边
    for (Method method : methods) {
        System.out.println(method);
    }
}

3. 总结

以上就是今天要讲的内容,本文仅仅简单介绍了Java的反射机制,因为有关反射的方法众多,本文只介绍了较为常用的一些去供大家学习和了解!
本文中的案例大家不要光看哦,试着放到程序中运行一下,对加深理解有很大的帮助的!

最后,创作不易,如果本文对您有一点帮助的话,就点赞、收藏、关注支持鼓励一下吧,您的支持是我创作的动力!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值