《Java 编程思想》第四版基于 JAVA 5 版本;《On Java 8》 基于 JAVA 8 版本。都是由作者 [美] Bruce Eckel所著,建议直接看新书《On Java 8》,它是是事实上的 《Java 编程思想》第五版。本博文我只记录1-9章的读书笔记,后面章节我将直接看《On Java 8》并另开博文记录。
参考:Java编程思想配书代码使用指南
我的补充,以mac为例
cd ~
vim .bash_profile
esc
:wq
source .bash_profile
<fail message="J2SE5 required" unless="version1.5"/>
接口确定了对某一特定对象所能发出的请求。
UML(Unified Modelling Language,统一建模语言)图
访问权限
protected:继承的类可以访问
默认:包访问权限,同一个包中的类可以访问
has-a关系:组合(composition)或者聚合(aggregation)
后期绑定来实现多态:当向对象发送消息时,被调用的代码直到运行时才能确定。
Java中,动态绑定是默认行为。
C++中,动态绑定不是默认行为,用关键字virtual来实现。
向上转型(upcasting)将子类看成父类。
保证所有对象都具备某些功能,因此可以更容易对所有对象进行某些操作。比如:堆上创建,参数传递,垃圾回收器的实现,异常处理。
基本类型不用new来创建变量,而是创建一个并非是引用的“自动”变量,这个变量直接存储“值”,并置于堆栈中,更高效。
高精度数字。两个包装器类型。
BigInteger:支持任意精度的整数。
BigDecimal:支持任意精度的定点数。
C++引入了名字空间。
Java希望用自己的域名反转来保证类库的命名独一无二。如域名是MindView.net,类类库名为net.mindview.utility。
有一个特定类会自动导入到每一个Java文件中:java.lang。
查找sun公司文档
用到时再看
作者自己搞了一个类库,简化了打印语句:net.mindview.util.Print
基本数据类型赋值:内容传递
对象赋值:引用传递
对象作为方法的实参:引用传递
Java里唯一用到逗号操作符的地方就是for循环的控制表达式,在初始化和步进控制部分,可以在for语句内定义多个变量,但它们必须是相同类型。
名字和参数类型列表
不行,因为有时候调用方法只是为了副作用而不关心其返回值,比如以下调用就无法区分到底该调用哪个方法
f()
void f(){}
int f(){return 1;}
如果你已经定义了一个构造器,编译器就不会再帮你自动创建默认构造器
public class Leaf {
int i = 0;
Leaf increment() {
i++;
return this;
}
}
class Person {
public void eat(Apple apple) {
Apple peeled = apple.getPeeled();
System.out.println("Yummy");
}
}
class Peeler {
static Apple peel(Apple apple) {
// ... remove peel
return apple; // Peeled
}
}
class Apple {
Apple getPeeled() { return Peeler.peel(this); }
}
public class PassingThis {
public static void main(String[] args) {
new Person().eat(new Apple());
}
}
顺序:先成员再方法,先静态再非静态
可以用new来创建并初始化一个不确定元素个数的数组。数组元素中的基本数据类型会自动初始化为空值。(0,false)
Random rand = new Random(47);
int[] a = new int[rand.nextInt(20)];
只能传入数组,Java SE5之前的写法
void printArray(Object[] args) {}
printArray(new Object[]{"one", "two", "three" });
传入数组和非数组都行,方便很多,Java SE5之后的新特性。
将0个参数传递给可变参数列表是可行的。
void printArray(Object... args) {}
printArray(new Object[]{"one", "two", "three" });
printArray("one", "two", "three");
printArray(); // Empty list is OK
重载方法时,最好只让最多一个方法使用可变参数列表,否则很容易出问题。
相同目录下的所有不具有明确package声明的文件,都被视为是该目录下默认包的一部分。
class Soup2 {
private Soup2() {} // 私有化构造方法,无法从这里创建对象
private static Soup2 ps1 = new Soup2(); // 静态实例ps1是唯一的
public static Soup2 access() { // 尽量将域设为private,只公开方法
return ps1; // 只能通过这个静态方法,创建唯一一个静态实例ps1
}
public void f() {}
}
代理是继承与组合之间的中庸之道,因为我们将一个成员对象置于所要构造的类中(就像组合),但与此同时我们在新类中暴露了该成员对象的所有方法(就像继承)。
虽然编译器强制你去初始化基类,但是它并不监督你必须将基类的成员对象也初始化,因此在这一点上你自己必须时刻注意。
一个永不改变的编译时常量
private final int valueOne = 9;
private static final int VALUE_TWO = 99;
public static final int VALUE_THREE = 39;
一个在运行时被初始化的值,而你不希望它被改变
private final int i4 = rand.nextInt(20);
static final int INT_5 = rand.nextInt(20);
示例代码
//: reusing/Beetle.java
// The full process of initialization.
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
*///:~
当某个成员对象被这个类的多个对象共享时,如果要对此成员对象进行必要的清理工作,就需要使用引用计数来跟踪仍旧访问着此共享对象的对象数量了。
//: polymorphism/ReferenceCounting.java
// Cleaning up shared member objects.
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(--refcount == 0)
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();
}
protected 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
*///:~
//: polymorphism/PolyConstructors.java
// Constructors and polymorphism
// don't produce what you might expect.
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.draw(), radius = 0
Glyph() after draw()
RoundGlyph.RoundGlyph(), radius = 5
*///:~
Java5中添加了协变返回类型,即子类的重写方法的返回值类型可以是父类方法的返回值类型的子类型
//: polymorphism/CovariantReturn.java
class Grain {
public String toString() { return "Grain"; }
}
class Wheat extends Grain {
public String toString() { return "Wheat"; }
}
class Mill {
Grain process() { return new Grain(); }
}
class WheatMill extends Mill {
Wheat process() { return new Wheat(); }
}
public class CovariantReturn {
public static void main(String[] args) {
Mill m = new Mill();
Grain g = m.process();
System.out.println(g);
m = new WheatMill();
g = m.process();
System.out.println(g);
}
} /* Output:
Grain
Wheat
*///:~
//: polymorphism/Transmogrify.java
// Dynamically changing the behavior of an object
// via composition (the "State" design pattern).
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
*///:~
在Java中,所有转型都会在运行时得到检查,如果不是我们希望的类型,就会返回一个ClassCastException(类转型异常)。
//: interfaces/classprocessor/Apply.java
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]
*///:~
//: interfaces/interfaceprocessor/FilterProcessor.java
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);
}
} /* Output:
Using Processor LowPass
Waveform 0
Using Processor HighPass
Waveform 0
Using Processor BandPass
Waveform 0
*///:~
一个子类只能继承一个父类,但一个子接口可以继承多个父接口
interface Vampire extends DangerousMonster, Lethal {
void drinkBlood();
}
在打算组合的不同接口中使用相同的方法名通常会造成代码可读性的混乱,请尽量避免这种情况。
//: interfaces/InterfaceCollision.java
package interfaces;
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 void f() {}
public int f(int i) { return 1; } // overloaded
}
class C3 extends C implements I2 {
public int f(int i) { return 1; } // overloaded
}
class C4 extends C implements I3 {
// Identical, no problem:
public int f() { return 1; }
}
// Methods differ only by return type:
//! class C5 extends C implements I1 {}
//! interface I4 extends I1, I3 {} ///:~
接口可以嵌套在类或其他接口中。这个特性还没发现有什么具体用途,知道有这个特性就行,不用了解细节。
恰当的原则应该是优先选择类而不是接口。从类开始,如果接口的必要性变得非常明确,那么就进行重构。