覆盖(重写)和隐藏
我们要考虑的是什么时候发生覆盖,什么时候发生隐藏
对于变量都是隐藏,对于方法才会发生覆盖
**覆盖:**子类重写父类的方法,要求方法名和参数类型完全一样(参数不能是子类),返回值和异常比父类小或者相同(即为父类的子类),访问修饰符比父类大或者相同。需要考虑动态绑定,是什么运行类型的事情。
覆盖是对于实例方法而言的
方法不能交叉覆盖:子类实例方法不能覆盖父类的静态方法;
子类的静态方法也不能覆盖父类的实例方法(编译时报错)
**隐藏:**父类和子类拥有相同名字的属性或者方法( 方法隐藏只有一种形式,就是父类和子类存在相同的静态方法)时,父类的同名的属性或者方法形式上不见了,实际是还是存在的。
隐藏是对于静态方法和成员变量(静态变量和实例变量)而言的就是没有动态绑定机制,那个编译类型调用就调用那个编译类型里的变量或者方法
(1)当发生隐藏的时候,声明类型是什么类,就调用对应类的属性或者方法,而不会发生动态绑定
(2) 属性只能被隐藏,不能被覆盖
(3)变量可以交叉隐藏:子类实例变量/静态变量可以隐藏父类的实例/静态变量
3、隐藏和覆盖的区别
(1)被隐藏的属性,在子类被强制转换成父类后,访问的是父类中的属性
在无强制转换时子类要访问父类的属性使用super关键字
(2)被覆盖的方法,在子类被强制转换成父类后,调用的还是子类自身的方法
子类要是想访问父类的方法,可以使用super关键字
RTTI(run time type identification,运行时类型检查)
RTTI只针对覆盖,不针对隐藏:因为覆盖是动态绑定,是受RTTI约束的,隐藏不受RTTI约束
运行时类型为引用变量所指向的对象的类型,编译时类型是引用变量自身的类型
参考https://blog.csdn.net/yezhuanxu/article/details/51451366
public class Test {
public static void main(String[] args) {
Circle circle = new Circle();//本类引用指向本类对象
Shape shape = new Circle();//父类引用指向子类对象(会有隐藏和覆盖)
System.out.println(circle.name);
circle.printType();
circle.printName();
//以上都是调用Circle类的方法和引用
System.out.println(shape.name);//调用父类被隐藏的name属性
shape.printType();//调用子类printType的方法
shape.printName();//调用父类隐藏的printName方法
}
}
class Shape {
public String name = "shape";
public Shape(){
System.out.println("shape constructor");
}
public void printType() {
System.out.println("this is shape");
}
public static void printName() {
System.out.println("shape");
}
}
class Circle extends Shape {
//父类属性被隐藏
public String name = "circle";
public Circle() {
System.out.println("circle constructor");
}
//对父类实例方法的覆盖
public void printType() {
System.out.println("this is circle");
}
//对父类静态方法的隐藏
public static void printName() {
System.out.println("circle");
}
}
运行结果
shape constructor
circle constructor
shape constructor
circle constructor
circle
this is circle
circle
shape
this is circle
shape
多态属性问题
对于非静态属性,子类可以继承父类的非静态属性。但是当子类和父类有相同的非静态属性时,并没有重写并覆盖父类的非静态属性,只是隐藏了父类的非静态属性。属性是不可以重写的。
对于非静态的方法,子类可以继承父类的非静态方法并可以重写覆盖父类的非静态属性方法
关于多态中属性和方法访问问题
属性是静态绑定的,看的是编译类型
方法是动态绑定的,看的试运行类型
import javafx.scene.shape.Circle;
import org.w3c.dom.ls.LSOutput;
import java.util.Arrays;
public class Exercise05{
public static void main(String[] args) {
//对于属性
cat c=new cat();
System.out.println(c.person);
Animal animal=c;
System.out.println(animal.person);
//对于方法 从子类开始查找,一直到达超类
animal.say();
c.fly();
//关于方法的动态绑定 里面的属性是静态绑定的
// 哪里定义的用哪里的 里面的方法是动态绑定的,根据具体的运行类型来判断。
c.kk();
}
}
class Animal{
public String person="父类中的你们好";
public String getPerson() {
return person;
}
public void say(){
System.out.println("动物在叫");
}
public void fly(){
System.out.println("动物在飞");
}
public void kk(){
System.out.println(person);
System.out.println(getPerson());
}
}
class cat extends Animal{
public String person="子类中的你们好";
@Override
public String getPerson() {
return person;
}
public void say(){
System.out.println("猫在叫");
}
}
运行结果:
子类中的你们好
父类中的你们好
猫在叫
动物在飞
父类中的你们好
子类中的你们好
对与statci修饰的方法和属性
对于静态的属性,子类可以继承父类的静态属性。但是和非静态的属性一样,会被隐藏。
对于静态的方法,子类可以继承父类的静态方法。但是子类不可重写覆盖父类的静态方法,子类的同名静态方法会隐藏父类的静态方法。
public class Test {
public static void main(String[] args) {
System.out.println("子类方法的引用");
System.out.println(Circle.name);
Circle.printName();
System.out.println("父类中方法中的引用");
System.out.println(Shape.name);
Shape.printName();
}
}
class Shape {
public static String name = "shape";
public static void printName() {
System.out.println("shape");
}
}
class Circle extends Shape {
public static String name = "circle";
//对父类静态方法的隐藏
public static void printName() {
System.out.println("circle");
}
}
对于final来说
1.final修饰的类是最终类,不能被继承
2.final修饰的方法可以被继承和重载,但是不可以被重写
3.final修饰的变量不能被修改,是个常量
4.final修饰变量可以被继承,隐藏
final修饰的方法,可以被继承不能被重写
public class Test {
public static void main(String[] args) {
Circle circle=new Circle();
Shape shape = new Shape();
System.out.println(circle.num);
System.out.println(shape.num);
circle.say();
}
}
class Shape {
final int num=10;
final void say(){
System.out.println("父类中的方法");
}
}
class Circle extends Shape {
final int num=11;
}
运行结果
11
10
父类中的方法