Java初学-8.3-代码块(实例初始化块/普通代码块和静态初始化块/静态代码块)

代码块又称初始化块,属于类中的成员,即类的一部分。类似于方法,将逻辑语句封装在方法体中,用{}包围起来。与方法不同的是,代码块没有方法名,没有返回值,没有参数,只有方法体,而且不能通过对象或类显式调用,而是在加载类时或创建对象时隐式调用。  

代码块可以用访问修饰符修饰,也可以写static关键字。

代码块可以分为两大类:实例初始化块/普通代码块和静态初始化块/静态代码块

实例初始化块/普通代码块

实例初始化块是在类体中定义的代码块,由一对大括号 {} 包围,不在任何方法内部。这种代码块会在每个对象创建时自动执行,并且在构造函数调用之前执行。它们通常用于对象状态的初始化。

示例1:

package CodeBlockTest;

public class CodeBlockTest01 {
    private int x;
    private int y;

    { // 实例初始化块
        x = 100;
        y = 200;
    }//;可加可不加

    public CodeBlockTest01() {
        System.out.println("x: " + x + " , y: " + y);
    }

    public static void main(String[] args) {
        CodeBlockTest01 CBT01 = new CodeBlockTest01(); // 构造函数前会执行实例初始化块
    }
}

示例2:

package CodeBlockTest;

public class CodeBlockTest03 {
    public static void main(String[] args) {
        CodeBlockTest03001 CBT03000101 = new CodeBlockTest03001("name");
        CodeBlockTest03001 CBT03000102 = new CodeBlockTest03001("name",12);
        CodeBlockTest03001 CBT03000103 = new CodeBlockTest03001("name",18,22.23);
    }
}
class CodeBlockTest03001{
    private int x;
    private int y;
    private int z;

    {
        x = 10;
        y = 20;
        z = 30;
        System.out.println("x = " + x);
        System.out.println("y = " + y);
        System.out.println("z = " + z);
    }

    public CodeBlockTest03001(String name) {
        System.out.println("CodeBlockTest03001(String name)");
    }

    public CodeBlockTest03001(String name,int a) {
        System.out.println("CodeBlockTest03001(String name,int a)");
    }

    public CodeBlockTest03001(String name,int a,double b) {
        System.out.println("CodeBlockTest03001(String name,int a,double b)");
    }
}

实例初始化块:每当创建一个新的 CodeBlockTest03001 对象时,这个实例初始化块都会被执行。它在所有构造器执行之前运行。

代码执行流程

当创建 CodeBlockTest03001 的对象时,执行流程如下:

分配内存:为新的 CodeBlockTest03001 对象分配内存。

实例初始化块执行:初始化块中的代码运行,设置 x, y, 和 z 的初始值,并打印它们。

构造器执行:调用与创建对象时提供的参数相匹配的构造器。构造器可以进一步初始化对象的状态,或者执行其他的初始化任务。

在给定的代码中,main 方法创建了三个 CodeBlockTest03001 的对象,每个对象都会先执行实例初始化块,然后调用相应的构造器。

则有:

每当一个类的构造器被调用来创建一个新的对象时,该类中的实例初始化块(非静态的初始化块)都会被执行。实例初始化块是在每个对象实例创建时都要运行的代码,它会在构造器执行之前运行。这是因为实例初始化块是为每个对象的初始化服务的,它的目的是确保在构造器开始执行之前,对象的某些状态已经被正确地设置。

静态初始化块/静态代码块

静态初始化块与实例初始化块类似,但它们使用 static 关键字定义。这意味着它们只在类加载时执行一次,而不是每次创建对象时都执行。它们通常用于初始化类的静态成员。

示例1:

package CodeBlockTest;

public class CodeBlockTest02 {
    static int x;

    static { // 静态初始化块
        x = 10000;
    }

    public static void main(String[] args) {
        System.out.println("x: " + CodeBlockTest02.x);
    }
}

示例2:

package CodeBlockTest;

public class CodeBlockTest04 {

    public static void main(String[] args) {

        CodeBlockTest04001 CBT04001 = new CodeBlockTest04001("嘻嘻~");

        CodeBlockTest04001 CBT04002 = new CodeBlockTest04001("哈哈",12);

        CodeBlockTest04001 CBT04003 = new CodeBlockTest04001("嘎嘎",18,22.3);

    }

}

