Thinking in Java from Chapter 7

时间:2021-05-13 02:45:34

From Thinking in Java 4th Edition

final

1. 对于基本类型,final使数值恒定不变

2. 对于对象引用,final使引用恒定不变,即不能指向别的对象,但指向的对象本身可以改变(The same for array in Java)

import java.util.*;
import static net.mindview.util.Print.*; class Value{
int i; // Package access
public Value(int i){this.i = i;}
} public class FinalData{
private static Random rand = new Random(47);
private String id; public FinalData(String id){this.id = id;} // Can be compile-time constants;
private final int valueOne = 9;
private static final int valueTwo = 99; // Typical public constant:
public static final int VALUE_THREE = 99; // Cannot be compile-time constants:
private final int i4 = rand.nextInt(20);
static final int INT_5 = rand.nextInt(20);
private Value v1 = new Value(11);
private final Value v2 = new Value(22);
private static final Value VAL_3 = new Value(33); // Arrays:
private final int[] a = {1, 2, 3, 4, 5, 6};
public String toString(){
return id + ": " + "i4 = " + i4 + ", INT_5" + INT_5;
} public static void main(String[] args){
FinalData fd1 = new FinalData("fd1");
//! fd1.valueOne++; // Error: can't change value
fd1.v2.i++; // Object isn't constant!
fd1.v1 = new Value(9); // OK -- not final for(int i = 0; i < f1.a.length; ++i)
++fd1.a[i]; // Object isn't constant! //! fd1.v2 = new Value(0); // Error: can't
//! fd1.VAL_3 = new Value(1); // change reference
//! fd1.a = new int[3]; print(fd1);
print("Creating new FinalData");
FinalData fd2 = new FinalData("fd2");
print(fd1);
print(fd2);
}
} /* Output:
fd1: i4 = 15, INT_5 = 18
Creating new FinalData
fd1: i4 = 15, INT_5 = 18
fd2: i4 = 13, INT_5 = 18
*/

Blank final

一个类中的final域可以做到根据对象有所不同,却又保持不变

class Poppet{
private int i;
Poppet(int ii) {i = ii;}
} public class BlankFinal{
private final int i = 0; // Initialized final
private final int j; // Blank final
private final Poppet p; // Blank final reference // Blank finals MUST be initialized in the constructor:
public BlankFinal(){
j = 1; // Initialize blank final
p = new Poppet(1); // Initialize blank final reference
} public BlankFinal(int x){
j = x; // Initialize blank final
p = new Poppet(x); // Initialize blank final reference
} public static void main(String[] args){
new BlankFinal();
new BlankFinal(47);
}
}

final方法

1. 锁定该方法,防止任何继承类修改它的含义

2. 效率

类中的所有private方法都被隐式地指定为final。

了解包括继承在内的初始化全过程:

import static net.mindview.util.Print.*;

class Insect{
private int i = 9;
protected int j; Insect(){
print("i = " + i + ", j = " + j);
j = 39;
} private static int x1 = printInit("static Insect.x1 initialized"); static int printInit(String s){
print(s);
return 47;
}
} public class Beetle extends Insect{
private int k = printInit("Beetle.k initialized"); public Beetle(){
print("k = " + k);
print("j = " + j);
} private static int x2 = printInit("static Beetle.x2 initialized"); public static void main(String[] args){
print("Beetle constructor");
Beetle b = new Beetle();
}
} /* Output:
static Insect.x1 initialized
static Beetle.x2 initialized
Beetle constructor
i = 9, j = 0
Beetle.k initialized
k = 47
j = 39
*/

继承

Note类

//: polymorphism/music/Note.java
package polymorphism.music; public enum Note{
MIDDLE_C, C_SHARP, B_FLAT; // Etc.
}

Instrument类

//: polymorphism/music/Instrument.java
package polymorphism.music;
import static net.mindview.util.Print.*; class Instrument{
public void play(Note n){
print("Instrument.play()");
}
}

Wind类

// Wind objects are instruments
// because they have the same interface:
public class Wind extends Instrument{
// Redefine interface method
public void play(Note n){
print("Wind.play() " + n);
}
}

