接着前面的学习:
本文地址:http://www.cnblogs.com/archimedes/p/java-study-note8.html,转载请注明源地址。
生活中的接口:
什么是接口?
一个Java接口是一些方法特征的集合,但没有方法的实现。
在类中实现接口可以使用关键字implements,其基本格式如下:
[修饰符] class <类名> [extends 父类名] [implements 接口列表]{
}
修饰符:可选参数,用于指定类的访问权限,可选值为public、abstract和final。
类名:必选参数,用于指定类的名称,类名必须是合法的Java标识符。一般情况下,要求首字母大写。
extends 父类名:可选参数,用于指定要定义的类继承于哪个父类。当使用extends关键字时,父类名为必选参数。
implements 接口列表:可选参数,用于指定该类实现的是哪些接口。当使用implements关键字时,接口列表为必选参数。当接口列表中存在多个接口名时,各个接口名之间使用逗号分隔。
实现上面例子中的接口:
public interface PCI { //java接口,相当于主板上的PCI插槽的规范 public void start(); public void stop(); }
Java接口中定义的方法在不同的地方被实现,可以具有完全不同的行为:
//声卡、网卡都实现了PCI插槽的规范,但行为完全不同 class SoundCard implements PCI { public void start() { System.out.println("SoundCard start..."); } public void stop() { System.out.println("Sound stop!"); } } class NetworkCard implements PCI { public void start() { System.out.println("NetworkCard send..."); } public void stop() { System.out.println("Send stop!"); } }
可以使用Java接口标识类型。运行时,根据实际创建的对象类型调用相应的方法实现:
public class javatest { public static void main(String[] args) { PCI nc = new NetworkCard(); PCI sc = new SoundCard(); nc.start(); sc.start(); } }
运行结果:
NetworkCard send...
SoundCard start...
为什么需要Java接口?
例子:为学校各中心开发这样一个小系统,包含类型:教员、中心、打印机,具体要求如下:
1、教员、以及中心都具有方法:输出详细信息
2、中心具有属性:打印机,能够通过中心的打印机打印教员或中心的详细信息
3、系统要具备良好的可扩展性与可维护性
先看方案1:
public class Teacher { //输出教员的详细信息 public String detail() { return “I am a teacher!"; } } public class Printer { public void print(String content) { System.out.println("start printing:"); System.out.println(content); } } public class ggSchool { private Printer printer = new Printer(); //输出学校的详细信息 public String detail() { return “this is ggSchool"; } //使用打印机打印教员信息 public void print(Teacher t){ printer.print(t.detail()); } //使用打印机打印学院信息 public void print(ggSchool s){ printer.print(s.detail()); } }
那么,问题来了:
每增加一种新类型,都需要增加相应的print方法,程序的可扩展性及可维护性极差,这不符合系统的要求
先看方案2(使用接口):
教师、中心都存在一个共同的方法特征:detail,它们对detail方法有各自不同的实现——这完全符合Java接口的定义
代码如下:
public interface Introduceable { public String detail(); } public class Teacher implements Introduceable { //输出教员的详细信息 public String detail() { return “I am a teacher!"; } } public class ggSchool implements Introduceable { private Printer printer = new Printer(); //输出学校的详细信息 public String detail() { return “this is ggSchool"; } public void print(Introduceable intro) { //使用print方法时,参数可以是任何Introduceable接口的实现类的对象, //不必再为不同的类型建立不同的print方法了 printer.print(intro.detail()); } } public class Printer { public void print(String content) { System.out.println("start printing:"); System.out.println(content); } }
通过Java接口,我们同样可以享受到多态性的好处,大大提高了程序的可扩展性及可维护性
什么是面向接口编程?
开发系统时,主体构架使用接口,接口构成系统的骨架,这样就可以通过更换接口的实现类来更换系统的实现
升级上述的思远系统,要求:
打印机有多种类型,比如:黑白打印机、彩色打印机等
学院可能配备其中任意一款打印机,负责打印教员、或者学院的详细信息
系统要具备良好的可扩展性与可维护性
第一步:抽象出Java接口
1、分析:
黑白、彩色打印机都存在一个共同的方法特征:print
黑白、彩色打印机对print方法有各自不同的实现
2、结论:
抽象出Java接口PrinterFace,在其中定义方法print
3、具体实现:
public interface PrinterFace { public void print(String content); }
第二步:实现Java接口
1、分析:
已经抽象出Java接口PrinterFace,并在其中定义了print方法黑白、彩色打印机对print方法有各自不同的实现
2、结论:黑白、彩色打印机都实现PrinterFace接口,各自实现print方法
3、具体实现:
public class ColorPrinter implements PrinterFace { public void print(String content) { System.out.println("彩色打印:"); System.out.println(content); } } public class BlackPrinter implements PrinterFace { public void print(String content) { System.out.println("黑白打印:"); System.out.println(content); } }
第三步:使用Java接口
1、分析:主体构架使用接口, 让接口构成系统的骨架
2、结论:更换实现接口的类就可以更换系统的实现
3、具体实现:
public class ggSchool implements Introduceable{ private PrinterFace printer; //打印机 public void setPrinter(PrinterFace p) { this.printer = p; } public String detail() { return "this is ggSchool!"; } public void print(Introduceable intro){ printer.print(intro.detail()); } } public class Test { public static void main(String[] args) { // 创建学院实例 ggSchool school=new ggSchool(); //为该学院配备黑白打印机 school.setPrinter(new BlackPrinter()); school.print(school); //为该学院配备彩色打印机 school.setPrinter(new ColorPrinter()); school.print(school); } }
抽象类与接口
抽象类的子类必须覆盖所有的抽象方法后才能被实例化,否则这个子类还是个抽象类。
如果一个抽象类中的所有方法都是抽象的,就可以将这个类用另外一种方式来定义,也就是接口定义。
抽象方法只需声明,不需实现。
接口是抽象方法和常量值的定义的集合。
从本质上讲,接口是一种特殊的抽象类。这种抽象类中包含常量和方法的定义,而没有变量和方法的实现。例如
注意:在接口的定义中,所有的成员都是public访问类型的,而不论是否用public关键字修饰;接口里的变量都是用public static final标识的,所以,接口中定义的变量就是全局静
态常量。
我们可以定义一个新的接口,用extends关键字去继承一个已有的接口。注意:只能接口继承接口,类不能继承接口。
一个类只能用implements关键字去实现一个接口中的所有方法
一个类可以在继承一个父类的同时,实现一个或多个接口,extends关键字必须位于implements关键字之前,如我们可以这样定义:
class classA { //... } public interface Interface1{ //... } public interface Interface2{ //... } class classB extends classA implements Interface1 Interface2{ //... }
抽象类和接口在Java中的应用
例子:假设有若干 (如1000)个Circle,Rectangle以及若干个其他形状,希望计算它们的总面积,直截了当的做法是将它们分别放到多个数组中,分别循环求出各形状的面积,然后累加,这种做法是不漂亮的。如果还有其它形状:triangle,ellipses等,上述方法显得“累赘”。希望有一种统一的表示,例如用一个数组shape[],接受所有的形状,然后用:
for (i=0; i<shape.length; i++) area_total += shape[i].area();
用抽象类实现多种形状面积的累加:
首先看看Circle和Rectangle两个类,如何完成相关参数的计算 :
class Circle { public float r; Circle(float r) { this.r = r; } public float area() { return 3.14 * r * r; } } class Rectangle { public float width, height; Rectangle (float w, float h) { width = w; height = h; } public float area() { return width * height; } }
现在要利用抽象类实现多种形状面积的累加,确保每种形状分别用不同的方法来计算它们的面积和周长。因此,超类Shape包含抽象方法computeArea,然后在不同的子类中实现和覆盖这个方法,同时添加toString方法来显示几何形状的一些基本属性。现在声明了1000个Shape对象的数组,然后循环1000次随机产生1000个平面图形对象,形状为圆、矩形、正方形三种之一。
abstract class Shape { abstract float computeArea(); } class Circle extends Shape { public float r; public Circle(float r) { this.r = r; } public float computeArea() { return (float)3.14 * r * r; } } class Rectangle extends Shape { public float width, height; Rectangle (float w, float h) { width = w; //这里不需"this" height = h; } public float computeArea() { return width * height; } }
用接口实现多种形状面积的累加:
用接口的方式实现多种形状面积的累加,需要将用抽象类表示的Shape类改成接口。由于接口的语法定义要求,我们要把原来抽象类中的成员变量去掉,成员方法改成抽象方法computeArea(),该方法返回一个double类型。所以,这个接口定义为:
public interface Shape2 { public abstract double computeArea(); }
接口实现如下:
interface Shape2 { public double computeArea(); } class Circle2 implements Shape2 { protected double radius; public Circle2(double _radius) { radius = _radius; } public double computeArea() { return Math.PI * radius * radius; } } class Rect2 implements Shape2 { protected double width, height; public Rect2(double w, double h) { width = w; height = h; } public double computeArea() { return width * height; } }
用一个object数组实现多种形状面积的累加:
定义一个数组,它可以同时存储矩形、圆和正方形,每个Java类都是由Object扩展而来的。因此,所有的类都属于Object类型,我们可以创建一个Object类型的数组来存储任何类型的对象,也就可以存储矩形、圆和正方形对象
完整代码如下:
package javatest; import java.util.*; import java.io.*; interface Shape2 { public double computeArea(); } class Circle2 implements Shape2 { protected double radius; public Circle2(double _radius) { radius = _radius; } public double computeArea() { return Math.PI * radius * radius; } } class Rect2 implements Shape2 { protected double width, height; public Rect2(double w, double h) { width = w; height = h; } public double computeArea() { return width * height; } } public class javatest { public static void main(String args[ ]) { Shape2 s[] = { new Circle2(4), new Rect2(4, 4), new Circle2(10), new Rect2(20, 2), new Rect2(8, 10) }; double total = 0; for(int i = 0; i < s.length; i++) total = total + s[i].computeArea(); System.out.println("totalArea = " + (int)total); } }
参考资料
《java接口讲义》--siyuan学院
《java课程讲义》--东华大学计算机学院