【Java】反射是什么?

一个人走得远了,就会忘记自己为了什么而出发,希望你可以不忘初心,不要随波逐流,一直走下去
欢迎关注点赞收藏留言
本文由 程序喵正在路上 原创,CSDN首发!
系列专栏:Java入门
首发时间:2022年7月28日
✅ 如果觉得博主的文章还不错的话,希望小伙伴们三连支持一下哦

阅读指南

  • 类加载器
    • 1. 类加载
    • 2. 类加载器
  • 反射
    • 1. 反射概述
    • 2. 获取 Class 类的对象
    • 3. 反射获取构造方法
    • 4. 反射获取成员变量
    • 5. 反射获取成员方法
    • 6. 练习之越过泛型检查
    • 7. 练习之运行配置文件指定内容

类加载器

1. 类加载

当程序使用某个类时,如果该类还未被加载到内存中,则系统回通过类的加载、类的连接、类的初始化这三个步骤来对类进行初始化。如果不出现意外情况,JVM 将会连续完成这三个步骤,所以有时也把这三个步骤统称为类加载或者类初始化

类的加载

  • 就是指将 class 文件读入内存,并为之创建一个 java.lang.Class 对象
  • 任何类被使用时,系统都会为之建立一个 java.lang.Class 对象

类的连接

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

类的初始化

  • 在该阶段,主要就是对类变量进行初始化

类的初始化步骤

  1. 假如类还未被加载和连接,则程序先加载并连接该类
  2. 假如该类的直接父类还未被初始化,则先初始化其直接父类
  3. 假如类中有初始化语句,则系统依次执行这些初始化语句

注意:在执行第 2 个步骤的时候,系统对直接父类的初始化步骤也遵循初始化步骤 1-3。也就是说,如果说直接父类还有父类,则系统会再次重复这三个步骤,以此类推,所以 JVM 最先初始化的总是 java.lang 包下的 Object

类的初始化时机

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

2. 类加载器

类加载器的作用

  • 负责将 .class 文件加载到内存中,并为之生成对应的 java.lang.Class 对象
  • 虽然我们不用过分关心类加载机制,但是了解这个机制我们就能更好地理解程序的运行

JVM 的类加载机制

  • 全盘负责:就是当一个类加载器负责加载某个 Class 时,该 Class 所依赖的和引用的其他 Class 也将由该类加载器负责载入,除非显示地使用另一个类加载器来载入
  • 父类委托:就是当一个类加载器负责加载某个 Class 时,先让父类加载器试图加载该 Class,只有在父类加载器无法加载该类时才尝试从自己的类路径中加载该类
  • 缓存机制:保证所有加载过的 Class 都会被缓存,当程序需要使用某个 Class 对象时,类加载器先从缓存区中搜索该 Class,只有当缓存区中不存在该 Class 对象时,系统才会读取该类对应的二级制数据,并将其转换成 Class 对象,存储到缓存区

ClassLoader:是负责加载类的对象

Java 运行时具有以下内置类加载器:

  • Bootstrap class loader:它是虚拟机的内置类加载器,通常表示为 null,并且没有父 null
  • Platform class loader:平台类加载器可以看到所有平台类,平台类包括由平台类加载器或者其祖先定义的 JavaSE 平台 API,其实现类和 JDK 特定的运行时类
  • System class loader:它也被称为应用程序类加载器,与平台类加载器不同。系统类加载器通常用于定义应用程序类路径,模块路径和 JDK 特定工具上的类
  • 类加载器的继承关系:System 的父加载器为 Platform,而 Platform 的父加载器为 Bootstrap

ClassLoader 中的两个方法:

  • static ClassLoader getSystemClassLoader():返回用于委派的系统类加载器
  • ClassLoader getParent():返回父类加载器进行委派

创建一个类 ClassLoaderDemo ,代码如下,结果会是什么呢?

public class ClassLoaderDemo {
    public static void main(String[] args) {
        ClassLoader c = ClassLoader.getSystemClassLoader();
        System.out.println("委派的系统类加载器:" + c);

        ClassLoader c1 = c.getParent();
        System.out.println("系统类加载器的父类加载器:" + c1);

        ClassLoader c2 = c1.getParent();
        System.out.println("平台类加载器的父类加载器:" + c2);
    }
}

运行结果:

在这里插入图片描述

反射

1. 反射概述

Java 反射机制:

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

2. 获取 Class 类的对象

我们要想通过反射去使用一个类,首先我们要获取到这个类的字节码文件对象,也就是类型为 Class 类型的对象

获取 Class 类型对象的三种方式:

  • 使用类的 Class 属性来获取该类对应的 Class 对象。举例:Student.class 将会返回 Student 类对应的 Class 对象
  • 调用对象的 getClass() 方法,返回该对象所属类对应的 Class 对象
    该方法是 Object 类中的方法,所有的 Java 对象都可以调用该方法
  • 使用 Class 类中的静态方法 forName(String className),该方法需要传入字符串参数,该字符串参数的值是某个类的全路径,也就是完整包名的路径

下面我们通过程序来看一下

创建一个学生类 Student

public class Student {
    //成员变量:一个私有,一个默认,一个公共
    private String name;
    int age;
    public String address;

    //构造方法:一个私有,一个默认,一个公共
    public Student() {
    }

    private Student(String name) {
        this.name = name;
    }

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

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

    //成员方法:一个私有,四个公共
    private void function() {
        System.out.println("function");
    }

    public void method1() {
        System.out.println("method1");
    }

    public void method2(String s) {
        System.out.println("method2:" + s);
    }

    public String method3(String s, int i) {
        return s + "," + i;
    }

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

创建一个测试类 ReflectDemo

public class ReflectDemo {
    public static void main(String[] args) throws ClassNotFoundException {
        //使用类的 Class 属性来获取该类对应的 Class 对象
        Class<Student> c1 = Student.class;
        System.out.println(c1);

        Class<Student> c2 = Student.class;
        System.out.println(c1 == c2);
        System.out.println("--------------------------");

        //调用对象的 getClass() 方法,返回该对象所属类对应的 Class 对象
        Student s = new Student();
        Class<? extends Student> c3 = s.getClass();
        System.out.println(c1 == c3);
        System.out.println("--------------------------");

        //使用 Class 类中的静态方法 forName(String className),该方法需要传入字符串参数,该字符串参数的值是某个类的全路径,也就是完整包名的路径
        Class<?> c4 = Class.forName("反射.Student");
        System.out.println(c1 == c4);
    }
}

运行结果

【Java】反射是什么?_第1张图片

3. 反射获取构造方法

Class 类中用于获取构造方法的方法:

  • Constructor[] getConstructors():返回所有公共构造方法对象的数组
  • Constructor[] getDeclaredConstructors():返回所有构造方法对象的数组
  • Constructor getConstructor(Class...parameterTypes):返回单个公共构造方法的对象
  • Constructor getDeclaredConstructor(Class...parameterTypes):返回单个构造方法的对象

Constructor 类中用于创建对象的方法:

  • T newInstance(Object... initargs):根据指定的构造方法创建对象

代码演示

import java.lang.reflect.Constructor;

public class ReflectDemo01 {
    public static void main(String[] args) throws Exception {
        //获取Class对象
        Class<?> c = Class.forName("反射.Student");

        //获取所有公共构造方法组成的数组
//        Constructor[] cons = c.getConstructors();

        //获取所有构造方法组成的数组
        Constructor<?>[] cons = c.getDeclaredConstructors();
        for (Constructor con : cons) {
            System.out.println(con);
        }

        System.out.println("------------------------------");

        //获取单个公共构造方法
        Constructor<?> con = c.getConstructor();
        Object obj = con.newInstance();
        System.out.println(obj);
    }
}

运行结果

【Java】反射是什么?_第2张图片
练习

► 练习1:通过反射实现与下面代码相同的效果

Student s = new Student("林青霞", 30, "西安");
System.out.println(s);
  • 基本数据类型也可以通过 .class 得到对应的 Class 类型

代码实现

import java.lang.reflect.Constructor;

public class ReflectDemo02 {
    public static void main(String[] args) throws Exception {
        //获取Class对象
        Class<?> c = Class.forName("反射.Student");

        //获取构造方法对象
        Constructor<?> con = c.getConstructor(String.class, int.class, String.class);

        //产生对象
        Object obj = con.newInstance("林青霞", 30, "西安");
        System.out.println(obj);
    }
}

运行结果

在这里插入图片描述
► 练习2:通过反射实现与下面代码相同的效果

Student s = new Student("林青霞");
System.out.println(s);
  • public void setAccessible(boolean flag):值为 true时,取消访问检查

代码实现

import java.lang.reflect.Constructor;

public class ReflectDemo03 {
    public static void main(String[] args) throws Exception {
        //获取Class对象
        Class<?> c = Class.forName("反射.Student");

        //获取构造方法对象
        Constructor<?> con = c.getDeclaredConstructor(String.class);

        //暴力反射
        con.setAccessible(true);

        //产生对象
        Object obj = con.newInstance("林青霞");
        System.out.println(obj);
    }
}

运行结果

在这里插入图片描述

4. 反射获取成员变量

Class 类中用于获取成员变量的方法:

  • Field[] getFields():返回所有公共成员变量对象的数组
  • Field[] getDeclaredFields():返回所有成员变量对象的数组
  • Field getField(String name):返回单个公共成员变量对象
  • Field getDeclaredField(String name):返回单个成员变量对象

Field 类中用于给成员变量赋值的方法:

