1.JavaBean
Java中提供了一套API用来访问某个属性的getter/setter方法,通过这些API可以使你不需要了解这个规则,这些API存放于包java.beans中。
一般的做法是通过类Introspector来获取某个对象的BeanInfo信息,然后通过BeanInfo来获取属性的描述器(PropertyDescriptor),通过这个属性描述器就可以获取某个属性对应的getter/setter方法,然后我们就可以通过反射机制来调用这些方法。
JavaBean的属性是根据其中的Setter和getter方法类确定的。
JavaBean的复杂内省操作
public class IntrospectorTest {
public static void main(String[] args) throws Exception {
Number n = new Number(2, 5);
String propertyName = "x";//设置属性名
Object retVal = getProperty(n, propertyName);//获取属性值
System.out.println(retVal);
Object retVal2 = getProperty2(n, propertyName);
System.out.println(retVal2);
Object x=10;
setProperty(n, propertyName, x);//设置属性值
System.out.println(n.getX());
}
//通过BeanInfo方法获取
private static Object getProperty2(Object obj, String propertyName)
throws IntrospectionException, IllegalAccessException,
InvocationTargetException {
BeanInfo beanInfo=Introspector.getBeanInfo(obj.getClass());//获beaninfo对象
PropertyDescriptor[] pds=beanInfo.getPropertyDescriptors();//获取属性集合
Object retVal2=null;
for(PropertyDescriptor pd:pds){
//遍历属性,获取指定属性
if(pd.getName().equals(propertyName)){
Method methodGetX=pd.getReadMethod();
retVal2=methodGetX.invoke(obj);
break;
}
}
return retVal2;
}
private static void setProperty(Object obj, String propertyName, Object x)
throws IntrospectionException, IllegalAccessException,
InvocationTargetException {
PropertyDescriptor pd2 = new PropertyDescriptor(propertyName, obj
.getClass());//建立描述器对象
Method methodSetX=pd2.getWriteMethod();//获取写入属性值方法
methodSetX.invoke(obj,x);
}
private static Object getProperty(Object obj, String propertyName)
throws IntrospectionException, IllegalAccessException,
InvocationTargetException {
PropertyDescriptor pd = new PropertyDescriptor(propertyName, obj
.getClass());//建立描述器对象
Method methodGetX = pd.getReadMethod();//通过描述器对象获取读取属性值方法
Object retVal = methodGetX.invoke(obj);//通过方法获取属性值
return retVal;
}
}
使用BeanUtils工具包操作JavaBean
//BeanUtils是义字符串形式对JavaBean进行操作
System.out.println(BeanUtils.getProperty(n, propertyName));//属性是String型
BeanUtils.setProperty(n, propertyName, "11");//设置属性值
BeanUtils.setProperty(n, "birthday.time", "111");//对象n里面有个birthday对象,birthday对象有time属性
//JavaBean支持延级操作
System.out.println(BeanUtils.getProperty(n, "birthday.time"));//获取属性值
//PropertyUtils以属性本身类型对JavaBean进行操作
PropertyUtils.setProperty(n, propertyName, 9);//设置的属性值是int型,与JavaBean本身属性类型一致
PropertyUtils.getProperty(n,"x");
2.注解
注解相当于一种标记,在程序中加了注解就等于打上了某种标价,javac编译器,开发工具和其他程序就可以用反射来了解你的类及各种元素有无何种标记,就去完成相应的任务,标记可以加在包,类,字段,方法,方法的参数以及局部变量上.
JDK1.5的注解类:
@Deprecated:已过时
用 @Deprecated 注释的程序元素,不鼓励程序员使用这样的元素,通常是因为它很危险或存在更好的选择。在使用不被赞成的程序元素或在不被赞成的代码中执行重写时,编译器会发出警告。
@Override:复写父类方法
表示一个方法声明打算重写超类中的另一个方法声明。如果方法利用此注释类型进行注解但没有重写超类方法,则编译器会生成一条错误消息。
@SuppressWarnings:压缩警告
指示应该在注释元素(以及包含在该注释元素中的所有程序元素)中取消显示指定的编译器警告。如果要在特定的方法中取消显示某个警告,则应该注释该方法而不是注释它的类。
注解的应用结构图
B.class.isAnnotionPresent(A.class):返回值是boolean型,如果B类中存在A类型注解,则返回true
A a=B.class.getAnnotion(A.class):如果B类中有A类型的注释,则返回该注释,否则返回null
自定义注解及其应用
定义一个最简单的注解:public @interface MyAnnotation{}
把他加在某个类上:@MyAnnotation public class AnnotationTest{}
RetentionPolicy.SOURCE,RetentionPolicy.CLASS,RetentionPolicy.RUNTIME分别对应:java源文件,class文件,内存中的字节码
Target:指示注释类型所适用的程序元素的种类。如果注释类型声明中不存在 Target 元注释,则声明的类型可以用在任一程序元素上。如果存在这样的元注释,则编译器强制实施指定的使用限制。
public class AnnotationTest {
public static void main(String[] args) {
//检查类里是否有注解
if(AnnotationTest.class.isAnnotationPresent(Annotation.class)){
Annotation at=(Annotation)AnnotationTest.class.getAnnotation(Annotation.class);//获取注解对象
System.out.println(at);
}
}
}
@Retention(RetentionPolicy.RUNTIME)//注解的生命周期
@Target({ElementType.METHOD,ElementType.TYPE})//为什么是type而不是class,type更精准,包括class,enum等
public @interface Annotation {}
为注解增加高级属性
注解的返回值类型可以是基本数据类型,字符串类型,class类型,枚举类型,注解类型,及前面这些类型的数组
数组类型的属性 int[] arr() default{1,2,3}
@MyAnnotation(arr={2,3,4}),如果属性只有一个元素,属性值可以省略大括号
枚举类型的属性 EnumTest.TrafficLamp lamp()
@MyAnnotation(lamp=EnuTest.TrafficLamp.GREEN)
注解类型的属性 MetaAnnotation annotationAttr() default @MetaAnnotation(“xxx”)
@MyAnnotation(annotationAttr=@MetaAnnotation(“yyy”))
例子:
@Annotation(color = "red" ,value="abc",arr={2,3,4},annotation2=@MetaAnotation("yyy"))
public class AnnotationTest {
@Annotation("xyz")//value属性在单独存在的情况下可以直接写"abc"
public static void main(String[] args) {
//检查类里是否有注解
if(AnnotationTest.class.isAnnotationPresent(Annotation.class)){
Annotation at=(Annotation)AnnotationTest.class.getAnnotation(Annotation.class);//获取注解对象
System.out.println(at.color());
System.out.println(at.value());
System.out.println(at.arr().length);
System.out.println(at.lamp().nextLamp().name());
System.out.println(at.annotation2());
}
}
}
@Retention(RetentionPolicy.RUNTIME)//注解的生命周期
@Target({ElementType.METHOD,ElementType.TYPE})//为什么是type而不是class?type更精准,包括class,enum等
public @interface Annotation {
String color() default "blue"; //设置属性的默认值
String value();
int[] arr() default {1,2};//设置数组类型属性
//EnumTest.TrafficLamp lamp() default EnumTest.TrafficLamp.RED;//设置枚举类型属性
MetaAnotation annotation2() default @MetaAnotation("xxx");//设置注解类型属性
}
public @interface MetaAnotation {
String value();
}
3.泛型
泛型是提供给javac编译器使用的,可以限定集合中的输入类型,让编译器挡住源程序中的非法输入,编译器编译带类型说明的集合时会去除掉”类型”信息,使程序运行效率不受影响,对于参数化的泛型类型,getClass()方法的返回值和原始类型完全一样.由于编译生成的字节码会去掉泛型的类型信息,只要能跳过编译器,就可以往某个泛型集合中加入其它类型的数据.
import java.util.ArrayList;
public class GenericTest {
public static void main(String[] args) throws Exception{
ArrayList<String> al=new ArrayList<String>();
ArrayList<Integer> al2=new ArrayList<Integer>();
System.out.println(al2.getClass()==al.getClass());//true,对于参数化的泛型类型,getClass()方法的返回值和原始类型完全一样
al2.getClass().getMethod("add", Object.class).invoke(al2, "abc");
System.out.println(al2.get(0));//abc,跳过了编译器
}
}
ArrayList<E>称为泛型类型,E为类型变量或类型参数
Integer为类型参数的实例或实际类型参数,ArrayList为原始类型
注意:参数化类型与原始类型具有兼容性,参数化类型不考虑参数的继承关系,在创建数组实例时,数组的元素不能使用参数化的类型.
泛型通配符的扩展应用
泛型中的?通配符:代表任意类型,任意类型传给他都可以接收
问题:定义一个方法,该方法用于打印出任意参数化类型的集合中的所有数据
public static void printCollection(Collection<?>collection){
//collection.add("abc");会报错,不确定是什么类型
System.out.println(collection.size());//size与类型无关,任意类型集合都具有size方法
for(Object obj:collection){
System.out.println(obj);
}
}
总结:使用?通配符可以引用其他各种参数化的类型,?通配符定义的主要作用是作引用,可以调用参与参数化无关的方法,不能调用与参数化有关的方法
?通配符的扩展
限定通配符的上边界 Vector<? extend Number>x=new Vector<Integer>();//?表示任意Number及Number的子类
限定通配符的下边界 Vector<? super Integer>x=new Vector<Byte>();//?表示任意Integer及Integer的父类
HashMap<String,Integer> hs=new HashMap<String,Integer>();//建立HashMap集合对象
hs.put("zxx",38);//添加元素
hs.put("lhm",45);
Set<Map.Entry<String,Integer>> entrySet=hs.entrySet();//建立键值对关系
//遍历键值对关系
for(Map.Entry<String, Integer> entry:entrySet){
System.out.println(entry.getKey());
System.out.println(entry.getValue());
}
自定义泛型方法及其应用
1.用于放置泛型类型参数的尖括号应在紧邻返回值类型之前,通常使用单个大写字母
2.只有引用数据类型才可作为泛型方法的实际参数
3.除了在应用泛型时可以使用extend限定符,在定义泛型时也可以使用
4.普通方法、构造方法和静态方法中都可以使用泛型
5.可以用类型变量表示异常,称为参数化的异常,可以用于方法的throws列表中,但是不能用于catch子句中
6.在泛型中可以同时有多个类型参数,在定义他们的尖括号中用逗号隔开
private static<T> void swap(T arr[],int x,int y){
T temp=arr[x];
arr[x]=arr[y];
arr[y]=temp;}
注意:使用swap(new int{1,2,4},1,2)无法调用上述方法,因为编译器不会对new int{}中的int进行拆箱和装箱,new int{}本身已是一个对象
练习:
1.编写一个泛型方法,自动将Object类型的对象转换成其他类型
private static <T> T autoConvert(Object obj){
return (T)obj;
}
2.定义一个方法,可以将任意类型的数组的所有元素填充为相应类型的某个对象
private static <T> void fillArray(T[] a,T obj){
for(int x=0;x<a.length;x++){
a[x]=obj;
}
}
参数类型的推断规则
1.当某个类型变量只在整个参数列表中的所有参数和返回值中一处被使用,根据调用方法使传递的参数类型或返回值类型来决定泛型参数的类型
swap(new String[3],3,4)→static<T> void wap(T[] a,3,4)
2.当某个类型变量在整个参数列表中的所有所有参数和返回值中多出被使用,且调用方法时这多处实际应用类型都对应同一种类型,参数类型即为这种类型
add(2,3)→static <T> T add(T a,T b)
3.当某个类型变量在整个参数列表中的所有所有参数和返回值中多出被使用,且这多处实际应用类型对应了不同的类型,并且没有返回值,此时取多个数据类型最大交集
file(new Integer[3],3,5)→static <T> void fill(T[]a,T v)
4.当某个类型变量在整个参数列表中的所有所有参数和返回值中多出被使用,且这多处实际应用类型对应了不同的类型,并且有返回值,优先考虑返回值类型
5.参数类型的类型推断具有传递性
自定义泛型类
类的实例对象中的多处都要用到同一个泛型,使用自定义泛型类
public class GenericDao<T>{
public T field1;
public void save(T obj){}
public T getById(int id){}
}
注意:
1.在对泛型进行参数化时,类型参数的实例必须是引用类型,不能使基本类型
2.当一个变量被声明为泛型时,只能被实例变量和方法调用,而不能被静态变量和静态方法调用,因为静态成员是被所有参数化的类所共享的
问题:通过反射获得泛型的参数化类型
class GenericTest{
public static void main(String[] args){
Method applyMethod=GenericTest.class.getMethod("applyVector", Vector.class);
Type[] types=applyMethod.getGenericParameterTypes();
ParameterizedType pType=(ParameterizedType)types[0];//参数化类型
System.out.println(pType.getRawType());//获得实际类型
System.out.println(pType.getActualTypeArguments()[0]);//获得原始类型
}
public static void applyVector(Vector<Date> v1){}}
学习感悟
不管是JavaBean还是注解还是泛型,之前都有接触,之前面向对象中所建立对象类中就经常有set和get方法,将成员变量定义为私有,对外提供set,get方法设置和获取属性值.注解之前接触最多的就是@override,复写方法时经常会出现的提示.泛型在讲集合的时候学习过,对于操作数据提供了方便.