基础——Class类与反射

一、java.lang.Class类

在这里插入图片描述

  1. java程序编译生成.class文件,.class文件中的内容其实就是.java中的内容,只是.class文件是给JVM看的,一般.class文件中存储的是字节码信息,我们能看懂是因为Idea帮我们反编译了
  2. java是面向对象的编程语言,任何事物都可以定义创建对象。.class文件也是一种事物,也可以定义类,也可以创建对象。
  3. java.lang.Class类,就是用来专门描述.class字节码文件的类。Class也是有对象的,只是Class类型的对象,程序员无法创建和销毁,是由JVM帮助我们创建和销毁,但是我们可以获取Class类型的文件,从而完成相关操作。

Class类型的定义
在这里插入图片描述
注意区别:

  1. class:定义类的关键字
  2. Class:类名称,就是一个普通的类名称

二、类的加载

前言,我们已经知道了java编译以后会生成.class文件供JVM运行,.class文件信息会加载到内存方法区存储.class文件的常量池(静态区)。

  1. 第一次使用类的信息时,.class字节码文件会被加载到内存,存储在方法区中。
  2. JVM会为加载到方法区的.class文件创建一个Class类型的对象,该对象被保存在堆内存中。相当于堆内存中的Class类型的对象指向了方法区中的.class文件。
  3. 一个类只会被加载一次,所以Class类型的对象只会有一个。
  4. 任意类型(基本类型/引用类型)都有Class类型的对象。

在这里插入图片描述

tip:Class可以被比喻为手术刀,反射好比是对类的解刨。

三、反射

反射就是通过获取到Class类型的对象,从而操作.class文件。

java中万物皆对象!

对象 类型 获取方式 执行方式
.class文件 Class 三种方式
成员变量 Field getField(…) / getFields() set(…) / get(…)
成员方法 Method getMethod(…) / getMethods() invoke()
构造方法 Constructor getConstructor(…) / getConstructors() newInstance()

1、获取.class字节码文件(Class类的对象)

