方法覆盖
从父类当中继承的方法不满足子类的使用需求时,就要考虑使用方法覆盖了;发生方法覆盖后子类对象会调用覆盖之后的方法。
方法覆盖的条件:
- 必须发生在具有继承关系的两个子类之间;
- 覆盖后的方法和原来的方法具有兼容的返回值类型,相同的方法名,相同的形式参数列表(子类的返回值类型必须是相同的类型或者是该类型的子类)
注意:
- 私有方法不能被继承,所以无法覆盖
- 构造方法无法被继承,所以不能覆盖【问题二】
- 覆盖之后的方法不能比原方法有更低的访问控制权限(可以从protected -> public)(举例来说 不能在编译期间是公有的,然后在执行期间突然被JVM阻止存取)
- 覆盖之后的方法不能比原方法抛出更多的异常,可以相同或者更少
- 方法覆盖只和方法有关,和属性无关
- 静态方法不存在覆盖(意义不大)【问题一】
示例:
class Animal{
void move(){
System.out.println("Animal moving");
}
void sing(){
}
}
要求子类Cat和Bird有不同的move方法:
class Cat extends Animal{
@Override
void move() {
System.out.println("Cat wondering");
}
}
class Bird extends Animal{
@Override
void move() {
System.out.println("Bird flying");
}
void sing(int i){
//此处sing和父类的sing没有发生方法覆盖,因为参数列表不同;但间接形成了方法重载
}
}
多态
多态属于面向对象三大特征之一,前提是封装形成独立体,独立体之间形成继承关系,从而产生多态机制;
多态是同一个行为具有多个不同表现形式或者形态的能力;
java中允许:
- 向上转型 子 —> 父(自动类型转换)父类型引用指向了一个子类型的对象
- 向下转型 父 —> 子 (强制类型转换)
多态指的是:编译阶段绑定父类中的方法,运行阶段动态绑定子类中的方法(编译时一种形态,运行时一种形态)
如果想访问子类特有方法,必须向下转型
Animal a1 = new Animal();
a1.move();//Animal move
Cat c1 = new Cat();
c1.move();//Cat wondering
Bird b1 = new Bird();
b1.move();//Bird flying
Animal和Cat有继承关系,Cat is a Animal,java中支持语法:父类型引用指向子类型对象,即:
Animal a = new Cat();
a就是父类型引用, new Cat();就是子类型对象
Animal a2 = new Cat();
a2.move();//Cat wondering
Animal a3 = new Bird();
a3.move();//Bird flying
分析上述代码:
- 编译阶段:对于编译器来说,只知道a2的类型是Animal,在运行时会去Animal.class字节码文件中查找move方法,找到了就会绑定上move方法,静态绑定成功
- 运行阶段:实际上在堆内存中创建的java对象是Cat,所以move的时候真正参与运行的是Cat对象,执行阶段就会动态绑定Cat对象中的move方法
但是假定要执行子类中特有的方法:
Animal a5 = new Animal();
a5.catchMouse();
编译就会报错:这是因为在静态绑定阶段无法从Animal.class字节码文件中查找到catchMouse方法,静态绑定失败,编译无法通过
也就是说:子类中特有的方法无法静态绑定
这时就必须进行向下转型:(也就是使用强制类型转换将Animal类型的a引用转换成Cat类型的c引用)
Cat cat = (Cat)a5;
cat.catchMouse();
但是向下转型也是有风险的:ClassCastException
Animal a6 = new Bird();
Cat cat1 = (Cat)a6;//这时编译器检测到a6是Animal类型,和Cat有继承关系,可以向下转型
cat1.catchMouse();//堆内存中实际创建的对象是Bird 实际运行过程中Bird转换为Cat就不行了 没有继承关系
避免类型转换异常的发生:instanceof运算符;可以在运行阶段动态绑定引用指向对象的类型 结果只能是true或false
c instance of Cat 为true: c引用指向堆内存中的对象是一个Cat
Animal a7 = new Bird();
if(a7 instanceof Cat){
Cat z = (Cat)a7;
z.catchMouse();//任何时候进行向下转型都需要instanceof判断
}
为什么要用instanceof运算符
public class InstanceofTest01 {
public static void main(String[] args) {
AnimalTest at = new AnimalTest();
at.test(new Bird());
at.test(new Cat());
}
}
class AnimalTest{
public void test(Animal a){
if(a instanceof Cat){
Cat c = (Cat) a;
c.catchMouse();
} else if (a instanceof Bird) {
Bird b = (Bird) a;
b.getClass();
}
}
}
在test中,可能传过来的是Bird 也可能传过来的是Cat 就需要进行判断
继承与多态
当定义出一组类的父型时,可以用子型的任何类来填补任何需要或者期待父型的位置
运用多态时,引用类型可以是实际对象的父类
Animal[] animals = new Animal[5];
animals[0] = new Dog();
animals[1] = new Cat();
animals[2] = new Wolf();
animals[3] = new Hippo();
animals[4] = new Lion();
for(int i = 0; i < animals.length; i++){
animals[i].eat();
animals[i].roam();
}
编译器会寻找引用类型来决定是否可以调用该方法,但在执行期间,编译器寻找的不是引用的类型而是堆上的对象,假定此时编译器同意这个调用,则唯一能通过的方法是覆盖的方法也具有相同的参数和返回值。
并且与上例相同 参数与返回值类型也可以是多态
当要覆盖父类的方法时,就必须要“履行约定”,比如这个约定是:
boolean trunOn(){}
这就表示:没有参数且具有布尔类型的返回值
多态存在的三个条件:
- 继承
- 方法覆盖
- 父类型引用指向子类型对象
多态显然是离不开方法覆盖机制的,多态就是因为编译时绑定的父类中的方法,运行时实际上绑定的子类的方法,如果子类对象的方法没有重写,这个时候创建子类对象就没有意义了,只有子类将方法重写之后调用到子类对象上的方法产生不同效果时,多态就形成了。
问题一:静态方法没有方法覆盖
public class StaticMethodOverWrite {
public static void main(String[] args) {
Math m = new Math();
Math s = new MathSubClass();
m.sum();
s.sum();
}
}
class Math{
public static void sum() {
System.out.println("Math sum execute");
}
}
class MathSubClass extends Math{
public static void sum() {
System.out.println("MathSubClass sum execute");
}
}
这就说明 静态方法执行的时候跟对象无关,虽然是引用.调用静态方法,但在执行的时候会变成类名.就会执行Animal的sum方法
问题二: 私有方法不能覆盖
public class PrivateMethodOverWrite {
private void doSome(){
System.out.println("Private doSome execute");
}
public static void main(String[] args) {
PrivateMethodOverWrite privateMethodOverWrite = new T();
privateMethodOverWrite.doSome();//此处执行的是子类中的方法 ,说明父类中私有方法没有继承
}
}
class T extends PrivateMethodOverWrite{
public void doSome(){//此处重写需要注意:访问权限不能更低,只能更高或者持平
System.out.println("SubClass doSome execute");
}
}
兼容的返回值类型
public class CompatibleReturnValue {
public Animal doSome(){
return null;
}
}
class SubClass extends CompatibleReturnValue{
@Override
public Cat doSome() {//返回值类型变为父类方法中声明的子类 可行
return null;
}
}
兼容的返回值类型:子类的返回值类型必须是相同的类型或者是该类型的子类
多态在实际开发中的作用
public class PolymorphismInActualDevelopment {
public static void main(String[] args) {
new Master(new Cat()).feed();
new Master(new Bird()).feed();
new Master(new Dog()).feed();
}
}
class Master{
Animal a;
public Master(Animal a) {//Animal a = new Cat();
this.a = a;
}
public void feed(){
a.eat();
}
}
class Dog extends Animal{
}
访问权限修饰符
- private表示私有的 只能在本类中访问
- public表示公开的 任何位置都能访问
- default表示只能在本类、同package下访问
- protected表示只能在本类,同package,子类中访问
访问控制修饰符 | 本类 | 同包 | 子类 | 任何位置(其他包中没有继承关系的类) |
---|---|---|---|---|
public | 可以 | 可以 | 可以 | 可以 |
protected | 可以 | 可以 | 可以 | 不行 |
default | 可以 | 可以 | 不行 | 不行 |
private | 可以 | 不行 | 不行 | 不行 |
从大到小排序:public > protected > default > private
访问权限修饰符可以修饰:
属性(4个都可以)
方法(4个都可以)
类(public & default)
接口(public & default)