# 反射
## Java反射機(jī)制定義
Java反射機(jī)制是指在運(yùn)行狀態(tài)中,<font color='red'>對(duì)于任意一個(gè)類(lèi),都能夠知道這個(gè)類(lèi)的所有屬性和方法</font>;<font color='red'>對(duì)于任意一個(gè)對(duì)象,都能夠調(diào)用它的任意一個(gè)方法和屬性</font>;這種動(dòng)態(tài)獲取的信息以及動(dòng)態(tài)調(diào)用對(duì)象的方法的功能稱(chēng)為java語(yǔ)言的反射機(jī)制。
<font color='red'>用一句話總結(jié)就是反射可以實(shí)現(xiàn)在運(yùn)行時(shí)可以知道任意一個(gè)類(lèi)的屬性和方法。</font>
#### 反射的概念
- 靜態(tài)編譯:在編譯時(shí)確定類(lèi)型,綁定對(duì)象,即通過(guò)。
- 動(dòng)態(tài)編譯:運(yùn)行時(shí)確定類(lèi)型,綁定對(duì)象。動(dòng)態(tài)編譯最大限度發(fā)揮了java的靈活性,體現(xiàn)了多態(tài)的應(yīng)用,有以降低類(lèi)之間的<font color='red'>藕合性</font>
耦合性:<font color='red'>指模塊之間的聯(lián)系及相互影響盡可能地少,必須聯(lián)系的應(yīng)加以明確的說(shuō)明</font>,這種聯(lián)系及相互影響稱(chēng)為模塊的藕合性。
耦合是<font color='orange'>影響軟件復(fù)雜程度的一個(gè)重要因素</font>。應(yīng)該采取的原則是:<font color='red'>盡量使用數(shù)據(jù)耦合,少用控制耦合,限制公共環(huán)境耦合的范圍,完全不用內(nèi)容耦合</font>
## 反射機(jī)制的優(yōu)點(diǎn)與缺點(diǎn)
#### 優(yōu)點(diǎn)
靈活性高。因?yàn)?*反射屬于動(dòng)態(tài)編譯**,即只有到運(yùn)行時(shí)才動(dòng)態(tài)創(chuàng)建 &獲取對(duì)象實(shí)例。
編譯方式說(shuō)明:
- 靜態(tài)編譯:在編譯時(shí)確定類(lèi)型 & 綁定對(duì)象。如常見(jiàn)的使用`new`關(guān)鍵字創(chuàng)建對(duì)象
- 動(dòng)態(tài)編譯:運(yùn)行時(shí)確定類(lèi)型 & 綁定對(duì)象。動(dòng)態(tài)編譯體現(xiàn)了`Java`的靈活性、多態(tài)特性 & 降低類(lèi)之間的藕合性
#### 缺點(diǎn)
#### 缺點(diǎn)
<font color='red'>執(zhí)行效率低</font>,因?yàn)榉瓷涞牟僮髦饕ㄟ^(guò)JVM執(zhí)行,所以時(shí)間成本會(huì)高于直接執(zhí)行相同操作
- 因?yàn)榻涌诘耐ㄓ眯裕?lt;font color='orange'>Java的invoke方法是傳object和object[]數(shù)組的</font>?;绢?lèi)型參數(shù)需要<font color='red'>裝箱</font>和<font color='red'>拆箱</font>,產(chǎn)生大量額外的對(duì)象和內(nèi)存開(kāi)銷(xiāo),頻繁促發(fā)GC(<font color='cornflowerblue'>垃圾回收</font>)
- 編譯器<font color='red'>難以</font>對(duì)動(dòng)態(tài)調(diào)用的代碼提前做<font color='red'>優(yōu)化</font>
- 反射需要<font color='red'>按名檢索類(lèi)和方法</font>,有一定的時(shí)間開(kāi)銷(xiāo).
- <font color='red'>容易破壞類(lèi)結(jié)構(gòu) </font>,因?yàn)榉瓷洳僮黟堖^(guò)了源碼,容易干擾類(lèi)原有的內(nèi)部邏輯
#### Java反射機(jī)制提供的功能

