java 接口

一,接口的概念

在java中,接口可以被看作是多个类的公共规范 ,是一种引用数据类型

二,语法规则

接口的定义格式与类基本相同,将class关键字换成interface关键字就定义了一个接口

java 复制代码
public interface 接口名称{
//抽象方法
public abstract void method1();//public abstract是固定搭配,可以不写
public void method2();
abstract void method3();
void method4();
//注意:在接口中上述写法都是抽象方法

提示:

1,创建接口时,接口的命名一般以大写字母I开头

2,接口的命名一般使用"形容词"词性的单词

3,接口的方法和属性不要加任何修饰符号,保持代码的简洁性

三,接口的使用

接口不能直接使用,必须要有一个"实现类"来"实现"该接口,实现接口中的所有抽象方法

java 复制代码
public class 类名称 implements 接口名称{
  //...
}

注意:子类和父类之间是extends继承关系,类与接口之间是inplements实现关系

代码实例:
请实现笔记本电脑使用USB鼠标、USB键盘的例子

  1. USB接口:包含打开设备、关闭设备功能
  2. 笔记本类:包含开机功能、关机功能、使用USB设备功能
  3. 鼠标类:实现USB接口,并具备点击功能
  4. 键盘类:实现USB接口,并具备输入功能



四,接口特性

1,接口是一种引用类型,但是不能直接new接口的对象(不能直接实例化)

要使用接口,必须有一个具体类实现该接口,然后通过该类的实例来操作接口。

2,接口中每一个方法都是public的抽象方法,即接口中的方法会被隐式的指定为public abstract(只能是public abstract,其他的修饰符都会报错)

java 复制代码
public interface USB{
  //Error:(4,18)java:此处不允许使用修饰符private
  private void openDevice();
  void closeDevice();
}

3,接口中的方法是不能在接口中实现的,只能由实现接口的类来实现

4,重写接口方法的时候不能使用默认的访问限制

所以重写USB中方法的时候,不能使用默认修饰符

5,接口中可以含有变量,但是接口中的变量会被隐式的指定为 public static final 变量

这说明了brand具有final属性

6,接口中不能有静态代码块和构造方法

1)为什么不能有静态代码块?

  • 静态代码块用于在类加载时执行初始化逻辑,但接口本身不能被实例化,也没有实例化过程。

  • 接口中的静态变量默认是 public static final 的常量,必须在声明时直接赋值,不需要(也不允许)通过静态代码块初始化。

替代方案:

如果需要初始化接口的静态成员,可以用静态方法

2) 为什么接口不能有构造方法?

  • 构造方法用于初始化对象实例,但接口不能被实例化(没有对象的概念)。

  • 接口的实现类会由JVM自动生成默认构造方法(除非显式定义),与接口本身无关。

替代方案

如果需要在实现类初始化时执行逻辑 ,可以在实现类中定义构造方法

7,接口虽然不是类,但是接口编译完成后字节码文件的后缀格式也是.class

8,如果类没有实现接口中的所有抽象方法,则类必须设置为抽象类

9,jdk8中:接口中还可以包含default方法和static方法

五,实现多个接口

在java中,类和类之间是单继承的,一个类只能有一个父类,即java中不支持多继承,但是一个类可以实现多个接口.

注意:一个类实现多个接口的时候,每个接口中的抽象方法都要实现,否则类必须设置为抽象类

上述都讲述了一个知识:一个类只能继承一个父类,但是一个类可以有多个接口.

此外,继承表达的含义是:"is-a",而接口表达的含义是"具有xxx特性"

这样设计的好处是可以让程序员不必关注类型,而只是关注某个类是否具备某种能力

六,接口之间的继承

经过之前的学习,我们知道java类与类之间是单继承的,一个类可以实现多个接口.但是接口与接口之间可以实现多继承 .即:用接口可以达到多继承的目的

