天天看點

【Java基礎】反射(reflect)一.Java類加載器簡單了解二.什麼是反射機制三.java.lang.Class示例類(以下代碼以這個類為基準)四.java.lang.reflect.Constuctor五.java.lang.reflect.Method六.java.lang.reflect.Field七.反射使用方式

目錄

  • 一.Java類加載器簡單了解
    • 1.類的加載
    • 2.類加載的時機(隻加載一次)
    • 3.類加載器
    • 4.類加載器的組成
  • 二.什麼是反射機制
  • 三.java.lang.Class
    • 1.什麼是Class對象
    • 2.擷取Class對象的三種方式?
    • 3.Class對象常用方法
      • 3.1.擷取Class類對應的實體類的相關的Class類的方法
      • 3.2.擷取構造函數的方法
      • 3.3.擷取類方法的方法
      • 3.4.擷取類中屬性的方法
      • 3.5.擷取類中注解的方法
      • 3.6.擷取接口的方法
      • 3.7.傳回字元串(String)的方法
      • 3.8.判斷目前類是什麼類的方法
      • 3.9.其他判斷方法
      • 3.10.其他常用方法
    • 4.反射生成java對象的兩種方式
      • 4.1.使用Class對象.newInstance()擷取對應執行個體
      • 4.2.通過Class擷取構造器Constructor
      • 4.3.舉個栗子
  • 示例類(以下代碼以這個類為基準)
  • 四.java.lang.reflect.Constuctor
    • 1.Constuctor是什麼?能做什麼?
    • 2.Constuctor的常用方法
    • 3.反射建立對象實作步驟
    • 4.舉個栗子
  • 五.java.lang.reflect.Method
    • 1.Method是什麼?能做什麼?
    • 2.Method的常用方法
    • 3.反射調用方法實作步驟
    • 4.舉個栗子
  • 六.java.lang.reflect.Field
    • 1.Fidle是什麼?能做什麼?
    • 2.Field的常用方法
    • 3.反射擷取字段實作步驟
    • 4.舉個栗子
  • 七.反射使用方式
    • 1.反射來擷取泛型資訊
    • 2.泛型擦除
    • 3.反射通過配置檔案運作功能的實作
    • 4.使用反射擷取類、方法、屬性上的注解
    • 5.使用反射映射Jdbc的結果集
    • 6.調用方法的反射工具類

一.Java類加載器簡單了解

1.類的加載

當程式要使用某個類時,如果該類還未被加載到記憶體中,則系統會通過加載,連接配接,初始化三步來實作對這個類進行初始化

  1. 加載:

    就是指将class檔案讀入記憶體,并為之建立一個Class對象,任何類被使用時系統都會建立一個Class對象

  2. 連接配接:

    驗證:確定被加載類的正确性

    準備:負責為類的靜态成員配置設定記憶體,并設定預設初始化值

    解析:将類的二進制資料中的符号引用替換為直接引用

  3. 初始化:

    局部變量儲存在棧區:必須手動初始化

    new 的對象儲存在堆區:虛拟機會進行預設初始化,基本資料類型初始化值為0,引用類型初始化值為null

2.類加載的時機(隻加載一次)

以下時機僅表示第一次的時候

  1. 直接使用java.exe指令來運作某個主類。
  2. 建立類的執行個體(new對象)。
  3. 使用類的靜态變量,或者為靜态變量指派。
  4. 調用類的靜态方法。
  5. 初始化某個類的子類,其父類先進入記憶體。
  6. 使用反射方式來強制建立某個類或接口對應的java.lang.Class對象

最先進入記憶體的類是Object類,并且最先執行的方法并不是main方法,而是Object類中的

registerNatives()

将對象注冊在作業系統上

3.類加載器

負責将.class檔案加載到記憶體中,并為之生成對應的Class對象

雖然我們在開發過程中不需要關心類加載機制,但是了解這個機制我們就能更好的了解程式的運作

4.類加載器的組成

  • Bootstrap ClassLoader 根類加載器

    也被稱為引導類加載器,負責Java核心類的加載,比如System類,在JDK中JRE的lib目錄下rt.jar檔案中的類

  • Extension ClassLoader 擴充類加載器

    負責JRE的擴充目錄中jar包的加載,在JDK中JRE的lib目錄下ext目錄

  • System ClassLoader 系統類加載器

    負責在JVM啟動時加載來自java指令的class檔案,以及classpath環境變量所指定的jar包和類路徑,主要是我們開發者自己寫的類

二.什麼是反射機制

簡單的來說,反射機制就是

程式在運作時能夠擷取自身的資訊

。在java中,隻要給定類的,那麼就可以通過反射機制來擷取類的所有資訊。

  • 反射機制是在運作狀态中,對任意個類,都能知道這個類的所有屬性,方法;
  • 對于任意一個對象,都能夠·動态調用它的任意一個方法和屬性;·
  • 這種動态擷取資訊以及動态調用的功能稱之為

    JAVA的反射機制;

反射會把一個類的組成如:

方法(Method)、構造方法(Constructor)、變量(Field),包(package)

等資訊,映射成一個個Java對象,通過這些反射對象我們可以動态的調用一個類的方法和屬性

哪裡用到了反射機制?

很多架構都是用到反射機制,如果

Hbernate,Struts

都是用反射機制實作的

反射的優點和缺點

編譯時加載類是靜态加載類

  • new 建立對象是靜态加載類,在編譯時刻就需要加載所有可用使用到的類,如果有一個用不了,那麼整個檔案都無法通過編譯

運作時加載類是動态加載類

  • Class c = Class.forName("類的全名")

    ,不僅表示了類的類型,還表示了動态加載類,編譯不會報錯,在運作時才會加載,使用接口标準能更友善動态加載類的實作。功能性的類盡量使用動态加載,而不用靜态加載。
  • 很多軟體比如QQ,360的線上更新,并不需要重新編譯檔案,隻是動态的加載新的東西

三.java.lang.Class

1.什麼是Class對象

  • Class 是反射的核心類,Class是由JVM在執行過程中動态加載的。JVM在第一次讀取到一種class類型時,将其加載進記憶體嗎,Class對象儲存的就是位元組碼資訊
  • 每加載一種class,JVM就為其建立一個Class類型的執行個體,并關聯起來
    以String類為例,當JVM加載String類時,它首先讀取String.class檔案到記憶體,然後,為String類建立一個Class執行個體并關聯起來:
    這個Class執行個體是JVM内部建立的,如果我們檢視JDK源碼,可以發現Class類的構造方法是private,隻有JVM能建立Class執行個體,我們自己的Java程式是無法建立Class執行個體的。
    由于JVM為每個加載的class建立了對應的Class執行個體,并在執行個體中儲存了該class的所有資訊,包括

    類名、包名、父類、實作的接口、所有方法、字段

    等,這種通過Class執行個體擷取class資訊的方法稱為

    反射(Reflection)

  • 任意類有且僅有一個class對象

