Java入门基础(三)面像对象——多态、接口

面向对象—多态、接口


1.多态

  • 概念:指的是一种实物,在不用时刻,所表现出的不同状态。

  • 多态的前提:要有继承,如果没有继承,那多态也无从谈起

  • 多态要有方法重写,如果不重写,语法上来说不报错,但是多态的存在就没有意义了

  • 父类引用指向子类对象,举例:

    Animal an = new Dog();   //这种写法才是多态!
    

2.多态的形式访问成员变量的特点:

  • 多态的形式访问成员变量:编译看左边,运行看左边
Fu fu = new Zi();
system.out.println(fu.num) //编译看左边,运行看左边
//打印的是父类定义的变量值
//编译看左边的意思是:编译器检查报错
//运行看右边的意思是:运行代码的时候,到底运行的是谁的代码
    
  • 多态的形式访问成员方法的特点:编译看左边,运行看右边(子类重写了父类的方法)
public class Test {
    public static void main(String[] args) {
//        Zi zi = new Zi();
//        zi.show();
//        System.out.println("这是主方法!");
        // 父类引用指向子类对象:
        // 左(父类)  右(子类)
        Fu fu = new Zi();
        ((Zi) fu).show2(); //这样就不会报错!(这样感觉就成了普通的继承,并不是多态!)
        //fu.show2(); //编译报错!(编译看左边,左边是父类,但父类没有相应的show2方法)
        Fu fu2 = new Zi2();
        fu2.show(); //运行的是右边的结果,即:子类的结果
    }
}

class Fu{
    public int age =100;
    public void show (){
        System.out.println(age);
        System.out.println("这是父类的方法!");
    }
}

class Zi extends Fu{

    //public int age;
    @Override //注解符
    public void show() {
        super.show();
        System.out.println(age);
        System.out.println("这是子类的方法!");
    }

    public void show2(){
        System.out.println("这是Zi的show2方法");
    }
}

class Zi2 extends Fu{
    @Override
    public void show() {
        //super.show();
        System.out.println("这是Zi2的show方法!");

    }
}
----------------------------------------
输出:
这是Zi的show2方法
这是Zi2的show方法!
  • 使用多态的好处:

    • 1.提高了代码的复用性,是靠继承保证的。
    • 2.提高了代码的扩展性(详情可以参考老师上课做得工具类的示例!!!,即:在工具类的类方法中的形参的类型用父类来声明,传递实际参数的时候,用子类对象来代表实际参数,即:造成"父类引用指向子类对象的效果"!)
    • 3.高内聚,低耦合;
    • 4.接口性;
    • 5.灵活性;
    • 6.简化性
  • 测试类:只需要提供一个入口即可,不需要在测试类中写太多的类方法,即:测试类中最好只有一个main函数即可。

  • 多态的弊端:

    • 多态的形式,去访问子类特有的方法,是调用不到的!(必须利用向下类型转换来访问子类特有的方法!)

      • 如果想要访问,我们可以向下转型
      Father father = new Zi(); //向上转型!
      Zi zi = (Zi) father; //向下转型!
      
      • 向下转型可能会遇到类型转换异常的情况:java.lang.ClassCastException(见老师上课演示)

      原因就是:其实写成了子类和子类之间的相互转换,这是不能转换的!只能把向上转型过的子类对象再向下回转(型)为原子类对象,不能把向上转型过的子类对象向下回转(型)为另一个子类对象(同属于一个父类的另一个子类)

      示例如下:

      package westos.org.newtest;
      
      public class Test {
          public static void main(String[] args) {
              Cat cat = new Cat();
              Animal an = cat; //多态:向上转换!
              Cat cat1 = (Cat) an; // 向下转换!
              //Tiger tiger1 = (Tiger)an;(编译器不报错,但是运行的时候会报出类型转换异常的错误!)
              //上下两者是等价的
              //Tiger tiget1 = (Tiger)cat;(编译器直接报错,说明两个不同的子类是不能相互转换的!)
      
              Tiger tiger = new Tiger();
               //新建立一个父对象,然后测试它能否被直接转换为子类对象(说明父类对象不能被强制转换为子类对象,子类指针不能指向父类对象(除非是向上转换过以后的!),但是父类对象可以指向子类对象(多态))
              Animal an1 = new Animal();
              Cat cat2 = (Cat)an1;//报错(类型转换异常)java.lang.ClassCastException  westos.org.newtest.Animal cannot be cast to westos.org.newtest.Cat
              cat2.eat();
          }
      }
      
      class Animal {
          String name;
          public void eat(){
              System.out.println(name+"这是Animal的eat方法!");
          }
      }
      
      class Cat extends Animal{
          String name = "小猫咪";
          @Override
          public void eat() {
              System.out.println(this.name +"爱吃小鱼干!");
          }
      }
      
      class Tiger extends Animal {
          String name = "小老虎";
          @Override
          public void eat() {
              System.out.println(name+"爱吃小猫咪!");
          }
      }
      
      • 多态本身就是“向上转型!”
  • 多态(向上,向下类型转换)的一个非常经典的示例:课堂上讲述的**“孔子装爹”**案例!

  • 动态绑定(在上课讲授多态情况下的内存图总提到的!)

    • 向上转换的时候,指针实际指向的区域是堆中子类对象中的一块super区域(初始化子类过程中初始化的父类对象),所以应用变量的时候它会直接取父对象的变量,但是在应用其方法的时候,会进行动态绑定,看子类中是否有定义的同名方法,如果有的话,则执行子类方法,否则,则执行父类方法。

