反射(reflect)应用详解

反射的概念

反射:通过Class获取字节码文件对象,使用字节码文件对象来获取类的一些描述信息

  • 成员变量 Field
  • 成员方法 Method
  • 构造方法 Constructor

Java文件 -编译-> 字节码文件 -运行-> JVM执行Class文件(类加载)

类加载

  • 当我们执行java.exe命令,这个字节码文件会被加载到内存当中
  • 同时还会为这个字节码文件创建一个Class的实例

获取字节码文件对象的方式三种

1.Class类中一个静态方法

static Class<?> forName(String className) 
			方式一的好处: 因为传入的是一个字符串,而这个字符串可以通过配置文件配置
			满足了设计原则: 开闭原则  对扩展开放,对修改关闭

2.通过数据类型的静态属性 class属性

 * 			如果形参需要我们传入Class类型,那么我们可以考虑使用方式二
 * 			public void show(Class c){}
 * 			show(int.class)

3.Object的getClass方法
如下案例:

public class T_getClass {
//获取class对象的3种方法
	public static void main(String[] args) throws Exception {
		// TODO Auto-generated method stub
		//方式一(常用)-------------------------
		Class<Person> pcla = (Class<Person>) Class.forName("com.ljx.tx0911.Person");
		System.out.println(pcla);
		//方式2
		Class<Person> pcla2=Person.class;
		System.out.println(pcla2);
		//方式3
		Person person=new Person();
		Class<? extends Person> cla3 = person.getClass();
		System.out.println(cla3);				
	}
}

class类的方法:

通过字节码文件对象获取构造方法对象 Constructor

  •  	Constructor<?>[] getConstructors() // 获取公有修饰的构造方法对象
    
  •  	Constructor<?>[] getDeclaredConstructors() // 获取所有的构造方法对象,含构造
    
  •  	Constructor<T> getConstructor(Class<?>... parameterTypes) //获取单个构造方法
    
  •  	Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) 	//获取私有单个构造方法
    

获取成员变量对象

  •  	Field[] getDeclaredFields()  获取所有成员变量对象
    
  •  	Field getDeclaredField(String name) 获取指定任意成员变量对象-》获取指定的成员变量,返回类型和名称
    

获取任意方法:

  •   	Method dm =pc.getDeclaredMethod("setId", int.class);
    
  •   	myc.getDeclaredMethod("service", null);   还有的形参对应的是null  说明service方法没有参数
    
  •   	Method[] dms = pc.getDeclaredMethods();//获取所有方法
    

生成Person对象:

  •   	pc.newInstance();
    
利用calss类方法创建对象的3中方式
	/*
		 * 获取Person对象的三种方式:
		 * 方式1、2步骤:
		 * 		1、获取class对象-》class.forname(uri)  
		 * 		2、获取特定构造器/构造器数组-》用class的getConstructor(类.class)
		 * 		3、获取对象-》用构造器的newInstance
		 * 方式3、步骤:
		 * 		1、获取class对象-》class.forname(uri) 
		 * 		2、获取对象-》用class对象的newInstance();//形参只能是空的,调用的是构造器的newInstance(null)
		 */
		Class<Person> pc = (Class<Person>) Class.forName("com.ljx.tx0911.Person");
		Constructor<Person> c = pc.getConstructor(String.class,int.class);
		//创建对象(有参),对应的是构造方法中的有参           ------------1
		Person p = c.newInstance("ljx",1);
		
		
		//创建对象(无参),对应的是构造的无参      ----------------------2
		Constructor<Person> c2 = pc.getConstructor(null);
		Person p2=c2.newInstance(null);
		
		//创建对象,直接用class来创建(实质还是利用无参构造方法对象来创建的) ----------------3
		Person p3 = pc.newInstance();       
		System.out.println(p3);
		
		System.out.println("--------------------------");

Constructor构造器的方法:

  •    c.toString://打印信息->public com.ljx.tx0911.Person()
    
  •   c.getName():获取构造方法名com.ljx.tx0911.Person
    
  •  c.getParameterCount():对应的方法中形参个数
    
  •  c.getModifiers():权限修饰符  1->public 2->private  4->protected 
    

所有案例演示的主方法和Person类:

主方法:只看第四行即可,目的是获取class对象

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		try {
			Class<Person> pc = (Class<Person>) Class.forName("com.ljx.tx0911.Person");
			//private java.lang.String com.ljx.tx0911.Person.name 
			//System.out.println(fname);
			//WriteFanme(fname);
			//Fieldtest(pc);	
			getMethods(pc);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}		
	}

Person类:

public class Person {
	private String name;
	private int id;
	@Override
	public String toString() {
		return "Person [name=" + name + ", id=" + id + "]";
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public Person(String name, int id) {
		super();
		this.name = name;
		this.id = id;
	}
	public Person() {
		super();
	}
	
}

案例演示
public static void test01(Class<Person> pc) throws Exception {
		Constructor<Person>[] cs = (Constructor<Person>[]) pc.getConstructors();
		for (Constructor<Person> c : cs) {
			System.out.println(c);
			System.out.println(c.getName()+"  "+c.getParameterCount()+"   "+c.getModifiers());//
		}
		System.out.println("-------------");
		/*	打印信息:
		 *  public com.ljx.tx0911.Person()
			com.ljx.tx0911.Person  0   1
			public com.ljx.tx0911.Person(java.lang.String,int)
			com.ljx.tx0911.Person  2   1
		 */
	}

Field类方法结合 (class对象对属性的操作)

通过反射获取成员变量对象

利用class对象获取Field的方法:

 * Field[] getFields()  获取公有修饰的成员变量对象-----不用!!
 * Field[] getDeclaredFields()  获取所有成员变量对象
 * Field getField(String name) 获取指定的公共成员变量对象--------不用!!
 * Field getDeclaredField(String name) 获取指定任意成员变量对象-》获取指定的成员变量,返回类型和名称

Field方法:

  • Object get(Object obj) 返回指定对象上此 Field 表示的字段的值。

  • f.getname->获取属性名

  • f.getType->获取属性类型

  • 给对象赋值:set(Object obj, Object value) 例子:f.set(e2, map.get(“name”));//e2为对象,value是String将指定对象变量上此 Field 对象表示的字段设置为指定的新值。(最底下的案例有体现

  • setInt/setChar… (类似上面的函数)

  • 判断是否有注解: f.isAnnotationPresent(myAnnotion.class)

  • 获取注解对象:tostring打印结果@com.ljx.Override.myAnnotion(value=林惊羽)-》在下一章节注解中会详细讲解

Field操作成员变量
获取所有成员变量信息