擷取Class對應的類或者接口成員Member(成員有:

屬性,方法,構造方法

)

java.lang.reflect.Member

是一個接口,代表

Class 的成員

,每個成員都有類型,分為是否從父類繼承,還有是否可以直接通路。

Member 有三個實作類:

  • java.lang.reflect.Constructor

    :表示該 Class 的構造函數
  • java.lang.reflect.Field

    :表示該 Class 的成員變量
  • java.lang.reflect.Method

    :表示該 Class 的成員方法

2.擷取Class對象的三種方式?

public class GetClass {
    public static void main(String[] args) throws ClassNotFoundException {
        // 第一種方式, 通過類名.class 屬性
        Class c1 = Student.class;

        // 第二種方式, 通過對象.getClass() 方法
        Class c2 = new Student().getClass();

        // 第三種方式, 通過Class.forName("包名.類名")
        Class c3 = Class.forName("com.demo.reflect.Student");//類路徑不對會報錯:  java.lang.ClassNotFoundException: xxxxx.Student

        // 列印資訊
        System.out.println(c1 + " :" + c1.getName());//class com.demo.reflect.Student :com.demo.reflect.Student
        System.out.println(c2 + " :" + c2.getName());//class com.demo.reflect.Student :com.demo.reflect.Student
        System.out.println(c3 + " :" + c3.getName());//class com.demo.reflect.Student :com.demo.reflect.Student

        //比較每一個Class對象是否一樣
        System.out.println(c1 == c2); //true
        System.out.println( c2 == c3);   //true
        //可以知道 :任意類有且僅有一個class對象
    }
}

class Student { }
           

三種方式差別:

第一種,使用 .class 方法。

  • 說明:僅适合在編譯前就已經明确要操作的 Class
  • 缺點: 要導入類的包,依賴太強,不導包就抛編譯錯誤

第二種,使用類對象的 getClass() 方法。

  • 适合有對象示例的情況下
  • 缺點: 對象都有了還要反射幹什麼

第三種,使用 Class.forName 靜态方法。

  • 前提:已明确類的全路徑名。
  • 缺點: 隻能用于引用類型,所謂類的完整路徑是:包名.類名

    例如:java.lang.String。

常用第三種方式建立Class對象

: 這種方式參數為一個字元串可以直接傳入也可讀取在配置檔案中等多種方法。

3.Class對象常用方法

3.1.擷取Class類對應的實體類的相關的Class類的方法

方法 描述
Class<?>[] getSuperclass() 擷取目前Class對象所有的父類(擷取的類沒有繼承任何父類的,都預設擷取object類)
Class<?>[] getClasses() 傳回類定義的公共的内部類,以及從父類、父接口那裡繼承來的内部類
Class<?>[] getDeclaredClasses() .傳回類中定義的公共、私有、保護的内部類

getSuperclass()

public static void main(String[] args) throws ClassNotFoundException
{
    Class<String> strClass = (Class<String>) Class.forName("java.lang.String");
    // 傳回調用類的父類
    Class<?> superClass = strClass.getSuperclass();
    System.out.println("目前實體類的Class類    :"+strClass);
    System.out.println("實體類的父類的Class類:"+superClass);
}

//目前實體類的Class類    :class java.lang.String
//實體類的父類的Class類:class java.lang.Object
           

getClasses()和getDeclaredClasses()

Person類

public class Person{
	public class PA{};
	private class PB{};
	public interface PIA{};
	private interface PIB{};
}
           

Student類

public class Student extends Person{
	//公共的内部類
	public class A{}
	//保護内部類
	protected class B{}
	//預設内部類
	class C{}
	//私有内部類:
	private class D{}
	//共有靜态内部類
	public static class E{}
	//共有内部接口
	public interface IA{}
	//保護内部接口
	protected interface IB{}
	//預設内部接口
	interface IC{}
	//私有内部接口
	private interface ID{}
}
           

main方法

public static void main(String[] args) throws ClassNotFoundException{
	Class<String> stuClass = (Class<String>) Class.forName("reflect.Student");
	
	//擷取調用類的所有公共的内部類和接口,包括繼承的共有的内部類和接口的Class
	System.out.println(stuClass.getName()+"的公有内部類和接口(包括繼承):");
	Class[]   strClasses=stuClass.getClasses();
	for (Class class1 : strClasses){
		System.out.println(class1.getName());
	}
	
	System.out.println("#-------------------------");
	
	System.out.println(stuClass.getName()+"的所有内部類和接口(包括繼承):");
	Class[]   stuAllClasses=stuClass.getDeclaredClasses();
	for (Class class1 : stuAllClasses) {
		System.out.println(class1.getName());
	}
}
           

執行結果:

reflect.Student的公有内部類和接口(包括繼承):

reflect.Student

$

A

reflect.Student

$

E

reflect.Student

$

IA

reflect.Person

$

PA

reflect.Person

$

PIA

#-------------------------

reflect.Student的所有内部類和接口(包括繼承):

reflect.Student

$

A

reflect.Student

$

B

reflect.Student

$

C

reflect.Student

$

D

reflect.Student

$

E

reflect.Student

$

IA

reflect.Student

$

IB

reflect.Student

$

IC

reflect.Student

$

ID

傳回一個成員内部類/屬性/方法/構造器所在的類的Class,這些方法是上面那兩個方法的逆操作

java.lang.reflect.Class.getDeclaringClass()    ;//傳回一個成員内部類所在的類的Class
java.lang.reflect.Field.getDeclaringClass()      ;//傳回一個字段所在的類的Class
java.lang.reflect.Method.getDeclaringClass()    ;//傳回一個方法所在的類的Class
java.lang.reflect.Constructor.getDeclaringClass() ;//傳回一個構造器所在的類的Class
           

3.2.擷取構造函數的方法

方法 描述
Constructor getConstructor(Class[] params) 根據指定參數獲得(子類或者繼承自父類)public構造器
Constructor[] getConstructors() 獲得(子類或者繼承自父類)的所有public的構造器
Constructor getDeclaredConstructor(Class[] params) 根據指定參數獲得本類(不包括父類)public和非public的構造器
Constructor[] getDeclaredConstructors() 獲得本類(不包括父類)所有public和非public的構造器

3.3.擷取類方法的方法

方法 描述
Method getMethod(String name, Class[] params) 根據方法名,參數類型獲得(子類或者繼承自父類)public的方法
Method[] getMethods() 獲得所有(子類或者繼承自父類)的public方法
Method getDeclaredMethod(String name, Class[] params) 根據方法名和參數類型,獲得本類(不包括父類)public和非public的方法
Method[] getDeclaredMethods() 獲得本類(不包括父類)所有的public和非public方法

3.4.擷取類中屬性的方法

