1. 接口的概念
在Java中,接口可以看成是:多个类共同的公共行为规范,是一种引用数据类型
1.1 接口的语法规则
[访问修饰符] interface 接口名 [extends 父接口列表] {
// 常量定义
// 抽象方法声明
// 默认方法
// 静态方法
// 私有方法
}
注意:
1. 创建接口时, 接口的命名一般以大写字母 I 开头
2. 接口中的方法和属性不要加任何修饰符号, 保持代码的简洁性
1.2 接口的成员变量
interface A{//使用interface来定义接口
//接口中的成员变量
int a = 100;
//类似于
public static final int a = 100;//默认是public static final一般情况下不这么写
}
注意:成员变量默认是 public static final 修饰
1.3 接口的成员方法
interface A{//使用interface来定义接口
//接口中的方法
public abstract void fun1();
//类似于
void fun1();//不写默认是public abstract
}
注意:成员方法默认是 public abstract 修饰(使用其他限定修饰符会报错)
1.4 接口的实现
接口不能直接使用,必须要有一个"实现类"来"实现"该接口,实现接口中的所有抽象方法(重写)。
1.4.1 实现单个接口
类使用 implements 关键字来实现接口,实现类必须实现接口中所有的抽象方法。
interface A{//使用interface来定义接口
public abstract void fun1();
}
class AA implements A{//使用implements关键字来实现,要重写接口内所有的方法
@Override
public void fun1() {
}
}
注意:子类和父类之间是extends 继承关系,类与接口之间是 implements 实现关系。
1.4.2 实现多个接口
一个类可以同时实现多个接口,多个接口名之间用逗号分隔。
interface A{//使用interface来定义接口
public abstract void fun1();
}
interface C{
void fun2();
}
class AAA implements A,C{//实现多个接口
@Override
public void fun1() {
}
@Override
public void fun2() {
A.super.fun2();
}
}
注意:在实现多个接口的时候,使用 ,将接口分隔开
2. 接口的特性
2.1 接口的特性(1)
接口中的方法是不能在接口中实现的,只能由实现接口的类来实现(抽象方法)
interface C{
void fun2(){}//报错
}
注意:不能有具体的实现过程,不能有方法体
interface C{
default void fun2(){//如果用default修饰,那么就可以有具体的实现
System.out.println("fun2 ");
}
static void fun3(){//如果用static修饰,那么就可以有具体的实现,可以不用被重写
System.out.println("static ");
}
}
注意:
1. 如果用default修饰,那么就可以有具体的实现
2. 如果用static修饰,那么就可以有具体的实现
interface A{//使用interface来定义接口
public abstract void fun1();
}
interface C{
default void fun2(){
System.out.println("fun2 ");
}
}
class AAA implements A,C{//实现多个接口
@Override
public void fun1() {
}
@Override
public void fun2() {
A.super.fun2();
}
}
class CC implements C{
}
注意:
1. 如果用default修饰,那么就可以有具体的实现(可以被重写)
2. 如果用static修饰,那么就可以有具体的实现 (不用被重写)
2.2 接口的特性(2)
重写接口中方法时,不能使用默认的访问权限
interface A{//使用interface来定义接口
public abstract void fun1();
}
class AA implements A{
@Override
public void fun1() {
}
}
注意:因为接口中是public限定修饰符,重写必须比原来的范围大或者相等(只能是public)
2.3 接口的特性(3)
接口中不能有代码块和构造方法
interface C{
public C(){
}//报错
static{
}//报错
{
}//报错
// 接口中不能有构造方法和代码块
}
2.4 接口的特性(4)
.如果类没有实现接口中的所有的抽象方法,则类必须设置为抽象类
interface A{
public abstract void fun1();
void fun11();
}
abstract class CC implements A{
@Override
public void fun1() {
}
}
可以在实现的基础上继承,实现对接口的全部重写
interface A{
public abstract void fun1();
void fun11();
}
abstract class CC implements A{
@Override
public void fun1() {
}
}
class AAA extends AA {
@Override
public void fun11() {
}
}
3. 实现单个接口练习
interface IUSB{
void openDevice();
void closeDevice();
}
class Mouse implements IUSB{
@Override
public void openDevice() {
System.out.println("打开鼠标");
}
@Override
public void closeDevice() {
System.out.println("关闭鼠标");
}
static void click(){
System.out.println("点击鼠标");
}
}
class KeyBoard implements IUSB{
@Override
public void openDevice() {
System.out.println("打开键盘");
}
@Override
public void closeDevice() {
System.out.println("关闭键盘");
}
static void input(){
System.out.println("键盘输入");
}
}
class Computer{
void openOn(){
System.out.println("打开电脑");
}
void closeOn(){
System.out.println("关闭电脑");
}
void useDevice(IUSB usb){
usb.openDevice();
if(usb instanceof Mouse){
Mouse.click();
}else if(usb instanceof KeyBoard){
KeyBoard.input();
}
usb.closeDevice();
}
}
public class Interface_Text_1 {
public static void main(String[] args) {
Computer computer = new Computer();
//打开电脑
computer.openOn();
System.out.println("========");
//使用电脑各个部件
Mouse mouse =new Mouse();
computer.useDevice(mouse);
KeyBoard keyBoard = new KeyBoard();
computer.useDevice(keyBoard);
//关闭电脑
System.out.println("============");
computer.closeOn();
}
}
//结果
打开电脑
========
打开鼠标
点击鼠标
关闭鼠标
打开键盘
键盘输入
关闭键盘
============
关闭电脑
4. 实现多个接口练习
类和类之间是单继承的,一个类只能有一个父类,即Java中不支持多继承,但是一个类可以实现多个接口
abstract class Animal{
String name;
int age;
public Animal(String name, int age) {
this.name = name;
this.age = age;
}
abstract void eat();
}
interface IRUNNING{
void run();
}
interface ISWIMMING{
void swim();
}
class Dog extends Animal implements IRUNNING,ISWIMMING{
Dog(String name ,int age){
super(name,age);
}
@Override
void eat() {
System.out.println(name+"吃狗粮");
}
@Override
public void run() {
System.out.println(name+"用狗腿跑");
}
@Override
public void swim() {
System.out.println(name+"用狗腿游");
}
}
public class interface_2 {
static void fun1(Animal animal){
animal.eat();
}
static void fun2(IRUNNING irunning){//粗略理解为接口当父类用
irunning.run();
}
static void fun3(ISWIMMING iswimming){
iswimming.swim();
}
public static void main(String[] args) {
Dog dog = new Dog("小黄",2);
fun1(dog);//向上转型
fun2(dog);//向上转型
fun3(dog);//向上转型
}
}
注意:一个类实现多个接口时,每个接口中的抽象方法都要实现,否则类必须设置为抽象类。
5. 接口的继承
类和类之间是单继承的,一个类可以实现多个接口,接口与接口之间可以多继承。即:用接口可以达到 多继承的目的。
interface B{
void funB();
}
interface BB{
void funBB();
}
//BBB接口同时继承B和BB接口
interface BBB extends B,BB{//相当于接口的合并
void funBBB();
}
注意:接口间的继承相当于把多个接口合并在一起.
在创建一个类实现这个合并的接口,要重写所有的类
interface B{
void funB();
}
interface BB{
void funBB();
}
//BBB接口同时继承B和BB接口
interface BBB extends B,BB{//相当于接口的合并
void funBBB();
}
class c implements BBB{//需要实现三个方法的重写
@Override
public void funB() {
}
@Override
public void funBB() {
}
@Override
public void funBBB() {
}
}
注意:要重写所有的抽象方法(包括继承的抽象方法)
6. java内置的接口
6.1 对象的比较
6.1.1 Comparable 接口
Comparable接口只包含一个抽象方法 CompareTo( )方法
Comparable 接口:
public interface Comparable<T> {
public int compareTo(T o);
}
T:是泛型类型参数,表示要比较的对象类型
CompareTo( )方法:用于比较当前对象(调用该方法的对象)与传入的对象 o 的大小关系
1. 如果当前对象小于传入对象 o
,则返回一个负整数。
2. 如果当前对象等于传入对象 o
,则返回 0。
3. 如果当前对象大于传入对象 o
,则返回一个正整数。
用例举例数组的排序:
class Student {
String name ;
int score;
public Student(String name,int score){
this.name =name;
this.score =score;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", score=" + score +
'}';
}
}
public class Comparable_1 {
public static void main(String[] args) {
Student[] students =new Student[3];
students[0] = new Student("zhangsan",88);
students[1] = new Student("lisi",90);
students[2] =new Student("wangwu",67);
Arrays.sort(students);//报错
System.out.println(Arrays.toString(students));
}
}
原因:因为Arrays.sort()方法在对对象数组进行排序时,要求数组中的元素所属的类必须实现java.lang.Comparable接口,而你定义的Student类并没有实现该接口,所以无法确定对象之间的比较规则,从而导致排序失败。
正确的操作:
import java.util.Arrays;
class Student implements Comparable<Student>{
String name ;
int score;
public Student(String name,int score){
this.name =name;
this.score =score;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", score=" + score +
'}';
}
@Override
public int compareTo(Student o) {
return this.score-o.score;
}
}
public class Comparable_1 {
public static void main(String[] args) {
Student[] students =new Student[3];
students[0] = new Student("zhangsan",88);
students[1] = new Student("lisi",90);
students[2] =new Student("wangwu",67);
Arrays.sort(students);
System.out.println(Arrays.toString(students));
}
}
练习:根据自己定义的比较规则,写一个冒泡排序
@Override
public int compareTo(Student o) {
return this.score-o.score;
}
void fun1(Comparable[] comparables){
for (int i = 0; i < comparables.length-1; i++) {
for (int j = 0; j < comparables.length-1-i; j++) {
if(comparables[j].compareTo(comparables[j+1])>0){
Comparable stm = comparables[j+1];
comparables[j+1] = comparables[j];
comparables[j] = stm;
}
}
}
}
这种定义比较规则有一个缺点:拓展性太差
在一般的情况下:我们会将这种比较规则定义专门在一个类里面(比较器)
6.1.2 Comparator 接口
比较器(Comparator)是一种用于定义对象之间比较规则的工具,它可以在不改变对象本身类结构的情况下,灵活地指定不同的排序或比较方式。
Comparator接口:
public interface Comparator<T> {
int compare(T o1, T o2);
// 其他默认方法和静态方法
}
compare方法用于比较两个对象 o1
和 o2
的大小,返回值规则如下:
1. 如果 o1
小于 o2
,返回负整数。
2. 如果 o1
等于 o2
,返回 0。
3. 如果 o1
大于 o2
,返回正整数。
class AgeCompare implements Comparator<Student> {
@Override
public int compare(Student o1, Student o2) {
return o1.age - o2.age;
}
}
冒泡排序:按照年龄排序
class AgeCompare implements Comparator<Student> {
@Override
public int compare(Student o1, Student o2) {
return o1.age - o2.age;
}
public static void fun1(Student[] students,Comparator<Student> comparator) {
for (int i = 0; i < students.length - 1; i++) {
for (int j = 0; j < students.length - i - 1; j++) {
if(comparator.compare(students[j],students[j+1])>0){
Student stem = students[j];
students[j] =students[j+1];
students[j+1] = stem;
}
}
}
}
}
注意:比较器,需要的比较,专门写出来比较灵活
6.1.3 两种接口的区别
1. Comparable
接口:定义在要进行比较的类内部。
2. Comparator
接口:定义在外部,独立于要比较的类。它允许在不修改类本身的情况下,为该类的对象定义不同的比较规则。
6.2 对象的拷贝
6.2.1 浅拷贝
实现拷贝操作,需要使用Object类中的clone方法
使用clone方法需要解决4个问题
1. 访问限定符,object类中clone方法用protect修饰,不能直接调用,需要子类重写
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
2. throws CloneNotSupportedException用于异常处理的一种声明方式:不支持克隆
public static void main(String[] args) throws CloneNotSupportedException {
Person person1 = new Person("zhangsan",13);
}
3. 向下转型,重写的返回值是一个object类型,要转为Personal类型
public static void main(String[] args) throws CloneNotSupportedException {
Person person1 = new Person("zhangsan",13);
Person person2 = (Person) person1.clone();
}
4. 实现Cloneable 接口(标记接口或者空接口),本身没有定义任何方法,作用是告诉 Java 虚拟机这个类的对象是可以被克隆的。
class Person implements Cloneable{//实现Cloneable 接口
String name;
int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
protected Object clone() throws CloneNotSupportedException {//重写
return super.clone();
}
}
public class Clone_1 {
public static void main(String[] args) throws CloneNotSupportedException {//声明
Person person1 = new Person("zhangsan",13);
Person person2 = (Person) person1.clone();//向下转型
System.out.println(person1);
System.out.println(person2);
System.out.println("==========");
person2.name = "lisi";
person2.age = 18;
System.out.println(person1);
System.out.println(person2);
}
//输出:
//Person{name='zhangsan', age=13}
//Person{name='zhangsan', age=13}
//==========
//Person{name='zhangsan', age=13}
//Person{name='lisi', age=18}
注意:
1. 浅拷贝:对于基本数据类型,会直接复制其值;而对于引用数据类型,仅仅复制对象的引用
2. String 是不可变类。这意味着一旦一个 String 对象被创建,它的内容就不能再被更改。
3. 当你对一个 String 变量进行赋值操作时,实际上是让该变量指向了一个新的 String 对象。
6.2.2 深拷贝
对于原对象中的所有属性,无论其是基本数据类型还是引用数据类型,都会创建一个完全独立的副本
深拷贝和浅拷贝,解决的问题大致相同,只有在重写有一些不同
class Fruit implements Cloneable{
int number;
public Fruit(int number) {
this.number = number;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public String toString() {
return "number= "+number;
}
}
class Apple implements Cloneable{
int size;
Fruit fruit;
public Apple(int size, Fruit fruit) {
this.size = size;
this.fruit = fruit;
}
@Override
protected Object clone() throws CloneNotSupportedException {
Apple stm = (Apple) super.clone();
stm.fruit = (Fruit) this.fruit.clone();
return stm;
}
@Override
public String toString() {
return "Apple{" +
"size=" + size +
" , " + fruit +
'}';
}
}
图解:
注意:
1. 可以理解成进行了两次拷贝,
2. 第一次拷贝,将对象本身进行了拷贝并存放在了一个新的地址中,并指向
3. 第二次拷贝,将引用类型属性所指对象具体的值进行了拷贝并存放在一个新的地址中,并指向
7. 接口和抽象类的区别
主要区别:抽象类中可以包含普通方法和普通字段, 这样的普通方法和字段可以被子类直接使用(不必重写), 而接口中不能包含普通方法, 子类必须重写所有的抽象方法
NO | 区别 | 抽象类 | 接口 |
1 | 组成 | 普通类+抽象方法 | 抽象方法+常量 |
2 | 权限 | 各种权限 | 只能public |
3 | 使用关键字 | abstract | interface |
4 | 子类使用关键字 | extends | implements |
5 | 限制 | 一个子类只能继承一个抽象类,可以实现多个接口 | 接口不能继承抽象类 一个接口可以继承多个接口(接口合并) |