反射介紹
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. 通過setAccessible讓jvm不檢查權限
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, 簡單來說就是用於封裝數據的
規範:
類使用公共進行修飾
提供私有修飾的成員變量
爲成員變量提供公共getter和setter方法
提供公共無參的構造
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. 將jar包Build 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();
}
}
}