Java——面向对象进阶(三)

news2025/4/8 13:08:47

在这里插入图片描述

前言:
抽象类,接口,内部类


文章目录

  • 一、抽象类
    • 1.1 抽象方法
    • 1.2 抽象类
    • 1.3 抽象类的使用
  • 二、 接口
    • 2.1 接口的定义和实现
    • 2.2 default 关键字
    • 2.3 实现接口时遇到的问题
  • 三、内部类
    • 3.1 成员内部类
    • 3.2 静态内部类
    • 3.3 成员内部类
    • 3.4 匿名内部类(最常用)

一、抽象类

在Java中,抽象类是一种不能被实例化的类,它通常用于定义一些共用的方法和字段,在该类中不知道具体的实现,但还是想让子类必须去实现这些方法,所以将成员方法定义成抽象方法,该类定义成抽象类。abstract 用于修饰方法方法和类,修饰的方法是抽象方法,修饰的类是抽象类。

1.1 抽象方法

使用abstract 关键字修饰方法,该方法就成了抽象方法,抽象方法只包含一个方法名,而没有方法体。

定义格式:
修饰符 abstract 返回值类型 方法名 (参数列表);

示例:
public abstract void run();

1.2 抽象类

使用abstract 关键字修饰类,

形式:
abstract class 类名字 { 

}

示例:
public abstract class Animal {
    public abstract void run()}

!注意:抽象类不一定有抽象方法,但有抽象方法的类一定要定义成抽象类

1.3 抽象类的使用

  1. 继承抽象类:

    • 如果一个类继承了一个抽象类,并且这个类不是抽象类,那么它必须实现所有从抽象类继承的抽象方法。
    • 如果一个类继承了一个抽象类,但没有实现所有的抽象方法,那么这个类必须声明为抽象类。
  2. 抽象类的继承链:

    • 一个抽象类可以继承另一个抽象类,并且可以选择性地实现部分或全部继承的抽象方法。
    • 最终,必须有一个具体(非抽象)的子类实现所有抽象方法。

示例代码:

  1. 子类实现所有抽象方法

    abstract class Animal {
        abstract void makeSound();
        abstract void eat();
    }
    
    class Dog extends Animal {
        @Override
        void makeSound() {
            System.out.println("Woof!");
        }
    
        @Override
        void eat() {
            System.out.println("Dog is eating.");
        }
    }
    
    public class Main {
        public static void main(String[] args) {
            Animal myDog = new Dog();
            myDog.makeSound();
            myDog.eat();
        }
    }
    

    Dog类继承了Animal类,并且实现了所有的抽象方法。

  2. 子类没有实现所有抽象方法,必须声明为抽象类

    abstract class Animal {
        abstract void makeSound();
        abstract void eat();
    }
    
    abstract class Canine extends Animal {
        @Override
        void makeSound() {
            System.out.println("Howl!");
        }
        // `eat` 方法没有实现,所以 Canine 仍然是抽象类
    }
    
    class Dog extends Canine {
        @Override
        void eat() {
            System.out.println("Dog is eating.");
        }
    }
    
    public class Main {
        public static void main(String[] args) {
            Animal myDog = new Dog();
            myDog.makeSound();
            myDog.eat();
        }
    }
    

    Canine类实现了makeSound方法,但没有实现eat方法,因此Canine必须声明为抽象类。Dog类继承了Canine并实现了eat方法,使得Dog成为具体类,可以被实例化。


二、 接口

接口(Interface)在Java中是一个重要的概念,用于定义一组方法的规范,而不提供任何实现。接口主要用于定义类之间的契约和规范,确保实现这些接口的类遵循相同的方法签名。

2.1 接口的定义和实现

接口的定义:
接口使用interface关键字定义,接口中的所有方法默认是publicabstract,所有成员变量默认是public static final。以下是一个简单的接口示例:

public interface Animal {
    void makeSound();
    void eat();
}

实现接口
类通过implements关键字实现接口,一个类可以实现多个接口,这与抽象类的单继承限制不同。以下是实现Animal接口的两个类:

public class Dog implements Animal {
    @Override
    public void makeSound() {
        System.out.println("Woof!");
    }

    @Override
    public void eat() {
        System.out.println("Dog is eating.");
    }
}

public class Cat implements Animal {
    @Override
    public void makeSound() {
        System.out.println("Meow!");
    }

    @Override
    public void eat() {
        System.out.println("Cat is eating.");
    }
}

类实现接口的要求:

  1. 必须重写实现的全部接口中所有抽象方法。
  2. 如果一个类实现了接口,但是没有重写完全部接口的全部抽象方法,这个类也必须定义成抽象类。