Music类

package polymorphism.music;

public class Music{
public static void tune(Instrument i){
// ...
i.play(Note.MIDDLE_C);
} public static void main(String[] args){
Wind flute = new Wind();
tune(flute); // Upcasting
}
} /* Output:
Wind.play() MIDDLE_C
*/

Stringed和Brass类

package polymorphism.music;
import static net.mindview.util.Print.*; class Stringed extends Instrument{
public void play(Note n){
print("Stringed.play() " + n);
}
} class Brass extends Instrument{
public void play(Note n){
print("Brass.play() " + n);
}
}

讲一个方法调用和一个方法主体关联在一起,被称作绑定

1. 前期绑定:程序执行前进行绑定。

2. 后期绑定(也叫动态绑定):在运行时根据对象的类型进行绑定。

Java中除了static方法和final方法(private方法属于final方法)之外,其它所有方法都是后期绑定。Java正是通过动态绑定来实现多态。

缺陷:“覆盖”私有方法

package polymorphism;
import static net.mindview.util.Print.*; public class PrivateOverride{
private void f(){ print("private f()");} public static void main(String[] args){
PrivateOverride po = Derived();
}
} class Derived extends PrivateOverride{
public void f() { print("public f()");}
} /* Output:
private f()
*/

只有普通的方法调用可以是多态的。如果直接访问某个域,这个访问就将在编译期间进行解析:

class Super{
public int field = 0;
public int getField(){ return field;}
} class Sub extends Super{
public int field = 1;
public int getField() { return field;}
public int getSuperField() { return super.field;}
} public class FiedlAccess{
public static void main(String[] args){
Super sup = new Sub(); // Upcasting
System.out.println("sup.field = " + sup.field + ", sup.getField() = " + sup.getField()); Sub sub = new Sub();
System.out.println("sub.field = " + sub.field + ", sub.getField" + sub.getField() + ", sub.getSuperField = " + sub.getSuperField());
}
} /* Output:
sup.field = 0, sup.getField() = 1
sub.field = 1, sub.getField() = 1, sub.getSuperField = 0
*/

如果方法是静态的,它的行为也不是多态的

class StaticSuper{
public static String staticGet(){
return "Base staticGet()";
} public String dynamicGet(){
return "Base dynamicGet()";
}
} class StaticSub extends StaticSuper {
public static String staticGet(){
return "Derived staticGet()";
} public String dynamicGet(){
return "Derived dynamicGet()";
}
} public class StaticPolymorphism {
public static void main(String[] args){
StaticSuper sup = new StaticSub(); // Upcasting System.out.println(sup.staticGet());
System.out.println(sup.dynamicGet());
}
} /* Output:
Base staticGet()
Derived dynamicGet()
*/

构造器并不具有多态性,它们实际是static方法,只不过该static声明是隐式的。基类构造器总是在导出类的构造过程中被调用,而且按照继承层次逐渐向上链接,以使每个基类构造器得到调用。

package polymorphism;
import static net.mindview.util.Print.*; class Meal{
Meal(){ print("Meal()");}
} class Bread{
Bread(){ print("Bread()");}
} class Cheese{
Cheese(){ print("Cheese()");}
} class Lettuce{
Lettuce(){ print("Lettuce()");}
} class Lunch extends Meal{
Lunch(){ print("Lunch()");}
} class PortableLunch extends Lunch{
PortableLunch() { print("PortableLunch()");}
} public class Sandwich extends PortableLunch{
private Bread b = new Bread();
private Cheese c = new Cheese();
private Lettuce l = new Lettuce(); public Sandwich(){ print("Sandwich()");} public static void main(String[] args){
new Sandwich();
}
} /* Output:
Meal()
Lunch()
PortableLunch()
Sandwich()
*/

Java运行顺序:

1. 加载类,如有基类,加载相应的基类

2. 从根基类开始做static初始化,然后是下一个导出类的static的初始化

3. 完成加载后,进行对象的创建。

