Java 5 新特性:
除本文中列出的新特性之外,新特性还有:注解和泛型。
注解:注解的介绍
泛型:泛型的介绍
1 静态导入
静态导入功能可以导入一个类中的所有静态成员。
静态导入功能导入类中的静态方法时,再调用此静态方法是就不用指定类名了。
比如System.out.println(); 静态导入System类中的所有静态成员后,
不用再指定类名,简化为 out.println();
静态导入比原先的导入更进了一步,以前导入包中的类,现在可以导入类中的方法。
但是,当类名重复时,需要制定具体的包名;当方法名重复时,需要指定具体的类名。
代码示例:
import java.util.*;
import static java.util.Arrays.*;//导入的是Arrays这个类中的所有静态成员。
import static java.lang.System.*;//导入了System类中的所有静态成员(System类中全是静态的)。
class StaticImportDemo{
public static void main(String[] args){
int[] arr = {3,1,5};
//Arrays.sort(arr);
sort(arr); //已导入Arrays类的所有静态成员,不需再指定类名。
//int index = Arrays.binarySearch(arr,1);
int index = binarySearch(arr,1); //已导入Arrays类的所有静态成员,不需再指定类名。
//因为Object类和Arrays类中都有toString()方法,所以需指定所属类名。
sop(Arrays.toString(arr));
sop("index="+index);
}
public static void sop(Object obj){
//因已导入System类的所有静态成员,所以省略了System。
out.println(obj);
}
}
2 可变参数
当方法的参数列表中同类型的参数个数不同时,以前使用方法的重载解决。
现在有了可变参数,就可以简单的解决这种情况。
可变参数格式:show( String str, Int... arr); //以自定义的show方法举例。
可变参数特点:
1,可变参数中是同类型的参数。
2,”...” 定义在变量类型和变量名之间,用来表示可变长度。
3,使用时可以传递多个参数也可以不传递参数。
4,☆一个方法只能有一个可变参数,并且可变参数必须位于参数列表的最后。
5,☆调用可变参数的方法时,编译器为该可变参数隐含创建一个数组,在方法体中以数组的形式访问可变参数。
可变参数其实就是一种数组参数的简写形式,不用每一次都手动的建立数组。
与数组参数不同的是:
数组参数的长度是在方法定义时,就定义好的,长度固定。
可变参数是在调用方法时,才根据传递的参数长度再创建数组。
代码示例:
import java.util.*;
class ParamMethodDemo{
public static void main(String[] args){
//show(3,4);
/* int[] arr = {3,4}; //虽然少定义了多个方法,但是每一次都要定义一个数组,作为实际参数。
show(arr); /*
show2(2,3,4,5,6);
show2(2,3,4,5,6,7,8,2,12,35,9); //不定义数组,直接传输数组,show的可变参数。
show3("String",3,1,4,5,6,2);
}
public static void show(int[] arr){
System.out.println(arr);
}
public static void show2(int... arr){ //“...”三个点表示可变参数。
System.out.println(arr.length);
}
public static void show3(String str,int... arr){//可变参数必须定义在参数列表的最后面。
System.out.println(str);
System.out.println(arr.length);
}
}
3 增强for循环
增强for循环格式:
for ( 数据类型 变量名 :被遍历的集合或者数组)
{ }
元素的数据类型,每个元素用变量存储,遍历集合中的元素。
对集合进行遍历,只能获取集合元素,但是不能对集合进行操作。
而迭代器除了遍历,还可以remove集合中的元素。如果是ListIterator,还可以在遍历过程中对集合进行增删改查的动作。
传统for循环和高级for循环有什么区别?
高级for循环有一个局限性,必须有被遍历的目标。
建议在遍历数组的时候,还是希望是用传统for,因为传统for可以定义脚标。
代码示例:
import java.util.*;
class ForEachDemo{
public static void main(String[] args){
ArrayList<String> al = new ArrayList<String>();
al.add("abc01");
al.add("abc02");
al.add("abc03");
/* Iterator<String> it = al.iterator(); //迭代器
while(it.hasNext()){
System.out.println(it.next());
} */
for(String s : al){ //遍历al集合中所有元素,存储到变量s。
System.out.println(s);
}
System.out.println(al);
int[] arr = {3,5,11};
for(int x=0; x<arr.length; x++){
System.out.println(arr[x]);
}
for(int i : arr){
System.out.println("i: "+i);
}
HashMap<Integer,String> hm = new HashMap<Integer,String>();
hm.put(1,"a");
hm.put(2,"b");
hm.put(3,"c");
Set<Integer> keySet = hm.keySet();
for(Integer i : keySet){
System.out.println(i+"::"+hm.get(i));
}
//Set<Map.Entry<Integer,String>> entrySet = hm.entrySet();
//for(Map.Entry<Integer,String> me : entrySet)
for(Map.Entry<Integer,String> me : hm.entrySet()){
System.out.println(me.getKey()+"...."+me.getValue());
}
}
}
4 自动装箱拆箱与享元设计模式
JDK1.5中,引入了基本数据类型包装类的自动装箱、自动拆箱功能。
自动装箱,如:
Integer num = 2; //自动装箱
把一个基本类型的数据,自动装箱成一个Integer对象。
自动拆箱,如:
Integer num = 2; //自动装箱
System.out.println(num + 3); //自动拆箱
由于num为Integer对象,而3为int属于基本数据类型,必须把num自动拆箱成基本数据类型后,才能和3相加。
自动装箱拆箱中,涉及到了享元设计模式。
比如:
class AutoBoxDemo{
public static void main(String[] args){
//自动装箱:
Integer iObj = 3;
//自动拆箱:
System. out.println(iObj + 12);
//结果:15
Integer i1 = 13;
Integer i2 = 13;
System. out.println(i1 == i2);
//结果:true
Integer i3 = 137;
Integer i4 = 137;
System. out.println(i3 == i4);
//结果:false
}
}
上面代码中,为什么一个结果为true,一个结果为false呢?
如果一个整数大小在一个字节的取值之间,即 -128~127 之间,
当这个整数被自动装箱包装成Integer对象时,
JVM就会将这些值比较小的基本数据类型对象放入一个缓冲池中,以便于程序的其他部分复用这个对象。
因为Java认为值很小的对象使用频率很高。
这就是享元设计模式!
享元设计模式的思想:常常用的东西,存放到公共空间,大家分享,节省内存。
所以,值为13的i1对象创建时被放入缓冲池,i2只是这个对象的另一个引用,所以 i1==i2 结果为true。
而值为137的i3对象,因为值大于了127,所以没有被放入缓冲池,i4时也是新创建的一个对象,而不是引用,所以 i3==i4 结果为false。
5 枚举
举例说明枚举的作用:
大家都知道一周只有7天,即星期一到星期日,
如果用整数1-7分别表示星期一到星期日,但是有人却取值为0,0不是这一周的任何一天,这时就发生了意外。
枚举,就是让某个变量的取值只能是若干个固定值中的一个,否则,编译器就报错。
枚举可以让编译器在编译阶段就控制源程序中的非法输入,而普通变量无法在编译阶段达到这样一个功能。
5.1 用普通类实现枚举的功能
模拟枚举的步骤:
1,私有化构造函数。
私有化原因:外部不能创建这个类的对象,这样这个类的取值就固定了,只能是类中定义的对象。
2,每一个枚举成员都要定义成全局静态常量(用public static final修饰)。
3,定义若干个枚举成员公共的方法。
可以是具体方法,直接定义了具体实现。
也可以是抽象方法,由枚举成员以匿名内部类实现。不同的枚举成员有不同的实现。
代码示例:
/*
*包含枚举成员共有方法的具体实现。
**/
class WeekDay {
//1,私有化构造方法
private WeekDay(){}
//2,每一个元素为全局静态常量,public static final ----枚举中的常量元素
public static final WeekDay MON =new WeekDay();
public static final WeekDay SUN =new WeekDay();
//3,自定义的方法,包含具体实现
public WeekDay nextDay(){
if(this == MON) //MON为枚举常量
return SUN;
return MON;
}
/*
缺点:如果枚举常量值非常多的时候,这个方法中会存在大量的if...else
*/
public String toString(){
return (this==SUN)? "SUN": "MON";
}
}
/*
*包含抽象方法时,每个枚举对象用匿名内部类实现:
* */
abstract class WeekDay2 {
//1,私有化构造方法
private WeekDay2(){}
// 采用匿名内部类实现抽象方法
public static final WeekDay2 MON =new WeekDay2(){
public WeekDay2 nextDay() {
return SUN;
}
public String toString(){
return "MON";
}
};
public static final WeekDay2 SUN =new WeekDay2(){
public WeekDay2 nextDay() {
return MON;
}
public String toString(){
return "SUN";
}
};
//枚举成员的公共方法是抽象的,定义枚举时再具体实现
public abstract WeekDay2 nextDay();
}
public class EnumDemo{
public static void main(String[] args){
WeekDay2 day = WeekDay2.SUN; //枚举SUN是静态的,可直接使用
System.out.println(day.nextDay());
}
}
5.2 Java5中的枚举
JDK1.5中如何定义枚举?
使用 enum 关键字定义一个枚举类。
枚举是一种特殊的类,其中每个元素都是该类的一个实例对象。
枚举定义中,如果枚举中只有枚举元素没有其他成员,则最后一个枚举元素后面可以省略分号。
举例:
class TestEnum{
public static void main(String[] args) {
WeekDayEnum weekDay =WeekDayEnum.MON;
System.out.println(weekDay);
}
}
enum WeekDayEnum {
//枚举中只有枚举元素时,最后一个元素后可以不加分号
SUN, MON, TUE, WED, THU, FRI, SAT
}
枚举通常定义在其他类内部,可看作内部类:
class TestEnum{
public static void main(String[] args) {
WeekDayEnum weekDay =WeekDayEnum.MON;
System.out.println(weekDay);
}
//定义在其他类内部枚举,可以看做内部类:
public enum WeekDayEnum {
//枚举中只有枚举元素时,最后一个元素后可以不加分号
SUN, MON, TUE, WED, THU, FRI, SAT
}
}
枚举关键字enum的好处:
使用enum关键字,就可以直接定义枚举类型的常量,代替用普通类实现枚举的3个步骤。
1,省略了public static final修饰符的书写。
2,省略私有化构造函数。
3,省略采用匿名内部类创建枚举常量的成员。
这些都封装在enum关键字中,大大简化了书写。
使用枚举关键字enum,可以很大程度简化枚举的书写。
5.3 带构造函数的枚举
枚举就相当于一个类,其中也可以定义构造方法、成员变量、普通方法和抽象方法。
枚举元素必须位于枚举体中的最开始部分,枚举元素列表的最后要有分号与其他成员分隔。
也就是说,枚举成员常量位于构造函数等其他一切成员之前。
把枚举中的成员方法或变量等放在枚举元素的前面,编译器会报告错误。
带构造方法的枚举:
1,构造方法必须定义成私有的
2,如果有多个构造方法,将根据枚举元素创建时所带的参数决定选择哪个构造方法创建对象。
枚举元素MON和MON() 的效果一样,都是调用默认的构造方法。
3,枚举是静态常量,所以枚举类一加载到内存时,相应的枚举成员就会调用构造函数。
代码示例:
public class EnumTest {
public static void main(String[] args) {
WeekDay day = WeekDay.FRI;
}
public enum WeekDay{
SUN(1),MON (),TUE, WED,THI ,FRI,SAT;
private WeekDay(){
System. out.println("first" );
}
private WeekDay(int value){
System. out.println("second" );
}
}
}
5.4 实现抽象方法的枚举
定义枚举TrafficLamp。
实现抽象的nextTrafficLamp方法:每个元素分别是由枚举类的子类来生成的实例对象,这些子类采用类似内部类的方式进行定义。
增加上表示时间的构造方法。
枚举只有一个成员时,就可以作为一种单例的实现方式。
代码示例:
public class EnumDemo4 {
public static void main(String[] args) {
TrafficLamp lamp = TrafficLamp.RED;
System. out.println(lamp.nextLamp());
//结果:GREEN
}
public enum TrafficLamp{
//匿名内部类的形式实现抽象方法。
RED(30) {
public TrafficLamp nextLamp() {
return GREEN ;
}
}, GREEN(45) {
public TrafficLamp nextLamp() {
return YELLOW ;
}
}, YELLOW(5) {
public TrafficLamp nextLamp() {
return RED ;
}
};
private int time;
private TrafficLamp(int time){ //构造方法
this.time = time;
}
public abstract TrafficLamp nextLamp(); //抽象方法
}
}