如果一个类实现了多个接口,而这些接口有相同的抽象方法,那么这个类只需要实现这个抽象方法一次即可,因为Java不允许同一个类中出现重复的方法签名。这种情况下,类只需要提供一次方法的实现,而不需要重复实现多次。


2.2 default 关键字

default 关键字主要用于接口中的默认方法(default methods),这是从Java 8开始引入的特性。默认方法允许在接口中为方法提供默认的实现,这样可以在不破坏现有实现的情况下,向接口添加新的方法。

默认方法(Default Methods)定义方式:

public interface InterfaceName {
    // 抽象方法
    void regularMethod();

    // 默认方法
    default void defaultMethod() {
        // 默认实现
        System.out.println("This is a default method.");
    }
}

在上面的例子中,defaultMethod 就是一个默认方法。默认方法使用 default 关键字修饰,它提供了一个默认的实现。所有实现了这个接口的类都会继承这个默认方法,如果需要的话,可以在实现类中重写默认方法。

示例:

public interface Vehicle {
    void start();
    
    default void honk() {
        System.out.println("Vehicle is honking.");
    }
}

public class Car implements Vehicle {
    @Override
    public void start() {
        System.out.println("Car is starting.");
    }

    // Car可以选择不重写honk方法,使用默认实现
}

public class Bike implements Vehicle {
    @Override
    public void start() {
        System.out.println("Bike is starting.");
    }

    @Override
    public void honk() {
        System.out.println("Bike is honking."); // Bike提供了自己的honk方法实现
    }
}

当需要扩展一个接口,但又不能破坏所有实现这个接口的类时,可以使用默认方法。

注意事项:
如果一个类实现了多个接口,而这些接口有相同的默认方法,实现类必须重写这个默认方法来解决冲突。

interface A {
    default void hello() {
        System.out.println("Hello from A");
    }
}

interface B {
    default void hello() {
        System.out.println("Hello from B");
    }
}

class C implements A, B {
    @Override
    public void hello() {
        A.super.hello(); // 明确指定调用接口A的默认方法
        //B.super.hello(); 或明确指定调用接口B的默认方法
    }
}

2.3 实现接口时遇到的问题

  1. 如果一个类继承了一个类并实现了一个接口,且父类和接口中有都有相同签名的方法。

    • 处理办法一:如果父类中的方法体,能满足当前业务的需求,在子类中可以不用重写。
    • 处理办法二:如果父类中的方法体,不能满足当前业务的需求,需要在子类中重写。
  2. 如果一个接口中,有多个抽象方法,但在实现类中,只需要用其中一个或部分

    • 方法一:实现类声明为抽象类
      你可以将实现类声明为抽象类,并只实现部分抽象方法,这样子类就可以根据需要选择性地实现接口的方法。

      // 定义一个接口
      interface MyInterface {
          void method1(); // 抽象方法1
          void method2(); // 抽象方法2
          void method3(); // 抽象方法3
      }
      
      // 实现类声明为抽象类,并只实现部分方法
      abstract class MyAbstractClass implements MyInterface {
          @Override
          public void method1() {
              System.out.println("Implemented method1");
          }
          
          // 不需要实现 method2 和 method3
      }
      
      // 子类继承抽象类,选择性实现部分方法
      class MyClass extends MyAbstractClass {
          @Override
          public void method2() {
              System.out.println("Implemented method2");
          }
      }
      
    • 方法二:使用适配器模式(Adapter Pattern)
      如果不想使用抽象类,可以使用适配器模式,它允许提供接口的默认实现,然后子类可以选择性地覆盖它们。

      // 定义一个接口
      interface MyInterface {
          void method1(); // 抽象方法1
          void method2(); // 抽象方法2
          void method3(); // 抽象方法3
      }
      
      // 创建适配器类,提供接口的默认实现
      abstract class MyInterfaceAdapter implements MyInterface {
          @Override
          public void method1() {
              // 默认实现
          }
          
          @Override
          public void method2() {
              // 默认实现
          }
          
          @Override
          public void method3() {
              // 默认实现
          }
      }
      
      // 子类选择性地覆盖需要的方法
      class MyClass extends MyInterfaceAdapter {
          @Override
          public void method2() {
              System.out.println("Implemented method2");
          }
      }
      

三、内部类

内部类(Inner Class)是定义在另一个类内部的类。Java 提供了多种内部类,包括成员内部类、局部内部类、匿名内部类和静态内部类。