4. 调用基类构造器。(基类成员如果是基本类型,则被设为默认值;基类成员如果是对象引用,则被设置为null)

5. 然后是导出类的构造器。

(每个类的编译代码都存在于自己的独立的文件中。该文件只在需要使用程序代码时才会被加载。可以说:类的代码在初次使用时才被加载。)

继承与类清理

package polymorphism;
import static net.mindview.util.Print.*; class Characteristic{
private String s; Characteristic(String s){
this.s = s;
print("Creating Characteristic " + s);
} protected void dispose(){
print("disposing Characteristic " + s);
}
} class Description{
private String s; Description(String s){
this.s = s;
print("Creating Description " + s);
} protected void dispose(){
print("disposing Description " + s);
}
} class LivingCreature{
private Characteristic p = new Characteristic("is alive");
private Description t = new Description("Basic Living Creature"); LivingCreature(){
print("LivingCreature()");
} protected void dispose(){
print("LivingCreature dispose");
t.dispose();
p.dispose();
}
} class Animal extends LivingCreature {
private Characteristic p = new Characteristic("has heart");
private Description t = new Description("Animal not Vegetable"); Animal(){ print("Animal()");} protected void dispose(){
print("Animal dispose");
t.dispose();
p.dispose();
super.dispose();
}
} class Amphibian extends Animal {
private Characteristic p = new Characteristic("Can live in water");
private Description t = new Description("Both water and land"); Amphibian(){
print("Amphibian()");
} protected void dispose(){
print("Amphibian dispose");
t.dispose();
p.dispose();
super.dispose();
}
} public class Frog extends Amphibian {
private Characteristic p = new Characteristic("Croak");
private Description t = new Description("Eats Bugs"); public Frog() { print("Frog()");} protected void dispose(){
print("Frog dispose");
t.dispose();
p.dispose();
super.dispose();
} public static void main(String[] args){
Frog frog = new Frog();
print("Bye!");
frog.dispose();
}
} /* Output:
Creating Characteristic is alive
Creating Description Basic Living Creature
LivingCreature()
Creating Characteristic has heart
Creating Description Animal not Vegetable
Animal()
Creating Characteristic Can live in water
Creating Description Both water and land
Amphibian()
Creating Characteristic Croak
Creating Description Eats Bugs
Frog()
Bye!
Frog dispose
disposing Description Eats Bugs
disposing Characteristic Croak
Amphibian dispose
disposing Description Both water and land
disposing Characteristic Can live in water
...
*/

共享对象

import static net.mindview.util.Print.*;

class Shared {
private int refcount = 0;
private static long counter = 0;
private final long id = counter++; public Shared(){
print("Creating " + this);
} public void addRef(){ ++refcount; } protected void dispose(){
if(0 == --refcount){
print("Disposing " + this);
}
} public String toString() { return "Shared " + id; }
} class Composing {
private Shared shared;
private static long counter = 0;
private final long id = counter++; public Composing(Shared shared) {
print("Creating " + this);
this.shared = shared;
this.shared.addRef();
} public void dispose(){
print("disposing " + this);
shared.dispose();
} public String toString(){ return "Composing " + id;}
} public class ReferenceCounting {
public static void main(String[] args){
Shared shared = new Shared();
Composing[] composing = {new Composing(shared),
new Composing(shared), new Composing(shared),
new Composing(shared), new Composing(shared)}; for(Composing c : composing)
c.dispose();
}
} /* Output:
Creating Shared 0
Creating Composing 0
Creating Composing 1
Creating Composing 2
Creating Composing 3
Creating Composing 4
disposing Composing 0
disposing Composing 1
disposing Composing 2
disposing Composing 3
disposing Composing 4
disposing Shared 0
*/

构造器内部的多态行为:

import static net.mindview.util.Print.*;

class Glyph {
void draw() { print("Glyph.draw()");} Glyph(){
print("Glyph() before draw()");
draw();
print("Glyph() after draw()");
}
} class RoundGlyph extends Glyph{
private int radius = 1; RoundGlyph(int r){
radius = r;
print("RoundGlyph.RoundGlyph(), radius = " + radius);
} void draw(){
print("RoundGlyph.draw(), radius = " + radius);
}
} public class PolyConstructors{
public static void main(String[] args){
new RoundGlyph(5);
}
} /* Output:
Glyph() before draw()
RoundGlyph.RoundGlyph(), radius = 0
Glyph() after draw()
RoundGlyph.RoundGlyph(), radius = 5
*/

用继承设计

import static net.mindview.util.Print.*;

class Actor {
public void act(){}
} class HappyActor extends Actor {
public void act() { print("HappyActor");}
} class SadActor extends Actor {
public void act() { print("SadActor");}
} class Stage {
private Actor actor = new HappyActor();
public void change() { actor = new SadActor();}
public void performPlay() { actor.act();}
} public class Transmogrify {
public static void main(String[] args){
Stage stage = new Stage();
stage.performPlay();
stage.change();
stage.performPlay();
}
} /* Output:
HappyActor
SadActor
*/

设计准则:用继承表达行为的差异,用组合表达状态上的变化。

向下转型与运行时类型识别(RTTI)

class Useful{
public void f() {}
public void g() {}
} class MoreUseful extends Useful {
public void f() {}
public void g() {} public void w() {}
public void v() {}
public void u() {}
} public class RTTI {
public static void main(String[] args){
Useful[] x = {
new Useful(),
new MoreUseful() // Upcast
}; x[0].f();
x[1].g(); // Compile time: method not found in Useful:
//! x[1].u();
((MoreUseful)x[1]).u(); // Downcast/RTTI
((MoreUseful)x[0]).u(); // Exception thrown
}
}

抽象类

包含抽象方法的类叫抽象类。所以为了使某个类成为抽象类,仅需将某些方法声明为抽象类即可。

如果从一个抽象类继承,并想创建该新类的对象,那么就必须为基类中的所有抽象方法提供方法定义。如果不这样做(可以选择不做),那么导出类便也是抽象类,且编译器将会强制我们用abstract关键字来限定这个类。

interface这个关键字产生了一个完全抽象的类,它根本就没有提供任何具体的实现。它允许创建:

1. 方法名

2. 参数列表

3. 返回类型

但没有任何方法体。

package interfaces.music4;
import polymorphism.music.Note;
import static net.mindview.util.Print.*; abstract class Instrument {
private int i; // Storage allocated for each
public abstract void play(Note n);
public String what() { return "Instrument";}
public abstract void adjust();
} class Wind extends Instrument {
public void play(Note n){
print("Wind.play() " + n);
} public String what() { return "Wind";}
public void adjust() {}
}

接口被用来建立类与类之间的协议。
接口也可以包含域,但是这些域隐式地被指定为static和final的

package interfaces.music5;
import polymorphism.music.Note;
import static net.mindview.util.Print.*; interface Instrument {
// Compile-time constant:
int VALUE = 5; // static & final // Cannot have method definitions:
void play(Note n); // Automatically public
void adjust();
} class Wind implements Instrument {
public void play(Note n){
print(this + ".play() " + n);
} public String toString() {return "Wind";}
public void adjust() {print(this + ".adjust()");}
} class Percussion implements Instrument {
public void play(Note n){
print(this + ".play() " + n);
} public String toString() {return "Percussion";}
public void adjust() {print(this + ".adjust()");}
}

完全解耦

package interfaces.classprocessor;
import java.util.*;
import static net.mindview.util.Print.*; class Processor {
public String name() {
return getClass().getSimpleName();
} Object process(Object input) { return input;}
} class Upcase extends Processor {
String process(Object input) {
// Covariant return
return ((String)input).toUpperCase();
}
} class Downcase extends Processor {
String process(Object input){
return ((String)input).toLowerCase();
}
} class Splitter extends Processor {
String process(Object input) {
// The split() argument divides a string into pieces:
return Arrays.toString(((String)input).split(" "));
}
} public class Apply {
public static void process(Processor p, Object s){
print("Using Processor " + p.name());
print(p.process(s));
} public 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);
}
} /* Output:
Using Processor Upcase
DISAGREEMENT WITH BELIEFS IS BY DEFINITION INCORRECT
Using Processor Downcase
disagreement with beliefs is by definition incorrect
Using Processor Splitter
[Disagreement, with, beliefs, is, by, definition, incorrect]
*/

接口可复用结构

Processor接口

//: interfaces/interfaceprocessor/Processor.java
package interfaces.interfaceprocessor; public interface Processor {
String name();
Object process(Object input);
}

Apply类

//: interfaces/interfaceprocessor/Apply.java
package interfaces.interfaceprocessor;
import static net.mindview.util.Print.*; public class Apply {
public static void process(Processor p, Object s){
print("Using Processor " + p.name());
print(p.process(s));
}
}

复用代码第一种方式,客户端程序员遵循接口编写自己的类

package interfaces.interfaceprocessor;
import java.util.*; public abstract class StringProcessor implements Processor {
public String name() {
return getClass().getSimpleName();
} public abstract String process(Object input); public static String s = "If she weights the same as a duck, she's made of wood"; public static void main(String[] args){
Apply.process(new Upcase(), s);
Apply.process(new Downcase(), s);
Apply.process(new Splitter(), s);
}
} class Upcase extends StringProcessor {
public String process(Object input) {
// Covariant return
return ((String)input).toUpperCase();
}
} class Downcase extends StringProcessor {
public String process(Object input) {
return ((String)input).toLowerCase();
}
}

适配器模式:适配器中的代码将接受你所拥有的接口,并产生你所需要的接口。

package interfaces.interfaceprocessor;
import java.util.*; public abstract class StringProcessor implements Processor {
public String name() {
return getClass().getSimpleName();
} public abstract String process(Object input); public static String s = "If she weights the same as a duck, she's made of wood"; public static void main(String[] args){
Apply.process(new Upcase(), s);
Apply.process(new Downcase(), s);
Apply.process(new Splitter(), s);
}
} class Upcase extends StringProcessor {
public String process(Object input) {
// Covariant return
return ((String)input).toUpperCase();
}
} class Downcase extends StringProcessor {
public String process(Object input) {
return ((String)input).toLowerCase();
}
} package interfaces.interfaceprocessor;
import interfaces.filters.*; 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);
}
} public class FilterProcessor {
public static void main(String[] args) {
Waveform w = new Waveform();
Apply.process(new FilterAdapter(new LowPass(1,0)), w);
Apply.process(new FilterAdapter(new HighPass(2.0)), w);
Apply.process(
new FilterAdapter(new BandPass(3.0, 4.0)), w);
}
}

具体类组合数个接口之后产生了一个新类:

interface CanFight {
void fight();
} interface CanSwim {
void swim();
} interface CanFly {
void fly();
} class ActionCharacter {
public void fight(){}
} class Hero extends ActionCharacter implements CanFight, CanSwim, CanFly {
public void swim() {}
public void fly() {}
} public class Adventure {
public static void t(CanFight x) { x.fight();}
public static void u(CanSwim x) { x.swim();}
public static void v(CanFly x) { x.fly();}
public static void w(ActionCharacter x) { x.fight();} public static void main(String[] args){
Hero h = new Hero();
t(h); // Treat it as a CanFight
u(h); // Treat it as a CanSwim
v(h); // Treat it as a CanFly
w(h); // Treat it as an ActionCharacter
}
}

这个例子展示了使用接口的核心原因:为了能够向上转型为多个基类型(以及由此而带来的灵活性)。

使用接口的第二个原因与使用抽象类相同:防止客户端程序员创建该类的对象,并确保这仅仅是建立一个接口。

通过继承,可以:

1. 在接口中添加新的方法声明

2. 在新接口中组合数个接口

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 menace() {}
public void destroy() {}
public void kill() {}
public void drinkBlood() {}
} public class HorrorShow {
static void u(Monster b) { b.menace();}
static void v(DangerousMonster d) {
d.menace();
d.destroy();
}
static void w(Lethal l) { l.kill();} public static void main(String[] args){
DangerousMonster barney = new DragonZilla();
u(barney);
v(barney); Vampire vlad = new VeryBadVampire();
u(vlad);
v(vlad);
w(vlad);
}
}

