反射機制的概述和字節碼對象的獲取方式 JavaBean的概述&BeanUtils的使用 反射操作構造方法、成員方法、成員屬性

 反射介紹

JAVA反射機制是在運行狀態中,

對於任意一個類,都能夠知道這個類的所有屬性和方法,

對於任意一個對象,都能夠調用它的任意一個方法 

這種動態獲取的以及動態調用對象的方法的功能稱爲java語言的反射機制.


  簡單來說, 就可以把.class文件比做動物的屍體, 而反射技術就是對屍體的一種解剖.

  通過反射技術, 我們可以拿到該字節碼文件中所有的東西, 例如成員變量, 成員方法, 構造方法, 而且還包括私有

package com.demo.test_02;

/**
 * 
 * @author Administrator
 * 
 *   反射:
 *   
 *      在運行時,我們可以獲取任意一個類的所有方法和屬性
 *      在運行時,我們可以調用任意一個對象的所有方法和屬性
 *      
 *      反射的前提:
 *         要獲取類的字節碼對象(Class對象)
 *   
 *
 */

public class ReflectDemo {

	public static void main(String[] args) throws Exception {		
		/**
		 * 
		 * 字節碼對象獲取的三種方式:
		 *  	1.對象.getClass()		該方法來自Object對象已經存在的情況下可以使用這種方式
		 *  	2.類名.class			類名.class這是一個靜態屬性只要知道類名就可以獲取字節碼對象
		 *  	3.Class.forName("com.demo.test")  通過Class類中的靜態方法,指定字符串  該字符串就會是(包名+類名)
		 *  					同樣此處會拋出異常ClassNotFoundException 防止傳入錯誤的類名		
		 */
		
		// 第一種方式:
		Student s = new Student();
		Class<? extends Student> clazz = s.getClass();
		
		// 第二種方式:
		Class	 calzz1=	Student.class;
		
		
		//第三種方式:
		Class	 clazz2 = Class.forName("com.demo.test_02.Student");
		
		/**
		 * 字節碼對象是用來描述什麼的?
		 * 
		 * 		用來描述.class文件的
		 * 		這裏我們也可以用面向對象的思想來說明:Java中描述事物都是通過類來描述的
		 * 		而字節碼對象也可以看做是事物,那如何描述這種事物呢:
		 * 			那就看這種事物有什麼組成的
		 * 			1.成員變量
		 * 			2.成員方法
		 * 			3.構造方法
		 * 
		 */			
	}
}

 反射操作構造方法             

    通過獲取的構造創建對象

    步驟:

    1.獲得Class對象

    2獲得構造

    3.通過構造對象獲得實例化對象

package com.demo.test_02;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

/**
 * 
 * @author Administrator
 * 
 *         反射操作構造方法 步驟: 1.獲得Class對象 2.獲得構造 3.通過構造獲得實例化對象
 * 
 *         直接使用Class類中的newIntance()和獲取的getConstructor()的區別就是: newInstance()方法,
 *         只能通過空參的構造方法創建對象 getConstructor(Class<T>… parameterTypes)方法,
 *         方法接受一個可變參數, 可以根據傳入的類型來匹配對應的構造方法
 *
 */

public class ReflectDemo2 {

	public static void main(String[] args) throws Exception {

		Class clazz = Class.forName("com.demo.test_02.Student");

		// mthoed_01(clazz);
		// method_02(clazz);

		method_03(clazz);

		// 通過字節碼對象實例化對象
		Object stu = clazz.newInstance();

		// System.out.println(stu);

	}

	private static void method_03(Class clazz) throws NoSuchMethodException, InstantiationException,
			IllegalAccessException, IllegalArgumentException, InvocationTargetException {

		// 獲得有參構造
		Constructor constructor = clazz.getConstructor(String.class, int.class);
		Object stu = constructor.newInstance("小明", 23);
		System.out.println(constructor);
		System.out.println(stu);
	}

	private static void method_02(Class clazz) throws NoSuchMethodException, Exception, IllegalAccessException,
			IllegalArgumentException, InvocationTargetException {

		// 獲得字節碼對象的無參構造
		Constructor constructor = clazz.getConstructor();
		Object stu = constructor.newInstance();
		System.out.println(constructor);
		System.out.println(stu);

	}

	private static void mthoed_01(Class clazz) {

		// 獲得所有的public 修飾的構造方法
		Constructor[] constructors = clazz.getConstructors();

		for (Constructor constructor : constructors) {
			System.out.println(constructor);
		}
	}
}

直接通過Class類中的newInstance()和獲取getConstructor()有什麼區別?

newInstance()方法, 只能通過空參的構造方法創建對象

getConstructor(Class<T>… parameterTypes)方法, 方法接受一個可變參數, 可以根據傳入的類型來匹配對應的構造方法

總結