按定义的位置来分:

  1. 成员内部内,类定义在了成员位置 (类中方法外称为成员位置,无static修饰的内部类)
  2. 静态内部类,类定义在了成员位置 (类中方法外称为成员位置,有static修饰的内部类)
  3. 局部内部类,类定义在方法内
  4. 匿名内部类,没有名字的内部类,可以在方法中,也可以在类中方法外。

3.1 成员内部类

定义格式:

class OuterClass {
    class InnerClass {
        // Inner class members
    }
}

内部类的使用格式

 外部类.内部类。 // 访问内部类的类型都是用 外部类.内部类

创建内部类实例:

  • 方式一:外部直接创建成员内部类的对象

    OuterClass outer = new OuterClass();
    OuterClass.InnerClass inner = outer.new InnerClass();
    //或
    OuterClass.InnerClass inner = new OuterClass().new InnerClass();
    
  • 方法二:在外部类中定义一个方法提供内部类的对象

    public class Outer {
        private class Inner{
        }
        
        public Inner getInstance(){
            return new Inner();
        }
    }
    
    public class Test {
        public static void main(String[] args) {
            Outer outer = new Outer();
    	    Inner inner = outer.getInstance();
        }
    }
    

成员内部类的细节

  1. 成员内部类可以被一些修饰符所修饰,比如: private,默认,protected,public,static等

  2. 成员内部类可以访问外部类的所有成员,包括私有成员。这是因为成员内部类持有一个对外部类实例的引用Outer.this

    public class Outer {
        private String outerField = "Outer field";
    
        class Inner {
            void display() {
                System.out.println("Inner class accessing: " + outerField);
                System.out.println("Inner class accessing through Outer.this: " + Outer.this.outerField);
            }
        }
    
        public static void main(String[] args) {
            Outer outer = new Outer(); // 创建外部类对象
            Outer.Inner inner = outer.new Inner(); // 创建内部类对象
            inner.display(); // 调用内部类的方法
        }
    }
    
    //输出:
    Inner class accessing: Outer field
    Inner class accessing through Outer.this: Outer field
    

    内存图示例:内存图示例

  3. 在 JDK 8 之前,局部变量必须显式声明为 final 才能被成员内部类。在 JDK 8 及之后,只要局部变量在初始化后没有被修改,它们就被隐式地视为 final(即有效 final),不再需要显式声明为 final。


3.2 静态内部类

定义格式:

class OuterClass {
    static class StaticInnerClass {
        // 静态内部类的成员
    }
}

创建实例:

OuterClass.StaticInnerClass innerObject = new OuterClass.StaticInnerClass();

静态内部类的特点

  1. 静态内部类可以直接访问外部类的静态成员,但不能访问外部类的非静态成员,如果要访问需要创建外部类的对象,用对象调用。

    public class Outer {
        private static String staticOuterField = "Static Outer Field";
        private String nonStaticOuterField = "Non-Static Outer Field";
    
        static class StaticInner {
            void display() {
                // 静态内部类可以访问外部类的静态成员
                System.out.println("Static Inner Class accessing static outer field: " + staticOuterField);
                
                // 但不能直接访问外部类的非静态成员
                // System.out.println("Static Inner Class accessing non-static outer field: " + nonStaticOuterField); // 编译错误
            }
        }
    
        public static void main(String[] args) {
            // 创建静态内部类的实例
            StaticInner inner = new StaticInner();
            inner.display(); // Output: Static Inner Class accessing static outer field: Static Outer Field
            
            // 如果要访问外部类的非静态成员,需要创建外部类的对象
            Outer outer = new Outer();
            System.out.println("Access non-static outer field through outer object: " + outer.nonStaticOuterField);
        }
    }
    
  2. 无需外部类实例: 可以直接创建静态内部类的实例,而不需要外部类的实例。

  3. :静态内部类中没有隐含的 Outer.this


3.3 成员内部类

局部内部类是定义在方法或作用域内部的内部类。

特点

  • 作用域限制: 局部内部类的作用域仅限于定义它的方法或代码块中,不能在外部访问。
  • 访问权限: 局部内部类可以访问外部类的所有成员,包括私有成员。
  • 访问局部变量: 局部内部类可以访问方法内的 final 局部变量(JDK 8+版本可以访问非 final 的局部变量,但其值不能在局部类中被修改)。
  • 生命周期: 局部内部类的生命周期仅限于方法调用,方法结束后局部内部类实例也会被销毁。

示例

public class Outer {
    private int outerField = 10;

    public void methodWithLocalInnerClass() {
        final int localVar = 20; // JDK 8+ 可以不用final修饰,但不可变

        class LocalInner {
            void display() {
                System.out.println("Outer field: " + outerField);
                System.out.println("Local variable: " + localVar);
            }
        }

        // 创建局部内部类的实例并调用方法
        LocalInner inner = new LocalInner();
        inner.display();
    }

