- 浏览: 250219 次
- 性别:
- 来自: 北京
文章分类
- 全部博客 (192)
- 技术研究学习 (19)
- 工作总结 (4)
- spring3.x (11)
- mail (2)
- jar (2)
- FCKeditor (1)
- quartz (2)
- json (1)
- jdbc (5)
- struts2 (6)
- java基础 (18)
- jboss (3)
- IT名称解析 (1)
- 测试工具 (2)
- 工作趣谈 (1)
- 数据库 (8)
- js (8)
- jquery (1)
- mysql (20)
- Sql (3)
- Oracle (1)
- easyui (0)
- log4j (1)
- 源码研究 (1)
- Jasper Report (0)
- Jbpm4 (4)
- xml (1)
- ireport (0)
- javavm (1)
- sitemesh (5)
- compass (1)
- jvm (1)
- ext (1)
- lucene (0)
- cxf (1)
- Blazeds (0)
- Resteasy (1)
- jaxb (1)
- tomcat (1)
- Rmi (1)
- BoneCP (1)
- velocity (3)
- OSCache (1)
- EHCache (1)
- 高性能开发 (9)
- 设计模式 (0)
- 网络协议应用 (1)
- Ibatis (1)
- powerdesigner (1)
- 架构师之路 (2)
- memcached (4)
- MapReduce (1)
- 测试组 (1)
- 图像处理 (2)
- LoadRunner (2)
- 报表 (1)
- 负载均衡 (1)
- 分布式 (3)
- c# (1)
- java中一些特殊问题 (3)
- java 8 (1)
- Mogodb (1)
- 项目设计与实现 (2)
- Ubuntu (1)
- eclipse (1)
- gradle (1)
- 私有云 (1)
- redis (1)
- 移动前端 (1)
最新评论
来源:http://blog.csdn.net/fanyuna/article/details/6748159
简单说:1.4和1.5最大的区别有两个,一个是1.5有泛型,另一个1.5可以自动封装八大基本数据类型的封装数据类型,即,Integer a = 4这个1.4是不可以的。1.5和1.6的区别不大。1.6我觉得最多的变化,我觉得最大的部分是在GUI上面,提供了很多方便的布局管理和扩展。
weblogic8,那咱就用jdk1.4吧,eclipse一改jdk版本,这不以前的项目基本都一片红了。
★jdk1.5的新特性:
1. 泛型
2 自动装箱/拆箱
3 for-each
4 static import
5 变长参数
1. 泛型 (避免类型强制转换可能引起的运行错误)
例如:
ArrayList list=new ArrayList();
list.add(new Integer(3));
list.add(new Integer(4));
int i=((Integer)(list.get(0))).parseInt();
很麻烦
ArrayList<Integer>list=new ArrayList<Integer>();
list.add(new Integer(3));
list.add(new Integer(4));
int i=list.get(0).parseInt();
2 自动装箱/拆箱
上面例子的最后一句可改为:
int i=list.get(0);
因为原始类型与对应的包装类不用显式转换
3 for-each
循环的增强
int a[]={........};//初始化
for(int i:a)
{
......
}
不用以前的i=0;i<a.length;i++
4 static import
以前调Java.math
Math.sqrt();
现在 static import java.lang.Math.sqrt;
再 sqrt();
相当于你自己类里有这个方法
5 变长参数
int sum(int ...intlist)
{
int sum;
sum=0;
for(int i=0;i<intlist.length;i++)
{
sum+=intlist[i];
}
return sum;
}
有任意个参数,把他看作数组
★jdk6.0新特性
增强的for循环语句
注释
枚举
“隐藏的”静态方法
可变参数(Vararg)
通配符和协变返回
增强的for循环语句
为了迭代集合和数组,增强的for循环提供了一个简单、兼容的语法。有两点值得一提:
一、在循环中,初始化表达式只计算一次。int表达式
未增强的For:
int sum = 0;
Integer[] numbers = computeNumbers();
for (int i=0; i < numbers.length ; i++)
sum += numbers[i];
增强后的For:
int sum = 0;
for ( int number: computeNumbers() )
sum += number;
局限性
增强的for循环迭代期间不能访问迭代器或下标
请看下面的例子:
for (int i=0; i < numbers.length ; i++) {
if (i != 0) System.out.print(",");
System.out.print(numbers[i]);
}
这是另一个例子:
for (Iterator<integer> it = n.iterator() ; it.hasNext() ; )
if (it.next() < 0)
it.remove();
注释
注释处理是一个很大的话题。因为本文只关注核心的语言特性,所以我们不打算涵盖它所有的可能形式和陷阱。 我们将讨论内置的注释(SuppressWarnings,Deprecated和Override)以及一般注释
处理的局限性。
Suppress Warnings
该注释关闭了类或方法级别的编译器警告。有时候您比编译器更清楚地知道,代码必须使用一个被否决的方法或执行一些无法静态确定是否类型安全的动作,而使用:
@SuppressWarnings("deprecation")
public static void selfDestruct() {
Thread.currentThread().stop();
}
这可能是内置注释最有用的地方。遗憾的是,1.5.0_04的javac不支持它。但是1.6支持它,并且Sun正在努力将其向后移植到1.5中。
Eclipse 3.1中支持该注释,其他IDE也可能支持它。这允许您把代码彻底地从警告中解脱出来。如果在编译时出现警告,可以确定是您刚刚把它添加进来——以帮助查看那些可能不安全的代码。随着泛型
的添加,它使用起来将更趁手。
Deprecated
遗憾的是,Deprecated没那么有用。它本来旨在替换@deprecated javadoc标签,但是由于它不包含任何字段,所以也就没有方法来建议deprecated类或方法的用户应该使用什么做为替代品。大多数
用法都同时需要javadoc标签和这个注释。
Override
Override表示,它所注释的方法应该重写超类中具有相同签名的方法:
@Override
public int hashCode() {
...
}
看上面的例子,如果没有在hashCode中将“C”大写,在编译时不会出现错误,但是在运行时将无法像期望的那样调用该方法。通过添加Override标签,编译器会提示它是否真正地执行了重写。
在超类发生改变的情况中,这也很有帮助。如果向该方法中添加一个新参数,而且方法本身也被重命名了,那么子类将突然不能编译,因为它不再重写超类的任何东西。
其它注释
注释在其他场景中非常有用。当不是直接修改行为而是增强行为时,特别是在添加样板代码的情况下,注释在诸如EJB和Web services这样的框架中运行得非常好。
注释不能用做预处理器。Sun的设计特别预防了完全因为注释而修改类的字节码。这样可以正确地理解该语言的成果,而且IDE之类的工具也可以执行深入的代码分析和重构之类的功能。
注释不是银弹。第一次遇到的时候,人们试图尝试各种技巧。请看下面这个从别人那里获得的建议:
public class Foo {
@Property
private int bar;
}
其思想是为私有字段bar自动创建getter和setter方法。遗憾的是,这个想法有两个失败之处:1)它不能运行,2)它使代码难以阅读和处理。 它是无法实现的,因为前面已经提到了,Sun特别阻止了对出
现注释的类进行修改。
即使是可能的,它也不是一个好主意,因为它使代码可读性差。第一次看到这段代码的人会不知道该注释创建了方法。此外,如果将来您需要在这些方法内部执行一些操作,注释也是没用的。 总之,不要
试图用注释去做那些常规代码可以完成的事情。
枚举
enum非常像public static final int声明,后者作为枚举值已经使用了很多年。对int所做的最大也是最明显的改进是类型安全——您不能错误地用枚举的一种类型代替另一种类型,这一点和int不同,所
有的int对编译器来说都是一样的。除去极少数例外的情况,通常都应该用enum实例替换全部的枚举风格的int结构。
枚举提供了一些附加的特性。EnumMap和EnumSet这两个实用类是专门为枚举优化的标准集合实现。如果知道集合只包含枚举类型,那么应该使用这些专门的集合来代替HashMap或HashSet。
大部分情况下,可以使用enum对代码中的所有public static final int做插入替换。它们是可比的,并且可以静态导入,所以对它们的引用看起来是等同的,即使是对于内部类(或内部枚举类型)。注意
,比较枚举类型的时候,声明它们的指令表明了它们的顺序值。
“隐藏的”静态方法
两个静态方法出现在所有枚举类型声明中。因为它们是枚举子类上的静态方法,而不是Enum本身的方法,所以它们在java.lang.Enum的javadoc中没有出现。
第一个是values(),返回一个枚举类型所有可能值的数组。
第二个是valueOf(),为提供的字符串返回一个枚举类型,该枚举类型必须精确地匹配源代码声明。
方法
关于枚举类型,我们最喜欢的一个方面是它可以有方法。过去您可能需要编写一些代码,对public static final int进行转换,把它从数据库类型转换为JDBC URL。而现在则可以让枚举类型本身带一个整
理代码的方法。下面就是一个例子,包括DatabaseType枚举类型的抽象方法以及每个枚举实例中提供的实现:
public enum DatabaseType {
ORACLE {
public String getJdbcUrl() {...}
},
MYSQL {
public String getJdbcUrl() {...}
};
public abstract String getJdbcUrl();
}
现在枚举类型可以直接提供它的实用方法。例如:
DatabaseType dbType = ...;
String jdbcURL = dbType.getJdbcUrl();
要获取URL,必须预先知道该实用方法在哪里。
可变参数(Vararg)
正确地使用可变参数确实可以清理一些垃圾代码。典型的例子是一个带有可变的String参数个数的log方法:
Log.log(String code)
Log.log(String code, String arg)
Log.log(String code, String arg1, String arg2)
Log.log(String code, String[] args)
当讨论可变参数时,比较有趣的是,如果用新的可变参数替换前四个例子,将是兼容的:
Log.log(String code, String... args)
所有的可变参数都是源兼容的——那就是说,如果重新编译log()方法的所有调用程序,可以直接替换全部的四个方法。然而,如果需要向后的二进制兼容性,那么就需要舍去前三个方法。只有最后那个带
一个字符串数组参数的方法等效于可变参数版本,因此可以被可变参数版本替换。
类型强制转换
如果希望调用程序了解应该使用哪种类型的参数,那么应该避免用可变参数进行类型强制转换。看下面这个例子,第一项希望是String,第二项希望是Exception:
Log.log(Object... objects) {
String message = (String)objects[0];
if (objects.length > 1) {
Exception e = (Exception)objects[1];
// Do something with the exception
}
}
方法签名应该如下所示,相应的可变参数分别使用String和Exception声明:
Log.log(String message, Exception e, Object... objects) {...}
不要使用可变参数破坏类型系统。需要强类型化时才可以使用它。对于这个规则,PrintStream.printf()是一个有趣的例外:它提供类型信息作为自己的第一个参数,以便稍后可以接受那些类型。
协变返回
协变返回的基本用法是用于在已知一个实现的返回类型比API更具体的时候避免进行类型强制转换。在下面这个例子中,有一个返回Animal对象的Zoo接口。我们的实现返回一个AnimalImpl对象,但是
在JDK 1.5之前,要返回一个Animal对象就必须声明。:
public interface Zoo {
public Animal getAnimal();
}
public class ZooImpl implements Zoo {
public Animal getAnimal(){
return new AnimalImpl();
}
}
协变返回的使用替换了三个反模式:
直接字段访问。为了规避API限制,一些实现把子类直接暴露为字段:
ZooImpl._animal
另一种形式是,在知道实现的实际上是特定的子类的情况下,在调用程序中执行向下转换:
((AnimalImpl)ZooImpl.getAnimal()).implMethod();
我看到的最后一种形式是一个具体的方法,该方法用来避免由一个完全不同的签名所引发的问题:
ZooImpl._getAnimal();
这三种模式都有它们的问题和局限性。要么是不够整洁,要么就是暴露了不必要的实现细节。
协变
协变返回模式就比较整洁、安全并且易于维护,它也不需要类型强制转换或特定的方法或字段:
public AnimalImpl getAnimal(){
return new AnimalImpl();
}
使用结果:
ZooImpl.getAnimal().implMethod();
使用泛型
我们将从两个角度来了解泛型:使用泛型和构造泛型。我们不讨论List、Set和Map的显而易见的用法。知道泛型集合是强大的并且应该经常使用就足够了。
我们将讨论泛型方法的使用以及编译器推断类型的方法。通常这些都不会出问题,但是当出问题时,错误信息会非常令人费解,所以需要了解如何修复这些问题。
泛型方法
除了泛型类型,Java 5还引入了泛型方法。在这个来自java.util.Collections的例子中,构造了一个单元素列表。新的List的元素类型是根据传入方法的对象的类型来推断的:
static <T> List<T> Collections.singletonList(T o)
示例用法:
public List<Integer> getListOfOne() {
return Collections.singletonList(1);
}
在示例用法中,我们传入了一个int。所以方法的返回类型就是List<Integer>。编译器把T推断为Integer。这和泛型类型是不同的,因为您通常不需要显式地指定类型参数。
这也显示了自动装箱和泛型的相互作用。类型参数必须是引用类型:这就是为什么我们得到的是List<Integer>而不是List<int>。
不带参数的泛型方法
emptyList()方法与泛型一起引入,作为java.util.Collections中EMPTY_LIST字段的类型安全置换:
static <T> List<T> Collections.emptyList()
示例用法:
public List<Integer> getNoIntegers() {
return Collections.emptyList();
}
与先前的例子不同,这个方法没有参数,那么编译器如何推断T的类型呢?基本上,它将尝试使用一次参数。如果没有起作用,它再次尝试使用返回或赋值类型。在本例中,返回的是List<Integer>,所
以T被推断为Integer。
如果在返回语句或赋值语句之外的位置调用泛型方法会怎么样呢?那么编译器将无法执行类型推断的第二次传送。在下面这个例子中,emptyList()是从条件运算符内部调用的:
public List<Integer> getNoIntegers() {
return x ? Collections.emptyList() : null;
}
因为编译器看不到返回上下文,也不能推断T,所以它放弃并采用Object。您将看到一个错误消息,比如:“无法将List<Object>转换为List<Integer>。”
为了修复这个错误,应显式地向方法调用传递类型参数。这样,编译器就不会试图推断类型参数,就可以获得正确的结果:
return x ? Collections.<Integer>emptyList() : null;
这种情况经常发生的另一个地方是在方法调用中。如果一个方法带一个List<String>参数,并且需要为那个参数调用这个传递的emptyList(),那么也需要使用这个语法。
集合之外
这里有三个泛型类型的例子,它们不是集合,而是以一种新颖的方式使用泛型。这三个例子都来自标准的Java库:
Class<T>
Class在类的类型上被参数化了。这就使无需类型强制转换而构造一个newInstance成为可能。
Comparable<T>
Comparable被实际的比较类型参数化。这就在compareTo()调用时提供了更强的类型化。例如,String实现Comparable<String>。对除String之外的任何东西调用compareTo(),都会在编译时失败
。
Enum<E extends Enum<E>>
Enum被枚举类型参数化。一个名为Color的枚举类型将扩展Enum<Color>。getDeclaringClass()方法返回枚举类型的类对象,在这个例子中就是一个Color对象。它与getClass()不同,后者可能返回
一个无名类。
通配符
泛型最复杂的部分是对通配符的理解。我们将讨论三种类型的通配符以及它们的用途。
首先让我们了解一下数组是如何工作的。可以从一个Integer[]为一个Number[]赋值。如果尝试把一个Float写到Number[]中,那么可以编译,但在运行时会失败,出现一个ArrayStoreException:
Integer[] ia = new Integer[5];
Number[] na = ia;
na[0] = 0.5; // compiles, but fails at runtime
如果试图把该例直接转换成泛型,那么会在编译时失败,因为赋值是不被允许的:
List<Integer> iList = new ArrayList<Integer>();
List<Number> nList = iList; // not allowed
nList.add(0.5);
如果使用泛型,只要代码在编译时没有出现警告,就不会遇到运行时ClassCastException。
上限通配符
我们想要的是一个确切元素类型未知的列表,这一点与数组是不同的。
List<Number>是一个列表,其元素类型是具体类型Number。
List<? extends Number>是一个确切元素类型未知的列表。它是Number或其子类型。
上限
如果我们更新初始的例子,并赋值给List<? extends Number>,那么现在赋值就会成功了:
List<Integer> iList = new ArrayList<Integer>();
List<? extends Number> nList = iList;
Number n = nList.get(0);
nList.add(0.5); // Not allowed
我们可以从列表中得到Number,因为无论列表的确切元素类型是什么(Float、Integer或Number),我们都可以把它赋值给Number。
我们仍然不能把浮点类型插入列表中。这会在编译时失败,因为我们不能证明这是安全的。如果我们想要向列表中添加浮点类型,它将破坏iList的初始类型安全——它只存储Integer。
通配符给了我们比数组更多的表达能力。
为什么使用通配符
在下面这个例子中,通配符用于向API的用户隐藏类型信息。在内部,Set被存储为CustomerImpl。而API的用户只知道他们正在获取一个Set,从中可以读取Customer。
此处通配符是必需的,因为无法从Set<CustomerImpl>向Set<Customer>赋值:
public class CustomerFactory {
private Set<CustomerImpl> _customers;
public Set<? extends Customer> getCustomers() {
return _customers;
}
}
通配符和协变返回
通配符的另一种常见用法是和协变返回一起使用。与赋值相同的规则可以应用到协变返回上。如果希望在重写的方法中返回一个更具体的泛型类型,声明的方法必须使用通配符:
public interface NumberGenerator {
public List<? extends Number> generate();
}
public class FibonacciGenerator extends NumberGenerator {
public List<Integer> generate() {
...
}
}
如果要使用数组,接口可以返回Number[],而实现可以返回Integer[]。
下限
我们所谈的主要是关于上限通配符的。还有一个下限通配符。List<? super Number>是一个确切“元素类型”未知的列表,但是可能是Mnumber,或者Number的超类型。所以它可能是一个
List<Number>或一个List<Object>。
下限通配符远没有上限通配符那样常见,但是当需要它们的时候,它们就是必需的。
下限与上限
List<? extends Number> readList = new ArrayList<Integer>();
Number n = readList.get(0);
List<? super Number> writeList = new ArrayList<Object>();
writeList.add(new Integer(5));
第一个是可以从中读数的列表。
第二个是可以向其写数的列表。
无界通配符
最后,List<?>列表的内容可以是任何类型,而且它与List<? extends Object>几乎相同。可以随时读取Object,但是不能向列表中写入内容。
公共API中的通配符
总之,正如前面所说,通配符在向调用程序隐藏实现细节方面是非常重要的,但即使下限通配符看起来是提供只读访问,由于remove(int position)之类的非泛型方法,它们也并非如此。如果您想要一个
真正不变的集合,可以使用java.util.Collection上的方法,比如unmodifiableList()。
编写API的时候要记得通配符。通常,在传递泛型类型时,应该尝试使用通配符。它使更多的调用程序可以访问API。
通过接收List<? extends Number>而不是List<Number>,下面的方法可以由许多不同类型的列表调用:
void removeNegatives(List<? extends Number> list);
构造泛型类型
现在我们将讨论构造自己的泛型类型。我们将展示一些例子,其中通过使用泛型可以提高类型安全性,我们还将讨论一些实现泛型类型时的常见问题。
集合风格(Collection-like)的函数
第一个泛型类的例子是一个集合风格的例子。Pair有两个类型参数,而且字段是类型的实例:
public final class Pair<A,B> {
public final A first;
public final B second;
public Pair(A first, B second) {
this.first = first;
this.second = second;
}
}
这使从方法返回两个项而无需为每个两种类型的组合编写专用的类成为可能。另一种方法是返回Object[],而这样是类型不安全或者不整洁的。
在下面的用法中,我们从方法返回一个File和一个Boolean。方法的客户端可以直接使用字段而无需类型强制转换:
public Pair<File,Boolean> getFileAndWriteStatus(String path){
// create file and status
return new Pair<File,Boolean>(file, status);
}
Pair<File,Boolean> result = getFileAndWriteStatus("...");
File f = result.first;
boolean writeable = result.second;
集合之外
在下面这个例子中,泛型被用于附加的编译时安全性。通过把DBFactory类参数化为所创建的Peer类型,您实际上是在强制Factory子类返回一个Peer的特定子类型:
public abstract class DBFactory<T extends DBPeer> {
protected abstract T createEmptyPeer();
public List<T> get(String constraint) {
List<T> peers = new ArrayList<T>();
// database magic
return peers;
}
}
通过实现DBFactory<Customer>,CustomerFactory必须从createEmptyPeer()返回一个Customer:
public class CustomerFactory extends DBFactory<Customer>{
public Customer createEmptyPeer() {
return new Customer();
}
}
泛型方法
不管想要对参数之间还是参数与返回类型之间的泛型类型施加约束,都可以使用泛型方法:
例如,如果编写的反转函数是在位置上反转,那么可能不需要泛型方法。然而,如果希望反转返回一个新的List,那么可能会希望新List的元素类型与传入的List的类型相同。在这种情况下,就需要一个泛
型方法:
<T> List<T> reverse(List<T> list)
具体化
当实现一个泛型类时,您可能想要构造一个数组T[]。因为泛型是通过擦除(erasure)实现的,所以这是不允许的。
您可以尝试把Object[]强制转换为T[]。但这是不安全的。
具体化解决方案
按照泛型教程的惯例,解决方案使用的是“类型令牌”,通过向构造函数添加一个Class<T>参数,可以强制客户端为类的类型参数提供正确的类对象:
public class ArrayExample<T> {
private Class<T> clazz;
public ArrayExample(Class<T> clazz) {
this.clazz = clazz;
}
public T[] getArray(int size) {
return (T[])Array.newInstance(clazz, size);
}
}
为了构造ArrayExample<String>,客户端必须把String.class传递给构造函数,因为String.class的类型是Class<String>。
拥有类对象使构造一个具有正确元素类型的数组成为可能
简单说:1.4和1.5最大的区别有两个,一个是1.5有泛型,另一个1.5可以自动封装八大基本数据类型的封装数据类型,即,Integer a = 4这个1.4是不可以的。1.5和1.6的区别不大。1.6我觉得最多的变化,我觉得最大的部分是在GUI上面,提供了很多方便的布局管理和扩展。
weblogic8,那咱就用jdk1.4吧,eclipse一改jdk版本,这不以前的项目基本都一片红了。
★jdk1.5的新特性:
1. 泛型
2 自动装箱/拆箱
3 for-each
4 static import
5 变长参数
1. 泛型 (避免类型强制转换可能引起的运行错误)
例如:
ArrayList list=new ArrayList();
list.add(new Integer(3));
list.add(new Integer(4));
int i=((Integer)(list.get(0))).parseInt();
很麻烦
ArrayList<Integer>list=new ArrayList<Integer>();
list.add(new Integer(3));
list.add(new Integer(4));
int i=list.get(0).parseInt();
2 自动装箱/拆箱
上面例子的最后一句可改为:
int i=list.get(0);
因为原始类型与对应的包装类不用显式转换
3 for-each
循环的增强
int a[]={........};//初始化
for(int i:a)
{
......
}
不用以前的i=0;i<a.length;i++
4 static import
以前调Java.math
Math.sqrt();
现在 static import java.lang.Math.sqrt;
再 sqrt();
相当于你自己类里有这个方法
5 变长参数
int sum(int ...intlist)
{
int sum;
sum=0;
for(int i=0;i<intlist.length;i++)
{
sum+=intlist[i];
}
return sum;
}
有任意个参数,把他看作数组
★jdk6.0新特性
增强的for循环语句
注释
枚举
“隐藏的”静态方法
可变参数(Vararg)
通配符和协变返回
增强的for循环语句
为了迭代集合和数组,增强的for循环提供了一个简单、兼容的语法。有两点值得一提:
一、在循环中,初始化表达式只计算一次。int表达式
未增强的For:
int sum = 0;
Integer[] numbers = computeNumbers();
for (int i=0; i < numbers.length ; i++)
sum += numbers[i];
增强后的For:
int sum = 0;
for ( int number: computeNumbers() )
sum += number;
局限性
增强的for循环迭代期间不能访问迭代器或下标
请看下面的例子:
for (int i=0; i < numbers.length ; i++) {
if (i != 0) System.out.print(",");
System.out.print(numbers[i]);
}
这是另一个例子:
for (Iterator<integer> it = n.iterator() ; it.hasNext() ; )
if (it.next() < 0)
it.remove();
注释
注释处理是一个很大的话题。因为本文只关注核心的语言特性,所以我们不打算涵盖它所有的可能形式和陷阱。 我们将讨论内置的注释(SuppressWarnings,Deprecated和Override)以及一般注释
处理的局限性。
Suppress Warnings
该注释关闭了类或方法级别的编译器警告。有时候您比编译器更清楚地知道,代码必须使用一个被否决的方法或执行一些无法静态确定是否类型安全的动作,而使用:
@SuppressWarnings("deprecation")
public static void selfDestruct() {
Thread.currentThread().stop();
}
这可能是内置注释最有用的地方。遗憾的是,1.5.0_04的javac不支持它。但是1.6支持它,并且Sun正在努力将其向后移植到1.5中。
Eclipse 3.1中支持该注释,其他IDE也可能支持它。这允许您把代码彻底地从警告中解脱出来。如果在编译时出现警告,可以确定是您刚刚把它添加进来——以帮助查看那些可能不安全的代码。随着泛型
的添加,它使用起来将更趁手。
Deprecated
遗憾的是,Deprecated没那么有用。它本来旨在替换@deprecated javadoc标签,但是由于它不包含任何字段,所以也就没有方法来建议deprecated类或方法的用户应该使用什么做为替代品。大多数
用法都同时需要javadoc标签和这个注释。
Override
Override表示,它所注释的方法应该重写超类中具有相同签名的方法:
@Override
public int hashCode() {
...
}
看上面的例子,如果没有在hashCode中将“C”大写,在编译时不会出现错误,但是在运行时将无法像期望的那样调用该方法。通过添加Override标签,编译器会提示它是否真正地执行了重写。
在超类发生改变的情况中,这也很有帮助。如果向该方法中添加一个新参数,而且方法本身也被重命名了,那么子类将突然不能编译,因为它不再重写超类的任何东西。
其它注释
注释在其他场景中非常有用。当不是直接修改行为而是增强行为时,特别是在添加样板代码的情况下,注释在诸如EJB和Web services这样的框架中运行得非常好。
注释不能用做预处理器。Sun的设计特别预防了完全因为注释而修改类的字节码。这样可以正确地理解该语言的成果,而且IDE之类的工具也可以执行深入的代码分析和重构之类的功能。
注释不是银弹。第一次遇到的时候,人们试图尝试各种技巧。请看下面这个从别人那里获得的建议:
public class Foo {
@Property
private int bar;
}
其思想是为私有字段bar自动创建getter和setter方法。遗憾的是,这个想法有两个失败之处:1)它不能运行,2)它使代码难以阅读和处理。 它是无法实现的,因为前面已经提到了,Sun特别阻止了对出
现注释的类进行修改。
即使是可能的,它也不是一个好主意,因为它使代码可读性差。第一次看到这段代码的人会不知道该注释创建了方法。此外,如果将来您需要在这些方法内部执行一些操作,注释也是没用的。 总之,不要
试图用注释去做那些常规代码可以完成的事情。
枚举
enum非常像public static final int声明,后者作为枚举值已经使用了很多年。对int所做的最大也是最明显的改进是类型安全——您不能错误地用枚举的一种类型代替另一种类型,这一点和int不同,所
有的int对编译器来说都是一样的。除去极少数例外的情况,通常都应该用enum实例替换全部的枚举风格的int结构。
枚举提供了一些附加的特性。EnumMap和EnumSet这两个实用类是专门为枚举优化的标准集合实现。如果知道集合只包含枚举类型,那么应该使用这些专门的集合来代替HashMap或HashSet。
大部分情况下,可以使用enum对代码中的所有public static final int做插入替换。它们是可比的,并且可以静态导入,所以对它们的引用看起来是等同的,即使是对于内部类(或内部枚举类型)。注意
,比较枚举类型的时候,声明它们的指令表明了它们的顺序值。
“隐藏的”静态方法
两个静态方法出现在所有枚举类型声明中。因为它们是枚举子类上的静态方法,而不是Enum本身的方法,所以它们在java.lang.Enum的javadoc中没有出现。
第一个是values(),返回一个枚举类型所有可能值的数组。
第二个是valueOf(),为提供的字符串返回一个枚举类型,该枚举类型必须精确地匹配源代码声明。
方法
关于枚举类型,我们最喜欢的一个方面是它可以有方法。过去您可能需要编写一些代码,对public static final int进行转换,把它从数据库类型转换为JDBC URL。而现在则可以让枚举类型本身带一个整
理代码的方法。下面就是一个例子,包括DatabaseType枚举类型的抽象方法以及每个枚举实例中提供的实现:
public enum DatabaseType {
ORACLE {
public String getJdbcUrl() {...}
},
MYSQL {
public String getJdbcUrl() {...}
};
public abstract String getJdbcUrl();
}
现在枚举类型可以直接提供它的实用方法。例如:
DatabaseType dbType = ...;
String jdbcURL = dbType.getJdbcUrl();
要获取URL,必须预先知道该实用方法在哪里。
可变参数(Vararg)
正确地使用可变参数确实可以清理一些垃圾代码。典型的例子是一个带有可变的String参数个数的log方法:
Log.log(String code)
Log.log(String code, String arg)
Log.log(String code, String arg1, String arg2)
Log.log(String code, String[] args)
当讨论可变参数时,比较有趣的是,如果用新的可变参数替换前四个例子,将是兼容的:
Log.log(String code, String... args)
所有的可变参数都是源兼容的——那就是说,如果重新编译log()方法的所有调用程序,可以直接替换全部的四个方法。然而,如果需要向后的二进制兼容性,那么就需要舍去前三个方法。只有最后那个带
一个字符串数组参数的方法等效于可变参数版本,因此可以被可变参数版本替换。
类型强制转换
如果希望调用程序了解应该使用哪种类型的参数,那么应该避免用可变参数进行类型强制转换。看下面这个例子,第一项希望是String,第二项希望是Exception:
Log.log(Object... objects) {
String message = (String)objects[0];
if (objects.length > 1) {
Exception e = (Exception)objects[1];
// Do something with the exception
}
}
方法签名应该如下所示,相应的可变参数分别使用String和Exception声明:
Log.log(String message, Exception e, Object... objects) {...}
不要使用可变参数破坏类型系统。需要强类型化时才可以使用它。对于这个规则,PrintStream.printf()是一个有趣的例外:它提供类型信息作为自己的第一个参数,以便稍后可以接受那些类型。
协变返回
协变返回的基本用法是用于在已知一个实现的返回类型比API更具体的时候避免进行类型强制转换。在下面这个例子中,有一个返回Animal对象的Zoo接口。我们的实现返回一个AnimalImpl对象,但是
在JDK 1.5之前,要返回一个Animal对象就必须声明。:
public interface Zoo {
public Animal getAnimal();
}
public class ZooImpl implements Zoo {
public Animal getAnimal(){
return new AnimalImpl();
}
}
协变返回的使用替换了三个反模式:
直接字段访问。为了规避API限制,一些实现把子类直接暴露为字段:
ZooImpl._animal
另一种形式是,在知道实现的实际上是特定的子类的情况下,在调用程序中执行向下转换:
((AnimalImpl)ZooImpl.getAnimal()).implMethod();
我看到的最后一种形式是一个具体的方法,该方法用来避免由一个完全不同的签名所引发的问题:
ZooImpl._getAnimal();
这三种模式都有它们的问题和局限性。要么是不够整洁,要么就是暴露了不必要的实现细节。
协变
协变返回模式就比较整洁、安全并且易于维护,它也不需要类型强制转换或特定的方法或字段:
public AnimalImpl getAnimal(){
return new AnimalImpl();
}
使用结果:
ZooImpl.getAnimal().implMethod();
使用泛型
我们将从两个角度来了解泛型:使用泛型和构造泛型。我们不讨论List、Set和Map的显而易见的用法。知道泛型集合是强大的并且应该经常使用就足够了。
我们将讨论泛型方法的使用以及编译器推断类型的方法。通常这些都不会出问题,但是当出问题时,错误信息会非常令人费解,所以需要了解如何修复这些问题。
泛型方法
除了泛型类型,Java 5还引入了泛型方法。在这个来自java.util.Collections的例子中,构造了一个单元素列表。新的List的元素类型是根据传入方法的对象的类型来推断的:
static <T> List<T> Collections.singletonList(T o)
示例用法:
public List<Integer> getListOfOne() {
return Collections.singletonList(1);
}
在示例用法中,我们传入了一个int。所以方法的返回类型就是List<Integer>。编译器把T推断为Integer。这和泛型类型是不同的,因为您通常不需要显式地指定类型参数。
这也显示了自动装箱和泛型的相互作用。类型参数必须是引用类型:这就是为什么我们得到的是List<Integer>而不是List<int>。
不带参数的泛型方法
emptyList()方法与泛型一起引入,作为java.util.Collections中EMPTY_LIST字段的类型安全置换:
static <T> List<T> Collections.emptyList()
示例用法:
public List<Integer> getNoIntegers() {
return Collections.emptyList();
}
与先前的例子不同,这个方法没有参数,那么编译器如何推断T的类型呢?基本上,它将尝试使用一次参数。如果没有起作用,它再次尝试使用返回或赋值类型。在本例中,返回的是List<Integer>,所
以T被推断为Integer。
如果在返回语句或赋值语句之外的位置调用泛型方法会怎么样呢?那么编译器将无法执行类型推断的第二次传送。在下面这个例子中,emptyList()是从条件运算符内部调用的:
public List<Integer> getNoIntegers() {
return x ? Collections.emptyList() : null;
}
因为编译器看不到返回上下文,也不能推断T,所以它放弃并采用Object。您将看到一个错误消息,比如:“无法将List<Object>转换为List<Integer>。”
为了修复这个错误,应显式地向方法调用传递类型参数。这样,编译器就不会试图推断类型参数,就可以获得正确的结果:
return x ? Collections.<Integer>emptyList() : null;
这种情况经常发生的另一个地方是在方法调用中。如果一个方法带一个List<String>参数,并且需要为那个参数调用这个传递的emptyList(),那么也需要使用这个语法。
集合之外
这里有三个泛型类型的例子,它们不是集合,而是以一种新颖的方式使用泛型。这三个例子都来自标准的Java库:
Class<T>
Class在类的类型上被参数化了。这就使无需类型强制转换而构造一个newInstance成为可能。
Comparable<T>
Comparable被实际的比较类型参数化。这就在compareTo()调用时提供了更强的类型化。例如,String实现Comparable<String>。对除String之外的任何东西调用compareTo(),都会在编译时失败
。
Enum<E extends Enum<E>>
Enum被枚举类型参数化。一个名为Color的枚举类型将扩展Enum<Color>。getDeclaringClass()方法返回枚举类型的类对象,在这个例子中就是一个Color对象。它与getClass()不同,后者可能返回
一个无名类。
通配符
泛型最复杂的部分是对通配符的理解。我们将讨论三种类型的通配符以及它们的用途。
首先让我们了解一下数组是如何工作的。可以从一个Integer[]为一个Number[]赋值。如果尝试把一个Float写到Number[]中,那么可以编译,但在运行时会失败,出现一个ArrayStoreException:
Integer[] ia = new Integer[5];
Number[] na = ia;
na[0] = 0.5; // compiles, but fails at runtime
如果试图把该例直接转换成泛型,那么会在编译时失败,因为赋值是不被允许的:
List<Integer> iList = new ArrayList<Integer>();
List<Number> nList = iList; // not allowed
nList.add(0.5);
如果使用泛型,只要代码在编译时没有出现警告,就不会遇到运行时ClassCastException。
上限通配符
我们想要的是一个确切元素类型未知的列表,这一点与数组是不同的。
List<Number>是一个列表,其元素类型是具体类型Number。
List<? extends Number>是一个确切元素类型未知的列表。它是Number或其子类型。
上限
如果我们更新初始的例子,并赋值给List<? extends Number>,那么现在赋值就会成功了:
List<Integer> iList = new ArrayList<Integer>();
List<? extends Number> nList = iList;
Number n = nList.get(0);
nList.add(0.5); // Not allowed
我们可以从列表中得到Number,因为无论列表的确切元素类型是什么(Float、Integer或Number),我们都可以把它赋值给Number。
我们仍然不能把浮点类型插入列表中。这会在编译时失败,因为我们不能证明这是安全的。如果我们想要向列表中添加浮点类型,它将破坏iList的初始类型安全——它只存储Integer。
通配符给了我们比数组更多的表达能力。
为什么使用通配符
在下面这个例子中,通配符用于向API的用户隐藏类型信息。在内部,Set被存储为CustomerImpl。而API的用户只知道他们正在获取一个Set,从中可以读取Customer。
此处通配符是必需的,因为无法从Set<CustomerImpl>向Set<Customer>赋值:
public class CustomerFactory {
private Set<CustomerImpl> _customers;
public Set<? extends Customer> getCustomers() {
return _customers;
}
}
通配符和协变返回
通配符的另一种常见用法是和协变返回一起使用。与赋值相同的规则可以应用到协变返回上。如果希望在重写的方法中返回一个更具体的泛型类型,声明的方法必须使用通配符:
public interface NumberGenerator {
public List<? extends Number> generate();
}
public class FibonacciGenerator extends NumberGenerator {
public List<Integer> generate() {
...
}
}
如果要使用数组,接口可以返回Number[],而实现可以返回Integer[]。
下限
我们所谈的主要是关于上限通配符的。还有一个下限通配符。List<? super Number>是一个确切“元素类型”未知的列表,但是可能是Mnumber,或者Number的超类型。所以它可能是一个
List<Number>或一个List<Object>。
下限通配符远没有上限通配符那样常见,但是当需要它们的时候,它们就是必需的。
下限与上限
List<? extends Number> readList = new ArrayList<Integer>();
Number n = readList.get(0);
List<? super Number> writeList = new ArrayList<Object>();
writeList.add(new Integer(5));
第一个是可以从中读数的列表。
第二个是可以向其写数的列表。
无界通配符
最后,List<?>列表的内容可以是任何类型,而且它与List<? extends Object>几乎相同。可以随时读取Object,但是不能向列表中写入内容。
公共API中的通配符
总之,正如前面所说,通配符在向调用程序隐藏实现细节方面是非常重要的,但即使下限通配符看起来是提供只读访问,由于remove(int position)之类的非泛型方法,它们也并非如此。如果您想要一个
真正不变的集合,可以使用java.util.Collection上的方法,比如unmodifiableList()。
编写API的时候要记得通配符。通常,在传递泛型类型时,应该尝试使用通配符。它使更多的调用程序可以访问API。
通过接收List<? extends Number>而不是List<Number>,下面的方法可以由许多不同类型的列表调用:
void removeNegatives(List<? extends Number> list);
构造泛型类型
现在我们将讨论构造自己的泛型类型。我们将展示一些例子,其中通过使用泛型可以提高类型安全性,我们还将讨论一些实现泛型类型时的常见问题。
集合风格(Collection-like)的函数
第一个泛型类的例子是一个集合风格的例子。Pair有两个类型参数,而且字段是类型的实例:
public final class Pair<A,B> {
public final A first;
public final B second;
public Pair(A first, B second) {
this.first = first;
this.second = second;
}
}
这使从方法返回两个项而无需为每个两种类型的组合编写专用的类成为可能。另一种方法是返回Object[],而这样是类型不安全或者不整洁的。
在下面的用法中,我们从方法返回一个File和一个Boolean。方法的客户端可以直接使用字段而无需类型强制转换:
public Pair<File,Boolean> getFileAndWriteStatus(String path){
// create file and status
return new Pair<File,Boolean>(file, status);
}
Pair<File,Boolean> result = getFileAndWriteStatus("...");
File f = result.first;
boolean writeable = result.second;
集合之外
在下面这个例子中,泛型被用于附加的编译时安全性。通过把DBFactory类参数化为所创建的Peer类型,您实际上是在强制Factory子类返回一个Peer的特定子类型:
public abstract class DBFactory<T extends DBPeer> {
protected abstract T createEmptyPeer();
public List<T> get(String constraint) {
List<T> peers = new ArrayList<T>();
// database magic
return peers;
}
}
通过实现DBFactory<Customer>,CustomerFactory必须从createEmptyPeer()返回一个Customer:
public class CustomerFactory extends DBFactory<Customer>{
public Customer createEmptyPeer() {
return new Customer();
}
}
泛型方法
不管想要对参数之间还是参数与返回类型之间的泛型类型施加约束,都可以使用泛型方法:
例如,如果编写的反转函数是在位置上反转,那么可能不需要泛型方法。然而,如果希望反转返回一个新的List,那么可能会希望新List的元素类型与传入的List的类型相同。在这种情况下,就需要一个泛
型方法:
<T> List<T> reverse(List<T> list)
具体化
当实现一个泛型类时,您可能想要构造一个数组T[]。因为泛型是通过擦除(erasure)实现的,所以这是不允许的。
您可以尝试把Object[]强制转换为T[]。但这是不安全的。
具体化解决方案
按照泛型教程的惯例,解决方案使用的是“类型令牌”,通过向构造函数添加一个Class<T>参数,可以强制客户端为类的类型参数提供正确的类对象:
public class ArrayExample<T> {
private Class<T> clazz;
public ArrayExample(Class<T> clazz) {
this.clazz = clazz;
}
public T[] getArray(int size) {
return (T[])Array.newInstance(clazz, size);
}
}
为了构造ArrayExample<String>,客户端必须把String.class传递给构造函数,因为String.class的类型是Class<String>。
拥有类对象使构造一个具有正确元素类型的数组成为可能
发表评论
-
Response.ContentType 详解
2013-03-13 09:45 1353不同的ContentType 会影响客户端所看到的效果.默认的 ... -
JAVA获取本地,远程macAddress
2013-02-27 13:56 981来源:http://www.2cto.com/kf/20110 ... -
java中断点续传
2013-02-22 15:38 1288转载自 http://www.ibm.com/ ... -
nio
2013-02-22 09:39 922来源http://blog.chinaunix.net ... -
使用非阻塞ServerSocketChannel、SocketChannel代替ServerSocket和Socket
2013-02-19 09:52 2577来源:http://blog.sina.com.cn/s/ ... -
java字符串编码类型获取
2012-10-05 10:11 938原创作品,允许转载,转载时请务必以超链接形式标明文章 ... -
Java网络编程总结
2012-09-29 14:42 0http://www.cnblogs.com/oubo/arc ... -
Java使用JNDI技术获取DataSource对象
2012-09-29 11:28 1223package common; imp ... -
Java并发包探秘 (二) ConcurrentHashMap
2012-09-27 10:49 952Java并发包中有很多精心设计的高并发容器。有Concur ... -
Java并发包探秘 (一) ConcurrentLinkedQueue
2012-09-27 10:47 1129Java并发包中有很多精心设计的高并发容器。有Conc ... -
集合之线程安全
2012-09-27 10:28 992Map map = Collections.synchro ... -
Collections.synchronizedMap(new LinkedHashMap())
2012-09-27 10:24 1266LinkedHashMap和LinkedHashSet是J ... -
Java栈与堆
2012-08-16 12:26 735Java栈与堆 ----对这两 ... -
Java中对象的六种可触及状态
2012-08-20 14:07 10291. 强可触及 垃圾收集器不会回收强可触及对象占据的空间 ... -
多线程 sleep()和wait()的区别
2012-08-14 10:12 1033接触了一些多线程的东西,还是从java入手吧。 相信看这篇文 ... -
java oop
2012-08-14 10:13 874对象一般都有两个特征:状态和行为 类实质上定义的是一种数据类 ... -
override与overload
2012-08-14 10:12 730重载Overload特点(两必须一可以) public boo ... -
Java中堆栈和内存分配原理
2012-08-13 09:32 786来源:http://uule.iteye.com/blog/1 ...
相关推荐
通过对比JDK1.4、JDK1.5和JDK1.6之间的区别,我们可以看到Java的发展历程中不断进步和完善的方向。JDK1.5通过引入泛型、自动装箱/拆箱等功能显著提高了开发效率和代码质量;而JDK1.6则在此基础上进一步增强了这些...
本文将深入探讨JDK 1.4、JDK 1.5(也称为Java 5)和JDK 6这三个重要版本的关键特性。 **JDK 1.4** JDK 1.4是Java发展历程中的一个里程碑,发布于2002年。这个版本引入了许多关键的新特性,包括: 1. **异常链**:...
在探讨如何在操作系统中同时安装了JDK1.4和JDK1.5的情况下更改环境变量等设置之前,我们先来了解一下JDK的基本概念及其版本更迭的重要性。 ### JDK简介 JDK(Java Development Kit)是Sun Microsystems为Java...
在JDK 1.4和JDK 1.5时代,JavaMail API已经相当成熟,可以满足基本的邮件发送需求。下面将详细介绍这两个版本中使用Java发送邮件的关键知识点。 1. **JavaMail API**: JavaMail API是Java平台上的一个开源库,用于...
JCE无限制权限策略文件,下载后解压,可以看到local_policy.jar和US_export_policy.jar以及readme.txt。将两个jar文件放到%JDK_HOME%\jre\lib\security下覆盖原来文件,记得先备份
尽管现在已经被更新的版本如JDK 8、JDK 11和JDK 17所取代,但JDK 1.4对于理解Java的发展历程具有重要意义。 **主要特性** 1. **异常链**:在JDK 1.4中,引入了异常链机制,允许一个异常捕获另一个异常,并将它们...
例如,Spring框架的AOP(面向切面编程)和IoC(控制反转)概念在JDK 1.4的环境下得以实现,而Hibernate的ORM(对象关系映射)技术也是基于JDK 1.4提供的JDBC和反射机制。 此外,压缩包中的`j2sdk-1_4_2_13-windows-...
这个压缩包包含了三个不同版本的JDK:JDK 1.5、JDK 1.6和JDK 1.8,其中1.5和1.6是早期版本,而1.8是最流行且广泛使用的版本之一。 **JDK 1.5(也称为Java 5.0)** JDK 1.5在2004年发布,引入了许多重要的新特性,如...
首先,JDK 1.4的主要改进之一是引入了正则表达式(Regular Expressions)的支持,这使得字符串处理和模式匹配变得更加强大和灵活。通过`java.util.regex`包,开发者可以创建复杂的模式来匹配、查找、替换或分割文本...
1. **支持jdk1.2 jdk1.3 jdk1.4 jdk1.5 jdk1.6**:这表明这个工具可以处理这些版本的Java编译后的字节码,意味着它可以打开并反编译这些版本的.class文件,帮助开发者查看对应时期的Java源代码。 2. **支持目录对...
本资源集合提供了四个不同版本的JDK:1.4、1.5、1.6和1.7,它们都是免安装版,可以直接使用,极大地简化了开发环境的搭建过程。 1. JDK 1.4:这是Java的一个重要里程碑,发布于2002年,引入了许多新的特性和改进。...
6. **改进的集合框架**: JDK 1.4对集合框架进行了增强,包括`Set`、`List`和`Map`接口的实现,如`TreeSet`、`TreeMap`等,提高了性能和易用性。 7. **改进的并发编程工具**: `java.util.concurrent`包首次引入,...
在网络编程方面,JDK1.4加强了Socket和ServerSocket的性能,并添加了Socket选项,如SO_SNDBUF和SO_RCVBUF,用于控制发送和接收缓冲区的大小,从而提高了网络通信的效率。 JDK1.4还改进了日志系统,引入了java.util....
以下是对JDK 1.4、1.5和1.6之间主要区别的详细分析: 首先,JDK 1.4与1.5之间最显著的区别在于引入了泛型(Generics)。泛型允许在定义集合类时指定元素类型,从而在编译时期就能检查类型安全,避免了类型转换可能...
总的来说,JDK 1.4免安装版是Java开发者的一个便利工具,特别是对于需要在旧项目或特定环境中使用JDK 1.4的开发者,它提供了一个快速部署和使用的途径。尽管当前版本已经较为陈旧,但了解其特性和改进对于理解Java的...
**JDK 1.4 API 和 JDK 1.6 API 的差异** JDK(Java Development Kit)是Oracle公司发布的用于开发和运行Java应用程序的工具集合。API(Application Programming Interface)是一组预先定义好的函数和类,供开发者在...
jdk1.4非安装版,可直接在MyEclipse等IDE工具导入jdk相应的版本。希望对你们有用处。
- 将JDK1.5和JDK1.6都添加进来,并设置默认使用的JRE为JDK1.5。 2. **为特定项目更改JDK版本**: - 对于需要使用JDK1.6的项目,可以在项目属性中进行单独设置。 - 进入项目属性设置(右键项目->属性),找到...
mac for jdk1.6 jdk6 安装版 里面有两个jdk1.6的安装包,都可以用 如果电脑上安装有1.7,1.8等高版本jdk就不要再下安装包了,安装包安装会报错 命令是这个:brew install java6或 brew install homebrew/cask-...
Java字节码版本与JDK版本是对应的,JDK1.5引入了新的字节码版本,使得1.5环境下编译的类无法在1.4环境下识别和执行。 错误信息"Unsupported major.minor version 49.0"中的49.0代表的就是Java字节码版本号,对应于...