Java接口

接口概念与特性

Java接口时一系列方法的声明,是一些特征方法的集合,一个接口只有方法的特征而没有方法的实现,因此这些方法可以在不同的地方被不同的类实现,而这些实现可以有不同的行为。

以下以Comparable接口为例,该接口有一个compareTo方法,接受一个Object对象,返回一个整型数值。(用于比较大小)

让一个类实现一个接口的步骤:

1)将类声明为实现给定的接口,使用implements关键字;

2)对接口中所有方法进行定义。

class A implements Comparable{
   public int ComparaTo(Object obj){
   ......
   }
}

接口的一些特性

1)接口中所有方法自动地属于public,因此,在接口声明方法中,不必提供关键字public。

2)接口不能包含实例域或静态方法,可以包含常量,常量自动地属于public static final。

3)实现接口时,必须将方法声明为public,与1)比较,该处为实现时。

public int compareTo(Object obj){...}

4)接口不是类,不能实例化接口;可以声明接口的变量,但该变量必须引用实现了该接口的类对象。

c = new Comparable(...)  //错误
Comparable c;//正确
c = new Employee(...)  //若Employee类实现了Comparable接口,此语句正确,否则错误

5)接口允许被拓展,使用extends关键字可以将接口拓展成另一个接口。

public interface A{...}
public interface B extends A{...}    //此时,接口B将包含A的方法和它自己定义的方法

6)尽管每个类只能有一个超类,但却可以实现多个接口;使用逗号分割开多个接口。

class A implements Comparable, Cloneable

一个实例:Employee类实现Comparable接口,使得能够使用Array.sort方法对工资排序。

 1 package interfaces;
 2 
 3 public class Employee implements Comparable<Employee> {
 4     private String name;
 5     private double salary;
 6 
 7     public Employee(String name, double salary){
 8         this.name = name;
 9         this.salary = salary;
10     }
11 
12     public String getName() {
13         return name;
14     }
15 
16     public double getSalary() {
17         return salary;
18     }
19 
20     public void raiseSalary(double byPercent){
21         double raise = salary * byPercent/100;
22         salary += raise;
23     }
24 
25     public int compareTo(Employee other){
26         return Double.compare(salary,other.salary);
27     }
28 }
Employee.java
 1 package interfaces;
 2 
 3 import java.util.Arrays;
 4 
 5 public class EmployeeSortTest {
 6     public static void main(String[] args){
 7         Employee[] staff = new Employee[3];
 8         staff[0] = new Employee("Harry",30000);
 9         staff[1] = new Employee("Tony", 52000);
10         staff[2] = new Employee("Tom", 36000);
11 
12         Arrays.sort(staff);
13 
14         for (Employee e:staff){
15             System.out.println("name="+e.getName()+",salary="+e.getSalary());
16         }
17     }
18 }
EmployeeSortTest.java
1 name=Harry,salary=30000.0
2 name=Tom,salary=36000.0
3 name=Tony,salary=52000.0
结果

接口与抽象类

在c++中,一个类允许有多个超类,该特性称为多继承,而Java不支持多继承。因而,使用抽象类作为通用属性就存在这样的问题:当一个类已经继承于一个类,就不能再拓展于第二个类了。接口解决了这个问题,因为一个类可以实现多个接口。

以下为抽象类和接口的区别与相似性:

          抽象类            接口
                      不能被实例化
        被子类继承           被子类实现
       包含或不包含抽象方法 包含或不包含抽象方法(java se 8后可以在接口中实现方法)
抽象方法必须被子类实现,否则子类也必须声明为抽象的       抽象方法必须被子类实现
   一个类只能有一个抽象(或普通)的超类       一个类可以实现多个接口
        表示该对象是什么       一般表示该对象能做什么
         abstract修饰         implements修饰

默认方法与冲突

可以为使用default关键字为接口方法提供一个默认的实现。

public interface A{
   default int func(){...}
}

这样,实现这个接口时可以选择不覆盖该方法。

如果一个接口中将一个方法定义为默认方法,然后又在超类或另一个接口中定义了同样的方法。这样就可能会出现命名冲突,Java解决办法

