认识类的初始化:
在初始化阶段,java虚拟机执行类的初始化语句,为类的静态变量赋予初始值。在程序中, 静态变量的初始化有两种途径:
(1)在静态变量的声明处进行初始化
(2)在静态代码块中进行初始化
在下面代码中,静态变量都被显式初始化,而静态变量c没有被显式初始化,它将保持默认值0
public class Sample{
public static int a = 1;//在静态变量的声明处被初始化
public static long b;
public static long c;
static{
b=2;//在静态代码块中进行初始化
}
}
静态变量的声明语句,以及静态代码块都被看做类的初始化语句,java虚拟机会按照初始化语句在类文件中的先后顺序来执行他们。例如当以下类Sample被初始化后,a的值是4
public class Sample{
static int a = 1;
static { a = 2; }
static { a = 4; }
public static void main(string args[]){
System.out.println("a = "+ a);//打印a = 4
}
}
类的初始化步骤:
- 假如这个类还没有进行加载和连接,那就先进行加载和连接。
- 假如类存在直接的父类,并且父类没有被初始化,那就直接先初始化父类
- 假如类中存在初始化语句,那就依次执行这些初始化语句
以下代码会输出:
count1 = 1
count2 = 0
package test4;
class Singleton {
/*
* 2.因为是初次使用Singleton这个类, 所以类里面的静态变量都要初始化默认值singleton=null ,count1=0,count2=0
* 3.开始按顺序初始化,给变量赋值:
* -singleton生成这个类的实例,那么就要执行这个类的构造方法Singleton() count1=1;count2=1;
* -count1 没有给初始化的值,那他的值就不变, count1=1;
* -count2给了一个初始化的值,那么count2=0;
*/
private static Singleton singleton = new Singleton();
public static int count1;
public static int count2=0;
private Singleton(){
count1 ++;
count2 ++;
}
public static Singleton getInstance(){
return singleton;
}
}
public class MyTest{
public static void main(String[] args) {
//1.因为使用的是Singleton中的方法, 所以得加载Sgingleton这个类;
Singleton singleton = Singleton.getInstance();
System.out.println("count1 = "+singleton.count1);
System.out.println("count2 = "+singleton.count2);
}
}
import java.util.Random;
class FinalTest {
public static final int x = new Random().nextInt(100);
static{
System.out.println("Final static block");
}
}
public class MyTest{
public static void main(String[] args) {
System.out.println(FinalTest.x);
}
}
输出:
Final static block
84
class FinalTest {
public static final int x = 6/2;
static{
System.out.println("Final static block");
}
}
public class MyTest{
public static void main(String[] args) {
System.out.println(FinalTest.x);
}
}
输出:3
为什么有的会输出静态代码块的内容,有的不会呢?
因为FinalTest.x 中的final x = 6/2,是编译时的常量,在使用的时候, 是不会对类初始化的,如果是非编译时的常量的话, 就会对类进行初始化。
所谓对类初始化和不对类初始化,就看类的静态代码块有没有执行。
课后测试题:
package test4;
class Parent{
static int a = 3;
static{
System.out.println("parent is block");
}
}
class Child extends Parent{
static int b = 4;
static{
System.out.println("child is block");
}
}
public class MyTest{
static{
//1.因为是启动类,所以会先输出
System.out.println("main is block");
}
public static void main(String[] args) {
// System.out.println(Child.b);
Parent parent;
//2.因为上面的parent并没有实例化,不做任何操作,输出“---”
System.out.println("-------");
//实例化Parent,初始化parent静态变量以及静态代码块,a=0(赋予默认值),输出“parent is block”
//然后初始化a,给a赋值,a=3
parent = new Parent();
//输出a
System.out.println(Parent.a);
//使用Child类,那么就得初始化child类。
//因为parent类已经初始化过了,所以直接初始化Child类静态变量以及静态代码块,先给b赋予默认值0,输出“child is block”。然后初始化b,给b赋值4,b=4;输出b
System.out.println(Child.b);
}
}
package test4;
class Parent{
static int a = 3;
static{
System.out.println("parent is block");
}
static void doSomething(){
System.out.println("do something");
}
}
class Child extends Parent{
static int b = 4;
static{
System.out.println("child is block");
}
}
public class MyTest{
public static void main(String[] args) {
System.out.println(Child.a);
Child.doSomething();
}
}
输出:
3
do something
发现child静态块的语句并没有输出(也就是没有初始化),不是说好调用哪个类的静态变量或者静态方法,就要初始化那个类吗?我明明掉用了Child类里面的静态变量a了呀?
哈哈,原因是我们在MyTest类里面调用的是并不是child类里面的属性, 而是他父类中的属性。所以,不会对child进行初始化的。
总结:
只有当程序访问的静态变量或静态方法确实在当前类或当前接口中定义时,才可以认为是对类或者接口的主动使用
package test4;
class Cl{
static{
System.out.println("my name is cl!");
}
}
public class MyTest{
public static void main(String[] args) throws Exception{
//获得系统类加载器
ClassLoader loader = ClassLoader.getSystemClassLoader();
Class<?> clazz = loader.loadClass("test4.Cl");
System.out.println("======分割=====");
clazz = Class.forName("test4.Cl");
}
}
输出结果:
======分割=====
my name is cl!
总结:
调用ClassLoader类的loadClass()方法加载一个类,并不是对类的主动调用,不会导致类的初始化。
当java虚拟机初始化一个类时,要求他的所有父类都已经初始化,但是这条规则,并不适用于接口。
- 在初始化一个类时,并不会先初始化它所实现的接口
- 在初始化一个接口时,并不会先初始化它的父接口
因此,一个父接口并不会因为他的子接口或者实现类的初始化而初始化,只有当程序首次使用特定接口的静态变量时,才会导致接口的初始化。