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())或有序集合(如 TreeSet、TreeMap)。

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)举例说明

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

2)和 Cloneable 接口的关联

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

3)对调用者的要求

以 TestDemo3 的 main 方法为例,它调用了 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 的实例, 编译器会及时提醒我们.

大纲总结

相关推荐
朝朝辞暮i6 小时前
C++ 第 23 课:class —— 开始真正进入面向对象
开发语言·c++·算法
leikooo6 小时前
用餐厅和盘子理解栈和堆
java·数据结构·链表·rust
小师兄吃牛肉7 小时前
C语言篇:宏
c语言·开发语言
郑同学的笔记7 小时前
【c++随笔28】strcmp 和 memcmp对比
开发语言·c++
言乐67 小时前
Python根据无法识别搜索词找出可能输入内容模型
开发语言·python·django·virtualenv·pygame
weixin_307779137 小时前
有限产能智能排产与动态重排智能体:从需求解构到技术实现
开发语言·人工智能·算法·架构
Ivanqhz8 小时前
激活函数在 Transformer 中的作用及各种变体简述
java·linux·数据库·人工智能·深度学习
kakakahahahaha8 小时前
Windows C盘临时文件清理:%temp%、Windows Temp、Prefetch、Windows.old与hiberfil.sys处理指南
c语言·开发语言·windows
朝朝辞暮i8 小时前
C++ 第 27 课:智能指针 shared_ptr
开发语言·c++·算法
沙漠之主8 小时前
C++编程教学设计资料:从入门到实战的完整课程方案
java·前端·c++