Java多态

一.多态概念 

  注:Python支持多态(同样还有鸭子类型【可以不必继承重写方法,只要方法名相同,动态语言的特性】)

  意味着允许不同类的对象对同一信息做出不同的反应。

  分类:

      编译时多态:设计时多态,方法重载

      运行时多态:程序运行时动态决定调用哪个方法

  Java多态必要条件:

      满足继承关系;

      父类引用指向子类对象

二.向上转型和向下转型

  1.向上转型(隐式转型/自动转型):

    父类引用指向子类实例,可以调用子类重写父类的方法以及父类派生的方法,无法调用子类独有的方法【小类转型为大类】

    注意:父类中的静态方法无法被子类重写,所以向上转型之后,只有调用父类原有的静态方法(如果想调用子类的静态方法就得向下转型)

   2.向下转型(强制类型转换):

     子类引用指向父类对象,此处必须进行强转,可以调用子类特有的方法【 必须满足转换条件才能进行强制转换】

 1 package com.swpu.test;
 2 
 3 import com.swpu.plo.Animal;
 4 import com.swpu.plo.Cat;
 5 import com.swpu.plo.Dog;
 6 
 7 public class Test {
 8 
 9     public static void main(String[] args) {
10         /**
11          * 向上转型,隐式转型,自动转型
12          * 父类引用指向子类实例,可以调用子类重写父类的方法以及父类派生的方法,无法调用子类独有的方法
13          * 【小类转型为大类】
14          */
15         Animal one =new Animal();
16         Animal two=new Cat();
17         Animal three=new Dog();
18         one.eat();
19         //无法调用Cat和Dog独有的方法
20         two.eat();
21         three.eat();
22         /**
23          * 输出:
24          * 所有的动物都要吃东西
25                小猫吃东西!!!
26                狗吃东西!!!
27 
28          */
29         System.out.println("**************");
30         
31         /**
32          * 向下转型,强制类型转换
33          * 子类引用指向父类对象,此处必须进行强转,可以调用子类特有的方法
34          * 必须满足转换条件才能进行强制转换
35          */
36         Cat tmp=(Cat)two;
37         tmp.eat();
38         tmp.run();
39         //会报错,Cat和Dog没有关系,能转换为Cat是因为two原来指向的就是Cat空间【相当于还原】
40 //        Cat tmp2 =(Dog)two;
41         
42         
43 
44     }
45 
46 }
View Code

   3.instanceof:判断对象是否是某个类的实例

    注:相当于Python中的isinstance方法(会检查继承链,肯定是所实例类的对象实例,也是其所继承类的实例),而Python中type只判断是否指向同一个对象(不会检查继承链)

    

    

 1     if(two instanceof Cat){
 2             Cat tmp=(Cat)two;
 3             tmp.eat();
 4             tmp.run();
 5             System.out.println("我是猫对象");
 6         }
 7         
 8         if (two instanceof Dog){
 9             Dog tmp=(Dog)two;
10             tmp.eat();
11             tmp.sleep();
12             System.out.println("我是狗对象");
13         }
14         if (two instanceof Animal){
15             System.out.println("我是Animal对象");
16 
17             
18         }
19         if (two instanceof Object){
20             System.out.println("我是Object对象");
21 
22         }
23         /*输出:
24          *  小猫吃东西!!!
25             小猫跑步~~~
26             我是猫对象
27             我是Animal对象
28             我是Object对象
29 
30          */
31                 
32         
View Code

   4.类型转换总结:

   

    注意:父类的静态方法不能在子类中被重写,可以同名(但是不构成重写,是子类特有的静态方法)

  5.instanceof使用例子:

 1 package com.swpu.plo;
 2 
 3 public class Master {
 4     /**
 5      * 猫:吃完东西就跑
 6      * 狗:吃完东西就睡觉
 7      * 如果有很多动物,则需要一直重载方法
 8      */
 9     //方案一:编写方法,传入不同类型得动物,调用各自的方法
10 //    public void feed(Cat cat){
11 //        
12 //        cat.eat();
13 //        cat.run();
14 //    }
15 //    public void feed(Dog dog){
16 //            
17 //        dog.eat();
18 //        dog.sleep();
19 //        }
20     /**
21      * 使用instanceof
22      */
23     //方案二:编写方法传入动物的父类,方法中通过类型的转换,调用指定子类的方法
24     //视情况用不同的方案
25     public void feed(Animal obj){
26         obj.eat();
27         if(obj instanceof Cat){
28             Cat tmp=(Cat)obj;
29             //tmp.eat();
30             tmp.run();
31         }
32         else if(obj instanceof Dog){
33             Dog tmp=(Dog)obj;
34             //tmp.eat();
35             tmp.sleep();
36         }
37     }
38     
39     /**
40      * 饲养何种动物
41      * 时间多:养狗
42      * 时间少:养猫
43      */
44     //方案一
45     public Dog hasManytime(){
46         System.out.println("时间多,养狗");
47         return new Dog();
48     }
49     public Cat hasLittletime(){
50         System.out.println("时间少,养猫");
51         return new Cat();
52     }
53     //方案二
54     public Animal raise(boolean haManyTime){
55         if(haManyTime){
56             System.out.println("时间多,养狗");
57             return new Dog();
58         }
59         else{
60             System.out.println("时间少,养猫");
61             return new Cat();
62         }
63     }
64 }
View Code
 1 package com.swpu.test;
 2 
 3 import com.swpu.plo.Animal;
 4 import com.swpu.plo.Cat;
 5 import com.swpu.plo.Dog;
 6 import com.swpu.plo.Master;
 7 
 8 public class MasterTest {
 9 
10     public static void main(String[] args) {
11         Cat cat=new Cat();
12         Dog dog=new Dog();
13         Master master=new Master();
14         master.feed(cat);
15         master.feed(dog);
16         System.out.println("**************");
17         boolean hasManyTime=true;
18 //        if(hasManyTime){
19 //            master.hasManytime();
20 //        }
21 //        else{
22 //            master.hasLittletime();
23 //        }
24         Animal tmp=master.raise(hasManyTime);
25         
26         
27 
28     }
29 
30 }
View Code

 三.抽象类和抽象方法

  1.抽象类:加关键字abstract

    不允许实例化,可以通过向上转型指向子类实例。

    注:Python中使用abc模块实现抽象基类和抽象方法,但是抽象基类仍然可以是实例化,但抽象方法必须被重写。

    

 无法调用抽象类Animal(如果要用,可以向上转型,Animal one=new Cat();)

  2.抽象方法: 

    不允许包含方法体且抽象方法需要在抽象类中,子类当中需要重写父类的方法,否则子类也是抽象类。 

  3.总结:

    abstract定义抽象类;

    抽象类不能直接实例化,只能被继承,可以通过向上转型完成对象实例;

    abstract定义抽象方法,不需要具体实现;

    包含抽象方法的类是抽象类;

    抽象类中可以没有抽象方法;

    static(不允许子类重写),final(最终得,不能改变),private(私有的,只能当前类使用)不能和abstract并存。

 四.接口

  如电脑,手机,只能表都有照相的功能,如果分别写,则需要给每个类定义照相的方法。

  1.定义接口并测试:

    建立关系,把接口引用指向实现类的方式,从而完成不同的功能

    1.1接口定义:

1 package com.swpu.photos;
2 //定义接口
3 public interface Iphoto {
4     //具有照相功能
5     public void photo();
6 
7 }
View Code

    1.2重写接口方法:

 1 package com.swpu.photos;
 2 
 3 public class Computer implements Iphoto {
 4     public void game() {
 5         System.out.println("电脑可以玩游戏");
 6 
 7     }
 8 
 9     @Override
10     public void photo() {
11         System.out.println("电脑也可以照相");
12 
13     }
14 }
View Code
 1 package com.swpu.photos;
 2 
 3 public class Phone implements Iphoto{
 4     public void phone(){
 5         System.out.println("手机可以打电话");
 6     }
 7     @Override
 8     public void photo() {
 9         System.out.println("手机可以照相");
10         
11     }
12 }
View Code
 1 package com.swpu.photos;
 2 
 3 public class Watch implements Iphoto{
 4     public void times() {
 5         System.out.println("智能手表可以看时间");
 6         
 7     }
 8     @Override
 9     public void photo() {
10         System.out.println("智能手表也可以照相");
11         
12     }
13 
14 }
View Code

    1.3测试:

package com.swpu.test;

import com.swpu.photos.Computer;
import com.swpu.photos.Iphoto;
import com.swpu.photos.Phone;
import com.swpu.photos.Watch;

