(一)泛型
1.泛型的本质是参数化类型,通俗的讲就是创建一个用类型作为参数的类:
规则:a、泛型的类型参数只能是类类型(包括自定义类),不能是简单类型。
b、同一种泛型可以对应多个版本(因为参数类型是不确定的),不同版本的泛型类实例是不兼容的。
c、泛型的类型参数可以有多个。
d、泛型的参数类型可以使用extends语句,例如<T extends superclass>。习惯上称为“有界类型”。
e、泛型的参数类型还可以是通配符类型。例如Class<?> classType = Class.forName(java.lang.String);
- List<Integer>list=newArrayList<Integer>();//泛型
- list.add(newInteger(10));
- Integera=list.get(0);
- Map<String,Integer>m=newHashMap<String,Integer>();
- m.put("a",1);//自动封装机制
- m.put("b",2);
- m.put("c",3);
- intb=m.get("b");//自动解封机制
List<Integer> list = new ArrayList<Integer>(); //泛型 list.add(new Integer(10)); Integer a = list.get(0); Map<String,Integer> m = new HashMap<String,Integer>(); m.put("a",1); //自动封装机制 m.put("b",2); m.put("c",3); int b = m.get("b"); //自动解封机制
2.自定义泛型类
- publicclassGen<T>{
- privateTob;//定义泛型成员变量
- publicGen(Tob){
- this.ob=ob;
- }
- publicTgetOb(){
- returnob;
- }
- publicvoidsetOb(Tob){
- this.ob=ob;
- }
- publicvoidshowTyep(){
- System.out.println("T的实际类型是:"+ob.getClass().getName());
- }
- }
- publicclassGenDemo{
- publicstaticvoidmain(String[]args){
- //定义泛型类Gen的一个Integer版本
- Gen<Integer>intOb=newGen<Integer>(88);
- intOb.showTyep();
- inti=intOb.getOb();
- System.out.println("value="+i);
- System.out.println("----------------------------------");
- //定义泛型类Gen的一个String版本
- Gen<String>strOb=newGen<String>("HelloGen!");
- strOb.showTyep();
- Strings=strOb.getOb();
- System.out.println("value="+s);
- }
- }
public class Gen<T> { private T ob; //定义泛型成员变量 public Gen(T ob) { this.ob = ob; } public T getOb() { return ob; } public void setOb(T ob) { this.ob = ob; } public void showTyep() { System.out.println("T的实际类型是: " + ob.getClass().getName()); } } public class GenDemo { public static void main(String[] args){ //定义泛型类Gen的一个Integer版本 Gen<Integer> intOb=new Gen<Integer>(88); intOb.showTyep(); int i= intOb.getOb(); System.out.println("value= " + i); System.out.println("----------------------------------"); //定义泛型类Gen的一个String版本 Gen<String> strOb=new Gen<String>("Hello Gen!"); strOb.showTyep(); String s=strOb.getOb(); System.out.println("value= " + s); } }
在Java 5之前,为了让类有通用性,往往将参数类型、返回类型设置为Object类型,当获取这些返回类型来使用时候,必须将其“强制”转换为原有的类型或者接口,然后才可以调用对象上的方法。
强制类型转换很麻烦,我还要事先知道各个Object具体类型是什么,才能做出正确转换。否则,要是转换的类型不对,比如将“Hello Generics!”字符串强制转换为Double,那么编译的时候不会报错,可是运行的时候就挂了。那有没有不强制转换的办法----有,改用 Java5泛型来实现。
3.用extends和super来控制泛型的类型范围
class A <B extends List>{ };
表示B只能是List的子类或子接口A<String> a= new A<String>();就是错误的。
如果用super的话就是表示父类或者父接口了。
4.通配符
- voidprint(List<Integer>a){
- for(Integeri:a){//表示从a中不断的取出对象定义成i然后进行循环
- System.out.println(i);
- }
- }
- voidprint(List<?>a){//类型通配符,用?表示不知道List里放的什么类型
- for(Integeri:a){
- System.out.println(i);
- }
- }
void print(List<Integer> a) { for (Integer i : a) { //表示从a中不断的取出对象定义成 i 然后进行循环 System.out.println(i); } } void print(List<?> a) { // 类型通配符,用?表示不知道List里放的什么类型 for (Integer i : a) { System.out.println(i); } }
看一个有趣的例子:
- publicclassCollectionGenFooDemo{
- publicstaticvoidmain(Stringargs[]){
- CollectionGenFoo<ArrayList>listFoo=null;
- listFoo=newCollectionGenFoo<ArrayList>(newArrayList());
- //出错了,不让这么干。
- //CollectionGenFoo<Collection>listFoo=null;
- //listFoo=newCollectionGenFoo<ArrayList>(newArrayList());
- System.out.println("实例化成功!");
- }
- }
public class CollectionGenFooDemo { public static void main(String args[]) { CollectionGenFoo<ArrayList> listFoo = null; listFoo = new CollectionGenFoo<ArrayList>(new ArrayList()); //出错了,不让这么干。 // CollectionGenFoo<Collection> listFoo = null; // listFoo=new CollectionGenFoo<ArrayList>(new ArrayList()); System.out.println("实例化成功!"); } }
当前看到的这个写法是可以编译通过,并运行成功。可是注释掉的两行加上就出错了,因为<T extends Collection>这么定义类型的时候,就限定了构造此类实例的时候T是确定的一个类型,这个类型实现了Collection接口,但是实现 Collection接口的类很多很多,如果针对每一种都要写出具体的子类类型,那也太麻烦了,我干脆还不如用Object通用一下。别急,泛型针对这种情况还有更好的解决方案,那就是“通配符泛型”。
为了解决类型被限制死了不能动态根据实例来确定的缺点,引入了“通配符泛型”,针对上面的例子,使用通配泛型格式为<? extends Collection>,“?”代表未知类型,这个类型是实现Collection接口。那么上面实现的方式可以写为:
- publicclassCollectionGenFooDemo{
- publicstaticvoidmain(Stringargs[]){
- CollectionGenFoo<ArrayList>listFoo=null;
- listFoo=newCollectionGenFoo<ArrayList>(newArrayList());
- //现在不会出错了
- CollectionGenFoo<?extendsCollection>listFoo1=null;
- listFoo=newCollectionGenFoo<ArrayList>(newArrayList());
- System.out.println("实例化成功!");
- }
- }
public class CollectionGenFooDemo { public static void main(String args[]) { CollectionGenFoo<ArrayList> listFoo = null; listFoo = new CollectionGenFoo<ArrayList>(new ArrayList()); //现在不会出错了 CollectionGenFoo<? extends Collection> listFoo1 = null; listFoo=new CollectionGenFoo<ArrayList>(new ArrayList()); System.out.println("实例化成功!"); } }
注意:
1)、如果只指定了<?>,而没有extends,则默认是允许Object及其下的任何Java类了。也就是任意类。
2)、通配符泛型不单可以向下限制,如<? extends Collection>,还可以向上限制,如<? super Double>,表示类型只能接受Double及其上层父类类型,如Number、Object类型的实例。
3)、泛型类定义可以有多个泛型参数,中间用逗号隔开,还可以定义泛型接口,泛型方法。这些都泛型类中泛型的使用规则类似。
(二)增强的for循环
- voidprint(List<?>a){
- for(Integeri:a){
- System.out.println(i);
- }
- }
void print(List<?> a) { for (Integer i : a) { System.out.println(i); } }
(三)可变参数(Vararg)
实现可变参数,传统上一般是采用“利用一个数组来包裹要传递的实参”的做法来应付.
J2SE 1.5中提供了Varargs机制,允许直接定义能和多个实参相匹配的形参。从而,可以用一种更简单的方式,来传递个数可变的实参。
1.定义实参个数可变的方法
在一个形参的“类型”与“参数名”之间加上三个连续的“.”
- privatestaticintsumUp(int...values){
- }
private static int sumUp(int... values) { }
注意,只有最后一个形参才能被定义成“能和不确定个实参相匹配”的。因此,一个方法里只能有一个这样的形参。另外,如果这个方法还有其它的形参,要把它们放到前面的位置上。
编译器会在背地里把这最后一个形参转化为一个数组形参,并在编译出的class文件里作上一个记号,表明这是个实参个数可变的方法。
实参个数可变的方法的秘密形态
- privatestaticintsumUp(int[]values){
- }
private static int sumUp(int[] values) { }
由于存在着这样的转化,所以不能再为这个类定义一个和转化后的方法签名一致的方法。
会导致编译错误的组合
- privatestaticintsumUp(int...values){
- }
- privatestaticintsumUp(int[]values){
- }
private static int sumUp(int... values) { } private static int sumUp(int[] values) { }
2.调用实参个数可变的方法
可以传递若干个实参
sumUp(1, 3, 5, 7);
在背地里,编译器会把这种调用过程转化为用“数组包裹实参”的形式:
偷偷出现的数组创建
sumUp(new int[]{1, 2, 3, 4});
另外,这里说的“不确定个”也包括零个,所以这样的调用也是合乎情理的:也可以传递零个实参
sumUp();
这种调用方法被编译器秘密转化之后的效果,则等同于这样:零实参对应空数组
sumUp(new int[]{});
注意这时传递过去的是一个空数组,而不是null。这样就可以采取统一的形式来处理,而不必检测到底属于哪种情况。
3.是数组?不是数组?
尽管在背地里,编译器会把能匹配不确定个实参的形参,转化为数组形参;而且也可以用数组包了实参,再传递给实参个数可变的方法;但是,这并不表示“能匹配不确定个实参的形参”和“数组形参”完全没有差异。
一个明显的差异是,如果按照调用实参个数可变的方法的形式,来调用一个最后一个形参是数组形参的方法,只会导致一个“cannot be applied to”的编译错误。
- privatestaticvoidtestOverloading(int[]i){
- System.out.println("A");
- }
- publicstaticvoidmain(String[]args){
- testOverloading(1,2,3);//编译出错
- }
private static void testOverloading(int[] i) { System.out.println("A"); } public static void main(String[] args) { testOverloading(1, 2, 3);//编译出错 }
由于这一原因,不能在调用只支持用数组包裹实参的方法的时候(例如在不是专门为J2SE 1.5设计第三方类库中遗留的那些),直接采用这种简明的调用方式。
如果不能修改原来的类,为要调用的方法增加参数个数可变的版本,而又想采用这种简明的调用方式,那么可以借助“引入外加函数(Introduce Foreign Method)”和“引入本地扩展(Intoduce Local Extension)”的重构手法来近似的达到目的。
4.当个数可变的实参遇到泛型
J2SE 1.5中新增了“泛型”的机制,可以在一定条件下把一个类型参数化。例如,可以在编写一个类的时候,把一个方法的形参的类型用一个标识符(如T)来代表,至于这个标识符到底表示什么类型,则在生成这个类的实例的时候再行指定。这一机制可以用来提供更充分的代码重用和更严格的编译时类型检查。
不过泛型机制却不能和个数可变的形参配合使用。如果把一个能和不确定个实参相匹配的形参的类型,用一个标识符来代表,那么编译器会给出一个“generic array creation”的错误。
当Varargs遇上泛型:
- privatestatic<T>voidtestVarargs(T...args){//编译出错
- }
private static <T> void testVarargs(T... args) {//编译出错 }
造成这个现象的原因在于J2SE 1.5中的泛型机制的一个内在约束——不能拿用标识符来代表的类型来创建这一类型的实例。在出现支持没有了这个约束的Java版本之前,对于这个问题,基本没有太好的解决办法。
不过,传统的“用数组包裹”的做法,并不受这个约束的限制。
可以编译的变通做法
- privatestatic<T>voidtestVarargs(T[]args){
- for(inti=0;i<args.length;i++){
- System.out.println(args[i]);
- }
- }
private static <T> void testVarargs(T[] args) { for (int i = 0; i < args.length; i++) { System.out.println(args[i]); } }
5.重载中的选择问题
实参个数固定的版本优先
- publicclassOverloadingSampleA{
- publicstaticvoidmain(String[]args){
- testOverloading(1);//打印出A
- testOverloading(1,2);//打印出B
- testOverloading(1,2,3);//打印出C
- }
- privatestaticvoidtestOverloading(inti){
- System.out.println("A");
- }
- privatestaticvoidtestOverloading(inti,intj){
- System.out.println("B");
- }
- privatestaticvoidtestOverloading(inti,int...more){
- System.out.println("C");
- }
- }
public class OverloadingSampleA { public static void main(String[] args) { testOverloading(1);//打印出A testOverloading(1, 2);//打印出B testOverloading(1, 2, 3);//打印出C } private static void testOverloading(int i) { System.out.println("A"); } private static void testOverloading(int i, int j) { System.out.println("B"); } private static void testOverloading(int i, int... more) { System.out.println("C"); } }
如果在编译器看来,同时有多个方法具有相同的优先权,它就会陷入无法就到底调用哪个方法作出一个选择的状态。在这样的时候,它就会产生一个 “reference to 被调用的方法名 is ambiguous”的编译错误,并耐心的等候作了一些修改,足以免除它的迷惑的新源代码的到来。
在引入了Varargs机制之后,这种可能导致迷惑的情况,又增加了一些。例如现在可能会有两个版本都能匹配,在其它方面也如出一辙,而且都是实参个数可变的冲突发生。
左右都不是,为难了编译器
- publicclassOverloadingSampleB{
- publicstaticvoidmain(String[]args){
- testOverloading(1,2,3);//编译出错
- }
- privatestaticvoidtestOverloading(Object...args){
- }
- privatestaticvoidtestOverloading(Objecto,Object...args){
- }
- }
public class OverloadingSampleB { public static void main(String[] args) { testOverloading(1, 2, 3);//编译出错 } private static void testOverloading(Object... args) { } private static void testOverloading(Object o, Object... args) { } }
另外,因为J2SE 1.5中有“Autoboxing/Auto-Unboxing”机制的存在,所以还可能发生两个版本都能匹配,而且都是实参个数可变,其它方面也一模一样,只是一个能接受的实参是基本类型,而另一个能接受的实参是包裹类的冲突发生。
- //Autoboxing/Auto-Unboxing带来的新问题
- publicclassOverloadingSampleC{
- publicstaticvoidmain(String[]args){
- /*编译出错*/
- testOverloading(1,2);
- /*还是编译出错*/
- testOverloading(newInteger(1),newInteger(2));
- }
- privatestaticvoidtestOverloading(int...args){
- }
- privatestaticvoidtestOverloading(Integer...args){
- }
- }
//Autoboxing/Auto-Unboxing带来的新问题 public class OverloadingSampleC { public static void main(String[] args) { /* 编译出错 */ testOverloading(1, 2); /* 还是编译出错 */ testOverloading(new Integer(1), new Integer(2)); } private static void testOverloading(int... args) { } private static void testOverloading(Integer... args) { } }
(四) 枚举(Enum)
- publicclassprogram{
- enumColor{
- Red,
- White,
- Yellow
- }
- publicstaticvoidmain(String[]args){
- Color[]cs=Color.values();
- for(Colorc:cs){
- System.out.println(c);
- }
- }
- }
public class program { enum Color { Red, White, Yellow } public static void main(String[] args) { Color[] cs = Color.values(); for(Color c : cs){ System.out.println(c); } } }
(五)静态导入(Static Import)
使用静态导入可以使被导入类的所有静态变量和静态方法在当前类直接可见,使用这些静态成员无需再给出他们的类名。
- importstaticjava.lang.Math.*;
- publicclassprogram{
- publicstaticvoidmain(String[]args){
- inti=(int)(random()*10);
- System.out.println(i);
- }
- }
import static java.lang.Math.*; public class program { public static void main(String[] args) { int i = (int)(random()*10); System.out.println(i); } }
(六) 格式化输入输出
- publicclassprogram{
- publicstaticvoidmain(String[]args){System.out.printf("Integer=%dString=%s",13,"abc");
- }
- }
public class program { public static void main(String[] args) { System.out.printf("Integer=%d String=%s", 13, "abc"); } }
格式化的格式是 "%[argument_index$][flags][width][.precision]conversion"。
(七) 标注(Annotation)
标注(Annotation)就是在程序中加上一些说明,容器在调用这些类,方法的时候就知道怎么处理了。
java.lang.annotation
接口 Annotation
所有已知实现类:
Deprecated, Documented, Inherited, Override, Retention, SuppressWarnings, Target
--------------------------------------------------------------------------------
public interface Annotation所有 annotation 类型都要扩展的公共接口。注意,手动扩展该公共接口的接口不 定义 annotation 类型。还要注意此接口本身不定义 annotation 类型。
例如下面是在seasar框架中使用的例子
- @StrutsActionForm(name="ownerForm")//canreplacethestrutsconfigfile
- publicclassOwnerForm{}
http://qidaoxp.javaeye.com/blog/474840
相关推荐
### Java 5 新特性详解 #### 一、引言 随着技术的发展,编程语言也在不断地进步和完善。Java 5 的发布标志着 Java 在功能性和便利性方面迈出了重要的一步。本篇文章将详细介绍 Java 5 中的一些关键特性,这些新特性...
标题 "使用模式设计及java5新特性在HibernateDAO中的应用" 涉及到的是软件开发中的两个关键领域:设计模式和Java编程语言的新特性,特别是在数据访问对象(DAO)层如何结合使用它们。这篇文章可能详细阐述了如何利用...
### Java 5新特性 #### 1. **泛型(Generics)** 泛型是Java 5最重要的特性之一,它允许在类、接口和方法声明中使用类型参数,增强了类型安全,减少了强制类型转换,提高了代码的可读性和可维护性。 #### 2. **...
Java 5是一个重要的Java平台版本,它引入了许多新特性,极大地提升了开发效率和代码质量。在"RowSetDemo"这个示例中,我们主要关注的是Java 5中的一个新特性——`java.sql.RowSet`接口及其相关的实现。`RowSet`是...
Java 5,也被称为JDK 1.5,是Java发展历程中的一个重要里程碑,引入了许多显著的新特性,极大地提升了开发效率和代码的可读性。以下是对这些关键特性的详细解释: 1. **自动拆装箱(Auto-boxing/Unboxing)**: 这一...
总的来说,JDK 1.5(Java 5.0)的新特性极大地提高了Java的生产力和代码质量。从泛型到增强的for循环,再到注解和枚举,这些改进都让Java开发者能够编写出更安全、更易于维护的代码。在实际开发中,理解并充分利用...
通过了解和熟练掌握这些新特性,开发者能够编写出更高效、更易于维护的 Java 5 代码。这些特性不仅提升了编程体验,也为后续的 Java 版本奠定了基础。学习并应用这些特性是保持 Java 技能与时俱进的关键。
Java8是Java编程语言的一次重大更新,引入了许多新的特性和功能,极大地提升了开发效率和代码的可读性。以下是一些主要的Java8新特性详解: 1. **Lambda表达式**:Lambda表达式是Java8中最显著的新特性,它为Java...
本文将深入探讨Java 8的三大关键新特性:接口的默认方法实现、Lambda表达式以及一系列相关的变化。 一、接口的默认方法实现 在Java 8之前,接口只能定义抽象方法,而不能包含具体实现。Java 8引入了默认方法...
JAVA 1.5的新特性是Java发展史上的一个重要里程碑,这一版本引入了一系列创新特性,旨在提升编程效率、增强代码安全性并简化开发流程。以下是对JAVA 1.5新特性的详细解析: ### 1. 泛型 (Generics) #### 概述 泛型...
在Java 8中,最重要的两个新特性无疑是Lambda表达式和Stream API。这两个特性都与函数式编程思想密切相关,让Java开发者能够更加灵活地处理数据。 ### Lambda表达式 Lambda表达式是Java 8中最显著的新特性之一,它...
5. **日期与时间API(java.time)**:取代了原来的`java.util.Date`和`Calendar`,新的日期时间API提供了更加方便和直观的方式来处理日期和时间。`LocalDate`、`LocalTime`和`LocalDateTime`是其中的主要类。 6. **...
总之,尽管Java 5的特性在当前看来可能并不新鲜,但考虑到许多企业仍使用旧版本的JDK,理解如何在这些环境中利用Java 5的新特性仍然是有价值的。通过巧妙的编码技巧和策略,开发者可以在不影响现有用户的同时,享受...
以下是关于Java 8新特性的详细讲解: 1. **函数式编程:Lambda表达式** Lambda表达式是Java 8中最显著的新特性,它允许我们将函数作为一个方法参数,或者以匿名函数的形式创建。Lambda表达式简洁且易于理解,使得...
Java 7是Java编程语言的一个重大更新,发布于2011年,它引入了许多新的特性和改进,旨在提升开发者的效率和代码的可读性。在这个主题中,我们将深入探讨Java 7的新特性以及Java的高级编程概念。 **1. 多线程并行流...
自 Java 8 发布后,Java 有了许多显著的新特性,极大地提升了开发效率和代码质量。以下是对 Java 8 及之后版本主要新特性的深入剖析: 1. **Lambda 表达式**: Java 8 引入了 Lambda 表达式,使得函数式编程成为...