Java——接口与实现类

目录

  • 接口
  • 实现接口
  • 常用接口:java.util.Comparator
    • Arrays.sort(Object[] arr, Comparator c)
  • 常用接口:java.lang.Comparable
    • Arrays.sort(Object[] arr)
  • JDK1.8关于接口的新特性
  • 接口的回调与多态
  • 接口参数
  • abstract类与接口的比较
  • 总结

接口

使用关键字interface来定义一个接口。定义接口包含接口声明接口体

接口体:只有两种成员
(1)全局的静态的常量:public static final,而且这三个单词可以省略

(2)公共的抽象的方法(没有方法体):public abstract,而且这两个单词可以省略
(没有变量,没有普通方法)

interface Printable{
public static final int MAX=100;//等价写法:int MAX=100;  可以省略修饰符
public abstract void add(); //等价写法:void add();
}

接口的思想在于它可以要求某些类有相同名称的方法,但是方法的具体内容(方法体的内容)可以不同,即要求这些类实现接口,以保证这些类一定有接口中声明的方法(方法绑定)。除此之外,接口在要求一些类有相同名称的方法的同时,并不强迫这些类具有相同的父类。
(限定某些类,这些类不是同一个父类派生出来的,但是又希望他们有相同的行为特征)

实现接口

类实现接口:
在java语言中,接口由类通过关键字implements来实现,以便使用接口中的方法。实现多个接口用逗号隔开。

class A implements Printable,Addable
class A extends B implements Printable, Sleepable

重写接口中的方法:
1、一个非抽象类实现某个接口,那么这个类必须重写这个接口中的所有方法。
由于接口中的方法是public abstract ,所以类在重写接口方法时不仅要去掉abstract修饰符,而且方法的访问权限一定要显示地用public来修饰。