接口的常见用法就是策略设计模式,你主要就是要声明:“你可以用任何你想要的对象来调用我的方法,只要你的对象遵循我的接口。”例如,Java中的Scanner类的构造器就是一个Readable接口。如果你创建了一个新类,并想让Scanner可以作用它,那么你就应该让它成为Readable:

import java.nio.*;
import java.util.*; public class RandomWorks implements Readable {
private static Random rand = new Random(47);
private static final char[] capitals = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray();
private static final char[] lowers = "abcdefghijklmnopqrstuvwxyz".toCharArray();
private static final char[] vowels = "aeiou".toCharArray(); private int count; public RandomWorks(int count) { this.count = count;} public int read(CharBuffer cb) {
if(count-- = 0)
return -1; // Indicates end of input
cb.append(capitals[rand.nextInt(capitals.length)]);
for(int i = 0; i < 4; ++i){
cb.append(vowels[rand.nextInt(vowels.length)]);
cb.append(vowels[rand.nextInt(vowels.length)]);
}
cb.append(" ");
return 10; // Number of characters appended
} public static void main(String[] args){
Scanner s = new Scanner(new RandomWorks(10));
while(s.hasNext())
System.out.println(s.next());
}
}

假设还有一个没有实现Readable的类,怎样才能让Scanner作用于它呢?

import java.util.*;

public class RandomDoubles {
private static Random rand = new Random(47); public double next() { return rand.nextDouble();} public static void main(String[] args){
RandomDoubles rd = new RandomDoubles(); for(int i = 0; i < 7; ++i)
System.out.println(rd.next() + " ");
}
}

我们需要再次使用适配器。但在本例中,被适配的类可以通过继承和实现Readable接口来创建。通过使用interface关键字提供的伪多重继承,可以生成既是RandomDoubles又是Readable的新类:

import java.nio.*;
import java.util.*; public class AdaptedRandomDoubles extends RandomDoubles implements Readable {
private int count;
public AdaptedRandomDoubles(int count) {
this.count = count;
} public int read(CharBuffer cb) {
if(count-- == 0)
return -1;
String result = Double.toString(next()) + " ";
cb.append(result);
return result.length();
} public static void main(String[] args){
Scanner s = new Scanner(new AdaptedRandomDoubles(7)); while(s.hasNextDouble())
System.out.println(s.nextDouble() + " ");
}
}

在这个方法中,我们可以在任何现有类之上添加新的接口,所以这意味着让方法接受接口类型,是一种让任何类都可以对该方法进行适配的方式。这就是使用接口而不是类的强大之处。

工厂方法设计模式。与直接调用构造器不同,我们在工厂对象上调用的是创建方法,而该工厂对象将生成接口的某个实现的对象。

import static net.mindview.util.Print.*;

interface Service {
void method1();
void method2();
} interface ServiceFactory {
Service getService();
} class Implementation1 implements Service {
Implementation1() {} // Package access
public void method1() {print("Implementation1 method1");}
public void method2() {print("Implementation2 method2");}
} class Implementation1Factory implements ServiceFactory {
public Service getService() {
return new Implementation1();
}
} class Implementation2 implements Service {
Implementation2() {} // Package access
public void method1() {print("Implementation2 method1");}
public void method2() {print("Implementation2 method2");}
} class Implementation2Factory implements ServiceFactory {
public Service getService(){
return new Implementation2();
}
} public class Factories {
public static void serviceConsumer(ServiceFactory fact){
Service s = fact.getService();
s.method1();
s.method2();
} public static void main(String[] args){
serviceConsumer(new Implementation1Factory());
// Implementations are completely interchangeable:
serviceConsumer(new Implementation2Factory());
}
} /* Output:
Implementation1 method1
Implementation1 method2
Implementation2 method1
Implementation2 method2
*/