最近在学Java,学到了面向对象这一章节,面向对象有三大特性:封装性、继承性、多态性,其中最难理解的就是多态性了,我也在网上查了很多资料,有许多都是只说了多态性中一些特性而已,为了我能巩固这些知识、同时也可以为大家参考,所以我写下这一篇详解。
多态是同一个行为具有多个不同表现形式或形态的能力。翻译过来就是说一个物体具有多种形态,多态就是不同对象对同一物体或事件发出不同的反应或响应。比如说人,人分男人,女人,这就是人这一个物体的多态,再比如说动物,动物有老虎、兔子和鸡等等。
继承关系是多态的必要条件,就好像说现在有三个类,人的类,男人的类,女人的类,人这一个类它有走路、说话的方法,然后另外一个男人的类继承人这个类,它就拥有走路、说话的方法,然后男人大声的说话,再来一个女人的类,也继承人这个类,它也拥有走路、说话的方法,然后女人细声细语的说话,这样子人就有了两个形态,大声说话的男人,细声细语说话的女人。
继续说上面的例子,男人虽然继承了人这个类的走路、说话的方法,但是在男人的类中,把说话的方法改为了大声的说话,女人的类中,把说话的方法改为了细声细语的说话,这就是使得不同的形态有着不同的特征。
这里要理解重写和重载的区别:
可以参考:https://blog.csdn.net/qq_42014192/article/details/89707483?spm=1001.2101.3001.6650.3&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-3-89707483-blog-124175633.pc_relevant_aa&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-3-89707483-blog-124175633.pc_relevant_aa
上面的例子中,人的类是父类,男人的类和女人的类是子类,在创建多态的时候,要用父类的引用指向子类的对象。(这里看不懂没事,先继续往下看,下面会继续解释的)
Person p = new Woman();//Person是父类,Woman是女人的类。
代码如下(示例):
public class Person {//人的类
String name = "Person";
public void say(){
System.out.println("person say");
}
public void show(){
System.out.println("person show");
}
}
public class Woman extends Person {//女人的类
String name = "Woman";
@Override
public void say(){
System.out.println("woman say");
}
@Override
public void show(){
System.out.println("woman show");
}
public void buy(){
System.out.println("buy buy ...");
}
}
public class Test {
public static void main(String[] args) {
// 多态的前提:1、要有继承关系 2、要有方法重写 3、父类的引用指向子类的对象
// 多态绑定
Person p = new Woman();
// 虚拟方法调用-编译看左边,运行看右边
p.say();
p.show();
// p.buy(); //编译错误,编译看左边。buy为Woman的方法,Person不能调用
}
}
注释中的==“虚拟方法调用-编译看左边,运行看右边”,在这里详细的说,它的意思就是向上转型,向上转型是什么呢?在上面我们说了子类会对父类中的一些方法进行重写,然后调用方法时就会调用子类重写的方法而不是原本父类的方法。向上转型后,子类单独定义的方法会丢失(即子类重载了父类中的方法),而子类中重写了父类的方法,当我们调用他们时,会调用重写的方法。
看上面的代码,Person类指向了Woman类,就是解释为这个人是女人,它就会调用女人类中的say()方法和show()方法,而不是Person类中的say()方法和show()方法。也就是上一段说的向上转型后,子类单独定义的方法会丢失(即子类重载了父类中的方法),而子类中重写了父类的方法,当我们调用他们时,会调用重写的方法。==
再展开说,如果说在Woman类中没有重写say()方法和show()方法,那它就会调用Person中的say()方法和show()方法。
public class Person {//人的类
String name = "Person";
public void say(){
System.out.println("person say");
}
public void show(){
System.out.println("person show");
}
}
public class Woman extends Person {//女人的类
String name = "Woman";
// @Override
// public void say(){
// System.out.println("woman say");
// }
// @Override
// public void show(){
// System.out.println("woman show");
// }
public void buy(){
System.out.println("buy buy ...");
}
}
public class Test {
public static void main(String[] args) {
// 多态的前提:1、要有继承关系 2、要有方法重写 3、父类的引用指向子类的对象
// 多态绑定
Person p = new Woman();
// 虚拟方法调用-编译看左边,运行看右边
p.say();
p.show();
// p.buy(); //编译错误,编译看左边。buy为Woman的方法,Person不能调用
}
}
// p.buy(); //编译错误,编译看左边。buy为Woman的方法,Person不能调用
解释这一句还得回到 注释中的==“虚拟方法调用-编译看左边,运行看右边”==,编译看左边,我们多态绑定的时候,左边是Person类,buy()的方法是Woman中的,所以不能调用bug()方法。那我们怎么样才能调用呢?继续看下去。
// 多态绑定
Person p = new Woman();
代码如下(示例):
public class Person {//人的类
String name = "Person";
public void say(){
System.out.println("person say");
}
public void show(){
System.out.println("person show");
}
}
public class Woman extends Person {//女人的类
String name = "Woman";
@Override
public void say(){
System.out.println("woman say");
}
@Override
public void show(){
System.out.println("woman show");
}
public void buy(){
System.out.println("buy buy ...");
}
}
public class Test {
public static void main(String[] args) {
// 多态的前提:1、要有继承关系 2、要有方法重写 3、父类的引用指向子类的对象
// 多态绑定
Person p = new Woman();
// 虚拟方法调用-编译看左边,运行看右边
p.say();
p.show();
// p.buy(); //编译错误,编译看左边。buy为Woman的方法,Person不能调用
System.out.println("类型没转化之前的属性:"+p.name);
// 想调用buy的方法,要进行强制类型转换
// 向下转型的方式
Woman w = (Woman)p;//注意:p指向的对象必须是Woman的对象才可以这么做
w.buy();
System.out.println("类型转化之后的属性:"+w.name);
}
}
还是上面的例子,我们要想调用Woman中的buy()方法,那我们要向下转型:
// 想调用buy的方法,要进行强制类型转换
// 向下转型的方式
Woman w = (Woman)p;
注意:p指向的对象必须是Woman的对象才可以这么做
就是说刚才p是指向Woman类,那我们只能向下转向Woman类,不能转向Man类,如果转向Man类就会编译报错。
Man m = (Man)p; // 发生错误:ClassCastException-类型转换异常
那么我们有没有什么方法来避免这样子的错误呢?有,为了解决类型转换异常,可以在向下转型时使用instanceof:
a instanceof Woman: a对象是否是Woman的一个实例(对象),如果是返回true,否则返回false
// 多态绑定
Person p = new Woman();
if(p instanceof Man){
System.out.println("This is a Man");
}else if(p instanceof Woman){
System.out.println("This is a Waman");
}
System.out.println(p instanceof Person);//true
System.out.println(p instanceof Woman);//true
System.out.println(p instanceof Man);//false
如果说Person继承了另外一个类,那么p是哪几个对象的实例呢?
不想看下面的代码,那就看下面的图,是代码的继承关系:
public class Ceature {
public void say(){
System.out.println("Ceature say");
}
public void show(){
System.out.println("Ceature show");
}
}
public class Person extends Ceature {
String name = "Person";
public void say(){
System.out.println("person say");
}
public void show(){
System.out.println("person show");
}
}
public class Woman extends Person {
String name = "Woman";
@Override
public void say(){
System.out.println("woman say");
}
@Override
public void show(){
System.out.println("woman show");
}
public void buy(){
System.out.println("buy buy ...");
}
}
public class Test {
public static void main(String[] args) {
Ceature c = new Ceature();
System.out.println(c instanceof Ceature);//true
System.out.println(c instanceof Person);//false
System.out.println(c instanceof Woman);//false
System.out.println(c instanceof Man);//false
}
}
结合上面的例子可以看出c指向的对象 instanceof 对象所在的类及父类:true,c指向的对象instanceof 对象所在的类的子类:false
代码与上面一样,现在实例例子不一样
Ceature cea = new Woman();
Person per = (Person)cea;
per.say();
//per.buy();//报错
Woman woman = (Woman)cea;
woman.buy();
这里cea指明是女人,所以per向下转型为Person时,还是指明为女人,不能说向下转型之后,女人就变了吧,所以这里还是女人的形态,但是它不具有女人的特有的方法,好比如就不能调用Woman类中的buy()方法,因为它已经转型为Person类了,Person中没有buy()的方法,但是其他特征还是女人的特征。
(不能理解上面的意思,就这样子理解,向下转型成Person类了,如果Woman中有重写Person中的方法,那就调用的是Woman中重写的方法,没有重写就是调用自己的方法,但是Woman特有的不能调用,还需要继续向下转型成Woman类才能调用,就可以理解为向上转型吧。)
多态的转型分为向上转型和向下转型两种。
向上转型:多态本身就是向上转型过的过程
使用格式:父类类型 变量名=new 子类类型();
适用场景:当不需要面对子类类型时,通过提高扩展性,或者使用父类的 功能就能完成相应的操作。
向下转型:一个已经向上转型的子类对象可以使用强制类型转换的格式,将父类引用类型转为子类引用各类型
使用格式:子类类型 变量名=(子类类型) 父类类型的变量;
适用场景:当要使用子类特有功能时。
上面就是多态的一些重点内容了,多态是面向对象特性中最难理解的一个特性,所以在学习的时候要多下功夫。如果上面有错,请大佬多多指教!加油吧!!!