#### <font color='red'>java.lang.Class</font> 類(lèi)
> 定義:<font color='red'>java.lang.Class</font>類(lèi)是反射機(jī)制的基礎(chǔ)
>? ? ? ? 作用:存放著對(duì)應(yīng)類(lèi)型對(duì)象的運(yùn)行時(shí)信息
>
> - 在Java程序運(yùn)行時(shí),Java虛擬機(jī)為所有類(lèi)型維護(hù)一個(gè)<font color='red'>java.lang.Class</font>對(duì)象
> - 該Class對(duì)象存放著所有關(guān)于該對(duì)象的 運(yùn)行時(shí)信息
> - 泛型形式為Class<T>
> <font color='red'>每種類(lèi)型的Class對(duì)象只有1個(gè) = 地址只有1個(gè)</font>
```Java
? ? ? ? // 對(duì)于2個(gè)String類(lèi)型對(duì)象,它們的Class對(duì)象相同
? ? ? ? Class c1 = "class".getClass();
? ? ? ? Class c2 = Class.forName("java.lang.String");
? ? ? ? // 用==運(yùn)算符實(shí)現(xiàn)兩個(gè)類(lèi)對(duì)象地址的比較
? ? ? ? System.out.println(c1 == c2);
? ? ? ? // 輸出結(jié)果:true
```

#### 實(shí)現(xiàn)方式
反射機(jī)制的實(shí)現(xiàn) 主要通過(guò) 操作<font color='red'>java.lang.Class</font>類(lèi)
- 獲取 目標(biāo)類(lèi)型的Class對(duì)象
- 通過(guò) Class 對(duì)象分別獲取Constructor類(lèi)對(duì)象、Method類(lèi)對(duì)象 & Field 類(lèi)對(duì)象
- 通過(guò) Constructor類(lèi)對(duì)象、Method類(lèi)對(duì)象 & Field類(lèi)對(duì)象分別獲取類(lèi)的構(gòu)造函數(shù)、方法&屬性的具體信息,并進(jìn)行后續(xù)操作
#### 獲取 目標(biāo)類(lèi)型的`Class`對(duì)象的方式主要有4種方法
```java
? private static void getTargetClass() throws ClassNotFoundException {
? ? ? ? /** 方式1:Object.getClass()
? ? ? ? * Object類(lèi)中的getClass()返回一個(gè)Class類(lèi)型的實(shí)例*/
? ? ? ? Boolean temp = true;
? ? ? ? Class type = temp.getClass();
? ? ? ? System.out.println(type);//結(jié)果:class java.lang.Boolean
? ? ? ? /** 方式2:T.class 語(yǔ)法,T = 任意Java類(lèi)型
? ? ? ? * 注:Class對(duì)象表示的是一個(gè)類(lèi)型,而這個(gè)類(lèi)型未必一定是類(lèi)
? ? ? ? *如,int不是類(lèi),但int.class是一個(gè)Class類(lèi)型的對(duì)象*/
? ? ? ? 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語(yǔ)法
? ? ? ? * Class<?> classType = Boolean . TYPE*/
? ? ? ? Class<?> temp4 = Boolean.TYPE;
? ? ? ? System.out.println(temp4);//結(jié)果:boolean
? ? }
```

#### 反射類(lèi)對(duì)應(yīng)關(guān)系
> Java反射機(jī)制的實(shí)現(xiàn)除了依靠Java.lang.Class類(lèi),還需要依靠:Constructor類(lèi)、Field類(lèi)、Method類(lèi),分別作用于類(lèi)的各個(gè)組成部分:
>
> 
>
> image.png
#### Type類(lèi)
> Type是Java中所有類(lèi)型的通用超級(jí)接口編程語(yǔ)言。這些包括原始類(lèi)型,參數(shù)化類(lèi)型,數(shù)組類(lèi)型、類(lèi)型變量和原始類(lèi)型
>? 以下為繼承Type子類(lèi)

