java笔记——接口

本文深入探讨了Java中的接口,包括接口的概念、接口属性、接口方法(如默认方法和静态方法)以及接口在多继承和回调中的角色。讲解了Comparator接口和对象克隆,并分析了接口与抽象类的区别。通过实例展示了如何使用接口实现比较功能和对象的深浅拷贝。
摘要由CSDN通过智能技术生成

前言

记录一下学习java核心卷1中第六章(接口、lambda表达式与内部类)中接口的内容。按照惯例,先绘制脑图。本文也会按照自己的脑图思路进行介绍,其中我感觉接口的特性和两个常用接口是重点需要掌握的内容。
在这里插入图片描述

1、接口概念

接口用来描述类应该做什么, 而不指定他们该具体怎么做。

为啥要用接口呢?我个人觉得,接口也是一种抽象,是对类的行为的抽象,java没有多继承,可以用接口实现多继承。使用接口类型引用实现该接口的类,也可以有利于代码的维护、安全。最后,大型项目可能需要定义一些明确地接口。

接口并不是类,编写接口的方式和类很相似,但是它们属于不同的概念。类描述对象的属性和方法。接口则包含类要实现的方法。

接口中不能提供什么?

  • 接口中不会有实例对象,接口不能被实例化,但是可以被实现。
  • 在java8之前,接口不实现方法

看完上面这些如果有点懵的话,那就先有个印象就好了。总之接口就是一个大抽象,里面有一堆没有定义如何实现的方法,也不能被new 成对象,作用就是定义一组类的某些公共行为,然后这个接口类型可以用来引用实现则合格接口的类。

具体看这个吧。

2、接口属性

接口类型可以用来声明对象,他们可以成为一个空指针,或是被绑定在一个以此接口实现的对象。这个性质是不是超像父类。

同样,接口能够使用instanceof函数来判断某个类是否实现了具体的接口

if(anObject instanceOf Comparable){...}

接口也能进行扩展(继承其他的接口)

// 文件名: Sports.java
public interface Sports
{
   public void setHomeTeam(String name);
   public void setVisitingTeam(String name);
}
 
// 文件名: Football.java
public interface Football extends Sports
{
   public void homeTeamScored(int points);
   public void visitingTeamScored(int points);
   public void endOfQuarter(int quarter);
}
 
// 文件名: Hockey.java
public interface Hockey extends Sports
{
   public void homeGoalScored();
   public void visitingGoalScored();
   public void endOfPeriod(int period);
   public void overtimePeriod(int ot);
}

Hockey接口自己声明了四个方法,从Sports接口继承了两个方法,这样,实现Hockey接口的类需要实现六个方法。相似的,实现Football接口的类需要实现五个方法,其中两个来自于Sports接口.

接口不能包含实例字段,但是能够包含常量。接口中的方法都会被自动设置为public,接口中的字段总是public static final的。

每个类只能有一个超类,但是能实现多个接口。使用逗号将各个接口分开。
class Employee implements Cloneable, Comparable

3、接口方法

3.1、静态和私有方法

java8之后允许在接口中添加静态方法,通常的做法是将静态方法放在伴随类中。总之这种功能用的比较少啦。

3.2、默认方法

接口中可以为接口方法提供一个默认的实现,但必须用default修饰符来修饰

public interface Comparable<T>{
	default int compareTo(T other) {return 0;}
}

但是这种其实也没啥意义,实现接口的时候都会被覆盖。

给出两个用途:

  • 可以用默认方法调用其他方法
public interface Collection{
   int size();
   default boolean isEmpty(){return size()==0;}
}
  • 可以实现“接口演化”:意思是接口添加了一个新方法之后,原来实现这个接口的类就必须要重新实现这个方法再编译,但是如果将新填进来的方法声明为default,那可以不用重新编译,并且使用实现这个接口的类的时候,调用新的方法将调用接口的默认实现方法。

3.3、解决默认方法冲突

冲突为啥会发生?就有两种情况:

1、接口中方法与超类中方法的签名相同。

默认超类优先。毕竟超类大大可能有方法的实现,接口中可是一定没有方法实现的。

2、两个接口中的方法的签名相同。

  • 如果两个方法中至少有一个提供了default的实现,那程序员需要选一个
  • 如果都没有提供实现,那相当于没有冲突,直接实现就好了。

其实很好理解的,“类优先” ,然后冲突了你就写明白到底用谁的?

4、接口特性

4.1、接口与抽象类

  1. 抽象类中的方法可以有方法体,就是能实现方法的具体功能,但是接口中的方法不行。
  2. 抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是 public static final 类型的。
  3. 接口中不能含有静态代码块以及静态方法(用 static 修饰的方法),而抽象类是可以有静态代码块和静态方法。
  4. 一个类只能继承一个抽象类,而一个类却可以实现多个接口。

注:JDK 1.8 以后,接口里可以有静态方法和方法体了。

注:JDK 1.8 以后,接口允许包含具体实现的方法,该方法称为"默认方法",默认方法使用 default 关键字修饰。更多内容可参考
Java 8 默认方法。

注:JDK 1.9 以后,允许将方法定义为 private,使得某些复用的代码不会把方法暴露出去。更多内容可参考 Java 9
私有接口方法。

4.2、接口与回调

回调是啥呢?就是发生某个行为时需要运行一个动作,也就是需要传入一个函数。java中没有传入函数的方法,但是可以传入一个对象呐,对象里面不是有函数嘛!那这个对象里面有好多函数,怎么知道该运行哪个函数呢?这就是接口的作用了,接口中指定了该调用哪个函数。

举个例子:

package interf;

import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.time.Instant;

import javax.swing.JOptionPane;
import javax.swing.Timer;


public class TimerTest {

	public static void main(String[] args) {
		var listener = new TimerPrinter();
		var timer = new Timer(1000, listener);
		timer.start();
		
		JOptionPane.showMessageDialog(null, "Quit Program?");
		System.exit(0);
	}

}

class TimerPrinter implements ActionListener{
	public void actionPerformed(ActionEvent event) {
		System.out.println("At the tone, the time is "+ Instant.ofEpochMilli(event.getWhen()));
		Toolkit.getDefaultToolkit().beep();
	}
}

在这里插入图片描述

5、常用接口

5.1、Comparator接口

1、使用Comparable接口实现比较功能。

public class Employee implements Comparable<Employee>{
	...
	public int compareTo(Employee other){
		return Double.compare(salary, other.salary);
	}
}

2、使用Comparator接口实现比较功能。

class LengthComparator implements Comparator<String>{
	public int compare(String first, String second){
		return first.length()-second.length();
	}
}

显然与Comparable接口不同点在于参数的个数。
具体完成比较的时候

String[] fields = {"Peter", "hhh", "Mary"};
Arrays.sort(fields, new LengthComparator());

3、基于lambda表达式使用Comparator来实现比较功能。
Comparator中包含很多静态方法,使用lambda表达式可以简化创建的过程,十分方便。

Arrays.sort(people, Comparator.comparing(Person::getName));

变体:

Arrays.sort(people, Comparator.comparing(Person::getName).thenComparing(Persion::getLastName));
Arrays.sort(people, Comparator.comparing(p -> p.getName().length()));

其中lambda表达式的语法见我另外的blog。

5.2、对象克隆

浅拷贝:并没有克隆对象中其他引用的对象。如果原对象和前对象共享的子对象是不可变的,那么这种恭喜那个就是安全的。

举个例子,String类就是不可变的

String类的值是保存在value数组中的,并且是被private final修饰的

1、private修饰,表明外部的类是访问不到value的,同时子类也访问不到,当然String类不可能有子类,因为类被final修饰了
2、final修饰,表明value的引用是不会被改变的,而value只会在String的构造函数中被初始化,而且并没有其他方法可以修改value数组中的值,保证了value的引用和值都不会发生变化

所以我们说String类是不可变的。

深拷贝:就是将引用类型都拷贝一下,因为不可变的子对象是很少的,需要对引用也拷贝。

进行之前需要考虑

  • 默认的clone是否满足要求
  • 是否可以在可变子对象中调用clone方法修补默认的clone方法
  • 是否不该使用clone

所实现的类必须满足:

  • 实现Cloneable
  • 重新定义clone方法,并制定public来修饰。

注意这里Cloneable接口并没有clone方法,这个clone方法是从Object中继承的,这个借口只是一个标记的作用,用于检查是不是有实现继承,这是为了允许在类型查询中用到instanceof。

写个简单的例子吧:

class Employee implements Clonable{
	...
	public Employee clone() throws CloneNoetSupportedException{
		Employee cloned = (Employee) super.clone();//
		cloned.hireDay = (Date)hireDay.clone();
		return cloned
	}
}

上面这个例子写的就是,先调用父类的clone把不可变的字段复制了,再重新克隆一个可变对象来生成最终的克隆对象。

数组克隆:
所有的数组类型都有一个公共的克隆方法,而不是受保护的,可以用这个方法创建一个新数组。

int[] luckyNumbers = {2,3,5,7,11,13};
int[] cloned = luckyNumbers.clone();

具体可以看看这个blog

总结

总是在焦虑、彷徨。担心走的慢了、担心卷、担心好多好多,内耗严重。总之,还在路上,一直走下去吧。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值