Java反射

一、   概念

  一句话说明反射“反射就是把Java类中的各种成分映射成相应的Java”。如果你对反射足够的了解,相信你一定会对这句话有很多共鸣。

  换句话说:一个类中的每个成员都可以用相应的反射API类的一个实例对象来表示(如下图),通过调用Class类的方法(getField、getMethod、getConstructor等)可以得到这些实例对象。


  


       也就是说一个Java类用一个Class类的对象表示,一个类中的组成部分:成员变量、方法、构造方法、包等等信息也用一个个的Java类来表示。就像汽车是一个类,汽车中的发动机,变速箱等等也是一个个的类。表示Java类的Class类,显然要提供一系列的方法,来获得其中的变量,方法,构造方法,修饰符,包等信息,这些信息就是用相应类的实例对象来表示,它们是Field、Method、Contructor、Package等等.

二、    理解Class类

       Java程序中各个Java类属于同一类事物,描述这一类事物的Java的类名就是Class(注意区别小写的class)。

       例如,Person类代表人,它的实例对象就是“张三”、“李四”这样一个个具体的人;同理,Class类代表Java类,它的实例对象对应各个类在内存中的字节码,如:Person类的字节码、ArrayList类的字节码等。

       获得字节码(Class类型)的三种方式如下:


  


  根据下图理解字节码的概念:


  


三、    几种常用的反射应用

  下面我们简单介绍构造方法、成员变量、成员方法的反射应用:

  3.1   构造方法的反射应用(Constructor类)

    Contructor类代表某个类中的一个构造方法。

    得到某个类所有的构造方法:

    例:Contructor[ ]constructors=Class.forName(“java.lang.String”).getConstructors();

    得到某个类的某一个构造方法:

    例:Contructor[ ]constructors=Class.forName(“java.lang.String”).getConstructors(StringBuffer.class);

    示例代码如下:

package com.tgb.constructorReflect;

import java.lang.reflect.Constructor;

public class TestConstructorReflect {

	public static void main(String[] args) throws Exception {
		// 1、通常情况(非反射)
		String str1 = new String(new StringBuffer("zhipeng"));
		// 2、反射情况
		Constructor<String> constructor = String.class.getConstructor(StringBuffer.class);
		String str2 = (String) constructor.newInstance(new StringBuffer("zhipeng"));
	}
}

  一个类有多个构造方法,用什么方式可以区分清楚想得到其中的哪个方法呢?根据参数的个数和类型,例如,CIass.getMethod(name,CIass…args)中的args参数就代表所要获取的那个方法的各个参数的类型的列表。重点:参数类型用什么方式表示?用Class实例对象。例如:int.Class、int[ ].Class等。

       Class.newInstance()方法内部就是先得到类的默认的构造方法对象的字节码,然后用它创建实例对象。

  3.2   成员变量的反射应用(Field类)

    Field类代表某一个类中的成员变量。

    示例代码1如下:

    先定义一个ReflectPoint类

public class ReflectPoint {
	private int x;// 注意x为private的
	public int y;

	public ReflectPoint(int x, int y) {
		super();
		this.x = x;
		this.y = y;
	}
}

    然后我们先拿到ReflectPoint的类的字节码,然后取出它某一属性的字节码,进而就可以得到这个类任何一个对象的相应的属性值:

import java.lang.reflect.Field;

public class TestFieldReflect {
	/**
	 * author: zhipeng
	 */
	public static void main(String[] args) throws Exception {
		ReflectPoint pt1 = new ReflectPoint(1, 2);
		ReflectPoint pt2 = new ReflectPoint(10, 12);

		Field fieldY = ReflectPoint.class.getField("y");// 拿到ReflectPoint【类】的是属性名为y的定义(字节码)

		System.out.println("pt1--->y--->" + fieldY.get(pt1));// 输出对象pt1的属性y:2
		System.out.println("pt2--->y--->" + fieldY.get(pt2));// 输出对象pt2的属性y:12

		Field fieldX = ReflectPoint.class.getDeclaredField("x");// x为private的,所以要用getDeclaredField
		fieldX.setAccessible(true);// 暴力反射
		System.out.println(fieldX.get(pt1));// 输出对象pt1的属性x:1

	}
}

    上面我们通过【FieldfieldY = ReflectPoint.class.getField("y");】拿到的Field对象是对应到类上面的成员变量,还是对应到对象上的成员变量呢?我们分析,类只有一个,而类的实例对象有多个,如果是与对象关联,那么关联的是哪个对象呢?所以fieldX代表的是x的定义,而不是具体的x变量。也就是说我们拿到的是x在ReflectPoint.class中对应的定义(字节码),要用它去获取某一个实例对象身上对应的属性值。

    示例代码2如下:

    首先定义一个Student类

/**
 * @author wangzhipeng
 */
public class Student {

	// 1、定义两个Field
	public String userName;
	public String password;

	// 2、构造方法,给两个Field赋值
	public Student(String username, String password) {
		this.userName = username;
		this.password = password;
	}

	// 3、重写它的toString方法
	@Override
	public String toString() {
		return "用户名:" + this.userName + "  密码:" + this.password;
	}

}

    然后,我们通过反射实现,将任意一个Student对象中属性值中的字母“a”改为“【b】”,代码如下:

import java.lang.reflect.Field;

public class TestChangeStringValue {
	public static void main(String[] args) throws IllegalArgumentException, IllegalAccessException {
		Student zhipeng = new Student("akobe", "a123");
		System.out.println(zhipeng);// 修改属性前,输出:用户名:akobe 密码:a123
		ChangeStrValue(zhipeng);// 调用更改属性的方法
		System.out.println(zhipeng);// 修改属性后,输出:用户名:【b】kobe 密码:【b】123

	}

	public static void ChangeStrValue(Object object) throws IllegalArgumentException, IllegalAccessException {
		// 1、通过反射,拿到一个类所有的属性
		Field[] fields = object.getClass().getDeclaredFields();
		// 2、循环变量属性,如果属性类型为String则将其属性中的字母"a"改为"【b】"
		for (Field field : fields) {
			if (field.getType() == String.class) {
				String oldValue = (String) field.get(object);
				String newValue = oldValue.replace("a", "【b】");
				field.set(object, newValue);
			}
		}

	}
}

  3.3   成员方法的反射应用(Method类)

    Method类代表某个类中的一个成员方法。


  


    示例代码1如下:

    首先定义一个Student类,里面有一个静态和一个非静态方法:

/**
 * @author wangzhipeng
 */
public class Student {

	// 1、定义一个静态方法Study()
	public static void Study() {
		System.out.println("static method--->Study()");
	}

	// 2、定义一个非静态方法(String songName)
	public void Sing(String songName) {
		System.out.println("not static method--->Sing():" + songName);
	}
}

    然后我们反射执行这两个方法,注意它们的差异:

import java.lang.reflect.Method;

public class MethodReflectTest {
	public static void main(String[] args) throws Exception {
		// 0、获得Student的class
		Class clazz = Student.class;

		// 1、反射执行Student类的【非静态】方法:Sing(String songName)
		Method methodSing = clazz.getMethod("Sing", String.class);// 第二个参数为参数的【类型】
		methodSing.invoke(new Student(), "冰雨");// 非静态方法属于某个对象,所以第一个参数为一个Student的对象。

		// 2、反射执行Student类的【静态】方法:static void Study()
		Method methodStudy = clazz.getMethod("Study", null);
		methodStudy.invoke(null, null);// 静态方法属于类,所以第一个参数为null;而这个方法没有参数,所以第二个方法为null
	}

}

    示例代码2如下:

    我们通过反射来执行某一类的main方法.

    首先定义一个Teacher类:

public class Teacher {

	// 定义static的main方法
	public static void main(String[] args) {
		for (String arg : args) {
			System.out.println(arg);
		}
	}
}

    然后我们写一个工具类,执行任何类的main方法:

	// 工具类,执行传入的类的main方法
	public static void ExecuteMainMehod(Class clazz, String[] argStrings) throws Exception {
		Method teacherMainMethod = clazz.getMethod("main", String[].class);
		// teacherMainMethod.invoke(null, new String[] { "aaa", "bbb"
		// });//报wrong number of arguments(即参数个数异常)

		teacherMainMethod.invoke(null, new Object[] { argStrings });// 需要将argStrings包一层再传入,因为jdk1.4默认会将该参数拆箱
	}

    测试,执行Teacher类的main方法:

	public static void main(String[] args) throws Exception {
		String[] argStrings = new String[] { "aaa", "bbb" };
		ExecuteMainMehod(Teacher.class, argStrings);// 输出:aaa
		// bbb
	}


四、    用途

  反射从java1.2就有了。它最大的用途应该是用来写框架,为什么呢?首先我们要理解,我们用框架(例如:Spring),其实是框架在调用我们的类。但是,框架已经写好了,而被调用的(我们的)类却还没写,这与我们的顺序思维有些不同。框架之所以能够调用还不存在的类,就意味着它离不开反射。

  当你的类还没有写时,我就能调用,只要你运行时写出来就行,这就是反射所做的事情。例如Spring:


  


  类似上面的spring配置,我们在所有的框架配置文件中都要配置类的名称【属性的名称】等,不用想就知道是为了反射。


五、    总结

       反射就是把Java类中的各种成分映射成相应的Java类,例如:将构造方法映射为Constructor类;将属性映射为Field类;将方法映射为Method类。

  我们接触到的反射的最大的用途应该是用来写框架,我们在Spring、Struts等等框架的配置文件中都要配置我们的业务类的类名、属性名等,就是为了运行时反射出它的对象或者属性值、方法等。因为框架都是要调用我们那些还没有写的不存在的业务类,所以它离不开反射。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值