class CodeBlockTest04001{

    private int a;

    private static int b;

    static {

        System.out.println("------------------");

        //a = 1;编译错误,因为a是非静态的        

b = 2;

        System.out.println("b = " + b);

        System.out.println("------------------");

    }

    public CodeBlockTest04001(String name) {

        System.out.println("CodeBlockTest04001(String name)");

    }

    public CodeBlockTest04001(String name,int x) {

        System.out.println("CodeBlockTest04001(String name,int a)");

    }

    public CodeBlockTest04001(String name,int x,double y) {

        System.out.println("CodeBlockTest04001(String name,int a,double y)");

    }

}

由输出结果可知,只在类加载时执行一次即输出一次,并且用于用于初始化类的静态成员。

那么类什么时候加载呢?

package CodeBlockTest;

public class CodeBlockTest05 {
    public static void main(String[] args) {
//1
        //这个静态初始化块会在类加载的时候执行,而不是在创建类的实例时执行。
        // 因此,当运行main方法并创建CodeBlockTest05001的实例时,下面的事件会发生:
        //类CodeBlockTest05001被加载,此时静态初始化块会被执行,输出:“CodeBlockTest05001中的静态代码块。”
        //然后,new CodeBlockTest05001()语句会调用CodeBlockTest05001类的默认无参构造器来创建一个新实例。
        /*System.out.println("----------------------------------------------");
        CodeBlockTest05001 CBT05001 = new CodeBlockTest05001();
        System.out.println("----------------------------------------------");
        */



//2
        //由于CodeBlockTest05002继承自CodeBlockTest05001,在创建CodeBlockTest05002的实例时,
        // JVM会先加载CodeBlockTest05001类(如果它还没有被加载的话),然后执行CodeBlockTest05001中的静态初始化块。
        // 接着,JVM会加载CodeBlockTest05002类,并执行其静态初始化块。
        // 最后,JVM会创建CodeBlockTest05002的实例,执行CodeBlockTest05002的构造器以及实例初始化块(如果有的话)。
        /*System.out.println("----------------------------------------------");
        CodeBlockTest05002 CBT05002 = new CodeBlockTest05002();
        System.out.println("----------------------------------------------");
        */




//3
        //调用类的静态成员时,也会执行其静态初始化块。
        //加载CodeBlockTest05003类:
        //当main方法尝试访问CodeBlockTest05003.name时,JVM检测到需要使用CodeBlockTest05003类的静态变量name。
        //如果CodeBlockTest05003类尚未被加载,JVM将加载这个类,包括执行其静态初始化块。
        //执行CodeBlockTest05003类的静态初始化块:
        //在加载CodeBlockTest05003类的过程中,静态初始化块会被执行,输出 "CodeBlockTest05003中的静态代码块。"。
        //访问CodeBlockTest05003.name:
        //由于CodeBlockTest05003类已经被加载并初始化,现在可以访问静态变量name。
        System.out.println(CodeBlockTest05003.name);

        //CodeBlockTest05003.CodeBlockTest0500301();
        //即只要程序首次主动使用了类的静态成员,无论是否创建了类的实例,相关的静态初始化过程都会发生。
    }
}
//类被加载的情况:
//1、创建类的实例:
//当你首次创建一个类的实例时,如果该类或其超类(父类)中包含静态代码块,这些静态代码块将会在实例创建之前执行。
// 这是因为类在实例化之前必须先被加载,而静态代码块正是在类加载时执行的。
//2、访问类的静态成员:
//当你首次访问一个类的静态成员(如静态变量或静态方法)时,如果该类还未被加载,JVM将加载该类并执行其静态代码块。
// 这是因为静态成员是类的一部分,而不是特定实例的一部分,所以它们的初始化和准备必须在类加载时完成。
//3、使用反射机制加载类(没学呢):
//当你使用反射API,如Class.forName(className),显式地加载一个类时,如果该类包含静态代码块,
// 静态代码块将在类加载时执行。这种情况下,类的加载完全由程序控制,通常是为了动态地获取类的信息或创建实例。
class CodeBlockTest05001{
    static {
        System.out.println("CodeBlockTest05001中的静态代码块。");
    }
}
class CodeBlockTest05002 extends CodeBlockTest05001{
    static {
        System.out.println("CodeBlockTest05002中的静态代码块。");
    }
}
class CodeBlockTest05003 {
    public static String name = "CodeBlockTest05003";
    public static void CodeBlockTest0500301(){
        System.out.println("静态方法: CodeBlockTest0500301()");
    }
    static {
        System.out.println("CodeBlockTest05003中的静态代码块。");
    }
}