image.png
#### 通過(guò) Class 對(duì)象分別獲取Constructor類(lèi)對(duì)象、Method類(lèi)對(duì)象 & Field 類(lèi)對(duì)
```java
// 即以下方法都屬于`Class` 類(lèi)的方法。
<-- 1. 獲取類(lèi)的構(gòu)造函數(shù)(傳入構(gòu)造函數(shù)的參數(shù)類(lèi)型)->>
? // 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();
// 最終都是獲得一個(gè)Constructor類(lèi)對(duì)象
// 特別注意:
? // 1. 不帶 "Declared"的方法支持取出包括繼承、公有(Public) & 不包括有(Private)的構(gòu)造函數(shù)
? // 2. 帶 "Declared"的方法是支持取出包括公共(Public)、保護(hù)(Protected)、默認(rèn)(包)訪問(wèn)和私有(Private)的構(gòu)造方法,但不包括繼承的構(gòu)造函數(shù)
? // 下面同理
<--? 2. 獲取類(lèi)的屬性(傳入屬性名) -->
? // a. 獲取指定的屬性(公共 / 繼承)
? Field getField(String name) ;
? // b. 獲取所有的屬性(公共 / 繼承)
? Field[] getFields() ;
? // c. 獲取指定的所有屬性 (不包括繼承)
? Field getDeclaredField(String name) ;
? // d. 獲取所有的所有屬性 (不包括繼承)
? Field[] getDeclaredFields() ;
// 最終都是獲得一個(gè)Field類(lèi)對(duì)象
<-- 3. 獲取類(lèi)的方法(傳入方法名 & 參數(shù)類(lèi)型)-->
? // a. 獲取指定的方法(公共 / 繼承)
? ? Method getMethod(String name, Class<?>... parameterTypes) ;
? // b. 獲取所有的方法(公共 / 繼承)
? Method[] getMethods() ;
? // c. 獲取指定的方法 ( 不包括繼承)
? Method getDeclaredMethod(String name, Class<?>... parameterTypes) ;
? // d. 獲取所有的方法( 不包括繼承)
? Method[] getDeclaredMethods() ;
// 最終都是獲得一個(gè)Method類(lèi)對(duì)象
<-- 4. Class類(lèi)的其他常用方法 -->
getSuperclass();
// 返回父類(lèi)
String getName();
// 作用:返回完整的類(lèi)名(含包名,如java.lang.String )
Object newInstance();
// 作用:快速地創(chuàng)建一個(gè)類(lèi)的實(shí)例
// 具體過(guò)程:調(diào)用默認(rèn)構(gòu)造器(若該類(lèi)無(wú)默認(rèn)構(gòu)造器,則拋出異常
// 注:若需要為構(gòu)造器提供參數(shù)需使用java.lang.reflect.Constructor中的newInstance()
```
#### 通過(guò) Constructor類(lèi)對(duì)象、Method類(lèi)對(duì)象 & Field類(lèi)對(duì)象分別獲取類(lèi)的構(gòu)造函數(shù)方法 & 屬性的具體信息 & 進(jìn)行操作
```java
// 即以下方法都分別屬于`Constructor`類(lèi)、`Method`類(lèi) & `Field`類(lèi)的方法。
<-- 1. 通過(guò)Constructor 類(lèi)對(duì)象獲取類(lèi)構(gòu)造函數(shù)信息 -->
? String getName();// 獲取構(gòu)造器名
? Class getDeclaringClass();// 獲取一個(gè)用于描述類(lèi)中定義的構(gòu)造器的Class對(duì)象
? int getModifiers();// 返回整型數(shù)值,用不同的位開(kāi)關(guān)描述訪問(wèn)修飾符的使用狀況
? Class[] getExceptionTypes();// 獲取描述方法拋出的異常類(lèi)型的Class對(duì)象數(shù)組
? Class[] getParameterTypes();// 獲取一個(gè)用于描述參數(shù)類(lèi)型的Class對(duì)象數(shù)組
<-- 2. 通過(guò)Field類(lèi)對(duì)象獲取類(lèi)屬性信息 -->
? String getName();// 返回屬性的名稱(chēng)
? Class getDeclaringClass(); // 獲取屬性類(lèi)型的Class類(lèi)型對(duì)象
? Class getType();// 獲取屬性類(lèi)型的Class類(lèi)型對(duì)象
? int getModifiers(); // 返回整型數(shù)值,用不同的位開(kāi)關(guān)描述訪問(wèn)修飾符的使用狀況
? Object get(Object obj) ;// 返回指定對(duì)象上 此屬性的值
? void set(Object obj, Object value) // 設(shè)置 指定對(duì)象上此屬性的值為value
<-- 3. 通過(guò)Method 類(lèi)對(duì)象獲取類(lèi)方法信息 -->
? String getName();// 獲取方法名
? Class getDeclaringClass();// 獲取方法的Class對(duì)象
? int getModifiers();// 返回整型數(shù)值,用不同的位開(kāi)關(guān)描述訪問(wèn)修飾符的使用狀況
? Class[] getExceptionTypes();// 獲取用于描述方法拋出的異常類(lèi)型的Class對(duì)象數(shù)組
? Class[] getParameterTypes();// 獲取一個(gè)用于描述參數(shù)類(lèi)型的Class對(duì)象數(shù)組
<--額外:java.lang.reflect.Modifier類(lèi) -->
// 作用:獲取訪問(wèn)修飾符
static String toString(int modifiers)?
// 獲取對(duì)應(yīng)modifiers位設(shè)置的修飾符的字符串表示
static boolean isXXX(int modifiers)
// 檢測(cè)方法名中對(duì)應(yīng)的修飾符在modifiers中的值
```
#### 舉個(gè)例子,獲取String 所有構(gòu)造方法

