• Java基础11——抽象类和接口


    Java基础11——抽象类和接口

    抽象类和抽象方法

    区分普通方法和抽象方法

    在Java 中,当一个类被 abstract 关键字修饰的时候这个类称为抽象类。当一个类的方法被 abstract 关键字修饰的时候,该方法称为抽象

    方法。抽象方法必须定义在抽象类中。当一个方法被定义为抽象方法后,意味着该方法不会有具体的实现,而是在抽象类的子类中通过方法重写进行实现。定义抽象方法的语法格式如下:

    [访问修饰符]abstract <返回类型><方法名>([参数列表])abstract 关键字表示该方法被定义为抽象方法。
    
    • 1
    • 2

    普通方法和抽象方法相比,主要有下列两点区别:

    • 抽象方法需要用修饰符 abstract 修饰,普通方法不允许
    • 普通方法有方法体,抽象方法没有方法体。

    区分普通类和抽象类

    在Java中,当一个类被abstract关键字修饰时,该类为抽象类。定义抽象类的方法如下:

        abstract class <类名>{
    
        }
    
    • 1
    • 2
    • 3

    abstract关键字表示该类被定义为抽象类。

    普通类与抽象类相比,主要有下列两点区别:

    • 抽象类需要用修饰符 abstract 修饰,普通类不允许
    • 普通类可以被实例化,抽象类不可以被实例化。
    public abstract class Person {
        //属性
        private String name = "无名氏";
        private int age = 100;
        private int weight = 0;
        //构造方法
        public Person(String name){
            this.name = name;
        }
        public void print(){
            System.out.println("我的名字是"+ this.name + ",年龄是" + this.age + ",体重是"+this.weight ".");
        }
    }
    
    class Test{
        public static void main(String[] args) {
       //Pet pet = new Pet("大黄");//错误示范,因为抽象类不能实例化
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    定义一个抽象类

    当一个类被定义为抽象类时,它可以包含各种类型的成员,包括属性、方法等,其中方法又分为普通方法和抽象方法,下面是抽象类结构的示例:

    public abstract class 类名{
    
        修饰符 数据类型  变量名;
    
        修饰符 abstract 返回值类型 方法名称(参数列表);
    
        修饰符 返回值类型 方法名称(参数列表){
    
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    提示:

    • abstract 修饰的抽象方法没有方法体
    • private 关键字不能用来修饰抽象方法,否则由于封装导致子类无法重写抽象方法
    • 抽象类不能被之间实例化,但有构造方法
    • 子类如果不是抽象类,则子类必须重写抽象类中的全部抽象方法
    • abstract 修饰符不能和final修饰符一起使用

    抽象类和具体类的比较

    抽象类和具体类是面向对象编程中的两个重要概念

    • **抽象类: **抽象类是一种不能被实例化的类,它只能被用作其他类的基类。它的目的是为了提供一种基于继承的模板,强制要求子类实现它的抽象方法。抽象类中可能包含一些实现方法,但是也可以只包含抽象方法。

    • 抽象类的主要特点:

      • 不能被实例化
      • 可以包含抽象方法和非抽象方法
      • 子类必须实现抽象方法
      • 抽象类可以包含构造函数。
    • 具体类: 具体类是可以被实例化的类,它可以直接使用,也可以继承自其他类。具体类中必须要实现它的所有方法,不能有未实现的方法。它可以包含属性、方法、构造函数等。

    • 具体类的主要特点:

      • 可以被实例化
      • 必须实现所有方法,可以包含属性、方法、构造函数等。
    两者的比较:
    • 抽象类和具体类在定义上的差异:抽象类是一种不能被实例化的类,它只能被用作其他类的基类。具体类则是可以被实例化的类。

    • 抽象类和具体类在实现上的差异:抽象类可以包含抽象方法和非抽象方法,子类必须实现抽象方法; 具体类必须实现它的所有方法,不能有未实现的方法。

    • 抽象类和具体类在使用上的差异:抽象类通常用于定义一些基础的、通用的行为和属性,而具体类则用于实现具体的业务逻辑和行为。

      总的来说,抽象类和具体类是两种不同类型的类,它们的作用和使用场景也不同。

      抽象类用于定义一些通用的行为和属性,并强制子类实现抽象方法,具体类则用于实现具体的业务逻辑和行为。

    内部类

    内部类是指讲一个类定义在另外一个类的内部,称为内部类。

    成员类可以解决类单继承问题

    public class AbstractDemo {//外部类
     private  static   int a =0;
      private  int b =3;
        public  void a(){
    //        Abstr abstr = new Abstr();
          new Abstr().test();
          Abstr.abc();
        }
        static  class  Abstr {
            private int aa =3;
            private static int bb=2;
             public void test(){
                 int a = 5;
                 System.out.println(aa);
                 aa=2;
                 System.out.println(aa);
                 System.out.println(bb);
                 Abstr.bb=4;
                 System.out.println(bb);
                 System.out.println(AbstractDemo.a);
               AbstractDemo.a=5;
                 System.out.println(AbstractDemo.a);
               System.out.println(a);
           }
           public static void abc(){
               System.out.println("a");
               Abstr.bb=3;
               new Abstr().aa=1;
           }
    
    }
    class Abstra{//内部类
       private  static  int b =3;
       private  int aa =2;
        public  void test1(){
            int b =2;
            System.out.println(b);//当前类的对象
            AbstractDemo.this.b=5;//外部类的对象
            System.out.println(AbstractDemo.this.b);
        }
      }
    
    public static void main(String[] args) {
        AbstractDemo.Abstr ab = new AbstractDemo.Abstr();
        ab.test();
        Abstr.abc();
        AbstractDemo.Abstra abs = new Abstractemo().new Abstra();  
        //        abs.test1();
        }
    }  
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    内部类

    在一个类中声明的类称为内部类. 内部类是一个嵌套类.

    内部类包含以下几种:

    • 成员类。在类的里面直接声明的类
    • 局部类。声明在一个块中, 当 main 方法调用结束,就销毁
    • 匿名类。

    匿名类

    之前定义的类都是命名类,也就是类都有明确的名称。匿名类是指没有明确名称的类。

    public abstract class AbstractDemo2 {
        public static void test(){
            System.out.println("哈哈哈");
        }
        public abstract void play();
    }
        public TestAbstract() {
        }
    public static void main(String[] args) {
        AbstractDemo2 stu = new AbstractDemo2() {
            @Override
            public void play() {
                System.out.println("发发发");
            }
        };
        stu.play();
    }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    lambda表达式

    接口中有一种特殊的接口,这类接口中只有一个抽象方法。我们把这种接口称为函数式接口(FunctionalInterface) 可以使用 @FunctionalInterface 标记函数接口 函数接口表示某个功能(能力)

    image-20230829204610651

    函数式接口的匿名类写法可以使用 lambda来简写.

    // 只能实现共有的匿名类

    @FunctionalInterface
    interface IType {
        void test();
    }
    class Demo{
        public static void main(String[] args){
            // 通常使用 匿名类
            IType a = new IType(){
               @Override
               public void test(){
                   // 实现
               }
            };
            
            // lambda
            IType b = () ->{
                // 实现
            };
        }    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    如果lambda中实现只有一句代码则可以省略 {} 这种写法java可以根据变量的类型推断出匿名类实现的接口,以及重写的方法。 方法写法上:

    • 如果方法没有参数则使用 ()表示。
    • 如果有参数, 则需要在()写形参列表,在实现中可以使用这些参数。
      • 参数的类型可以省略,可以推断出参数类型
      • 如果只有一个参数则 () 可以省略
    Notes:
    • static 成员类, 使用 类名. 调用. 使用 new 外部类.内部类();
    • 实例成员类, 使用外部类的对象来调用. 使用 对象.new 内部类();
    • 成员类属于类的成员,所以可以调用外部类的 private 成员
    • 在实例成员类中可以使用 外部类.this 表示外部类对象 (实例成员类是由外部类对象创建的)
    • 成员类中的 this 表示的是成员类
    • 外部类可以直接调用成员类的静态成员,实例成员需要创建成员类的对象来调用
    • 外部类可以访问成员类的 private 成员
    • 类的成员: 字段、方法、成员类/接口
    • 成员类(内部类): 在类的里面声明的类. 可以被继承
    • 外部类的访问修饰符只能是: public / package-access
    • 成员类的访问修饰符可以是所有的访问修饰符
    • 可以使用 final static abstract 等修饰符修饰成员类

    接口基本概念

    约定好规范,然后按照规范来做。接口就是定义规范。

    • 描述规范的数据类型
    • 只定义规范(不需要实现):提供一种约定,使实现接口的类在形式上保持一致。
    • Java中规范可以用接口interface来表示

    抽象类中可以有普通方法,而接口中的方法默认都是抽象的,也可以说接口是一个 ”特殊的 抽象类 “,接口不能被实例化,而且没有构造方法。

    接口的格式

    public interface 接口名{
        // 接口成员
    }
    
    • 1
    • 2
    • 3

    解析:

    • 定义接口使用interface修饰符

    • 一个接口可以继承其它接口,称为父接口,且接口可以多继承;它会继承父接口中声明的常量和抽象方法

    • 成员列表中的成员变量声明[public][static][final] 数据类型 成员变量名 = 常量;即,接口中的成员变量默认都是public、static、final的,因此,public、static、final 可以省略

    • 成员列表中的成员方法声明[public][abstract] 返回值类型 方法名称(参数列表);即接口中的方法默认都是public、abstract的,因此,public、abstract可以省略

    • 接口中的变量只能是静态常量( static final ) , 所以可以省略 public static final ,静态常量在定义时就要赋值,且不可变。

    接口的使用

    与抽象类一样,使用接口要通过子类,子类通过implements关键字实现接口。

    子类通过 implements 关键字实现接口,实现接口就必须实现(重写)接口中的抽象方法

    实现接口的语法格式如下:

    [修饰符] 类名 implements 接口名{ 
    	//实现方法
        // 普通方法
        // 属性
        
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    ​ 解析:

    • 实现接口要用implements关键字
    • 一个类可以实现多个接口,各接口之间用逗号分隔
    • 实现接口的类必须实现接口中定义的所有抽象方法,即使类中不使用某个抽象方法也必须实现它,通常用空方法体实现子类不需要的抽象方法,如果抽象方法有返回值,可返回默认值。
    • 接口的实现类允许包含普通方法
    • 在实现抽象方法时需要指定public权限,否则会产生编译错误
    • 类和类之间叫做继承,用extends
    • 类和接口之间叫做实现implement;

    多个接口实现

    java 中继承是单继承,使用 extends 关键字;

    一个接口实现类可以实现多个接口,使用 implements ,多个接口之间用 , 隔开。
    public class Computer implements USBInterface, ChargeInterface{ 
        //定义一个实现类实现多个接口
    	public void play() { // 普通方法
        
        System.out.println("play game"); 
        }
        @Override 
    public void charge() { //重写抽象方法
        
        System.out.println("充电"); 
    }
        @Override 
    public void service() { //重写抽象方法
    
            System.out.println("USB接口"); 
    
    } 
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    一个类可以同时继承和实现接口, extends 要在 implements 之前
    public class LenovoComputer extends Computer implements USBInterface, ChargeInterface{
    
    }
    
    • 1
    • 2
    • 3
    接口与接口之间是继承关系,使用 extends 关键字。多个接口使用 , 隔开
    public interface USBC extends USBInterface,ChargeInterface{ 
    
    } 
    
    • 1
    • 2
    • 3

    JDK8.0 接口新特性 default 和 static

    **default:**在 jdk8.0 中 default 关键字可用于在接口中修饰方法(默认方法), default 修饰的方法可以有具体实现,也只能在接口中出现。 default 修饰的方法可以被重写。

    默认方法可以在不破坏已经在使用该接口的所有代码。默认方法有时也称为防御方法(defender method)或 虚拟扩展方法(virtual extension method)

    **static:**接口中还可以有 static 修饰的方法,称为静态方法(类方法)。 static 方法必须直接使用 接口名.方法名调用。

    从Java 8 开始,接口允许定义默认方法(注意是在接口), 格式:

     public default 返回值 方法名(){
     	方法体
     }
    
    • 1
    • 2
    • 3

    从Java 8 开始,接口允许定义静态方法。

     public static  返回值 方法名(){
      方法体
      }
    
    
    • 1
    • 2
    • 3
    • 4

    接口名称。静态方法名(参数列表);

    public interface Usb {//接口
    
     //接口中的抽象方法
     public abstract void a();
     //新添加的默认方法
     public default void b(){
         System.out.println("实现接口升级");
     }
     //新添加的静态方法
     public static void c(){
         System.out.println("实现静态方法");
     }
    }
    public class Usbfeng implements Usb{//实现类1
     @Override
     public void a() {
         System.out.println("Usbfeng");
     }
    @Override
    public void b(){
        System.out.println("默认default覆盖写法");//重写默认b()方法完全可以,最后实例化对象调用
    }  
        }
    public class Usbshan implements Usb{//实现类二
        @Override
        public void a() {
            System.out.println("Usbshan");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    1.接口的默认方法,可以通过接口实现类对象,直接调用

    2.接口的默认方法,也可以被接口实现类进行覆盖重写

    public class Test {//测试类
      public static void main(String[] args) {
    //创建了实现类对象
      Usbfeng usbfeng = new Usbfeng();
      usbfeng.a();//调用抽象方法,实现的是右侧实现类
      usbfeng.b();//调用默认方法,如果实现类中没有,会向上找接口
    
      Usbshan usbshan = new Usbshan();
      usbshan.a();
      usbshan.b();//调用mo
    
      Usb.c();
       }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    JDK9 接口新特性 private

    我们需要抽取一个共有方法,用来解决多个默认方法之间重复代码的问题,但是这个共有方法不应该让实现类使用,所以私有化

    解决方案:

    从Java 9开始接口当中允许定义私有方法。

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

    private 返回值类型 方法名称(参数列表){
        方法体
    }
    
    • 1
    • 2
    • 3
    public interface Typc {//代码重复率高
        public default void a(){
            System.out.println("默认方法A");
            System.out.println("AAA");
            System.out.println("BBB");
            System.out.println("CCC");
        }
    public default void b(){
        System.out.println("默认方法B");
        System.out.println("AAA");
        System.out.println("BBB");
        System.out.println("CCC");
    }
    }
    运用私有化后
    
    public interface Typc {
        //因为被私有化,只有a和b能够使用c,其余的地方无法使用
        public default void a(){
            System.out.println("默认方法A");
            c();
        }
        public default void b(){
            System.out.println("默认方法B");
            c();
        }
        private void c(){
            System.out.println("AAA");
            System.out.println("BBB");
            System.out.println("CCC");
        }
    }
    测试
    
    public class Testtypec {
        public static void main(String[] args) {
            Typcc typcc = new Typcc();
            typcc.b();
            typcc.a();
        }
    }
    
    输出结果
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    静态私有方法,解决多个静态方法之间重复代码问题
    private static 返回值类型 方法名称 (参数列表){
        方法体
    }
    
    • 1
    • 2
    • 3
    1. 静态私有方法同理,只有本接口多个静态方法可以使用,其余作用域无法使用。
    2. 在Java9以后出现并使用
    3. JDK9 接口中可以使用 private 修饰方法,供接口中其他方法调用。

    抽象类和接口的区别(重点)

    特性接口抽象类
    组合可以在新类中组合多个接口只能继承一个抽象类
    状态只含有静态字段、抽象方法、默认方法、静态方法可以包含字段
    默认方法和抽象方法默认方法不需要在子类里实现,它只能引用接口中的方法抽象方法必须在子类里实现
    构造器不能有构造器(接口没有构造、不能实例化)有构造、同样不能实例化
    访问权限隐式 public可以为 protected 或包访问权限

    举例:

    @FunctionalInterface
    interface IType {
        void test();
    }
    class Demo{
       public static void main(String[] args){
       // 通常使用 匿名类
        IType a = new IType(){
         @Override
         public void test(){ 
             // 实现
         }
        };
           
           // lambda
           IType b = () ->{
            // 实现
            };   
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    如果lambda中实现只有一句代码则可以省略{}这种写法,java可以根据变量的类型推断出匿名类实现的接口,以及重写的方法。方法的写法上:

    • 如果方法没有参数则使用()表示。

    • 如果有参数,则需要在()写形参列表,在实现中可以使用这些参数。

    • 参数的类型可以省略,可以推断出参数类型

    • 如果方法没有参数则使用()表示。

    • 如果只有一个参数()可以省略。

    深克隆和浅克隆

    浅克隆

    浅克隆指创建一个新对象,并将原始对象的所有非静态字段的值复制到新对象中。如果字段的类型是基本类型,那么会复制其值;如果字段的类型是引用类型,则会复制引用,即新对象和原始对象将引用同一个对象。

    深克隆

    深克隆是指创建一个新对象,并将原始对象的所有字段新对象中,包括引用类型字段所引用的对象。

    简单来说就是浅克隆对象中的引用数据类型字段是同一个引用,深克隆会比浅克隆多克隆一个引用数据类型

    protected Person clone() throws CloneNotSupportedException {
        //        return (Person) super.clone();
        Person copy = (Person) super.clone();
        // 将引用数据类型 clone 一份, 再设置
        Dog copyDog = copy.getDog().clone();
        copy.dog = copyDog;
        return copy;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
  • 相关阅读:
    vue3 - setup之defineEmits
    Hadoop IPC‘s epoch 8 is less than the last promised epoch 9 ; journal id:
    CSS 布局案例: 2行、多行每行格数不定,最后一列对齐
    Nginx默认会自动忽略请求头Headers里带下划线_的参数
    OpenSSF安全计划:SBOM将驱动软件供应链安全
    全网最牛自动化测试框架系列之pytest(6)-Fixture(固件)
    蓝桥杯练习题十 - 煤球数目(c++)
    springboot个性化课程推荐系统毕业设计源码131805
    为什么大数据技术那么火?
    什么是Nginx?
  • 原文地址:https://blog.csdn.net/m0_68154641/article/details/132867819