2017年8月27日 反射的初步认识

   反射原理主要是为了做框架用的,但是了解底层原理对以后深入理解框架概念还是蛮有帮助的。

  

   JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。JAVA反射(放射)机制:“程序运行时,允许改变程序结构或变量类型,这种语言称为动态语言”。从这个观点看,Perl,Python,Ruby是动态语言,C++,Java,C#不是动态语言。但是JAVA有着一个非常突出的动态相关机制:Reflection,用在Java身上指的是我们可以于运行时加载、探知、使用编译期间完全未知的classes。换句话说,Java程序可以加载一个运行时才得知名称的class,获悉其完整构造(但不包括methods定义),并生成其对象实体、或对其fields设值、或唤起其methods。

 

1.反射概述?

反射是Java中提供的一种对象动态特性,它是一种不能预知未来,但可以驾驭未来的技术.主要应用于一些框架编程中.

2.反射的起点?

Java中反射的起点是一个Class对象,每个类都有一个这样的对象,此类
类对象会在类加载时创建,存储在堆内存,用于记录方法区中类的结构信息(例如属性信息,方法信息,....).

Class 对象的获得?
1)类名.class
2)实例对象.getClass();
3)Class.forName("包名.类名");

例如:
package part;
class Point{.....}

Class c1=Point.class;
Class c2=new Point().getClass();
Class c3=Class.forName("part.Point");

每个类对应的Class对象在内存中只有一份.

c1==c2;true
c2==c3;true

扩展:了解类加载器及类的加载机制?
1)Bootstrap ClassLoader (启动类加载器)
2)ExtClassLoader(扩展类加载器)
3)AppClassLoader(应用类加载器)
4)自定义类加载器(场景,及实现)

例如 class TeduClassLoader extends ClassLoader{}

3.通过类(Class)对象构建类的对象(new 的实例)

1)静态方式构建对象
new Pint();
2)动态方式构建对象
Class<?> c=Class.forName("pkg.Point");
Object obj=c.newInstance();(必须存在无参构造函数)

回顾Spring IOC

applicationContext.xml
<beans>
<bean id="date" class="java.util.Date"/>
<bean id="projectService" class="cn.tedu.ProjectServiceImpl"/>
</beans>
当spring构建Date对象时,会:
a)解析xml,获得bean元素对应属性的值
b)动态构建对象Class.forName("java.util.Date").newInstance();
c)将对象存储到容器(例如hashmap);
d)需要时执行从容器获得

4.通过类(Class)对象获得构造方法(Constructor)对象.
1)getDeclaredConstructor(....)
2)getDeclaredConstructors();

获得构造方法对象以后,可通过构造方法对象构建类的对象
....
Constructor<?> con1=
c1.getDeclaredConstructor(int.class);
Object obj2=con1.newInstance(10)

5.通过类(Class)对象获得属性对象.
1)getDeclaredField(属性名)
2)getDeclaredFields()

获得属性对象以后,可以通过属性对象为对象属性赋值等.
Field f=c1.getDeclaredField("id");
f.setAccessible(true);
f.set(obj2, 20);
Object v=f.get(obj2) 获得obj2对象的f属性的值

6.通过类(Class)对象获得实例方法对象.
1)getDeclaredMethod(....)
2)getDeclaredMethods()

获得方法对象以后可以动态执行方法对象

Method m1=c1.getDeclaredMethod("getId");
Method m2=c1.getDeclaredMethod("setId",Integer.class);
m1.setAccessible(true);
m2.setAccessible(true);
m2.invoke(obj2, 100);
Object result=m1.invoke(obj2);

 

