继承是java面向对象编程技术的一块基石,因为它允许创建分等级层次的类。继承可以理解为一个对象从另一个对象获取属性的过程。
如果类A是类B的父类,而类B是类C的父类,我们也称C是A的子类,类C是从类A继承而来的。在Java中,类的继承是单一继承,也就是说,一个子类只能拥有一个父类
继承中使用的关键字是extends。类的继承决定了一个对象和另一个对象是否是IS-A(是一个)关系。通过继承,我们能实现一个对象获取另一个对象的属性。
一、继承
通过上面的概述,已经知道了继承,下面通过Code来分析继承:
Animal类:(祖父类)
public class Animal {
public String name = "Animal, 多态成员变量测式,父类对象";
public void show() {
System.out.println("动物....(祖父类)");
}
public void eat() {
System.out.println("动物吃东西....(祖父类)");
}
public void sleep() {
System.out.println("动物睡觉....(祖父类)");
}
}
Mammal类:(父类)
public class Mammal extends Animal {
public String name = "Mammal Animal";
public void fight() {
System.out.println("哺乳动物喜欢打架吗?...(添加新的功能)");
}
public void fly(){
System.out.println("哺乳动物会飞吗?...(添加新的功能)");
}
//测试私有是否可以继承
private void test() {
System.out.println("private...");
}
//测试保护是否可以继承
protected void test(int a) {
System.out.println("protected...(被访问了)");
}
//测试保护是否可以继承
void test(char c) {
System.out.println("default...(被访问了)");
}
}
Cat类:(子类)
public class Cat extends Mammal{
public String name = "Cat";
public void show() {
System.out.println("猫...(对父类的重写)");
}
}
Dog类:(子类)
public class Dog extends Mammal{
public String name = "Dog,验证变量的多态";
public void show() {
System.out.println("狗...(子类对父类的重写)");
}
public void sleep(){
System.out.println("狗睡觉...(子类对父类的重写,验证方法的 多态)");
}
}
ExtendsDemo类:(测试类)
public class ExtendsDemo {
public static void main(String[] args) {
// 验证继承性
System.out.println("@@@@@@@@@ 验证继承性: @@@@@@@@@");
Dog dog = new Dog();
dog.eat();
// 测试继承的权限
System.out.println("
@@@@@@@@@ 测试继承的权限: @@@@@@@@@");
Cat cat = new Cat();
// 私有的不能访问
// cat.test();
// 保护和默认的都可以
cat.test('c');
cat.test(1);
// 子类添加新的功能
System.out.println("
@@@@@@@@@ 子类添加新的功能: @@@@@@@@@");
Mammal mammal = new Mammal();
mammal.fight();
mammal.fly();
// 验证重写
System.out.println("
@@@@@@@@@ 验证重写: @@@@@@@@@");
Animal animal = new Animal();
animal.show();
dog.show();
// 验证多态性
System.out.println("
@@@@@@@@@ 验证多态性: @@@@@@@@@");
// 成员变量的多态验证
Animal animal2 = new Dog();
System.out.println(animal2.name);
System.out.println("成员变量的多态,编译看左边,运行也看左边, 也就是调用的是父类的成员变量
");
// 成员方法的多态验证
animal2.sleep();
System.out.println("成员方法的多态,编译看左边,运行也看右边, 也就是调用的是子类的成员方法");
}
}
测试截图:
- 继承的父类、子类
- 超类:被继承的类叫做超类(父类)。也就是已经存在的类且被定义在某个类的extends关键词后面的类叫做父类
- 子类:继承的类叫做子类(基类)。也就是通过超类派生的类
- 继承的权限
- 被private修饰的成员方法和成员变量不能被继承
- 被public,protected和默认修饰的成员方法和成员变量能被继承
- 通过继承子类可以得到超类的非private修饰的方法,如上面Code中的:dog.eat();
在Dog类中并没有eat()方法,但是却可以调用,是因为Dog继承自Animal,得到了Animal的eat()方法
- 通过继承也可以得到超类非private修饰的成员变量
- super关键字 :和this相对,this表示本身对象,而super则是代表超类对象。
public Dog() {
super(); //调用父类的构造
}
public void sleep() {
System.out.println("狗睡觉...(子类对父类的重写,验证方法 的多态)");
super.fly(); //调用父类的方法
}
- super和this的总结:
- this的三个用途:
- 引用隐式参数
- 调用该类的其他构造器
public Dog() {
this("this测试");
}
public Dog(String name) {
super();
this.name = name;
}
- 调用该类的方法
- super的两个用途:
- 调用超类的方法
- 调用超类的构造器
- 如果子类没有显示的调用超类的构造器,则将自动调用超类的默认构造器。如果超类没有不带参数的构造器,并且子类构造器中由没有显示的调用超类的其他构造,那么Java编译器将报错。
- 继承层次:
对于上面的程序示例,Animal派生Mammal,Mammal派生Dog和Cat,这些继承关系之间就组成了一个层次关系。
由一个公共超类派生出的所有类的集合被称为继承层次。在继承层次中,从某个特定的类到其祖先的路径被称为继承链。
图解:
二、多态和重写(覆盖)
多态是对象采取多种形式表现的能力。当一个父类引用是用来指向一个子类对象时,在通过父类对象去调用子类的方法,就是一个多态表现。
- 多态存在的条件
- 继承:多态的存在必须要继承
- 重写父类方法:除了继承外,多态还要重写父类的方法
- 多态是什么?
多态是指当父类和子类都有同样的方法的时候,且父类对象变量引用了子类的对象,那么在调用的时候,就会动态的绑定到具体的子类,然后在调用子类的这个方法,而不是因为是父类的对象变量就调用父类的方法。
所以有了a)的两点条件,解释一下:
- 继承:因为要父类对象去引用子类的对象,所以要有继承
- 重写:因为要有和父类同样的方法,那么这个方法就是继承去的,所 以为了体现不同,最好重写,不然这种多态是无意义的。
- 多态的实现机制:动态绑定(下面介绍)
- 多态的三点重点:
成员变量的多态:编译看左边(看父类),运行看左边(看父类), 所以调用的还是父类的成员对象。
静态方法的多态:编译看左边(看父类),运行看左边(看父类), 所以调用的还是父类的静态方法。
成员方法的多态:编译看左边(看父类),运行看右边(看子类), 所以调用的是子类的成员方法。
- 构造方法的调用:
在创建子类对象的时候,同时还会调用父类的构造方法,对父类数据进行初始化。
- 多态的优点
- 提高了代码的维护性
- 提高了代码的扩展性
三、绑定
程序绑定的概念:绑定指的是一个方法的调用与方法所在的类(方法主体)关联起来。对java来说,绑定分为静态绑定和动态绑定;或者叫做前期绑定和后期绑定
- 静态绑定
在程序执行前方法已经被绑定,此时由编译器或其它连接程序实现。例如:C。针对java简单的可以理解为程序编译期的绑定;这里特别说明一点,java当中的方法只有final,static,private和构造方法是前期绑定
- 动态绑定
后期绑定:在运行时根据具体对象的类型进行绑定。若一种语言实现了后期绑定,同时必须提供一些机制,可在运行期间判断对象的类型,并分别调用适当的方法。也就是说,编译器此时依然不知道对象的类型,但方法调用机制能自己去调查,找到正确的方法主体。不同的语言对后期绑定的实现方法是有所区别的。但我们至少可以这样认为:它们都要在对象中安插某些特殊类型的信息。
动态绑定的过程:
1. 虚拟机提取对象的实际类型的方法表;
2. 虚拟机搜索方法签名;
3. 调用方法。
四、阻止继承
在实际的开发中可能不允许某个类定义子类,那么这种类叫做最终类或者叫做final类。但这个final类如何实现呢?见下代码就是一个final类:
final public class Person extends Mammal{
private String name;
private String gender;
private int age;
public Person(){}
public Person(String name,String gender, int age){
super();
this.name = name;
this.age = age;
this.gender = gender;
}
....
}
扩展知识点:
- instanceof关键字
java中的instanceof运算符是用来在运行时指出对象是否是特定类的一个实例。instanceof通过返回一个布尔值来指出,这个对象是否是这个特定类或者是它的子类的一个实例。
if (dog instanceof Animal) {
System.out.println("dog是Animal类的一个实例");
} else {
System.out.println("dog不是Animal类的一个实例");
}
if (animal instanceof Dog) {
System.out.println("animal是Dog类的一个实例");
} else {
System.out.println("animal不是Dog类的一个实例");
}
当将超类转成子类之前一定要使用instanceof对子类对象进行检查。如下代码段:
Animal animal = new Dog();
if (animal instanceof Dog) {
Dog dog = (Dog)animal;
System.out.println("animal是Dog类的一个实例,可以转 换");
}else {
System.out.println("animal不是Dog类的一个实例");
}
- 受保护的访问
受保护的访问就是对protected修饰的方法和成员变量的访问。访问权限见总结12
- final修饰符(重点)
- final修饰变量:那么变量变为常量,在赋初值之后,值不可以在改变
- final修饰类:那么类就变为最终类,不可在派生子类
- final修饰方法:则该方法可以被继承,但是不可以被重写。
- final修饰对象:那么对象的引用不可再变,但是引用对象的值是可以变的。(就 是说对象被final修饰后,就不能在指向其他的对象,但是对 象中存储的值是可以变的)