JAVA高新技术——JDK1.5新特性

来源:互联网 发布:太空工程师编程模块 编辑:程序博客网 时间:2024/06/10 07:31


静态导入

一般导入import是导入一个类或某个包中的所有类。而静态导入是import static 导入的类名或者类名的某个静态方法。

其作用是简化书写,调用静态方法时不用再指定包名了,前提是方法名没有重复。

例:

package cn.itcast.day1;//静态导入import static java.lang.Math.*;public class StaticImport {public static void main(String[] arg){int x=1;x++;System.out.println(x);System.out.println(max(3, 6));System.out.println(abs(3 - 6));}}

可变参数

当写代码时,不知道所写方法的参数个数时,可以用可变参数 ,如public static int add(int x,int ...args)

可变参数注意点:

1、只能出现参数列表最后

2、。。。位于变量类型与变量名之间

3、调用可变参数方法时,实际上是为该可变参数创建一个数组

例:

public class VariableParam {/** * @param args */public static void main(String[] args) {// TODO Auto-generated method stubSystem.out.println(add(1, 2, 3, 4, 5, 6, 7));;}public static int add(int x,int...args){int sum=x;for (int i = 0; i < args.length; i++) {sum+=args[i];}return sum;}}

增强for循环

语法:for ( type 变量名:集合变量名 )  { … } 
注意:集合变量可以是数组或实现了Iterable接口的集合类
上例用增强for循环演示:
public static int add(int x,int...args){int sum=x;for (int arg : args) {sum+=arg;}return sum;

自动拆装箱

//自动装箱 :Integer num1=new Integer(num1);Integer num1=3;//自动拆箱:Integer.valueOf(num1)+3System.out.println(num1+3);//当数值在-128~127时,对象会被重用Integer i1=120;Integer i2=120;System.out.println(i1==i2); //true//不在-128~127时,与其他对象一样都再new一个对象Integer a1=200;Integer a2=200;System.out.println(a1==a2);//false

在-128~127内对象重用是一种设计模式,称为享元设计模式:对象一样,对象位置可能不一样,要接受位置参数

枚举

用途:
枚举就是要让某个类型的变量的取值只能为若干个固定值中的一个,否则,编译器就会报错。
枚举可以让编译器在编译时就可以控制源程序中填写的非法值,普通变量的方式在开发阶段无法实现这一目标
注意:
1、枚举类型是一种类型。也有构造函数(必须是私有的),也有成员变量以及成员函数。
2、枚举元素其实就是对象。枚举元素必须位于枚举体的最开始部分。
3、知道如何选择带参数的构造方法。
4、定义抽象成员函数时,知道如何实现成员函数。

例子:
//基本的:包含构造函数的星期类enum WeekDay{SUN(2),MON,TUE,WED,THE,FRI,SAT;//枚举的构造函数,不能是publicprivate WeekDay(){System.out.println("first");}private WeekDay(int num){System.out.println("second"+"....."+num);}}//高级的:交通灯枚举,包含构造函数及成员enum TrafficLamp{GREEN{public TrafficLamp nextLamp(){return RED;}},RED(){public TrafficLamp nextLamp(){return YELLOW;}},YELLOW(1){public TrafficLamp nextLamp(){return GREEN;}};public abstract TrafficLamp nextLamp();private TrafficLamp() {System.out.println("不带参数的构造函数");}private TrafficLamp(int num){System.out.println("带参数的构造函数");}}public class EnumDemo {public static void main(String[] args) {TrafficLamp trafficLampRed=TrafficLamp.RED;System.out.println(trafficLampRed.nextLamp());}}


注解

三个基本注解

@SuppressWarnings
该注解的作用是给编译器一条指令,告诉它对被注解的代码元素内部的某些警告保持静默。 
比如使用了过时的方法,eclipse会在方法上画一条横线。JAVAC命令也会产生警告。
@SuppressWarnings("dedeprecation)
此注解作用就是告诉编译器,虽然我用的方法过时了,但是我还是坚持要用,你就不要再提示了。

@Deprecated
该注解是告诉用户此方法过时了,不建议使用了。

@Override
该注解是确保重写父类函数成功。

注意:
注解是一个特殊的类型。使用注解,就相当于创建了对象。
注解相当于一种标记,在程序中加了注解就等于为程序打上了某种标记,没加,则等于没有某种标记。
标记可以加在包,类,字段,方法,方法的参数以及局部变量上。

注解的应用



定义注解类————>给一个类加注解————>应用被注解过得类

测试:定义一个注解类,并给一个类加注解时。
public class AnnotationDemo {@MyAnnotationpublic static void main(String[] args) {//isAnnotationPresent判断是否使用了某个注解if(AnnotationDemo.class.isAnnotationPresent(MyAnnotation.class)){System.out.println("yes");}else{System.out.println("no");}}}
发现输出结果是no,也就是我们并没有使用注解。这是为何?
答:
注解是一种类型,注解其实也是可以有自己的注解的,称为元注解。其中有一个Retention元注解。
Retention元注解默认值是RetentionPolicy.CLASS,也就是说@MyAnnotation注解在运行的时候已经被过滤掉了。
解决这个问题的方式就是在注解类上在加个元注解@Retention(RetentionPolicy.RUNTIME)。
Tip:
@Retention元注解有三种取值:RetetionPolicy.SOURCE、RetetionPolicy.CLASS、RetetionPolicy.RUNTIME,
分别对应:java源文件-->class文件-->内存中的字节码。

但还是没有解决,输出的还是no!

因为上面的代码,@MyAnnotation给主函数加注解了。
而 AnnotationDemo.class.isAnnotationPresent(MyAnnotation.class)判断的是类是否使用了注解MyAnnotation。所以输出no。

除了Retention元注解,还有其他的元注解。如Target元注解。限定注解适用位置(类上,方法上,变量上等)。
默认是任意位置。如果设置Target等于ElementType.METHOD,原来加在类上的注解会报错。可以通过数组限定多个适用的位置。

为注解添加属性

注意:注解的属性不是像成员变量一样,而是像成员方法一样。
比如在注解类中添加一个color属性。
@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public @interface MyAnnotation {String color();}
这样在给类添加注解时给属性一个标示,属性是为了更细的区分。
一个注解相当于一个胸牌,如果你胸前贴了胸牌,就是传智播客的学生,否则,就不是。
如果还想区分出是传智播客哪个班的学生,这时候可以为胸牌在增加一个属性来进行区分。
所以在给类添加注解时需要传入属性:
public class AnnotationDemo {@MyAnnotation(color="red")public static void main(String[] args) {//isAnnotationPresent判断是否使用了某个注解if(AnnotationDemo.class.isAnnotationPresent(MyAnnotation.class)){System.out.println("yes");}}}


泛型


基本应用

Jdk 1.5的集合类希望你在定义集合时,明确表示你要向集合中装哪种类型的数据,无法加入指定类型以外的数据。
没有使用泛型时,只要是对象,不管是什么类型的对象,都可以存储进同一个集合中。
使用泛型集合,可以将一个集合中的元素限定为一个特定类型,集合中只能存储同一个类型的对象,这样更安全;
并且当从集合获取一个对象时,编译器也可以知道这个对象的类型,不需要对对象进行强制类型转换,更方便。

泛型进阶

泛型是用在编译时候的。即让问题在编译的时候就发现。而在运行的时候是否泛型是没有区别的,编译器编译带类型说明的集合时会去除掉“类型”信息,
目的就是使程序运行效率不受影响。因此,对于参数化的泛型类型,getClass()方法的返回值和原始类型完全一样。

由于编译生成的字节码会去掉泛型的类型信息,只要能跳过编译器,就可以往某个泛型集合中加入其它类型的数据,
例如,用反射得到集合,再调用其add方法即可。
import java.util.ArrayList;public class GenericsDemo {public static void main(String[] args) throws Exception{//只能放Integer类型的数据ArrayList<Integer> al=new ArrayList<Integer>();al.add(1);//自动装箱// al.add("abc");  错误!但可以通过反射越过编译al.getClass().getMethod("add", Object.class).invoke(al, "abc");System.out.println(al); }}//输出[1, abc]

附:泛型术语:

ArrayList<E>称为泛型类型
ArrayList<E>中的E称为类型变量或类型参数
ArrayList<Integer>称为参数化的类型
ArrayList<Integer>中的Integer称为类型参数的实例或实际类型参数
ArrayList<Integer>中的<>念着typeof
ArrayList称为原始类型

参数化类型与原始类型的兼容性:
 参数化类型可以引用一个原始类型的对象,编译报告警告,例如,ArrayList<String> a2=new ArrayList();//考虑到对以前代码的兼容性,编译器是可以通过的
 原始类型可以引用一个参数化类型的对象,编译报告警告,例如,ArrayList a1=new ArrayList<String>();//原来的方法接受一个集合参数,新的类型也要能传进去

简单地说:两边缺一个没有关系!

参数化类型不考虑类型参数的继承关系:
Vector<String> v = new Vector<Object>(); //错误!不写<Object>没错,写了就是明知故犯
Vector<Object> v = new Vector<String>(); //也错误!

简单地说:两边要是都有尖括号,其内容必须相同!

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

思考题:
下面的代码会报错误吗?
Vector v1 = new Vector<String>();
Vector<Object> v = v1;
答案:编译的时候是不会报错的,因为编译器是一行一行按照语法检查代码的,因此不会出错。
Vector<Integer> vectorList[] = new Vector<Integer>[10];

泛型练习:
public class GenericsDemo {public static void main(String[] args) throws Exception{HashMap<String,Integer> hm=new HashMap<String, Integer>();hm.put("xiaoming", 19);hm.put("zhangsan", 21);Set<Map.Entry<String,Integer>> entrySet=hm.entrySet();for(Map.Entry<String , Integer> en:entrySet){System.out.println(en.getKey()+"::"+en.getValue());}}}

通配符基础

问题:
定义一个方法,该方法用于打印出任意参数化类型的集合中的所有数据,该方法如何定义呢?
如果将printCollection中的参数设置为Collection<Object>类型,那么传入参数的时候,如果参数化的类型是其他类型,就会报错。
例:
public class GenericsDemo {public static void main(String[] args) throws Exception{printCollection(new ArrayList<String>());//错误。等价于:ArrayList<Object> a=new ArrayList<String>()}public static void printCollection(ArrayList<Object> a){//函数体}}

Java1.5中使用?通配符可以引用其他各种参数化的类型,?通配符定义的变量主要用作引用。
此时,printCollection方法中可以调用与参数化无关的方法,不能调用与参数化有关的方法——即方法中不能有泛型。
例如ArrayList类中的add(E e)方法就与参数化有关,不能调用。size()方法与参数化无关就可以调用。
public class GenericsDemo {public static void main(String[] args) throws Exception{printCollection(new ArrayList<String>());}public static void printCollection(ArrayList<?> a){// a.add("aaa");错误 ,与参数化有关的函数a.size(); //可以,与参数无关的函数}}
之所以add方法是错的,是因为他不知道自己未来匹配的一定是String。
比如:如果add方法可以用,那么如果你是new ArrayList<Interger>岂不是运行会出错?但编译没出错。
注:上述例子没有意义,只是为了说明哪些可以编译通过,哪些不可以。



通配符拓展

限定通配符的上边界:
 正确:Vector<? extends Number> x = new Vector<Integer>();
 错误:Vector<? extends Number> x = new Vector<String>(); //只能是Number及其子类
限定通配符的下边界:
 正确:Vector<? super Integer> x = new Vector<Number>();
 错误:Vector<? super Integer> x = new Vector<Byte>();//只能是Integer及其父类


自定义泛型方法

由C++的模板函数引入自定义
如下函数的结构很相似,仅类型不同:
int add(int x,int y) {
           return x+y;
}    
float add(float x,float y) {
          return x+y;         
}    
double add(double x,double y) {
          return x+y;         
}    
C++用模板函数解决,只写一个通用的方法,它可以适应各种类型,示意代码如下:
template<class T>
T add(T x,T y) {
         return (T) (x+y);
}
Java中的泛型类型(或者泛型)类似于 C++ 中的模板。但是这种相似性仅限于表面,Java 语言中的泛型基本上完全是在编译器中实现,
用于编译器执行类型检查和类型推断,然后生成普通的非泛型的字节码,这种实现技术称为擦除(erasure)(编译器使用泛型类型信息保证类型安全,
然后在生成字节码之前将其清除)。这是因为扩展虚拟机指令集来支持泛型被认为是无法接受的,
这会为 Java 厂商升级其 JVM 造成难以逾越的障碍。所以,java的泛型采用了可以完全在编译器中实现的擦除方法。
擦除,这就是之前提到的,编译器最终会去掉“类型”信息!!
比如:
例如,下面这两个方法,编译器会报告错误,它不认为是两个不同的参数类型,而认为是同一种参数类型。
private static void applyGeneric(Vector<String> v){
}
private static void applyGeneric(Vector<Date> v){
}
注意:
Java的泛型方法没有C++模板函数功能强大,java中的如下代码无法通过编译:
<T> T add(T x,T y) {
        return (T) (x+y);
}
因为T类型的对象可能并不具备加法运算的功能。


用于放置泛型的类型参数的尖括号紧邻返回值之前。按照惯例,类型参数通常用单个大写字母表示。
虽然上述加法不能通过编译,但其他的一些简单的可以。
例如:交换数组中的两个元素的位置的泛型方法语法定义如下:

public static void main(String[] args) throws Exception{String[] s=new String[]{"abc","def","rtg"};int[] i=new int[]{1,2,3};Integer[] in=new Integer[]{1,2,3};swap(s,0,1);//swap(i,0,1);错误swap(in,0,1); //Ok}public static <T> void swap(T[] a,int x,int y){T temp=a[x];a[x]=a[y];a[y]=temp;}

为什么int数组传递会错误? 只有引用类型才能作为泛型方法的实际参数!而此处不会自动装箱。int[]本身已经是对象了,你想要的有可能就是int数组呢?





0 0
原创粉丝点击