Java & JVM
Java是跨平台的语言,JVM是跨语言的平台。
- Java【write once,run anywhere】一次编译到处运行。由于Java经过前端编译器[Javac]生成的是字节码class文件,而这个class文件在不同平台的虚拟机都是可以运行的,这也就是Java语言称为是跨平台的语言的原因。
- JVM是跨语言的平台,与其说JVM是JVM,不如说是CVM(个人理解),即Class VM. 因为如今的JVM已经不再是专为Java而用的,JVM可以加载运行所有符合规范的class文件。
类加载ClassLoading
类加载图示
类加载子系统
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xuj6W3eQ-1636987563537)(images/image-20211022115649594.png)] 【JVM】之类加载子系统_java](https://s2.51cto.com/images/blog/202301/11152928_63be65589add420497.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=/resize,m_fixed,w_1184)
类加载过程
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-y4xYO1GT-1636987563539)(images/image-20211021233921436.png)] 【JVM】之类加载子系统_后端_02](https://s2.51cto.com/images/blog/202301/11152928_63be6558caf1691307.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=/resize,m_fixed,w_1184)
加载Loading
- 通过一个类的全限定名来获取定义此类的二进制字节流。[Java虚拟机规范没有规定某个Class文件必须从哪里获取,如何获取?]
- 从ZIP压缩文件中读取。
- 从网络中获取。
- 运行时生成。比如动态代理技术,在
java.lang.refrect.Proxy
中,就是用了ProxyGenerator.generateProxyClass()
来为特定的接口生成形式为*$Proxy
的代理类的二进制字节流。 - 由其他文件生成,如JSP。由JSP文件生成对应的Class文件。
- 从加密文件中获取,这是典型的防Class文件被反编译的保护措施,通过加载时解密Class文件来保障程序运行逻辑不被窥探。
- 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。
- 解释:方法区存储有class类元数据,运行时常量池,JIT编译代码,静态变量,方法。
- 在内存中生成一个代表这个类的
java.lang.Class
对象,作为方法区这个类的各种的访问入口。
注意: 加载结束后,Java虚拟机外部的二进制字节流就按照虚拟机所设定的格式存储在方法区中了。类型信息放置在方法区之后,会在Java堆内存中实例化一个java.lang.Class 类的对象,这个对象将作为程序访问方法区中的类型数据的外部接口。
下面我们基于加密文件来打破双亲委派
,并实现加密class文件的加载
- 第一步,将普通的java文件的对应的class文件生成,放在指定的目录下。[这里是java源码]
package com.example.demo.jvm;
public class EncryptUtils {
public static String encrypt(String str) {
return "加密:" + str;
}
}
- 第二步,使用加密算法生成加密后的class文件。[这里使用^异或运算。
N^M^M=N
]
File file = new File("C:\\data\\EncryptUtils.class");
FileInputStream inputStream = new FileInputStream(file);
FileOutputStream outputStream = new FileOutputStream("C:\\data\\EncryptUtils_encode.class");
int byt;
while ((byt = inputStream.read()) != -1){
byt ^= 10;//进行自定义的加密方式
outputStream.write(byt);
}
outputStream.close();
inputStream.close();
- 第三步,继承
ClassLoader
,实现loadClass
方法。特殊的类使用自定义加载器【打破双亲委派的关键在于重写loadClass方法】
// 自定义类的加载器: 打破双亲委派机制
public class YoungClassLoader extends ClassLoader {
private final String filePath;
public YoungClassLoader(String filePath) {
this.filePath = filePath;
}
// 重写loadClass()方法可以打破双亲委派机制。
// 重写findClass()方法不能打破双亲委派机制。
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
// 表明特殊的类使用自定义加载逻辑。
if ("com.example.demo.jvm.EncryptUtils".equals(name)) {
try {
InputStream inputStream = new FileInputStream(filePath);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
// byte[] buffer = new byte[1024];
int len = 0;
while ((len = inputStream.read()) != -1) {
byteArrayOutputStream.write(len ^ 10);
}
byte[] bytes = byteArrayOutputStream.toByteArray();
return defineClass(name, bytes, 0, bytes.length);
} catch (Exception e) {
e.printStackTrace();
}
}
return super.loadClass(name);
}
}
- 第四步:使用自定义加载器加载class文件,并使用反射技术调用加密方法
public class YoungEncryptUtils {
static Class<?> aClass = null;
static {
try {
YoungClassLoader youngClassLoader = new YoungClassLoader("C:\\data\\EncryptUtils_encode.class");
aClass = youngClassLoader.loadClass("com.example.demo.jvm.EncryptUtils");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public static String iEncrypt(String str){
try {
Method encryptMethod = aClass.getMethod("encrypt", String.class);
return (String) encryptMethod.invoke(null, str);
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
throw new RuntimeException("加密异常!");
}
public static void main(String[] args) throws ClassNotFoundException {
// com.example.demo.jvm.classloader.YoungClassLoader@6acbcfc0
System.out.println(aClass.getClassLoader());
// sun.misc.Launcher$AppClassLoader@18b4aac2
System.out.println(aClass.getClassLoader().getParent());
// 加密:Java
System.out.println(YoungEncryptUtils.iEncrypt("Java"));
}
}
链接Linking
验证Verification
验证是连接阶段的第一步,这一阶段的目的是确保Class文件的字节流中包含的信息符合《Java虚拟机规范》的全部约束要求,保证这些信息被当做代码运行后不会危害虚拟机自身的安全。
Java虚拟机如果不检查输入的字节流,对其完全信任的话,很可能会因为载入了有错误或者有恶意企图的字节码流而导致整个系统受到攻击甚至奔溃,所以验证字节码是Java虚拟机保护自身的一项必要措施。
验证阶段大致分为四个阶段:
- 文件格式验证。
- 例如:是否以魔数OXCAFEBABE开头。
- 主次版本号是否在当前虚拟机接受范围之内。
- 元数据验证。
- 字节码验证。
- 符号引用验证。
准备Preparation
准备阶段是正式为类中定义的变量(即静态变量,被static修饰的变量)分配内存并设置类变量初始值的阶段。从概念上讲,这些变量所使用的内存都应当在方法区中进行分配。但是:方法区本质上是一个逻辑区域。JDK7及之前,Hotspot虚拟机使用永久代来实现方法区,此时可以说类变量在方法区,但是JDK8及之后,类变量则会随着Class对象一起存放在Java堆中。
【此时类变量在方法区是错误的】,因为方法区此时是使用元空间(MetaSpace)来实现的。
注意:准备阶段进行内存分配的是类变量[static修饰的],而不包括实例变量,实例变量是随着对象实例化一起在堆中进行分配。
public static int value = 123;
上述这条语句在准备阶段是将赋默认值为0,而不是123;因为这时候未开始执行任何Java方法,而把value赋值为123的putstatic指令是程序被编译后,存放于类构造器<clinit>()
方法中,所以在类的初始化阶段,才会赋值为123;
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GlHdQuuK-1636987563541)(images/image-20211022092626948.png)] 【JVM】之类加载子系统_java_03](https://s2.51cto.com/images/blog/202301/11152929_63be65592010d52203.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=/resize,m_fixed,w_1184)
而当value被final修饰时,编译时Javac将会为value生成ConstantValue属性,在准备阶段虚拟机就会根据ConstantValue的设置将value赋值为123;
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AKrrEcx4-1636987563544)(images/image-20211022093058205.png)] 【JVM】之类加载子系统_后端_04](https://s2.51cto.com/images/blog/202301/11152929_63be65597061a45958.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=/resize,m_fixed,w_1184)
上述是未被final修饰的,下面是final修饰的。
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-f3Hp9jV7-1636987563549)(images/image-20211022093143759.png)] 【JVM】之类加载子系统_java_05](https://s2.51cto.com/images/blog/202301/11152929_63be6559a158f48143.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=/resize,m_fixed,w_1184)
解析Resolution
- 解析阶段是Java虚拟机将常量池内的符号引用替换为直接引用的过程。
- 符号引用以一组符号来描述所引用的目标,符号可以是任何形式的字面量,只要使用时能无歧义的定位到目标即可。
- 直接引用是可以直接指向目标的指针、相对偏移量或者是一个能间接定位到目标的句柄。
- 解析动作主要针对的是类或接口、字段、类方法、接口方法、方法类型等。对应常量池中的CONSTANT_Class_info、CONSTANT_Fieldref_info、CONSTANT_Methodref_info、CONSTANT_InterfaceMethodref_info、CONSTANT_MethodType_info等。
初始化阶段Initialization
- 初始化阶段就是执行类构造器
clinit<>()
方法的过程。 - 此方法不需要定义,是由Java编译器自动收集类中的所有类变量的赋值动作和静态代码块中
static{}
的语句合并而来。 - 类构造器方法指令按语句在源文件中出现的顺序执行。
- 若该类具有父类,JVM保证在子类的
clinit<>()
方法在执行前,父类的clinit<>()
方法已经执行完成。 - 虚拟机必须保证一个类的
clinit<>()
方法在多线程下被同步加锁。
static {
value = 12;
// System.out.println(value); // 异常
}
public static int value = 123;
上述代码最终value为123;可以向前赋值,但是不可以访问。即System.out.println(value);
编译异常。
public static int value = 123;
static {
value = 12;
// System.out.println(value); // 异常
}
上述代码最终value为12;此时System.out.println(value);
是正常的。
类加载器ClassLoader
结构图
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UjtiG3XA-1636987563550)(images/image-20211022103409333.png)] 【JVM】之类加载子系统_后端_06](https://s2.51cto.com/images/blog/202301/11152929_63be6559f03045600.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=/resize,m_fixed,w_1184)
引导类加载器
引导类加载器BootstrapClassLoader, 又称为根加载器。
- 这个类加载器是由c/c++实现的,嵌套在JVM中。
- 用来加载java的核心库。(JAVA_HOME/jre/lib/rt.jar、resource.jar或sun.boot.class.path路径下的内容),用于提供JVM自身需要的类。
- 并不继承自java.lang.ClassLoader,没有父加载器。
- 出于安全考虑,Bootstrap引导类加载器只加载名为java、javax、sun等开头的类。
- 启动类加载器无法被Java程序直接引用,用户在编写自定义类加载器时,如果需要把加载器请求委派给引导类加载器处理,那直接使用null代替即可。
扩展类加载器
扩展类加载器ExtClassLoader
- 由Java编写的,由sun.misc.Launcher$ExtClassLoader实现。
- 派生自ClassLoader类。
- 父加载器是引导类加载器。
- 从
java.ext.dirs
系统属性所指定的目录中加载类库,或从JDK的安装目录的jre/lib/ext子目录(扩展目录)下加载类库。如果用户创建的jar放在此目录下,也会自动由扩展类加载器加载。
系统类加载器
系统类加载器AppClassLoader,又称为应用类加载器
- Java语言编写,有sun.misc.Launcher$AppClassLoader实现。
- 派生自ClassLoader类。
- 父加载器是扩展类加载器。
- 它负责加载环境变量classpath或系统属性java.class.path指定路径下的类库。
- 该类加载是程序中默认的类加载器。一般来说,Java应用的类都是有它来完成加载。
- 通过ClassLoader#getSystemClassLoader()方法可以获取到该类加载器
System.getProperty("java.class.path");
// 输出
D:\Program Files\Java\jdk1.8.0_171\jre\lib\charsets.jar;
D:\Program Files\Java\jdk1.8.0_171\jre\lib\deploy.jar;
D:\Program Files\Java\jdk1.8.0_171\jre\lib\ext\access-bridge-64.jar;
D:\Program Files\Java\jdk1.8.0_171\jre\lib\ext\cldrdata.jar;
D:\Program Files\Java\jdk1.8.0_171\jre\lib\ext\dnsns.jar;
D:\Program Files\Java\jdk1.8.0_171\jre\lib\ext\jaccess.jar;
D:\Program Files\Java\jdk1.8.0_171\jre\lib\ext\jfxrt.jar;
D:\Program Files\Java\jdk1.8.0_171\jre\lib\ext\localedata.jar;
D:\Program Files\Java\jdk1.8.0_171\jre\lib\ext\nashorn.jar;
D:\Program Files\Java\jdk1.8.0_171\jre\lib\ext\sunec.jar;
D:\Program Files\Java\jdk1.8.0_171\jre\lib\ext\sunjce_provider.jar;
D:\Program Files\Java\jdk1.8.0_171\jre\lib\ext\sunmscapi.jar;
D:\Program Files\Java\jdk1.8.0_171\jre\lib\ext\sunpkcs11.jar;
D:\Program Files\Java\jdk1.8.0_171\jre\lib\ext\zipfs.jar;
D:\Program Files\Java\jdk1.8.0_171\jre\lib\javaws.jar;
D:\Program Files\Java\jdk1.8.0_171\jre\lib\jce.jar;
D:\Program Files\Java\jdk1.8.0_171\jre\lib\jfr.jar;
D:\Program Files\Java\jdk1.8.0_171\jre\lib\jfxswt.jar;
D:\Program Files\Java\jdk1.8.0_171\jre\lib\jsse.jar;
D:\Program Files\Java\jdk1.8.0_171\jre\lib\management-agent.jar;
D:\Program Files\Java\jdk1.8.0_171\jre\lib\plugin.jar;
D:\Program Files\Java\jdk1.8.0_171\jre\lib\resources.jar;
D:\Program Files\Java\jdk1.8.0_171\jre\lib\rt.jar;
D:\learning\code\spring-demo\target\classes;
D:\work\workSoftWare\mvnRespo\com\google\code\gson\gson\2.8.8\gson-2.8.8.jar;
D:\work\workSoftWare\mvnRespo\org\springframework\boot\spring-boot-starter-web\2.4.5\spring-boot-starter-web-2.4.5.jar;
D:\work\workSoftWare\mvnRespo\org\springframework\boot\spring-boot-starter-json\2.4.5\spring-boot-starter-json-2.4.5.jar;
D:\work\workSoftWare\mvnRespo\com\fasterxml\jackson\core\jackson-databind\2.11.4\jackson-databind-2.11.4.jar;
D:\work\workSoftWare\mvnRespo\com\fasterxml\jackson\core\jackson-annotations\2.11.4\jackson-annotations-2.11.4.jar;
D:\work\workSoftWare\mvnRespo\com\fasterxml\jackson\core\jackson-core\2.11.4\jackson-core-2.11.4.jar;
D:\work\workSoftWare\mvnRespo\com\fasterxml\jackson\datatype\jackson-datatype-jdk8\2.11.4\jackson-datatype-jdk8-2.11.4.jar;
D:\work\workSoftWare\mvnRespo\com\fasterxml\jackson\datatype\jackson-datatype-jsr310\2.11.4\jackson-datatype-jsr310-2.11.4.jar;
D:\work\workSoftWare\mvnRespo\com\fasterxml\jackson\module\jackson-module-parameter-names\2.11.4\jackson-module-parameter-names-2.11.4.jar;
D:\work\workSoftWare\mvnRespo\org\springframework\boot\spring-boot-starter-tomcat\2.4.5\spring-boot-starter-tomcat-2.4.5.jar;
D:\work\workSoftWare\mvnRespo\org\apache\tomcat\embed\tomcat-embed-core\9.0.45\tomcat-embed-core-9.0.45.jar;
D:\work\workSoftWare\mvnRespo\org\glassfish\jakarta.el\3.0.3\jakarta.el-3.0.3.jar;
D:\work\workSoftWare\mvnRespo\org\apache\tomcat\embed\tomcat-embed-websocket\9.0.45\tomcat-embed-websocket-9.0.45.jar;
D:\work\workSoftWare\mvnRespo\org\springframework\spring-web\5.3.6\spring-web-5.3.6.jar;
D:\work\workSoftWare\mvnRespo\org\springframework\spring-beans\5.3.6\spring-beans-5.3.6.jar;
D:\work\workSoftWare\mvnRespo\org\springframework\spring-webmvc\5.3.6\spring-webmvc-5.3.6.jar;
D:\work\workSoftWare\mvnRespo\org\springframework\spring-aop\5.3.6\spring-aop-5.3.6.jar;
D:\work\workSoftWare\mvnRespo\org\springframework\spring-context\5.3.6\spring-context-5.3.6.jar;
D:\work\workSoftWare\mvnRespo\org\springframework\spring-expression\5.3.6\spring-expression-5.3.6.jar;
D:\work\workSoftWare\mvnRespo\org\springframework\boot\spring-boot-starter\2.4.5\spring-boot-starter-2.4.5.jar;
D:\work\workSoftWare\mvnRespo\org\springframework\boot\spring-boot\2.4.5\spring-boot-2.4.5.jar;
D:\work\workSoftWare\mvnRespo\org\springframework\boot\spring-boot-autoconfigure\2.4.5\spring-boot-autoconfigure-2.4.5.jar;
D:\work\workSoftWare\mvnRespo\org\springframework\boot\spring-boot-starter-logging\2.4.5\spring-boot-starter-logging-2.4.5.jar;
D:\work\workSoftWare\mvnRespo\ch\qos\logback\logback-classic\1.2.3\logback-classic-1.2.3.jar;
D:\work\workSoftWare\mvnRespo\ch\qos\logback\logback-core\1.2.3\logback-core-1.2.3.jar;
D:\work\workSoftWare\mvnRespo\org\apache\logging\log4j\log4j-to-slf4j\2.13.3\log4j-to-slf4j-2.13.3.jar;
D:\work\workSoftWare\mvnRespo\org\apache\logging\log4j\log4j-api\2.13.3\log4j-api-2.13.3.jar;
D:\work\workSoftWare\mvnRespo\org\slf4j\jul-to-slf4j\1.7.30\jul-to-slf4j-1.7.30.jar;
D:\work\workSoftWare\mvnRespo\jakarta\annotation\jakarta.annotation-api\1.3.5\jakarta.annotation-api-1.3.5.jar;
D:\work\workSoftWare\mvnRespo\org\springframework\spring-core\5.3.6\spring-core-5.3.6.jar;
D:\work\workSoftWare\mvnRespo\org\springframework\spring-jcl\5.3.6\spring-jcl-5.3.6.jar;
D:\work\workSoftWare\mvnRespo\org\yaml\snakeyaml\1.27\snakeyaml-1.27.jar;
D:\work\workSoftWare\mvnRespo\org\slf4j\slf4j-api\1.7.30\slf4j-api-1.7.30.jar;
D:\work\workSoftWare\mvnRespo\org\apache\httpcomponents\httpclient\4.5.13\httpclient-4.5.13.jar;
D:\work\workSoftWare\mvnRespo\org\apache\httpcomponents\httpcore\4.4.14\httpcore-4.4.14.jar;
D:\work\workSoftWare\mvnRespo\commons-codec\commons-codec\1.15\commons-codec-1.15.jar;
D:\work\workSoftWare\mvnRespo\com\alibaba\fastjson\1.2.75\fastjson-1.2.75.jar;
D:\work\workSoftWare\mvnRespo\cn\hutool\hutool-setting\4.6.17\hutool-setting-4.6.17.jar;
D:\work\workSoftWare\mvnRespo\cn\hutool\hutool-core\4.6.17\hutool-core-4.6.17.jar;
D:\work\workSoftWare\mvnRespo\cn\hutool\hutool-log\4.6.17\hutool-log-4.6.17.jar;
D:\Program Files\JetBrains\IntelliJ IDEA 2020.3.2\lib\idea_rt.jar
自定义加载器
用户自定义加载器
在Java的日常应用程序开发中,类的加载几乎都是由上述3种类加载器相互配合执行的,在必要时,我们可以自定义加载器,来定制类的加载方式。
为什么要自定义加载器呢?
- 隔离加载类
- 修改类加载器的方式
- 扩展加载源
- 防止源码泄漏【上面有一个加密的例子】
获取 ClassLoader 的途径
- 方式二:获取当前线程上下文的 ClassLoader
Thread.currentThread().getContextClassLoader()
ClassLoader.getSystemClassLoader()
DriverManager.getCallerClassLoader()
双亲委派机制
双亲委派模型的工作过程:
- 如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到最顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个加载请求(它的搜索范围中没有找到所需的类)时,子加载器才会尝试自己去完成加载。
用户自定义类加载器实现步骤:
- 开发人员可以通过继承抽象类 java.lang.ClassLoader 类的方式,实现自己的类加载器,以满足一些特殊的需求
- 在 JDK1.2 之前,在自定义类加载器时,总会去继承 ClassLoader 类并重写 loadClass() 方法,从而实现自定义的类加载类,但是在 JDK1.2 之后已不再建议用户去覆盖 loadclass() 方法,而是建议把自定义的类加载逻辑写在 findClass()方法中
- 在编写自定义类加载器时,如果没有太过于复杂的需求,可以直接继承 URLClassLoader 类,这样就可以避免自己去编写 findClass() 方法及其获取字节码流的方式,使自定义类加载器编写更加简洁。
使用URLClassLoader
来实现自定义Class类加载。
public class ClassLoadingTest {
public static void main(String[] args) throws MalformedURLException, ClassNotFoundException {
File file = new File("D:\\learning\\code\\spring-demo\\target\\classes\\com\\ooyhao\\springdemo\\AllocationTest.class");
IClassLoader iClassLoader = new IClassLoader(new URL[]{file.toURL()});
Class<?> test = iClassLoader.loadClass("com.ooyhao.springdemo.AllocationTest");
System.out.println(test);
}
}
class IClassLoader extends URLClassLoader {
public IClassLoader(URL[] urls) {
super(urls);
}
}
双亲委派机制的优点
- 避免类的重复加载。【每一个类加载器都有自己的加载范围】
- 保护程序安全,防止核心API被恶意篡改。
- 自定义类:java.lang.String
- 自定义类:java.lang.YoungUtils
代码演示1
我们在classpath下创建java.lang.String
类,并运行main方法。
package java.lang;
public class String {
public static void main(String[] args) {}
}
错误: 在类 java.lang.String 中找不到 main 方法, 请将 main 方法定义为:
public static void main(String[] args)
否则 JavaFX 应用程序类必须扩展javafx.application.Application
通过上述代码运行以及报错信息,我们可以看到,无法在java.lang包中定义自己的String类。这是因为双亲委派机制,
- 当需要加载
java.lang.String
,系统类加载器会委派给扩展类加载器,而扩展类加载器会委派给引导类加载器。 - 引导类加载器可以在rt.jar中加载到JDK核心API中的
java.lang.String
类。 - 所以并没有加载我们自己写的String类,当执行main方法时,自然就会发现在String类中找不到main方法,因为在JDK核心API的
java.lang.String
类中没有main方法。
代码演示2
package java.lang;
public class YoungString {
public static void main(String[] args) {}
}
"D:\Program Files\Java\jdk1.8.0_171\bin\java.exe" -=354784387688100 "-javaagent:D:\Program Files\JetBrains\IntelliJ IDEA 2020.3.2\lib\idea_rt.jar=7083:D:\Program Files\JetBrains\IntelliJ IDEA 2020.3.2\bin" -Dfile.encoding=UTF-8 -classpath "D:\Program Files\Java\jdk1.8.0_171\jre\lib\charsets.jar;D:\Program Files\Java\jdk1.8.0_171\jre\lib\deploy.jar;D:\Program Files\Java\jdk1.8.0_171\jre\lib\ext\access-bridge-64.jar;D:\Program Files\Java\jdk1.8.0_171\jre\lib\ext\cldrdata.jar;D:\Program Files\Java\jdk1.8.0_171\jre\lib\ext\dnsns.jar;D:\Program Files\Java\jdk1.8.0_171\jre\lib\ext\jaccess.jar;D:\Program Files\Java\jdk1.8.0_171\jre\lib\ext\jfxrt.jar;D:\Program Files\Java\jdk1.8.0_171\jre\lib\ext\localedata.jar;D:\Program Files\Java\jdk1.8.0_171\jre\lib\ext\nashorn.jar;D:\Program Files\Java\jdk1.8.0_171\jre\lib\ext\sunec.jar;D:\Program Files\Java\jdk1.8.0_171\jre\lib\ext\sunjce_provider.jar;D:\Program Files\Java\jdk1.8.0_171\jre\lib\ext\sunmscapi.jar;D:\Program Files\Java\jdk1.8.0_171\jre\lib\ext\sunpkcs11.jar;D:\Program Files\Java\jdk1.8.0_171\jre\lib\ext\zipfs.jar;D:\Program Files\Java\jdk1.8.0_171\jre\lib\javaws.jar;D:\Program Files\Java\jdk1.8.0_171\jre\lib\jce.jar;D:\Program Files\Java\jdk1.8.0_171\jre\lib\jfr.jar;D:\Program Files\Java\jdk1.8.0_171\jre\lib\jfxswt.jar;D:\Program Files\Java\jdk1.8.0_171\jre\lib\jsse.jar;D:\Program Files\Java\jdk1.8.0_171\jre\lib\management-agent.jar;D:\Program Files\Java\jdk1.8.0_171\jre\lib\plugin.jar;D:\Program Files\Java\jdk1.8.0_171\jre\lib\resources.jar;D:\Program Files\Java\jdk1.8.0_171\jre\lib\rt.jar;D:\learning\code\spring-demo\target\classes;D:\work\workSoftWare\mvnRespo\com\google\code\gson\gson\2.8.8\gson-2.8.8.jar;D:\work\workSoftWare\mvnRespo\org\springframework\boot\spring-boot-starter-web\2.4.5\spring-boot-starter-web-2.4.5.jar;D:\work\workSoftWare\mvnRespo\org\springframework\boot\spring-boot-starter-json\2.4.5\spring-boot-starter-json-2.4.5.jar;D:\work\workSoftWare\mvnRespo\com\fasterxml\jackson\core\jackson-databind\2.11.4\jackson-databind-2.11.4.jar;D:\work\workSoftWare\mvnRespo\com\fasterxml\jackson\core\jackson-annotations\2.11.4\jackson-annotations-2.11.4.jar;D:\work\workSoftWare\mvnRespo\com\fasterxml\jackson\core\jackson-core\2.11.4\jackson-core-2.11.4.jar;D:\work\workSoftWare\mvnRespo\com\fasterxml\jackson\datatype\jackson-datatype-jdk8\2.11.4\jackson-datatype-jdk8-2.11.4.jar;D:\work\workSoftWare\mvnRespo\com\fasterxml\jackson\datatype\jackson-datatype-jsr310\2.11.4\jackson-datatype-jsr310-2.11.4.jar;D:\work\workSoftWare\mvnRespo\com\fasterxml\jackson\module\jackson-module-parameter-names\2.11.4\jackson-module-parameter-names-2.11.4.jar;D:\work\workSoftWare\mvnRespo\org\springframework\boot\spring-boot-starter-tomcat\2.4.5\spring-boot-starter-tomcat-2.4.5.jar;D:\work\workSoftWare\mvnRespo\org\apache\tomcat\embed\tomcat-embed-core\9.0.45\tomcat-embed-core-9.0.45.jar;D:\work\workSoftWare\mvnRespo\org\glassfish\jakarta.el\3.0.3\jakarta.el-3.0.3.jar;D:\work\workSoftWare\mvnRespo\org\apache\tomcat\embed\tomcat-embed-websocket\9.0.45\tomcat-embed-websocket-9.0.45.jar;D:\work\workSoftWare\mvnRespo\org\springframework\spring-web\5.3.6\spring-web-5.3.6.jar;D:\work\workSoftWare\mvnRespo\org\springframework\spring-beans\5.3.6\spring-beans-5.3.6.jar;D:\work\workSoftWare\mvnRespo\org\springframework\spring-webmvc\5.3.6\spring-webmvc-5.3.6.jar;D:\work\workSoftWare\mvnRespo\org\springframework\spring-aop\5.3.6\spring-aop-5.3.6.jar;D:\work\workSoftWare\mvnRespo\org\springframework\spring-context\5.3.6\spring-context-5.3.6.jar;D:\work\workSoftWare\mvnRespo\org\springframework\spring-expression\5.3.6\spring-expression-5.3.6.jar;D:\work\workSoftWare\mvnRespo\org\springframework\boot\spring-boot-starter\2.4.5\spring-boot-starter-2.4.5.jar;D:\work\workSoftWare\mvnRespo\org\springframework\boot\spring-boot\2.4.5\spring-boot-2.4.5.jar;D:\work\workSoftWare\mvnRespo\org\springframework\boot\spring-boot-autoconfigure\2.4.5\spring-boot-autoconfigure-2.4.5.jar;D:\work\workSoftWare\mvnRespo\org\springframework\boot\spring-boot-starter-logging\2.4.5\spring-boot-starter-logging-2.4.5.jar;D:\work\workSoftWare\mvnRespo\ch\qos\logback\logback-classic\1.2.3\logback-classic-1.2.3.jar;D:\work\workSoftWare\mvnRespo\ch\qos\logback\logback-core\1.2.3\logback-core-1.2.3.jar;D:\work\workSoftWare\mvnRespo\org\apache\logging\log4j\log4j-to-slf4j\2.13.3\log4j-to-slf4j-2.13.3.jar;D:\work\workSoftWare\mvnRespo\org\apache\logging\log4j\log4j-api\2.13.3\log4j-api-2.13.3.jar;D:\work\workSoftWare\mvnRespo\org\slf4j\jul-to-slf4j\1.7.30\jul-to-slf4j-1.7.30.jar;D:\work\workSoftWare\mvnRespo\jakarta\annotation\jakarta.annotation-api\1.3.5\jakarta.annotation-api-1.3.5.jar;D:\work\workSoftWare\mvnRespo\org\springframework\spring-core\5.3.6\spring-core-5.3.6.jar;D:\work\workSoftWare\mvnRespo\org\springframework\spring-jcl\5.3.6\spring-jcl-5.3.6.jar;D:\work\workSoftWare\mvnRespo\org\yaml\snakeyaml\1.27\snakeyaml-1.27.jar;D:\work\workSoftWare\mvnRespo\org\slf4j\slf4j-api\1.7.30\slf4j-api-1.7.30.jar;D:\work\workSoftWare\mvnRespo\org\apache\httpcomponents\httpclient\4.5.13\httpclient-4.5.13.jar;D:\work\workSoftWare\mvnRespo\org\apache\httpcomponents\httpcore\4.4.14\httpcore-4.4.14.jar;D:\work\workSoftWare\mvnRespo\commons-codec\commons-codec\1.15\commons-codec-1.15.jar;D:\work\workSoftWare\mvnRespo\com\alibaba\fastjson\1.2.75\fastjson-1.2.75.jar;D:\work\workSoftWare\mvnRespo\cn\hutool\hutool-setting\4.6.17\hutool-setting-4.6.17.jar;D:\work\workSoftWare\mvnRespo\cn\hutool\hutool-core\4.6.17\hutool-core-4.6.17.jar;D:\work\workSoftWare\mvnRespo\cn\hutool\hutool-log\4.6.17\hutool-log-4.6.17.jar" java.lang.YoungString
java.lang.SecurityException: Prohibited package name: java.lang
at java.lang.ClassLoader.preDefineClass(ClassLoader.java:662)
at java.lang.ClassLoader.defineClass(ClassLoader.java:761)
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
at .URLClassLoader.defineClass(URLClassLoader.java:467)
at .URLClassLoader.access$100(URLClassLoader.java:73)
at .URLClassLoader$1.run(URLClassLoader.java:368)
at .URLClassLoader$1.run(URLClassLoader.java:362)
at java.security.AccessController.doPrivileged(Native Method)
at .URLClassLoader.findClass(URLClassLoader.java:361)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
at sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:495)
Error: A JNI error has occurred, please check your installation and try again
Exception in thread "main"
报错信息如上,可以看到,即使我们在java.lang包中定义一个不与JDK核心API重名的类,也是不可以的。因为这个包是被禁止使用的。Prohibited package name: java.lang
其他
如何判断两个 class 对象是否相同
在 JVM 中表示两个 class 对象是否为同一个类存在两个必要条件:
- 类的完整类名必须一致,包括包名。
- 加载这个类的 ClassLoader(指 ClassLoader 实例对象)必须相同。
换句话说,在 JVM 中,即使这两个类对象(class 对象)来源同一个 Class 文件,被同一个虚拟机所加载,但只要加载它们的 ClassLoader 实例对象不同,那么这两个类对象也是不相等的。
对类加载器的引用
JVM 必须知道一个类型是由启动加载器加载的还是由用户类加载器加载的。如果一个类型是由用户类加载器加载的,那么 JVM 会将这个类加载器的一个引用作为类型信息的一部分保存在方法区中。当解析一个类型到另一个类型的引用的时候,JVM 需要保证这两个类型的类加载器是相同的。
类的主动使用和被动使用
Java 程序对类的使用方式分为:主动使用和被动使用。
主动使用,又分为七种情况:
- 创建类的实例
- 访问某个类或接口的静态变量,或者对该静态变量赋值
- 调用类的静态方法
- 反射(比如:Class.forName(“com.atguigu.Test”))
- 初始化一个类的子类
- Java 虚拟机启动时被标明为启动类的类
- JDK 7 开始提供的动态语言支持:
java.lang.invoke.MethodHandle 实例的解析结果
REF_getStatic、REF_putStatic、REF_invokeStatic 句柄对应的类没有初始化,则初始化
除了以上七种情况,其他使用 Java 类的方式都被看作是对类的被动使用,都不会导致类的初始化。