java反射机制

一.反射的概念及作用

1.概念:通过Java反射机制,可以访问程序中已加载到JVM中的Java对象的描述,实现访问、检测和修改描述Java对象本身信息的功能。在java.lang.reflect包中提供了对该功能的支持。

2.作用:

1.反编译:.class-->.java

        2.通过反射机制访问java对象的属性,方法,构造方法等;

3.sun为我们提供了那些反射机制中的类:

java.lang.Class;    

java.lang.reflect.Constructor; 

java.lang.reflect.Field;        

java.lang.reflect.Method;

java.lang.reflect.Modifier;

面试题:

如何获取类的字节码文件对象,并且有几种方式呢?

1)Object类中的getClass()方法,表示正在运行的那个类:Class类

2)数据类型的class属性  举例:String.class,Student.class

3)Class类中的特有方法:forName(String className):(重点,数据库加载驱动:Drivers)

public static Class<?> forName(String className):获取字节码文件对象(参数是一个字符串...,字符串的内容是一个类的全路径名称)

最常用的方式是方式3)

package reflect;

public class ReflectDemo {

	/**
	 * @param args
	 * @throws Exception 
	 */
	public static void main(String[] args) throws Exception {
		
		//方式一:创建Person对象
		Person p1 = new Person();
		Class c1 = p1.getClass();
		
		//创建对象
		Person p2 = new Person();
		Class c2 = p2.getClass();
		
		System.out.println(p1==p2);//false
		System.out.println(c1==c2);//true
		
		//方式二:通过数据类型的Class属性
		Class c3 = Person.class ;
		System.out.println(c3==c1);//true
		//方式三:参数不是一个类名,而是类的全路径名称
		
		// 获取一个类中的构造方法,首先找到这个类的class文件对象,通过反射
		Class c4 = Class.forName("reflect.Person");
		System.out.println("c4:"+c4); //c4:class reflect.Person
		System.out.println(c4==c1);
		
	}
}
4.通过反射机制得到某个类的构造器,然后调用该构造器创建该类的一个实例 

1)获取构造器

package reflect;

import java.lang.reflect.Constructor;

public class ReflectDemo2 {

	public static void main(String[] args) throws Exception {
		//获取一个类中的构造方法,首先找到这个类的class文件对象,通过反射
		Class c = Class.forName("reflect.Person");
		
		//1.public Constructor<?>[] getDeclaredConstructors():获取的是当前字节码文件对象中所有的构造方法
		Constructor[] con = c.getDeclaredConstructors();
		for(Constructor constructor:con){
			System.out.println(constructor);
		}
		System.out.println("-----------");
		/**
		 * 输出:private reflect.Person(java.lang.String,int,java.lang.String)
		 * reflect.Person(java.lang.String,int)
		 *	public reflect.Person()
		 * 
		 * */
		
		//2.public Constructor<?>[] getConstructors():获取的是当前字节码文件对象中所有的公共的构造方法
		Constructor[] publicCon= c.getConstructors();
		for(Constructor constructor:publicCon){
			System.out.println(constructor);
		}
		System.out.println("-----------");
		
		/**
		 * public reflect.Person()
		 * */		
	}
}
2)获取单个的构造器并创建一个实例

package reflect;

import java.lang.reflect.Constructor;

/**
 * 之前创建对象:
 * 		给对象的成员属性赋值
 * 		Person p = new Person("韩庚",20,"北京")
 * 		System.out.println(p) ;
 * 
 *  下面通过反射获取构造方法并且给成员属性赋值
 *2017年12月13日 
 */
public class ReflectDemo3 {

	public static void main(String[] args) throws Exception {
		// 获取Person类的字节码文件对象
		Class c = Class.forName("reflect.Person");
		
		//通过反射获取指定的构造方法getConstructor(Class ...parameterTyps)
		Constructor con = c.getConstructor(String.class,int.class,String.class);
		
		//创建构造器的实例对象,来给他指定的字节码文件对象里面的成员变量赋值
		Object obj = con.newInstance("韩庚",20,"北京");//实际参数-->通过反射--->Person.class---->getConstructor() 
		System.out.println(obj);
	}
}
3).获取单个的私有构造器并创建实例

