类加载包括三个步骤:加载,连接,初始化
加载:将编译出来的字节码加载到JVM的方法区,并在堆区生成一个Class对象
连接包括三个步骤:
①验证:验证字节码是否符合JVM的要求
②准备:将类的静态成员分配内存,并赋默认值
③解析:将符号引用变成直接引用
初始化:给类的静态变量赋初始值
静态内部类和非静态内部类的区别:
(1)静态内部类和非静态内部类在外部类被加载时都不一定会被加载,只有达到各自的加载条件才被加载。
public class InnerClass {
public static class StaticInner {
}
public class Inner {
}
public static void main(String[] args) {
InnerClass innerClass = new InnerClass();//可以导致外部类被类加载
}
}
此段代码当外部类被加载时,内部类都没有被加载,运行时加入参数 -verbose:class
查看所有被加载的类,打印到控制台,如下图,只有外部类被加载了
(2)静态内部类的对象不依赖与外部类对象,非静态内部类的对象依赖于外部类对象
(3)静态内部类只能访问外部类的静态成员,非静态内部类可以访问外部类的静态成员和实例成员
原因:所有的类,方法,属性要被分配内存之后,才能会被使用
①静态内部类不可以访问外部类的实例成员,因为实例成员只有在创建对象时才被分配内存,此时根据(2),静态内部类的对象存在,外部类的对象不一定存在。
②非静态内部类可以访问外部类的静态成员和实例成员,因为根据(2)非静态内部类的对象存在,外部类的对象一定已经存在了。
(4)静态内部类可以存在静态成员和非静态成员,非静态内部类只可以存在非静态成员
(5)静态内部类和外部类的类加载没有关系,非静态内部类和外部类,先加载外部类(初始化),再加载非静态内部类(初始化)
- 因为非静态内部类的对象一定出现在外部类的后边,所有外部类先被类加载,非静态内部类才能类类加载
(6)外部类可以访问静态内部类的静态成员,必须通过类名.属性/方法访问到,静态内部类的实例成员和非静态内部类的实例成员外部类都是不可以访问的
- 因为外部类访问内部类的静态成员通过 静态内部类类名.属性/方法,可以导致静态内部类进行类加载,不可以访问内部类的实例成员,因为外部类的对象存在时,内部类的对象不一定存在。
static静态代码块是什么时候被调用的?
都知道是类加载时候被调用的,那到底是在类加载的那个阶段被调用的呢?
代码验证:
public class Test {
static {
System.out.println("static");
}
class Test1{
Class[] myClass = {Test .class};//可以导致Test类被加载
public static void main(String[] args) {
Test1 test1 = new Test1();
}
}
加参数:-verbose:class
如图看出来Test类被加载了,但是static静态代码块没有被调用,所有static静态代码块在加载时没有被调用,在类初始化时被调用的