Java反射

http://blog.csdn.net/ichsonx/article/details/9108173

http://blog.csdn.net/liujiahan629629/article/details/18013523

http://blog.csdn.net/shakespeare001/article/details/8082634


TestRef.java
import java.lang.reflect.Method; 
import java.lang.reflect.InvocationTargetException;

/** 
* Created by IntelliJ IDEA. 
* File: TestRef.java 
* User: leizhimin 
* Date: 2008-1-28 14:48:44 
*/
 
public class TestRef {

    public staticvoid main(String args[]) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
        Foo foo = new Foo("这个一个Foo对象!");
        Class clazz = foo.getClass(); 
        Method m1 = clazz.getDeclaredMethod("outInfo");
        Method m2 = clazz.getDeclaredMethod("setMsg", String.class);
        Method m3 = clazz.getDeclaredMethod("getMsg");
        m1.invoke(foo); 
        m2.invoke(foo, "重新设置msg信息!"); 
        String msg = (String) m3.invoke(foo); 
        System.out.println(msg); 
    } 


class Foo { 
    private String msg; 

    public Foo(String msg) { 
        this.msg = msg; 
    } 

    public void setMsg(String msg) {
        this.msg = msg; 
    } 

    public String getMsg() { 
        return msg; 
    } 

    public void outInfo() {
        System.out.println("这是测试Java反射的测试类"); 
    } 
}
 
控制台输出结果:
这是测试Java反射的测试类 
重新设置msg信息! 

Process finished with exit code 0 


 



传送门:http://blog.csdn.net/hbcui1984/article/details/2719089

JAVA反射使用手

        本篇文章为在工作中使用JAVA反射的经验总结,也可以说是一些小技巧,以后学会新的小技巧,会不断更新。本文不准备讨论JAVA反射的机制,网上有很多,大家随便google一下就可以了。

        在开始之前,我先定义一个测试类Student,代码如下:

[Java] view plaincopy
  1. package chb.test.reflect;  
  2.   
  3. public class Student {  
  4.     private int age;  
  5.     private String name;  
  6.     public int getAge() {  
  7.         return age;  
  8.     }  
  9.     public void setAge(int age) {  
  10.         this.age = age;  
  11.     }  
  12.     public String getName() {  
  13.         return name;  
  14.     }  
  15.     public void setName(String name) {  
  16.         this.name = name;  
  17.     }  
  18.       
  19.     public static void hi(int age,String name){  
  20.         System.out.println("大家好,我叫"+name+",今年"+age+"岁");  
  21.     }  
  22. }<pre></pre>  

一、JAVA反射的常规使用步骤

    反射调用一般分为3个步骤:

  • 得到要调用类的class
  • 得到要调用的类中的方法(Method)
  • 方法调用(invoke)

     代码示例:

[Csharp] view plaincopy
  1. Class cls = Class.forName("chb.test.reflect.Student");  
  2. Method m = cls.getDeclaredMethod("hi",new Class[]{int.class,String.class});  
  3. m.invoke(cls.newInstance(),20,"chb");<pre></pre>  

二、方法调用中的参数类型

        在方法调用中,参数类型必须正确,这里需要注意的是不能使用包装类替换基本类型,比如不能使用Integer.class代替int.class。

       如我要调用Student的setAge方法,下面的调用是正确的:

 

[Java] view plaincopy
  1. Class cls = Class.forName("chb.test.reflect.Student");  
  2. Method setMethod = cls.getDeclaredMethod("setAge",int.class);  
  3. setMethod.invoke(cls.newInstance(), 15);<pre></pre>  

 

       而如果我们用Integer.class替代int.class就会出错,如:

[Java] view plaincopy
  1. Class cls = Class.forName("chb.test.reflect.Student");  
  2. Method setMethod = cls.getDeclaredMethod("setAge",Integer.class);  
  3. setMethod.invoke(cls.newInstance(), 15);<pre></pre>  

 

       jvm会报出如下异常:

[HTML] view plaincopy
  1. java.lang.NoSuchMethodException: chb.test.reflect.Student.setAge(java.lang.Integer)  
  2.     at java.lang.Class.getDeclaredMethod(Unknown Source)  
  3.     at chb.test.reflect.TestClass.testReflect(TestClass.java:23)<pre></pre>  

 

三、static方法的反射调用

 

       static方法调用时,不必得到对象示例,如下:

[Java] view plaincopy
  1. Class cls = Class.forName("chb.test.reflect.Student");  
  2. Method staticMethod = cls.getDeclaredMethod("hi",int.class,String.class);  
  3. staticMethod.invoke(cls,20,"chb");//这里不需要newInstance  
  4. //staticMethod.invoke(cls.newInstance(),20,"chb");<pre></pre>  

