java反射機制筆記

概念:類和對象之間的關係--》類是對象的一種描述,對象是一類實物的一個具體實例子

反射:得到元數據的行爲,得到類 中所有成員和行爲德爾一種機制

Class是對一切類的行爲和狀態的抽象

Class類:用於描述一切類/接口/枚舉/註解,枚舉是一種類,註解是一種接口。

Class實例:就是指JVM(java虛擬機)中的一份字節碼,

爲了明確區分Class實例表示的是哪個類的字節碼.Class類提供了泛型。

1.對象有編譯類型和運行類型:

Object obj = new Date();

編譯類型爲:Object

運行類型爲(就是對象的真實類型):Date

需求:根據對象obj調用Date類中的一個方法toLocaleString();

如果直接寫成 obj.toLocaleString();//編譯出錯

原因是在編譯過程中會檢查編譯類型是否有該方法。

編譯類型Object沒有該方法就會編譯不通過

解決方案:強制轉換obj爲Date類型,前提是必須知道對象的真實類型是什麼?

Date d = (Date)obj;

d.toLocaleString();//YES

如何得到Class的實例:

1.類名.class

2.Class.forName(String className);//根據一個類的全限定名來構造Class對象

3.每一個對象都有一個getClass方法,返回對象的運行時類型。該方法存在於超類Object中,java中所有類都繼承於Object

java中九個預定義的Class實例:

八種基本數據類型沒有類的權限定名,也沒有getClass方法該如何獲取基本數據類型的Class實例

byte short char int float double long boolean void關鍵字

上述八種基本數據類型和void關鍵字都有class屬性

int的Class對象:Class clazz = int.class;

void關鍵字的Class對象 Class clazz = void.class;

所有的數據類型都有class屬性,表示的都是Class對象

在java中基本數據類型與它的包裝類不是同一種數據類型

驗證:在eclipse中創建兩個方法,

public void show(int i){}

public void show(Integer i){}//兩個方法在eclipse中編譯通過說明Integer和int不是同一種數據類型

在八大基本數據類型的包裝類型中都有一個常量TYPE,表示其基本數據類型的Class實例

 即Integer.TYPE == int.class 但是Integer.class != int.class;Integer.TYPE != Integer.class

所有具有相同元素類型和維數的數組才共享同一份字節碼(Class對象)

獲取class實例代碼:

public class ClassInstanceDemo {

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

//獲取Class實例的第一種方法 類名.class

Class<Date> clazz1 = Date.class;

//獲取Class實例第二種方法 Class.forName(String className)

Class<?> clazz2 = Class.forName("java.util.Date");

//獲取Class實例第三種方法,對象.getClass();得到對象的運行時類型

Class<?> clazz3 = new String().getClass();

Class<?> clazz4 = new Date().getClass();

System.out.println(clazz1==clazz2);//true 一個類的字節碼在JVM中有且只有一份。

System.out.println(clazz2 == clazz3);//false

System.out.println(clazz1 == clazz3);//false

//clazz1==clazz2==clazz4 因爲表示的都是JVM中共同的一份字節碼(Date.class)

}

}

獲取構造器並創建對象實例代碼:

package cn.bsxy.clazz;


import java.lang.reflect.Constructor;


class User{

public User(){

System.out.println("User ...user()");

}

public User(String name){

System.out.println("User..."+name);

}

private User(String name,int age){

System.out.println("User...userName:"+name+" UserAge:"+age);

}

}

/**

 * 

 * @author smartluobo

 * 功能:通過類反射機制獲取類的構造器,使用獲取的構造器創建該類的對象

 *

 */

