四、代码块
4.1、基本介绍
代码块又称为初始化块,属于类中的成员(即 是类的一部分),类似于方法,将逻辑语句封装在方法体中,通过{}包围起来。
但是和方法不同,没有方法名,没有返回,没有参数,只有方法体,而且不用通过对象或类显示调用,而是加载类时,或创建对象时隐式调用。
-
基本语法:
-
[修饰符]{ 代码体 };
-
说明注意:
-
修饰符 可选填,但是 写的话也只能写static
-
代码块分为两类,使用static修饰的叫静态代码块,没有static修饰的,叫非静态代码块。
-
逻辑语句可以为任何逻辑语句。
-
“;”号可以写,也可以不写!
-
-
使用场景:
-
相当于另外一种形式的构造器,可以做初始化操作
-
如果多个构造器中都有重复的语句,可以抽取到初始化块中,提高代码的重用性。
-
代码样例:(我想每个构造函数里面都去输入开场白,即可使用代码块的形式进行初始化操作)
public class text4 { public static void main(String[] args) { new Moive("送你一朵小红花"); System.out.println("-------------------------------------"); new Moive("飞驰人生",100); } } class Moive{ private String name; private double price; private String director; //代码块初始化 { System.out.println("电影屏幕打开"); System.out.println("电影广告。。。。。"); System.out.println("电影开始播放。。。。。。"); }; public Moive(String name) { this.name = name; } public Moive(String name, double price) { this.name = name; this.price = price; } public Moive(String name, double price, String director) { this.name = name; this.price = price; this.director = director; } }
-
-
4.2、代码块细节
4.2.1、细节1
static代码块也叫静态代码块,作用就是对类进行初始化,而且它随着类的加载而执行的,因为类只会加载一次,所以静态代码块只会执行一次。如果是普通代码块,每创建一个对象,就执行。
-
代码示例:
public class text05 { public static void main(String[] args) { AA aa1 = new AA(); AA aa2 = new AA(); } } class AA{ static{ System.out.println("AA类的静态代码块,,,,,,,,,,"); }; { System.out.println("AA的普通代码块。。。。。。。。。。。"); }; }
输出:
AA类的静态代码块,,,,,,,,,, AA的普通代码块。。。。。。。。。。。 AA的普通代码块。。。。。。。。。。。
-
从上述代码可以看出来,静态代码块只执行了一次,那么引出问题:类什么时候被加载呢?
4.2.2、细节2---------类什么时候被加载呢?(重点!)
-
创建对象实例时(new)比如上述代码创建AA对象;
-
创建子类对象实例,父类也会被加载
代码实例:
public class text05 { public static void main(String[] args) { AA aa1 = new AA(); AA aa2 = new AA(); } } class BB{ static{ System.out.println("父类BB的静态代码块,,,,,,,,,,"); }; { System.out.println("父类BB的普通代码块。。。。。。。。。。。"); }; } class AA extends BB{ static{ System.out.println("子类AA的静态代码块,,,,,,,,,,"); }; { System.out.println("子类AA的普通代码块。。。。。。。。。。。"); }; }
输出:
父类BB的静态代码块,,,,,,,,,, 子类AA的静态代码块,,,,,,,,,, 父类BB的普通代码块。。。。。。。。。。。 子类AA的普通代码块。。。。。。。。。。。 父类BB的普通代码块。。。。。。。。。。。 子类AA的普通代码块。。。。。。。。。。。
-
使用类的静态成员时
代码样例:
public class text05 { public static void main(String[] args) { // AA aa1 = new AA(); System.out.println("------------"); System.out.println(AA.num); } } class BB{ static{ System.out.println("父类BB的静态代码块,,,,,,,,,,"); }; { System.out.println("父类BB的普通代码块。。。。。。。。。。。"); }; } class AA extends BB{ public static int num = 23; static{ System.out.println("子类AA的静态代码块,,,,,,,,,,"); }; { System.out.println("子类AA的普通代码块。。。。。。。。。。。"); }; }
输出:
父类BB的静态代码块,,,,,,,,,, 子类AA的静态代码块,,,,,,,,,, 23
-
普通的代码块,在创建对象实例时,会被隐式的调用。被创建一次,就会调用一次。
如果只是使用类的静态成员时,普通代码块并不会执行。(如上述代码实例)
4.2.3、小结1
-
static代码块是类加载时,执行,只会执行一次,因为类只加载一次。
-
普通代码块是在创建对象时调用的,创建一次,调用一次。可以理解为 普通代码块是对构造函数的补充
-
类加载的三种情况需要背下来!
4.2.4、细节3-----高频面试题---在一个类中代码块的调用顺序:(注意这里是在 一个类 的情况)
-
先调用静态代码块和静态属性初始化(注意:静态代码块和静态属性初始化调用的优先级一样,如果有多个静态代码块和多个静态变量初始化,按照顺序执行)
-
调用普通代码块和普通属性的初始化(注意:普通代码块和普通属性初始化调用的优先级一样,如果有多个普通代码块和多个普通属性初始化,同样按照顺序执行)
-
最后调用构造方法。
代码示例:
public class text06 {
public static void main(String[] args) {
DD dd = new DD();
}
}
class DD{
private int n2 = getVal2();
static {
System.out.println("dd的静态代码块方法。。。。。。");
};
{
System.out.println("dd的普通代码块。。。。。。。");
};
private static int n1 = getVal();
public static int getVal(){
System.out.println("getVal()方法被调用");
return 10;
}
public int getVal2(){
System.out.println("getVal2()方法被调用");
return 20;
}
public DD() {
System.out.println("我是dd的无参构造方法。。。。。。。");
}
}
输出:
dd的静态代码块方法。。。。。。
getVal()方法被调用
getVal2()方法被调用
dd的普通代码块。。。。。。。
我是dd的无参构造方法。。。。。。。
-
个人理解:
-
因为类是在创建对象之前先加载的,静态变量(也就是带static的)是随着类的加载而加载的,所以先执行静态成员
-
然后创建对象,执行普通代码块 和 普通代码属性。
-
构造方法是随着对象的创建,从而初始化的,所以最后执行。
-
4.2.5、细节4--构造器 super 和 普通代码块执行顺序
构造器的最前面其实隐含了 super()和 调用普通代码块,然而静态相关的代码块,属性初始化,在类加载时,就执行完毕,因此是优先于构造器 和 普通代码块执行的。
代码示例:
输出:
我是PP的static代码块。。。。。。。。
PP的构造函数。。。。。
我是KK的普通代码块,,,,,,
KK的构造函数。。。。。。。
4.2.6、细节6--------在创建一个子类的对象时(继承关系)执行顺序 *面试题!
-
静态代码块,静态属性初始化,普通代码块,普通属性初始化,构造方法的调用顺序如下:
-
父类的静态代码块和静态属性(优先级一样,按定义顺序执行)
-
子类的静态代码块和静态属性(优先级一样,按定义顺序执行)
-
父类的普通代码块和普通属性初始化(优先级一样,按定义顺序执行)
-
父类的构造函数(如果还有就往上找)
-
子类的普通代码块和普通属性初始化(优先级一样,按定义顺序执行)
-
子类的构造函数
-
-
静态代码块只能调用静态成员,普通代码块可以调用任意成员
-
代码示例:
public class CodeBlockDetail04 { public static void main(String[] args) { //老师说明 //(1) 进行类的加载 //1.1 先加载 父类 A02 1.2 再加载 B02 //(2) 创建对象 //2.1 从子类的构造器开始 //new B02();//对象 new C02(); } } class C02 { private int n1 = m2(); private static int n2 = 200; private void m1() { System.out.println("我是m1"); } private static int m2() { System.out.println("我是m2"); return 10; } static { //静态代码块,只能调用静态成员 //System.out.println(n1);错误 System.out.println(n2);//ok //m1();//错误 m2(); } { //普通代码块,可以使用任意成员 System.out.println(n1); System.out.println(n2);//ok m1(); m2(); } } class A02 { //父类 private static int n1 = getVal01(); static { System.out.println("A02的一个静态代码块..");//(2) } { System.out.println("A02的第一个普通代码块..");//(5) } public int n3 = getVal02();//普通属性的初始化 public static int getVal01() { System.out.println("getVal01");//(1) return 10; } public int getVal02() { System.out.println("getVal02");//(6) return 10; } public A02() {//构造器 //隐藏 //super() //普通代码和普通属性的初始化...... System.out.println("A02的构造器");//(7) } } class B02 extends A02 { // private static int n3 = getVal03(); static { System.out.println("B02的一个静态代码块..");//(4) } public int n5 = getVal04(); { System.out.println("B02的第一个普通代码块..");//(9) } public static int getVal03() { System.out.println("getVal03");//(3) return 10; } public int getVal04() { System.out.println("getVal04");//(8) return 10; } //一定要慢慢的去品.. public B02() {//构造器 //隐藏了 //super() //普通代码块和普通属性的初始化... System.out.println("B02的构造器");//(10) // TODO Auto-generated constructor stub } }
抽出一个代码片段进行一个分析:
class C02 { private int n1 = m2(); private static int n2 = 200; private void m1() { System.out.println("我是m1"); } private static int m2() { System.out.println("我是m2"); return 10; } static { //静态代码块,只能调用静态成员 //System.out.println(n1);错误 System.out.println(n2);//ok //m1();//错误 m2(); } { //普通代码块,可以使用任意成员 System.out.println(n1); System.out.println(n2);//ok m1(); m2(); } }
这个代码片段,private int n1 = m2();这里是普通属性初始化调用静态方法。执行顺序:先执行静态代码块,再去执行private int n1 = m2();,然后依次按照规则执行。
以上 是我对代码块的学习记录~~欢迎小伙伴留言!