代码块又称初始化块,属于类中的成员,即类的一部分。类似于方法,将逻辑语句封装在方法体中,用{}包围起来。与方法不同的是,代码块没有方法名,没有返回值,没有参数,只有方法体,而且不能通过对象或类显式调用,而是在加载类时或创建对象时隐式调用。
代码块可以用访问修饰符修饰,也可以写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());
//静态代码块只能调用静态属性和静态方法。
}
}