[align=center][img]http://images.china-pub.com/ebook25001-30000/27680/shupi.jpg[/img][/align]
从书名上来说, 这本书可谓是典型的标题党, 整本书不过是来讲解JDK5.0的一些新功能而已, 却标榜为高手秘笈, 难道掌握了这些新功能就成高手了?这个是台湾翻译版, 相比大陆的一些技术性翻译图书来说, 要轻松活泼的多, 问题在于, 很多术语大陆跟台湾是不同的, 而简单的还可以转换成大陆的说法, 而有些又沿用了台湾的说法, 搞得读起来偶尔需要做一个转换的麻烦. 是一本了解JDK5.0新特性的不可多得的好书. 大部分是好的, 不过对里面的关于JDK5.0中非常重要的改进:并发, 却是虎头蛇尾. 讲的太简单啦. 也许是这部分的内容太庞大, 太高深了, 需要高手们自己去摸索吧. 如果非要去了解JDK1.5的新特性, 也许<java编程思想>是一个更好的替代选择.
[b]Arrays[/b]
注意是Arrays, 不是一般是array哦, 它提供toString()方法, 输出内容的可读性更强.另外提供了一个deepToString()用来对多维数组进行打印输出.
Arrays还提供了deepEquals()这个方法来比较多维数组.
[b]Queue[/b]
在需要FIFO功能的时候使用.
如果要加入元素, 调用offer(), 大部分的queue都是有固定大小的, 如果已经满了的话, 调用add()会抛出异常, 而offer()方法在无法加入元素时返回false. 这样更符合queue的使用方式.同样, remove()在queue为空时会抛出异常, 较好的选择是使用poll()方法, 当没有东西时返回null.这两种方法都会试着从queue的首端移除元素, 如果不移除的话, 可以使用element()或者peek().
在JDK中, LinkedList被改造来实现Queue接口, 这样既可以当List来使用, 也可以当Queue来使用了.
PriorityQueue使用了一个Comparator用来对Queue进行排序处理.
[b]StringBuilder[/b]
在不需要线程安全的场景下代替StringBuffer.
[b]泛型[/b]
泛型是不能向上转型的.
例如:
我们无法将其转换成一个List<Number>类型, 甚至List<Object>.
泛型中很重要的一个概念就是erasure(擦除), 在jdk5中, 泛型是一个编译期的程序, 而且所有的类型信息在编译期将被处理掉. 一旦class被编译之后, 类型信息将会被擦除掉.
比如代码这样写:
编译之后将变成这样:
对于可以接受任意字符的List容器来说, 可以在泛型中以"?"的形式来表达通配符, 我的意思是, 如果不知道确切类型, 而有需要使用到了泛型, 可以用问号来指代任意泛型, 以免出现unchecked警告.但是这样还有一个问题, 因为编译期无法检查以确保类型安全性, 所以它拒绝对List<?>的add(), addAll()与set()所作的任何调用. 也就是说, 将泛型类型赋予通配符会使得其基本上变成只读的了.
[b]枚举[/b]
枚举的几个重要观点:
枚举不是class
枚举继承自java.lang.Enum
枚举类型不是Integer
枚举没有public的构造函数
枚举值是public, static和final
枚举值以";"结束
不能在枚举值之前声明你的变量
枚举的构造函数必须是private或default的
枚举可以用==或equals来做比较.
枚举实现了java.lang.Comparable接口
枚举的toString()可以被改写, 而且一般会被改写.
枚举的valueOf()与toString()互补出现, 因此改写了toString()方法, 必须同时改写valueOf()方法, 以保持二者一致.
EnumMap是一个以Enum为key的Map而已.
EnumSet是一个以Enum为元素的Set而已.它有很多工具方法比较有用:
allOf(Class elementType) 返回一个指定类型中所有元素组成的EnumSet
compentOf(EnumSet e) 返回与制定enumset类型相同的新的EnumSet.
copyOf(Collection c) 返回指定collection组成新的EnumSet
noneOf(Class elementType) 返回没有任何值的新的EnumSet
of(E... e) 使用指定元素创建EnumSet
range(E from, E to) 由范围内的值创建EnumSet, 不过最好使用of来替换range
如果Enum包含了更多了内容, 可以通过接口的方式将这些信息暴露出去, 也就是让enum实现指定的接口.
enum内部业务的实现方式有两种.
方式1:
方式2:
方式二更简单, 建议使用.
[b]autoboxing and unboxing[/b]
boxing指的是将基本类型转换成对应的wrapper类型, 因为这个过程是自动发生的, 所以被称之为autoboxing.
将一个null复制给wrapper类型, 然后包装类型unboxing给基本类型, 因为null对基本类型是不合法的, 因此会抛出NullPointerexception.
[b]变长参数[/b]
使用变长参数的一个好处是, 不传进一个参数也是合法的选项.
[b]annotation[/b]
四种标准的注解的注解:
Target: 指定哪个程序单元可以有其所定义的annotation
Retention: 告诉编译器是否要将annotation丢弃掉, 还是保留在编译后的class文件中.
Documented:用来指示在javadoc中显示注解信息.
Inherited:指出被annotated的类是通过继承得到的.也就是说在父类中标注之后, 子类也会具有注解信息.
注解的用法:
检查注解最简单的方式是使用isAnnotationPresent()这个方法
如果你不想获得继承的注解, 可以使用getDeclaredAnnotations()来代替getAnnoations()方法.
[b]Thread[/b]
[b]Executor [/b]
定义了为对象提供Thread的方法, 并且由对象来处理时机控制与运行Thread, 而不是强迫你吧这些逻辑写在thread的class中. 你把Runnable对象加入到Executor中(实际上是加到内部的queue中), 然后Executor使用它自己的thread来抽出对象并运行.
Executors工厂方法创建的Executor有:
newSingleThreadExecutor()产生大小为1的pool, 每次只会执行一个task
newFixedThreadPool(int poolSize)创建指定thread数目来执行task的Executor.
newCachedThreadPool()会使用它所需的thread数目于它的queue中运行对象. 它会反复的利用可用的thread, 也会创建新的thread.
newScheduledThreadPool(), newSingleScheduledExecutor().
[b]ExecutorService[/b]
是Executor的子接口, 它新增了两个方法:
shutdown() 停掉service, 但会先让service尝试完成正在运行以及queue的task. 在调用之后不再接受新的task
shutdownNow() 停掉service. 且不允许queued或运行中的task完成. 当前运行中的task基本上都直接砍掉, 且queued的task会由一个List<Runnable>返回给程序使用.
[b]Callable接口[/b]
跟Runnable有些类似, 不过它是个泛型接口, 唯一的方法call()有返回值.最好的使用方式是将其传入ExecutorService的submit()方法中, 让他来处理要执行动作.
[b]Future[/b]
在call()返回值的时候, 必须要有个方法来取回值, 而不需要马上就持有它(这会强迫你的程序等待), 这就是Future的用途. 他能让你操作Callable对象,包括取得call()的值与停止它的执行, 而不会导致整个程序停下来.它的方法包括:
cancel(boolean mayInterruptIfRunning) 会尝试取消task的执行. 如果task还没有开始的话, 他会被取消, 如果已经开始的话, mayInterruptIfRunning会用来指出它是否被取消.
V get() 会返回task的结果, 有需要的时候,会等到完成为止.
V get(long timeout, TimeUnit unit) 如果指定的等待时间内完成的话, 该方法会返回task的结果.
boolean isCancelled() 指示在正常完成前是否被取消.
boolean isDone()是否完成.
我抄了个Callable结合ExecutorService和Future的例子:
打印结果:
[quote]false
6217083424191153481570895074724388259417810995026131178158146825139866421109872619
1045671132998288097986820898386217182809301689339130641309911770755670755344821075
2073112662019386477429940865298560762994916324501566002958620407054605666269049301
5883236300481581686155857207823807806745314803399643089702659102174401932674300023
8786285913362882940670155842601215059649362441481792688794501993902335629941492523
8120941551625116634961531849551935952280326722784983388196863767821993032341748383
5555428211355443725306713786271262797261172182804027465110377216775272218176499231
887586789834706174044612998917855583677079
true[/quote]
[b]FutureTask[/b]
可以不用ExecutorService不用参与的执行task, 还是上面的例子, 换一种写法而已:
[b]ScheduleExecutor[/b]
按指定的时间来执行任务.
我又抄一段程序来说明下:
scheduleAtFixedRate()方法表示从指定的时间(0s)之后按照指定的频率(10s)执行, 这里时间单位是s(SECONDS)
schedule()方法与前一个方法不同之处在于它只执行一次, 他会在1小时(60* 60)之后, 取消TimePrinter的执行.
[b]Synchronizing[/b]
在同步方面, 有四个类:
[b]Semaphore[/b]
一个Semaphore代表一个或者多个通行证. thread调用acquire()以从semaphore中取得通行证, 并在使用完通行证时调用release(), 如果得不到通行证, acquire()将被block住, 换句话说,semaphore就像party门口的保镖, 仅允许固定数量的人同时进场狂欢.
acquire()有数种变化, 可以在遇到block的时候能够对接下来怎么做有些控制权.这包括tryAcquire().它要么没有被block, 不然就等待指定的timeout, acquireUninterruptibly(),就算遇到InterruptionException也不罢手.
[b]CountDownLatch[/b]
用来block住thread直到一组特定的操作完成. 当latch被创建之后是关闭的, 任何调用latch的await()方法都会被block, 知道latch打开位置, 这可以让各个thread都在等待latch, 以确保所有的操作都在继续以前完成.
执行那些必要操作的thread可以调用countDown()来递减在构造时指定的计数器, 当latch归零 时候, latch打开, 而等在await()那里的thread则开始继续执行.
[b]
Exchanger[/b]
提供了两个thread的汇合点. 最常见的Exchanger运行方式是, 当生产者将数据填入缓存区时, 消费者从其他的来源将资料消耗掉, 一点生产者填满它的缓冲区, 而消费者也把自己的缓冲区消耗掉, 二者就可以交换缓冲区并继续执行, 也就是双发都必须完成手头的工作.
[b]CyclicBarrier[/b]
是另外一种thread汇合点工具, 用来处理多个thread必须回合同一个点的情况,在创建barrier的时候会指定thread的数目, 然后各个thread在它遇到准备汇合点的时候await(), 这会block该thread, 直到所有相关的thread都到齐为止.一旦所有的thread都调用了await(), block就会停掉, 所有thread就可以继续. 此外, barrier是全有全无的, 如果一个thread出现异常失败并提前离开barrier, 则所有的thread都会离开.
从书名上来说, 这本书可谓是典型的标题党, 整本书不过是来讲解JDK5.0的一些新功能而已, 却标榜为高手秘笈, 难道掌握了这些新功能就成高手了?这个是台湾翻译版, 相比大陆的一些技术性翻译图书来说, 要轻松活泼的多, 问题在于, 很多术语大陆跟台湾是不同的, 而简单的还可以转换成大陆的说法, 而有些又沿用了台湾的说法, 搞得读起来偶尔需要做一个转换的麻烦. 是一本了解JDK5.0新特性的不可多得的好书. 大部分是好的, 不过对里面的关于JDK5.0中非常重要的改进:并发, 却是虎头蛇尾. 讲的太简单啦. 也许是这部分的内容太庞大, 太高深了, 需要高手们自己去摸索吧. 如果非要去了解JDK1.5的新特性, 也许<java编程思想>是一个更好的替代选择.
[b]Arrays[/b]
注意是Arrays, 不是一般是array哦, 它提供toString()方法, 输出内容的可读性更强.另外提供了一个deepToString()用来对多维数组进行打印输出.
Arrays还提供了deepEquals()这个方法来比较多维数组.
[b]Queue[/b]
在需要FIFO功能的时候使用.
如果要加入元素, 调用offer(), 大部分的queue都是有固定大小的, 如果已经满了的话, 调用add()会抛出异常, 而offer()方法在无法加入元素时返回false. 这样更符合queue的使用方式.同样, remove()在queue为空时会抛出异常, 较好的选择是使用poll()方法, 当没有东西时返回null.这两种方法都会试着从queue的首端移除元素, 如果不移除的话, 可以使用element()或者peek().
在JDK中, LinkedList被改造来实现Queue接口, 这样既可以当List来使用, 也可以当Queue来使用了.
PriorityQueue使用了一个Comparator用来对Queue进行排序处理.
[b]StringBuilder[/b]
在不需要线程安全的场景下代替StringBuffer.
[b]泛型[/b]
泛型是不能向上转型的.
例如:
List<Integer> ints = new ArrayList<Integer>();
我们无法将其转换成一个List<Number>类型, 甚至List<Object>.
泛型中很重要的一个概念就是erasure(擦除), 在jdk5中, 泛型是一个编译期的程序, 而且所有的类型信息在编译期将被处理掉. 一旦class被编译之后, 类型信息将会被擦除掉.
比如代码这样写:
List<String> strings = new LinkedList<String>();
编译之后将变成这样:
List strings = new LinkedList();
对于可以接受任意字符的List容器来说, 可以在泛型中以"?"的形式来表达通配符, 我的意思是, 如果不知道确切类型, 而有需要使用到了泛型, 可以用问号来指代任意泛型, 以免出现unchecked警告.但是这样还有一个问题, 因为编译期无法检查以确保类型安全性, 所以它拒绝对List<?>的add(), addAll()与set()所作的任何调用. 也就是说, 将泛型类型赋予通配符会使得其基本上变成只读的了.
[b]枚举[/b]
枚举的几个重要观点:
枚举不是class
枚举继承自java.lang.Enum
枚举类型不是Integer
枚举没有public的构造函数
枚举值是public, static和final
枚举值以";"结束
不能在枚举值之前声明你的变量
枚举的构造函数必须是private或default的
枚举可以用==或equals来做比较.
枚举实现了java.lang.Comparable接口
枚举的toString()可以被改写, 而且一般会被改写.
枚举的valueOf()与toString()互补出现, 因此改写了toString()方法, 必须同时改写valueOf()方法, 以保持二者一致.
EnumMap是一个以Enum为key的Map而已.
EnumSet是一个以Enum为元素的Set而已.它有很多工具方法比较有用:
allOf(Class elementType) 返回一个指定类型中所有元素组成的EnumSet
compentOf(EnumSet e) 返回与制定enumset类型相同的新的EnumSet.
copyOf(Collection c) 返回指定collection组成新的EnumSet
noneOf(Class elementType) 返回没有任何值的新的EnumSet
of(E... e) 使用指定元素创建EnumSet
range(E from, E to) 由范围内的值创建EnumSet, 不过最好使用of来替换range
如果Enum包含了更多了内容, 可以通过接口的方式将这些信息暴露出去, 也就是让enum实现指定的接口.
enum内部业务的实现方式有两种.
方式1:
public enum Opcode {
PUSH(1) {
@Override
public void perform(StackMachine machine, int[] operands) {
machine.push(operands[0]);
}
},
ADD(0) {
@Override
public void perform(StackMachine machine, int[] operands) {
machine.push(machine.pop() + machine.pop());
}
};
int operands;
Opcode(int operands){
this.operands = operands;
}
public abstract void perform(StackMachine machine, int[] operands);
}
方式2:
public enum SwitchOpcode {
PUSH(1), ADD(0);
int operands;
SwitchOpcode(int operands) {
this.operands = operands;
}
public void perform(StackMachine machine, int[] operands) {
switch (this) {
case PUSH:
machine.push(operands[0]);
break;
case ADD:
machine.push(machine.pop() + machine.pop());
break;
default:
throw new UnsupportedOperationException();
}
}
}
方式二更简单, 建议使用.
[b]autoboxing and unboxing[/b]
boxing指的是将基本类型转换成对应的wrapper类型, 因为这个过程是自动发生的, 所以被称之为autoboxing.
将一个null复制给wrapper类型, 然后包装类型unboxing给基本类型, 因为null对基本类型是不合法的, 因此会抛出NullPointerexception.
[b]变长参数[/b]
使用变长参数的一个好处是, 不传进一个参数也是合法的选项.
[b]annotation[/b]
四种标准的注解的注解:
Target: 指定哪个程序单元可以有其所定义的annotation
Retention: 告诉编译器是否要将annotation丢弃掉, 还是保留在编译后的class文件中.
Documented:用来指示在javadoc中显示注解信息.
Inherited:指出被annotated的类是通过继承得到的.也就是说在父类中标注之后, 子类也会具有注解信息.
注解的用法:
检查注解最简单的方式是使用isAnnotationPresent()这个方法
如果你不想获得继承的注解, 可以使用getDeclaredAnnotations()来代替getAnnoations()方法.
[b]Thread[/b]
[b]Executor [/b]
定义了为对象提供Thread的方法, 并且由对象来处理时机控制与运行Thread, 而不是强迫你吧这些逻辑写在thread的class中. 你把Runnable对象加入到Executor中(实际上是加到内部的queue中), 然后Executor使用它自己的thread来抽出对象并运行.
Executors工厂方法创建的Executor有:
newSingleThreadExecutor()产生大小为1的pool, 每次只会执行一个task
newFixedThreadPool(int poolSize)创建指定thread数目来执行task的Executor.
newCachedThreadPool()会使用它所需的thread数目于它的queue中运行对象. 它会反复的利用可用的thread, 也会创建新的thread.
newScheduledThreadPool(), newSingleScheduledExecutor().
[b]ExecutorService[/b]
是Executor的子接口, 它新增了两个方法:
shutdown() 停掉service, 但会先让service尝试完成正在运行以及queue的task. 在调用之后不再接受新的task
shutdownNow() 停掉service. 且不允许queued或运行中的task完成. 当前运行中的task基本上都直接砍掉, 且queued的task会由一个List<Runnable>返回给程序使用.
[b]Callable接口[/b]
跟Runnable有些类似, 不过它是个泛型接口, 唯一的方法call()有返回值.最好的使用方式是将其传入ExecutorService的submit()方法中, 让他来处理要执行动作.
[b]Future[/b]
在call()返回值的时候, 必须要有个方法来取回值, 而不需要马上就持有它(这会强迫你的程序等待), 这就是Future的用途. 他能让你操作Callable对象,包括取得call()的值与停止它的执行, 而不会导致整个程序停下来.它的方法包括:
cancel(boolean mayInterruptIfRunning) 会尝试取消task的执行. 如果task还没有开始的话, 他会被取消, 如果已经开始的话, mayInterruptIfRunning会用来指出它是否被取消.
V get() 会返回task的结果, 有需要的时候,会等到完成为止.
V get(long timeout, TimeUnit unit) 如果指定的等待时间内完成的话, 该方法会返回task的结果.
boolean isCancelled() 指示在正常完成前是否被取消.
boolean isDone()是否完成.
我抄了个Callable结合ExecutorService和Future的例子:
public class RandomPrimeSearch implements Callable<BigInteger> {
private static final Random r = new SecureRandom();
private int bitSize;
public RandomPrimeSearch(int bitSize) {
this.bitSize = bitSize;
}
@Override
public BigInteger call() throws Exception {
return BigInteger.probablePrime(bitSize, r);
}
}
public class CallableTest {
public static void main(String[] args)throws Exception{
ExecutorService service = Executors.newFixedThreadPool(5);
Future<BigInteger> prime1 = service.submit(new RandomPrimeSearch(512));
Future<BigInteger> prime2 = service.submit(new RandomPrimeSearch(512));
Future<BigInteger> prime3 = service.submit(new RandomPrimeSearch(512));
Future<BigInteger> prime4 = service.submit(new RandomPrimeSearch(512));
System.out.println(prime1.isDone());
System.out.println(prime1.get().multiply(prime2.get()).multiply(prime3.get()).multiply(prime4.get()));
System.out.println(prime1.isDone());
}
}
打印结果:
[quote]false
6217083424191153481570895074724388259417810995026131178158146825139866421109872619
1045671132998288097986820898386217182809301689339130641309911770755670755344821075
2073112662019386477429940865298560762994916324501566002958620407054605666269049301
5883236300481581686155857207823807806745314803399643089702659102174401932674300023
8786285913362882940670155842601215059649362441481792688794501993902335629941492523
8120941551625116634961531849551935952280326722784983388196863767821993032341748383
5555428211355443725306713786271262797261172182804027465110377216775272218176499231
887586789834706174044612998917855583677079
true[/quote]
[b]FutureTask[/b]
可以不用ExecutorService不用参与的执行task, 还是上面的例子, 换一种写法而已:
FutureTask<BigInteger> task = new FutureTask<BigInteger>(new RandomPrimeSearch(512));
new Thread(task).start();
System.out.println(task.isDone());
System.out.println(task.get());
System.out.println(task.isDone());
[b]ScheduleExecutor[/b]
按指定的时间来执行任务.
我又抄一段程序来说明下:
public class ScheduleTest {
public static void main(String[] args)throws Exception{
ScheduledExecutorService schedule = Executors.newSingleThreadScheduledExecutor();
final ScheduledFuture<?> timeHandle = schedule.scheduleAtFixedRate(new TimePrinter(), 0, 10, TimeUnit.SECONDS);
schedule.schedule(new Runnable() {
@Override
public void run() {
timeHandle.cancel(false);
}}, 60* 60, TimeUnit.SECONDS);
}
}
class TimePrinter implements Runnable{
@Override
public void run() {
System.out.printf("Current time: %tr%n", new Date());
}
}
scheduleAtFixedRate()方法表示从指定的时间(0s)之后按照指定的频率(10s)执行, 这里时间单位是s(SECONDS)
schedule()方法与前一个方法不同之处在于它只执行一次, 他会在1小时(60* 60)之后, 取消TimePrinter的执行.
[b]Synchronizing[/b]
在同步方面, 有四个类:
[b]Semaphore[/b]
一个Semaphore代表一个或者多个通行证. thread调用acquire()以从semaphore中取得通行证, 并在使用完通行证时调用release(), 如果得不到通行证, acquire()将被block住, 换句话说,semaphore就像party门口的保镖, 仅允许固定数量的人同时进场狂欢.
acquire()有数种变化, 可以在遇到block的时候能够对接下来怎么做有些控制权.这包括tryAcquire().它要么没有被block, 不然就等待指定的timeout, acquireUninterruptibly(),就算遇到InterruptionException也不罢手.
[b]CountDownLatch[/b]
用来block住thread直到一组特定的操作完成. 当latch被创建之后是关闭的, 任何调用latch的await()方法都会被block, 知道latch打开位置, 这可以让各个thread都在等待latch, 以确保所有的操作都在继续以前完成.
执行那些必要操作的thread可以调用countDown()来递减在构造时指定的计数器, 当latch归零 时候, latch打开, 而等在await()那里的thread则开始继续执行.
[b]
Exchanger[/b]
提供了两个thread的汇合点. 最常见的Exchanger运行方式是, 当生产者将数据填入缓存区时, 消费者从其他的来源将资料消耗掉, 一点生产者填满它的缓冲区, 而消费者也把自己的缓冲区消耗掉, 二者就可以交换缓冲区并继续执行, 也就是双发都必须完成手头的工作.
[b]CyclicBarrier[/b]
是另外一种thread汇合点工具, 用来处理多个thread必须回合同一个点的情况,在创建barrier的时候会指定thread的数目, 然后各个thread在它遇到准备汇合点的时候await(), 这会block该thread, 直到所有相关的thread都到齐为止.一旦所有的thread都调用了await(), block就会停掉, 所有thread就可以继续. 此外, barrier是全有全无的, 如果一个thread出现异常失败并提前离开barrier, 则所有的thread都会离开.