Tiger核心库技术简介

摘要:Java之所以得到很多程序员的亲睐,除了她的严谨的面向对象特性外,还有一个不容轻视的因素,那就是她强大的类库。一门语言如果没有库,功能将会大打折扣,在JDK5.0版本中,其核心库也有了诸多的改进,本文将就其新特性进行简介。

1.      访问环境变量和调用子进程

1.1       访问环境变量

虽然Java从一开始推出的时候,就一再强调她的跨平台特性,“一次编译,到处运行”。所以能够访问平台专有信息的System.getenv()方法从一开始进入javalang包时,就遭到了大多数人的反对。虽然1.0版本中抛弃了其中的一些内容,但是在tiger版本中我们又可以使用这个方法了,请注意该方法的名称全部是小写。

使用的方法很简单:如清单1

 

public class EnvTest {

public static void main(String args[]) {

System.out.println(System.getenv(args[0]));

}

}

 

只要给定变量的名称,就可以得到它的当前值。

 

Tiger提供了两个版本的getenv(),第二个版本返回系统当前设置中所有的环境变量对应的“名值”对。清单2说明该方法的使用:

 

import java.util.Map;

public class EnvDump {

public static void main(String args[]) {

for (Map.Entry entry: System.getenv().entrySet()) {

System.out.println(entry.getKey() + “/” + entry.getValue());

}

}

}

 

1.2       访问子进程

J2SE平台的前期版本提供了Runtime类的exec()方法用来创建子进程的运行,在Tiger版本中,这个方法依然有效,但是为了更方便的定制子进程,Tiger提供了ProcessBuilder类,它使依据改变了的进程变量来创建子进程更加便利。ProcessBuilder提供了directory(File)方法来改变进程的工作目录,用enviroment()方法在进程空间中添加和删除环境变量。清单3说明了Processor的简单用法,它使用 ipconfig 命令获得 Internet 配置信息。该方法适用于多数平台,否则可以将 ipconfig 改写成所用平台上的工作命令。启动进程构造程序之后,需要获得其 InputStream,以读入所创建进程的结果。

 

import java.io.*;

public class ProcessTest {

public static void main(String args[]) throws IOException {

Process p = new ProcessBuilder("ipconfig").start();

InputStream is = p.getInputStream();

BufferedReader br = new BufferedReader(new InputStreamReader(is));

String line;

while ((line = br.readLine()) != null) {

System.out.println(line);

}

}

}

 

运行结果如清单4

 

Windows 2000 IP Configuration

 

Ethernet adapter 本地连接:

 

Connection-specific DNS Suffix  . :

        IP Address. . . . . . . . . . . . : 10.97.69.166

Subnet Mask . . . . . . . . . . . : 255.255.255.128

Default Gateway . . . . . . . . . : 10.97.69.129

 

ProcessBuilder 类不仅能生成新的进程,而且还能获得其结果。在调用其 start() 方法之前,还可以调整进程所执行的上下文。如果不喜欢环境变量,可以使用 environment 获得当前设置,并调用 clear() 清除映射。如果需要添加环境变量,可以调用 environment 获得当前设置,然后通过 put(name, value) 添加新的变量。如果希望使用新的工作目录,可以调用 directory() 并提供新的工作目录作为 File 对象。就是这么简单。

使用表示将运行的命令及其参数的数目可变的字符串参数来创建 ProcessBuilder,一旦使用新的环境变量和工作目录配置 ProcessBuilder,就可以调用 start() 来执行命令。

 

2         并发集合

Java中的集合框架一直是令人津津乐道的,在Tiger中,集合框架新添加了Queue接口以及这个接口的并发和非并发实现,以及并发Map的实现和专用于读操作大大超过写操作的情况下的并发ListSet实现。

2.1     Queue接口

虽然在List的两端可以添加删除元素达到模拟queue的性能,但是Queue的提出提供了支持添加、删除和检查集合的更为方便的方法:如清单5所示

 

public boolean offer(Object element)

public Object remove()

public Object poll()

public Object element()

public Object peek()

 

