一、反射概述
反射是指對于任何一個Class類,在 “運行的時候”,不用創(chuàng)建對象,就可以直接得到這個類全部成分。
- 在運行時,可以直接得到這個類的構造器對象:Constructor
- 在運行時,可以直接得到這個類的成員變量對象:Field
- 在運行時,可以直接得到這個類的成員方法對象:Method
這種運行時動態(tài)獲取類信息以及動態(tài)調(diào)用類中成分的能力稱為 Java 語言的反射機制。
反射的作用:反射是在運行時獲取類的字節(jié)碼文件對象,然后可以解析類中的全部成分。
反射的關鍵:反射的第一步都是先得到編譯后的 Class 類對象,然后就可以得到 Class 的全部成分。
注意:“類對象”和“類的對象”之間的區(qū)別,“類的對象”是 new 出來對象,而“類對象”是其本身,即 class 文件,是“類”類型。
二、反射獲取類對象
反射的第一步:獲取 Class 類的對象。
三種方式:
Class c1 = Class.forName(“全類名");
Class c2 = 類名.class;
Class c3 = 對象.getClass();
舉例:
public class Test {
public static void main(String[] args) throws Exception {
// 1、Class類中的一個靜態(tài)方法:forName(全限名:包名 + 類名)
Class c = Class.forName("com.itheima.d2_reflect_class.Student");
System.out.println(c); // Student.class
// 2、類名.class
Class c1 = Student.class;
System.out.println(c1);
// 3、對象.getClass() 獲取對象對應類的Class對象。
Student s = new Student();
Class c2 = s.getClass();
System.out.println(c2);
}
}
三、反射獲取構造器對象
步驟:
獲取構造器的方法:
Constructor類中用于創(chuàng)建對象的方法:
注:反射可以破壞封裝性,私有的也可以執(zhí)行了。
舉例:
public class Student {
private String name;
private int age;
// 構造器私有化
private Student(){
System.out.println("無參數(shù)構造器執(zhí)行!");
}
public Student(String name, int age) {
System.out.println("有參數(shù)構造器執(zhí)行!");
this.name = name;
this.age = age;
}
...
}
@Test
public void getDeclaredConstructor() throws NoSuchMethodException {
// 1. 第一步:獲取類對象
Class c = Student.class;
// 2. 定位單個構造器對象 (按照參數(shù)定位到無參構造器)
Constructor con = c.getDeclaredConstructor();
System.out.println(con.getName() + " ==> " + con.getParameterCount()); // Student.class ==> 0
// 3. 定位單個構造器對象 (按照參數(shù)定位到有參構造器)
Constructor con1 = c.getDeclaredConstructor(String.class, int.class);
System.out.println(con1.getName() + " ==> " + con1.getParameterCount()); // Student.class ==> 2
}
@Test
public void getDeclaredConstructors(){
// 1. 第一步:獲取類對象
Class c = Student.class;
// 2. 提取類中的全部構造方法
Constructor[] constructors = c.getDeclaredConstructors();
// 3. 遍歷構造器
// 拿到所有的構造器
for (Constructor con : constructors) {
System.out.println(con.getName() + " ==> " + con.getParameterCount());
}
}
暴力反射舉例:
@Test
public void getDeclaredConstructor() throws Exception {
// 1. 第一步:獲取類對象
Class c = Student.class;
// 2. 定位單個構造器對象 (按照參數(shù)定位到無參構造器)
Constructor con = c.getDeclaredConstructor();
// 遇到私有的構造器對象,可以暴力反射
con.setAccessible(true);
Student student = (Student) con.newInstance();
System.out.println(student); // Student{name='null', age=0}
}
四、反射獲取成員變量對象
步驟:
反射的第一步是先得到類對象,然后從類對象中獲取類的成分對象。
Class類中用于獲取成員變量的方法:
Field類中用于取值、賦值的方法:
注:某成員變量是非public的,需要打開權限(暴力反射),然后再取值、賦值。
舉例:
public class Student {
private String name;
private int age;
public static String schoolName;
public static final String COUNTTRY = "中國";
....
}
@Test
public void getDeclaredField() throws Exception {
// 1. 第一步:獲取類對象
Class c = Student.class;
// 2. 第二步:根據(jù)名稱定位某個成員變量
Field f = c.getDeclaredField("age");
System.out.println(f.getName() +" ===> " + f.getType()); // age ===> int
}
@Test
public void getDeclaredFields(){
// 1. 第一步:獲取類對象
Class c = Student.class;
// 2. 第二步:根據(jù)名稱定位某個成員變量
Field[] fields = c.getDeclaredFields();
// 3. 第三步:遍歷
for (Field field : fields) {
System.out.println(field.getName() + " ==> " + field.getType());
// name ==> class java.lang.String ...
}
}
暴力反射舉例:
@Test
public void setField() throws Exception {
// 1. 第一步:反射第一步,獲取類對象
Class c = Student.class;
// 2. 第二步:提取某個成員變量
Field ageF = c.getDeclaredField("age");
ageF.setAccessible(true); // 暴力打開權限
// 3. 第三步:賦值
Student s = new Student();
ageF.set(s, 18); // 相當于 s.setAge(18);
System.out.println(s);
// 4. 第四步:取值
int age = (int) ageF.get(s);
System.out.println(age);
}
五、反射獲取方法對象
步驟:
獲取成員方法的方法:
Method類中用于觸發(fā)執(zhí)行的方法:
注:某成員方法是非 public 的,需要打開權限(暴力反射),然后再取值、賦值。
舉例:
public class Dog {
public void run(){
System.out.println("狗跑的賊快~~");
}
private void eat(){
System.out.println("狗吃骨頭");
}
private String eat(String name){
System.out.println("狗吃" + name);
return "吃的很開心!";
}
public static void sleep(){
System.out.println("狗在睡覺!");
}
}
@Test
public void getDeclaredMethods(){
// 1. 第一步:獲取類對象
Class c = Dog.class;
// 2. 第二步:提取全部方法;包括私有的
Method[] methods = c.getDeclaredMethods();
// 3. 第三步:遍歷全部方法
for (Method method : methods) {
System.out.println(method.getName() +" 返回值類型:"
+ method.getReturnType() + " 參數(shù)個數(shù):" + method.getParameterCount());
}
// getName 返回值類型:String 參數(shù)個數(shù):0 ...
}
暴力反射舉例:
@Test
public void getDeclaredMethod() throws Exception {
// 1. 第一步:獲取類對象
Class c = Dog.class;
// 2. 第二步:提取單個方法對象
Method eat = c.getDeclaredMethod("eat");
Method eat2 = c.getDeclaredMethod("eat", String.class);
// 暴力打開權限了
eat.setAccessible(true);
eat2.setAccessible(true);
// 3. 第三步: 觸發(fā)方法的執(zhí)行
Dog d = new Dog();
// 注意:方法如果是沒有返回值,那么返回的是null。
Object result = eat.invoke(d); // 狗吃骨頭
System.out.println(result); // null
Object result2 = eat2.invoke(d, "骨頭"); // 狗吃骨頭
System.out.println(result2); // 吃的很開心!
}
六、 反射的作用
6.1 繞過編譯階段為集合添加數(shù)據(jù)
繞過泛型約束:泛型只是在編譯階段可以約束集合只能操作某種數(shù)據(jù)類型,編譯成Class文件后,進入運行階段的時候,泛型會自動擦除。
反射是作用在運行時的技術,此時集合的泛型將不能產(chǎn)生約束了,此時是可以為集合存入其他任意類型的元素。
舉例:
public class Test {
public static void main(String[] args) throws Exception {
// 需求:反射實現(xiàn)泛型擦除后,加入其他類型的元素
ArrayList<String> lists1 = new ArrayList<>();
ArrayList<Integer> lists2 = new ArrayList<>();
System.out.println(lists1.getClass() == lists2.getClass()); // true ArrayList.class
ArrayList<Integer> lists3 = new ArrayList<>();
lists3.add(23);
lists3.add(22);
// lists3.add("黑馬"); // 報錯
Class c = lists3.getClass(); // ArrayList.class ===> public boolean add(E e)
// 定位類對象 c 中的 add 方法
Method add = c.getDeclaredMethod("add", Object.class); // 第二個參數(shù)代表此時 add方法中參數(shù)是任意類型
boolean rs = (boolean) add.invoke(lists3, "黑馬");
System.out.println(rs); // true
System.out.println(lists3); // [23, 22, 黑馬]
}
}
舉例2:變量被賦值后,泛型也會被擦除
public class Test2 {
public static void main(String[] args) {
// 需求:反射實現(xiàn)泛型擦除后,加入其他類型的元素
ArrayList<Integer> list1 = new ArrayList<>();
list1.add(23);
list1.add(22);
// list.add("黑馬"); // 報錯
ArrayList list2 = list1;
list2.add("白馬");
list2.add(false);
System.out.println(list2); // [23, 22, 白馬, false]
}
}
6.2 通用框架的底層原理
需求:給定任意一個對象,在不清楚對象字段的情況可以,可以把對象的字段名稱和對應值存儲到文件中去。
public class MyBatisUtil {
/**
* 保存任意類型的對象
*/
public static void save(Object obj){
try (
PrintStream ps = new PrintStream(new FileOutputStream("day11-oop/src/data.txt", true));
){
// 提取到這個對象的全部成員變量,只有反射可以解決
Class c = obj.getClass(); // c.getSimpleName() 獲取當前類名 c.getName() 獲取全限名:包名+類名
ps.println("================" + c.getSimpleName() + "================");
// 提取到它全部成員變量
Field[] fields = c.getDeclaredFields();
// 獲取成員變量的信息
for (Field field : fields) {
field.setAccessible(true);
String name = field.getName();
String value = field.get(obj) + "";
ps.println(name + " ==> " + value);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
public class Test {
public static void main(String[] args) {
Student s = new Student();
s.setName("豬八戒");
s.setClassName("西天跑路1班");
s.setAge(1000);
s.setHobby("吃,睡");
s.setSex('男');
MyBatisUtil.save(s);
Teacher t = new Teacher();
t.setName("波仔");
t.setSex('男');
t.setSalary(6000);
MyBatisUtil.save(t);
}
}
測試結果:
文章來源:http://www.zghlxwxcb.cn/news/detail-730429.html
文章參考:Java入門基礎視頻教程,java零基礎自學就選黑馬程序員Java入門教程(含Java項目和Java真題)文章來源地址http://www.zghlxwxcb.cn/news/detail-730429.html
到了這里,關于Java 基礎進階篇(十七):反射概述及獲取對象的方式的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!