2.抽象类

  • abstract 关键字 :抽象的,可以修饰类,修饰方法!

    • 被abstract修饰过的类(则,该类变成抽象类),此类就不能直接实例化了
    • 在抽象类中用abstract 修饰一个方法,则继承该抽象类的子类必须重写这个方法!
  • 特点:

    • 一个类,里面有了抽象方法,那么这个类,必须为抽象类
    • 一个抽象类里面,可以没有抽象方法!(如果有方法,则不一定全都为抽象方法!)
    • 抽象类中可以有非抽象方法
    • 抽象类里面有没有构造方法?有
    • 采用多态的方式来完成父类数据的初始化(已证实:可以)
    public class Test {
        public static void main(String[] args) {
            Animal an1 = new Cat();
            System.out.println(an1.age);
            System.out.println(((Cat) an1).name);
            an1.eat();
            an1.sleep();
    
            ((Cat)an1).catchMouse();
        }
    }
    -------------------------------------------
    输出:
    抽象类初始化已经调用!
    Cat类初始化函数已经被调用!
    1
    小猫咪
    10岁的小猫咪要吃小鱼干!
    10岁的小猫咪在白天睡觉!
    10岁的小猫咪会抓老鼠!
    
    public abstract class Animal {
        int age;
        String name;
    
        Animal(){
            System.out.println("抽象类初始化已经调用!");
            age = 1;
            name = "动物";
        }
        //定义吃饭的抽象方法:(具体由子类自己来实现)
        public abstract void eat();
    
        //定义睡觉的抽象方法:(具体由子类自己来实现)
        public abstract void sleep();
    
    }
    
    package westos.org.test2;
    
    public class Cat extends Animal{
        int age;
        String name;
    
        Cat(){
            System.out.println("Cat类初始化函数已经被调用!");
            age = 10;
            name ="小猫咪";
        }
        @Override
        public void eat() {
            System.out.println(age+"岁的"+name+"要吃小鱼干!");
        }
    
        @Override
        public void sleep() {
            System.out.println(age+"岁的"+name+"在白天睡觉!");
        }
    
        public void catchMouse(){
            System.out.println(age+"岁的"+name+"会抓老鼠!");
        }
    }
    
    
    package westos.org.test2;
    
    public class Dog extends Animal {
        String name  = "小狗";
        int age = 10;
    
        @Override
        public void eat() {
            System.out.println(age+"岁的"+name+"要啃骨头!");
        }
    
        @Override
        public void sleep() {
            System.out.println(age+"岁的"+name+"在晚上睡觉!");
        }
    
        public void watchDoor(){
            System.out.println(age+"岁的"+name+"会看门!");
        }
    }
    
    
    • 抽象类的构造函数调用必须依靠多态?(个人认为单纯的继承也可以!已证实:可以)
    public class Test {
        public static void main(String[] args) {
            Cat cat1 = new Cat();
            cat1.eat();
            cat1.sleep();
            cat1.sleep();
    
        }
    }
    -------------------------------------------------------
    输出:
    抽象类初始化已经调用!
    Cat类初始化函数已经被调用!
    10岁的小猫咪要吃小鱼干!
    10岁的小猫咪在白天睡觉!
    10岁的小猫咪在白天睡觉!
    
    • 抽象类成员变量的特点:1.可以定义成员变量,也可以定义常量。
    • abstract 不能修饰变量;
  • 抽象类,既可以定义抽象方法,也可以定义非抽象方法

    • 抽象方法:它是强制子类必须重写
    • 非抽象方法: 一般就是让你继承袭来继续使用,但子类也可以重写非抽象类的方法!
  • 一个类如果没有抽象方法,可不可以定义为抽象类?如果可以,有什么意义?

    • 可以,外界不能创建该类的对象!(抽象类不能直接初始化,只能依靠子类间接初始化)
  • abstract 在方法中使用,能不能和下面的关键字一起使用?

    • private abstract 两者互相矛盾,abstract 强制子类重写 ,private 限定后又无法继承(更不能重写了!)
    • final abstract 两者互相矛盾, abstract 强制子类重写,final 修饰的也没法重写
    • abstract static 两者也互相矛盾,abstract 强制子类重写,static 修饰过后的方法属于类方法,谈不上重写!(重写:是先造出父类的对象,再造出子类的对象,然后用子类对象与父类对象的同名方法去对父类的方法进行覆盖,但是static声明的方法属于类,不需要实例化即可使用,也就谈不上实例化的继承和覆盖了!,参考内存图!)
      • 引出了一个问题:继承和static的关系:(待补充!)
  • IEDA 神器插件-----------> 彩虹括号(方便辨别!)

