1.5新特性

来源:互联网 发布:js简易购物车合计代码 编辑:程序博客网 时间:2024/05/18 03:37


Jdk5.0新特性:

静态导入  import static java.lang.Math.*;

当导入静态Math类的所有方法后 调用其方法时便可以省略类名

-------------------------------------------------------------------------

可变参数(...用到函数的参数上,当要操作的同一个类型元素个数不确定的时候,可用这个方式,这个参数可以接受任意个数的同一类型的数据。

可变参数的特点:

1, 可变参数必须定义在参数列表结尾(也就是必须是最后一个参数,否则编译会失败。)

2, 调用可变参数的方法时,编译器为该可变参数隐含创建一个数组,在方法体中以数组的形式访问可变参数

---------------------------------------------------

增强for循环:foreach语句,foreach简化了迭代器。

for( 元素类型 变量名 : 集合变量 ) {

  …

}

集合变量可以是数组 或是实现了Iterator接口的集合类

高级for循环和传统for循环的区别:

高级for循环在使用时,必须要明确被遍历的目标。这个目标,可以是Collection集合或者数组。

如果遍历数组,还需要对数组元素进行操作,建议用传统for循环因为可以通过角标操作元素。而高级for中的变量只是临时用于存放集合或数组中元素的临时存储区,变量中存储的只是元素的副本,并不是数组中的元素,所以高级for无发改变被遍历的元素只适用获取 如果只为遍历获取,可以简化成高级for循环,它的出现为了简化书写。

 

增强for循环只能针对实现了Iterable接口的集合进行迭代;Iterable是jdk5中新定义的接口,就一个方法iterator方法,只有实现了Iterable接口的类,才能保证一定有iterator方法,java有这样的限定是因为增强for循环内部还是用迭代器实现的.

-------------------------------------------------------

自动拆装箱:java中数据类型分为两种 : 基本数据类型   引用数据类型(对象)

在 java程序中所有的数据都需要当做对象来处理,针对8种基本数据类型提供了包装类,如下:


int --> Integer

byte --> Byte

short --> Short

long --> Long

char --> Character

double --> Double

float --> Float

boolean --> Boolean


 

1)、Integer x = 1; x = x + 1;  经历了什么过程?装箱 à 拆箱 à 装箱

2)、为了优化,虚拟机为包装类提供了缓冲池,Integer池的大小 -128~127一个字节的大小

3)、String池:Java为了优化字符串操作 提供了一个缓冲池;

Integer i1 = 127;   Integer i1 = new Integer(127);   Integer i1=Integer.valueOf(127);

Integer i2 = 127     Integer i1 = newInteger(127);   Integeri1=Integer.valueOf(127);

i1 ==i2  true         i1 ==i2  false                   i1==i2  true  

当数值在byte范围内时 把byte 封装成Integer对象时 Integer会将其放入一个byte对象池缓存起来 因为byte数据使用频率很大 当需要再次封装byte时 Integer会去byte对象池查找 如果该数值已经存在  则不会再开辟新的空间

享元设计模式有很多个小的对象 他们有很多属性相同 把他们封装成一个对象,那些不同的属性 把他们作为方法的参数 称之为 外部状态 相同的属性 称之为 内部状态

Window中的所有文件夹 虽然 文件夹名不同 位置不同(外部状态) 但是他们共享文件夹对象

----------------------------------------------------------

 

Collection在jdk1.5以后,有了一个父接口Iterable,这个接口的出现的将iterator方法进行抽取,提高了扩展性。

--------------------------------------------------

枚举:关键字enum    java.lang

问题:对象的某个属性的值不能是任意的,必须为固定的一组取值其中的某一个;

为什么要有枚举

枚举就是要让某个类型的变量的取值只能为若干个固定值中的一个,否则,编译器就会报错,枚举可以让编译器在编译时就可以控制源程序中填写的非法值,普通变量的方式在开发阶段无法实现这一目标

 枚举的每个元素都是一个枚举对象

解决办法:

1)、在setGrade方法中做判断,不符合格式要求就抛出异常;

2)、直接限定用户的选择,通过自定义类模拟枚举的方式来限定用户的输入,写一个Grade类,私有构造函数,对外提供5个静态的常量表示类的实例

