5.1 继承性
-
继承性的好处:
- 减少了代码的冗余,提高了代码的复用性
- 便于功能的扩展
- 为多态性的使用,提供了前提
-
继承性的格式:
class A extends B {}
-
A:子类、派生类、subclass
-
B:父类、超类、基类、superclass
-
体现:一旦子类A继承父类B以后,子类A中就获取了父类B中声明的结构:属性、方法。特别的,父类中声明为 private 的属性或方法,子类继承父类以后,仍然认为获取了父类中私有的结构。只是因为封装性的影响,使得子类不能直接调用父类的结构而已。
子类继承父类以后,还可以声明自己特有的属性或方法:实现功能的拓展
子类和父类的关系,不同于子集和集合的关系
- Java 中继承的规定
- 一个类只能有一个父类
- 一个类可以被多个子类继承
- 子父类是相对的概念
- 子类直接继承的父类,称为:直接父类。间接继承的父类称为:间接父类
- 子类继承父类以后,就获取了直接父类以及所有间接父类中声明的属性和方法
1.如果我们没有显式的声明一个类的父类的话,则此类继承于 java.lang.Object 类
2.所有的 java 类(出 java.lang.Object 类之外)都直接或间接的继承于 java.lang.Object 类
3.意味着,所有的 java 类具有 java.lang.Object 类声明的功能
-
代码体现
class Animal { String name; //定义name属性 //定义动物叫的方法 void shout() { System.out.println("动物叫"); } } //定义 Dog 类继承 Animal 类 class Dog extends Animal { public void printName() { System.out.println("name=" + name); } }
5.2 方法的重写
5.2.1 重写的定义
子类继承父类以后,对父类中同名同参数的方法,进行覆盖操作
重写以后,当创建子类对象以后,通过子类对象调用子父类中的同名同参数
的方法时,实际执行的是子类重写父类的方法
5.2.2 要求
-
子类重写的方法必须和父类被重写的方法具有相同的方法名称、参数列表
-
子类重写的方法的返回值类型不能大于父类被重写的方法的返回值类型
- 父类被重写的方法的返回值类型是 void,则子类重写的方法的返回值类型只能是 void
- 父类被重写的方法的返回值类型是 A 类型,则子类重写的方法的返回值可以是 A 类型或 A 类的子类
- 父类被重写的方法的返回值类型是基本数据类型,则子类重写的方法的返回值类型必须是相同的基本数据类型
-
子类的重写的方法的权限修饰符不小于父类被重写的方法的权限修饰符
- 特殊情况:子类不能重写父类中声明为 private 权限的方法
-
子类方法抛出的异常不能大于父类被重写方法的异常
子类和父类中的同名同参数的方法要么都声明为非 static 的(考虑重写),要么都声明为 static 的(不是重写)。因为 static 方法是属于类的,子类无法覆盖父类的方法。
5.2.3 代码体现
class Animal {
String name; //定义name属性
//定义动物叫的方法
void shout() {
System.out.println("动物叫");
}
}
//定义 Dog 类继承 Animal 类
class Dog extends Animal {
void shout() {
System.out.println("狗叫");
}
}
5.3 关键字:super
super 理解为:父类的
super 可以用来调用:属性、方法和构造器
5.3.1 super 的使用
- 可以在子类的方法或构造器中。通过使用 “super.属性” 或 “super.方法” 的方式,显式的调用父类中声明的属性或方法。但是,通常情况下,我们习惯省略 “super.”
- 特殊情况:当子类和父类中定义了同名的属性时,我们想要在子类中调用父类中声明的属性,则必须显式的使用 “super.属性” 的方式,表明调用的是父类中的属性
- 特殊情况:当子类重写了父类中方法以后,我们想要在子类的方法中调用父类中被重写的方法时,则必须显式的使用 “super.方法” 的方式,表明调用的是父类中被重写的方法
5.3.2 super 调用构造器
-
可以在子类的构造器中显式的使用 “super(形参列表)” 的方式,调用父类中声明的指定的构造器
-
“super(形参列表)” 的使用,必须声明在子类构造器的首行
-
在类的构造器中,针对于 “this(形参列表)” 或 “super(形参列表)” 只能二选一,不能同时出现
-
在构造器的首行,没有显式的声明 “this(形参列表)” 或 “super(形参列表)”,则默认调用的是父类中空参的构造方法
-
在类的多个构造器中,至少有一个类的构造器中使用了 “super(形参列表)” ,调用父类中的构造器
5.3.3 this 和 super 的区别
区别点 | this | super |
---|---|---|
访问属性 | 访问本类中的属性,如果本类没有此属性则从父类中继续查找 | 直接访问父类中的属性 |
调用方法 | 访问本类中的方法,如果本类没有此方法则从父类中继续查找 | 直接访问父类中的方法 |
调用构造器 | 调用本类中构造器,必须放在构造器的首行 | 调用父类构造器,必须放在子类构造器的首行 |
5.3.4 代码体现
public class SuperTest {
public static void main(String[] args) {
Dog dog = new Dog();
Dog dog1 = new Dog("");
}
}
class Animal {
public Animal(String name) {
System.out.println("我是一只" + name);
}
}
class Dog extends Animal {
public Dog() {
super("沙皮狗");
System.out.println("ddd");
}
public Dog(String name) {
this();
}
}
/* 运行结果:
我是一只沙皮狗
ddd
我是一只沙皮狗
ddd
*/
5.4 子类对象实例化过程
- 从结果上来看:(继承性)
- 子类继承父类以后,就获取了父类中声明的属性或方法。创建子类的对象,在堆空间中,就会加载所有父类中声明的属性。
- 从过程上来看:
- 当我们通过子类的构造器创建子类对象时,我们一定会直接或间接的调用其父类的构造器,进而调用父类的父类的构造器,直到调用了 java.lang.Object 类中空参的构造器为止。正因为加载过所有的父类的结构,所有才可以看到内存中有父类中的结构,子类对象才可以考虑进行调用
明确:虽然创建子类对象时,调用了父类的构造器,但是自始至终就创建过一个对象,即为 new 的子类对象
5.5 多态性
- 理解多态性:可以理解为一个事物的多种形态;实现代码的通用性
- 对象的多态性:父类的引用指向子类的对象(子类的对象赋给父类的引用)
- 多态的使用:虚拟方法调用
- 有了对象的多态性以后,我们在编译期,只能调用父类中声明的方法,但在运行期,我们实际
执行的是子类重写父类的方法。总结:编译看左边,运行看右边 - 多态性的使用前提:
- 类的继承关系
- 方法的重写
- 对象的多态性,只适用于方法,不适用于属性(编译和运行都看左边)
public class PersonTest {
public static void main(String[] args) {
Person p1 = new Person();
p1.eat();
Man man = new Man();
man.eat();
man.age = 25;
man.earnMoney();
//*************************************//
System.out.println("**********************");
//对象的多态性:父类的引用指向子类的对象
Person p2 = new Man();
//多态的使用:当调用子类同名同参数的方法时,实际执行的是子类重写父类的方法,虚拟方法调用
p2.eat();
p2.walk();
System.out.println(p2.id);//1001
//不能调用子类所特有的方法、属性,编译时,p2 是 Person 类型
///p2.earnMoney();
// p2.isSmoking = true;
//有了对象的多态性以后,内存中实际上是加载了子类特有的属性和方法的,
//但是由于变量声明为父类类型,导致编译时,只能调用父类中声明的属性和方法。
//子类特有的属性和方法不能调用
//如何才能调用子类特有的属性和方法
//向下转型:使用强制类型转换符
Man m1 = (Man) p2;
// m1.earnMoney();
m1.isSmoking = true;
//使用强转时,可能出现 ClassCastException 的异常
//Woman w1 = (Woman) p2;
//w1.goShopping();
/*
instanceof 关键字的使用
a instanceof A:判断对象 a 是否是类 A 的实例。如果是,返回 true:如果不是,返回 false
使用情景:为了避免在向下转型时出现ClassCastException的异常,我们在向下转型之前,先进行
instanceof 的判断,一旦返回true,就进行向下转型。如果返回false,不进行向下转型
如果 a instanceof A 返回true,则 a instanceof B 也返回true
其中,类 B 是 类 A 的父类
*/
if (p2 instanceof Woman) {
Woman w1 = (Woman) p2;
w1.goShopping();
System.out.println("*******Woman***********");
}
if (p2 instanceof Man) {
Man m2 = (Man) p2;
m2.earnMoney();
System.out.println("*******Man***********");
}
if (p2 instanceof Person) {
System.out.println("*******Person***********");
}
if (p2 instanceof Object) {
System.out.println("*******Object***********");
}
//练习:
//问题一:编译时通过,运行时不通过
//举例一:
// Person p3 = new Woman();
// Man m3 = (Man) p3;
//举例二:
// Person p4 = new Person();
// Man m4 = (Man) p4;
//问题二:编译通过,运行时也通过
Object obj = new Woman();
Person p = (Person) obj;
//问题三:编译不通过
// Woman woman = new Man();
}
}
class Person {
String name;
int age;
int id = 1001;
public void eat() {
System.out.println("人吃饭");