源代码:https://gitee.com/AgentXiao/reflection
反射的要点:
1、反射的概念(是什么、有什么用)
2、获得Class类对象
3、使用反射操作基本方法、构造方法、属性
4、反射的效率问题
5、使用反射操作泛型和注解
一、什么是反射机制
1、指的是可以在运行时加载、探知、使用编译期间完全未知的类。
2、程序在运行状态中,可以动态加载一个只有名称的类,对于任意一个已加载的类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性。
3、加载完类之后,在堆内存中,就产生了一个 Class 类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,我们形象的称之为:反射。
4、java.lang.Class类十分特殊,用来表示java中类型(class/interface/enum/annotation/primitive type/void)本身。
5、当一个class被加载,或当加载器(class loader)的defineClass()被JVM调用,JVM 便自动产生一个Class 对象。
6、Class类是Reflection的根源。
二、获得Class类的对象
- getClass()
- Class.forName()
- .class
注意:Class类的对象是针对类的,因此同一个类的Class类对象是相同的。
/**
* @ClassName Demo01
* @Description 测试获得Class类的对象
* @Author xwd
* @Date 2018/10/19 16:13
*/
public class Demo01 {
public static void main(String[] args) {
String computer = "pri.xiaowd.reflection.Computer";
try {
Class c1 = Class.forName(computer);
Class c2 = new Computer().getClass();
Class c3 = Computer.class;
System.out.println(c1.hashCode());
System.out.println(c2.hashCode());
System.out.println(c3.hashCode());
System.out.println(computer.getClass().hashCode() == String.class.hashCode());
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
以上代码的执行结果:
测试反射机制三、获得类的基本信息
/**
* @ClassName Demo02
* @Description 获得类的基本信息(类名、构造器、方法、属性)
* @Author xwd
* @Date 2018/10/19 16:36
*/
public class Demo02 {
public static void main(String[] args) {
String computer = "pri.xiaowd.reflection.Computer";
try {
Class clazz = Class.forName(computer);
//获得类名
String className = clazz.getName(); //pri.xiaowd.reflection.Computer
String classSimpleName = clazz.getSimpleName(); //Computer
System.out.println(className);
System.out.println(classSimpleName);
//获得构造器
//Constructor[] constructors = clazz.getConstructors();//只获得public修饰的构造器
Constructor[] constructors = clazz.getDeclaredConstructors();//获得所有定义的构造器
for(Constructor constructor:constructors){
System.out.println("构造器:"+constructor);
}
//考虑重载问题
Constructor c1 = clazz.getConstructor(null);//获得空构造
Constructor c2 = clazz.getConstructor(String.class,String.class);//获得带参构造
//获得方法
//Method[] methods = clazz.getMethods();//只获得public修饰的方法
Method[] methods = clazz.getDeclaredMethods();//获得全部方法
for(Method method:methods){
System.out.println("方法:"+method);
}
//考虑重载问题
Method m1 = clazz.getMethod("getName",null);
Method m2 = clazz.getMethod("setName",String.class);
//获得属性
//Field[] fields = clazz.getFields();//获得public修饰的属性
Field[] fields = clazz.getDeclaredFields();//获得所有属性
for(Field field:fields){
System.out.println("属性:"+field);
}
Field f1 = clazz.getDeclaredField("name");//获得name属性
} catch (Exception e) {
e.printStackTrace();
}
}
}
四、通过反射实现动态操作基本方法、构造方法、属性
/**
* @ClassName Demo03
* @Description 通过反射实现动态操作
* @Author xwd
* @Date 2018/10/19 17:13
*/
public class Demo03 {
public static void main(String[] args) {
String computer = "pri.xiaowd.reflection.Computer";
try {
Class<Computer> clazz = (Class<Computer>) Class.forName(computer);
//通过反射调用无参构造
Computer c1 = clazz.newInstance();
System.out.println(c1);
//通过反射调用有参构造
Constructor constructor = clazz.getConstructor(String.class,String.class);
Computer c2 = (Computer) constructor.newInstance("001","xwd");
System.out.println(c2.getId());
//通过反射API调用普通方法
Method method = clazz.getDeclaredMethod("setName", String.class);
method.invoke(c1,"lsm");
System.out.println(c1.getName());
//通过反射API可以直接访问属性
Field f = clazz.getDeclaredField("id");
f.setAccessible(true);//不需要再进行安全检查,可以直接访问私有属性
f.set(c1,"000001");
System.out.println(c1.getId());
System.out.println(f.get(c1));
} catch (Exception e) {
e.printStackTrace();
}
}
}
五、反射的效率
使用setAccessible(true)禁用安全检查可以加快反射的效率。
/**
* @ClassName Demo04
* @Description 测试反射的效率
* @Author xwd
* @Date 2018/10/20 20:52
*/
public class Demo04 {
/**
* @MethodName test1
* @Descrition 测试直接调用情况下的耗时
* @Param []
* @return void
*/
public static void test1(){
Computer c = new Computer();
long start = System.currentTimeMillis();
for(int i=0;i<1000000000L;i++){
c.getId();
}
long end = System.currentTimeMillis();
System.out.println("执行10亿次getId(),直接调用耗时:"+(end - start));
}
/**
* @MethodName test2
* @Descrition 使用反射机制调用,不禁用安全检查
* @Param []
* @return void
*/
public static void test2() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Computer c = new Computer();
Class clazz = c.getClass();
Method getName = clazz.getMethod("getName");
long start = System.currentTimeMillis();
for(int i=0;i<1000000000L;i++){
getName.invoke(c,null);
}
long end = System.currentTimeMillis();
System.out.println("执行10亿次getId(),使用反射机制调用,不禁用安全检查耗时:"+(end - start));
}
/**
* @MethodName test3
* @Descrition 使用反射机制调用,禁用安全检查
* @Param []
* @return void
*/
public static void test3() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Computer c = new Computer();
Class clazz = c.getClass();
Method getName = clazz.getMethod("getName");
getName.setAccessible(true);
long start = System.currentTimeMillis();
for(int i=0;i<1000000000L;i++){
getName.invoke(c,null);
}
long end = System.currentTimeMillis();
System.out.println("执行10亿次getId(),使用反射机制调用,禁用安全检查耗时:"+(end - start));
}
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
test1();
test2();
test3();
}
}
控制台的输出信息为:
测试反射的效率六、反射操作泛型
Java采用 泛型擦除 的机制来引入泛型。Java中的泛型仅仅是给编译器javac使用的,确保数据的安全性和免去强制类型转换的麻烦。但是,一旦编译完成,所有的和泛型有关的类型全部擦除。
因为反射是操作加载好的类的,因此通过反射是无法直接操作泛型的。
- 为了通过反射操作这些类型以迎合实际开发的需要,Java就新增了ParameterizedType,GenericArrayType,TypeVariable 和WildcardType几种类型来代表不能被归一到Class类中的类型但是又和原始类型齐名的类型。
/**
* @ClassName Demo05
* @Description 反射操作泛型
* @Author xwd
* @Date 2018/10/21 21:07
*/
public class Demo05 {
public void test01(Map<String,Computer> map, List<Computer> list){
System.out.println("Demo05.test01()");
}
public Map<Integer,Computer> test02(){
System.out.println("Demo05.test02()");
return null;
}
public static void main(String[] args) {
try {
//获得指定方法的参数泛型信息
Method m1 = Demo05.class.getMethod("test01",Map.class,List.class);
Type[] types = m1.getGenericParameterTypes();//获得带泛型的参数类型
for(Type type:types){
System.out.println(type); //遍历参数类型
if(type instanceof ParameterizedType){
//获得真正的泛型信息
Type[] genericTypes = ((ParameterizedType)type).getActualTypeArguments();
for(Type genericType:genericTypes){
System.out.println("参数泛型类型:"+genericType);
}
}
}
System.out.println("*******************************");
//获得指定方法的返回值泛型信息
Method m2 = Demo05.class.getMethod("test02",null);
Type returnTypes = m2.getGenericReturnType();//获得返回值的类型
System.out.println(returnTypes); //遍历参数类型
if(returnTypes instanceof ParameterizedType){
//获得真正的泛型信息
Type[] genericTypes = ((ParameterizedType)returnTypes).getActualTypeArguments();
for(Type genericType:genericTypes){
System.out.println("返回值泛型类型:"+genericType);
}
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
}
控制台输出信息:
反射操作泛型七、反射操作注解
详见【基础总结】注解Annotation
网友评论