Java 反射机制

Java 反射机制

 2016-09-16 |   Visit count

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。—-百度百科

动态:运行期间动态绑定执行规则。

静态:编译以后就已经确定的执行过程。

动态加载类到方法区

1
2
3
4
5
6
7
8
9
10
11
public void test1(){
    String str = new String("Hello");
    Class cls = String.class;
    //动态获取 String类型的方法信息:
    Method[] all = cls.getDeclaredMethods();
    //Method 代表方法的信息
    // method.getName 可以获取方法的名称
    for (Method method : all) {
        System.out.println(method.getName());
    }
}

创建对象过程

1.Java 将类加载到方法区(自动完成)

2.利用类创建对象。内存中有一个String对象,方法区中还有类的信息!通过Class对象可以获取类的相关信息。

54136091-file_1487994424894_c44a.png

动态获取类的信息

动态获取类的方法信息

Foo类(package:demo):

1
2
3
4
5
6
7
8
9
10
//编译以后是 demo.Foo.class
//类的全名是 demo.Foo
class Foo{
    public int test(){
        return 5;
    }
    public double test1(){
        return 5d;
    }
}

动态获取Foo类的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Test
public void testClassForName(){
  /*
     * 动态的加载类信息到方法区
     * 并且返回对应的Class对象!
     * Class 对象可以访问类的全部信息!
     * 
     * 将className对应的类文件,从磁盘中加载
     * 内存方法区,返回这个类的信息
     */
    String className = "demo.Foo";
    try {
        Class cls = Class.forName(className);
        Method[] all = cls.getDeclaredMethods();
        for (Method method : all) {
            System.out.println(method.getName());
        }
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
}

输出:

1
2
test
test1​

动态获取类的属性信息

Eoo类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Eoo{
    int id;
    String name;
    double salary;
    String meno;
    public Eoo(){
    	
    }
    public Eoo(int id, String name, double salary, String meno) {
        super();
        this.id = id;
        this.name = name;
        this.salary = salary;
        this.meno = meno;
    }
    // get,set略
}

动态获取Eoo类的属性:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Test
public void testField()throws Exception {
  /*
     * 动态获取一个类的全部属性信息
     * 1 动态加载一个类到方法区
     * 2 动态获取类的属性信息
     */
    String className = "demo.Eoo";
    //动态加载类
    Class cls = Class.forName(className);
    //动态获取类声明的属性信息
    Field[] all = cls.getDeclaredFields();
    for (Field field : all) {
        //getName 获取属性的名字
        System.out.print(field.getName()+" ");
    }
}

输出:

1
id name salary meno

动态获取类的构造器信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Test
public void testCon() throws Exception{
  /* 
     * 1 动态加载类
     */
    String className = "demo.Eoo";
    Class cls = Class.forName(className);
	
    Constructor[] all = cls.getDeclaredConstructors();
    for (Constructor c : all) {
        System.out.print(c.getName());
        //获取构造器的参数类型列表
        // Parameter 参数  Type类型
        // Class[] 代表所有参数的类型列表
        Class[] types = c.getParameterTypes();
        System.out.println(Arrays.toString(types));
	}
}

输出:

1
2
demo.Eoo[]
demo.Eoo[int, class java.lang.String, double, class java.lang.String]​

动态创建对象

调用无参构造器创建对象

如果没有无参数构造器,将发生异常!Class 提供了方法 newInstance()。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Test
public void testNewInstance() throws Exception{
  /*
     * 动态调用无参数构造器创建对象
     * 1 动态加载类
     * 2 利用class 的方法 newInstance 执行
     * 无参数构造器常见对象
     * 注意:类必须有无参数,否则出异常 
     */
    String className = "java.util.Date";
    Class cls = Class.forName(className);
    // cls.newInstance()调用无参数构造器创建对象
    Object obj = cls.newInstance();
    System.out.println(obj);
    
    //静态的创建对象!编译已经就固定了!
    Date date = new Date();
}

输出:

1
Fri Sep 16 20:04:55 CST 2016

调用有参构造器创建对象

如果没有对应有参数构造器!将发生异常!参数传递错误、将发生异常!

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
/**
 * 调用 className 类名对应的类的有参数构造器,paramTypes 代表对应构造器的参数列表
 * className + paramTypes 共同决定调用哪个构造器!执行构造器还需要具体的参数params
 */
public Object create(String className,Class[] paramTypes,Object[] params)
    throws Exception{
    // 动态加载类
    // 动态获取指定参数类型的构造器
    // 执行这个构造器,传递 params 参数。
    Class cls = Class.forName(className);
    //getDeclaredConstructor 在类信息中查找
    //给定参数类型的构造器信息
    Constructor c = cls.getDeclaredConstructor(paramTypes);
    //执行构造器 c.newInstance() 方法,创建对象
    //返回值就是这个构造器创建的对象
    Object obj = c.newInstance(params);
    return obj;
}
@Test
public void testCreate() throws Exception {
    String className = "java.util.Date";
    //类型列表==Class类型的数组
    Class[] paramTypes = {long.class};
    //实际参数列表
    Object[] params={-1000L*60*60*24*365};
    Object obj = create(className, paramTypes, params);
    System.out.println(obj);
    //思考:如何动态调用 new String("Hello");
    className = "java.lang.String";
    /*
     * {} 只能拥有声明变量时候直接初始化
     * 不能用于赋值语句!
     * 赋值语句可以使用 new Object[]{"Hello"} 
     */
    paramTypes = new Class[]{String.class};
    params = new Object[]{"Hello"};
    obj = create(className, paramTypes, params);
    System.out.println(obj);//Hello
	
    //思考:如何动态调用 new String(byte[],"utf-8");
    obj = create("java.lang.String", 
        new Class[]{byte[].class, String.class}, 
        new Object[]{new byte[]{65,66,67,68}, "UTF-8"});
    System.out.println(obj);
}

输出:

1
2
3
Wed Jan 01 08:00:00 CST 1969
Hello
ABCD​

动态获取类的属性值

实现过程(如何利用反射API实现动态属性访问):

1.找到对象的类型信息(方法区)

2.在信息中找属性信息(Field)

3.在对象上获取属性的值!

类Goo:

1
2
3
4
5
6
7
8
9
10
11
12
public class Goo {
    public int id;
    public String name;
    public Goo() {
        
    }
    public Goo(int id, String name) {
        super();
        this.id = id;
        this.name = name;
    }
}

动态获取属性的值:

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
 /**
 * 获取obj对象的 fieldName 对应属性的值
 * @param obj
 * @param fieldName
 * @return 属性值
 */
public Object get(Object obj, String fieldName)throws Exception{
    //1 获取类信息
    /* 
     * Java 中对象的getClass()方法可以获取 对象的类型信息!
     * Java 中 有3种方法可以获取Class信息
     *  1. 类名.class 获取类信息(静态)
     *  2. Class.forName("类名") 获取类信息 动态 
     *  3. obj.getClass() 获取类信息。运行期间,通过当前对象获取类信息
     */
    Class cls = obj.getClass();
    //找到属性:
    /* 
     * getDeclaredField 按照属性名在cls中查找
     * 类信息。 当属性没有找到时候,抛出异常!
     */
    Field field = cls.getDeclaredField(fieldName);
    //在对象上获取属性的值!
    /*
     * get方法:在一个对象上获取属性的值,对象上没有对应的属性,抛出异常
     */
    Object value = field.get(obj);
    return value;
}  
 
@Test
public void testGetField()throws Exception {
    /*
     * 动态获取对象的属性
     */
    Goo goo = new Goo(5, "Tom");
    Object v1 = get(goo, "id");
    Object v2 = get(goo, "name");
    System.out.println(v1+" "+v2);
}

输出:

1
5 Tom

动态调用类的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
@Test
public void testinvoke()throws Exception{
    List<String> list=new ArrayList<String>();
    list.add("tom");
    list.add("jack");
    // 动态获取类信息
    Class cls = list.getClass();
    // 通过方法名和参数类型找到对应的方法
    Method method = cls.getDeclaredMethod("remove", new Class[]{int.class});
    // 调用方法,传递对象和具体参数
    Object value=method.invoke(list,new Object[]{0});
    System.out.println(value); //tom
}

反射的意义

常见的框架的底层都是使用反射实现的!如:Spring MyBatis Struts2 Hibernate …

现有application.xml文件:

1
2
3
4
5
<?xml version="1.0" encoding="UTF-8"?>
<beans>
    <bean id="date" class="java.util.Date"></bean>
    <bean id="list" class="java.util.ArrayList"></bean>
</beans>

模拟Spring框架的getBean()方法:

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
public class ApplicationContext {
    private HashMap<String, Object> map= new HashMap<String, Object>();	
    public ApplicationContext(String xml) {
        SAXReader reader = new SAXReader();
        try{
            //读取xml
            InputStream in = this.getClass().getClassLoader().getResourceAsStream(xml);
            Document doc = reader.read(in);
            //解析XML内容 获取全部的<bean>
            List<Element> beans = doc.getRootElement().elements();
            for (Element e : beans) {
                //e 是每个 <bean> 元素
                String id = e.attributeValue("id");
                String className = e.attributeValue("class");
                //利用反射创建对象
                Class cls = Class.forName(className);
                Object obj = cls.newInstance();
                //对象缓存到 map中
                map.put(id, obj);
            }
			
        }catch(Exception e){
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }
    public Object getBean(String id){
        return map.get(id);
    }
    public static void main(String[] args) {
        String conf = "application.xml";
        ApplicationContext ac = new ApplicationContext(conf);
        Object o = ac.getBean("date");
        System.out.println(o); //Fri Sep 16 21:30:07 CST 2016
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值