概念
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技术的认识,懂了总比不懂要好,你说呢?