概念



1、代码块
  • 局部代码块 
    用于限定变量生命周期,及早释放,提高内存利用率
  • 静态代码块 
    对类的数据进行初始化,仅仅只执行一次。
  • 构造代码块 
    把多个构造方法中相同的代码可以放到这里,每个构造方法执行前,首先执行构造代码块。


2、继承

继承是已有的类中派生出新的类,新的类能够吸收已有类的数据属性和行为,并能扩展新的功能。



代码块的执行顺序

public class Test {
    public String name;
    public int age;

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

    {
        System.out.println("这是构造代码块");
    }

    public Test(){
        System.out.println("这是无参构造函数");
    }

    public Test(String name,int age){
        this.name = name;
        this.age = age;
        System.out.println("这是有参数构造函数");
    }

}
class TestDemo{
    public static void main(String args[]){
        Test t1 = new Test();
        System.out.println("------------------------------------");
        Test t2 = new Test("funga",24);
    }
}

大家思考下,在执行上面的main方法之后,打印出来的结果会是怎样的呢?

这是静态代码块
这是构造代码块
这是无参构造函数
------------------------------------
这是构造代码块
这是有参数构造函数

执行解析:当我们使用new Test()去创建Test类对象的时候,类加载器会加载Test类,加载的时候会执行静态代码块(如果有静态变量、静态方法也会被初始化),创建对象的时候,首先会执行构造代码块,然后执行无参构造函数。 
当我们使用new Test()去创建Test类对象的时候,该类已经载入虚拟机,所以不会再执行静态代码块,但是构造代码块每次在创建对象之前都会执行,之后执行对象的构造函数。

本文发表于个人GitHub主页,原文请移步详解Java中代码块和继承 阅读。



继承

public class Father {
    private String name;
    public int age;
    public static String SUCCESS = "成功";
    public Father(String name,int age){
        this.name = name;
        this.age = age;
        System.out.println("构造函数说:我叫" + name + ",我" + age + "了");
    }

    public static void say(String word){
        System.out.println(word);
    }

    public void intr(){
        System.out.println("我叫" + name + ",我" + age + "了");
    }

}
public class Son extends Father {

}
class TestDemo2{
    public static void main(String args){
        Son s = new Son();
    }
}

想想,我们执行上面main方法执行结果是什么呢?

解析:没错,编译就不会通过,为什么呢?因为子类只继承父类的默认构造函数,即无参数构造函数,当父类没有默认构造函数,子类不能从父类继承,所以,我们将子类作如下修改看看能否成功?

public class Son extends Father {
    public Son(){};
}

这次,我们在子类中显示增加了一个无参数的构造方法,请问这样是否可以通过编译了呢?

解析:当我们编译的时候还是报错了,为什么呢?因为,子类构造函数中必须会调用父类的构造函数,如果没有显示调用,默认调用父类无参构造函数,这里父类没有无常构造,所以编译又报错了,所以,正确的写法,是通过super关键字,在子类构造方法中显示调用父类的有参构造方法。

public class Son extends Father {
    public Son(){
        super("funga",25);
    };

    public Son(String name,int age){
        super(name,age);
    }
}

下面我们来看看子类调用父类的变量和方法情况:

class TestDemo{
    public static void main(String args[]){
        Son s = new Son();
        //下面这句是编译出错的,父类的私有成员变量(函数)是不能被继承的
       // System.out.prinlnt(s.name);
        System.out.println(s.age);
        System.out.println(s.SUCCESS);
        s.intr();
        s.say("hahaha");
        Son.intr();
    }
}

解析:子类不能继承父类的私有变量和函数,所以也无法直接使用(调用)。父类的静态变量(函数),子类也也可以通过对象或者类调用。



代码块和继承

public class Fu {
    static {
        System.out.println("这是父类的静态代码块");
    }

    {
        System.out.println("这是父类的构造代码块");
    }

    public Fu(){
        System.out.println("这是父类的构造函数");
    }
}
public class Zi extends Fu {
    static {
        System.out.println("这是子类的静态代码块");
    }

    {
        System.out.println("这是子类的构造代码块");
    }

    public Zi(){
        System.out.println("这是子类的构造函数");
    }
}
/*
    测试类
*/
class TestDemo{
    public static void main(String args[]){
        Zi z = new Zi();
        System.out.println("--------------分割线-----------------");
        Zi z2 = new Zi();
    }
}

结合上面代码块执行原理和继承的知识,我们执行测试类,会得到怎样的输出结果呢?

分析:这次我们先分析,再执行看结果: 
1、当通过new Zi()创建Zi类对象的时候,会加载Zi类,当加载Zi类的时候优先加载Fu类,那么就会执行Fu类的静态代码块,输出“这是父类的静态代码块”,继而加载Zi类,输出“这是子类的静态代码块” 
2、当加载完Fu类和Zi类之后,创建Zi类对象的时候,会优先执行Fu类的构造代码块和构造方法,也就是会输出“这是父类的构造代码块”和“这是父类的构造函数”,之后执行自身的构造代码块和构造函数,输出“这是子类的构造代码块”和“这是子类的构造函数” 
3、执行第二个new Zi()的时候,Fu类和Zi类都已经加载,所以静态代码块不会被执行。后面的步骤与上面2一样。所以,输出的结果应该是下面这样的:

这是父类的静态代码块
这是子类的静态代码块
这是父类的构造代码块
这是父类的构造函数
这是子类的构造代码块
这是子类的构造函数
--------------分割线-----------------
这是父类的构造代码块
这是父类的构造函数
这是子类的构造代码块
这是子类的构造函数

后记 
实际开发中,我们不用这么折腾自己,弄的这么复杂,但是些知识点有必要理解,加深对Java技术的认识,懂了总比不懂要好,你说呢?