J2SE 5.0新功能

    技术2022-05-11  70

    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)的变量,如intfloat等,在添加到Collection当中时,需要使用他们对应的封装类IntegerFloat等,如:

              ArrayList<Integer> list = new ArrayList<Integer>();          list.add(0, new Integer(42));           int total = (list.get(0)).intValue();

    5.0当中,通过使用AutoboxingAuto-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.0Enhanced 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使得我们可以使用类Cprintf方法中的格式化变量,对输出进行格式化,如

         System.out.printf(“%s ] %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里面添加了新的用于提高同步性能的集合类--QueueQueue是一个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.      BlockingQueueQueue的一个子接口,她提供了新的用于阻塞的操作,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对象,剩下的启动线程等工作,都会有对应的实现类来完成。ScheduledExecutorServiceExecutorService增加了,时间上的控制,即用户可以在提交的时候额外的定义该任务的启动时机,以及随后的执行间隔和延迟等。

         例子:

         任务:

         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.      FutureCallable

         Callable是一个类似于Runnable的接口,他与Runnable的区别是,她在执行完毕之后能够返回结果。Future用于获取线程的执行结果,或者取消已向Executor的任务。当我们通过Future提供的get()方法获取任务的执行结果时,如果任务没有完成,则调用get()方法的线程将会被阻塞,知道任务完成为止。一般我们都会使用Future的实现类FutureTask

         例子:

         Cal lable对象:

         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.      CompletionServiceExecutorCompletionService

         CompletionService类似于一个ExecutorQueue的混合。我们可以通过submit()CompletionService提交任务,然后通过poll()来获取第一个完成的任务,也可以通过take()来阻塞等待下一个完成的任务。ExecutorCompletionServiceCompletionService的实现类,他需要提供一个Executor作为构造函数的参数。

         例子:

         Executor executor = …;

         CompletionService cs = new ExecutorCompletionService(executor);

         Future fs = cs.submit(…);

         Future ft = cs.take();

     

    4.      Semaphore

         信号量是用于同步和互斥的低级原语。信号量提供的acquire()release()操作,与操作系统上的pv操作同。

         例子:

         缓冲区:

         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()来减少计数。  

         例子:

         任务:

         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被称为“启动大门”,所有任务线程都在该Latchawait(),直到某个非任务线程调用countDown()触发,所有任务线程开始同时工作。

     

    7.      Exchanger

         Exchanger是一个类似于计数值为2CyclicBarrier。她允许两个线程在某个点上进行数据交换。

           例子:

         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.      LockCondition

         锁是最基本的同步原语。通过在锁上面调用lock()unlock()操作,可以达到与synchronized关键字相似的效果,但是有一点要注意的是,锁必须显式释放,如果由于抛出异常,而没有释放锁,将导致死锁出现。Condition提供的await(),signal(),signal()操作,与原来的wai(),notify(),notifyAll()操作具有相似的含义。Lock的两个主要子类是ReentrantLockReadWriteLock。其中ReadWriteLock的作用是允许多人读,而一人写。

         例子:

         使用LockCondition的生产者,消费者问题

         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

    AnnotationJ2SE提供的一种新语法,根据规范的说明,他的作用简单来说就是简化代码的维护。例如,EJB需要7个文件,一个XML文件来表示,通过Annotation,我们可以只需要维护源代码,其他工作都可以通过自动生成达到。这个做法跟Lomboz的效果比较一致,不过Lomboz是通过XDoclet来实现,而现在只要使用Annotation就可以了,但是Annotation只是一个必要条件,如果真要达到自动生成其他文件的目的,除了Annotation之外,还需要其他类,或者工具的支持。

    1.      预定义Annotation

    Tiger为我们预定义了3个可以用的Annotation,分别是OverrideDeprecatedSuppressWarnings

                          i.              Override

    1.        作用:

    Override的作用就是,被生命为Override的方法,必须是对父类中方法的覆盖,如果父类中没有出现该方法,或者方法没有正确覆盖父类中的方法,则编译期就会出错。

     

     

    2.        语法:

    @Override public void methodA(){…}

     

    3.        例子:

    父类:

    /*

     * Created on 2005-1-4

     *

     */

    package test;

    /**

     *

     * @author cenyongh@mails.gscas.ac.cn

     */

    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 cenyongh@mails.gscas.ac.cn

     */

    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 cenyongh@mails.gscas.ac.cn

     */

    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 cenyongh@mails.gscas.ac.cn

     */

    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;

    }

    注:当有多个属性时,属性名称可以任意。注意的是,属性的类型必须是基本类型,StringenumClassAnnotations和这些类型的数组形式,而且属性可以通过default来声明默认值。

     

                       ii.              使用:

    Annotation使用的时候,跟声明符(publicprivate等)类似,但一般他们都是放在第

    一个,即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 cenyongh@mails.gscas.ac.cn

     */

    @Retention(RetentionPolicy.RUNTIME)

    public @interface Remote {}

     

    使用类:

    /*

     * Created on 2005-1-4

     *

     */

    package test;

    import java.lang.reflect.*;

    /**

     *

     * @author cenyongh@mails.gscas.ac.cn

     */

    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 loopgeneric types等新功能大,但这个只是相对来说,由于Annotation并不影响Java的语义,所以,对于一般Java程序员来说,完全可以忽略。而且,如果真的要利用Annotation,程序员还需要自己书写reflect方面的代码来提取这些Annotation来做其他额外的工作,这个一般来说,应该比较少出现。但是Annotation对于像插件开发商,其他程序员是的确有用的,像EJB开发的时候,我们可以不需要通过XDoclet的方式,也能达到所需的效果。


    最新回复(0)