JDK5中新增了很多新的java特性,利用这些新语法可以帮助开发人员编写出更加高效、清晰,安全的代码。
这些新特性主要有:
1.静态导入
2.自动装箱/拆箱
3.增强for循环
4.可变参数
5.枚举
6.泛型
7.元数据
1.静态导入
静态导入用于简化程序对静态属性和方法的调用
语法:Import static 包名.类名.静态属性|静态方法|*
例如:
import static java.lang.System.out
import static java.lang.Math.*
2.自动装箱/拆箱
自动装箱:指开发人员可以把一个基本数据类型直接赋给对应的包装类。
自动拆箱:指开发人员可以把一个包装类对象直接赋给对应的基本数据类型。
Integer i = 1; //装箱
int j = i; //拆箱
典型应用:
List list = new ArrayList();
list.add(1);
int j=(Integer)list.get(0);//拆箱
3.增强for循环
引入增强for循环的原因:在JDK5以前的版本中,遍历数组或集合中的元素,需要先获得数组的长度或集合的迭代器,比较麻烦!
JDK5中定义了一种新的语法--增强for循环,以简化此类操作。增强for循环只能在数组或实现Iterable接口的集合类上。
语法格式:
for(变量类型 变量:需要迭代的数组或集合){
}
代码:
@Test
public void test1() {
int arr[] = { 1, 2, 3 };
for (int num : arr) {
System.out.println(num);
}
}
输出:
1
2
3
@Test
public void test2() {
List list = new ArrayList();
list.add(1);
list.add(2);
list.add(3);
for (Object obj : list) {
int i=(Integer)obj;
System.out.println(i);
}
}
输出:
1
2
3
@Test
public void test3() {
Map map=new HashMap();// HashMap按hash函数保存数据,保存的数据与存的顺序可能不一样
map.put("1", "aaa");
map.put("2", "bbb");
map.put("3", "ccc");
//传统方式1
Set set=map.keySet();
Iterator it =set.iterator();
while(it.hasNext()){
String key=(String)it.next();
String value=(String)map.get(key);
System.out.println(key+"="+value);
}
}
输出:
3=ccc
2=bbb
1=aaa
@Test
public void test4() {
Map map=new LinkedHashMap();
map.put("1", "aaa");
map.put("2", "bbb");
map.put("3", "ccc");
//传统方式1
Set set=map.keySet();
Iterator it =set.iterator();
while(it.hasNext()){
String key=(String)it.next();
String value=(String)map.get(key);
System.out.println(key+"="+value);
}
}
输出:
1=aaa
2=bbb
3=ccc
@Test
public void test5() {
Map map = new LinkedHashMap();
map.put("1", "aaa");
map.put("2", "bbb");
map.put("3", "ccc");
// 传统方式2
Set set = map.entrySet();
Iterator it = set.iterator();
while (it.hasNext()) {
Map.Entry entry = (Entry) it.next();
String key = (String) entry.getKey();
String value = (String) entry.getValue();
System.out.println(key + "=" + value);
}
}
输出:
1=aaa
2=bbb
3=ccc
@Test
public void test6() {
Map map = new LinkedHashMap();
map.put("1", "aaa");
map.put("2", "bbb");
map.put("3", "ccc");
// 增强for取map的第1种方式
for (Object obj : map.keySet()) {
String key = (String) obj;
String value = (String) map.get(key);
System.out.println(key + "=" + value);
}
}
输出:
1=aaa
2=bbb
3=ccc
@Test
public void test7() {
Map map = new LinkedHashMap();
map.put("1", "aaa");
map.put("2", "bbb");
map.put("3", "ccc");
// 增强for取map的第2种方式
for (Object obj : map.entrySet()) {
Map.Entry entry = (Entry) obj;
String key = (String) entry.getKey();
String value = (String) entry.getValue();
System.out.println(key + "=" + value);
}
}
输出:
1=aaa
2=bbb
3=ccc
// 使用增强for需要注意的问题:增强for只适合取数据,要修改数组或集合中的数据,要用传统方式
@Test
public void test8() {
int arr[]={1,2,3};
for(int i:arr)
{
i=10;
}
System.out.println(arr[0]);
System.out.println(arr[1]);
System.out.println(arr[2]);
List list = new ArrayList();
list.add("1");
for(Object obj:list)
{
obj=10;
}
System.out.println(list.get(0));
}
输出:
1
2
3
1
4.可变参数
测试JDK中具有可变参数的类Arrays.asList()方法。分别传多个参、传数组,传数组又传参的情况。
注意:传入基本数据类型数组的问题
从JDK5开始,Java允许为方法定义长度可变的参数。语法:
public void foo(int ...args){
}
注意事项:
调用可变参数的方法时,编译器将自动创建一个数组保存传递给方法的可变参数,因此,程序员可以在方法体中以数组的形式访问可变参数
可变参数只能处于参数列表的最后,所以一个方法最多只能有一个长度可变的参数
代码:
@Test
public void testSum() {
sum(1,2,3,4,5,6);
}
public void sum(int ...nums) {
//可变参数你把他看成数组
int sum=0;
for(int i:nums){
sum+=i;
}
System.out.println(sum);
}
输出:
21
@Test
public void testAa() {
aa(1,2,3,4,5,6);
}
//可变参数需要注意的问题:public void aa(int ...nums,int x) { 这样不行,参数会被直接看成一个数组,x没有值
public void aa(int x,int ...nums) {
//可变参数你把他看成数组
int sum=0;
for(int i:nums){
sum+=i;
}
System.out.println(sum);
}
输出:
20
@Test
public void bb(){
List list=Arrays.asList("1","2","3");
System.out.println(list);
String arr[]={"1","2","3","4"};
list = Arrays.asList(arr);
System.out.println(list);
int nums[]={1,2,3,5};//这个细节一定要小心,注意可变参数类型,int nums[]是基本类型数组,这个数组就是一个对象,Arrays.asList(nums)的参数是对象集合。
list=Arrays.asList(nums);
System.out.println(list);
}
输出:
[1, 2, 3]
[1, 2, 3, 4]
[[I@1e63e3d]
5.枚举
1).枚举的作用
一些程序在运行时,它需要的数据不能是任意的,而必须是一定范围内的值,jdk5以前采用自定义类来解决,jdk5以后可以直接采用枚举解决。
JDK5新增的enum关键字用于定义一个枚举类。
一个枚举也可以有构造函数、字段和方法。
2).枚举类具有如下特性:
枚举类也是一种特殊形式的Java类。
枚举类中声明的每一个枚举值代表枚举类的一个实例对象。
与Java中的普通类一样,在声明枚举类时,也可以声明属性、方法和构造函数,但枚举类的构造函数必须为私有的(这点不难理解)。
枚举类也是可以实现接口或继承抽象类。
JDK5中扩展了swith语句,它除了可以接收int,byte,char,short外,还可以接收一个枚举类型。
若枚举类只有一个枚举值,则可以当作单态设计模式使用。
Java中声明的枚举类,均是java.lang.Enum类的孩子,它继承了Enum类的所有方法。常用方法:
name()
ordinal()
valueof(Class enumClass,String name)
values()此方法虽然在JDK文档中查找不到,但每个枚举类都具有该方法,它用于遍历枚举的所有枚举值。
代码:
1>.
@Test
public void test() {
print(Grade.B);
}
public void print(Grade g) {
String value = g.getValue();
System.out.println(value);
}
// 如何定义枚举的构造函数、方法和字段,去封装更多的信息
enum Grade {
A("100-90"), B("89-80"), C("79-70"), D("69-60"), E("59-0");
private String value;// 封装每个对象对应的分数
private Grade(String value) {
this.value = value;
}
public String getValue() {
return this.value;
}
}
输出:
89-80
2>.
@Test
public void test() {
print(Grade.B);
}
public void print(Grade g) {
String value = g.localValue();
System.out.println(value);
}
//带抽象方法的枚举
enum Grade {
A("100-90") {
public String localValue() {
return "优";
};
},
B("89-80") {
public String localValue() {
return "良";
};
},
C("79-70") {
public String localValue() {
return "一般";
};
},
D("69-60") {
public String localValue() {
return "差";
};
},
E("59-0") {
public String localValue() {
return "不及格";
};
};
private String value;// 封装每个对象对应的分数
private Grade(String value) {
this.value = value;
}
public String getValue() {
return this.value;
}
public abstract String localValue();
}
输出:
良
3>.测试枚举的常用方法
@Test
public void test2(){
System.out.println(Grade.C.name());
System.out.println(Grade.C.ordinal());
String str="B";
//Grade g=Grade.valueOf(Grade.class,str);
Grade g=Grade.valueOf(str);//str给出的值若无效则会报错
System.out.println(g);
Grade gs[]=Grade.values();
for(Grade g1:gs){
System.out.println(g1);
}
}
输出:
C
2
B
A
B
C
D
E
6.泛形(Generic)
1)泛形的作用
JDK5以前,对象保存到集合中就会失去其特性,取出时通常要程序员手工进行类型的强制转换,这样不可避免就会引发程序的一些安全性问题。例如:
ArrayList list=new ArrayList();
list.add("abc");
Integer num=(Integer)list.get(0);//运行时会出错,但编码时发现不了
JDK5中的泛形允许程序员在编写集合代码时,就限制集合的处理类型,从而把原来程序运行时可能发生问题,转变为编译时的问题,以此提高程序的可读性和稳定性(尤其在大型程序中更为突出)。示例。
List<String> list1=new ArrayList<String>();
list1.add("aaaa");
String s=list1.get(0);
2)掌握泛形的基本使用
掌握泛形集合的存取
3)泛形的几个注意事项
使用泛形时几个常见问题:
使用泛形时,泛形类型须为引用类型,不能是基本数据类型
ArrayList<Object> list=new ArrayList<String>();//错
ArrayList<String> list=new ArrayList<Object>();//错
ArrayList<String> list=new ArrayList();//对
ArrayList list=new ArrayList<String>();//对
用泛形时,如果两边都使用到泛形,两边必须要一样
注意:泛形是提供给javac编译器使用的,它用于限定集合的输入类型,让编译器在源代码级别上,即挡住向集合中插入非法数据。但编译器编译带有泛形的java程序后,生成的class文件中将不再带有泛形信息,以此使程序运行效率不受到影响,这个过程称之为“擦除”。
泛形的基本术语,以ArrayList<E>为例:<>念typeof
ArrayList<E>中的E称为类型参数变量
ArrayList<Integer>中的Integer称为实际类型参数
整个称为ArrayList<E>泛形类型
整个ArrayList<Integer>称为参数化的类型ParameterizedType
4)自定义泛形---泛形方法
Java程序中的普通方法、构造方法和静态方法中都可以使用泛形。
方法使用泛形前,必须对泛形进行声明,语法:<T>,T可以是任意字母,但通常必须要大写。<T>通常需要放在方法的返回值声明之前。例如:
public static <T> void doxx(T t);
练习:
编写一个泛形方法,实现指定位置上的数组元素的交换。(代码3)
编写一个泛形方法,接收一个任意数组,并颠倒数组中所有元素。(代码3)
注意:
只有对象类型才能作为泛形方法的实际参数。
在泛形中可以同时有多个类型,例如:
public static <K,V> V getValue(K key){return map.get(key);}
5)自定义泛形---泛形类和反射泛形
如果一个类多处都要用到同一个泛形,这时可以把泛形定义在类上(即类级别的泛形),语法格式如下:
public class GenericDao<T>{
private T field1;
public void save(T obj){}
public T getId(int id){}
}
注意,静态方法不能使用类定义的泛形,而应单独定义泛形。
泛形的典型应用:BaseDao和反射泛形
public BaseDao(){
Type type=this.getClass().getGenericSuperclass();
ParameterizedType pt=(ParameterizedType)type;
class=(Class)pt.getActualTypeArguments()[0];
}
6)
代码1:
package cn.itcast.generic;
//自定义带泛形的方法
public class Demo2 {
public void testa(){
a("aaa");
}
public <T> T a(T t){
return null;
}
public <T,E,K> void b(T t,E e,K k){
}
}
代码2:
package cn.itcast.generic;
//自定义类上的泛形
//类上面声明的泛形作用于整个类,静态方法上的泛形需要单独声明
public class Demo3<T,E,K> {
public void testa(){
}
public T a(T t){
return null;
}
public void b(T t,E e,K k){
}
public static <T> void c(T t){
}
}
代码3:
package cn.itcast.generic;
public class Demo4 {
// 编写一个泛形方法,实现指定位置上的数组元素的交换
public <T> void swap(T arr[], int pos1, int pos2) {
T temp = arr[pos1];
arr[pos1] = arr[pos2];
arr[pos2] = temp;
}
// 编写一个泛形方法,接收一个任意数组,并颠倒数组中所有元素。
public <T> void reverse1(T arr[]) {
int start = 0;
int end = arr.length - 1;
while (true) {
if (start >= end)
break;
T temp = arr[start];
arr[start] = arr[end];
arr[end] = temp;
start++;
end--;
}
}
public <T> void reverse2(T arr[]) {
int lenth = arr.length;
for (int i = 0; i < lenth / 2; i++) {
T temp = arr[i];
arr[i] = arr[lenth - i - 1];
arr[lenth - i - 1] = temp;
}
}
}