详解Java中代码块和继承

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

概念

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

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


你可能感兴趣的:(java,构造函数,代码块)