★JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动
态调用对象的方法的功能称为Java语言的反射机制。
反射(Reflection)是Java程序开发语言的特征之一,它允许运行中的Java程序对自身进行检查, 也称自审,并能直接操作程序的内部属性。例如,使用它能获得Java类中各成员的名
称并显示出来。
Java的这一能力在实际应用中应用得很多,在其它的程序语言中根本就不存在这一牲。例如,Pascal、C或者C++中就没有办法在程序中获得函数定义相关的信息。
JavaBean是类反射的实际应用之一,它能让一些工具可视化的操作软件组件。这些工具通过类反射动态的载入并取得Java组件(类)的属性。后面学习的各种框架,基本上都会有反
射的使用。
反射最大的好处是解耦。
示例一:
- package reflect;
- import java.lang.reflect.Method;
- public class ReflectionHelloWorld {
- public static void main(String[] args) {
- try {
- //new UserModel();
- Class c = Class.forName("reflect.vo.Person");//依赖//<span style="color: rgb(255, 0, 0);font-size:14px; white-space: pre;">★</span><span style="color:#ff0000;">这里还要注意一个细节问题:就是这里只需要类名,不要后缀.java</span>
- Method methods[] = c.getMethods();
- for(int i=0;i<methods.length;i++){
- System.out.println(methods[i].toString());
- }
- } catch (ClassNotFoundException e) {
- e.printStackTrace();
- }
- }
- }
- public final void java.lang.Object.wait() throws java.lang.InterruptedException
- public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
- public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
- public native int java.lang.Object.hashCode()
- public final native java.lang.Class java.lang.Object.getClass()
- public boolean java.lang.Object.equals(java.lang.Object)
- public java.lang.String java.lang.Object.toString()
- public final native void java.lang.Object.notify()
- public final native void java.lang.Object.notifyAll()
调用其中的work()方法。(采用DAO模式-----见DAO模式)
api模块
USB.java(接口)
- package reflect.usb.api;
- public interface USB {
- public abstract void work();
- }
Usb1.java
- package reflect.usb.impl;
- import reflect.usb.api.USB;
- public class Usb1 implements USB {
- @Override
- public void work() {
- System.out.println("Usb1 is working....");
- }
- }
- package reflect.usb.impl;
- import reflect.usb.api.USB;
- public class Usb2 implements USB {
- @Override
- public void work() {
- System.out.println("Usb2.......working......");
- }
- }
USBFactory.java
- package reflect.usb.factory;
- import java.io.FileInputStream;
- import java.util.Properties;
- import reflect.usb.api.USB;
- public class USBFactory {
- public static USB getUSB(){
- //return new Usb2();
- try {
- Properties p = new Properties();//<code>Properties</code> <span style="color: rgb(255, 0, 0);font-size:14px; white-space: pre;">★</span>类表示了一个持久的属性集
- FileInputStream fin = new FileInputStream("usb.config");
- p.load(fin);//<span style="color: rgb(255, 0, 0);font-size:14px; white-space: pre;">★</span>从输入流中读取属性列表
- String name = p.getProperty("name").trim();//<span style="color: rgb(255, 0, 0);font-size:14px; white-space: pre;">★</span>用指定的键在此属性列表中搜索属性,并且除去多余的空格
- Class c = Class.forName(name);
- //Class c = Class.forName("reflect.usb.impl.Usb2");
- return (USB) c.newInstance();//<span style="color: rgb(255, 0, 0);font-size:14px; white-space: pre;">★</span>创建此 <tt>Class</tt> 对象所表示的类的一个新实例(new这个类的对象)
- } catch (Exception e) {
- e.printStackTrace();
- }
- return null;
- }
- }
- package reflect.usb;
- import reflect.usb.factory.USBFactory;
- public class Client {
- public static void main(String[] args) {
- USBFactory.getUSB().work();
- }
- }
二、反射使用的三个步骤
用于反射的类,如Method,可以在java.lang.reflect包中找到。使用这些类的时候必须要遵循三个步骤:
第一步:获得你想操作的类的java.lang.Class对象。在运行中的Java程序中,用java.lang.Class类来描述类和接口等。
第二步:调用诸如getDeclaredMethods的方法,取得该类中定义的所有方法的列表。
第三步:使用反射的API来操作这些信息。
如下面这段代码:
- Class c = Class.forName("java.lang.String");
- Method ms[] = c.getDeclaredMethods();
- System.out.println(ms[0].toString());
三、获取Class对象的三种方式
★ 方式一
通过对象的getClass方法进行获取。这种方式需要具体的类和该类的对象,以及调用getClass方法。
- private static void getClassObj1(){
- Person p = new Person("Jack",25);
- Class clazz = p.getClass();
- System.out.println(clazz);
- }
★ 方式二
任何数据类型(包括基本数据类型)都具备着一个静态的属性class,通过它可直接获取到该类型对应的Class对象。这种方式要使用具体的类,然后调用类中的静态属性class完成,无需调
用方法,性能更好。
- private static void getClassObj2(){
- //Class clazz =Person.class;
- //Class clazz =int.class;
- Class clazz =Integer.class;
- System.out.println(clazz);
- }
★ 方式三
通过Class.forName()方法获取。这种方式仅需使用类名(字符串),就可以获取该类的Class对象,更有利于扩展。可以实现解耦
- private static void getClassObj2(){
- //Class clazz =Person.class;
- //Class clazz =int.class;
- Class clazz =Integer.class;
- System.out.println(clazz);
- }
四、类的解剖(获取类的定义信息)
★ 获取类的方法
找出一个类中定义了些什么方法,这是一个非常有价值也非常基础的反射用法。
getMethods() : 获取当前类及其父类声明的public方法
getDeclaredMethods() :获取当前类声明的所有方法,包括private等其它非public方法
- private static void fetchMethods(String className) throws Exception {
- Class c = Class.forName(className);
- //Method methods[] = c.getMethods();
- Method methods[] = c.getDeclaredMethods();
- for(int i=0;i<methods.length;i++){
- Method m = methods[i];
- System.out.println("name= "+ m.getName());
- System.out.println("delaringClass= "+m.getDeclaringClass());
- Class paramTypes[] = m.getParameterTypes();
- for(int j=0; j<paramTypes.length; j++){
- System.out.println("param#"+j+": "+ paramTypes[j]);
- }
- Class exceptions[] = m.getExceptionTypes();
- for(int j=0; j<exceptions.length; j++){
- System.out.println("exception#"+j+" :"+ exceptions[j]);
- }
- System.out.println("returnType= " + m.getReturnType());//获得返回值类型
- System.out.println("-------------------");
- }
- }
★ 获取类的构造器
找出一个类中定义的构造方法,构造器没有返回类型。
getConstructors() : 获取当前类的public构造方法
getDeclaredConstructors() :获取当前类声明的所有构造方法,包括private等其它非public方法
- private static void fetchConstructors(String className) throws Exception {
- Class c = Class.forName(className);
- //Constructor cons[] = c.getConstructors();
- Constructor cons[] = c.getDeclaredConstructors();//构造方法的类
- for(int i=0;i<cons.length;i++){
- Constructor con = cons[i];
- System.out.println("name= "+ con.getName());
- System.out.println("delaringClass= "+con.getDeclaringClass());//获得次构造方法对象的类
- Class paramTypes[] = con.getParameterTypes();//获得其中参数的类型
- for(int j=0; j<paramTypes.length; j++){
- System.out.println("param#"+j+": "+ paramTypes[j]);
- }
- Class exceptions[] = con.getExceptionTypes();//获得异常类型
- for(int j=0; j<exceptions.length; j++){
- System.out.println("exception#"+j+" :"+ exceptions[j]);
- }
- System.out.println("-------------------");
- }
- }
★ 获取类的属性字段
找出一个类中定义了哪些属性字段。
getFields() : 获取当前类及其父类声明的所有可访问公共字段
getDeclaredFields() :获取当前类声明的所有可访问公共字段,包括private等其它非public的
- private static void fetchFields(String className) throws Exception {
- Class c = Class.forName(className);
- //Field fields[] = c.getFields();
- Field fields[] = c.getDeclaredFields();
- for(int i=0;i<fields.length;i++){
- Field fld = fields[i];
- System.out.println("name= "+ fld.getName());
- System.out.println("delaringClass= "+fld.getDeclaringClass());
- System.out.println("type= "+ fld.getType());
- int mod = fld.getModifiers();
- //System.out.println("modifiers= "+ mod);
- System.out.println("modifiers= "+ Modifier.toString(mod));
- System.out.println("-----------");
- }
- }
五、类的调用(调用类中的成员)
★ 构造类对象
使用构造器新建对象。根据指定的参数类型找到相应的构造函数,传入相应参数调用执行,以创建一个新的对象实例。
- private static void operateConstructor(String className) throws Exception{
- Class clazz = Class.forName(className);
- //调无参public的构造方法,太简单了,直接调newInstance();
- //Object obj = clazz.newInstance();
- //相当于要实现: Person p = new Person("Jack",10);
- //1先获取“Person(String name, int age)”这个构造方法--Constructor对象--con
- Class parameterTypes[] = new Class[2]; //组织形参
- parameterTypes[0] = String.class;
- //parameterTypes[1] = int.class;//※※※Person类中的构造方法为“public Person(String name, int age)”时,OK。但构造方法为“public Person(String name, Integer age)”时,WA。
- //parameterTypes[1] = Integer.class; //当构造方法为“public Person(String name, int age)”时,不行,因为它会在匹配构造方法时,严格匹配第2个参数为“Integer”的构造方法,不会自动装箱拆箱
- parameterTypes[1] = Integer.TYPE; //※※※该TYPE属性即是int。当Person类中的构造方法为“public Person(String name, int age)”时,OK。但构造方法为“public Person(String name, Integer age)”时,WA。
- Constructor con = clazz.getConstructor(parameterTypes);
- //2然后把参数“Jack,10”传入,调用该"Constructor对象--con"的newInstance方法,进行创建对象
- //组织实参
- Object params[] = new Object[2];
- params[0] = new String("Jack");
- params[1] = new Integer(20);
- //调用构造方法
- Object retObj = con.newInstance(params);//new Person("Jack",10);
- System.out.println(retObj);
- }
★ 调用方法
根据方法名称执行方法。根据方法名与参数类型匹配指定的方法,传入相应参数与对象进行调用执行。若是静态方法,则不需传入具体对象。
- private static void callMethod(String className) throws Exception{
- Class clazz = Class.forName(className);
- //※调用空参方法
- //1先获取Method对象
- Method method = clazz.getMethod("toString", null);
- Object obj = clazz.newInstance();
- //2再执行该Method对象的invoke()方法
- Object returnValue = method.invoke(obj, null);//obj.toString();
- //Person p = new Person("Jack",30);
- //Object returnValue = method.invoke(p, null);
- System.out.println(returnValue);
- //※调用带参数的方法
- //1先获取Method对象--m--需要先构造形参,通过形参找到对应的方法
- Class paramTypes[] = new Class[2];
- paramTypes[0] = Integer.TYPE;
- paramTypes[1] = double.class;
- Method m = clazz.getMethod("sum",paramTypes );
- //2再执行该Method对象的invoke()方法 -----需要一个对象obj,还需要相应的实参
- Object obj2 = clazz.newInstance();
- Object params2[] = new Object[2];
- params2[0] = 100;
- params2[1] = 123.5;
- Object retObj2 = m.invoke(obj2,params2 );
- System.out.println("retObj2= "+retObj2);
- //※调用静态方法
- Method m2 = clazz.getMethod("show", null);
- m2.invoke(null, null);//对象用null
- //※暴力访问
- Class paramTypes3[] = new Class[]{Integer.TYPE} ;
- //用clazz.getDeclaredMethods()可以获取非public方法的名称,本例中这一步省了,直接指定方法名“sum”
- Method m3 = clazz.getDeclaredMethod("sum",paramTypes3 );
- m3.setAccessible(true);//◎◎强行设置为可访问
- Object obj3 = clazz.newInstance();
- Object params3[] = { 100 };
- Object retObj3 = m3.invoke(obj3, params3);
- double d = (Double)retObj3;
- System.out.println("=============");
- System.out.println(d);
- }
★ 获取与设置属性值
根据属性名称读取与修改属性的值,访问非静态属性需传入对象为参数。
- private static void changeFieldValue(String className) throws Exception{
- Class clazz = Class.forName(className);
- Field fld = clazz.getField("age");
- Object p = clazz.newInstance();
- //fld.setInt(p, 100);
- //System.out.println(fld.getInt(p));
- fld.set(p, 66);
- Object retObj = fld.get(p);
- System.out.println(retObj);
- //※暴力访问
- Field fld2 = clazz.getDeclaredField("name");//必须用getDeclaredField()方法才能获得非public字段
- fld2.setAccessible(true);//打开访问权限的开关
- Object obj = clazz.newInstance();
- fld2.set(obj, "Tom");
- Object ret = fld2.get(obj);
- System.out.println(ret);
- }
六、练习(模拟Java内省的功能)
★ 准备工作
定义一个Model类,里面所有的属性都是private的,然后为每个属性提供getter和setter方法;
再准备一个Map,map的key值都是类里面的属性字段的字符串表示,值任意。
★ 真正的工作
设计一个方法Object getModel(Map map,Class cls),传入一个包含所有值的Map,然后再传入Model类的class,那么返回Model类的实例,这个实例里面已经包含好了所有相关的数据。
也就是把Map中的数据通过反射,设置回到Model类实例中。
源代码:
Person.java
- package reflect2.spring.vo;
- public class Person {
- private String name;
- public int age;
- public Person(String name, int age) {
- this.name = name;
- this.age = age;
- }
- public Person(){
- }
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- public int getAge() {
- return age;
- }
- public void setAge(int age) {
- this.age = age;
- }
- @Override
- public String toString() {
- return "Person [name=" + name + ", age=" + age + "]";
- }
- }
- package reflect2.spring.vo;
- import java.io.Serializable;
- public class UserModel{
- private String uuid;
- private String name;
- private int type;
- private String pwd;
- public UserModel(String uuid, String name, int type, String pwd) {
- this.uuid = uuid;
- this.name = name;
- this.type = type;
- this.pwd = pwd;
- }
- public UserModel(String uuid, int type){
- this.uuid = uuid;
- this.type = type;
- }
- public UserModel(){
- }
- private UserModel(String uuid){
- this.uuid = uuid;
- }
- public String getUuid() {
- return uuid;
- }
- public void setUuid(String uuid) {
- this.uuid = uuid;
- }
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- public int getType() {
- return type;
- }
- public void setType(int type) {
- this.type = type;
- }
- public String getPwd() {
- return pwd;
- }
- public void setPwd(String pwd) {
- this.pwd = pwd;
- }
- public String toString() {
- return "{"+uuid+","+name+","+type+","+pwd+"}";
- }
- }
- package reflect2.spring;
- import java.lang.reflect.Field;
- import java.lang.reflect.Method;
- import java.util.Map;
- public class SpringDaoUtil {
- private SpringDaoUtil(){
- }
- public static Object setValues(Map map, Class c) throws Exception{
- Object obj = c.newInstance();
- Field flds[] = c.getDeclaredFields();
- for(Field fld: flds){
- Object value = map.get(fld.getName());
- if(value==null){
- System.out.println(fld.getName()+"字段的数据为空");
- }else{
- //通过“字段名”得到相应set方法的名称如:age --> setAge
- String methodName = "set" + fld.getName().substring(0, 1).toUpperCase() + fld.getName().substring(1);
- Class paramTypes[] = new Class[1];
- paramTypes[0] = fld.getType();
- Method m = c.getDeclaredMethod(methodName, paramTypes);
- Object objs[] = new Object[]{value};
- m.invoke(obj, objs);
- }
- }
- return obj;
- }
- }
- package reflect2.spring;
- import java.util.HashMap;
- import java.util.Map;
- import reflect.vo.UserModel;
- import reflect2.spring.vo.Person;
- public class Client {
- public static void main(String[] args) {
- try {
- Map<String, String> map = new HashMap<String, String>();
- map.put("uuid", "100");
- map.put("name", "Jack");
- //map.put("type", "1");
- map.put("pwd", "1");
- UserModel user = (UserModel) SpringDaoUtil.setValues(map, UserModel.class);
- System.out.println(user);
- System.out.println("888888888888888888888888");
- Map<String, Object> map2 = new HashMap<String, Object>();
- map2.put("name", "张三");
- map2.put("age", 20);
- //Person p = (Person) SpringDaoUtil.setValues(map2, Person.class);
- UserModel p = (UserModel) SpringDaoUtil.setValues(map2, UserModel.class);
- System.out.println(p);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }