反射详细篇

一、反射介绍

反射机制:Java反射机制在运行状态,对于任意一个类,都能够获取到这个类的所有属性和方法,对于任意一个对象,都能够调用它的任意一个方法和属性(包括私有的方法和属性),这种动态获取的信息以及动态调用对象的方法的功能就称为java语言的反射机制。通俗点讲,通过反射,该类对我们来说是完全透明的,想要获取任何东西都可以。

(面试讲:通过反射,类对我们来说都是完成透明的,可以动态的获取类的属性和方法,可以动态的调用对象的属性和方法)

加载概述:当程序要使用某个类时,若类还没有加载到内存时,则系统会通过加载、连接、初始化三步来实现对这个类进行初始化。

二、3种方式反射获取字节码文件对象

想要使用反射机制,就必须要先获取到该类的字节码文件对象(.class),通过字节码文件对象,就能够通过该类中的方法获取到我们想要的所有信息(方法,属性,类名,父类名,实现的所有接口等等),每一个类对应着一个字节码文件也就对应着一个Class类型的对象,也就是字节码文件对象。

通过反射获取各个时期的字节码文件对象 

  • Person.java源文件                                (Class clazz = Class.forName("全限定类名"),获取字节码文件对象)
  • Person.class字节码文件                     ( Class clazz = Person.class ,获取字节码文件对象)
  • Person p = new Person()实例对象     ( Person clazz = p.getClass(),获取字节码文件对象)

三、反射功能

下面讲解的都是对于Student对象的操作,通过Student字节码对象clazz来进行操作。

public class Student {
	public Student() {}
	public Student(String username, String sex) {
		this.username = username;
		this.sex = sex;
	}
	private String username;
	private String sex;
	public String getUsername() {
		return username;
	}
	public void setUsername(String username) {
		this.username = username;
	}
	public String getSex() {
		return sex;
	}
	public void setSex(String sex) {
		this.sex = sex;
	}
	@Override
	public String toString() {
		return "Student [username=" + username + ", sex=" + sex + "]";
	}
}

1、通过反射对构造方法操作

                /*反射对构造方法操作*/
		Class clazz = Class.forName("com.xue.test.Student");
		//无参构造方法
		Student stu = (Student) clazz.newInstance();                        //通过反射实例化Student对象
		stu.setSex("女");
		stu.setUsername("美丽");
		System.out.println(stu);
		//有参构造方法
		Constructor myCon = clazz.getConstructor(String.class, String.class);//获取指定有参构造
		Student stu2 = (Student)myCon.newInstance("张三","男");
		System.out.println(stu2);

2、通过反射对属性操作

                /*反射对属性进行操作*/
		Field field = clazz.getDeclaredField("sex");				//暴力反射对私有变量sex
		field.setAccessible(true); 						//去除私有权限
		field.set(stu2, "女");
		System.out.println(stu2);

3、通过反射对方法进行操作

		/*反射对方法进行操作*/
		//Method method = clazz.getMethod("sex");				//针对无参方法
		Method method = clazz.getMethod("setSex", String.class);		//针对有参方法
		method.invoke(stu2, "男");
		System.out.println(stu2);

我在这介绍了三种必须掌握并能使用的操作反射方法。还有更多的方法(获取类的接口,指定类的输出流等等)基本上比较少使用,我就不作多于的解释。

四、反射应用案例

1、ArrayList<Integer>的一个对象,在这个集合中添加一个字符串数据,如何实现?(泛型擦除,泛型反射)

	private static void demo1()
			throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException {
		ArrayList<Integer> list = new ArrayList<>();
		list.add(1);
		list.add(2);
		//泛型只在编译期有效,在运行期会被擦除掉 
		Class clazz = Class.forName("java.util.ArrayList");//获取字节码对象
		Method m = clazz.getMethod("add", Object.class);    //通过字节码对象获取add()方法
		m.invoke(list, "aaa");                              //通过反射对list添加字符串"aaa"
		System.out.println(list);
	}

2、此方法可以将obj对象中propertyName的属性的值设置为value,public void setProperty(Object obj, String propertyName, Object value){}

	public static void setProperty(Object obj, String propertyName, Object value) throws Exception, SecurityException {
		Class clazz = obj.getClass();                    //获取传进来的字节码对象
		Field f = clazz.getDeclaredField(propertyName);  //通过字节码对象获取propertyName属性
		f.setAccessible(true);                           //将其属性设置为可见(private-public)
		f.set(obj, value);                               //将obj对象propertyName属性值设置为value
	}

3、动态代理

代理:本来自己应该做的事情,请了别人来做,被请的人就是代理对象
java.lang.reglect包下提供了一个Proxy类和InvocationHandler接口,通过使用这个类和接口就可以生成动态代理对象,只能对接口做代理

(1)、实现InvocatiionHandler接口类

public class MyInvocation implements InvocationHandler {
	private Object obj;
	public MyInvocation(Object obj) {
		this.obj = obj;
	}
	
	public Object createProxy() {
		Object ob = Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), this);
		return ob;
	}
//反射中调用方法得调用invoke,对这个方法重写,当调用别的方法时,写执行下列重写方法,在重写方法里面在选择合适时机调用别的方法
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		Object result;
		System.out.println("准备");
		result = method.invoke(obj, args);
		System.out.println("完事");
		return result ;
	}

}

(2)、接口与实现类

//定义接口
interface Animal {
	void eat();
	
	void sleep();
}

//实现接口
public class Dog implements Animal {

	@Override
	public void eat() {
		System.out.println("狗吃饭");
	}

	@Override
	public void sleep() {
		System.out.println("狗睡觉");
	}
}

(3)、使用动态代理

package com.xue.test;

/**
 * 动态代理Dog类
 * @author 
 *
 */
public class Test15 {
	public static void main(String[] args) {
		Animal dog = new Dog();
		Animal an = (Animal)new MyInvocation(dog).createProxy();
		an.eat();
		an.sleep();
	}
}

五、总结

这些都是反射机制的基础,虽然在SE中体现不了多大的用处,但到了EE学习各种框架时,如Spring动态代理就是其中一个运用到反射的机制,掌握反射能让你更轻松学习各类框架。而我先前对反射了解不多(先学了EE),现在算是反过来对反射进行了一个系统的学习。对于将要学习EE的朋友们,这个知识点也算是SE的尽头,希望你们重视这一个知识点的学习,不要懈怠。

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值