本文共 9509 字,大约阅读时间需要 31 分钟。
反射与泛型
反射
Class类
class本身是一种数据类型(Type),class/interface的数据类型是Class,JVM为每个加载的class创建唯一的Class实例。
Class实例包含该class的所有信息,通过Class实例获取class信息的方法称为反射(Reflection)获取一个class的Class实例: 1)Class cls = String.class; 2)Class cls = "str".getClass(); 3)Class cls = Class.forName("java.lang.String");注意 Class的==比较和instanceof的区别
Integer n = new Integer(123);boolean b3 = n instanceof Integer; // trueboolean b4 = n instanceof Number; // true// 精确判断时boolean b1 = n.getClass() == Integer.class; // trueboolean b2 = n.getClass() == Number.class; // false
从Class实例获取class信息: 1)getName():获取完整类名 2)getSimpleName():获取简单的类名 3)getPackage():获取包名从Class实例判断class类型: 1)isInterface():判断是否是接口类型 2)isEnum():判断是否是枚举类型 3)isArray():判断是否是数组类型 4)isPrimitive():判断是否是基本类型创建class实例: cls.newInstance() JVM总是动态加载class,可以在运行期根据条件控制加载class
// commons logging优先使用Log4jLogFactory factory;if(isClassPresent("org.apache.logging.log4j.Logger")){ factory = createLog4j();}else{ factory = createJdkLog();}boolean isClassPresent(String name){ try{ Class.forName(name); return true; }catch(Exception e){ return false; }}
访问字段
通过Class实例获取字段field信息: 1)getField(name)——获取某个public的field(包括父类) 2)getDeclaredField(name)——获取当前类的某个field(不包括父类) 3)getFields()——获取所有public的field(包括父类) 4)getDeclaredFields()——获取当前类的所有field(不包括父类)Field对象包含一个field的所有信息 1)getName():字段名称 2)getType():字段定义的数据类型 3)getModifiers():字段修饰符获取和设置field的值 1)get(Object obj) 2)set(Object, Object)通过反射访问Field需要通过SecurityManager设置的规则通过设置setAccessible(true)来访问非public字段
调用方法
通过Class实例方法获取Method信息: 1)getMethod(name, Class ...):获取某个public的method(包括父类) 2)getDeclaredMethod(name, Class ...):获取当前类的某个method(不包括父类) 3)getMethods():获取所有public的method(包括父类) 4)getDeclaredMethods():获取当前类的所有method(不包括父类)Method对象包含一个method的所有信息 1)getName():获取方法名 2)getReturnType():获取返回值类型 3)getParameterTypes():获取方法参数类型 4)getModifiers():获取方法修饰符调用Method: Object invoke(Object obj, Object ...args)通过设置setAccessible(true)来访问非public方法反射调用Method也遵守多态的规则
调用构造方法
Class.newInstance()只能调用public无参数构造方法 String s = (String) String.class.newInstance(); // ^_^ Integer n = (Integer) Integer.class.newInstance(); // ERROR通过Class实例获取Constructor信息: 1)getConstructor(Class...):获取某个public 的 Constructor 2)getDeclaredConstructor(Class...):获取某个Constructor 3)getConstructors():获取所有public的Constructor 4)getDeclareConstructors():获取所有的Constructor通过Constructor实例可以创建一个实例对象 newInstance(Object ... parameters)通过设置setAccessible(true)来访问非public构造方法
获取继承关系
获取父类的Class: 1)Class getSuperclass() 2)Object的父类是null 3)interface的父类是null获取当前类直接实现的interface: 1)Class[] getInterfaces() 2)getInterfaces()获取的不包括间接实现的interface;如果想获取所有的,可以递归调用获取 3)没有interface的class返回空数组 4)interface返回继承的interface判断一个向上转型是否成立: boolean isAssignableFrom(Class)
注解 Annotation
使用注解
注解是放在Java源码的类、方法、字段、参数前的一种标签注解本身对代码逻辑没有任何影响,如何使用注解由工具决定编译器可以使用的注解 @Override:让编译器检查该方法是否正确实现了覆写 @Deprecated:告诉编译器该方法已经标记为“作废” @SuppressWarnings:让编译器忽略警告注解可以定义配置参数和默认值 配置参数由注解类型定义 配置参数可以包括: 所有基本类型 String 枚举类型 数组 配置参数必须是常量
定义注解
使用@interface定义注解,例如
@Target(ElementType.METHOD)public @interface Report { int type() default 0; String level() default "info"; String value() default "";}
使用元注解(meta annotation)定义注解
1)@Target 2)@Retention 3)@Repeatable 4)@Inherited@Target
使用@Target定义Annotation可以被应用于源码的哪些位置: 类或接口:ElementType.TYPE 字段:ElementType.FIELD 方法:ElementType.METHOD 构造方法:ElementType.CONSTRUCTOR 方法参数:ElementType.PARAMETER
@Retention
使用@Retention定义Annotation的生命周期 仅编译器:RetentionRolicy.SOURCE 仅class文件:RetentionRolicy.CLASS 运行期:RetentionRolicy.RUNTIME如果@Retention不存在,则该Annotation默认为CLASS通常自定义的Annotation都是RUNTIME
@Repeatable
使用@Repeatable定义Annotation是否可重复JDK>=1.8
@Inherited
使用@Inherited定义子类是否可继承父类定义的Annotation 仅针对@Target为TYPE类型的Annotation 仅针对class的继承 对interface的继承无效
定义Annotation的步骤:
1)用@interface定义注解2)用元注解配置注解 Target:必须设置 Retention:一般设置为RUNTIME 通常不必写@Inherited,@Repeatable等3)定义注解参数和默认值
处理注解
注解本身对代码逻辑没有任何影响,SOURCE类型的注解在编译期就被丢掉了,CLASS类型的注解仅保存在class文件中,RUNTIME类型的注解在运行期可以被读取,如何使用注解由工具决定
Q:如何读取RUNTIME类型的注解?Annotation也是class,所有Annotation继承自java.lang.annotation.Annotation,可以使用反射API
使用反射API读取Annotation:isAnnotationPresent、getAnnotation
isAnnotationPresent
判断某个注解是否存在
Class.isAnnotationPresent(Class) Field.isAnnotationPresent(Class) Method.isAnnotationPresent(Class) Constructor.isAnnotationPresent(Class)
getAnnotation
获取某个注解信息
Class.getAnnotation(Class) Field.getAnnotation(Class) Method.getAnnotation(Class) Constructor.getAnnotation(Class)
getParameterAnnotations
获取参数类型的注解:getParameterAnnotations()
Annotation[][] annos = m.getParameterAnnotations();
可以在运行期通过反射读取RUNTIME类型的注解 不要漏写@Retention(RetentionPolicy.RUNTIME)可以通过工具处理注解来实现相应的功能: 1)对JavaBean的属性值按规则进行检查 2)JUnit会自动运行@Test标记的测试方法
练习
请根据注解; @NotNull:检查该属性为非null @Range:检查整型介于min~max,或者检查字符串长度介于min~max @ZipCode:检查字符串是否全部由数字构成,且长度恰好为value实现对Java Bean的属性值检查。如果检查未通过,抛出异常
泛型
什么是泛型
泛型(Generic)就是定义一种模板,例如ArrayList在代码中为用到的类创建对应的ArrayList <类型> ArrayList strList = new ArrayList 类型>();编译器会针对泛型作检查,要注意泛型的继承关系 可以吧ArrayList 向上转型为List (T不能变) 不能把ArrayList 向上转型为ArrayList 或List ArrayList 和ArrayList 两者没有继承关系
使用泛型
定义泛型类型: List 的泛型接口为强类型 void add(Number) Number get(int)不指定泛型参数类型时,编译器会给出警告,且只能将 视为Object类型
编写泛型
泛型一般用在集合类中,编写类型时,需要定义泛型类型public class Pair {...}静态方法不能引用泛型类型 ,必须定义其他类型 来实现泛型 public static Pair create(K first, K last){ ... }泛型可以同时定义多种类型
擦拭法
Java的泛型是采用擦拭法(Type Erasure)实现的,编译器把类型视为Object,再根据实现安全的强制转型
擦拭法是局限: 1)不能是基本类型,例如int 2)Object字段无法持有基本类型 3)无法取得带泛型的Class 4)无法判断带泛型的Class 5)不能实例化T类型,因为擦拭后实际上是new Object()
可以继承自泛型类
public class IntPair extends Pair{ }Class clazz = IntPair.class;Type t = clazz.getGenericSuperclass();if(t instanceof ParameterizedType){ ParameterizedType pt = (ParameterizedType)t; Type[] types = pt.getActualTypeArguments(); Type firstType = types[0]; Class typeClass = (Class ) firstType; System.out.println(typeClass); // Integer}
父类类型是Pair<Integer>;子类的类型是IntPair;子类可以获取父类的泛型类型
继承关系:extends通配符
public class Pair{ ... }public class PairHelper { static int get(Pair p) { Number first = p.getFirst(); Number last = p.getLast(); return first.intValue() + last.intValue(); }}PairHelper.get(new Pair (1, 2)); // √PairHelper.get(new Pair (1, 2)); // ×
Pair<Integer>不是Pair<Number>的子类;get()不接受Pair<Integer>,所以可以使用<? extends Number>使方法接收所有泛型类型为Number或Number类的Pair类
public class PairHelper { static int get(Pair p) { Number first = p.getFirst(); Number last = p.getLast(); return first.intValue() + last.intValue(); }}PairHelper.get(new Pair(1, 2)); // √PairHelper.get(new Pair (1, 2)); // √
对Pair 调用getFirst()方法: 方法签名:? extends Number getFirst() 可以安全赋值给Number类型的变量:Number x = p.getFirst(); 不可预测实际类型就是Integer:Integer x = p.getFirst();对Pair 调用setFirst()方法会报错: 方法签名:void setFirst(? extends Number) 无法传递任何Number类型给setFirst(? extends Number)
<? extends Number> 的通配符:
允许调用get方法获得Number的引用 不允许调用set方法传入Number的引用 唯一例外:可以调用setFirst(null) <T extends Number> 的通配符 限定定义Pair<T>时只能是Number或Number的子类super通配符
public class Pair{ ... }public class PairHelper { static int set(Pair p, Integer first, Integer last) { p.setFirst(first); p.setLast(last); }}PairHelper.set(new Pair (1, 2), 3, 4); // √PairHelper.set(new Pair (1, 2), 3, 4); // ×
Pair<Integer>不是Pair<Number>的子类;set()不接受Pair<Number>,所以可以使用<? super Integer>使方法接收所有泛型类型为Integer或Integer超类的Pair类
public class PairHelper { static int set(Pair p, Integer first, Integer last) { p.setFirst(first); p.setLast(last); }}PairHelper.set(new Pair(1, 2), 3, 4); // √PairHelper.set(new Pair (1, 2), 3, 4); // √
对Pair 调用setFirst()方法: 方法签名:void setFirst(? super Integer) 可以安全传入Integer类型的变量:p.setFirst(new Integer(123));对Pair 调用getFirst()方法会报错: 方法签名:? super Integer getFirst() 无法赋值给Integer类型的变量
<T super Integer> 的通配符
允许调用set方法传入Integer的引用 不允许调用get方法获得Integer的引用 唯一例外:可以获取Object引用Object o = p.getFirst(); <T extends Number> 的通配符 限定定义Pair时只能是Integer或Integer的超类 extends和super通配符的区别允许调用方法获取T的引用 允许调用方法传入T的引用
public class Collections{ // 把src的每个元素复制到dest中 public staticvoid copy(List dest, List src){ for(var i=0; i
无限定通配符<?>
不允许调用set方法(null除外)只能调用get方法获取Object引用可以用消除
泛型与反射
部分反射API是泛型 ClassConstructor 可以声明带泛型的数组,但不能直接创建带泛型的数组,必须强制转型,例如
Pair[] ps = null; // √Pair [] ps = new Pair [2]; // ×Pair [] ps = (Pair [])new Pair[2]; // √
// 不安全地使用带泛型的数组==>通过引用Pair[] arr = new Pair[2];Pair[] ps = (Pair []) arr;ps[0] = new Pair ("a", "b");arr[1] = new Pair (1, 2);// ClassCastExceptionPair p = ps[1];String s = p.getFirst();
// 安全地使用带泛型的数组 ==> 不要使用arr的引用@SuppressWarnings("unchecked")Pair[] ps = (Pair []) new Pair[2];
不能直接创建 T[] 数组: 因为擦拭后变为new Object[5];必须借助Class可以通过Array.newInstance(Class , int)创建T[]数组,需要强制转型
T[] createArray(Classcls){ return (T[]) Array.newInstance(cls, 5);}
public class ArrayHelper{ @SafeVarargs // 消除编译器警告 staticT[] asArray(T... objs){ return objs; }}String[] ss = ArrayHelper.asArray("a", "b", "c");Integer[] ns = ArrayHelper.asArray(1, 2, 3);
转载地址:https://blog.csdn.net/yangwei234/article/details/84977315 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!