接口是从多个相似类中抽象出来的规范,接口不提供任何实现。从某个角度看,接口可被当成一个特殊类,因此Jva源文件里最多只能有一个public接口,如果一个Java源文件里定义了一个public接口,则该源文件的主文件名必须与该接口名相同。接口完全支持多继承,子接口扩展某个父接口,将会获得父接口里定义的所以抽象方法、常量。
[修饰符] interface 接口名 extends 父接口1, 父接口2...
{
零个到多个常量定义...
零个到多个抽象方法定义...
零个到多个私有方法、默认方法或类方法定义...
零个到多个内部类、接口、枚举定义...
}
修饰符可以是public或者省略,如果省略了public访问控制符,则默认采用包权限访问控制符,即只有在相同包结构下才可以访问该接口。一个接口可以有多个直接父接口,但接口只能继承接口,不能继承类。
由于接口定义的是一种规范,因此接口里不能包含构造器和初始化块定义。接口里可以包含成员变量(只能是静态常量)、方法(只能是抽象实例方法、类方法、默认方法或私有方法)、内部类(包括内部接口、枚举)。
接口中定义成员变量时,不管是否使用public static final修饰符,接口里的成员变量总是使用这三个修饰符来修饰。而且接口里没有构造器和初始化块,因此接口里定义的成员变量只能在定义时指定默认值。
// 系统自动为接口里定义的成员变量增加public static final修饰符
// 可以省略访问控制修饰符,因为接口里的常量只能是public
int MAX_SIZE = 50;
public static final int MAX_SIZE = 50;
如果不是定义默认方法、类方法或私有方法,系统将自动为普通方法增加abstract修饰符;定义接口里的普通方法时不管是否使用public abstract修饰符,接口里的普通方法总是使用public abstract修饰符。接口里的普通方法不能有方法实现(方法体)。
Java 9为接口增加了一种新的私有方法,私有方法的作用就是作为工具方法,为接口中的默认方法或类方法提供支持。私有方法可以拥有方法体,但私有方法不能使用default修饰。私有方法可以使用static修饰,也就是说,私有方法既可以是类方法,也可以是实例方法。private void test() {} 私有成员方法 或 private static void test() {} 私有类方法
必须有方法体。
在接口中定义默认方法,需要用default修饰,不能使用static修饰,且总是以public修饰,如果没有指定,系统会自动添加public default void test() {}
必须有方法体。
类方法使用static修饰,不能使用default修饰,无论程序是否指定,类方法总是public修饰,如果开发者没有指定public,系统会自动为类方法添加public修饰符,必须有方法体。
接口里定义的内部类、内部接口、内部枚举默认采用public static两个修饰符,不管定义时是否指定这两个修饰符,系统都会自动使用public static对它们进行修饰。
public interface Output
{
//接口里定义的成员变量只能是常量
int MAX_CACHE_LINE = 50;
//接口里定义的普通方法只能是public的抽象方法
void out();
void getData(String msg);
//在接口中定义默认方法,需要使用default修饰
default void print(String.. msg)
{
for(String msg:msgs)
System.out.println(msg);
}
//在接口中定义默认方法,需要使用default修饰
default void test()
{
System.out.println("默认的test方法");
}
//在接口中定义类方法,需要使用static修饰
static String staticTest()
{
return "接口里的类方法";
}
//定义私有方法
private void foo()
{
System.out.println("foo私有方法");
}
//定义私有静态方法
private static void bar()
{
System.out.println("bar私有静态方法");
}
}
接口里定义内部类默认使用public static修饰,也就是说接口内部类只能是静态内部类。如果为接口内部类指定访问控制符,则只能指定public访问控制符;如果定义接口内部类时省略访问控制符,则该内部类默认是public访问控制权限。
接口里可以定义内部接口,系统默认添加public static修饰符。如果定义接口里的内部接口时指定访问控制符,则只能使用public修饰符。
接口不能用于创建实例,但接口可以用于声明引用类型变量。当使用接口来声明引用类型变量时,这个引用类型变量必须引用到其实现类的对象。一个类实现了一个或多个接口之后,这个类必须完全实现这些接口里所定义的全部抽象方法;否则,该类将保留从父接口那里继承到的抽象方法,该类也必须定义成抽象类。
[修饰符] class 类名 extends 父类 implements 接口1, 接口2...
{
类体
}
实现接口方法时,必须使用public访问控制修饰符,因为接口里的方法都是public的,而子类(相当于实现类)重写父类方法时访问权限只能更大或者相等,所以实现类实现接口里的方法时只能使用public访问权限。
//定义一个Product接口
interface Product
{
int getProduceTime();
}
//让Printer类实现Output和Product接口
public class Printer implement Output, Product
{
private String[] printData = new String[MAX_CACHE_LINE];
//用以记录当前需打印的作业数
private int dataNum = 0;
//接口Output的抽象方法的实现
public void out()
{
//只要还有作业,就继续打印
System.out.println("打印机打印:"+printData[0]);
//把作业队列整体前移一位,并将剩下的作业数减一
System.arraycopy(printData,1,printData,0--dataNum);
}
public void getData(String msg)
{
if(dataNum >= MAX_CACHE_LINE)
System.out.println("输出队列已满");
else
printData[dataNum++] = msg;
}
//接口Product的抽象方法的实现
public int getProduceTime() {
return 45; }
public static void main(String[] args)
{
Output o = new Printer();
o.getData("helloworld");
o.getData("hello");
o.out();
o.getData("world");
o.getData("wow");
o.out();
o.print("孙","悟","空");
o.test();
Product p = new Printer();
System.out.println(p.getProduceTime());
//所有接口类型的引用变量都可直接赋给Object类型的变量
Object obj = p;
}
}
接口不能显式继承任何类,但所有接口类型的引用变量都可以直接赋给Object类型的引用变量。上面程序把Product类型的变量直接赋给Object类型变量,这是利用向上转型来实现的。因为编译器知道任何Java对象都必须是Object或其子类的实例,Product类型的对象也不例外(它必须是Product接口实现类的对象,该实现类肯定是Object的显式或隐式子类)。
Computer类可以组合一个Output,可以用不同类来替换,如Printer和BetterPrinter,将输出设备分离,只和Output接口耦合。
public class Computer
{
private Output out;
public Computer(Output out)
{
this.out = out;
}
//模拟获取字符串输入的方法
public void keyIn(String msg)
{
out.getData(msg);
}
//模拟打印的方法
public void print()
{
out.out();
}
}
Computer类不再负责创建Output对象,这里提供一个工厂类来生成Output对象。
public class OutputFactory
{
public Output getOutput()
{
return new Printer();
}
public static void main(String[] args)
{
OutputFactory of = new OutputFactory();
Computer c = new Computer(of.getOutput());
c.keyIn("Hello");
c.keyIn("World");
c.print();
}
}
某个方法需要完成某个行为,但这个行为具体实现无法确定,必须等到执行该方法时才能确定。对于这样的需要,必须把处理行为作为参数传入该方法。考虑使用一个Command接口来定义一个方法,用这个方法来封装处理行为。
public interface Command
{
//接口里定义的process方法用于封装处理行为
void process(int[] target);
}
// 两种实现
public interface PrintCommand implements Command
{
void process(int[] target){
for(int tmp:target)
System.out.println("迭代输出目标数组的元素:"+tmp);
}
}
public interface AddCommand implements Command
{
void process(int[] target){
int sum = 0;
for(int tmp:target)
sum += tmp;
System.out.println("迭代输出目标数组的元素:"+sum);
}
}
通过Command接口,实现让ProcessArray类和具体处理行为分离。
public class ProcessArray
{
public void process(int[] target, Command cmd)
{
cmd.process(target);
}
}
public class CommandTest
{
public static void main(String[] args)
{
ProcessArray pa = new ProcessArray();
int[] target = {
3, -4, 6, 4};
pa.process(target, new PrintCommand());
pa.process(target, new AddCommand());
}
}