java 复制代码
interface IRuning{
  void run();
}
interface ISwimming{
  void swim();
//两栖的动物,既能跑,也能游.
interface IAmphibious extends IRunning,ISwimming{
 ... ... ...
}
class Frog implements IAmphibious{
 ... ... ...
}

通过接口继承创建了一个新的接口 IAmphibious表示两栖的,此时创建的Frog类,就继续要实现run方法,也需要实现swim方法

注:接口之间的继承相当于把多个接口合在一起

七,接口使用实例

1,对对象数组进行排序:

按照我们之前的理解,数组我们有一个现成的sort方法,能否直接使用这个方法呢

代码出错了,所以和普通的整数不一样,两个整数是可以直接进行比较的,大小关系明确.而两个学生对象的大小关系怎么确定,需要我们额外指定

让我们的Student类实现Comparable接口,并实现其中的compareTo方法

从上述代码中我们可以看到,在sort代码中会自动调用commpareTo方法.compareTo的参数是Object,其实传入的就是Student类型的对象

然后比较当前对象和参数对象的大小关系(按分数来算)
如果当前对象应排在参数对象之前, 返回小于 0 的数字;
如果当前对象应排在参数对象之后, 返回大于 0 的数字;
如果当前对象和参数对象不分先后, 返回 0;
再次执行程序, 结果就符合预期了.
**注意事项:**对于sort数组来讲,需要传入的数组的每个对象都是"可比较"的,需要具备compareTo这样的能力,通过重写compareTo方法的方式,就可以定义比较规则

2,如何使用compareTo

这个问题比较重要,因此我们将他的地位提高一点

八,compareTo的使用

compareTo方法是Java中用于比较对象顺序 的重要方法,主要定义在Comparable接口 中。以下是关于如何在Java中使用compareTo的详细说明

1,compareTo的基本语法:

java 复制代码
public int compareTo(T o)

它返回:

  • 负整数 :当前对象小于参数对象

  • :当前对象等于参数对象

  • 正整数 :当前对象大于参数对象

2,实现comparable接口

要使类可比较,需要实现Comparable接口:

分析:

1)comprable是java当中的一个接口,用于定义对象之间的自然顺序

2)当一个类实现了Comparable接口 时,他必须重写compareTo方法,该方法用于比较当前对象与另一个对象的顺序

3)在这个例子中,person类实现了Comparable<person>,表示person类的对象可以与其他person类的对象进行比较

4)person有两个私有成员变量:name(姓名,字符串类型)和age(年龄,整数类型)

5)重写compareTo方法:

compareTo方法是comparable接口中定义的方法,用于比较当前对象与另一个对象的顺序

首先比较两个person对象的name,使用string类的compareTo方法来比较两个姓名字符串

姓名相同就用age来比较

3,常用类的compareTo用法

1)string类:

java 复制代码
String str1 = "apple";
String str2 = "banana";
int result = str1.compareTo(str2); // 返回负值,因为"apple"字典序在"banana"之前

2)integer类:

java 复制代码
Integer num1 = 10;
Integer num2 = 20;
int result = num1.compareTo(num2); // 返回负值,因为10 < 20

3)自定义比较逻辑:

注:只要是自定义的类型 ,涉及到了大小的比较 ,目前的一个结论是:一定要实现Comparable接口

4,使用comparator

除了实现Comparable接口,还可以使用Comparator

java 复制代码
List<String> names = Arrays.asList("John", "Alice", "Bob");

// 自然顺序排序
Collections.sort(names); // 使用String的compareTo

// 自定义排序
Collections.sort(names, (s1, s2) -> s2.compareTo(s1)); // 降序排序

5,注意事项:

  1. compareTo应该与equals方法保持一致 :如果x.compareTo(y)==0,那么x.equals(y)应该为true

  2. 比较时应考虑null值情况 (通常null被认为小于非null值

  3. 对于可能溢出的数值比较,使用Integer.compare()Double.compare()等静态方法

6,代码实例

为了进一步加深对接口的理解,我们可以尝试自己实现一个sort方法来完成刚才的排序过程(使用冒泡排序)

代码执行结果:

冒泡排序部分也可以这么写

7,compareTo的缺陷

compareTo比较方法,一般用于固定的比较 ,不适合非常灵活的比较

8,用途

用在默认的比较

九,Comparator的使用

在 Java 中,Comparator 是一个接口,用于定义自定义的对象比较规则,主要用于排序(如 Collections.sort()Arrays.sort())或有序集合(如 TreeSetTreeMap)。

1,Comparator的基本用法:

1)integer.compare()方法