三种方式:

  1. java.lang.Object类的成员方法

    public Class<?> getClass():获取Class类型的对象(获取.class字节码文件)

  2. 使用对象的class属性

    任意类型都有一个隐藏的class属性:获取Class类型的对象

  3. java.lang.Class类的静态方法(建议使用

    public static Class<?> forName(String className)

    Class类获取类名的方式
    (1) public String getSimpleName():只获取类名
    (2)public String getName():获取包名和类名

2、获取构造方法

2.1 步骤:

  1. 获取Class类型的对象(建议使用Class类型静态forName方法的方式)

  2. 通过Class类型的对象获取构造方法对象

    java.lang.Class类 成员方法
    (1)public Constructor[] getConstructores()

    a. 只能获取public修饰的所有构造方法。
    b. 每个构造方法被封装成一个Constuctor类型的对象,被存储在数组中

     注意:Constructor类,专门用来描述构造方法
    

    (2)Constructor getConstructor(Class... parameterTypes)

    a. 根据参数类型获取构造方法对象,只能获得public修饰的构造方法。
    b. 如果不存在对应的构造方法,则会抛出 java.lang.NoSuchMethodException异常。

     注意:
     1. 参数是可变参数,调用此方法时,可以不写参数,获取的空参构造
     2. 可以写参数,给定的参数必须是Class对象
    
     比如:
     参数 String name,int age
     调用此方法: String.class,int.class
    

2.2 执行构造方法

java.lang.reflect.Constructor类的成员方法

public T newInstance(Object ... params)

参数:

  1. Object...params:可变参数,可以传递数组,参数列表,不传递(执行的就是空参构造)
  2. 参数作用:对象中的成员变量,所需要的的具体数据,不传递使用默认值

返回值:

  1. Object类型:被创建出来的对象,提升为Object类型
  2. 对比:假设已经获取了满参构造方法con,
    使用反射之前:Person p = new Person(“张三”,87);
    使用反射之后: Person p = (Person)con.newInstance(“张三”,87)

3、获取成员方法

2.1 步骤:

  1. 获取Class类型的对象(建议使用Class类型静态forName方法的方式)

  2. 通过Class类型的对象获取构造方法对象

    java.lang.Class类 成员方法
    (1)public Method[] getMethods()

    a. 只能获取public修饰的所有构造方法。
    b. 每个构造方法被封装成一个Method类型的对象,被存储在数组中

     注意:Method类,专门用来描述成员方法
    

    (2)Method getMethod​(String name, 方法的参数类型... 类型.class)

    a. 根据参数类型获取成员方法对象,只能获得public修饰的构造方法。
    b. 如果不存在对应的成员方法(参数类型传递不对),则会抛出 java.lang.NoSuchMethodException异常。

     注意:
     1. 参数是可变参数,调用此方法时,可以不写参数,获取的空参构造
     2. 可以写参数,给定的参数必须是Class对象
    
     比如:
     参数 String name,int age
     调用此方法: String.class,int.class
    

2.2 执行成员方法

java.lang.reflect.Method类的成员方法

public Object invoke​(Object obj, Object... args)

参数:

  1. Object:传递指定的对象(通过构造方法创建的对象)。如果obj=null,则表示该方法是静态方法
  2. Object... args:可变参数,传递方法的参数。如果参数传递不对会出现java.lang.IllegalArgumentException: wrong number of arguments异常。

返回值:
基本类型会被包装为对应的包装类型,如果是数组类型需要转型,基本类型不会被包装为Object[]类型。

public class Animal {
    public void eat(){
        System.out.println("吃东西");
    }
    public String[] num(int i){
        return new String[]{"1","2"};
    }
}
/*
反射案例
需求:写一个"框架",不能改变该类的任何代码的前提下,可以帮我们创建任意类的对象,并且执行其中任意方法

实现:
1. 创建配置文件
2. 反射

步骤:
1.创建一个配置文件(config.properties),存储键值对
2.读取配置文件,获取String类型的类名和方法名称
3.获取Class类型的对象
4.获取构造方法
5.获取成员方法
 */
public class Demo {
    public static void main(String[] args) throws Exception {
        //1.创建一个配置文件(config.properties),存储键值对
        Properties prop = new Properties();
        //2.读取配置文件,获取String类型的类名和方法名称
        Reader reader = new FileReader("config.properties");
        prop.load(reader);
        String className = prop.getProperty("ClassName");
        //3.获取Class类型的对象
        Class<?> c = Class.forName(className);
        //4.获取构造方法
        Constructor<?> con = c.getConstructor();
        Animal animal = (Animal) con.newInstance();
        //5.获取成员方法
        String methodName = prop.getProperty("MethodName");
        Method method = c.getMethod(methodName,int.class);
        Object invoke = method.invoke(animal,0);
        System.out.println(Arrays.toString((Object[]) invoke));
    }
}

config.properties配置文件

# Animal的完整类名(包名 + 类名)
ClassName=com.domain.Animal
# 调用的成员函数名称
MethodName=num

4、总结——反射示意图

一个类可以好比是一个人体,有心肝肺。
在这里插入图片描述

类加载器

类加载器是负责加载类的对象。将.class文件(硬盘)加载到内存生成Class对象。

1、类加载器的组成

  • BootstrapClassLoader 根类加载器也被称为引导类加载器,负责Java核心类的加载。比如System,String等。

  • ExtClassLoader 扩展类加载器负责JRE的扩展目录中jar包的加载。在JDK中JRE的lib目录下ext目录

  • AppClassLoader 系统类加载器负责在JVM启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar包和类路径。

2、继承关系

在这里插入图片描述

3、类加载器的获取和调用机制

使用 类.class.getClassLoader() 获得加载自己的类加载器

  • 类加载器加载机制:全盘负责委托机制
  1. 全盘负责:A类如果要使用B类(不存在),A类加载器必须负责加载B类。
  2. 委托机制:A类加载器如果要加载资源B,必须询问父类加载是否加载。 如果加载,将直接使用。 如果没有
    机制,自己再加载。

采用全盘负责委托机制保证一个class文件只会被加载一次,形成一个Class对象

猜你喜欢

转载自blog.csdn.net/weixin_38708854/article/details/106871314
今日推荐