java的多态性
文章内容选自尚硅谷
java多态性的使用方法
java的多态性的用法一般是父类的引用指向子类的对象,即在创建对象的时候,假如声明了变量p为A类型的变量,但是在new对象的时候却new的是A类型子类的对象。
- 用法为 子类的对象赋值给父类的引用,即 A类型 变量 = new A类型的子类()
- 多态一般用于子类重写父类方法的情况,当父类.方法(重写的方法)时候,调用的其实是子类中重写的方法,但是当我们按ctrl+点击来查找方法位置的时候,找到的是父类中定义的方法。由此可见,编译和调用其实是分开的。
- 使用多态性的时候,引用的方法必须是父类中定义过的方法。
代码如下
先创建一个Person的父类
package com.atguigu.java4;
public class Person {
String name;
int age;
public void eat(){
System.out.println("人吃饭");
}
public void walk(){
System.out.println("人走路");
}
}
再创建两个子类,分别为Man类和Woman类
package com.atguigu.java4;
public class Man extends Person {
boolean isSmoking;
public void earnMoney(){
System.out.println("男人挣钱");
}
public void eat(){
System.out.println("男人吃饭");
}
public void walk(){
System.out.println("男人走路");
}
}
package com.atguigu.java4;
public class Woman extends Person {
boolean isBeauty;
public void eat(){
System.out.println("女人吃饭");
}
public void walk(){
System.out.println("女人走路");
}
public void goShopping(){
System.out.println("女人爱逛街");
}
}
Man类和Woman类都重写了Person类的walk和eat方法。
最后进行测试
package com.atguigu.java4;
public class PersonTest {
public static void main(String[] args) {
Person p1 = new Person();
Person p2 = new Man();
p2.eat();
}
}
得到的运行结果为
男人吃饭
经过以上的代码验证,采用多态,new出来的对象必须是左边声明类的子类,而且在调用方法的时候,调用方法的是一般经过子类重写后的方法,而且实际上也运行的是子类重写后的方法。
在用多态调用方法的时候,调用的方法必须是父类中声明过的方法
多态性在调用方法的时候分为三种情况
- 调用的方法是子类中特有的方法,即调用的方法没有对父类的方法进行重写,此时编译器会报错。
- 调用的方法是子类中重写后的方法,虽然用ctrl+点击方法会定位到父类中声明的方法,但实际上运行的是子类中重写过后的方法。
- 调用的方法是没有被子类进行重写,这样做不会报错,但是这样做不能体现多态的优越性:如果仅因为要调用父类的方法而采用多态new了一个子类的对象,还不如直接就new一个父类的对象,只有调用的方法是子类重写的方法,采用多态才有意义。
综上,多态在编译期间,要看左边父类声明过该方法没有,在实际运行期间,运行的是子类中重写后的方法,记住“编译看左边,运行看右边”口诀。
多态的使用
- 采用多态的前提必须要有继承关系
- 多态调用方法的时候一般调用被子类重写后的方法
创建一个AnimalTest类来测试代码
package com.atguigu.java4;
public class AnimalTest {
public static void main(String[] args) {
AnimalTest test = new AnimalTest();
test.fun(new Dog());
test.fun(new Cat());
}
public void fun(Animal animal){
animal.eat();
animal.shout();
}
// public void fun(Cat cat){
// cat.eat();
// cat.shout();
// }
//
// public void fun(Dog dog){
// dog.eat();
// dog.shout();
// }
}
class Animal{
public void eat(){
System.out.println("吃东西");
}
public void shout(){
System.out.println("动物叫");
}
}
class Dog extends Animal{
public void shout(){
System.out.println("wangwangwang");
}
public void eat(){
System.out.println("eat bone");
}
}
class Cat extends Animal{
public void eat(){
System.out.println("eat fish");
}
public void shout(){
System.out.println("miaomiaomiao");
}
}
运行结果为
eat bone
wangwangwang
eat fish
miaomiaomiao
分析代码,采用了匿名对象的方式来把cat和dog对象传递给animal对象
test.fun(new Dog());
test.fun(new Cat());
这两句代码实际上相当于
Animal animal = new Dog();
Animal animal = new Cat();
如果不允许采用多态性的话,就必须在代码中再重载两个fun方法
// public void fun(Cat cat){
// cat.eat();
// cat.shout();
// }
//
// public void fun(Dog dog){
// dog.eat();
// dog.shout();
// }
要是不允许多态,就必须重载两个fun方法,用于传递不同类型的形参
因此采用多态,能够有效的避免代码的冗余。
对象的多态性不适用于属性
在Person类和Man类中分别假如属性id并进行初始化
package com.atguigu.java4;
public class Person {
String name;
int age;
int id = 1001;
public void eat(){
System.out.println("人吃饭");
}
public void walk(){
System.out.println("人走路");
}
}
package com.atguigu.java4;
public class Man extends Person {
boolean isSmoking;
int id = 1002;
public void earnMoney(){
System.out.println("男人挣钱");
}
public void eat(){
System.out.println("男人吃饭");
}
public void walk(){
System.out.println("男人走路");
}
}
package com.atguigu.java4;
public class PersonTest {
public static void main(String[] args) {
Person p1 = new Person();
Person p2 = new Man();
p2.eat();
System.out.println(p2.id);
}
}
运行结果为
男人吃饭
1001
可见,多态性的使用仅仅针对方法,不针对属性,属性的编译和运行都看左边的父类。
ps:p2对象中的堆空间中,其实有两个id属性,一个id属性是继承自父类,并且已经默认初始化为1001,另一个id属性是Man类自身属性,默认初始化为1002