Java平台无关性是如何实现的?
- 一次加载,到处执行,其实并不是Java语言实现了跨平台的特性,而是在任何的平台上都有JVM虚拟机,Java通过javac在编译时将.java文件生成字节码并存入到.class文件中,有了class文件才有了跨平台的基础,这样JVM才能解析,将字节码生成特定平台上的机器码
- Java源码首先被编译成字节码,再有不同平台的JVM进行解析,Java语言在不同的平台上运行不需要进行重新编译,Java虚拟机在执行字节码时,把字节码转化成具体平台上的机器指令,所以所并不是Java语言支持跨平台性,而是在任何的平台上都有对应的JVM可以对其进行解析。
为什么JVM不直接将源码解析成机器码去执行?
理论上而言,完全可以直接解析源码,这样也可以跨平台,但是引入字节码有额外的好处
- 字节码更便于虚拟机读取,不用在解析字符串,所以运行速度比直接解析源代码快。
- 生成字节码的过程中,编译器可以预先作语法和安全性的检查,出错的机会更少。
- 字节码比源码更加紧凑,文件尺寸更小,方便网络传输
- 字节码不一定非要java源码生成其他一些语言也可以编译生成字节码。
Java是如何加载class文件?
- Class Loader:依据特定的格式(全限定类名),加载class文件到内存
- Runtime Data Area:JVM内存空间结构模型
- Execution Engine(执行引擎):对命令进行解析(解析class文件中的字节码)
- Native Interface(本地接口):融合不同开发语言的原生库为Java所用
- 编译器将 .java源文件编译成 .class字节码文件
- CLassLoader将字节码转化为JVM中的Class<>对象
- JVM通过Class<>对象,去实例化对象,操作类中的属性与方法
谈谈类加载器(ClassLoader)
- ClassLoader在Java中有着非常重要的作用,它主要是工作在Class装载的加载阶段,其主要作用是从系统外部获得Class二进制数据流。是Java的核心组件,所有的Class都是由ClassLoader进行加载,ClassLoader负责通过将Class文件里边的二进制数据流装载进系统,然后交给Java虚拟机进行连接,初始化等操作。
双亲委派模型:
- BoorStrapClassLoader:C++编写,加载核心类库 java.*
- ExtClassLoader:Java编写,加载扩展库 javax.*
- AppClassLoader:Java编写,加载程序所在目录 classPath路径
ClassLoader中的loadClass方法解析:
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 {
//判断parent是否为空
if (parent != null) {
//不为空再次调用loadClass方法去寻找是否存在加载过的类
c = parent.loadClass(name, false);
} else {
//获取最顶层ClassLoader
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;
}
}
为什么要使用双亲委派模型?
- 避免加载多份重复的字节码
LoadClass和forName的区别?
类加载分为
- 隐式加载:new 调用类加载器
- 显示加载:loadClass,forName
两者之间的区别
- 调用Class.forName方法时,会有一个boolean参数表示是否需要初始化,而Class.forName是默认需要初始化的,一旦初始化,就会触发目标对象的static代码块,去初始化static代码块
- ClassLoader.loadClass方法中的第二个参数表示目标对象是否进行连接,默认情况下是false,不进行连接的,所以就不会初始化静态的部分。