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


静态导入

一般导入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 stub
		System.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)+3
		System.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;
		//枚举的构造函数,不能是public
		private 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 {
	@MyAnnotation
	public 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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值