Java注解与反射系列——Class类

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第7天

Java注解与反射系列——Class类

Class类

对象照镜子后可以得到的信息:某个类的属性、方法和构造器、某个类到底实现了哪些接口。对于每个类而言,JRE 都为具保首一个不变的Class类型的对象,一个Class对象包含了特定某个结构(class/interface/enum/annotation/primitive type/void/[)的有关信息

  • Class本身也是一个类
  • Class对象只能由系统建立对象
  • 一个加载的类在JVM中只会有一个Class实例
  • 一个Class对象对应的是一个加载到JVM中的一个.class文件》
  • 每个类的实例都会记得自己是由哪个Class 实例所生成
  • 通过Class可以完整地得到一个类中的所有被加载的结构
  • Class类是Reflection的根源,针对任何你想动态加载、运行的类,唯有先获得相应的Class对象

常用方法

方法 说明
static ClassforName(String name) 返回指定类名name的Class对象
Object newInstance() 调用缺省构造函数,返回Class对象的一个实例
getName() 返回此Class对象所表示的实体(类,接口,数组类或void)的名称。
Class getSuperClass() 返回当前Class对象的父类的Class对象
Class[] getinterfaces() 获取当前Class对象的接口
ClassLoader getClassLoader() 返回该类的类加载器
Constructor[]getConstructors() 返回一个包含某些Constructor对象的数组
Method getMothed(String name,Class.. T) 返回一个Method对象,此对象的形参类型为paramType
Field[]getDeclaredFields() 返回Field对象的一个数组

哪些类型有Class对象?

  1. class
  2. interface
  3. array
  4. enum
  5. annotation
  6. primitive type
  7. void

这里大家应该都认识吧

类的加载过程

  1. 类的加载
  2. 类的链接
  3. 类的初始化

类的加载

将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构然后生成一个代表这个类的java.lang.Class对象

链接

将Java类的二进制代码合并到JVM的运行状态之中的过程。 步骤如下

  • 验证:确保加载的类信息符合JVM规范,没有安全方面的问题
  • 准备:正式为类变量(static)分配内存并设置类变量默认初始值的阶段,这些内存都将在方法区中进行分配。
  • 解析:虚拟机常量池内的符号引用(常量名)替换为直接引用(地址)的过程。

初始化

执行类构造器< clinit >()方法的过程。类构造器< clinit >()方法是由编译期自动收集类中所有类变量的赋值动作和静态代码块中的语句合并产生的。(类构造器是构造类信息的,不是构造该类对象的构造器)。

当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先触发其父类的初始化。虚拟机会保证一个类的< clinit >()方法在多线程环境中被正确加锁和同步。

什么时候会发送类的初始化

类的主动引用

一定会发生类的初始化

  1. 当虚拟机启动,先初始化main方法所在的类
  2. new一个类的对象
  3. 调用类的静态成员(除了final常量)和静态方法
  4. 使用java.lang.reflect包的方法对类进行反射调用
  5. 当初始化一个类,如果其父类没有被初始化,则先会初始化它的父类

类的被动引用

不会发生类的初始化

  1. 当访问一个静态域时,只有真正声明这个域的类才会被初始化。如:当通过子类引用父类的静态变量,不会导致子类初始化
  2. 通过数组定义类引用,不会触发此类的初始化
  3. 引用常量不会触发此类的初始化(常量在链接阶段就存入调用类的常量池中了)

获取Class实例

package example.reflect;

import example.reflect.entity.Stu;

public class GetClassDemo {
    public static void main(String[] args) throws ClassNotFoundException {
        //最推荐使用
        Class<Stu> stuClass = Stu.class;
        //方法二
        Class stu = Class.forName("example.reflect.entity.Stu");
        //方法三
        Stu stu1 = new Stu();
        Class<? extends Stu> aClass = stu1.getClass();


        System.out.println(aClass);
        System.out.println(stuClass);
        System.out.println(stu);
    }
}

复制代码

结果

在这里插入图片描述

类加载器的作用

将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后在堆中生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口。

类加载器就是用来把class装载进内存的

类缓存

标准的JavaSE类加载器可以按要求查找类,但一旦某个类被加载到类加载器中,它将维持加载(缓存)一段时间。不过JVM垃圾回收机制可以回收这些Class对象

Java中的类加载器

  1. 引导类加载器:用C++编写的,是JVM自带的类加载器,负责Java平台核心库,用来装载核心类库。该加载器无法直接获取
  2. 扩展类加载器:负责jre/lib/ext目录下的jar包或-D java.ext.dirs指定目录下的jar包装入工作库
  3. 系统类加载器:负责java-classpath或-D java.class.path所指的目录下的类与jar包装入工作,是最常用的加载器

在这里插入图片描述

获取类的加载器

package example.reflect;

public class GetClassLoader {
    public static void main(String[] args) {
        //获取系统类的加载器
        ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
        System.out.println(systemClassLoader);

        //获取系统类的加载器的父类的加载器,即获取扩展类加载器
        ClassLoader parent = systemClassLoader.getParent();
        System.out.println(parent);
        
        //获取根加载器
        ClassLoader parent1 = parent.getParent();
        System.out.println(parent1);

    }
}

复制代码

在这里插入图片描述

用户自定义的类都是在AppClassLoader里的 系统即JDK内置类都是在系统类加载器中的

获取类中的信息

package example.reflect;

import example.reflect.entity.Stu;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;

public class GetClassInfo {
    public static void main(String[] args) throws NoSuchMethodException {
        Class<Stu> stuClass = Stu.class;

        ClassLoader classLoader = stuClass.getClassLoader();
        String name = stuClass.getName();
        String simpleName = stuClass.getSimpleName();
        Package aPackage = stuClass.getPackage();
        String packageName = stuClass.getPackageName();
        Field[] fields = stuClass.getFields();
        Field[] declaredFields = stuClass.getDeclaredFields();
        Method[] methods = stuClass.getMethods();
        //获取单个指定方法,需要指定名称+参数
        Method setAge = stuClass.getMethod("setAge", int.class);
        Constructor<?>[] declaredConstructors = stuClass.getDeclaredConstructors();

        HashMap<String, Object> map = new HashMap<>();
        map.put("类加载器", classLoader);
        map.put("类名", name);
        map.put("简单类名", simpleName);
        map.put("包", aPackage);
        map.put("包名", packageName);
        map.put("属性(仅public)", fields);
        map.put("属性(所有)", declaredFields);
        map.put("本类及父类所有方法",methods);
        map.put("setAge方法",setAge);

        map.forEach((x, y) -> {
            System.out.println(x + ":" + y);
        });

        for (Field field : fields) {
            System.out.println(field);
        }
        System.out.println("----------------------");
        for (Field declaredField : declaredFields) {
            System.out.println(declaredField);
        }
        System.out.println("======================");
        for (Method method : methods) {

            System.out.println(method);
        }
        System.out.println("-=-=-==-=-=-=-=-=-=--=-");
        for (Constructor<?> declaredConstructor : declaredConstructors) {
            System.out.println(declaredConstructor);
        }
    }
}

复制代码

在这里插入图片描述

猜你喜欢

转载自juejin.im/post/7104972083448250381