故事:昨天一位朋友在面试中被问到:如果我自定义个String类行不行?
你是否知道可不可以?如果回答说不可以,那面试官可能会问为什么不可以?
下面我给你整个案例试试看:
代码语言:javascript复制package java.lang;
/**
* {@code @description:} TODO
*
* @author tianwc 公众号:Java后端技术全栈
* 在线刷题 1200 java面试题和1000 篇技术文章:
* <a href="https://woaijava.cc/">博客地址</a>
* {@code @date:} 2024-07-02 21:27
* {@code @version:} 1.0
*/
public class String {
public static void main(String[] args) {
System.out.println("Hello World!");
}
}
没有什么,就单纯的定义一个java.lang.String 类。
会报错吗?可以说会,也可以说不会。
先说为什么不会报错。
从语法的角度以及编译,都不会报错。
为什么会报错?
我们运行这个main方法,确实报错了。
代码语言:javascript复制错误: 在类 java.lang.String 中找不到 main 方法, 请将 main 方法定义为:
public static void main(String[] args)
否则 JavaFX 应用程序类必须扩展javafx.application.Application
错误信息里面提到在类 java.lang.String 中找不到 main 方法,这不是有毛病吧,我这String类里明细有main方法。
这里我们呢就不得不聊聊类加载,类加载过程是加载、链接(验证、准备、解析)以及初始化。
类加载过程中有个加载,也可以说成是装载,说白了就是讲字节码放到JVM中的过程。
我们在实际开发中,会用到大量的类,还加上JDK自带了很多类,如果想上面这类场景:同时有两个String类,那请问JVM需要先装哪个?如果两个都装载了,那在用的时候,到底是用哪个?
所以,就有类加载器,每个类加载器自己都有对应的加载目录。
我们都知道类加载器有下面几种:
Bootstrap ClassLoader主要负责加载 <JAVA_HOME>/jre/lib 目录下的核心Java类,如java.lang等,我们前面说的到String类就是有这个类加载器来加载。
Extension ClassLoader主要负责加载 <JAVA_HOME>/jre/lib/ext 目录下的类文件,以及通过系统变量java.ext.dirs指定的其他目录中的类文件。
Application ClassLoader负责加载应用程序类路径(classpath)下的类文件,通常是通过-cp或-classpath指定的目录或JAR包。
自定义类加载器可以根据开发人员的实际需求加载不同来源的类文件,例如从网络、数据库等载入类。
那上面为什么会报错找不到main方法呢?
这里就得聊聊委派机制,在JVM中有个双亲委派模型。
就是一个类加载器准备加载一个类时,先看看是否加载过,没有加载就交给父类优先去加载,再加上 Bootstrap ClassLoader 是顶层类加载器,并且这个加载器是负责加载 <JAVA_HOME>/jre/lib 目录下的核心Java类,刚好在这个lib目录下有个rt.jar,同时,在这个jar包里也有个java.lang.String,所以,就优先加载了rt.jar包中String类了,这个类里确实也没有main方法,所以就报错了呗。
到此,我们就搞清楚了为什么没有main的错误信息的原因了。