Java三大特性之继承
一、介绍
笔记重点:构造器、protected关键字(这个自行查阅)、向上转型、private关键字(继承非常重要的要点)
复用代码是Java众多引人注目的功能之一。但要想成为极具革命性的语言,仅仅能够复制代码并对加以改变是不够的,它还必须能够做更多的事情。在这句话中最引人注目的是“复用代码”,尽可能的复用代码使我们程序员一直在追求的。
继承是使用已存在的类的定义作为基础建立新类的技术,新类的定义可以增加新的数据或新的功能,也可以用父类的功能,但不能选择性地继承父类。通过使用继承我们能够非常方便地复用以前的代码,能够大大的提高开发的效率。
为什么要继承?
答:通过继承可以使对对象的描述更加清晰,可以实现代码的复用,可以实现重写类中的变量或方法。
二、深入理解继承
1.Java的继承具有单继承的特点,即只能继承一个父类,每个子类只有一个直接父类,但是其父类又可以继承于另一个类,从而实现了子类可以间接继承多个父类,但其本质上划分仍然是一个父类和子类的关系。
2.Java的继承通过extends关键字来实现,实现继承的类被称为子类,被继承的类称为父类(有的也称其为基类、超类)
3.子类拥有父类非private的属性和方法。(便于理解是这么和你们说的,这种说法其实是错的 =.=!暂且这么理解好了。)
4.子类可以拥有自己属性和方法,即子类可以对父类进行扩展。
5.子类可以用自己的方式实现父类的方法。
6.为什么国内把extends翻译成继承呢?除了与历史原因有关外,把extends翻译成为继承也是有其道理的:子类扩展父类,将可以获得父类的全部属性和方法,这与汉语中得继承(子辈从父辈那里获得一笔财富成为继承)具有很好的类似性。
三、什么样的类不能被继承呢?
1.final(终态)修饰的类是无法被继承的,但是也不要误解有final的类都不能被继承。
Example 1:
Example 2:
2.构造器(注意下面有构造器的相关介绍)被private关键字修饰的类无法被继承。必须定义显式构造函数才能被继承(例如被protected与private关键字修饰)。
Example 1:
四、构造器(简单介绍上边有个例子)
什么是构造器?
答:简单来说构造器即是与类名相同的特殊方法,但它没有返回值。深入来说Java中引入构造器,确保每一个对象都得到初始化,Java在有能力操作对象之前,系统会自动调用相应的构造器,保证初始化的进行。构造器最大的用处就是在创建对象时执行初始化,当创建一个对象时,系统会为这个对象的实例进行默认的初始化。如果想改变这种默认的初始化,就可以通过自定义构造器来实现。在java中,使用new关键是调用构造器,从而返回该类的实例。也就是说:构造方法是定义在Java类中的一个用来初始化对象的方法,是创建对象的根本途径 。
使用构造器要注意一下几点:1.如果我们没有自己定义构造方法,系统就会默认为该类提供一个无参的构造器。一旦我们为一个类提供了构造器,系统将不会该类提 默认无参的构造器。(下面提供例子 Example 1、2 供理解)
2.有参的构造方法和无参构造方法是可以同时存在的(方法重载)//不提供例子,自己敲。
3.构造方法不但可以给属性赋值,还可以保证给对象的属性赋一个合理的值。
Example 1:
1 public class test1 { 2 public static void main(String[] args) { 3 // TODO 自动生成的方法存根 4 test2 p=new test2(); 5 System.out.println("Hello"); 6 } 7 } 8 class test2{ 9 private String b; 10 //public test2(){}---系统已经默认为该类提供无参的构造方法 11 }
Example 2:
1 public class test1 { 2 public static void main(String[] args) { 3 // TODO 自动生成的方法存根 4 //new关键是调用构造器,返回该类实例 5 test2 p=new test2();//报错 6 System.out.println("Hello"); 7 } 8 } 9 class test2{ 10 private String b; 11 //因为我们已经定义了有参构造方法,所以系统将不再提供默认的无参构造方法 12 public test2(String b){ 13 this.b=b; 14 } 15 }
补个重点:在构造器中调用父类构造器来完成初始化,而父类构造器具有执行父类初始化所需要的所有知识和能力。
1 public class test1 { 2 public static void main(String[] args) { 3 // TODO 自动生成的方法存根 4 //new关键是调用构造器,返回该类实例 5 test3 p=new test3();//报错 6 System.out.println("Hello"); 7 } 8 } 9 class test2{ 10 public test2(){ 11 System.out.println("test2"); 12 } 13 } 14 class test3 extends test2{ 15 public test3(){ 16 //super();---已经默认调用父类构造器 17 System.out.println("test3"); 18 } 19 } 输出: test2 test3 Hello
解析:子类继承父类是从父类向外扩展的,从父类向子类一层层扩展。子类会默认调用父类的构造器,这个调用是有前提的父类必须要有默认的构造器,如果没有就会报错,编译器会报错:无法找到符合父类形式的构造器。
1 public class test1 { 2 public static void main(String[] args) { 3 // TODO 自动生成的方法存根 4 //new关键是调用构造器,返回该类实例 5 test3 p=new test3();//报错 6 System.out.println("Hello"); 7 } 8 } 9 class test2{ 10 public test2(String name){ 11 System.out.println("test2------"+name); 12 } 13 } 14 class test3 extends test2{ 15 public test3(){ 16 super("zs");//---必须调用父类有参构造器 17 System.out.println("test3"); 18 } 19 } 输出: test2------zs test3 Hello
总结:继承时子类会默认调用父类的构造器,但是如果没有默认的父类构造器,子类必须要显示的指定父类的构造器,而且必须是在子类构造器中做的第一件事(第一行代码)。
五、向上转型
继承中父类与子类是“is a”的关系,比如“学生”类继承了“人”这个类,那么可以说学生是一个人(这就是向上转型)。向上转型是安全的,但是可能会丢失一部分属性和方法(转型过程中,子类的新有的方法都会遗失掉,在编译时,系统会提供找不到方法的错误)。
1 public class test1 { 2 public static void main(String[] args) { 3 // TODO 自动生成的方法存根 4 //new关键是调用构造器,返回该类实例 5 test3 p=new test3();//报错 6 test2 p1=new test2("351"); 7 p1.p(p);//-----转型并且是安全的 8 } 9 } 10 class test2{ 11 public test2(String name){ 12 System.out.println("test2------"+name); 13 } 14 public void p(test2 p1){ 15 System.out.println("+++++"); 16 } 17 } 18 class test3 extends test2{ 19 public test3(){ 20 super("zs"); 21 System.out.println("test3"); 22 } 23 }
扩展:
1、父类变,子类就必须变。
2、继承破坏了封装,对于父类而言,它的实现细节对与子类来说都是透明的。
3、其实子类不仅拥有父类的非private的属性和方法,也拥有父类的private的属性和方法。首先需要明确:private、public和protected这几个关键字,和继承没有关系,有关系的应该叫做“可见性”。父类不能控制自己的成员被子类继承走,但是可以约束某些被继承的成员变量,对于子类来说是不可见的。
(个人理解为子类拥有父类的所有属性和方法只是无法使用,这个不是瞎扯是可以查出来的)。