JDK_Class

我最后的祝福是要给那些人——他们知道我不完美却还爱着我 ——《流萤集》

关于Class 你想知道的我这里都有😊

  • ☕️ JDK Class类分析
  • 📃 class文件构成分析
  • 🔧 字节码工具介绍
  • ➕ 类加载

JDK中的Class类

https://docs.oracle.com/javase/8/docs/api/java/lang/Class.html

先来看看JDK中对Class类的介绍

Instances of the class Class represent classes and interfaces in a running Java application. An enum is a kind of class and an annotation is a kind of interface. Every array also belongs to a class that is reflected as a Class object that is shared by all arrays with the same element type and number of dimensions. The primitive Java types (boolean, byte, char, short, int, long, float, and double), and the keyword void are also represented as Classobjects.

Class has no public constructor. Instead Class objects are constructed automatically by the Java Virtual Machine as classes are loaded and by calls to the defineClass method in the class loader.

类的实例表示运行中的Java应用程序中的类和接口。枚举是一种类,注解是一种接口。每个数组也属于一个类,这个类反映为一个类对象,由具有相同元素类型和维数的所有数组共享。原始Java类型(boolean、byte、char、short、int、long、float和double)和关键字void也表示为类对象。

类没有公共构造函数。相反,类对象是在类装入时由Java虚拟机自动构造的,并通过调用类装入器中的defineClass方法构造的。

类的成员 包含 (Declared前缀可以获得公开的类)

  • getFields 字段
  • getMethods 方法
  • getConstructors 构造函数
  • getClasses 内部类
  • getAnnotations 注解
  • getSuperclass 父类
  • getInterfaces 接口

更多细节可以查看 JLS 第8章

getFields 分析

//
//
// java.lang.reflect.Field handling
//
//

// Returns an array of "root" fields. These Field objects must NOT
// be propagated to the outside world, but must instead be copied
// via ReflectionFactory.copyField.
private Field[] privateGetDeclaredFields(boolean publicOnly) {
    checkInitted();
    Field[] res;
    ReflectionData<T> rd = reflectionData();
    if (rd != null) {
        res = publicOnly ? rd.declaredPublicFields : rd.declaredFields;
        if (res != null) return res;
    }
    // No cached value available; request value from VM
    res = Reflection.filterFields(this, getDeclaredFields0(publicOnly));
    if (rd != null) {
        if (publicOnly) {
            rd.declaredPublicFields = res;
        } else {
            rd.declaredFields = res;
        }
    }
    return res;
}

从缓存中获取, 如果为空则从VM获取 , 其中 reflectionData 反射缓存Bo (useCaches 默认为true)

private volatile transient SoftReference<ReflectionData<T>> reflectionData;

// Incremented by the VM on each call to JVM TI RedefineClasses()
// that redefines this class or a superclass.
private volatile transient int classRedefinedCount = 0;

// Lazily create and cache ReflectionData
private ReflectionData<T> reflectionData() {
    SoftReference<ReflectionData<T>> reflectionData = this.reflectionData;
    int classRedefinedCount = this.classRedefinedCount;
    ReflectionData<T> rd;
    if (useCaches &&
        reflectionData != null &&
        (rd = reflectionData.get()) != null &&
        rd.redefinedCount == classRedefinedCount) {
        return rd;
    }
    // else no SoftReference or cleared SoftReference or stale ReflectionData
    // -> create and replace new instance
    return newReflectionData(reflectionData, classRedefinedCount);
}
private ReflectionData<T> newReflectionData(SoftReference<ReflectionData<T>> oldReflectionData,int classRedefinedCount) {
        if (!useCaches) return null;
        while (true) {
            ReflectionData<T> rd = new ReflectionData<>(classRedefinedCount);
            // try to CAS it...
            if (Atomic.casReflectionData(this, oldReflectionData, new SoftReference<>(rd))) {
                return rd;
            }
            // else retry
            oldReflectionData = this.reflectionData;
            classRedefinedCount = this.classRedefinedCount;
            if (oldReflectionData != null &&
                (rd = oldReflectionData.get()) != null &&
                rd.redefinedCount == classRedefinedCount) {
                return rd;
            }
        }
    }

简单总结 Class的成员 字段 方法 和构造函数 通常与反射一起使用

Class文件的构成

通过javac xxx 可以生成 xxx.class 文件

字节码结构

