继承是所有OOP语言不可缺少的部分,在java中使用extends关键字来表示继承关系。当创建一个类时,总是在继承,如果没有明确指出要继承的类,就总是隐式地从根类Object进行继承。java语言不支持多重继承,也就是说,子类至多只能有一个父类。
1.继承的作用
通过继承,子类可以使用父类中的一些成员变量和方法,从而提高代码的重用性,提高开发效率。
2.哪些类不能被继承
被final修饰的类不能被继承。java中常见的不能被继承的类有:String,StringBuffer,StringBuilder,以及基本类型的包装类Double,Integer,Long等。
3.继承的实现
3.1 子类可以继承父类原有的属性和方法,也可以增加父类所不具备的属性和方法。
package zhishidian; class Pet {
//父类的属性,有被private和public修饰的 private String name = "无名氏"; //昵称 private int health = 100; //健康值 public int love = 0; //亲密度 /** * 无参构造方法 */ public Pet(){ this.health = 95; System.out.println("执行宠物的无参构造方法"); } /** * 有参构造方法 * @param name 昵称 */ public Pet(String name){ this.name = name; } public String getName(){ return name; } public int getHealth(){ return health; } public int getLove(){ return love; } /** * 输出宠物信息 */ public void print(){ System.out.println("宠物的自白: 我的名字叫" + this.name + ",我的健康值是" + this.health + ",我和主人的亲密程度是" + this.love + "。"); } } public class Dog extends Pet { //子类可以增加父类所不具备的属性和方法 private String strain;//品种 public String getStrain(){ return strain; } /** * 有参构造方法 * @param name 昵称 * @param strain 品种 */ public Dog(String name, String strain){ //此处不能使用this.name=name; this.name继承自父类,但是被private修饰。 super(name); this.strain = strain; } public static void main(String[] args) { // 1.创建宠物对象pet并输出信息 Pet pet = new Pet("贝贝"); pet.print(); // 2.创建狗狗对象dog并输出信息 Dog dog = new Dog("欧欧","雪纳瑞"); //子类继承父类原有的属性和方法 dog.print(); System.out.println(dog.love); } }
3.2 直接重写父类中的方法。
重写(又叫覆盖)是继承的实现方式之一,也就是说只有在子类中才会出现方法的重写。重写是指在子类中保留父类成员方法的名称不变,参数列表不变,重写成员方法的实现内容,修改成员方法的返回值类型,或更改成员方法的存储权限。(修改存储权限只能从小的范围到大的范围修改,例如private可以修改为public,但是反向出错)
package com.java.test; class ExtendsTest{ protected int adds(int a,int b){ int s=0; s=a+b; return s; } protected ExtendsTest doIt(){ return null; } } public class tests extends ExtendsTest{ //重写成员方法的实现内容,修改成员方法的存储权限 public int adds(int a,int b){ return a; } //修改成员方法的返回值类型 protected tests doIt(){ return null; } //重构,只重写实现内容 protected int adds(int a,int b){ System.out.println("重构"); return a+b; } }
4.父类中不能被子类继承的内容
4.1 父类中的构造函数不能被子类继承
构造函数必须与类名一致,并且不能有返回值(包括void),主要作用是完成对象的初始化工作。构造函数总是伴随着new操作一起执行,且不能由程序的编写者直接调用,必须要由系统调用,且只在对象实例化时自动调用一次(普通方法可以被对象调用多次)。
当父类没有提供无参的构造函数时,子类的构造函数必须显式地调用父类的构造函数,如果父类提供了无参数的构造函数,子类可以不显式调用父类的构造函数,编译器默认会调用父类提供的无参构造函数。当有父类时,在实例化对象时会先执行父类的构造函数,然后执行子类的构造函数。
package zhishidian; class FathersClass{ public FathersClass(String s){ System.out.println("父类的构造函数执行"); } } public class ConstructorTest extends FathersClass{ /* * 当父类没有提供无参数的构造方法时,子类的构造函数(子类的构造函数可以有参数也可以没有参数)中必须显式 * 地调用父类的构造函数,使用super(参数名)调用,若没有显示调用,出错; * 当父类提供了无参的构造方法时,子类不需要显式调用父类的无参构造方法,编译器会默认调用父类的构造方法; * */ public ConstructorTest() { super("s"); System.out.println("子类的构造函数执行"); } public static void main(String[] args){ //ConstructorTest继承了FathersClass,继承父类就是要获得其成员变量成员方法, ConstructorTest ct=new ConstructorTest(); } }
运行结果:
父类的构造函数执行 子类的构造函数执行
父类中没有无参的构造函数,若在子类的构造方法中不使用super("s")进行显式调用父类的有参构造函数,出错:
4.2 子类只能继承父类的非私有(被public和protected修饰)的成员变量和方法。