继承
语法和概念
修饰符 class 子类名 extends 父类名
- 子类从它的父类中继承所有的数据域和方法,也可以添加新的数据域和新的方法。
- 如果类 B 从类 A 派生,或者说类 B 扩展自类 A,或者说类 B 继承类 A,
- 称类 A 为"父类",也称为超类、基类;
- 称类 B 为"子类",也称为次类、扩展类、派生类。
继承的注意点
子类不是父类的子集,子类一般比父类包含更多的数据域和方法, 子类可以对父类进行扩展,也可以用自己的方式实现父类的方法(即下面要讲的重写)
子类对象确实拥有父类对象中的所有属性和方法,但是父类对象中的
private
属性和方法,子类是无法访问到的,只是拥有,但不能使用。
若想访问private
属性,则需要在父类中提供用来访问其私有字段的public或protected方法Java 的继承是单继承,但是可以多层继承1。Java不允许多重继承(和C++不同),即
//错误写法 public class A{} public class B{} public class C extends A, B{}
在继承关系之中,如果要实例化子类对象,会默认先调用父类构造,为父类之中的属性初始化,之后再调用子类构造,为子类之中的属性初始化。在任何的情况下,子类都逃不出父类构造的调用
- 子类和父类的构造函数均无参数
class A { public A() { // 父类无参构造 System.out.println("*************************") ; } } class B extends A { public B() { // 子类构造 //这里会自动补充一句super(); System.out.println("#########################"); } } public class TestDemo { public static void main(String args[]) { B b = new B() ; // 实例化子类对象 } } /*输出为 ************************* ######################### */
- 父类的构造函数有参数,子类无参数
class A { public A(String msg) { // 父类构造 System.out.println("*************************"); } } class B extends A { public B() { // 子类构造 super("Hello"); // 调用父类构造,这一句不可省略 System.out.println("#########################"); } } public class TestDemo { public static void main(String args[]) { B b = new B(); // 实例化子类对象 } } /*输出为 ************************* ######################### */
- 子类和父类的构造函数均无参数
super 关键字
super
表示使用它的类的父类。super
可用于:- 调用父类的构造方法
- 调用父类的方法(在子类需要重写父类时使用)
- 访问父类的数据域
调用父类的构造方法
super();
super(参数列表);
super
语句必须是子类构造方法的第一条语句。- 父类的构造方法不被子类继承,
不能在子类中使用父类构造方法名来调用父类构造方法,调用父类的构造方法的唯一途径是使用
super
关键字。 - 如果子类中没显式调用,则编译器自动将
super();
作为子类构造方法的第一条语句。也就是说如果父类构造器没有参数,则在子类的构造方法中不需要使用super
关键字调用父类构造方法,系统会自动调用父类的无参构造方法
//有参数 public class Animal{ public String name; public int id; public Animal(int name, int id){ this.name = name; this.id = id; } } public class Dog extends animal{ public Dog(int name, int id){ super(name, id); } } //无参数 public class Animal{ public String name; public int id; public Animal(){ System.out.println(name + " : " + id); } } public class Dog extends animal{ public Dog(int name, int id){ super(); //也可以不写; } }
调用父类的实例方法
- 如果是继承的方法,是没有必要使用
super
来调用,直接即可调用。 - 如果子类覆盖或重写了父类的方法,则只有使用
super
才能在子类中调用父类中的被重写的方法。
super.methodName([parameter list]){ //重新定义方法 }
访问父类的数据域
- 如果是继承的数据,是没有必要使用
super
来调用,直接即可调用。 - 如果子类覆盖或重写了父类的数据,则只有使用
super
才能在子类中调用父类中的被重写的数据。
super.variableName
this 关键字
this
关键字表示当前对象(实例),可用于:
- 调用当前类的构造方法(调用类方法不能用
this
) - 限定当前对象的数据域变量
### 调用当前类的构造方法
通过this
调用当前类的构造方法时必须是方法的第一条语句。(和调用父类使用的super
用法相似)this();
调用默认构造方法。this(参数);
调用带参构造方法。- 使用规则:(参考该博客)
假如在一个构造方法中使用了
this
语句,那么它必须作为构造方法的第一条语句。只能在一个构造方法中使用
this
语句来调用类的其他构造方法,而不能在实例方法中用this
语句来调用类的其他构造方法。只能用
this
语句来调用其他构造方法,而不能通过方法名来直接调用构造方法。
public class animal{ public int id; public animal(int id){ this.id = id; } public animal(){ this(10); } public static void main(String[] args){ animal cat = new animal(); } }
限定当前对象的数据域变量
- 一般用于方法内的局部变量与对象的数据域变量同名的情况,将成员变量和局部变量进行区分(和C++相似)
public Student(String name, int age) { //注:可以使用this进行区分成员变量和局部变量 this.name = name; this.age = age; }
多态的初步介绍
多态是同一个行为具有多个不同表现形式或形态的能力;是同一个接口,使用不同的实例而执行不同操作。多态的好处是——可以使程序有良好的扩展,并可以对所有类的对象进行通用处理。
- 多态的必要条件
- 继承
- 重写
- 父类引用指向子类对象:
Parent p = new Child()
;
- 多态调用方法的流程:当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误;如果有,再去调用子类的同名方法。
下面我们重点理解一下第三个必要条件——
父类引用指向子类对象。例如以下程序——
class Animal{
public void move(){
System.out.println("Animal move!");
}
}
class Dog extends Animal{
public int age;
public void move(){
age = 10;
System.out.println("Dog move!");
}
public void bark(){
System.out.println("Dog bark!");
}
}
public class Test{
public static void main(String args[]){
Animal a = new Animal(); // Animal 对象
Animal b = new Dog(); // Dog 对象
a.move();// 执行 Animal 类的方法
b.move();//执行 Dog 类的方法
b.bark();//编译错误!!!!!!!
}
}
最后一行编译错误原因是——
因为此时Animal b = new Dog();
满足父类引用指向子类对象的情况,首先检查在父类中是否有该方法。因为父类Animal
并没有bark()
方法,因此会报错。
如果将Animal b = new Dog();
改为Dog b = new Dog();
的话,以上程序会被正常编译。