Thinking in Java 好书全是干货
一、抽象类和抽象方法
抽象方法:这种方法只有声明而没有方法体,下面是抽象方法生命所采用的语法
abstract void f();
包含抽象方法的类叫做抽象类,如果一个类包含一个或多个抽象方法,该类必须被限定为抽象的,并且编译器不会允许直接创建一个抽象类对象,正确的做法是从这个抽象类继承并为抽象方法完成定义,这样就可以创建这个类的对象。但如果导出类还有抽象方法,那这个类还应该加上abstract声明为抽象类。简单的使用如下
package tij.interfacedemo;public class Test { public static void main(String[] args) { new Wind().play(Note.MIDDLE_A); }}enum Note{ MIDDLE_A,MIDDLE_B,MIDDLE_C;}abstract class Instrument{//抽象父类 private int i;// public abstract void play(Note n); public String what(){ return "Instrument"; } public abstract void adjust();}class Wind extends Instrument{ public void play(Note n){ System.out.println("Wind.play() "+n); } public String what(){ return "wind"; } public void adjust(){ }}
其实可以发现和普通继承没什么区别。
二、接口
接口(interface)是一个完全抽象的类,没有任何具体实现方法,允许创建者创建方法的方法名、参数列表以及返回类型,但没有任何方法体。接口体现的思想是:“实现了这个接口的类看起来都像这样”。并且接口具有继承的一系列特点,如向上转型等等。
接口可以加public关键字(如果要加也只能加public,不能加private和protected),不添加就是默认访问权限(包访问权限)。接口中的方法是自动是public abstract的。接口也可以包含域(即引用变量或基本变量),并且自动是static final的,并且也不能被private和protected修饰。如下例:package tij.interfacedemo;public class Test { public static void main(String[] args) { new Wind().play(Note.MIDDLE_A); }}enum Note{ MIDDLE_A,MIDDLE_B,MIDDLE_C;}interface Instrument{// int i=5;// void play(Note n); String what(); void adjust();}class Wind implements Instrument{ public void play(Note n){ System.out.println("Wind.play() "+n); } public String what(){ return "wind"; } public void adjust(){ }}
三、完全解耦
这是接口的一个很好的功能
在继承中,如果一个方法接受一个类的实例作为参数,那么你可以用这个类或子类的实例当做传入参数,如下例:package tij.interfacedemo;import java.util.Arrays;public class Test { static void process(Processor p, Object s) { System.out.println("Using Processor:" + p.name()); System.out.println(p.process(s)); } static String s = "Disagreement with beliefs is by definition incorrect"; public static void main(String[] args) { process(new Upcase(), s); process(new Downcase(), s); process(new Splitter(), s); }}class Processor { public String name() { return getClass().getSimpleName(); } Object process(Object input) { return input; }}class Upcase extends Processor { String process(Object input) { return ((String) input).toUpperCase(); }}class Downcase extends Processor { String process(Object input) { return ((String) input).toLowerCase(); }}class Splitter extends Processor { String process(Object input) { return Arrays.toString(((String) input).split(" ")); }}
在本例中,Test.process方法可以接受一个Processor以及其子类的实例对象,然后对一个Object的对象s进行操作,根据传入的Processor不同,进行的操作也不同,这种方法体现了策略设计模式,这类方法包含要执行的固定部分(s),策略包含变化的部分(p)。但是在这个例子中要注意两个与本章不相关的事儿:1.应该想想为什么子类明明没有重写name方法,但输出却还是像重写了一样。2.子类重写了process方法,但返回值不是Object而是String,重写方法必须要是与类方法的返回类型的相同或者是其子类。
但是假如现在我们发现了一系列滤波器类,如下:
class Waveform{//代表波形 private static long counter; private final long id=counter++; public String toString(){ return "Waveform"+id; }}class Filter{//滤波器 public String name(){ return getClass().getSimpleName(); } public Waveform process(Waveform input){ return input; }}class LowPass extends Filter{ double cutoff;//设定低通滤波器的滤波上限 public LowPass(double cutoff){ this.cutoff=cutoff; } public Waveform process(Waveform input){ return input; }}class HighPass extends Filter{ double cutoff;//设置高通滤波器的滤波下限 public HighPass(double cutoff){ this.cutoff=cutoff; } public Waveform process(Waveform input){ return input; }}class BandPass extends Filter{ double lowCutoff,highCutoff;//设置带通滤波器的滤波上下限 public BandPass(double lowCut,double highCut){ this.lowCutoff=lowCut; this.highCutoff=highCut; } public Waveform process(Waveform input){ return input; }}
那么如果想把各种滤波器传给Test.process方法,那这会被编译器阻止,因为process方法只接受processor类以及子类,那如果希望运用了策略设计模式的Test.process方法仍能接受滤波器,那么首先就需要把processor改变成接口:
interface Processor { String name() ; Object process(Object input) ;}abstract class StringProcessor implements Processor{ public String name(){ return getClass().getSimpleName(); }}class Upcase extends StringProcessor { public String process(Object input) { return ((String) input).toUpperCase(); }}class Downcase extends StringProcessor { public String process(Object input) { return ((String) input).toLowerCase(); }}class Splitter extends StringProcessor { public String process(Object input) { return Arrays.toString(((String) input).split(" ")); }}class Waveform{//代表波形 private static long counter; private final long id=counter++; public String toString(){ return "Waveform"+id; }}
但接下来我们发现,滤波器这个类是我们找到的,我们并不能对这么个类内的代码进行修改,如何让新的Test.process还能接受滤波器呢,于是我们可以采用适配器设计模式,代码如下:
class FilterAdapter implements Processor { Filter filter; public FilterAdapter(Filter filter) { this.filter = filter; } public String name() { return filter.name(); } public Waveform process(Object input) { return filter.process((Waveform) input); }}
这样传进去这个FilterAdapter适配器就OK了
四、Java中的多重继承
接口的另一个重要的功能就是能实现多重继承
package tij.interfacedemo;public class Test { public static void main(String[] args) { Hero superman=new Hero(); superman.fight(); superman.fly(); superman.swim(); }}class ActionCharacter { public void fight() { System.out.println("fight"); }}interface CanFight { void fight();}interface CanSwim { void swim();}interface CanFly { void fly();}class Hero extends ActionCharacter implements CanFight, CanSwim, CanFly { public void fly() { System.out.println("fly"); } public void swim() { System.out.println("swim"); }}
五、通过继承来拓展接口
通过继承可以生成一个新的接口,以此来对原来的接口进行拓展;还可以通过继承在新接口中组合多个接口(玩的真花= =)。如下:
interface Monster { void menace();}interface DangerousMonster extends Monster { void destroy();}interface Lethal { void kill();}class DragonZilla implements DangerousMonster { public void menace() {} public void destroy() {}}interface Vampire extends DangerousMonster, Lethal { void drinkBlood();}class VeryBadVampire implements Vampire { public void destroy() {} public void menace() {} public void kill() {} public void drinkBlood() {}}
接口之间可以继承,可以多继承,可以相互拓展
1.组合接口时的名字冲突
在实现多重继承时,如果不同接口有相同方法怎么办
interface I1 { void f();}interface I2 { int f(int i);}interface I3 { int f();}class C { public int f() { return 1; }}class C2 implements I1, I2 { public int f(int i) { return 1; } public void f() {}//重载}class C3 extends C implements I2{ public int f(int i) {//重载 return 0; }}class C4 extends C implements I3{ //可以不必重写int f()方法,因为从C那里继承过来了,但C那里的f()必须是public的}//class C5 extends C implements I1{// //错误//}//interface I4 extends I1,I3{// //错误//}
因此在设计接口的时候请尽量避免这点
六、适配接口
接口的一种常用的方法就是之前提到的策略设计模式,如编写一个进行某些操作的方法,而这个方法接收一些接口,就是说如果你的对象遵循我的接口那就能用。
下面的例子中使用了scanner,这个类需要接收一个readable对象,其中arg用来存储要输入的字符串的import java.io.IOException;import java.nio.CharBuffer;import java.util.Random;import java.util.Scanner;public class Test { public static void main(String[] args) { Scanner s = new Scanner(new RandomWords(10)); while (s.hasNext()) { System.out.println(s.next()); } }}class RandomWords implements Readable { private static Random rand = new Random(47); private static final char[] capitals = "ABCDEFGHIGKLMNOPQRST".toCharArray(); private static final char[] lowers = "ABCDEFGHIGKLMNOPQRST".toLowerCase() .toCharArray(); private static final char[] vowels = "aeiou".toCharArray(); private int count; public RandomWords(int count) { this.count = count; } public int read(CharBuffer arg) throws IOException { if (count-- == 0) { return -1; } arg.append(capitals[rand.nextInt(capitals.length)]); for (int i = 0; i < 4; i++) { arg.append(vowels[rand.nextInt(vowels.length)]); arg.append(lowers[rand.nextInt(lowers.length)]); } arg.append(" "); return 10;// Number of characters appended }}
接口的还有一个功能就是之前我们提到过的适配器设计模式,在这里再举另一个与scanner相关的例子。
首先假如我们现在有一个如下的类:class RandomDoubles{ private static Random rand =new Random(47); public double next(){ return rand.nextDouble(); }}
希望让他作为一个readable传入给scanner,来生成随机的double类型数,这需要装饰设计模式
import java.io.IOException;import java.nio.CharBuffer;import java.util.Random;import java.util.Scanner;public class Test { public static void main(String[] args) { Scanner s=new Scanner(new AdaptedRandomDoubles(7)); while(s.hasNext()){ System.out.println(s.next()); } }}class AdaptedRandomDoubles extends RandomDoubles implements Readable { private int count; public AdaptedRandomDoubles(int count){ this.count=count; } public int read(CharBuffer cb) throws IOException { if(count--==0){ return -1; } String result=Double.toString(this.next()); cb.append(result); return result.length(); }}
七、接口中的域
实例变量都是static final的,好了结束
八、嵌套接口
推荐先看完内部类再来看这个
1.类中的接口
class t1 implements A.C,A.B{//访问不到A.D public void f() { }}class A { interface B { void f(); } public class BImp implements B { public void f() {} } private class BImp2 implements B { public void f() {} } public interface C { void f(); } class CImp implements C { public void f() {} } private interface D { void f(); } private class DImp implements D { public void f() {} } public class DImp2 implements D { public void f() {} }}
2.接口中的接口
interface E{ interface G{ //默认为public void f(); }}class t2 implements E.G{ public void f() { }}
九、接口与工厂
接口时实现多重继承的途径,而生成遵循某个接口的对象的典型方式就是工厂方法设计模式
package tij.interfacedemo;public class Test { public static void main(String[] args) { Factories.serviceConsumer(new Implementation1Factory()); Factories.serviceConsumer(new Implementation2Factory()); }}interface Service { void method1(); void method2();}interface ServiceFactory { Service getService();}class Implementation1 implements Service { Implementation1() {} public void method1() {System.out.println("Implementation1 method1");} public void method2() {System.out.println("Implementation1 method2");}}class Implementation1Factory implements ServiceFactory { public Service getService() { return new Implementation1(); }}class Implementation2 implements Service { Implementation2() {} public void method1() {System.out.println("Implementation2 method1");} public void method2() {System.out.println("Implementation2 method2");}}class Implementation2Factory implements ServiceFactory { public Service getService() { return new Implementation1(); }}class Factories{ static void serviceConsumer(ServiceFactory fact){ Service s = fact.getService(); s.method1(); s.method2(); }}
这样设计的好处就是将方法的实现与实例对象的生成分离开来,而且在使用Factories.serviceConsumer的时候不需要特定指定是哪种Service.