《Java编程思想》阅读笔记二
Java编程思想
这是一个通过对《Java编程思想》(Think in java)进行阅读同时对java内容查漏补缺的系列。一些基础的知识不会被罗列出来,这里只会列出一些程序员经常会忽略或者混淆的知识点。
所列知识点全部都是针对自己个人而言,同时也欢迎大家进行补充。
第九章(接口)
任何抽象性都应该是应真正的需求而产生的。
访问权限
p172
interface如果不加public关键字,则只具有包访问权限。
重名
p181
可以通过extends来扩展接口,但在实现多重继承时要注意不能实现签名或返回类型不同的接口方法,除非其传入参数不一样。最好避免使用同一个方法名称。
interface M{
void menace();
void kill();
}
interface Danger{
void menace(int s);
void kill();
}
interface Vampire extends Danger,M{
}
public class Monster implements Vampire{
public static void main(String[] args) {
System.out.println("Hello,every one,I'm cpacm");
}
public void menace(int s) {
// TODO Auto-generated method stub
}
public void kill() {
// TODO Auto-generated method stub
}
public void menace() {
// TODO Auto-generated method stub
}
}
tip:切勿过渡设计
策略模式
p182
去商店去买东西,可以选择不同的出行方式但都能达到买东西的目的,这种选择模式就是策略模式。
把出行方式抽象为一个接口,实现公交车,自行车,走路,出租车等实例,最后自己决定使用哪种方式。
策略模式:定义一系列的算法,把每一个算法封装起来, 并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化。也称为政策模式(Policy)。策略模式把对象本身和运算规则区分开来,其功能非常强大,因为这个设计模式本身的核心思想就是面向对象编程的多形性的思想。
环境类(Context):用一个ConcreteStrategy对象来配置。维护一个对Strategy对象的引用。可定义一个接口来让Strategy访问它的数据。
抽象策略类(Strategy):定义所有支持的算法的公共接口。 Context使用这个接口来调用某ConcreteStrategy定义的算法。
具体策略类(ConcreteStrategy):以Strategy接口实现某具体算法。
适配器模式
p183
将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以在一起工作。
在Android中常用到的各种Adapter就是用的适配器模式思想。
工厂模式
p187
工厂模式主要用以下几种形态:
- 简单工厂(Simple Factory): 主要代码由一个Factory管理,每添加一个产品都需要在factory类中修改代码;
- 工厂方法(Factory Method):一种产品对应一个工厂,增加一种产品就同时增加一个工厂;
- 抽象工厂(Abstract Factory):通常用于多种产品且每种都有不同型号的情况下,针对型号建立工厂,每个工厂只生产该型号的产品。
抽象工厂和工厂可以根据不同情况下相互转化。
第十章(内部类)
一、访问权
p191
内部类拥有其外围类的所有元素的访问权。
意思是通过内部类能够获得其外部类的内存信息(参数值)。
故不能直接通过创建普通对象的方法创建内部类,必须要通过外部类才能创建。
public class DotThis {
void f() {
System.out.println("DotThis.f()");
}
public class Inner {
public DotThis outer() {
return DotThis.this;
// A plain "this" would be Inner's "this"
}
}
public Inner inner() {
return new Inner();
}
public static void main(String[] args) {
DotThis dt = new DotThis();
DotThis.Inner dti = dt.inner();
dti.outer().f();
}
}
二、.new
p193
.new语法,可以直接创建其内部类,但前提也是要提供其外部类的引用。
public class DotNew {
public class Inner{};
public static void main(String[] args) {
DotNew dn = new DotNew();
DotNew.Inner dti = dn.new Inner();
}
}
当内部类向上转型为基类或接口时,往往是为了隐藏实现细节。
三、局部
p196
内部类可以嵌入在任何堆作用域内,但只在其作用域内可用。称作为局部内部类
public class Parcel6 {
private void inTrack(boolean b){
if(b){
class TrackSlip{
private String id;
TrackSlip(String s){
id = s;
}
String getSlip(){return id;}
}
TrackSlip ts = new TrackSlip("slip");
String s = ts.getSlip();
}
}
//TrackSlip ts = new TrackSlip("slip"); //run error
public void track(){
inTrack(true);
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Parcel6 p = new Parcel6();
p.track();
}
}
四、匿名内部类
p200
匿名内部类是一个没有名字的内部类,通常使用它来简化代码编写,同时必须继承一个父类或实现一个接口。
public class Parcel7 {
public static void main(String[] args) {
Thread t = new Thread() {
public void run() {
for (int i = 1; i <= 5; i++) {
System.out.print(i + " ");
}
}
};
t.start();
}
}
//1 2 3 4 5
tip:常用的扩展是将只使用一次的工厂类作为匿名内部类放在生产类上。
五、嵌套类
p201
将内部类声明为static时,此时就作为嵌套类使用。创建嵌套类的对象并不需要外围对象,同时也不能从嵌套类的对象中访问非静态的外围对象。
六、接口内部类
p202
在接口中可以放入嵌套类。
public interface ClassInInterface {
void howdy();
class Test implements ClassInInterface{
@Override
public void howdy() {
// TODO Auto-generated method stub
System.out.println("Howdy!");
}
public static void main(String[] args) {
new Test().howdy();
}
}
}
七、内部类作用
p205
使用内部类的一些好处
- 可以解决多重继承的问题,因为内部类可以访问外围类的信息,所以获得内部类相当于获得外围类和内部类两种信息。
- 在一个外围类中,可以让多个内部类以不同的方式实现同一个接口或类从而实现不同的特性。
八、模版模式
p207
模板方法模式:定义一个操作中算法的框架,而将一些步骤延迟到子类中。模板方法模式使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
模板模式需要一个抽象类和继承该抽象类的子类来实现。同样使用出门买东西的情景:出门->买东西->回来。
我们不关心怎么出去和回来,所以把出门和回来的方法写在抽象类中,然后将买东西这个方法抽象化放在抽象类中以便子类实现。
接着创建子类继承抽象类,买一种东西可以创建一个新类,从而实现抽象方法。
而策略模式是使用委托方法实现的,需要一个用来实现
方法的接口存在。
模板模式和策略模式通常可以互相替换。
九、命令设计模式
p211
命令模式的结构
顾名思义,命令模式就是对命令的封装,首先来看一下命令模式类图中的基本结构:
Command类:是一个抽象类,类中对需要执行的命令进行声明,一般来说要对外公布一个execute方法用来执行命令。
ConcreteCommand类:Command类的实现类,对抽象类中声明的方法进行实现。
Client类:最终的客户端调用类。
以上三个类的作用应该是比较好理解的,下面我们重点说一下Invoker类和Recevier类。
Invoker类:调用者,负责调用命令。
Receiver类:接收者,负责接收命令并且执行命令。
命令模式的精髓所在:把命令的调用者与执行者分开,使双方不必关心对方是如何操作
十、继承内部类
p212
当继承内部类的时候必须在构造器中传入外围类的引用和外围类的super;
第十一章(持有对象)
一、各个容器
p220
ArrayList LinkedList 都是按插入顺序存放数据
ArrayList在随机访问速度上比较快,而LinkedList在插入和删除数据比较有优势,具有Queue,Stack的特性。
HashSet TreeSet LinkedHashSet
HashMap TreeMap LinkedHashMap
通用点:Hash开头的容器都是通过Hash值来查找数据,所以特点是无序但速度快;
Tree开头的容器都会将存入的数据进行升序排列;
Linked则是按插入的顺序进行排序。
(后面17章会介绍其原理)
二、迭代器
p226
迭代器具有以下特性:
1)创建代价小
2)单向移动
3)next()获取下一个对象,hasNext()判断是否具有下一个对象,remove()移除当前对象。
(ListIterator作为List特有的迭代器,具有双向移动功能,其对应方法为hasPrevious(),previous())
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.TreeSet;
public class CrossContainerIteration {
public static void display(Iterator<Pet> it) {
while (it.hasNext()) {
Pet p = it.next();
System.out.println(p.id() + ":" + p + " ");
}
System.out.println();
}
public static void main(String[] args) {
// TODO Auto-generated method stub
List<Pet> pets = new ArrayList<>();
pets.add(new Pet("cat"));
pets.add(new Pet("dog"));
pets.add(new Pet("bird"));
pets.add(new Pet("fish"));
pets.add(new Pet("pig"));
LinkedList<Pet> petsLL = new LinkedList<>(pets);
HashSet<Pet> petsHS = new HashSet<>(pets);
TreeSet<Pet> petsTS = new TreeSet<>(pets);
display(pets.iterator());
display(petsLL.iterator());
display(petsHS.iterator());
display(petsTS.iterator());
}
}
/*0:Pet cat
1:Pet dog
2:Pet bird
3:Pet fish
4:Pet pig
0:Pet cat
1:Pet dog
2:Pet bird
3:Pet fish
4:Pet pig
3:Pet fish
1:Pet dog
4:Pet pig
2:Pet bird
0:Pet cat
2:Pet bird
0:Pet cat
1:Pet dog
3:Pet fish
4:Pet pig */
```
### 二、栈
`p230`
后进先出
通常可以使用LinkedList来实现Stack的功能
```java
public class Stack<T> {
private LinkedList<T> storge = new LinkedList<>();
public void push(T v){
storge.addFirst(v);
}
public T peek(){
return storge.getFirst();
}
public T pop(){
return storge.removeFirst();
}
public boolean empty(){
return storge.isEmpty();
}
}
```
### 三、队列
`p236 `
Queue 先进先出
同样可以使用LinkedList来实现功能,由于其实现了Queue接口,可以将其向上转型。
```java
public class QueueDemo {
public static void printQ(Queue queue) {
while (queue.peek() != null)
System.out.print(queue.remove() + " ");
System.out.println();
}
public static void main(String[] args) {
Queue<Integer> queue = new LinkedList<Integer>();
Random rand = new Random(47);
for (int i = 0; i < 10; i++)
queue.offer(rand.nextInt(i + 10));
printQ(queue);
Queue<Character> qc = new LinkedList<Character>();
for (char c : "Brontosaurus".toCharArray())
qc.offer(c);
printQ(qc);
}
} /*
* Output: 8 1 1 1 5 14 3 1 0 1 B r o n t o s a u r u s
*/
```
### 三、优先队列
`p237`
PriorityQueue
简单来说就是具有排序功能的队列,下一个弹出的元素是在队列中优先级最高的一个。使用Comparator比较器来进行比较。
```java
public class PriorityQueueDemo {
public static void main(String[] args) {
PriorityQueue<Integer> priorityQueue = new PriorityQueue<Integer>();
Random rand = new Random(47);
for (int i = 0; i < 10; i++)
priorityQueue.offer(rand.nextInt(i + 10));
QueueDemo.printQ(priorityQueue);
/*
* Output: 0 1 1 1 1 1 3 5 8 14 1 1 2 3 3 9 9 14 14 18 18 20 21 22 23 25 25 25
*/
List<Integer> ints = Arrays.asList(25, 22, 20, 18, 14, 9, 3, 1, 1, 2,
3, 9, 14, 18, 21, 23, 25);
priorityQueue = new PriorityQueue<Integer>(ints);
QueueDemo.printQ(priorityQueue);
priorityQueue = new PriorityQueue<Integer>(ints.size(),
Collections.reverseOrder());
priorityQueue.addAll(ints);
QueueDemo.printQ(priorityQueue);
/*
* 25 23 22 21 20 18 18 14 14 9 9 3 3 2 1 1
*/
String fact = "EDUCATION SHOULD ESCHEW OBFUSCATION";
List<String> strings = Arrays.asList(fact.split(""));
PriorityQueue<String> stringPQ = new PriorityQueue<String>(strings);
QueueDemo.printQ(stringPQ);
stringPQ = new PriorityQueue<String>(strings.size(),
Collections.reverseOrder());
stringPQ.addAll(strings);
QueueDemo.printQ(stringPQ);
Set<Character> charSet = new HashSet<Character>();
for (char c : fact.toCharArray())
charSet.add(c); // Autoboxing
PriorityQueue<Character> characterPQ = new PriorityQueue<Character>(
charSet);
QueueDemo.printQ(characterPQ);
}
/*
* A A B C C C D D E E E F H H I I L N
* N O O O O S S S T T U U U W W U U U T T S S S O O O O N N L I I H H F E E E D
* D C C C B A A A B C D E F H I L N O S T U W
*/
}
```
**总结:四种容器List Set Queue Map**
## 第十二章(通过异常处理错误)
**Java的基本理念是“结构不佳的代码不能运行**
### 自定义异常
`p252`
我们可以继承Exception类来自定义异常,在自定义异常类中可以添加一些动作,比如写入日志等等。
```java
class MyException extends Exception {
public MyException() {}
public MyException(String msg) { super(msg); }
}
public class FullConstructors {
public static void f() throws MyException {
System.out.println("Throwing MyException from f()");
throw new MyException();
}
public static void g() throws MyException {
System.out.println("Throwing MyException from g()");
throw new MyException("Originated in g()");
}
public static void main(String[] args) {
try {
f();
} catch(MyException e) {
e.printStackTrace(System.out);
}
try {
g();
} catch(MyException e) {
e.printStackTrace(System.out);
}
}
} /* Output:
Throwing MyException from f()
MyException
at FullConstructors.f(FullConstructors.java:11)
at FullConstructors.main(FullConstructors.java:19)
Throwing MyException from g()
MyException: Originated in g()
at FullConstructors.g(FullConstructors.java:15)
at FullConstructors.main(FullConstructors.java:24)
*///:~
```
异常也是类的一种,所以我们可以扩展使其获得更强大的功能。
```java
class MyException2 extends Exception {
private int x;
public MyException2() {
}
public MyException2(String msg) {
super(msg);
}
public MyException2(String msg, int x) {
super(msg);
this.x = x;
}
public int val() {
return x;
}
public String getMessage() {
return "Detail Message: " + x + " " + super.getMessage();
}
}
public class ExtraFeatures {
public static void f() throws MyException2 {
System.out.println("Throwing MyException2 from f()");
throw new MyException2();
}
public static void g() throws MyException2 {
System.out.println("Throwing MyException2 from g()");
throw new MyException2("Originated in g()");
}
public static void h() throws MyException2 {
System.out.println("Throwing MyException2 from h()");
throw new MyException2("Originated in h()", 47);
}
public static void main(String[] args) {
try {
f();
} catch (MyException2 e) {
e.printStackTrace(System.out);
}
try {
g();
} catch (MyException2 e) {
e.printStackTrace(System.out);
}
try {
h();
} catch (MyException2 e) {
e.printStackTrace(System.out);
System.out.println("e.val() = " + e.val());
}
}
} /*
Throwing MyException2 from f()
chapter12.MyException2: Detail Message: 0 null
at chapter12.ExtraFeatures.f(ExtraFeatures.java:32)
at chapter12.ExtraFeatures.main(ExtraFeatures.java:47)
Throwing MyException2 from g()
chapter12.MyException2: Detail Message: 0 Originated in g()
at chapter12.ExtraFeatures.g(ExtraFeatures.java:37)
at chapter12.ExtraFeatures.main(ExtraFeatures.java:52)
Throwing MyException2 from h()
chapter12.MyException2: Detail Message: 47 Originated in h()
at chapter12.ExtraFeatures.h(ExtraFeatures.java:42)
at chapter12.ExtraFeatures.main(ExtraFeatures.java:57)
e.val() = 47
*/// :~
```
`p257`
所有的异常都可以由Exception进行捕获,可以通过Exception打印发生错误时所获取的信息。
### Rethrow
`p258`
可以通过throw将catch到的异常重新抛出,不过异常里面是原来异常抛出的调用栈信息。可以使用 fillInStackTrace()更新,调用 fillInStackTrace那一行就成了异常的新发生地。
```java
public class Rethrowing {
public static void f() throws Exception {
System.out.println("originating the exception in f()");
throw new Exception("thrown from f()");
}
public static void g() throws Exception {
try {
f();
} catch(Exception e) {
System.out.println("Inside g(),e.printStackTrace()");
e.printStackTrace(System.out);
throw e;
}
}
public static void h() throws Exception {
try {
f();
} catch(Exception e) {
System.out.println("Inside h(),e.printStackTrace()");
e.printStackTrace(System.out);
throw (Exception)e.fillInStackTrace();
}
}
public static void main(String[] args) {
try {
g();
} catch(Exception e) {
System.out.println("main: printStackTrace()");
e.printStackTrace(System.out);
}
try {
h();
} catch(Exception e) {
System.out.println("main: printStackTrace()");
e.printStackTrace(System.out);
}
}
} /* Output:
originating the exception in f()
Inside g(),e.printStackTrace()
java.lang.Exception: thrown from f()
at Rethrowing.f(Rethrowing.java:7)
at Rethrowing.g(Rethrowing.java:11)
at Rethrowing.main(Rethrowing.java:29)
main: printStackTrace()
java.lang.Exception: thrown from f()
at Rethrowing.f(Rethrowing.java:7)
at Rethrowing.g(Rethrowing.java:11)
at Rethrowing.main(Rethrowing.java:29)
originating the exception in f()
Inside h(),e.printStackTrace()
java.lang.Exception: thrown from f()
at Rethrowing.f(Rethrowing.java:7)
at Rethrowing.h(Rethrowing.java:20)
at Rethrowing.main(Rethrowing.java:35)
main: printStackTrace()
java.lang.Exception: thrown from f()
at Rethrowing.h(Rethrowing.java:24)
at Rethrowing.main(Rethrowing.java:35)
*///:~
```
### 异常链
`p260`
Throwable的子类可以接受一个cause对象作为参数,cause表示原始异常,这样可以通过把原始异常传递给新的异常使得可以追踪所有连接起来的异常。
在Throwable子类中,只有三种基本的异常类提供了带cause参数的构造器,分别为Error,Exception和RuntimeException。如果要把其他类型的异常链接起来,应该使用initCause()方法。
### return
`p268`
return 与 fianl共用时,即使在try里面实行return,finally里面的代码还是会运行,
而在final里面使用热力return 的话,即使抛出了异常也不会产生任何输出。
### 异常使用指南
`p281`
1. 在恰当的级别处理问题。
2. 解决问题并且重新调用产生异常的方法。
3. 进行少许修补,然后绕过异常发生的地方继续执行。
4. 用别的数据进行计算,以代替异常发生的地方继续执行。
5. 吧当前运行环境下能做的事情尽量做完,然后将相同的异常重抛到更高层。
6. 吧当前运行环境下能做的事情尽量做完,然后将不同的异常重抛到更高层。
7. 终止程序
8. 进行简化。
9. 蓝类库和程序更安全。
--------
[《Java编程思想》阅读笔记一](http://www.cnblogs.com/cpacm/p/5568405.html)