抄?你也要解决下问题呀。都tm的照搬
网上对这个问题没有一个人讲的清楚,还都是抄别人的,十篇文章都是抄的一个人,还讲的不清楚。哎,世风日下。
一、类加载定义
java编译器将 .java 文件编译成扩展名为 .class 的文件也就是字节码文件。.class 文件中保存着java转换后,虚拟机将要执行的指令。当需要某个类的时候,java虚拟机会加载 .class 文件,并创建对应的class对象,将class文件加载到虚拟机的内存,这个过程被称为类的加载。
二、类加载过程中用到了双亲委派模型
简单来说就是:在类加载的时候总会找他的爸爸,从最基础的类开始慢慢加载。
当一个非顶层类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,因此所有的加载请求最终都应该传送到顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个加载请求(它的搜索范围中没有找到所需的类)时,子加载器才会尝试自己去加载。
2.补充完一丢丢基础,再来讨论一下类的加载中,静态代码块一定会被执行吗?答案:不一定
代码如下(示例):
package com.rufeng.study.rufeng.util;
import com.sun.scenario.effect.impl.sw.sse.SSEBlend_SRC_OUTPeer;
import java.util.HashMap;
import java.util.Map;
/**
* @author rufeng
* @date 2021/3/8 11:55
*/
class B extends TestSuan {
public static int anInt =1;
static {
System.out.println("B1");
}
{
System.out.println("B2");
}
}
public class TestSuan {
public static int p =2;
static {
System.out.println("A1");
}
{
System.out.println("A2");
}
public static String VALUE = "static value loading";
public static final String FIANL_VALUE = "fianl value loading";
public static void main(String[] args) {
System.out.println(B.p);
运行结果
此处可以看到类B已经加载,但是静态代码块没有被加载。这里要说一下被动引用的问题。当类加载出现一下几种情况是加载类的时候不会初始化的(这句话如果有不严谨的,请大佬指出)。
1、通过子类引用父类的静态字段或者父类特有静态方法,不会导致子类初始化(子类是否会进行加载、连接等过程取决于虚拟机的参数设置)。
2、通过数组定义来引用类,不会触发此类的初始化。
3、调用类B使用类A的常量变量时,不会触发常量所在的类A的初始化,A类的常量在编译阶段会存入调用类B的常量池中,类B看起来是访问类A的常量,实际上是在自身的常量池(B类的常量池)中访问该常量,所以此时类B的静态代码块不会被加载。
总结
简单的加载顺序可以理解为(特殊情况特殊对待)
1、先执行父类静态代码块,父类静态成员变量 (代码先后顺序执行);
2、再执行子类静态代码块,子类静态成员变量(代码先后顺序执行);
3、再执行父类方法块(方法块与静态方法块的区别是方法块属于对象而不是类本身);
4、再执行父类构造器完成父类对象实例化;
5、再执行子类方法块;
6、最后执行子类构造器,完成子类对象实例化。
(重点注意: 子类静态方法以及父类静态方法的执行顺序是怎样往往取决于你在哪里调用该方法。)
7、若你在对象实例化之前,用父类进行调用静态方法,则它出现在父类静态代码块之后(因为只有完成了类加载之后,才能调用静态方法)。
8、若你在对象实例化之前,用子类进行调用静态方法,则出现在子类静态代码块之后(加载子类,必须加载父类,那父类静态代码块先执行,子类代码块后执行,完成加载再执行子类静态方法)。
9、若在对象实例化之后,则该静态函数调用会发生在对象构造方法执行之后。