方法 描述
Field getField(String name) 根據名字得到(子類或者繼承自父類)中相應的public變量
Field[] getFields() 獲得類中所有(子類或者繼承自父類)中public的變量
Field getDeclaredField(String name) 根據名字獲得本類(不包括父類)public和非public變量
Field[] getDeclaredFields() 獲得本類所有(不包括父類)的public和非public變量

3.5.擷取類中注解的方法

方法 描述

<A extends Annotation>

[] getAnnotation(Class

< A >

annotationClass)
傳回指定元素上存在所有(子類或者繼承自父類)注解
Annotation[] getAnnotations() 傳回此元素上存在所有(子類或者繼承自父類)注解
Annotation[] getDeclaredAnnotations() 傳回此元素上存在所有(不包括父類)注解

<A extends Annotation>

getDeclaredAnnotation(Class

<A>

annotationClass)
傳回指定元素上存在所有(不包括父類)注解
boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) 如果指定類型的注解存在于此元素上,則傳回 true,否則傳回 false。

<A extends Annotation>

A[] getAnnotationsByType(Class

<A>

annotationClass):
由于Java8增加了重複注解,是以需要使用此方法擷取修飾該元素、指定類型的多個Annotation。

<A extends Annotation>

A[] getDeclaredAnnotationsByType(Class

<A>

annotationClass)
由于Java8增加了重複注解,是以需要使用此方法擷取直接修飾該元素、指定類型的多個Annotation。
boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) 如果此元素上存在指定類型的注解,則傳回true,否則傳回false。

如果需要擷取某個注解裡的中繼資料,則可以将注解強制類型轉換為所需的注解類型,然後通過注解對象的抽象方法來通路這些中繼資料。如((MyTag) e).name();

為了讓注解起作用,必須為這些注解提供一個注解處理工具方法。

3.6.擷取接口的方法

方法 描述

Class<?>

[] getInterfaces()
擷取由此對象表示的類或接口實作的所有接口
Type[] getGenericInterfaces() 擷取由此對象表示的類或接口直接實作的接口的Type

getInterfaces:主要是 擷取由此對象表示的類或接口實作的接口

getGenericInterfaces: 主要是 擷取由此對象表示的類或接口直接實作的接口的Type。

差別在于getGenericInterfaces可以傳回其參數化類型,例如: Collection、 List中的String和Coupon

Java反射注解妙用(擷取所有接口說明)

3.7.傳回字元串(String)的方法

方法 描述
String getCanonicalName() 傳回 Java Language Specification 中所定義的底層類的規範化名稱
String getName() 擷取目前Class表示的類的完整名字:(包名.類名)
String getSimpleName() 傳回源代碼中給出的底層類的簡稱。
String toString() 将對象轉換為字元串。
public static void main(String[] args) throws ClassNotFoundException{
	Class<String> strClass = (Class<String>) Class.forName("java.lang.String");
	System.out.println("目前Class表示的實體的      規範化名字:"+strClass.getCanonicalName());
	System.out.println("目前Class表示的實體的          完整名字:"+strClass.getName());
	System.out.println("目前Class表示的實體的                  簡稱:"+strClass.getSimpleName());
	System.out.println("目前Class表示的實體的toString():"+strClass.toString());
}

//目前Class表示的實體的      規範化名字:java.lang.String
//目前Class表示的實體的          完整名字:java.lang.String
//目前Class表示的實體的                  簡稱:String
//目前Class表示的實體的toString():class java.lang.String
           

3.8.判斷目前類是什麼類的方法

方法 描述
boolean isLocalClass() 判斷是不是局部類,也就是方法裡面的類
boolean isMemberClass() 判斷是不是成員内部類,也就是一個類裡面定義的類
boolean isAnonymousClass() 判斷目前類是不是匿名類,匿名類一般用于執行個體化接口
public class ClassTest{  
    public static void main(String[] args) {  
        ClassTest son = new ClassTest();  
 
        //測試匿名類  
        son.testAnonymous(new AnonymousClass() {  
            @Override  
            public void test() {  
                System.out.println("AnonymousClass是成員類:-> " + this.getClass().isMemberClass());  
                System.out.println("AnonymousClass是匿名類: -> " + this.getClass().isAnonymousClass());  
                System.out.println("AnonymousClass是局部類:-> " + this.getClass().isLocalClass());  
                System.out.println("#---------------------------------------");  
            }  
        });  
        //測試成員類  
        son.testMember();  
        //測試靜态成員類  
        new StaticMemberClass();  
        //測試局部類  
        class LocalClass{  
            public LocalClass(){  
                System.out.println("LocalClass是成員類:-> " + this.getClass().isMemberClass());  
                System.out.println("LocalClass是匿名類: -> " + this.getClass().isAnonymousClass());  
                System.out.println("LocalClass是局部類:-> " + this.getClass().isLocalClass());  
                System.out.println("#---------------------------------------");  
            }  
        }  
        new LocalClass();  
    }  
    //測試匿名内部類
    private void testAnonymous(AnonymousClass inner) {  
        inner.test();  
    }  
    //測試成員内部類:
    private void testMember() {  
        new MemberClass();  
    }  
      
    /** 
     * 接口,用于測試匿名類
     */  
    interface AnonymousClass{  
        public void test();  
    }  
      
    /** 
     * 靜态成員類 
     */  
    static class StaticMemberClass{  
        public StaticMemberClass() {  
            System.out.println("StaticMemberClass是成員類:-> " + this.getClass().isMemberClass());  
            System.out.println("StaticMemberClass是匿名類: -> " + this.getClass().isAnonymousClass());  
            System.out.println("StaticMemberClass是局部類:-> " + this.getClass().isLocalClass()); 
            System.out.println("#---------------------------------------");  
        }  
    }  
    /** 
     * 成員類 
     */  
class MemberClass{  
   public MemberClass() {  
        	 System.out.println("MemberClass是成員類:-> " + this.getClass().isMemberClass());  
             System.out.println("MemberClass是匿名類: -> " + this.getClass().isAnonymousClass());  
             System.out.println("MemberClass是局部類:-> " + this.getClass().isLocalClass());  
            System.out.println("#---------------------------------------");  
        }  
    }  
}  
           

測試結果:

AnonymousClass是成員類:-> false

AnonymousClass是匿名類: -> true

AnonymousClass是局部類:-> false

#---------------------------------------

MemberClass是成員類:-> true

MemberClass是匿名類: -> false

MemberClass是局部類:-> false

#---------------------------------------

StaticMemberClass是成員類:-> true

StaticMemberClass是匿名類: -> false

StaticMemberClass是局部類:-> false

#---------------------------------------

LocalClass是成員類:-> false

LocalClass是匿名類: -> false

LocalClass是局部類:-> true

#---------------------------------------

3.9.其他判斷方法

方法 描述
boolean isAnnotation() 判斷目前Class對象是否是注釋類型
boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) 如果指定類型的注釋存在于此元素上,則傳回 true,否則傳回 false。
boolean isAssignableFrom(Class<?> cls) 判定此 Class 對象所表示的類或接口與指定的 Class 參數所表示的類或接口是否相同,或是該類或接口的父類或父接口。
boolean isEnum() 當且僅當該類聲明為源代碼中的枚舉時傳回 true。
boolean isArray() 目前元素是數組類時傳回true。
boolean isInstance(Object obj) 判定指定的 Object 是否與此 Class 所表示的對象指派相容。
boolean isInterface() 判定指定的 Class 對象是否表示一個接口類型。
boolean isPrimitive() 判定指定的 Class 對象是否表示一個基本類型。
boolean isSynthetic() 如果此類是複合類,則傳回 true,否則 false。

3.10.其他常用方法

方法 描述
ClassLoader getClassLoader() 傳回該類的類加載器
Package getPackge() 擷取此類的包
URL getResource(String name) 通過傳入的檔案名去擷取對應檔案
InputStream getResourceAsStream(String name) 通過傳入的檔案名去擷取對應資源

Java中getResourceAsStream的用法

getResource和getResourceAsStream

4.反射生成java對象的兩種方式

4.1.使用Class對象.newInstance()擷取對應執行個體

newInstance()預設調用無參構造方法 ,若是沒有,則會報異常

沒有無參構造函數異常:

【Java基礎】反射(reflect)一.Java類加載器簡單了解二.什麼是反射機制三.java.lang.Class示例類(以下代碼以這個類為基準)四.java.lang.reflect.Constuctor五.java.lang.reflect.Method六.java.lang.reflect.Field七.反射使用方式

私有的構造函數異常:

【Java基礎】反射(reflect)一.Java類加載器簡單了解二.什麼是反射機制三.java.lang.Class示例類(以下代碼以這個類為基準)四.java.lang.reflect.Constuctor五.java.lang.reflect.Method六.java.lang.reflect.Field七.反射使用方式

4.2.通過Class擷取構造器Constructor

有帶參數的構造函數的類,先擷取到其構造對象,再通過該構造方法類擷取執行個體:

//擷取構造函數類的對象
Constroctor constroctor = User.class.getConstructor(String.class); 

// 使用構造器對象的newInstance方法初始化對象
Object obj = constroctor.newInstance("name"); 
           

4.3.舉個栗子

User類

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private String name;
    private  Integer age;
}

           
import java.lang.reflect.Constructor;
public class TestReflection {
 
