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.
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。