  • void set(Object obj, Object value):给 obj 对象的成员变量赋值为 value

代码演示

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;

public class ReflectDemo04 {
    public static void main(String[] args) throws Exception {
        //获取Class对象
        Class<?> c = Class.forName("反射.Student");

        //获取公共成员变量
//        Field[] fields = c.getFields();
        //获取所有成员变量
        Field[] fields = c.getDeclaredFields();
        for (Field field : fields) {
            System.out.println(field);
        }
        System.out.println("----------------------------");
        
        Field addressField = c.getField("address");

        //获取无参构造方法创建的对象
        Constructor<?> con = c.getConstructor();
        Object obj = con.newInstance();
        
        addressField.set(obj, "西安");
        System.out.println(obj);
    }
}

运行结果

【Java】反射是什么?_第3张图片
练习

► 练习:通过反射实现与下面代码相同的效果

Student s = new Student();
s.name = "林青霞";
s.age = 30;
s.address = "西安";
System.out.println(s);

代码实现

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;

public class ReflectDemo05 {
    public static void main(String[] args) throws Exception {
        //获取Class对象
        Class<?> c = Class.forName("反射.Student");

        //获取无参构造方法创建的对象
        Constructor<?> con = c.getConstructor();
        Object obj = con.newInstance();
        System.out.println(obj);

        Field nameField = c.getDeclaredField("name");
        //取消访问检查
        nameField.setAccessible(true);
        nameField.set(obj, "林青霞");

        Field ageField = c.getDeclaredField("age");
        ageField.setAccessible(true);
        ageField.set(obj, 30);

        Field addressField = c.getField("address");
        addressField.set(obj, "西安");

        System.out.println(obj);
    }
}

运行结果

在这里插入图片描述

5. 反射获取成员方法

Class 类中用于获取成员方法的方法:

  • Method[] getMethods():返回所有公共成员方法对象的数组,包括继承的
  • Method[] getDeclaredMethods():返回所有成员方法对象的数组,不包括继承的
  • Method getMethod(String name, Class...parameterTypes):返回单个公共成员方法对象
  • Method getDeclaerdMethod(String name, Class...parameterTypes):返回单个成员方法对象

Method 类中用于调用成员变量的方法:

  • Object invoke(Object obj, Object ...args):调用 obj 对象的成员方法,参数是 args,返回值是 Object 类型

代码演示

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

public class ReflectDemo06 {
    public static void main(String[] args) throws Exception {
        //获取Class对象
        Class<?> c = Class.forName("反射.Student");

//        Method[] methods = c.getMethods();
        Method[] methods = c.getDeclaredMethods();
        for (Method method : methods) {
            System.out.println(method);
        }

        System.out.println("-----------------");

        Method m = c.getMethod("method1");

        Constructor<?> con = c.getConstructor();
        Object obj = con.newInstance();

        m.invoke(obj);
    }
}

运行结果

【Java】反射是什么?_第4张图片

练习

► 练习:通过反射实现与下面代码相同的效果

Student s = new Student();
s.method1();
s.method2("林青霞");
String ss = s.method3("林青霞", 30);
System.out.println(ss);
s.function();

代码实现

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

public class ReflectDemo07 {
    public static void main(String[] args) throws Exception {
        //获取Class对象
        Class<?> c = Class.forName("反射.Student");

        Constructor<?> con = c.getConstructor();
        Object obj = con.newInstance();

        Method m1 = c.getMethod("method1");
        m1.invoke(obj);

        Method m2 = c.getMethod("method2", String.class);
        m2.invoke(obj, "林青霞");

        Method m3 = c.getMethod("method3", String.class, int.class);
        Object o = m3.invoke(obj, "林青霞", 30);
        System.out.println(o);

        Method m4 = c.getDeclaredMethod("function");
        m4.setAccessible(true);
        m4.invoke(obj);
    }
}

运行结果

【Java】反射是什么?_第5张图片

6. 练习之越过泛型检查

现在我们有一个 ArrayList 集合,如果想在这个集合中添加一个字符串数据,该怎么实现呢?

代码实现

import java.lang.reflect.Method;
import java.util.ArrayList;

public class ReflectTest01 {
    public static void main(String[] args) throws Exception {
        //创建集合
        ArrayList<Integer> arr = new ArrayList<>();

        Class<? extends ArrayList> c = arr.getClass();
        Method m = c.getMethod("add", Object.class);
        
        m.invoke(arr, "hello");
        m.invoke(arr, "world");
        m.invoke(arr, "Java");

        System.out.println(arr);
    }
}

运行结果

在这里插入图片描述

7. 练习之运行配置文件指定内容

通过配置文件运行类中的方法

Student.class(要运行的类)

public class Student {
    public void study(){
        System.out.println("好好学习,天天向上");
    }
}

class.txt(配置文件)

className=反射.Student
methodName=study

ReflectTest02.class(主函数)

public class ReflectTest02 {
    public static void main(String[] args) throws Exception {
        //加载数据
        Properties prop = new Properties();
        FileReader fr = new FileReader("D:\\IDEA\\idea_Demo\\反射\\class.txt");
        prop.load(fr);
        fr.close();

        String className = prop.getProperty("className");
        String methodName = prop.getProperty("methodName");

        //通过反射来使用
        Class<?> c = Class.forName(className);      //反射.Student

        Constructor<?> con = c.getConstructor();
        Object obj = con.newInstance();

        Method m = c.getMethod(methodName);     //study
        m.invoke(obj);
    }
}

运行结果

在这里插入图片描述



Java入门的内容到这里就一段落了
这次的分享就到这里啦,继续加油哦
有出错的地方欢迎在评论区指出来,共同进步,谢谢啦

你可能感兴趣的:(Java入门,java,jvm,开发语言)