    public static void main(String[] args) {
//      第一、通過Class.forName方式
        Class clazz1 = null;
        try {
            clazz1 = Class.forName("com.demo.reflect.createObj.User");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
 
//      第二、通過對象執行個體方法擷取對象
        Class clazz2 = User.class;
 
//      第三、通過Object類的getClass方法
        User user = new User();
        Class clazz3 = user.getClass();
 
        System.out.println(clazz1);
        System.out.println(clazz2);
        System.out.println(clazz3);

        //第一種調用Class對象newInstance()方式建立對象
        User user1 = null;
        try {
             user1 =(User) clazz1.newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        user1.setName("齊天大聖");
        user1.setAge(500);
        System.out.println("user1:"+user1.toString());


        //第二種擷取Class的構造器對象Constructor方式建立對象
        User user2 = null;
        try {
            // 擷取構造函數
            Constructor constroctor = clazz2.getConstructor(String.class,Integer.class);
            // 通過構造器對象的newInstance方法進行對象的初始化
            user2 = (User) constroctor.newInstance("豬八戒",1000);
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("user2:"+user2.toString());
 
    }
}
           

執行結果:

class com.demo.reflect.createObj.User

class com.demo.reflect.createObj.User

class com.demo.reflect.createObj.User

user1:User(name=齊天大聖, age=500)

user2:User(name=豬八戒, age=1000)

示例類(以下代碼以這個類為基準)

import java.sql.Timestamp;
public class Question {
	private Integer id;
	private String content;
	private Timestamp  creationDate;
	public String userName;
	
	public Question() {}
	public Question(Integer id, String content, Timestamp creationDate) {
		super();
		this.id = id;
		this.content = content;
		this.creationDate = creationDate;
	}
	
	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public String getContent() {
		return content;
	}
	public void setContent(String content) {
		this.content = content;
	}
	public Timestamp getCreationDate() {
		return creationDate;
	}
	public void setCreationDate(Timestamp creationDate) {
		this.creationDate = creationDate;
	}
}
           

四.java.lang.reflect.Constuctor

1.Constuctor是什麼?能做什麼?

是什麼 ?

  • 構造器的描述對象,描述着指定類的構造器

能做什麼

  • 可以通過其newInstance()方法來建立對象

2.Constuctor的常用方法

方法 描述
Class<?>[] getParameterTypes() 按照聲明順序以Class數組的形式獲得該構造方法的各個參數的類型
boolean equals(Object obj) 将其Constructor與指定對象進行比較。

<T extends Annotation>

T getAnnotation(Class

<T>

annotationClass)
如果該構造器對象存在指定類型的注解,則傳回該注解,否則傳回null
Annotation[] getAnnotations() 傳回該構造器對象上的所有注解,如果沒有注解,則傳回空數組
T getDeclaredAnnotation(Class annotationClass) 如果該構造器對象存在指定類型的注解,則傳回該注解,否則傳回 null
Annotation[] getDeclaredAnnotations() 傳回該構造器對象上的所有注解,如果沒有注解,則傳回空數組
T[] getAnnotationsByType(Class annotationClass) 如果該構造器對象存在指定類型的注解,則傳回該注解數組,否則傳回 null
T[] getDeclaredAnnotationsByType(Class annotationClass) 如果該構造器對象存在指定類型的注解,則傳回該注解數組,否則傳回 null
boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) 如果該構造器對象上有指定類型的注解,則傳回 true,否則為 false
setAccessible(boolean bool)

1.設定為true調用非public方法,此外,setAccessible(true)可能會失敗。

2.如果JVM運作期存在SecurityManager,那麼它會根據規則進行檢查,有可能阻止setAccessible(true)。例如,某個SecurityManager可能不允許對java和javax開頭的package的類調用setAccessible(true),這樣可以保證JVM核心庫的安全。

getModifiers() 獲得可以解析出該方法所采用修飾符的整數,它是一個int,不同的bit表示不同的含義
Class<?>[] getExceptionTypes() 以Class數組的形式獲得該方法可能抛出的異常類型

3.反射建立對象實作步驟

//第一步:擷取class對象
         Class clazz = Class.forName("com.panshi.Foo");  // clazz對象是Foo的描述
//第二步:通過class對象擷取到其構造器描述
         Constructor constructor = clazz.getConstructor([參數類型清單]);
//第三步:調用構造器描述的newInstance來建立執行個體對象
         Foo foo = constructor.newInstance([參數清單]);
           

具體栗子:

有參構造器執行個體化對象

//1.擷取class對象
Class<Question> clazz=(Class<Question>) Class.forName("com.reflect.test.Question");
//2.通過class對象擷取到構造器
Constructor<Question> constructor=clazz.getConstructor(Integer.class,String.class,Timestamp.class);
//3.調用構造器的 newInstance()來建立執行個體
Question question=(Question) constructor.newInstance(1,"我要進華為",new Timestamp(System.currentTimeMillis()));
//4.列印結果
System.out.println(question.getContent());
//我要進華為
           

無參構造器執行個體化(簡化)(調用無參構造器執行個體化)

//Class clazz = Foo.class;
//Foo foo = clazz.newInstance();
           

具體栗子:

//1.擷取class對象
 Class<Question> clazz=(Class<Question>) Class.forName("com.reflect.test.Question");
//2.通過class對象直接建立執行個體
 Question question=clazz.newInstance();
//4.列印結果
 question.setContent("111111");
 System.out.println(question.getContent());
//  111111
           

4.舉個栗子

/**
     * 列印類的構造方法
     */
    public static void printClassConstructor(Object obj) {
        //要擷取類的資訊,首先要擷取類的類類型

        Class c = obj.getClass();//傳遞的是哪個子類的對象,c就是該子類的類類型
        //擷取類的名稱
        System.out.println("類的名稱是:" + c.getName());

        /**
         * 構造函數也是對象
         * java.lang.Constructor中封裝了構造函數的資訊
         * getConstructor()方法擷取所有的public的構造函數
         * getDeclaredConstructors得到所有的構造函數
         */
        Constructor[] cs = c.getDeclaredConstructors();
        for (Constructor constructor : cs) {
            System.out.print(constructor.getName() + "(");
            //擷取構造函數的參數清單---》得到的是參數清單的類類型
            Class[] paramTypes = constructor.getParameterTypes();
            for (Class class1 : paramTypes) {
                System.out.print(class1.getName() + ",");
            }
            System.out.println(")");
        }
    }
    @Test
    public void testPrintClassMessage() {
        printClassConstructor(new StringBuffer());
    }
           

執行結果:

【Java基礎】反射(reflect)一.Java類加載器簡單了解二.什麼是反射機制三.java.lang.Class示例類(以下代碼以這個類為基準)四.java.lang.reflect.Constuctor五.java.lang.reflect.Method六.java.lang.reflect.Field七.反射使用方式

五.java.lang.reflect.Method

1.Method是什麼?能做什麼?

是什麼 ?

  • 方法的描述對象,描述着指定類的方法

能做什麼 ?

  • 允許通過invoke()方法來動态調用對象 (invoke調用的意思)

2.Method的常用方法

方法 描述
String getName() 傳回方法名稱,例如:“getScore”
Class<?>[] getParameterTypes() 按照聲明順序以Class數組的形式獲得該方法的各個參數的類型
Class<?> getReturnType() 以Class對象的形式獲得該方法的傳回值的類型,也是一個Class執行個體,例如:String.class
invoke(Object obj, Object… args) 利用指定參數args執行指定對象obj中的該方法,傳回值為object型。

<T extends Annotation>

T getAnnotation(Class

<T>

annotationClass)
如果該方法對象存在指定類型的注解,則傳回該注解,否則傳回null
Annotation[] getAnnotations() 傳回該方法對象上的所有注解,如果沒有注解,則傳回空數組
T getDeclaredAnnotation(Class annotationClass) 如果該方法對象存在指定類型的注解,則傳回該注解,否則傳回 null
Annotation[] getDeclaredAnnotations() 傳回該方法對象上的所有注解,如果沒有注解,則傳回空數組
T[] getAnnotationsByType(Class annotationClass) 如果該字方法對象存在指定類型的注解,則傳回該注解數組,否則傳回 null
T[] getDeclaredAnnotationsByType(Class annotationClass) 如果該方法對象存在指定類型的注解,則傳回該注解數組,否則傳回 null
boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) 如果該方法對象上有指定類型的注解,則傳回 true,否則為 false
setAccessible(boolean bool)

1.設定為true調用非public方法,此外,setAccessible(true)可能會失敗。

2.如果JVM運作期存在SecurityManager,那麼它會根據規則進行檢查,有可能阻止setAccessible(true)。例如,某個SecurityManager可能不允許對java和javax開頭的package的類調用setAccessible(true),這樣可以保證JVM核心庫的安全。

getModifiers() 獲得可以解析出該方法所采用修飾符的整數,它是一個int,不同的bit表示不同的含義
isVarArgs() 檢視該構造方法是否允許帶有可變參數,允許為true
Class<?>[] getExceptionTypes() 以Class數組的形式獲得該方法可能抛出的異常類型

3.反射調用方法實作步驟

如何擷取某個方法

方法的名稱和方法的參數清單才能唯一決定某個方法

方法的反射操作

//1.調用有參方法
m.invoke(對象,參數清單)

//2.調用無參方法
m.invoke(對象,null)
           

如果調用的是私有方法那麼需要暴力通路

//1.Class對象.getDeclaredMethod()

//2. m.setAccessiable(true);
           

方法如果沒有傳回值,傳回null,如果有傳回值傳回Object類型,然後再強制類型轉換為原函數的傳回值類型

/**
 * 反射擷取無參數成員方法并運作
 *  步驟:
 *      1.擷取class對象中所有的public成員方法
 *          Method[] getMethods() 擷取的是class檔案中所有的成員方法,包括繼承的方法
 *          Method類是描述成員方法的對象
 *      2.擷取class對象中指定的public成員方法
 *          Method getMethod("字元串類型的方法名",參數清單)
 *      3.執行,使用Method類中的方法,運作所擷取的方法
 *          Object invoke(Object obj , Object ... o)
 */



//第一步: 擷取class對象
Class clazz = Class.forName("com.panshi.Foo");  // clazz對象是Foo的描述

//第二步: 通過class對象擷取到指定的方法描述
Method method = clazz.getMethod("setName", String.class);

//第三步: 反射調用
method.invoke(foo,"小明"); // 等同于  foo.setName("小明");
           

具體栗子:

//第一步擷取class對象
		Class<Question> clazz=(Class<Question>) Class.forName("com.reflect.test.Question");
		
		Question question=clazz.newInstance();
		
		//第二步 擷取class對象擷取到指定的方法描述
		Method method=clazz.getMethod("setContent", String.class);
		
