搞懂Java代码块

2021/6/20 17:20:19

本文主要是介绍搞懂Java代码块,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

一、代码块简介

来自:韩顺平老师的基础课程讲解

1.1 基本介绍

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

1.2 基本语法

  • 基本语法

[修饰符] {
代码
};

注意:

  • 修饰符可选,要写的话,也只能写static。
    • 使用static 修饰的叫:静态代码块。
    • 没有static修饰的叫:普通代码块。
  • 逻辑语句可以为任何逻辑语句(输入、输出、方法调用、循环、判断等)
  • ; 号可以写上,也可以省略。

1.3 代码块的好处

代码块的好处和案例演示:

  • 相当于另外一种形式的构造器(对构造器的补充机制),可以做初始化的操作。
  • 每创建一个类对象,都会调用一次普通代码块。
  • 场景:如果多个构造器中都有重复的语句,可以抽取到初始化块中,提高代码的重用性。

案例:CodeBlock01.java

package com.alan.codeblock;
/**
 * 开发人员: Zzw
 * 开发时间: 2021/6/20  9:39
 * 文件名称: CodeBlock01
 * 开发工具: IntelliJ IDEA
 * 项目功能: 代码块入门
 */
public class CodeBlock01 {
    public static void main(String[] args) {
        Movie movie1 = new Movie();
        System.out.println("==========");
        Movie movie2 = new Movie("潘叔劝你...");
        System.out.println("==========");
        Movie movie3 = new Movie("嘎子,你把握不住",66);
    }
}

class Movie{
    private String name;
    private double price;

    // 代码块
    {
        System.out.println("电影前戏");
        System.out.println("电影开始");
    }
    public Movie(){
        System.out.println("Movie()...");
    }

    public Movie(String name) {
        System.out.println("Movie(String name)...");
        this.name = name;
    }

    public Movie(String name, double price) {
        System.out.println("Movie(String name, double price)...");
        this.name = name;
        this.price = price;
    }
}

输出:

电影前戏
电影开始
Movie()...
==========
电影前戏
电影开始
Movie(String name)...
==========
电影前戏
电影开始
Movie(String name, double price)...

=> 对于普通代码块,在创建(new)一个对象时,不管调用的哪个构造器,都会先执行一遍代码块的内容。

二、代码块使用细节

2.1 静态代码块与类加载

  • static代码块也叫静态代码块,作用就是对类进行初始化,而且它随着类的加载而执行,并且只会执行一次。如果是普通代码块,每创建一个对象,就执行一次。
    • static 静态代码块只可以放在类中,独立于其他方法。
    • 静态方法与静态代码块中不允许有 this 关键字。
  • 类什么时候被加载【重要】
    • 创建对象实例时(new)
    • 创建子类对象实例,父类也会被加载(先加载父类)
    • 使用类的静态成员时(静态属性,静态方法)
    • 使用反射:Class.forName();
  • 普通的代码块,在创建对象实例时,会被隐式的调用。被创建一次,就会调用一次。如果只是使用类的静态成员时,普通代码块并不会执行,和类的加载无关。

案例:CodeBlockDetail01.java

package com.alan.codeblock;

import org.junit.Test;

/**
 * 开发人员: Zzw
 * 开发时间: 2021/6/20  11:20
 * 文件名称: CodeBlockDetail01
 * 开发工具: IntelliJ IDEA
 * 项目功能: 代码块的使用细节
 */
public class CodeBlockDetail01 {
    public static void main(String[] args) {
        // 类加载的情况举例
        // 1.创建该类对象时
        // 静态代码块只执行一次 在类加载时执行
        Dog dog = new Dog();

        // 2.创建子类对象实例 父类也会被加载 而且父类先加载 子类再加载
        Son son = new Son();

        // 3.使用类的静态成员(静态属性和静态方法)
        System.out.println("Dog.name: " + Dog.name);
    }
}

class Dog{
    public static String name = "哈哈";
    {
        System.out.println("Dog的 普通代码块 执行...");
    }
    static {
        System.out.println("Dog的 静态代码块 执行...");
    }
}

class Father {
    static {
        System.out.println("老子的 静态代码块 执行...");
    }
}

class Son extends Father {
    public static String name = "儿子";

    static {
        System.out.println("儿子的 静态代码块 执行...");
    }
}

输出:

Dog的 静态代码块 执行...
Dog的 普通代码块 执行...
Dog的 普通代码块 执行...
老子的 静态代码块 执行...
儿子的 静态代码块 执行...
Dog.name: 哈哈

解释:

  1. Dog dog1 = new Dog();
    输出1:Dog的 静态代码块 执行…
    解释:创建(new) Dog 对象 => 类的加载 => Dog的静态代码块就会执行
    输出2:Dog的 普通代码块 执行…
    解释:创建(new) Dog 对象 => Dog的普通代码块就会执行
  2. Dog dog2 = new Dog();
    输出:Dog的 普通代码块 执行…
    解释:创建(new) Dog 对象 => Dog的普通代码块就会执行,之前类已经加载(智慧加载一次),所以不会执行静态代码块
  3. Son son = new Son();
    输出1:老子的 静态代码块 执行…
    输出2:儿子的 静态代码块 执行…
    解释:创建(new) Son对象 => Son 类的加载 => Father 类的加载 => Father 的静态代码块执行 => Son 的静态代码块执行
  4. System.out.println("Dog.name: " + Dog.name);
    输出:Dog.name: 哈哈
    解释:Dog 类已被加载,不会再次执行静态代码块,如果没有 1和2 的创建对象,此时会先输出“Dog的 静态代码块 执行… ”,再输出“Dog.name: 哈哈”

