Loading

Java基础之——继承的基本使用

继承的定义

继承本质:对某一批类的抽象,从而实现对现实世界更好的建模

extends的意思是“扩展”,子类是父类的扩展。

继承的特性

  • Java中类只有单继承,没有多继承
  • 子类拥有父类非 private 的属性、方法,还可以有自己特有的内容
  • 所有类都继承于Object类(默认继承)
  • 父类使用protected修饰的字段子类可以访问
  • 继承是类和类之间的关系。除此之外,类和类之间的关系还有依赖、组合、聚合等
  • 继承关系的两个类,一个为子类(派生类、扩展类),一个为父类(基类、超类)
  • 子类和父类是“is a”的关系
  • 继承是多态的前提,如果没有继承,就没有多态

优点:

  • 提高了代码的复用性(多个类相同的成员可以放到同一个类中)
  • 提高了代码的维护性(如果方法的代码需要修改,修改一处即可)
  • 继承让类与类之间产生了关系,类的耦合性增强了,当父类发生变化时子类实现也不得不跟着变化,削弱了子类的独立性
class Person {
    protected String name;
    protected int age;
}

class Student extends Person {
    public String hello() {
        return "Hello," + name;
    }
}
public interface A {
    public void eat();
    public void sleep();
}
 
public interface B {
    public void show();
}
 
public class C implements A,B {
}

super关键字

super关键字表示父类(超类)

子类引用父类的字段时,可以用super.fieldName

注意:

  • 调用父类的构造方法只能出现在子类的构造方法中,且必须在第一行
  • super中的参数决定了调用父类的哪个构造方法。如果子类构造方法中没有出现super,编译器自动加入super。
class Student extends Person {
    protected int score;

    public Student(String name, int age, int score) {
        super(name, age); // 自动调用父类的构造方法
        this.score = score;
    }
}

子类不会继承任何父类的构造方法。子类默认的构造方法是编译器自动生成的,不是继承的

阻止继承

正常情况下,只要某个类没有final修饰符,那么任何类都可以从该类继承

从Java 15开始,允许使用sealed修饰类,并通过permits明确写出能够从该class继承的子类名称。

例如,定义一个Shape类:

public sealed class Shape permits Rect, Circle, Triangle {
    ...
}

上述Shape类就是一个sealed类,它只允许指定的3个类继承它

这种sealed类主要用于一些框架,防止继承被滥用。

sealed类在Java 15中目前是预览状态,要启用它,必须使用参数--enable-preview--source 15

向上转型

子类其实是一种特殊的父类

Java允许把一个子类对象直接赋值给一个父类引用变量,自动完成类型转换,即:父类的引用指向子类类型

把一个子类类型安全地变为父类类型的赋值,被称为向上转型(upcasting)

向上转型实际上是把一个子类型安全地变为更加抽象的父类型:

Person p = new Student();

Student s = new Student();
Person p = s; // upcasting, ok
Object o1 = p; // upcasting, ok
Object o2 = s; // upcasting, ok

注意到继承树是Student > Person > Object

所以,可以把Student类型转型为Person,或者更高层次的Object

  • 上溯造型为任何一个父类类型(自动完成) 例如:Shape s = new Circle();

  • 上溯造型为实现的一个父接口(自动完成) 例如:List i = new ArrayList();

  • 下溯造型回到它自己所在的类(强制转换) 只能曾经上溯造型过的对象才能进行下溯造型

向下转型

把一个父类类型强制转型为子类类型,就是向下转型(downcasting)

Person p1 = new Student(); // upcasting, ok
Person p2 = new Person();

Student s1 = (Student) p1; // ok
Student s2 = (Student) p2; // runtime error! ClassCastException!

p1 实际指向类Student,p2 实际指向Person,在向下转型的时候,把p1转型为Student会成功,因为p1确实指向Student实例,

把p2转型为Student会失败,因为p2的实际类型是Person不能把父类变为子类,因为子类功能比父类多,多的功能无法凭空变出来。

instanceof操作符

为了避免向下转型出错,Java提供了instanceof操作符,可以先判断一个实例究竟是不是某种类型

Person p = new Person();
System.out.println(p instanceof Person); // true
System.out.println(p instanceof Student); // false

Student s = new Student();
System.out.println(s instanceof Person); // true
System.out.println(s instanceof Student); // true

Student n = null;
System.out.println(n instanceof Student); // false

利用instanceof,在向下转型前可以先判断:

Person p = new Student();
if (p instanceof Student) {
    // 只有判断成功才会向下转型:
    Student s = (Student) p; // 一定会成功
}

区分继承和组合

在使用继承时,我们要注意逻辑一致性

具有has关系不应该使用继承,而是使用组合,即Student可以持有一个Book实例

class Student extends Person {
    protected Book book;
    protected int score;
}

因此,继承是is关系,组合是has关系。

成员访问规则

子类成员变量于父类重名,创建子类对象时,有两种访问方式:

  • 直接通过子类对象访问成员变量(等号左边是谁,就优先用谁,没有则向上找)
Son son = new Son();
System.out.println(son.num);
  • 间接通过成员方法访问成员变量(该方法属于谁,就优先用谁,没有则向上找)
class Parent() {
    int num = 100;
    public void showParent() {
    	System.out.ptintln(num);
    }
}

class Son extends Parent {
    int num = 200;
    public void showSon() {
    	System.out.ptintln(num);
    }
}

class test {
    public static void main(String[] args) {
        Son son = new Son();
        son.showSon();    //该方法是子类的,优先用子类的,输出200
        son.showParent(); //该方法在父类中定义,输出100
    }
}

方法的重写

使用注解@Override表示该子类的方法是对父类方法的重写,注解的作用是用来检测方法是不是有效的重写

重写规则:

  • 方法名、参数列表相同

  • 修饰符:范围可以扩大,不能缩小(public > protected > default > private)

  • 抛出的异常:范围可以缩小,不能扩大

  • 返回值:范围可以缩小,不能扩大

  • final、私有方法不能重写

  • 静态方法只能被静态方法覆盖,非静态方法只能被非静态方法覆盖

  • 静态方法的调用只和定义的数据类型有关

  • 非静态方法的调用:编译看左边,运行看右边

class Animal {
   	public void move() {
      	System.out.println("动物可以移动");
   	}
}
 
class Dog extends Animal {
   	public void move() {
      	System.out.println("狗可以跑和走");
   	}
}
 
public class TestDog {
   	public static void main(String args[]){
      	Animal a = new Animal(); // Animal 对象
      	Animal b = new Dog(); // Dog 对象
 
      	a.move();// 执行 Animal 类的方法
      	b.move();//执行 Dog 类的方法
   	}
}
posted @ 2021-03-24 19:22  qinuna  阅读(137)  评论(0编辑  收藏  举报