一、概述
1.静态导入,Static Import
2.自动装箱/拆箱,Autoboxing/Unboxing
3.可变参数,Varargs
4.增强for循环(for-each),Enhanced for Loop
5.枚举,Typesafe Enums
6.泛型,Generics
7.注解,Annotatioins
二、详细解释
1.静态导入
语法:import static 包名.类名.静态属性|静态方法|*
示例:import static java.lang.Math.max;(这是导入静态方法,此时可以直接使用max(..))
import static java.lang.System.out;(这是导入静态属性,此时输出可以这么写out.println("helloWorld"))
import static java.lang.Math.*;(这是导入指定类全部静态属性和方法)
2.自动装箱/拆箱
* 自动装箱:把一个基本数据类型直接赋给对应的包装类变量,或者赋给Object变量.
* 自动拆箱:把包装类对象直接赋给一个对应的基本类型变量.
(1)jdk1.5之前的基本类型与包装类型转换
private static void demo(){
int i = 0;
Integer j = new Integer(i);
int m = j.intValue();
}
(2)jdk1.5时,基本类型与包装类型之间的转换
private static void demo(){
int i = 0;
Integer j = i;
int m = j;
}
3.可变参数
(1)可变参数,即java允许为方法定义长度可变的参数.
语法:public void foo(int...args){..}
注意:
* 可变参数可以当作数组使用.
* 仅传递对象数组时,数组将被打散.
* 基本类型数组将被当作一个对象看待.建议在使用可变参数时,尽量不用基本类型,直接使用其包装类型.
* 可变参数只能处于参数列表的最后(只能有一个可变参数)
* 可变长参数是Object[]数组.
(2)示例(测试JDK中具有可变参数的类Arrays.asList()方法)
public static void main(String[] args) {
List list = Arrays.asList(1,2,3,4,5);//传递多个参数
String[] arrStrs = {"a","bb","ccc"};
list = Arrays.asList(arrStrs);//传递对象数组
list = Arrays.asList();//无参
System.out.println(list);//输出为空即:[]
int[] ints = {1,2,3,4,5};//基本类型数组,注意区别对象数组
list = Arrays.asList(ints);
System.out.println(list);// 输出为对象的字符串,例如:[[I@459189e1]
}
4.增强for循环
(1) * 引入增强for循环的原因:抛弃Iterator
* foreach语句的使用范围
遍历数组
遍历实现Iterator接口的集合类.
* 语法格式;
for(变量类型 变量 : 需迭代的数组或集合){}
* 注意:
增强for循环不能在遍历时删除和修改数据,因为它隐藏了Iterator.
(2)简单举例
private static void demo(){
Person[] arrPers = new Person[3];
person[0] = new Person("joey","30");
person[1] = new Person("chanle","31");
person[2] = new Person("ross","32");
for(Person p : arrPers){
System.out.println(p.getName + ":" + p.getAge());
}
}
5.枚举
(1)枚举概述
* 枚举其实就是一种类型,和int,char相近,就是定义变量时用来限制输入的,而只能赋enum里规定的值.
* 例如下面的Color枚举类,其枚举值(RED,BLANK..)都是Color类型的类静态常量,这些枚举值都是public static final的,
即平常写的常量,所以最好全部大写.
* 枚举中的构造器只能私有private,绝对不允许有public构造器,这样可以保证外部代码无法新构造枚举类的实例.
* 当枚举值只有一个时,可以理解为单例类.
(2)为什么需要枚举
* 如果一个类只能拥有一个实例对象,则可以把它设计成单例类.
但是一个类,如何限制只可以实例化俩个对象呢?
* 一些方法在运行时,它需要的数据不能是任意的,而必须是一定范围内的值(例如性别),此类问题在jdk5以前采用自定义带有枚举功能
的类解决,java5以后可以直接使用枚举予以解决.
(3)枚举常见应用
* 常量
public enum Color {
RED,GREEN,BLANK,YELLOW
}
* switch
JDK1.6之前的switch语句只支持int,char,enum类型,使用枚举,能让我们的代码可读性更强.
enum Signal {
GREEN, YELLOW, RED
}
public class TrafficLight {
Signal color = Signal.RED;
public void change() {
switch (color) {
case RED:
color = Signal.GREEN;
break;
case YELLOW:
color = Signal.RED;
break;
case GREEN:
color = Signal.YELLOW;
break;
}
}
}
* 向枚举中添加新方法
//如果打算自定义自己的方法,那么必须在enum实例序列的最后添加一个分号。而且 Java 要求必须先定义 enum 实例
public enum Color {
RED("红色", 1), GREEN("绿色", 2), BLANK("白色", 3), YELLO("黄色", 4); //先写实例,且最后要有分号.
// 成员变量
private String name;
private int index;
// 构造方法
private Color(String name, int index) {
this.name = name;
this.index = index;
}
// 普通方法
public static String getName(int index) {
for (Color c : Color.values()) {
if (c.getIndex() == index) {
return c.name;
}
}
return null;
}
// get set 方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getIndex() {
return index;
}
public void setIndex(int index) {
this.index = index;
}
}
* 覆盖枚举的方法
toString()方法覆盖的例子
public enum Color {
RED("红色", 1), GREEN("绿色", 2), BLANK("白色", 3), YELLO("黄色", 4);
// 成员变量
private String name;
private int index;
// 构造方法
private Color(String name, int index) {
this.name = name;
this.index = index;
}
//覆盖方法
@Override
public String toString() {
return this.index+"_"+this.name;
}
}
* 实现接口
所有的枚举都继承自java.lang.Enum类。由于Java 不支持多继承,所以枚举对象不能再继承其他类
public interface Behaviour {
void print();
String getInfo();
}
public enum Color implements Behaviour{
RED("红色", 1), GREEN("绿色", 2), BLANK("白色", 3), YELLO("黄色", 4);
// 成员变量
private String name;
private int index;
// 构造方法
private Color(String name, int index) {
this.name = name;
this.index = index;
}
//接口方法
@Override
public String getInfo() {
return this.name;
}
//接口方法
@Override
public void print() {
System.out.println(this.index+":"+this.name);
}
}
* 使用接口组织枚举
public interface Food {
enum Coffee implements Food{
BLACK_COFFEE,DECAF_COFFEE,LATTE,CAPPUCCINO
}
enum Dessert implements Food{
FRUIT, CAKE, GELATO
}
}
6.泛型
(1)
* 泛型是对集合中的元素进行类型限定,将原来可能发生的运行时问题转换成编译时问题-即编译时检查.
* 泛型是提供给javac编译器使用的,当编译通过,并最终生成class文件以后,将不再带有泛型信息,保证运行效率,这个过程叫类型擦除(type erasure).
* 擦除是为了兼容jdk1.5以前的应用.
* 只有对象类型才能作为泛型方法的实际参数.
(2) 泛型是不斜变的
List<Object> 不是 List<String> 的父类型
而数组是不同
Integer[] intArr = new Integer[10];
Number[] numArr = intArr;
上边代码是有效的,因为一个Integer是一个Number,因而一个Integer数组是一个Number数组.
但是对于泛型来说不同:
List<Integer> intList = new ArrayList<Integer>();
List<Number> numList = intList; //这句话是无效的
(3) 类型通配符(可以解决不斜变问题)
示例:
void printList(List<?> l){
for(Object o : l){
System.out.println(o);
}
}
上面代码的问号是一个类型通配符.List<?>是任何泛型List的父类型,所以你可以完全将
List<Object>、List<Integer等传递给pringList().
(4)通配符扩展
* 限定通配符的上边界:
正确的:Vector<? extends Number> x = new Vector<Integer>();
错误的:Vector<? extends Number> x = new Vector<String>();
* 限定通配符的下边界:
正确的:Vector<? super Integer> x = new Vector<Number>();
错误的: Vector<? super Integer> x = new Vector<Byte>();
* 限定通配符总是包括自己
即:Vector<? extends Number> x = new Vector<Number>();
7.注解
(1)概述
* 什么是注解
要解释注解,需要先说一说什么是元数据(metadata).
元数据就是数据的数据.也就是说,元数据是描述数据的.就像数据表中的字段一样,每个字段描述了这个字段下的数据的含义.
j2se5.0中提供的注解就是java源代码的元数据,也就是说注解是描述java源代码的.
(2)j2se5.0中预定义的注解(3个)
* @Override
此注解只可以注解在类的方法上,并且只在Source(即源码级别)级别可用.
功能:可以保证编译时期覆盖父类函数的声明的正确性(可用来判断是否覆盖了父类方法).
此注解定义源码:
@Target(ElementType.METHOD) //只能用于方法上
@Retention(RetentionPolicy.SOURCE) //作用范围,仅在Source级别有效
public @interface Override {
}
* @Deprecated
使用该注解的元素,编译器会发出警告信息,提示方法已经过程.但并不影响程序运行.
此注解源码:
@Documented
@Retention(RetentionPolicy.RUNTIME) //作用范围,运行时有效
public @interface Deprecated {
}
* @SuppressWarnings
上边的Deprecated是为了让编译器产生警告信息,而这个注解是为了不让编译器产生警告信息.
功能:就是关闭特定的警告信息,例如使用集合的时候未指定泛型.
其参数有:
deprecation:试用了过时的类或方法时的警告.
unchecked:执行了未检查的转换时的警告.
fallthrough: 当Switch程序块直接通往下一种情况而没有Break时的警告.
path: 在类路径、源文件路径等中有不存在的路径时的警告.
serial:当在可序列化的类上缺少serialVersionUID定义时的警告.
finally:任何finally子句不能正常完成时的警告.
all:关于以上所有情况的警告.
示例:集合没有加泛型时会有提示,即下滑曲线(看着烦人).这时添加@SuppressWarnings (value={"unchecked"})会关闭警告.
此注解有属性,可以同时抑制多个警告,例如:@SuppressWarnings(value={"unchecked", "fallthrough"})
注解源码:
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE}) //可以注解的范围
@Retention(RetentionPolicy.SOURCE) //作用范围,源码级别
public @interface SuppressWarnings {
String[] value();
}
(3)注解分类
* 标记注解 : 没有变量,只有名称标识. @annotation
* 单值注解 : 在标记注释的基础上提供一段数据. @annotation("data")
* 完整注解 : 可以包括多个数据成员,每个数据成员由名称和值构成. @annotation(val1="data1",val2="data2")
(4)j2se5.0内置的四种元注解
* @Target : 表示该注解可以用于什么地方,可能的ElementType参数包括:
CONSTRUCTOR : 构造器的声明.
FIELD : 域声明(包括enum实例).
LOCAL_VARIABLE : 局部变量声明.
METHOD : 方法声明.
PACKAGE : 包声明.
PARAMETER : 参数声明.
TYPE : 类、接口(包括注解类型)或enum声明.
* @Retention : 表示需要在什么级别保存该注解信息.可选的参数有:
SOURCE : 注解将被编译器丢弃
CLASS : 注解在class文件中可用,但会被vm丢弃.
RUNTIME : vm将在运行期也保留注释,因此可以通过反射机制读取注解的信息.
* @Documented : 将此注解包含在Javadoc中.
* Inherited : 允许子类继承父类中的注解.
(5)自定义注解
* 注解应用结构
@interface A{..} <--- @A <--- Class C{..}
Class B{..}
注解类 用用了注解的类 对"应用了注解的类"
进行反射操作的类,即注解的处理过程.
* 示例:
需求:写一个注解类@MyTest,功能类似Junit中的@Test.
动态接受需要运行的类名,然后运行所有被添加了注解的方法.
代码:
//首先,注解类:
@Retention(RetentionPolicy.RUNTIME)
@Target(value=ElementType.METHOD)
public @interface MyTest {
}
//其次,测试类:
public class TestOne {
@MyTest
public void abc(){
System.out.print("abc");
}
@MyTest
public void def(){
System.out.print("def");
}
public void hello(){
System.out.print("hello");
}
}
//最后,注解处理类:
public class MyTestMain {
public static void main(String[] args)throws Exception {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
System.out.println("输入要测试的类"); //输入TestOne的完全限定名
String clazzName = br.readLine();
Class clazz = Class.forName(clazzName);
Object obj = clazz.newInstance();
Method[] methods = clazz.getMethods();
for(Method m : methods){
if(m.isAnnotationPresent(MyTest.class)){
m.invoke(obj); //最后输出 abcdef,即添加了@MyTest注解的方法被执行了.
}
}
}
}