JavaDay27 反射
一、反射總結
-
反射:
當一個Java文件編譯之後,編譯成一個.class文件,也就是字節碼文件,當這個字節碼文件【加載】到內存的方法區/代碼區,JVM會根據【加載】的字節碼文件內容,創建一個Class的類對象。這個Class類對象裏面包含了當前字節碼文件裏面的所有內容。只要我們獲取到這個Class對象,就可以爲所欲爲!!!
這個Class對象中包含了對應字節碼文件的所有成員變量(Field) 所有成員方法(Method)構造方法(Constructor)。 -
1.獲取Class對象的三種方式:
1> Class.forName(“完整的類名字符串”);
完整類名字符串是指 包名.類名
2> 類名.class;
3> 指定類對象.getClass();最常用的是Class.forName(“完整的類名字符串”);
後期大量使用 -
2.Constructor, Method, Field
都是Java反射這個包(java.lang.reflect)裏面的類
Constructor 是構造方法類
Method 是成員方法類
Field 是成員變量類 -
3.Constructor常用方法:
返回值 | 方法名 | 作用 |
---|---|---|
Constructor[] | getConstructors(); | 獲取所有public修飾的構造方法 |
Constructor[] | getDeclaredConstructors(); | 獲取當前類裏面所有的構造方法,包含用private修飾的構造方法 |
Constructor | getConstructor(Object… paramterTyeps); | 根據所需參數不同,獲取指定的構造方法對象 |
Constructor | getDeclaredConstructor(Object… paramterTyeps); | 根據所需參數不同,獲取指定的構造方法對象,包括私有化的方法 |
Object | newInstance(Object… initargs); | 給予確定的參數,通過反射調用構造方法,這裏的參數列表是一個不定參數列表 |
- 4.Method常用方法
返回值 | 方法名 | 作用 |
---|---|---|
Method[] | getMethods(); | 獲取當前類裏面所有的public修飾的成員方法,這裏或顯示父類繼承而來的public方法 |
Method[] | getDeclaredMethods(); | 獲取當前類裏面的所有方法,包括private修飾的方法,但是會過濾父類繼承而來的方法 |
Method | getMethod(String methodName, Object… args); | 根據方法的名字和對應的參數列表,獲取指定方法 |
Method | getDeclaredMethod(String methodName, Object… args); | 根據方法的名字和對應的參數列表,獲取指定方法,可以獲取private修飾的方法 |
無 | invoke(Object obj, Object… args); | 執行成員方法的函數,第一個參數是執行該方法的類對象,第二個參數是執行該方法需要的參數列表 |
- 5.Field常用方法
返回值 | 方法名 | 作用 |
---|---|---|
Field[] | getFields(); | 獲取所有的用public修飾的成員變量 |
Field[] | getDeclaredFields(); | 獲取所用成員變量,包括用private 修飾的成員變量 |
Field | getField(String fieldName); | 根據成員變量的名字獲取對應的成員變量 |
Field | getDeclaredField(String fieldName); | 根據成員變量的名字獲取包括private修飾在內的成員變量 |
無 | set(Object obj, Object value); | 設置成員變量的數值,第一個參數是調用該成員變量的對象,第二個參數是賦予數值 |
- 6.暴力反射賦予權限的函數
setAccessible(boolean )
二、獲取Class 類 對象的三種方式
1.首先新建一個 Person 類,作爲示例
package com.qfedu.a_reflect;
public class Person {
private int id;
private String name;
public int test;
public static int testStatic = 10;
private Person() {}
public Person(int id, String name) {
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public static void eat() {
System.out.println("黃燜雞米飯~~~");
}
public void sleep(int num) {
System.out.println(name + "每天睡" + num + "個小時");
}
public void game() {
System.out.println("大吉大利,今晚喫雞~~~");
}
private void testPrivate() {
System.out.println("這是一個Private修飾的私有化方法");
}
@Override
public String toString() {
return "Person [id=" + id + ", name=" + name + "]";
}
}
2.通過反射獲取類對象
package com.qfedu.a_reflect;
public class GetClassObject {
public static void main(String[] args) throws ClassNotFoundException {
//如果想要爲所欲爲,首先獲取到Class類對象
/*方式1:Class.forName("完整的類名字符串");
完整類名是包括 包名.類名
*/
Class cls1 = Class.forName("com.qfedu.a_reflect.Person");
System.out.println(cls1);
//方式2:類名.class
Class cls2 = Person.class;
System.out.println(cls2);
//方式3:通過對象獲取到對應的Class類對象
Class cls3 = new Person(1, "逗比").getClass();
System.out.println(cls3);
System.out.println(cls1 == cls2);
System.out.println(cls2 == cls3);
System.out.println(cls1 == cls3);
}
}
三、Constructor常用方法
通過Class類對象獲取當前類的構造方法並且調用
package com.qfedu.a_reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class GetClassConstructor {
public static void main(String[] args) throws
ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
/*
【加載】指定類的字節碼文件,獲取對應的Class對象
下面的語句做了兩件事情:
1. 讓JVM根據類名,加載Person.java對應的字節碼文件Person.class 到內存的代碼區
2. 把加載到內存代碼區Person.class 字節碼文件生成一個Class類對象返回
*/
Class cls = Class.forName("com.qfedu.a_reflect.Person");
//Constructor 這是構造方法的類
//第一種:可以通過Class獲取所有的【非私有化構造方法】,方法是 getConstuctors();
Constructor[] constructors = cls.getConstructors();
for (Constructor constructor : constructors) {
System.out.println(constructor);
}
System.out.println("---------------------------");
//第二種:暴力反射(獲取所有構造方法,不管是不是私有化)
Constructor[] allConstructors = cls.getDeclaredConstructors();
for (Constructor constructor : allConstructors) {
System.out.println(constructor);
}
//第三種方法:
/*
根據參數獲取具體的構造方法
getConstructor(Class... parameterTypes);
上面括號裏面是不定參數列表,表示參數的類型要求是Class類型,但是數量不限制
在確定數據類型的情況下,可以直接通過數據類型.class獲取對應Class數據類型
例如:
int.class String.class 這裏會自動包裝爲Integer.class和String.class
*/
System.out.println("----------------------------------");
Constructor aConstructor = cls.getConstructor(int.class, String.class);
System.out.println(aConstructor);
//☆☆☆☆根據Constructor對象創建類對象,即調用構造函數
/*
構造方法Constructor對象獲取完畢,怎麼利用Constructor對象創建一個Person類對象
newInstance(Object... initargs)
也是一個不定參的方法,需要的參數都是Object類型的,參數個數不確定
*/
Person p = (Person) aConstructor.newInstance(1, "海洋");//默認產生是Object對象,因此需要強轉
System.out.println(p.getId() + ":" + p.getName());
p.sleep(5);
p.game();
p.eat(); //如何通過反射機制,調用static修飾的成員方法,並且不報警告
/*
通過暴力反射,藉助於指定的參數,獲取private修飾的無參構造方法
*/
System.out.println("---------------------------------------");
Constructor privateConstructor = cls.getDeclaredConstructor(null);
System.out.println(privateConstructor);
//這裏需要通過setAccessible(boolean ) 給予操作Private修飾方法的權限
privateConstructor.setAccessible(true);
Person p2 = (Person) privateConstructor.newInstance(null);
p2.setId(2);
p2.setName("劉德華");
System.out.println(p2);
/*
單例和反射的共存:
在實際開發中,如果一個類是一個單例類,那麼一般不會有程序猿通過反射的方式來使用這個類裏面
私有化的構造方法,這違背了單例的原則
*/
}
}
至此:上面除了 Person 類以及暴力反射之外所有代碼的整理精簡版如下
/**
* @author GJXAIOU
* @create 2019-08-10-13:56
*/
public class ReflectPractice {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
// 1.獲取class類對象
Class person = Class.forName("entity.Person");
// 2.獲取具體的構造方法
// 獲取無參構造方法
Constructor constructor = person.getConstructor();
// 獲取帶有參數的構造方法
Constructor constructor1 = person.getConstructor(int.class,String.class);
// 獲取全部構造方法(非私有的)
Constructor[] constructors = person.getConstructors();
for (Constructor constructor2 : constructors) {
System.out.println(constructor);
}
// 3.使用構造方法構建類對象,默認是Object類型,需要強轉
Person person1 = (Person) constructor.newInstance();
Person person2 = (Person) constructor1.newInstance(2, "張三");
// 4.使用成員變量和成員方法
System.out.println(person1.getName());
System.out.println(person2.getName());
}
補充:單例
package com.qfedu.a_reflect;
public class SingleDemo {
int id;
//爲了判定數據,定義一個靜態私有化成員變量,數據類型爲該類的變量,保存上次創建的數據的類對象的首地址
private static SingleDemo s = null;
//1.私有化構造方法
private SingleDemo(int id) {
this.id = id;
}
//2.提供一個類外可以通過類名直接調用的,返回值爲當前類對象類型的,參數爲對應需要的構造方法參數,
//方法名通常爲getInstance,作爲類外獲取方式
public static SingleDemo getInstance(int id) {
//但我們調用這個公開的,靜態方法修飾的獲取類對象的這種方法時,對這個保存地址的變量進行判定
if (null == s) {
s = new SingleDemo(id);
}
return s; //返回上次創建對象
}
}
四、Method常用方法
4.通過反射藉助於Class類對象,獲取這個類裏面所有的成員方法,並且進行調用
package com.qfedu.a_reflect;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* 通過反射藉助於Class類對象,獲取這個類裏面所有的成員方法
* Method 就是成員方法類
*/
public class GetClassMethod {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {
//1.加載對應類的字節碼文件,獲取該類的Class類對象
Class cls = Class.forName("com.qfedu.a_reflect.Person");
//獲取所有的公共的方法,這裏也會獲取一些額外Object裏面公開的方法
Method[] allPublicMethods = cls.getMethods();
for (Method method : allPublicMethods) {
System.out.println(method);
}
System.out.println("$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$");
//暴力反射
//能夠獲取Person裏面的private方法, 並且能夠過濾掉從父類繼承而來的方法
Method[] allMethods = cls.getDeclaredMethods();
for (Method method : allMethods) {
System.out.println(method);
}
/*
通過反射機制,執行類中的成員方法
invoke(Object obj, Object... args);
Object obj 這是底層調用該方法的類對象
the object the underlying method is invoked from
Object... args 不定參數,是執行該放的參數列表,是Object類型
args is arguments used for method call
*/
//1.先利用反射,創建一個當前類的對象
System.out.println("$$$$$$$$$$$$$$$$$$$$$$$$$$$$");
Person p = (Person) cls.getConstructor(int.class, String.class).
newInstance(1, "狗蛋");
//獲取一個指定的方法,需要的參數是方法的名字字符串和參數列表,
Method aPublicMethod = cls.getMethod("sleep", int.class);
System.out.println(aPublicMethod);
aPublicMethod.invoke(p, 15);
System.out.println("$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$");
//獲取一個靜態方法
Method aPublicStaticMethod = cls.getMethod("eat", null);
aPublicStaticMethod.invoke(null, null);
//利用暴力反射獲取一個私有化的成員方法
Method aPrivateMethod = cls.getDeclaredMethod("testPrivate", null);
aPrivateMethod.setAccessible(true);
aPrivateMethod.invoke(p, null);
}
}
五、Field常用方法
5.通過反射獲取Class類對象裏面所有的成員變量
package com.qfedu.a_reflect;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
/**
* 通過反射獲取Class類對象裏面所有的成員變量
* Field 成員變量類
*
*/
public class GetClassField {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException {
//加載字節碼文件,獲取Class類對象
Class cls = Class.forName("com.qfedu.a_reflect.Person");
//獲取所有用public修飾的成員變量
Field[] allPublicFields = cls.getFields();
for (Field field : allPublicFields) {
System.out.println(field);
}
System.out.println("$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$");
//暴力反射,獲取私有化成員變量
Field[] allFields = cls.getDeclaredFields();
for (Field field : allFields) {
System.out.println(field);
}
System.out.println("$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$");
//獲取一個公開的成員變量
Field aPublicField = cls.getField("test");
System.out.println(aPublicField);
//set方法
//set(Oject obj, Object value);
//第一個參數: 要操作的是哪一個對象裏面的成員變量
//第二個參數: 需要設置的值
//首先獲得對象,然後調用set方法
Person p = (Person) cls.getConstructor(int.class, String.class).
newInstance(1, "狗蛋");
aPublicField.set(p, 20);
System.out.println(p.test);
System.out.println("$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$");
//靜態的
Field aStaticField = cls.getField("testStatic");
System.out.println(aStaticField);
aStaticField.set(null, 20);
System.out.println(Person.testStatic);
//暴力反射:私有化的
System.out.println("$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$");
Field aPrivateField = cls.getDeclaredField("id");
System.out.println(aPrivateField);
aPrivateField.setAccessible(true);
aPrivateField.set(p, 10);
System.out.println(p.getId());
}
}
總結
-
反射:
一個Java文件,編譯之後生成的.class文件,程序運行時加載到內存中,會保存在內存的【代碼區】,而這個.class字節碼文件,會對應生成一個Class類對象,藉助於Class類對象,可以完成一系列反射操作-
獲取Class類對象的方式:
1.Class.forName(“完整的類名”); 最常用!!!
2.已知類名.class;
3.對應類的類對象.getClass(); -
Constructor
getConstructors();
getDeclaredConstructors();
getConstructor(Class… parameterTypes);
例如:getConstructor(int.class, String.class);
getDeclaredConstructor(Class… parameterTypes);
Constructor的類對象.newInstance(Object… args); -
Method
invoke(Object obj, Object… args); -
Field
set(Object obj, Object value); -
設置權限的方式:
setAccessible(boolean b); -
反射和單例類的一個平衡
一個類如果是單例類,一般不會採用反射的方式去暴力獲取該類的構造方法,不符合單例的機制
-