1.1 什么类的加载load?(Node类的加载)
答.从硬盘上找到Node.class,解析该文件内容,生成Node类,把Node类的信息存放在内存的方法区
1.2 什么情况下回触发类的加载?并且该类不在内存中
(1)按需加载(懒加载过程)
a.实例化该类的一个对象 new Node(1);
b.使用类的静态属性或者静态方法 Main.merge(…)
c.用到子类,必须要有父类 new CNode
d.先加载父类的静态代码块,再加载子类的静态代码块
1.3 其他规则
静态属性的初始化顺序:
按代码的书写顺序,执行
a.静态属性定义时的初始化
b.静态代码
代码块定义:使用“{}”定义的一段代码;
构造代码块是跟着对象的加载而加载的。
2.1 普通代码块
普通代码块:定义在方法中的代码块;
public class Test1 {
public static void main(String[] args) {
{
//普通代码块
int a=10;
System.out.println(a);
}
int a=20;
System.out.println(a);
}
}
2.2 构造块
构造块:定义在类中的代码块(不加修饰符)
class A{
//定义在类中,不加任意修饰符
{
System.out.println("A的构造块");
}
//构造方法
public A(){
System.out.println("A的构造方法");
}
}
public class Test2 {
public static void main(String[] args) {
new A();
new A();
}
}
通过上述代码可以发现:构造块优先于构造方法执行,每产生一个新的对象就调用一次构造块。所以构造块可以进行简单的逻辑操作(在调用构造方法之前)。
2.3 静态代码块
静态代码块:使用static定义的代码块,它是跟着类的加载而加载的。
根据静态代码块所在的类不同又可分为以下两种类型:
1.在非主类中的静态代码块
2.在主类中的静态代码块
2.3.1 在非主类中的静态代码块
class B{
//构造块
{
System.out.println("B类的构造块");
}
//代码方法
public B(){
System.out.println("B类的构造方法");
}
//静态代码块
static {
System.out.println("B类的静态代码块");
}
}
public class Test3 {
public static void main(String[] args) {
System.out.println("==============================");
new B();
new B();
System.out.println("==============================");
}
}
可以发现:
1.在非主类的静态代码块中,在类加载时执行,优先于构造块执行
2.无论产生多少实例化对象,静态代码块只执行一次
静态块的主要作用是为static属性进行初始化!
2.3.2 在主类中的静态代码块
public class Test4 {
//构造块
{
System.out.println("Test4类的构造块");
}
//构造方法
public Test4(){
System.out.println("Test4类的构造方法");
}
//静态代码块
static {
System.out.println("Test4类的静态代码块");
}
public static void main(String[] args) {
System.out.println("===========================");
new Test4();
new Test4();
System.out.println("===========================");
}
}
在主类中定义的静态块,优先于主方法(main)执行,在JVM启动时执行,当编译执行时JVM会首先加载主类,此时主类中的静态代码块就会执行。
3.1 规则
1.优先初始化父类的属性,(和下面的执行顺序相同)
2.按照下面的顺序执行初始化
a.按照代码书写顺序,执行定义时和构造代码块
b.再去执行构造方法
3.静态代码块只在加载时执行一次,先执行父类的静态代码块,再执行子类的静态代码块。
3.2关于执行顺序的问题
1.父类的加载在子类之前
2.父类的构造方法在子类之前调用
3.static属性初始化是在类的加载时执行(按顺序执行)
1)定义时初始化
2)构造代码块时初始化
4.普通属性初始化是在对象的构造时执行(按顺序执行)
1)定义时初始化
2)构造代码块时初始化
3)构造方法时初始化
5.只有用到类,才会加载类
class Hello{
//构造方法
public Hello(){
System.out.println("1.Hello父类的构造方法");
}
//非静态代码块
{
System.out.println("2.Hello父类非静态代码块");
}
//静态代码块
static{
System.out.println("3.Hello父类静态代码块");
}
}
public class HelloA extends Hello{
public HelloA(){
System.out.println("4.helloA构造方法");
}
//非静态代码块
{
System.out.println("5.HelloA非静态代码块");
}
//静态代码块
static{
System.out.println("6.HelloA静态代码块");
}
public static void main(String[] args) {
System.out.println("7.---start---");
new HelloA();
new HelloA();
System.out.println("8.---end---");
}
}
按照上面讲的执行顺序执行:因为先用到了主类HeoolA,所以先加载了静态代码块;
执行结果:
class Hello1{
//构造方法
public Hello1(){
System.out.println("1.Hello1父类的构造方法");
}
//非静态代码块
{
System.out.println("2.Hello1父类非静态代码块");
}
//静态代码块
static{
System.out.println("3.Hello1父类静态代码块");
}
}
class Hello2 extends Hello1 {
public Hello2() {
System.out.println("4.hello2构造方法");
}
//非静态代码块
{
System.out.println("5.Hello2非静态代码块");
}
//静态代码块
static {
System.out.println("6.Hello2静态代码块");
}
}
public class Test{
public static void main(String[] args) {
System.out.println("7.---start---");
new Hello2();
new Hello2();
System.out.println("8.---end---");
}
}
运行结果:
注:代码块的内容参考本文章!!!