抽象类与普通类相比最大的优势在于:可以实现对于子类覆写方法的控制,但是在抽象类里面依然会保留一些普通方法,而普通方法里面可能会涉及到一些安全或者隐私的操作问题,那么这样在进行开发的过程之中,如果想要对外部隐藏全部的实现细节,则就可以通过接口来进行描述。
接口可以理解为一个纯粹的抽象类(最原始的定义接口之中只包含抽象方法与全局常量),由于JDK1.8之后引入了Lambda表达式的概念,所以接口的定义也得到了加强,除了抽象方法与全局常量之外,还可以定义普通方法和静态方法。如果从设计本身的角度来讲,接口之中的组成还是应该以抽象方法和全局常量为主
//由于类名称与接口名称的定义要求相同,所以为了区分接口往往在接口名称前加上I
interface IMessage { //定义一个接口
public static final String INFO = "www.jmzhang.com"; //全局常量
public abstract String getInfo() ; //抽象方法
}
public class JavaDemo {
public static void main(String args[]) {
}
}
但是上面的程序存在很明显的问题,此时的接口肯定无法直接产生实例化对象:
//由于类名称与接口名称的定义要求相同,所以为了区分接口往往在接口名称前加上I
interface IMessage { //定义一个接口
public static final String INFO = "www.jmzhang.com"; //全局常量
public abstract String getInfo() ; //抽象方法
}
class MessageImpl implements IMessage { //实现了接口
public String getInfo() {
return "得到一条秘密消息,有人胖了"
}
}
public class JavaDemo {
public static void main(String args[]) {
IMessage msg = new MessageImpl() ;
System.out.println(msg.getInfo()) ;
System.out.println(IMessage.INFO) ; //直接输出全局变量。
}
}
java里面之所以使用接口主要的目的是一个子类可以实现多个接口, 也就说可以利用接口实现多继承的功能。
//由于类名称与接口名称的定义要求相同,所以为了区分接口往往在接口名称前加上I
interface IMessage { //定义一个接口
public static final String INFO = "www.jmzhang.com"; //全局常量
public abstract String getInfo() ; //抽象方法
}
interface IChannel {
public abstract boolean connect() ; //定义抽象方法
}
class MessageImpl implements IMessage,IChannel { //实现了接口
public String getInfo() {
if(this.connect()) {
return "得到一条秘密消息,有人胖了" ;
}
return "通道建立失败,无法获取消息" ;
}
public boolean connect() {
System.out.println("消息发送通道已经成功建立。") ;
return true;
}
}
public class JavaDemo {
public static void main(String args[]) {
IMessage msg = new MessageImpl() ;
System.out.println(msg.getInfo()) ;
}
}
但是这个时候就需要考虑一个实际的情况了,关于对象的转型问题了。
此时的MessageImpl子类的对象可以任意的实现父接口的转换。
范例:观察转换
public class JavaDemo {
public static void main(String args[]) {
IMessage msg = new MessageImpl() ;
IChannel chl = (IChannel) msg ;
System.out.println(chl.connect()) ;
}
}
由于MessageImpl子类实现了IMessage 与IChannel两个接口,所以这个子类可以是这两个接口任意一个接口的实例,那么就表示此时这两个接口实例之间是可以转换的。
在java程序里面接口是不允许去继承父类的,所以接口绝对不会是Object的子类, 但是根据之前的分析,IMessageImpl是Object的子类,所以接口一定可以通过Object接收。 Object类可以接收所有的引用类型。
public class JavaDemo {
public static void main(String args[]) {
IMessage msg = new MessageImpl() ;
Object obj = msg ; //向上转型
IChannel chl = (IChannel) msg ;
System.out.println(chl.connect()) ;
}
}
Object 类对象可以接收所有的数据类型,包括基本数据类型、类对象、接口对象、数组。
由于接口描述的是一个公共的定义标准,所以在接口之中的所有抽象方法的访问权限都是public,写又不写都是一样的。
实现接口的可以是抽象类,一个抽象类可以实现多个接口,而一个普通类只能够继承一个抽象类并且可以实现多个父接口,但是要求先继承后实现。
范例: 子类继承抽象类并且实现接口
//由于类名称与接口名称的定义要求相同,所以为了区分接口往往在接口名称前加上I
interface IMessage { //定义一个接口
public static final String INFO = "www.jmzhang.com"; //全局常量
public abstract String getInfo() ; //抽象方法
}
interface IChannel {
public abstract boolean connect() ; //定义抽象方法
}
abstract class DatabaseAbstract {
public abstract boolean getDatabaseConnection();
}
class MessageImpl extends DatabaseAbstract implements IMessage,IChannel { //实现了接口
public String getInfo() {
if(this.connect()) {
if(this.getDatabaseConnection()){
return "得到一条秘密消息,有人胖了" ;
}
else {
return "数据库消息无法访问。"
}
}
return "通道建立失败,无法获取消息" ;
}
public boolean connect() {
System.out.println("消息发送通道已经成功建立。") ;
return true;
}
public boolean getDatabaseConnection() {
return true;
}
}
public class JavaDemo {
public static void main(String args[]) {
IMessage msg = new MessageImpl() ;
System.out.println(msg.getInfo()) ;
}
}
虽然接口无法去继承一个父类,但是一个接口却可以通过extends 继承若干个父接口, 此时称为接口的多继承。
//由于类名称与接口名称的定义要求相同,所以为了区分接口往往在接口名称前加上I
interface IMessage { //定义一个接口
public static final String INFO = "www.jmzhang.com"; //全局常量
public abstract String getInfo() ; //抽象方法
}
interface IChannel {
public abstract boolean connect() ; //定义抽象方法
}
interface IService extends IMessage,IChannel {
public String service() ;
}
class MessageService implements IService {
public String getInfo() {
return "lalala" ;
}
public boolean connect() {
return true ;
}
public String service() {
return "获取消息服务。" ;
}
}
public class JavaDemo {
public static void main(String args[]) {
IService msg = new MessageService() ;
System.out.println(msg.getInfo()) ;
}
}
在实际的开发当中,接口的使用往往有三种形式:
接口由抽象方法与全局常量所组成,但是如果项目设计不当,可能出现一种非常严重的问题。
一直强调该操作属于接口结构设计不当的结果,那么在最初的时候任何人都不能保证接口设计的足够完善,在这种情况下,为了方便子类的修改,往往不会让子类直接实现接口,而是在中间追加一个过渡的抽象类。
接口里面还可以定义static方法,而static方法就可通接口直接调用
//由于类名称与接口名称的定义要求相同,所以为了区分接口往往在接口名称前加上I
interface IMessage { //定义一个接口
public abstract String message() ; //抽象方法
public default boolean connect() { //方法是一个公共方法,都具备
System.out.println("建立消息的发送通道") ;
return true ;
}
public static IMessage getInstance() {
return new MessageImpl() ;
}
}
class MessageImpl implements IMessage {
public String message() {
if (this.connect()) {
return "www.jmzhang.com";
}
return "没有消息发送。" ;
}
}
public class JavaDemo {
public static void main(String args[]) {
IMessage msg = IMessage.getInstance() ;
System.out.println(msg.message()) ;
}
}
如果现在真的可以在接口里面定义普通方法或static方法,那么这个功能就已经可以取代抽象类,但是这些方法都属于挽救原则,也并不应该将该方法作为接口的主要设计原则。接口设计应该奉行:接口就是抽象方法。
对于接口而言在开发之中最为重要的应用就是进行标准的制定。
interface IUSB {
public boolean check() ; //检查通过可以工作
public void work() ;
}
class Computer {
public void plugin(IUSB usb) {
if(usb.check()) {
usb.work() ; //开始工作
}
else {
System.out.println("硬件设备出现异常,无法使用。") ;
}
}
}
class Keyboard implements IUSB {
public boolean check() {
return true ;
}
public void work() {
System.out.println("开始进行码字任务。") ;
}
}
class Print implements IUSB {
public boolean check() {
return false ;
}
public void work() {
System.out.println("开始进行照片打印。") ;
}
}
public class JavaDemo {
public static void main(String args[]) {
Computer com = new Computer() ;
com.plugin(new Keyboard()) ;
com.plugin(new Print()) ;
}
}
对接口而言,已经可以明确的必须有子类,并且子类可以通过对象的向上转型来获取接口的实例化对象。但是在进行对象的实例化过程之中也可能存在有设计问题。
interface IFood {
public void eat() ;
}
class Bread implements IFood {
public void eat() {
System.out.println("吃面包!") .
}
}
public class JavaDemo {
public static void main(String args[]) {
IFood bread = new Bread() ;
bread.eat();
}
}
在本程序之中,根据接口进行子类的定义,并且利用对象的向上转型进行接口对象的实例化处理。 此时的程序结构如下:
客户端需要明确的知道具体的哪一个子类,如果说现在面包吃腻了,需要牛奶了,那么客户端就要做出修改。
interface IFood {
public void eat() ;
}
class Bread implements IFood {
public void eat() {
System.out.println("吃面包!") .
}
}
class Milk implements IFood {
public void eat() {
System.out.println("喝牛奶!") .
}
}
public class JavaDemo {
public static void main(String args[]) {
IFood bread = new Milk() ;
bread.eat();
}
}
所以此时的程序出现有耦合的问题,而造成耦合最直接的元凶:“关键字new”。 以JVM的设计为例,java实现可移植性的关键在于:JVM,而JVM的核心原理:利用一个虚拟机运行Java程序,所有的程序并不与具体的操作系统有任何关联,而是由JVM来进行匹配。所以良好的设计应该避免耦合。
范例: 工厂设计模式
interface IFood {
public void eat() ;
}
class Bread implements IFood {
public void eat() {
System.out.println("吃面包!") .
}
}
class Milk implements IFood {
public void eat() {
System.out.println("喝牛奶!") .
}
}
class Factory {
public static IFood getInstance(String className) {
if("bread".equals(className)){
return new Bread() ;
}
else if ("milk".equals(className)) {
return new Milk() ;
}
else {
return null ;
}
}
}
public class JavaDemo {
public static void main(String args[]) {
IFood food = Factory.getInstance(args[0]) ;
food.eat() ;
}
}
在上面的程序中,客户端程序类与IFood接口的子类没有任何的关联,所有的关联都是通过Factory类完成的,而在我们程序运行的时候可以通过初始化参数进行要使用的子类的定义。
如果日后进行子类扩充只需要修改Factory程序类即可实现。
代理设计模式可以帮助用户将所有的开发注意力只集中在核心业务功能的处理上,
代理业务提供有两个子类,其中一个类是真实业务操作类,另外一个主题是代理业务操作类,没有代理业务操作,真是业务无法进行。
接口与抽象类都可以使用的情况下一定要优先使用接口,因为接口可以避免单继承的局限。
interface IClassName {
public String getClassName() ;
}
class Company implements IClassName {
public String getClassName() {
return "Company" ;
}
}
public class JavaDemo {
public static void main(String args[]) {
IClassName company = new Company() ;
System.out.println(company.getClassName()) ;
}
}
interface IGraphical {
public void paint() ;
}
class Point {
private double x ;
private double y ;
public Point(double x, double y) {
this.x = x ;
this.y = y ;
}
public double getX() {
return this.x ;
}
public double getY() {
return this.y ;
}
}
class Triangle implements IGraphical {
private Point [] x ;
private Point [] y ;
private Point [] z ;
public Triangle(Point [] x, Point [] y, Point [] z) {
this.x = x ;
this.y = y ;
this.z = z ;
}
public void paint() {
System.out.println("绘制第一条边,开始坐标:[" + this.x[0].getX() + "," + this.x[0].getY() + "]" + ",结束坐标:[" + this.x[1].getX() + "," + this.x[1].getY() + "]" ) ;
System.out.println("绘制第二条边,开始坐标:[" + this.y[0].getX() + "," + this.y[0].getY() + "]" + ",结束坐标:[" + this.y[1].getX() + "," + this.y[1].getY() + "]" ) ;
System.out.println("绘制第三条边,开始坐标:[" + this.z[0].getX() + "," + this.z[0].getY() + "]" + ",结束坐标:[" + this.z[1].getX() + "," + this.z[1].getY() + "]" ) ;
}
}
class Circular implements IGraphical {
private double radius ;
public Circular(double radius) {
this.radius = radius ;
}
public void paint() {
System.out.println("以半径" + this.radius + "绘制圆形。") ;
}
}
class Factory {
public static IGraphical getInstance(String className, double ... args) {
if("triangle".equalsIgnoreCase(className)) {
return new Triangle(new Point[] {
new Point(args[0], args[1]), new Point(args[2], args[3])
},new Point[] {
new Point(args[4], args[5]), new Point(args[6], args[7])
},new Point[] {
new Point(args[8], args[9]), new Point(args[10], args[11])
})
}else if("circular".equalsIgnoreCase(className)) {
return new Circular(args[0]) ;
}
else {
return null ;
}
}
}
public class JavaDemo {
public static void main(String agrs[]) {
IGraphical iga = Factory.getInstance("trangle") ;
iga.paint() ;
IGraphical igb = Factory.getInstance("circluar", 11) ;
igb.paint() ;
}
}
abstract class AbstractShape {
public abstract double area() ;
public abstract double perimeter() ;
}
class Circular extends AbstactShape {
private double radius ;
public Circular(double radius) {
this.radius = radius ;
}
public double area() {
return 3.1415926 * this.radius * this.radius ;
}
public double perimeter() {
return 2 * 3.1415926 * this.radius ;
}
}