3)、jdk5中新定义了枚举类型,专门用于解决此类问题;

4)、枚举就是一个特殊的java,可以定义属性、方法、构造函数、实现接口、继承类;

枚举相当于一个类,其中也可以定义构造方法,成员变量,普通方法和抽象方法

枚举元素必须位于枚举体重的最开始部分,枚举元素列表的后面要有分号与其他成员分离。把枚举中的成员方法或变量等放在枚举元素的前面,编译器会报错

带构造方法的枚举

构造方法必须定义成私有的

u  如果有多个构造方法,该如何选哪种构造方法

u  枚举元素MON()和MON的效果一样,都是调用默认的构造方法

带方法的枚举

l  定义枚举TrafficLamp

l  实现普通的next()方法

l  实现抽象的next()方法:每个元素分别是有枚举类的子类来生成实例对象,这些子类采用类似内部类的方式来定义

l  增加上表示时间的构造方法

//定义交通灯 枚举:

public enum TrafficLamp{RED(25){public TrafficLamp next() {return GREE;}},GREE(45){public TrafficLamp next() {return YELLOW;}},YELLOW(5){public TrafficLamp next() {return GREE;}};public abstract TrafficLamp next();int time;private TrafficLamp(int time) {this.time = time;}}

枚举只有一个元素时,就可以作为一种单例的实现方式

每一个枚举元素都是枚举类的实际对象

 

构造方法都必须是私有的 都必须定义在 枚举元素列表之后 枚举列表必须以分号结束

Enum.valueOf(串);  把字符串转为枚举,返回枚举变量

Enum[] static values() 返回枚举元素数组

public final int ordinal()返回枚举常量的序数(它在枚举声明中的位置,其中初始常量序数为零)。

==========================================================

注解

了解注解及java提供的几个基本注解

l  先通过@SuppressWarnings的应用让大家认识和了解一下注解:

Ø  通过System.runFinalizersOnExit(true);的编译警告引出@SuppressWarnings("deprecation")

l  @Deprecated

Ø  直接在刚才的类中增加一个方法,并加上@Deprecated标注,在另外一个类中调用这个方法。

l  @Override

Ø  public boolean equals(Reflect other)方法与HashSet结合讲解

l  总结:

Ø  注解相当于一种标记,在程序中加了注解就等于为程序打上了某种标记,没加,则等于没有某种标记,以后,javac编译器,开发工具和其他程序可以用反射来了解你的类及各种元素上有无何种标记,看你有什么标记,就去干相应的事。标记可以加在包,类,字段,方法,方法的参数以及局部变量上。

Ø  看java.lang包,可看到JDK中提供的最基本的annotation。

@Deprecated    @Override  @SuppressWarnings

 

注解的应用结构图


自定义注解及其应用

定义注解的格式public@interfaceMyAnnotation {}

l  用反射进行测试AnnotationTest的定义上是否有@MyAnnotation

l  根据发射测试的问题,引出@Retention元注解的讲解,其三种取值:RetetionPolicy.SOURCE、RetetionPolicy.CLASS、RetetionPolicy.RUNTIME;分别对应:java源文件-->class文件-->内存中的字节码

l  思考 三注解的存活期(作用期):@Override(覆盖检查java源文件)@SuppressWarnings(取消显示指定的编译器警告java源文件)@Deprecated(判断过时内存中的字节码)

l  演示和讲解@Target元注解   元注解表示注解的注解

Ø  Target的默认值为任何元素,设置Target等于ElementType.METHOD,原来加在类上的注解就报错了,改为用数组方式设置{ElementType.METHOD,ElementType.TYPE}就可以了。

l  元注解以及其枚举属性值不用记,只要会看jdk提供那几个基本注解的API帮助文档的定义或其源代码,按图索骥即可查到,或者直接看java.lang.annotation包下面的类。

@Retention(RetentionPolicy)元注解指示注释类型的注释要保留多久(标明注释的存活期在什么阶段)。 默认为 RetentionPolicy.CLASSclass文件阶段

RetentionPolicy注释保留策略与 Retention 元注释类型一起使用,以指定保留多长的注释。一共有三个阶段:

