java编程笔记19 java高级特性
1,自动装箱与拆箱
自动装箱是指把基本数据类型封装成类对象,拆箱是指把类对象拆成基本数据类型。
例如下列代码在java 5.0以后都是可以编译通过的
//自动装箱
Integer in = 3;
Short sh = 2;
Long lo = 3L;
Double dbl = 3.4D;
Float fl = 1.2F;
Byte bt = 12;
Character cht = 'c';
// 自动拆箱
int i = new Integer(3);
short s = new Short((short)1);
long l = new Long(3L);
double d = new Double(2.3D);
float f = new Float(1.2F);
byte b = new Byte((byte)23);
char ch = new Character('a');
而在java5.0以前则不行,因为两边的数据类型不一样,一个是基本数据类型一个是对象,必须通过调用wrapper类的方法来实现对象和基本数据类型之间的转化
关于包装类在这里不详述。
2,新的for/in循环,
只需要在for循环里指出遍历的元素类型,和遍历的对象即可:
package com.book.ch10.java5;
import java.util.ArrayList;
import java.util.List;
public class JForInArray {
public static void main(String[] args) {
String[] array = { "a", "b", "c", "d" };
for (String s : array) {
System.out.println("遍历数组:" + s);
}
List<String> list = new ArrayList<String>();
list.add("A");
list.add("b");
list.add("C");
list.add("D");
for (String obj : list) {
System.out.println("遍历集合:" + obj);
}
}
}
3,可变长参数法
定义一个可变长参数的方法是在参数类型的后面加上省略号,然后加上可变长参数名。可变长参数在方法内部表现为数组,可以使用传统的数组遍历方式来使用数组,也可以使用新的for/in循环:
package com.book.ch10.java5;
public class JVarargsTest {
public static double avg(double... values) {//可变长参数的定义方法
double total = 0;
int count = values.length;
for (double i : values) {//可变长参数的使用
total += i;
}
return total / count;
}
public static void main(String[] args) {
System.out.printf("平均数为 %s. %n", avg(3, 4.2, 2));
System.out.printf("平均数为 %s. %n", avg());
System.out.printf("平均数为 %s. %n", avg(new double[] { 3, 4.2, 2 }));
}
}
4,协变式返回类型
允许在覆盖父类方法的时候,使父类方法的返回值更加具体。例如ArrayList类的get(int index)返回的是Object,继承ArrayList之后可以覆盖get方法,并修改返回值为String,因为String也是Object的子类。在java5.0版本以前,这是不允许的。
package com.book.ch10.java5;
import java.util.ArrayList;
import java.util.List;
@SuppressWarnings(value="serial")
public class JCovariantReturnTypesTest extends ArrayList {
@Override
//可以这样来覆盖父类中的方法,在5.0以前是不允许在覆盖的时候返回值类型跟父类不一样的
public String get(int index) {
return (String)super.get(index);
}
public static void main(String...args){
JCovariantReturnTypesTest list = new JCovariantReturnTypesTest();
list.add("字符串");
// 可以直接返回 String 类型 而不是父类中的 Object
String result = list.get(0);
System.out.printf("%s", result);
}
}
5,静态导入
即impot Static指令导入某个类的静态方法和静态变量,在使用的时候就可以直接使用变量名和方法名,而不用再带上“类名. ”了。
package com.book.ch10.java5;
// 静态导入变量
import static java.lang.Math.PI;
// 静态导入方法
import static java.lang.Math.sin;
// 静态导入所有的方法跟变量
import static java.lang.Math.*;
public class JStaticImportTest {
public static void main(String[] args){
// 直接使用方法和变量,无需 Math 前缀
System.out.printf("sin(%s) = %s%n", PI, sin(PI));
System.out.printf("cos(%s) = %s%n", PI, cos(PI));
}
}
6,泛型
在这里只介绍最基本的语法,后面将详细介绍
类与方法都可以被泛型化,泛型避免了一些运行时错误,使得许多错误在编译阶段就能发现。
类泛型ArrayList<E>代表的意思是ArrayList存放的数据类型是E类型,E代表一个类的类型
方法泛型public E get(int index)代表方法的返回值是E型的
看一个例子:
package com.book.ch10.java5;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
public class JGenericTest {
public static void main(String[] args){
// 范型 限制为 Date 类型
List<Date> list = new ArrayList<Date>();
list.add(new Date());
list.add(new Date());
list.add(new Date());
// 无需 ClassCast.
Date date = list.get(0);
System.out.printf("%s %n", date);
for(Date dd : list){
System.out.printf("for/in 循环:%1$tF %1$tT%n", dd);
}
}
}
注意,泛型编程只能使用类,不能使用基本类型。
集合中使用多个泛型的一个例子:
package com.book.ch10.java5;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
public class JHashMapGeneric {
public static void main(String[] args) {
Map<String, Date> map = new HashMap<String, Date>();//多个泛型
map.put("time_1", new Date());
map.put("time_2", new Date());
map.put("time_3", new Date());
for(String key : map.keySet()){
Date date = map.get(key);
System.out.printf("Key: %1$s, Value: %2$tF %2$tT%n", key, date);
}
}
}
再来看一下泛型通配符:通配符只能用来进行类型安全检查,而不能用来声明具体的类泛型
<?>代表任何类型,不过单独使用?基本没有什么用(单独一个?也就是不用泛型的意思),一般与extends和super一起用
<?>与extends一起表示指定类型及其所有子类
<?>与super一起表示指定类型及其所有父类
由于通配符只能进行类型安全检查,不能用来声明具体的泛型类,故下面的用法是错误的
List <?> = new ArrayList<?> //不能用来做声明用
看下面用通配符做类型检查的一个例子:
package com.book.ch10.java5;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
public class JWildCard {
//通配符指定接受的list可以是Number及其子类
public static void numberList(List<? extends Number> list){
for(Number num : list){
System.out.printf("输出数字:%1$s %n", num);
}
}
//同上
public static void dateList(List<? extends Date> list){
for(Date date : list){
System.out.printf("输出日期:%1$tF %1$tT%n", date);
}
}
public static void main(String[] args){
List<Date> dateList = new ArrayList<Date>();//类型声明要用具体的泛型
List<Integer> intList = new ArrayList<Integer>();
dateList.add(new Date());
dateList.add(new Date());
intList.add(3);
intList.add(6);
numberList(intList);
dateList(dateList);
}
}
泛型化一个类是在类名后面定义泛型类型,泛型化一个方法需要在方法名的返回类型前定义泛型类型:下面写一个自己的泛型:
package com.book.ch10.java5;
import java.util.Date;
public class JGenericMethod {
// 范型化类. 在类名后使用<>定义了范型类型 T.
static class A<T> {
// 定义之后直接使用 T 即可
public T generic(T t){
return t;
}
}
static class B {
// 范型化方法. 在方法返回类型前使用<>定义范型类型 T
public <T> T generic(T t){
return t;
}
// 范型化方法. 在方法返回类型前使用<>定义范型类型 T, 该范型类型只能为 Date 及其子类
public <T extends Date> T genericDate(T t){
return t;
}
}
public static void main(String[] args){
A<String> a = new A<String>();
String ss = a.generic("一个字符串");
B b = new B();
String ss2 = b.generic("另一个字符串");
Date date = b.genericDate(new Date());
}
}
7,枚举类型
枚举类型不能在方法内部声明,只能声明为一个类变量,
public enum Sex{MALE,FEMAL,UNKNOW};
枚举类型里德变量是实实在在的对象,可以把他们看成是static,final,public的
package com.book.ch10.java5;
public class JEnumeration {
public static enum Sex { MALE, FEMALE, UNKOWN, };
public static void main(String[] args){
System.out.printf("Sex.FEMALE.toString(): \t%s %n", Sex.FEMALE.toString());
System.out.printf("Sex.FEMALE.ordinal(): \t%s %n", Sex.FEMALE.ordinal());
System.out.printf("Sex.FEMALE.getClass(): \t%s %n", Sex.FEMALE.getClass());
System.out.printf("%n遍历所有的 Sex 枚举变量: ");
for(Sex sex : Sex.values()){
System.out.printf("%s, ", sex);
}
switch(Sex.FEMALE){
case MALE:
// do something
break;
case FEMALE:
// do something
break;
case UNKOWN:
// do something
break;
default:
// do something
}
}
}
下面来看一下扩展枚举类型,enum和class,interface一样是一种对象。因此它可以扩展,添加属性或方法,跟interface不能有构造函数一样,enum也有一些限制:
enum内部可以有构造方法,但不能有public的构造方法
enum内置对象间必须用逗号隔开,声明完所有的内置对象后用分号结束
enum内置对象必须使用声明的构造函数,否则编译错误
看一个例子:
package com.book.ch10.java5; public class JEnumTest { static enum Type { // 内置的枚举类型对象 // 各枚举类型对象之间用逗号隔开。 MANAGER("经理", 10000), ASSISTANT("助理", 6000), WORKER("员工", 4000), ;//分号结束 // 扩展的属性 private String name; private double pay; // 构造函数 不能为 public Type(String name, double pay){ this.name = name; this.pay = pay; } } static class Person { public Person(String name, Type type){// 注意person的类型
this.name = name; this.type = type; } private String name; private Type type; public String getName() { return name; } public void setName(String name) { this.name = name; } public Type getType() { return type; } public void setType(Type type) { this.type = type; } } public static void printInfo(Person person){ System.out.printf("姓名:%1$s, \t职业:%2$s, \t岗位薪水:%3$s %n", person.getName(), person.getType().name, person.getType().pay); } public static void main(String[] args){ Person p1 = new Person("张三", Type.MANAGER); Person p2 = new Person("李四", Type.WORKER); Person p3 = new Person("王五", Type.ASSISTANT); printInfo(p1); printInfo(p2); printInfo(p3); } }