public class Test1 {
    public static void main(String[] args){
        Iphoto ip=new Phone();
        ip.photo();
        ip =new Computer();
        ip.photo();
        ip=new Watch();
        ip.photo();
    }
    

}
/**
 * 输出:
 *  手机可以照相
    电脑也可以照相
    智能手表也可以照相
**/
View Code

  2.接口成员:

    2.1接口规则:

      接口定义了某一批类所需要遵守的规范;

      接口不关心这些类的内部数据,也不关心这些类里的方法的实现细节,它只规定这些类里必须提供什么方法。 

      注:

        接口最好以I开头【规范】,接口访问修饰符只能是public或者是默认的;

        接口当中抽象方法可以不写abstract关键字,修饰符可以不写public【默认也会使用public】;

        当类实现接口时,需要去实现接口中的所有抽象方法,否则可以将类定义为抽象类不重写,继续延续下去;

        接口中可以定义常量 ,默认public static final,可以用接口名(引用)加常量直接访问(INet.TMP);

        如果实现类和接口中的有同名的常量,如果是接口引用指向实现类,调用的是接口的常量,如果是实现类引用指向实现类实例,则是实现类中的常量   

 1 package com.swpu.photos;
 2 
 3 //接口最好以I开头【规范】,接口访问修饰符只能是public或者是默认的
 4 public interface INet {
 5     // 接口当中抽象方法可以不写abstract关键字,修饰符可以不写public【默认也会使用public】
 6     // 当类实现接口时,需要去实现接口中的所有抽象方法,否则可以将类定义为抽象类不重写,继续延续下去
 7     public void net();
 8     public void connection();
 9 
10     // 接口中可以定义常量 ,默认public static final,可以用接口名(引用)加常量直接访问(INet.TMP)
11     // 如果实现类和接口中的有同名的常量,如果是接口引用指向实现类,调用的是接口的常量,如果是实现类引用指向实现类实例,则是实现类中的常量
12     int TMP = 20;
13 }
View Code

   3.默认方法和静态方法:

    3.1默认方法:  

      default:默认方法 可以带方法体 JDK1.8后新增;
      可以在实现类中重写,并可以通过接口的引用调用。

    3.2静态方法:    

      static:静态方法 可以带方法体 JDK1.8后新增;
      不可以在实现类中重写,可以使用接口名调用。

    3.3例:  

 1 package com.swpu.photos;
 2 
 3 //接口最好以I开头【规范】,接口访问修饰符只能是public或者是默认的
 4 public interface INet {
 5     // 接口当中抽象方法可以不写abstract关键字,修饰符可以不写public【默认也会使用public】
 6     // 当类实现接口时,需要去实现接口中的所有抽象方法,否则可以将类定义为抽象类不重写,继续延续下去
 7     public void net();
 8 
 9     // 接口中可以定义常量 ,默认public static final,可以用接口名(引用)加常量直接访问(INet.TMP)
10     // 如果实现类和接口中的有同名的常量,如果是接口引用指向实现类,调用的是接口的常量,如果是实现类引用指向实现类实例,则是实现类中的常量
11     int TMP = 20;
12     //default:默认方法  可以带方法体 JDK1.8后新增
13     //可以在实现类中重写,并可以通过接口的引用调用
14     default void connection(){
15         System.out.println("我是接口中的默认方法");
16     }
17     //static:静态方法 可以带方法体  JDK1.8后新增
18     //不可以在实现类中重写,可以使用接口名调用
19     static void stop(){
20         System.out.println("我是接口中的静态方法");
21     }
22 }
View Code
 1 package com.swpu.photos;
 2 
 3 public class Phone implements INet{
 4     public void phone(){
 5         System.out.println("手机可以打电话");
 6     }
 7     @Override
 8     public void net() {
 9         System.out.println("手机能上网");
10     }
11     @Override
12     public void connection() {
13         INet.super.connection();//调用接口中默认方法
14     }
15     
16     
17 }
View Code

     3.4处理多接口同名的默认方法:

      需要在子类中重写同名方法。

 1 package com.swpu.photos;
 2 
 3 //接口最好以I开头【规范】,接口访问修饰符只能是public或者是默认的
 4 public interface INet {
 5     // 接口当中抽象方法可以不写abstract关键字,修饰符可以不写public【默认也会使用public】
 6     // 当类实现接口时,需要去实现接口中的所有抽象方法,否则可以将类定义为抽象类不重写,继续延续下去
 7     public void net();
 8 
 9     // 接口中可以定义常量 ,默认public static final,可以用接口名(引用)加常量直接访问(INet.TMP)
10     // 如果实现类和接口中的有同名的常量,如果是接口引用指向实现类,调用的是接口的常量,如果是实现类引用指向实现类实例,则是实现类中的常量
11     int TMP = 20;
12     //default:默认方法  可以带方法体 JDK1.8后新增
13     //可以在实现类中重写,并可以通过接口的引用调用
14     default void connection(){
15         System.out.println("我是INet接口中的默认方法");
16     }
17     //static:静态方法 可以带方法体  JDK1.8后新增
18     //不可以在实现类中重写,可以使用接口名调用
19     static void stop(){
20         System.out.println("我是接口中的静态方法");
21     }
22 }
View Code
 1 package com.swpu.photos;
 2 //定义接口
 3 public interface Iphoto {
 4     //具有照相功能
 5     public void photo();
 6     default void connection(){
 7         System.out.println("我是Iphoto接口中的默认方法");
 8     }
 9 
10 
11 }
View Code
 1 package com.swpu.photos;
 2 
 3 public class Phone implements INet,Iphoto{
 4     public void phone(){
 5         System.out.println("手机可以打电话");
 6     }
 7     @Override
 8     public void net() {
 9         System.out.println("我是INet接口中的方法,手机能上网");
10     }
11     @Override
12     public void connection() {
13         System.out.println("我自己的collection方法 ");
14     }
15     @Override
16     public void photo() {
17         System.out.println("我是Iphoto接口中的方法,我可以照相");
18         
19     }
20     
21     
22 }
View Code

    3.5注:

      可以继承加实现接口(注继承要在实现类前,如public class Phone extends Tel implements IPhoto,INet(){},只能继承一个类,当可以实现多个接口)

      如果继承的类里面和实现的接口里面有同名的方法,默认指向父类当中的方法。

    3.6处理多接口同名的常量:

      需要明确表明是哪个接口中的常量。

 1 package com.swpu.test;
 2 interface one{
 3     static int x=10;
 4 }
 5 interface two{
 6     static int x=20;
 7 }
 8 
 9 public class Test2 implements one,two{
10     public void test(){
11         System.out.println(one.x);
12         System.out.println(two.x);
13     }
14     public static void main(String[] args){
15         new Test2().test();
16     }
17 
18 }
View Code

       如果继承的类里面和实现的接口有同名的常量:无法像同名方法那样默认为父类,需要自己在实现类中从新定义常量才不会报错。

  4.接口的继承:

     接口的继承可以继承多个接口,如果有重名的默认方法,需要在子接口中自定义。 