public class ReflectConstructorDemo {

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

Class<User> clazz = User.class;

//clazz.newInstance()該方法是Class類中的方法,該方法調用當前class描述類的默認無參構造方法創建對象

//衆所周知java中沒創建一個類,jdk默認提供了該類的一個無參構造方法,但是一旦我們重新寫了該類的有參構造方法。

//該類的jdk提供的無參構造方法將被覆蓋,因此當我們寫了該類的有參構造方法後沒有手動聲明無參構造方法使用clazz.newInstance();

//方法會拋異常,同時也不能使用clazz.getConstructor()和clazz.getDeclaredConstructor()方法

User u0 = clazz.newInstance();

//getConstructors()獲取當前class描述類的所有public修飾的構造器

Constructor[] cs = clazz.getConstructors();

for (Constructor c : cs) {

// System.out.println(c);

}

System.out.println("*************============**************");

//getDeclaredConstructors()獲取當前class描述類的所有構造器(忽略訪問權限)

cs = clazz.getDeclaredConstructors();

for (Constructor c : cs) {

// System.out.println(c);

}

System.out.println("*************============**************");

//getConstructor() 獲取當前class描述類的默認無參構造器

Constructor<User> c = clazz.getConstructor();

//newInstance()使用獲取的構造器創建對象

User u = c.newInstance();

System.out.println(u);

//getConstructor(Class<?>... parameterTypes)獲取指定參數類型的構造器,類的所有構造器名稱和類名相同

//該方法只能獲取public修飾的構造器,若指定的構造器乃私有則會拋出異常

//因此指定某個構造器只需指定參數列表即可,參數列表分爲三個維度(參數類型,參數個數,和參數順序)

//記住該方法的參數不是構造器的參數。而是構造器參數類型的Class實例

Constructor<User> c1 = clazz.getConstructor(String.class);

User u2 = c1.newInstance("jony");

//getDeclaredConstructor(Class<?>... parameterTypes) 該方法是對上一個方法的補充,當前class表述類的所有構造器

Constructor<User> c3 = clazz.getDeclaredConstructor(String.class,int.class);

//我們都知道要在類中使用其他類的私有構造方法來創建對象是編譯不通過的因此getDeclaredConstructor(Class<?>... parameterTypes)

//雖然能獲取到當前class描述的類的所有構造方法(忽略訪問權限)但是如果我們拿到指定的構造器是private修飾我們依然不能用該構造方法創建對象

//此時我們需要通過 Constructor.setAccessible(true);來設置運行時忽略安全性。

c3.setAccessible(true);

User u3 = c3.newInstance("monkey",12);

/**

* Class<T> clazz = T.class;

* 總結:通過反射機制獲取類的構造器的方法有四個

* 1.clazz.getConstructors();獲取所有public修飾的構造器

* 2.clazz.getDeclaredConstructors();獲取所有構造器 忽略訪問權限

* 3.clazz.getConstructor(Class<T>...parameterType);獲取指定的public修飾的構造器

* 4.clazz.getDeclaredConstructor(Class<T>...parameterType)獲取指定的構造器忽視訪問權限

* 通過反射機制創建當前class描述類的對象的方式兩種

* 1.clazz.newInstance();調用當前class描述類的無參構造方法創建對象

* 2.Constructor<T> c = clazz.getConstructor(Class<T>...parameterType)

* T t = c.newInstance(參數);獲取指定的構造器傳入相關參數創建對象

*/

}


}



獲取方法和屬性執行方法和對屬性取值和賦值代碼:

package cn.bsxy.clazz;


import java.lang.reflect.Field;

import java.lang.reflect.Method;


class SuperEmp{

private int sex;

public String department;

public void sayHiSupper(){

System.out.println("this SuperEmp say Hi");

}

private void sayHiPrivate(){

}

}

class Emp extends SuperEmp{

private String name;

public int age;

public void sayHi(){

System.out.println("this Emp say Hi...");

}

public void sayHi(String name){

System.out.println("this Emp to **"+name+"** say Hi");

}

public void sayHello(String name){

System.out.println("this Emp to **"+name+"** say Hello");

}

public void sayByeBye(String name,int time){

System.out.println("this Emp to **"+name+"** say byebye"+"this time = "+time);

}

private void sayHiPrivate(){

System.out.println("this method is a private method");

}

public static void staticMethod(){

System.out.println("this method is a static method");

}

public void varParamMethod(int... a){

if(null != a && a.length > 0){

System.out.println("success a.length="+a.length);

}else{

System.out.println("faile");

}

}

@Override

public String toString() {

return "name:"+name+"******age:"+age;

}

}

public class ReflectMethodDemo {


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

//testReflectMethod();

testReflectField();

}

public static void testReflectField()throws Exception{

Class<Emp> clazz = Emp.class;

/**

* clazz.getFields();方法獲取當前class描述類和該類的父類的所有public修飾的屬性

*/

Field[] fs = clazz.getFields();

for (Field field : fs) {

System.out.println(field);

}

//clazz.getDeclaredFields();方法獲取當前class描述類的所有屬性忽視訪問權限

System.out.println("*************============**************");

fs = clazz.getDeclaredFields();

for (Field field : fs) {

System.out.println(field);

}

System.out.println("*************============**************");

//clazz.getDeclaredField(String fieldName)獲取指定名稱的字段,該類所有字段

//clazz.getField(String fieldName) 獲取指定名稱的字段,該類及父類所有public修飾的字段

Field fName = clazz.getDeclaredField("name");

System.out.println(fName);

System.out.println("*************============**************");

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

System.out.println(fAge);

System.out.println("*************============**************");

/**

* 字段和方法一樣底層都從屬於對象,因此要對拿到的字段進行賦值或者取值

* 都必須先獲取到對象,利用反射創建對象

*/

Emp e = clazz.newInstance();

fAge.set(e, 18);

System.out.println(e);

System.out.println("*************============**************");

//對對象私有屬性進行賦值時需要先調用field.setAccessible(true);

fName.setAccessible(true);

fName.set(e, "jony");

System.out.println(e);

int age = fAge.getInt(e);

System.out.println(age);

String name = (String) fName.get(e);

System.out.println(name);

}

