03-Java核心類庫_列舉、註解與反射
目錄
11.2 得到class的幾種方式(將類載入到記憶體的方式)
八,列舉、註解與反射
反射和內省,如果不進行框架開發,則很少使用;註解一般使用定義好的,自定義註解用的比較少
1,列舉概述
1.1 簡介
JDK1.5引入了新的型別——列舉。
在JDK1.5 之前,我們定義常量都是: public static fianl.... 。很難管理。
列舉,可以把相關的常量分組到一個列舉型別裡,而且列舉提供了比常量更多的方法。
用於定義有限數量的一組同類常量,例如:
- 錯誤級別: 低、中、高、急
- 一年的四季: 春、夏、秋、冬
- 商品的型別: 美妝、手機、電腦、男裝、女裝...
在列舉型別中定義的常量是該列舉型別的例項
2,列舉定義
2.1 Java1.5版本之前
Level.java
package com.kaikeba;
public class Level {
// static表示可以通過類名訪問 final表示為常量(變數名稱為大寫)
public static final Level LOW = new Level(1);
public static final Level MEDIUM = new Level(2);
public static final Level HIGH = new Level(3);
private int levelValue;
// private修飾的構造方法 表示外界不能通過構造方法例項化物件 同時也不能新增型別
private Level(int levelValue) {
this.levelValue = levelValue;
}
public int getLevelValue() {
return levelValue;
}
public void setLevelValue(int levelValue) {
this.levelValue = levelValue;
}
}
Demo.java
package com.kaikeba;
public class Demo {
public static void main(String[] args) {
System.out.println(Level.LOW.getLevelValue());
}
}
2.2 Java1.5版本之後
Level.java
package com.kaikeba;
// 注意這裡將class換成enum
public enum Level {
// 新版本列舉的方法
LOW(1), MEDIUM(2), HIGH(3);
// 也可以這樣來表示 直接從順序上判斷優先順序
// LOW, MEDIUM, HIGH;
private int levelValue;
// private修飾的構造方法 表示外界不能通過構造方法例項化物件 同時也不能新增型別
private Level(int levelValue) {
this.levelValue = levelValue;
}
public int getLevelValue() {
return levelValue;
}
public void setLevelValue(int levelValue) {
this.levelValue = levelValue;
}
}
Demo.java
package com.kaikeba;
public class Demo {
public static void main(String[] args) {
System.out.println(Level.LOW.getLevelValue());
}
}
3,列舉常用方法
3.1 歸納
3.2 例項
Demo.java
package com.kaikeba;
public class Demo {
public static void main(String[] args) {
// compareTo方法
System.out.println(Level.LOW.compareTo(Level.MEDIUM));
System.out.println(Level.LOW.compareTo(Level.HIGH));
// name方法
System.out.println(Level.LOW.name());
// toString方法
System.out.println(Level.LOW.toString());
// ordinal方法
System.out.println(Level.LOW.ordinal());
// valueOf方法
Level x = Enum.valueOf(Level.class, "HIGH");
System.out.println(x.name());
}
}
4,列舉介面
所有的列舉都繼承自java.lang.Enum類。由於Java 不支援多繼承,所以列舉物件不能再繼承其他類。
每個列舉物件,都可以實現自己的抽象方法
舉例
Level.java
main函式
存在的問題:LOW、MEDIUM、HIGH都是一樣的輸出
5,列舉注意事項
一旦定義了列舉,最好不要妄圖修改裡面的值,除非修改是必要的;
列舉類預設繼承的是java.lang.Enum類而不是Object類;
列舉類不能有子類,因為其列舉類預設被final修飾;
只能有private構造方法;
switch中使用列舉時,直接使用常量名,不用攜帶類名;
不能定義name屬性,因為自帶name屬性;
不要為列舉類中的屬性提供set方法,不符合列舉最初設計初衷;
6,註解概述
註解與註釋:註釋是為了程式設計師更好的理解程式碼,只能存在於原始檔中,一旦編譯變成.class檔案,便會忽略掉註釋。註解可以理解為讓機器理解的,可以保留到程式碼執行的階段。
Java 註解(Annotation)又稱 Java 標註,是 JDK5.0 引入的一種註釋機制。
Java 語言中的類、方法、變數、引數和包等都可以被標註。和註釋不同,Java 標註可以通過反射獲取標註內容。在編譯器生成類檔案時,標註可以被嵌入到位元組碼中。Java 虛擬機器可以保留標註內容,在執行時可以獲取到標註內容 。 當然它也支援自定義 Java 標註。
主要用於:
- 編譯格式檢查
- 反射中解析
- 生成幫助文件
- 跟蹤程式碼依賴
- 等
7,內建註解
7.1 概述
@Override : 重寫 *
定義在java.lang.Override
@Deprecated:廢棄 *
定義在java.lang.Deprecated
@SafeVarargs
Java 7 開始支援,忽略任何使用引數為泛型變數的方法或建構函式呼叫產生的警告。
@FunctionalInterface: 函式式介面 *
Java 8 開始支援,標識一個匿名函式或函式式介面。
@Repeatable:標識某註解可以在同一個宣告上使用多次
Java 8 開始支援,標識某註解可以在同一個宣告上使用多次。
SuppressWarnings:抑制編譯時的警告資訊。 *
定義在java.lang.SuppressWarnings
三種使用方式
- @SuppressWarnings("unchecked") [^ 抑制單型別的警告]
- @SuppressWarnings("unchecked","rawtypes") [^ 抑制多型別的警告]
- @SuppressWarnings("all") [^ 抑制所有型別的警告]
引數列表
- all 抑制所有警告
- boxing 抑制裝箱、拆箱操作時候的警告
- cast 抑制對映相關的警告
- dep-ann 抑制啟用註釋的警告
- deprecation 抑制過期方法警告
- fallthrough 抑制確在switch中缺失breaks的警告
- finally 抑制finally模組沒有返回的警告
- hiding 抑制相對於隱藏變數的區域性變數的警告
- incomplete-switch 忽略沒有完整的switch語句
- nls 忽略非nls格式的字元
- null 忽略對null的操作
- rawtypes 使用generics時忽略沒有指定相應的型別
- restriction 抑制禁止使用勸阻或禁止引用的警告
- serial 忽略在serializable類中沒有宣告serialVersionUID變數
- static-access 抑制不正確的靜態訪問方式警告
- synthetic-access 抑制子類沒有按最優方法訪問內部類的警告
- unchecked 抑制沒有進行型別檢查操作的警告
- unqualified-field-access 抑制沒有許可權訪問的域的警告
- unused 抑制沒被使用過的程式碼的警告
7.2 概述
1)Override重寫
IDEA中每次儲存程式碼,都會將.java檔案轉換為.class檔案, 這時會檢查屬性或方法的註解,檢視其是否符合註解的格式要求,若不符合則給出錯誤資訊:
2)Deprecated廢棄
對於前期編寫的一些方法,後期可能不再需要,若此時將其刪除,則會使之前呼叫此方法的地方出現問題,所以建議擴充套件程式碼,而不是刪除。
package com.kaikeba;
public class Demo {
public static void main(String[] args) {
Person p = new Person();
p.setAge(12);
}
}
class Person{
private int age;
public int getAge() {
return age;
}
/**
* 此方法已經被廢棄,請通過setAge2進行操作
* @param age
*/
@Deprecated
public void setAge(int age) {
this.age = age;
}
public void setAge2(int age) {
if(age < 0 || age > 120) {
throw new RuntimeException("該年齡不合理");
}
this.age = age;
}
}
3)SuppressWarnings:抑制編譯時的警告資訊
8,自定義註解
8.1 元註解
1)簡介
作用在其他註解的註解
2)包括
@Retention - 標識這個註解怎麼儲存,是隻在程式碼中,還是編入class檔案中,或者是在執行時可以通過反射訪問。
@Documented - 標記這些註解是否包含在使用者文件中 javadoc。
@Target - 標記這個註解應該是哪種 Java 成員。
@Inherited - 標記這個註解是自動繼承的
- 子類會繼承父類使用的註解中被@Inherited修飾的註解
- 介面繼承關係中,子介面不會繼承父介面中的任何註解,不管父介面中使用的註解有沒有 被@Inherited修飾
- 類實現介面時不會繼承任何介面中定義的註解
8.2 自定義註解——註解架構
01) Annotation與RetentionPolicy 與ElementType
每 1 個 Annotation 物件,都會有唯一的 RetentionPolicy 屬性;至於 ElementType 屬性,則有 1~n個。
(02) ElementType(註解的用途型別)
"每 1 個 Annotation" 都與 "1~n 個 ElementType" 關聯。當 Annotation 與某個 ElementType 關聯時,就意味著:Annotation有了某種用途。例如,若一個 Annotation 物件是 METHOD 型別,則該Annotation 只能用來修飾方法。
package java.lang.annotation; public enum ElementType { TYPE, /* 類、介面(包括註釋型別)或列舉宣告 */ FIELD, /* 欄位宣告(包括列舉常量) */ METHOD, /* 方法宣告 */ PARAMETER, /* 引數宣告 */ CONSTRUCTOR, /* 構造方法宣告 */ LOCAL_VARIABLE, /* 區域性變數宣告 */ ANNOTATION_TYPE, /* 註釋型別宣告 */ PACKAGE /* 包宣告 */ }
(03) RetentionPolicy(註解作用域策略)
"每 1 個 Annotation" 都與 "1 個 RetentionPolicy" 關聯。
- a) 若 Annotation 的型別為 SOURCE,則意味著:Annotation 僅存在於編譯器處理期間,編譯器處理完之後,該 Annotation 就沒用了。 例如," @Override" 標誌就是一個 Annotation。當它修飾一個方法的時候,就意味著該方法覆蓋父類的方法;並且在編譯期間會進行語法檢查!編譯器處理完後,"@Override" 就沒有任何作用了。
- b) 若 Annotation 的型別為 CLASS,則意味著:編譯器將 Annotation 儲存於類對應的 .class 檔案中,它是 Annotation 的預設行為。
- c) 若 Annotation 的型別為 RUNTIME,則意味著:編譯器將 Annotation 儲存於 class 檔案中,並且可由JVM讀入。
package java.lang.annotation; public enum RetentionPolicy { SOURCE, /* Annotation資訊僅存在於編譯器處理期間,編譯器處理完之後就沒有該 Annotation資訊了 */ CLASS, /* 編譯器將Annotation儲存於類對應的.class檔案中。預設行為 */ RUNTIME /* 編譯器將Annotation儲存於class檔案中,並且可由JVM讀入 */ }
8.3 自定義註解——定義格式
@interface 自定義註解名{}
注意事項
1. 定義的註解,自動繼承了java.lang,annotation.Annotation介面
2. 註解中的每一個方法,實際是宣告的註解配置引數
- 方法的名稱就是 配置引數的名稱
- 方法的返回值型別,就是配置引數的型別。只能是:基本型別/Class/String/enum
3. 可以通過default來宣告引數的預設值
4. 如果只有一個引數成員,一般引數名為value
5. 註解元素必須要有值,我們定義註解元素時,經常使用空字串、0作為預設值。
舉例
註解傳參
也可以不傳遞全部引數
9,反射概述
JAVA反射機制是在執行狀態(程式已經執行起來後)中,獲取任意一個類的結構 , 建立物件 , 得到方法,執行方法 , 屬性 !;
這種在執行狀態動態獲取資訊以及動態呼叫物件方法的功能被稱為java語言的反射機制。
spring是很早就編寫的框架,用它來管理現在建立的物件,所用到的就是反射機制
10,類載入器與資源目錄
10.1 類載入器
.java檔案通過編譯得到.class位元組碼檔案,再通過類載入器載入到記憶體中,才可以執行,根據類建立物件
Java類載入器(Java Classloader)是Java執行時環境(Java Runtime Environment)的一部分, 負責動態載入Java類到Java虛擬機器的記憶體空間中。
java預設有三種類載入器,BootstrapClassLoader、ExtensionClassLoader、App ClassLoader。
- BootstrapClassLoader(引導啟動類載入器): 嵌在JVM核心中的載入器,該載入器是用C++語言寫的,主要負責載入JAVA_HOME/lib下的類庫,引 導啟動類載入器無法被應用程式直接使用。
- ExtensionClassLoader(擴充套件類載入器): ExtensionClassLoader是用JAVA編寫,且它的父類載入器是Bootstrap。 是由sun.misc.Launcher$ExtClassLoader實現的,主要載入JAVA_HOME/lib/ext目錄中的類 庫。 它的父載入器是BootstrapClassLoader
- App ClassLoader(應用類載入器): App ClassLoader是應用程式類載入器,負責載入應用程式classpath目錄下的所有jar和class文 件。它的父載入器為Ext ClassLoader
類通常是按需載入,即第一次使用該類時才載入。
由於有了類載入器,Java執行時系統不需要知道檔案與 檔案系統。學習類載入器時,掌握Java的委派概念很重要。
雙親委派模型:如果一個類載入器收到了一個類載入請求,它不會自己去嘗試載入這個類,而是把這個請求 轉交給父類載入器去完成。每一個層次的類載入器都是如此。因此所有的類載入請求都應該傳遞到最頂層的 啟動類載入器中,只有到父類載入器反饋自己無法完成這個載入請求(在它的搜尋範圍沒有找到這個類) 時,子類載入器才會嘗試自己去載入。
委派的好處就是避免有些類被重複載入。
獲得類載入器
通過類載入器讀取配置檔案內容
建立新資料夾resource(截圖中應該命名為resource)
將該資料夾設定為資源根目錄
將config.txt檔案放入剛建立的資料夾中,並修改內容
再次執行程式碼
11,Class與載入方式
11.1 所有型別的Class物件
要想了解一個類,必須先要獲取到該類的位元組碼檔案物件.
在Java中,每一個位元組碼檔案,被載入到記憶體後,都存在一個對應的Class型別的物件
11.2 得到class的幾種方式(將類載入到記憶體的方式)
1. 如果在編寫程式碼時, 指導類的名稱, 且類已經存在, 可以通過
- 包名.類名.class 得到一個類的 類物件
2. 如果擁有類的物件, 可以通過
- Class 物件.getClass() 得到一個類的 類物件
3. 如果在編寫程式碼時, 知道類的名稱 , 可以通過
- Class.forName(包名+類名): 得到一個類的 類物件
上述的三種方式, 在呼叫時, 如果類在記憶體中不存在, 則會載入到記憶體 ! 如果類已經在記憶體中存在, 不 會重複載入, 而是重複利用 !
舉例
package com.kaikeba;
public class Demo {
public static void main(String[] args) throws ClassNotFoundException {
// 方式1:通過包名.類名.class載入類
Class<Book> c1 = com.kaikeba.Book.class;
System.out.println(c1);
// 方式2:通過類的物件獲取類的資訊(此時類已經載入到記憶體中了)
Book b = new Book();
Class<Book> c2 = (Class<Book>) b.getClass();
System.out.println(c2);
// 方式3:通過forName+類的字串名稱載入類
Class<Book> c3 = (Class<Book>) Class.forName("com.kaikeba.Book");
System.out.println(c3);
System.out.println(c1 == c2 && c1 == c3);
}
}
當把Book類刪除後
所以一些框架可以通過這種方法將類載入到記憶體中去:
12,反射中的構造方法
12.1 通過class物件 獲取一個類的構造方法
1. 通過指定的引數型別, 獲取指定的單個構造方法
getConstructor(引數型別的class物件陣列)
例如:
構造方法如下: Person(String name,int age)
得到這個構造方法的程式碼如下:
Constructor c = p.getClass().getConstructor(String.class,int.class);
2. 獲取構造方法陣列
getConstructors();
3. 獲取所有許可權的單個構造方法
getDeclaredConstructor(引數型別的class物件陣列)
4. 獲取所有許可權的構造方法陣列
getDeclaredConstructors();
12.2 Constructor 建立物件
newInstance(Object... para)
呼叫這個構造方法, 把對應的物件建立出來
引數: 是一個Object型別可變引數, 傳遞的引數順序 必須匹配構造方法中形式引數列表的順 序!
setAccessible(boolean flag)
如果flag為true 則表示忽略訪問許可權檢查 !(可以訪問任何許可權的方法)
舉例——無參構造方法
package com.kaikeba;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class Demo {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
// 將類載入到記憶體中
Class<Person> pClass = (Class<Person>) Class.forName("com.kaikeba.Person");
// 獲得無參構造方法
Constructor<Person> c1 = pClass.getConstructor();
// 使用無參構造方法例項化物件
Person p = c1.newInstance();
System.out.println(p);
}
}
舉例——全參構造方法
package com.kaikeba;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class Demo {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
// 將類載入到記憶體中
Class<Person> pClass = (Class<Person>) Class.forName("com.kaikeba.Person");
// 獲得全參構造方法
// Constructor<Person> c2 = pClass.getConstructor(new Class[]{String.class, int.class});
Constructor<Person> c2 = pClass.getConstructor(String.class, int.class);
// 使用全參構造方法例項化物件
Person p = c2.newInstance("張三", 18);
System.out.println(p);
}
}
12.3 反射技術打破封裝的例子
1)在Person類中新增私有化的構造方法(必須用getDeclaredConstructor方法獲取私有的構造方法)
執行提示報錯
忽略許可權檢查
13,反射中的方法
通過反射找到類中的方法,再通過物件執行類的方法。
13.1 通過class物件 獲取一個類的方法
1. getMethod(String methodName , class.. clss)
根據引數列表的型別和方法名, 得到一個方法(public修飾的)
2. getMethods();
得到一個類的所有方法 (public修飾的)
3. getDeclaredMethod(String methodName , class.. clss)
根據引數列表的型別和方法名, 得到一個方法(除繼承以外所有的:包含私有, 共有, 保護, 預設)
4. getDeclaredMethods();
得到一個類的所有方法 (除繼承以外所有的:包含私有, 共有, 保護, 預設)
13.2 Method 執行方法
invoke(Object o,Object... para) :
- 引數1. 要呼叫方法的物件
- 引數2. 要傳遞的引數列表
getName()
- 獲取方法的方法名稱
setAccessible(boolean flag)
- 如果flag為true 則表示忽略訪問許可權檢查 !(可以訪問任何許可權的方法)
13.3 舉例
package com.kaikeba;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Demo {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
// 將類載入到記憶體中
Class pClass = Class.forName("com.kaikeba.Person");
// 獲得私有的構造方法
Constructor c1 = pClass.getConstructor();
// 建立物件
Object o = c1.newInstance();
// 獲取類的方法
Method setName = pClass.getMethod("setName", String.class);
// 執行setName方法(引數一:呼叫方法的物件,引數二:呼叫方法時傳遞的引數)
setName.invoke(o, "haha");
System.out.println(o);
}
}
獲得private修飾的方法
忽略許可權獲得private修飾的方法,並執行
package com.kaikeba;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Demo {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
// 將類載入到記憶體中
Class pClass = Class.forName("com.kaikeba.Person");
// 獲得私有的構造方法
Constructor c1 = pClass.getConstructor();
// 建立物件
Object o = c1.newInstance();
// 獲取類的方法
Method setName = pClass.getMethod("setName", String.class);
Method setAge = pClass.getDeclaredMethod("setAge", int.class);
setAge.setAccessible(true);// 忽略許可權檢查
// 執行setName方法(引數一:呼叫方法的物件,引數二:呼叫方法時傳遞的引數)
setName.invoke(o, "haha");
setAge.invoke(o, 18);
System.out.println(o);
}
}
14,反射中的屬性
14.1 通過class物件 獲取一個類的屬性
1. getDeclaredField(String filedName)
- 根據屬性的名稱, 獲取一個屬性物件 (所有屬性)
2. getDeclaredFields()
- 獲取所有屬性
3. getField(String filedName)
- 根據屬性的名稱, 獲取一個屬性物件 (public屬性)
4. getFields()
- 獲取所有屬性 (public)
14.2 Field 屬性的物件型別
常用方法:
1. get(Object o ); 獲取指定物件的此屬性值
- 引數: 要獲取屬性的物件
2. set(Object o , Object value);設定指定物件的屬性的值
- 引數1. 要設定屬性值的 物件
- 引數2. 要設定的值
3. getName() 獲取屬性的名稱
4. setAccessible(boolean flag)
- 如果flag為true 則表示忽略訪問許可權檢查 !(可以訪問任何許可權的屬性)
15,反射與屬性
通過反射獲取註解的內容
15.1 獲取類/屬性/方法的全部註解物件
Annotation[] annotations01 = Class/Field/Method.getAnnotations();
for (Annotation annotation : annotations01) {
System.out.println(annotation);
}
15.2 根據型別獲取類/屬性/方法的註解物件
註解型別 物件名 = (註解型別) c.getAnnotation(註解型別.class);
15.3 舉例
TableAnnotation.java
package com.kaikeba.demo;
import java.lang.annotation.*;
@Target(ElementType.TYPE) // 允許使用在類上
@Retention(RetentionPolicy.RUNTIME) // 持久化策略
@Documented // 允許寫在文件中
public @interface TableAnnotation {
/**
* 用於標註類對應的表格名稱(與資料庫關聯)
* @return
*/
String value();
}
ColumnAnnotation.java
package com.kaikeba.demo;
import java.lang.annotation.*;
@Target(ElementType.FIELD)// 允許使用在屬性上
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ColumnAnnotation {
/**
* 描述列名
* @return
*/
String columnName();
/**
* 描述型別
* @return
*/
String type();
/**
* 描述資料的長度
* @return
*/
String length();
}
Book.java
package com.kaikeba.demo;
import java.util.Objects;
// 內部原理:通過反射找到類,並找到類上的註解,最終基於註解中的內容與資料庫中的表格進行繫結
// 這裡雖然加上了自定義的四個註解,但是缺少背後與資料庫關聯的邏輯,所以沒有實際作用
@TableAnnotation("test_Book")// 通過註解描述 這個類和資料庫中的Book對應
public class Book {
@ColumnAnnotation(columnName = "id", type = "int", length = "11")
private int id;
@ColumnAnnotation(columnName = "name", type = "varchar", length = "50")
private String name;
@ColumnAnnotation(columnName = "info", type = "varchar", length = "1000")
private String info;
public Book(int id, String name, String info) {
this.id = id;
this.name = name;
this.info = info;
}
public Book() {
}
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 String getInfo() {
return info;
}
public void setInfo(String info) {
this.info = info;
}
@Override
public String toString() {
return "Book{" +
"id=" + id +
", name='" + name + '\'' +
", info='" + info + '\'' +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Book book = (Book) o;
return id == book.id &&
Objects.equals(name, book.name) &&
Objects.equals(info, book.info);
}
@Override
public int hashCode() {
return Objects.hash(id, name, info);
}
}
Demo.java
package com.kaikeba.demo;
import java.io.File;
import java.lang.reflect.Field;
import java.text.Annotation;
public class Demo {
public static void main(String[] args) throws ClassNotFoundException {
Class c = Class.forName("com.kaikeba.demo.Book");
TableAnnotation ta = (TableAnnotation) c.getAnnotation(TableAnnotation.class);
String value = ta.value();
System.out.println("表名:" + value); // 類名作為資料庫中表格的名稱
Field[] fs = c.getDeclaredFields(); // 屬性名作為資料庫中的欄位名稱
for(Field f : fs) {
ColumnAnnotation ca = f.getAnnotation(ColumnAnnotation.class);
System.out.println(f.getName() + "屬性,對應資料庫中的欄位:" + ca.columnName() + ";資料型別:" + ca.type() + ";資料長度:" + ca.length());
}
}
}
執行效果
一些ORM框架的原理:當使用提供的註解時,框架通過找到類,來獲取註解的值(類名以及屬性),然後就可以根據值在資料庫中建立表(將類名與屬性作為表格與欄位的名稱)
16,內省
16.1 簡介
基於反射 , java所提供的一套應用到JavaBean的API
- 一個定義在包中的類 ,
- 擁有無參構造器
- 所有屬性私有,
- 所有屬性提供get/set方法
- 實現了序列化介面
這種類, 我們稱其為 bean類 . (一些沒有業務邏輯的類)
Java提供了一套java.beans包的api , 對於反射的操作, 進行了封裝
16.2 Introspector
獲取Bean類資訊
方法:
- BeanInfo getBeanInfo(Class cls)
- 通過傳入的類資訊, 得到這個Bean類的封裝物件 .
16.3 BeanInfo
常用的方法:
- MethodDescriptor[] getPropertyDescriptors():
- 獲取bean類的 get/set方法 陣列
16.4 MethodDescriptor
常用方法:
1. Method getReadMethod();
- 獲取一個get方法
2. Method getWriteMethod();
- 獲取一個set方法
有可能返回null 注意 ,加判斷 !
16.5 內省的機制
程式碼
Book.java
package com.kaikeba.demo;
import java.io.Serializable;
import java.util.Objects;
/**
* 實現序列化介面
*/
public class Book implements Serializable {
/**
* 所有屬性必須私有 且通過Getter和Setter方法訪問
*/
private int id;
private String name;
private String info;
private boolean flag;
public Boolean getFlag() {
return flag;
}
public void setFlag(Boolean flag) {
this.flag = flag;
}
public Book(int id, String name, String info) {
this.id = id;
this.name = name;
this.info = info;
}
/**
* 必須有無參構造方法
*/
public Book() {
}
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 String getInfo() {
return info;
}
public void setInfo(String info) {
this.info = info;
}
@Override
public String toString() {
return "Book{" +
"id=" + id +
", name='" + name + '\'' +
", info='" + info + '\'' +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Book book = (Book) o;
return id == book.id &&
Objects.equals(name, book.name) &&
Objects.equals(info, book.info);
}
@Override
public int hashCode() {
return Objects.hash(id, name, info);
}
}
Demo.java
package com.kaikeba;
import com.kaikeba.demo.Book;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
public class Demo {
public static void main(String[] args) throws IntrospectionException {
Class c = Book.class;
BeanInfo bi = Introspector.getBeanInfo(c);
PropertyDescriptor[] pds = bi.getPropertyDescriptors();
for(PropertyDescriptor pd : pds) {
Method get = pd.getReadMethod();
Method set = pd.getWriteMethod();
System.out.println("屬性名稱:" + pd.getName());
System.out.println("屬性型別:" + pd.getPropertyType());
System.out.println("屬性方法:");
System.out.println(get);
System.out.println(set);
System.out.println();
}
}
}
課堂上老師演示的程式碼說,Boolean型別獲取到的方法分別是:is方法名,set方法名,分別對應於get方法名,set方法名。但是我測試時並沒有顯示這樣的差別;
通過內省機制,可以更快速的操作Bean物件獲得Getter和Setter方法(相比於用基礎的反射操作)
相關文章
- Java列舉類、註解和反射Java反射
- Java列舉類與註解詳解——一篇文章讀懂列舉類與註解詳Java
- Day69.註解&列舉類的複習 -Java註解&列舉類Java
- 8.集合、泛型、列舉、註解、反射泛型反射
- 03-Java核心類庫_多執行緒Java執行緒
- 註解與反射反射
- Java基礎(十)——列舉與註解Java
- 基於註解的 PHP 列舉類實現PHP
- Java反射與註解Java反射
- Python元類與列舉類Python
- Java之註解與反射Java反射
- Java註解與反射機制Java反射
- Java註解與反射的使用Java反射
- Java enum列舉類詳解 列舉的常見用法Java
- 註解 & 反射反射
- 列舉類
- Java反射-註解Java反射
- 註解和反射反射
- 列舉工具類
- java列舉類Java
- 【進階】Spring中的註解與反射Spring反射
- Java註解與反射學習筆記Java反射筆記
- Java —— 列舉類(enum 類)Java
- Java核心技術梳理-類載入機制與反射Java反射
- Java註解和反射Java反射
- Java 註解和反射Java反射
- Java(4)列舉類Java
- 【python】Enum 列舉類Python
- 註解與抽取基類
- JAVA-註解(2)-自定義註解及反射註解Java反射
- iOS開發 列舉註釋iOS
- java_06列舉類Java
- 小白都能學會的Java註解與反射機制Java反射
- Java ”框架 = 註解 + 反射 + 設計模式“ 之 註解詳解Java框架反射設計模式
- springboot mybatis列舉配置(每次只需新增一個列舉類即可)Spring BootMyBatis
- Java反射和註解基本用法Java反射
- 註解和反射Day02反射
- Python 列舉類原始碼解析Python原始碼