最近用到了动态代理, 在Spring框架中也大量使用了反射来完成Ioc和AOP. 对于反射一直也都是使用, 也没怎么系统的学习. 这篇文章就系统的总结一下在Java中反射的相关机制!
Github源码: https://github.com/JasonkayZK/Java_Samples/tree/java-reflection
Java反射 1. 反射概述 在运行过程中:
对于任意一个类 : 都能够知道这个类的所有属性和方法;
对于任意一个对象: 都能够调用它的任意一个方法和属性;
这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制
实际上, 我们创建的每一个类也都是对象! 即类本身是java.lang.Class类的实例对象, 被称为类对象!
2. Class对象特点 Class类的API如下图所示:
从图中可以得出以下几点:
Class 类的实例对象表示正在运行的 Java 应用程序中的类和接口; 也就是jvm中有很多的实例,每个类都有唯一的Class对象;
Class 类没有公共构造方法, Class 对象是在加载类时由 Java 虚拟机自动构造的. 也就是说我们不需要创建,JVM已经帮我们创建了;
Class 对象用于提供类本身的信息,比如有几种构造方法, 有多少属性,有哪些普通方法;
3. 反射的使用 假设有一个JavaBean: Hero类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 package reflection.pojo;public class Hero { public String name; public double hp; protected double armor; public int speed; public String getName () { return name; } public void setName (String name) { this .name = name; } public double getHp () { return hp; } public void setHp (double hp) { this .hp = hp; } public double getArmor () { return armor; } public void setArmor (double armor) { this .armor = armor; } public int getSpeed () { return speed; } public void setSpeed (int speed) { this .speed = speed; } @Override public String toString () { return "Hero{" + "name='" + name + '\'' + ", hp=" + hp + ", armor=" + armor + ", speed=" + speed + '}' ; } }
1): 获取类对象 获取类对象的方法有3种:
Class.forName() [常用]
Hero.class
new Hero().getClass()
在一个JVM中, 同一个ClassLoader引导创建的类, 只会有一个类对象存在!
所以对于上述三个方法来说, 都是使用的AppClassLoader引导创建的, 所以产生的类对象都相同:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 package reflection.chapter3.getClass;import reflection.pojo.Hero;public class GetClassDemo { public static void main (String[] args) { String className = "reflection.pojo.Hero" ; try { Class clazz1 = Class.forName(className); Class clazz2 = Hero.class; Class clazz3 = new Hero ().getClass(); System.out.println(clazz1 == clazz2); System.out.println(clazz2 == clazz3); } catch (ClassNotFoundException e) { e.printStackTrace(); } } }
输出为:
三种方法比较:
使用Class.forName()静态方法最常用!
使用Hero.class方法需要导入对于的类的包!
使用new方法已经直接new出了对象, 一般不再需要反射了!
一般都第一种,一个字符串可以传入也可写在配置文件中等多种方法.
2): 利用反射创建对象 与传统的通过new来获取对象 的方法不同: 反射会先拿到Hexo的”类对象”, 然后通过类对象获取”构造器对象”, 再通过构造器对象创建一个对象!
如, 使用默认的构造器方法构造对象:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 package reflection.chapter4.constructObject;import reflection.pojo.Hero;import java.lang.reflect.Constructor;public class DefaultConstructor { public static void main (String[] args) { try { Class clazz = Class.forName("reflection.pojo.Hero" ); Constructor constructor = clazz.getConstructor(); Hero hero = (Hero) constructor.newInstance(); System.out.println(hero); } catch (Exception e) { e.printStackTrace(); } } }
输出为:
1 Hero{name='null' , hp=0.0 , armor=0.0 , speed=0 }
当Hero的构造方法不是无参构造方法的时候: 需要先获取对应的构造器方法!
如: 获取构造函数构造对象
1.Hero类中添加构造方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 Hero(String str){ System.out.println("(默认)的构造方法 s = " + str); } public Hero () { System.out.println("调用了公有、无参构造方法执行了。。。" ); } public Hero (char name) { System.out.println("姓名:" + name); } public Hero (String name ,float hp) { System.out.println("姓名:" +name+"血量:" + hp); } protected Hero (boolean n) { System.out.println("受保护的构造方法 n = " + n); } private Hero (float hp) { System.out.println("私有的构造方法 血量:" + hp); }
2.通过反射机制获取对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 package reflection.chapter4.constructObject;import reflection.pojo.Hero;import java.lang.reflect.Constructor;import java.lang.reflect.InvocationTargetException;public class SelectConstructor { public static void main (String[] args) throws IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchMethodException, ClassNotFoundException { Class clazz = Class.forName("reflection.pojo.Hero" ); System.out.println("----公有构造方法----" ); Constructor [] constructors = clazz.getConstructors(); for (Constructor constructor : constructors) { System.out.println(constructor); } System.out.println("----所有的构造方法(包括:私有、受保护、默认、公有)----" ); constructors = clazz.getDeclaredConstructors(); for (Constructor constructor : constructors) { System.out.println(constructor); } System.out.println("----获取公有、无参的构造方法----" ); Constructor cons = clazz.getConstructor(null ); System.out.println("consturctor = " + cons); Object object = cons.newInstance(); System.out.println("Object: " + (Hero)object); System.out.println("----获取私有构造方法,并调用----" ); cons = clazz.getDeclaredConstructor(float .class); System.out.println("consturctor = " + cons); cons.setAccessible(true ); object = cons.newInstance(100 ); System.out.println("Object: " + (Hero)object); } }
输出为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 ----公有构造方法---- public reflection.pojo.Hero()public reflection.pojo.Hero(char )public reflection.pojo.Hero(java.lang.String,float ) ----所有的构造方法(包括:私有、受保护、默认、公有)---- reflection.pojo.Hero(java.lang.String) public reflection.pojo.Hero()public reflection.pojo.Hero(char )public reflection.pojo.Hero(java.lang.String,float )protected reflection.pojo.Hero(boolean )private reflection.pojo.Hero(float ) ----获取公有、无参的构造方法---- consturctor = public reflection.pojo.Hero() 调用了公有、无参构造方法执行了... Object: Hero{name='null' , hp=0.0 , armor=0.0 , speed=0 } ----获取私有构造方法,并调用---- consturctor = private reflection.pojo.Hero(float ) 私有的构造方法 血量:100.0 Object: Hero{name='null' , hp=0.0 , armor=0.0 , speed=0 }
总结:
获取构造器批量 的方法:
public Constructor[] getConstructors():
所有”公有的”构造方法
public Constructor[] getDeclaredConstructors():
获取所有的构造方法(包括私有、受保护、默认、公有)
获取构造器单个 的方法:
public Constructor getConstructor(Class… parameterTypes):
获取单个的”公有的”构造方法
public Constructor getDeclaredConstructor(Class…parameterTypes):
获取”某个构造方法”可以是私有的,或受保护、默认、公有;
3): 获取成员变量并使用 基本步骤:
获取对象: 通过new或者反射获得对象;
获取属性: Field f1 = hero.getDeclaredField(“属性名”)
修改属性: f1.set(hero, 实参) 此处为对象, 而不是类对象!!!
例1: 获取并修改属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 package reflection.chapter5.param;import reflection.pojo.Hero;import java.lang.reflect.Field;public class GetAndModifyParamDemo { public static void main (String[] args) { Hero hero = new Hero (); try { Field field = hero.getClass().getDeclaredField("name" ); field.set(hero, "teemo" ); System.out.println(hero); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (NoSuchFieldException e) { e.printStackTrace(); } } }
输出为:
1 Hero{name='teemo' , hp=0.0 , armor=0.0 , speed=0 }
补充: getField和getDeclaredField的区别
getField: 只能获取public的,包括从父类继承来的字段;
getDeclaredField: 可以获取本类所有的字段,包括private的,但是不能获取继承来的字段.
(注: 这里只能获取到private的字段,但并不能访问该private字段的值,除非加上setAccessible(true))
4): 获取成员方法并使用 基本步骤:
*实例: *
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 package reflection.chapter6.method;import reflection.pojo.Hero;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;public class InvokeMethodDemo { public static void main (String[] args) { Hero hero = new Hero (); Hero heroSet = new Hero (); try { Method method = hero.getClass().getMethod("setName" , String.class); method.invoke(heroSet, "Garon" ); hero.setName("Teemo" ); System.out.println("hero: " + hero); System.out.println("heroSet: " + heroSet); } catch (Exception e) { e.printStackTrace(); } } }
输出结果为:
1 2 hero: Hero{name='Teemo', hp=0.0, armor=0.0, speed=0} heroSet: Hero{name='Garon', hp=0.0, armor=0.0, speed=0}
关于Java方法反射的源码实现的分析: 深入分析Java方法反射的实现原理
5): 获取main方法并使用 *例: *
在Hero中添加main方法:
1 2 3 4 5 6 7 public static void main (String[] args) { System.out.println("执行main方法" ); for (String arg : args) { System.out.println(arg); } }
通过反射获取main方法, 并执行:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 package reflection.chapter7.main;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;public class InvokeMainDemo { public static void main (String[] args) { try { Class clazz = Class.forName("reflection.pojo.Hero" ); Method mainMethod = clazz.getMethod("main" , String[].class); mainMethod.invoke(null , (Object)new String [] {"a" , "b" , "c" }); mainMethod.invoke(null , new Object [] {new String [] {"a" , "b" , "c" }}); } catch (Exception e) { e.printStackTrace(); } } }
输出结果:
4. 反射的应用: 1): 通过反射读取并运行配置文件内容 首先准备两个业务类:
1 2 3 4 5 6 7 8 9 package reflection.chapter8.settings.service;public class Service1 { public void doService1 () { System.out.println("Service 1" ); } }
1 2 3 4 5 6 7 8 9 package reflection.chapter8.settings.service;public class Service2 { public void doService2 () { System.out.println("Service 2" ); } }
此时如果需要讲业务方法一切换为业务方法二时, 如果使用非反射方式 : 必须修改源代码, 然后重新编译, 运行才可以!
如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 package reflection.chapter8.settings;import reflection.chapter8.settings.service.Service1;import reflection.chapter8.settings.service.Service2;public class CommonDemo { public static void main (String[] args) { new Service2 ().doService2(); } }
如果使用反射, 将会方便的多!
如:
1 2 class =reflection.chapter8.settings.service.Service2 method =doService2
测试类中,首先取出类名称和方法名,然后通过反射去调用这个方法;
当需要从调用第一个业务方法,切换到调用第二个业务方法的时候,不需要修改一行代码,也不需要重新编译,只需要修改配置文件,再运行即可
例如: 测试类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 package reflection.chapter8.settings;import java.io.IOException;import java.io.InputStream;import java.lang.reflect.Constructor;import java.lang.reflect.Method;import java.util.Properties;public class ReflectSettingsDemo { public static void main (String[] args) { Properties properties; InputStream in = null ; try { properties = new Properties (); in = ReflectSettingsDemo.class.getClassLoader().getResourceAsStream("reflection.properties" ); properties.load(in); String className = properties.getProperty("class" ); String methodName = properties.getProperty("method" ); Class clazz = Class.forName(className); Method method = clazz.getMethod(methodName); Constructor constructor = clazz.getConstructor(); method.invoke(constructor.newInstance()); } catch (Exception e) { e.printStackTrace(); } finally { if (in != null ) { try { in.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
最后读取配置文件后输出:
将配置文件修改为:
1 2 class =reflection.chapter8.settings.service.Service1 method =doService1
输出为
2): 通过反射越过泛型检查 泛型是在编译期间起作用的。在编译后的.class文件中是没有泛型的。所有比如T或者E类型啊,本质都是通过Object处理的。所以可以通过使用反射来越过泛型!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 package reflection.chapter9.genericType;import java.lang.reflect.Method;import java.util.ArrayList;import java.util.List;public class AviodGenericTypeCheckDemo { @SuppressWarnings({"unchecked", "rawtypes"}) public static void main (String[] args) throws Exception { List <String> list = new ArrayList <>(); list.add("this" ); list.add("is" ); Class listClazz = list.getClass(); Method method = listClazz.getMethod("add" , Object.class); method.invoke(list, 5 ); for (Object obj : list) { System.out.println(obj); } } }
正常情况下, 由于声明的泛型类型为String, 而向其中加入的是Integer类型, 所以编译器在检查期将报错!
而使用了反射之后, 通过反射调用add()方法将越过编译器类型检查, 而成功加入
最终执行会输出结果:
附录 Github源码: https://github.com/JasonkayZK/Java_Samples/tree/java-reflection
*引用: *
Java基础之—反射(非常重要) ↩︎
反射有什么用 ↩︎
深入分析Java方法反射的实现原理