用類加載器的5中形式讀取.properties文件(這個.properties文件一般放在src的下面)
用類加載器進行讀取:這里采取先向大家講讀取類加載器的幾種方法;然后寫一個例子把幾種方法融進去,讓大家直觀感受。最后分析原理。(主要是結合所牽涉的方法的源代碼的角度進行分析)
這里先介紹用類加載器讀取的幾種方法:
1.任意類名.class.getresourceasstream("/文件所在的位置");【文件所在的位置從包名開始寫】
2.和.properties文件在同一個目錄下的類名.class.getresourceasstream("文件所在的位置");【文件所在的位置從包名開始寫,注意這里和上面的相比較少了一個斜杠/】
當然你也可以寫成跟1一樣的形式即:任意類名.class.getresourceasstream("/文件所在的位置");
3.任意類名.class.getclassloader().getresourceasstream("文件所在的位置");【文件所在的位置從包名開始寫】
4.任意類名.class.getclassloader().getresource("文件所在的位置").openstream();【文件所在的位置從包名開始寫】
5.任意類名.class.getclassloader().getresource("文件所在的位置")..openconnection().getinputstream();【文件所在的位置從包名開始寫】
//一個例子,說明上述5中方法的用法。
上面圖片中的各個紅色矩形就是我要讀取的properties文件。主要是兩類。一類直接放在src下面。另一類是放在某個文件夾下面.
//f.properties文件的內容如下圖所示;
//上述五種情況說明的代碼如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
|
package com.qls.commonclass; import java.io.ioexception; import java.io.inputstream; import java.util.properties; import com.qls.counter.ok; /** * 分別用類加載器的5種方法讀取f.properties文件。 * @author 秦林森 * */ public class test6 { public static void main(string[] args) throws ioexception { // todo auto-generated method stub /**第一種情形獲取輸入流。 * 任意類名.class.getresourceasstream("/文件所在的位置");【文件所在的位置從包名開始寫】 * @param args */ //獲取輸入流 inputstream in = test. class .getresourceasstream( "/com/qls/counter/f.properties" ); /** * 第二種情形獲取輸入流。 * 和.properties文件在同一個目錄下的類名.class.getresourceasstream("文件所在的位置"); * 【文件所在的位置從包名開始寫,注意這里和上面的相比較少了一個斜杠/】 * 這里隨便選擇一個與:f.properties在同一個目錄下的類比如ok這個類吧! * 這里你自然也可以寫成跟第一種情況一樣的形式: * 即: * inputstream in2 = ok.class.getresourceasstream("/com/qls/counter/f.properties"); * 因為第一種情況是針對任意一個類而言的公式。 */ inputstream in2 = ok. class .getresourceasstream( "f.properties" ); /** * 第三種情形獲取輸入流: * 任意類名.class.getclassloader().getresourceasstream("文件所在的位置"); * 【文件所在的位置從包名開始寫】 */ inputstream in3 = test2. class .getclassloader().getresourceasstream( "com/qls/counter/f.properties" ); /** * 第四中情形獲取輸入流: * 任意類名.class.getclassloader().getresource("文件所在的位置").openstream(); * 【文件所在的位置從包名開始寫】 */ inputstream in4 = test4. class .getclassloader().getresource( "com/qls/counter/f.properties" ).openstream(); /** * 第五種情形獲取輸入流: * .任意類名.class.getclassloader().getresource("文件所在的位置").openconnection().getinputstream(); * 【文件所在的位置從包名開始寫】 */ inputstream in5 = test5. class .getclassloader().getresource( "com/qls/counter/f.properties" ).openconnection().getinputstream(); //創建properties properties prop= new properties(); //把輸入流in加載到prop中 /* * 驗證上述5中輸入流是否成立。只需帶入prop.load(inputstream inputstream);驗證即可。 * 也就是: * prop.load(in); * prop.load(in2); * prop.load(in3); * prop.load(in4); * prop.load(in5); */ prop.load(in5); system.out.println("sixi="+prop.getproperty("sixi")); system.out.println("ouyangfeng="+prop.getproperty("ouyangfeng")); system.out.println("rape="+prop.getproperty("farm")); } }/* output: sixi=river ouyangfeng=masses farm=flower **/ //:~ |
上述5中方法的原理分析。
首先看看class中的resolvename(string name)究竟是干什么的。源碼如下所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
private string resolvename(string name) { if (name == null ) { return name; } if (!name.startswith( "/" )) { class <?> c = this ; while (c.isarray()) { c = c.getcomponenttype(); } string basename = c.getname(); int index = basename.lastindexof( '.' ); if (index != - 1 ) { name = basename.substring( 0 , index).replace( '.' , '/' ) + "/" +name; } } else { name = name.substring( 1 ); } return name; } |
下面我把這個源碼講的內容翻譯人類語言:
給任意一個字符串name,如果該name是以/開始的,則該函數返回的是:去掉/這個字符的字符串。(如name="/ouyangfeng" 則調用該函數之后得到的結果是:name=ouyangfeng)。
如果該name這個字符串不是以/開始的,則該函數返回的結果是調用這個函數類所在的包名+name組成的字符串(例如假設test5所在的包名是:com.qls.mount 。則:test5.class.resolvename("ouyangfeng");返回結果是:com/qls/mount/ouyangfeng)
也就是幫助文檔講述的:
if the name
begins with a '/'
('\u002f'), then the absolute name of the resource is the portion of the name
following the '/'
.
otherwise, the absolute name is of the following form:modified_package_name/name
where the modified_package_name
is the package name of this object with '/'
substituted for '.'
('\u002e').
上述英文我簡要翻譯一下:如果name是以一個/開頭,則這個資源的絕對name就是:name中/之后的部分內容。
否則:這這個絕對name就是如下形式。包名/name,把這里包名中的.用/代替掉。【如:com.qls.river把.用/代替掉就是:com/qls/river】
然后我們再看看class類中的getresourceasstream(string name)的源碼和classloader中的getresourceasstream(string name)中的源碼。
class類中的getresourceasstream(string name)的源碼如下:
1
2
3
4
5
6
7
8
9
|
public inputstream getresourceasstream(string name) { name = resolvename(name); //注意這里有一個resolvename(string name)方法,根據上述的分析,易知道這個源碼的意思. classloader cl = getclassloader0(); if (cl== null ) { // a system class. return classloader.getsystemresourceasstream(name); } return cl.getresourceasstream(name); } |
classloader中的getresourceasstream(string name)中的源碼如下:
1
2
3
4
5
6
7
8
|
public inputstream getresourceasstream(string name) { url url = getresource(name); try { return url != null ? url.openstream() : null ; //這句代碼的意思是:如果url不是null時返回的是:url.openstream(),反之如果url為null則返回null. } catch (ioexception e) { return null ; } } |
通過這class.resolvename(string name)中的源代碼和classloader.getresourceasstream(string name)中的源代碼以及class.getresourceasstream(string name)中的原代碼我們易知道上述五種情況是怎么來的。無需記憶。
只需學會數學推理即可。
大家在看源碼是:發現class.resolvename(string name)這個方法是private的,你用普通方法根本調用不了,下面我順便提一下:如何調用這個方法。以便大家可以更好的理解這個方法所講的意思。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
package com.qls.commonclass; import java.lang.reflect.invocationtargetexception; import java.lang.reflect.method; /** * 用反射調用classloade中的 private string resolvename(string name) * 驗證這個方法所講的意思。 * @author 秦林森 * */ public class test7 { public static void main(string[] args) throws exception{ // todo auto-generated method stub //得到這個方法。 method method = class . class .getdeclaredmethod( "resolvename" , new class []{string. class }); //由于這個方法是private,所以要獲取這個方法的訪問權限 method.setaccessible( true ); //寫一個實例,以便調用這個方法。 object obj = test7. class ; //test這個類所在的包是:com.qls.commonclass //調用這個方法 string invoke = (string) method.invoke(obj, new object[]{ "ouyangfeng" }); string invoke2 = (string) method.invoke(obj, new object[]{ "/ouyangfeng" }); system.out.println( "invoke=" +invoke); system.out.println( "invoke2=" +invoke2); } } /* output: invoke=com/qls/commonclass/ouyangfeng invoke2=ouyangfeng 由此可以證明了:以/開頭的字符串"/ouyangfeng"調用resolvename(string name) 這個方法之后返回的結果是:ouyangfeng 不以/開頭的字符串:"ouyangfeng"返回的結果是:調用這個方法的包名/name 【在本例中是test7調用resolvename(string name),而test7所在的包是:com.qls.commonclass 所以返回結果是:com/qls/commonclass/ouyangfeng】 */ //:~ |
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。
原文鏈接:https://www.cnblogs.com/1540340840qls/p/6184109.html