		//第三步:反射調用
		method.invoke(question,"我要上王者");
		//列印結果
     System.out.println(question.getContent());
     //  我要上王者  說明 我們調用了setContent()方法并為它傳入了參數
           

4.舉個栗子

/**
     * 列印類的方法(出參,入參)
     */
    public static void printClassMethod(Object obj) {
        //要擷取類的資訊,首先要擷取類的類類型

        Class c = obj.getClass();//傳遞的是哪個子類的對象,c就是該子類的類類型
        //擷取類的名稱
        System.out.println("類的名稱是:" + c.getName());

        /*
         * Method類,方法的對象
         * 一個成員方法就是一個Method對象
         * getMethods()方法擷取的是所有的public的函數,包括父類繼承而來的
         * getDeclaredMethods()擷取的是多有該類自己聲明的方法,不問通路權限
         */
        Method[] ms = c.getMethods();//c.getDeclaredMethods();
        for (int i = 0; i < ms.length; i++) {
            //得到方法的傳回值類型的類類型
            Class retrunType = ms[i].getReturnType();
            System.out.print(retrunType.getName() + " ");

            //得到方法的名稱
            System.out.print(ms[i].getName() + "(");

            //擷取的參數類型--->得到的是參數清單的類型的類類型
            Class[] paraTypes = ms[i].getParameterTypes();
            for (Class class1 : paraTypes) {
                System.out.print(class1.getName() + ",");
            }
            System.out.println(")");
        }
    }
    
	@Test
    public void  testPrintClassMessage(){
        printClassMethod(new Object());
    }
           

執行結果:

【Java基礎】反射(reflect)一.Java類加載器簡單了解二.什麼是反射機制三.java.lang.Class示例類(以下代碼以這個類為基準)四.java.lang.reflect.Constuctor五.java.lang.reflect.Method六.java.lang.reflect.Field七.反射使用方式

六.java.lang.reflect.Field

1.Fidle是什麼?能做什麼?

是什麼 ?

  • 提供有關類或接口的單個字段的資訊

能做什麼 ?

  • 允許通過get,set方法來動态取值,設值

2.Field的常用方法

方法 描述
String getName() 傳回方字段名稱
Class<?> getType() 傳回字段類型,也是一個Class執行個體 (擦除泛型)例如,String.class
AnnotatedType getAnnotatedType()

1.傳回一個 AnnotatedType 對象,該對象表示使用類型來指定由該字段對象表示的字段的類型

2.通過其 getType() 方法,我們可以擷取到對應的字段類型

<T extends Annotation>

T getAnnotation(Class

<T>

annotationClass)
如果該字段對象存在指定類型的注解,則傳回該注解,否則傳回null
Annotation[] getAnnotations() 傳回該字段對象上的所有注解,如果沒有注解,則傳回空數組
T getDeclaredAnnotation(Class annotationClass) 如果該字段對象存在指定類型的注解,則傳回該注解,否則傳回 null
Annotation[] getDeclaredAnnotations() 傳回該字段對象上的所有注解,如果沒有注解,則傳回空數組
T[] getAnnotationsByType(Class annotationClass) 如果該字段對象存在指定類型的注解,則傳回該注解數組,否則傳回 null
T[] getDeclaredAnnotationsByType(Class annotationClass) 如果該字段對象存在指定類型的注解,則傳回該注解數組,否則傳回 null
boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) 如果該字段對象上有指定類型的注解,則傳回 true,否則為 false
setAccessible(boolean bool)

1.設定為true調用非public方法,此外,setAccessible(true)可能會失敗。

2.如果JVM運作期存在SecurityManager,那麼它會根據規則進行檢查,有可能阻止setAccessible(true)。例如,某個SecurityManager可能不允許對java和javax開頭的package的類調用setAccessible(true),這樣可以保證JVM核心庫的安全。

getModifiers() 獲得可以解析出該方法所采用修飾符的整數,它是一個int,不同的bit表示不同的含義
Class<?>[] getExceptionTypes() 以Class數組的形式獲得該方法可能抛出的異常類型

3.反射擷取字段實作步驟

如何擷取某個字段

字段的反射操作

//——>取值
//方式1.擷取obj對象該Field的字段值(可以是基本類型也可以是引用類型)
Object value = f.get(Object obj); 

//方式2.擷取obj對象該Field的字段值(隻能為基本類型)
xxx getXxx(Object obj)//此處的xxx表示8個基本資料類型 如 getInt(),getDouble()


//——>指派
//方式1. 将obj對象的該Field字段設定成val值(可以是基本類也可以是引用類型)
field.set(Object obj, Object val);  // 等同于 user.name = "小明";

//方式2. 将obj對象的該Field字段設定成val值(隻能是引用類型)
//void setXxx(Object obj,xxx val):
           

如果調用的是私有方法那麼需要暴力通路

//1.Class對象.getDeclaredField()

//2. f.setAccessiable(true);
           

栗子:

/**
 * 反射擷取成員變量值,并修改值
 *  Class類中的方法:
 *      1.getFields(),傳回值為Field[]
 *          擷取所有成員變量,Field類為表述成員變量的類
 *      2.getField(字元串類型的變量名),傳回值為Field
 *          擷取指定成員變量
 *  Field類中的方法:
 *      1.void set(Object obj,Object value)
 *          用來修改成員變量,前者為對象,後者為值
 */
 
			//第一步 擷取class對象
			Class clazz=Class.forName("com.reflect.test.Question");
			//第二步: 通過class對象擷取到字段描述
			Field field=clazz.getDeclaredField("userName");

			Question question= clazz.newInstance();
			//反射調用
            // 1.使用set方法()指派
			field.set(question,"我是字段,我設值");
			
            //2.使用get方法()取值
            Object value=field.get(question);
			System.out.println(value);//列印 我是字段,我設值
        // 說明:set()設值成功,get()方法 取值成功
           

4.舉個栗子

/**
     * 列印類的成員變量(類型,名稱)
     */
    public static void printClassField(Object obj) {
        //要擷取類的資訊,首先要擷取類的類類型

        Class c = obj.getClass();//傳遞的是哪個子類的對象,c就是該子類的類類型
        //擷取類的名稱
        System.out.println("類的名稱是:" + c.getName());

        /**
         * 成員變量也是對象,是java.lang.reflect.Field這個類的的對象
         * Field類封裝了關于成員變量的操作
         * getFields()方法擷取的是所有public的成員變量的資訊
         * getDeclareFields()方法擷取的是該類自己聲明的成員變量的資訊
         */
        Field[] fs = c.getDeclaredFields();
        for (Field field : fs) {
            //得到成員變量的類型的類類型
            Class fieldType = field.getType();
            String typeName = fieldType.getName();
            //得到成員變量的名稱
            String fieldName = field.getName();
            System.out.println(typeName + " " + fieldName);
        }
    }
    
    @Test
    public void testPrintClassMessage() {
        printClassField(new HashMap<>());
    }
           

執行結果:

【Java基礎】反射(reflect)一.Java類加載器簡單了解二.什麼是反射機制三.java.lang.Class示例類(以下代碼以這個類為基準)四.java.lang.reflect.Constuctor五.java.lang.reflect.Method六.java.lang.reflect.Field七.反射使用方式

七.反射使用方式

1.反射來擷取泛型資訊

通過指定對應的Class對象,程式可以獲得該類裡面所有的Field,不管該Field使用private 方法public。獲得Field對象後都可以使用getType()來擷取其類型。

但此方法隻對普通Field有效,若該Field有泛型修飾,則不能準确得到該Field的泛型參數,如Map<String,Integer>;

為了獲得指定Field的泛型類型,我們采用:

Type gType = f.getGenericType();得到泛型類型
           

然後将Type對象強轉為ParameterizedType,其表示增加泛型後的類型

Type getRawType()//傳回被泛型限制的類型;
Type[]  getActualTypeArguments()//傳回泛型參數類型;
           

利用反射來擷取泛型的類型(泛型資訊)

步驟:

  • 擷取目前類
  • 擷取目标字段
  • 擷取包含泛型類型的類型 getGenericType()
  • 強轉至子類ParameterizedType 因為Type沒有任何對應的方法
  • 獲得泛型真正的類型 getActualTypeArguments()
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;
public class GetGenericTypeDemo14 {
   Map<String,Integer> map = new HashMap<String,Integer>();
  
   public static void main(String[] args) throws Exception {
      Class c = GetGenericTypeDemo14.class;
      Field f = c.getDeclaredField("map");
      System.out.println(f);
      System.out.println(f.getName());//map
     
      // Class<?> getType()  傳回一個 Class 對象,它辨別了此 Field 對象所表示字段的聲明類型。
      Class cl = f.getType();
      System.out.println("獲得其類型:"+cl);
//獲得其類型:interface java.util.Map
    
      /**
       *  Type getGenericType() 傳回一個 Type 對象,它表示此 Field 對象所表示字段的聲明類型。
       *  Type是Class的接口;
       */
      Type t = f.getGenericType();//包含泛型的類型
      System.out.println(t);
//java.util.Map<java.lang.String, java.lang.Integer>
     
      /**
       * Type這個類裡面沒有任何的方法,是以需要調用子類的方法,那麼大的類型轉到小的類型,需要強轉!
       */
      ParameterizedType pt = (ParameterizedType)t;//強轉到其子類
      /**
       *  Type[] getActualTypeArguments()
                   傳回表示此類型實際類型參數的 Type對象的數組。
          Type getOwnerType()
                   傳回 Type 對象,表示此類型是其成員之一的類型。
          Type getRawType()
                   傳回 Type 對象,表示聲明此類型的類或接口。
       */
     
      t = pt.getRawType();//類型的類或接口
      System.out.println(t);
     
      Type[] ts = pt.getActualTypeArguments();
      for (Type type : ts) {
         System.out.println(type);
         /**
          *  class java.lang.String
             class java.lang.Integer
          */
      }
   }
}
           

列印:

java.util.Map junereflect624.GetGenericTypeDemo14.map

map

獲得其類型:interface java.util.Map

java.util.Map<java.lang.String, java.lang.Integer>

interface java.util.Map

class java.lang.String

class java.lang.Intege

2.泛型擦除

  • 定義集合類,泛型String,要求向集合中添加Integer類型。
  • 集合類的泛型為String,向集合添加Integer類型資料會編譯失敗。
  • 僞泛型:編譯後的class檔案是沒有泛型的。
  • 想法:繞過泛型,直接調用class檔案中的add方法。

    步驟:

    • 擷取ArrayList類的class檔案對象。
    • 擷取ArrayList.class檔案中的方法add()。
    • 向其中添加字元串元素。
import java.lang.reflect.Method;
import java.util.ArrayList;

public class GenericErasure{
    public static void main(String[] args) throws Exception {
        ArrayList<String> list = new ArrayList<>();
        list.add("abc");
        list.add("def");
        //擷取ArrayList類的class檔案對象
        Class<? extends ArrayList> c1 = list.getClass();
        //擷取ArrayList.class檔案中的方法add()
        Method add = c1.getMethod("add",Object.class);
        System.out.println(add);
        //向其中添加字元串元素
        add.invoke(list,1);
        System.out.println(list);
    }
}
           

3.反射通過配置檔案運作功能的實作

  • 調用某類的某個方法,不清楚哪個類的哪個方法。
  • 通過配置檔案實作此功能:
    • 運作的類名與方法名,以鍵值對的形式寫在文本中。
    • 運作哪個類,讀取配置檔案即可。
  • 實作步驟:
    1. 準備配置檔案,鍵值對的形式。
    2. IO流讀取配置檔案 Reader。
    3. 将檔案中的鍵值對存儲到集合中Properties(集合中儲存的鍵值對就是要運作的類名與方法)。
    4. 反射擷取指定類的class檔案對象。
    5. 通過class檔案對象,擷取指定的方法。
    6. 運作方法。
//測試類1
public class Person {
    public void eat(){
        System.out.println("人在吃飯");
    }
}

//測試類2
public class Student {
    public void study(){
        System.out.println("學生在學習");
    }
}
           
className=cn.mrzhang.demo2.Student
methodName=study

#className=cn.mrzhang.demo2.Person
#methodName=job
           
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.Properties;

/**
 * 調用3個類中一個類的方法
 *  不清楚調用哪個類中的哪個方法,不能修改代碼
 *
 *      通過配置檔案實作此功能
 *          運作的類名與方法名,以鍵值對的形式寫在文本中
 *          運作哪個類,讀取配置檔案即可
 *      實作步驟:
 *          1.準備配置檔案,鍵值對的形式
 *          2.IO流讀取配置檔案 Reader
 *          3.将檔案中的鍵值對存儲到集合中Properties
 *              集合中儲存的鍵值對就是要運作的類名與方法名
 *          4.反射擷取指定類的class檔案對象
 *          5.通過class檔案對象,擷取指定的方法
 *          6.運作方法
 */
public class TestReflectConfig {
    public static void main(String[] args) throws Exception {
        //IO流讀取配置檔案
        InputStream resourceAsStream = TestDemo2.class.getClassLoader().getResourceAsStream("config.properties");
        //建立集合對象
        Properties p = new Properties();
        //調用方法load傳遞流對象
        p.load(resourceAsStream);
        //釋放流對象
        resourceAsStream.close();
        //通過鍵擷取值
        String className = p.getProperty("className");
        String methodName = p.getProperty("methodName");
        //反射擷取指定類的class檔案對象
        Class<?> c1 = Class.forName(className);
        Object obj = c1.newInstance();
        //反射擷取指定的方法
        Method method = c1.getMethod(methodName);
        //執行方法
        method.invoke(obj);
    }
}
           

4.使用反射擷取類、方法、屬性上的注解

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TestAnnotation {
    public int id() default -1;
    public String msg() default "Hi";
 
}


public @interface Check {
    String value();
}

public @interface Perform {}
 

           
@TestAnnotation(msg="hello")
public class Test {
    @Check(value="hi")
    int a;
 
    @Perform
    public void testMethod(){}
 
    @SuppressWarnings("deprecation")
    public void test1(){
        Hero hero = new Hero();
        hero.say();
        hero.speak();
    }
 