2、一个抽象类实现某个接口,既可以重写接口中的方法,也可以直接拥有接口中的方法。(抽象类不必重写接口中所有方法

interface Computable{
 final int MAX=100;
 void speak(String s);
 int f(int x);
 float g(float x,float y); 
}

abstract class A implements Computable{
 public int f(int x){
   int sum =0;
   for(int i=1;i<=x;i++){
     sum=sum+i;
     }
   return sum;
 }
}

接口的细节说明
程序可以用接口名访问接口中的常量,但是如果一个类实现了接口,那么该类可以直接在类体中使用该接口中的常量。

如果父类实现了某个接口,那么子类也就自然实现了该接口,子类不必在显式地使用关键字implements声明实现这个接口。

接口也可以通过关键字extends继承另一个接口,子接口将继承父接口中的全部方法和常量。

常用接口:java.util.Comparator

这个接口是代表Java中比较两个对象的大小标准,而且是一种“定制”比较的标准。

抽象方法:int compare(Object o1, Object o2)
这个接口中没有规定如何比较两个对象的大小,但是规定了:
如果你认为o1 大于 o2,就返回正整数表示
如果你认为o1 小于 o2,就返回负整数表示
如果你认为o1 等于 o2,就返回0表示

演示代码:

public class TestComparator {
	public static void main(String[] args) {
		Student s1 = new Student("杨洪强", 24, 89);
		Student s2 = new Student("苏海波", 23, 100);	
			
		//比较s1和s2的大小
		/*if(s1 > s2){//错误的  < >只用于基本数据类型
			System.out.println("s1 > s2");
		}*/
		
		AgeComparator c = new AgeComparator();
		if(c.compare(s1, s2) > 0){//如果比较s1,s2的结果是正整数,说明s1>s2
			System.out.println("s1 > s2的年龄");
		}else if(c.compare(s1, s2) <0){
			System.out.println("s1 < s2的年龄");
		}else{
			System.out.println("s1 = s2的年龄");
		}
		
		ScoreComparator s = new ScoreComparator();
		if(s.compare(s1, s2) > 0){//如果比较s1,s2的结果是正整数,说明s1>s2
			System.out.println("s1 > s2的成绩");
		}else if(s.compare(s1, s2) <0){
			System.out.println("s1 < s2的成绩");
		}else{
			System.out.println("s1 = s2的成绩");
		}		
	}
}
//实现Comparator接口,来定制两个学生比较的具体实现方式
//例如:按照年龄比较
class AgeComparator implements Comparator{
	@Override
	public int compare(Object o1, Object o2) {
		//(1)向下转型
		Student s1 = (Student) o1;
		Student s2 = (Student) o2;
		
		//(2)开始比较
		if(s1.getAge() > s2.getAge()){
			return 1;
		}else if(s1.getAge() < s2.getAge()){
			return -1;
		}
		return 0;
	}
	
}
//实现Comparator接口,来定制两个学生比较的具体实现方式
//例如:按照成绩比较
class ScoreComparator implements Comparator{
	@Override
	public int compare(Object o1, Object o2) {
		//(1)向下转型
		Student s1 = (Student) o1;
		Student s2 = (Student) o2;
		
		//(2)开始比较
		if(s1.getScore() > s2.getScore()){
			return 1;
		}else if(s1.getScore() < s2.getScore()){
			return -1;
		}
		return 0;
	}	
}
class Student{
	private String name;
	private int age;
	private int score;
	public Student(String name, int age, int score) {
		super();
		this.name = name;
		this.age = age;
		this.score = score;
	}
	public Student() {
		super();
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	public int getScore() {
		return score;
	}
	public void setScore(int score) {
		this.score = score;
	}
	@Override
	public String toString() {
		return "Student [name=" + name + ", age=" + age + ", score=" + score + "]";
	}
}

Arrays.sort(Object[] arr, Comparator c)

根据“指定比较器”产生的顺序对指定对象数组进行排序。数组中的所有元素都必须是通过“指定比较器”可相互比较的

public static void sort(Object[] a, Comparator c)
第一个形参:需要排序的数组,Object[]说明可以是任意类型的对象数组
第二个形参:比较器对象 Comparator接口不能创建对象,只能传入实现类对象

Student[] all = new Student[5];
		all[0] = s1;
		all[1] = s2;
		all[2] = new Student("张三",23,88);
		all[3] = new Student("李四",24,44);
		all[4] = new Student("王五",25,45);

 //按年龄比较
     	Arrays.sort(all, new AgeComparator());
		//按照成绩比较
		Arrays.sort(all, new ScoreComparator());		
		for (int i = 0; i < all.length; i++) {
			System.out.println(all[i]);
		}

常用接口:java.lang.Comparable

使比较类重写comparable接口,对象就可以调用比较方法进行大小比较。

java.util.Comparator:定制比较,定制顺序
int compare(Object o1, Object o2):
o1与o2比较,o1>o2,返回正整数
o1与o2比较,o1 o1与o2比较,o1=o2,返回0

java.lang.Comparable:自然比较,自然顺序
int compareTo(Object obj)
this与obj对象比较,this > obj,返回正整数
this与obj对象比较,this < obj,返回负整数
this与obj对象比较,this = obj,返回0

演示代码:

public class TestComparable {
	public static void main(String[] args) {
		Student s1 = new Student("杨洪强", 24, 89);
		Student s2 = new Student("苏海波", 23, 100);
		
		//按成绩比较
		if(s1.compareTo(s2)>0){
			System.out.println("s1 > s2成绩");
		}else if(s1.compareTo(s2)<0){
			System.out.println("s1 < s2成绩");
		}else{
			System.out.println("s1 = s2成绩");
		}
		
		//按年龄比较,只能再用定制比较,补充完成这个功能
	}
}
class Student implements Comparable{
	private String name;
	private int age;
	private int score;
	public Student(String name, int age, int score) {
		super();
		this.name = name;
		this.age = age;
		this.score = score;
	}
	public Student() {
		super();
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	public int getScore() {
		return score;
	}
	public void setScore(int score) {
		this.score = score;
	}
	@Override
	public String toString() {
		return "Student [name=" + name + ", age=" + age + ", score=" + score + "]";
	}
	@Override
	public int compareTo(Object obj) {
		//this与obj比较,this和obj都是学生对象
		Student other = (Student) obj;
		//例如:对于学生对象来说,最常用的是按成绩排名,那么我就可以把自然顺序定位成绩升序
	if(this.score > other.score){
			return 1;
		}else if(this.score < other.score){
			return -1;
		}
		return 0;		
		
		return this.score - other.score;
	}
}

Arrays.sort(Object[] arr)

根据元素的自然顺序对指定对象数组按升序进行排序。数组中的所有元素都必须实现 Comparable 接口。

public class TestArrays {
	public static void main(String[] args) {
		Student[] all = new Student[5];
		all[0] = new Student("杨洪强", 24, 89);
		all[1] = new Student("苏海波", 23, 100);
		all[2] = new Student("张三",23,88);
		all[3] = new Student("李四",24,44);
		all[4] = new Student("王五",25,45);
					
		Arrays.sort(all);//这里面排序过程中,调用了元素本身的compareTo()方法
		//因为元素本身是Student类型,它实现了java.lang.Comparable接口
		//本身就具备比较大小的能力,即拥有compareTo()方法		
		for (int i = 0; i < all.length; i++) {
			System.out.println(all[i]);
		}
	}
}

JDK1.8关于接口的新特性

1、Java8对接口进行了修改:

  • Java8之前,接口中的成员只有两种:
    (1)全局的静态的常量:public static final,可以省略
    (2)公共的抽象的方法:public abstract,可以省略

  • Java8之后,接口又增加了两种新成员:(可以有方法体)
    (3)静态方法:public static,不可以省略
    调用方式:接口名.静态方法(实参列表)
    (4)默认方法:public default,不可以省略
    接口中权限修饰符都是public ,且都可以省略不写

2、为什么Java8要允许接口中定义静态方法?
是因为JDK发展了一段时间后,发现类库中,多了很多这样的成组的API:
(1)Path接口和Paths工具类
(2)Collection接口和Collections工具类…
一般工具类中都是静态方法,这些静态方法,基本上都是为前面这个对应接口服务的。 这样的话,就会出现很多API,使得程序员的学习成本增加了,使用成本也增加了。

于是把这样的静态方法,直接挪到接口中定义就好了。减少了这样的工具类的出现。

3、为什么么Java8要允许接口中定义默认方法?
是因为有的时候,一个接口它的大多数实现类对接口的抽象方法的实现代码是一样,那么我们写好几次就太麻烦了。
即相当于,默认方法是原来的抽象方法有了一个默认实现。如果实现类的实现和接口一样,就不需要重写,如果不一样就重写即可。

4、冲突问题
(1)当一个类同时实现了两个甚至更多个接口时,这些个接口中出现了方法签名相同的默认方法时,
那么我们必须在这个实现类中,做出选择。
选择一:保留其中一个,放弃另一个
选择二:两者都不用,完全重写一个

(2)当一个类同时继承了父类,又实现了接口,那么当父类中出现了和接口中方法签名一样的方法时,怎么办?
那么我们怎么选择?
选择一:默认选择,编译器默认选择父类
选择二:改选保留接口的
选择三:完全自己重写

public class TestInterface18 {
	public static void main(String[] args) {
		MyInter.test();		
		Son s = new Son();
		s.test();
	}
}
interface MyInter{
	public static void test(){
		System.out.println("接口中的静态方法");
	}	
	void fun();	
	public default void method(){
		System.out.println("接口中的默认方法");
	}
}
class InterImpl1 implements MyInter{
	@Override
	public void fun() {
		System.out.println("aaaa");
	}	
}
class InterImpl2 implements MyInter{
	@Override
	public void fun() {
		System.out.println("aaaa");
	}	
}
class InterImpl3 implements MyInter{
	@Override
	public void fun() {
		System.out.println("bbbb");
	}
	//在类中,重写接口的默认方法是,default要去掉
	@Override
	public void method() {
		System.out.println("重写接口的默认方法");
	}	
}
interface A{
	public default  void test(){
		System.out.println("aaa");
	}
}
interface B{
	public default  void test(){
		System.out.println("bbb");
	}
}
class C implements A,B{
	//选择一:保留其中一个,放弃另一个
	//在类中,重写接口的默认方法是,default要去掉
/*	public void test(){
//		A.super.test();//保留A接口的实现
		B.super.test();//保留B接口的实现
	}*/
	
	public void test(){
		System.out.println("ccc");
	}
}
class Father{
	public void test(){
		System.out.println("ffff");
	}
}
interface D{
	public default void test(){
		System.out.println("dddd");
	}
}
class Son extends Father implements D{
	//选择一:默认选择,保留父类的
	//选择二:该选,保留干爹的
//	public void test(){
//		D.super.test();
//	}	
	//选择三:完全自己重写
	public void test(){
		System.out.println("ssss");
	}
}

接口的回调与多态

接口回调:可以把实现某一接口的类创建的对象的引用赋值给该接口声明的接口变量,那么该接口变量就可以调用被类实现的接口方法。(实际上,当接口变量调用被类实现的接口方法时,就是通知相应的对象调用这个方法,但这个接口变量是无法调用类中其他的非接口方法的,终点强调了这个接口的方法特性——体现类根据接口里的行为标准给出的具体行为

接口回调类似于向上转型对象调用子类的重写方法。

接口的思想在于它可以要求某些类有相同名称的方法,但方法的具体内容可以不同,即要求这些类实现接口,以保证这些类一定有接口中所声明的方法;接口在要求一些类有相同名称的方法的同时,并不强迫这些类具有相同的父类

接口多态:不同的类在实现同一个接口时可能具有不同的实现方式,那么接口变量在回调接口方法时就可能具有多种形态

interface  ShowMessage {
   void 显示商标(String s);
}
class TV implements ShowMessage {
   public void 显示商标(String s) {
      System.out.println(s);
   }
}
class PC implements ShowMessage {
   public void 显示商标(String s) { 
     System.out.println(s);
   }
}
public class Example6_2 {
   public static void main(String args[]) {
      ShowMessage sm;                  //声明接口变量
      sm=new TV();                     //接口变量中存放对象的引用
      sm.显示商标("长城牌电视机");      //接口回调。
      sm=new PC();                     //接口变量中存放对象的引用
      sm.显示商标("联想奔月5008PC机"); //接口回调
   } 
}

接口参数

如果一个方法的参数是接口类型,可以将任何实现该接口的类的实例引用传递给该接口参数,那么接口参数就可以回调类实现的接口方法。(和向上转型一样,不用重载多个方法)

interface SpeakHello {
    void speakHello();
}
class Chinese implements SpeakHello {
   public  void speakHello() {
       System.out.println("中国人习惯问候语:你好,吃饭了吗? ");
   }
}
class English implements SpeakHello {
    public  void speakHello() {
       System.out.println("英国人习惯问候语:你好,天气不错 ");
    }
}
class KindHello {
   public void lookHello(SpeakHello hello) { //接口类型参数
       hello.speakHello();                    //接口回调
   }
}
public class Example6_5 {
   public static void main(String args[]) {
       KindHello kindHello=new KindHello();
       kindHello.lookHello(new Chinese());
       kindHello.lookHello(new English());
   }
}

abstract类与接口的比较

1、abstract类和接口都可以有abstract方法。
2、接口中只可以有常量,不能有变量;而abstract类中既可以有常量,也可以有变量。
3、abstract类中也可以有非abstract方法,接口不可以。

abstract类除了提供重要的需要子类重写的abstract方法,也提供了子类可以继承的变量和非abstract方法。

如果某个问题需要使用继承才能更好地解决,例如,子类除了需要重写父类的abstract方法,还需要从父类继承一些变量或一些重要的非abstract方法,就可以考虑使用abstract类。如果不需要继承,只需要若干个类给出某些重要的abstract方法的实现细节,就可以考虑使用接口。

总结

可以将接口理解为类的继承(一种特殊的父类),无论是从形式上,还是使用方法上。

子类重写父类方法近似实现类“重写”接口的抽象方法。
子类向上转型近似实现类回调接口方法。

你可能感兴趣的:(javaSE,java,接口)