JAVA入门笔记8 —— 抽象和接口

一、抽象

1. 抽象的概念

  • 对于生活中具体的对象来说,其动作是可以比较清晰地描述的,而从若干个具体对象中提炼出抽象的概念后,其动作往往不能清晰定义
  • 比如正方形、圆形、三角形都有面积的简便计算公式,而抽象概念 “图形” 没有一个通用的面积简便计算公式;又如猫吃鱼、狗吃骨头,而抽象概念 “动物” 就不好定义它吃什么
  • 在面向对象中也有类似的情况,对于比较抽象的父类来说,其每个子类都有大同小异的同名方法,而父类中这个方法往往因太抽象而不好定义

2. java中的抽象方法和抽象类

(1)定义

  • java中通过abstract关键字定义抽象方法和抽象类,要求
    1. 抽象方法所在的类必须是抽象类
    2. 抽象类中可以定义普通方法
  • 示例
    权限修饰符 abstract 返回值类型 抽象类名{
          
          
    	// 定义抽象方法
    	权限修饰符 abstract 返回值类型 抽象方法名();
    	
    	// 定义普通方法
    	...	
    }
    

(2)使用

  • 使用抽象类和抽象方法的步骤:

    1. 不能直接new创建抽象类对象
    2. 必须用一个子类继承抽象类
    3. 子类类中必须重写父类中的抽象方法(去掉abstract并加上方法体)
    4. 创建并使用子类对象
  • 示例

    // 抽象类
    abstract class Animal{
          
          
    	// 抽象方法
        public abstract void eat();
    }
    
    // 继承自抽象类
    class Cat extends Animal {
          
          
    	// 重写抽象方法
    	@Override
        public void eat(){
          
          
            System.out.println("猫吃鱼");
        }
    }
    
    class Dog extends Animal {
          
          
    	@Override
        public void eat(){
          
          
            System.out.println("狗吃骨头");
        }
    }
    
    public class Abstract {
          
          
        public static void main(String[] args) {
          
          
            Cat cat = new Cat();
            Dog dog = new Dog();
    
            cat.eat();  // 猫吃鱼
            dog.eat();  // 狗吃骨头
        }
    }
    
  • 注意

    1. 抽象类不能创建对象实例,若创建会报错。只能创建其子类对象实例
    2. 抽象类中可以有构造方法,是供子类成员创建对象时初始化抽象父类所用(super()调用)
    3. 抽象类中不一定包含抽象方法,但有抽象方法的一定是抽象类。未含抽象方法的抽象类,目的就是不想让调用者创建此类对象,一般用于特殊的类结构设计
    4. 抽象类的子类,必须重写抽象父类中所有的抽象方法,除非此子类也是抽象类

二、接口

1. 接口的概念

  • 生活中的接口实例有电源接口、USB接口等,国家制定一套统一的标准,各个厂商按照标准生产,就都可以通用。抽象一点说,接口代表着一种公共的标准和规范
  • 把生活中 “接口” 这个概念引用到编程中,我们可以给多个类定义一个公共的规范

2. java中的接口

  • 接口是多个类的通用规范,是一种抽象数据类型,最重要的是其中的抽象方法(代表着规范)。接口相当于类的蓝图,指明了一个类必须要做什么和不能做什么

  • 关于java接口的理解,可以参考这里:JAVA基础——接口(全网最详细教程)

  • 接口所在的.java文件,编译后也会生成一个.class文件,可以把接口看做特殊的类

  • 使用接口的步骤

    1. 使用interface关键字定义一个接口

      public interface 接口名{
              
              
      	// ...
      }
      
    2. 接口不能直接new对象使用,必须有一个实现类来 “实现” 此接口

      public class 实现类名称 implements 接口名称{
              
              
      	// ...
      }
      
    3. 接口的实现类中必须覆写接口中的全部抽象方法,如果有抽象方法没有覆写,这个就只能是抽象类而不能使用了

    4. 创建实现类的对象,进行使用

  • 接口中包含的内容:

    1. java7引入:常量、抽象方法
    2. java8引入:默认方法、静态方法
    3. java9引入:私有方法

