jdk5出现了许多新特性,升级的三个因素是:提高效率,简化书写和提高安全性。比如泛型的出现就是为了解决编译时期遇到的安全问题,StringBuilder的出现提高了对可变字符串操作的效率等。
JDK5出现的新特性:
1.静态导入
顾名思义,静态导入就是导入静态的成员,通过import static实现,它可以用来导入一个类的所有或指定的静态成员,当我们要使用这个成员时,可以不用指明具体包名或对象。如 import static java.lang.System.*; ,要往控制台输出数据只要写out.println()就可以了。一个例子:
package cn.it.java5NewFeature;
import static java.lang.Math.pow;
public class StaticImport {
/**
* @param args
* jdk5新特性之 静态导入
* 用来导入一个类的静态成员,当我们要使用时可以不用类名.去调用
* 直接拿来用就可以了,但要注意如果有重名的成员还是要指明所属的具体包或类
*/
// @SuppressWarnings("unchecked")
public static void main(String[] args) {
System.out.println(Math.PI);// 未使用静态导入时要用Math的PI值必须用类名显式地调用
System.out.println(pow(2, 3));// 使用静态导入方式导入了Math类的pow方法后,可直接使用
}
}
2.可变参数
这个特性允许在定义方法时不指明具体的形式参数个数,而是用 参数类型... 参数名 来代替,注意,不确定的参数必须放在参数列表的最后一个,否则无法匹配。java实际是通过在函数内部封装了一个数组来接收这个参数来实现的。
比如,要定义一个求和的方法,但具体是多少个数求和不确定,这时就可以用到可变参数。当然直接传入一个数组也可以实现,可变参数提供了一个更简便的方式。
package cn.it.java5NewFeature;
public class VariableParam {
/**
* @param args
* jdk5新特性之 可变参数
* 当不确定方法的参数个数时,可以利用此特性,用...表示参数个数为0到多个
* 原理:java在内部封装了一个数组,用来接收传进来的参数
*/
public static void main(String[] args) {
System.out.println(add()); // 打印结果为0
System.out.println(add(1,7,3));// 打印结果为11
System.out.println(add(1,6,3,7,3));// 打印结果为20
}
// 这是一个简单的可变参数的例子, 。。。 代表参数个数不确定,前后加不加空格都行
public static int add(int ...is ){
int sum = 0;
for(int i:is)
{
sum += i;
}
return sum;
}
}
3.自动装箱和拆箱
java中有8中基本类型:byte char short int double float long boolean ,并分别为它们定义了数据包装类,对应为:Byte Character Short Integer Double Float Long Boolean 。java可以自动将基本类型包装成对应的引用类型。
package cn.it.java5NewFeature;
public class AutoBox {
/**
* @param args
* jdk5新特性之 自动装箱、拆箱
* java语言为基本类型封装了类,称为基本数据类型包装类
* 当用基本类型去创建对应的包装类对象时,java会自动将基本类型封装成类再返回,这个过程叫自动装箱
* 当用包装类进行基本运算时,java会先将包装类对象转成基本数据类型,再将计算结果自动装箱返回
*/
public static void main(String[] args) {
Integer i1 = 3;// 这里3是基本数据类型int类型的,java会自动将3包装秤Integer再返回这个对象的引用
System.out.println(i1 + 6);// 这里会先将i1拆成int型数据3,和6相加后再包装成Integer打印
System.out.println(i1.getClass().getName());// 在这里打印一下i1的类型为java.lang.Integer
// 特别的,当Integer类型的值在-128~127时,java只在内存产生一个对象,如下:
Integer i2 = 3;
System.out.println(i1 == i2);// 这时将打印true,表明i1和i2是同一个对象
Integer i3 = 200, i4 =200;
System.out.println(i3 == i4);// 这时打印false,表明i3和i4是两个对象
/*
* Integer的这种做法其实用到了享元模式
* 享元模式就是将很多小对象中相同的属性抽取出来作为内部状态形成一个对象,不同属性为外部状态,
* 作为方法参数传入,使数据可以共享,减少对象的创建,节约系统资源
*/
}
}
4.泛型 在 java学习日记7 里面已经有了较为详细的整理,这里不再赘述。
5.枚举
为了防止程序在运行过程中产生某个类的不期望的实例对象,可以将这个类定义成枚举类型。枚举是一种特殊的类,它的构造方法是私有的,也就是说不能在外部实例化它的对象,枚举的每个成员变量都是本类的实例对象,且是静态的,即可通过枚举名直接调用。枚举就是要让某个类型的变量的取值只能是若干个固定值中的一个,否则编译器就会报错。
package cn.it.java5NewFeature;
public class EnumTest {
/**
* @param args
* jdk5新特性之 枚举
* 枚举是一种特殊的类,其中每个元素都是该类的一个实例对象
*/
public static void main(String[] args) {
// 测试 用普通类实现枚举
System.out.println(Season.SPRING.nextSeason());
// 测试 简单的枚举类
AWeek.WEEKEND.saySomething();
// 测试 带构造函数的枚举类
Person.MAN.showName();
// 测试 带抽象方法的枚举类
Phone.SMARTPHONE.call();
/*
* 运行结果如下:
SUMMER
终于周末了,好开心~~~
小明
用智能手机打电话。。。
*/
}
}
// 定义一个带抽象方法的枚举类
enum Phone
{
TELLPHONE{
public void call(){
System.out.println("用座机打电话。。。");
}
},SMARTPHONE{
public void call(){
System.out.println("用智能手机打电话。。。");
}
};
public abstract void call();
}
// 定义一个带构造函数的枚举类
enum Person
{
MAN("小明"),WOMAN("小红");
private String name;
private Person(){}
private Person(String name)
{
this.name = name;
}
public void showName()
{
System.out.println(this.name);
}
}
// 定义一个枚举类,枚举一周内天的信息,包含工作日WEEKEND和休息日WEEKDAY
enum AWeek
{
WEEKEND,WEEKDAY; // 枚举常量只能放在第一句。当下面没有其他语句时,分号可
省略不写
public void saySomething()
{
if(this == WEEKEND)
System.out.println("终于周末了,好开心~~~");
else
System.out.println("工作日,努力工作!!!");
}
}
// 用普通类实现简单的枚举
class Season
{
// 每个成员都是本类的示例对象,且为 public final static 的
public final static Season SPRING = new Season();
public final static Season SUMMER = new Season();
public final static Season AUTUMN = new Season();
public final static Season WINTER = new Season();
private Season(){}// 构造函数必须私有
// 定义一个方法用来返回当前季节的下一个季节
public Season nextSeason()
{
if(this == SPRING)
{
return SUMMER;
}
else if(this == SUMMER)
{
return AUTUMN;
}
else if(this == AUTUMN)
{
return WINTER;
}
else
return SPRING;
}
// 为了让打印的结果有意义,可以覆盖toString方法
public String toString()
{
if(this == SPRING)
{
return "SPRING";
}
else if(this == SUMMER)
{
return "SUMMER";
}
else if(this == AUTUMN)
{
return "AUTUMN";
}
else
return "WINTER";
}
}
6.注解
注解也是一个特殊的类(与接口很相似),它与类、接口、枚举在同一个层次,他们都属于java的一个类型(TYPE)。用注解相当于产生了一个注解的实例对象。注解是jdk新特性里最难掌握的。
注解相当于一种标记,加上注解就等于微程序打上了标记。以后javac编译器、开发工具和其他应用程序就可以用反射来了解。
包、类、成员变量、方法、方法参数和局部变量上都可以加注解。
注解的生存期有三种:源代码阶段(编译),class文件(类加载器),内存中的字节码。默认的注解的生命周期保存到class文件阶段。可以给注解加一个注解即元注解的方式指明生命周期, 通过@Retentionvalue 来设置
java提供了几个基本的注解:
1.@SuppressWarnings("deprecation") 压缩警告, 表示告诉编译器不要提示过时的警告.
该注解的作用是阻止编译器发出某些警告信息。它可以有以下参数:
deprecation:过时的类或方法警告。
unchecked:执行了未检查的转换时警告。
fallthrough:当Switch 程序块直接通往下一种情况而没有Break 时的警告。
path:在类路径、源文件路径等中有不存在的路径时的警告。
serial:当在可序列化的类上缺少serialVersionUID 定义时的警告。
finally:任何finally 子句不能完成时的警告。
all:关于以上所有情况的警告。
2.@Deprecated 用在方法或类上,标记下面这个方法或类已经过时了。应保留到运行时。
3.@Override 用在方法上,表示该方法时覆盖父类的某个方法
声明一个注解类:
@interface 注解名 // AnnotationName
{
}
在一个类上应用注解:
@注解名 // AnnotationName
class 类名 // ClazzName
{
}
对应用了注解类的类进行反射:
ClazzName.class.isAnnotionPresent(AnnotationName.class);
AnnotationName annotation = ClazzName.getAnnotion(AnnotationName.class);
也可以为注解添加属性。
// 定义一个注解
@interface Anno{
String color();
String value(); // 这个属性比较特殊,如果只有这一个属性,则在使用注解时不用写属性名
}
// 定义一个使用注解的方法
class clazz{
@Anno(color = "blue",value = "value")
public void show(){
Anno a = (Anno)clazz.class.getAnnotation(Anno.class);
}
}
元注解:
a. @Retention
它是被定义在一个注解类的前面,用来说明该注解的生命周期。它有以下参数:
RetentionPolicy.SOURCE:指定注解只保留在一个源文件当中。
RetentionPolicy.CLASS:指定注解只保留在一个class 文件中。
RetentionPolicy.RUNTIME:指定注解可以保留在程序运行期间。
b. @Target
它是被定义在一个注解类的前面,用来说明该注解可以被声明在哪些元素前。它有以下参数:
ElementType.TYPE:说明该注解只能被声明在一个类前。
ElementType.FIELD:说明该注解只能被声明在一个类的字段前。
ElementType.METHOD:说明该注解只能被声明在一个类的方法前。
ElementType.PARAMETER:说明该注解只能被声明在一个方法参数前。
ElementType.CONSTRUCTOR:说明该注解只能声明在一个类的构造方法前。
ElementType.LOCAL_VARIABLE:说明该注解只能声明在一个局部变量前。
ElementType.ANNOTATION_TYPE:说明该注解只能声明在一个注解类型前。
ElementType.PACKAGE:说明该注解只能声明在一个包名前。
注解的生命周期
一个注解可以有三个生命周期,它默认的生命周期是保留在一个CLASS 文件,但它也可以由一个@Retetion 的元注解指定它的生命周期。
a. java 源文件当在一个注解类前定义了一个@Retetion(RetentionPolicy.SOURCE)的注解,那么说明该注解只保留在一个源文件当中,当编译器将源文件编译成class 文件时,它不会将源文件中定义的注解保留在class 文件中。
b. class 文件中当在一个注解类前定义了一个@Retetion(RetentionPolicy.CLASS)的注解,那么说明该注解只保留在一个class 文件当中,当加载class 文件到内存时,虚拟机会将注解去掉,从而在程序中不能访问。
c. 程序运行期间当在一个注解类前定义了一个@Retetion(RetentionPolicy.RUNTIME)的注解,那么说明该注解在程序运行期间都会存在内存当中。此时,我们可以通过反射来获得定义在某个类上的所有注解。