integer.compare(int x, int y) 是integer类的一个静态方法,用于比较两个int类型的值x和y,它的返回值遵循以下规则:

如果 x<y ,返回 -1;

如果 x == y ,返回 0 ;

如果 x > y ,返回1;

2)为什么使用 integer.compare()

在java中,直接比较两个 int 类型的值可以使用 关系运算符 (如 < == > )但使用 integer.compare() 方法可以使代码更加 简洁 ,并且 integer.compare() 方法的 返回值 与 Comparator 接口的compare 方法的要求一致,即返回 -1 0 1 ,这样使代码具有一致性

2,代码实例:

十,Clonable接口和深拷贝

1,概念理解

java内置了一些很有用的接口,Clonable就是其中之一

Object类中存在一个clone方法,调用这个方法可以创建一个对象的拷贝,但是要想合理调用clone方法,必须要实现Clonable接口,否则就会抛出出 CloneNotSupportedException异常

在 Java 中,Cloneable 是一个标记接口(没有方法)表示一个类允许被克隆默认Object.clone() 方法是浅拷贝只复制基本类型和引用,不复制引用对象) 。要实现深拷贝 ,需要重写 clone() 方法并手动复制引用对象。

2,浅拷贝与深拷贝的区分

  • 浅拷贝(Shallow Copy)

    • 只复制对象的基本类型字段 (如 int, String)和引用地址(如对象、数组)。

    • 不会复制引用指向的实际对象,新旧对象共享同一份引用数据。

    • 修改其中一个对象的引用字段,会影响另一个对象!

  • 深拷贝(Deep Copy)

    • 完全复制对象及其所有引用指向的对象,生成一个完全独立的副本。

    • 修改副本的引用字段,不会影响原对象。

    • 一句话理解

  • 浅拷贝是"复制钥匙"(多个钥匙开同一把锁)。

  • 深拷贝是"复制钥匙 + 复制锁"(每个钥匙开自己的锁)。

3,代码实例

代码输出结果:

为了方便理解,我们把上述代码分成两部分来看

第一部分:

1)implements Cloneable:

这是java的一个**"标记接口"** , 没有实际方法 ,只是告诉JVM:这个类的对象可以被克隆 ,如果不实现它,调用super.clone()会抛出 CloneNotSupportedException

2)clone( )方法

先调用 supper.clone( )(即Object的clone方法),它会创建一个新对象,复制原对象所有的字段值 (如果字段是基本类型,直接拷贝值 ;如果是引用类型,拷贝的是引用地址 ,这也是"浅克隆的特点)"

因为Object.clone( )返回的是Object类型,所以需要强转为Animal

第二部分:

3)animal.clone( ):

调用Animal重写的clone方法,生成一个新的Animal对象(和原对象内容一样,但是内存地址不同)

4) == 的作用

在java中, == 比较的是对象的内存地址,如果地址相同,才会返回true,否则返回false

5)为什么结果输出的是false?

因为animal和animal2是两个不同的对象,他们在堆内存占用不同的空间,地址不一样.所以逻辑运算符的结果是false

6)拓展小知识(深克隆vs浅克隆)

当前代码是 浅克隆 :如果 animal 类里有 引用字段 类型, super.clone( ) 只会拷贝 引用地址 (比如 private List<String> habits;),克隆后的对象和原对象会共享这个引用.如果修改其中一个对象的 habits ,那么另一个也会受到影响.

如果需要深克隆(完全独立,修改互不影响)就需要手动处理引用类型字段,比如在clone方法里对引用类型也进行克隆或新对象赋值

4,前提回顾,变量的类型

1,基本数据类型(Primitive Types)

存储的是实际的值 ,直接分配在栈内存中。

Java 有 8 种基本类型:

  • byte, short, int, long

  • float, double

  • char, boolean

2,引用数据类型(Reference Types)

存储的是对象的引用(内存地址) ,而不是对象本身。

引用类型包括:

  • 类(Class) (如 String, Person, List

  • 接口(Interface)

  • 数组(Array)

  • 枚举(Enum)

5,代码实例(浅拷贝的地址有关性)

代码比较长,我们依旧分两部分

第一部分:

第二部分

代码运行结果:

throws:

当一个方法使用throws声明某个异常时,意思是这个方法不打算在内部处理该异常(比如不用 try - catch捕获),而是把"处理异常的责任"抛给调用这个方法的上层代码

1)举例说明