public static void testReflectMethod() throws Exception{

/**

* 通過反射獲取方法

*/

//獲取類在JVM中的字節碼 即Class實例

Class<Emp> clazz = Emp.class;

/**

* 通過反射獲取當前class描述類的方法集合

* clazz.getMethods()方法獲取該類和其直接間接父類所聲明的所有public修飾的方法

* clazz.getDeclaredMethods();該方法獲取當前class描述類的所有方法(忽視訪問權限)

* 但是放方法不能獲取繼承過來的所有方法,包括直接父類和間接父類的公共或私有方法

*/

Method[] ms = clazz.getMethods();

for (Method method : ms) {

// System.out.println(method);

}

ms = clazz.getDeclaredMethods();

for (Method method : ms) {

System.out.println(method);

}

System.out.println("*************============**************");

/**

* 在java的同一個類中通過方法簽名來唯一確定一個方法

* 方法簽名包括方法名稱和參數列表,參數列表分爲(參數類型,參數個數,參數順序)

* clazz.getMethod(String methodName,Class...) 

* 該方法通過指定的方法簽名獲取方法,methodName方法名稱,Class...可變參數類型的class

* 可以獲取該類的public修飾的方法亦獲取直接或間接父類public修飾的方法

*/

Method m = clazz.getMethod("sayHi");

System.out.println(m);

System.out.println("*************============**************");

m = clazz.getMethod("sayHi", String.class);

System.out.println(m);

System.out.println("*************============**************");

m = clazz.getMethod("sayHiSupper");//獲取父類方法

System.out.println(m);

System.out.println("*************============**************");

/**

* clazz.getDeclaredMethod(String, Class...)

* 該方法通過方法簽名獲取指定方法

* 能獲取該類的所有方法,包括公共的和私有的 但是不能獲取直接父類和間接父類的方法

*/

m = clazz.getDeclaredMethod("sayHello", String.class);

System.out.println(m);

System.out.println("*************============**************");

m = clazz.getDeclaredMethod("sayByeBye", String.class,int.class);

/**

* 執行方法,java中所有的非靜態方法,

* 在上面獲取到指定方法的基礎上我們要做的是執行方法。

* java中類的非靜態方法都從屬與該類的對象。因此需要執行方法我們需要先拿到對象。

* 通過獲取指定構造器創建對象,也可使用Class的newInstance()調用默認無參構造方法創建對象。

*/

Emp e = clazz.newInstance();

/**

* Method中的invoke方法需要牀底兩個參數,一個是該方法底層從屬的對象obj,另一個是可變參數即執行該方法的實際參數.

* 當方法的參數是非可變參數的時候我們可以直接傳值如m.invoke(e, "lucy",17);

* 亦可對參數進行包裝m.invoke(e, new Object[]{"lucy",18});

* 但是當方法的參數是可變參數,獲取指定方法時,方法實際參數的class傳值等於該類型的數組類型的class

* 如方法聲明 public void varParamMethod(int... a)

* 獲取該方法時使用clazz.getDeclaredMethod("varParamMethod", int[].class);獲取

* 執行該方法時方法的實際參數爲實際參數類型的數組類型,如執行varParamMethod()方法的實際參數

* 爲new int[]{1},爲了加強通用性我們一般在參數數組類型的外層再加上一層包裝

* new Object[]{new int[]{1,2,3}}

*/

System.out.println("*************============**************");

m.invoke(e, "lucy",17);

m.invoke(e, new Object[]{"lucy",18});

m = clazz.getDeclaredMethod("varParamMethod", int[].class);

m.invoke(e,new int[]{1});

m.invoke(e, new Object[]{new int[]{1,2,3}});

System.out.println("*************============**************");

m = clazz.getDeclaredMethod("staticMethod");

System.out.println(m);

//調用靜態方法,傳入的方法的底層從屬對象可以寫成null

m.invoke(null);

System.out.println("*************============**************");

m = clazz.getDeclaredMethod("sayHiPrivate");

//當獲取的是一個私有方法時要執行該方法必須在執行該方法之前執行m.setAccessible(true);

m.setAccessible(true);

m.invoke(e);

}

/**

* 使用反射調用可變參數的方法

* 對於數組類型的引用類型的參數,底層會自動解包,爲了解決該問題。我們使用Object的一維數組把實際參數包裝起來

* 以後使用反射調用invoke方法,在傳遞實際參數的時候,無論是基本數據類型還是引用數據類型,也無論是可變參數類型還是

* 不變參數類型,都是用一個規定:一起實際參數都包裝在Object的一維數組中,就是通用的

* 如:m.invoke(方法底層所屬對象,new Object[]{實際參數});通用

*/

}



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