    public static void main(String[] args) {
        boolean hasAnnotation = Test.class.isAnnotationPresent(TestAnnotation.class);
 
        if ( hasAnnotation ) {
            TestAnnotation testAnnotation = Test.class.getAnnotation(TestAnnotation.class);
            //擷取類的注解
            System.out.println("id:"+testAnnotation.id());
            System.out.println("msg:"+testAnnotation.msg());
        }
        
        try {
            Field a = Test.class.getDeclaredField("a");
            a.setAccessible(true);
            //擷取一個成員變量上的注解
            Check check = a.getAnnotation(Check.class);
 
            if ( check != null ) {
                System.out.println("check value:"+check.value());
            }
 
            Method testMethod = Test.class.getDeclaredMethod("testMethod");
 
            if ( testMethod != null ) {
                // 擷取方法中的注解
                Annotation[] ans = testMethod.getAnnotations();
                for( int i = 0;i < ans.length;i++) {
                    System.out.println("method testMethod annotation:"+ans[i].annotationType().getSimpleName());
                }
            }
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
            System.out.println(e.getMessage());
        } catch (SecurityException e) {
            e.printStackTrace();
            System.out.println(e.getMessage());
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
            System.out.println(e.getMessage());
        }
    }
}
           

5.使用反射映射Jdbc的結果集

資料表

CREATE TABLE `user` (
  `id` int(11) DEFAULT NULL,
  `name` varchar(50) DEFAULT '',
  `pwd` varchar(50) DEFAULT '',
  `age` tinyint(2) DEFAULT '0'
) ENGINE=InnoDB DEFAULT CHARSET=utf8

           

使用者類

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private int id;
    private String name;
    private String pwd;
    private int age;
 
    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", pwd='" + pwd + '\'' +
                ", age=" + age +
                '}';
    }
}
           

擷取資料庫連接配接簡易工具類

public class ConnectDBFactory {
    public static  Connection getDBConnection(){
        Connection conn = null;
        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
            String url = "jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=gbk&useSSL=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Hongkong  ";
            String user = "root";
            String password = "root";
            conn = DriverManager.getConnection(url,user,password);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return conn;
    }
}
           

反射處理的Jdb-c新增/查詢映射工具

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;

public class SqlSession {
    public static String getSaveObjectSql(Object o) throws InvocationTargetException, IllegalAccessException {

        String sql = "insert into ";
        /*擷取Class對象*/
        Class c = o.getClass();
        /*擷取pojo所有的方法*/
        Method[] methods = c.getDeclaredMethods();
        /*擷取全類名*/
        String cName = c.getName();
        /*通過全類名擷取資料庫名稱*/
        String tableName = cName.substring(cName.lastIndexOf(".") + 1, cName.length());
        sql += tableName + "(";
        /*字段名字*/
        List<String> fieldList = new ArrayList<>();
        /*字段對應的值*/
        List valueList = new ArrayList();

        /*周遊Class對象的Method對象,就可以執行相對于的方法了*/
        for (Method method :
                methods) {
            String methodName = method.getName();
            /*找出get方法,并設定值*/
            if (methodName.startsWith("get") && !method.equals("getClass")) {
                String fieldName = methodName.substring(3, methodName.length());
                fieldList.add(fieldName);
                Object res = method.invoke(o, null);
                if (res instanceof String) {
                    valueList.add("\"" + res + "\"");
                } else {
                    valueList.add(res);
                }
            }
        }

        /*拼接sql語句的字段*/
        for (int i = 0; i < fieldList.size(); i++) {
            if (i < fieldList.size() - 1) {
                sql += fieldList.get(i) + ",";
            } else {
                sql += fieldList.get(i) + ") values (";
            }
        }

        /*拼接sql語句的值*/
        for (int i = 0; i < valueList.size(); i++) {
            if (i < valueList.size() - 1) {
                sql += valueList.get(i) + ",";
            } else {
                sql += valueList.get(i) + ")";
            }
        }

        return sql;
    }


    /*儲存資料的操作*/
    public int saveObject(Object o) throws InvocationTargetException, IllegalAccessException, SQLException {
        Connection connection = ConnectDBFactory.getDBConnection();
        String sql = getSaveObjectSql(o);
        PreparedStatement statement = connection.prepareStatement(sql);
        int i = 0;
        i = statement.executeUpdate();
        return i;
    }

    /*
     * 查詢資料,查詢出來的資料映射到pojo的每一個屬性上
     * */
    public List<Object> getObject(String pname, int id) throws ClassNotFoundException {
        List<Object> list = new ArrayList<>();

        /*通過包名擷取資料表名*/
        String tableName = pname.substring(pname.lastIndexOf(".") + 1, pname.length());
        String sql = "select * from " + tableName + " where Id = " + id;

        Connection conn = ConnectDBFactory.getDBConnection();
        Class c = Class.forName(pname);
        Object obj = null;
        try {
            Statement statement = conn.createStatement();
            ResultSet resultSet = statement.executeQuery(sql);
            Method[] methods = c.getDeclaredMethods();

            while (resultSet.next()) {
                obj = c.newInstance();
                for (Method method : methods
                ) {
                    String methodName = method.getName();
                    if (methodName.startsWith("set")) {
                        /*通過方法名擷取資料庫的列名*/
                        String columnName = methodName.substring(3, methodName.length());
                        /*擷取參數的類型*/
                        Class[] params = method.getParameterTypes();
                        /*判斷參數的類型*/
                        if (params[0] == String.class) {
                            method.invoke(obj, resultSet.getString(columnName));
                        }
                        if (params[0] == int.class) {
                            method.invoke(obj, resultSet.getInt(columnName));
                        }
                    }
                }
                list.add(obj);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return list;
    }


    public static void main(String[] args) {
        try {
            SqlSession session = new SqlSession();

            //新增
            User user = new User();
            user.setAge(22);
            user.setName("JiemingLi");
            user.setId(44);
            user.setPwd("123456");
            int resNum  = session.saveObject(user);
            if(resNum > 0){
                System.out.println("成功");
            }else{
                System.out.println("插入失敗");
            }

            //查詢
            List<Object> list = session.getObject("com.demo.reflect.jdbc.User", 44);
            System.out.println(list.size());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
           

6.調用方法的反射工具類

public static Object setMethod(String className,String methodName,Object[] valueArr,Class[] classArr) throws Exception, SecurityException{
		//擷取class對象
		Class clazz=Class.forName(className);
	
		//擷取該class對象對應的構造器描述
		Constructor<?> constr=clazz.getConstructor();
		
		//根據class對象擷取指定方法描述
		Method method=clazz.getMethod(methodName,classArr);
	
		//調用方法
		return method.invoke(constr.newInstance(), valueArr);
	}
           

大家都說 Java 反射效率低,你知道原因在哪裡麼?

java反射機制性能優化

java 擷取某個包下的所有類名

利用java反射來查找某個包下的所有類,并得到class取得執行個體

java利用反射擷取某個包下的所有擷取對象屬性、方法、并執行個體化