1 package com.swpu.photos;
2 
3 public interface ISon extends IFather1,IFather2{
4     void fly();
5     void run();
6 }
View Code

 五.内部类

  1.含义:

    在Java中,可以将一个类定义在另一个类里面或者方法里面,这样的类被称为内部类。与之对应,包含内部类的类被称为外部类。

  2.作用:

    内部类隐藏在外部类之内,更好的实现消息隐藏。

  3.内部类分类:

    成员内部类;静态内部类;方法内部类;匿名内部类

    3.1成员内部类:

      内部类中最常见的,也成为普通内部类。

 1 package com.swpu.people;
 2 //外部类
 3 public class Person {
 4     int age;
 5     public Heart getheart(){
 6 
 7         return new Heart();
 8     }
 9     public void eat(){
10         System.out.println("吃东西");
11     }
12     //成员内部类
13     /*
14      * 1.内部类在外部使用时,无法直接实例化,需要借由外部信息才能完成实例化;
15      * 2.内部类的访问修饰符可以任意。但是访问范围会受到影响;
16      * 3.内部类可以直接访问外部类的成员;如果出现同名的属性,优先访问内部类定义的;
17      * 4.可以通过外部类.this.成员的方式,访问外部类中同名的信息;
18      * 5.外部类访问内部类信息,需要通过内部类实例,无法直接访问;
19      * 6.内部类编译后.class文件命名:外部类$内部类.class
20      */
21     class Heart{
22         public void eat(){
23             System.out.println("我是内部类里的吃东西");
24         }
25         public String beat(){
26             //调用外部类的eat方法
27             Person.this.eat();
28             return age+"heart is running";
29         }
30     }
31 
32 }
View Code
 1 package com.swpu.people;
 2 
 3 public class PeopleTest {
 4 
 5     public static void main(String[] args){
 6         Person p1=new Person();
 7         p1.age=15;
 8         /**
 9          * 获取内部类对象实例:
10          *     方式一:new 外部类.new 内部类
11          * 
12          */
13         Person.Heart heart=new Person().new Heart();
14         System.out.println(heart.beat());
15         //方式二:外部类对象.new 内部类
16         heart =p1.new Heart(); 
17         System.out.println(heart.beat());
18         //方式三:外部类对象.获取方法
19         heart =p1.getheart();
20         System.out.println(heart.beat());
21 
22     }
23 }
View Code

    3.2静态内部类:

      静态内部类对象可以不依赖于外部类对象直接创建。

 1 package com.swpu.people;
 2 //外部类
 3 public class Person {
 4     int age;
 5     public Heart getheart(){
 6 
 7         return new Heart();
 8     }
 9     public void eat(){
10         System.out.println("吃东西");
11     }
12     /*
13      * 静态内部类:
14      *     1.静态内部类中,只能直接访问外部类的静态方法,如果需要调用非静态方法可以通过对象实例调用
15      */
16     static class Heart{
17         static void say(){
18             System.out.println("Hello");
19         }
20         public String beat(){
21             //调用外部类的eat方法
22             new Person().eat();
23             return new Person().age+"heart is running";
24         }
25     }
26 
27 }
View Code
package com.swpu.people;