一些队列有大小长度的限制,因此如果想在一个已满的队列中加入一个新项,多出的项就会被拒绝。这时新的 offer 方法就可以起作用了。它不是对调用 add() 方法抛出一个 unchecked 异常,而只是得到由 offer() 返回的 falseremove() poll() 方法都是从队列中删除第一个元素(head)。remove() 的行为与 Collection 接口的版本相似,但是新的 poll() 方法在用空集合调用时不是抛出异常,只是返回 null。因此新的方法更适合容易出现异常条件的情况。后两个方法 element() peek() 用于在队列的头部查询元素。与 remove() 方法类似,在队列为空时,element() 抛出一个异常,而 peek() 返回 null

2.1.1    基本队列实现

1、  Tiger中,java.util.LinkedList已经被改造为实现了java.util.Listjava.util.Queue两个接口。清单6显示的是LinkedList的简单使用:

 

         Queue queue = new LinkedList();
         queue.offer("One");
         queue.offer("Two");
         queue.offer("Three");
         queue.offer("Four");
         System.out.println("Head of queue is: " + queue.poll());
 
         输出的结果应该是One

 

2、  util包新增加的AbstractQueue类,其工作方式类似于AbstractListAbstractSet类,在需要创建自己所需的对垒时,可以直接继承该类,必须实现offer()poll()peek()三个方法,读者可以在其中提供优化的实现。读者也可以不必创建自己的子类,而是使用几个tiger提供的实现,其中两个时不阻塞队列:PriorityQueueConcurrentLinkedQueuePriorityQueue类实际上是维护了一个有序的队列,加入到该Queue中的元素按照它们的自然顺序来进行排序,排序的依据是元素对java.util.Comparable的实现或者传递给该Queue构造函数的Comparator参数。如果将清单6的具体实现改为PriorityQueue,则输入的结果应该为Four,因为按字母顺序排列,Four是排在了第一个。ConcurrentLinkedQueue 是基于链接节点的、线程安全的队列。并发访问不需要同步。因为它在队列的尾部添加元素并从头部删除它们,所以只要不需要知道队列的大小,ConcurrentLinkedQueue 对公共集合的共享访问就可以工作得很好。收集关于队列大小的信息会很慢,需要遍历队列。

3、   

2.1.2    阻塞队列实现

Tiger提供的 java.util.concurrent 包在 集合框架 中加入了 BlockingQueue 接口和五个阻塞队列类。简单的讲,阻塞队列的意思就是当队列无空间时,添加元素的线程执行操作阻塞,直到有空间;或者是,当队列为空无元素可删时,执行删除的线程阻塞,知道有元素可删。BlockingQueue 接口的 Javadoc 给出了阻塞队列的基本用法,如清单 7 所示。生产者中的 put() 操作会在没有空间可用时阻塞,而消费者的 take() 操作会在队列中没有任何东西时阻塞。

 

class Producer implements Runnable {

   private final BlockingQueue queue;

   Producer(BlockingQueue q) { queue = q; }

   public void run() {

     try {

       while(true) { queue.put(produce()); }

     } catch (InterruptedException ex) { ... handle ...}

   }

   Object produce() { ... }

 }

 

 class Consumer implements Runnable {

   private final BlockingQueue queue;

   Consumer(BlockingQueue q) { queue = q; }

   public void run() {

     try {

       while(true) { consume(queue.take()); }

     } catch (InterruptedException ex) { ... handle ...}

   }

   void consume(Object x) { ... }

 }

 

 class Setup {

   void main() {

     BlockingQueue q = new SomeQueueImplementation();

     Producer p = new Producer(q);

     Consumer c1 = new Consumer(q);

     Consumer c2 = new Consumer(q);

     new Thread(p).start();

     new Thread(c1).start();

     new Thread(c2).start();

   }

 }

 

另外五个阻塞队列提供的情况各有不同:

  • ArrayBlockingQueue:一个由数组支持的有界队列。
  • LinkedBlockingQueue:一个由链接节点支持的可选有界队列。
  • PriorityBlockingQueue:一个由优先级堆支持的无界优先级队列。
  • DelayQueue:一个由优先级堆支持的、基于时间的调度队列。
  • SynchronousQueue:一个利用 BlockingQueue 接口的简单聚集(rendezvous)机制

 

