目录
1. Java反射---基础巩固
1.1Class概述
1.2 Field概述
1.3Method概述
1.4 Constructor概述
2. 注解(Annotation)
2.1 Annotation概述
2.2 使用元注解
2.3 自定义注解
2.4 使用反射API读取注解
3. 泛型---基础巩固
4. 泛型---擦拭法
5. 泛型---通配符
5.1 extends通配符
5.2 super通配符
5.3 无限定通配符
6. 反射与泛型
Class实例比较和instanceof的差别:
从Class实例获取class信息:
- getName( )
- getSimpleName( )
- getPackage( )
从Class实例判断class类型:
- isInsterface( )
- isEnum( )
- isArray( )
- isPrimitive( ) —— 判断是否是基本类型,如int、double等
创建class实例:
- newInstance( )
动态加载
- 利用JVM动态加载class的特性,可以在运行期根据条件加载不同的实现类
class总结
- JVM为每个加载的class创建对应的Class实例来保存class的所有信息
- 获取一个class对应的Class实例后就可以获取该class的所有信息
- 通过Class实例获取class信息的方法称为Reflection
- JVM总是动态加载class,可以在运行期根据条件控制加载class
通过Class实例获取field信息:
- getField(name):获取某个public的field(包括父类)
- getDeclaredField(name):获取当前类的某个field(不包括父类)
- getFields():获取所有public的field(包括父类)
- getDeclaredFields():获取当前类的所有field(不包括父类)
Field对象:(包含一个field的所有信息)
- getName()
- getType()
- gtModifiers() —— 获取修饰类型
获取和设置field的值:(对static类型也能操作)
- get(Object)获取一个实例的该字段
- set(Object,Object)设置一个实例的该字段值
提示: setAccessibl(true)可能失败
- 定义了SecurityManager
- SecurityManager的规则阻止了对该Field设置accessible,例如:当规则应用于所有的java和javax开头的package的类
Field总结
- Field对象封装了字段的所有信息
- 通过Class实例的方法可以获取Field实例:getField/getFields/getDeclaredField/getDeclaredFields
- 通过Field实例可以获取字段信息:getName/getType/getModifiers
- 通过Field实例可以读取或设置某个对象的字段:get(Object)/set(Object,Object)
通过Class实例获取method信息:
- getMethod(name,Class...):获取某个public的method(包括父类)
- getDeclaredMethod(name,Class...):获取当前类的某个method(不包括父类)
- getMethods():获取所有public的method(包括父类)
- getDeclaredMethods():获取当前类的所有method(不包含父类)
Method对象:(包含一个method的所有信息)
- getName()
- getReturnType()
- getParameterType()
- getModifiers()
Method方法调用:
- Object invoke(Object) —— 调用无参method
- Object invoke(Object,Object...) —— 调用有参method
提示: setAccessibl(true)可能失败
多态
从Person,class获取的Method作用于Student实例时:
- 实际调用方法是Student覆写的方法
- 保证了多态的正确性
Method总结
- Method对象封装了方法的所有信息
- 通过Class实例的方法可以获取Method实例:getMethod/getMehtods/getDeclaredMethod/getDeclaredMethods
- 通过Method实例可以获取方法信息:getName/getReturnType/getParameterTypes/getModifiers
- 通过Method实例可以调用某个对象的方法:Object invoke(Object instance,Object...parameters)
- 通过设置setAccessible(true)来访问非publuc方法
Class.newInstance()只能调用public无参数构造方法
Constructor对象包含一个构造方法得到所有信息,可以创建一个实例:
通过Class实例获取Constructor信息
- getConstructor(Class...):获取某个public的Constructtor
- getDeclaredConstructor(Class...):获取某个Constructor
- getConstructors():获取所有public的Constructor
- getDeclaredConstructors():获取所有Constructor
提示: setAccessibl(true)可能失败
Constructor总结
- Constructor对象封装了构造方法的所有信息
- 通过Class实例的方法可以获取Constructor实例:getConstructor/getConstructors/getDeclaredConstructor/getDeclaredConstructors
- 通过Constructor实例可以创建一个实例对象:newInstance(Object...parammeters)
- 通过设置setAccessible(true)来访问非public构造方法
其他反射
获取父类的Class:
- Class getSuperclass()
- Object的父类是null
- intrface的父类是null
获取当前类直接实现的interface:
- Class[] getInterfaces()
- 不包括间接实现的interface
- 没有interface的class返回空数组
- interface返回继承的interface
判断一个向上转型是否成立:
- bool isAssignableFrom(Class)
提示:Number下有多个实现类,无法转型为Integer
利用以上反射遍历指定class下的所有interfaces:
其他反射总结
- 通过Class对象可以获取继承关系:getSuperclass() 、getInterfaces()
- 通过Class对象的isAssignableFrom()方法可以判断一个向上转型是否正确
- 注解是放在Java源码的类、方法、字段、参数前的一种标签
注解的作用:
- 注解本身对代码逻辑没有任何影响
- 如何使用注解由工具决定
编译器可以使用的注解:
- @Override:让编译器检查该方法是否正确地实现类覆盖
- @Deprecated:告诉编译器该方法已经被标记为“作废”,在其他地方引用将会出现编译警告
- @SuppressWarnings
注解定义配置参数:
- 配置参数由注解类型定义
- 配置参数可以包括:所有基本类型、String、枚举类型、数组
- 配置参数必须为常量
- 缺少某个配置参数将使用默认值
- 如果只写常量,相当于省略了value参数
- 如果只写注解,相当于全部使用默认值
注解概述总结
- 注解(Annotation)是Java语言用于工具处理的标注
- 注解可以配置参数,没有指定配置的参数使用默认值
- 如果参数名称为value可以省略参数名称
使用@interface定义注解:——非元注解
- 注解的参数类似无参数方法
- 可以设定一个默认值(推荐)
- 把最常用的参数命名为value(推荐)
使用@Target定义Annotation可以应用于源码的哪些位置:
- 类或接口:ElementType.TyPE
- 字段:ElementType.FIELD
- 方法:ElementType.METHOD
- 构造方法:ElementType.CONSTRUCTOR
- 方法参数:ElementType.PARAMETER
使用@Retention定义Annotation的生命周期:
- 仅编译期:RetentionPolicy.SOURCE
- 仅class文件:RetentionPolicy.CLASS
- 运行期:RetentionPolicy.RUNTIME
如果@Retention不存在,则该Annotation默认为CLASS;通常自定义的Annotation都是RUNTIME。
Annotation的生命周期:
- RetentionPolicy.SOURCE:编译器在编译时直接丢弃@Override
- RetentionPolucy.CLASS:该Annotation仅存储在class文件中
- RetentionPolicy.RUNTIME:在运行其可以读取该Annotation
使用@Repeatable定义Annotation是否可重复:
- JDK>=1.8
使用@Inherited定义子类是否可继承父类定义的Annotation
- 仅针对@Target为TYPE类型的Annotation
- 仅针对class的继承
- 对interface的继承无效
定义注解的步骤:
- 用@interface定义注解
- 用元注解(meta annotation)配置注解:Target必须设置、Retention一般设置为RUNTIME、通常不必写@Inherited和@Repeatable等
- 定义注解参数和默认值
举个例子
自定义注解总结
- 使用@interface定义注解
- 可定义多个参数和默认值,核心参数使用value名称
- 必须设置@Target来指定Annotation可以应用的范围
- 应当设置@Retention为RUNTIME便于运行期读取该Annotation
如何读取RUNTIME类型的注解:
- Annotation也是class
- 所有Annotation继承自java.lang.annotation.Annotation
- 使用反射API
使用反射API读取Annotation:
- Class.isAnnotationPresent(Class)
- Field.isAnnotationPresent(Class)
- Method.isAnnotationPresent(Class)
- Constructor.isAnnotationPresent(Class)
动态判断注解
反射API读注解总结
- 可以在运行期通过反射读取RUNTIME类型的注解,不要漏写@Rentention(RetentionPolicy.RUNTIME)
- 可以通过工具处理注解来实现相应的功能:对JavaBean的属性值按规则进行检查、JUnit会自动运行@Test注解的测试方法
泛型概述
泛型就是定义一种模板,例如ArrayList
- 在代码中为用到的类创建对应的ArrayList<类型>,如:ArrayList
strList=new ArrayList (); - 编译器针对类型做检查:strList.add("hello"); strList.add(new Integer(123)); //compile error!
泛型的继承关系
- ArrayList
实现了List 接口:可以向上转型 注:不能把ArrayList
向上转型为ArrayList 或List ,因为ArrayList 和ArrayList 之间没有继承关系。代码如下: 泛型总结
- 泛型就是编写模板代码来适应任意类型
- 不必对类型进行强制转换
- 编译器将对类型进行检查
- 注意泛型的继承关系
使用泛型
- List
list = new ArrayList<>(); 可以省略类型,编译器能自动推断出类型 Arrays.sort()中的排序的自定义类型需要实现Comparable
泛型接口: 总结
- 使用泛型时,把泛型参数
替换为需要的class类型:List list = new ArrayList<>(); - 不指定泛型参数类型时,编译器会给出警告,且只能将
视为Object类型
编写泛型
如何编写一个泛型类:
- 按照某种类型编写类,例如:String
- 标记所有的特定类型
- 把特定的类型替换为T,并声明
泛型类型
不能用于静态方法:
- 编译错误
- 编译器无法在静态字段或静态方法中使用泛型类型
- 静态方法需要单独写成“泛型”,使用另一个泛型
泛型可以定义多种类型
- 使用类型
编写泛型总结
- 编写泛型时需要定义泛型类型
:public class Pair {...} - 静态方法不能引用泛型类型
,必须定义其他类型 来实现“泛型”:public static create(K first,K last){...} - 泛型可以同时定义多种类型
:public class Pair {...}
擦拭法
- 编译器把类型
视为Object
- 编译器根据
实现安全的强制转型 擦拭法局限性
- 无法取得带泛型的Class
- 无法判断带泛型的Class
- 不能实例化T类型
- 因为擦拭后实际上是new Object()
- 实例话T类型必须借助Class
分析equals方法:
泛型继承
可以继承自泛型类:
- 父类的类型是Pair
,子类的类型是IntPair - 子类可以获取父类的泛型类型Integer
注:Type继承结构
获取父类泛型类型
总结
- Java的泛型采用擦拭法实现
- 擦拭法决定了泛型
: 不能是基本类型
不能获取带泛型类型的Class,如Pair
.class 不能判断带泛型类型的类型,如public boolean equals(T obj)
不能判断带泛型类型的类型,如x instanceof Pair
不能实例化T类型,如new T()
泛型方法要防止重复定义方法,如public boolean equals(T obj)
- 子类可以获取父类的泛型类型
泛型的继承关系:
- Pair
不是pair 的子类 - add()不接受Pair
- 使用 extends Number>使方法接收所有泛型类型为Number或Number子类的Pair类
对Pair extends Number>调用getFirst()方法:
- 方法签名:? extends Number getFirst()
- 可以安全赋值给Number类型的变量:Number x=p.getFirst();
- 不可以预测实际类型就是Integer:Integer x=p.getFirst();
extends Number>的通配符:
- 允许调用get方法获取Number的引用
- 不允许调用set方法传入Number的引用
- 唯一例外:可以调用setFirst(null)
extends总结
- 使用类似 extends Number>通配符作为方法参数时表示:
- 方法内部可以调用获取Number引用的方法Number n=obj.getXxx()
- 方法内部无法调用传入Number引用的方法obj.setXxx(Number n) ----(null除外)
- 使用类似
定义泛型类型时表示:泛型类型限定为Number或Number的子类
泛型的继承关系:
- 使用 super Integer>使方法接收所有泛型类型为Integer或Integer超类的Pair类
对Pair super Integer>调用setFirst()方法:
- 方法签名:void setFirst(? super Integer)
- 可以安全传入Integer类型的变量:p.setFirst(new Integer(123));
对Pair super Integer>调用getFirst()方法:
- 方法签名:?super Integer getFirst()
- 无法赋值给Integer类型的变量
super Integer>的通配符:
- 允许调用set方法传入Integer的引用
- 不允许调用get方法获得Integer的引用
- 唯一例外:可以获取Object引用Object 0 = p.getFirst();
extends和super比较
方法参数为 extends T>和方法参数为 super T>的区别:
- extends T> 允许调用方法获取T的引用
- super T> 允许调用方法传入T的引用
>的通配符:
- 不允许调用set方法(null除外)
- 只能调用get方法获取Object引用
- Pair>和Pair不同
- 可以引入泛型参数
消除>通配符
super测试Demo代码如下:
super&无限定通配符总结
- 使用类似 super Integer>通配符作为方法参数时表示:
- 方法内部可以调用传入Integer引用的方法obj.setXxx(Integer n)
- 方法内部无法调用获取Integer引用的方法(Object除外)
- 使用类似
定义泛型类时表示:泛型类型限定为Integer或Integer的超类 - 无限顶通配符>很少使用,可以用
替换
部分反射API是泛型
Class
是泛型:
- T newInstance()
- Class super T> getSuperclass()
Costructor
是泛型:
泛型数组
可以声明带泛型的数组,但不能用new创建带泛型的数组:
必须通过强制实现带泛型的数组:
不安全 VS 安全使用带泛型的数组:
带泛型的数组实际上是编译器的类型擦除:
不能直接创建T[ ]数组:
- 擦拭后代码变为new Object[5]
- 必须借助Class
利用可变参数创建T[ ]数组:
- @SafeVarargs消除编译器警告
总结
- 部分反射API是泛型:Class
、Constructor - 可以声明带泛型的数组,但不能直接创建带泛型的数组,必须强制转型
- 可以通过Array.newInstance(Class
,int)创建T[ ]数组,需要强制转型
感觉打开了新世界------哈哈,加油,一起在现有知识基础上学习新知识!
谢谢阅读 ----知飞翀