3.接口(注意区分接口和继承的区别!)

  • 概念:用来定义实物的一些额外的扩展功能,将来那类实物,想要具备,这些额外的功能,就可以实现这个接口
  • 定义接口的语法 interface 接口名{}
  • 类 implements 接口 (这是一种实现关系!)
    • 这个类就是这个接口的子类,这个接口 可以叫做这个类的父接口

接口的特点:

  • 接口不能直接实例化

  • 接口没有构造方法这一说

  • 接口的子类有什么要求:

    • 1.要求子类必须重写子类中所有的抽象方法
    • 2.如果你不想重写,你这个类可以作为一个抽象类,将接口中的抽象方法继承下来即可(一般不推荐这么做,没啥意义!)
  • 接口中的方法存在默认修饰符 public abstract

    示例如下:

    interface AA{
        public abstract void aa();
        void aa(); //这个与上面其实是一样的
    }
    

接口中的成员特点:

  • 接口中没有构造方法!
  • 接口中的成员变量:
    • 接口中的成员变量全是公共的静态常量
    • 接口中的成员变量前面存在默认修饰符 public static final
    • 接口中的成员变量如何使用?
  • 接口中的成员方法:
    • 接口中的方法存在默认修饰符 public abstract
public class Test {
    public static void main(String[] args) {
        MyInterface interface1 = new Student();//多态
        System.out.println(interface1.num);//多态下的成员变量访问规则:编译看左边,运行看左边	
        Student student = new Student();//普通继承
        System.out.println(student.num);

    }
}

interface MyInterface{
    int num = 20;
    public abstract void show();
}

class Student implements MyInterface{
    int num = 10;
    @Override
    public void show() {
        System.out.println("子类的show方法!");
    }
}
----------------------------
输出:
20
10

类,接口之间的关系:

  • 类与类的关系:继承关系 extends Java中只支持单继承

  • 类与接口的关系:实现关系 implements 可以多实现 一个类可以实现多个接口

    class MyClass implements AA,BB,CC{
        
    }
    
  • 接口与接口之间的关系:继承关系 extends 支持多继承 一个接口可以继承多个接口

    interface CCC extends AAA,BBB{
        
    }
    
  • 一个类在继承一个类的同时,也可以实现多个接口

    class CCC extends AAA implemens BBB,DDD,EEE{
       
    }
    
  • JDK 1.8 允许接口的方法有功能上的实现,但这个方法用default来修饰

    • JAVA用接口上的多实现弥补了JAVA类不支持多继承的弊端。
发布了34 篇原创文章 · 获赞 4 · 访问量 1215

猜你喜欢

转载自blog.csdn.net/zx123456789kk/article/details/102594564