    public static void main(String[] args) {
        Outer outer = new Outer();
        outer.methodWithLocalInnerClass();
    }
}

3.4 匿名内部类(最常用)

匿名内部类是一种没有显式声明类名的内部类,它在创建对象的同时定义类的实现。匿名内部类通常用于创建只需使用一次且没有额外的逻辑的类实例。

格式:

new 类名或者接口名() {
     重写方法;
};
这里同时包含了继承或实现,方法重写,创建对象
从new关键字到";"整个整体是匿名内部类的实例,"{}"内的是匿名内部类,没有类名

使用:
以接口为例,匿名内部类的使用,代码如下:

interface Swim {
    public abstract void swimming();
}

public class Test {
    public static void main(String[] args) {
        // 接口 变量 = new 实现类(); // 多态,走子类的重写方法
        Swim s2 = new Swim() {
            @Override
            public void swimming() {
                System.out.println("蛙泳...");
            }
        };
        s2.swimming();
    }
}


如果你喜欢这篇文章,点赞👍+评论+关注⭐️哦!
欢迎大家提出疑问,以及不同的见解。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1824946.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

qmt量化交易策略小白学习笔记第32期【qmt编程之获取行业概念数据--如何获取迅投行业成分股数据】

qmt编程之获取迅投行业成分股数据 qmt更加详细的教程方法,会持续慢慢梳理。 也可找寻博主的历史文章,搜索关键词查看解决方案 ! 感谢关注,咨询免费开通量化回测与获取实盘权限,欢迎和博主联系! 获取迅投…

大模型是什么?能干嘛?怎么学?

引言 随着人工智能技术的飞速发展,大模型研究已成为该领域的一大热点。这些研究覆盖了众多方向,每个方向都面临着独特的研究焦点和挑战。本文将逐一探讨一些备受关注的研究方向,包括检索增强生成RAG、大模型Agent、Mamba、MoE、LoRA等&#…

Star-CCM+自动网格执行方法与设置技巧

在Star中进行一个仿真项目时,有时会创建多个自动网格。网格创建结束后需要执行。在Star中,网格执行可以分为三种。分别是:单独执行操作;多个执行操作;全部执行操作。接下来将三种执行操作的方法与步骤进行介绍。 其次,如果不习惯用自定义控制网格,有时在一个项目中就会…

new Set( )的基本使用以及如何去重对象数组

目录 Set 对象方法 Set 对象作用 实现数组的去重 实现字符串的去重 实现并集 交集 差集 实现去重对象数组 相关参考资料 在 ES6 中,引入了一个新的数据结构类型:Set。而 Set 与 Array 的结构是很类似的,且 Set 和 Array 可以相互进…

散文:乡村回忆

散文:乡村回忆 在记忆的长河中,旧时代如同一本泛黄的相册,静静地躺在时光的角落。每当我翻开那些尘封的照片,那些遥远而又熟悉的画面便跃然纸上,带我回到了那个纯真的年代。 那时的天空总是那么蓝,那么纯…

基于python-CNN的常见鱼类分类识别-含数据集+pyqt界面

代码下载地址: https://download.csdn.net/download/qq_34904125/89383072 本代码是基于python pytorch环境安装的。 下载本代码后,有个requirement.txt文本,里面介绍了如何安装环境,环境需要自行配置。 或可直接参考下面博文…

海豚调度异常处理: 使用 arthas 在内存中删除启动失败的工作流

💡 本系列文章是 DolphinScheduler 由浅入深的教程,涵盖搭建、二开迭代、核心原理解读、运维和管理等一系列内容。适用于想对 DolphinScheduler了解或想要加深理解的读者。祝开卷有益。大数据学习指南 大家好,我是小陶,DolphinSch…

哪些因素驱动新零售发展?新零售与传统零售、电子商务区别在哪?

零售业正经历着一场前所未有的变革,这场变革由多种因素驱动,涉及技术、消费习惯以及商业模式的全面升级。我们称之为”新零售”,它不仅仅是一个概念,更是零售业未来发展的方向。新零售的兴起,标志着零售行业正在迈向一…

[图解]《分析模式》漫谈04-Martin Fowler叫的是哪家的士

1 00:00:01,230 --> 00:00:04,190 今天我们来探讨一个有趣的话题 2 00:00:05,130 --> 00:00:08,350 Martin Fowler,他叫的是哪一家的的士 3 00:00:11,980 --> 00:00:15,240 第2章这里,Martin Fowler写 4 00:00:15,250 --> 00:00:18,550 他…

专业媒体公关-北京-上海-60城媒体邀约服务机构-51媒体网

传媒如春雨,润物细无声,大家好,我是51媒体网胡老师。 「51媒体」提供的媒体邀约服务致力于帮助企业或机构邀请全国范围内的媒体进行现场报道宣传。 媒体邀约的重要性 媒体邀约是一种有效的宣传手段,可以帮助企业或活动在公众中…

django学习入门系列之第二点《浏览器能识别的标签4》

文章目录 input类型下拉框多行文本往期回顾 input类型 1&#xff1a;打出后可以在里面编写内容 <!-- 自闭合标签 --> <!-- 默认行内标签 --> <input type"text">placeholder 显示背景 <input type"text" id"txtUser" p…

链路追踪-微服务小白入门(6)

背景 什么是链路追踪 随着微服务分布式系统变得日趋复杂&#xff0c;越来越多的组件开始走向分布式化&#xff0c;如分布式服务、分布式数据库、分布式缓存等&#xff0c;使得后台服务构成了一种复杂的分布式网络。在服务能力提升的同时&#xff0c;复杂的网络结构也使问题定…

网络编程之XDP和TC

一、TC之于XDP 在前面分析过XDP&#xff0c;今天简单分析一下与其相关的TC&#xff0c;即traffic control,流量控制。在分析XDP时知道其只能用于ingress方向触发&#xff0c;而TC却可以在两个方向即ingress和egress方向触发。也可以简单理解成它可以同时钩住进出两个方向的数据…

气膜球幕影院:多元化应用的新选择—轻空间

随着科技的进步和人们对体验需求的不断提升&#xff0c;气膜球幕影院以其独特的结构和灵活的应用&#xff0c;逐渐成为多个领域的重要工具。 教育领域 在教育领域&#xff0c;气膜球幕影院展现出强大的优势。其便捷的搭建方式使得教育机构可以在不同场合快速部署。通过全景投影…

HK1-BOX X3刷UBUNTU 24.04,并开启WIFI

端午刚好有点时间&#xff0c;顺便把改完散热的HK1-BOX刷了个最新OC版的UBUNTU 24&#xff0c;这里记录下操作的步骤&#xff1a; 准备材料 HK1-BOX S905X3&#xff1a;注意X4的不行固件没匹配的。建议先改完散热&#xff0c;不然作为7X24小时的机器长时间高温还是很伤硬件的…

6个免费自动写文章软件,简直好用到爆

对于创作者而言&#xff0c;创作一篇高质量的文章并非易事&#xff0c;它需要耗费大量的时间与精力去构思、组织语言、斟酌字句。灵感并非总是源源不断&#xff0c;有时我们可能会陷入思维的僵局&#xff0c;不知从何下手。而此时&#xff0c;免费自动写文章软件就如同黑暗中的…

Java--多维数组

1.多维数组可以看成是数组的数组&#xff0c;比如二维数组就是一个特殊的一维数组&#xff0c;其每一个元素都是一个一维数组 2.二维数组 下列数组啊可看成一个两行五列的数组 int a[][] new int[2][5]; 3.输出二维数组的第一个数组中具体元素&#xff0c;通过调用打…

【JAVA开发笔记】实战演练,如何用EasyExcel导出表格,并且自定义合并单元格

目录 1. 前言 2. EasyExcel简介 3. EasyExcel简单导出案例讲解 3.1 EasyExcel依赖引入 3.2 测试类创建 3.3 Excel导出实现 4. EasyExcel合并单元案例讲解 4.1 实现自定义合并策略 4.2 使用自定义合并策略 5. 总结 1. 前言 项目上&#xff0c;需将一个列表数据导出Ex…

AI实践与学习6-RAG流程优化学习

背景 RAG流程很多细节优化点&#xff0c;助力AIGC。 内容 LangChain在RAG功能上的一些能力 多路向量检索 多向量检索器的核心想法是将我们想要用于答案合成的文档与我们想要用于检索的参考文献分开。这允许系统为搜索优化文档的版本&#xff08;例如&#xff0c;摘要&…

Aivis:AI声音模仿系统的创新之旅

在人工智能技术的不断进步中&#xff0c;声音合成技术也迎来了新的发展机遇。Aivis项目正是这一领域的杰出代表&#xff0c;它提供了一个全流程的工具&#xff0c;让用户能够从数据集的创建到学习再到推理&#xff0c;一站式地生成逼真的语音。 Aivis是一个基于Bert-VITS2模型的…