2.2 单类代码块的调用顺序

创建一个对象时,在一个类调用顺序是:(重点,难点)

  1. 调用 静态代码块和静态属性 初始化
    注意:静态代码块和静态属性初始化调用的优先级一样,如果有多个静态代码块和多个静态变量初始化,则按定义顺序调用
  2. 调用 普通代码块和普通属性 的初始化
    注意:普通代码块和普通属性初始化调用的优先级一样,如果有多个普通代码块和多个普通属性初始化,则按定义顺序调用
  3. 调用构造方法

案例:CodeBlockDetail02.java

package com.alan.codeblock;

/**
 * 开发人员: Zzw
 * 开发时间: 2021/6/20  14:31
 * 文件名称: CodeBlockDetail02
 * 开发工具: IntelliJ IDEA
 * 项目功能:
 */
public class CodeBlockDetail02 {
    public static void main(String[] args) {
        Number number = new Number();
    }
}

class Number {
    public Number() {
        System.out.println("Number() 构造器...");
    }

    public int num1 = getNum1();

    public int getNum1() {
        System.out.println("getNum1() 普通函数调用...");
        return 233;
    }

    {
        System.out.println("普通代码块 调用...");
    }

    public static int num2 = getNum2();

    static {
        System.out.println("静态代码块 调用...");
    }

    public static int getNum2() {
        System.out.println("getNum2() 静态函数调用...");
        return 688;
    }
}

输出:

getNum2() 静态函数调用...
静态代码块 调用...
getNum1() 普通函数调用...
普通代码块 调用...
Number() 构造器...

注:可以通过查看 Number.class文件 => 普通代码块是放在构造器中,并先执行,详情继续往下读。
Number.class

package com.alan.codeblock;

class Number {
    public int num1 = this.getNum1();
    public static int num2 = getNum2();

    public Number() {
        System.out.println("普通代码块 调用...");
        System.out.println("Number() 构造器...");
    }

    public int getNum1() {
        System.out.println("getNum1() 普通函数调用...");
        return 233;
    }

    public static int getNum2() {
        System.out.println("getNum2() 静态函数调用...");
        return 688;
    }

    static {
        System.out.println("静态代码块 调用...");
    }
}

2.3 构造器与普通代码块

无参构造器的最前面其实隐含了super()和调用普通代码块,静态相关的代码块,属性初始化,在类加载时,就执行完毕,因此是优先于构造器和普通代码块执行的。

class A {
	public A() {
    	// 隐藏的代码
        // 1. super();
        // 2.调用普通代码块
        System.out.println("A 的无参构造器...");
    }
}

案例:CodeBlockDetail03.java

package com.alan.codeblock;

/**
 * 开发人员: Zzw
 * 开发时间: 2021/6/20  15:10
 * 文件名称: CodeBlockDetail03
 * 开发工具: IntelliJ IDEA
 * 项目功能: 构造器与普通代码块
 */
public class CodeBlockDetail03 {
    public static void main(String[] args) {
        new Student();
    }
}

class Person {

    {
        System.out.println("Person 的普通代码块被执行...");
    }

    public Person() {
        // super();
        // 调用普通代码块
        System.out.println("Person 的无参构造器被执行...");
    }
}


class Student extends Person{

    {
        System.out.println("Student 的普通代码块被执行...");
    }

    public Student() {
        // super();
        // 调用普通代码块
        System.out.println("Student 的无参构造器被执行...");
    }
}

输出:

Person 的普通代码块被执行...
Person 的无参构造器被执行...
Student 的普通代码块被执行...
Student 的无参构造器被执行...

解释:在创建(new) Student 类时,调用顺序:

  1. 子类无参构造器中调用隐藏的 super()
  2. 通过子类的 super() 调用父类的无参构造器
  3. 父类的无参构造器中再去调用父类的父类(Object)的无参构造器…
  4. 父类的普通代码块
  5. 父类的无参构造器中实际的代码
  6. 子类的普通代码块
  7. 子类的无参构造器中实际的代码

2.4 静态与普通代码之间的调用

静态代码块只能直接调用静态成员(静态属性和静态方法),普通代码块可以调用任意成员。
这个也很好理解哦:

  • 静态代码块与静态变量 是在类加载时为其申请的内存空间,此时并没有创建类,也就不存在类的实例成员变量(普通代码),就无法调用普通代码块与普通变量,静态方法中也不能使用 this 关键字。
  • 创建类后,内存就有了普通代码与静态代码,所以普通代码可以调用静态成员与方法。

三、



这篇关于搞懂Java代码块的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程