day25
Map接口
一、Map的實現類的結構:
|----Map:雙列數據,存儲key-value對的數據 ---類似於高中的函數:y = f(x)
|----HashMap:作為Map的主要實現類;線程不安全的,效率高;存儲null的key和value
|----LinkedHashMap:保證在遍歷map元素時,可以按照添加的順序實現遍歷。
原因:在原有的HashMap底層結構基礎上,添加了一對指針,指向前一個和后一個元素。對於頻繁的遍歷操作,此類執行效率高於HashMap。
|----TreeMap:保證按照添加的key-value對進行排序,實現排序遍歷。此時考慮key的自然排序或定制排序,底層使用紅黑樹
|----Hashtable:作為古老的實現類;線程安全的,效率低;不能存儲null的key和value
|----Properties:常用來處理配置文件。key和value都是String類型
HashMap的底層:數組+鏈表 (jdk7及之前)
數組+鏈表+紅黑樹 (jdk 8)
面試題:
- HashMap的底層實現原理?
- HashMap 和 Hashtable的異同?
- CurrentHashMap 與 Hashtable的異同?(暫時不講)
二、Map結構的理解:
Map中的key:無序的、不可重復的,使用Set存儲所有的key ---> key所在的類要重寫equals()和hashCode() (以HashMap為例)
Map中的value:無序的、可重復的,使用Collection存儲所有的value --->value所在的類要重寫equals()
一個鍵值對:key-value構成了一個Entry對象。
Map中的entry:無序的、不可重復的,使用Set存儲所有的entry
三、HashMap的底層實現原理?以jdk7為例說明:
HashMap map = new HashMap():
在實例化以后,底層創建了長度是16的一維數組Entry[] table。
...可能已經執行過多次put... ---> map.put(key1,value1):
首先,調用key1所在類的hashCode()計算key1哈希值,此哈希值經過某種算法計算以后,得到在Entry數組中的存放位置。
如果此位置上的數據為空,此時的key1-value1添加成功。 ----情況1
如果此位置上的數據不為空,(意味着此位置上存在一個或多個數據(以鏈表形式存在)),比較key1和已經存在的一個或多個數據的哈希值:
如果key1的哈希值與已經存在的數據的哈希值都不相同,此時key1-value1添加成功。----情況2
如果key1的哈希值和已經存在的某一個數據(key2-value2)的哈希值相同,繼續比較:調用key1所在類的equals(key2)方法,比較:
如果equals()返回false:此時key1-value1添加成功。----情況3
如果equals()返回true:使用value1替換value2。
補充:關於情況2和情況3:此時key1-value1和原來的數據以鏈表的方式存儲。
在不斷的添加過程中,會涉及到擴容問題,當超出臨界值(且要存放的位置非空)時,擴容。默認的擴容方式:擴容為原來容量的2倍,並將原有的數據復制過來。
jdk8 相較於jdk7在底層實現方面的不同:
- new HashMap():底層沒有創建一個長度為16的數組
- jdk 8底層的數組是:Node[],而非Entry[]
- 首次調用put()方法時,底層創建長度為16的數組
- jdk7底層結構只有:數組+鏈表。jdk8中底層結構:數組+鏈表+紅黑樹。
4.1 形成鏈表時,七上八下(jdk7:新的元素指向舊的元素。jdk8:舊的元素指向新的元素)
4.2 當數組的某一個索引位置上的元素以鏈表形式存在的數據個數 > 8 且當前數組的長度 > 64時,此時此索引位置上的所數據改為使用紅黑樹存儲。
DEFAULT_INITIAL_CAPACITY : HashMap的默認容量,16
DEFAULT_LOAD_FACTOR:HashMap的默認加載因子:0.75
threshold:擴容的臨界值,=容量 * 填充因子:16 * 0.75 => 12
TREEIFY_THRESHOLD:Bucket中鏈表長度大於該默認值,轉化為紅黑樹:8
MIN_TREEIFY_CAPACITY:桶中的Node被樹化時最小的hash表容量:64
四、LinkedHashMap的底層實現原理(了解)
源碼中:
static class Entry<K,V> extends HashMap.Node<K,V> {
Entry<K,V> before, after;//能夠記錄添加的元素的先后順序
Entry(int hash, K key, V value, Node<K,V> next) {
super(hash, key, value, next);
}
}
五、Map中定義的方法:
添加、刪除、修改操作:
Object put(Object key,Object value):將指定key-value添加到(或修改)當前map對象中
void putAll(Map m):將m中的所有key-value對存放到當前map中
Object remove(Object key):移除指定key的key-value對,並返回value
void clear():清空當前map中的所有數據
元素查詢的操作:
Object get(Object key):獲取指定key對應的value
boolean containsKey(Object key):是否包含指定的key
boolean containsValue(Object value):是否包含指定的value
int size():返回map中key-value對的個數
boolean isEmpty():判斷當前map是否為空
boolean equals(Object obj):判斷當前map和參數對象obj是否相等
元視圖操作的方法:
Set keySet():返回所有key構成的Set集合
Collection values():返回所有value構成的Collection集合
Set entrySet():返回所有key-value對構成的Set集合
①遍歷所有的key集
//遍歷所有的key集:keySet()
Set set = map.keySet();
Iterator iterator = set.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
②遍歷所有的value集
//遍歷所有的value集: values()
Collection values = map.values();
for(Object obj : values){
System.out.println(obj);
}
③遍歷所有的key-value
//遍歷所有的key-value //方式一:entrySet() Set entrySet = map.entrySet(); Iterator iterator1 = entrySet.iterator(); while(iterator1.hasNext()){ Object obj = iterator1.next(); //entrySet集合中的元素都是entry Map.Entry entry = (Map.Entry) obj; System.out.println(entry.getKey() + "------>" + entry.getValue()); } System.out.println();
//方式二:
Set keySet = map.keySet();
Iterator iterator2 = keySet.iterator();
while(iterator2.hasNext()){
Object key = iterator2.next();
Object value = map.get(key);
System.out.println(key + "======" + value);
}
總結:常用方法:
添加:put(Object key,Object value)
刪除:remove(Object key)
修改:put(Object key,Object value)
查詢:get(Object key)
長度:size()
遍歷 :keySet() / values() / entrySet()
TreeSet
向TreeMap中添加key-value,要求key必須是由同一個類創建的對象
因為要按照key進行排序:自然排序 、定制排序
TreeSet排序例子
import org.junit.Test;
import java.util.*;
public class TreeMapTest {
//自然排序 @Test public void test1(){ TreeMap map = new TreeMap(); User u1 = new User("Tom",23); User u2 = new User("Jerry",32); User u3 = new User("Jack",20); User u4 = new User("Rose",18); map.put(u1,98); map.put(u2,89); map.put(u3,76); map.put(u4,100); Set entrySet = map.entrySet(); Iterator iterator1 = entrySet.iterator(); while (iterator1.hasNext()){ Object obj = iterator1.next(); Map.Entry entry = (Map.Entry) obj; System.out.println(entry.getKey() + "---->" + entry.getValue()); } } //定制排序 @Test public void test2(){ TreeMap map = new TreeMap(new Comparator() { @Override public int compare(Object o1, Object o2) { if(o1 instanceof User && o2 instanceof User){ User u1 = (User)o1; User u2 = (User)o2; return Integer.compare(u1.getAge(),u2.getAge()); } throw new RuntimeException("輸入的類型不匹配!"); } }); User u1 = new User("Tom",23); User u2 = new User("Jerry",32); User u3 = new User("Jack",20); User u4 = new User("Rose",18); map.put(u1,98); map.put(u2,89); map.put(u3,76); map.put(u4,100); Set entrySet = map.entrySet(); Iterator iterator1 = entrySet.iterator(); while (iterator1.hasNext()){ Object obj = iterator1.next(); Map.Entry entry = (Map.Entry) obj; System.out.println(entry.getKey() + "---->" + entry.getValue()); } }
}
Collections:操作Collection、Map的工具類
reverse(List):反轉 List 中元素的順序
shuffle(List):對 List 集合元素進行隨機排序
sort(List):根據元素的自然順序對指定 List 集合元素按升序排序
sort(List,Comparator):根據指定的 Comparator 產生的順序對 List 集合元素進行排序
swap(List,int, int):將指定 list 集合中的 i 處元素和 j 處元素進行交換
Object max(Collection):根據元素的自然順序,返回給定集合中的最大元素
Object max(Collection,Comparator):根據 Comparator 指定的順序,返回給定集合中的最大元素
Object min(Collection)
Object min(Collection,Comparator)
int frequency(Collection,Object):返回指定集合中指定元素的出現次數
void copy(List dest,List src):將src中的內容復制到dest中
boolean replaceAll(List list,Object oldVal,Object newVal):使用新值替換 List 對象的所有舊值
其中要注意copy函數的使用:
//報異常:IndexOutOfBoundsException("Source does not fit in dest")
List dest = new ArrayList();
Collections.copy(dest,list);
正確的用法:
List dest = Arrays.asList(new Object[list.size()]); System.out.println(dest.size());//list.size(); Collections.copy(dest,list);
System.out.println(dest);
Collections 類中提供了多個 synchronizedXxx() 方法,該方法可使將指定集合包裝成線程同步的集合,從而可以解決多線程並發訪問集合時的線程安全問題
//返回的list1即為線程安全的List
List list1 = Collections.synchronizedList(list);
Properties
Properties:常用來處理配置文件。key和value都是String類型
例子
import java.io.FileInputStream; import java.io.IOException; import java.util.Properties;
public class PropertiesTest {
//Properties:常用來處理配置文件。key和value都是String類型 public static void main(String[] args) { FileInputStream fis = null; try { Properties pros = new Properties(); fis = new FileInputStream("jdbc.properties"); pros.load(fis);//加載流對應的文件 String name = pros.getProperty("name"); String password = pros.getProperty("password"); System.out.println("name = " + name + ", password = " + password); } catch (IOException e) { e.printStackTrace(); } finally { if(fis != null){ try { fis.close(); } catch (IOException e) { e.printStackTrace(); } } } }
}
day26
泛型的使用
1.jdk 5.0新增的特性
2.在集合中使用泛型:
以ArrayList為例:
@Test public void test1(){ ArrayList<Integer> list = new ArrayList<>();
list.add(78); list.add(87); list.add(99); list.add(65); //list.add("Tom"); Iterator<Integer> iterator = list.iterator(); while (iterator.hasNext()){ System.out.println(iterator.next()); }
}
以HashSet為例
@Test public void test2(){
Map<String, Integer> map = new HashMap<>(); map.put("Tom",87); map.put("Jerry",87); map.put("Jack",67); //map.put(123, "kkk"); //泛型的嵌套 Set<Map.Entry<String, Integer>> entry = map.entrySet(); Iterator<Map.Entry<String, Integer>> iterator = entry.iterator(); while(iterator.hasNext()){ Map.Entry<String, Integer> e = iterator.next(); String key = e.getKey(); Integer value = e.getValue(); System.out.println(key + "----->" + value); }
}
總結:
① 集合接口或集合類在jdk5.0時都修改為帶泛型的結構。
② 在實例化集合類時,可以指明具體的泛型類型
③ 指明完以后,在集合類或接口中凡是定義類或接口時,內部結構(比如:方法、構造器、屬性等)使用到類的泛型的位置,都指定為實例化的泛型類型。
比如:add(E e) --->實例化以后:add(Integer e)
④ 注意點:泛型的類型必須是類,不能是基本數據類型。需要用到基本數據類型的位置,拿包裝類替換
⑤ 如果實例化時,沒有指明泛型的類型。默認類型為java.lang.Object類型。
3.如何自定義泛型結構:泛型類、泛型接口;泛型方法
如果定義了泛型類,實例化沒有指明類的泛型,則認為此泛型類型為Object類型,如果大家定義了類是帶泛型的,建議在實例化時要指明類的泛型。
定義一個繼承了Order的子類,它的類型為
public class SubOrder extends Order<Integer> {
public static <E> List<E> copyFromArrayToList(E[] arr){ ArrayList<E> list = new ArrayList<>(); for (E e: arr){ list.add(e); } return list; }
}
那么,由於子類在繼承帶泛型的父類時,指明了泛型類型。則實例化子類對象時,不再需要指明泛型。
SubOrder sub1 = new SubOrder();
sub1.setOrderT(1122);
如果一個子類在繼承時未聲明類型:
public class SubOrder1<T> extends Order<T> {
}
聲明時:
SubOrder1<String> sub2 = new SubOrder1<>();
sub2.setOrderT("order2......");
泛型不同的引用不能相互賦值。
ArrayList<String> list1 = null;
ArrayList<Integer> list2 = new ArrayList<>();
//泛型不同的引用不能相互賦值。
//list1 = list2;
說明:
1. 泛型類可能有多個參數,此時應將多個參數一起放在尖括號內。比如:< E1,E2,E3>
2. 泛型類的構造器如下 public GenericClass(){}
。而下面是錯誤的public GenericClass<E>(){}
3. 實例化后,操作原來泛型位置的結構必須與指定的泛型類型一致。
4. 泛型不同的引用不能相互賦值。盡管在編譯時 ArrayList
5. 泛型如果不指定,將被擦除,泛型對應的類型均按照 Object 處理,但不等價於Object。
經驗: 泛型要使用一路都用。要不用,一路都不要用。
6. 如果泛型結構是一個接口或抽象類,則不可創建泛型類的對象 。
7. jdk1.7,泛型的簡化 操作
ArrayList<Fruit> flist = new ArrayList<>();
8. 泛型的指定中不能使用基本數據類型,可以使用包裝類替換。
9. 在類接口上聲明的泛型,在本類或本接口中即代表某種類型,可以作為非靜態屬性的類型、非靜態方法的參數類型、非靜態方法的返回值類型。
但在靜態方法中不能使用類的泛型。
10. 異常類不能是泛型的
11. 不能使用
new E[]
。但是可以
E[] elements = (E[])new Object[capacity];
參考:ArrayList 源碼中聲明:
Object[] elementData
而非泛型參數類型數組。
12. 父類有泛型,子類可以選擇保留泛型也可以選擇指定泛型類型:
子類不保留父類的泛型:按需實現
沒有類型 擦除
具體類型
子類保留父類的泛型:泛型子類
全部保留
部分保留
結論:子類必須是“富二代”,子類除了指定或保留父類的泛型,還可以增加自 己的泛型
泛型方法
泛型方法的格式: [訪問權限] <泛型> 返回類型 方法名 ([泛型標識 參數名稱]) 拋出的異常
泛型在繼承方面的體現
雖然類A是類B的父類,但是G<A>和G<B>二者不具備子父類關系,二者是並列關系。
補充:類A是類B的父類,A<G> 是 B<G> 的父類
通配符的使用
通配符:?
類A是類B的父類,G<A>和G<B>是沒有關系的,二者共同的父類是:G<?>
@Test public void test1(){ List<Object> list1 = null; List<String> list2 = null;
List<?> list = null; list = list1; list = list2; System.out.println(list1); System.out.println(list2); List<String> list3 = new ArrayList<>(); list3.add("ASA"); list3.add("DGA"); list3.add("Gesag"); //list.add("AA"); //list.add('?'); list.add(null); //獲取(讀取):允許讀取數據,讀取的數據類型為Object。 Object o = list.get(0); System.out.println(o);
分析:
因為?是兩者的共同父類,所以可以將list1和2傳遞給list
添加(寫入):對於List<?>就不能向其內部添加數據。除了null
讀取:允許讀取數據,讀取的數據類型為Object。
有限制條件的通配符的使用。
? extends A:
G<? extends A> 可以作為G<A>和G<B>的父類,其中B是A的子類
? super A:
G<? super A> 可以作為G<A>和G<B>的父類,其中B是A的父類
@Test public void test4(){
List<? extends Person> list1 = null; List<? super Person> list2 = null; List<Student> list3 = new ArrayList<Student>(); List<Person> list4 = new ArrayList<Person>(); List<Object> list5 = new ArrayList<Object>(); list1 = list3; list1 = list4; //list1 = list5; //list2 = list3; list2 = list4; list2 = list5; //讀取數據: list1 = list3; Person p = list1.get(0); //編譯不通過 //Student s = list1.get(0); list2 = list4; Object obj = list2.get(0); ////編譯不通過 //Person obj = list2.get(0); //寫入數據: //編譯不通過 //list1.add(new Student()); //編譯通過 list2.add(new Person()); list2.add(new Student());
}
File類的使用
- File類的一個對象,代表一個文件或一個文件目錄(俗稱:文件夾)
- File類聲明在java.io包下
- File類中涉及到關於文件或文件目錄的創建、刪除、重命名、修改時間、文件大小等方法,並未涉及到寫入或讀取文件內容的操作。如果需要讀取或寫入文件內容,必須使用IO流來完成。
- 后續File類的對象常會作為參數傳遞到流的構造器中,指明讀取或寫入的"終點".
如何創建File類的實例
File(String filePath)
File(String parentPath,String childPath)
File(File parentFile,String childPath)
相對路徑:相較於某個路徑下,指明的路徑。
絕對路徑:包含盤符在內的文件或文件目錄的路徑
路徑分隔符
windows:\\
unix:/
*/
public String getAbsolutePath():獲取絕對路徑
public String getPath() :獲取路徑
public String getName() :獲取名稱
public String getParent():獲取上層文件目錄路徑。若無,返回null
public long length() :獲取文件長度(即:字節數)。不能獲取目錄的長度。
public long lastModified() :獲取最后一次的修改時間,毫秒值
如下的兩個方法適用於文件目錄:
public String[] list() :獲取指定目錄下的所有文件或者文件目錄的名稱數組
public File[] listFiles() :獲取指定目錄下的所有文件或者文件目錄的File數組
public boolean renameTo(File dest):把文件重命名為指定的文件路徑
比如:file1.renameTo(file2)為例:
要想保證返回true,需要file1在硬盤中是存在的,且file2不能在硬盤中存在。
public boolean isDirectory():判斷是否是文件目錄
public boolean isFile() :判斷是否是文件
public boolean exists() :判斷是否存在
public boolean canRead() :判斷是否可讀
public boolean canWrite() :判斷是否可寫
public boolean isHidden() :判斷是否隱藏
創建硬盤中對應的文件或文件目錄
public boolean createNewFile() :創建文件。若文件存在,則不創建,返回false
public boolean mkdir() :創建文件目錄。如果此文件目錄存在,就不創建了。如果此文件目錄的上層目錄不存在,也不創建。
public boolean mkdirs() :創建文件目錄。如果此文件目錄存在,就不創建了。如果上層文件目錄不存在,一並創建
刪除磁盤中的文件或文件目錄
public boolean delete():刪除文件或者文件夾
刪除注意事項:Java中的刪除不走回收站。
要想刪除成功,io4文件目錄下不能有子目錄或文件
day27
流
一、流的分類:
1.操作數據單位:字節流、字符流
2.數據的流向:輸入流、輸出流
3.流的角色:節點流、處理流
抽象基類 | 字節流 | 字符流 |
---|---|---|
輸入流 | InputStream | Reader |
輸出流 | OutputStream | Writer |
二、流的體系結構
抽象基類 | 節點流(或文件流) | 緩沖流(處理流的一種) |
---|---|---|
InputStream | FileInputStream(read(byte[] buffer)) | BufferedInputStream (read(byte[] buffer)) |
OutputStream | FileOutputStream(write(byte[] buffer,0,len) | BufferedOutputStream (write(byte[] buffer,0,len) / flush() |
Reader | FileReader (read(char[] cbuf)) | BufferedReader (read(char[] cbuf) / readLine()) |
Writer | FileWriter (write(char[] cbuf,0,len) | BufferedWriter (write(char[] cbuf,0,len) / flush() |
說明點:
1. read()的理解:返回讀入的一個字符。如果達到文件末尾,返回-1
2. 異常的處理:為了保證流資源一定可以執行關閉操作。需要使用try-catch-finally處理
3. 讀入的文件一定要存在,否則就會報FileNotFoundException。
學會對read()操作升級:使用read的重載方法
示例代碼:
@Test public void testFileReader1(){ FileReader fr = null; try{ //1.File類的實例化 File file = new File("hello.txt"); //2.FileReader流的實例化 fr = new FileReader(file);
//3. 讀入的操作 //read(char[] cbuf):返回每次讀入cbuf數組中的字符的個數。如果大奧文件末尾,返回-1 char[] cbuf = new char[5]; int len; while((len = fr.read(cbuf)) != -1){ String str = new String(cbuf, 0 ,len); System.out.println(str); } }catch (IOException e){ e.printStackTrace(); }finally { if(fr != null){ try { fr.close(); } catch (IOException e) { e.printStackTrace(); } } }
}
讀取文件的推薦方式:
while((len = fr.read(cbuf)) != -1){
String str = new String(cbuf, 0 ,len);
System.out.println(str);
}
從內存中寫出數據到硬盤的文件里。
說明:
1. 輸出操作,對應的File可以不存在的。並不會報異常
2. File對應的硬盤中的文件如果不存在,在輸出的過程中,會自動創建此文件。
File對應的硬盤中的文件如果存在:
如果流使用的構造器是:FileWriter(file,false) / FileWriter(file):對原有文件的覆蓋
如果流使用的構造器是:FileWriter(file,true):不會對原有文件覆蓋,而是在原有文件基礎上追加內容
IO流基本操作:
1.File類的實例化
2.FileReader流的實例化
3.讀入的操作
4.資源的關閉
read():返回讀入的一個字符。如果達到文件末尾,返回-1
不能使用字符流來處理圖片等字節數據
測試FileInputStream和FileOutputStream的使用
- 對於文本文件(.txt,.java,.c,.cpp),使用字符流處理
- 對於非文本文件(.jpg,.mp3,.mp4,.avi,.doc,.ppt,...),使用字節流處理
使用字節流FileInputStream處理文本文件,可能出現亂碼。
圖片復制操作示例代碼
/* 實現對圖片的復制操作 */ @Test public void testFileInputOutputStream() { FileInputStream fis = null; FileOutputStream fos = null; try { // File srcFile = new File("愛情與友情.jpg"); File destFile = new File("愛情與友情2.jpg");
// fis = new FileInputStream(srcFile); fos = new FileOutputStream(destFile); //復制的過程 byte[] buffer = new byte[5]; int len; while((len = fis.read(buffer)) != -1){ fos.write(buffer,0,len); } } catch (IOException e) { e.printStackTrace(); } finally { if(fos != null){ // try { fos.close(); } catch (IOException e) { e.printStackTrace(); } } if(fis != null){ try { fis.close(); } catch (IOException e) { e.printStackTrace(); } } }
}
處理流之一:緩沖流的使用
1.緩沖流:
- BufferedInputStream
- BufferedOutputStream
- BufferedReader
- BufferedWriter
2.作用:提供流的讀取、寫入的速度
提高讀寫速度的原因:內部提供了一個緩沖區
3.處理流,就是“套接”在已有的流的基礎上。
說明:
① 關閉外層流的同時,內層流也會自動的進行關閉。關於內層流的關閉,我們可以省略.
② 使用String讀取數據:
//方式二:使用String
String data;
while((data = br.readLine()) != null){
//方法一:
//bw.write(data + "\n");//data中不包含換行符
//方法二:
bw.write(data);//data中不包含換行符
bw.newLine();//提供換行的操作
}
復制文件的示例代碼:
//實現文件復制的方法 public void copyFileWithBuffered(String srcPath,String destPath){ BufferedInputStream bis = null; BufferedOutputStream bos = null;
try { //1.造文件 File srcFile = new File(srcPath); File destFile = new File(destPath); //2.造流 //2.1 造節點流 FileInputStream fis = new FileInputStream((srcFile)); FileOutputStream fos = new FileOutputStream(destFile); //2.2 造緩沖流 bis = new BufferedInputStream(fis); bos = new BufferedOutputStream(fos); //3.復制的細節:讀取、寫入 byte[] buffer = new byte[1024]; int len; while((len = bis.read(buffer)) != -1){ bos.write(buffer,0,len); } } catch (IOException e) { e.printStackTrace(); } finally { //4.資源關閉 //要求:先關閉外層的流,再關閉內層的流 if(bos != null){ try { bos.close(); } catch (IOException e) { e.printStackTrace(); } } if(bis != null){ try { bis.close(); } catch (IOException e) { e.printStackTrace(); } } //說明:關閉外層流的同時,內層流也會自動的進行關閉。關於內層流的關閉,我們可以省略. //fos.close(); //fis.close(); }
}
處理流之二:轉換流的使用
1.轉換流:屬於字符流
- InputStreamReader:將一個字節的輸入流轉換為字符的輸入流
- OutputStreamWriter:將一個字符的輸出流轉換為字節的輸出流
2.作用:提供字節流與字符流之間的轉換
//參數2指明了字符集,具體使用哪個字符集,取決於文件dbcp.txt保存時使用的字符集
InputStreamReader isr = new InputStreamReader(fis,"UTF-8");
3.解碼:字節、字節數組 --->字符數組、字符串
編碼:字符數組、字符串 ---> 字節、字節數組
4.字符集
- ASCII:美國標准信息交換碼。用一個字節的7位可以表示。
- ISO8859-1:拉丁碼表。歐洲碼表。用一個字節的8位表示。
- GB2312:中國的中文編碼表。最多兩個字節編碼所有字符
- GBK:中國的中文編碼表升級,融合了更多的中文文字符號。最多兩個字節編碼
- Unicode:國際標准碼,融合了目前人類使用的所有字符。為每個字符分配唯一的字符碼。所有的文字都用兩個字節來表示。
- UTF-8:變長的編碼方式,可用1-4個字節來表示一個字符。
示例代碼:
/* 此時處理異常的話,仍然應該使用try-catch-finally
綜合使用InputStreamReader和OutputStreamWriter
*/
@Test
public void test2() throws Exception {
//1.造文件、造流
File file1 = new File("dbcp.txt");
File file2 = new File("dbcp_gbk.txt");FileInputStream fis = new FileInputStream(file1); FileOutputStream fos = new FileOutputStream(file2); InputStreamReader isr = new InputStreamReader(fis,"utf-8"); OutputStreamWriter osw = new OutputStreamWriter(fos,"gbk"); //2.讀寫過程 char[] cbuf = new char[20]; int len; while((len = isr.read(cbuf)) != -1){ osw.write(cbuf,0,len); } //3.關閉資源 isr.close(); osw.close();
}
day28
Path
- jdk 7.0 時,引入了 Path、Paths、Files三個類。
- 此三個類聲明在:java.nio.file包下。
- Path可以看做是java.io.File類的升級版本。也可以表示文件或文件目錄,與平台無關
-
如何實例化Path:使用Paths.
static Path get(String first, String … more)
: 用於將多個字符串串連成路徑
static Path get(URI uri)
: 返回指定uri對應的Path路徑
如何使用Paths實例化Path
示例代碼:
@Test public void test1() { Path path1 = Paths.get("d:\\nio\\hello.txt");//new File(String filepath)
Path path2 = Paths.get("d:\\", "nio\\hello.txt");//new File(String parent,String filename); System.out.println(path1); System.out.println(path2); Path path3 = Paths.get("d:\\", "nio"); System.out.println(path3);
}
Path中的常用方法
String toString()
: 返回調用 Path 對象的字符串表示形式
boolean startsWith(String path)
: 判斷是否以 path 路徑開始
boolean endsWith(String path)
: 判斷是否以 path 路徑結束
boolean isAbsolute()
: 判斷是否是絕對路徑
Path getParent()
:返回Path對象包含整個路徑,不包含 Path 對象指定的文件路徑
Path getRoot()
:返回調用 Path 對象的根路徑
Path getFileName()
: 返回與調用 Path 對象關聯的文件名
int getNameCount()
: 返回Path 根目錄后面元素的數量
Path getName(int idx)
: 返回指定索引位置 idx 的路徑名稱
Path toAbsolutePath()
: 作為絕對路徑返回調用 Path 對象
Path resolve(Path p)
:合並兩個路徑,返回合並后的路徑對應的Path對象
File toFile()
: 將Path轉化為File類的對象
File file = path1.toFile();//Path--->File的轉換
Path newPath = file.toPath();//File--->Path的轉換
Files工具類的使用:操作文件或目錄的工具類
以這兩條代碼為例,導入文件路徑:
Path path1 = Paths.get("d:\\nio", "hello.txt");
Path path2 = Paths.get("atguigu.txt");
Path copy(Path src, Path dest, CopyOption … how)
: 文件的復制
說明:要想復制成功,要求path1對應的物理上的文件存在。path1對應的文件沒有要求。
Files.copy(path1, path2, StandardCopyOption.REPLACE_EXISTING);
Path createDirectory(Path path, FileAttribute<?> … attr)
: 創建一個目錄
說明:要想執行成功,要求path對應的物理上的文件目錄不存在。一旦存在,拋出異常。
Path path3 = Paths.get("d:\\nio\\nio1");
Files.createDirectory(path3);
Path createFile(Path path, FileAttribute<?> … arr)
: 創建一個文件
說明:要想執行成功,要求path對應的物理上的文件不存在。一旦存在,拋出異常。
Path path4 = Paths.get("d:\\nio\\hi.txt");
Files.createFile(path4);
void delete(Path path)
: 刪除一個文件/目錄,如果不存在,執行報錯
Files.delete(path4);
void deleteIfExists(Path path)
: Path對應的文件/目錄如果存在,執行刪除.如果不存在,正常執行結束
Files.deleteIfExists(path3);
Path move(Path src, Path dest, CopyOption…how)
: 將 src 移動到 dest 位置
說明:要想執行成功,src對應的物理上的文件需要存在,dest對應的文件沒有要求。
Files.move(path1, path2, StandardCopyOption.ATOMIC_MOVE);
long size(Path path)
: 返回 path 指定文件的大小
long size = Files.size(path2);
System.out.println(size);
boolean exists(Path path, LinkOption … opts)
: 判斷文件是否存在
System.out.println(Files.exists(path2, LinkOption.NOFOLLOW_LINKS));
boolean isDirectory(Path path, LinkOption … opts)
: 判斷是否是目錄
說明:不要求此path對應的物理文件存在。
System.out.println(Files.isDirectory(path1, LinkOption.NOFOLLOW_LINKS));
boolean isRegularFile(Path path, LinkOption … opts)
: 判斷是否是文件
boolean isHidden(Path path)
: 判斷是否是隱藏文件
說明:要求此path對應的物理上的文件需要存在。才可判斷是否隱藏。否則,拋異常。
System.out.println(Files.isHidden(path1));
boolean isReadable(Path path)
: 判斷文件是否可讀
System.out.println(Files.isReadable(path1));
boolean isWritable(Path path)
: 判斷文件是否可寫
System.out.println(Files.isWritable(path1));
boolean notExists(Path path, LinkOption … opts)
: 判斷文件是否不存在
System.out.println(Files.notExists(path1, LinkOption.NOFOLLOW_LINKS));
- StandardOpenOption.READ:表示對應的Channel是可讀的。
- StandardOpenOption.WRITE:表示對應的Channel是可寫的。
- StandardOpenOption.CREATE:如果要寫出的文件不存在,則創建。如果存在,忽略
- StandardOpenOption.CREATE_NEW:如果要寫出的文件不存在,則創建。如果存在,拋異常
InputStream newInputStream(Path path, OpenOption…how)
:獲取 InputStream 對象
InputStream inputStream = Files.newInputStream(path1, StandardOpenOption.READ);
OutputStream newOutputStream(Path path, OpenOption…how)
: 獲取 OutputStream 對象
OutputStream outputStream = Files.newOutputStream(path1, StandardOpenOption.WRITE,StandardOpenOption.CREATE);
SeekableByteChannel newByteChannel(Path path, OpenOption…how)
: 獲取與指定文件的連接,how 指定打開方式。
SeekableByteChannel channel = Files.newByteChannel(path1, StandardOpenOption.READ,StandardOpenOption.WRITE,StandardOpenOption.CREATE);
DirectoryStream<Path> newDirectoryStream(Path path)
: 打開 path 指定的目錄
Path path2 = Paths.get("e:\\teach");
DirectoryStream<Path> directoryStream = Files.newDirectoryStream(path2);
Iterator<Path> iterator = directoryStream.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
對象流的使用
1.ObjectInputStream 和 ObjectOutputStream
2.作用:用於存儲和讀取基本數據類型數據或對象的處理流。它的強大之處就是可以把Java中的對象寫入到數據源中,也能把對象從數據源中還原回來。
3.要想一個java對象是可序列化的,需要滿足相應的要求。見Person.java
Person.java
import java.io.Serializable;
/**
Person需要滿足如下的要求,方可序列化
1.需要實現接口:Serializable
2.當前類提供一個全局常量:serialVersionUID
3.除了當前Person類需要實現Serializable接口之外,還必須保證其內部所有屬性
也必須是可序列化的。(默認情況下,基本數據類型可序列化)
補充:ObjectOutputStream和ObjectInputStream不能序列化static和transient修飾的成員變量
@author shkstart
@create 2019 上午 10:38
*/
public class Person implements Serializable{public static final long serialVersionUID = 475463534532L;
private String name;
private int age;
private int id;
private Account acct;public Person(String name, int age, int id) {
this.name = name;
this.age = age;
this.id = id;
}public Person(String name, int age, int id, Account acct) {
this.name = name;
this.age = age;
this.id = id;
this.acct = acct;
}@Override
public String toString() {
return "Person{" +
"name='" + name + ''' +
", age=" + age +
", id=" + id +
", acct=" + acct +
'}';
}public int getId() {
return id;
}public void setId(int id) {
this.id = id;
}public String getName() {
return name;
}public void setName(String name) {
this.name = name;
}public int getAge() {
return age;
}public void setAge(int age) {
this.age = age;
}public Person(String name, int age) {
this.name = name; this.age = age;
}
public Person() {
}
}class Account implements Serializable{
public static final long serialVersionUID = 4754534532L;
private double balance;@Override public String toString() { return "Account{" + "balance=" + balance + '}'; } public double getBalance() { return balance; } public void setBalance(double balance) { this.balance = balance; } public Account(double balance) { this.balance = balance; }
}
4.序列化機制:
對象序列化機制允許把內存中的Java對象轉換成平台無關的二進制流,從而允許把這種二進制流持久地保存在磁盤上,或通過網絡將這種二進制流傳輸到另一個網絡節點。當其它程序獲取了這種二進制流,就可以恢復成原來的Java對象。
RandomAccessFile的使用
- RandomAccessFile直接繼承於java.lang.Object類,實現了DataInput和DataOutput接口
- RandomAccessFile既可以作為一個輸入流,又可以作為一個輸出流
- 如果RandomAccessFile作為輸出流時,寫出到的文件如果不存在,則在執行過程中自動創建。如果寫出到的文件存在,則會對原有文件內容進行覆蓋。(默認情況下,從頭覆蓋)
- 可以通過相關的操作,實現RandomAccessFile“插入”數據的效果
示例代碼:
/* 使用RandomAccessFile實現數據的插入效果 */ @Test public void test3() throws IOException {
RandomAccessFile raf1 = new RandomAccessFile("hello.txt","rw"); raf1.seek(3);//將指針調到角標為3的位置 //保存指針3后面的所有數據到StringBuilder中 StringBuilder builder = new StringBuilder((int) new File("hello.txt").length()); byte[] buffer = new byte[20]; int len; while((len = raf1.read(buffer)) != -1){ builder.append(new String(buffer,0,len)) ; } //調回指針,寫入“xyz” raf1.seek(3); raf1.write("xyz".getBytes()); //將StringBuilder中的數據寫入到文件中 raf1.write(builder.toString().getBytes()); raf1.close();
}
網絡編程
一、網絡編程中有兩個主要的問題:
- 1.如何准確地定位網絡上一台或多台主機;定位主機上的特定的應用
- 2.找到主機后如何可靠高效地進行數據傳輸
二、網絡編程中的兩個要素:
- 1.對應問題一:IP和端口號
- 2.對應問題二:提供網絡通信協議:TCP/IP參考模型(應用層、傳輸層、網絡層、物理+數據鏈路層)
三、通信要素一:IP和端口號
1.IP:唯一的標識 Internet 上的計算機(通信實體)
2.在Java中使用InetAddress類代表IP
3.IP分類:IPv4 和 IPv6 ; 萬維網 和 局域網
4.域名: www.baidu.com www.mi.com www.sina.com www.jd.com www.vip.com
5.本地回路地址:127.0.0.1 對應着:localhost
6.如何實例化InetAddress:兩個方法:getByName(String host) 、 getLocalHost()
兩個常用方法:getHostName() / getHostAddress()
7.端口號:正在計算機上運行的進程。
- 要求:不同的進程有不同的端口號
- 范圍:被規定為一個 16 位的整數 0~65535。
8.端口號與IP地址的組合得出一個網絡套接字:Socket
TCP的3個例子
通過三個例子學習客戶端與服務端的交互
例子1: 客戶端發送信息給服務端,服務端將數據顯示在控制台上
public class TCPTest1 {
//客戶端 @Test public void client() { Socket socket = null; OutputStream os = null; try { //1.創建Socket對象,指明服務器端的ip和端口號 InetAddress inet = InetAddress.getByName("192.168.14.100"); socket = new Socket(inet,8899); //2.獲取一個輸出流,用於輸出數據 os = socket.getOutputStream(); //3.寫出數據的操作 os.write("你好,我是客戶端mm".getBytes()); } catch (IOException e) { e.printStackTrace(); } finally { //4.資源的關閉 if(os != null){ try { os.close(); } catch (IOException e) { e.printStackTrace(); } } if(socket != null){ try { socket.close(); } catch (IOException e) { e.printStackTrace(); } } } } //服務端 @Test public void server() { ServerSocket ss = null; Socket socket = null; InputStream is = null; ByteArrayOutputStream baos = null; try { //1.創建服務器端的ServerSocket,指明自己的端口號 ss = new ServerSocket(8899); //2.調用accept()表示接收來自於客戶端的socket socket = ss.accept(); //3.獲取輸入流 is = socket.getInputStream(); //不建議這樣寫,可能會有亂碼
// byte[] buffer = new byte[1024];
// int len;
// while((len = is.read(buffer)) != -1){
// String str = new String(buffer,0,len);
// System.out.print(str);
// }
//4.讀取輸入流中的數據
baos = new ByteArrayOutputStream();
byte[] buffer = new byte[5];
int len;
while((len = is.read(buffer)) != -1){
baos.write(buffer,0,len);
}System.out.println(baos.toString()); System.out.println("收到了來自於:" + socket.getInetAddress().getHostAddress() + "的數據"); } catch (IOException e) { e.printStackTrace(); } finally { if(baos != null){ //5.關閉資源 try { baos.close(); } catch (IOException e) { e.printStackTrace(); } } if(is != null){ try { is.close(); } catch (IOException e) { e.printStackTrace(); } } if(socket != null){ try { socket.close(); } catch (IOException e) { e.printStackTrace(); } } if(ss != null){ try { ss.close(); } catch (IOException e) { e.printStackTrace(); } } } }
}
總結:
客戶端步驟分為:
- 創建Socket對象,指明服務器端的ip和端口號(關鍵字: Socket、InetAddress、getByName)
- 獲取一個輸出流,用於輸出數據(關鍵字: OutputStream、 socket.getOutputStream)
- 寫出數據的操作(關鍵字: getBytes)
- 關閉資源(后用先關,先關輸出流,再關socket)
服務端步驟分為:
- 創建服務器端的ServerSocket,指明自己的端口號(關鍵字: ServerSocket)
- 調用accept()表示接收來自於客戶端的socket(關鍵字: accept)
- 獲取輸入流(關鍵字: getInputStream)
- 讀取輸入流中的數據(關鍵字: ByteArrayOutputStream)
- 關閉資源
例子2:客戶端發送文件給服務端,服務端將文件保存在本地。
public class TCPTest2 {
/* 這里涉及到的異常,應該使用try-catch-finally處理 */ @Test public void client() throws IOException { //1. Socket socket = new Socket(InetAddress.getByName("127.0.0.1"),9090); //2. OutputStream os = socket.getOutputStream(); //3. FileInputStream fis = new FileInputStream(new File("beauty.jpg")); //4. byte[] buffer = new byte[1024]; int len; while((len = fis.read(buffer)) != -1){ os.write(buffer,0,len); } //5. fis.close(); os.close(); socket.close(); } /* 這里涉及到的異常,應該使用try-catch-finally處理 */ @Test public void server() throws IOException { //1. ServerSocket ss = new ServerSocket(9090); //2. Socket socket = ss.accept(); //3. InputStream is = socket.getInputStream(); //4. FileOutputStream fos = new FileOutputStream(new File("beauty1.jpg")); //5. byte[] buffer = new byte[1024]; int len; while((len = is.read(buffer)) != -1){ fos.write(buffer,0,len); } //6. fos.close(); is.close(); socket.close(); ss.close(); }
}
分析:步驟與例子1是一樣的,差別在於服務端保存文件的IO方法。
例子3:從客戶端發送文件給服務端,服務端保存到本地。並返回“發送成功”給客戶端。並關閉相應的連接。
public class TCPTest3 {
/* 這里涉及到的異常,應該使用try-catch-finally處理 */ @Test public void client() throws IOException { //1. Socket socket = new Socket(InetAddress.getByName("127.0.0.1"),9090); //2. OutputStream os = socket.getOutputStream(); //3. FileInputStream fis = new FileInputStream(new File("beauty.jpg")); //4. byte[] buffer = new byte[1024]; int len; while((len = fis.read(buffer)) != -1){ os.write(buffer,0,len); } //關閉數據的輸出 socket.shutdownOutput(); //5.接收來自於服務器端的數據,並顯示到控制台上 InputStream is = socket.getInputStream(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); byte[] bufferr = new byte[20]; int len1; while((len1 = is.read(buffer)) != -1){ baos.write(buffer,0,len1); } System.out.println(baos.toString()); //6. fis.close(); os.close(); socket.close(); baos.close(); } /* 這里涉及到的異常,應該使用try-catch-finally處理 */ @Test public void server() throws IOException { //1. ServerSocket ss = new ServerSocket(9090); //2. Socket socket = ss.accept(); //3. InputStream is = socket.getInputStream(); //4. FileOutputStream fos = new FileOutputStream(new File("beauty2.jpg")); //5. byte[] buffer = new byte[1024]; int len; while((len = is.read(buffer)) != -1){ fos.write(buffer,0,len); } System.out.println("圖片傳輸完成"); //6.服務器端給予客戶端反饋 OutputStream os = socket.getOutputStream(); os.write("你好,美女,照片我已收到,非常漂亮!".getBytes()); //7. fos.close(); is.close(); socket.close(); ss.close(); os.close(); }
}
URL網絡編程
1.URL:統一資源定位符,對應着互聯網的某一資源地址
2.格式:
http://localhost:8080/examples/beauty.jpg?username=Tom
協議 主機名 端口號 資源地址 參數列表
- url.getProtocol( ): 獲取該URL的協議名
- url.getHost( ): 獲取該URL的主機名
- url.getPort( ): 獲取該URL的端口號
- url.getPath( ): 獲取該URL的文件路徑
- url.getFile( ): 獲取該URL的文件名
- url.getQuery( ): 獲取該URL的查詢名
public class URLTest1 {
public static void main(String[] args) { HttpURLConnection urlConnection = null; InputStream is = null; FileOutputStream fos = null; try { URL url = new URL("http://localhost:8080/examples/beauty.jpg"); urlConnection = (HttpURLConnection) url.openConnection(); urlConnection.connect(); is = urlConnection.getInputStream(); fos = new FileOutputStream("day10\\beauty3.jpg"); byte[] buffer = new byte[1024]; int len; while((len = is.read(buffer)) != -1){ fos.write(buffer,0,len); } System.out.println("下載完成"); } catch (IOException e) { e.printStackTrace(); } finally { //關閉資源 if(is != null){ try { is.close(); } catch (IOException e) { e.printStackTrace(); } } if(fos != null){ try { fos.close(); } catch (IOException e) { e.printStackTrace(); } } if(urlConnection != null){ urlConnection.disconnect(); } } }
}
UDP協議的網絡編程
public class UDPTest {
//發送端 @Test public void sender() throws IOException { DatagramSocket socket = new DatagramSocket(); String str = "我是UDP方式發送的導彈"; byte[] data = str.getBytes(); InetAddress inet = InetAddress.getLocalHost(); DatagramPacket packet = new DatagramPacket(data,0,data.length,inet,9090); socket.send(packet); socket.close(); } //接收端 @Test public void receiver() throws IOException { DatagramSocket socket = new DatagramSocket(9090); byte[] buffer = new byte[100]; DatagramPacket packet = new DatagramPacket(buffer,0,buffer.length); socket.receive(packet); System.out.println(new String(packet.getData(),0,packet.getLength())); socket.close(); }
}
day29
反射初步了解
在Person類外部,不可以通過Person類的對象調用其內部私有結構。可以通過反射,創建Person類的對象,調用對象指定的屬性、方法,甚至可以通過反射,可以調用Person類的私有結構的。比如:私有的構造器、方法、屬性。
疑問1:通過直接new的方式或反射的方式都可以調用公共的結構,開發中到底用那個?
建議:直接new的方式。
什么時候會使用:反射的方式。 反射的特征:動態性
疑問2:反射機制與面向對象中的封裝性是不是矛盾的?如何看待兩個技術?
不矛盾。
關於java.lang.Class類的理解
- 類的加載過程:
程序經過javac.exe命令以后,會生成一個或多個字節碼文件(.class結尾)。接着我們使用java.exe命令對某個字節碼文件進行解釋運行。相當於將某個字節碼文件加載到內存中。此過程就稱為類的加載。加載到內存中的類,我們就稱為運行時類,此運行時類,就作為Class的一個實例。 - 換句話說,Class的實例就對應着一個運行時類。
- 加載到內存中的運行時類,會緩存一定的時間。在此時間之內,我們可以通過不同的方式 來獲取此運行時類。
獲取Class的實例的方式
(前三種方式需要掌握)
- 方式一:調用運行時類的屬性:.class
- 方式二:通過運行時類的對象,調用getClass()
- 方式三:調用Class的靜態方法:forName(String classPath) (常用)
- 方式四:使用類的加載器:ClassLoader (了解)
public void test3() throws ClassNotFoundException { //方式一:調用運行時類的屬性:.class Class clazz1 = Person.class; System.out.println(clazz1); //方式二:通過運行時類的對象,調用getClass() Person p1 = new Person(); Class clazz2 = p1.getClass(); System.out.println(clazz2);
//方式三:調用Class的靜態方法:forName(String classPath) Class clazz3 = Class.forName("com.atguigu.java.Person"); //clazz3 = Class.forName("java.lang.String"); System.out.println(clazz3); System.out.println(clazz1 == clazz2); System.out.println(clazz1 == clazz3); //方式四:使用類的加載器:ClassLoader (了解) ClassLoader classLoader = ReflectionTest.class.getClassLoader(); Class clazz4 = classLoader.loadClass("com.atguigu.java.Person"); System.out.println(clazz4); System.out.println(clazz1 == clazz4);
}
Class實例可以是哪些結構
萬事萬物皆對象
@Test public void test4(){ Class c1 = Object.class; Class c2 = Comparable.class; Class c3 = String[].class; Class c4 = int[][].class; Class c5 = ElementType.class; Class c6 = Override.class; Class c7 = int.class; Class c8 = void.class; Class c9 = Class.class;
int[] a = new int[10]; int[] b = new int[100]; Class c10 = a.getClass(); Class c11 = b.getClass(); // 只要數組的元素類型與維度一樣,就是同一個Class System.out.println(c10 == c11);
}
newInstance
newInstance():調用此方法,創建對應的運行時類的對象。內部調用了運行時類的空參的構造器。
要想此方法正常的創建運行時類的對象,要求:
1.運行時類必須提供空參的構造器
2.空參的構造器的訪問權限得夠。通常,設置為public。
在javabean中要求提供一個public的空參構造器。原因:
1.便於通過反射,創建運行時類的對象
2.便於子類繼承此運行時類時,默認調用super()時,保證父類有此構造器
了解類的加載器
- 對於自定義類,使用系統類加載器進行加載
- 調用系統類加載器的getParent():獲取擴展類加載器
- 調用擴展類加載器的getParent():無法獲取引導類加載器
- 引導類加載器主要負責加載java的核心類庫,無法加載自定義類的。
類加載器作用是用來把類(class)裝載進內存的。JVM規范定義了如下類型的類的加載器。
@Test public void test1(){ //對於自定義類,使用系統類加載器進行加載 ClassLoader classLoader = ClassLoaderTest.class.getClassLoader(); System.out.println(classLoader); //調用系統類加載器的getParent():獲取擴展類加載器 ClassLoader classLoader1 = classLoader.getParent(); System.out.println(classLoader1); //調用擴展類加載器的getParent():無法獲取引導類加載器 //引導類加載器主要負責加載java的核心類庫,無法加載自定義類的。 ClassLoader classLoader2 = classLoader1.getParent(); System.out.println(classLoader2);
ClassLoader classLoader3 = String.class.getClassLoader(); System.out.println(classLoader3);
}
Properties
Properties:用來讀取配置文件
@Test public void test2() throws Exception {
Properties pros = new Properties(); //此時的文件默認在當前的module下。 //讀取配置文件的方式一: //FileInputStream fis = new FileInputStream("jdbc.properties"); //FileInputStream fis = new FileInputStream("src\\jdbc1.properties"); //pros.load(fis); //讀取配置文件的方式二:使用ClassLoader //配置文件默認識別為:當前module的src下 ClassLoader classLoader = ClassLoaderTest.class.getClassLoader(); InputStream is = classLoader.getResourceAsStream("jdbc1.properties"); pros.load(is); String user = pros.getProperty("user"); String password = pros.getProperty("password"); System.out.println("user = " + user + ",password = " + password);
}
總結:
- 注意兩種方式讀取文件的路徑不同,方式一在當前module下,方式二在當前module的src下
- 使用getResourceAsStream讀取文件
- 將要讀去的數據寫入一個.properties文件中,創建方式New --> Resource Bundle
獲取運行時類的屬性、方法、父類等
- 獲取屬性結構
getFields():獲取當前運行時類及其父類中聲明為public訪問權限的屬性
getDeclaredFields():獲取當前運行時類中聲明的所有屬性。(不包含父類中聲明的屬性)
- 獲取權限修飾符 數據類型 變量名
getModifiers():權限修飾符
getType():數據類型
getName():變量名
- 獲取方法結構
getMethods():獲取當前運行時類及其所有父類中聲明為public權限的方法
getDeclaredMethods():獲取當前運行時類中聲明的所有方法。(不包含父類中聲明的方法)
- 獲取注解、權限修飾符、返回值類型、方法名、形參列表、拋出的異常
例如:
`/*
@Xxxx
權限修飾符 返回值類型 方法名(參數類型1 形參名1,...) throws XxxException{}
*/`
getAnnotations(): 獲取方法聲明的注解
Modifier.toString(m.getModifiers()): 權限修飾符
獲取的權限修飾符比較特殊, getModifiers()返回的是int類型(如1, 2, 3, 4),這些數字分別代表public, protected等
使用Modifier.toString將它轉化為字符串形式的權限修飾符 getReturnType().getName():返回值類型
getName()
形參列表
Class[] parameterTypes = m.getParameterTypes(); if(!(parameterTypes == null && parameterTypes.length == 0)){ for(int i = 0;i < parameterTypes.length;i++){
if(i == parameterTypes.length - 1){ System.out.print(parameterTypes[i].getName() + " args_" + i); break; } System.out.print(parameterTypes[i].getName() + " args_" + i + ","); } }
拋出的異常
Class[] exceptionTypes = m.getExceptionTypes(); if(exceptionTypes.length > 0){ System.out.print("throws "); for(int i = 0;i < exceptionTypes.length;i++){ if(i == exceptionTypes.length - 1){ System.out.print(exceptionTypes[i].getName()); break; }
System.out.print(exceptionTypes[i].getName() + ","); } }