2019.07.27(day22)
内省:
就是自查的意思,本质就是反射,利用反射自省类中的属性和方法
自省的实现方式有两种
方式一:
jdk(jre)中自带的一套自省的类库,类库中包含的是api方法
侧重:属性和属性的值,以及属性所对应的getter和setter方法
方式二:
apache基金会提供的一套公共的自省类库Commons-BeanUtils.jar
注解:
将来的发展趋势
用直接可以替换xml配置和属性文件的配置
用注解的开发效率很高
注解实际就是一个标识,注解所代表的功能是用反射代码实现的
这段反射代码用于解析注解(找到并确定注解的存在)
然后根据注解设定的属性,来决定做什么样的功能
使用注解的步骤:
1.如何定义注解 创建注解
2.把注解应用在对应的目标上 把注解放在什么地方
3.通过反射的api,解析注解 有注解标识的就做对应的功能
1.如何定义注解
//代表此注解可以修饰的目标
@Target({ElementType.TYPE,ElementType.METHOD})
//此注解的保留策略,在运行期间能使用此注解
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
//在注解中可以放注解的属性,注意不是方法
public String value();
public String name() default "abc";
public String[] colors() default {"red","blue"};
}
元注解/源注解:
用于修饰注解的注解,是jdk自带的注解
@Target(ElementType.XXX);//把注解应用在什么样的目标
@Target({ElementType.XXX,ElementType.XXX1});//把注解应用在什么样的目标
@Target(ElementType.METHOD):被修饰的注解能够在什么地方使用
(注解、类/接口、字段、方法、构造方法、参数上..)上面使用。
ElementType:
ANNOTATION_TYPE 注解类型声明
CONSTRUCTOR 构造方法声明
FIELD字段声明(包括枚举常量)
LOCAL_VARIABLE局部变量声明
METHOD 方法声明
PACKAGE 包声明
PARAMETER 参数声明
TYPE 类、接口(包括注释类型)或枚举声明
@Retention(RetentionPolicy.RUNTIME);//注解的保留策略
被它修饰的注解保留域。
编译器 类加载器 JVM
.java-> .class->加载->运行
SOURCE:源码级别,给编译器看的,
编译完之后直接丢弃该种策略的注解。
生成的.class字节码文件中没有该类型的注解
CLASS:给类加载器看的,
在类加载时可以做一系列引导操作。
编译器编译后存在,类加载器加载完之后,
丢弃该保留域的注解。
RUNTIME:给JVM看的,在程序运行的过程中做相关的
操作。编译器编译后保留,
类加载器加载后保留,
在JVM运行时通过反射技术反射使用的是
什么注解,注解属性的值是什么,
根据他们两个的值做对应的操作。
@Documented:自定义的注解如果使用Documented注解,
生成的doc文档上,使用该自定义注解的
地方会显示注解信息;
如果去掉@Documented,
生成的doc文档上将不再有注解信息。
注解中的属性详解:
a.注解定义属性和接口中定义的方法类似,默认是public
public 类型 属性名称();
b.定义属性时,如果没有使用default指定的默认值
使用当前注解必须为该属性赋值
(可以利用该特点,设置必须赋值的属性不要使用default指定默认值)
使用default指定默认之后,该属性在注解使用时可以赋值,也可以不赋值。
c.注解中的属性类型是有要求的
可以是八种基本数据类型,可以是枚举类型、Class类型、String等,
以及以上类型的“一维”数组
比如:String[] likes() default {};
在注解使用时,如果数组类型的属性的值是一个值得话,
赋值时可以把“{}”省略掉。
@Anno1(name="zs",likes="fb")
@Anno1(name={"zs","lisi"},likes={"fb","jc"})
d.存在一个特殊的属性:value
如果只为该属性赋值,value=(可以省略);
但是如果为注解中多个属性同时赋值的话,value=是不可以省略的
2.把注解应用到对应的目标上
在对应的目标写
@注解的名称(属性名=属性值)
@注解的名称(属性名1=属性值1,属性名2=属性值2,....属性名n=属性值n)
3.反射注解
总结:
用注解就是根据注解和注解上的属性值,来决定是否做一些事情
可以是指行指定的功能,也可以是调用一个具体方法
泛型:
一种参数化的类型
//非泛型,不是参数化类型
ArrayList list0=new ArrayList();
list0.add("abc");
list0.add(10);
//泛型标准化做法,参数化类型,参数是String类型
ArrayList<String> list1=new ArrayList<String>();
list1.add("abc");
list1.add("bcd");
//泛型,不推荐
ArrayList list2=new ArrayList<String>();
list2.add("abc");
list2.add("bcd");
list2.add(10);
new Demo1().xxx(list2);
//泛型,不推荐
ArrayList<String> list3=new Demo1().yyy();
list3.add("abc");
list3.add("bcd");
//list3.add(10);//错误
//下列的做法是错误的做法,编译报错,两端的泛型必须类型一致
//ArrayList<Object> list4=new ArrayList<String>();
自定义泛型:
-类上的泛型,泛型类
泛型一定要先定义,后使用,在类的名字后面定义
泛型的类型,一定要引用数据类型,不能是基本数据类型
不能对确且泛型类型类型使用instanceof关键字
比如:
//此种写法语法报错
if(num instanceof Generic<Number>){}
定义在类上,在类的内部起作用,在整个类范围内类型一致
类上的泛型的集体类型需要在创建类的对象的时候指定泛型的具体类型
如果在使用类上的泛型是不指定泛型的具体类型
默认的具体类型为泛型的上边界
-方法上的泛型,泛型方法
泛型要先定义后使用,在返回值之前定义,通常用一个大写的字母来定义一个泛型
可以定义多个,且定义在方法上,在方法的内部起作用
方法上的泛型,在方法被调用的时候自动推断出具体的泛型,无论何时,尽量使用泛型方法
作用域小,只局限于方法
比如:
public<T> T save(T t){
return t;
}
<T>就是泛型的标志,放在修饰符和返回类型之间,可以放置多个符号,<T,E>,
说明公有两个类型需要参数化确定
泛型的上边界:
泛型所取的类型是上边界类或上边界
如果不指定泛型默认上边界是Object
泛型擦除:
泛型在编译期间起作用,真正执行期间,泛型已经被擦除了
在编译阶段,编译器会将所有使用泛型的地方替换为该泛型的上边界
并在必要的时候,增加上必须的类型强制转换和类型检查
所以,在执行期间没有泛型的概念
泛型在编译期间就被擦除掉了,此过程就叫泛型擦除