四、private的成员变量赋值

    如果直接通过反射给类的private成员变量赋值,是不允许的,这时我们可以通过setAccessible方法解决。代码示例:

[Java] view plaincopy
  1. Class cls = Class.forName("chb.test.reflect.Student");  
  2. Object student = cls.newInstance();//得到一个实例  
  3. Field field = cls.getDeclaredField("age");  
  4. field.set(student, 10);  
  5. System.out.println(field.get(student));<pre></pre>  

 

     运行如上代码,系统会报出如下异常:

[HTML] view plaincopy
  1. java.lang.IllegalAccessException: Class chb.test.reflect.TestClass can not access a member of class chb.test.reflect.Student with modifiers "private"  
  2.     at sun.reflect.Reflection.ensureMemberAccess(Unknown Source)  
  3.     at java.lang.reflect.Field.doSecurityCheck(Unknown Source)  
  4.     at java.lang.reflect.Field.getFieldAccessor(Unknown Source)  
  5.     at java.lang.reflect.Field.set(Unknown Source)  
  6.     at chb.test.reflect.TestClass.testReflect(TestClass.java:20)<pre></pre>  

    解决方法:

[Java] view plaincopy
  1. Class cls = Class.forName("chb.test.reflect.Student");  
  2. Object student = cls.newInstance();  
  3. Field field = cls.getDeclaredField("age");  
  4. field.setAccessible(true);//设置允许访问  
  5. field.set(student, 10);  
  6. System.out.println(field.get(student));<pre></pre>  

    其实,在某些场合下(类中有get,set方法),可以先反射调用set方法,再反射调用get方法达到如上效果,代码示例:

[Java] view plaincopy
  1. Class cls = Class.forName("chb.test.reflect.Student");  
  2. Object student = cls.newInstance();  
  3.   
  4. Method setMethod = cls.getDeclaredMethod("setAge",Integer.class);  
  5. setMethod.invoke(student, 15);//调用set方法  
  6.               
  7. Method getMethod = cls.getDeclaredMethod("getAge");  
  8. System.out.println(getMethod.invoke(student));//再调用get方法<pre></pre>  


 反射,当时经常听他们说,自己也看过一些资料,也可能在设计模式中使用过,但是感觉对它没有一个较深入的了解,这次重新学习了一下,感觉还行吧!


           一,先看一下反射的概念:

              主要是指程序可以访问,检测和修改它本身状态或行为的一种能力,并能根据自身行为的状态和结果,调整或修改应用所描述行为的状态和相关的语义。

             反射是java中一种强大的工具,能够使我们很方便的创建灵活的代码,这些代码可以再运行时装配,无需在组件之间进行源代码链接。但是反射使用不当会成本很高!

             看概念很晕的,继续往下看。

 

      二,反射机制的作用:

              1,反编译:.class-->.java

              2,通过反射机制访问java对象的属性,方法,构造方法等;

             这样好像更容易理解一些,下边我们具体看怎么实现这些功能。


      三,在这里先看一下sun为我们提供了那些反射机制中的类:

java.lang.Class;                

java.lang.reflect.Constructor; java.lang.reflect.Field;        

java.lang.reflect.Method;

java.lang.reflect.Modifier;


            很多反射中的方法,属性等操作我们可以从这四个类中查询。还是哪句话要学着不断的查询API,那才是我们最好的老师。


         四,具体功能实现:

                1,反射机制获取类有三种方法,我们来获取Employee类型

[java] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. //第一种方式:  
  2. Classc1 = Class.forName("Employee");  
  3. //第二种方式:  
  4. //java中每个类型都有class 属性.  
  5. Classc2 = Employee.class;  
  6.    
  7. //第三种方式:  
  8. //java语言中任何一个java对象都有getClass 方法  
  9. Employeee = new Employee();  
  10. Classc3 = e.getClass(); //c3是运行时类 (e的运行时类是Employee)  

 

    2,创建对象:获取类以后我们来创建它的对象,利用newInstance

[java] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. Class c =Class.forName("Employee");  
  2.   
  3. //创建此Class 对象所表示的类的一个新实例  
  4. Objecto = c.newInstance(); //调用了Employee的无参数构造方法.  


    3,获取属性:分为所有的属性和指定的属性:

      a,先看获取所有的属性的写法:

[java] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. //获取整个类  
  2.             Class c = Class.forName("java.lang.Integer");  
  3.               //获取所有的属性?  
  4.             Field[] fs = c.getDeclaredFields();  
  5.        
  6.                    //定义可变长的字符串,用来存储属性  
  7.             StringBuffer sb = new StringBuffer();  
  8.             //通过追加的方法,将每个属性拼接到此字符串中  
  9.             //最外边的public定义  
  10.             sb.append(Modifier.toString(c.getModifiers()) + " class " + c.getSimpleName() +"{\n");  
  11.             //里边的每一个属性  
  12.             for(Field field:fs){  
  13.                 sb.append("\t");//空格  
  14.                 sb.append(Modifier.toString(field.getModifiers())+" ");//获得属性的修饰符,例如public,static等等  
  15.                 sb.append(field.getType().getSimpleName() + " ");//属性的类型的名字  
  16.                 sb.append(field.getName()+";\n");//属性的名字+回车  
  17.             }  
  18.       
  19.             sb.append("}");  
  20.       
  21.             System.out.println(sb);  

       b,获取特定的属性,对比着传统的方法来学习:


[java] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. public static void main(String[] args) throws Exception{  
  2.               
  3. <span style="white-space:pre">  </span>//以前的方式:  
  4.     /* 
  5.     User u = new User(); 
  6.     u.age = 12; //set 
  7.     System.out.println(u.age); //get 
  8.     */  
  9.               
  10.     //获取类  
  11.     Class c = Class.forName("User");  
  12.     //获取id属性  
  13.     Field idF = c.getDeclaredField("id");  
  14.     //实例化这个类赋给o  
  15.     Object o = c.newInstance();  
  16.     //打破封装  
  17.     idF.setAccessible(true); //使用反射机制可以打破封装性,导致了java对象的属性不安全。  
  18.     //给o对象的id属性赋值"110"  
  19.     idF.set(o, "110"); //set  
  20.     //get  
  21.     System.out.println(idF.get(o));  
  22. }  

 4,获取方法,和构造方法,不再详细描述,只来看一下关键字:

方法关键字

含义

getDeclaredMethods()

获取所有的方法

getReturnType()

获得方法的放回类型

getParameterTypes()

获得方法的传入参数类型

getDeclaredMethod("方法名",参数类型.class,……)

获得特定的方法

 

 

构造方法关键字

含义

getDeclaredConstructors()

获取所有的构造方法

getDeclaredConstructor(参数类型.class,……)

获取特定的构造方法

 

 

父类和父接口

含义

getSuperclass()

获取某类的父类

getInterfaces()

获取某类实现的接口

  

         这样我们就可以获得类的各种内容,进行了反编译。对于JAVA这种先编译再运行的语言来说,反射机制可以使代码更加灵活,更加容易实现面向对象。

 

  五,反射加配置文件,使我们的程序更加灵活:

             在设计模式学习当中,学习抽象工厂的时候就用到了反射来更加方便的读取数据库链接字符串等,当时不是太理解,就照着抄了。看一下.NET中的反射+配置文件的使用:

             当时用的配置文件是app.config文件,内容是XML格式的,里边填写链接数据库的内容:

[html] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1.       <configuration>  
  2. lt;appSettings>  
  3. <add     key=""  value=""/>  
  4. lt;/appSettings>  
  5.         </configuration>  

 反射的写法:   

[csharp] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. assembly.load("当前程序集的名称").CreateInstance("当前命名空间名称".要实例化的类名);  

 

          这样的好处是很容易的方便我们变换数据库,例如我们将系统的数据库从SQL Server升级到Oracle,那么我们写两份D层,在配置文件的内容改一下,或者加条件选择一下即可,带来了很大的方便。

            

         当然了,JAVA中其实也是一样,只不过这里的配置文件为.properties,称作属性文件。通过反射读取里边的内容。这样代码是固定的,但是配置文件的内容我们可以改,这样使我们的代码灵活了很多!


    综上为,JAVA反射的再次学习,灵活的运用它,能够使我们的代码更加灵活,但是它也有它的缺点,就是运用它会使我们的软件的性能降低,复杂度增加,所以还要我们慎重的使用它。



一、什么是反射机制?为什么要用反射机制?

所谓Java反射机制是指,程序在运行状态时,可以加载一个运行时才得知名称的class,能够知道这个类的所有属性和方法,并生成其对象实体、或对其fields设值、或调用其方法;即利用反射技术,根据一个类名称,可以得到该类的构造方法、属性、方法等信息,并创建其对象。用一句话来概括,反射就是加载一个运行时才知道的类以及它的完整内部结构。 

那我们为什么要用反射机制呢?