在给私有构造器创建实例之前要先调用public void setAccessible(boolean flag):在访问的时候取消java语言访问检查(强制性),参数的值为true则表示反射的对象在使用时应该取消java语言的访问检查。

package reflect;

import java.lang.reflect.Constructor;

/**
 *通过反射获取私有构造器并赋值
 *
 *2017年12月14日 
 */
public class ReflectDemo4 {

	public static void main(String[] args) throws Exception {
		// 获取Person类的字节码文件对象
		Class c1 = Class.forName("reflect.Person");
		
		//通过反射获取指定的构造方法getConstructor(Class ...parameterTyps)
		Constructor con = c1.getDeclaredConstructor(String.class);
		
		//public void setAccessible(boolean flag):在访问的时候取消java语言访问检查(强制性)
		//参数的值为true则表示反射的对象在使用时应该取消java语言的访问检查
		con.setAccessible(true);//在给构造器创建实例对象前应该取消检查
		
		//为构造器创建实例对象
		Object obj = con.newInstance("韩庚");
		
		System.out.println(obj);
		
	}
}
4.通过反射机制得到某个类的成员变量并使用


1)获取字节码文件对象(forName())

2)获取构造器对象(getConstructor())通过无参构造

3)创建构造器实例(newInstance)

4)通过反射获取单个成员变量并赋值(c.getField("address"))

5)调用set()方法赋值

package reflectfield;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;

/**
 * 通过反射获取成员变量并使用
 *	成员变量--->Field
 *2017年12月14日 
 */
public class ReflectDemo {

	public static void main(String[] args) throws Exception {
		// 1)通过反射获取字节码文件对象
		Class c = Class.forName("reflectfield.Person");
		
		/*//2)获取所有的公共的成员变量public Field[] getFields():所有的公共的可访问的字段,返回的是Field对象数组
		Field[] fields = c.getFields();
		//Field[] fields = c.getDeclaredFields() ;
		for(Field f : fields){
			System.out.println(f);
		}*/
		/**
		 * 结果:public java.lang.String reflectfield.Person.address
		 * */
		
		//获取到这个Field对象之前,还获取构造器对象(通过无参构造创建Person类的实例)
		Constructor con = c.getConstructor() ;
		//创建构造器实例
		Object obj = con.newInstance() ;//Person的实例对象
		//System.out.println(obj);
		//通过反射获取单个成员变量并赋值
		Field addressFiled = c.getField("address");
		addressFiled.set(obj, "北京");
		System.out.println(obj);     //Person [name=null, age=0, address=北京]
		
		System.out.println("----------------");
		
		//给name赋值并使用(private)
		Field nameField = c.getDeclaredField("name");
		//给name属性设置值
		nameField.setAccessible(true);
		nameField.set(obj, "韩庚");
		System.out.println(obj);
		
		System.out.println("----------------");
		
		//给age赋值并使用(default)
		Field ageField = c.getDeclaredField("age");
		//ageField.setAccessible(true);
		ageField.set(obj, 18);
		System.out.println(obj);		
	}
}
5.通过反射得到某个类的成员方法并使用


package reflectmethod;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

public class ReflectDemo {

	public static void main(String[] args) throws Exception {
		//反射获取字节码文件对象
		Class c = Class.forName("reflectmethod.Person");
		
		//获取成员方法(Method)
		//public Method[] getMethods():获取当前该字节码文件对象(Person.class)中自己本身以及它父类中所有的公共成员方法
		//public Method[] getDeclaredMethods():获取当前字节码文件对象本身所有的成员方法
		Method[] methods = c.getDeclaredMethods();
		for(Method m : methods){
			System.out.println(m);
		}
		
		System.out.println("-------------------------");
		
		//获取构造器对象,通过构造器创建当前字节码文件的实例对象
		Constructor con = c.getConstructor();
		Object obj = con.newInstance();   //Person对象
		
		//获取单个成员方法
		Method m1 = c.getMethod("show");
		/**
		 * public Object invoke(Object obj, Object... args)
		 * 参数1:表示当前针对哪个实例对象进行方法的调用
		 * 参数2:当前调用该方法的时候里面传递的实际参数
		 */
		m1.invoke(obj) ;
		System.out.println("-------------------------");
		
		//调用getString()方法
		Method m2 = c.getMethod("getString", String.class,int.class);
		Object objString = (String)m2.invoke(obj, "hello",100) ;
		System.out.println(objString);
		
		System.out.println("-------------------------");
		
		//调用私有的fun()
		Method m3 = c.getDeclaredMethod("fun");
		m3.setAccessible(true);//取消Java语言的访问检查
		m3.invoke(obj);
	}
}
练习 给ArrayList<Integer>的一个对象,在这个集合中添加一个字符串数据