public class PeopleTest {

    public static void main(String[] args){
        Person p1=new Person();
        p1.age=15;
        /*
         * 获取静态内部类实例的方法:
         *     1.静态内部类中,只能直接访问外部类的静态成员,如果需要调用非静态成员,可以通过对象实例;
         *  2.静态内部类对象实例时,可以不依赖于外部类对象;
         *  3.可以通过外部类.内部类.静态成员的方式,访问内部类的静态成员;
         *  4.当内部类属性与外部类属性同名时,默认直接调用内部类中的成员;
         *      如果需要访问外部类中的静态属性,则可以通过   外部类.属性   的方式
         *      如果需要访问外部类中的非静态属性,则可以通过   new 外部类().属性   的方式
         * 
         */
        Person.Heart heart=new Person.Heart();
        System.out.println(heart.beat());
        Person.Heart.say();
    }
}
View Code

    3.3方法内部类:

      定义在外部类方法中的内部类,也称局部内部类。

 1 package com.swpu.people;
 2 //外部类
 3 public class Person {
 4     int age;
 5     public void eat(){
 6         System.out.println("吃东西");
 7     }
 8     
 9     public Object getheart(){
10         /*
11          * 方法内部类:
12          *     1.定义在方法内部,作用范围也在方法内;
13          *     2.和方法内部成员使用规则一样,class前面不能加public,private,protected,static;
14          *     3.类中不能包含静态成员’
15          *     4.类中可以包含final,abstract修饰的成员(不推荐)
16          * 
17          */
18        class Heart{
19             public void say(){
20                 System.out.println("Hello");
21             }
22             public String beat(){
23                 //调用外部类的eat方法
24                 new Person().eat();
25                 return new Person().age+"heart is running";
26             }
27     }
28     return new Heart().beat();
29  
30     }
31 
32 }
View Code
 1 package com.swpu.people;
 2 
 3 public class PeopleTest {
 4 
 5     public static void main(String[] args){
 6         Person p1=new Person();
 7         p1.age=15;
 8         /*
 9          * 获取方法内部类
10          */
11         System.out.println(p1.getheart());
12     }
13 }
View Code

    3.4匿名内部类:

      没有名字,将类的定义和创建一起完成(如果只用一次可以这样,对系统内存消耗相对较小)

 1 package com.swpu.test;
 2 
 3 import com.swpu.people.Man;
 4 import com.swpu.people.Person;
 5 import com.swpu.people.Woman;
 6 
 7 public class PersonTest {
 8     //方案一
 9     /*
10     public void getRead(Man man){
11         man.read();
12     }
13     public void getRead(Woman woman){
14         woman.read();
15     }
16     */
17     //方案二
18     public void getRead(Person p){
19         p.read();
20     }
21     public static void main(String[] args) {
22         PersonTest p1=new PersonTest();
23         // TODO Auto-generated method stub
24         /*
25         Man man=new Man();
26         Woman woman =new Woman();
27         p1.getRead(man);
28         p1.getRead(woman);
29         */
30         //匿名内部类
31         /*
32          * 1.匿名内部类没有类型名称,实例对象名称;
33          * 2.编译后的文件命名:外部类$数字.class;
34          * 3.无法使用private/public/protected/adstract/static等;
35          * 4.无法在匿名内部类编写构造方法,可以添加构造代码块;
36          * 5.不能出现静态成员
37          * 6.匿名内部类可以继承父类,也可以实现接口(但是不能同时实现)
38          */
39         p1.getRead(new Person(){
40             @Override
41             public void read() {
42                 // TODO Auto-generated method stub
43                 System.out.println("我是匿名内部类");
44                 
45             }
46         });
47         
48 
49     }
50 
51 }
View Code

    

原文地址:https://www.cnblogs.com/lyq-biu/p/10696800.html