`

《Java 5 Tiger 程序高手秘笈》读书笔记

    博客分类:
  • Book
阅读更多

从书名上来说, 这本书可谓是典型的标题党, 整本书不过是来讲解JDK5.0的一些新功能而已, 却标榜为高手秘笈, 难道掌握了这些新功能就成高手了?这个是台湾翻译版, 相比大陆的一些技术性翻译图书来说, 要轻松活泼的多, 问题在于, 很多术语大陆跟台湾是不同的, 而简单的还可以转换成大陆的说法, 而有些又沿用了台湾的说法, 搞得读起来偶尔需要做一个转换的麻烦. 是一本了解JDK5.0新特性的不可多得的好书. 大部分是好的, 不过对里面的关于JDK5.0中非常重要的改进:并发, 却是虎头蛇尾. 讲的太简单啦. 也许是这部分的内容太庞大, 太高深了, 需要高手们自己去摸索吧. 如果非要去了解JDK1.5的新特性, 也许<java编程思想>是一个更好的替代选择.

Arrays
注意是Arrays, 不是一般是array哦, 它提供toString()方法, 输出内容的可读性更强.另外提供了一个deepToString()用来对多维数组进行打印输出.
Arrays还提供了deepEquals()这个方法来比较多维数组.

Queue
在需要FIFO功能的时候使用.
如果要加入元素, 调用offer(), 大部分的queue都是有固定大小的, 如果已经满了的话, 调用add()会抛出异常, 而offer()方法在无法加入元素时返回false. 这样更符合queue的使用方式.同样, remove()在queue为空时会抛出异常, 较好的选择是使用poll()方法, 当没有东西时返回null.这两种方法都会试着从queue的首端移除元素, 如果不移除的话, 可以使用element()或者peek().
在JDK中, LinkedList被改造来实现Queue接口, 这样既可以当List来使用, 也可以当Queue来使用了.
PriorityQueue使用了一个Comparator用来对Queue进行排序处理.

StringBuilder
在不需要线程安全的场景下代替StringBuffer.

泛型
泛型是不能向上转型的.
例如:
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()所作的任何调用. 也就是说, 将泛型类型赋予通配符会使得其基本上变成只读的了.

枚举
枚举的几个重要观点:
枚举不是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();
        }
    }
}

方式二更简单, 建议使用.

autoboxing and unboxing
boxing指的是将基本类型转换成对应的wrapper类型, 因为这个过程是自动发生的, 所以被称之为autoboxing.
将一个null复制给wrapper类型, 然后包装类型unboxing给基本类型, 因为null对基本类型是不合法的, 因此会抛出NullPointerexception.

变长参数
使用变长参数的一个好处是, 不传进一个参数也是合法的选项.

annotation
四种标准的注解的注解:
Target: 指定哪个程序单元可以有其所定义的annotation
Retention: 告诉编译器是否要将annotation丢弃掉, 还是保留在编译后的class文件中.
Documented:用来指示在javadoc中显示注解信息.
Inherited:指出被annotated的类是通过继承得到的.也就是说在父类中标注之后, 子类也会具有注解信息.

注解的用法:
检查注解最简单的方式是使用isAnnotationPresent()这个方法
如果你不想获得继承的注解, 可以使用getDeclaredAnnotations()来代替getAnnoations()方法.

Thread
Executor
定义了为对象提供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().

ExecutorService
是Executor的子接口, 它新增了两个方法:
shutdown() 停掉service, 但会先让service尝试完成正在运行以及queue的task.  在调用之后不再接受新的task
shutdownNow() 停掉service. 且不允许queued或运行中的task完成. 当前运行中的task基本上都直接砍掉, 且queued的task会由一个List<Runnable>返回给程序使用.

Callable接口
跟Runnable有些类似, 不过它是个泛型接口, 唯一的方法call()有返回值.最好的使用方式是将其传入ExecutorService的submit()方法中, 让他来处理要执行动作.

Future
在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());
	}
}

打印结果:
引用
false
6217083424191153481570895074724388259417810995026131178158146825139866421109872619
1045671132998288097986820898386217182809301689339130641309911770755670755344821075
2073112662019386477429940865298560762994916324501566002958620407054605666269049301
5883236300481581686155857207823807806745314803399643089702659102174401932674300023
8786285913362882940670155842601215059649362441481792688794501993902335629941492523
8120941551625116634961531849551935952280326722784983388196863767821993032341748383
5555428211355443725306713786271262797261172182804027465110377216775272218176499231
887586789834706174044612998917855583677079
true


FutureTask
可以不用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());


ScheduleExecutor
按指定的时间来执行任务.
我又抄一段程序来说明下:
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的执行.

Synchronizing
在同步方面, 有四个类:
Semaphore
一个Semaphore代表一个或者多个通行证. thread调用acquire()以从semaphore中取得通行证, 并在使用完通行证时调用release(), 如果得不到通行证, acquire()将被block住, 换句话说,semaphore就像party门口的保镖, 仅允许固定数量的人同时进场狂欢.
acquire()有数种变化, 可以在遇到block的时候能够对接下来怎么做有些控制权.这包括tryAcquire().它要么没有被block, 不然就等待指定的timeout, acquireUninterruptibly(),就算遇到InterruptionException也不罢手.

CountDownLatch
用来block住thread直到一组特定的操作完成. 当latch被创建之后是关闭的, 任何调用latch的await()方法都会被block, 知道latch打开位置, 这可以让各个thread都在等待latch, 以确保所有的操作都在继续以前完成.
执行那些必要操作的thread可以调用countDown()来递减在构造时指定的计数器, 当latch归零 时候, latch打开, 而等在await()那里的thread则开始继续执行.

Exchanger

提供了两个thread的汇合点. 最常见的Exchanger运行方式是, 当生产者将数据填入缓存区时, 消费者从其他的来源将资料消耗掉, 一点生产者填满它的缓冲区, 而消费者也把自己的缓冲区消耗掉, 二者就可以交换缓冲区并继续执行, 也就是双发都必须完成手头的工作.

CyclicBarrier
是另外一种thread汇合点工具, 用来处理多个thread必须回合同一个点的情况,在创建barrier的时候会指定thread的数目, 然后各个thread在它遇到准备汇合点的时候await(), 这会block该thread, 直到所有相关的thread都到齐为止.一旦所有的thread都调用了await(), block就会停掉, 所有thread就可以继续. 此外, barrier是全有全无的, 如果一个thread出现异常失败并提前离开barrier, 则所有的thread都会离开.
分享到:
评论

相关推荐

    JAVA 5.0 TIGER程序高手秘笈.rar

    《JAVA 5.0 TIGER程序高手秘笈》是一本深入探讨Java 5.0(也称为Tiger版本)编程技术的专业书籍。这个版本在Java的发展历程中扮演了重要角色,引入了一系列创新特性,极大地提升了开发效率和代码质量。以下是对其中...

    Java 5.0 Tiger程序高手秘笈.rar

    本资料集《Java 5.0 Tiger程序高手秘笈》旨在帮助开发者深入理解并熟练掌握这一版本的Java技术。 一、泛型(Generics) Java 5.0最重要的特性之一就是泛型,它允许在类、接口和方法声明中使用类型参数,从而增强了...

    Java5.0 Tiger程序高手秘笈

    Java5.0 Tiger程序高手秘笈 Java5.0 Tiger程序高手秘笈 Java5.0 Tiger程序高手秘笈

    Java5.0 Tiger程序高手秘笈(含源码)

    本书《Java5.0 Tiger程序高手秘笈》正是为了帮助开发者掌握这些新特性而编写,结合源码分析,将有助于深入理解Java 5.0的核心改进。 1. **泛型**:Java 5.0引入了泛型,这是一种类型安全机制,允许在编译时检查类型...

    Java 5.0 Tiger程序高手秘笈(Part_2)

    Java 5.0 TIiger程序高手秘笈.pdf 下载两个压缩包后双击其中一个,就打开了

    Java 5.0 Tiger程序高手秘笈(PDF) Java.rar

    《Java 5.0 Tiger程序高手秘笈》是一本针对Java 5.0(也称为Java SE 5.0)的高级开发指南,其核心目标是帮助开发者掌握Tiger版本中的新特性和优化技术,从而提升编程技能和实战能力。在Java 5.0中,许多重要的更新和...

    JAVA 5.0 TIGER程序高手秘笈.

    JAVA 5.0 TIGER程序高手秘笈.

    JAVA 5.0 TIGER程序高手秘笈

    JAVA 5.0 TIGER程序高手秘笈.pdf

    java 5.0 tiger程序高手秘笈.part2

    java 5.0 tiger程序高手秘笈.part2

    Java5.0Tiger程序高手秘笈PDF.rar

    《Java 5.0 Tiger 程序高手秘笈》是一本专为Java程序员设计的进阶学习资料,旨在帮助读者深入理解Java 5.0(也被称为Tiger)版本的新特性和高级编程技巧。Java 5.0是Java发展历程中的一个重要里程碑,引入了许多关键...

    java 5.0 tiger程序高手秘笈.part1.rar

    java 5.0 tiger程序高手秘笈.part1

    JAVA.5.0.TIGER程序高手秘笈

    《JAVA.5.0.TIGER程序高手秘笈》是一本深入探讨Java 5.0(也称为Java Tiger)编程技术的专业书籍,属于"notebook系列",旨在帮助程序员提升在Java平台上的开发技能。该书可能涵盖了Java 5.0的新特性、核心概念以及...

Global site tag (gtag.js) - Google Analytics