1)超类优先。如果一个超类提供了具体的方法,接口中的同名同参数的方法将被忽略。

2)接口冲突。若两个接口有同样的方法的实现,那么必须在子类中覆盖该方法。

接口与回调

回调时一种常见的程序设计模式。在这种模式中,可以指出某个特定事件发生时应该采取的动作。例如按下鼠标产生什么效果。下面是一个例子:

java.swing中有一个Timer类,可以使用它在到达指定时间间隔发出通告。创建一个对象实现java.awt.event包中的ActionListener接口。到达指定间隔时间发生一些事情。

 1 package timer;
 2 
 3 import javax.swing.*;
 4 import java.awt.*;
 5 import java.awt.event.ActionEvent;
 6 import java.awt.event.ActionListener;
 7 import java.util.Date;
 8 
 9 public class TimerTest {
10 
11     public static void main(String[] args) {
12         ActionListener listener = new TimePrinter();
13 
14         Timer t = new Timer(10000, listener);//定时器对象,传递对象
15         t.start();
16         JOptionPane.showMessageDialog(null, "quit program");//产生消息框
17         System.exit(0);
18     }
19 }
20 
21 class TimePrinter implements ActionListener{
22     public void actionPerformed(ActionEvent event){//实现事件响应方法
23         System.out.println("at the time, the time is "+ new Date());
24         Toolkit.getDefaultToolkit().beep();//发出铃响
25     }
26 }
示例

接口与深浅拷贝

浅拷贝:原变量和副本都是同一个对象的引用。

Employee e = new Employee();
Employee copy = e;

深拷贝:元变量和副本属于不同的引用,有相同的状态。

这里需要实现Cloneable接口,这个接口指示一个类提供clone方法。它是Object的一个protected方法。这说明不能直接调用这个方法。

设计为protected的原因:Object类对某个对象进行clone,它逐一地拷贝所有域。如果域是基本类型自然没有问题,如果域包含一个对象的引用,那么拷贝这个域将得到相同的引用。如此一来,拷贝对象仍然和原对象共享一些信息。

也就是说,如果默认clone方法不能满足需求(课变得而子对象),那么必须:

1)实现Cloneable接口

2)重新定义clone方法,指定public修饰符。

以下例子:拷贝一个Employee实例,修改日期

package clone;

import java.util.Date;
import java.util.GregorianCalendar;

public class Employee implements Cloneable {
    private String name;
    private double salary;
    private Date hireDay;

    public Employee(String name, double salary){
        this.name = name;
        this.salary = salary;
        hireDay = new Date();
    }

    public Employee clone() throws CloneNotSupportedException{//实现克隆方法
        Employee cloned = (Employee) super.clone();//此时name和hireDay是引用
        cloned.hireDay = (Date) hireDay.clone();//拷贝hireDay
        return cloned;
    }

    public void setHireday(int year, int month, int day){
        Date newHireDay = new GregorianCalendar(year,month-1,day).getTime();//格林威治时间
        hireDay.setTime(newHireDay.getTime());
    }

    public void raiseSalary(double byPercent){
        double raise = salary * byPercent/100;
        salary += raise;
    }

    public String toString(){
        return "Employee[name=" + name + ",salary=" + salary + ",hireDay=" + hireDay + "]";
    }
}
Emlpoyee.java
 1 package clone;
 2 
 3 public class CloneTest {
 4     public static void main(String[] args){
 5         try{
 6             Employee original = new Employee("John", 50000);
 7             original.setHireday(2000,1,1);
 8             Employee copy = original.clone();
 9             copy.setHireday(2002,12,31);
10             System.out.println("original="+original);
11             System.out.println("copy="+copy);
12         }catch (CloneNotSupportedException e){
13             e.printStackTrace();
14         }
15     }
16 }
cloneTest.java

结果:

original=Employee[name=John,salary=50000.0,hireDay=Sat Jan 01 00:00:00 CST 2000]
copy=Employee[name=John,salary=50000.0,hireDay=Tue Dec 31 00:00:00 CST 2002]

原文地址:https://www.cnblogs.com/lht-record/p/8372047.html