已经给定了对象,直接用对象代调用getClass()方法获取字节码文件对象,用字节码文件对象调用方法,不用获取构造器并且通过构造器创建当前字节码文件的实例对象

package reflectest;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;

public class ArrayListTest {

	/**
	 * 给ArrayList<String>的一个对象,我想在这个集合中添加一个字符串数据,如何实现呢?
	 * 
	 * 反射的应用:
	 * 		1)在集合中应用
	 * 		2)用来反射区读取配置文件来加载里面的内容(MySQL,Oracle等等,SQLServer,MangoDB)
	 * @param args
	 * @throws SecurityException 
	 * @throws NoSuchMethodException 
	 * @throws InvocationTargetException 
	 * @throws IllegalArgumentException 
	 * @throws IllegalAccessException 
	 */
	public static void main(String[] args) throws 
		NoSuchMethodException,
		SecurityException,
		IllegalAccessException,
		IllegalArgumentException,
		InvocationTargetException {
		//创建ArrayList对象
		ArrayList<String> arry = new ArrayList<String>();
		
		//利用反射类给集合中添加字符串
		
		//获取ArrayList类的字节码文件
		Class c = arry.getClass();
		
		//通过反射获取公共访问的方法
		Method m = c.getMethod("add", Object.class);
		
		//调用底层invoke(当前字节码文件对象的实例,实际参数)
		m.invoke(arry, "hello");
		m.invoke(arry, "world");
		m.invoke(arry, "java");
		
		System.out.println(arry);		
	}
}

6.反射的应用-设置配置文件

需要创建一个.txt文件,以键值对的方式写内容

package reflectest;

import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Properties;


public class Test {

	public static void main(String[] args) throws 
	IOException, ClassNotFoundException, 
	NoSuchMethodException, SecurityException, 
	InstantiationException, IllegalAccessException,
	IllegalArgumentException, InvocationTargetException {
		//文本文件是一种键值对象的形式
		//要将文本文件中的内容加载到属性集合类中
		
		//创建属性集合类对象
		Properties prop = new Properties();
		FileReader fr = new FileReader("class.txt");
		//属性集合类的加载方法:load()
		prop.load(fr);
		fr.close();
		
		//通过键获得值
		String className = prop.getProperty("className");
		String methodName = prop.getProperty("methodName");
		
		//将反射应用到配置文件中
		Class c = Class.forName(className);
		//通过反射获取构造器对象,并通过构造器对象创建该类的实例
		Constructor con = c.getConstructor();
		Object obj = con.newInstance();
		
		//获取成员方法
		Method m = c.getMethod(methodName);
		m.invoke(obj);
	}

}


package reflectest;

public class Student {
	public void show(){
		System.out.println("student study!");
	}
}

练习:1:写一个方法, public void setProperty(Object obj, String propertyName, Object value){}, 此方法可将obj对象中名为propertyName的属性的值设置为value

package reflectest;

import java.lang.reflect.Field;

/**
 * 1:写一个方法,
 * public void setProperty(Object obj, String propertyName, Object value){},
 * 此方法可将obj对象中名为propertyName的属性的值设置为value
*/
public class Tool {
	public void setProperty(Object obj, String propertyName, Object value) throws 
	NoSuchFieldException, SecurityException, 
	IllegalArgumentException, IllegalAccessException{
		
		//获取类的字节码文件对象
		Class c = obj.getClass();
		
		//获取Field对象
		Field field = c.getDeclaredField(propertyName);
		
		防止出现	IllegalAccessException:设置Java语言取消访问检查
		field.setAccessible(true);
		field.set(obj, value);
	}
}

