java基础之继承
一、定义
就如上一篇文章所属,在二维平面中所有的图形都可以抽象为形状
。对于这个抽象也有自己基本的属性,每一种图形都有自己面积
与周长
的计算方式。可以把共有的属性提取出来放在一个类中,在这里我们称之为基类
、父类
。后面有其他的二维图形如三角形
、正方形
等,只要使用关键字extends
即可复用我们抽象出来的方法,通过这个关键字产生的类称之为子类
。上述方式称之为继承
。所以继承表述的是一种is-a
的关系,A是B的一种,那么可以表示为A继承与B。
二、继承初始化顺序
public class Shape {
private Float circumference;
private Float square;
public Shape(){
System.out.println("基类初始化");
}
}
public class EquilateralTriangle extends Shape{
private Float length;
private Float height;
public EquilateralTriangle(){
System.out.println("子类初始化");
}
}
//输出结果
基类初始化
子类初始化
从代码的结构可以看出,在子类初始化的时候假如基类存在无参数的构造函数,子类的构造函数将会隐式的调用基类的无参数构造。在Java
中不指定构造函数,会有默认的无参数构造函数。但是在基类中指定了构造函数,则需要使用super
关键字对基类进行初始化。
三、protected关键字
在上述的案例中,一个图形的周长和面积是根据具体的图形以及其自己计算方式计算出来,但是这两个值的计算只有在形状被确定下来之后,计算才会有意义。所以需要在子类中调用set
方法设置对应的值,但是又不希望被其他的类调用。在这里就需要使用到protected
关键字。protected
关键字访问权限是同包或者子类可以访问,在其他包中的其他类是不可以被访问。
public class Shape {
private Float circumference;
private Float square;
public Shape(){
}
public Float getSquare() {
return square;
}
public Float getCircumference() {
return circumference;
}
protected void setCircumference(Float circumference){
this.circumference = circumference;
}
protected void setSquare(Float square){
this.square = square;
}
}
public class EquilateralTriangle extends Shape{
private Float length;
private Float height;
public EquilateralTriangle(Float length, Float height){
if (length > 0 && height > 0){
this.height = height;
this.length = length;
Float circumference = length*3;
setCircumference(circumference);
Float square = length*height/2;
setSquare(square);
}
}
@Override
public Float getCircumference() {
return super.getCircumference();
}
@Override
public Float getSquare() {
return super.getSquare();
}
public void setLength(Float length) {
this.length = length;
Float circumference = length*3;
setCircumference(circumference);
Float square = length*height/2;
setSquare(square);
}
public void setHeight(Float height) {
this.height = height;
Float circumference = length*3;
setCircumference(circumference);
Float square = length*height/2;
setSquare(square);
}
public static void main(String[] args) {
EquilateralTriangle equilateralTriangle = new EquilateralTriangle(1F,2F);
System.out.println(equilateralTriangle.getCircumference());
System.out.println(equilateralTriangle.getSquare());
}
}
//输出结果
3.0
1.0
四、继承优点以及缺点
- 优点
- 代码复用,可以较少重复代码。
- 缺点
- 继承层次过深,会导致代码结构复杂,耦合性强。
- 基类的修改会直接影响到子类。
五、向上转型
在上面我们谈到is-a
的关系,等边三角形是一种平面图形。我们可以说等边三角形是一种图形但是不能说图形就是等边三角形。如下
public class Shape {
private Float circumference;
private Float square;
// get set等各种方法
public Shape(){
System.out.println("基类初始化");
}
static void printSquare(Shape shape){
System.out.println(shape.getSquare());
}
}
public class EquilateralTriangle extends Shape{
private Float length;
private Float height;
// get set等各种方法
public static void main(String[] args) {
EquilateralTriangle equilateralTriangle = new EquilateralTriangle(1F,2F);
Shape.printSquare(equilateralTriangle);
}
}
通过Shape.printSquare(equilateralTriangle)
,可以看出equilateralTriangle
是Shape
类型。
将子类转换成父类,在继承关系上面是向上移动的,所以一般称之为向上转型。由于向上转型是从一个叫专用类型向较通用类型转换,所以它总是安全的,唯一发生变化的可能就是属性和方法的丢失。这就是为什么编译器在未曾明确表示转型
或未曾指定特殊标记
的情况下,仍然允许向上转型的原因。
六、继承的使用场景
在Java编程思想
中有句话可作为判断是否使用继承一句:问一问自己是否需要从子类向父类进行向上转型。如果必须向上转型,则继承是必要的,但是如果不需要,则应当好好考虑自己是否需要继承。