一、反射与注解
内置注解
java内置了3种注解,用来为编译器提供检查。
@SuppressWarnings
@Deprecated
@Override
自定义注解
元注解
元注解是用来修饰注解的注解,java提供了3种元注解。
- 1、@Retention
RetentionPolicy.SOURCE 表明注解仅存在源码之中,不存在.class文件,更不能运行时可见。常见的注解为@Override, @SuppressWarnings。
RetentionPolicy.CLASS 这是默认的注解保留策略。这种策略下,注解将存在与.class文件,但是不能被运行时访问。通常这种注解策略用来处理一些字节码级别的操作。
RetentionPolicy.RUNTIME 这种策略下可以被运行时访问到。通常情况下,我们都会结合反射来做一些事情。 - 2、@Target 注解修饰的位置
- 3、@Inherited 注解是否可以被子类继承
自定义注解并使用
如下定义了一个注解
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.FIELD})
@Inherited
public @interface MyAnnotation {
String name();
String value() default "";
}
在user中使用了这个注解,并通过反射获取使用的注解
package testreflect;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
class User {
@MyAnnotation(name = "xiaohua")
private String name;
public User() {
}
public User(String name) {
this.name = name;
}
@MyAnnotation(name = "xiaoming", value = "hello")
public void sayHi() {
System.out.println("hi:i am " + name);
}
@Override
public String toString() {
// TODO Auto-generated method stub
return name.toString();
}
}
public class GetAnnotation {
@SuppressWarnings("unchecked")
public static void main(String[] args)
throws ClassNotFoundException, NoSuchFieldException, SecurityException, NoSuchMethodException,
InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
/**
* 获取类模板,一般用.class
*/
Class<User> clazz = User.class;//不会初始化静态块,不会初始化非静态块,不会调用构造函数
Object obj = new User();
clazz=(Class<User>) Class.forName("testreflect.User");//会初始化类静态块,但不会初始化非静态的代码块,也不调用构造函数
clazz = (Class<User>) obj.getClass();//会先初始化静态块,接着执行非静态块的初始化,最后调用构造函数
/**
* 获取类模板信息
*/
System.out.println(clazz.getName());
// 获取自有属性
Field[] fields = clazz.getDeclaredFields();
// 获取自有方法
Method[] methods = clazz.getDeclaredMethods();
// 获取构造器
Constructor<?>[] constructors = clazz.getDeclaredConstructors();
/**
* 执行方法,修改属性
*/
// 新建对象
Constructor<User> constructor = clazz.getDeclaredConstructor(String.class);
User xiaoming = constructor.newInstance("xiaoming");
System.out.println(xiaoming);
// 调用方法
Class<String>[] emptyClass = (Class<String>[]) new Class<?>[] {};// 泛型数组的替代解决方案
Method method = clazz.getDeclaredMethod("sayHi", emptyClass);
method.invoke(xiaoming, new Class<?>[] {});
// 修改属性
Field field = clazz.getDeclaredField("name");// public的属性
field.setAccessible(true);field.set(xiaoming, "lihua");field.setAccessible(false);
method.invoke(xiaoming, new Object[0]);
/**
* 读取注解
*/
// 获取修饰类的注解
Annotation[] annotations = clazz.getAnnotations();
MyAnnotation myAnnotation = clazz.getAnnotation(MyAnnotation.class);
// 获取方法参数注解
Annotation[][] mAnnotations = method.getParameterAnnotations();// 二维数组,第一个代表参数个数,第二个代表参数注解
}
}
二、反射与泛型
泛型的引入
Java采用泛型擦除机制来引入泛型。Java中的泛型仅仅是给编译器Javac使用的,确保数据的安全性和免去强制类型转换的麻烦。但是编译一旦完成,所有和泛型有关的类型全部被擦除。
为了通过反射操作这些类型以迎合实际开发的需要,Java新增了以下几种类型来代表不能被归一到Class类中的类型但是又和原始类型齐名的类型:
ParameterizedType:表示一种参数化的类型,比如Collection<>
GenericArrayType:表示一种元素类型是参数化类型或者类型变量的数组类型,比如比如List<>[],T[]这种。
TypeVariable:是各种类型变量的公共父接口类型变量,如参数化类型中的E、K等类型变量,表示泛指任何类,如果加上extends/super限定,则就会有相应的上限、下限。
WildcardType:代表一种通配符类型表达式,比如?、? extends Number、? super Integer。(wildcard是一个单词:就是”通配符“)
加入之后java中的类型如下:
- raw type:原始类型,对应Class, 包括数组、接口、注解、枚举等结构。
- parameterized types:参数化类型,对应ParameterizedType
- array types:参数化数组类型,对应GenericArrayType
- type variables:参数化类型变量,对应TypeVariable
分析:
List<T ? entends>[]:这里的List就是ParameterizedType,T就是TypeVariable,T ? entends就是WildcardType(注意,WildcardType不是Java类型,而是一个表达式),整个List<T ? entends>[]就是GenericArrayType。
泛型数组?
java禁止创建泛型数组,理由是下述代码产生的安全问题:
//创建list<String>数组
List<String>[] lsa = new List<String>[10]; //1
//转化为Object数组
Object o = lsa;
Object[] oa = (Object[]) o;
//赋值list<Integer>数组
List<Integer> li = new ArrayList<Integer>();
li.add(new Integer(3));
oa[1] = li;
//获取第一个位置的值
String s = lsa[1].get(0); //2
如果在1出允许使用泛型数组,那么2处在运行过程中会出现类型转换异常。
泛型数组的替代解决方法是使用?,如下:
List<?>[] lsa = new List<?>[10]; //1
Object o = lsa;
Object[] oa = (Object[]) o;
List<Integer> li = new ArrayList<Integer>();
li.add(new Integer(3));
oa[1] = li;
String s = (String) lsa[1].get(0); //2
根据通配符的定义及泛型擦除的保留上界原则,在2处返回的是Object,所以需要做显示的类型转化。
总结:要想使用泛型数组,要求程序员必须执行一次显示的类型转换,也就是将类型检查的问题从编译器交给了程序员。java的设计思想正是如此。
反射获取泛型
package testreflect;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Map;
class User<T> {
public void sayHi(T t) {
System.out.println("hi,i am:" + t.toString());
}
public void syaHello(Map<String, Object> map) {
for (Object str : map.values()) {
System.out.println("hi,i am:" + str.toString());
}
}
}
public class GetGenerics {
public static void main(String[] args) throws NoSuchMethodException, SecurityException {
Class<User> uclass = User.class;
{
// 获取类方法
Method method = uclass.getMethod("sayHi", Object.class);
// 获取指定方法参数的泛型信息
Type[] types = method.getGenericParameterTypes();
for (Type type : types) {
System.out.println(type);
if (type instanceof ParameterizedType) {
Type[] genericTypes = ((ParameterizedType) type).getActualTypeArguments();
for (Type t : genericTypes) {
System.out.println(t);
}
}
}
}
{
// 获取类方法
Method method = uclass.getMethod("syaHello", Map.class);
// 获取指定方法参数的泛型信息
Type[] types = method.getGenericParameterTypes();
for (Type type : types) {
System.out.println(type);
if (type instanceof ParameterizedType) {
Type[] genericTypes = ((ParameterizedType) type).getActualTypeArguments();
for (Type t : genericTypes) {
System.out.println(t);
}
}
}
}
/**
* 打印结果:
* T
* java.util.Map<java.lang.String, java.lang.Object>
* class java.lang.String
* class java.lang.Object
*
*/
}
}
网友评论