继承性基础
哲学三问
什么是继承性
银行卡有很多种,有借记卡、信用卡、亲情卡、工资卡等等,他们各有不同,但都具有相同的银行卡特征,即余额、卡号等共有的属性,如果每定义一个类都需要写一次,那就太麻烦了。有没有什么办法抽取出相同特征呢?
利用继承性,我们可以在银行卡类中定义共同特征,然后将单独的特征再定义。
在Java 中,继承的关键字用的是“extends”,即子类不是父类的子集,而是对父类的“扩展”
为什么需要继承性(优点)
-
减少代码冗余,提高代码复用性
-
便于功能拓展
如果现在所有的银行卡都需要添加实名登记功能,那么我们就可以在父类中直接写,通过继承,其他子类自然可以获得这个功能,不必每个都写一遍。
-
为多态性提供了基础
怎么使用
extends
关键字
class A extends B{}
-
A: 子类、派生类、subclass
-
B: 父类、超类、基类、superclass
例如现在要让Student类继承Person类
public class Student extends Person{}
调用时可以直接当作自己的来用
Student stu = new Student("Math");
stu.setName("yuan");
stu.setAge(18);
System.out.println("student " + stu.getName() + " is " + stu.getAge() + " years old, and his/her major is " + stu.getMajor() + ".");
此时此刻我想起一句歌词。哦~~爸爸妈妈给我的不少不多~~~~
一旦子类A继承父类B以后,子类A中就获取了父类B中声明的所有的属性和方法。不过,如果是父类中的private
私有属性和方法呢?
私有属性、方法继承
一般地,我们可以浅显地理解为:继承了。但是不能直接使用,必须通过共有方法来操作。私有方法也是,必须通过共有方法来进行调用。
但是这只是表象,如果你深入理解,会发现完全不是这样,并且这个理解在逻辑上有漏洞:既然继承了这个属性和方法,就应该能够在自己的类中进行调用,你父类的私有属性和方法自己都可以调用,为什么我子类的私有方法就不行,天生低人一等?现在这个情况即使使用封装性来解释也是差强人意。
来看官方的解释:
Private Members in a Superclass
A subclass does not inherit the
private
members of its parent class. However, if the superclass has public or protected methods for accessing its private fields, these can also be used by the subclass.A nested class has access to all the private members of its enclosing class—both fields and methods. Therefore, a public or protected nested class inherited by a subclass has indirect access to all of the private members of the superclass.
翻译一下:(第二句话涉及内部类,如果没接触过可以先忽略)
父类中的私有成员
子类不会继承父类的
private
私有成员。然而,如果父类有public
或protected
方法能够访问它的私有属性,那这些属性就可以被子类使用。内部类可以访问其外部类的所有私有成员,包括属性和方法。因此,由子类继承的
public
或protected
的内部类可以间接访问父类的所有私有成员。
也就是说,子类会继承public
或protected
的成员。
分析内存后,会发现,当一个子类被实例化的时候,默认会先调用父类的构造方法对父类进行初始化,即在内存中创建一个父类对象,然后再父类对象的外部放上子类独有的属性,两者合起来成为一个子类的对象。
说人话就是:
实际上并没有被继承,但是,子类在创建时会附带着创建父类对象,因此父类对象中的private属性虽然对我来说不可见,但是我拥有你的公共方法,通过公共方法可以来对你进行操作。
为什么要这样做呢?
这样的设计的意义就是我们可以用这个方法将我们的成员保护得更好,让子类的设计者也只能通过父类指定的方法修改父类的私有成员,这样将能把类保护得更好,这对一个完整的继承体系是尤为可贵的。
关于继承性的规定
- 一个类可以被多个子类继承。
- Java中类的单继承性:一个类只能有一个父类
- 子父类是相对的概念。
- 子类直接继承的父类,称为:直接父类。间接继承的父类称为:间接父类
- 子类继承父类以后,就获取了直接父类以及所有间接父类中声明的属性和方法
所有类的父类Object
-
如果我们没有显式的声明一个类的父类的话,则此类继承于
java.lang.Object
类 -
所有的java类(除
java.lang.Object
类之外)都直接或间接的继承于java.lang.Object
类 -
意味着,所有的java类具有
java.lang.Object
类声明的功能。
练习
- 根据下图实现类。在
CylinderTest
类中创建Cylinder
类的对象,设置圆柱的底面半径和高,并输出圆柱的体积。
public class Cylinder extends Circle {
private double length;
public Cylinder() {
length = 1.0;
}
public double getLength() {
return length;
}
public void setLength(double length) {
this.length = length;
}
public double findVolume() {
return findArea() * length;
}
}
public class Circle {
private double radius;
public Circle() {
radius = 1.0;
}
public double getRadius() {
return radius;
}
public void setRadius(double radius) {
this.radius = radius;
}
public double findArea(){
return Math.PI * Math.sqrt(radius);
}
}
这是测试方法
public static void main(String[] args) {
Cylinder cylinder = new Cylinder();
cylinder.setRadius(2.0);
cylinder.setLength(3.0);
double volume = cylinder.findVolume();
System.out.println("底面半径为" + cylinder.getRadius() + ",高为" +
cylinder.getLength() + "的圆柱,它的体积为:" + cylinder.findVolume());
}
方法的重写
方法的重写(override / overwrite)
什么是重写
子类继承父类以后,可以对父类中同名同参数的方法,进行覆盖操作
为什么要重写
有时父类的方法并不适合子类,需要重写修改方法体。
怎么重写
方法的声明
权限修饰符
返回值类型
方法名(形参列表)
throws
异常的类型
{
//方法体
}
约定俗成:子类中的叫重写的方法,父类中的叫被重写的方法
- 子类重写的方法的方法名和形参列表与父类被重写的方法的方法名和形参列表相同
重写方法的权限
子类
重写的方法的权限修饰符大于等于父类
被重写的方法的权限修饰符
- 特殊地:子类不能重写父类中声明为
private
权限的方法
返回值
-
父类被重写的方法的返回值类型是void,则子类重写的方法的返回值类型只能是void
-
父类被重写的方法的返回值类型是A类型,则子类重写的方法的返回值类型可以是A类或A类的子类
-
父类被重写的方法的返回值类型是基本数据类型(比如:double),则子类重写的方法的返回值类型必须是相同的基本数据类型(必须也是double)
异常抛出
子类重写的方法抛出的异常类型不大于父类被重写的方法抛出的异常类型(具体放到异常处理时候讲)
例外:静态方法
子类和父类中的同名同参数的方法要么都声明为非static的(这时候是重写),要么都声明为static的(不是重写)。
练习
public class Circle {
private double radius;
public Circle() {
radius = 1.0;
}
public double getRadius() {
return radius;
}
public void setRadius(double radius) {
this.radius = radius;
}
public double findArea(){
return Math.PI * getRadius() * getRadius();
}
}
定义一个Cylinder
类,重写findArea()
计算圆柱表面积
答案:
public class Cylinder extends Circle {
private double length;
public Cylinder() {
length = 1.0;
}
public double getLength() {
return length;
}
public void setLength(double length) {
this.length = length;
}
//计算体积
public double findVolume() {
// return findArea() * length;
return Math.PI * getRadius() * getRadius() * getLength();
}
@Override
public double findArea() {
return Math.PI * this.getRadius() * this.getRadius() * 2
+ Math.PI * this.getRadius() * 2 * this.getLength();
}
}
protected修饰符
还记得我们学修饰符时省略了protected
吗?现在是时候去验证一下了。
public class Student extends Person{
protected String major;
}
在另一个包下创建新的类,试试调用
public class Doctor {
String work;
public Doctor() {
this.age = 1;//这时会报错
}
}
为什么会报错呢?protected的描述是不同包下子类,原来是没有加继承关键字,加上后就可以了。提醒自己不要忘记哦~~
public class Doctor extends Student {
String work;
public Doctor() {
this.age = 1;
}
面试题
面试题:区分方法的重载与重写
答案:
方法的重写Overriding和重载Overloading是Java多态性的不同表现。
重载,是一个类中多态性的一种表现,是同一方法在面对不同参数时有不同的方法对应。记住“两同一不同”:相同类,相同方法名,不同形参。如果在子类中定义某方法与其父类有相同的名称和参数,我们说该方法被重写 (Overriding)。子类的对象使用这个方法时,将调用子类中的定义,对它而言,父类中的定义如同被"屏蔽"了。
重写,父类与子类之间多态性的一种表现,是子类对父类方法相同方法的覆盖,子类重写的是父类中同名同参数的方法。也就是相同参数时,使用不同的方法来对应。如果在一个类中定义了多个同名的方法,它们或有不同的参数个数或有不同的参数类型,则称为方法的重载(Overloading)。
本文参考: