文章目录
- 一、详解初始接口
- 1.1 什么是接口接口?
- 1.2 接口定义规则
- 二、接口的详解使用
- 二、接口的接口特性
- 三、实现多接口
- 四、详解接口实现实例
- 五、接口Clonable实现深浅拷贝
- 六、详解抽象类与接口的接口区别
一、初始接口
1.1 什么是详解接口?
接口就是公共的行为规范标准,大家在实现时,接口只要符合规范标准,详解就可以通用。接口
在Java中,详解接口可以看成是接口:多个类的公共规范,是详解一种引用数据类型。
1.2 接口定义规则
Interface关键字用来声明一个接口。
interface 接口名称 { // 声明变量 // 抽象方法}
代码如下(示例):
interface IInterface { //任何类型为 public static final 字段 //任何类型位 public abstract 方法}
1.接口是隐式抽象的,当声明一个接口的时候,不必使用abstract关键字。
2.接口中每一个方法也是隐式抽象的,声明时同样不需要abstract关键字。
3.接口中的方法都是公有的。
4.接口中的方法和属性不要加任何修饰符号,保持代码简洁性.
二、接口的使用
接口不能直接使用,必须要有一个"实现类"来"实现"该接口,实现接口中的所有抽象方法。
定义一个Animal接口:
//定义Animal接口interface Animal { void eat();}
子类和父类之间是extends 继承关系,类与接口之间是 implements 实现关系。
class Dog implements Animal { @Override public void eat() { System.out.println("吃狗粮!"); }}class Cat implements Animal { @Override public void eat() { System.out.println("吃猫粮!"); }}
测试一下接口是否支持多态:
public static void func(Animal animal) { animal.eat(); } public static void main(String[] args) { func(new Dog()); func(new Cat()); }
二、接口的特性
1.接口类型是一种引用类型,但是不能直接new接口的对象
public static void main(String[] args) { Animal animal = new Animal(); }
2.接口中的每一个方法都是隐式指定为public abstract,如果设定为其他修饰符都会报错
interface IInterface { protected void func();}
3.接口中的方法不能有具体的实现
interface Animal { void eat(){ System.out.println("吃饭!"); }}
4.重写接口方法时只能使用public访问限权修饰
class Dog implements Animal { @Override void eat() { System.out.println("吃狗粮!"); }}
5.接口中的变量默认为public static final变量
interface Animal { int age = 18; void eat();}public static void main(String[] args) { System.out.println(Animal.age);//可以用接口名访问,证明是静态的 Animal.age = 20;//无法修改,说明被final修饰 }
6.接口中不能有静态代码块和构造方法
7.接口虽然不是类,但是在编译后生成的字节码文件后缀也是.class
8.如何一个类没有实现接口中的所有抽象方法,该类必须设置为抽象类.
9.JDK1.8中,接口中可以包含default方法.
10.接口间的继承
在Java中,类和类之间是单继承的,一个类可以实现多个接口,接口与接口之间可以多继承。即:用接口可以达到多继承的目的。
接口可以继承一个接口, 达到复用的效果. 使用 extends 关键字.
interface IRunning { void running();}interface ISwimming { void swimming();}//两栖动物,既有跑,又有游的功能interface IAmphibious extends IRunning,ISwimming { }
接口间的继承相当于把多个接口合并在一起.
三、实现多接口
java中,不支持多继承,但一个类可以实现多个接口.
//定义一个Animal类class Animal { public String name; public Animal(String name) { this.name = name; }}
//定义一组会飞的,会跑的,会游泳的接口interface IFlying { void fly();}interface IRunning { void run();}interface ISwimming { void swim();}
我们创建几个具体的动物
class Cat extends Animal implements IRunning { //猫是会跑的 public Cat(String name) { super(name); } @Override public void run() { System.out.println(this.name+"正在跑"); }}
class Fish extends Animal implements ISwimming { //鱼会游泳 public Fish(String name) { super(name); } @Override public void swim() { System.out.println(this.name+"正在游"); }}`````javaclass Frog extends Animal implements IRunning,ISwimming { //青蛙会跑也会游 public Frog(String name) { super(name); } @Override public void run() { System.out.println(name+"正在跑"); } @Override public void swim() { System.out.println(name+"正在游"); }}
注意:一个类实现多个接口时,每个接口中的抽象方法都要实现,否则类必须设置为抽象类。
有了接口之后, 类的使用者就不必关注具体类型,而只关注某个类是否具备某种能力.
//我们实现一个方法,我们不用管到底是什么动物,只要会跑就可以调用.public static void run(IRunning iRunning) { iRunning.run(); }
//这里我们写一个机器人类class Robot implements IRunning { public String name; public Robot(String name) { this.name = name; } @Override public void run() { System.out.println(name + "正在跑"); }}
public static void main(String[] args) { run(new Cat("喵喵")); run(new Robot("机器人")); }
在这里我们不用管是不是动物,只要具有这个功能即可调用.
四、接口实现实例
我们对Person类型数组进行排序.
class Person { public String name; public int age; public Person(String name, int age) { this.name = name; this.age = age; } @Override public String toString() { return "Person{ " + "name='" + name + '\'' + ", age=" + age + '}'; }} public static void main(String[] args) { Person[] person = { new Person("张三",18),new Person("lisi",20),new Person("wangwu",21)}; Arrays.sort(person); System.out.println(Arrays.toString(person)); }
我们这里对Person数组进行排序,根据的是name,还是age?
在系统的排序的方法中会将数组类型强制转换为Comparable,但我们的Person未实现Comparable接口,所以会报类转换异常.
那让母后我们来让Person类实现Comparable功能.
我们在实现Comparable功能时,系统提示我们必须实现compareTo方法.
@Override public int compareTo(Person o) { if(this.age >o.age) { return 1; }else if(this.age < o.age) { return -1; }else { return 0; } }
现在我们试着排序一些Person数组
我们可以发现Person数组已经能够根据age进行排序了.
这时候有同学就要问了:
这里为什么要这样书写,其实这个的Person是一个泛型,但大家不用去研究它,我们来看一下String的源码.
我们可以发现也是这样写的,我们只需要模仿就行了.
大家想一下要是不想按age排序了,现在想实现按照name排序,那么我们该怎么去写?
直接去修改CompareTo方法吗?这里CompareTo已经被其他程序调用过了,我们直接修改会造成程序紊乱,我们可以这样写:
class NameComparator implements Comparator{ @Override public int compare(Person o1, Person o2) { return o1.name.compareTo(o2.name); }}public static void main(String[] args) { Person[] person = { new Person("zhangsan",18),new Person("lisi",20),new Person("wangwu",21)}; NameComparator nameComparator = new NameComparator(); Arrays.sort(person,nameComparator); System.out.println(Arrays.toString(person)); }
我们可以写一个类去实现Comparator接口去指定比较对象的内容.
我们这里自己写一个冒泡排序,去按照Person的age去排序
public static void BubbleSort(Comparable[] arr) { for (int i = 0; i < arr.length-1; i++) { for (int j = 0; j < arr.length-1-i; j++) { if(arr[j].compareTo(arr[j+1]) >0) { Comparable tmp = arr[j]; arr[j] = arr[j+1]; arr[j+1] = tmp; } } } } public static void main(String[] args) { Person[] person = { new Person("zhangsan",18),new Person("lisi",20),new Person("wangwu",21)}; BubbleSort(person); System.out.println(Arrays.toString(person)); }
五、Clonable实现深浅拷贝
当我们想实现对象的拷贝时,我们就需要实现Clonable接口.
//定义一个学生类class Student implements Cloneable { public String name;}
我们可以发现Clonable接口是一个空接口,也可以称作标记接口.
我们发现Student类实现了Clonable接口但还是不能克隆.
我们在底层发现,clone()方法是protected修饰的,不同包只能通过子类super.调用,所以我们在Student必须重写这个方法.
class Student implements Cloneable { public String name; @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } }
我们重写之后发现,还是不能调用clone()方法,我们继续看底层代码.
我们可以发现父类抛了一个异常,那么我们也必须加上.
因为它返回的是一个Object类型的,我们必须强制转换为Student类型.
public static void main(String[] args) throws CloneNotSupportedException { Student student = new Student(); student.name = "张三"; Student student1 = (Student) student.clone(); }
public static void main(String[] args) throws CloneNotSupportedException { Student student = new Student(); student.name = "张三"; Student student1 = (Student) student.clone(); System.out.println(student); System.out.println(student1); }
我们打印了一下对象,这时候Student对象已经克隆成功,但这里只是浅拷贝.
class IScore { double score;} class Student implements Cloneable { public String name; public IScore iScore = new IScore(); @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } @Override public String toString() { return "Student{ " + "name='" + name + '\'' + '}'; } }
public static void main(String[] args) throws CloneNotSupportedException { Student student = new Student(); student.name = "张三"; Student student1 = (Student) student.clone(); student.iScore.score = 100; System.out.println("student: "+student.iScore.score); System.out.println("student1: "+student1.iScore.score); }
我们可以发现在克隆后,修改student的score,student1的score也被修改了.
我们发现,两个对象指向了同一块Score,所以我们要将Score也进行克隆.
class IScore implements Cloneable{ double score; @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); }} class Student implements Cloneable { public String name; public IScore iScore = new IScore(); @Override protected Object clone() throws CloneNotSupportedException { Student student = (Student)super.clone(); student.iScore = (IScore)this.iScore.clone(); return student; } }
这样就可以实现深拷贝.
六、抽象类与接口的区别
1、抽象类中可以包含普通方法,但接口中只能包含public与abstract方法(JDK 1.8之前),JDK1.8之后允许接口中出现default方法;
2、抽象类中的成员变量没有访问权限的限制,但接口中的变量只能被public static final修饰;
3、一个接口可以继承多个接口,但一个类只能有一个父类,类可以实现多个接口;
4、抽象类是对一类事物的抽象,接口则是对行为的抽象。一个类继承一个抽象类代表“是不是”的关系,而一个类实现一个接口则表示“有没有”的关系。
核心区别: 抽象类中可以包含普通方法和普通字段, 这样的普通方法和字段可以被子类直接使用(不必重写), 而接口中不能包含普通方法, 子类必须重写所有的抽象方法.