image.png
#### 特別注意:訪問(wèn)權(quán)限問(wèn)題
> 反射機(jī)制的默認(rèn)行為受限于Java的訪問(wèn)控制
>? 如,無(wú)法訪問(wèn)( private )私有的方法、字段
#### 沖突
> Java安全機(jī)制只允許查看任意對(duì)象有哪些域,而不允許讀它們的值
>? 若強(qiáng)制讀取,將拋出異常
#### 解決方案
> 脫離Java程序中安全管理器的控制、屏蔽Java語(yǔ)言的訪問(wèn)檢查,從而脫離訪問(wèn)控制
>? 具體實(shí)現(xiàn)手段:使用Field類(lèi)、Method類(lèi) & Constructor類(lèi)對(duì)象的setAccessible()
```java
void setAccessible(boolean flag)? ?
// 作用:為反射對(duì)象設(shè)置可訪問(wèn)標(biāo)志
// 規(guī)則:flag = true時(shí) ,表示已屏蔽Java語(yǔ)言的訪問(wèn)檢查,使得可以訪問(wèn) & 修改對(duì)象的私有屬性
boolean isAccessible()?
// 返回反射對(duì)象的可訪問(wèn)標(biāo)志的值
static void setAccessible(AccessibleObject[] array, boolean flag)?
// 設(shè)置對(duì)象數(shù)組可訪問(wèn)標(biāo)志
```
#### 實(shí)例1:利用反射獲取類(lèi)的屬性 & 賦值
```java
/**
? ? * 測(cè)試類(lèi)定義
? ? */
? ? public static class Student {
? ? ? ? private String name;
? ? ? ? public Student() {
? ? ? ? ? ? System.out.println("創(chuàng)建了一個(gè)Student實(shí)例");
? ? ? ? }
? ? }
? ? /**
? ? * 測(cè)試方法
? ? */
? ? private static void example() throws Exception {
? ? ? ? //利用反射獲取屬性 & 賦值
? ? ? ? // 1. 獲取Student類(lèi)的Class對(duì)象
? ? ? ? Class<Student> studentClass = Student.class;
? ? ? ? // 2. 通過(guò)Class對(duì)象創(chuàng)建Student類(lèi)的對(duì)象
? ? ? ? Constructor<?> constructor = studentClass.getDeclaredConstructor();
? ? ? ? constructor.setAccessible(true);
? ? ? ? Object mStudent = studentClass.newInstance();
? ? ? ? // 3. 通過(guò)Class對(duì)象獲取Student類(lèi)的name屬性
? ? ? ? Field f = studentClass.getDeclaredField("name");
? ? ? ? // 4. 設(shè)置私有訪問(wèn)權(quán)限
? ? ? ? f.setAccessible(true);
? ? ? ? // 5. 對(duì)新創(chuàng)建的Student對(duì)象設(shè)置name值
? ? ? ? f.set(mStudent, "我是java 反射");
? ? ? ? // 6. 獲取新創(chuàng)建Student對(duì)象的的name屬性 & 輸出
? ? ? ? System.out.println(f.get(mStudent));
? ? }
```
測(cè)試結(jié)果

