Java 代码块详解
基本介绍
代码块 又称 初始化块, 是类的一部分,属于类中的成员,类似于方法,将逻辑语句封装在方法体中,通过 {} 封装起来。 但是代码块 没有方法名,没有返回,没有参数, 只有方法体,而且不用通过对象或类显示调用,而是加载类的时候,或创建对象的时候隐式调用。
基本语法
- 修饰符可选,并且修饰符只能为 static 。
- 代码块分为两类,用 static 修饰的为静态代码块,没有 static 修饰的为普通代码块。
- 代码块中可以写任何语句(输入,输出,方法调用,循环,判断等等)。
- ; 可以省略。
代码块示例
student类
public class Student {
private int id;
private String name;
private int age;
{
System.out.println("代码块被执行~~");
}
public Student(int id, String name, int age) {
System.out.println("构造器被调用~~~");
this.id = id;
this.name = name;
this.age = age;
}
}
BlockCodeTest类
public class BlockCodeTest {
public static void main(String[] args) {
Student s = new Student(1, "王昭君", 18);
}
}
运行结果: 通过运行结果我们发现,代码块调用的顺序优先于构造器的,所以,代码块 相当于另外一种形式的构造器,可以作为构造器的补充机制,做初始化的操作。
代码块使用细节
1. static代码块也叫静态代码块,作用就是对类进行初始化,它随类加载而执行,并且只会执行一次。如果是普通代码块(非静态代码块),每创建一个对象执行一次。
🚀类加载时机【 非常重要 】
1.创建实例对象时(new) 2.创建子类对象实例时,父类也会被加载 3.使用类的静态成员时(静态属性、静态方法)
代码示例1 【创建实例对象时(new)】
public class BlockCodeTest {
public static void main(String[] args) {
A a = new A();
}
}
class A {
static {
System.out.println("静态代码块被执行~~~");
}
}
运行结果
代码示例2 【创建子类对象实例时,父类也会被加载】
public class BlockCodeTest {
public static void main(String[] args) {
B b = new B();
}
}
class A {
static {
System.out.println("A类静态代码块执行~~~");
}
}
class B extends A {
static {
System.out.println("B类静态代码块执行~~~");
}
}
运行结果:
在实例化B类的对象时,其父类A类也被加载,并且父类加载先于子类。
代码示例3 【使用类的静态成员时】
public class BlockCodeTest {
public static void main(String[] args) {
System.out.println(A.num);
}
}
class A {
public static int num = 1;
static {
System.out.println("A静态代码块执行~~~");
}
}
运行结果: 调用静态变量,触发类加载,调用了A类的静态代码块。
代码示例4 【静态代码块在类加载时执行,并且只会执行一次】
public class BlockCodeTest {
public static void main(String[] args) {
A a1 = new A();
A a2 = new A();
}
}
class A {
public static int num = 1;
static {
System.out.println("A静态代码块执行~~~");
}
}
运行结果: static代码块, 是在类加载时执行的, 而且只会执行一次。
2. 普通的代码块,在创建对象实例时,会被隐式调用,被创建一次,就会被调动一次。 可以理解为在用类的构造器实例化对象的时候,构造器中有自动调用普通代码块机制。如果只是使用类的静态成员时,普通代码块并不会执行。
代码示例1
public class BlockCodeTest {
public static void main(String[] args) {
A a1 = new A();
A a2 = new A();
A a3 = new A();
}
}
class A {
{
System.out.println("A普通代码块执行~~~");
}
}
运行结果: 实例化了3个对象,普通代码块执行了3次。
代码示例2
public class BlockCodeTest {
public static void main(String[] args) {
System.out.println(A.num);
}
}
class A {
public static int num = 1;
{
System.out.println("A普通代码块执行~~~");
}
static {
System.out.println("A静态代码块执行~~~");
}
}
运行结果: 使用类的静态成员,普通代码块并不会执行。
🚀小结:
1.static代码块是在类加载时执行的,并且只会执行一次。 2.普通代码块是在创建对象的时候调用的,与类加载无关,创建一次对象,调用一次。
3. 创建一个对象时,在一个类中的调用顺序: 1) 调用静态代码块和静态属性的初始化(静态代码块和静态属性的初始化调用的优先级一样,如果有多个静态代码块和多个静态属性初始化,则按照他们的定义顺序调用) 2) 调用普通代码块和普通属性的初始化(普通代码块和普通属性的初始化调用的优先级一样,如果有多个普通代码块和多个普通属性初始化,则按照他们定义的顺序调用) 3)最后调用构造方法
代码示例
public class BlockCodeTest {
public static void main(String[] args) {
A a = new A();
}
}
class A {
private static int num1 = getVal1();
private int num2 = getVal2();
public A() {
System.out.println("构造器被执行...");
}
static {
System.out.println("A静态代码块儿执行~~");
}
{
System.out.println("A普通代码块儿执行");
}
public static int getVal1() {
System.out.println("getVal1执行...");
return 1;
}
public int getVal2() {
System.out.println("getVal2执行...");
return 2;
}
}
运行结果: 实例化一个A对象,首先产生类加载,完成静态属性的初始化和执行静态代码块(静态代码块和静态属性的初始化调用的优先级一样,因为A类中静态属性的初始化在静态代码块的前面,所以先完成静态属性的初始化),之后执行构造器,因为构造器中有自动调用普通代码块机制和初始化普通属性的机制,所以先完成普通属性的初始化和执行普通代码块(当然这里也没有优先级),最后执行构造器中的代码。
4. 构造器的前面其实隐藏了super() 和 调用普通代码块
代码示例
public class BlockCodeTest {
public static void main(String[] args) {
B b = new B();
}
}
class A {
{
System.out.println("A类的普通代码块被执行...");
}
public A() {
System.out.println("A类的构造器被调用...");
}
}
class B extends A {
{
System.out.println("B类的普通代码块被执行...");
}
public B() {
System.out.println("B类的构造器被调用...");
}
}
运行结果: 调用B类构造器的时候,因为构造器中有隐含的super()和调用本类的普通代码块,所以,先执行其父类A类的构造器,其A类同时也有隐含的super()和调用本类的普通代码块,其父类为object,没有相关操作,最后执行结果如上。
5. 有继承关系时,他们的 静态代码块,静态属性的初始化 ,普通属性的初始化 , 普通代码块, 构造方法的调用顺序如下: ① 父类的静态代码块和静态属性(优先级一样,按定义顺序执行) ② 子类的静态代码块和静态属性(优先级一样,按定义顺序执行) ③ 父类的普通代码块和普通属性 (优先级一样,按定义顺序执行) ④ 父类的构造方法 ⑤ 子类的普通代码块和普通属性 (优先级一样,按定义顺序执行) ⑥ 子类的构造方法
代码示例
public class BlockCodeTest {
public static void main(String[] args) {
B b = new B();
}
}
class A {
private static int num1 = getVal1();
private int num2 = getVal2();
{
System.out.println("A类的普通代码块被执行...");
}
static {
System.out.println("A类的静态代码块被执行...");
}
public A() {
System.out.println("A类构造器被执行");
}
public static int getVal1() {
System.out.println("A类静态属性初始化...");
return 1;
}
public int getVal2() {
System.out.println("A类普通属性初始化...");
return 2;
}
}
class B extends A {
private static int num1 = getVal11();
private int num2 = getVal22();
{
System.out.println("B类的普通代码块被执行...");
}
static {
System.out.println("B类的静态代码块被执行...");
}
public B() {
System.out.println("B类构造器被执行");
}
public static int getVal11() {
System.out.println("B类静态属性初始化...");
return 1;
}
public int getVal22() {
System.out.println("B类普通属性初始化...");
return 2;
}
}
运行结果: 6. 静态代码块只能调用静态成员(静态属性和静态方法),普通代码块可以调用任意成员。
|