第一,反射的目的就是为了扩展未知的应用。比如,我们写好了一个软件,其中定义了一些接口,程序已经过编译并且发布了,当我们以后需要扩展功能时,不可能去修改已经安装在别人机器上的软件源码,此时我们只需要另写一个插件,让其实现某些接口即可,程序运行时,通过反射技术动态的创建和编译新写的类,并获知其内部细节,就可以调用其方法了;

第二,在编码阶段不知道那个类名,要在运行期从配置文件读取类名, 这时候就没有办法以new的方式硬编码,而必须用到反射才能创建这个对象。 

二、如何使用反射?

1. 反射中的类

Java反射机制的实现要借助于4个类:ClassConstructorFieldMethod,其中,

Class——类对象(Class 类的实例表示正在运行的 Java 应用程序中的类和接口 

Constructor——类的构造器对象

Field——类的属性对象

Method——类的方法对象

可以看到一个类的各个组成部分(构造方法、属性、方法)都被封装成一个单独的类。

而java.lang.Class 提供了四种独立的反射调用,以不同的方式来获得以上信息。

获取构造方法的反射调用:

Constructor getConstructor(Class[] params) -- 匹配给定的参数类型来得到相应的公共构造函数  

Constructor[] getConstructors() -- 获得类的所有公共构造函数  

Constructor getDeclaredConstructor(Class[] params) -- 匹配给定的参数类型来得到相应 的构造函数 (从publicprivateprotect、默认中匹配选择

Constructor[] getDeclaredConstructors() -- 获得类的所有构造函数

获取字段属性的反射调用:

Field getField(String name) -- 获得匹配给定名称参数的公共字段  

Field[] getFields() -- 获得类的所有公共字段  

Field getDeclaredField(String name) -- 获得匹配给定名称参数的字段(从publicprivateprotect、默认中匹配选择  

Field[] getDeclaredFields() -- 获得类声明的所有字段 

获取方法的反射调用:

Method getMethod(String name, Class[] params) -- 匹配给定的方法参数名name和参数 类型param的公共方法  

Method[] getMethods() -- 获得类的所有公共方法  

Method getDeclaredMethod(String name, Class[] params) -- 匹配给定的方法参数名 name和参数类型param的方法(从publicprivateprotect、默认中匹配选择

Method[] getDeclaredMethods() -- 获得类声明的所有方法

 

2. 反射的使用步骤

步骤一:获取类的Class对象和创建类的实例对象

· 获取类的Class对象:

① 通过Class.forName(String className) ——className表示完整的类路径

② 运用.class语法,如String.class

③ 调用ObjectgetClass()方法,如:

String str = "abc";

Class c = str.getClass();

④ 对于基本数据类型,可以使用其包装类中与定义好的TYPE字段,如

Class c = Integer.TYPE;

其中,以“.class”来创建Class对象的引用时,不会自动地初始化该Class对象,而使用Class.forName()立即就进行了初始化。例如,如果一个static final属性值是“编译期常量”(public static final int A = 2;),那么这个值不需要对类进行初始化就可以被读取。

注:获取Class的引用,其实就是在加载或寻找编译期编译出来的“.class”字节码文件。

附(

Class.forName() 和 ClassLoader.loadClass()的区别

Class.forName("xx")等同于 Class.forName("xx",true,CALLClass.class.getClassLoader()),第二个参数(bool)表示装载类的时候是否初始化该类,即调用类的静态块的语句及初始化静态成员变量并为其分配空间
ClassLoader loader = Thread.currentThread.getContextClassLoader(); //也可以用(ClassLoader.getSystemClassLoader())

Class cls = loader.loadClass("xx"); //这句话没有执行初始化,其实与 Class.forName("xx.xx"false,loader)是一致的,只是loader.loadClass("xx")执行的 是更底层的操作。
只有执行cls.NewInstance()才能够初始化类,得到该类的一个实例 

· 创建类的实例对象,如:

Class c = Class.forName("com.ReflectionTest.");

Object obj = c.newInstance();

步骤二:调用诸如getDeclaredMethods()的方法,已取得类的构造方法、属性或者方法

例如:

Class c = Class.forName("java.lang.String");

Method m[] = c.getDeclaredMethods(); 

步骤三:对于第二步骤取得的属性、方法、构造方法等采用Reflection API进行处理

下面引用一个例子:

 /**
    * 通过java的反射机制动态修改对象的属性
    * @param o
    */
    public void test2(Customer o) {
        try {
            Class c = o.getClass();
            //getMethod方法第一个参数指定一个需要调用的方法名称,第二个参数是需要调用方法的参数类型列表,如无参数可以指定null,该方法返回一个方法对象 
            Method sAge = c.getMethod("setAge", new Class[] { int.class });
            Method gAge = c.getMethod("getAge", null);
            Method sName = c.getMethod("setName", new Class[] { String.class });
            //动态修改Customer对象的age
            Object[] args1 = { new Integer(25) };
            sAge.invoke(o, args1);
            //动态取得Customer对象的age
            Integer AGE = (Integer) gAge.invoke(o, null);
            System.out.println("the Customer age is: " + AGE.intValue());
            //动态修改Customer对象的name
            Object[] args2 = { new String("李四") };
            sName.invoke(o, args2);

        } catch (Throwable e) {
            System.err.println(e);
        }
    }

3.反射机制的功能?

① 在运行时查询并调用任意一个类所具有的成员变量和方法;

② 在运行时判断任意一个对象所属的类。例如:

String str = new String("abc");

Class c = str.getClass();

Boolean flag = c.isInstance(new String()) 

③ 在运行时创建类的实例;

·运行时创建实例对象

有两种办法生成实例对象:

针对无参数的构造方法,可以使用Class类里的newInstance()方法,该newInstance()方法不带参。例如:

Class c = String.class;

Object obj = c.newInstance();

要调用有参数的构造方法,就需要通过Constructor类的newInstance(Object ... obj)方法带参。例如:

//获得StringClass实例

Class clazz=String.class;    

//创建一个数组,这个数组用来放要实例化对象的构造器里的参数类型    

Class[] param=new Class[1];

//放入构造器中的参数类型,如果有多个,按顺序放入   

param[0]=String.class;    

//实例化一个构造器对象,并把放着构造器参数类型的数组作为参数传进去    

Constructor constructor=clazz.getConstructor(param);

//创建一个Object数组,用于放构造器中对应的值    

Object[] obj=new Object[1];    

//将值放入到数组中,这里要注意,param数组中放入的是什么类型,这里就要按顺序放入    

obj[0]="zhang3";    

//实例化对象,并把放着构造器要传入的参数的数组传到方法中    

String str=(String)constructor.newInstance(obj)

这样,我们就通过java.lang.reflect.Constructor实例化出来了String类型的对象。

·newInstance()new关键字创建实例的比较

在初始化一个类,生成一个实例的时候,newInstance()方法和new关键字除了一个 是方法,一个是关键字外,最主要的区别在于创建对象的方式不一样,前者是使用类加载机制,后者是创建一个新类。那么为什么会有两种创建对象方式?这主要考虑到软件的可伸缩、可扩展和可重用等软件设计思想。

JVM的角度看,我们使用关键字new创建一个类的时候,这个类可以没有被加载。但是使用newInstance()方法的时候,就必须保证:

1、这个类已经加载;

2、这个类已经链接了(即为静态域分配存储空间,并且如果必须的话将解析这个类创建的对其他类的所有引用)。而完成上面两个步骤的正是Class的静态方法forName()所完成的,这个静态方法调用了启动类加载器,即加载javaAPI的那个加载器。

可以看出,newInstance()实际上是把new这个方式分解为两步,即首先调用Class加载方法加载某个类,然后实例化。+这样分步的好处是显而易见的。我们可以在调用class的静态加载方法forName时获得更好的灵活性,提供给了一种降耦的手段

最后用最简单的描述来区分new关键字和newInstance()方法的区别:  newInstance:弱类型。低效率。只能调用无参构造方法

New:强类型。相对高效。能调用任何public构造。

jvm会执行静态代码段,只要记住一个概念,静态代码是和class绑定的,class装载成功就表示执行了你的静态代码了。而且以后不会再走这段静态代码了。

④ 当要调用的一个类的方法为private时,可以采用反射技术调用;

先根据完整类名加载该类——>利用newInstanceConstructor实例化——>通过getDeclaredMethods()得到私有的方法——>取消Java语言访问检查以访问private方法(即调用setAccessible(true)  ——>最后通过Methodinvoke方法调用该私有方法。

⑤ 利用反射实现类的动态加载(实现java动态绑定的前提

多态是Java面向对象语言的一个重要特性,而向上转型是实现多态的重要一步,对于面向接口或父类的引用,JVM是靠后期绑定(动态绑定)的机制进行对象类型的判定的,即在运行时才能确定调用哪些代码。而实现动态绑定的基础便是反射机制的应用,通过反射在运行时根据类名得到该类的具体信息。

三、我们学习过的知识中哪些用了反射机制?

工厂模式、SpringIOC(或DI)、AOP都使用到了java反射机制


发布了14 篇原创文章 · 获赞 26 · 访问量 18万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章