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

在这里插入图片描述

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


文章目录

  • 一、抽象类
    • 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 {@Overridevoid makeSound() {System.out.println("Woof!");}@Overridevoid 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 {@Overridevoid makeSound() {System.out.println("Howl!");}// `eat` 方法没有实现,所以 Canine 仍然是抽象类
    }class Dog extends Canine {@Overridevoid 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 {@Overridepublic void makeSound() {System.out.println("Woof!");}@Overridepublic void eat() {System.out.println("Dog is eating.");}
}public class Cat implements Animal {@Overridepublic void makeSound() {System.out.println("Meow!");}@Overridepublic 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 {@Overridepublic void start() {System.out.println("Car is starting.");}// Car可以选择不重写honk方法,使用默认实现
}public class Bike implements Vehicle {@Overridepublic void start() {System.out.println("Bike is starting.");}@Overridepublic 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 {@Overridepublic void hello() {A.super.hello(); // 明确指定调用接口A的默认方法//B.super.hello(); 或明确指定调用接口B的默认方法}
}

2.3 实现接口时遇到的问题

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

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

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

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

      // 定义一个接口
      interface MyInterface {void method1(); // 抽象方法1void method2(); // 抽象方法2void method3(); // 抽象方法3
      }// 创建适配器类,提供接口的默认实现
      abstract class MyInterfaceAdapter implements MyInterface {@Overridepublic void method1() {// 默认实现}@Overridepublic void method2() {// 默认实现}@Overridepublic void method3() {// 默认实现}
      }// 子类选择性地覆盖需要的方法
      class MyClass extends MyInterfaceAdapter {@Overridepublic 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() {@Overridepublic void swimming() {System.out.println("蛙泳...");}};s2.swimming();}
}


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

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

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

相关文章

sslyze一键检查服务器检查服务器的 SSL/TLS 安全性(KALI工具系列二十五)

目录 1、KALI LINUX 简介 2、sslyze工具简介 3、信息收集 3.1 目标主机IP(服务器) 3.2 KALI的IP 4、操作示例 4.1 扫描主机和端口 4.2 批量扫描 4.3 插件扫描 4.4 输出结果 5、总结 1、KALI LINUX 简介 Kali Linux 是一个功能强大、多才多艺…

[Python学习篇] Python字符串

字符串是 Python 中最常用的数据类型,一般使用单引号或引号来创建字符串 语法: 字符串变量名A 字符串变量值A 字符串变量名B "字符串变量值B" 示例: a Hello A print(a) b "Hello B" print(b) 字符串特征 一对引号字…

竟然与 package-lock.json 更新有关!部分用户 H5 页面白屏问题!

一.问题 1 场景 现象 接到部分用户反馈进入xxx H5 页面空白; 研发测日志里问题用户的线上页面URL地址可以正常访问,没有复现问题!!! 定位问题 监控平台和客户端日志报错: SyntaxError: Unexpected toke…

调试了一下午,终于把tailwindcss搞进Blazor了

在Vue和Uniapp项目中使用tailwindcss后,实在是太香了,非常符合我这从XAML走过来的老程序员的手感,所以老想着在Blazor项目中引入。看了几个老外大佬的视频,调试了一下午,终于是捣鼓成功了。由于咱们Blazor项目不在node…

STM32硬件接口I2C应用(基于MP6050)

目录 概述 1 STM32Cube控制配置I2C 1.1 I2C参数配置 1.2 使用STM32Cube产生工程 2 HAL库函数介绍 2.1 初始化函数 2.2 写数据函数 2.3 读数据函数 3 认识MP6050 3.1 MP6050功能介绍 3.2 加速计测量寄存器 ​编辑3.3 温度计量寄存器 3.4 陀螺仪测量寄存器 4 MP60…

C++ Windows下Glog日志库安装使用教程

🙋 介绍:glog是google推出的一款轻量级c++开源日志框架。  环境配置:windows+VS2015+gflags 2.2.2+glog-0.3.5。为避免新版本(glog V0.7.1)踏坑,建议装低版本,这里我选用的是V0.3.5。 1. 下载 在gflags官方中下载gflags代码,官方地址 在Glog官方中下载,githut地址:…

简单易用的多功能图床Picsur

什么是 Picsur ? Picsur 是一款易于使用、可自行托管的图片分享服务,类似于 Imgur,并内置转换功能。支持多种格式的图片,包括 QOI、JPG、PNG、WEBP(支持动画)、TIFF、BMP、GIF(支持动画&#xf…

下载依赖有问题(只有自己有问题)

有缓存! 删除node_modules 命令:npm run clean 前提是该项目支持这个命令:package.json > scripts 内有 clean 例如下面这个就没有clean,则直接手动删除 清除缓存 npm cache clean --force pnpm store prune删除lock文件 …

Superset 二次开发之Git篇 git cherry-pick

Cherry-Pick 命令是 Git 中的一种功能,用于将特定的提交(commit)从一个分支应用到另一个分支。它允许你选择性地应用某些提交,而不是合并整个分支。Cherry-Pick 非常适合在需要将特定更改移植到其他分支时使用,例如从开…

可视化图表:如此高颜值柱状图,其实简单配置就能实现。

这不又有某个boss给我图截图一些柱状性图表,说他们的前端觉得很难,说了一堆技术术语,他也不懂,截图我看到后,就给了他一个网址,马上就解决了。 在这里给大家摘录出几个比较有特色的柱状图出来,让…

PyTorch计算机视觉入门:使用自己的数据集训练神经网络

前言 计算机视觉,作为人工智能领域的一个重要分支,近年来在图像识别、物体检测、图像生成等应用上取得了显著的进步。PyTorch,作为一款灵活且强大的深度学习框架,为开发者提供了便捷的工具来构建和训练计算机视觉模型。本文将指导…

RFID技术在农产品管理中的应用

使用RFID技术对农产品生产、加工、存储和销售的全过程进行跟踪,追溯食品的生产和加工过程,能够有效加强农产品的管理,如图7—10所示。 将RFID技术应用于农业食品安全,首先是建立完整、准确的食品供应链信息记录。借助RFID 对物体…

Python 全栈系列253 再梳理flask-celery的搭建

说明 最近做了几个实验,将结论梳理一下,方便以后翻看。 1 flask-celery 主要用于数据流的同步任务,其执行由flask-aps发起,基于IO并发的方法,达到资源的高效利用,满足业务上的需求。2 目前部署环境有算网…

腾讯大模型最新路线图,主打一个“实用”

让大模型更接地气,腾讯交出一份阶段性答卷。 大模型狂奔一年多后,正迈向产业落地的深水区。如何用好这一革命性的新技术,已成为整个社会的一道必答题。 去年,国内大模型百花齐放,外界关注的焦点也集中在模型能力上。…

AI Agent智能应用从0到1定制开发(完结)

在数字化时代的浪潮中,人工智能(AI)代理智能应用如同星辰般璀璨,引领着技术革新的潮流。从零开始定制开发一款AI Agent智能应用,就像是在无垠的宇宙中绘制一颗新星的轨迹,每一步都充满了挑战与创新的火花。…

echarts写某个市地图

const geoJSON {"type":"FeatureCollection","features":[{"type":"Feature","properties":{"adcode":440303,"name":"罗湖区","center":[114.123885,22.555341],"…

【2024最新精简版】MyBatis面试篇

文章目录 mybatis内部实现过程mybatis延迟加载请说说MyBatis的工作原理mybatis接口里的方法,参数不同时能重载吗mybatis分页插件的原理是什么?mybatis的一级、二级缓存👍mybatis如何实现多表查询mybatis如何实现批量插入👍mybatis动态SQL标签…

关于使用pycharm中控制台运行代码错误之FileNotFoundError: [Errno 2] No such file or directory:

在使用pycharm环境下复现《python编程:从入门到实践》这本书第16.1.1内容中分析csv文件头一节的代码时出现如下问题: 1、文章中使用的数据来源问题 直接参考本站Kenny C同学的文章提供内容即可。 https://github.com/kenidi8215/Hello-World 打开网页&a…

Java面试题--JVM大厂篇之掌握JVM性能优化:选择合适的垃圾回收器

掌握JVM性能优化:选择合适的垃圾回收器 引言: ​  在Java开发中,性能优化往往是提高应用稳定性和用户体验的关键所在。而垃圾回收器的选择和优化,是JVM性能调优的核心环节。如何在众多垃圾回收器中选出适合自己应用需求的那一个&#xff1…

Docker笔记-Debian容器内搭建ssh服务

登陆容器之后修改密码: passwd 密码设置完成后安装openssh-server apt-get install openssh-server 修改端口号为50022并添加配置 vim /etc/ssh/sshd_config 修改成 Port 50022 PasswordAuthentication yes PermitRootLogin yes 启动 rootlinux:~# /etc/in…