就像 Personclone 方法,它调用了 super.clone()(即 Object 类的 clone 方法 ),而 Object 类的 clone 方法本身就声明了会抛出 CloneNotSupportedException 。如果当前类(Person)不处理这个异常,就通过 throws 告诉调用 Person.clone() 的代码:"我可能会抛出这个异常,你得处理"

2)和 Cloneable 接口的关联

Cloneable 是 Java 的一个标记接口,一个类实现了 Cloneable 接口,才 "允许" 被克隆 。如果一个类没实现 Cloneable 接口,却调用了 clone 方法,Object 类的 clone 逻辑就会抛出 CloneNotSupportedException 。所以在重写 clone 方法时,为了遵循异常处理规则,需要通过 throws 声明这个潜在的异常,让调用者知晓并处理。

3)对调用者的要求

TestDemo3main 方法为例,它调用了 p1.clone() ,而 Person.clone() 声明了会抛出 CloneNotSupportedException 。此时 main 方法有两种选择:

  • 自己捕获异常 :用 try-catch 包裹调用代码,像这样:
  • 继续向上抛异常 :也就是代码里的写法,用 throws 把异常抛给更上层(这里 main 方法是程序入口,抛给 JVM ,JVM 遇到未处理的受检异常会终止程序并提示 )。不过实际开发中,对于 main 方法这种程序入口,一般建议用 try-catch 处理,让程序更健壮。

4). 总结一下 throws 的作用

  • 声明异常:告诉调用者当前方法可能抛出的异常类型,让调用者决定如何处理(捕获或者继续抛 )。
  • 遵循规则 :对于受检异常,要么处理(try-catch ),要么声明(throws ),否则代码编译不通过,这是 Java 编译器对异常处理的强制约束,能帮我们提前规避潜在的运行时错误,让程序逻辑更严谨。

简单说,throws 就是一种 "甩锅" 式的异常声明,把处理异常的任务传递给调用方,保证代码在编译阶段就对异常有清晰的处理规划 。

6上述浅拷贝代码改为深拷贝代码

十一,抽象类和接口的区别

抽象类和接口都是java中多态的常见使用方法

核心区别:

抽象类中可以包含普通方法和普通字段,这样的普通方法和字段可以被子类直接使用(不必重写),而接口中不能包含普通方法,子类必须重写所有的抽象方法

如之前写的Aniaml的例子,此处的Animal中包含一个name这样的属性,这个属性在任何子类中都是存在的,因此此处的Animal只能作为一个抽象类而不能成为一个接口

java 复制代码
class Animal {
  protected String name;

  public Animal(String name) {
    this.name = name;
  }
}

再次提醒:

抽象类存在的意义是为了让编译器更好的校验, 像 Animal 这样的类我们并不会直接使用, 而是使用它的子类.
万一不小心创建了 Animal 的实例, 编译器会及时提醒我们.

大纲总结

相关推荐
JouJz9 分钟前
设计模式之工厂模式:对象创建的智慧之道
java·jvm·设计模式
MZ_ZXD00144 分钟前
flask校园学科竞赛管理系统-计算机毕业设计源码12876
java·spring boot·python·spring·django·flask·php
源代码•宸1 小时前
C++高频知识点(十三)
开发语言·c++·经验分享·面经
wa的一声哭了2 小时前
python基础知识pip配置pip.conf文件
java·服务器·开发语言·python·pip·risc-v·os
Kay_Liang2 小时前
MySQL SQL语句精要:DDL、DML与DCL的深度探究
开发语言·数据库·sql·mysql·database
钢铁男儿2 小时前
C# 接口(接口可以继承接口)
java·算法·c#
流形填表2 小时前
AI 助力:如何批量提取 Word 表格字段并导出至 Excel
开发语言·人工智能·word·excel·办公自动化
肉肉不想干后端2 小时前
分布式ID:基于K8s-PodName的百度雪花ID生成方案优化
java
每天吃饭的羊2 小时前
箭头函数(Arrow Functions)和普通函数(Regular Functions)
开发语言·javascript·ecmascript