第9章《接口》和第10章《内部类》主要介绍了抽象类、接口、内部类、匿名类以及嵌套类等概念,以及它们的区别和联系。主要内容如下:
接口和内部类
1.抽象类(abstract)
抽象类也称抽象基类,用于表示所有导出类的共同部分,是普通类和接口间的中庸之道,用关键字 abstract 限定。例如我们之前的“乐器”例子中的 Instrument 类就可以定义为抽象类。
我们可以通过抽象类操作一系列导出类,然而创建抽象类对象则没有什么意义,为防止使用者创建抽象类对象做一些没有意义的事情,Java 提供了抽象方法机制,抽象方法只有声明没有方法体。以创建 Instrument 抽象类为例:
abstract class Instrument { // 抽象类
public abstract void play(); // 抽象方法
public String what() { return "Instrument"; } // 抽象类中的方法也可以包含方法定义
}
如果一个类包含一个或多个抽象方法,该类必须被限定为抽象类,否则编译会报错。
如果从一个抽象类继承,并想创建该导出类的对象,那么导出类必须要为基类中的所有抽象方法提供方法定义,否则导出类也是抽象类,必须用 abstract 关键字限定。
2.接口(interface)
2.1.接口概念
接口和抽象方法相比更向前迈进一步,interface 关键字会产生一个完全抽象的类,没有提供任何具体实现,只允许创建者确定方法名、参数列表和返回类型,没有任何方法体。以创建 Instrument 接口为例:
interface Instrument { // 接口
void play(Note n);
String what();
}
注意,上面接口中 interface 前面没有加 public 关键字,说明具有包访问权限,该接口只能在同一个包中可用。接口中的方法没有使用 public 关键字限定,但是它们自动就是 public 的!
要想让某个类遵循(实现)某个接口,需要用 implements 关键字:
class Wind implements Instrument {
public void play(Note n) {
print(this + ".play() " + n);
}
public String what() {
return "Wind";
}
}
2.2.几种常用设计模式
策略设计模式
创建一个能够根据所传递的参数对象的不同而具有不同行为的方法,即被称为策略设计模式。通过接口即可实现,接着上面的例子,如果想要实现一个 “演奏乐曲” 的方法,不同的乐器演奏出的效果是不同的,可以这样设计:
// Wind 类上面已经实现了 Instrument 接口,这里忽略
class Stringed implements Instrument {
public void play(Note n) {
print(this + ".play() " + n);
}
public String what() {
return "Stringed";
}
}
// 演奏乐曲
public class PlayMusic {
String name;
public void tune(Instrument i) {
i.play(Note.MIDDLE_C);
}
}
public static void main(String[] args) {
PlayMusic player = new PlayMusic();
player.tune(new Wind());
player.tune(new Stringed());
}
Wind.play() MIDDLE_C
Stringed.play() MIDDLE_C
策略模式和多态是一个思想,只不过多态是通过继承和方法重写实现的一种语言机制,而策略模式是一种算法,强调做一件事情的不同方法,方法间不能重复。
适配器设计模式
以榨汁机为例,榨汁机(Juicer)可以将水果(Fruits)榨成果汁:
// 水果
public class Fruits {
private String name;
private Fruites(String n) { name = n; }
public string toString() { return "Fruits " + name; }
}
// 榨汁机
public interface Juicer {
String work(Fruits f);
}
现在我们购买一个九阳榨汁机开始榨汁:
// 九阳榨汁机
public class JiuyangJuicer implements Juicer {
public String work(Fruits f) { return "Jiuyang juicing: " f.toString; }
}
// 榨汁机开始榨汁
public class JuicerWord {
JiuyangJuicer juicer = new JiuyangJuicer();
String result = juicer.work(new Fruits("apple"));
print(result);
}
Jiuyang juicing: apple
但是问题来了,现在插排只能插两孔插座的榨汁机(老式榨汁机 OldJuicer),九阳榨汁机是三孔的,现在怎么办?-- 我们可以买个适配器(JuicerAdapter):
public class JuicerAdapter implements Juicer {
JiuyangJuicer juicer;
public JuicerAdapter(JiuyangJuicer juicer) {
this.juicer = juicer;
}
public String work(Fruits f) { juicer.work("apple"); }
}
public class JuicerAdapterWork {
JiuyangJuicer juicer = new JiuyangJuicer(); // 我们的九阳榨汁机
JuicerAdapter juicerAdapter = new JuicerAdapter(juicer); // 买的适配器
String result = juicerAdapter.work(new Fruits("apple")); // 通过适配器开始榨汁
print(result);
}
2.3.多重继承
一个类只能继承自一个基类,但是可以继承自多个接口:
interface CanFight { void fight(); }
interface CanSwim { void swim(); }
class ActionCharacter { public void fight() {} }
class Hero extends ActionCharacter implements CanFight, CanSwim {
public void fight() {}
public void swim() {}
}
3.内部类
将一个类的定义置于另一个类内部,就是内部类。例如:
public class Parcel {
class Contents {
private int i = 11;
public int value() { return i; }
}
class Destination {
private String lable;
Destination(String whereTo) {
lable = whereTo;
}
String readLable() { return label; }
}
public Destination to(String s) {
return new Destination(s); // 返回内部类的引用
}
public Contents contents() {
return new Contents();
}
public void ship(String dest) { // 外围类的内部使用内部类
Contents c = new Contents();
Destination d = new Destination(dest);
System.out.println(d.readLable());
}
public static void main(String[] args) {
Parcel p = new Parcel();
p.ship("Tasmania");
Parcel.Contents c = p.contents(); // 对外围类使用内部类
Parcel.Destination d = p.to("Borneo");
}
}
3.1.内部类的特性
1.可以访问外围对象的所有方法和字段,但外围对象不可以访问内部类的方法和元素;
2.如果内部类位于方法的作用域内(即局部内部类),对于外围类的对象是不可见的。
3.2.生成内部类对象
想要创建外围类对象,必须通过创建外围类实现。在拥有外围类之前是不可能创建内部类对象的。因为内部类对象会暗暗地连接到创建它的外围类对象上(但如果你创建的是嵌套类 -- 静态内部类,就不需要对外围类对象的引用)。
public class DotThis {
void f() { System.out.println("DotThis.f()"); }
public class Inner {
public DotThis outer() { return DotThis.this; }
}
public Inner inner() { return new Inner(); }
public static void main(String[] args) {
DotThis dt = new DotThis(); // 1.生成外围类
DotThis.Inner dti = dt.inner(); // 2.1.方法一:通过外围类生成内部类
DotThis.Inner dtiN = dt.new Inner(); // 2.2.方法二:通过 .new 生成内部类
dti.outer().f(); // 3.内部类调用外围类的方法
}
}
客户端无法访问 private 内部类,通过这种方式可以完全阻止任何依赖于类型的编码,并且完全隐藏实现的细节。
3.3.匿名类
匿名类是一种继承自某个类或者实现某个接口没有名字的类:
public class Parcel9 {
public Destination destination(final String dest) {
return new Destination() {
private String lable = dest;
public String readLable() { return lable; }
};
}
}
使用匿名类需要注意以下几点:
1.匿名类要使用一个外部定义的对象,为保证内部类和外围类参数的一致性,其参数引用必须是 final 的;
2.使用匿名类时,我们必须是继承一个类或者实现一个接口,但是两者不可兼得,同时也只能继承一个类或者实现一个接口;
3.匿名类中是不能定义构造函数的;
4.匿部类中不能存在任何的静态成员变量和静态方法;
5.匿名类为局部内部类,所以局部内部类的所有限制同样对匿名内部类生效;
6.匿名类不能是抽象的,它必须要实现继承的类或者实现的接口的所有抽象方法。
3.4.嵌套类
嵌套类是静态内部类,即嵌套类对象和外围类对象之间没有任何联系。所以与普通内部类相比嵌套类具有以下特点:
1.创建嵌套类对象前不需要外围类的对象;
2.不能从嵌套类对象中访问非静态的外围类对象;
3.普通内部类的字段和方法只能放在外部层次上,不能有 static 数据和 static 字段,也不能包含嵌套类,但是嵌套类可以包含所有这些东西。
例如可以使用嵌套类来放置我们的测试代码:
public class TestBed {
public void f() { System.out.println("f()"); }
public static class Tester {
public static void main(String[] args) { // 嵌套类
TestBed t = new TestBed();
t.f();
}
}
}
3.5.闭包
内部类这部分提到了闭包的概念,但是我并没有看懂,然后自己在网上看了一些帖子,感觉这个解释的很明白:
闭包能够将一个方法作为变量去存储 --> Java里将内部类对象向上转型为接口类型即可实现闭包!
3.6.局部内部类
局部内部类创建于方法体内部,可以访问外围类所有成员,但是外围成员不可以访问局部内部类。
interface ILog { void write(); }
public class LocalInnerClass {
private int length =0;
public ILog logger() {
class InnerClass implements ILog { // 局部内部类
public void write(String message) {
length = message.length();
System.out.println("LocalInnerClass.InnerClass:" + length);
}
}
return new InnerClass();
}
}
3.7.内部类继承
class WithInner { class Inner() {} }
public class InheritInner extends WithInner.Inner {
// InheritInner() {} -- 编译错误
InheritInner(WithInner wi) { wi.super(); }
}
首先继承时要指明外围类和内部类的关系,其次是构造内部类对象时必须使用 .super 提供指向外围类的引用。
网友评论