java基础加强--JDK1.5新特性
笔记摘要:
这里主要介绍了JDK1.5的一些新特性:静态导入、可变参数、增强for循环、基本数据类型的自动装箱与拆箱以及枚举和注解。
其中的自动装箱与拆箱涉及到了享元模式,另外的一个新特性:泛型,由于内容过多,单独成篇。
一、静态导入
1、import语句可以导入一个类或某个包中的所有类
2、import static语句导入一个类中的某个静态方法或所有静态方法
3、语法举例:
import staticjava.lang.Math.sin;
import static java.lang.Math.*;
二、可变参数
1、 问题:一个方法接受的参数个数不固定,例如:
System.out.println(countScore(2,3,5));
System.out.println(countScore(1,2,3,5));
2、 可变参数的特点:
1> 只能出现在参数列表的最后;这个要记住
2> "..."位于变量类型和变量名之间,前后有无空格都可以;
3> 调用可变参数的方法时,编译器为该可变参数隐含创建一个数组,在方法体中以数组的形式访问可变参数。
可变参数示例
package cn.xushuai.Test; public class VariableArgs { public static void main(String[] args) { System.out.println(add(1,2,3,4)); System.out.println(add(1,4)); } public static int add(int x,int ...args){ int sum = 0; for(int i=0;i<args.length;i++){ sum+= args[i]; } return sum; } }
四、增强for循环
1、 语法:
for ( type 变量名:集合变量名 ) { … }
2 注意事项:
迭代变量必须在( )中定义!
集合变量可以是数组或实现了Iterable接口的集合类
举例:
public static int add(int x,int ...args) { int sum = x; for(int arg:args) { sum += arg; } return sum; }
五、基本数据类型的自动拆箱与装箱
1、自动装箱:
Integer num1 = 12;
2、自动拆箱:
System.out.println(num1 + 12);
3 、基本数据类型的对象缓存:
Integer num1 = 12; Integer num2 = 12; 这里相等,-128----127之间时,会使用同一个对象 System.out.println(num1 == num2); Integer num3 = 129; 这里不相等,因为是不同的对象 Integer num4 = 129; System.out.println(num3 == num4); //false Integer num5 = Integer.valueOf(12); Integer num6 = Integer.valueOf(12) ; 这块的道理同上 System.out.println(num5 == num6); //true
4、享元模式(flyweight):
有很多个小的对象,它们有很多属性相同,又频繁使用,这时把它们变成一个对象,将该对象缓存起来。将那些不同的属性变成方法的参数,称之
为外部状态,那些相同的属性叫做内部状态
六、 枚举
1,. 为什么要枚举?
枚举就是要让某个类型的变量的取值只能为若干个固定值中的一个,否则,编译器就会报错,枚举可以让编译器在编译时间就可以控制源程序中填写的
非法值,普通变量的方式在开发阶段无法实现这一目标。
例如:
要定义星期几或性别的变量,该怎么定义?假设用1-7分别表示星期一到星期日,但有人可能会写成int weekday = 0;或即使使用常量方式也无法阻止意外。
2 、枚举的特性
1> 枚举也是一种特殊形式的java类
2> 枚举类中声明的每一个枚举值就代表枚举类的一个实例对象,
例如可以调用WeekDay.SUN.getClass()getName(),和WeekDay.class.getName(),并且已经覆盖了toString()方法。
3> 枚举的构造函数必须是私有的,而且枚举列表要放在最上面,且末尾必须是';’
4> 与java中的普通类一样,在声明枚举时,也可以声明属性、构造函数、成员变量、普通方法和抽象方法, 枚举也可以实现接口,或继承抽象类。
5> JDK5中扩展了switch语句,它除了可以接收int,byte,char,short类型外,还可以接收一个枚举类型。
6> 若枚举只有一个枚举值,则可以当做单例设计模式使用。(作为单例模式的补充)
enumA{ A; }
3、带有构造方法的枚举:
1> 构造方法必须是定义为私有的,如果有多个构造方法。
1> 枚举元素MON和MON()的效果一样,都是调用默认的构造方法
3> 在枚举值后面加个括号就可以调用指定的构造函数
public enum WeekDay{ SUN(),MON(1),TUE(2),WED,THU,FRI,SAT ; private WeekDay() { System.out.println("first"); } private WeekDay(int day) { System.out.println("Second"); } }
4. 枚举的常用方法
name() , ordinal() valueOf(Class enumClass,String name)将字符串转化为对应的枚举值
values() : 此方法虽然在JDK文档中查不到,但是每个枚举类都具有该方法,它用于遍历 枚举的所有枚举值
常用方法示例:
package cn.xushuai.test; public class EnumTest { public static void main(String[] args) { WeekDay weekday = WeekDay.MON; System.out.println(weekday); //MON System.out.println(weekday.name()); //MON System.out.println(weekday.ordinal()); //1,枚举的位置 System.out.println(WeekDay.valueOf("SUN")); //SUN,将字符串转成对应的枚举元素 System.out.println(WeekDay.valueOf("SUN").toString()); //SUN,返回枚举常量的名称 System.out.println(WeekDay.values()); //枚举元素的数组 } public enum WeekDay{ SUN,MON,TUE,WED,THU,FRI,SAT; } }
5、用普通类如何实现枚举功能,定义一个Grade的类来模拟枚举功能
星期的枚举类示例:
1> 私有构造方法
2> 每个元素分别用一个公有的静态成员表示(可以通过内部类实现抽象方法来返回自己 类型的的实例对象)
3> 可以有若干个公有方法或抽象方法
例如:
我们在周一至周日的枚举中,在返回值的时候要在nextDay()方法中进行大量的if-else判断,这样显得太冗杂,这时我们可以 把nextDay()方法
定义为抽象的,只要我们在每一个nextDay()方法中进行不同的实现,就可以将大量的if else语句转换成了 一个个独立的类,就像下面的getGrade()方法一样。
public abstract class Grade { private Grade(){} public abstract Grade getGrade(); public static final Grade A = new Grade(){ public Grade getGrade(){ return A; } }; public static final Grade B = new Grade(){ public Grade getGrade(){ return B; } }; public static final Grade C = new Grade(){ public Grade getGrade(){ return C; } }; public String toString(){ if(this==A){ return "90-100"; } if(this==B){ return "80-90"; } if(this==C){ return "70-80"; } return null; } } public class Enum { public static void main(String[] args){ Grade ClassA = Grade.A; Grade ClassB = Grade.B; Grade ClassC = Grade.C; System.out.println(ClassA.getGrade()); System.out.println(ClassB.getGrade()); System.out.println(ClassC.getGrade()); }
6、带有抽象方法的枚举
定义枚举TrafficLamp
1、实现抽象的nextLamp方法:
2、每个元素分别是由枚举类的子类来生成的实例对象,这些子类采用类似内部类的方式进行定义,并在元素后面的括号中指定参数去调用指定的构造方法
类的方法返回的类型可以是自己这个类型,类里面可以定义静态常量,常量指向的结果就是自己的实例对象
public enum TrafficLamp{ RED(30){ public TrafficLamp nextLamp(){ return YELLOW; } }, YELLOW(10){ public TrafficLamp nextLamp(){ return GREEN; } }, GREEN(45){ public TrafficLamp nextLamp(){ return RED; } }; public abstract TrafficLamp nextLamp(); private int time = 0; private TrafficLamp(int time){this.time = time;} public int getTime(){ return this.time; } }
七、注解
相当于一种标记,在程序中加了注解就相当于为程序打了某种标记,以后,javac编译器开发工具和其他程序可以用反射来了解你的类及各种
元素上有无何种标记。按照标记采取相应的措施,标记可以加在包,类,字段,方法的参数及局部变量上
7.1注解的应用结构图
7.2注解中需要用到的类
1、注释类型 Retention
指示注释类型的注释的生存周期。如果注释类型声明中不存在 Retention 注释,默认为 RetentionPolicy.CLASS。
其三种取值:RetentionPolicy.CLASS、RetentionPolicyRUNTIME.、RetentionPolicy.SOURCE
分别对应: java源文件------>class文件-------->内存中的字节码
2、注释类型 Target
指示注释类型所适用的程序元素的种类。如果注释类型声明中不存在 Target 元注释,则声明的类型可以用在任一程序元素上。如果存在这样的元注释,
则编译器强制实施指定的使用限制。例如,此元注释指示该声明类型是其自身,即元注释类型。它只能用在注释类型声明上:
7.3、为注解添加属性
1、 什么是注解的属性
一个注解相当于一个胸牌,如果你胸前贴了胸牌,就是某个学校的的学生,否则,就不是。如果还想区分出是哪个班的学生,这时候可以为胸牌在
增加一个属性来进行区分。加了属性的标记效果为:@MyAnnotation(color="red")
2、 定义基本类型的属性和应用属性:
在注解类中增加Stringcolor();
@MyAnnotation(color="red")
3、 用反射方式获得注解对应的实例对象后,再通过该对象调用属性对应的方法
MyAnnotation a =(MyAnnotation)AnnotationTest.class.getAnnotation(MyAnnotation.class);
System.out.println(a.color());
可以认为上面这个@MyAnnotation是MyAnnotaion类的一个实例对象
4、 为属性指定缺省值:
String color()default "yellow";
5、 value属性:
String value()default "zxx";
如果注解中有一个名称为value的属性,且你只想设置value属性(即其他属性都采用默认值或者你只有一个value属性),
那么可以省略value=部分,例如:@MyAnnotation("lhm")。
7.4、为注解添加高级属性
1、 数组类型的属性
int [] arrayAttr() default {1,2,3};
@MyAnnotation(arrayAttr = {2,3,4})
如果数组属性中只有一个元素,这时候属性值部分可以省略大括
2、 枚举类型的属性
EnumTest.TrafficLamp lamp() ;
@MyAnnotation(lamp = EnumTest.TrafficLamp.GREEN)
3、 注解类型的属性:
MetaAnnotation annotationAttr() default @MetaAnnotation("xxxx");
@MyAnnotation(annotationAttr = @MetaAnnotation(“yyy”) )
可以认为上面这个@MyAnnotation是MyAnnotaion类的一个实例对象,同样的道理,可以认为上面这个@MetaAnnotation是MetaAnnotation类的
一个实例对象,调用代码如下:
MetaAnnotation ma = myAnnotation.annotationAttr();
System.out.println(ma.value());
注解的详细语法可以通过看java语言规范了解,即看java的language specification。
注解的定义与使用反射的方式获取注解示例
为注解添加注解类型的属性做准备
public @interface MetaAnnotation { String value(); }
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import javax.lang.model.element.Element; //在注解类上加注解(元注解) //RetentionPolicy类是为注解的生命周期加注解,这里的生命周期,指定为运行阶段,默认值为class阶段 @Retention(RetentionPolicy.RUNTIME) //Target,为定义注解的位置,这里指定可以放在方法和类上 @Target({ElementType.METHOD,ElementType.TYPE}) public @interface MyAnnotation { //为注解添加属性 //String color(); String color() default "red"; //只要value赋值,其他都为默认,即可通过编译 String value(); int[] array() default {1,2,3}; //为注解添加数组类型的属性 MetaAnnotation annotationAttr() default @MetaAnnotation("50");//为注解添加注解类型的属性 }
import java.lang.annotation.Annotation; //加上一个标记,相当于获取一个注解的实例对象,同时初始化属性值:注解,颜色和值 @MyAnnotation(annotationAttr=@MetaAnnotation("90"), color = "red",value="100",array=1) //如果只有value属性,可以省略"="号,如果有其他属性,那些属性可以设置为默认以便通过编译 //@MyAnnotation("100") public class AnnotationTest { //@MyAnnotation //为过时的方法添加注解 @SuppressWarnings("deprecation") public static void main(String[] args){ //方法已过时,需要添加注解 System.runFinalizersOnExit(true); //方法已过时 show(); //用反射进行测试AnnotationTest的定义上是否有@MyAnnotation if(AnnotationTest.class.isAnnotationPresent(MyAnnotation.class)){ MyAnnotation myAnnotation = (MyAnnotation)AnnotationTest.class.getAnnotation(MyAnnotation.class); //获取注解 System.out.println(myAnnotation); //获取注解的属性 System.out.println(myAnnotation.color()); System.out.println(myAnnotation.value()); System.out.println(myAnnotation.array().length); //获取注解属性为注解的值 System.out.println(myAnnotation.annotationAttr().value()); } } //添加“已过时”注解 @Deprecated private static void show(){ System.out.println("Hello java!!!"); } }