1. Generic Types
在5.0以前,当我们需要从集合类当中提取出某个元素时,需要显式的对他进行下塑造型,如
ArrayList list = new ArrayList();
list1.add(0, new Integer(42));
int total = ((Integer)list1.get(0)).intValue();
在5.0当中,通过使用Generic Types,即将集合类(Collection)的声明写成Collection<E>,这样我们就可以在使用的时候不需要再进行显示的造型
ArrayList<Integer> list = new ArrayList<Integer>();
list.add(0, new Integer(42));
int total = list.get(0).intValue();
2. Autoboxing and Auto-Unboxing of Primitive Types
由于集合类(Collection)里面只能保存从Object当中继承而来的对象,所以对于基本类型(Primitive Type)的变量,如int,float等,在添加到Collection当中时,需要使用他们对应的封装类Integer,Float等,如:
ArrayList<Integer> list = new ArrayList<Integer>();
list.add(0, new Integer(42));
int total = (list.get(0)).intValue();
在5.0当中,通过使用Autoboxing和Auto-Unboxing我们就不再需要显式的使用这些封装类了。
ArrayList<Integer> list = new ArrayList<Integer>();
list.add(0, 42);
int total = list.get(0);
3. Enhanced for Loop
一般来说,当我们需要对数组和集合进行遍历时,我们需要这样做:
ArrayList<Integer> list = new ArrayList<Integer>();
list.add(0,1);
list.add(1,2);
for (Iterator i = list.iterator(); i.hasNext();) {
Integer value=(Integer)i.next();
}
但使用了5.0的Enhanced for Loop以后,我们的循环可以变得很简单:
ArrayList<Integer> list = new ArrayList<Integer>();
list.add(0,1);
list.add(1,2);
for(int i:list){
System.out.println(i);
}
同理,数组的遍历也从原来的:
int[] b = new int[3];
for(int i=0;i<b.length;i++){System.out.println(b[i]);}
变为:
int[] b = new int[3];
for(int i:b){System.out.println(i);}
总结:
Enhanced for Loop的语法:
for ( FormalParameter : Expression )
Statement
等价于原来的:
for ( Iterator<E> #i = Expression.iterator(); #i.hasNext(); ) {
FormalParameter = #i.next();
Statement
}
即 FormalParameter = Expression.iteraotor().next();
4. Enumerated Types
从5.0开始,j2se支持枚举类型。简单的枚举使用如下:
public enum Color{RED,BLUE,GREEN;};
public static void main(String[] args){
for(Color c:Color.values()){
System.out.println(c);
}
}
输出为
RED
BLUE
GREEN
稍微复杂一点的使用,可以增加对枚举类型的定义:
public enum Color{RED(1),BLUE(5),GREEN(7);
private int value;
Color(int value){this.value = value;}
public int value(){return this.value;}
};
public static void main(String[] args){
for(Color c:Color.values()){
System.out.println(c);
System.out.println(c.value() == 1);
}
}
输出为:
RED
True
BLUE
false
GREEN
false
其中在枚举的声明当中的这部分内容:
private int value;
Color(int value){this.value = value;}
public int value(){return this.value;}
就等价与声明了一个Color类:
public class Color{
private int value;
Color(int value){this.value = value;}
public int value(){return this.value;}
}
还有就是,枚举也能用于switch结构当中,象
switch(c){
case RED:…
case BLUE:…
}
5. Static Import
以前,当我们要使用某个类当中的某个static变量,如BorderLayout.CENTER时,我们需要进行完整的书写,但通过使用Static Import,我们可以只书写CENTER即可,即:
import static java.awt.BorderLayout.* // 导入
getContentPane().add(new JPanel(),CENTER); // 使用
Static Import的使用,使得我们在不引入类的情况下,能只引用某个类的static变量
6. Formatted Output and Formatted Input
Formatted Output使得我们可以使用类C的printf方法中的格式化变量,对输出进行格式化,如
System.out.printf(“%s %5d %n”,user,total);
7. Varargs
先看一个5.0当中的例子:
public class Test{
public Test(){}
public void out(String ...args){
for(String s:args){
System.out.println(s);
}
}
public static void main(String[] args){
Test t = new Test();
t.out("a","b","c");
}
}
可以注意到,在out方法的参数表当中,我们的参数声明是…args,这就是5.0引入的Varargs概念,即允许你一次传达多个参数,同样的效果如果需要在5.0之前实现的话,我们可能需要把方法的参数列表声明为一个数组,或集合,然后在调用该方法时,把参数封装到一个数组或者集合当中。即:
public void out(String[] args){}
out(new String[]{“a”,”b”,”c”});
8. 同步集合-Queue,BlockingQueue
J2SE 5.0里面添加了新的用于提高同步性能的集合类--Queue。Queue是一个FIFO队列,她的作用主要是提高了并发访问性能,她与原来集合类当中的List的区别包括:
1. 她提供了一些新的方法,用于添加元素的offer(),用于删除元素的poll(),用于获取队首元素的peak()。这些操作与原来的add(),remove()的区别在于,原有的add(),remove()和element()当所需操作失败的时候会抛出异常,而新的offer()和poll()则不会,当队列不能容纳新元素时,offer()直接返回,当队列没有元素可取的时候,poll()操作,返回null。
例子:
public class Test{
private ArrayBlockingQueue<Integer> list;
public Test(int capacity){
list = new ArrayBlockingQueue<Integer>(capacity);
}
public void add(int size){
for(int i=0;i<size;i++){
list.add(i);
output();
}
}
public void output(){
for(int i:list){
System.out.print(i+" ");
}
}
public static void main(String[] args){
Test t = new Test(2);
t.add(3);
}
}
往一个容量为2的队列里面添加3个元素,他的输出为:
======= Start ===============================================
0
========================================= Stop ==============
======= Start ===============================================
0 1
========================================= Stop ==============
Exception in thread "main" java.lang.IllegalStateException: Queue full
at java.util.AbstractQueue.add(Unknown Source)
at Test.add(Test.java:14)
at Test.main(Test.java:32)
把上述代码修改为:
public void add(int size){
for(int i=0;i<size;i++){
list.offer(i);
output();
}
}
输出为:
======= Start ===============================================
0
========================================= Stop ==============
======= Start ===============================================
0 1
========================================= Stop ==============
======= Start ===============================================
0 1
========================================= Stop ==============
2. BlockingQueue是Queue的一个子接口,她提供了新的用于阻塞的操作,take()和put(),当队列没有空间,而线程欲通过put()往队列中添加元素时,线程会被阻塞。同理,当队列没有空间,而线程欲通过take()往队列中获取元素时,线程也会被阻塞。利用BlockingQueeu,我们可以很简单的实现生产者和消费者问题。
例子:
生产者:
public class Producer extends Thread{
private BlockingQueue queue;
private int count;
public Producer(BlockingQueue queue){
this.queue = queue;
count = 0;
}
public void run(){
while(true){
try{
queue.put(produce());
}catch(Exception e){
e.printStackTrace();
}
}
}
private Integer produce(){
System.out.println("Produce: "+count);
return new Integer(count++);
}
}
消费者:
public class Consumer extends Thread{
private BlockingQueue queue;
private int id;
public Consumer(BlockingQueue queue){
this.queue = queue;
}
public void run(){
while(true){
try{
Thread.currentThread().sleep(step);
consume((Integer)queue.take());
}catch(Exception e){
e.printStackTrace();
}
}
}
private void consume(Integer i){
System.out.println("\t\t Consume: "+i);
}
}
测试程序:
public class CPTest{
public static void main(String[] args){
ArrayBlockingQueue queue = new ArrayBlockingQueue(3);
Consumer c1 = new Consumer(queue);
Consumer c2 = new Consumer(queue);
Producer p = new Producer(queue);
c1.start();
c2.start();
p.start();
}
}
3. PriorityQueue提供了基于优先级的排序。她会根据元素的自然序对所有加入的元素进行排序,另外,通过初始化时,提供定制的Comparator实现,可以改变他的顺序。注意:通过PriorityQueue返回的Iterator并不会保证按序返回队列中的值。
例子:
public class PQTest{
private PriorityQueue<Integer> queue;
public PQTest(int capacity,Comparator com){
queue = new PriorityQueue<Integer>(capacity,com);
}
public void add(int size){
for(int i=0;i < size;i++){
queue.offer(i);
}
}
public void output(){
for(int i:queue){
System.out.print(i+" ");
}
System.out.println();
for(int i = 0 ; i<5;i++)
System.out.print(queue.poll()+" ");
}
public static void main(String[] args){
PQTest t = new PQTest(6,new PQComparator());
t.add(5);
t.output();
}
}
Comparator实现为:
public class PQComparator implements Comparator<Integer>{
public int compare(Integer i1,Integer i2){
return -1 * (i1.intValue() - i2.intValue());
}
}
输出为:
======= Start ===============================================
4 3 1 0 2
4 3 2 1 0
========================================= Stop ==============
9. 同步(Concurrent)
1. Executor接口
Executor接口提供了一个类似于线程池的管理工具。用于只需要往Executor中提交Runnable对象,剩下的启动线程等工作,都会有对应的实现类来完成。ScheduledExecutorService比ExecutorService增加了,时间上的控制,即用户可以在提交的时候额外的定义该任务的启动时机,以及随后的执行间隔和延迟等。
例子:
任务:
public class ETask implements Runnable{
private int id = 0;
public ETask(int id){
this.id = id;
}
public void run(){
try{
System.out.println(id+" Start");
Thread.sleep(1000);
System.out.println(id+" Do");
Thread.sleep(1000);
System.out.println(id+" Exit");
}catch(Exception e){
e.printStackTrace();
}
}
}
测试类:
public class ETest{
public static void main(String[] args){
ExecutorService executor = Executors.newFixedThreadPool(2);
for(int i=0;i<5;i++){
Runnable r = new ETask(i);
executor.execute(r);
}
executor.shutdown();
}
}
输出:
0 Start
1 Start
0 Do
1 Do
0 Exit
2 Start
1 Exit
3 Start
2 Do
3 Do
2 Exit
3 Exit
4 Start
4 Do
4 Exit
2. Future和Callable
Callable是一个类似于Runnable的接口,他与Runnable的区别是,她在执行完毕之后能够返回结果。Future用于获取线程的执行结果,或者取消已向Executor的任务。当我们通过Future提供的get()方法获取任务的执行结果时,如果任务没有完成,则调用get()方法的线程将会被阻塞,知道任务完成为止。一般我们都会使用Future的实现类FutureTask。
例子:
Callable对象:
public class ETask implements Callable{
private String id = null;
public ETask(String id){
this.id = id;
}
public String call(){
try{
System.out.println(id+" Start");
Thread.sleep(1000);
System.out.println(id+" Do");
Thread.sleep(1000);
System.out.println(id+" Exit");
}catch(Exception e){
e.printStackTrace();
}
return id;
}
}
测试类:
public class ETest{
public static void main(String[] args){
ExecutorService executor = Executors.newFixedThreadPool(2);
for(int i=0;i<5;i++){
try{
Callable c = new ETask(String.valueOf(i));
FutureTask ft = new FutureTask(c);
executor.execute(ft);
System.out.println("Finish:" + ft.get());
}catch(Exception e){
e.printStackTrace();
}
}
executor.shutdown();
}
}
输出:
0 Start
0 Do
0 Exit
Finish:0
1 Start
1 Do
1 Exit
Finish:1
2 Start
…
3. CompletionService和ExecutorCompletionService
CompletionService类似于一个Executor和Queue的混合。我们可以通过submit()向CompletionService提交任务,然后通过poll()来获取第一个完成的任务,也可以通过take()来阻塞等待下一个完成的任务。ExecutorCompletionService是CompletionService的实现类,他需要提供一个Executor作为构造函数的参数。
例子:
Executor executor = …;
CompletionService cs = new ExecutorCompletionService(executor);
Future fs = cs.submit(…);
Future ft = cs.take();
4. Semaphore
信号量是用于同步和互斥的低级原语。信号量提供的acquire()和release()操作,与操作系统上的p,v操作同。
例子:
缓冲区:
public class Buffer{
private Semaphore s = null;
private Semaphore p = null;
Vector<Integer> v = new Vector<Integer>();
public Buffer(int capacity){
s = new Semaphore(capacity);
p = new Semaphore(0);
}
public void put(int i){
try{
s.acquire();
v.add(new Integer(i));
p.release();
}catch(Exception e){
e.printStackTrace();
}
}
public int get(){
int i = 0;
try{
p.acquire();
i = ((Integer)v.remove(0)).intValue();
s.release();
}catch(Exception e){
e.printStackTrace();
}
return i;
}
}
生产者:
public class Producer extends Thread{
private Buffer b;
private int count;
private int step;
private int id;
public Producer(Buffer b,int step,int id){
this.b = b;
this.step = step;
this.id = id;
count = 0;
}
public void run(){
try{
while(true){
System.out.println("In put");
b.put(count);
System.out.println("Producer "+id+":"+count);
count++;
Thread.sleep(step);
System.out.println("Out put");
}
}catch(Exception e){
e.printStackTrace();
}
}
}
消费者:
public class Consumer extends Thread{
private Buffer b;
private int step;
private int id;
public Consumer(Buffer b,int step,int id){
this.b = b;
this.step = step;
this.id = id;
}
public void run(){
try{
while(true){
System.out.println("In get");
System.out.println("\t\tConsume "+id+":"+b.get());
System.out.println("Out get");
Thread.sleep(step);
}
}catch(Exception e){
e.printStackTrace();
}
}
}
测试程序:
public class CPTest{
public static void main(String[] args){
Buffer b = new Buffer(3);
Consumer c1 = new Consumer(b,1000,1);
Consumer c2 = new Consumer(b,1000,2);
Producer p1 = new Producer(b,100,1);
Producer p2 = new Producer(b,100,2);
c1.start();
c2.start();
p1.start();
p2.start();
}
}
5. CyclicBarrier
CyclicBarrier可以让一组线程在某一个时间点上进行等待,当所有进程都到达该等待点后,再继续往下执行。CyclicBarrier使用完以后,通过调用reset()方法,可以重用该CyclicBarrier。线程通过调用await()来减少计数。
CyclicBarrier
例子:
任务:
public class Task extends Thread{
private String id;
private CyclicBarrier c;
private int time;
public Task(CyclicBarrier c,String id,int time){
this.c = c;
this.id = id;
this.time = time;
}
public void run(){
try{
System.out.println(id+" Start");
Thread.sleep(time);
System.out.println(id+" Finish");
c.await();
System.out.println(id+" Exit");
}catch(Exception e){
e.printStackTrace();
}
}
}
测试类:
public class Test{
public static void main(String[] args){
CyclicBarrier c = new CyclicBarrier(3,new Runnable(){
public void run(){
System.out.println("All Work Done");
}
});
Task t1 = new Task(c,"1",1000);
Task t2 = new Task(c,"2",3000);
Task t3 = new Task(c,"3",5000);
t1.start();
t2.start();
t3.start();
}
}
输出结果:
1 Start
2 Start
3 Start
1 Finish
2 Finish
3 Finish
All Work Done
3 Exit
1 Exit
2 Exit
6. CountdownLatch
CountdownLatch具有与CyclicBarrier相似的功能,也能让一组线程在某个点上进行同步。但是与CyclicBarrier不同的是:1.CountdownLatch不能重用,2.线程在CountdownLatch上调用await()操作一定会被阻塞,直到计数值为0时才会被唤醒,而且计数值只能通过conutDown()方法进行减少。
特别的,当CountdownLatch的值为1时,该Latch被称为“启动大门”,所有任务线程都在该Latch上await(),直到某个非任务线程调用countDown()触发,所有任务线程开始同时工作。
7. Exchanger
Exchanger是一个类似于计数值为2的CyclicBarrier。她允许两个线程在某个点上进行数据交换。
例子:
public class FillAndEmpty {
Exchanger<DataBuffer> exchanger = new Exchanger();
DataBuffer initialEmptyBuffer = ... a made-up type
DataBuffer initialFullBuffer = ...
public class FillingLoop implements Runnable {
public void run() {
DataBuffer currentBuffer = initialEmptyBuffer;
try {
while (currentBuffer != null) {
addToBuffer(currentBuffer);
if (currentBuffer.full())
currentBuffer = exchanger.exchange(currentBuffer);
}
}catch(InterruptedException ex) { ... handle ... }
}
}
public class EmptyingLoop implements Runnable {
public void run() {
DataBuffer currentBuffer = initialFullBuffer;
try {
while (currentBuffer != null) {
takeFromBuffer(currentBuffer);
if (currentBuffer.empty())
currentBuffer = exchanger.exchange(currentBuffer);
}
} catch (InterruptedException ex) { ... handle ...}
}
}
public void start() {
new Thread(new FillingLoop()).start();
new Thread(new EmptyingLoop()).start();
}
}
Exchange
8. Lock,Condition
锁是最基本的同步原语。通过在锁上面调用lock()和unlock()操作,可以达到与synchronized关键字相似的效果,但是有一点要注意的是,锁必须显式释放,如果由于抛出异常,而没有释放锁,将导致死锁出现。Condition提供的await(),signal(),signal()操作,与原来的wai(),notify(),notifyAll()操作具有相似的含义。Lock的两个主要子类是ReentrantLock和ReadWriteLock。其中ReadWriteLock的作用是允许多人读,而一人写。
例子:
使用Lock和Condition的生产者,消费者问题
public class BoundedBuffer {
final Lock lock = new ReentrantLock();
final Condition notFull = lock.newCondition();
final Condition notEmpty = lock.newCondition();
final Object[] items = new Object[100];
int putptr, takeptr, count;
public void put(Object x) throws InterruptedException {
lock.lock();
try {
while (count == items.length)
notFull.await();
items[putptr] = x;
if (++putptr == items.length)
putptr = 0;
++count;
notEmpty.signal();
} finally {
lock.unlock();
}
}
public Object take() throws InterruptedException {
lock.lock();
try {
while (count == 0)
notEmpty.await();
Object x = items[takeptr];
if (++takeptr == items.length)
takeptr = 0;
--count;
notFull.signal();
return x;
} finally {
lock.unlock();
}
}
}
9. 小结:新的concurrent包提供了一个从低到高的同步操作。
10.Annotation
Annotation是J2SE提供的一种新语法,根据规范的说明,他的作用简单来说就是简化代码的维护。例如,EJB需要7个文件,一个XML文件来表示,通过Annotation,我们可以只需要维护源代码,其他工作都可以通过自动生成达到。这个做法跟Lomboz的效果比较一致,不过Lomboz是通过XDoclet来实现,而现在只要使用Annotation就可以了,但是Annotation只是一个必要条件,如果真要达到自动生成其他文件的目的,除了Annotation之外,还需要其他类,或者工具的支持。
1. 预定义Annotation
Tiger为我们预定义了3个可以用的Annotation,分别是Override,Deprecated,SuppressWarnings。
i. Override
1. 作用:
Override的作用就是,被生命为Override的方法,必须是对父类中方法的覆盖,如果父类中没有出现该方法,或者方法没有正确覆盖父类中的方法,则编译期就会出错。
2. 语法:
@Override public void methodA(){…}
3. 例子:
父类:
/*
* Created on 2005-1-4
*
*/
package test;
/**
*
* @author [email protected]
*/
public class Test {
public Test() {
}
public void hello() {
System.out.println("hello");
}
public void bye() {
System.out.println("bye");
}
}
子类:
/*
* Created on 2005-1-5
*
*/
package test;
/**
*
* @author [email protected]
*/
public class OverTest extends Test{
@Override public void hello(){}
@Override public void methodA(){}
}
错误信息:
The method methodA() of type OverTest must override a superclass method
4. 评价:
通过Override,我们能够确保子类中的方法的确覆盖了父类中的方法。但是现在一般的IDE都可能提供了这种表示覆盖的提示,像我正在使用的Eclipse就有一个对应的符号,表示某个方法是否覆盖了父类中的方法,所以这个标记,或者只对那些还在使用Ultraedit的程序员有比较明显的作用。
ii. Deprecated
1. 作用:
用于声明一个方法不建议再被使用。
2. 语法:
@Deprecated public void methodA(){…}
3. 例子:
声明类:
/*
* Created on 2005-1-4
*
*/
/**
*
* @author [email protected]
*/
public class Test {
public Test() {
}
/**
*
* @deprecated
* */
public void hello() {
System.out.println("hello");
}
@Deprecated public void bye() {
System.out.println("bye");
}
}
调用类:
/*
* Created on 2005-1-5
*
*/
/**
*
* @author [email protected]
*/
public class CallTest {
public CallTest(){
Test t = new Test();
t.hello();
t.bye();
}
}
编译信息:
CallTest.java:14: warning: [deprecation] hello() in Test has been deprecated
t.hello(); ^
CallTest.java:15: warning: [deprecation] bye() in Test has been deprecated
t.bye(); ^
2 warnings
4. 评价:
对于Deprecated这个标记,我认为,已经有一个deprecated在之前的JavaDoc标记中定义过了,为什么J2SE还要额外的增加这样的一个,完全搞不懂。
iii. SuppressWarnings
1. 作用:
这个标记的作用就是可以让编译器忽略掉一些编译期的错误,不报错。
2. 语法:
@SuppressWarnings("unchecked") public void void methodA(){…}
3. 例子:
原文件:
public void bye() {
ArrayList list = new ArrayList();
list.add("Hello");
System.out.println("bye");
}
错误信息:
warning: [unchecked] unchecked call to add(E) as a member of the raw type java.util.ArrayList
list.add("Hello");
修改后的文件:
@SuppressWarnings( { "unchecked" }) public void bye() {
ArrayList list = new ArrayList();
list.add("Hello");
System.out.println("bye");
}
4. 评价:
SuppressWarnings的作用就是可以隐藏某些编译期的消息,他主要使用的地方或者就是在范型方面。但是,实际上这个标记在Tiger当中是没有实现的,所以上述修改后的代码,在他的输出结果当中,还是会输出同样的错误,呵呵。
2. 自定义Annotation
程序员可以根据自己的需求自定义一些特定的Annotation。但是,我认为有一点是很重要的,就是Annotation的存在不应该影响Java程序的语义,也就是说,使用Annotation前后,程序的含义,执行都不应该发生改变,即使一个程序员完全不了解Annotation的语法,他也应该能够毫无障碍的阅读Java源代码。
i. 语法:
Annotation一般有三种形式,就是不含有属性的,只有一个属性,和含有多个属性的。
1. 不含属性:
public @interface MyAnno{}
注:不含属性的Annotation的作用就跟不含方法的接口类似,只是作为标记的用途。
2. 一个属性:
public @interface MyAnno{
String value();
}
注:当只含有一个属性时,属性的名称需要命名为value,这是Annotation里面的一个规定。
3. 多个属性:
public @interface MyAnno{
String hello();
int income() default 1;
}
注:当有多个属性时,属性名称可以任意。注意的是,属性的类型必须是基本类型,String,enum,Class,Annotations和这些类型的数组形式,而且属性可以通过default来声明默认值。
ii. 使用:
Annotation使用的时候,跟声明符(public,private等)类似,但一般他们都是放在第
一个,即public之前。
1. 不含属性:
@MyAnno public void methodA(){…}
2. 一个属性:
@MyAnno("hello") pubic void methodA(){…}或者
@MyAnno(value="hello") public void methodA(…)
3. 多个属性:
@MyAnno(hello="hello",income="1") public void methodA(){…}
iii. 例子:
Annotation类:
/*
* Created on 2005-1-4
*
*/
package test;
import java.lang.annotation.*;
/**
*
* @author [email protected]
*/
@Retention(RetentionPolicy.RUNTIME)
public @interface Remote {}
使用类:
/*
* Created on 2005-1-4
*
*/
package test;
import java.lang.reflect.*;
/**
*
* @author [email protected]
*/
public class Test {
public Test() {}
@Remote public void hello() {
System.out.println("hello");
}
@Remote public void bye() {
System.out.println("bye");
}
public static void main(String[] args) {
try {
Class c = Class.forName("test.Test");
Method[] ms = c.getMethods();
for (Method m : ms) {
if (m.isAnnotationPresent(Remote.class)) {
System.out.println(m.getName());
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
iv. 注意事项:
在声明一个新的Annotation时,一般需要带上@Retention标记,因为这个表明了Annotation的存活时间。@Retention的取值有3个,RetentionPolicy.SOURCE表示Annotation只在编译期有效,编译之后就会被编译器丢掉。RetentionPolicy.CLASS表示Annotaion将会保存到.class文件当中,但是并不会被VM提取。RetentionPolicy.RUNTIME表示Annotaion将会保存到.class文件当中,而且运行时,也会被VM提取,即通过reflect方法,能够获取到该标记。
3. 评价:
总的来说,Annotation的意义并不如之前提到的enhance for loop,generic types等新功能大,但这个只是相对来说,由于Annotation并不影响Java的语义,所以,对于一般Java程序员来说,完全可以忽略。而且,如果真的要利用Annotation,程序员还需要自己书写reflect方面的代码来提取这些Annotation来做其他额外的工作,这个一般来说,应该比较少出现。但是Annotation对于像插件开发商,其他程序员是的确有用的,像EJB开发的时候,我们可以不需要通过XDoclet的方式,也能达到所需的效果。
本文地址:http://com.8s8s.com/it/it12085.htm