深入解析Java反射原理

1、什么是反射

Java反射就是在运行时,对于任意一个类,程序都能获取这个类的所有属性和方法。对于任意一个对象,程序都能调用它的所有方法和属性。这种动态获取类的信息和动态调用对象方法和属性的功能成为Java的反射机制。
Java反射就是把类的各种成分映射成一个个Java对象
要想解析一个类,首先需要获取到这个类的字节码文件对象,也就是.class文件。而且一个类有且只有一个.class文件。

2、怎么获取类的字节码文件

Java提供了四种方式来获取字节码文件对象。如cn.com.kk.TestClass这个类
(1)知道具体的类时

 Class tem = TestClass.class;

通过此方法获取的Class对象不会初始化。
(2)通过实例对象获取Class对象

TestClass test = new TestClass();
Class tem = test.getClass();

(3)通过类的加载器获取

Class clazz = ClassLoader.LoadClass("cn.com.kk.TestClass");

通过此方法获取的Class对象不会初始化。
(4)通过Class.forName()传入类路径获取(推荐)

Class tem = Class.forName("cn.com.kk.TestClass");

Class.forName()方法内部调用的是一个native方法 forName0(className, true, ClassLoader.getClassLoader(caller), caller);第二个参数表示类是否需要初始化,Class.forName(className)方法默认是需要初始化的。
一旦初始化,就是出发类的static代码块执行,static参数也会初始化。

3、如何使用反射

先设定一个Person类

package cn.com.kk.test;
public class Person {
	private Integer age;
	protected String name;
	public Boolean sex;
	Double money;
	
	Person(String name) {};
	public Person(){}
	public Person(Integer age) {
	}
	public Person(Integer age, String name) {
		super();
		this.age = age;
		this.name = name;
	}
	protected Person(Integer age, String name, String sex) {
	}
	private Person(Boolean sex) {
	}
	
	public String getName() {
		return name;
	}
	protected boolean getSex() {
		return sex;
	}
	private Integer setAge(Integer age) {
		this.age = age;
		return age;
	}
    // 测试main方法的反射
    public static void main(String[] args) {
		System.out.println("main方法执行了。。。");
	}
}

通过反射获取并操作这个类的相关属性和方法

public class Test {

	public static void main(String[] args) throws Exception {
		Class clazz = Class.forName("com.jiuqi.kk.test.Person");
		
		// 调用构造方法
		// 获取所有的公有构造方法
		Constructor[] conArray = clazz.getConstructors();
		for (Constructor con : conArray) {
			System.out.println(con);
		}
		System.out.println("----------------------------------------");
		
		// 获取所有的构造方法
		conArray = clazz.getDeclaredConstructors();
		for (Constructor con : conArray) {
			System.out.println(con);
		}
		System.out.println("----------------------------------------");
		
		// 获取公有、无参的构造方法
		Constructor con = clazz.getConstructor(null);
		System.out.println(con);
		Object obj = con.newInstance(); // 实例化一个Person对象
		System.out.println("----------------------------------------");
		
		// 获取私有、有参的构造方法
		con = clazz.getDeclaredConstructor(Boolean.class);
		System.out.println(con);
		con.setAccessible(true); // 暴力访问,忽略访问修饰符
		obj = con.newInstance(true); // 调用构造方法
		System.out.println("----------------------------------------");
		
		// 调用方法
		// 获取所有的公有方法
		Method[] methods = clazz.getMethods();
		for (Method m : methods) {
			System.out.println(m);
		}
		System.out.println("----------------------------------------");
		
		// 获取所有的方法
		methods = clazz.getDeclaredMethods();
		for (Method m : methods) {
			System.out.println(m);
		}
		System.out.println("----------------------------------------");
		
		// 获取公有的getName()方法
		Method m = clazz.getMethod("getName", null);
		System.out.println(m);
		System.out.println("----------------------------------------");
		
		// 获取私有的setAge()方法
		m = clazz.getDeclaredMethod("setAge", Integer.class);
		System.out.println(m);
		Object o = clazz.getConstructor().newInstance(); // 实例化一个person对象
		m.setAccessible(true); // 忽略私有修饰符
		Object result = m.invoke(o, 24); // 调用方法给参数赋值,第一个是要调用的对象(反射获取),
		                                 //第二个是传入的实参。返回值是该方法的返回值。
		System.out.println(result); // 24

        // 获取main方法
		Method methodMain = clazz.getMethod("main", String[].class);//第一个参数:方法名称,第二个参数:方法形参的类型,
		//3、调用main方法
		// methodMain.invoke(null, new String[]{"a","b","c"});
		//第一个参数,对象类型,因为方法是static静态的,所以为null可以,
		//第二个参数是String数组,这里要注意在jdk1.4时是数组,jdk1.5之后是可变参数
		//这里拆的时候将  new String[]{"a","b","c"} 拆成3个对象。。。所以需要将它强转。
		methodMain.invoke(null, (Object)new String[]{"a","b","c"});
	}
}
4、反射的实际运用

(1)通过反射运行配置文件内容
还是以Person类为例,相关配置文件pro.txt如下

className = com.jiuqi.kk.test.Person
methodName = setAge
methodValue = 25

测试类

public class Test {
    public static void main(String[] args) throws Exception {
		//通过反射获取Class对象
		Class stuClass = Class.forName(getValue("className"));
		//2获取setAge()方法
		Method m = stuClass.getMethod(getValue("methodName"), Integer.class);
		//3.调用setAge()方法
		Object o = m.invoke(stuClass.getConstructor().newInstance(), 
		                    Integer.valueOf(getValue("methodValue")));
		System.out.println(o); // 25
	}
	
	//此方法接收一个key,在配置文件中获取相应的value
	public static String getValue(String key) throws IOException{
		Properties pro = new Properties();//获取配置文件的对象
		FileReader in = new FileReader("pro.txt");//获取输入流
		pro.load(in);//将流加载到配置文件对象中
		in.close();
		return pro.getProperty(key);//返回根据key获取的value值
	}
}

(2)通过反射越过泛型检查
Java泛型是伪泛型,因为Java在编译期间,所有的泛型信息会被擦除掉,也叫类型擦除。
所以可以通过反射来越过泛型检查

public class Test {
    public static void main(String[] args) throws Exception {
        List<String> list = new ArrayList<String>();
        list.add("aa");
        list.add("bb");
        System.out.println(list);// [aa,bb]
		//通过反射获取Class对象
		Class clazz = list.getClass();
		//2获取add()方法
		Method m = clazz.getMethod("add", Object.class);
		//3.调用add()方法
		m.invoke(list, 25);
		System.out.println(list); // [aa,bb,25]
	}
}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值