# 反射
## Java反射機制定義
Java反射機制是指在運行狀態(tài)中,<font color='red'>對于任意一個類鼎俘,都能夠知道這個類的所有屬性和方法</font>;<font color='red'>對于任意一個對象审丘,都能夠調(diào)用它的任意一個方法和屬性</font>窝趣;這種動態(tài)獲取的信息以及動態(tài)調(diào)用對象的方法的功能稱為java語言的反射機制。
<font color='red'>用一句話總結(jié)就是反射可以實現(xiàn)在運行時可以知道任意一個類的屬性和方法训柴。</font>
#### 反射的概念
- 靜態(tài)編譯:在編譯時確定類型哑舒,綁定對象,即通過。
- 動態(tài)編譯:運行時確定類型幻馁,綁定對象洗鸵。動態(tài)編譯最大限度發(fā)揮了java的靈活性,體現(xiàn)了多態(tài)的應(yīng)用仗嗦,有以降低類之間的<font color='red'>藕合性</font>
耦合性:<font color='red'>指模塊之間的聯(lián)系及相互影響盡可能地少膘滨,必須聯(lián)系的應(yīng)加以明確的說明</font>,這種聯(lián)系及相互影響稱為模塊的藕合性稀拐。
耦合是<font color='orange'>影響軟件復(fù)雜程度的一個重要因素</font>火邓。應(yīng)該采取的原則是:<font color='red'>盡量使用數(shù)據(jù)耦合,少用控制耦合德撬,限制公共環(huán)境耦合的范圍铲咨,完全不用內(nèi)容耦合</font>
## 反射機制的優(yōu)點與缺點
#### 優(yōu)點
靈活性高。因為**反射屬于動態(tài)編譯**蜓洪,即只有到運行時才動態(tài)創(chuàng)建 &獲取對象實例纤勒。
編譯方式說明:
- 靜態(tài)編譯:在編譯時確定類型 & 綁定對象。如常見的使用`new`關(guān)鍵字創(chuàng)建對象
- 動態(tài)編譯:運行時確定類型 & 綁定對象隆檀。動態(tài)編譯體現(xiàn)了`Java`的靈活性摇天、多態(tài)特性 & 降低類之間的藕合性
#### 缺點
#### 缺點
<font color='red'>執(zhí)行效率低</font>,因為反射的操作主要通過JVM執(zhí)行恐仑,所以時間成本會高于直接執(zhí)行相同操作
- 因為接口的通用性泉坐,<font color='orange'>Java的invoke方法是傳object和object[]數(shù)組的</font>【账基本類型參數(shù)需要<font color='red'>裝箱</font>和<font color='red'>拆箱</font>坚冀,產(chǎn)生大量額外的對象和內(nèi)存開銷,頻繁促發(fā)GC(<font color='cornflowerblue'>垃圾回收</font>)
- 編譯器<font color='red'>難以</font>對動態(tài)調(diào)用的代碼提前做<font color='red'>優(yōu)化</font>
- 反射需要<font color='red'>按名檢索類和方法</font>鉴逞,有一定的時間開銷.
- <font color='red'>容易破壞類結(jié)構(gòu) </font>,因為反射操作饒過了源碼记某,容易干擾類原有的內(nèi)部邏輯
#### Java反射機制提供的功能
![img](反射.assets/12693685-301282eafa32f611.png)
#### <font color='red'>java.lang.Class</font> 類
> 定義:<font color='red'>java.lang.Class</font>類是反射機制的基礎(chǔ)
>? ? ? ? 作用:存放著對應(yīng)類型對象的運行時信息
>
> - 在Java程序運行時,Java虛擬機為所有類型維護一個<font color='red'>java.lang.Class</font>對象
> - 該Class對象存放著所有關(guān)于該對象的 運行時信息
> - 泛型形式為Class<T>
> <font color='red'>每種類型的Class對象只有1個 = 地址只有1個</font>
```Java
? ? ? ? // 對于2個String類型對象构捡,它們的Class對象相同
? ? ? ? Class c1 = "class".getClass();
? ? ? ? Class c2 = Class.forName("java.lang.String");
? ? ? ? // 用==運算符實現(xiàn)兩個類對象地址的比較
? ? ? ? System.out.println(c1 == c2);
? ? ? ? // 輸出結(jié)果:true
```
![image-20201214172821158](反射.assets/image-20201214172821158.png)
#### 實現(xiàn)方式
反射機制的實現(xiàn) 主要通過 操作<font color='red'>java.lang.Class</font>類
- 獲取 目標類型的Class對象
- 通過 Class 對象分別獲取Constructor類對象液南、Method類對象 & Field 類對象
- 通過 Constructor類對象、Method類對象 & Field類對象分別獲取類的構(gòu)造函數(shù)勾徽、方法&屬性的具體信息滑凉,并進行后續(xù)操作
#### 獲取 目標類型的`Class`對象的方式主要有4種方法
```java
? private static void getTargetClass() throws ClassNotFoundException {
? ? ? ? /** 方式1:Object.getClass()
? ? ? ? * Object類中的getClass()返回一個Class類型的實例*/
? ? ? ? Boolean temp = true;
? ? ? ? Class type = temp.getClass();
? ? ? ? System.out.println(type);//結(jié)果:class java.lang.Boolean
? ? ? ? /** 方式2:T.class 語法,T = 任意Java類型
? ? ? ? * 注:Class對象表示的是一個類型,而這個類型未必一定是類
? ? ? ? *如,int不是類畅姊,但int.class是一個Class類型的對象*/
? ? ? ? Class temp2 = Boolean.class;
? ? ? ? System.out.println(temp2);//結(jié)果:class java.lang.Boolean
? ? ? ? Class temp21 = int.class;
? ? ? ? System.out.println(temp21);//結(jié)果:int
? ? ? ? /** 方式3:static method Class.forName
? ? ? ? Class<?> classType = Class . forName ("java.lang.Boolean"); */
? ? ? ? Class temp3 = Class.forName("java.lang.Boolean");
? ? ? ? System.out.println(temp3);//結(jié)果:class java.lang.Boolean
? ? ? ? /** 方式4:TYPE語法
? ? ? ? * Class<?> classType = Boolean . TYPE*/
? ? ? ? Class<?> temp4 = Boolean.TYPE;
? ? ? ? System.out.println(temp4);//結(jié)果:boolean
? ? }
```
![image-20201214180258702](反射.assets/image-20201214180258702.png)
#### 反射類對應(yīng)關(guān)系
> Java反射機制的實現(xiàn)除了依靠Java.lang.Class類咒钟,還需要依靠:Constructor類、Field類若未、Method類朱嘴,分別作用于類的各個組成部分:
>
> ![img](https:////upload-images.jianshu.io/upload_images/12693685-12fdb465f74491df.png?imageMogr2/auto-orient/strip|imageView2/2/w/864/format/webp)
>
> image.png
#### Type類
> Type是Java中所有類型的通用超級接口編程語言。這些包括原始類型粗合,參數(shù)化類型萍嬉,數(shù)組類型、類型變量和原始類型
>? 以下為繼承Type子類
![img](https:////upload-images.jianshu.io/upload_images/12693685-5b894c289d2824f6.png?imageMogr2/auto-orient/strip|imageView2/2/w/877/format/webp)
image.png
#### 通過 Class 對象分別獲取Constructor類對象隙疚、Method類對象 & Field 類對
```java
// 即以下方法都屬于`Class` 類的方法壤追。
<-- 1. 獲取類的構(gòu)造函數(shù)(傳入構(gòu)造函數(shù)的參數(shù)類型)->>
? // a. 獲取指定的構(gòu)造函數(shù) (公共 / 繼承)
? Constructor<T> getConstructor(Class<?>... parameterTypes)
? // b. 獲取所有的構(gòu)造函數(shù)(公共 / 繼承)
? Constructor<?>[] getConstructors();
? // c. 獲取指定的構(gòu)造函數(shù) ( 不包括繼承)
? Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
? // d. 獲取所有的構(gòu)造函數(shù)( 不包括繼承)
? Constructor<?>[] getDeclaredConstructors();
// 最終都是獲得一個Constructor類對象
// 特別注意:
? // 1. 不帶 "Declared"的方法支持取出包括繼承、公有(Public) & 不包括有(Private)的構(gòu)造函數(shù)
? // 2. 帶 "Declared"的方法是支持取出包括公共(Public)供屉、保護(Protected)行冰、默認(包)訪問和私有(Private)的構(gòu)造方法,但不包括繼承的構(gòu)造函數(shù)
? // 下面同理
<--? 2. 獲取類的屬性(傳入屬性名) -->
? // a. 獲取指定的屬性(公共 / 繼承)
? Field getField(String name) ;
? // b. 獲取所有的屬性(公共 / 繼承)
? Field[] getFields() ;
? // c. 獲取指定的所有屬性 (不包括繼承)
? Field getDeclaredField(String name) 贯卦;
? // d. 獲取所有的所有屬性 (不包括繼承)
? Field[] getDeclaredFields() 资柔;
// 最終都是獲得一個Field類對象
<-- 3. 獲取類的方法(傳入方法名 & 參數(shù)類型)-->
? // a. 獲取指定的方法(公共 / 繼承)
? ? Method getMethod(String name, Class<?>... parameterTypes) ;
? // b. 獲取所有的方法(公共 / 繼承)
? Method[] getMethods() 撵割;
? // c. 獲取指定的方法 ( 不包括繼承)
? Method getDeclaredMethod(String name, Class<?>... parameterTypes) 贿堰;
? // d. 獲取所有的方法( 不包括繼承)
? Method[] getDeclaredMethods() ;
// 最終都是獲得一個Method類對象
<-- 4. Class類的其他常用方法 -->
getSuperclass();
// 返回父類
String getName();
// 作用:返回完整的類名(含包名啡彬,如java.lang.String )
Object newInstance();
// 作用:快速地創(chuàng)建一個類的實例
// 具體過程:調(diào)用默認構(gòu)造器(若該類無默認構(gòu)造器羹与,則拋出異常
// 注:若需要為構(gòu)造器提供參數(shù)需使用java.lang.reflect.Constructor中的newInstance()
```
#### 通過 Constructor類對象、Method類對象 & Field類對象分別獲取類的構(gòu)造函數(shù)方法 & 屬性的具體信息 & 進行操作
```java
// 即以下方法都分別屬于`Constructor`類庶灿、`Method`類 & `Field`類的方法纵搁。
<-- 1. 通過Constructor 類對象獲取類構(gòu)造函數(shù)信息 -->
? String getName();// 獲取構(gòu)造器名
? Class getDeclaringClass()往踢;// 獲取一個用于描述類中定義的構(gòu)造器的Class對象
? int getModifiers()腾誉;// 返回整型數(shù)值,用不同的位開關(guān)描述訪問修飾符的使用狀況
? Class[] getExceptionTypes()峻呕;// 獲取描述方法拋出的異常類型的Class對象數(shù)組
? Class[] getParameterTypes()利职;// 獲取一個用于描述參數(shù)類型的Class對象數(shù)組
<-- 2. 通過Field類對象獲取類屬性信息 -->
? String getName();// 返回屬性的名稱
? Class getDeclaringClass()瘦癌; // 獲取屬性類型的Class類型對象
? Class getType()猪贪;// 獲取屬性類型的Class類型對象
? int getModifiers(); // 返回整型數(shù)值讯私,用不同的位開關(guān)描述訪問修飾符的使用狀況
? Object get(Object obj) 热押;// 返回指定對象上 此屬性的值
? void set(Object obj, Object value) // 設(shè)置 指定對象上此屬性的值為value
<-- 3. 通過Method 類對象獲取類方法信息 -->
? String getName()西傀;// 獲取方法名
? Class getDeclaringClass();// 獲取方法的Class對象
? int getModifiers()桶癣;// 返回整型數(shù)值拥褂,用不同的位開關(guān)描述訪問修飾符的使用狀況
? Class[] getExceptionTypes();// 獲取用于描述方法拋出的異常類型的Class對象數(shù)組
? Class[] getParameterTypes()牙寞;// 獲取一個用于描述參數(shù)類型的Class對象數(shù)組
<--額外:java.lang.reflect.Modifier類 -->
// 作用:獲取訪問修飾符
static String toString(int modifiers)?
// 獲取對應(yīng)modifiers位設(shè)置的修飾符的字符串表示
static boolean isXXX(int modifiers)
// 檢測方法名中對應(yīng)的修飾符在modifiers中的值
```
#### 舉個例子,獲取String 所有構(gòu)造方法
![img](反射.assets/12693685-a9e0550bd2fb9e30.png)
image.png
#### 特別注意:訪問權(quán)限問題
> 反射機制的默認行為受限于Java的訪問控制
>? 如肿仑,無法訪問( private )私有的方法、字段
#### 沖突
> Java安全機制只允許查看任意對象有哪些域碎税,而不允許讀它們的值
>? 若強制讀取,將拋出異常
#### 解決方案
> 脫離Java程序中安全管理器的控制馏锡、屏蔽Java語言的訪問檢查雷蹂,從而脫離訪問控制
>? 具體實現(xiàn)手段:使用Field類、Method類 & Constructor類對象的setAccessible()
```java
void setAccessible(boolean flag)? ?
// 作用:為反射對象設(shè)置可訪問標志
// 規(guī)則:flag = true時 杯道,表示已屏蔽Java語言的訪問檢查匪煌,使得可以訪問 & 修改對象的私有屬性
boolean isAccessible()?
// 返回反射對象的可訪問標志的值
static void setAccessible(AccessibleObject[] array, boolean flag)?
// 設(shè)置對象數(shù)組可訪問標志
```
#### 實例1:利用反射獲取類的屬性 & 賦值
```java
/**
? ? * 測試類定義
? ? */
? ? public static class Student {
? ? ? ? private String name;
? ? ? ? public Student() {
? ? ? ? ? ? System.out.println("創(chuàng)建了一個Student實例");
? ? ? ? }
? ? }
? ? /**
? ? * 測試方法
? ? */
? ? private static void example() throws Exception {
? ? ? ? //利用反射獲取屬性 & 賦值
? ? ? ? // 1. 獲取Student類的Class對象
? ? ? ? Class<Student> studentClass = Student.class;
? ? ? ? // 2. 通過Class對象創(chuàng)建Student類的對象
? ? ? ? Constructor<?> constructor = studentClass.getDeclaredConstructor();
? ? ? ? constructor.setAccessible(true);
? ? ? ? Object mStudent = studentClass.newInstance();
? ? ? ? // 3. 通過Class對象獲取Student類的name屬性
? ? ? ? Field f = studentClass.getDeclaredField("name");
? ? ? ? // 4. 設(shè)置私有訪問權(quán)限
? ? ? ? f.setAccessible(true);
? ? ? ? // 5. 對新創(chuàng)建的Student對象設(shè)置name值
? ? ? ? f.set(mStudent, "我是java 反射");
? ? ? ? // 6. 獲取新創(chuàng)建Student對象的的name屬性 & 輸出
? ? ? ? System.out.println(f.get(mStudent));
? ? }
```
測試結(jié)果
![img](反射.assets/12693685-1a8ecebe6b58a07a.png)
#### 利用反射調(diào)用類的構(gòu)造函數(shù)
```java
/**
? ? * 測試類定義
? ? */
? ? public static class Student2 {
? ? ? ? private String name;
? ? ? ? public Student2() {
? ? ? ? ? ? System.out.println("無參構(gòu)造 ");
? ? ? ? }
? ? ? ? public Student2(String str) {
? ? ? ? ? ? System.out.println("有參構(gòu)造 " + "****" + str);
? ? ? ? }
? ? }
? ? /**
? ? * 利用反射調(diào)用構(gòu)造函數(shù)
? ? */
? ? private static void example2() throws Exception {
? ? ? ? //利用反射調(diào)用構(gòu)造函數(shù)
? ? ? ? // 1. 獲取Student類的Class對象
? ? ? ? Class studentClass = Student2.class;
? ? ? ? // 2.1 通過Class對象獲取Constructor類對象,從而調(diào)用無參構(gòu)造方法
? ? ? ? // 注:構(gòu)造函數(shù)的調(diào)用實際上是在newInstance()党巾,而不是在getConstructor()中調(diào)用
? ? ? ? Object mObj1 = studentClass.getConstructor().newInstance();
? ? ? ? // 2.2 通過Class對象獲取Constructor類對象(傳入?yún)?shù)類型)萎庭,從而調(diào)用有參構(gòu)造方法
? ? ? ? Object mObj2 = studentClass.getConstructor(String.class).newInstance("OK");
? ? }
```
[圖片上傳失敗...(image-1c62c9-1532574152311)]
#### 利用反射調(diào)用方法
```java
/**
? ? * 測試類定義
? ? */
? ? public static class Student {
? ? ? ? public Student() {
? ? ? ? ? ? System.out.println("創(chuàng)建了一個Student實例");
? ? ? ? }
? ? ? ? // 無參數(shù)方法
? ? ? ? public void setName1() {
? ? ? ? ? ? System.out.println("調(diào)用了無參方法:setName1()");
? ? ? ? }
? ? ? ? // 有參數(shù)方法
? ? ? ? public void setName2(String str) {
? ? ? ? ? ? System.out.println("調(diào)用了有參方法setName2(String str):" + str);
? ? ? ? }
? ? }
? ? /**
? ? * 利用反射調(diào)用方法
? ? */
? ? private static void example2() throws Exception {
? ? ? ? //利用反射調(diào)用方法
? ? ? ? // 1. 獲取Student類的Class對象
? ? ? ? Class studentClass = Student.class;
? ? ? ? // 2. 通過Class對象創(chuàng)建Student類的對象
? ? ? ? Object mStudent = studentClass.newInstance();
? ? ? ? // 3.1 通過Class對象獲取方法setName1()的Method對象:需傳入方法名
? ? ? ? // 因為該方法 = 無參,所以不需要傳入?yún)?shù)
? ? ? ? Method msetName1 = studentClass.getMethod("setName1");
? ? ? ? // 通過Method對象調(diào)用setName1():需傳入創(chuàng)建的實例
? ? ? ? msetName1.invoke(mStudent);
? ? ? ? // 3.2 通過Class對象獲取方法setName2()的Method對象:需傳入方法名 & 參數(shù)類型
? ? ? ? Method msetName2 = studentClass.getMethod("setName2", String.class);
? ? ? ? // 通過Method對象調(diào)用setName2():需傳入創(chuàng)建的實例 & 參數(shù)值
? ? ? ? msetName2.invoke(mStudent, "Carson_Ho");
? ? }
```
![img](反射.assets/12693685-950889882fd29df7.png)
## API
Kotlin 反射 API 主要來自于 `kotlin.reflect`齿拂、`kotlin.reflect.full` 和 `kotlin.reflect.jvm` 包驳规。其中 `kotlin.reflect`、`kotlin.reflect.full` 是主要的 Kotlin 反射 API署海,而 `kotlin.reflect.jvm` 包主要用于 Kotlin 反射 和 Java 反射的互操作吗购。
??**`kotlin.reflect`** 包是 Kotlin 反射核心 API,它的類圖如下圖 1-1 所示砸狞,它們都是接口捻勉,詳細說明如下:
- `KCkass`。表示一個具有反射功能的類刀森。
- `KParameter`踱启。表示一個具有反射功能的 可傳遞給函數(shù)或?qū)傩缘膮?shù)。
- `KCallable`研底。表示具有反射功能的可調(diào)用實例埠偿,包括屬性和函數(shù),它的直接子接口有 KProperty 和 KFunction飘哨。
- `KFunction`胚想。表示一個具有反射功能的函數(shù)。
- `KProperty`芽隆。表示一個具有反射功能的屬性浊服,它有很多子接口统屈。KProperty0、KProperty1 和 KProperty2 后面的數(shù)字表示接收者作為參數(shù)的個數(shù)牙躺。
- `KMutableProperty`愁憔。表示一個具有反射功能的使用 var 聲明的屬性。KMutableProperty0孽拷、KMutableProperty1 和 KMutableProperty2 后面的數(shù)字含義同 KProperty吨掌。
![img](反射.assets/16251880-81a3a8fbbc0457c9.png)
**注意:**Kotlin 反射 API 所需要的運行時組件來自于獨立的 **`kotlin-reflect.jar`** 文件,在 Android 等移動平臺上為了減少應(yīng)用程序包的大小脓恕,應(yīng)用程序包再默認情況下不包含 **`kotlin-reflect.jar`** 文件膜宋。如果要在應(yīng)用中使用反射功能,則需要額外添加 **`kotlin-reflect.jar`** 文件至應(yīng)用程序包中炼幔,并添加 **`kotlin-reflect.jar`** 到項目的類路徑秋茫。
# Properties
Properties 繼承于 Hashtable。表示一個持久的屬性集乃秀,屬性列表以key-value的形式存在肛著,key和value都是字符串。
Properties 類被許多Java類使用跺讯。例如枢贿,在獲取環(huán)境變量時它就作為System.getProperties()方法的返回值。
我們在很多**需要避免硬編碼的應(yīng)用場景**下需要使用properties文件來加載程序需要的配置信息刀脏,比如JDBC局荚、MyBatis框架等。Properties類則是properties文件和程序的中間橋梁火本,不論是從properties文件讀取信息還是寫入信息到properties文件都要經(jīng)由Properties類危队。
### 常見方法
除了從Hashtable中所定義的方法,Properties定義了以下方法:
![img](反射.assets/2510824-6f56a4a4165d4acc.png)
Properties類
下面我們從**寫入钙畔、讀取茫陆、遍歷**等角度來解析Properties類的常見用法:
### 寫入
Properties類調(diào)用setProperty方法將鍵值對保存到內(nèi)存中,此時可以通過getProperty方法讀取擎析,propertyNames方法進行遍歷簿盅,但是并沒有將鍵值對持久化到屬性文件中,故需要調(diào)用store方法持久化鍵值對到屬性文件中揍魂。
```java
package cn.habitdiary;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Date;
import java.util.Enumeration;
import java.util.Properties;
import junit.framework.TestCase;
public class PropertiesTester extends TestCase {
? ? public void writeProperties() {
? ? ? ? Properties properties = new Properties();
? ? ? ? OutputStream output = null;
? ? ? ? try {
? ? ? ? ? ? output = new FileOutputStream("config.properties");
? ? ? ? ? ? properties.setProperty("url", "jdbc:mysql://localhost:3306/");
? ? ? ? ? ? properties.setProperty("username", "root");
? ? ? ? ? ? properties.setProperty("password", "root");
? ? ? ? ? ? properties.setProperty("database", "users");//保存鍵值對到內(nèi)存
? ? ? ? ? ? properties.store(output, "Steven1997 modify" + new Date().toString());
? ? ? ? ? ? ? ? ? ? ? ? // 保存鍵值對到文件中
? ? ? ? } catch (IOException io) {
? ? ? ? ? ? io.printStackTrace();
? ? ? ? } finally {
? ? ? ? ? ? if (output != null) {
? ? ? ? ? ? ? ? try {
? ? ? ? ? ? ? ? ? ? output.close();
? ? ? ? ? ? ? ? } catch (IOException e) {
? ? ? ? ? ? ? ? ? ? e.printStackTrace();
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? }
? ? }
}
```
### 讀取
下面給出常見的六種讀取properties文件的方式:
```java
package cn.habitdiary;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Locale;
import java.util.Properties;
import java.util.PropertyResourceBundle;
import java.util.ResourceBundle;
/**
* 讀取properties文件的方式
*
*/
public class LoadPropertiesFileUtil {
? ? private static String basePath = "src/main/java/cn/habitdiary/prop.properties";
? ? private static String path = "";
? ? /**
? ? * 一桨醋、 使用java.util.Properties類的load(InputStream in)方法加載properties文件
? ? *
? ? * @return
? ? */
? ? public static String getPath1() {
? ? ? ? try {
? ? ? ? ? ? InputStream in = new BufferedInputStream(new FileInputStream(
? ? ? ? ? ? ? ? ? ? new File(basePath)));
? ? ? ? ? ? Properties prop = new Properties();
? ? ? ? ? ? prop.load(in);
? ? ? ? ? ? path = prop.getProperty("path");
? ? ? ? } catch (FileNotFoundException e) {
? ? ? ? ? ? System.out.println("properties文件路徑書寫有誤,請檢查现斋!");
? ? ? ? ? ? e.printStackTrace();
? ? ? ? } catch (IOException e) {
? ? ? ? ? ? e.printStackTrace();
? ? ? ? }
? ? ? ? return path;
? ? }
? ? /**
? ? * 二喜最、 使用java.util.ResourceBundle類的getBundle()方法
? ? * 注意:這個getBundle()方法的參數(shù)只能寫成包路徑+properties文件名,否則將拋異常
? ? *
? ? * @return
? ? */
? ? public static String getPath2() {
? ? ? ? ResourceBundle rb = ResourceBundle
? ? ? ? ? ? ? ? .getBundle("cn/habitdiary/prop");
? ? ? ? path = rb.getString("path");
? ? ? ? return path;
? ? }
? ? /**
? ? * 三庄蹋、 使用java.util.PropertyResourceBundle類的構(gòu)造函數(shù)
? ? *
? ? * @return
? ? */
? ? public static String getPath3() {
? ? ? ? InputStream in;
? ? ? ? try {
? ? ? ? ? ? in = new BufferedInputStream(new FileInputStream(basePath));
? ? ? ? ? ? ResourceBundle rb = new PropertyResourceBundle(in);
? ? ? ? ? ? path = rb.getString("path");
? ? ? ? } catch (FileNotFoundException e) {
? ? ? ? ? ? // TODO Auto-generated catch block
? ? ? ? ? ? e.printStackTrace();
? ? ? ? } catch (IOException e) {
? ? ? ? ? ? e.printStackTrace();
? ? ? ? }
? ? ? ? return path;
? ? }
? ? /**
? ? * 四瞬内、 使用class變量的getResourceAsStream()方法
? ? * 注意:getResourceAsStream()方法的參數(shù)按格式寫到包路徑+properties文件名+.后綴
? ? *
? ? * @return
? ? */
? ? public static String getPath4() {
? ? ? ? InputStream in = LoadPropertiesFileUtil.class
? ? ? ? ? ? ? ? .getResourceAsStream("cn/habitdiary/prop.properties");
? ? ? ? Properties p = new Properties();
? ? ? ? try {
? ? ? ? ? ? p.load(in);
? ? ? ? ? ? path = p.getProperty("path");
? ? ? ? } catch (IOException e) {
? ? ? ? ? ? e.printStackTrace();
? ? ? ? }
? ? ? ? return path;
? ? }
? ? /**
? ? * 五迷雪、
? ? * 使用class.getClassLoader()所得到的java.lang.ClassLoader的
? ? * getResourceAsStream()方法
? ? * getResourceAsStream(name)方法的參數(shù)必須是包路徑+文件名+.后綴
? ? * 否則會報空指針異常
? ? * @return
? ? */
? ? public static String getPath5() {
? ? ? ? InputStream in = LoadPropertiesFileUtil.class.getClassLoader()
? ? ? ? ? ? ? ? .getResourceAsStream("cn/habitdiary/prop.properties");
? ? ? ? Properties p = new Properties();
? ? ? ? try {
? ? ? ? ? ? p.load(in);
? ? ? ? ? ? path = p.getProperty("path");
? ? ? ? } catch (IOException e) {
? ? ? ? ? ? e.printStackTrace();
? ? ? ? }
? ? ? ? return path;
? ? }
? ? /**
? ? * 六、 使用java.lang.ClassLoader類的getSystemResourceAsStream()靜態(tài)方法
? ? * getSystemResourceAsStream()方法的參數(shù)格式也是有固定要求的
? ? *
? ? * @return
? ? */
? ? public static String getPath6() {
? ? ? ? InputStream in = ClassLoader
? ? ? ? ? ? ? ? .getSystemResourceAsStream("cn/habitdiary/prop.properties");
? ? ? ? Properties p = new Properties();
? ? ? ? try {
? ? ? ? ? ? p.load(in);
? ? ? ? ? ? path = p.getProperty("path");
? ? ? ? } catch (IOException e) {
? ? ? ? ? ? // TODO Auto-generated catch block
? ? ? ? ? ? e.printStackTrace();
? ? ? ? }
? ? ? ? return path;
? ? }
? ? public static void main(String[] args) {
? ? ? ? System.out.println(LoadPropertiesFileUtil.getPath1());
? ? ? ? System.out.println(LoadPropertiesFileUtil.getPath2());
? ? ? ? System.out.println(LoadPropertiesFileUtil.getPath3());
? ? ? ? System.out.println(LoadPropertiesFileUtil.getPath4());
? ? ? ? System.out.println(LoadPropertiesFileUtil.getPath5());
? ? ? ? System.out.println(LoadPropertiesFileUtil.getPath6());
? ? }
}
```
其中第一虫蝶、四章咧、五、六種方式都是先獲得文件的輸入流能真,然后通過Properties類的load(InputStream inStream)方法加載到Properties對象中赁严,最后通過Properties對象來操作文件內(nèi)容。
第二粉铐、三中方式是通過ResourceBundle類來加載Properties文件疼约,然后ResourceBundle對象來操做properties文件內(nèi)容。
**其中最重要的就是每種方式加載文件時蝙泼,文件的路徑需要按照方法的定義的格式來加載忆谓,否則會拋出各種異常,比如空指針異常踱承。**
### 遍歷
下面給出四種遍歷Properties中的所有鍵值對的方法:
```java
? ? /**
? ? * 輸出properties的key和value
? ? */
? ? public static void printProp(Properties properties) {
? ? ? ? System.out.println("---------(方式一)------------");
? ? ? ? for (String key : properties.stringPropertyNames()) {
? ? ? ? ? ? System.out.println(key + "=" + properties.getProperty(key));
? ? ? ? }
? ? ? ? System.out.println("---------(方式二)------------");
? ? ? ? Set<Object> keys = properties.keySet();//返回屬性key的集合
? ? ? ? for (Object key : keys) {
? ? ? ? ? ? System.out.println(key.toString() + "=" + properties.get(key));
? ? ? ? }
? ? ? ? System.out.println("---------(方式三)------------");
? ? ? ? Set<Map.Entry<Object, Object>> entrySet = properties.entrySet();
? ? ? ? //返回的屬性鍵值對實體
? ? ? ? for (Map.Entry<Object, Object> entry : entrySet) {
? ? ? ? ? ? System.out.println(entry.getKey() + "=" + entry.getValue());
? ? ? ? }
? ? ? ? System.out.println("---------(方式四)------------");
? ? ? ? Enumeration<?> e = properties.propertyNames();
? ? ? ? while (e.hasMoreElements()) {
? ? ? ? ? ? String key = (String) e.nextElement();
? ? ? ? ? ? String value = properties.getProperty(key);
? ? ? ? ? ? System.out.println(key + "=" + value);
? ? ? ? }
? ? }
```
dom4j
maven