	public static  void Fieldtest(Class<Person> pc) throws Exception{
/*
 *  * Field[] getDeclaredFields()  获取所有成员变量对象
	* Field getDeclaredField(String name) 获取指定任意成员变量对象-》获取指定的成员变量,返回类型和名称
	 
 */		
		Field[] dfs = pc.getDeclaredFields();
		for (Field f : dfs) {
			System.out.println(f);
		}
		/*打印信息如下:
		 *  private java.lang.String com.ljx.tx0911.Person.name
			private int com.ljx.tx0911.Person.id
		 */
	}
利用反射生成getName方法保存在文件中(放在a.txt中)
private static void WriteFanme(Field f) {//f:含有Person的name属性以及String类型
	// TODO Auto-generated method stub
	StringBuffer sb = new StringBuffer();
	String name=f.getName();//name
	String ftype=f.getType().getName();//java.lang.String
	System.out.println(name+"  "+ftype);//name  java.lang.String
	System.out.println("-----------------------------------");
	sb.append("public  ").append(ftype+"  ").append("get");
	sb.append(name.substring(0, 1).toUpperCase()).append(name.substring(1)).append("()");
	sb.append("{\r\n").append("\treturn ").append(name).append(";\r\n}");
	System.out.println(sb);
	String s = new String(sb);
	File fi = new File("a.txt");
	try {
		BufferedWriter bw = new BufferedWriter(new FileWriter(fi) );
		bw.write(s);
		bw.flush();
	} catch (IOException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}	
}

打印信息如下,且分割线之后的代码写入文件中!(这里的StringBuffer转String不熟练!!!

name  java.lang.String
-----------------------------------
public  java.lang.String  getName(){
	return name;
}

Method类方法结合 (class对象对方法的操作)

Method类的获取

利用class对象获取如下
class获取方法:

  •   	Method dm =pc.getDeclaredMethod("setId", int.class);//class获取任意方法
    
  •  	myc.getDeclaredMethod("service", null);   还有的形参对应的是null  说明service方法没有参数
    
  •   	Method[] dms = pc.getDeclaredMethods();//获取所有方法
    

利用字节码对象生成Person对象:

  •   	pc.newInstance();
    
Method类的方法:

调用方法模板:

  •   invoke(Object obj, Object... args) 对带有指定参数的指定对象调用对应方法
    

调用不带参数的方法(调用加获取)

  •   Method dm = pc.getDeclaredMethod("getId", null);
    
  •   System.out.println(dm.invoke(p, null));	
    

调用带参数的方法:(调用加获取)

  •   Method m1 = pc.getDeclaredMethod("setId", int.class);
    
  •   m1.invoke(p, 18);
    

获取方法名称

  •   getName() 以 String 形式返回此 Method 对象表示的方法名称
    

获取所有方法:

  •   Method[] dms = pc.getDeclaredMethods();
    
案例:获取方法名、利用反射调用方法、获取方法数组
	public static void getMethods(Class<Person> pc) throws Exception{
		Person p = pc.newInstance();
		Method m1 = pc.getDeclaredMethod("setId", int.class);
		Method m2=pc.getDeclaredMethod("setName", String.class);
		m1.invoke(p, 18);
		m2.invoke(p, "连接线");
		System.out.println(m1.getName());//setId
		System.out.println(p);//Person [name=连接线, id=18]
				
		Method[] dms = pc.getDeclaredMethods();
		for (Method m : dms) {
			System.out.println(m);
		}
	}

打印信息:

setId
Person [name=连接线, id=18]
public java.lang.String com.ljx.tx0911.Person.toString()
public java.lang.String com.ljx.tx0911.Person.getName()
public int com.ljx.tx0911.Person.getId()
public void com.ljx.tx0911.Person.setName(java.lang.String)
public void com.ljx.tx0911.Person.setId(int)

反射结合Properties

目的:利用反射获取properties中的类和方法信息,调用该类的方法

Properties类中的方法

(前提条件:先在src下创建reflect.properties)

  •  Properties ps = new Properties();
    
  •  ps加载字节流,读取文件入内存
    
  •  ps.load(new FileInputStream(new File("src/reflect.properties")));
     获取ps中的配置信息(key->value)
    
  •   String classpath=ps.getProperty("classPath");	
    
案例演示:
//目的:利用反射获取properties中的类和方法信息,调用该类的方法
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Properties ps = new Properties();
		try {
			ps.load(new FileInputStream(new File("src/reflect.properties")));
			String classpath=ps.getProperty("classPath");		
			String method=ps.getProperty("method");
			System.out.println(method);
			//方法一:
			Class<MyServlet> myc = (Class<MyServlet>) Class.forName(classpath);
			Constructor<MyServlet> cs = myc.getConstructor(null);
			MyServlet my = cs.newInstance(null);
			my.service();//实现调用
			
			//方法2:
			Method m2 = myc.getDeclaredMethod("service", null);
			m2.invoke(my, null);//实现调用
			
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

reflect.properties文件:

classPath=com.ljx.tx0911.MyServlet
method=service

反射结合map生成对象

以下案例有两种方法:建议仔细阅读

  •   方式1  利用方法调用(invoke)-》setId/setName  设置值进对象中
    
  •   //方式2利用Field的set方法将属性注入进对象中(就算是属性是Private修饰也一样能注入!!!!(很强))
    

原因:方式2中f.setAccessible(true);//跳过private安全检查

案例:通过反射 把一个map转成对象
public class T_06reflect {
//通过反射 把一个map转成对象
	public static void main(String[] args) {
		// TODO Auto-generated method stub
			HashMap<String, String> map = new HashMap<>();
			map.put("id", "001");
			map.put("name", "张小凡");
			Emp e =mapToEmp(map);
			System.out.println(e);		
			
	}



@Test
private static Emp mapToEmp(HashMap<String, String> map) {
	// TODO Auto-generated method stub
	Emp e=null;
	try {
		Class<Emp> ec = (Class<Emp>) Class.forName("com.ljx.tx0911.Emp");
		Constructor<Emp> c = ec.getConstructor();
		 e = c.newInstance();
		 
		//方式1  利用方法调用(invoke)-》setId/setName  设置值进对象中
		 Method[] dms = ec.getDeclaredMethods();
		 Method m1 = ec.getDeclaredMethod("setId", String.class);
		 Method m2 = ec.getDeclaredMethod("setName", String.class);
		 m1.invoke(e, map.get("id"));
		 m2.invoke(e, map.get("name"));
		 System.out.println(e);//Emp [name=张小凡, id=001]
		 System.out.println("--------------------");

		//方式2利用Field的set方法将属性注入进对象中(就算是Private修饰也一样能注入!!!!(很强))
		 //通过比较f的name和Person中的name是否一致来向对象设置值
		 Emp e2=ec.newInstance();
		 Field[] fs = ec.getDeclaredFields();
		 for (Field f : fs) {
			String key=f.getName();			
			f.setAccessible(true);//跳过private安全检查
			if(f.getName().equals("name")){
				f.set(e2, map.get("name"));
			}
			if(f.getName().equals("id")){
				f.set(e2, map.get("id"));
			}
		}
		 System.out.println("打印e2:");
		 System.out.println(e2);//Emp [name=张小凡, id=001]
		 	
	} catch (Exception e1) {
		// TODO Auto-generated catch block
		e1.printStackTrace();
	}
	return e;
}
}
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值