java源文件阶段(RetentionPolicy.SOURCE)-->class文件阶段(RetentionPolicy.CLASS)-->内存中的字节码阶段(RetentionPolicy.RUNTIME)

@Target元注解 @Target(ElementType) 单种@Target({ElementType})多种 指示注释类型所适用的程序元素的种类。注释适用8种地方 TYPE 类,接口,枚举FIELD字段 方法 构造方法 包 参数  局部变量


为注解增加基本属性

l  什么是注解的属性

Ø  一个注解相当于一个胸牌,如果你胸前贴了胸牌,就是传智播客的学生,否则,就不是。如果还想区分出是传智播客哪个班的学生,这时候可以为胸牌在增加一个属性来进行区分。加了属性的标记效果为:@MyAnnotation(color="red")

l  定义基本类型的属性和应用属性:

Ø  在注解类中增加String color();

Ø  @MyAnnotation(color="red")

用反射方式获得注解对应的实例对象后,再通过该对象调用属性对应的方法

Ø  MyAnnotation a = (MyAnnotation)AnnotationTest.class.getAnnotation(MyAnnotation.class);

Ø  System.out.println(a.color());

Ø  可以认为上面这个@MyAnnotation是MyAnnotaion类的一个实例对象

为属性指定缺省值:

Ø  String color() default "yellow";default了默认值 注释使用时 可以不指定

value属性

Ø  String value() default "zxx";

Ø  如果注解中有一个名称为value的属性,且你只想设置value属性(即其他属性都采用默认值或者你只有一个value属性),那么可以省略value=部分,例如:@MyAnnotation("lhm")。多个属性都需要指定那么value不可省略  只需要指定value一个属性就可以省略

属性的缺省值

value属性的特殊性

数组属性怎么使用              注释类的属性 要注意属性的类型

为注解增加高级属性

数组类型的属性

Ø  int [] arrayAttr() default {1,2,3};

Ø  @MyAnnotation(arrayAttr={2,3,4})

Ø  如果数组属性中只有一个元素,这时候属性值部分可以省略大括

枚举类型的属性     枚举和类 接口是平级的

Ø  EnumTest.TrafficLamp lamp() ;

Ø  @MyAnnotation(lamp=EnumTest.TrafficLamp.GREEN)

注解类型的属性:

Ø  MetaAnnotation annotationAttr() default@MetaAnnotation("xxxx");

Ø  @MyAnnotation(annotationAttr=@MetaAnnotation(“yyy”) )

Ø  可以认为上面这个@MyAnnotation是MyAnnotaion类的一个实例对象,同样的道理,可以认为上面这个@MetaAnnotation是MetaAnnotation类的一个实例对象,调用代码如下:

  MetaAnnotation ma =  myAnnotation.annotationAttr();

  System.out.println(ma.value());

l  注解的详细语法可以通过看java语言规范了解,即看java的language specification。

import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;@Retention(RetentionPolicy.RUNTIME)//@Retention元注释 说明该注释 存活期间在 运行期间@Target( {ElementType.TYPE,ElementType.METHOD})//@Target元注释 说明该注释 适用于 类接口枚举和方法上public @interface myAnnotation {//为注释定义属性String name() default "ztt";//default定义属性默认值 缺省值String value();//数组类型 属性int[] array() default {1,3,5};//枚举类型属性TrafficLamp lamp() default TrafficLamp.GREE;//独立的枚举EnumDemo.TrafficLamp TLamp() default EnumDemo.TrafficLamp.GREE;//从属于类的枚举//注释类型属性annFormy ann() default @annFormy("hehe");//class对象类型属性Class<?> clazz() default String.class;//Class<String> clazz() default String.class; 区别在于 该class对象 只能表示 String类(String的class对象)}
=========================================================================

泛型:jdk1.5版本以后出现的一个安全机制。表现格式:< >

好处:

1:将运行时期的问题ClassCastException问题转换成了编译失败,体现在编译时期,程序员就可以解决问题。

2:避免了强制转换的麻烦。

 

只要带有<>的类或者接口,都属于带有类型参数的类或者接口,在使用这些类或者接口时,必须给<>中传递一个具体的引用数据类型。