首先,先创建一个实体类Student,然后在里面定义2个属性和若干方法

 1 package reflect;
 2 
 3 public class Student {
 4     private String name;
 5     private int age;
 6     public void setName(String name) {
 7         this.name = name;
 8     }
 9     public void setAge(int age) {
10         this.age = age;
11     }
12     public Student(String name) {
13         super();
14         this.name = name;
15     }
16     public Student(int age) {
17         super();
18         this.age = age;
19     }
20     public Student() {
21         System.out.println("无参构造器");
22     }
23     public String getName() {
24         return name;
25     }
26     public int getAge() {
27         return age;
28     }
29     
30     /*
31      * 下面为方法
32      */
33     
34     public void methodTest(){
35         System.out.println("methodTest()");
36     }
37     
38     public void methodTest(int num){
39         System.out.println("methodTest()");
40     }
41 
42     private void methodTest(String str){
43         System.out.println("methodTest()");
44     }
45     
46     private int methodTest(String str,int num){
47         return 1;
48     }
49     
50     public  static void methodTest(int[] num){
51         System.out.println("静态methodTest()");
52     }
53     public static void main(String[] args) {
54         System.out.println("main方法");
55     }
56 }

 

 

 

  在测试类中实现获得实体类的属性和方法
  1 package reflect;
  2 
  3 import java.lang.reflect.Constructor;
  4 import java.lang.reflect.Field;
  5 import java.lang.reflect.Method;
  6 import java.lang.reflect.Modifier;
  7 
  8 import org.junit.Before;
  9 import org.junit.Test;
 10 
 11 12 
 13 public class Demo1 {
 14     private Class c;
 15 
 16     @Before
 17     public void testBefore() throws Exception {
 18         // System.out.println("before");
 19         c = Class.forName("reflect.Student");// 通过类名找到该类
 20     }
 21 
 22     @Test
 23     public void test1() throws Exception {
 24         Constructor s = c.getConstructor(null); // 获得无参构造方法
 25         Student st1 = (Student) s.newInstance(null); // 调用该构造方法,因为调用后返回的是object类型,所以需要强转
 26         System.out.println(st1);
 27     }
 28 
 29     @Test
 30     public void test2() throws Exception {
 31         Constructor s = c.getConstructor(String.class);
 32         Student st2 = (Student) s.newInstance("lilei");
 33         System.out.println(st2.getName());
 34         st2.setName("lilei*****");
 35         System.out.println(st2.getName());
 36     }
 37 
 38     @Test
 39     public void test3() throws Exception {
 40         Student st3 = (Student) c.newInstance();
 41         st3.setAge(0);
 42         System.out.println(st3.getAge());
 43         // Assert.assertEquals(false, false);
 44     }
 45 
 46     /*
 47      * 调用有参的方法 public void methodTest(int num){
 48      * System.out.println("methodTest()"); }
 49      */
 50     @Test
 51     public void test4() throws Exception {
 52         Student s = new Student();
 53         Method method = c.getMethod("methodTest", int.class);// 获得方法
 54         method.invoke(s, 1);
 55     }
 56 
 57     /*
 58      * 调用无参无返回值的方法 public void methodTest(){ System.out.println("methodTest()");
 59      * }
 60      */
 61     @Test
 62     public void test5() throws Exception {
 63         Student s = new Student();
 64         Method method = c.getMethod("methodTest", null);// 获得方法
 65         method.invoke(s, null); // 无参的就写null
 66     }
 67 
 68     /*
 69      * 调用私有方法 private int methodTest(String str,int num){ return 1; }
 70      */
 71     @Test
 72     public void test6() throws Exception {
 73         Student s = new Student();
 74         // 想要获得的方法为private修饰的,所以需要用getDeclaredMethod来调用
 75         Method method = c.getDeclaredMethod("methodTest", String.class, int.class);
 76         // 调用后需要设置打开Accessible为可用
 77         method.setAccessible(true);
 78         int num = (int) method.invoke(s, "xixi", 2);
 79         System.out.println(num);
 80     }
 81 
 82     /*
 83      * 调用静态方法 public static void methodTest(int[] num){
 84      * System.out.println("methodTest()"); }
 85      */
 86     @Test
 87     public void test7() throws Exception {
 88         // Student s= new Student();
 89         Method method = c.getMethod("methodTest", int[].class);// 获得方法
 90         method.invoke(null, new int[] { 1, 20 }); // 静态方法直接写null
 91     }
 92 
 93     /*
 94      * 调用main方法 public static void main(String[] args) {
 95      * System.out.println("main方法"); }
 96      */
 97     @Test
 98     public void test8() throws Exception {
 99         Method method = c.getMethod("main", String[].class);
100         /*
101          * 因为JDK版本兼容问题这样写会发生数字长度错误,旧版本是把数组里的数拆开来分别调main方法
102          */
103         // method.invoke(null, new String[]{"a","b"});
104 
105         // 第一种解决办法,将String数组外面套一个Object数组,拆开后还是String数组,满足main方法的要求
106         method.invoke(null, new Object[] { new String[] { "a", "b" } });
107         // 第二种解决方法,强转为object类型,不是数组,这样就不会拆了
108         method.invoke(null, (Object) new String[] { "a", "b" });        
109     }
110 
111     @Test
112     public void test9() {
113         Field[] field = c.getDeclaredFields();
114         StringBuffer sb = new StringBuffer();
115         //属性为类就可以直接使用getSimpleName()来获得类名
116         sb.append(Modifier.toString(c.getModifiers()) + " class " + c.getSimpleName() + "{\n");
117         for (Field f : field) {
118             sb.append("\t");// 空格
119             /*
120              *  获得属性的修饰符,例如public,static等等,这里getModifiers()方法获得的值为int类型,每个int值
121              *  对应了不同的修饰符类型,必须再用toString将其对应出来,才能正确显示为String,不然只有数字
122              */
123             sb.append(Modifier.toString(f.getModifiers()) + " ");
124             /*
125              *  属性的类型的名字,先使用getType()方法获得类型,返回:class java.lang.String
126              *  再调用getSimpleName() ,返回:String
127              *  如果使用getName(),返回:java.lang.String
128              *  我们只想获得String,那么则使用getType().getSimpleName()
129              */
130             sb.append(f.getType().getSimpleName() + " ");        
131             sb.append(f.getName() + ";\n");// 属性的名字+回车
132         }
133         sb.append("}");
134         System.out.println(sb);
135     }
136 
137 }

 test9打印结果:

 

