黑马程序员__JAVA基础加强__高新技术(三)

时间:2021-05-26 19:42:42

------------android培训java培训、java学习型技术博客、期待与您交流! ------------

1.内省

    JavaBean

    JavaBean是一种特殊的Java类,主要用于传递数据信息,这种java类中的方法主要用于访问私有的字段,且方法名符合某种命名规则。

    如果要在两个模块之间传递多个信息,可以将这些信息封装到一个JavaBean中,这种JavaBean的实例对象通常称之为值对象(Value Object,简称VO)。这些信息在类中用私有字段来存储,如果读取或设置这些字段的值,则需要通过一些相应的方法来访问,大家觉得这些方法的名称叫什么好呢?JavaBean的属性是根据其中的setter和getter方法来确定的,而不是根据其中的成员变量。如果方法名为setId,中文意思即为设置id,至于你把它存到哪个变量上,用管吗?如果方法名为getId,中文意思即为获取id,至于你从哪个变量上取,用管吗?去掉set前缀,剩余部分就是属性名,如果剩余部分的第二个字母是小写的,则把剩余部分的首字母改成小的。

    如:

        setId()的属性名id

        isLast()的属性名last

        setCPU的属性名是什么?CPU

        getUPS的属性名是什么?UPS 

    总之,一个类被当作javaBean使用时,JavaBean的属性是根据方法名推断出来的,它根本看不到java类内部的成员变量。

    一个符合JavaBean特点的类可以当作普通类一样进行使用,但把它当JavaBean用肯定需要带来一些额外的好处,我们才会去了解和应用JavaBean!好处如下:

    在Java EE开发中,经常要使用到JavaBean。很多环境就要求按JavaBean方式进行操作,别人都这么用和要求这么做,那你就没什么挑选的余地!
    JDK中提供了对JavaBean进行操作的一些API,这套API就称为内省。如果要你自己去通过getX方法来访问私有的x,怎么做,有一定难度吧?用内省这套api操作JavaBean比用普通类的方式更方便。

代码示例:

ReflectPoint pt1 = new ReflectPiont(5,5);//新建一个JavaBean类的对象实例

String propertyName = "x";//用户指定的一个类中的属性

//新建一个PropertyDescriptor类的实例,将指定属性与的指定类的Class文件传入
PropertyDescriptor pd = new PropertyDescriptor(propertyName,pt1.getClass));

Method methodGetX = pd.getReadMethod();//得到里面getX()方法
Object retVal = methodGetX.invoke(pt1);//调用上面获取到的方法
System.out.println(retVal);//打印字符串

Method methodSetX = pd.getWriteMethod();//得到里面setX()方法
methodSteX.invole(pt1,8);//调用方法
System.out.println(pt1.getX());//打印设置之后的值

稍等复杂点的方法:

BeanInfo beanInfo =  Introspector.getBeanInfo(pt1.getClass());//得到这个JavaBean的信息
PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();//得到里面所有的属性信息
Object retVal = null;
for(PropertyDescriptor pd : pds)//遍历
{
if(pd.getName().equals(propertyName))
{
Method methodGetX = pd.getReadMethod();//得到成员变量的get方法
retVal = methodGetX.invoke(pt1);//调用方法
break;
}
}
return retVal;


    Beanutils工具包

    Beanutils工具包是由apache开发,方便于对JavaBean的进行一些操作, 在使用Beanutils工具包的时候,记得导入logging包,不然会运行出错

    方法:

        BeanUtils.getProperty()
        BeanUtils.setProperty(pt1,"x","9");set里的X是int  但是我们用Beanutils设置值的时候是用String类型设置进去的,返回也是String类型的。

 

    PropertiesUtils类
    PropertiesUtils.setProperty(pt1,"x",9);
        BeanUtils是以字符串的形式对javabean进行操作,
        而PropertiesUtils是以属性本身的类型进行操作。

代码示例:

//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.注解 

    Annotation(注解)是JDK1.5及以后版本引入的。它可以用于创建文档,跟踪代码中的依赖性,甚至执行基本编译
时检查。注解是以‘@注解名’在代码中存在的,根据注解参数的个数,我们可以将注解分为:标记注解、单值注解、完整注解三类。它们都不会直接影响到程序的语义,只是作为注解(标识)存在,我们可以通过反射机制编程实现对这些元数据(用来描述数据的数据)的访问。另外,你可以在编译时选择代码里的注解是否只存在于源代码级,或者它也能在class文件中出现。

    注解相当于一种标记,在程序中加了注解就等于为程序打上了某种标记,没加,则等于没有某种标记,以后,javac编译器,开发工具和其他程序可以用反射来了解你的类及各种元素上有无何种标记,看你有什么标记,就去干相应的事。标记可以加在包,类,字段,方法,方法的参数以及局部变量上。(JDK1.5新特性)

    @Deprecated:已过时
        用 @Deprecated 注释的程序元素,不鼓励程序员使用这样的元素,通常是因为它很危险或存在更好的选择。在使用不被赞成的程序元素或在不被赞成的代码中执行重写时,编译器会发出警告。
    @Override:复写父类方法
        表示一个方法声明打算重写超类中的另一个方法声明。如果方法利用此注释类型进行注解但没有重写超类方法,则编译器会生成一条错误消息。
    @SuppressWarnings:压缩警告
        指示应该在注释元素(以及包含在该注释元素中的所有程序元素)中取消显示指定的编译器警告。如果要在特定的方法中取消显示某个警告,则应该注释该方法而不是注释它的类。

    元注解

黑马程序员__JAVA基础加强__高新技术(三)

    @Target 

    表示该注解用于什么地方,可能的 ElemenetType 参数包括:

    ElemenetType.CONSTRUCTOR 构造器声明
    ElemenetType.FIELD 域声明(包括 enum 实例)
    ElemenetType.LOCAL_VARIABLE 局部变量声明
    ElemenetType.METHOD 方法声明2013/5/12
    ElemenetType.PACKAGE 包声明
    ElemenetType.PARAMETER 参数声明
    ElemenetType.TYPE 类,接口(包括注解类型)或enum声明

 

    @Retention

    表示在什么级别保存该注解信息。可选的 RetentionPolicy 参数包括:
    RetentionPolicy.SOURCE 注解将被编译器丢弃
    RetentionPolicy.CLASS 注解在class文件中可用,但会被VM丢弃
    RetentionPolicy.RUNTIME VM将在运行期也保留注释,因此可以通过反射机制读取注解的信息。

    @Documented:将此注解包含在javadoc中


    @Inherited:允许子类继承父类中的注解

    为注解增加基本属性 

    什么是注解的属性

        一个注解相当于一个胸牌,如果你胸前贴了胸牌,就是传智播客的学生,否则,就不是。如果还想区分出是传智播客哪个班的学生,这时候可以为胸牌在增加一个属性来进行区分。加了属性的标记效果为:@MyAnnotation(color="red")

    定义基本类型的属性和应用属性:

        在注解类中增加String color();

        @MyAnnotation(color="red")

    用反射方式获得注解对应的实例对象后,再通过该对象调用属性对应的方法

        MyAnnotation a = (MyAnnotation)AnnotationTest.class.getAnnotation(MyAnnotation.class);

        System.out.println(a.color());

        可以认为上面这个@MyAnnotation是MyAnnotaion类的一个实例对象

    为属性指定缺省值:

        String color() default "yellow";

    value属性:
        String value() default "zxx";

        如果注解中有一个名称为value的属性,且你只想设置value属性(即其他属性都采用默认值或者你只有一个value属性),那么可以省略value=部分,例如:@MyAnnotation("lhm")。

