java反射

反射reflect知识点用于编写框架,框架开发者需要重点掌握。


什么是反射?

反射的概念是由 Smith 1982 年首次提出的,主要是指程序可以访问、检测和修改它本身状态或行为的一种能力。
JAVA 反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法;这种动态获取的信息以及动态调用对象的方法的功能称为 java 语言的反射机制。
平常情况我们通过 new关键字 来生成一个类的实例,但有时候我们没法直接 new ,只能通过反射动态生成。

什么是动态语言?

程序运行时,允许改变程序结构或变量类型,这种语言称为动态语言”。从这个观点看, Perl Python Ruby 是动态语言, C++ Java C# 不是动态语言。但是java却通过Reflection 映射机制实现了动态语言的特性。

一个标准类中无非就是三个部分,构造器、属性、方法。

构造器:创建对象,完成对象初始化。

属性:   封装数据。

方法:   实现功能,执行功能。

java反射机制提供了对这三部分的具体操作方法。

现有一员工类:

public class Employee {
	
	public String name ="Jerry";
	private int pass;
	
	public	Employee(){
	}

	private	Employee(List<?> list){
	}

	public	Employee(String name, int pass){
	}
	
	private void add(){
	} 
    
	private void add(String str,int b){
	}
    
	private void arr(String [] str){
	}
}

反射第一步:加载类 (有三种方式),获取类的字节码文件并将其加载到内存中。

		 //方式一
		 Class className1 = Class.forName("com.test.Employee.java");
		 //方式二
		 Class className2 = new Employee().getClass();
		 //方式三
		 Class className3 = Employee.class;

反射第二步:反射类中的对象,调用对应的方法反射指定的构造or方法or属性

调用getConstructors()方法,反射类中指定的构造函数。                 

调用getMethod()方法,反射类中指定的方法。                                    

调用getField()方法,反射类中指定的属性。

以上三种方法只针对调用的对象修饰符为public时使用

getDeclaredConstructor();

getDeclaredMethod();

getDeclaredField();

以上三种方法可用于调用的对象修饰符为private时使用

反射第三步:操作对象

创建测试类,创建测试类需要junit jar包的支持。

public class Test {
	
	/**反射无参构造*/
	@org.junit.Test
	public void test() throws Exception{
		//加载类
		Class className = Employee.class;
		//反射员工类的无参构造
		Constructor c = className.getConstructor(); 
		//创建实例,因为反射的是无参构造,newInstance无需传值
	    Employee e = (Employee) c.newInstance();
	}
	
	/**反射多参构造*/
	@org.junit.Test
	public void test2() throws Exception{
		Class className = Employee.class;
		//反射员工类的多参构造,构造参数传类对象,其他类型同理
		Constructor c = className.getConstructor(String.class,int.class);
		//创建实例,因反射的是多参构造,需传入构造方法的参数
	    Employee e = (Employee) c.newInstance("hello",211);
	}
	
	/**反射构造参数为List集合,且为private构造*/
	@org.junit.Test
	public void test3() throws Exception{
		Class className = Employee.class;
		//构造方法参数为List集合,且为private构造
		Constructor c = className.getDeclaredConstructor(List.class);
		c.setAccessible(true); //暴力反射,如果调用的构造方法为private,则使用此方法强制反射
		Employee e = (Employee) c.newInstance(new ArrayList());
	}
	
	/**反射add的无参方法*/
	@org.junit.Test
	public void test4() throws Exception{
		Class className = new Employee().getClass();
		//反射add的无参方法
		Method method =  className.getDeclaredMethod("add");
		method.setAccessible(true); //强制反射
		//invoke()方法运行对象,invoke方法第一个参数是对象,后面的参数传add方法的参数
		Employee e = (Employee) method.invoke(new Employee());
	}
	
	/**反射add的多参方法*/
	@org.junit.Test
	public void test5() throws Exception{
		Class className = new Employee().getClass();
		//反射add的多参方法
		Method method =  className.getDeclaredMethod("add",String.class, int.class);
		method.setAccessible(true);
		Employee e = (Employee) method.invoke(new Employee(),"hello", 16);
	}
	
	/**反射 arr数组参方法*/
	@org.junit.Test
	public void test6() throws Exception{
		Class className = new Employee().getClass();
		//反射arr数组参方法
		Method method =  className.getDeclaredMethod("arr",String[].class);
		method.setAccessible(true);
		/*因javaAPI版本问题,因此会将传入String数组拆分成三个String对象,但被反射
		的类中没有三个String类型参数的arr方法,因此会报错找不到该方法,此时可将该数组对象
		强转为Object类型,欺骗JVM将该数组对象当成一个参数来解析,则JVM就不会拆分该数组,因此
		当反射一个形参为数组类型的方法时,在传参时将传入的数组对象强转为Object类型即可 */
		Employee e = (Employee) method.invoke(new Employee(),(Object)new String[]{"hello","world","haha"});
	}
	
	/**反射获取字段*/
	@org.junit.Test
	public void test7() throws Exception{
		Class className = new Employee().getClass();
		//拿取员工类的name字段
		Field f = className.getField("name");
		//要获取name得要说明是哪个对象的属性,此处传入员工对象,f接收到的name为String类型,则此处用String接收
		String str = f.get(new Employee()).toString();
	}
	
	/**通过反射设置字段的值*/
	@org.junit.Test
	public void test8() throws Exception{
		Employee e= new Employee();
		Class className = new Employee().getClass();
		//拿取员工类的name字段
		Field f = className.getField("name");
		f.set(e, "hello ");
		System.out.println(e.name);	//结果为hello
	}
	
	/**反射获取private字段的值*/
	@org.junit.Test
	public void test9() throws Exception{
		Class className = new Employee().getClass();
		//拿取员工类的pass字段,private修饰用Declared取值
		Field f = className.getDeclaredField("pass");
		f.setAccessible(true); //强制反射
		//f是取自Employee的对象
		int i = (int)f.get(new Employee());
		System.out.println(i);
	}
}

此处涉及一面试知识点: private修饰的对象在类外部是否可以访问(除过get/set)?

答:在外部类中不能访问私有对象,但是java反射可以,可通过setAccessible(true)方法设置强制反射。

通常情况即使是当前类,private属性或方法也是不能访问的,你需要设置压制权限setAccessible(true)

来取得private的访问权。但这已经破坏了面向对象的规则,所以除非万不得已,尽量少用。


java反射的应用:Spring框架 IOC(控制反转)、Hibernate框架:关联映射、白盒测试等。

Spring框架的IOC简化实现:



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值