Jdk 1.5的集合类希望你在定义集合时,明确表示你要向集合中装哪种类型的数据,无法加入指定类型以外的数据

泛型是提供给javac编译器使用的,可以限定集合中的输入类型,让编译器挡住源程序中的非法输入,编译器编译带类型说明的集合时会去除掉“类型”信息,使程序运行效率不受影响,对于参数化的泛型类型,getClass()方法的返回值和原始类型完全一样。由于编译生成的字节码会去掉泛型的类型信息,只要能跳过编译器,就可以往某个泛型集合中加入其它类型的数据,例如,用反射得到集合,再调用其add方法即可。

 

泛型技术:其实应用在编译时期,是给编译器使用的技术,到了运行时期,泛型就不存在了。

为什么? 因为泛型的擦除:也就是说,编辑器检查了泛型的类型正确后,在生成的类文件中是没有泛型的。

在运行时,如何知道获取的元素类型而不用强转呢?

泛型的补偿:因为存储的时候,类型已经确定了是同一个类型的元素,所以在运行时,只要获取到该元素的类型,在内部进行一次转换即可,所以使用者不用再做转换动作了。

l ArrayList<E>类定义和ArrayList<Integer>类引用中涉及如下术语:

Ø 整个称为ArrayList<E>泛型类型

Ø ArrayList<E>中的E称为类型变量或类型参数

Ø 整个ArrayList<Integer>称为参数化的类型

Ø ArrayList<Integer>中的Integer称为类型参数的实例或实际类型参数

Ø ArrayList<Integer>中的<>念着typeof

Ø ArrayList称为原始类型

参数化类型与原始类型的兼容性

Ø 参数化类型bu可以引用一个原始类型的对象,编译报告警告,例如,
Collection<String> c = new Vector();//编译警告但是可以编译通过可运行

Ø 原始类型bu可以引用一个参数化类型的对象,编译报告警告,例如,
Collection c = new Vector<String>();//原来的方法接受一个集合参数,新的类型也要能传进去

l 参数化类型不考虑类型参数的继承关系:

Ø Vector<String> v = newVector<Object>(); //错误!///不写<Object>没错,写了就是明知故犯

Ø Vector<Object> v = newVector<String>(); //也错误!

编译器不允许创建泛型变量的数组。即在创建数组实例时,数组的元素不能使用参数化的类型,例如,下面语句有错误:

    Vector<Integer>vectorList[] = new Vector<Integer>[10];

思考题:下面的代码会报错误吗?  不会   参数化类型和原始类型相互兼容

Vector v1 = newVector<String>();  原始类型  参数化类型

Vector<Object> v = v1;           参数化类型  原始类型

泛型中的类型参数严格说明集合中装载的数据类型是什么和可以加入什么类型的数据,记住:Collection<String>和Collection<Object>是两个没有转换关系的参数化的类型。

  假设Vector<String> v = new Vector<Object>();可以的话,那么以后从v中取出的对象当作String用,而v实际指向的对象中可以加入任意的类型对象;假设Vector<Object> v = new Vector<String>();可以的话,那么以后可以向v中加入任意的类型对象,而v实际指向的集合中只能装String类型的对象。

泛型中的?通配符

l 问题:

Ø 定义一个方法,该方法用于打印出任意参数化类型的集合中的所有数据,该方法如何定义呢?

l 错误方式:

public static voidprintCollection(Collection<Object> cols) {

        for(Object obj:cols) {

            System.out.println(obj);

        }

        /* cols.add("string");//没错

         cols = newHashSet<Date>();//会报告错误! Collection<Object> cols= new HashSet<Date>();*/

}

l 正确方式:

public static voidprintCollection(Collection<?> cols) {

        for(Object obj:cols) {

            System.out.println(obj);

        }

        //cols.add("string");//错误,因为它不知自己未来匹配就一定是String

        cols.size();//没错,此方法与类型参数没有关系

         cols = newHashSet<Date>();  Collection<> cols= new HashSet<Date>(); 

    }

 

l 总结:

Ø 使用?通配符可以引用其他各种参数化的类型,?通配符定义的变量主要用作引用,可以调用与参数化无关的方法,不能调用与参数化有关的方法。

泛型中的?通配符的扩展

l 限定通配符的上边界:

Ø 正确:Vector<? extends Number> x = new Vector<Integer>();

Ø 错误:Vector<? extends Number> x = new Vector<String>();

l 限定通配符的下边界:

Ø 正确:Vector<? super Integer> x = new Vector<Number>();

Ø 错误:Vector<? super Integer> x = new Vector<Byte>();

l 提示:

Ø 限定通配符总是包括自己

Ø  ?只能用作引用,不能用它去给其他变量赋值

    Vector<? extends Number> y = new Vector<Integer>();

    Vector<Number> x = y;

    上面的代码错误,原理与Vector<Object > x11 = new Vector<String>();相似,

    只能通过强制类型转换方式来赋值。


定义泛型方法

用于放置泛型的类型参数的尖括号应出现在方法的其他所有修饰符之后和在方法的返回类型之前,也就是紧邻返回值之前。按照惯例,类型参数通常用单个大写字母表示。

l 交换数组中的两个元素的位置的泛型方法.

只有引用类型才能作为泛型方法的实际参数,swap(new int[3],3,5);语句会报告编译错误。

l 除了在应用泛型时可以使用extends限定符,在定义泛型时也可以使用extends限定符,例如,Class.getAnnotation()方法的定义。并且可以用&来指定多个边界,如<V extends Serializable & cloneable> void method(){}

l 普通方法、构造方法和静态方法中都可以使用泛型。

l 也可以用类型变量表示异常,称为参数化的异常,可以用于方法的throws列表中,但是不能用于catch子句中。

l 在泛型中可以同时有多个类型参数,在定义它们的尖括号中用逗号分,例如:

public static <K,V> VgetValue(K key) { return map.get(key);}


类型参数的类型推断

方法上泛型的类型  就是传入的实参的类型的共属类型(公共父类)

l 编译器判断范型方法的实际类型参数的过程称为类型推断,类型推断是相对于知觉推断的,其实现方法是一种非常复杂的过程。

l 根据调用泛型方法时实际传递的参数类型返回值的类型来推断,具体规则如下:

Ø 当某个类型变量只在整个参数列表中的所有参数和返回值中的一处被应用了,那么根据调用方法时该处的实际应用类型来确定,这很容易凭着感觉推断出来,即直接根据调用方法时传递的参数类型或返回值来决定泛型参数的类型,例如:

     swap(newString[3],3,4)   à    static <E> void swap(E[] a, int i, int j)

Ø 当某个类型变量在整个参数列表中的所有参数和返回值中的多处被应用了,如果调用方法时这多处的实际应用类型都对应同一种类型来确定,这很容易凭着感觉推断出来,例如:

     add(3,5)   à static <T> T add(T a, T b)

Ø 当某个类型变量在整个参数列表中的所有参数和返回值中的多处被应用了,如果调用方法时这多处的实际应用类型对应到了不同的类型,且没有使用返回值,这时候取多个参数中的最大交集类型,例如,下面语句实际对应的类型就是Number编译没问题,只是运行时出问题

     fill(newInteger[3],3.5f)   à static <T> void fill(T[] a, T v)

Ø 当某个类型变量在整个参数列表中的所有参数和返回值中的多处被应用了,如果调用方法时这多处的实际应用类型对应到了不同的类型,并且使用返回值,这时候优先考虑返回值的类型例如,下面语句实际对应的类型就是Integer了,编译将报告错误,将变量x的类型改为float,对比eclipse报告的错误提示,接着再将变量x类型改为Number,则没有了错误:

     int x =(3,3.5f) à static <T> T add(T a, T b) T为实参的公共父类 Number为int和float的公共父类

Ø 参数类型的类型推断具有传递性,下面第一种情况推断实际参数类型为Object,编译没有问题,而第二种情况则根据参数化的Vector类实例将类型变量直接确定为String类型,编译将出现问题:

    copy(new Integer[5],new String[5])à static <T> void copy(T[] a,T[] b);两T同等级

    copy(new Vector<String>(), newInteger[5]) à static <T> void copy(Collection<T> a ,T[] b);

当两个 形参 都是泛型 的时候  那么 需要先看 这两个 泛型的等级是不是相同的 如果为相同 如1 那么这两个泛型的类型 就有接收的实参确定,或调用者的返回值类型确定,       

