对类的加载过程以及java.lang.Class的理解

Java程序经过javac编译后生成字节码文件,通过java.exe命令解释运行。类加载过程包括加载、链接(验证、准备、解析)和初始化。Class对象对应运行时类,每个类在JVM中只有一个Class实例。获取Class对象有四种方式:通过类的class属性、实例的getClass()方法、forName()静态方法和ClassLoader的loadClass()方法。

程序经过javac.exe命令以后(编译),会生成一个或多个字节码文件(.class结尾),接着我们使用java.exe命令,对某个字节码文件进行解释运行。相当于将某个字节码文件所对应的类加载到内存中。此过程就称为类的加载过程·(是运行的过程,不包括编译),加载到内存中的类我们就称为运行时类,此运行时类就作为Class的实例。(类本身也是对象)


1. 类的过程

类的加载(Load)→  类的链接(Link)→  类的初始化(Initialize)

  • 类的加载:将类的.class文件读入内存,并为之创建一个java.lang.Class对象,此过程由类的加载器完成
  • 类的链接:将类的二进制数据合并到JRE中(JVM的运行状态之中)的过程
  • 验证:确保加载的类信息符合JVM规范,例如:以cafe开头、没有安全方面的问题
  • 准备:正式为类变量(static)分配内存并设置类变量默认初始值的阶段,这些内存都将在方法区中进行分配
  • 解析:虚拟机常量池内的符号引用(常量名)替换为直接引用(地址)的过程
  • 类的初始化:JVM负责对类进行初始化
    • 执行类构造器<clinit>()方法的过程,类构造器<clinit>()方法是由编译期自动收集类中所有类变量的赋值动作和静态代码块中的语句合并产生的。(类构造器是构造类信息的,不是构造该类对象的构造器)
    • 当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先触发其父类的初始化。
    • 虚拟机会保证一个类的<clinit>()方法在多线程环境中被正确加锁和同步,以保证只执行一次。

2. java.lang.Class

  • Class的实例就对应着一个运行时类,加载到内存中的运行时类,会缓存一定的时间,在此时间之内,可以通过不同的方式来获取此运行时类作为Class的实例
  • 对于每个类而言,JRE都为其保留一个不变的Class类型的对象。一个Class对象包含了特定某个结构(class/interface/enum/annotation/primitive type/void/[])的有关信息。
  • Class本身也是一个类
  • Class对象只能由系统建立对象
  • 一个加载的类在JVM中只会有一个Class实例
  • 一个Class对象对应的是一个加载到JVM中的一个.class文件
  • 每个类的实例都会记得自己是由哪个 Class 实例所生成
  • 通过Class可以完整地得到一个类中的所有被加载的结构
  • Class类是Reflection的根源,针对任何想动态加载、运行的类,唯有先获得相应的Class对象

3.  Class类的常用方法

4. 获取Class类的实例(四种方法)

①.前提:若已知具体的类,通过类的class属性获取,该方法最为安全可靠,程序性能最高
实例:Class clazz = String.class;
②.前提:已知某个类的实例,调用该实例的getClass()方法获取Class对象
实例:Class clazz = “www.atguigu.com”.getClass();
③.前提:已知一个类的全类名,且该类在类路径下,可通过Class类的静态方法forName()获取,可能抛出 ClassNotFoundException
实例:Class clazz = Class.forName(“java.lang.String”);
④.其他方式(不做要求)
ClassLoader cl = this.getClass().getClassLoader();
Class clazz4 = cl.loadClass(“类的全类名”);

