`

黑马程序员Java培训和Android培训Java技术三

 
阅读更多
黑马程序员
三十一
对javabean复杂内省操作
关键代码
BeanInfo beanInfo=Introspector.getBeanInfo(pt1.getClass());
PropertyDescriptor[] pds=beanInfo.getPropertyDescriptors();
Object retVal=null;
for(PropertyDescriptor pd:pds)
{
if(pd.getName().equals(propertyName))
{
Method methodGetX=pd.getReadMethod();
retVal=methodGetX.invoke(pt1);
break;
}

}

三十二
Beanutils工具包
演示用eclipse如何加入jar包,先只是引入beanutils包,等程序运行出错后再引入logging包。
在前面内省例子的基础上,用BeanUtils类先get原来设置好的属性,再将其set为一个新值。
---get属性时返回的结果为字符串,set属性时可以接收任意类型的对象,通常使用字符串。
用PropertyUtils类先get原来设置好的属性,再将其set为一个新值。
---get属性时返回的结果为该属性本身的类型,set属性时只接受该属性本来的类型。
BeanUtils.getProperty();
BeanUtils.set();
BeanUtils支持对于属性的级联操作。java7中BeanUtils可以设置Map对象。
Map对象。JavaBean与Map可以相互转换,因为Map对象的的Key相当于属性。
PropertyUtils对于数据类型敏感。

三十三
Annotation注解:就是告诉编译器某些情况该如何处理。一个注解就是一个类。
存在于java.lang包中。Deprecated Override SuppressWarning

先通过@SuppressWarning的应用让大家直观地了解注解,然后引出@Deprecated @Override
总结:注解相当于一种标记,加了注解就等于给程序打上了某种标记,没加,则等于没有某种标记,以后,javac编译器,开发工具和其他程序可以用反射来了解你的类及各种元素上有无何种标记,看你有什么标记,就去干相应的事。标记可以加在包中,类,字段,方法,方法的参数以及局部变量上。

三十四
isAnnotation()

自定义注解及其应用
定义一个最简单的注解:public @interface MyAnnotation{}
把它加在某个类上;@MyAnnotation public class AnnotationTest{}
用反射进行测试AnnotationTest的定义上是否有@MyAnnotation
根据发射测试的问题,引出@Retention元注解的讲解(即注解的注解),其三种取值:RetentionPolicy.SOURCE,RetentionPolicy.CLASS,RetentionPolicy.RUNTIME;分别对应:java源文件-->class文件-->内存中的字节码。

@target注解表明某一注解只能应用于某个特定的类型上。

三十五
为注解增加基本属性
什么是注解的属性
一个注解相当于一个胸牌,如果你胸前贴了胸牌,就是传智博客的学生,否则,就不是。如果还想区分出是传智博客哪个班的学生,这时候可以为胸牌增加一个属性来进行区分。加了属性的标记效果为:@MyAnnotation(color="red")
定义基本类型的属性和应用属性
在注解中增加String.color();
@MyAnnotation(color="red")
用反射的方式获得注解对应的实例对象后,再通过该对象调用属性对应的方法
MyAnnotation a=(MyAnnotation)AnnotationTest.class.getAnnotation(MyAnnotation.class);
System.out.println(a.color());
可以认为上面这个@MyAnnotation是MyAnnotation类的一个实例对象
为属性指定缺省值:
String color() default"yellow";
value属性:
String value() default"zxx";
如果注解中有一个名称为value的属性,且你只想设置value属性(即其他属性都采用默认值或者你只有一个value属性),那么可以省略value=部分,例如:@MyAnnotation("ihm")。




为注解增加高级属性
数组类型的属性
---int[] arrayAttr() default {1,2,3}
---@MyAnnotation(arrayAttr={2,3,4})
---如果数组属性中只有一个元素,这时候属性值部分可以省略大括号。
枚举的属性
---EnumTest.TrafficLamp lamp();
---@MyAnnotation(lamp=EnumTest.TrafficLamp.GREEN)
注解类型的属性:
---MetaAnnotation annotationAttr() default @MetaAnnotation("xxxx");
---@MyAnnotation(annotionAttr=@MetaAnnotation("yyy"))
可以认为上面这个@MyAnnotation是MyAnnotion类的一个实例对象,t同样的道理,可以认为上面这个@MetaAnnotation是MetaAnnotation类的一个实例对象,调用代码如下:
MetaAnnotation ma=myAnnotion.annotationAttr();

注意:枚举和注解都是特殊的类,不能用new创建它们的实例对象,创建枚举的实例对象就是在其中增加元素。在程序中创建一个注解的实例对象,直接用@放上一个标记即可。
MetaAnnotation注解的定义:
public @interface MetaAnnotion{
   String value();
}
System.out.println(ma.value());
注解的详细语法可以通过看java语言规范了解,即java的Language Specification

我们可以尝试一下注释的Class类型的属性。

三十六
体验泛型
Jdk1.5以前的集合类中存在的问题
ArrayList collection=new ArrayList();
collection.add(1);
collection.add(1L);//存入一个长整型的数
collection.add("abc");
int i=(Interger)arrayList(1);//编译要强制类型转换且运行时出错!
Jdk 1.5的集合类希望你在定义集合时,明确表示你要向集合中装哪种类型的数据,无法加入指定类型以外的数据
ArrayList<Integer> collection2=new ArrayList<Integer>();
collection2.add(1);
/*collection2.add(1L);
collection2.add("abc");*///这两行代码编译时就报告了语法错误
int i2=collection2.get(0);//不需要再进行类型转换
泛型是提供给javac编译器使用的,可以限定集合中的输入类型,让编译器挡住源程序中的非法输入,编译器编译带类型说明的集合时会去掉"类型"信息,使程序运行效率不受影响,对于参数化的泛型类型,getClass()方法的返回值和原始类型完全一样。由于编译生成的字节码会去掉泛型的类型信息,只要能跳过编译器,就可以往某个类型集合中加入其他类型的数据,例如,用反射得到集合,再调用其add方法即可。

注意:泛型是jdk1.5的所有新特性中最难深入掌握的部分,不过,我们在实际应用中不能掌握的那么深入,掌握泛型中的一些最基本的内容就差不多了。没有使用泛型,只要是对象,不管是什么类型的对象,都可以存储进同一个集合中。使用泛型集合,可以将一个集合中的元素限定为一个特定类型,集合中只能存储同一个类型的对象,这样更安全;并且当从集合获取一个对象时,编译器也可以知道这个对象的类型,不需要对对象进行强制类型转换,这样更方便。
在jdk1.5中,你可以按原来的方式将各种不同类型的数据装到一个集合中,但编译器会报告unchecked警告。
泛型可以省去类型转换的繁琐。

三十七
了解泛型
ArrayList<E>类定义和ArrayList<Integer>类引用中涉及如下术语:
--整个称为ArrayList<E>泛型类型
--ArrayList<E>中的E称为类型变量或类型参数
--整个ArrayList<Integer>称为参数化的类型
--ArrayList<Integer>中的Integer称为类型参数的实例或实际类型参数
--ArrayList<Integer>中的<>念着typeof
--ArrayList称为原始类型
参数化类型与原始类型的兼容性:
--参数化类型可以引用一个原始类型的对象,编译报告警告,例如,Collection<String> c=new Vector();  //可不可以,不就是编译器一句话的事吗?
--原始类型可以引用一个参数化类型的对象,编译器报告,例如,Collection c=new Vector<String>();  //原来的方法接受一个集合参数,新的类型也要能传进去。
参数化类型不考虑类型参数的继承关系:
--Vector<String> v=new Vector<Object>(); //错误
--Vector<Object> v=new Vector<string>(); //也错误
在创建数组实例时,数组的元素不能使用参数化的类型,例如,下面语句有错误:
Vector<Integer> vector ist[]=new Vector<Integer>[10];
思考题:下面的代码会报错吗?
Vector v1=new Vector<String>();
Vector<Object> v=v1;不报错

泛型中的类型参数严格说明集合中装载的数据类型是什么和加入什么类型的数据,记住:Collection<String>和Collection<Object>是两个没有转换关系的参数化的类型。
假设Vector<String> v=new Vector<Object>;可以的话,那么以后从v中取出的对象当作String用,而v实际指向的对象中可以加入任意类型对象;假设Vector<Object> v=new Vector<String>();可以的话,那么以后可以向v中加入任意的类型对象,而v实际指向的集合中只能装String 类型的对象。

三十八
泛型中的?通配符 代表任意类型 带有通配符的定义的变量不能调用与参数类型有关的方法。
问题:
定义一个方法,该方法用于打印出任意参数化类型集合中的所有数据,该方法如何定义呢?
错误方式:
public static void printCollection(Collection<Object> cols)
{
    for(Object obj:cols)
    {
       System.out.println(obj);
     }
    /*cols.add("String");// 对
      cols=new HashSet<Data>();//会报告错误!*/

}

正确方式:
public static void printCollection(Collection<?> cols)
{
     for(Object obj: cols)
     {     System.out.println(obj);
     }
   //cols.add("String");错误,因为它不知自己未来匹配就一定是String
     cols.size();//没错,此方法与类型参数没有关系
     cols=new Hashset<Date>();
}
总结:
使用?通配符可以引用其他各种参数化的类型,?通配符定义的变量主要用作引用,可以调用与参数化无关的方法,不能调用与参数化有关的方法。

/*Cols<Object>中的Object只是说明Cols<Object>实例对象中的方法接受的参数是Object
Cols<Object>是一种具体类型,new HashSet<Date>也是一种具体类型,两者没有兼容性问题。*/

Collection<?> a可以与任意参数化的类型匹配,但是到底匹配的是什么类型,只有以后才可以知道,所以,a=new ArrayList<Integer>和a=new ArrayList<String>都可以,但是a.add(new Date())或者a.add("abc")都不行。







通配符中的?通配符的扩展
限定通配符的上边界:
正确: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>();
提示:
限定通配符总是包括自己。

我理解的这个参数类型就是对于某种的更高层次抽象,赋值的原则为相关抽象可以引用相关的下一级具体。

三十九
泛型集合类的综合案例
能写出下面的代码即掌握了Java的泛型集合类:
HashMap<String.Integer> hm=new hashMap<String.Integer>();
hm.put("zxx",19);
hm.put("lis",18);

Set<Map.Entry<String.Integer>> mes=hm.entrySet();
for(Map.Entry<String.Integer> me: mes)
{
   System.out.println(me.getKey()+":"+me.getValue());
}


对在jsp页面中也经常要对Set或Map集合进行迭代:
<c:forEach items="${map}" var="entry">
${entry.key}:${entry.value}
</c:forEach>

四十
由C++的模板函数引入自定义泛型
如下函数的结构很相似,仅类型不同:
int add(int x,int y)
{
   return x+y;
}
float add(float x,float y)
{
  return x+y;
}
double add(double x,double y)
{
  return x+y;
}

C++用模板函数解决,只写了一个通用的方法,它可以适应各种类型,示意代码如下:
template<class T>
T add(T x,T y)
  {
     return(T)(x+y);
   }

定义泛型方法
Java的泛型方法没有C++模板函数功能强大,java中的如下代码无法通过编译:
<T> T add(T x,T y)
{
  return(T)(x+y);//return null
}

交换数组中的两个元素的位置的泛型方法语法定义如下:
static<E> void swap(E[] a,int i,int j)
{
   E t=a[i];
   a[i]=a[j];
   a[j]=t;
}

用于放置泛型的类型参数的尖括号应出现在方法的其他所有修饰符之后和在方法的返回类型之前,也就是紧邻返回值之前。按照惯例,参数类型通常用单个大写字母表示。

只有引用类型才能作为泛型方法的实际参数,swap(new int[3],3.5);语句会报告编译错误。

除了在应用泛型时可以使用extends限定符,在定义泛型时也可以使用extends限定符,例如,Class.getAnnotation()方法的定义。并且可以用&来指定多个边界,如<V extends Serializable& cloneable> void method(){}

普通方法,构造函数和静态方法中都可以使用泛型。编译器不允许创建类型变量的数组。

也可以用类型变量表示异常,称为参数化的异常,可以用于方法的throws列表中,但是不能用于catch子句中。

在泛型中可以同时有多个类型参数,在定义它们的尖括号中用逗号分,例如:
public static <K,V>V getValuale(K key){return map.get(key);}



用下面的代码说明对异常如何采用泛型
private static <T extends Exception> sayHello() throws T
{
  try{}
  catch(Exception e){
      throw(T) e;
    }
}

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics