小结
- 继承是面向对象编程的一种强大的代码复用方法
- Java只允许单继承,所有类最终的根类是Object
- protected允许子类访问父类的字段和方法
- 子类的构造方法可以通过super()调用父类的构造方法
- 可以安全地向上转型为更抽象的类型
- 可以强制地向下转型,最好借助instanceof判断
- 子类和父类的关系是is,has关系不能继承
继承是面向对象编程中非常强大的机制,它首先可以复用代码。当我们让一个类从另一个类中继承时,这个类就获得了另一个类的所有功能,我们只需要为这个类编写新增的功能。
java使用extends关键字来实现继承:
class Person{
private String name;
private int age;
public String getName() {...}
public void setName(String name){...}
public int getAge(){...}
public void setAge(int age){...}
}
class Student extends Person{
private int score;//不需要重复name和age字段/方法
public int getScore(){...}//只需要定义新增score字段/方法
public void setScore(int score){...}
}
通过继承,student只需要编写额外的功能,不再需要重复代码,在oop的术语中,我们把Person称为超类(super class),父类(parent class),基类(base class),把Student称为子类(subclass),扩展类(extended class)
继承树
在定义Person的时候,没有写extends。在java中,没有明确写extends的类,编译器会自动加上extends Object。所以,任何类,除了object,都会继承自某个类。
object<-Person<-Stutent(Person、Student的继承树)
protected
继承有个特点是子类无法访问父类的private字段或者private方法,例如Student类就无法访问Person类的name和age字段
class Person{
private String name;
private int age;
}
class Student extends Person{
private int score;
public String hello(){
return "hello,"+name;//编译错误:name在Person中是private访问控制
}
}
把private改为protected。用protected修饰的字段可以被子类访问,因此,protected关键字可以把字段和方法的访问权限控制在继承树内部,一个protected字段和方法可以被其子类,以及子类的子类所访问
class Person{
protected String name;
protected int age;
}
class Student extends Person{
private int score;
public String hello(){
return "hello,"+name;//ook
}
}
super
super关键字表示父类。子类引用父类的字段时,可以用super.fieldName。
class Student extends Person{
private int score;
public String hello(){
return "hello,"+super.name;//这里使用super.name或者name,或者this.name,效果都是一样的
}
}
但是,有时候就必须使用super,比如下例,如果父类没有默认的构造方法,子类就必须显式调用super()并给出参数以便让编译器定位到父类的一个合适的构造方法,否则会因为无法调用Person()的构造方法而编译失败。另一问题:子类不会继承任何父类的构造方法,子类默认的构造方法是编译器自动生成的,不是继承的。
class Person{
protected String name;
protected int age;
public Person(String name,int age){//构造方法
this.age = age;
this.name = name;
}
}
class Student extends Person{//继承
protected int score;
public Student(String name,int age,int score){
//如果没有这句则编译失败
super(name,age);//调用父类的构造方法Person(String,int)
this.score = score;
}
}
class Animal{
void eat(){
System.out.println("animal:eat");
}
}
class Dog extends Animal{
void eat(){
System.out.println("dog:eat");
}
void eatTest(){
this.eat();//指向自己的引用
super.eat();//调用父类的方法
}
}
public class Test {
public static void main(String[] args){
Animal a = new Animal();
a.eat();
Dog b = new Dog();
b.eatTest();
}
}
向上转型
Student是从Person继承下来的,一个引用类型为Person变量,能够指向Student类型的实例,这种把一个子类类型安全地变为父类类型的赋值,被称为向上转型(upcasting)
注意,继承树是Student>Person>Object,所以,可以把Student类型转型为Person,或者更高层次的Object
Person s = new Student("cheng",18,100);
向下转型
如果把一个父类类型强制转型为子类类型,就是向下转型(downcasting)
Person p1 = new Student();//upcasting 0k
Person p2 = new Person();
Student s1 = (Student)p1;//ok
Student s2 = (Student)p2;//error,因为p2实际类型是Person
//不能把父类变为子类,因为子类功能比父类多,多的功能无法凭空变出来
为了避免向下转型出错,java提供了instanceof操作符,可以先判断一个实例究竟是不是某种类型:
instanceof实际上判断一个变量所指向的实例是否是指定类型,或者这个类型的子类。如果一个引用变量为null,那么对任何instanceof的判断都为false
public static void main(String[] args){
Person p1 = new Student();//upcasting 0k
Person p2 = new Person();
Student s1 = (Student)p1;//ok
if(p2 instanceof Student){//只有判断成功才会向下转型
Student s2 = (Student)p2;
}
}
区分继承和组合
继承是is关系,组合是has关系
构造器
子类是不继承父类的构造器(构造方法)的,它只是调用。如果父类的构造器带有参数,则必须在子类的构造器中显式的通过super关键字调用父类的构造器并配以适当的参数列表
如果父类的构造器没有参数,则子类的构造器不需要通过super调用,系统会自动调用父类的无参构造器
class SuperClass{
private int n;
SuperClass(){
System.out.println("SuperClass");
}
SuperClass(int n){
System.out.println("SuperClass(n)");
this.n = n;
}
}
class SubClass extends SuperClass{
private int n;
SubClass(){
System.out.println("SubClass");
}
SubClass(int n){
super(200);
System.out.println("SubClass(n)"+n);
this.n = n;
}
}
class SubClass2 extends SuperClass{
private int n;
SubClass2(){
super(200);
System.out.println("subClass2 ");
}
SubClass2(int n){
this.n = n;
System.out.println("SubClass2 "+n);
}
}
public class Test {
public static void main(String[] args){
System.out.println("-------SubClass类继承--------");
SubClass a = new SubClass();
SubClass b = new SubClass(100);
System.out.println("-------SubClass2类继承--------");
SubClass2 c = new SubClass2();
SubClass2 d = new SubClass2(200);
}
/*输出
-------SubClass类继承--------
SuperClass
SubClass
SuperClass(n)
SubClass(n)100
-------SubClass2类继承--------
SuperClass(n)
subClass2
SuperClass
SubClass2 200*/
}