Constructor<?>[] getConstructors()
                Constructor<T> getConstructor(Class<?>... parameterTypes) 
            
                方法1: 獲取該類中所有的構造方法, 返回的是一個數組
                方法2: 方法接受一個可變參數, 可以根據傳入的類型, 來匹配對應的構造方法




 反射操作公共成員變量

         反射public成員變量(字段) 

        通過反射運行public變量流程

        1. 通過反射獲取該類的字節碼對象

      Class clazz = Class.forName("com.demo.Person");

        2. 創建該類對象

        Object p = clazz.newInstance();

        3. 獲取該類中需要操作的字段(成員變量)

        getField(String name) --> 方法傳入字段的名稱.

    注意: 此方法只能獲取公共的字段

            Field f = clazz.getField("age");

        4. 通過字段對象中的方法修改屬性值

        void set(Object obj, Object value)  --> 參數1): 要修改那個對象中的字段, 參數2): 將字段修改爲什麼值.

        f.set(ps, 99);

package com.demo.test_02;

import java.lang.reflect.Field;
/*
 * 反射操作公有成員變量
 *    通過反射運行public成員變量流程
 *    	1.通過反射獲取類的字節碼文件
 *    	2.創建該類的對象
 *    	3.獲取要操作的該類的字段
 *    	4.通過字段對象中的方法修改屬性值
 *     		
 */
public class ReflectDemo3 {

	public static void main(String[] args) throws Exception {

		// 1.通過反射獲取類的字節碼文件
		Class<?> clazz = Class.forName("com.demo.test_02.Student");

		// method_01(clazz);				
		// method_02(clazz);		
		// method_03(clazz);				
	}

	private static void method_03(Class<?> clazz) {
		// 獲得所有的public公有的成員變量
		Field[] fields = clazz.getFields();
		for (Field field : fields) {
			System.out.println(field);
		}
	}

	private static void method_02(Class<?> clazz) {
		// 獲取所有的成員變量包括私有
		Field[] declaredFields = clazz.getDeclaredFields();
		for (Field field : declaredFields) {
			System.out.println(field);
		}
	}

