理论知识看目录,代码详解看内容
一、代码块入门
代码块又称为初始化块,属于类中的成员(类的一部分),类似于方法,可以将逻辑语句封装在方法体中,使用{} 包围。
和方法不同,代码块没有方法名,没有返回,没有参数,只有方法体,而且不通过对象或者类显式调用,而是加载类或创建对象时隐式调用。
基本语法:
[修饰符] {
};
? 修饰符可选,如果写的话只能写static ? 代码块分为两类,没有static 修饰的叫普通代码块、非静态代码块;有static 修饰的叫静态代码块 ? 代码块中可以写任意业务逻辑语句 ? ; 可以省略,也可以写上
代码块的作用: 相当于是另一种形式的构造器,可以做初始化操作。例如:如果多个构造器中有重复的语句,可以抽取到代码块中,提高代码的重用性。
代码块的调用顺序优先于构造器
创建一个有代码块的类:
public class Cook {
{
System.out.println("买菜");
System.out.println("洗菜");
System.out.println("切菜");
}
public Cook() {
System.out.println("炒白菜");
}
public Cook(String name) {
System.out.println("炒" + name);
}
}
执行测试方法可以看到以下结果: 可见,代码块的执行顺序是在构造方法之前
二、代码块详解(超祥)
1. static代码块也叫静态代码块,作用就是对类进行初始化,而它随着类的加载而执行,并且只会执行一次,如果是普通的代码块,每创建一个对象就执行。
创建一个类
class AAA {
static {
System.out.println("AAA的静态代码块执行了...");
}
{
System.out.println("AAA的普通代码块执行了...");
}
}
执行以下代码:
public static void main(String[] args) {
AA aa = new AA();
AA aa2 = new AA();
}
可以看到,类只会被加载一次,所以静态代码块也只会执行一次,而普通代码块会随着类实例的创建而执行
2. 类是么时候被加载?
① 创建对象实例时(new)
class AA {
static {
System.out.println("AA的静态代码块执行了...");
}
}
执行下面代码,可以看到如下结果,说明创建对象时类有被加载
public static void main(String[] args) {
AA aa = new AA();
}
② 创建子类对象实例时,父类也会被加载
创建一个子类BB类,是AA类的子类
class BB extends AA {
static {
System.out.println("BB的静态代码块执行了...");
}
}
执行下面代码,可以看到如下结果
public static void main(String[] args) {
BB bb = new BB();
}
可以看到父类会先被加载,子类后被加载,继承时有说到过
③ 使用类的静态成员时(静态属性、静态方法)
创建一个带有静态成员的CC类
class CC {
public static int n1 = 100;
static {
System.out.println("CC的静态代码块执行了...");
}
}
执行以下代码:
public static void main(String[] args) {
System.out.println(CC.n1);
}
可以看到如下结果:
因为在main方法中调用了CC类的静态属性,所以类会被加载
3. 创建一个对象时,在一个类中的调用顺序:
① 静态属性初始化和静态代码块
静态属性初始化和静态代码块的优先级一样,如果有多个静态代码块和静态变量初始化,则按照代码定义的顺序。
定义一个A类:
class A {
private static int n1 = getVal();
static {
System.out.println("A类的静态代码块执行了");
}
public static int getVal() {
System.out.println("getVal() 方法执行了");
return 10;
}
}
执行以下代码:
public static void main(String[] args) {
A a = new A();
}
可以看到,静态变量初始化先被执行,因为静态变量初始化调用了getVal 方法,所以该方法会先执行,然后将值赋给n1 ,然后代码继续向下执行,会继续执行静态代码块
② 普通属性初始化和普通代码块
普通属性初始化和普通代码块的优先级一样,如果有多个普通代码块和成员变量初始化,则按照代码定义的顺序。
增加A的代码:
class A {
{
System.out.println("A类的普通代码块执行了");
}
private static int n1 = getVal();
private int n2 = getVal2();
static {
System.out.println("A类的静态代码块执行了");
}
public static int getVal() {
System.out.println("getVal() 方法执行了");
return 10;
}
public int getVal2() {
System.out.println("getVal2() 方法执行了");
return 20;
}
}
执行以下代码:
public static void main(String[] args) {
A a = new A();
}
因为静态时随着类加载的,所以静态的会先执行,而非静态的也是根据顺序执行的。
③ 构造器
在A类上增加一个无参构造器:
class A {
{
System.out.println("A类的普通代码块执行了");
}
private static int n1 = getVal();
private int n2 = getVal2();
static {
System.out.println("A类的静态代码块执行了");
}
public A() {
System.out.println("A类的无参构造器执行了");
}
public static int getVal() {
System.out.println("getVal() 方法执行了");
return 10;
}
public int getVal2() {
System.out.println("getVal2() 方法执行了");
return 20;
}
}
执行以下代码:
public static void main(String[] args) {
A a = new A();
}
可以看到构造器时最后执行的
4. 构造器最前面隐含了super()和调用普通代码块
class 类名 extends 父类名 {
public 类名() {
super();
...
}
}
所以到目前为止,一个类的执行顺序应该是:
父类静态代码块→本类静态代码块→父类的普通代码块→父类的构造器→本类的普通代码块→本类的构造器
因为在构造器中先有super再执行普通代码块的,所以会一直追溯到Object类,到Object类时没有父类了,就开始执行Object的普通代码块(虽然没有),然后执行Object类的构造方法,执行后会往下找下一级的普通代码块,然后是构造方法,以此类推,所以普通代码块的执行顺序是优先于构造方法的。
5. 有继承关系的静态代码块、静态属性初始化、普通代码块、普通代码初始化、构造方法的调用顺序:
① 父类静态代码块和静态属性初始化(优先级按代码顺序执行)
② 子类静态代码块和静态属性初始化(优先级按代码顺序执行)
③ 父类普通代码块和普通属性初始化(优先级按代码顺序执行)
如果父类属性初始化时调用了方法,并且子类重写了该方法,执行的是子类的方法
④ 父类构造方法
⑤ 子类普通代码块和普通属性初始化(优先级按代码顺序执行)
⑥ 子类构造方法
创建测试方法,看下执行什么:
package object05;
public class Test001 {
public static void main(String[] args) {
new Zi();
}
}
class Fu {
{
System.out.println("Fu类的普通代码块");
}
static {
System.out.println("Fu类的静态代码块");
}
private static int n1 = getInt1();
private int n2 = getInt2();
public Fu() {
System.out.println("Fu类的构造方法");
}
public static int getInt1() {
System.out.println("Fu类的初始化静态成员的方法");
return 10;
}
public int getInt2() {
System.out.println("Fu类的初始化普通成员的方法");
return 10;
}
}
class Zi extends Fu {
private static int n3 = getInt3();
private int n4 = getInt4();
{
System.out.println("Zi类的普通代码块");
}
static {
System.out.println("Zi类的静态代码块");
}
public Zi() {
System.out.println("Zi类的构造方法");
}
public static int getInt3() {
System.out.println("Zi类的初始化静态成员的方法");
return 10;
}
public int getInt4() {
System.out.println("Zi类的初始化普通成员的方法");
return 10;
}
}
因为父类会先被加载并且父类的静态代码块在上面,所以先执行父类的静态代码块再执行父类的初始化静态成员的方法。 然后加载子类,子类的静态成员在静态代码块的上面,所以会先被初始化,然后执行子类的静态代码块。 当类加载完后,会先执行父类的普通代码块和普通成员初始化,也是根据代码顺序执行的。 再然后执行父类的构造方法。 父类执行完毕后,就轮到子类了,也是根据顺序执行普通代码块和普通成员初始化,最后执行子类构造。
6. 静态代码块只能对用静态成员(静态属性和静态方法),普通代码块可以调用任意成员
class C {
private int n1 = 10;
private static int n2 = 20;
{
System.out.println(n1);
System.out.println(n2);
}
static {
System.out.println(n2);
}
}
|