代码示例:

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 {}

    为注解增加高级属性

    数组类型的属性

        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(annotationAttr = @MetaAnotaion("yyy"))

        可以认为上面这个@MyAnnotation是MyAnnotaion类的一个实例对象,同样的道理,可以认为上面这个@MetaAnnotation是MetaAnnotation类的一个实例对象,调用代码如下:

         MetaAnnotation ma =  myAnnotation.annotationAttr();

        System.out.println(ma.value());

    注解的详细语法可以通过看java语言规范了解,即看java的language specification。

代码示例:

@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()方法的返回值和原始类型完全一样。由于编译生成的字节码会去掉泛型的类型信息,只要能跳过编译器,就可以往某个泛型集合中加入其它类型的数据,例如,用反射得到集合,再调用其add方法即可。

    Jdk1.5以前的集合类中存在什么问题

ArrayList collection = new ArrayList();

collection.add(1);
collection.add(1L);
collection.add("abc");

int i = (Integer) collection.get(1);//编译要强制类型转换且运行时出错!

    Jdk1.5的集合类希望你在定义集合时,明确表示你要向集合中装哪种类型的数据,无法加入指定类型以外的数据

ArrayList<Integer> collection2 = new ArrayList<Integer>();

collection2.add(1);

/*collection2.add(1L);
collection2.add(“abc”);*///这两行代码编译时就报告了语法错误

int i2 = collection2.get(0);//不需要再进行类型转换

    测试在运行时期会被去掉泛型的“类型”信息

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");//运用反射调用add方法向里面添加String类型的数据
System.out.println(al2.get(0));//abc,跳过了编译器
}
}

 

    了解泛型(genericity)

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

    编译器不允许创建泛型变量的数组。即在创建数组实例时,数组的元素不能使用参数化的类型,例如,下面语句有错误:
        Vector<Integer> vectorList[] = new Vector<Integer>[10];
    思考题:下面的代码会报错误吗?
        Vector v1 = new Vector<String>();
        Vector<Object> v = v1;
        不会报错,因为编译器只按照语法进行检查

    泛型中的?通配符

    问题:定义一个方法,该方法用于打印出任意参数化类型的集合中的所有数据,该方法如何定义呢?

    错误方式:

public static void printCollection(Collection<Object> cols) 
{
for(Object obj:cols)
{
System.out.println(obj);
}
/* cols.add("string");//没错

cols = new HashSet<Date>();//会报告错误!*/
}

    正确方式:

public static void printCollection(Collection<?> cols) 
{
for(Object obj:cols) {
System.out.println(obj);
}
//cols.add("string");//错误,因为它不知自己未来匹配就一定是String
cols.size();//没错,此方法与类型参数没有关系
cols = new HashSet<Date>();
}

   总结:使用?通配符可以引用其他各种参数化的类型,?通配符定义的变量主要用作引用,可以调用与参数化无关的方法,不能调用与参数化有关的方法。

    ?通配符的扩展

    限定通配符的上边界  Vector<? extend Number>x=new Vector<Integer>();//?表示任意Number及Number的子类

    限定通配符的下边界  Vector<? super Integer>x=new Vector<Byte>();//?表示任意Integer及Integer的父类

 

    提示:限定通配符总是包括自己。

        ?只能用作引用,不能用它去给其他变量赋值
        Vector<? extends Number> y = new Vector<Integer>();

        Vector<Number> x = y;

       上面的代码错误,原理与Vector<Object > x11 = new Vector<String>();相似,只能通过强制类型转换方式来赋值。

    综合案例

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());
}


个人总结

    JavaBean对我们开发者的作用就是能在不更改源代码的情况下,直接调用用户传入指定的方法,利用内省这套方法,更简单的对JavaVBean的操作。泛型是JDK1.5之后出现的新特性,是直接在编译时期检测某个集合或者其他容器中是否添加有非法的元素,将运行时期的错误转到编译时期,提高代码的编写速度,泛型只要掌握最基本的用法就完全足够我们在开发的时候用了。