package reflectest;

public class ToolDemo {

	public static void main(String[] args) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
		
		//创建Person类对象
		Person p = new Person();
		
		// 创建工具类对象
		Tool tool = new Tool();
		
		//设置name属性
		tool.setProperty(p, "name", "韩庚");
		tool.setProperty(p, "age", 18);
		System.out.println(p);

	}

}

class Person{
	private String name;
	private int age;
	
	@Override
	public String toString() {
		return name + "----" + age ;
	}	
}
二.java的动态代理机制

在java的动态代理机制中,有两个重要的类或接口,一个是InvocationHandler(interface)、另一个则是Proxy(class),这一个类和接口是实现我们动态代理所必须用到的。下面是javaAPI文档对这两者的描述:

InvocationHandler:

每一个动态代理类都必须要实现InvocationHandler这个接口,并且每个代理类的实例都关联到了一个handler,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的 invoke 方法来进行调用。我们来看看InvocationHandler这个接口的唯一一个方法 invoke 方法:

Object invoke(Object proxy, Method method, Object[] args) throws Throwable
proxy:  指代我们所代理的那个真实对象
method:  指代的是我们所要调用真实对象的某个方法的Method对象
args:  指代的是调用真实对象某个方法时接受的参数

先了解完Proxy,下面可以通过实例加深了解

Proxy:

Proxy这个类的作用就是用来动态创建一个代理对象的类,它提供了许多的方法,但是我们用的最多的就是  newProxyInstance  这个方法:

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,  InvocationHandler h)  throws IllegalArgumentException
这个方法的作用就是得到一个动态的代理对象,其接收三个参数,

loader:  一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载
interfaces:  一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了
h:  一个InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上

上面介绍完了有关的接口和类,以下通过实例加深对动态代理的理解
首先定义一个UserDao的接口,声明四种方法
package reflectHander;

public interface UserDao {

	//定义四个方法
	//增
	public abstract void add() ;
	//删
	public abstract void delete() ;
	//update:修改
	public abstract void update() ;
	//查
	public abstract void serach() ;
}
接着,定义一个类来实现这个接口,这个类就是我们真实的对象
package reflectHander;

public class UserDaoImpl implements UserDao {

	@Override
	public void add() {
		System.out.println("增加功能");

	}

	@Override
	public void delete() {
		System.out.println("删除功能");

	}

	@Override
	public void update() {
		System.out.println("修改功能");

	}

	@Override
	public void serach() {
		System.out.println("查询功能");

	}

}
下一步,我们就定义一个动态代理类,前面说过每一个动态代理必须实现 InvocationHandler 这个接口
package reflectHander;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class MyInvocationHandler implements InvocationHandler{
	//这个是我们要代理的真实对象
	private Object target;
	
	//构造方法:给我们要代理的对象赋初值
	public MyInvocationHandler(Object target) {
		this.target = target;
	}
	
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		// 权限校验
		System.out.println("权限校验");
		//当代理对象调用真实对象的方法时,其自动的跳转到代理对象关联的handler对象的invoke方法来进行调用
		Object result = method.invoke(target, args) ;
		//日志记录
		System.out.println("日志记录");
		return result; //返回值就是代理对象
	}
}
最后来看一下测试类

package reflectHander;

import java.lang.reflect.Proxy;

public class Test {

	public static void main(String[] args) {
		// 创建用户操作的对象(要代理的真实对象)
		UserDao ud = new UserDaoImpl();
//		ud.add();
//		ud.delete();
//		ud.update();
//		ud.serach();
		
		System.out.println("--------");
		//给ud设置代理对象
		//要代理哪个真实对象,就将该对象作为参数传进去,最后是通过该真实对象来调用其方法的
		MyInvocationHandler handler = new MyInvocationHandler(ud) ;
		
		UserDao proxy = (UserDao) Proxy.newProxyInstance(ud.getClass().getClassLoader(), ud.getClass().getInterfaces(),handler);
		
		proxy.add();
		proxy.delete();
		proxy.update();
		proxy.serach();
			
	}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值