前两个类 ArrayBlockingQueue LinkedBlockingQueue 几乎相同,只是在底层存储实现方面有所不同,LinkedBlockingQueue 并不总是有容量界限。无大小界限的 LinkedBlockingQueue 类在添加元素时永远不会有阻塞队列的等待(至少在其中有 Integer.MAX_VALUE 元素之前不会,这个时候采用的容量限制即是Integer.MAX_VALUE)。

 

PriorityBlockingQueue 是具有无界限容量的队列,它利用所包含元素的 Comparable 排序顺序来以逻辑顺序维护元素。可以将它看作 TreeSet 的可能替代物。例如,在队列中加入字符串 OneTwoThree Four 会导致 Four 被第一个取出来。对于没有天然顺序的元素,可以为构造函数提供一个 Comparator 。不过对 PriorityBlockingQueue使用时需要注意,从 iterator() 返回的 Iterator 实例并不一定按照优先级顺序返回元素。如果必须以优先级顺序遍历所有元素,那么让它们都通过 toArray() 方法并自己对它们排序,像 Arrays.sort(pq.toArray())

新的 DelayQueue 实现可能是其中最有意思(也是最复杂)的一个。加入到队列中的元素必须实现新的 Delayed 接口(只有一个方法 —— long getDelay(java.util.concurrent.TimeUnit unit))。因为队列的大小没有界限,使得添加可以立即返回,但是在延迟时间过去之前,不能从队列中取出元素。如果多个元素完成了延迟,那么最早失效/失效时间最长的元素将第一个取出。实际上没有听上去这样复杂。清单8演示了这种新的阻塞队列集合的使用:

 

import java.util.*;

import java.util.concurrent.*;

 

public class Delay {

  /**

   * Delayed implementation that actually delays

   */

  static class NanoDelay implements Delayed {

    long trigger;

    NanoDelay(long i) {

      trigger = System.nanoTime() + i;

    }

    public int compareTo(Object y) {

      long i = trigger;

      long j = ((NanoDelay)y).trigger;

      if (i < j) return -1;

      if (i > j) return 1;

      return 0;

    }

    public boolean equals(Object other) {

      return ((NanoDelay)other).trigger == trigger;

    }

    public boolean equals(NanoDelay other) {

      return other.trigger == trigger;

    }

    public long getDelay(TimeUnit unit) {

      long n = trigger - System.nanoTime();

      return unit.convert(n, TimeUnit.NANOSECONDS);

    }

    public long getTriggerTime() {

      return trigger;

    }

    public String toString() {

      return String.valueOf(trigger);

    }

  }

  public static void main(String args[]) throws InterruptedException {

    Random random = new Random();

    DelayQueue queue = new DelayQueue();

    for (int i=0; i < 5; i++) {

      queue.add(new NanoDelay(random.nextInt(1000)));

    }

    long last = 0;

    for (int i=0; i < 5; i++) {

      NanoDelay delay = (NanoDelay)(queue.take());

      long tt = delay.getTriggerTime();

      System.out.println("Trigger time: " + tt);

      if (i != 0) {

        System.out.println("Delta: " + (tt - last));

      }

      last = tt;

    }

  }

}

 

这个例子首先是一个内部类 NanoDelay,它实质上将暂停给定的任意纳秒(nanosecond)数,这里利用了 System 的新 nanoTime() 方法。然后 main() 方法只是将 NanoDelay 对象放到队列中并再次将它们取出来。如果希望队列项做一些其他事情,就需要在 Delayed 对象的实现中加入方法,并在从队列中取出后调用这个新方法。(请随意扩展 NanoDelay 以试验加入其他方法做一些有趣的事情。)显示从队列中取出元素的两次调用之间的时间差。如果时间差是负数,可以视为一个错误,因为永远不会在延迟时间结束后,在一个更早的触发时间从队列中取得项。

SynchronousQueue 类是最简单的。它没有内部容量。它就像线程之间的手递手机制。在队列中加入一个元素的生产者会等待另一个线程的消费者。当这个消费者出现时,这个元素就直接在消费者和生产者之间传递,永远不会加入到阻塞队列中

2.2     ConcurrentMap实现

新的 java.util.concurrent.ConcurrentMap 接口和 ConcurrentHashMap 实现只能在键不存在时将元素加入到 map 中,只有在键存在并映射到特定值时才能从 map 中删除一个元素。