不同类型的代码块(普通代码块和静态代码块)以及构造函数的使用方式(只有一个类):

示例:

package CodeBlockTest;

public class CodeBlockTest07 {
    public static void main(String[] args) {
        CodeBlockTest07001 CBT0701 = new CodeBlockTest07001("少林呱呱");
        CodeBlockTest07001 CBT0702 = new CodeBlockTest07001("富贵呱呱",12);
        CodeBlockTest07001 CBT0703 = new CodeBlockTest07001("逍遥呱呱",13,8);

    }
}
class CodeBlockTest07001{
    //每创建一次对象就会输出一次
    private String name = getName();

    {
        System.out.println("CodeBlockTest07001的第一个普通代码块。");
    }

    {
        System.out.println("CodeBlockTest07001的第二个普通代码块。");
    }

    public String getName(){
        System.out.println("getName()方法。");
        return "CodeBlockTest07001";
    }



//---------------------------------------------------------------------------------------------------
    //只输出一次!!!
    private static int x = getX();

    private static int y = getY();

    static{
        System.out.println("CodeBlockTest07001的第一个静态代码块。");
    }

    static{
        System.out.println("CodeBlockTest07001的第二个静态代码块。");
    }

    public static int getX(){
        System.out.println("getX()方法");
        return 123;
    }

    public static int getY(){
        System.out.println("getY()方法");
        return 456;
    }
//---------------------------------------------------------------------------------------------------
    //构造器
    public CodeBlockTest07001(String NAME){
        System.out.println("第一个构造器CodeBlockTest07001(String NAME)。");
    }
    public CodeBlockTest07001(String NAME, int A){
        System.out.println("第二个构造器CodeBlockTest07001(String NAME, int A)。");
    }
    public CodeBlockTest07001(String NAME, int A, int B){
        System.out.println("第三个构造器CodeBlockTest07001(String NAME, int A, int B)。");
    }
}
/*
执行顺序:
静态变量初始化:
x的初始化通过调用getX()方法来进行。
y的初始化通过调用getY()方法来进行。
这些初始化在类首次被加载时发生,并且发生在任何静态代码块之前(因为初始化顺序遵循它们在源代码中的声明顺序)。
静态代码块执行:
第一个静态代码块执行。
第二个静态代码块执行。
这些静态代码块在类首次被加载时执行,并且只执行一次。
创建对象:
对于每个对象创建:执行普通代码块。
调用对应的构造函数。
普通代码块执行:
每次创建对象时,普通代码块都会按照它们在源代码中的出现顺序执行。
name的初始化通过调用getName()方法来进行。
详细分析
静态变量和静态代码块:
x和y的初始化在类首次被加载时发生,并且在任何静态代码块之前(因为初始化顺序遵循它们在源代码中的声明顺序)。
两个静态代码块按照它们在源代码中的出现顺序执行。
普通代码块:
每次创建CodeBlockTest07001类的实例时,普通代码块都会执行。
普通代码块按照它们在源代码中的出现顺序执行。
构造函数:
构造函数在每个对象创建时被调用。
三个不同的构造函数被调用,分别对应不同的参数列表。
*/

package CodeBlockTest;

public class CodeBlockTest08 {
    public static void main(String[] args) {

        CodeBlockTest08002 CBT0801 = new CodeBlockTest08002();
    }
}

class CodeBlockTest08001{
    {
        System.out.println("父类CodeBlockTest08001中的普通代码块。");
    }
    public CodeBlockTest08001() {
        System.out.println("父类CodeBlockTest08001的无参构造函数。");
    }
}

class CodeBlockTest08002 extends CodeBlockTest08001{
    {
        System.out.println("子类CodeBlockTest08002中的普通代码块。");
    }
    public CodeBlockTest08002() {
        System.out.println("子类CodeBlockTest08002的无参构造函数。");
    }
}

构造函数调用顺序:

当创建一个子类对象时,构造函数的调用顺序是先调用父类的构造函数,再调用子类的构造函数。如果父类没有显式地调用其自身的构造函数,则会默认调用无参构造函数。