类型 名称 说明 长度
u4 magic 魔数,识别Class文件格式 4个字节
u2 minor_version 副版本号 2个字节
u2 major_version 主版本号 2个字节
u2 constant_pool_count 常量池计算器 2个字节
cp_info constant_pool 常量池 n个字节
u2 access_flags 访问标志 2个字节
u2 this_class 类索引 2个字节
u2 super_class 父类索引 2个字节
u2 interfaces_count 接口计数器 2个字节
u2 interfaces 接口索引集合 2个字节
u2 fields_count 字段个数 2个字节
field_info fields 字段集合 n个字节
u2 methods_count 方法计数器 2个字节
method_info methods 方法集合 n个字节
u2 attributes_count 附加属性计数器 2个字节
attribute_info attributes 附加属性集合 n个字节

这是一张Java字节码总的结构表,我们按照上面的顺序逐一进行解读就可以了。

首先,我们来说明一下:class文件只有两种数据类型:无符号数。如下表所示:

数据类型 定义 说明
无符号数 无符号数可以用来描述数字、索引引用、数量值或按照utf-8编码构成的字符串值。 其中无符号数属于基本的数据类型。以u1、u2、u4、u8来分别代表1个字节、2个字节、4个字节和8个字节
表是由多个无符号数或其他表构成的复合数据结构。 所有的表都以“_info”结尾。 由于表没有固定长度,所以通常会在其前面加上个数说明。

实际上整个class文件就是一张表,其结构就是上面的表一了。

更多细节参考https://www.jianshu.com/p/2c106b682cfb

操纵字节码

Javassist 、 ASM 、 cglib

更多参考 https://blog.csdn.net/Mary881225/article/details/64132547

类加载

Class 是什么时候加载进来的

类加载的时机
  • JVM启动的时候,会使用 ClassLoader 加载 java自身相关的类
  • 自定义类使用的时候加载
类装载的过程

加载 、链接(验证、准备、解析)、初始化

Jvm把class文件字节码加载到内存中,并将这些静态数据装换成运行时数据区中方法区的类型数据,在运行时数据区堆中生成一个代表这个类

  • 类加载:Jvm把class文件字节码加载到内存中,并将这些静态数据装换成运行时数据区中方法区的类型数据,在运行时数据区堆中生成一个代表这个类的java.lang.Class对象,作为方法区类数据的访问入口。
  • 链接:执行下面的校验、准备和解析步骤,其中解析步骤是可选的。
  • 初始化:执行类变量赋值和静态代码块。

你知道Class.forName 和 ClassLoder.loadClass的区别吗?

class MyClass {
    static {//静态块  
        System.err.println("static block run");
    }
    static String init(){
        return "init";
    }
}

/**
 * 静态代码块的执行是处在类加载的最后一个阶段“初始化”
 *
 * 加载 、验证、准备、解析、初始化
 */
public class StaticInitDemo {
    public static void main(String[] args) {

       
        System.out.println("run MyClass.class");
        Class c = MyClass.class;

        System.out.println("run ClassLoader.loadClass");
        try {
          Thread.currentThread().getContextClassLoader().loadClass("com.practical.MyClass");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        System.out.println("run Class forName");
        try {
            Class.forName("com.practical.MyClass");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

上述代码运行结果为

run MyClass.class
run ClassLoader.loadClass
run Class forName
static block run
Class.forName
@CallerSensitive
public static Class<?> forName(String className)
            throws ClassNotFoundException {
    Class<?> caller = Reflection.getCallerClass();
    return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
}
ClassLoader
public Class<?> loadClass(String name) throws ClassNotFoundException {
    return loadClass(name, false);
}
 protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // First, check if the class has already been loaded
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    if (parent != null) {
                        c = parent.loadClass(name, false);
                    } else {
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }

                if (c == null) {
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    long t1 = System.nanoTime();
                    c = findClass(name);

                    // this is the defining class loader; record the stats
                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }

结论:

Class.forName得到的class是已经初始化完成的,该方法适合那些在类加载时需要初始化一些东西的情况。比如,加载数据库驱动。

Classloder.loaderClass得到的class是还没有链接的,该方法适合在类加载时不需要一些初始化的情况。

ClassNotFoundException and NoClassDefFoundError(LinkageError)

ClassNotFoundException 发生在类加载时候

NoclassDefFoundError 发生在在链接时候


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 951488791@qq.com

文章标题:JDK_Class

字数:1.9k

本文作者:zhengyumin

发布时间:2019-08-31, 19:38:34

最后更新:2020-01-12, 23:08:44

原始链接:http://zyumin.github.io/2019/08/31/JDK-Class/

版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。