1. 反射
1.1 反射的概述:
? 專業(yè)的解釋:
? 是在運行狀態(tài)中,對于任意一個類,都能夠知道這個類的所有屬性和方法;
? 對于任意一個對象,都能夠調(diào)用它的任意屬性和方法;
? 這種動態(tài)獲取信息以及動態(tài)調(diào)用對象方法的功能稱為Java語言的反射機制。
? 通俗的理解:
-
利用反射創(chuàng)建的對象可以無視修飾符調(diào)用類里面的內(nèi)容
-
可以跟配置文件結(jié)合起來使用,把要創(chuàng)建的對象信息和方法寫在配置文件中。
讀取到什么類,就創(chuàng)建什么類的對象
讀取到什么方法,就調(diào)用什么方法
此時當需求變更的時候不需要修改代碼,只要修改配置文件即可。
1.2 學習反射到底學什么?
反射都是從class字節(jié)碼文件中獲取的內(nèi)容。
- 如何獲取class字節(jié)碼文件的對象
- 利用反射如何獲取構(gòu)造方法(創(chuàng)建對象)
- 利用反射如何獲取成員變量(賦值,獲取值)
- 利用反射如何獲取成員方法(運行)
1.3 獲取字節(jié)碼文件對象的三種方式
- Class這個類里面的靜態(tài)方法forName(“全類名”)(最常用)
- 通過class屬性獲取
- 通過對象獲取字節(jié)碼文件對象
代碼示例:
//1.Class這個類里面的靜態(tài)方法forName
//Class.forName("類的全類名"): 全類名 = 包名 + 類名
Class clazz1 = Class.forName("com.itheima.reflectdemo.Student");
//源代碼階段獲取 --- 先把Student加載到內(nèi)存中,再獲取字節(jié)碼文件的對象
//clazz 就表示Student這個類的字節(jié)碼文件對象。
//就是當Student.class這個文件加載到內(nèi)存之后,產(chǎn)生的字節(jié)碼文件對象
//2.通過class屬性獲取
//類名.class
Class clazz2 = Student.class;
//因為class文件在硬盤中是唯一的,所以,當這個文件加載到內(nèi)存之后產(chǎn)生的對象也是唯一的
System.out.println(clazz1 == clazz2);//true
//3.通過Student對象獲取字節(jié)碼文件對象
Student s = new Student();
Class clazz3 = s.getClass();
System.out.println(clazz1 == clazz2);//true
System.out.println(clazz2 == clazz3);//true
1.4 字節(jié)碼文件和字節(jié)碼文件對象
java文件:就是我們自己編寫的java代碼。
字節(jié)碼文件:就是通過java文件編譯之后的class文件(是在硬盤上真實存在的,用眼睛能看到的)
字節(jié)碼文件對象:當class文件加載到內(nèi)存之后,虛擬機自動創(chuàng)建出來的對象。
? 這個對象里面至少包含了:構(gòu)造方法,成員變量,成員方法。
而我們的反射獲取的是什么?字節(jié)碼文件對象,這個對象在內(nèi)存中是唯一的。
1.5 獲取構(gòu)造方法
規(guī)則:
? get表示獲取
? Declared表示私有
? 最后的s表示所有,復數(shù)形式
? 如果當前獲取到的是私有的,必須要臨時修改訪問權(quán)限,否則無法使用
方法名 | 說明 |
---|---|
Constructor<?>[] getConstructors() | 獲得所有的構(gòu)造(只能public修飾) |
Constructor<?>[] getDeclaredConstructors() | 獲得所有的構(gòu)造(包含private修飾) |
Constructor getConstructor(Class<?>… parameterTypes) | 獲取指定構(gòu)造(只能public修飾) |
Constructor getDeclaredConstructor(Class<?>… parameterTypes) | 獲取指定構(gòu)造(包含private修飾) |
代碼示例:
public class ReflectDemo2 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
//1.獲得整體(class字節(jié)碼文件對象)
Class clazz = Class.forName("com.itheima.reflectdemo.Student");
//2.獲取構(gòu)造方法對象
//獲取所有構(gòu)造方法(public)
Constructor[] constructors1 = clazz.getConstructors();
for (Constructor constructor : constructors1) {
System.out.println(constructor);
}
System.out.println("=======================");
//獲取所有構(gòu)造(帶私有的)
Constructor[] constructors2 = clazz.getDeclaredConstructors();
for (Constructor constructor : constructors2) {
System.out.println(constructor);
}
System.out.println("=======================");
//獲取指定的空參構(gòu)造
Constructor con1 = clazz.getConstructor();
System.out.println(con1);
Constructor con2 = clazz.getConstructor(String.class,int.class);
System.out.println(con2);
System.out.println("=======================");
//獲取指定的構(gòu)造(所有構(gòu)造都可以獲取到,包括public包括private)
Constructor con3 = clazz.getDeclaredConstructor();
System.out.println(con3);
//了解 System.out.println(con3 == con1);
//每一次獲取構(gòu)造方法對象的時候,都會新new一個。
Constructor con4 = clazz.getDeclaredConstructor(String.class);
System.out.println(con4);
}
}
1.6 獲取構(gòu)造方法并創(chuàng)建對象
涉及到的方法:newInstance
代碼示例:
//首先要有一個javabean類
public class Student {
private String name;
private int age;
public Student() {
}
public Student(String name) {
this.name = name;
}
private Student(String name, int age) {
this.name = name;
this.age = age;
}
//get,set方法
public String toString() {
return "Student{name = " + name + ", age = " + age + "}";
}
}
//測試類中的代碼:
//需求1:
//獲取空參,并創(chuàng)建對象
//1.獲取整體的字節(jié)碼文件對象
Class clazz = Class.forName("com.itheima.a02reflectdemo1.Student");
//2.獲取空參的構(gòu)造方法
Constructor con = clazz.getConstructor();
//3.利用空參構(gòu)造方法創(chuàng)建對象
Student stu = (Student) con.newInstance();
System.out.println(stu);
System.out.println("=============================================");
//測試類中的代碼:
//需求2:
//獲取帶參構(gòu)造,并創(chuàng)建對象
//1.獲取整體的字節(jié)碼文件對象
Class clazz = Class.forName("com.itheima.a02reflectdemo1.Student");
//2.獲取有參構(gòu)造方法
Constructor con = clazz.getDeclaredConstructor(String.class, int.class);
//3.臨時修改構(gòu)造方法的訪問權(quán)限(暴力反射)
con.setAccessible(true);
//4.直接創(chuàng)建對象
Student stu = (Student) con.newInstance("zhangsan", 23);
System.out.println(stu);
1.7 獲取成員變量
規(guī)則:
? get表示獲取
? Declared表示私有
? 最后的s表示所有,復數(shù)形式
? 如果當前獲取到的是私有的,必須要臨時修改訪問權(quán)限,否則無法使用
方法名:
方法名 | 說明 |
---|---|
Field[] getFields() | 返回所有成員變量對象的數(shù)組(只能拿public的) |
Field[] getDeclaredFields() | 返回所有成員變量對象的數(shù)組,存在就能拿到 |
Field getField(String name) | 返回單個成員變量對象(只能拿public的) |
Field getDeclaredField(String name) | 返回單個成員變量對象,存在就能拿到 |
代碼示例:
public class ReflectDemo4 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
//獲取成員變量對象
//1.獲取class對象
Class clazz = Class.forName("com.itheima.reflectdemo.Student");
//2.獲取成員變量的對象(Field對象)只能獲取public修飾的
Field[] fields1 = clazz.getFields();
for (Field field : fields1) {
System.out.println(field);
}
System.out.println("===============================");
//獲取成員變量的對象(public + private)
Field[] fields2 = clazz.getDeclaredFields();
for (Field field : fields2) {
System.out.println(field);
}
System.out.println("===============================");
//獲得單個成員變量對象
//如果獲取的屬性是不存在的,那么會報異常
//Field field3 = clazz.getField("aaa");
//System.out.println(field3);//NoSuchFieldException
Field field4 = clazz.getField("gender");
System.out.println(field4);
System.out.println("===============================");
//獲取單個成員變量(私有)
Field field5 = clazz.getDeclaredField("name");
System.out.println(field5);
}
}
public class Student {
private String name;
private int age;
public String gender;
public String address;
public Student() {
}
public Student(String name, int age, String address) {
this.name = name;
this.age = age;
this.address = address;
}
public Student(String name, int age, String gender, String address) {
this.name = name;
this.age = age;
this.gender = gender;
this.address = address;
}
//get,set方法
public String toString() {
return "Student{name = " + name + ", age = " + age + ", gender = " + gender + ", address = " + address + "}";
}
}
1.8 獲取成員變量并獲取值和修改值
方法 | 說明 |
---|---|
void set(Object obj, Object value) | 賦值 |
Object get(Object obj) | 獲取值 |
代碼示例:
public class ReflectDemo5 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
Student s = new Student("zhangsan",23,"廣州");
Student ss = new Student("lisi",24,"北京");
//需求:
//利用反射獲取成員變量并獲取值和修改值
//1.獲取class對象
Class clazz = Class.forName("com.itheima.reflectdemo.Student");
//2.獲取name成員變量
//field就表示name這個屬性的對象
Field field = clazz.getDeclaredField("name");
//臨時修飾他的訪問權(quán)限
field.setAccessible(true);
//3.設置(修改)name的值
//參數(shù)一:表示要修改哪個對象的name?
//參數(shù)二:表示要修改為多少?
field.set(s,"wangwu");
//3.獲取name的值
//表示我要獲取這個對象的name的值
String result = (String)field.get(s);
//4.打印結(jié)果
System.out.println(result);
System.out.println(s);
System.out.println(ss);
}
}
public class Student {
private String name;
private int age;
public String gender;
public String address;
public Student() {
}
public Student(String name, int age, String address) {
this.name = name;
this.age = age;
this.address = address;
}
public Student(String name, int age, String gender, String address) {
this.name = name;
this.age = age;
this.gender = gender;
this.address = address;
}
//get,set 方法
public String toString() {
return "Student{name = " + name + ", age = " + age + ", gender = " + gender + ", address = " + address + "}";
}
}
1.9 獲取成員方法
規(guī)則:
? get表示獲取
? Declared表示私有
? 最后的s表示所有,復數(shù)形式
? 如果當前獲取到的是私有的,必須要臨時修改訪問權(quán)限,否則無法使用
方法名 | 說明 |
---|---|
Method[] getMethods() | 返回所有成員方法對象的數(shù)組(只能拿public的) |
Method[] getDeclaredMethods() | 返回所有成員方法對象的數(shù)組,存在就能拿到 |
Method getMethod(String name, Class<?>… parameterTypes) | 返回單個成員方法對象(只能拿public的) |
Method getDeclaredMethod(String name, Class<?>… parameterTypes) | 返回單個成員方法對象,存在就能拿到 |
代碼示例:
public class ReflectDemo6 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
//1.獲取class對象
Class<?> clazz = Class.forName("com.itheima.reflectdemo.Student");
//2.獲取方法
//getMethods可以獲取父類中public修飾的方法
Method[] methods1 = clazz.getMethods();
for (Method method : methods1) {
System.out.println(method);
}
System.out.println("===========================");
//獲取所有的方法(包含私有)
//但是只能獲取自己類中的方法
Method[] methods2 = clazz.getDeclaredMethods();
for (Method method : methods2) {
System.out.println(method);
}
System.out.println("===========================");
//獲取指定的方法(空參)
Method method3 = clazz.getMethod("sleep");
System.out.println(method3);
Method method4 = clazz.getMethod("eat",String.class);
System.out.println(method4);
//獲取指定的私有方法
Method method5 = clazz.getDeclaredMethod("playGame");
System.out.println(method5);
}
}
1.10 獲取成員方法并運行
方法
Object invoke(Object obj, Object… args) :運行方法
參數(shù)一:用obj對象調(diào)用該方法
參數(shù)二:調(diào)用方法的傳遞的參數(shù)(如果沒有就不寫)
返回值:方法的返回值(如果沒有就不寫)
代碼示例:
package com.itheima.a02reflectdemo1;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class ReflectDemo6 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
//1.獲取字節(jié)碼文件對象
Class clazz = Class.forName("com.itheima.a02reflectdemo1.Student");
//2.獲取一個對象
//需要用這個對象去調(diào)用方法
Student s = new Student();
//3.獲取一個指定的方法
//參數(shù)一:方法名
//參數(shù)二:參數(shù)列表,如果沒有可以不寫
Method eatMethod = clazz.getMethod("eat",String.class);
//運行
//參數(shù)一:表示方法的調(diào)用對象
//參數(shù)二:方法在運行時需要的實際參數(shù)
//注意點:如果方法有返回值,那么需要接收invoke的結(jié)果
//如果方法沒有返回值,則不需要接收
String result = (String) eatMethod.invoke(s, "重慶小面");
System.out.println(result);
}
}
public class Student {
private String name;
private int age;
public String gender;
public String address;
public Student() {
}
public Student(String name) {
this.name = name;
}
private Student(String name, int age) {
this.name = name;
this.age = age;
}
//get,set 方法
public String toString() {
return "Student{name = " + name + ", age = " + age + "}";
}
private void study(){
System.out.println("學生在學習");
}
private void sleep(){
System.out.println("學生在睡覺");
}
public String eat(String something){
System.out.println("學生在吃" + something);
return "學生已經(jīng)吃完了,非常happy";
}
}
面試題:
? 你覺得反射好不好?好,有兩個方向
? 第一個方向:無視修飾符訪問類中的內(nèi)容。但是這種操作在開發(fā)中一般不用,都是框架底層來用的。
? 第二個方向:反射可以跟配置文件結(jié)合起來使用,動態(tài)的創(chuàng)建對象,動態(tài)的調(diào)用方法。文章來源:http://www.zghlxwxcb.cn/news/detail-713130.html
后記
????????美好的一天,到此結(jié)束,下次繼續(xù)努力!欲知后續(xù),請看下回分解,寫作不易,感謝大家的支持??! ??????文章來源地址http://www.zghlxwxcb.cn/news/detail-713130.html
到了這里,關(guān)于從零開始學習 Java:簡單易懂的入門指南之反射(三十八)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!