	private static void method_01(Class<?> clazz)
			throws InstantiationException, IllegalAccessException, NoSuchFieldException {
		// 2.創建該類的對象
		Object stu = clazz.newInstance();

		// 3.獲取要操作的該類的字段
		Field f = clazz.getField("gender");

		System.out.println(f);

		// 4.通過字段對象中的方法修改屬性值
		f.set(stu, "男");
		Object object = f.get(stu);

		System.out.println(object);
	}
}

 方法總結

             通過反射獲取成員變量並使用  
                     Field[] getFields()              --> 返回該類所有(公共)的字段
                     Field getField(String name)      --> 返回指定名稱字段
            
                    Field[] getDeclaredFields()      --> 暴力反射獲取所有字段(包括私有
                    Field getDeclaredField(String name) --> 暴力反射獲取指定名稱字段                    
            Field:
                      Object get(Object obj)          --> Field對象調用, 返回傳入對象的具體字段
                      void set(Object obj, Object value) -->  Field對象調用
                                                        參數1: 要修改的對象
                                                        參數2: 將此對象的字段修改爲什麼值.


反射操作私有成員變量

     反射private成員變量(字段)

        反射private屬性執行流程

        1. 獲取學生類字節碼對象
        2. 獲取學生對象
        3. 通過getDeclaredField方法獲取私有字段
        4. 通過setAccessiblejvm不檢查權限
        5. 通過set方法設置對象爲具體的值

package com.demo.test_02;

import java.lang.reflect.Field;
/**
 * 反射private屬性執行流程 1. 獲取學生類字節碼對象 2. 獲取學生對象 3. 通過getDeclaredField方法獲取私有字段
 * 4. 通過setAccessible讓jvm不檢查權限 5. 通過set方法設置對象爲具體的值
 *
 */
public class ReflectDemo4 {

	public static void main(String[] args) throws Exception {

		// 1. 獲取學生類字節碼對象
		Class<?> clazz = Class.forName("com.demo.test_02.Student");

		 method_01(clazz);
		 method_02(clazz);
	}

	private static void method_02(Class<?> clazz)
			throws InstantiationException, IllegalAccessException, NoSuchFieldException {
		// 2. 獲取學生對象
		Object stu = clazz.newInstance();

		// 3. 通過getDeclaredField方法獲取私有字段
		Field f = clazz.getDeclaredField("name");
		Field f1 = clazz.getDeclaredField("age");

		// 4. 通過setAccessible讓jvm不檢查權限
		f.setAccessible(true);
		f1.setAccessible(true);

		// 5. 通過set方法設置對象爲具體的值
		f.set(stu, "老王");
		f1.set(stu, 24);
		
		Object name = f.get(stu);
		Object age = f1.get(stu);
		System.out.println(name);
		System.out.println(age);
		
	}
	private static void method_01(Class<?> clazz) throws InstantiationException, IllegalAccessException {
		// 2. 獲取學生對象
		Object stu = clazz.newInstance();

		// 3. 通過getDeclaredField方法獲取私有字段
		Field[] declaredFields = clazz.getDeclaredFields();
		for (Field field : declaredFields) {
			field.setAccessible(true);
			System.out.println(field);
		}
	}
}

 方法總結

               Field[] getDeclaredFields()      --> 暴力反射獲取所有字段(包括私有
               Field getDeclaredField(String name) --> 暴力反射獲取指定名稱字段
               void setAccessible(boolean flag) --> jvm不檢查權限


 通過反射獲取成員方法並使用

反射獲取普通成員方法

反射public方法執行流程

1. 獲取學生類字節碼對象
2. 反射手段創建學生對象
3. 調用getMethod方法獲取Method對象, 方法形參接受方法的名字
4. 調用Method方法中的invoke()將方法運行


package com.demo.test_02;

import java.lang.reflect.Method;

/**
 * 反射public方法執行流程 
 * 1. 獲取學生類字節碼對象 
 * 2. 反射手段創建學生對象 
 * 3. 調用getMethod方法獲取Method對象,方法形參接受方法的名字 
 * 4. 調用Method方法中的invoke()將方法運行
 *
 */

public class ReflectDemo5 {

	public static void main(String[] args) throws Exception {
		
			//1. 獲取學生類字節碼對象
		Class<?> clazz = Class.forName("com.demo.test_02.Student");
		
		
			//2. 反射手段創建學生對象
		Object stu = clazz.newInstance();
		
		//3. 調用getMethod方法獲取Method對象,方法形參接受方法的名字
		Method m = clazz.getMethod("add");
		
		//4. 調用Method方法中的invoke()將方法運行
		m.invoke(stu);
	}

}
package com.demo.test_02;

public class Student {

	private String name;
	private int age;	
	public String gender;

	public Student(String name, int age, String gender) {
		super();
		this.name = name;
		this.age = age;
		this.gender = gender;
	}
	public Student(String name, int age) {
		super();
		this.name = name;
		this.age = age;
	}
	public Student() {
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	@Override
	public String toString() {
		return "Student [name=" + name + ", age=" + age + "]";
	}	
	public void add(){
		System.out.println("helloWorld");
	}	
}

 方法總結

Class:
    Method getMethod(String name, Class<?>... parameterTypes)  
            // 此方法由字節碼對象調用
            // 參數1: 要反射的方法名稱
            // 參數2: 此方法需要接受的參數類型(注意,傳入的都是字節碼)
Method:
    Object invoke(Object obj, Object... args)  
            // 方法由Method對象調用
            // 參數1: 要由那個對象調用方法
            // 參數2: 方法需要的具體實參(實際參數)


 問題私有的成員方法怎麼玩? 

// 獲取字節碼對象

Class clazz = Class.forName("com.demo.Student");

// 創建學生對象

Object stu = clazz.newInstance();

// 暴力反射獲取方法

Method method = clazz.getDeclaredMethod("method");

// jvm不檢查權限

method.setAccessible(true);

// 執行方法

method.invoke(stu);


 JavaBean的概述和規範

JavaBean的概述:

將需要操作的多個屬性封裝成JavaBean, 簡單來說就是用於封裝數據的

規範:

類使用公共進行修飾

提供私有修飾的成員變量

爲成員變量提供公共gettersetter方法

提供公共無參的構造

package com.demo.test_03;

import java.io.Serializable;

public class Student implements Serializable{

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	private String name;

	private int age;

	private String gender;

	public Student() {
	}

	public Student(String name, int age, String gender) {
		super();
		this.name = name;
		this.age = age;
		this.gender = gender;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public String getGender() {
		return gender;
	}

	public void setGender(String gender) {
		this.gender = gender;
	}

	@Override
	public String toString() {
		return "Student [name=" + name + ", age=" + age + ", gender=" + gender + "]";
	}
}

 BeanUtils的概述

BeanUtils的由來 

之前我們使用的類都是來自Java編寫好的源代碼

而這個BeanUtils卻是一個叫做Apache的組織編寫.

那麼這個組織編寫的代碼當中有一個系列可以很方便的提高我們今後的開發效率.

這個系列爲Commons, BeanUtils就是其中之一

 準備工作

1. 導入兩個jar

commons-beanutils-1.8.3.jar

commons-logging-1.1.1.jar

2. jarBuild path 配置到當前的classpath環境變量中

2.3BeanUtils的常用方法
static void    setProperty(Object bean, String name, Object value) 
static String getProperty(Object bean, String name) 
static void    populate(Object bean, Map properties) 
	
	setProperty  用來給對象中的屬性賦值(瞭解)
		參數1: 需要設置屬性的對象
		參數2: 需要修改的屬性名稱
		參數3: 需要修改的具體元素	
	
	getProperty 用來獲取對象中的屬性(瞭解)
		參數1: 要獲取的javaBean對象
		參數2: 對象中的哪個屬性
		
	Populate 用來給對象中的屬性賦值(掌握)

		參數1: 要設置屬性的對象
		參數2: 將屬性以Map集合的形式傳入
					Key : 屬性的名稱
					Value:  屬性具體的值
package com.demo_02;

import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.beanutils.BeanUtils;

/*
 * BeanUtils:Apache commons提供的一個組件,主要功能就是爲了簡化JavaBean封裝數據的操作
 * 			static void	setProperty(Object bean, String name, Object value) 
 * 			static String getProperty(Object bean, String name) 
 * 			static void	populate(Object bean, Map properties) 
 * 
 * 注意:BeanUtils的setProperty和getProperty方法底層並不是直接操作成員變量,而是操作和成員變量名有關的get和set方法
 */
public class BeanUtilsDemo {
	public static void main(String[] args) throws ReflectiveOperationException  {
		//static void	populate(Object bean, Map properties) 
		Person p = new Person();
		
		Map<String,Object> map = new HashMap<String,Object>();
		map.put("name", "laowang");
		map.put("age", 38);
		map.put("gender", "male");
		
		BeanUtils.populate(p,map);
		System.out.println(p);
		
	}

	private static void method() throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
		Person p = new Person();
		//System.out.println(p);
		
		//static void	setProperty(Object bean, String name, Object value) :給JavaBean對象的成員變量進行賦值
		BeanUtils.setProperty(p, "name", "zhangsan");
		//BeanUtils.setProperty(p, "age", 18);
		//System.out.println(p);
		
		//static String getProperty(Object bean, String name) 
		String name = BeanUtils.getProperty(p, "name");
		System.out.println(name);
	}
}

 方法總結

三個方法底層是通過反射實現而且反射操作的是setXxx方法和getXxx方法.

所以編寫JavaBean的時候一定要注意格式

2.4自定義BeanUtils的賦值和獲取方法實現.
2.4.1功能分析
定義MyBeanUtils工具類, 實現與BeanUtils相同的功能

public static void setProperty(Object bean,String name,Object value)
		// 設置任意對象的, 任意屬性, 爲任意的值
public static String getProperty(Object bean,String name)
		// 獲取任意對象的任意屬性
public static void populate(Object bean,Map map)
		// 修改任意對象中的屬性, 爲傳入Map集合中的鍵和值 					
package com.demo_03;

import java.lang.reflect.Field;

public class MyBeanUtils {
	private MyBeanUtils() {}
	
	//public static void setProperty(Object bean,String name,Object value)
	public static void setProperty(Object bean,String name,Object value) throws ReflectiveOperationException {
		//根據JavaBean對象獲取對應的字節碼對象
		Class clazz = bean.getClass();
		//根據字節碼對象獲取對應的Field對象
		Field f = clazz.getDeclaredField(name);
		//設置權限,讓虛擬機不進行訪問的檢查
		f.setAccessible(true);
		//賦值
		f.set(bean, value);
	}
	//public static String getProperty(Object bean,String name)
	public static String getProperty(Object bean,String name) throws ReflectiveOperationException {
		Class clazz = bean.getClass();
		Field f = clazz.getDeclaredField(name);
		f.setAccessible(true);
		Object obj = f.get(bean);
		return obj.toString();
	}
	
}

2.5自定義BeanUtils的populate方法實現
2.5.1功能分析
public static void populate(Object bean,Map map)
// 修改任意對象中的屬性, 爲傳入Map集合中的鍵和值 
思路: 
1.獲取傳入對象的字節碼對象
2.獲取map集合中所有的鍵和值
3.調用Class中的getDeclaredField()方法將每一個鍵傳入, 得到Field對象
4.通過Field對象中的set方法賦值
5.Try catch捕獲getDeclaredField方法可能發生的異常.(爲了方式傳入錯誤的值)
//public static void populate(Object bean,Map map)
	public static void populate(Object bean,Map map) throws ReflectiveOperationException {
		//通過JavaBean對象來獲取對應的字節碼對象
		Class clazz = bean.getClass();
		//獲取Map中所有的key
		Set keys = map.keySet();
		for (Object key : keys) {
			
			try {
				//根據key來獲取對應的Field對象
				Field f = clazz.getDeclaredField(key.toString());
				//根據key來獲取Map中對應的value
				Object value = map.get(key);
				
				f.setAccessible(true);
				f.set(bean, value);
			} catch(NoSuchFieldException e) {
				//e.printStackTrace();
			}
		}
	}







發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章