我最后的祝福是要给那些人——他们知道我不完美却还爱着我 ——《流萤集》
关于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 aClass
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
, anddouble
), and the keywordvoid
are also represented asClass
objects.
Class
has no public constructor. InsteadClass
objects are constructed automatically by the Java Virtual Machine as classes are loaded and by calls to thedefineClass
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