对应《疯狂Java讲义(第5版)》6.5-6.6 章节
抽象类(abstract) 从多个类中抽象出来的模板,避免子类设计的随意性。
某种情况下,父类只能知道子类应该具备一个怎样的方法,但是不能够明确知道如何实现该方法,只能在子类中才能确定如何去实现方法体。例如:所有几何图形都应该具备一个计算面积的方法,但是不同的几何图形计算面积的方式不同。
抽象方法 (abstract修饰)没有方法主体,表明这个方法必须由子类重写实现。
public abstract void test( ); 后面无{ } public void test( ){ } 已经定义方法体,只是方法体为空,不可使用abstract修饰
Java语法规定,包含抽象方法的类只能定义为抽象类 。其中,包含抽象方法指:
直接定义一个抽象方法 继承抽象类或接口,但没有完全实现全部抽象方法
抽象类无法实例化 ,无法使用new创建抽象类的实例,抽象类只能被继承,其构造器只能在创建子类实例时通过子类调用。
抽象类普通方法可依赖于抽象方法,而抽象方法推迟到子类中实现。
Shape.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 public abstract class Shape { { System.out.println("执行Shape的初始化块..." ); } private String color; public abstract double calPerimeter () ; public abstract String getType () ; public Shape () {} public Shape (String color) { System.out.println("执行Shape的构造器..." ); this .color = color; } public void setColor (String color) { this .color = color; } public String getColor () { return this .color; } }
Triangle.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 public class Triangle extends Shape { private double a; private double b; private double c; public Triangle (String color , double a , double b , double c) { super (color); this .setSides(a , b , c); } public void setSides (double a , double b , double c) { if (a >= b + c || b >= a + c || c >= a + b) { System.out.println("三角形两边之和必须大于第三边" ); return ; } this .a = a; this .b = b; this .c = c; } public double calPerimeter () { return a + b + c; } public String getType () { return "三角形" ; } }
Circle.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 public class Circle extends Shape { private double radius; public Circle (String color , double radius) { super (color); this .radius = radius; } public void setRadius (double radius) { this .radius = radius; } public double calPerimeter () { return 2 * Math.PI * radius; } public String getType () { return getColor() + "圆形" ; } public static void main (String[] args) { Shape s1 = new Triangle ("黑色" , 3 , 4 , 5 ); Shape s2 = new Circle ("黄色" , 3 ); System.out.println(s1.getType()); System.out.println(s1.calPerimeter()); System.out.println(s2.getType()); System.out.println(s2.calPerimeter()); } }
Shape类型引用变量可直接指向Triangle和Circle类型的对象,调用相应方法,无需转换为子类类型,更好发挥多态 的优势。
接口(interface) 定义多个类应遵守的规范,规定这批类必须提供某些方法,而不关心实现细节 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 [public ] interface 接口名 extends 父接口1 , 父接口2. .. { 成员变量(静态常量)定义 抽象方法(普通方法)定义 内部类、接口、枚举定义 私有方法、默认方法或类方法定义(Java9新增) }
修饰符只能为public
或省略
(包权限访问);
接口只能继承接口,不能继承类,可有多个父接口,获得所有抽象方法、常量;
一个Java源文件只能有一个Public接口,且文件名与接口名相同;
接口可以用于声明引用变量类型(可引用接口实现类的对象),或通过接口直接调用常量(均为public static final)。
Output.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 public interface Output { int MAX_CACHE_LINE = 50 ; void out () ; void getData (String msg) ; default void print (String... msgs) { for (String msg : msgs) { System.out.println(msg); } } default void test () { System.out.println("默认的test()方法" ); } static String staticTest () { return "接口里的类方法" ; } private void foo () { System.out.println("foo私有方法" ); } private static void bar () { System.out.println("bar私有静态方法" ); } }
默认方法 default,自动添加public,只能由接口的实现类的实例 调用默认方法,相当于有方法体的实例方法 类方法 static,自动添加public,可以由接口直接调用 私有方法 private,私有类方法 private static ,用于实现隐藏的工具方法,为默认方法或类方法提供支持 接口被类实现(implements) 1 2 3 4 [修饰符] class 类名 extends 父类 implements 接口1 , 接口2. .. { }
类必须实现接口定义的全部抽象方法 ,否则将保留抽象方法,且必须定义为抽象类 ;
实现接口方法,必须使用public
控制符,权限只能更大(接口抽象方法默认public abstract);
可同时实现多个接口,弥补单继承的不足。
Printer.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 interface Product { int getProduceTime () ; }public class Printer implements Output , Product { private String[] printData = new String [MAX_CACHE_LINE]; private int dataNum = 0 ; public void out () { while (dataNum > 0 ) { 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; } } public int getProduceTime () { return 45 ; } public static void main (String[] args) { Output o = new Printer (); o.getData("轻量级Java EE企业应用实战" ); o.getData("疯狂Java讲义" ); o.out(); o.getData("疯狂Android讲义" ); o.getData("疯狂Ajax讲义" ); o.out(); o.print("孙悟空" , "猪八戒" , "白骨精" ); o.test(); Product p = new Printer (); System.out.println(p.getProduceTime()); Object obj = p; } }
面向接口编程 简单工厂模式 具体场景:Computer类需要组合一个输出设备,如前文提到的Printer类,在系统重构时,需要用BetterPrinter代替Printer。
对于如下Computer,完全与Printer类分离,只与Output接口耦合,且不负责创建Output对象。
Computer.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 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(); } }
提供Output工厂负责生成Output对象,改变getOutput()
方法即可更改Output具体的实现类。
OutputFactory.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class OutputFactory { public Output getOutput () { return new BetterPrinter (); } public static void main (String[] args) { OutputFactory of = new OutputFactory (); Computer c = new Computer (of.getOutput()); c.keyIn("轻量级Java EE企业应用实战" ); c.keyIn("疯狂Java讲义" ); c.print(); } }
重构后的BetterPrinter,修改return new BetterPrinter()
即可运行新的BetterPrinter对象
BetterPrinter.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 public class BetterPrinter implements Output { private String[] printData = new String [MAX_CACHE_LINE * 2 ]; private int dataNum = 0 ; public void out () { while (dataNum > 0 ) { System.out.println("高速打印机正在打印:" + printData[0 ]); System.arraycopy(printData , 1 , printData, 0 , --dataNum); } } public void getData (String msg) { if (dataNum >= MAX_CACHE_LINE * 2 ) { System.out.println("输出队列已满,添加失败" ); } else { printData[dataNum++] = msg; } } }
命令模式 适用场景:某个方法需要完成某一行为,但具体实现无法确定,必须等到执行该方法时才可以确定
具体一点:某个方法需要遍历某个数组的数组元素,但无法确定在遍历时如何处理这些元素,需要在调用该方法时指定具体的处理行为
使用Command接口定义方法,封装处理行为,但具体行为尚未定义。
1 2 3 4 5 public interface Command { void process (int [] target) ; }
数组的处理类中,Command参数负责具体的处理行为。
1 2 3 4 5 6 7 public class ProcessArray { public void process (int [] target , Command cmd) { cmd.process(target); } }
传入Command对象,确定数组的处理行为。
1 2 3 4 5 6 7 8 9 10 11 12 13 public class CommandTest { public static void main (String[] args) { ProcessArray pa = new ProcessArray (); int [] target = {3 , -4 , 6 , 4 }; pa.process(target , new PrintCommand ()); System.out.println("------------------" ); pa.process(target , new SquareCommand ()); } }
两次不同的处理行为通过PrintCommand
和SquareCommand
类提供
1 2 3 4 5 6 7 8 9 10 public class PrintCommand implements Command { public void process (int [] target) { for (int tmp : target ) { System.out.println("迭代输出目标数组的元素:" + tmp); } } }
1 2 3 4 5 6 7 8 9 10 public class SquareCommand implements Command { public void process (int [] target) { for (int tmp : target ) { System.out.println("数组元素的平方是:" + tmp*tmp); } } }