#### 利用反射調(diào)用類(lèi)的構(gòu)造函數(shù)
```java
/**
? ? * 測(cè)試類(lèi)定義
? ? */
? ? public static class Student2 {
? ? ? ? private String name;
? ? ? ? public Student2() {
? ? ? ? ? ? System.out.println("無(wú)參構(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類(lèi)的Class對(duì)象
? ? ? ? Class studentClass = Student2.class;
? ? ? ? // 2.1 通過(guò)Class對(duì)象獲取Constructor類(lèi)對(duì)象,從而調(diào)用無(wú)參構(gòu)造方法
? ? ? ? // 注:構(gòu)造函數(shù)的調(diào)用實(shí)際上是在newInstance(),而不是在getConstructor()中調(diào)用
? ? ? ? Object mObj1 = studentClass.getConstructor().newInstance();
? ? ? ? // 2.2 通過(guò)Class對(duì)象獲取Constructor類(lèi)對(duì)象(傳入?yún)?shù)類(lèi)型),從而調(diào)用有參構(gòu)造方法
? ? ? ? Object mObj2 = studentClass.getConstructor(String.class).newInstance("OK");
? ? }
```
[圖片上傳失敗...(image-1c62c9-1532574152311)]
#### 利用反射調(diào)用方法
```java
/**
? ? * 測(cè)試類(lèi)定義
? ? */
? ? public static class Student {
? ? ? ? public Student() {
? ? ? ? ? ? System.out.println("創(chuàng)建了一個(gè)Student實(shí)例");
? ? ? ? }
? ? ? ? // 無(wú)參數(shù)方法
? ? ? ? public void setName1() {
? ? ? ? ? ? System.out.println("調(diào)用了無(wú)參方法: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類(lèi)的Class對(duì)象
? ? ? ? Class studentClass = Student.class;
? ? ? ? // 2. 通過(guò)Class對(duì)象創(chuàng)建Student類(lèi)的對(duì)象
? ? ? ? Object mStudent = studentClass.newInstance();
? ? ? ? // 3.1 通過(guò)Class對(duì)象獲取方法setName1()的Method對(duì)象:需傳入方法名
? ? ? ? // 因?yàn)樵摲椒?= 無(wú)參,所以不需要傳入?yún)?shù)
? ? ? ? Method msetName1 = studentClass.getMethod("setName1");
? ? ? ? // 通過(guò)Method對(duì)象調(diào)用setName1():需傳入創(chuàng)建的實(shí)例
? ? ? ? msetName1.invoke(mStudent);
? ? ? ? // 3.2 通過(guò)Class對(duì)象獲取方法setName2()的Method對(duì)象:需傳入方法名 & 參數(shù)類(lèi)型
? ? ? ? Method msetName2 = studentClass.getMethod("setName2", String.class);
? ? ? ? // 通過(guò)Method對(duì)象調(diào)用setName2():需傳入創(chuàng)建的實(shí)例 & 參數(shù)值
? ? ? ? msetName2.invoke(mStudent, "Carson_Ho");
? ? }
```

## API
Kotlin 反射 API 主要來(lái)自于 `kotlin.reflect`、`kotlin.reflect.full` 和 `kotlin.reflect.jvm` 包。其中 `kotlin.reflect`、`kotlin.reflect.full` 是主要的 Kotlin 反射 API,而 `kotlin.reflect.jvm` 包主要用于 Kotlin 反射 和 Java 反射的互操作。
??**`kotlin.reflect`** 包是 Kotlin 反射核心 API,它的類(lèi)圖如下圖 1-1 所示,它們都是接口,詳細(xì)說(shuō)明如下:
- `KCkass`。表示一個(gè)具有反射功能的類(lèi)。
- `KParameter`。表示一個(gè)具有反射功能的 可傳遞給函數(shù)或?qū)傩缘膮?shù)。
- `KCallable`。表示具有反射功能的可調(diào)用實(shí)例,包括屬性和函數(shù),它的直接子接口有 KProperty 和 KFunction。
- `KFunction`。表示一個(gè)具有反射功能的函數(shù)。
- `KProperty`。表示一個(gè)具有反射功能的屬性,它有很多子接口。KProperty0、KProperty1 和 KProperty2 后面的數(shù)字表示接收者作為參數(shù)的個(gè)數(shù)。
- `KMutableProperty`。表示一個(gè)具有反射功能的使用 var 聲明的屬性。KMutableProperty0、KMutableProperty1 和 KMutableProperty2 后面的數(shù)字含義同 KProperty。

