1、概述:
1)JDK5.0版本以后出现的新特性,用于解决安全问题,是一个类型安全机制。
2)JDK1.5的集合类希望在定义集合时,明确表明你要向集合中装入那种类型的数据,无法加入指定类型以外的数据。
3)泛型是提供给javac编译器使用的可以限定集合中的输入类型说明的集合时,会去掉“类型”信息,使程序运行效率不受影响,对参数化的泛型类型,getClass()方法的返回值和原始类型完全一样。
4) 没有使用泛型时,只要是对象,不管是什么类型的对象,都可以存储进同一个集合中。使用泛型集合,可以将一个集合中的元素限定为一个特定类型,集合中只能存储同一个类型的对象,这样更安全;并且当从集合获取一个对象时,编译器也可以知道这个对象的类型,不需要对对象进行强制类型转换,这样更方便。泛型就是把原来的类名进行了延长<在JDK 1.5中,你还可以按原来的方式将各种不同类型的数据装到一个集合中,但编译器会报告unchecked警告。
Note:
1)由于编译生成的字节码会去掉泛型的类型信息,只要能跳过编译器,就可以往某个泛型集合中加入其它类型的数据,如用反射得到集合,再调用add方法即可。
2) 在使用java提供的对象时,什么时候写泛型?
通常在集合框架中很常见,只要见到<>就要定义泛型。其实<>就是用来接收类型的。当使用集合时,将集合中要存储的数据类型作为参数传递到<>中即可。
2、格式:通过<>来定义要操作的引用数据类型
如:ArrayList<String> = new ArrayList<String>(); //定义要存入集合中的元素指定为String类型
3、优点:
a、将运行时期出现的问题ClassCastException,转移到了编译时期。方便于程序员解决问题。让运行时期问题减少、安全。
b、避免了强制转换的麻烦。如在实现某一个接口时,指定传入接口方法的实参的类型的话,在复写该接口方法时就可以直接使用指定类型,而不需要强制转换。
4、术语:
如:ArrayList<E>类和ArrayList<Integer>
1、ArrayList<E>整个称为泛型类型
2、ArrayList<E>中的E称为类型变量或类型参数
3、整个ArrayList<Integer>称为参数化类型
4、ArrayList<Integer>中的Integer称为类型参数的实例或实际类型参数
5、ArrayList<Integer>中的<>称为typeof
6、ArrayList称为原始类型
7、参数化:parametered,已经将参数变为实际类型的状态。
5、参数化类型的说明:
1)参数化类型与原始类型的兼容性(原来的方法接受一个集合参数,新类型也要能传进去)
a、参数化类型可引用一个原始类型的对象,编译只是报警告。
如:Collection<String>coll = new Vector();
b、原始类型可引用一个参数化类型的对象,编译报告警告
如:Collectioncoll = new Vector<String>();
2)参数的类型不考虑类型参数的继承关系:
Vector<String> v = newVector<Objec>();//错误的
Vector<Objec> v = newVector<String>();//错误的,不写Object没错,写了就是明知故犯
3)编译器不允许创建泛型变量的数组。即在创建数组实例时,数组的元素不能使用参数化的类型
如:Vector<Integer>vectorList[] = new Vector<Integer>[10];//错误的
6、泛型限定:(用于泛型扩展)通配符:
? 可以理解为占位符。类型不明确时使用,不能使用类型特有方法,例如length()方法。
T 代表具体类型,可以接收并操作这个类型。
? extends E: 可以接收 E 类型或者 E 的子类型,上限。
? super E: 可以接收 E 类型或者 E 的父类型,下限。
Note:以下是错误的写法。集合(对象)的泛型的类型左右两边要一致。通过上限或者下限来解决。
ArrayList<Person> al1 = new ArrayList<Student>();
TreeSet< Student > al = new TreeSet < Person >();
示例如下:
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.Iterator;
import java.util.TreeSet;
//动物 父类
class Animal {
private String name;
Animal(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
// 猫 继承父类
class Cat extends Animal {
Cat(String name) {
super(name);
}
}
public class GenericTest {
public static void main(String[] args) {
ArrayList<Animal> al = new ArrayList<Animal>();
al.add(new Animal("abc1"));
al.add(new Animal("abc2"));
al.add(new Animal("abc3"));
printColl(al);// 父类对象的元素集合可以调用
ArrayList<Cat> al1 = new ArrayList<Cat>();
al1.add(new Cat("abc--1"));
al1.add(new Cat("abc--2"));
al1.add(new Cat("abc--3"));
printColl(al1); // 子类对象的元素集合也可以调用
}
// 定义一个上限的泛型方法
public static void printColl(Collection<? extends Animal> al) {
Iterator<? extends Animal> it = al.iterator();
while (it.hasNext()) {
System.out.println(it.next().getName());
}
}
}
7、泛型类
1)若类实例对象中要使用到同一泛型参数,即这些地方引用类型要保持同一个实际类型时,这时候就要采用泛型类型的方式进行定义,也就是类级别的泛型。
2)什么时候定义泛型类
当类中要操作的引用数据类型不确定的时候,早期定义Object来完成扩展。现在定义泛型来完成扩展。
3)泛型类定义的泛型,在整个类中有效。如果被方法使用,那么泛型类的对象明确要操作的具体类型后,所以要操作的类型就已经固定了。
4)类级别的泛型是根据引用该类名时指定的类型信息来参数化类型变量的,例如,如下两种方式都可以:
GenericDao<String>dao = null;
newgenericDao<String>();
5)语法格式:
class Utils<TT>{
private TT s;
public void settt(TT s){
this.s=s;
}
public TT getTT(){
return s;
}
}
Note:
a、在对泛型进行参数化时,类型参数的实例必须是引用类型,不能是基本类型。
b、当一个变量被声明为参数时,只能被实例变量和方法调用(还有内嵌类型),而不能被静态变量和静态方法调用,因为静态成员是被所有参数化的类共享的。
8、泛型方法
概述:为了让不同方法可以操作不同类型,而且类型还不确定,那么可以将泛型定义在方法上。其中方法上的泛型可以不和类泛型相同,静态方法不可以访问类上定义的泛型,但如果静态方法操作的应用数据类型不确定,可以将泛型定义在方法上。
特点:
1)位置:用于放置泛型的类型参数的<>应出现在方法的其他所有修饰符之后和在方法的返回类型之前,也就是紧邻返回值之前,按照惯例,类型参数通常用单个大写字母表示。
2)只有引用类型才能作为泛型方法的实际参数。
3)除了在应用泛型时可以使用extends限定符,在定义泛型时也可以使用extends限定符。
例如:Class.getAnnotation()方法的定义。并且可以用&来指定多个边界,如<V extends Serializable& cloneable> void method(){}。
4) 普通方法、构造函数和静态方法中都可以使用泛型。
5) 可以用类型变量表示异常,称之为参数化的异常,可用于方法的throws列表中,但是不能用于catch子句中。
6) 在泛型中可同时有多个类型参数,在定义它们的<>中用逗号分开。
例如:public static <K,V> V getValue(K key) { return map.get(key);}
示例如下:
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
/*
* 泛型限制:
* ? 通配符,可以理解为占位符
* ? extends E: 可以接收 E 类型或者 E 的子类型。上限。
* ? super E: 可以接收 E 类型或者 E 的父类型。下限。
*/
class Person {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
Person(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person [name=" + name + "]";
}
}
class Student extends Person {
Student(String name) {
super(name);
}
@Override
public String toString() {
return "Student [name=" + getName() + "]";
}
}
public class GenericDemo2 {
public static void main(String[] args) {
ArrayList<String> al1 = new ArrayList<String>();
al1.add("abcd");
al1.add("efg");
al1.add("xyz");
System.out.println("---------printElement1(al1)----------");
printElement1(al1);
// printElement2(al2);
ArrayList<Person> al = new ArrayList<Person>();
al.add(new Person("zhangsan"));
al.add(new Student("li"));
System.out.println("---------printElement1(al)----------");
printElement1(al);
}
public static <T> void printElement1(ArrayList<T> al) {
Iterator<T> it = al.iterator();
while (it.hasNext()) {
T t = it.next();
t.toString();
System.out.println(t);
}
}
public static void printElement2(ArrayList<? extends Person> al) {
Iterator<? extends Person> it = al.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
}
}
T和?有什么区别:
1)T限定了类型,传入什么类型即为什么类型,可以定义变量,接收赋值的内容。
2)?为通配符,也可以接收任意类型但是不可以定义变量。
但是这样定义,虽然提高了扩展性,可还是有一个局限性,就是不能使用其他类对象的特有方法。通配符方案要比泛型方法更有效,当一个类型变量用来表达两个参数之间或参数和返回值之间的关系时,即同一个类型变量在方法签名的两处被使用,或者类型变量在方法体代码中也被使用,而不是仅在签名的时候使用,才需要使用泛型方法。
Note:泛型的定义:
1)定义泛型:当又不确定的类型需要传入到集合中,需要定义泛型。
2)定义泛型类:如果类型确定后,所操作的方法都是属于此类型,则定义泛型类。
3)定义泛型方法:如果定义的方法确定了,里面所操作的类型不确定,则定义泛型方法。
9、类型参数的类型推断
1)编译器判断范型方法的实际类型参数的过程称为类型推断,类型推断是相对于知觉推断的,其实现方法是一种非常复杂的过程。
2)根据调用泛型方法时实际传递的参数类型或返回值的类型来推断,具体规则如下:
a、当某个类型变量只在整个参数列表中的所有参数和返回值中的一处被应用了,那么根据调用方法时该处的实际应用类型来确定,这很容易凭着感觉推断出来,即直接根据调用方法时传递的参数类型或返回值来决定泛型参数的类型,例如:
swap(new String[3],3,4)
——> static <E> voidswap(E[] a,int i,int j)
b、当某个类型变量在整个参数列表中的所有参数和返回值中的多处被应用了,如果调用方法时这多处的实际应用类型都对应同一种类型来确定,这很容易凭着感觉推断出来,例如:
add(3,5)
——> static<T> T add(T a,T b)
c、当某个类型变量在整个参数列表中的所有参数和返回值中的多处被应用了,如果调用方法时这多处的实际应用类型对应到了不同的类型,且没有使用返回值,这时候取多个参数中的最大交集类型,例如,下面语句实际对应的类型就是Number了,编译没问题,只是运行时出问题:
fill(new Integer[3],3.5f)
——> static<T> void fill(T[] a,T v)
d、当某个类型变量在整个参数列表中的所有参数和返回值中的多处被应用了,如果调用方法时这多处的实际应用类型对应到了不同的类型,并且使用返回值,这时候优先考虑返回值的类型,例如,下面语句实际对应的类型就是Integer了,编译将报告错误,将变量x的类型改为float,对比eclipse报告的错误提示,接着再将变量x类型改为Number,则没有了错误:
intx =(3,3.5f)
——> static<T> T add(T a,T b)
e、参数类型的类型推断具有传递性,下面第一种情况推断实际参数类型为Object,编译没有问题,而第二种情况则根据参数化的Vector类实例将类型变量直接确定为String类型,编译将出现问题:
copy(newInteger[5],new String[5])
——> static<T> void copy(T[] a,T[] b);
copy(newVector<String>(), new Integer[5])
——> static<T> void copy(Collection<T> a ,T[] b);
二、枚举
1、概述:枚举是JDK1.5出现的新特性,字面意思理解就是,枚举中的元素是有限的,可以一一列举出来。
2、枚举的由来:
问题:要定义星期几或性别的变量,该怎么定义?假设用1-7分别表示星期一到星期日,但有人可能会写成int weekday = 0;或即使使用常量方式也无法阻止意外。
解决方案:枚举就是要让某个类型的变量的取值只能为若干个固定值中的一个,否则,编译器就会报错。枚举可以让编译器在编译时就可以控制源程序中填写的非法值,普通变量的方式在开发阶段无法实现这一目标。
示例:
/*
* 用普通类如何实现枚举功能,定义一个Weekday的类来模拟枚举功能。
1、私有的构造方法
2、每个元素分别用一个公有的静态成员变量表示
3、可以有若干公有方法或抽象方法。采用抽象方法定义nextDay就将大量的if.else语句转移成了一个个独立的类。
*/
abstract class WeekDay {
private WeekDay() {
}
public final static WeekDay SUN = new WeekDay() {
public WeekDay nextDay() {
return MON;
}
};
public final static WeekDay MON = new WeekDay() {
public WeekDay nextDay() {
return SUN;
}
};
public abstract WeekDay nextDay();
public String toString() {
return this == SUN ? "SUM" : "MON";
}
}
public class WeekDayDemo {
public static void main(String[] args) {
// 只能获取WeekDay中指定的值,否则会出错
String today = WeekDay.MON.toString();
// WeekDay.MON内部实现了WeekDay的nextDay方法,所以有返回值
String nextDay = WeekDay.MON.nextDay().toString();
System.out.println("today is " + today + ",nextDay is " + nextDay);
}
}
Note:
1)通过enum关键字定义枚举类,枚举类是一个特殊的class,每个元素都是该类的一个实例对象。
2)用枚举类规定值,如上面的WeekDay类。以后用此类型定义的值只能是这个类中规定好的那些值,若不是这些值,编译器不会通过。
3)好处:在编译时期就会发现错误,表明值不符合,减少了运行时期的错误。
4)如果调用者想打印枚举类中元素的信息,需由编写此类的人定义toString方法。
3、常用方法:
构造器:
1)构造器只是在构造枚举值的时候被调用。
2)构造器只有私有private,绝不允许有public构造器。这样可以保证外部代码无法重新构造枚举类的实例
3)构造器可以有多个,调用哪个即初始化相应的值。
非静态方法:(所有的枚举类都继承了Enum方法)
1)String toString() ; //返回枚举量的名称
2)int ordinal() ; //返回枚举值在枚举类中的顺序,按定义的顺序排
3)Class getClass(); //获取对应的类名
4) String name(); //返回此枚举常量的名称,在其枚举声明中对其进行声明。
静态方法:
1)valueOf(String e) ; //转为对应的枚举对象,即将字符串转为对象
2)values() ; //获取所有的枚举对象元素
示例如下:
public class EnumDemo {
public static void main(String[] args) {
WeekDay weekDay = WeekDay.SUN;
System.out.println(weekDay);// 输出枚举常量名
System.out.println(weekDay.name());// 输出对象名
System.out.println(weekDay.getClass());// 输出对应类
System.out.println(weekDay.toString());// 输出枚举对象名
System.out.println(weekDay.ordinal());// 输出此对象在枚举常量的次序
System.out.println(WeekDay.valueOf("WED"));// 将字符串转化为枚举常量
System.out.println(WeekDay.values().length);// 获取所以的枚举元素,并打印其长度
}
// 定义枚举内部类
public enum WeekDay {
SUN(1), MON, TUE, WED, THI, FRI, SAT;// 分号可有可无,但如果下面还有方法或其他成员时,分号不能省。
// 而且当有其他方法时,必须在这些枚举变量的下方。
// 无参构造器
private WeekDay() {
System.out.println("无参构造器");
}
// 有参构造器
private WeekDay(int day) {
System.out.println("有参构造器");
}
}
}
结果如下:
4、枚举的高级应用
1)枚举就相当于一个类,其中也可以定义构造方法、成员变量、普通方法和抽象方法。
2)枚举元素必须位于枚举体中的最开始部分,枚举元素列表的后要有分号与其他成员分隔。把枚举中的成员方法或变量等放在枚举元素的前面,编译器报告错误。
3)带构造方法的枚举
a、构造方法必须定义成私有的
b、如果有多个构造方法,该如何选择哪个构造方法?
c、枚举元素MON和MON()的效果一样,都是调用默认的构造方法。
4)带方法的枚举
示例如下:/*
* 抽象的枚举方法
* 此时枚举中的常量需要子类来实现,这是可以利用内部类的方式来定义枚举常量
* 带方法的枚举
1)定义枚举TrafficLamp
2)实现普通的next方法
3)实现抽象的next方法:每个元素分别是由枚举类的子类来生成的实例对象,这些子类
4)用类似内部类的方式进行定义。
5)增加上表示时间的构造方法
* */
public class TrafficLampDemo {
public enum TrafficLamp {
RED(30) {
public TrafficLamp nextLamp() {
return GREEN;
}
},
GREEN(30) {
public TrafficLamp nextLamp() {
return YELLOW;
}
},
YELLOW(5) {
public TrafficLamp nextLamp() {
return RED;
}
};
private int time;
// 构造器
private TrafficLamp(int time) {
this.time = time;
}
// 抽象方法
public abstract TrafficLamp nextLamp();
}
}
Note:
1)匿名内部类比较常用
2)类的方法返回的类型可以是本类的类型
3)类中可定义静态常量,常量的结果就是自己这个类型的实例对象
4)枚举只有一个成员时,就可以作为一种单例的实现方式。
三、注解
1、概述
1)注解(Annotation)相当于一种标记,在程序中加了注解就等于为程序打上了某种标记,没加,则等于没有某种标记。以后,javac编译器、开发工具和其他程序可以用反射来了解你的类及各种元素上有无何种标记,看你有什么标记,就去干相应的事。
2)标记可以加在包,类,字段,方法,方法的参数以及局部变量上。
3)在java.lang包中提供了最基本的注解(annotation)。
4)格式:@注解类名。
如果只有一个value名称的属性或其他属性缺省,则可@注解名(”属性值”);
如果有多个或不缺省或者需重新赋值,则@注解名(属性名=”属性值”,…)。
2、java中三种最基本的注解
1)如@SuppressWarning(”deprecation”):表示压制过时警告;或者说不要警告过时提示。SupressWarning是告知编译器或开发工具等不需要再提示指定的警告了;“deprecation”是警告的信息,即过时警告。
2)@Deprecated:表示告知调用者,该成员函数、字段等已经过时,不再推荐使用。源代码标记@Deprecated是在JDK1.5中作为内置的annotation引入的,用于表明类(class)、方法(method)、字段(field)已经不再推荐使用,并且在以后的JDK版本中可能将其删除,编译器在默认情况下检测到有此标记的时候会提示警告信息。例如:假定之前的某个类升级了,其中的某个方法已经过时了,不能够将过时的方法删除,因为可能会影响到之前调用此这个方法的某些程序,这时就可以通过在方法上加这个注解来标记。
3)@Override:表示下面的方法是在覆盖(父类方法),如果不存在覆盖,就会报错。加上此注解,可对类中的方法判断是否是要覆盖的父类的方法。典型的例子即在类中覆盖equals(Object obj)方法时,其中的参数类型必须是Object,才能被覆盖;若不是,则不存在覆盖。此时如果加上了此注解就会提示警告。
3、注解的应用结构图
Note:注解就相当于一个你的源程序中要调用的一个类,要在源程序中应用某个注解,得先准备好了这个注解类。就像你要调用某个类,得先有开发好这个类。
4、自定义注解及其应用
1)定义格式:@interface名称{statement}
如:最简单的注解类:public @interface MyAnnotation{}
2)元注解(注解的注解)
即在定义注解类的时候加注解。如两个常用于元注解的注解:Retention和Target
1)Retetion:用于说明注解保留在哪个阶段(即注解的生命周期)。
一个注解的生命周期包含:java源程序--(javac)-->class文件--(类加载器)-->内存中的字节码分别对应Retetion这个枚举类的值:
RetetionPolicy.SOURSE:java源文件时期,如@Overried和@SuppressWarning
RetetionPolicy.CLASS: class文件时期(默认阶段)
RetetionPolicy.RUNTIME:运行时期,如@Deprecated
Note:a)当在源程序上加了注解,javac将java源程序编译为class文件时,会对注解的生命周期进行判断。如果该注解只保留在源程序,则编译时会将该注解进行相应的处理操作b)class文件中不是字节码,只有把class文件中的内容加载进内存,用类加载器加载处理后(进行完整的检查等处理),最终得到的二进制内容才是字节码,
2)Target:用于说明注解类的使用范围。如在方法上还是类上,默认值是任何地方。
其值可设置为枚举类ElementType类中的任何一个,包括:包、字段、方法、方法参数、构造器、类等值。取值为:
PACKAGE(包声明)
FIELD(字段声明)
ANNOTATION_TYPE(注释类型声明)
CONSIRUCTOR(构造器声明)
METHOD(方法声明)
PARAMETER(参数声明)
TYPE(类、接口(包含注释类型)或枚举声明)
LOCAL_VARIABLE(局部变量声明)
Note:其中代表类的值是TYPE。因为class、enum、interface和@interface等都是平级的,所以统属于Type,不可用CLASS表示。
3)注解的应用
通过反射方式来获取自定义的注解类,步骤跟注解的应用结构一致。
如:
第一、定义注解类:@interfaceA{}
第二、应用了“注释类”的类:@A class B{}
第三、对“应用注释类的类”进行反射操作的类:class c{...},操作如下:
B.class.isAnnotionPresent(A.class);//判断是否存在此注解类
A a = B.class.getAnnotation(a.class);//存在的话则得到这个注释类的对象
5、为注解添加基本属性
1)属性:一个注解相当于一个胸牌,但仅通过胸牌还不足以区别带胸牌的两个人,这时就需要给胸牌增加一个属性来区分,如颜色等。
2)定义格式:同接口中的方法一样:String color();
定义缺省格式:String value() default “heima”;
3)应用:直接在注解的括号中添加自身的属性,如:
@MyAnnotation(color=”red”)
1)如果注解中有一个名称为value的属性,且你只想设置value属性(即其他属性都采用默认值或者你只有一个value属性),那么可以省略value=部分,例如:@SuppressWarnings("deprecation")。
2)可以为属性值指定缺省值(default),应用时同样可以重新设置属性值。
3)用反射方式获得注解对应的实例对象后,可以通过该对象调用属性对应的方法来获取属性值。
6、为注解增加高级属性
1)可以为注解增加的高级属性的返回值类型有:
1)八种基本数据类型
2)String类型
3)Class类型
4)枚举类型
5)注解类型
6)前五种类型的数组
2)数组类型的属性:
如:int[]arrayArr() default {1,2,3};//可不定义默认值
应用:@MyAnnotation(arrayArr={2,3,4}) //可重新赋值
Note:若数组属性中只有一个元素(或重新赋值为一个元素),这时属性值部分可省略大括号。
3)枚举类型的属性:
假设定义了一个枚举类TrafficLamp,它是EnumTest的内部类,其值是交通灯的三色。
定义:EnumTest.TrafficLamplamp();
应用:@MyAnnotation(lamp=EnumTestTrafficLamp.GREEN)
4)注解类型的属性:
假定有个注解类:MetaAnnotation,其中定义了一个属性:String value()
定义:MetaAnnotation annotation() default @MetaAnnotation(”xxx”);
应用:@MyAnnotation(annotation=@MetaAnnotation(”yyy”))//重新赋值
可以认为上面这个@MyAnnotation是MyAnnotaion类的一个实例对象,同样的道理,可以认为上面这个@MetaAnnotation是MetaAnnotation类的一个实例对象,调用代码如下:
MetaAnnotationma =MyAnnotation.annotation();
System.out.println(ma.value());
5)Class类型的属性:
定义:Class cls();
应用:@MyAnnotation(cls=AnnotationDemo.class)
Note:这里的.class必须是已定义的类,或是已有的字节码对象。
6)注解的详细语法可通过查看java语言规范了解即javaLanguage Specification
示例:package annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)//元注释
@Target({ElementType.METHOD,ElementType.TYPE})//元注解,指定使用范围
//注解类
public @interface MyAnnotation {
String color() default "red" ;
String value();
//数组
int[] arr() default {1,2,3};
//枚举
EnumTest.TrafficLamp lamp() default EnumTest.TrafficLamp.GREEN;
//注解类
MetaAnnotation annotation() default @MetaAnnotation("heima");
//Class类
Class clazz() default System.class;
}
import java.lang.reflect.Method;
//注解类的应用,给属性赋值或者重新赋值
@MyAnnotation(lamp=EnumTest.TrafficLamp.YELLOW,value="heima",
clazz=AnnotationDemo.class,annotation=@MetaAnnotation("itheima"))
//应用类
public class AnnotationDemo {
@SuppressWarnings("deprecation")//此注解用于抑制过时信息的提示
@MyAnnotation("Method")//自定义注解应用在方法上
public static void main(String[] args) throws NoSuchMethodException, SecurityException {
System.runFinalizersOnExit(true); //这是一个过时了的方法 ,如果没有注解就会有警告提示
//判断此类是否有MyAnnotation注解
if (AnnotationDemo.class.isAnnotationPresent(MyAnnotation.class)) {
//如果有,则获取该注解
MyAnnotation annotation =AnnotationDemo.class.getAnnotation(MyAnnotation.class);
System.out.println(annotation);//@cn.itheima.Demo.MyAnnotation()
System.out.println(annotation.color());//red
System.out.println(annotation.value());//heima
System.out.println(annotation.arr().length);//3
System.out.println(annotation.lamp());//YEllOW
System.out.println(annotation.annotation().value());//itheima
System.out.println(annotation.clazz());//class cn.itheima.demo.AnnotationDemo
}
//获取方法上的注解
Method mainMethod=AnnotationDemo.class.getMethod("main",String[].class);
MyAnnotation annotationMethod=(MyAnnotation) mainMethod.getAnnotation(MetaAnnotation.class);
SuppressWarnings sw=mainMethod.getAnnotation(SuppressWarnings.class);
System.out.println(sw);//null
System.out.println(annotationMethod);//null
}
}
四、增强for循环1、格式:
for( 数据类型 变量名 : 被遍历的集合(collection)或者数组 ) { 执行语句 }
2、说明:
a、对集合进行遍历。只能获取集合元素。但是不能对集合进行操作。可以看作是迭代器的简写形式。
b、迭代器除了遍历,还可以进行remove集合中元素的动作。如果使用ListIterator,还可以在遍历过程中对集合进行增删改查的操作。
3、传统for循环和高增强or循环的区别:
高级for有一个局限性。必须有被遍历的目标(集合或数组)。
传统for遍历数组时有索引。
Note:
1)增强for循环所迭代的是集合必须是数组或者是实现了Iterator接口的集合类。
2)在遍历数组的时候要使用定义的角标,还是建议使用传统for循环。
3)变量类型前可加修饰符,如final(可被局部内部类访问到)。
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class ForInhance {
public static void main(String[] args) {
// 传统for与高级for遍历数组
String[] arr = { "www", "itheima", "com" };
for (int x = 0; x < arr.length; x++) {
System.out.println(arr[x]);
}
for (String i : arr) {
System.out.println("i:" + i);
}
// 定义一个HashMap集合
HashMap<Integer, String> hm = new HashMap<Integer, String>();
hm.put(1, "www");
hm.put(2, "itheima");
hm.put(3, "com");
// keySet取出方式的高级for遍历
Set<Integer> keySet = hm.keySet();
for (Integer i : keySet) {
System.out.println(i + "::" + hm.get(i));
}
// entrySet取出方式的高级for遍历
for (Map.Entry<Integer, String> me : hm.entrySet()) {
System.out.println(me.getKey() + "------" + me.getValue());
}
}
}
五、可变参数
1、概述:如果一个方法在参数列表中传入多个参数,个数不确定,那么每次都要复写该方法。这时可以用数组作为形式参数。但是在传入时,每次都需要定义一个数组对象,作为实际参数。在JDK1.5版本开始,就提供了一个新特性:可变参数(VariableParameter)。
2、格式:用"…"这三个点表示,且这三个点位于变量类型和变量名之间,前后有无空格皆可。
Note:
1) 可变参数其实就是数组参数的简写形式,不用每一次都手动的建立数组对象,只要将要操作的元素作为参数传递即可,隐式将这些参数封装成了数组。
2) 在使用时注意:可变参数一定要定义在参数列表的最后面。
public class VariableParameterDemo {
public static void main(String[] args) {
int retVal = add(108, 109, 110, 111, 112, 113);
System.out.println("总和为:" + retVal);
}
// ...代表多个参数
public static int add(int x, int... args) {
int sum = x;
// 增强for循环
for (int arg : args) {
sum += arg;
}
return sum;
}
}
六、静态导入1、写法:
import staticjava.util.Arrays.*; //导入的是Arrays这个类中的所以静态成员。
import staticjava.lang.System.*; //导入了Ssytem类中所以静态成员。
没加static导入的是类,加上static导入的全是某一个类中所以的静态成员,这样写在调用该类的静态方法时可以不用再写类名。如:Arrays.sort(数组);就可以直接写sort(数组);
Note:
1)当导入的两个类中有同名成员时,需要在成员前加上相应的类名。
2)当类名重名时,需要指定具体的包名。当方法重名时,指定具体所属的对象或者类。
示例:import java.util.*;
import static java.util.Arrays.*;
import static java.lang.System.*;
class StaticImport // extends Object
{
public static void main(String[] args) {
out.println("itheima");// 打印输出时就可以直接省略书写System.
int[] arr = { 3, 1, 5 };
sort(arr);// 使用Arrays工具类的方法sort时就可以省略书写Array.
int index = binarySearch(arr, 1);// 半分查找也是一样可以省略
out.println("Index=" + index);
// 当没有指定继承时,所以类默认继承了Object,
// 因为toString方法都具备,所以为了区分,必须写上具体调用者
out.println(Arrays.toString(arr));
}
}
七、自动拆箱、自动装箱
概述:说到这个特性之前得说说包装类——基本数据类型所定义的应引类型称为基本数据类型的包装类。JDK1.5之前,基本数据类型与包装类之间的转换都得依靠指定的方法手动转换的,而JDK1.5之后,这些操作在使用的时候自动完成,其中包括自动装箱、自动拆箱。
使用:
1)自动装箱:
eg: Integer num = 1;
2) 自动装箱:
eg: num = num+2;
Note:对于基本数据类型的说明:整数在-128 ~ 127之间的数,包装成Integer类型对象,会存入常量池中的缓存,再创建一个对象的时候,如果其值在这个范围内,就会直接到常量池中寻找,因为这些小数值使用的频率很高,所以缓存到常量池中,被调用时就方便很多。
示例:
public class AutoDemo {
public static void main(String[] args) {
// 定义一个Integer类的书numa
Integer numa = 1;//自定装箱
int numc = numa + 1;//自动拆箱
System.out.println(numc + "---" + numa.getClass().getName());
// 定义第二个Integer数numb,所赋的值与numa相等
Integer numb = 1;
// 判断numa是否等于numb
System.out.println("numa == numb\t" + (numa == numb));// true
// 再的定义a、b,数字相等但都在-127~128范围外
Integer a = 128;
Integer b = 128;
// 判断两个数是否相等
System.out.println("a == b\t" + (a == b));// false
}
}
以上所述即JDK1.5主要的新特性,预知更多新特性可Google详解,上述仅代表个人观点,如有出入请谅解。