什么是多态
链接:https://www.nowcoder.com/questionTerminal/f797be7df69c4f24b7ff795809b22200
来源:牛客网
多态:相同类型的引用变量,调用同一个方法时呈现出多种不同的行为特征。
对象的实例变量不具备多态性。
Java引用变量有两个类型:编译时类型,运行时类型
编译时类型由声明该变量时使用的类型决定
运行时类型由实际赋给该变量的对象决定
如果编译时类型和运行时类型不一致,就可能出现多态(Polymorphism)
指允许不同类的对象对同一消息做出响应。即同一消息可以根据发送对象的不同而采用多种不同的行为方式。(发送消息就是函数调用)
多态也称作动态绑定、后期绑定、运行时绑定
多态产生的原因:函数的重载和函数覆盖
多态的表现:不同对象,调用相同函数结果不同;相同对象,调用相同函数名函数(参数列表返回值可能不一样)结果不同。
多态的存在有三个前提
1.要有继承关系
2.子类要重写父类的方法
3.父类引用指向子类对,
例子https://www.zhihu.com/question/30082151
成员变量
编译看左边(父类),运行看左边(父类)
成员方法
编译看左边(父类),运行看右边(子类)。动态绑定
静态方法
编译看左边(父类),运行看左边(父类)。
(静态和类相关,算不上重写,所以,访问还是左边的)
只有非静态的成员方法,编译看左边,运行看右边
Java 编译时多态和运行时多态
根据何时确定执行多态方法中的哪一个,多态分为两种情况:编译时多态和运行时多态。如果在编译时能够确定执行多态方法中的哪一个,称为编译时多态,否则称为运行时多态。
一、编译时多态
方法重载都是编译时多态。根据实际参数的数据类型、个数和次序,Java在编译时能够确定执行重载方法中的哪一个。
方法覆盖表现出两种多态性,当对象引用本类实例时,为编译时多态,否则为运行时多态。例如,以下声明p、m引用本类实例,调用toString()方法是编译时多态。
public class Test {
public static void main(String[] args) {
Person p = new Person(); //对象引用本类实例
Man m = new Man(); //编译时多态,执行Person类的toString()
System.out.println(p.toString());
System.out.println(m.toString()); //编译时多态,执行Man类的toString()
}
}
class Person{
public String toString() {
String name = "Person";
return name;
}
}
class Man extends Person{
public String toString(){
String name = "Man";
return name;
}
}
二、运行时多态
1.当以下父类对象p引用子类实例时,p.toString)执行谁的setName()方法?
Person p = new Man();
p.toString();
Java支持运行时多态,意为p.toString()实际执行p所引用实例的toString(),究竟执行Person类还是Man类的方法,运行时再确定。如果Man类声明了toString()方法,则执行之;否则执行Person类的toString()方法。
程序运行时,Java从实例所属的类开始寻找匹配的方法执行,如果当前类中没有匹配的方法,则沿着继承关系逐层向上,依次在父类或各祖先类中寻找匹配方法,直到Object类。寻找p.toString()匹配执行方法的过程如下图所示。
因此,父类对象只能执行那些在父类中声明、被子类覆盖了的子类方法,如toString(),不能执行子类增加的成员方法。
2.将上述例子中toString方法改为getName,因为在Object类中有toString类,无法测试Person与Man中所匹配的执行方法。
public class Test { //例子2
public static void main(String[] args) {
Person p = new Man();
System.out.println(((Man) p).getName()); //返回结果为Man
}
}
class Person{}
class Man extends Person{
public String getName(){
String name = "Man";
return name;
}
}
此例中Person类型要引用Man类的实例,因Person中未定义setName()方法,故需要把Person类显式地转换为Man类,然后调用Man中的getName方法。
3.将例子1中Person和Man的方法名改为静态的getName()方法,会返回什么结果呢?
public class Test { //例子3
public static void main(String[] args) {
Person p = new Man();
System.out.println(p.type); //返回结果为P
System.out.println(p.getName()); //返回结果为Person
}
}
class Person{
String type = "P";
public static String getName() {
String name = "Person";
return name;
}
}
class Man extends Person{
String type = "M";
public static String getName(){
String name = "Man";
return name;
}
}
栗子中子类Man隐藏父类Person的属性,而 Person p = new Man() 表示“先声明一个Person类的对象p,然后用Man类对p进行实例化”,即引用类型为Person类,实际代表的是Man类。因此,访问的是Person的属性及静态方法,详细解释如下。
所谓静态,就是在运行时,虚拟机已经认定此方法属于哪个类。“重写”只能适用于实例方法,不能用于静态方法。对于静态方法,只能隐藏,重载,继承。
子类对于父类静态方法的隐藏(hide),子类的静态方法完全体现不了多态,就像子类属性隐藏父类属性一样,在利用引用访问对象的属性或静态方法时,是引用类型决定了实际上访问的是哪个属性,而非当前引用实际代表的是哪个类。因此,子类静态方法不能覆盖父类的静态方法。
父类中属性只能被隐藏,而不能被覆盖;而对于方法来说,方法隐藏只有一种形式,就是父类和子类存在相同的静态方法。
作者:若明天不见
来源:CSDN
原文:https://blog.csdn.net/why_still_confused/article/details/51295707
版权声明:本文为博主原创文章,转载请附上博文链接!
Java中的方法覆盖(Overriding)和方法重载(Overload)是什么意思?
链接:https://www.nowcoder.com/questionTerminal/7b2152a85b9a4ebab6dfda7e995a8491
来源:牛客网
方法重写的原则:
- 重写方法的方法名称、参数列表必须与原方法的相同,返回类型可以相同也可以是原类型的子类型(从Java SE5开始支持)。
- 重写方法不能比原方法访问性差(即访问权限不允许缩小)。
- 重写方法不能比原方法抛出更多的异常。
- 被重写的方法不能是final类型,因为final修饰的方法是无法重写的。
- 被重写的方法不能为private,否则在其子类中只是新定义了一个方法,并没有对其进行重写。
- 被重写的方法不能为static。如果父类中的方法为静态的,而子类中的方法不是静态的,但是两个方法除了这一点外其他都满足重写条件,那么会发生编译错误;反之亦然。即使父类和子类中的方法都是静态的,并且满足重写条件,但是仍然不会发生重写,因为静态方法是在编译的时候把静态方法和类的引用类型进行匹配。
- 重写是发生在运行时的,因为编译期编译器不知道并且没办法确定该去调用哪个方法,JVM会在代码运行的时候作出决定。
方法重载的原则:
- 方法名称必须相同。
- 参数列表必须不同(个数不同、或类型不同、参数类型排列顺序不同等)。
- 方法的返回类型可以相同也可以不相同。
- 仅仅返回类型不同不足以成为方法的重载。
- 重载是发生在编译时的,因为编译器可以根据参数的类型来选择使用哪个方法。
重写和重载的不同:
- 方法重写要求参数列表必须一致,而方法重载要求参数列表必须不一致。
- 方法重写要求返回类型必须一致(或为其子类型),方法重载对此没有要求。
- 方法重写只能用于子类重写父类的方法,方法重载用于同一个类中的所有方法。
- 方法重写对方法的访问权限和抛出的异常有特殊的要求,而方法重载在这方面没有任何限制。
- 父类的一个方法只能被子类重写一次,而一个方法可以在所有的类中可以被重载多次。
- 重载是编译时多态,重写是运行时多态。
网友评论