【参考】《疯狂Java讲义》
目录
普通初始化块
初始化块对Java对象进行初始化操作
如果没有new一个类的对象,那么这个类的初始化块不会执行:
public class Test {
public static void main(String[] args) {
System.out.println(">>>开始执行主函数");
}
}
class A{
{
System.out.println(">>>执行A类的初始化块");
}
}
输出结果:
>>>开始执行主函数
可见,A类的初始化块没有执行,因为整个程序没有new一个A类的对象。
初始化块会在构造器之前执行,因为先加载类,才能构造对象:
public class Test {
public static void main(String[] args) {
System.out.println(">>>这是主函数里的一个语句");
A a1 = new A();
}
}
class A{
{
System.out.println(">>>执行A类的初始化块");
}
public A() {
System.out.println(">>>执行A类的无参构造函数");
}
}
输出结果:
>>>这是主函数里的一个语句
>>>执行A类的初始化块
>>>执行A类的无参构造函数
要new一个这个类的对象,才能执行这个类的初始化块,例如,改变上面程序主函数中输出语句和new语句的顺序:
public class Test {
public static void main(String[] args) {
A a1 = new A();
System.out.println(">>>这是主函数里的一个语句");
}
}
class A{
{
System.out.println(">>>执行A类的初始化块");
}
public A() {
System.out.println(">>>执行A类的无参构造函数");
}
}
输出结果:
>>>执行A类的初始化块
>>>执行A类的无参构造函数
>>>这是主函数里的一个语句
初始化块对Java对象进行初始化操作,所以,如果new了2个对象,那么会执行2次初始化块:
public class Test {
public static void main(String[] args) {
A a1 = new A();
A a2 = new A();
}
}
class A{
{
System.out.println(">>>执行A类的初始化块");
}
public A() {
System.out.println(">>>执行A类的无参构造函数");
}
}
输出结果:
>>>执行A类的初始化块
>>>执行A类的无参构造函数
>>>执行A类的初始化块
>>>执行A类的无参构造函数
一个类里面可以有多个初始化块
一个类里面可以有多个初始化块,前面定义的初始化块先执行,后面定义的初始化块后执行:
public class Test {
public static void main(String[] args) {
A a1 = new A();
}
}
class A{
{
System.out.println(">>>执行A类的第1个初始化块");
}
{
System.out.println(">>>执行A类的第2个初始化块");
}
public A() {
System.out.println(">>>执行A类的无参构造函数");
}
}
输出结果:
>>>执行A类的第1个初始化块
>>>执行A类的第2个初始化块
>>>执行A类的无参构造函数
同样,如果new了2个对象:
public class Test {
public static void main(String[] args) {
A a1 = new A();
A a2 = new A();
}
}
class A{
{
System.out.println(">>>执行A类的第1个初始化块");
}
{
System.out.println(">>>执行A类的第2个初始化块");
}
public A() {
System.out.println(">>>执行A类的无参构造函数");
}
}
输出结果:
>>>执行A类的第1个初始化块
>>>执行A类的第2个初始化块
>>>执行A类的无参构造函数
>>>执行A类的第1个初始化块
>>>执行A类的第2个初始化块
>>>执行A类的无参构造函数
虽然Java允许一个类里定义2个实例初始化块,但这没有任何意义。因为实例初始化块是在创建Java对象时隐式执行的,而且它们总是全部执行,因此完全可以把多个实例初始化块合并成一个实例初始化块,从而可以让程序更加简洁,可读性更强。
初始化块里的代码可包含任何可执行性语句
初始化块里的代码可以包含任何可执行性语句,包括定义局部变量、调用其他对象的方法,以及使用分支、循环语句等。
public class Test {
public static void main(String[] args) {
A a1 = new A();
System.out.println("new好的对象的score值:" + a1.score);
}
}
class A{
static String name = "JAVASE";
int id = 3;
int number;
int score;
{
this.number = 10;
showName();
System.out.println("id = " + this.id);
System.out.println("初始化块里面的score值:" + this.score);
showScore();
}
public static void showName() {
System.out.println("name = " + name);
}
public void showScore() {
System.out.println(this.score);
}
public A() {
score = 20;
}
}
输出结果:
name = JAVASE
id = 3
初始化块里面的score值:0
0
new好的对象的score值:20
初始化块里的score值和new好的对象的score值,是因为先执行初始化块,再执行构造器,构造器给score赋值20。
初始化块是Java类的一种成员
初始化块是Java类里可出现的第4种成员(前面依次有成员变量、方法和构造器)。
初始化块虽然也是Java类的一种成员,但它没有名字,也就没有标识,因此无法通过类、对象来调用初始化块。实例初始化块只在创建Java对象时隐式执行,而且在构造器执行之前自动执行。类初始化则在类初始化阶段自动执行。
实例初始化块、声明实例变量指定的默认值的执行顺序
实例初始化块、声明实例变量指定的默认值都可认为是对象的初始化代码,它们的执行顺序与源程序中的排列顺序相同。
public class Test {
public static void main(String[] args) {
System.out.println(new A().id);
}
}
class A{
int id = 3;
{
this.id = 4;
}
}
输出结果:
4
交换一下顺序:
public class Test {
public static void main(String[] args) {
System.out.println(new A().id);
}
}
class A{
{
this.id = 4;
}
int id = 3;
}
输出结果:
3
可见,实例初始化块、声明实例变量指定的默认值是按代码中的排序顺序执行的。
编译后的初始化块
实际上实例初始化块是一个假象,使用javac命令编译Java类后,该Java类中的实例初始化块会消失—实例初始化块中代码会被“还原”到每个构造器中,且位于构造器所有代码的前面。
静态初始化块(类初始化块)
定义
如果定义初始化块时使用了static修饰符,则这个初始化块就变成了类初始化块,也被称为静态初始化块(实例初始化块负责对对象执行初始化,类初始化块则负责对类进行初始化)。类初始化块是类相关的,系统将在类初始化阶段执行类初始化块,而不是在创建对象时才执行。因此类初始化块总是比实例初始化块先执行。
同样,没有new对象,也都不执行:
public class Test {
public static void main(String[] args) {
System.out.println(">>>开始执行主函数");
}
}
class A{
{
System.out.println(">>>执行A类的实例初始化块");
}
static {
System.out.println(">>>执行A类的类初始化块(静态初始化块)");
}
}
输出结果:
>>>开始执行主函数
new了对象会执行:
public class Test {
public static void main(String[] args) {
A a1 = new A();
}
}
class A{
{
System.out.println(">>>执行A类的实例初始化块");
}
static {
System.out.println(">>>执行A类的类初始化块(静态初始化块)");
}
}
输出结果:
>>>执行A类的类初始化块(静态初始化块)
>>>执行A类的实例初始化块
初始化块执行顺序问题
public class Test {
{
System.out.println(">>>执行Test类的实例初始化块");
}
static {
System.out.println(">>>执行Test类的类初始化块(静态初始化块)");
}
public static void main(String[] args) {
System.out.println(">>>开始执行主函数");
}
}
输出结果:
>>>执行Test类的类初始化块(静态初始化块)
>>>开始执行主函数
public class Test {
public static void main(String[] args) {
System.out.println(">>>开始执行主函数");
}
{
System.out.println(">>>执行Test类的实例初始化块");
}
static {
System.out.println(">>>执行Test类的类初始化块(静态初始化块)");
}
}
输出结果:
>>>执行Test类的类初始化块(静态初始化块)
>>>开始执行主函数
类初始化块只会执行一次:
public class Test {
public static void main(String[] args) {
System.out.println(">>>开始执行主函数");
Test t1 = new Test();
Test t2 = new Test();
}
{
System.out.println(">>>执行Test类的实例初始化块");
}
static {
System.out.println(">>>执行Test类的类初始化块(静态初始化块)");
}
}
输出结果:
>>>执行Test类的类初始化块(静态初始化块)
>>>开始执行主函数
>>>执行Test类的实例初始化块
>>>执行Test类的实例初始化块
继承问题
public class Test {
public static void main(String[] args) {
System.out.println(">>>开始执行主函数");
Bottom t1 = new Bottom();
Bottom t2 = new Bottom();
}
}
class Root{
static {
System.out.println(">>>Root的类初始化块");
}
{
System.out.println(">>>Root的普通初始化块");
}
public Root() {
System.out.println(">>>Root的无参构造器");
}
}
class Mid extends Root{
static {
System.out.println(">>>Mid的类初始化块");
}
{
System.out.println(">>>Mid的普通初始化块");
}
public Mid() {
System.out.println(">>>Mid的无参构造器");
}
}
class Bottom extends Mid{
static {
System.out.println(">>>Bottom的类初始化块");
}
{
System.out.println(">>>Bottom的普通初始化块");
}
public Bottom() {
System.out.println(">>>Bottom的无参构造器");
}
}
输出结果:
>>>开始执行主函数
>>>Root的类初始化块
>>>Mid的类初始化块
>>>Bottom的类初始化块
>>>Root的普通初始化块
>>>Root的无参构造器
>>>Mid的普通初始化块
>>>Mid的无参构造器
>>>Bottom的普通初始化块
>>>Bottom的无参构造器
>>>Root的普通初始化块
>>>Root的无参构造器
>>>Mid的普通初始化块
>>>Mid的无参构造器
>>>Bottom的普通初始化块
>>>Bottom的无参构造器
public class Test {
public static void main(String[] args) {
System.out.println(">>>开始执行主函数");
Bottom t1 = new Bottom();
Bottom t2 = new Bottom();
}
}
class Root{
static {
System.out.println(">>>Root的类初始化块");
}
{
System.out.println(">>>Root的普通初始化块");
}
public Root() {
System.out.println(">>>Root的无参构造器");
}
}
class Mid extends Root{
static {
System.out.println(">>>Mid的类初始化块");
}
{
System.out.println(">>>Mid的普通初始化块");
}
public Mid() {
System.out.println(">>>Mid的无参构造器");
}
public Mid(String msg) {
System.out.println(">>>Mid的带参数构造器 : +" + msg);
}
}
class Bottom extends Mid{
static {
System.out.println(">>>Bottom的类初始化块");
}
{
System.out.println(">>>Bottom的普通初始化块");
}
public Bottom() {
super("JAVAEE");
System.out.println(">>>Bottom的无参构造器");
}
}
输出结果:
>>>开始执行主函数
>>>Root的类初始化块
>>>Mid的类初始化块
>>>Bottom的类初始化块
>>>Root的普通初始化块
>>>Root的无参构造器
>>>Mid的普通初始化块
>>>Mid的带参数构造器 : +JAVAEE
>>>Bottom的普通初始化块
>>>Bottom的无参构造器
>>>Root的普通初始化块
>>>Root的无参构造器
>>>Mid的普通初始化块
>>>Mid的带参数构造器 : +JAVAEE
>>>Bottom的普通初始化块
>>>Bottom的无参构造器
类初始化块和声明类变量时所指定的初始值的顺序
类初始化块和声明类变量时所指定的初始值都是该类的初始化代码,它们的执行顺序与源程序中的排列顺序相同:
public class Test {
public static void main(String[] args) {
System.out.println(A.id);
}
}
class A{
static int id = 11;
static {
id = 22;
}
}
输出结果:
22
如果换一下顺序:
public class Test {
public static void main(String[] args) {
System.out.println(A.id);
}
}
class A{
static {
id = 22;
}
static int id = 11;
}
输出结果:
11
改正
前面的说法有点小问题,不是只有new对象了才会执行类初始化块,如果主函数就在这个类里面,那么不管new不new,都会执行他的类初始化块,因为要执行这个类的主函数,要先加载这个类。
但还是要new一个对象才能执行普通初始化块。