文章目录
接口
1.1 接口的概念
接口就是公共的行为规范标准,大家在实现时,只要符合规范标准,就可以通用。例如USB接口,TPC接口等。在java中,接口可以看成是:多个类的公共规范,是一种引用类型数据类型。
1.2 接口语法规则
接口的定义格式与一定类的格式基本相同,只不过将class关键字变成了interface关键字,就定义为一个接口。
例1:定义一个接口
public interface 接口名(例如:IOperatation){
//抽象方法
public abstract void method1();//在接口内部的方法默认是public abstract修饰的,所以为了简洁可以不用写
public void method2();
abstract void method3();
void method4();
//注意:推荐使用方式4,代码简洁
}
提示:
1.创建接口时,接口的命名一般以大写字母 I 开头。
2.接口的命名一般使用"形容词"词性单词。
3.阿里编码规范中,接口中的方法和属性不加任何修饰符号,保持代码简洁性。
1.3 接口使用
接口不能直接使用,必要要有一个"实现类"来具体实现该接口,实现接口中所有的抽象方法。接口和类的关联用关键字implements
例2:类和接口的关联
public class 类名称 implements 接口名称{
//类方法对接口内的抽象方法的重写的具体实现
}
注意:子类和父类之间是extends的继承关系,类与接口之间是implements实现关系。
1.4 接口特性
1.接口类型是一种引用类型,但是不能直接new接口对象。即接口是抽象的,不能实例化。
2.接口中的每一个方法都是public的抽象方法,即接口的方法都会被隐式的指定为public abstract(且只能是public abstract,其他修饰符都会报错)。例如:private修饰会报错。
3.接口中的抽象方法是不能在接口中实现的(即不能有{}的主体部分),需要一个类来关联接口,在类的内部具体实现接口的抽象方法。
4.重写接口中方法时,不能使用default访问权限修饰。因为接口中的抽象方法默认是public的,故关联类中的重写方法的访问权限不能低于接口的访问权限。故只能也是public来修饰重写的类成员方法。
5.接口中可以含有含有变量,但是接口中的变量会被隐式的指定为public static final变量。
6.接口中不能有静态代码块和构造方法。
7.接口虽然不是类,但是接口编译完成后的字节码文件的后缀格式也是 .class 。
8.一个类如果关联一个接口一般要重写该接口所有的抽象方法,除非这个类也是抽象类。但是出来混总是要还的,当下一个类继承这个抽象类时,也必须重写所有的抽象方法。
9.jdk8中:接口中还可以包含default方法。
1.5 实现多个接口
在java中,类与类之间是单继承的,一个类只能有一个父类,即java中不支持多继承,但是一个类可以实现多个接口。
例3:多个接口的实现
class Animal{
protected 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(name+"正在用四条腿跑!");
}
}
class Fish extends Animal implements ISwimming{
public Fish(String name) {
super(name);
}
@Override
public void swim() {
System.out.println(name+"正在用尾巴游泳!");
}
}
class Frog extends Animal implements ISwimming,IRunning{
public Frog(String name) {
super(name);
}
@Override
public void run() {
System.out.println(name+"正在向前跳!");
}
@Override
public void swim() {
System.out.println(name+"正在蹬腿游泳!");
}
}
class Duck extends Animal implements IRunning,ISwimming,IFlying{
public Duck(String name) {
super(name);
}
@Override
public void fly() {
System.out.println(name+"正在煽动翅膀飞!");
}
@Override
public void run() {
System.out.println(name+"正在用两条腿奔跑!");
}
@Override
public void swim() {
System.out.println(name+"正在用两条腿游泳!");
}
}
public class TestDemo1 {
public static void run(IRunning iRunning){
iRunning.run();
}
public static void swim(ISwimming iSwimming){
iSwimming.swim();
}
public static void fly(IFlying iFlying){
iFlying.fly();
}
public static void main(String[] args) {
Cat cat=new Cat("小猫");
run(cat);
System.out.println("===============");
Fish fish=new Fish("小鱼");
swim(fish);
System.out.println("===============");
Frog frog=new Frog("青蛙");
run(frog);
swim(frog);
System.out.println("===============");
Duck duck=new Duck("鸭子");
run(duck);
swim(duck);
fly(duck);
}
}
注意: 一个类实现多个接口时,每个接口中的抽象方法都要实现,除非类是抽象类。
以上的代码展示了java面向对象在编程中最常见的用法:一个子类继承一个父类,同时实现多种接口。
继承表达的含义:"is"的意思,而接口表达的含义:具有 "xxx"的特性或者功能。
猫是一种动物,具有跑的特性。
鱼是一种动物,具有游泳的特性。
青蛙也是一种动物,既能跑也能游泳。
鸭子也是一种动物,既能跑,也能游泳,还能飞。
这样有了接口之后,关于类的使用我们就不用关注具体的类型,而只需关注这个类是否具备某种特性或者功能。
所以我们只需要定义一个静态方法(带参数),参数类型为要实现某种功能的接口类型,在这个方法内部,我们并不关注到底是哪种动物,只需要该静态方法传入的类有关联这个接口,从而这个类就具有这个接口的特性或者功能即可。
1.6 接口间的继承
在java中,类与类之间是单继承,一个类可以实现多个接口,接口与接口之间可以多继承,即用接口可以达到多继承的目的。接口可以继承一个即可,达到复用的效果,使用extends关键字。
例4:接口之间的继承实现
interface IRunning1{
void run();
}
interface ISwimming1{
void swim();
}
interface ITwoKindOfAnimal extends ISwimming1,IRunning1{//一个接口继承了两个接口
void fly();
}
class Duck1 implements ITwoKindOfAnimal{
String name;
public Duck1(String name) {
this.name = name;
}
@Override
public void run() {
System.out.println(name+"正在用两条腿跑!");
}
@Override
public void swim() {
System.out.println(name+"正在水面游泳!");
}
@Override
public void fly() {
System.out.println(name+"正在用翅膀飞!");
}
}
public class TestDemo2 {
public static void swim(ISwimming1 iSwimming1){//单独的一个接口实现一种特性
iSwimming1.swim();
}
public static void run(IRunning1 iRunning1){//单独的一个接口实现一种特性
iRunning1.run();
}
public static void flyRunSwim(ITwoKindOfAnimal iTwoKindOfAnimal){//一个接口继承了两个接口,就可以同时实现三种特性
iTwoKindOfAnimal.fly();
iTwoKindOfAnimal.run();
iTwoKindOfAnimal.swim();
}
public static void main(String[] args) {
Duck1 duck1=new Duck1("鸭子");
System.out.println("同时实现三种特性");
System.out.println("================");
flyRunSwim(duck1);
System.out.println("================");
System.out.println("单独实现两种特性");
System.out.println("================");
swim(duck1);
run(duck1);
}
}
1.7 特殊的接口
1.7.1 Comparable接口实现compareTo方法
例5.Comparable接口实现compareTo方法
class Student implements Comparable<Student>{
private String name;
private int score;
private int age;
public Student(String name, int score, int age) {
this.name = name;
this.score = score;
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", score=" + score +
", age=" + age +
'}';
}
@Override
public int compareTo(Student o) {//重写compareTo方法实现排序
return this.age-o.age;//数组后面-数组前面,从小到大排序
// return o.age-this.age;从大到小排序
}
}
public class TestDemo3 {
public static void main(String[] args) {
Student[] students=new Student[]{
new Student("张三",95,15),
new Student("李四",92,20),
new Student("王五",94,18)
};
System.out.println("排序前:"+Arrays.toString(students));
Arrays.sort(students);
System.out.println("排序后:"+Arrays.toString(students));
}
}
按年龄排序: 重写了compareTo方法
也可以按分数,姓名(string类自带的compareTo方法比较)重写
注意: 这里的comparaTo的方法在类中已经写死了,如果我们要在之前的基础上继续按分数或者姓名排序,就要重写comparaTo方法,但是该类中已经重写了,而且只能重写一次,所以就限制了,故我们要灵活实现在原有的基础排序上继续进行比较或者排序,我们就要提供一种比较器comparator。
1.7.2 Comparator接口实现compare方法
例6:comparator接口实现compare方法
class Student{
public String name;
public int score;
public int age;
public Student(String name, int score, int age) {
this.name = name;
this.score = score;
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", score=" + score +
", age=" + age +
'}';
}
}
class AgeComparator implements Comparator<Student> {//实现年龄比较
@Override
public int compare(Student o1, Student o2) {
return o1.age-o2.age;
}
}
class ScoreComparator implements Comparator<Student>{//实现分数比较
@Override
public int compare(Student o1, Student o2) {
return o1.score-o2.score;
}
}
class NameComparator implements Comparator<Student>{//实现姓名比较
@Override
public int compare(Student o1, Student o2) {
return o1.name.compareTo(o2.name);//string自己类的compareTo,比较string类的大小
}
}
public class TestDemo4 {
public static void main(String[] args) {
Student[] students=new Student[]{
new Student("zhangsan",95,15),
new Student("lisi",92,20),
new Student("wangwu",94,18)
};
AgeComparator ageComparator=new AgeComparator();//年龄比较器
ScoreComparator scoreComparator=new ScoreComparator();//分数比较器
NameComparator nameComparator=new NameComparator();//姓名比较器
System.out.println("排序前:"+ Arrays.toString(students));
Arrays.sort(students,ageComparator);
System.out.println("按age排序:"+Arrays.toString(students));
Arrays.sort(students,scoreComparator);
System.out.println("按score排序:"+Arrays.toString(students));
Arrays.sort(students,nameComparator);
System.out.println("按name排序:"+Arrays.toString(students));
}
}
分别按年龄,分数,姓名比较,比较器comparator接口更灵活。
1.7.3 Clonable接口实现深浅拷贝
1.7.3.1 Clonable接口实现浅拷贝
例7:Clonable接口实现浅拷贝
class Money{
public double m=99.9;
}
class Person implements Cloneable{
public Money money=new Money();
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public class Test {
public static void main(String[] args)throws CloneNotSupportedException {
Person person1=new Person();
Person person2=(Person) person1.clone();
System.out.println("Person2修改前");
System.out.println(person1.money.m);
System.out.println(person2.money.m);
System.out.println("Person2修改后");
person2.money.m=100.0;
System.out.println(person1.money.m);
System.out.println(person2.money.m);
}
}
如上述代码,通过clone,我们只是拷贝了Person对象,并,没有拷贝Person中的Money对象,所以当通过person2对象这个引用修改了m的值后,person1这个引用访问m的时候,值也发生了改变,所以也就是浅拷贝。
1.7.3.2 Clonable接口实现深拷贝
例8:Clonable接口实现深拷贝
class Money implements Cloneable{//要克隆money,Money这个类也必须要关联Cloneable接口
public double m=99.9;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
class Person implements Cloneable{
public Money money=new Money();
@Override
protected Object clone() throws CloneNotSupportedException {
Person tmp=(Person) super.clone();
tmp.money=(Money) this.money.clone();//克隆Peson里面的money
return tmp;
}
}
public class Test {
public static void main(String[] args)throws CloneNotSupportedException {
Person person1=new Person();
Person person2=(Person) person1.clone();//把返回值tmp这个引用给了person2
System.out.println("Person2修改前");
System.out.println(person1.money.m);
System.out.println(person2.money.m);
System.out.println("Person2修改后");
person2.money.m=100.0;
System.out.println(person1.money.m);
System.out.println(person2.money.m);
}
}
1.8 接口和抽象类的区别
1.8.1 相同点
1.抽象类和接口都不能直接实例化对象的。
2.抽象类和接口都不能被private修饰。
3.抽象类和接口的抽象方法都是不能在自己的内部具体实现的,即没有{…}主体部分,需要一个类来继承抽象类和一个类来关联该接口来具体实现各自内部的抽象方法。
4.当一个类继承抽象类和一个类关联该接口后,在各自类的内部都需要重写重写所有的抽象方法,除非把一个类定义成抽象类。
5.由于抽象方法要被重写,故抽象类,接口内部的抽象方法不能被final和static修饰。
6.抽象类和接口有静态方法(有{…}主体)jdk8之后。
1.8.2 不同点
1.抽象类中可以包含普通方法和普通变量或者静态变量,这样的方法和字段可以被子类继承后直接使用,不用重写。但是接口中不能有普通方法,只能是抽象方法默认被 public abstract 修饰,且子类必须重写所有的抽象方法。接口中也有默认的default方法。(jdk8之后)
2.抽象类中可以有构造方法,实例代码块,静态代码块,而接口中没有。
3.接口中的成员变量被隐式指定为public static final修饰,抽象类中的变量没有被指定。
4.一个类只能继承一个抽象类,但是一个类可以实现多个接口,接口也能继承多个接口。