(1)接口中的抽象方法

  • 在接口中:定义抽象方法

    1. 在任何版本的java中,都可以在接口中定义抽象方法
      public abstract 返回值类型 方法名(参数列表); 
      
    2. 注意:接口中的抽象方法,修饰符必须是public abstract,这两个关键字可以省略,但是不能写别的(不省略、省略一部分、都省略,都一样)
  • 在实现类中:覆盖重写接口中所有的抽象方法。

    1. 在IDEA中,先写出空的实现类,此时由于没有进行抽象方法覆写,在定义位置会有红色波浪线
    2. 把光标放在波浪线上,按ALT+回车进入自动修复,选implements可以自动生成所有覆写方法的结构
  • 示例

    // 1. 定义接口
    interface MyInterface {
          
          
        // 这四个都是抽象方法,"public abstract"可以任意省略
        public abstract void method1();
        public void method2();
        abstract void method3();
        void method4();
    }
    
    // 2. 定义实现类
    class MyInterfaceImpl implements MyInterface{
          
          
        @Override
        public void method1() {
          
          
            System.out.println("这是方法1");
        }
    
        @Override
        public void method2() {
          
          
            System.out.println("这是方法2");
        }
    
        @Override
        public void method3() {
          
          
            System.out.println("这是方法3");
        }
    
        @Override
        public void method4() {
          
          
            System.out.println("这是方法4");
        }
    }
    
    // 3. 使用实现类
    public class Main {
          
          
        public static void main(String[] args) {
          
          
            MyInterfaceImpl impl = new MyInterfaceImpl();
            impl.method1(); // 这是方法1
            impl.method2(); // 这是方法2
            impl.method3(); // 这是方法3
            impl.method4(); // 这是方法4
        }
    }
    

(2)接口中的默认方法

  • 默认方法的作用:解决接口升级问题

    1. 比如说如果接口要进行修改升级,多加一个抽象方法,则用户程序中此接口的所有实现类都需要覆写这个新加的方法,这会导致大量的修改工作。
    2. 为了避免修改,可以不添加抽象方法而是在接口中添加一个默认方法。默认方法其中是有方法体的,因此不需要在实现类覆写。
    3. 默认方法也可以覆写,通过实现类.默认方法名()调用时,先在子类中找,找不到则向上去接口找。
  • 在接口中:定义默认方法

    1. 从java8开始,可以在接口中定义默认方法
      public default 返回值类型 方法名(参数列表){
              
              
      	// 方法体...
      }
      
    2. 注意:接口中的默认方法,修饰符必须是public,这个关键字可以省略,但是不能写别的
  • 参考:Java 8 新特性:接口增强

(3)接口中的静态方法

  • 类似类中的静态成员方法,接口中的静态成员方法也是和接口一起存储,对于实例对象来说是共享内存的

  • 在接口中:定义静态方法

    1. 从java8开始,可以在接口中定义静态方法
      public static 返回值类型 方法名(参数列表){
              
              
      	// 方法体...
      }
      
    2. 注意:接口中的静态方法,修饰符必须是public,这个关键字可以省略,但是不能写别的
  • 在实现类中

    1. 接口中的静态方法不能被继承,因为一个类是可以实现多个接口的,如果接口中的静态方法的方法前面相同,就会发生继承冲突,所以索性就从继承这个层面阻断冲突的发生。
    2. 不能通过实现类的对象实例调用接口中的静态方法
  • 使用静态方法:通过接口名.静态方法名()直接调用

  • 参考:Java8新特性——接口静态方法

(4) 接口中的私有方法

  • 当接口中有两个或多个方法存在大量相同代码时,我们应该把它们抽取出来作为一个方法,从而减少冗余代码。这个方法应该仅在接口内使用,而不能让实现类使用,也就是说应该是私有化的

  • 在接口中:从java9开始,可以定义私有方法

    1. 普通私有方法:解决多个默认方法间的重复代码问题

      private 返回值类型 方法名称(参数列表){
              
              
      	// 方法体...
      }
      
    2. 静态私有方法:解决多个静态方法间的重复代码问题

      private static 返回值类型 方法名称(参数列表){
              
              
      	// 方法体...
      }
      
  • 示例

    interface MyInterface {
          
          
        public static void StaticMethod1(){
          
          
            System.out.println("这是静态方法1");
            StaticCommon();
        }
    
        public static void StaticMethod2(){
          
          
            System.out.println("这是静态方法1");
            StaticCommon();
        }
    
        // 通过静态私有方法解决静态方法间的重复代码问题
        private static void StaticCommon(){
          
          
            System.out.println("公共内容");
        }
    }
    
    public class Main {
          
          
        public static void main(String[] args) {
          
          
            // 使用接口名直接调用接口的静态防方法
            MyInterface.StaticMethod1();
            MyInterface.StaticMethod2();
        }
    }
    
    /*
    这是静态方法1
    公共内容
    这是静态方法1
    公共内容
    */
    

(5)接口中的 “成员变量”

  • 接口中也可定义成员变量,但是必须用public static final修饰。类似接口抽象方法,程序中可以任意省略这三个修饰符,但是不改变其特性从效果上看,这就是接口的常量
  • 在接口中定义 “成员变量” 的格式:
    public static final 数据类型 常量名称 = 常量;
    
  • 注意:
    1. 由于public,接口的 “成员变量” 可以被实现类继承
    2. 由于static,可以通过接口名直接访问“成员变量”
    3. 由于final,这是一个不可变变量,在定义时必须赋值;若不手动赋值,自动赋值为0
  • 对于常量,推荐使用全大写+下划线进行命名

(6)接口成员小结(java 9)

  1. 成员变量其实是常量

    1. 格式
      [public][static][final] 数据类型 常量名称 = 常量值;
      
    2. 注意:
      1. 常量必须赋值,且一经赋值不可改变
      2. 常量名称推荐全大写+下划线分割
  2. 接口中最重要的是抽象方法

    1. 格式
      [public][abstract] 返回值类型 方法名称(参数列表);
      
    2. 注意:
      1. 抽象方法不能直接使用,必须通过 “实现类” 间接使用
      2. 实现类中必须覆写所有接口所有抽象方法,除非实现类也是抽象类
  3. 从java 8开始,接口中允许定义默认方法

    1. 格式
      [public] default 返回值类型 方法名称(参数列表){
              
              
      	// 方法体...
      }
      
    2. 注意:默认方法也可以在实现类中覆写
  4. 从java 8开始,接口中允许定义静态方法

    1. 格式
      [public] static 返回值类型 方法名称(参数列表){
              
              
      	// 方法体...
      }
      
    2. 注意:应该通过接口名称调用,不能通过实现类对象调用接口的静态方法
  5. 从java 9开始,接口中允许定义私有方法

    1. 格式
      // 普通私有方法
      private 返回值类型 方法名称(参数列表){
              
              
      	// 方法体...
      }
      
      // 静态私有方法
      private static 返回值类型 方法名称(参数列表){
              
              
      	// 方法体...
      }
      
    2. 注意:私有方法只能在接口中使用,不能通过实现类使用

(7)用接口实现多继承

  • java中的类是没有多继承的,但是可以通过接口实现类似的效果

  • 使用接口时,需要注意

    1. 接口是没有静态代码块没有构造方法

    2. 一个类的直接父类有且只有一个,但一个类可以同时实现多个接口,格式

      public class MyInterfaceImpl implents InterfaceA,InterfaceB{
              
              
      	// 覆盖重写实现的所有接口的抽象方法
      }
      
    3. 如果类实现的多个接口中有重名的抽象方法,只要覆盖重写一个即可

    4. 如果类实现的多个接口中有重名的默认方法,实现类必须对冲突的默认方法覆盖重写

    5. 如果实现类的直接父类中的方法,和接口中的默认方法同名,优先用父类中的方法

    6. 如果实现类中没有覆写所有接口中的所有抽象方法,这个类只能是抽象类

  • 类和接口的关系

    1. 类和类之间是单继承的,直接父类仅有一个
    2. 类和接口直接是多实现的,一个类可以实现多个接口
    3. 接口和接口之间是多继承的,注意
      1. 多个父接口中的抽象方法若重复,没关系(因为没有方法体)
      2. 多个父接口中的默认方法若重复,子接口必须覆写默认方法,而且要带着default关键字
  • 示例

    interface MyInterfaceA{
          
          
        public abstract void methodA();
        public abstract void methodCommon();
        public default void methodDefault(){
          
          
            System.out.println("接口A默认方法");
        }
    }
    
    interface MyInterfaceB{
          
          
        public abstract void methodB();
        public abstract void methodCommon();
        public default void methodDefault(){
          
          
            System.out.println("接口B默认方法");
        }
    }
    
    /*
    这个子接口中有四个抽象方法
        1. methodA 来自 MyInterfaceA
        2. methodB 来自 MyInterfaceB
        3. methodCommon 来自 MyInterfaceA和MyInterfaceB
        4. method 来自 自己
    */
    interface MyInterface extends MyInterfaceA,MyInterfaceB{
          
          
        public abstract void method();
        
        @Override
        default void methodDefault() {
          
          
            System.out.println("子接口中需要覆写冲突的默认方法");
        }
    }
    
    public class Main {
          
          
        public static void main(String[] args) {
          
          
            // ... 
        }
    }
    

猜你喜欢

转载自blog.csdn.net/wxc971231/article/details/109064531