反射

# 反射

## 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ī)制提供的功能

![img](反射.assets/12693685-301282eafa32f611.png)

#### <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

```

![image-20201214172821158](反射.assets/image-20201214172821158.png)

#### 實(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

? ? }

```

![image-20201214180258702](反射.assets/image-20201214180258702.png)

#### 反射類(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è)組成部分:

>

> ![img](https:////upload-images.jianshu.io/upload_images/12693685-12fdb465f74491df.png?imageMogr2/auto-orient/strip|imageView2/2/w/864/format/webp)

>

> 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)

![img](https:////upload-images.jianshu.io/upload_images/12693685-5b894c289d2824f6.png?imageMogr2/auto-orient/strip|imageView2/2/w/877/format/webp)

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)造方法

![img](反射.assets/12693685-a9e0550bd2fb9e30.png)

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é)果

![img](反射.assets/12693685-1a8ecebe6b58a07a.png)

#### 利用反射調(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");

? ? }

```

![img](反射.assets/12693685-950889882fd29df7.png)

## 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。

![img](反射.assets/16251880-81a3a8fbbc0457c9.png)

**注意:**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定義了以下方法:

![img](反射.assets/2510824-6f56a4a4165d4acc.png)

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

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 今天感恩節(jié)哎,感謝一直在我身邊的親朋好友。感恩相遇!感恩不離不棄。 中午開(kāi)了第一次的黨會(huì),身份的轉(zhuǎn)變要...
    余生動(dòng)聽(tīng)閱讀 10,836評(píng)論 0 11
  • 彩排完,天已黑
    劉凱書(shū)法閱讀 4,471評(píng)論 1 3
  • 表情是什么,我認(rèn)為表情就是表現(xiàn)出來(lái)的情緒。表情可以傳達(dá)很多信息。高興了當(dāng)然就笑了,難過(guò)就哭了。兩者是相互影響密不可...
    Persistenc_6aea閱讀 129,626評(píng)論 2 7

友情鏈接更多精彩內(nèi)容