如果两泛型 等级不同static <T> void copy(Collection<T> a ,T[] b);第一个T为二级泛型,第二个T为以及泛型,那么泛型的类型 由一级泛型 所接收的实参确定二级泛型则体现了泛型的传递性  这种情况一般出现在集合

 

定义泛型类型

l 如果类的实例对象中的多处都要用到同一个泛型参数,即这些地方引用的泛型类型要保持同一个实际类型时,这时候就要采用泛型类型的方式进行定义,也就是类级别的泛型,语法格式如下:

    public class GenericDao<T > {

        private T field1;

        public void save(T obj){}

        public T getById(int id){}

    }

l 类级别的泛型是根据引用该类名时指定的类型信息来参数化类型变量的,例如,如下两种方式都可以:

Ø GenericDao<String> dao =null;

Ø newgenericDao<String>();

l 注意:

Ø 在对泛型类型进行参数化时,类型参数的实例必须是引用类型,不能是基本类型。

Ø 当一个变量被声明为泛型时,只能被实例变量、方法和内部类调用,而不能被静态变量和静态方法调用。因为静态成员是被所有参数化的类所共享的,所以静态成员不应该有类级别的类型参数。

问题:类中只有一个方法需要使用泛型,是使用类级别的泛型,还是使用方法级别的泛型?类级别的

通过反射获得泛型的参数化类型

示例代码:Class GenericalReflection {  private Vector<Date> dates = new Vector<Date>();  public void setDates(Vector<Date> dates) {    this.dates = dates;  }  public static void main(String[] args) {<span style="white-space:pre"></span>Method methodApply = GenericalReflection.class.getDeclaredMethod("applyGeneric",Vector.class);   ParameterizedType pType = (ParameterizedType)                    (methodApply .getGenericParameterTypes())[0];            System.out.println("setDates("                    + ((Class) pType.getRawType()).getName() + "<"                    + ((Class) (pType.getActualTypeArguments()[0])).getName()                    + ">)" );  }}

什么时候用泛型类呢?

当类中的操作的引用数据类型不确定的时候,以前用的Object来进行扩展的,现在可以用泛型来表示。这样可以避免强转的麻烦,而且将运行问题转移到的编译时期。

泛型在程序定义上的体现:

//泛型类:将泛型定义在类上。

class Tool<Q> {    private Q obj;    public  void setObject(Qobj) {        this.obj = obj;    }    public Q getObject() {        return obj;    }}

//当方法操作的引用数据类型不确定的时候,可以将泛型定义在方法上。

    public <W> void method(W w) {

        System.out.println("method:"+w);

    }

//静态方法上的泛型:静态方法无法访问类上定义的泛型。如果静态方法操作的引用数据类型不确定的时候,必须要将泛型定义在方法上。

    public static <Q> void function(Q t) {

        System.out.println("function:"+t);

    }

//泛型接口.

interface Inter<T> {    void show(T t);}class InterImpl<R>implements Inter<R> {    public void show(R r) {        System.out.println("show:"+r);    }}

泛型的细节:

1)、泛型到底代表什么类型取决于调用者传入的类型,如果没传,默认是Object类型;

2)、使用带泛型的类创建对象时,等式两边指定的泛型必须一致;

    原因:编译器检查对象调用方法时只看变量,然而程序运行期间调用方法时就要考虑对象具体类型了;

3)、等式两边可以在任意一边使用泛型,在另一边不使用(考虑向后兼容);

ArrayList<String> al = new ArrayList<Object>();  //

//要保证左右两边的泛型具体类型一致就可以了,这样不容易出错。

ArrayList<? extends Object> al = new ArrayList<String>();

al.add("aa");  //

//因为集合具体对象中既可存储String,也可以存储Object的其他子类,所以添加具体的类型对象不合适,类型检查会出现安全问题。 ?extends Object 代表Object的子类型不确定,怎么能添加具体类型的对象呢?

public static void method(ArrayList<? extends Object> al) {

al.add("abc");  //

  //只能对al集合中的元素调用Object类中的方法,具体子类型的方法都不能用,因为子类型不确定。

}

 








0 0
原创粉丝点击