**注意:**Kotlin 反射 API 所需要的運(yùn)行時(shí)組件來(lái)自于獨(dú)立的 **`kotlin-reflect.jar`** 文件,在 Android 等移動(dòng)平臺(tái)上為了減少應(yīng)用程序包的大小,應(yīng)用程序包再默認(rèn)情況下不包含 **`kotlin-reflect.jar`** 文件。如果要在應(yīng)用中使用反射功能,則需要額外添加 **`kotlin-reflect.jar`** 文件至應(yīng)用程序包中,并添加 **`kotlin-reflect.jar`** 到項(xiàng)目的類(lèi)路徑。
# Properties
Properties 繼承于 Hashtable。表示一個(gè)持久的屬性集,屬性列表以key-value的形式存在,key和value都是字符串。
Properties 類(lèi)被許多Java類(lèi)使用。例如,在獲取環(huán)境變量時(shí)它就作為System.getProperties()方法的返回值。
我們?cè)诤芏?*需要避免硬編碼的應(yīng)用場(chǎng)景**下需要使用properties文件來(lái)加載程序需要的配置信息,比如JDBC、MyBatis框架等。Properties類(lèi)則是properties文件和程序的中間橋梁,不論是從properties文件讀取信息還是寫(xiě)入信息到properties文件都要經(jīng)由Properties類(lèi)。
### 常見(jiàn)方法
除了從Hashtable中所定義的方法,Properties定義了以下方法:

Properties類(lèi)
下面我們從**寫(xiě)入、讀取、遍歷**等角度來(lái)解析Properties類(lèi)的常見(jiàn)用法:
### 寫(xiě)入
Properties類(lèi)調(diào)用setProperty方法將鍵值對(duì)保存到內(nèi)存中,此時(shí)可以通過(guò)getProperty方法讀取,propertyNames方法進(jìn)行遍歷,但是并沒(méi)有將鍵值對(duì)持久化到屬性文件中,故需要調(diào)用store方法持久化鍵值對(duì)到屬性文件中。
```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");//保存鍵值對(duì)到內(nèi)存
? ? ? ? ? ? properties.store(output, "Steven1997 modify" + new Date().toString());
? ? ? ? ? ? ? ? ? ? ? ? // 保存鍵值對(duì)到文件中
? ? ? ? } catch (IOException io) {
? ? ? ? ? ? io.printStackTrace();
? ? ? ? } finally {
? ? ? ? ? ? if (output != null) {
? ? ? ? ? ? ? ? try {
? ? ? ? ? ? ? ? ? ? output.close();
? ? ? ? ? ? ? ? } catch (IOException e) {
? ? ? ? ? ? ? ? ? ? e.printStackTrace();
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? }
? ? }
}
```
### 讀取
下面給出常見(jiàn)的六種讀取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類(lèi)的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文件路徑書(shū)寫(xiě)有誤,請(qǐng)檢查!");
? ? ? ? ? ? e.printStackTrace();
? ? ? ? } catch (IOException e) {
? ? ? ? ? ? e.printStackTrace();
? ? ? ? }
? ? ? ? return path;
? ? }
? ? /**
? ? * 二、 使用java.util.ResourceBundle類(lèi)的getBundle()方法
? ? * 注意:這個(gè)getBundle()方法的參數(shù)只能寫(xiě)成包路徑+properties文件名,否則將拋異常
? ? *
? ? * @return
? ? */
? ? public static String getPath2() {
? ? ? ? ResourceBundle rb = ResourceBundle
? ? ? ? ? ? ? ? .getBundle("cn/habitdiary/prop");
? ? ? ? path = rb.getString("path");
? ? ? ? return path;
? ? }
? ? /**
? ? * 三、 使用java.util.PropertyResourceBundle類(lèi)的構(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ù)按格式寫(xiě)到包路徑+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ù)必須是包路徑+文件名+.后綴
? ? * 否則會(huì)報(bào)空指針異常
? ? * @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類(lèi)的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());
? ? }
}
```
其中第一、四、五、六種方式都是先獲得文件的輸入流,然后通過(guò)Properties類(lèi)的load(InputStream inStream)方法加載到Properties對(duì)象中,最后通過(guò)Properties對(duì)象來(lái)操作文件內(nèi)容。
第二、三中方式是通過(guò)ResourceBundle類(lèi)來(lái)加載Properties文件,然后ResourceBundle對(duì)象來(lái)操做properties文件內(nèi)容。
**其中最重要的就是每種方式加載文件時(shí),文件的路徑需要按照方法的定義的格式來(lái)加載,否則會(huì)拋出各種異常,比如空指針異常。**
### 遍歷
下面給出四種遍歷Properties中的所有鍵值對(duì)的方法:
```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();
? ? ? ? //返回的屬性鍵值對(duì)實(shí)體
? ? ? ? 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