再来专门练习下字段field的一些用法:

首先创建一个实体类Teacher

package reflect;

public class Teacher {
   public String name ="li";
   private int age =10;
   private int code=1;
   
}

接着,创建一个测试类:

package reflect;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

import org.junit.Test;

public class demo2 {

    @Test
    public void test1() throws  Exception{
        Class c = Class.forName("reflect.Teacher");
        Teacher teacher=new Teacher();
        //获取name字段
        Field field =c.getField("name");
        //输出字段name的修饰类型
        String s1=Modifier.toString(field.getModifiers());
        System.out.println("字段name的修饰类型"+s1);
        //获得字段name的类
        Class type=field.getType();
        System.out.println("字段name的修饰类"+type);
        //先判断获取到的字段是什么类型再进行强转
        if(type.equals(String.class)){
            String s2 =type.getSimpleName();
            System.out.println("字段name的类型"+s2);
        }    
        //输出字段name的值
        String s3=(String) field.get(teacher);
        System.out.println("字段name的值"+s3);                    
    }
    
    @Test
    public void test2() throws  Exception{
        Class c = Class.forName("reflect.Teacher");
        Teacher teacher=new Teacher();
        Field field =c.getDeclaredField("age");
        field.setAccessible(true);//因为age为private修饰的,所以必须用setAccessible方法破解
        System.out.println(field.get(teacher));
        field.set(teacher, 100);
        System.out.println(field.get(teacher));
    }
}

 

最后需要注意的是一个获取字段修饰符类型的方法getModifiers()

getModifiers()返回值是int,要输出必须使用Modifier.toString(f.getModifiers())来进行输出

其对应关系如下:

 

如何将int型数字写入泛型为String的List集合里

public class ReflectDemo04 {
public static void main(String[] args) throws Exception {
ArrayList<String> list = new ArrayList<>();// Object[]
list.add("A");
list.add("B");
// list.add(100);编译阶段做不到
// 请用反射将100存储到list集合
Class<?> c = list.getClass();
Method m = c.getDeclaredMethod("add", Object.class);
m.invoke(list, 100);
// 输出list集合内容
System.out.println(list);
}
}

 

posted on 2017-08-27 13:50  Loseheart  阅读(199)  评论(0编辑  收藏  举报