ConcurrentMap中有一个新的 putIfAbsent() 方法用于在 map 中进行添加。这个方法以要添加到 ConcurrentMap 实现中的键和值为参数,就像普通的 put() 方法,但是只有在 map 不包含这个键时,才能将键加入到 map 中。如果 map 已经包含这个键,那么这个键的现有值就会返回。这个操作等于于清单9的代码:

if (!map.containsKey(key))

return map.put(key, value);

else

return map.get(key);

 

putIfAbsent() 方法一样,重载后的 remove() 方法有两个参数 —— 键和值。在调用时,只有当键映射到指定的值时才从 map 中删除这个键。如果不匹配,那么就不删除这个键,并返回 false。如果值匹配键的当前映射内容,那么就删除这个键。清单 10 显示了这种操作的等价源代码:

 

if (map.get(key).equals(value)) {

map.remove(key);

return true;

} else {

return false;

}

2.3   CopyOnWriteArrayList CopyOnWriteArraySet

copy-on-write 模式是这样声明的,为了维护对象的一致性快照,要依靠不可变性(immutability)来消除在协调读取不同的但是相关的属性时需要的同步。对于集合,这意味着如果有大量的读(即 get() 和迭代,不必同步操作以照顾偶尔的写(即 add())调用。对于新的 CopyOnWriteArrayList CopyOnWriteArraySet 类,所有可变的(mutable)操作都首先取得后台数组的副本,对副本进行更改,然后替换副本。这种做法保证了在遍历自身更改的集合时,永远不会抛出 ConcurrentModificationException。遍历集合会用原来的集合完成,而在以后的操作中使用更新后的集合。

这些新的集合,CopyOnWriteArrayList CopyOnWriteArraySet,最适合于读操作通常大大超过写操作的情况。一个最常提到的例子是使用监听器列表。已经说过,Swing 组件还没有改为使用新的集合。相反,它们继续使用 javax.swing.event.EventListenerList 来维护它们的监听器列表。

如清单11所示,集合的使用与它们的非 copy-on-write 替代物完全一样。只是创建集合并在其中加入或者删除元素。即使对象加入到了集合中,原来的 Iterator 也可以进行,继续遍历原来集合中的项。

 

import java.util.*;

import java.util.concurrent.*;

 

public class CopyOnWrite {

  public static void main(String args[]) {

    List list1 = new CopyOnWriteArrayList(Arrays.asList(args));

    List list2 = new ArrayList(Arrays.asList(args));

    Iterator itor1 = list1.iterator();

    Iterator itor2 = list2.iterator();

    list1.add("New");

    list2.add("New");

    try {

      printAll(itor1);

    } catch (ConcurrentModificationException e) {

      System.err.println("Shouldn't get here");

    }

    try {

      printAll(itor2);

    } catch (ConcurrentModificationException e) {

      System.err.println("Will get here.");

    }

  }

  private static void printAll(Iterator itor) {

    while (itor.hasNext()) {

      System.out.println(itor.next());

    }

  }

}

 

这个示例程序用命令行参数创建 CopyOnWriteArrayList ArrayList 这两个实例。在得到每一个实例的 Iterator 后,分别在其中加入一个元素。当 ArrayList 迭代因一个 ConcurrentModificationException 问题而立即停止时,CopyOnWriteArrayList 迭代可以继续,不会抛出异常,因为原来的集合是在得到 iterator 之后改变的。如果这种行为(比如通知原来一组事件监听器中的所有元素)是您需要的,那么最好使用 copy-on-write 集合。如果不使用的话,就还用原来的,并保证在出现异常时对它进行处理。

3         Formatter

对于那些从一开始就使用 Java 编程而从没有接触过 C 的人,或者,对那些对 C 没有足够了解的人,格式化字符串是一些古怪的文本串,它们指定一组变量的输出特性。不是用加号将字符串连接在一起(如 firstName + " " + lastName),而是提供一个字符串描述输出,并提供参数以在方法调用结束时,替换字符串中的占位符:String s = String.format("%1$s %2$s", firstName, lastName)

首先,让我们分析新的 java.util.Formatter 类。您可能不会经常直接使用这个类,但是它提供了所要进行的格式化的内部机制。在这个类的 Javadoc 中,会看到一个描述所支持的格式化选项的表。这些选项的范围从以类似 %7.4f 这样的格式指定浮点数的精度和位数,到格式化时间的 %tT,到格式化第三个参数 %3$s

Formatter 格式化输出分为两步:创建一个 Appendable 对象以存储输出,用 format() 方法将带格式的内容放到这个对象中。下面列出了 Appendable 接口的实现器:

 

  • BufferedWriter
  • CharArrayWriter
  • CharBuffer
  • FileWriter
  • FilterWriter
  • LogStream
  • OutputStreamWriter
  • PipedWriter
  • PrintStream
  • PrintWriter
  • StringBuffer
  • StringBuilder
  • StringWriter
  • Writer

 

在使用 Formatter 类时,可以将实现了这个接口的对象传递给构造函数 Formatter 以把它作为目标。大多数这种类看起来很相似,除了 StringBuilder 类。StringBuilder StringBuffer 类几乎相同,只有一个大的区别:它不是线程安全的。如果知道要在单线程中构建字符串,就使用 StringBuilder。如果构建过程会跨越多个线程,则使用 StringBuffer。清单12显示了通常如何开始使用 Formatter

 

StringBuilder sb = new StringBuilder();

Formatter formatter = new Formatter(sb, Locale.US);

 

创建了Formatter 类后,用格式化字符串和参数调用其 format() 方法。如果需要使用与传递给出构造函数的不同的 Locale 作为格式化输出的一部分,还可以向 format() 方法传递一个 Locale 对象。清单 13 显示了两种不同的 format()

 

public Formatter format(String format, Object... args)

public Formatter format(Locale l, String format, Object... args)

 

如果希望得到精度为 10 位数字的 Pi 值,清单 14 中的代码会将这个值放到 StringBuilder 中并打印输出。打印 formatter 对象将显示 Appendable 对象的内容。

 

import java.util.Locale;

import java.util.Formatter;

 

public class Build {

public static void main(String args[]) {

StringBuilder sb = new StringBuilder();

Formatter formatter = new Formatter(sb, Locale.US);

formatter.format("PI = %12.10f", Math.PI);

System.out.println(formatter);

}

}

3.1     PrintStream支持

PrintStream 类中定义了常见的、分别用于写入标准输出和标准错误的 System.out System.err 对象。Tiger 引入了两个新的构造函数(用于直接写入文件)和六个方法以提供对格式化的支持(三对)。第一对是另一版本的 append() 方法。这一对方法实现了新的 java.lang.Appendable 接口。一般不会直接调用这些方法。直接调用的是 format() printf(),其中 printf() 版本只是 format() 版本的方便的包装器,如清单15所示:

 

public PrintStream format(String format, Object... args)

public PrintStream format(Locale l, String format, Object... args)

 

要记住新的变量参数支持,它是由 ... 指派的

 

清单16演示了用 PrintStream format() 方法打印今天的日期:

 

import java.util.Calendar;

 

public class Now {

     public static void main(String args[]) {

         System.out.format("Today is %1$tB %1$te, %1$tY.",Calendar.getInstance());

     }

}

 

运行这个程序的输出是 Today is January 19, 2005.,当然实际的输出取决于运行这个程序的日期。上面代码中的格式化字符串 %1$tB 告诉程序使用第一个参数并打印 date 对象的完整月名。格式化字符串 %1$te 意味着显示月中的日期,而格式化字符串 %1$tY 是四位数字的年。在 Formatter 对象的 Javadoc 中列出了打印日期的其他选项。

3.2     String支持

String 类有两个新的 static format() 方法,它们的作用与相应的 printf() 类似。发送一个格式化字符串和参数(还可能有 Locale)、并使用在格式化字符串中指定的格式转换参数。如果是这个方法的 String 版本,那么是以 String 对象而不是经过流返回结果。这些方法不是很显眼,但是有了它们就可以避免直接使用 Formatter 对象并创建中间的 StringBuilder

3.3     格式化任意对象

到目前为止看到的每项内容都是描述如何使用新的格式化能力对已有对象和基本数据进行格式化。如果希望用 Formatter 提供对自己的对象的支持,那么就要用到 Formattable 接口。通过在自己的类中实现如清单 17 所示的一个 formatTo() 方法,可以对自已的类使用格式化字符串:

 

void formatTo(Formatter formatter, int flags, Integer width, Integer precision)

 

清单 18 演示了通过提供一个具有 name 属性的简单类来使用 Formattable 接口。这个 name 显示在输出中,它支持控制输出的宽度和对齐。

 

import java.util.Locale;

import java.util.Formatter;

import java.util.Formattable;

 

public class MyObject implements Formattable {

  String name;

  public MyObject(String name) {

    this.name = name;

  }

  public void formatTo(

         Formatter fmt,

         int f,

         Integer width,

         Integer precision) {

 

    StringBuilder sb = new StringBuilder();

    if (precision == null) {

      // no max width

      sb.append(name);

    } else if (name.length() < precision) {

      sb.append(name);

    } else {

      sb.append(name.substring(0, precision - 1)).append('*');

    }

 

    // apply width and justification

    if ((width != null) && (sb.length() < width)) {

      for (int i = 0, n=sb.length(); i < width - n; i++) {

        if ((f & Formattable.LEFT_JUSTIFY) == Formattable.LEFT_JUSTIFY) {

          sb.append(' ');

        } else {

          sb.insert(0, ' ');

        }

      }

    }

    fmt.format(sb.toString());

  }

 

  public static void main(String args[]) {

   MyObject my1 = new MyObject("John");

   MyObject my2 = new MyObject("Really Long Name");

   // First / Using toString()

   System.out.println("First Object : " + my1);

   // Second / Using Formatter

   System.out.format("First Object : '%s'//n", my1);

   // Second / Using Formatter

   System.out.format("Second Object: '%s'//n", my2);

   // Second / Using Formatter with width

   System.out.format("Second Object: '%10.5s'//n", my2);

   // Second / Using Formatter with width and left justification

   System.out.format("Second Object: '%-10.5s'//n", my2);

  }

}

 

运行这个程序会生成如清单19所示的输出。前两行展示了使用 toString Formatter 的不同结果。后三行显示宽度和对齐控制的选项。

 

First Object : MyObject@10b62c9

First Object : 'John'

Second Object: 'Really Long Name'

Second Object: '     Real*'

Second Object: 'Real*    

 

4         XML文件中装载属性

Properties这个类对于读者来说应该并不陌生,在应用程序的配置文件中大多采用的都是这样的属性文本文件。在Tiger中,java.util.Properties 类现在提供了一种为程序装载和存储设置的更容易的方法:loadFromXML(InputStream is) storeToXML(OutputStream os, String comment) 方法。

4.1     XML属性文件

XML属性文件的指定DTD如清单20所示:

 

<?xml version="1.0" encoding="UTF-8"?>

<!-- DTD for properties -->

<!ELEMENT properties ( comment?, entry* ) >

<!ATTLIST properties version CDATA #FIXED "1.0">

<!ELEMENT comment (#PCDATA) >

<!ELEMENT entry (#PCDATA) >

<!ATTLIST entry key CDATA #REQUIRED>

 

简单的XML属性文件如清单21所示:

 

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">

<properties>

<comment>Hi</comment>

<entry key="foo">bar</entry>

<entry key="fu">baz</entry>

</properties>

 

相信读者对此一目了然。

4.2     存取XML属性文件

4.2.1    读取XML属性文件

读取 XML 版本的 Properties 文件与读取老格式的文件没什么不同。如清单22所示

 

import java.util.*;

import java.io.*;

 

public class LoadSampleXML {

public static void main(String args[]) throws Exception {

Properties prop = new Properties();

FileInputStream fis = new FileInputStream("sampleprops.xml");

prop.loadFromXML(fis);

prop.list(System.out);

System.out.println("/nThe foo property: " + prop.getProperty("foo"));

}

}

4.2.2    保存XML属性文件

用新的 storeToXML() 方法创建xml文件。只要传递一个 OutputStream 和一个用于注释的 String 就可以了。代码如清单23所示:

 

import java.util.*;

import java.io.*;

 

public class StoreXML {

public static void main(String args[]) throws Exception {

Properties prop = new Properties();

prop.setProperty("one-two", "buckle my shoe");

prop.setProperty("three-four", "shut the door");

prop.setProperty("five-six", "pick up sticks");

prop.setProperty("seven-eight", "lay them straight");

prop.setProperty("nine-ten", "a big, fat hen");

FileOutputStream fos = new FileOutputStream("rhyme.xml");

prop.storeToXML(fos, "Rhyme");

fos.close();

}

}

 

运行结果如清单24所示:

 

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">

<properties>

<comment>Rhyme</comment>

<entry key="seven-eight">lay them straight</entry>

<entry key="five-six">pick up sticks</entry>

<entry key="nine-ten">a big, fat hen</entry>

<entry key="three-four">shut the door</entry>

<entry key="one-two">buckle my shoe</entry>

</properties>

 

 

 

5         其他核心库的增强

另外,Tiger以下核心库方面也有了一定的增强,由于篇幅所限,这里就不一一赘述了,请感兴趣的读者参照JDK 5.0 API Documentation中的相关内容。

5.1     Scanner

java.util.Scanner 类可用于将文本转换成原语或字符串。由于它是基于 java.util.regex 包的,因此它也提供了一种方式来管理正则表达式,该表达式把搜索建立在流、文件数据、字符串或 Readable 接口的封装行为(implementor)上。

5.2     JavaBeans组件体系结构

已经添加了一个称为 IndexedPropertyChangeEvent PropertyChangeEvent 子类来支持界限属性,该属性使用索引来指出 bean 的更改部分。另外,已经在 PropertyChangeSupport 类中添加了一些方法,用以支持激发索引属性改变事件。

5.3     Math

java.math 包包含了下面的增强功能:

·    BigDecimal 类已经为固定精度浮点计算添加了支持。参见 JSR 13.

·    Math StrictMath 库包含双曲线超越函数(sinhcosh tanh)、立方根和基于 10 的对数等。

·    十六进制浮点数支持 —— 为允许特定浮点值的精确和可预知的说明,十六进制表示法可用于浮点数的字面值,以及用于 Float Double 中浮点数转换方法的字符串。

 

5.4     网络

5.5     安全性

这一版本的 J2SE 在安全方面大大增强了。它为安全性令牌提供了更好的支持, 为更多的安全标准(SASLOCSP TSP)提供了支持,改进了可伸缩性(SSLEngine)和性能,此外在 Crypto Java GSS 方面也提供了许多增强功能。有关更多信息,请参见上面的链接。

5.6     国际化

增强功能有:

·    现在 字符处理是以 4.0 版本的 Unicode 标准为基础的。这就影响了 java.lang 包中的 Character 类和 String 类、java.text 包中的排序规则和双向文本分析功能、java.util.regex 包中的 Character 类及 J2SE 的其他许多部分。作为这一升级的一部分,JSR 204 专家组已经指定了对辅助字符的支持,而且在整个 J2SE 中已经实现了该支持。有关更多信息,请参见 JSR 204 Character 类文档。

·    DecimalFormat 类已经得到了增强,现在可以格式化和解析 BigDecimal BigInteger 值,而不会丢失精确度。这些值的格式化是自动得到增强的;必须启用 setParseBigDecimal 方法才可以进行到 BigDecimal 的解析。

·    现在,Vietnamese java.util java.text 包中所有的 Locale 敏感功能方面得到了支持。有关支持的 Locale 和写系统的完整信息,请参见 支持的 Locale

 

5.7     序列化

已经添加了支持来处理 1.5 版本中新增的枚举类型。序列化一个枚举实例的规定不同于序列化一个普通可序列化对象的那些规则:枚举实例的序列化表单只包括它的枚举常量名和指出它的基本枚举类型的信息。反序列化行为也是不同的 —— 类信息用于查找相应的枚举类,并且为了获取返回的枚举常量,通过使用那个类和接收的常量名来调用 Enum.valueOf 方法。

 

 

6         结束语

以上所讲到的只是J2SE 15核心库新特性的一小部分,还有很多其他新特性限于篇幅不能一一讲述,有兴趣进一步详细了解这些新特性的读者可以参看http://java.sun.com/j2se/1.5.0/docs/relnotes/features.html可获取更多信息。

 

Tiger“通过增强Java平台的力量、允许开发者更容易地使用,Java编程语言的这些改进将吸引大量各种Java开发者,是“Java技术发展历程的一个重要里程碑
阅读更多
想对作者说点什么?
相关热词

博主推荐

换一批

没有更多推荐了,返回首页