用類加載器的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 package com.qls.commonclass; 2 3 import java.io.IOException; 4 import java.io.InputStream; 5 import java.util.Properties; 6 7 import com.qls.counter.Ok; 8 9 /** 10 * 分別用類加載器的5種方法讀取f.properties文件。 11 * @author 秦林森 12 * 13 */ 14 public class Test6 { 15 16 public static void main(String[] args) throws IOException { 17 // TODO Auto-generated method stub 18 /**第一種情形獲取輸入流。 19 * 任意類名.class.getResourceAsStream("/文件所在的位置");【文件所在的位置從包名開始寫】 20 * @param args 21 */ 22 //獲取輸入流 23 InputStream in = Test.class.getResourceAsStream("/com/qls/counter/f.properties"); 24 /** 25 * 第二種情形獲取輸入流。 26 * 和.properties文件在同一個目錄下的類名.class.getResourceAsStream("文件所在的位置"); 27 * 【文件所在的位置從包名開始寫,注意這里和上面的相比較少了一個斜杠/】 28 * 這里隨便選擇一個與:f.properties在同一個目錄下的類比如Ok這個類吧! 29 * 這里你自然也可以寫成跟第一種情況一樣的形式: 30 * 即: 31 * InputStream in2 = Ok.class.getResourceAsStream("/com/qls/counter/f.properties"); 32 * 因為第一種情況是針對任意一個類而言的公式。 33 */ 34 InputStream in2 = Ok.class.getResourceAsStream("f.properties"); 35 /** 36 * 第三種情形獲取輸入流: 37 * 任意類名.class.getClassLoader().getResourceAsStream("文件所在的位置"); 38 * 【文件所在的位置從包名開始寫】 39 */ 40 InputStream in3 = Test2.class.getClassLoader().getResourceAsStream("com/qls/counter/f.properties"); 41 /** 42 * 第四中情形獲取輸入流: 43 * 任意類名.class.getClassLoader().getResource("文件所在的位置").openStream(); 44 * 【文件所在的位置從包名開始寫】 45 */ 46 InputStream in4 = Test4.class.getClassLoader().getResource("com/qls/counter/f.properties").openStream(); 47 /** 48 * 第五種情形獲取輸入流: 49 * .任意類名.class.getClassLoader().getResource("文件所在的位置").openConnection().getInputStream(); 50 * 【文件所在的位置從包名開始寫】 51 */ 52 InputStream in5 = Test5.class.getClassLoader().getResource("com/qls/counter/f.properties").openConnection().getInputStream(); 53 //創建Properties 54 Properties prop=new Properties(); 55 //把輸入流in加載到prop中 56 /* 57 * 驗證上述5中輸入流是否成立。只需帶入prop.load(InputStream inputStream);驗證即可。 58 * 也就是: 59 * prop.load(in); 60 * prop.load(in2); 61 * prop.load(in3); 62 * prop.load(in4); 63 * prop.load(in5); 64 */ 65 prop.load(in5); 66 System.out.println("sixi="+prop.getProperty("sixi")); 67 System.out.println("ouyangfeng="+prop.getProperty("ouyangfeng")); 68 System.out.println("rape="+prop.getProperty("farm")); 69 } 70 71 }/* 72 Output: 73 sixi=river 74 ouyangfeng=masses 75 farm=flower 76 **///:~
上述5中方法的原理分析。
首先看看Class中的resolveName(String name)究竟是干什么的。源碼如下所示:
1 private String resolveName(String name) { 2 if (name == null) { 3 return name; 4 } 5 if (!name.startsWith("/")) { 6 Class<?> c = this; 7 while (c.isArray()) { 8 c = c.getComponentType(); 9 } 10 String baseName = c.getName(); 11 int index = baseName.lastIndexOf('.'); 12 if (index != -1) { 13 name = baseName.substring(0, index).replace('.', '/') 14 +"/"+name; 15 } 16 } else { 17 name = name.substring(1); 18 } 19 return name; 20 }
下面我把這個源碼講的內容翻譯人類語言:
給任意一個字符串name,如果該name是以/開始的,則該函數返回的是:去掉/這個字符的字符串。(如name="/ouyangfeng" 則調用該函數之后得到的結果是:name=ouyangfeng)。
如果該name這個字符串不是以/開始的,則該函數返回的結果是調用這個函數類所在的包名+name組成的字符串(例如假設Test5所在的包名是:com.qls.mount 。則:Test5.class.resolveName("ouyangfeng");返回結果是:com/qls/mount/ouyangfeng)
也就是幫助文檔講述的:
- If the
namebegins with a'/'('\u002f'), then the absolute name of the resource is the portion of thenamefollowing the'/'. - Otherwise, the absolute name is of the following form:
modified_package_name/nameWhere the
modified_package_nameis 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 public InputStream getResourceAsStream(String name) { 2 name = resolveName(name);//注意這里有一個resolveName(String name)方法,根據上述的分析,易知道這個源碼的意思. 3 ClassLoader cl = getClassLoader0(); 4 if (cl==null) { 5 // A system class. 6 return ClassLoader.getSystemResourceAsStream(name); 7 } 8 return cl.getResourceAsStream(name); 9 }
ClassLoader中的getResourceAsStream(String name)中的源碼如下:
1 public InputStream getResourceAsStream(String name) { 2 URL url = getResource(name); 3 try { 4 return url != null ? url.openStream() : null;//這句代碼的意思是:如果url不是null時返回的是:url.openStream(),反之如果url為null則返回null. 5 } catch (IOException e) { 6 return null; 7 } 8 }
通過這Class.resolveName(String name)中的源代碼和ClassLoader.getResourceAsStream(String name)中的源代碼以及Class.getResourceAsStream(String name)中的原代碼我們易知道上述五種情況是怎么來的。無需記憶。
只需學會數學推理即可。
大家在看源碼是:發現Class.resolveName(String name)這個方法是private的,你用普通方法根本調用不了,下面我順便提一下:如何調用這個方法。以便大家可以更好的理解這個方法所講的意思。
1 package com.qls.commonclass; 2 3 import java.lang.reflect.InvocationTargetException; 4 import java.lang.reflect.Method; 5 6 /** 7 * 用反射調用ClassLoade中的 private String resolveName(String name) 8 * 驗證這個方法所講的意思。 9 * @author 秦林森 10 * 11 */ 12 public class Test7 { 13 14 public static void main(String[] args) throws Exception{ 15 // TODO Auto-generated method stub 16 //得到這個方法。 17 Method method = Class.class.getDeclaredMethod("resolveName", new Class[]{String.class}); 18 //由於這個方法是private,所以要獲取這個方法的訪問權限 19 method.setAccessible(true); 20 //寫一個實例,以便調用這個方法。 21 22 Object obj = Test7.class;//Test這個類所在的包是:com.qls.commonclass 23 //調用這個方法 24 String invoke = (String) method.invoke(obj, new Object[]{"ouyangfeng"}); 25 String invoke2 = (String) method.invoke(obj, new Object[]{"/ouyangfeng"}); 26 System.out.println("invoke="+invoke); 27 System.out.println("invoke2="+invoke2); 28 29 } 30 31 }/* 32 Output: 33 invoke=com/qls/commonclass/ouyangfeng 34 invoke2=ouyangfeng 35 由此可以證明了:以/開頭的字符串"/ouyangfeng"調用resolveName(String name) 36 這個方法之后返回的結果是:ouyangfeng 37 不以/開頭的字符串:"ouyangfeng"返回的結果是:調用這個方法的包名/name 38 【在本例中是Test7調用resolveName(String name),而Test7所在的包是:com.qls.commonclass 39 所以返回結果是:com/qls/commonclass/ouyangfeng】 40 41 *///:~
