jdk1.5新特性
1.泛型 2.foreach 3.自动拆箱装箱 4.枚举
5.静态导入(Static import) 6.元数据(Metadata) 7.线程池 8.Java Generics
下面详细讨论每一个新特性:
一、泛型(Generics) javascript
泛型是JDK1.5中一个最“酷”的特征。引入泛型,咱们将得到编译时类型的安全和运行时更小地抛出 ClassCastExceptions的可能。java
JDK1.5中,你能够声明一个集合将接收/返回的对象的类型。程序员
JDK1.4中,建立雇员名字的清单 (List)须要一个集合对象,像下面的语句: spring
List listOfEmployeeName = new ArrayList();
在JDK1.5中,你将使用下面语句
List<String> listOfEmployeeName = new ArrayList<String>();
最“酷”的是,若你试图插入非string类型的值,你将在编译时发现而且修正这类问题。没有泛型,你会发现这样一个bug,当你的客户调用后会告 诉你,你所编写的程序抛出ClassCastException异常而崩溃。
另外,当你从集合中获得一个元素时你无需进行强制转换。故原先为:
String employeeName = ((String) listOfEmployee.get(i));
而下面的语句将比上面的更加简单:
String employeeName = listOfEmployee.get(i);
不清楚对象的类型而强制转换对象是不合理的,且更重要的是,它将在运行时失败。假使用户无心间传入一个包含string buffers类型而非string类型的集合,那结果会怎样呢。在Listing A中,客户被要求传入一个编译器没法强制的strings类型集合。Listing B中显示了一样的方法使用泛型是如何实现的。
Listing A
static boolean checkName(Collection employeeNameList, String name) {
for (Iteratori = employeeNamList.iterator(); i.hasNext(); ) {
String s = (String) i.next();
if(s.equals(name)){
return true;
//print employee name here ......
}
}
return false;
}
Listing B
static boolean checkName(Collection<String> employeeNameList, String name) {
for (Iteratori = employeeNamList.iterator(); i.hasNext(); ) {
if(i.next().equals(name)){
return true;
//print employee name here ......
}
}
return false;
}
如今,经过方法签名能够清楚知道输入集合必须只能包含strings。若是客户试图传入一个包含string buffers的集合,程序将不会编译。同时注意,该方法不包含任何强制转换。它只须要短短一行,一旦你习惯泛型后,它也更加清晰。
二、在JDK当前版本下的For循环语法以下:
void printAll(Collection c) {
for (Iteratori = c.iterator(); i.hasNext(); ) {
Employee emp = (Employee)i.next();
System.out.println(emp.getName());
}
}
如今,用加强的For语句实现相同方法:
void printAll(Collection c) {
for (Object o : c)
System.out.println((TimerTask)o).getName());
}
在这类For循环中,你应该将":"当作"in",因此,在该例中能够当作"for Object o in c"。你能够发现这种For循环更具可读性。
三、自动置入/自动取出(Autoboxing/unboxing)
Java有基本数据类型,在这些基本数据类型周围又有包装类。一般,编程人员须要将一种类型转换成另外一种。看看Listing C.中的代码片段。
Listing C
public class Employee {
private static final Integer CHILD = new Integer(0);
public static void main(String args[]) {
//code for adding n to an Integer
int n=10;
Integer age= new Integer(30);
Integer ageAfterTenYear= new Integer(age.intValue +10);
}
}
请注意,用于计算ageAfterTenYear的内循环代码看上去是多么杂乱。如今,在Listing D.中看看相同的程序使用autoboxing重写后的样子。
Listing D
public class Employee {
public static void main(String args[]) {
int n=10;
Integer age= new Integer(30);
Integer ageAfterTenYear= age +10;
}
}
值得注意的是:先前,若你取出(unbox)Null值,它将变为0。在次代码中,编译器将自动转换Integer为int而后加上10后将其转换回Integer
四、类型安全的枚举(Typesafeenums)
类型安全枚举提供下列特性:
他们提供编译时类型安全。
他们都是对象,所以你不须要将他们放入集合中。
他们做为一种类的实现,所以你能够添加一些方法。
他们为枚举类型提供了合适的命名空间。
他们打印的值具备情报性(informative)― 若是你打印一个整数枚举(intenum),你只是看见一个数字,它可能并不具备情报性。
例一:
enum Season { winter, spring, summer, fall }
例二:
public enum Coin {
penny(1), nickel(5), dime(10), quarter(25); 数据库
Coin(int value) { this.value = value; } 编程
private final int value;
public int value() { return value; }
}
五、静态导入(Static import)
静态导入使代码更易读。一般,你要使用定义在另外一个类中的常量(constants),像这样:
import org.yyy.pkg.Increment;
class Employee {
public Double calculateSalary(Double salary{
return salary + Increment.INCREMENT * salary;
}
}
当时使用静态导入,咱们无需为常量名前缀类名就能使用这些常量,像这样:
import static org.yyy.pkg.Increment;
class Employee {
public Double calculateSalary(Double salary{
return salary + INCREMENT * salary;
}
}
注意,咱们能够调用INCREMENT这一常量而不要使用类名Increment.。
六、元数据(Metadata)
元数据特征志于使开发者们借助厂商提供的工具能够进行更简易的开发。看一看Listing E.中的代码。
Listing E
import org.yyy.hr;
public interface EmployeeI extends Java.rmi.Remote {
public String getName() throwsJava.rmi.RemoteException;
public String getLocation () throwsJava.rmi.RemoteException;
}
public class EmployeeImpl implements EmployeeI {
public String getName(){
}
public String getLocation (){
}
}
经过元数据的支持,你能够改写Listing E中的代码为:
import org.yyy.hr;
public class Employee {
@Remote public String getName() {
...
}
@Remote public public String getLocation() {
...
}
}
7线程池
Java5中,对Java线程的类库作了大量扩展,其中线程池就是Java5的新特征之一,除了线程池以外,还有不少多线程相关内容,为多线程的编程带来了极大便利。为了编写高效稳定可靠的多线程程序,线程部分的新增内容显得尤其重要。数组
有关Java5线程新特征的内容所有在Java.util.concurrent下面,里面包含数目众多的接口和类,熟悉这部分API特征是一项艰难的学习过程。目前有关这方面的资料和书籍都少之又少,大所属介绍线程方面书籍还停留在java5以前的知识层面上。固然新特征对作多线程程序没有必须的关系,在java5以前通用能够写出很优秀的多线程程序。只是代价不同而已。浏览器
线程池的基本思想仍是一种对象池的思想,开辟一块内存空间,里面存放了众多(未死亡)的线程,池中线程执行调度由池管理器来处理。当有线程任务时,从池中取一个,执行完成后线程对象归池,这样能够避免反复建立线程对象所带来的性能开销,节省了系统的资源。
Java5以前,要实现一个线程池是至关有难度的,如今Java5为咱们作好了一切,咱们只需按照提供的API来使用,便可享受线程池带来的极大便利。
Java5的线程池分好多种:固定尺寸的线程池、可变尺寸链接池、。
在使用线程池以前,必须知道如何去建立一个线程池,在Java5中,须要了解的是java.util.concurrent.Executors类的API,这个类提供大量建立链接池的静态方法,是必须掌握的。
1、固定大小的线程池
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
/**
* Java线程:线程池
*/
public class Test {
public static void main(String[] args) {
//建立一个可重用固定线程数的线程池
ExecutorService pool = Executors.newFixedThreadPool(2);
//建立实现了Runnable接口对象,Thread对象固然也实现了Runnable接口
Thread t1 = new MyThread();
Thread t2 = new MyThread();
Thread t3 = new MyThread();
Thread t4 = new MyThread();
Thread t5 = new MyThread();
//将线程放入池中进行执行
pool.execute(t1);
pool.execute(t2);
pool.execute(t3);
pool.execute(t4);
pool.execute(t5);
//关闭线程池
pool.shutdown();
}
}
class MyThread extends Thread{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"正在执行。。。");
}
}
pool-1-thread-1正在执行。。。
pool-1-thread-1正在执行。。。
pool-1-thread-1正在执行。。。
pool-1-thread-1正在执行。。。
pool-1-thread-2正在执行。。。
Process finished with exit code 0安全
2、单任务线程池ruby
在上例的基础上改一行建立pool对象的代码为:
//建立一个使用单个 worker 线程的 Executor,以无界队列方式来运行该线程。
ExecutorService pool = Executors.newSingleThreadExecutor();
输出结果为:
pool-1-thread-1正在执行。。。
pool-1-thread-1正在执行。。。
pool-1-thread-1正在执行。。。
pool-1-thread-1正在执行。。。
pool-1-thread-1正在执行。。。
Process finished with exit code 0
对于以上两种链接池,大小都是固定的,当要加入的池的线程(或者任务)超过池最大尺寸时候,则入此线程池须要排队等待。
一旦池中有线程完毕,则排队等待的某个线程会入池执行。
3、可变尺寸的线程池
与上面的相似,只是改动下pool的建立方式:
//建立一个可根据须要建立新线程的线程池,可是在之前构造的线程可用时将重用它们。
ExecutorService pool = Executors.newCachedThreadPool();
pool-1-thread-5正在执行。。。
pool-1-thread-1正在执行。。。
pool-1-thread-4正在执行。。。
pool-1-thread-3正在执行。。。
pool-1-thread-2正在执行。。。
Process finished with exit code 0
4、延迟链接池
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
* Java线程:线程池
*/
public class Test {
public static void main(String[] args) {
//建立一个线程池,它可安排在给定延迟后运行命令或者按期地执行。
ScheduledExecutorService pool = Executors.newScheduledThreadPool(2);
//建立实现了Runnable接口对象,Thread对象固然也实现了Runnable接口
Thread t1 = new MyThread();
Thread t2 = new MyThread();
Thread t3 = new MyThread();
Thread t4 = new MyThread();
Thread t5 = new MyThread();
//将线程放入池中进行执行
pool.execute(t1);
pool.execute(t2);
pool.execute(t3);
//使用延迟执行风格的方法
pool.schedule(t4, 10, TimeUnit.MILLISECONDS);
pool.schedule(t5, 10, TimeUnit.MILLISECONDS);
//关闭线程池
pool.shutdown();
}
}
class MyThread extends Thread {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "正在执行。。。");
}
}
pool-1-thread-1正在执行。。。
pool-1-thread-2正在执行。。。
pool-1-thread-1正在执行。。。
pool-1-thread-1正在执行。。。
pool-1-thread-2正在执行。。。
Process finished with exit code 0
5、单任务延迟链接池
在四代码基础上,作改动
//建立一个单线程执行程序,它可安排在给定延迟后运行命令或者按期地执行。
ScheduledExecutorService pool = Executors.newSingleThreadScheduledExecutor();
pool-1-thread-1正在执行。。。
pool-1-thread-1正在执行。。。
pool-1-thread-1正在执行。。。
pool-1-thread-1正在执行。。。
pool-1-thread-1正在执行。。。
Process finished with exit code 0
6、自定义线程池
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* Java线程:线程池-自定义线程池
*/
public class Test {
public static void main(String[] args) {
//建立等待队列
BlockingQueue<Runnable> bqueue = new ArrayBlockingQueue<Runnable>(20);
//建立一个单线程执行程序,它可安排在给定延迟后运行命令或者按期地执行。
ThreadPoolExecutor pool = new ThreadPoolExecutor(2,3,2,TimeUnit.MILLISECONDS,bqueue);
//建立实现了Runnable接口对象,Thread对象固然也实现了Runnable接口
Thread t1 = new MyThread();
Thread t2 = new MyThread();
Thread t3 = new MyThread();
Thread t4 = new MyThread();
Thread t5 = new MyThread();
Thread t6 = new MyThread();
Thread t7 = new MyThread();
//将线程放入池中进行执行
pool.execute(t1);
pool.execute(t2);
pool.execute(t3);
pool.execute(t4);
pool.execute(t5);
pool.execute(t6);
pool.execute(t7);
//关闭线程池
pool.shutdown();
}
}
class MyThread extends Thread {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "正在执行。。。");
try {
Thread.sleep(100L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
pool-1-thread-1正在执行。。。
pool-1-thread-2正在执行。。。
pool-1-thread-2正在执行。。。
pool-1-thread-1正在执行。。。
pool-1-thread-2正在执行。。。
pool-1-thread-1正在执行。。。
pool-1-thread-2正在执行。。。
Process finished with exit code 0
建立自定义线程池的构造方法不少,本例中参数的含义以下:
ThreadPoolExecutor
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue)
用给定的初始参数和默认的线程工厂及处理程序建立新的 ThreadPoolExecutor。使用 Executors 工厂方法之一比使用此通用构造方法方便得多。
参数:
corePoolSize - 池中所保存的线程数,包括空闲线程。
maximumPoolSize - 池中容许的最大线程数。
keepAliveTime - 当线程数大于核心时,此为终止前多余的空闲线程等待新任务的最长时间。
unit - keepAliveTime 参数的时间单位。
workQueue - 执行前用于保持任务的队列。此队列仅保持由 execute 方法提交的 Runnable 任务。
抛出:
IllegalArgumentException - 若是 corePoolSize 或 keepAliveTime 小于零,或者 maximumPoolSize 小于或等于零,或者 corePoolSize 大于 maximumPoolSize。
NullPointerException - 若是 workQueue 为 null
自定义链接池稍麻烦些,不过经过建立的ThreadPoolExecutor线程池对象,能够获取到当前线程池的尺寸、正在执行任务的线程数、工做队列等等。
8.Java Generics
在JDK1.5以前的版本中,对于一个Collection类库中的容器类实例,可将任意类型
对象加入其中(都被看成Object实例看待);从容器中取出的对象也只是一个Object实例,须要将其强制转型为期待的类型,这种强制转型的运行时正确性由程序员自行保证。
例如如下代码片段:
List intList = new ArrayList(); //建立一个List,准备存放一些Integer实例
intList.add(new Integer(0));
intList.add(“1”); //不当心加入了一个字符串;但在编译和运行时都不报错,只有仔细的代码走才能揪出
Integer i0 = (Integer)intList.get(0);
Integer i1 = (Integer)intList.get(1); //编译经过,直到运行时才抛ClassCastException
而在JDK1.5中,能够建立一个明确只能存放某种特定类型对象的容器类实例,例如以下代码:
List<Integer> intList = new ArrayList<Integer>(); //intList为存放Integer实例的List
intList.add(new Integer(0));
Integer i0 = intList.get(0); //无需(Integer)强制转型;List<Integer>的get()返回的就是Integer类型对象
intList.add(“1”); //编译不经过,由于List<Integer>的add()方法只接受Integer类型的参数
“List<Integer> intList = new ArrayList<Integer>();”就是最简单且最经常使用的Generic应用;显然,运用Generic后的代码更具可读性和健壮性。
2 Generic类
JDK1.5中Collection类库的大部分类都被改进为Generic类。如下是从JDK1.5源码中
截取的关于List和Iterator接口定义的代码片段:
public interface List<E> {
void add(E x);
Iterator<E> iterator;
}
public interface Iterator<E> {
E next();
boolean hasNext();
}
以List为例,“public interface List<E>”中的E是List的类型参数,用户在使用List时可为类型参数指定一个肯定类型值(如List<Integer>)。
类型值为Java编译器所用,确保用户代码类型安全。例如,编 译器知道List<Integer>的add()方法只接受Integer类型的参数,所以若是你在代码中将一个字符串传入add()将致使 编译错误;
编译器知道Iterator<Integer>的next()方法返回一个Integer的实例,你在代码中也就无需对返回结果进 行(Integer)强制转型。
代码检验经过(语法正确且不会致使运行时类型安全问题)后,编译器对现有代码有一个转换工做。简单的说,就是去除代码中的 类型值信息,在必要处添加转型代码。例如以下代码:
public String demo() {
List<String> strList = new ArrayList<String>();
strList.add(“Hello!”);
return strList.get(0);
}
编译器在检验经过后,将其转换为:
public String demo() {
List strList = new ArrayList(); //去除类型值<String>
strList.add(“Hello!”);
return (String)strList.get(0); //添加转型动做代码(String)
}
可见,类型值信息只为Java编译器在编译时所用,确保代码无类型安全问题;验证经过以后,即被去除。
对于JVM而言,只有如JDK1.5以前版本同样的 List,并没有List<Integer>和List<String>之分。这也就是Java Generics实现中关键技术Erasure的基本思想。如下代码在控制台输出的就是“true”。
List<String> strList = new ArrayList<String>();
List<Integer> intList = new ArrayList<Integer>();
System.out.println(strList.getClass() == intList.getClass());
能够将Generic理解为:为提升Java代码类型安全性(在编译时确保,而非等到运行时才暴露),Java代码与Java编译器之间新增的一种约定 规 范。Java编译器在编译结果*.class文件中供JVM读取的部分里没有保留Generic的任何信息;JVM看不到Generic的存在。
对于Generic类(设为GenericClass)的类型参数(设为T):
1) 因为对于JVM而言,只有一个GenericClass类,因此GenericClass类的静态字段和静态方法的定义中不能使用T。
T只能出如今 GenericClass的非静态字段或非静态方法中。也即T是与GenericClass的实例相关的信息;
2)T只在编译时被编译器理解,所以也就不能与运行时被JVM理解并执行其表明的操做的操做符(如instanceof 和new)联用。
class GenericClass<T> {
T t1;
public void method1(T t){
t1 = new T(); //编译错误,T不能与new联用
if (t1 instanceof T) {}; //编译错误,T不能与instanceof联用
};
static T t2; //编译错误,静态字段不能使用T
public static void method2(T t){};//编译错误,静态方法不能使用T
}
Generic类能够有多个类型参数,且类型参数命名通常为大写单字符。例如Collection类库中的Map声明为:
public interface Map<K,V> {
……;
}
3 Generic类和原(Raw)类
对每个Generic类,用户在使用时能够不指定类型参数。例如,对于List<E>,用户能够以“List<String> list;”方式使用,也能够以“List list;”方式使用。
“List<String>”被称为参数化的Generic类(类型参数被赋值),而“List”称为原类。原类 List的使用方式和效果与JDK1.5以前版本List的同样;使用原类也就失去了Generic带来的可读性和健壮性的加强。
容许原类使用方式的存在显然是为了代码的向前兼容:即JDK1.5以前的代码在JDK1.5下仍然编译经过且正常运行。
当你在JDK1.5中使用原类并向原类实例中添加对象时,编译器会产生警告,由于它没法保证待添加对象类型的正确性。编译经过是为了保证代码向前兼容,产生警告是提醒潜在的风险。
public void test () {
List list = new ArrayList();
list.add("tt");//JDK1.5编译器对此行产生警告
}
4 Generic类和子类
List<String> ls = new ArrayList<String>();
List<Object> lo = ls; //编译错误:Type mismatch: cannot convert from List<Dummy> to List<Object>
以上第二行代码致使的编译错误“Type mismatch: cannot convert from List<Dummy> to List<Object>”是否是有点出人意料?
直观上看,就像String是Object的子类,所以‘Object o = “String”’合法同样,存放String的List是存放Object的List的子类,所以第二行应该是合法的。反过来分析,若是第二行是合法 的,那么以下会致使运行时异常的代码也是合法的:
lo.add(new Object); //会致使在ls中添加了非String对象
String s = ls.get(0); //ls.get(0)返回的实际上只是一个Object实例,会致使ClassCastException
编译器显然不容许此种情形发生,所以不容许“List<Object> lo = ls”编译经过。
所以,直观上的“存放String的List是存放Object的List的子类”是错误的。更通常的说,设Foo是Bar的子类,G是Generic类型声明,G<Foo>不是G<Bar>的子类。
5 参数化的Generic类和数组
咱们知道,若是T是S的子类,则T[]也是S[]的子类。所以,以下代码编译经过,只
在运行时于第三行代码处抛ArrayStoreException。
String[] words = new String[10];
Object[] objects = words;
Objects[0] = new Object(); //编译经过,但运行时会抛ArrayStoreException
再分析以下代码:
List<String>[] wordLists = new ArrayList<String>[10];
ArrayList<Integer> integerList = new ArrayList<Integer>();
integerList.add(123);
Object[] objects = wordLists;
objects[0] = integerList;//运行时不出错,由于运行时ArrayList<String>和ArrayList<Integer>都为ArrayList
String s = wordlists[0].get(0); //编译经过,运行时抛ClassCastException
就出现了“正确使用了Generic,但在运行时仍然出现ClassCastException”的情形。显然Java编译器不容许此种情形的发生。
事实上,以上代码的第一行“List<String>[] wordLists = new ArrayList<String>[10];”就是编译不经过的,也就不存在接下来的代码。
更通常地说,不能建立参数化的Generic类的数组。
6 类型参数通配符?
由“Generic类和子类”节知,Collection<Object>不是存放其它类型对象的Collection(例如Collection<String>)的基类(抽象),那么如何表示任一种参数化的Collection的呢?使用 Collection<?>。
?即表明任一类型参数值。例如,咱们能够很容易写出下面的通用函数printCollection():
public static void printCollection(Collection<?> c) {
//如此遍历Collection的简洁方式也是JDK1.5新引入的
for (Object o : c) {
System.out.println(o);
}
}
这样,既能够将Collection<String>的实例,也能够将Collection<Integer>的实例做为参数调用printCollection()方法。
然而,要注意一点,你不能往Collection<?>容器实例中加入任何非null元素,例如以下代码的第三行编译不经过:
public static void testAdd(Collection<?> c) {
c.add(null); //编译经过
c.add(“test”); //编译错误
}
很好理解:c中要存放的对象的具体类型不肯定,编译器没法验证待添加对象类型的正确性,所以不能加入对象实例;而null能够看做是任一个类的实例,于是容许加入。
另外,尽管c中要存放的对象的类型不肯定,但咱们知道任何类都是Object子类,所以从c中取出的对象都统一做为Object实例。
更进一步,若是BaseClass表明某个可被继承的类的类名,那么Collection<? extends BaseClass>表明类型参数值为BaseClass或BaseClass某个子类的任一参数化Collection。对于 Collection<? extends BaseClass>的实例c,由于c中要存放的对象具体类型不肯定,不能往其加入非null对象,但从c中取出的对象都统一做为 BaseClass实例。事实上,你能够把Collection<?>看做Collection<? extends Object>的简洁形式。
另外一种情形:若是SubClass表明任一个类的类名,那么Collection<? super SubClass>表明类型参数值为SubClass或SubClass某个祖先类的任一参数化Collection。对于 Collection<? super SubClass>的实例c,你能够将SubClass实例加入其中,但从中取出的对象都是Object实例。
7 Generic方法
咱们能够定义Generic类,一样能够定义Generic方法,即将方法的一个或多个参数的类型参数化,如代码:
public static <T> void fromArrayToCollection(T[] a, Collection<T> c) {
for (T o : a) {
c.add(o); //合法。注意与Collection<?>的区别
}
}
咱们能够以以下方式调用fromArrayToCollection():
Object[] oa = new Object[100];
Collection<Object> co = new ArrayList<Object>();
fromArrayToCollection(oa, co); //此时,T即为Object
String[] sa = new String[100];
Collection<String> cs = new ArrayList<String>();
fromArrayToCollection(sa, cs); //此时,T即为String
fromArrayToCollection(sa, co); //此时,T即为Object
Integer[] ia = new Integer[100];
Float[] fa = new Float[100];
Number[] na = new Number[100];
Collection<Number> cn = new ArrayList<Number>();
fromArrayToCollection(ia, cn); //此时,T即为Number
fromArrayToCollection(fa, cn); //此时,T即为Number
fromArrayToCollection(na, cn); //此时,T即为Number
fromArrayToCollection(na, co); //此时,T即为Object
fromArrayToCollection(na, cs); //编译错误
经过以上代码能够看出,咱们在调用fromArrayToCollection()时,无需明确指定T为什么种类型(与Generic类的使用方式不一样), 而是像调用通常method同样,直接提供参数值,编译器会根据提供的参数值自动为T赋类型值或提示编译错误(参数值不当)。
考虑以下函数sum()
public static long sum(Collection<? extends Number> numbers) {
long sum = 0;
for (Number n : numbers) {
sum += n.longValue();
}
return sum;
}
咱们也能够将其以Generic方法实现:
public static <T extends Number> long sum(Collection<T> numbers) {
long sum = 0;
for (Number n : numbers) {
sum += n.longValue();
}
return sum;
}
那么对于一个方法,当要求参数类型可变时,是采用Generic方法,仍是采用类型参数通配符方式呢?通常而言,若是参数类型间或参数类型与返回值类型间存在某种依赖关系,则采起Generic方法,不然采起类型参数通配符方式。
这一原则在Collection类库的源代码中获得了很好的体现,例如Collection接口的containsAll()、addAll()和toArray()方法:
interface Collection<E> {
public boolean containsAll(Collecion<?> c); //参数间类型以及参数与返回
//值间类型无依赖
<T> T[] toArray(T[] a); //参数a与返回值都是相同类的数组,有依赖
}
固然,根据须要,两者也能够结合使用,例如Collections中的copy()方法:
class Collections {
public static <T> void copy(List<T> dest, List<? extends T> src) {
…….
}
}
jdk1.6新特性
1.Desktop类和SystemTray类 2.使用JAXB2来实现对象与XML之间的映射 3.StAX
4.使用Compiler API 5.轻量级Http Server API 6.插入式注解处理API(Pluggable Annotation Processing API)
7.用Console开发控制台程序 8.对脚本语言的支持 9.Common Annotations
1.Desktop类和SystemTray类
在JDK1.6中,AWT新增长了两个类:Desktop和SystemTray.
前者能够用来打开系统默认浏览器浏览指定的URL,打开系统默认邮件客户端给指定的邮箱发邮件,用默认应用程序打开或编辑文件(好比,用记事本打开以txt为后缀名的文件),用系统默认的打印机打印文档;后者能够用来在系统托盘区建立一个托盘程序.
2.使用JAXB2来实现对象与XML之间的映射
JAXB是Java Architecture for XML Binding的缩写,能够将一个Java对象转变成为XML格式,反之亦然.
我 们把对象与关系数据库之间的映射称为ORM,其实也能够把对象与XML之间的映射称为OXM(Object XML Mapping).原来JAXB是Java EE的一部分,在JDK1.6中,SUN将其放到了Java SE中,这也是SUN的一向作法.JDK1.6中自带的这个JAXB版本是2.0,比起1.0(JSR 31)来,JAXB2(JSR 222)用JDK5的新特性Annotation来标识要做绑定的类和属性等,这就极大简化了开发的工做量.实际上,在Java EE 5.0中,EJB和Web Services也经过Annotation来简化开发工做.另外,JAXB2在底层是用StAX(JSR 173)来处理XML文档.除了JAXB以外,咱们还能够经过XMLBeans和Castor等来实现一样的功能.
3.StAX
StAX(JSR 173)是JDK1.6.0中除了DOM和SAX以外的又一种处理XML文档的API.
StAX 的来历:在JAXP1.3(JSR 206)有两种处理XML文档的方法:DOM(Document Object Model)和SAX(Simple API for XML).
JDK1.6.0 中的JAXB2(JSR 222)和JAX-WS 2.0(JSR 224)都会用到StAXSun决定把StAX加入到JAXP家族当中来,并将JAXP的版本升级到1.4(JAXP1.4是JAXP1.3的维护版 本).JDK1.6里面JAXP的版本就是1.4.
StAX 是The Streaming API for XML的缩写,一种利用拉模式解析(pull-parsing)XML文档的API.StAX经过提供一种基于事件迭代器(Iterator)的API让 程序员去控制xml文档解析过程,程序遍历这个事件迭代器去处理每个解析事件,解析事件能够看作是程序拉出来的,也就是程序促使解析器产生一个解析事件 而后处理该事件,以后又促使解析器产生下一个解析事件,如此循环直到碰到文档结束符;
SAX 也是基于事件处理xml文档,但倒是用推模式解析,解析器解析完整个xml文档后,才产生解析事件,而后推给程序去处理这些事件;DOM采 用的方式是将整个xml文档映射到一颗内存树,这样就能够很容易地获得父节点和子结点以及兄弟节点的数据,但若是文档很大,将会严重影响性能.
4.使用Compiler API
如今我 们能够用JDK1.6 的Compiler API(JSR 199)去动态编译Java源文件,Compiler API结合反射功能就能够实现动态的产生Java代码并编译执行这些代码,有点动态语言的特征.
这 个特性对于某些须要用到动态编译的应用程序至关有用,好比JSP Web Server,当咱们手动修改JSP后,是不但愿须要重启Web Server才能够看到效果的,这时候咱们就能够用Compiler API来实现动态编译JSP文件,固然,如今的JSP Web Server也是支持JSP热部署的,如今的JSP Web Server经过在运行期间经过Runtime.exec或ProcessBuilder来调用javac来编译代码,这种方式须要咱们产生另外一个进程去 作编译工做,不够优雅容易使代码依赖与特定的操做系统;Compiler API经过一套易用的标准的API提供了更加丰富的方式去作动态编译,是跨平台的.
5.轻量级Http Server API
JDK1.6 提供了一个简单的Http Server API,据此咱们能够构建本身的嵌入式Http Server,它支持Http和Https协议,提供了HTTP1.1的部分实现,没有被实现的那部分能够经过扩展已有的Http Server API来实现,程序员本身实现HttpHandler接口,HttpServer会调用HttpHandler实现类的回调方法来处理客户端请求,在这 里,咱们把一个Http请求和它的响应称为一个交换,包装成HttpExchange类,HttpServer负责将HttpExchange传给 HttpHandler实现类的回调方法.
6.插入式注解处理API(Pluggable Annotation Processing API)
插入式注解处理API(JSR 269)提供一套标准API来处理Annotations(JSR 175)
实 际上JSR 269不只仅用来处理Annotation,我以为更强大的功能是它创建了Java 语言自己的一个模型,它把method,package,constructor,type,variable, enum,annotation等Java语言元素映射为Types和Elements(二者有什么区别?),从而将Java语言的语义映射成为对象,我 们能够在javax.lang.model包下面能够看到这些类. 咱们能够利用JSR 269提供的API来构建一个功能丰富的元编程(metaprogramming)环境.
JSR 269用Annotation Processor在编译期间而不是运行期间处理Annotation,Annotation Processor至关于编译器的一个插件,称为插入式注解处理.若是Annotation Processor处理Annotation时(执行process方法)产生了新的Java代码,编译器会再调用一次Annotation Processor,若是第二次处理还有新代码产生,就会接着调用Annotation Processor,直到没有新代码产生为止.每执行一次process()方法被称为一个"round",这样整个Annotation processing过程能够看做是一个round的序列.
JSR 269主要被设计成为针对Tools或者容器的API. 举个例子,咱们想创建一套基于Annotation的单元测试框架(如TestNG),在测试类里面用Annotation来标识测试期间须要执行的测试方法.
7.用Console开发控制台程序
JDK1.6 中提供了java.io.Console 类专用来访问基于字符的控制台设备.你的程序若是要与Windows下的cmd或者Linux下的Terminal交互,就能够用Console类代劳. 但咱们不老是能获得可用的Console,一个JVM是否有可用的Console依赖于底层平台和JVM如何被调用.若是JVM是在交互式命令行(好比 Windows的cmd)中启动的,而且输入输出没有重定向到另外的地方,那么就能够获得一个可用的Console实例.
8.对脚本语言的支持
如: ruby,groovy,javascript
9.Common Annotations
Common annotations本来是Java EE 5.0(JSR 244)规范的一部分,如今SUN把它的一部分放到了Java SE 6.0中.
随 着Annotation元数据功能(JSR 175)加入到Java SE 5.0里面,不少Java 技术(好比EJB,Web Services)都会用Annotation部分代替XML文件来配置运行参数(或者说是支持声明式编程,如EJB的声明式事务),若是这些技术为通用 目的都单独定义了本身的Annotations,显然有点重复建设,,为其余相关的Java技术定义一套公共的Annotation是有价值的,能够避免 重复建设的同时,也保证Java SE和Java EE 各类技术的一致性.
下 面列举出Common Annotations 1.0里面的10个Annotations Common Annotations Annotation Retention Target Description Generated Source ANNOTATION_TYPE,CONSTRUCTOR,FIELD,LOCAL_VARIABLE,METHOD,PACKAGE,PARAMETER,TYPE 用于标注生成的源代码Resource Runtime TYPE,METHOD,FIELD用于标注所依赖的资源,容器据此注入外部资源依赖,有基于字段的注入和基于setter方法的注入两种方式 Resources Runtime TYPE同时标注多个外部依赖,容器会把全部这些外部依赖注入PostConstruct Runtime METHOD标注当容器注入全部依赖以后运行的方法,用来进行依赖注入后的初始化工做,只有一个方法能够标注为PostConstruct PreDestroy Runtime METHOD当对象实例将要被从容器当中删掉以前,要执行的回调方法要标注为PreDestroy RunAs Runtime TYPE用于标注用什么安全角色来执行被标注类的方法,这个安全角色和Container的Security角色一致的.RolesAllowed Runtime TYPE,METHOD用于标注容许执行被标注类或方法的安全角色,这个安全角色和Container的Security角色一致的PermitAll Runtime TYPE,METHOD容许全部角色执行被标注的类或方法DenyAll Runtime TYPE,METHOD不容许任何角色执行被标注的类或方法,代表该类或方法不能在Java EE容器里面运行DeclareRoles Runtime TYPE用来定义能够被应用程序检验的安全角色,一般用isUserInRole来检验安全角色.
jdk1.7新特性
1 对集合类的语言支持; 2 自动资源管理; 3 改进的通用实例建立类型推断; 4 数字字面量下划线支持;
5 switch中使用string; 6 二进制字面量; 7 简化可变参数方法调用。
下面咱们来仔细看一下这7大新功能:
1 对集合类的语言支持
Java将包含对建立集合类的第一类语言支持。这意味着集合类的建立能够像Ruby和Perl那样了。
本来须要这样:
List<String> list = new ArrayList<String>();
list.add("item");
String item = list.get(0);
Set<String> set = new HashSet<String>();
set.add("item");
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("key", 1);
int value = map.get("key");
如今你能够这样:
List<String> list = ["item"];
String item = list[0];
Set<String> set = {"item"};
Map<String, Integer> map = {"key" : 1};
int value = map["key"];
这些集合是不可变的。
2 自动资源管理
Java中某些资源是须要手动关闭的,如InputStream,Writes,Sockets,Sql classes等。这个新语言特性容许try语句自己申请更多资源,
这些资源做用于try代码块,并自动关闭。
这个:
BufferedReader br = new BufferedReader(new FileReader(path));
try {
return br.readLine();
} finally {
br.close();
}
变成了这个:
try (BufferedReader br = new BufferedReader(new FileReader(path)) {
return br.readLine();
}
你能够定义关闭多个资源:
try (
InputStream in = new FileInputStream(src);
OutputStream out = new FileOutputStream(dest))
{
// code
}
为了支持这个行为,全部可关闭的类将被修改成能够实现一个Closable(可关闭的)接口。
3 加强的对通用实例建立(diamond)的类型推断
类型推断是一个特殊的烦恼,下面的代码:
Map<String, List<String>> anagrams = new HashMap<String, List<String>>();
经过类型推断后变成:
Map<String, List<String>> anagrams = new HashMap<>();
这个<>被叫作diamond(钻石)运算符,这个运算符从引用的声明中推断类型。
4 数字字面量下划线支持
很长的数字可读性很差,在Java 7中可使用下划线分隔长int以及long了,如:
int one_million = 1_000_000;
运算时先去除下划线,如:1_1 * 10 = 110,120 – 1_0 = 110
5 switch中使用string
之前你在switch中只能使用number或enum。如今你可使用string了:
String s = ...
switch(s) {
case "quux":
processQuux(s);
// fall-through
case "foo":
case "bar":
processFooOrBar(s); break;
case "baz":
processBaz(s);
// fall-through
default:
processDefault(s); break;
}
6 二进制字面量
因为继承C语言,Java代码在传统上迫使程序员只能使用十进制,八进制或十六进制来表示数(numbers)。
因为不多的域是以bit导向的,这种限制可能致使错误。你如今可使用0b前缀建立二进制字面量:
int binary = 0b1001_1001;
如今,你可使用二进制字面量这种表示方式,而且使用很是简短的代码,可将二进制字符转换为数据类型,如在byte或short。
byte aByte = (byte)0b001;
short aShort = (short)0b010;
7 简化的可变参数调用
当程序员试图使用一个不可具体化的可变参数并调用一个*varargs* (可变)方法时,编辑器会生成一个“非安全操做”的警告。
JDK 7将警告从call转移到了方法声明(methord declaration)的过程当中。这样API设计者就可使用vararg,由于警告的数量大大减小了。