类字段的初始化遵循这样的顺序:默认初始化(清零)-> 指定初始化 -> 构造方法初始化。
字段也是变量,当在程序中定义了一个变量,那么程序运行时,这个变量必然会得到一块内存中的空间。问题在于这块空间中可能本来就已经存储了数据,这些数据是其他程序(也可能是该程序自己的其他部分)上次运行时残留的。这些数据几乎不太可能是我们需要的,因而不能直接从指向该块空间的变量中取值并使用,而必须先将其初始化成我们想要的。
(在这里多说几句,从上面的分析中可以看到,计算机内存的使用规则,或者说很多语言中的清理或垃圾回收机制是怎么回事。这是我花了挺长时间才明白的。。。所谓清理一个变量,其实就是让该变量放弃了内存中某块空间的使用权,也就是说其他变量可以自由使用对应的空间了。但这并不意味着放弃使用权的同时还要把这块空间清零,比如全部弄成二进制的0。打个不恰当的比方,如果我要搬家了,那么无论留下多少垃圾我一般都是不会去管的,因为这是新来者要做的事;当然也可以预见,我要搬到的新家会面临同样的境况。。。)
据说在C语言和C++编程中,很大一部分bug都是由于变量没有恰当的初始化造成的,所以Java采取了适当的机制来避免这种错误。其中之一就是类子段的默认初始化;另外一个是方法的局部变量若未初始化就使用会引起编译器的报错。
一、非static变量的初始化
1、默认初始化
也称为清零,即当程序运行时(具体的说,应该是new一个对象的时候),先将类中的字段所指向的空间全部设成默认值(一般是该字段类型对应的零值)。Java中各个类型的默认值如下:
boolean -> false
char -> ''
byte -> 0
short -> 0
int -> 0
long -> 0
float -> 0.0
double -> 0.0
reference -> null
最后一个是引用类型,即对象引用。
2、指定初始化
就是在声明类字段的同时为变量赋值,如下所示:
然而指定初始化有个“缺点”,就是不够灵活。一旦给某个字段设置了初始值,那么所有该类new出来的对象,这个字段的值都会是一样。显然,这不一定适合所有的情景,有时候我们希望某个字段的值只有在new一个对象的时候才确定,而不是千篇一律,这时候就该构造方法上场了。
3、构造方法初始化
先上代码,如下:
以上就是初始化类的非static字段常用的方法,所谓非static字段又称为对象字段,这些字段都是与对象本身紧密关联的,深一点的讲,就是每个对象都有一个对应字段的存储空间,空间中存储的值各不相同。非static字段是对象的私有字段,从上面的三种初始化方法也可以看出来,非static字段初始化都发生在new对象的时候。除了以上三种方法外,还有一种不太常见的初始化方法,称为实例初始化,其语法如下:
二、static字段的初始化
static字段又称为类字段,也就是和类本身紧密关联的,不是每个对象独有而是共享的。static字段指向的是类拥有的一块空间,只有一块!每个对象存有的只是该空间的一个引用,所以取得的值显然是相同的。
static字段的初始化与非static字段的区别有两点,一是“块初始化”时要加上static关键字,如下:
那么,static和非static字段的关键区别在于执行时机。非static字段的初始化是在new一个对象的时候,而static字段的初始化是在类加载的时候。类加载发生在什么时候呢?可这是一个深奥的问题,简单来说,有以下几种情况会引起类的加载:
1)new一个对象的时候,也就是调用构造方法的时候;
2)直接访问类的是static字段的时候;
3)调用static方法的时候;
4)欢迎补充。
从这里应该可以推断出,构造方法也是static的,虽然并没有显式声明。
另外要指出的是,类加载的时候,所有与static有关的初始化操作都会执行:比如你直接访问了一个static字段,那么所有的static字段都会被初始化,同时static块的所有代码也都会被执行。
需要注意的是,static初始化只会执行一次,因为类只会被加载一次,不存在什么首次加载二次加载。类被加载到内存后,会一直存在,直到被回收。
字段也是变量,当在程序中定义了一个变量,那么程序运行时,这个变量必然会得到一块内存中的空间。问题在于这块空间中可能本来就已经存储了数据,这些数据是其他程序(也可能是该程序自己的其他部分)上次运行时残留的。这些数据几乎不太可能是我们需要的,因而不能直接从指向该块空间的变量中取值并使用,而必须先将其初始化成我们想要的。
(在这里多说几句,从上面的分析中可以看到,计算机内存的使用规则,或者说很多语言中的清理或垃圾回收机制是怎么回事。这是我花了挺长时间才明白的。。。所谓清理一个变量,其实就是让该变量放弃了内存中某块空间的使用权,也就是说其他变量可以自由使用对应的空间了。但这并不意味着放弃使用权的同时还要把这块空间清零,比如全部弄成二进制的0。打个不恰当的比方,如果我要搬家了,那么无论留下多少垃圾我一般都是不会去管的,因为这是新来者要做的事;当然也可以预见,我要搬到的新家会面临同样的境况。。。)
据说在C语言和C++编程中,很大一部分bug都是由于变量没有恰当的初始化造成的,所以Java采取了适当的机制来避免这种错误。其中之一就是类子段的默认初始化;另外一个是方法的局部变量若未初始化就使用会引起编译器的报错。
一、非static变量的初始化
1、默认初始化
也称为清零,即当程序运行时(具体的说,应该是new一个对象的时候),先将类中的字段所指向的空间全部设成默认值(一般是该字段类型对应的零值)。Java中各个类型的默认值如下:
boolean -> false
char -> ''
byte -> 0
short -> 0
int -> 0
long -> 0
float -> 0.0
double -> 0.0
reference -> null
最后一个是引用类型,即对象引用。
2、指定初始化
就是在声明类字段的同时为变量赋值,如下所示:
public class Apple{
private String color = "green";
}
指定初始化其实是为我们提供了更多的选择,因为我们可能不喜欢编译器给字段的默认值。但是需要特别注意的是,即使有指定初始化,也不会省略默认初始化。也就是说,即使在声明字段的时候给字段赋了值,那么jvm的执行顺序也是先默认初始化,然后在赋与指定的值。
然而指定初始化有个“缺点”,就是不够灵活。一旦给某个字段设置了初始值,那么所有该类new出来的对象,这个字段的值都会是一样。显然,这不一定适合所有的情景,有时候我们希望某个字段的值只有在new一个对象的时候才确定,而不是千篇一律,这时候就该构造方法上场了。
3、构造方法初始化
先上代码,如下:
public class Apple{
private String color;
public Apple(String color) {
this.color = color;
}
public static void main(String[] args) {
Apple apple = new Apple("green");
}
}
从代码的角度,构造方法初始化就是在构造方法内部对字段赋值,而值来自于构造方法的参数。这就需要在调用构造方法(也就是new一个对象)的时候传入响应的参数,从而达到了个性化赋值的目的。
以上就是初始化类的非static字段常用的方法,所谓非static字段又称为对象字段,这些字段都是与对象本身紧密关联的,深一点的讲,就是每个对象都有一个对应字段的存储空间,空间中存储的值各不相同。非static字段是对象的私有字段,从上面的三种初始化方法也可以看出来,非static字段初始化都发生在new对象的时候。除了以上三种方法外,还有一种不太常见的初始化方法,称为实例初始化,其语法如下:
public class Apple {
private String color;
{
color = "red";
System.out.println("first");
}
public Apple(String color) {
System.out.println("secend");
}
}
本质上,这种语法是为了对应static块,在new一个对象的时候会响应的执行花括号内的代码,代码可以用来给字段赋值,但也可以做任何事。需要注意的是执行顺序,默认初始化 -> 指定初始化 -> 实例初始化 -> 构造方法。
二、static字段的初始化
static字段又称为类字段,也就是和类本身紧密关联的,不是每个对象独有而是共享的。static字段指向的是类拥有的一块空间,只有一块!每个对象存有的只是该空间的一个引用,所以取得的值显然是相同的。
static字段的初始化与非static字段的区别有两点,一是“块初始化”时要加上static关键字,如下:
public class Apple {
private static String color = "green;
static {
color = "red";
System.out.println("first");
}
public Apple(String color) {
System.out.println("secend");
}
}
二是,static字段没有构造方法初始化。显然构造方法是针对对象的,而static字段不属于任何对象,不需要依赖对象而存在。你当然可以在构造方法中对static字段赋值,但是这已经不能叫初始化了,因为并不“初始”。
那么,static和非static字段的关键区别在于执行时机。非static字段的初始化是在new一个对象的时候,而static字段的初始化是在类加载的时候。类加载发生在什么时候呢?可这是一个深奥的问题,简单来说,有以下几种情况会引起类的加载:
1)new一个对象的时候,也就是调用构造方法的时候;
2)直接访问类的是static字段的时候;
3)调用static方法的时候;
4)欢迎补充。
从这里应该可以推断出,构造方法也是static的,虽然并没有显式声明。
另外要指出的是,类加载的时候,所有与static有关的初始化操作都会执行:比如你直接访问了一个static字段,那么所有的static字段都会被初始化,同时static块的所有代码也都会被执行。
需要注意的是,static初始化只会执行一次,因为类只会被加载一次,不存在什么首次加载二次加载。类被加载到内存后,会一直存在,直到被回收。