前言:
本篇博客将带大家了解Java中接口相关的知识。
目录
何为接口
在生活中,大家对于各种各样的接口早已见怪不怪了。对于不同的接口,与之相匹配的东西也不一样。如:电脑上的USB接口只能插符合USB协议的设备;而对于电源插座上的插孔,只能插符合规范的设备。由此,我们可以知晓:接口就是公共的行为规范,在实现时,只要符合规范,就能使用。在Java中也是如此。在Java中,接口可以看成多个类的公共规范,是一种引用数据类型。
接口的特点
- 接口的定义格式与定义类的格式基本相同,将class关键字换成interface关键字即可。创建接口时,接口的命名一般以大写字母I开头。接口的命名一般使用“形容词”词性的单词。接口中的方法和属性尽量不要加任何修饰符,以保持代码的简洁。
- 接口不能直接使用,必须要借助一个类来实现该接口(借助implements),实现接口中全部的抽象方法。再举一个例子:笔记本电脑通过USB接口实现对鼠标、键盘的操控
public class Computer { public void open(){ System.out.println("开机!"); } public void close(){ System.out.println("关机!"); } public void useDevice(IUSB usb){ usb.openDevice(); if(usb instanceof Mouse){ Mouse mouse = (Mouse)usb; mouse.click(); }else if(usb instanceof KeyBoard){ KeyBoard keyBoard = (KeyBoard) usb; keyBoard.inPut(); } usb.closeDevice(); } }
public class Test { public static void main(String[] args) { Computer computer = new Computer(); Mouse mouse = new Mouse(); KeyBoard keyBoard = new KeyBoard(); computer.useDevice(mouse); System.out.println("=================="); computer.useDevice(keyBoard); } }
效果:
- 接口是一种引用类型,但是不能直接new接口的对象(因为接口是抽象化的,无法实例化)。
- Java中,接口中的每一个方法都是public的抽象方法。即:接口中的每一个方法都会被隐式地指定为public abstract。如果要想让接口中的方法不是抽象方法,则方法要被default修饰(jdk8满足)。
- Java中,接口中可以含有变量,但是接口中的变量都会被隐式地指定为public static final。
- 接口中的方法是不能在接口中实现的(全是抽象方法),只能由实现接口的类来实现。
- 重写接口中的方法时,不能使用默认的访问权限(重写的相关知识点)。
- 接口中不能含有静态代码块和构造方法。
- 接口虽然不是类,但是接口编译完成后字节码文件的后缀格式也是.class
- 如果类没有实现接口中的所有抽象方法,则类必须要设置为抽象类。
实现多个接口
在Java中,一个类只能继承一个父类(Java不支持多继承),但是一个类可以实现多个接口。一个类在实现多个接口的时候,每个接口中的抽象方法都要实现,否则该类必须设置为抽象类。
注意:在继承和实现接口同时存在的时候,要先写继承,再写实现接口
举例:
abstract class Animal{
public String name;
public Animal(String name) {
this.name = name;
}
}
interface IRunning{//跑步
void run();
}
interface ISwimming{//游泳
void swim();
}
interface IFly{//飞
void fly();
}
class Dog extends Animal implements IRunning{//先继承再实现接口
public Dog(String name) {
super(name);
}
public void run(){
System.out.println(name+" 正在跑步(Dog版)");
}
}
class Frog extends Animal implements ISwimming{
public Frog(String name) {
super(name);
}
public void swim(){
System.out.println(name+" 正在游泳(Frog版)");
}
}
class Bird extends Animal implements IRunning,ISwimming,IFly{//一个类实现多个接口(用逗号隔开)
public Bird(String name) {
super(name);
}
public void run(){
System.out.println(name+" 正在跑步(Bird版)");
}
public void swim(){
System.out.println(name+" 正在游泳(Bird版)");
}
public void fly(){
System.out.println(name+" 正在飞(Bird版)");
}
}
public class Test {
public static void run(IRunning iRunning){
iRunning.run();
}
public static void swim(ISwimming iSwimming){
iSwimming.swim();
}
public static void fly(IFly iFly){
iFly.fly();
}
public static void main(String[] args) {
run(new Dog("小狗"));
System.out.println("========");
swim(new Frog("小青蛙"));
System.out.println("========");
run(new Bird("小鸟"));
swim(new Bird("小鸟"));
fly(new Bird("小鸟"));
}
}
效果:
接口表达的含义是具有某一种特性。通过接口的实现会让程序员忘记类型,使用类的时候不必再关注类的类型,而更加关注类具有的能力(如上述的小鸟,可以run、swim和fly)
接口间的继承
类和类之间是单继承的,一个类可以实现多个接口,接口与接口之间是可以多继承的。换而言之,用接口可以达到多继承的目的。
举例:
interface A{
void funcA();
}
interface B{
void funcB();
}
interface CC extends A,B{//接口的继承(接口的拓展),相当于把多个接口合并在一起
void func();//代表CC这个接口,不仅具备func这个功能,还具备了A和B的接口的功能
//这样写的好处就是一个CC解决了多个方法的重写问题
}
class C implements CC{//对CC里面的方法进行重写的时候,不仅要重写func还有funcA和funcB都要重写(其实还是有点继承的味道),否则报错
public void func(){
//……
}
public void funcA(){
//……
}
public void funcB(){
//……
}
}
到这里,关于接口的知识点已经差不多都了解完了。那么,我们再举一个关于接口的例子吧:
import java.util.Arrays;
public class Test {
public static void main(String[] args){
int[] array = {1,3,2,10,5,3,7};
Arrays.sort(array);//排序
System.out.println(Arrays.toString(array));
}
}
由上述代码可知,sort方法可以对数组中的数据进行排序,那么如果需要排序的对象是学生类呢?
如下:
class Student{
public String name;
public int age;
public int score;
public Student(String name, int age, int score){
this.name = name;
this.age = age;
this.score = score;
}
public String toString() {
return "Student{"+
"name='" + name + '\''+
", age=" +age +
",score=" + score +
'}';
}
}
public class Test {
public static void main(String[] args) {
Student[] students = new Student[3];//初始化
students[0] = new Student("zhangsan", 12, 10);
students[1] = new Student("lisi", 13, 11);
students[2] = new Student("wangwu", 10, 9);
}
}
如何对这数组中的三个学生进行比较呢?又按照什么标准进行排序呢?名字?年龄?还是分数呢?直接将students数组放入sort中是否可以像之前一样直接出排好顺序的数组元素呢?答案显然是不能的。要想实现排序,我们需要额外指定。让Student类继承Comparable接口,并实现其中的CompareTo方法。
import java.util.Arrays;
import java.util.Comparator;
class Student implements Comparable<Student>{
public String name;
public int age;
public int score;
public Student(String name, int age, int score){
this.name = name;
this.age = age;
this.score = score;
}
public String toString() {
return "Student{"+
"name='" + name + '\''+
", age=" +age +
",score=" + score +
'}';
}
public int compareTo(Student o){
//按年龄比较
if(this.age > o.age){
return 1;
}else if(this.age < o.age){
return -1;
}else{
return 0;
}
}
}
public class Test {
public static void main(String[] args) {
Student[] students = new Student[3];//初始化
students[0] = new Student("zhangsan", 12, 10);
students[1] = new Student("lisi", 13, 11);
students[2] = new Student("wangwu", 10, 9);
Arrays.sort(students);//像上面一样通过sort方法进行排序
System.out.println(Arrays.toString(students));
}
}
效果:
同样,也可以按照分数和姓名来进行排序:
通过比较器,也有一样的效果:
import java.util.Arrays;
import java.util.Comparator;
class Student{
public String name;
public int age;
public int score;
public Student(String name, int age, int score){
this.name = name;
this.age = age;
this.score = score;
}
public String toString() {
return "Student{"+
"name='" + name + '\''+
", age=" +age +
",score=" + score +
'}';
}
}
//按年龄比较:
class AgeComparator implements Comparator<Student>{
public int compare(Student o1, Student o2){
return o1.age - o2.age;
}
}
public class Test {
public static void main(String[] args) {
Student[] students = new Student[3];//初始化
students[0] = new Student("zhangsan", 12, 10);
students[1] = new Student("lisi", 13, 11);
students[2] = new Student("wangwu", 10, 9);
//比较器
AgeComparator ageComparator = new AgeComparator();
Arrays.sort(students,ageComparator);
System.out.println(Arrays.toString(students));
}
}
按照其他两种比较方法也是一样的:
//按分数比较:
class ScoreComparator implements Comparator<Student>{
public int compare(Student o1, Student o2){
return o1.score - o2.score;
}
}
//按名字比较:(引用类型)
class NameComparator implements Comparator<Student>{
public int compare(Student o1, Student o2){
return o1.name.compareTo(o2.name);
}
}
Cloneable接口和深拷贝
关于Cloneable接口:
Object类中存在一个Clone方法,调用这个方法可以创建一个对象的“拷贝”,但是要想合法调用clone方法,必须要先实现Cloneable接口,否则抛CloneNotSupportedException异常。
举例:
class Student implements Cloneable{
public String name;
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
'}';
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();//父类的克隆
//protected修饰的
}
}
public class Test {
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);
}
}
效果:
这段代码如何引出?
方法如下:
关于深、浅拷贝:
何为深拷贝?何又为浅拷贝?
浅拷贝:D是由C拷贝而来,当C改变的时候,D也随之改变,这种拷贝就叫做浅拷贝。
深拷贝:B是由A拷贝而来,当A改变的时候,B未发生改变,这种拷贝就叫做深拷贝。
举例说明:
浅拷贝:
class Money{
public double money = 12.25;
}
class Student implements Cloneable{
public String name;
public Money m = new Money();
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
'}';
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public class Test {
public static void main(String[] args) throws CloneNotSupportedException {
Student student1 = new Student();
Student student2 = (Student)student1.clone();//向下转型(强制类型转换)
System.out.println(student1.m.money);
System.out.println(student2.m.money);
System.out.println("=========");
student1.m.money = 19.21;
System.out.println(student1.m.money);
System.out.println(student2.m.money);
}
}
效果:
改变的明明是student1.m.money,结果student2.m.money也发生了改变,这种就叫做浅拷贝。
图解如下:
深拷贝:
class Money implements Cloneable{
public double money = 12.25;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
class Student implements Cloneable{
public String name;
public Money m = new Money();
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
'}';
}
@Override
protected Object clone() throws CloneNotSupportedException {
Student student = (Student)super.clone();//只是克隆了Student对象
student.m = (Money)this.m.clone();//克隆了student对象的Money对象
return student;
//return super.clone();
}
}
public class Test {
public static void main(String[] args) throws CloneNotSupportedException {
Student student1 = new Student();
Student student2 = (Student)student1.clone();//向下转型(强制类型转换)
System.out.println(student1.m.money);
System.out.println(student2.m.money);
System.out.println("=========");
student1.m.money = 19.21;//深拷贝
System.out.println(student1.m.money);
System.out.println(student2.m.money);
}
}
效果:
只改变了student.m.momey,并未改变student2.m.money,这种拷贝叫做深拷贝。
图解如下:
抽象类和接口的区别
核心区别:
抽象类可以包含普通方法和普通字段,这样的普通方法和普通字段可以被子类直接使用而不必重写;而接口中不能包含普通字段,子类必须重写接口中所有的方法(抽象方法)。