普通代码块执行顺序:

普通代码块在每个对象创建时执行,并且按照它们在源代码中的出现顺序执行。子类的普通代码块在父类构造函数调用之后执行。

由此引出继承关系中构造函数和普通代码块的执行顺序:

package CodeBlockTest;

public class CodeBlockTest09 {
    public static void main(String[] args) {
        /*new CodeBlockTest09001("少林呱呱");

        new CodeBlockTest09002("富贵呱呱");*/

        new CodeBlockTest09003("逍遥呱呱");
    }
}

class CodeBlockTest09001{
    private int a = getA();
    {
        System.out.println("CodeBlockTest09001的普通代码块。");
    }

    public int getA(){
        System.out.println("getA()方法。");
        return 1;
    }

//------------------------------------------------------------------------------------------------
    private static int b = getB();
    static {
        System.out.println("CodeBlockTest09001的静态代码块。");
    }

    public static int getB(){
        System.out.println("getB()方法。");
        return 2;
    }
    //------------------------------------------------------------------------------------------------
    public CodeBlockTest09001(String NAME){
        System.out.println("CodeBlockTest09001(String NAME)构造器。");
    }
}

class CodeBlockTest09002 extends CodeBlockTest09001{
    private int c = getC();
    {
        System.out.println("CodeBlockTest09002的普通代码块。");
    }
    public int getC(){
        System.out.println("getC()方法。");
        return 3;
    }

    //------------------------------------------------------------------------------------------------
    private static int d = getD();
    static {
        System.out.println("CodeBlockTest09002的静态代码块。");
    }

    public static int getD(){
        System.out.println("getD()方法。");
        return 4;
    }
    //------------------------------------------------------------------------------------------------
    public CodeBlockTest09002(String NAME){
        super(NAME);
        System.out.println("CodeBlockTest09002(String NAME)构造器。");
    }
}

class CodeBlockTest09003 extends CodeBlockTest09002{
    private int e = getE();
    {
        System.out.println("CodeBlockTest09003的普通代码块。");
    }
    public int getE(){
        System.out.println("getE()方法。");
        return 5;
    }

    //------------------------------------------------------------------------------------------------
    private static int f = getF();
    static {
        System.out.println("CodeBlockTest09003的静态代码块。");
    }

    public static int getF(){
        System.out.println("getF()方法。");
        return 6;
    }
    //------------------------------------------------------------------------------------------------
    public CodeBlockTest09003(String NAME){
        super(NAME);
        System.out.println("CodeBlockTest09003(String NAME)构造器。");
    }
}

输出结果为:

getB()方法。

CodeBlockTest09001的静态代码块。

getD()方法。

CodeBlockTest09002的静态代码块。

getF()方法。

CodeBlockTest09003的静态代码块。

//从结果上看先输出父类的静态属性和静态代码块(按实际在代码中的顺序输出),再输出子类的静态属性和静态代码块

getA()方法。

CodeBlockTest09001的普通代码块。

CodeBlockTest09001(String NAME)构造器。

getC()方法。

CodeBlockTest09002的普通代码块。

CodeBlockTest09002(String NAME)构造器。

getE()方法。

CodeBlockTest09003的普通代码块。

CodeBlockTest09003(String NAME)构造器。

//紧接着是父类的普通属性和普通代码块(同样地按实际在代码中的顺序输出),再输出父类的构造器。然后是子类的普通属性和普通代码块(同样地按实际在代码中的顺序输出),再输出子类的构造器。

/*new CodeBlockTest09001("少林呱呱");

        new CodeBlockTest09002("富贵呱呱");*/

当这两行不注释时,输出也有相同的效果。并且其输出也说明了静态代码块只执行一次即只输出一次。

普通代码块和静态代码块调用的不同:

package CodeBlockTest;

public class CodeBlockTest10 {
    public static void main(String[] args) {

    }
}
class CodeBlockTest10001{
    public int x;
    public static int y;
    public int getX(){
        return this.x;
    }
    public static int getY(){
        return y;//this???
    }
    {
        x = 10;
        System.out.println(getX());
        y = 20;
        System.out.println(getY());
    }
    static {
        y = 100;
        System.out.println(getY());
        //x = 200;
        //System.out.println(getX());
        //静态代码块只能调用静态属性和静态方法。
    }
}

  • 36
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值