<think>我们正在处理的是ProGuard配置转换的问题。用户希望将给定的ProGuard规则转换为proguard-maven-plugin中的option配置。 给定的规则是: -keepclassmembernames class * { java.lang.Class class$(java.lang.String); java.lang.Class class$(java.lang.String, boolean); } 在ProGuard中,`-keepclassmembernames`选项用于保留指定成员(字段和方法)的名称,但允许移除这些成员(如果未被使用)并混淆名(除非也被保留)。不过,这里指定的是方法,所以会保留这两个方法的名字。 在proguard-maven-plugin中,我们使用`<options>`标签来配置ProGuard规则,每个选项通常作为一个单独的`<option>`元素。 具体转换: - 规则中使用了通配符`*`表示所有。 - 我们要保留的是中名为`class$`的两个方法,一个接受一个String参数,另一个接受一个String和一个boolean参数。 在ProGuard配置中,方法签名的写法是:`<returnType> <methodName>(<argType1>,<argType2>,...)`。 因此,我们需要在`<options>`中写入对应的规则。注意,在XML中配置时,需要将规则放在`<option>`标签内。 转换后的规则应该是: <option>-keepclassmembernames class * { java.lang.Class class$(java.lang.String); java.lang.Class class$(java.lang.String, boolean); }</option> 但是,注意在XML中,特殊字符可能需要转义,不过这里没有特殊字符(如<,>,&等),所以可以直接写入。 另外,proguard-maven-plugin允许在配置文件中直接使用ProGuard的配置语法,所以我们可以直接将规则放入一个`<option>`中。 然而,由于规则中有花括号,并且是多行,我们可以在XML中这样写(注意保持格式正确,或者将整个规则写在一行,但为了可读性,可以保留多行,但需要确保XML标签的完整性): 通常,我们可以这样写: <options> <option>-keepclassmembernames class * { java.lang.Class class$(java.lang.String); java.lang.Class class$(java.lang.String, boolean); }</option> </options> 或者为了可读性,我们可以将整个规则写在一个`<option>`标签内,但将多行合并为一行(因为XML中多行文本在标签内是允许的,但是ProGuard配置本身不要求换行): 但是,由于ProGuard配置中换行和空格不影响,我们可以选择写在一行。 不过,如果用户希望保留原有的格式,也可以这样写(但注意在XML中,换行和缩进会被保留,但ProGuard会忽略多余的空格): <options> <option>-keepclassmembernames class * { java.lang.Class class$(java.lang.String); java.lang.Class class$(java.lang.String, boolean); }</option> </options> 但是,在XML标签中,换行和空格都是允许的,所以这样写是没问题的。 因此,我们可以在proguard-maven-plugin的配置中添加如下选项: 在pom.xml的插件配置部分: <plugin> <groupId>com.github.wvengen</groupId> <artifactId>proguard-maven-plugin</artifactId> <version>...</version> <executions>...</executions> <configuration> <options> ... 其他选项 ... <option>-keepclassmembernames class * { java.lang.Class class$(java.lang.String); java.lang.Class class$(java.lang.String, boolean); }</option> </options> </configuration> </plugin> 注意:如果配置中出现问题,可以尝试将整个规则写在一行,避免换行带来的潜在问题(例如,某些解析器可能对换行敏感,但ProGuard通常不会)。但是,根据proguard-maven-plugin的文档,它应该能够处理多行配置。 另外,需要注意的是,`-keepclassmembernames`规则会保留这些方法的名字,但允许删除这些方法(如果它们没有被使用)。如果用户希望保留这些方法不被删除,应该使用`-keepclassmembers`(不带names)或者同时使用其他规则。 但是,用户给出的规则就是`-keepclassmembernames`,所以我们按照原样转换。 因此,转换后的结果就是上述的配置。 </think>要将 `-keepclassmembernames` 规则转换为 `proguard-maven-plugin` 的配置格式,需在 `<options>` 标签内添加等效配置。具体转换如下: ### 转换后的配置 ```xml <plugin> <groupId>com.github.wvengen</groupId> <artifactId>proguard-maven-plugin</artifactId> <configuration> <options> <!-- 保留指定方法的原始名称 --> <option>-keepclassmembernames class * { java.lang.Class class$(java.lang.String); java.lang.Class class$(java.lang.String, boolean); }</option> </options> </configuration> </plugin> ``` ### 关键说明: 1. **格式直接转换** ProGuard 原生规则可直接嵌套在 `<option>` 标签内,无需语法修改。`class *` 通配符表示对所有生效。 2. **作用解析** 该规则确保以下方法名不被混淆: - `class$(java.lang.String)` - `class$(java.lang.String, boolean)` 用于避免 JVM 动态加载时的 `ClassCastException` 问题[^2][^3]。 3. **注意事项** - 若规则需与其他配置合并,按顺序添加多个 `<option>` 即可。 - 保留方法名前需确认其必要性,避免过度保留影响混淆效果。 ### 对比原始规则 | 原始 ProGuard 规则 | `proguard-maven-plugin` 等效配置 | |-------------------|--------------------------------| | `-keepclassmembernames class * { ... }` | `<option>-keepclassmembernames class * { ... }</option>` | 此转换直接保留原始语义,确保目标方法的名称在混淆过程中不被修改。 --- ### 相关问题 1. 如何在 `proguard-maven-plugin` 中排除特定包不被混淆? 2. ProGuard 规则 `-keepclassmembers` 和 `-keepclassmembernames` 有何区别? 3. 混淆后出现 `ClassCastException` 应如何诊断修复[^2][^3]? 4. 多模块 Maven 项目中如何统一管理 ProGuard 配置? [^1]: 关于 JDK 路径配置的引用 [^2]: `ClassCastException` 型转换错误分析 [^3]: 字符串与整型转换异常的解决方案 [^4]: 版本兼容性导致的加载失败问题
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值