Java基礎-高階特性-簡單總結(I/O和反射機制)
Java中按照流向分,分為輸入流和輸出流,按照處理資料單元分,分為字元流和位元組流。這個總結開始會簡單寫一點方法,然後會寫一下常用模板,套用就好了。
File類的常用方法:
方法 | 說明 |
boolean exists( ) | 測試檔案是否存在 |
String getAbsolutePath( ) | 返回此物件表示的檔案的絕對路徑 |
String getName( ) | 返回此物件表示的檔案的名稱 |
String getParent( ) | 返回此File物件的路徑名的上一級,如果路徑名沒有上一級,則返回null |
boolean delete( ) | 刪除此物件指定的檔案 |
boolean createNewFile( ) | 建立空檔案,不建立資料夾 |
boolean isSirectory( ) | 測試此File物件表示的是否為目錄 |
boolean mkdir( ) | 建立一個目錄,它的路徑名由當前File物件指定 |
boolean mkdirs( ) | 建立包括父目錄的目錄 |
Java中的輸出流主要由OutputStream和Write組成,輸入流主要由InputStream和Reader作為基類。都是抽象類。
InputStream常用的子類有FileInputStream,用於從檔案中讀取資料。常用方法:
方法 | 說明 |
int read( ) | 從輸入流中讀取下一個位元組資料 |
int read(byte [ ] b) | 從輸入流中讀取資料,並將資料儲存在緩衝區陣列b中,返回實際讀取的位元組數 |
int read(byte [ ] b,int off,int len) | 從輸入流中讀取最多len長度的位元組,儲存到位元組陣列b中,儲存的位置從off開始 |
void close() | 關閉輸入流 |
Reader類的常用子類有BufferedReader,接受Reader物件作為引數,並對其新增字元緩衝器。常用方法:
方法 | 說明 |
int read( ) | 從輸入流中讀取單個字元,返回所讀取的字元資料 |
int read(byte [ ] c) | 從輸入流中最多讀取c.length個字元,儲存到字元陣列c中,返回實際讀取的字元數 |
int read(char [ ] c,int off.int len) | 從輸入流中讀取最多len個字元,儲存到字元陣列c中,儲存的位置從off開始,返回實際讀取的字元數 |
void close( ) | 關閉流 |
OutputStream類的常用子類為FileOutputStream,用於向檔案寫資料。常用方法:
方法 | 說明 |
void weite( int c) | 將指定的位元組資料寫入此輸出流中 |
void weite( byte [ ] buf) | 將陣列buf中的所有位元組寫入此輸出流中 |
void write(byte [ ] b,int off.int len) | 將位元組陣列中從偏移量off開始的長度為len的位元組資料輸出到輸出流中 |
void close( ) | 關閉輸出流 |
Write類的常用子類為BufferedWriter,用於將資料緩衝到字元輸出流。常用方法:
方法 | 說明 |
void write( String str) | 將str字串裡包含的字元輸出到指定的輸出流中 |
void write( String str,int off,int len) | 將str字串從off位置開始,長度為len的多個字串輸出到輸出流中 |
void close( ) | 關閉輸出流 |
void flush( ) | 重新整理輸出流 |
讀寫二進位制檔案:
利用DataInputStream類讀二進位制檔案,利用DataOutputStream類寫二進位制檔案。
順帶一提,這兩個類搭配使用,可以按照與平臺無關的方式從流中讀取基本資料型別的資料,如int、float、double和boolean等,此外,DataInputStream的readUTF()方法能讀取採用UTF-8字符集編碼的字串。同樣,在向外輸出時也遵循此規則,但是,方法中並沒有readString( )和writeString( )方法。
序列化和反序列化:
序列化就是將物件的狀態儲存到特定儲存介質中的過程,也就是將物件狀態轉換為可保持或可傳輸格式的過程。序列化後的物件儲存的是二進位制狀態。
Java中只有實現了java.io.Serializable介面的類的物件才能被序列化。
反序列化是從特定儲存介質中讀取資料並重新構建成物件的過程。反序列化要進行強制型別轉換。(如果一個可序列化的類,有多個父類(包括直接父類或間接父類),則這些父類要麼是可序列化的,要麼有無引數的構造器,否則會丟擲異常)。對於一些不想被序列化的敏感資訊,可以用transient來修飾。
當需要序列化某個特定物件時,它的各個成員物件也必須是可序列化的。所有儲存到磁碟中的物件都有一個序列號,當程式試圖序列化一個物件時,會檢查是否已經被序列化,只有序列化後的物件才能被轉換成位元組序列輸出。如果物件已經被序列化,則程式直接輸出一個序列化編號。序列化編號也是一個序列化後的物件,在反序列化時也得到一個物件。
通常用到輸入輸出時的寫法:
位元組流:
(輸入流)FileInputStream fis =new FileInputStream( 路徑 );
BufferedInputStream bis =new BufferedInputStream( fis );
(輸出流)FileOutputStream fos =new FileOutputStream(路徑);
BufferedOutputStream bos =new BufferedOutputStream (fos);
字元流:
(輸入)FileReader fd =new FileReader (路徑);
BufferedReader br =new BufferedReader (fd);
(輸出)FileWriter fw =new FileWriter (路徑) ;
BufferedWriter bw=new BufferedWriter (fw) ;
轉換格式時:
(輸入)FileInputStream fis =new FileInputStream (路徑) ;
InputStreamReader isr =new InputStreamReader (fis,"格式") ;
BufferedReader br =new BufferedReader (isr);
(輸出)FileOutputStream fos =new FileOutputStream (路徑);
OutputStreamWriter osw =new OutputStreamWriter (fos,"格式") ;
BufferedWriter bw =new BufferedWriter (osw) ;
二進位制檔案:
(輸入)FileInputStream fis =new FileInputStream (路徑);
DataInputStream dis =new DataInputStream (fis) ;
(輸出)FileOutputStream fos =new FileOutputStream (路徑) ;
DataOutputStream dos =new DataOutputStream (fos) ;
序列化和反序列化:
(序列化)FileOutputStream fos =new FileOutputStream (路徑) ;
ObjectOutputStream oos =new ObjectOutputStream (fos) ;
(反序列化)FileInputStream fis =new FileOutputStream (路徑) ;
ObjectInputStream ois =new ObjectInputStream (fis) ;
基本格式差不多就是這樣了,最後一個很重要的點,開了的流記得關,先開的後關,後開的先關。
反射:
反射這方面我的理解也並不是太深,就簡單講講表面。
Java的反射機制是Java的特性之一,反射機制是構建框架技術的基礎所在。Java反射機制是指在執行狀態中,動態獲取資訊以及動態呼叫物件方法的功能。Java反射有三個動態性質,分別是執行時生成物件例項,執行期間呼叫方法,執行時更改屬性。
通過Java反射,可以實現以下功能:
在執行時判斷任意一個物件所屬的類;
在執行時構造任意一個類的物件;
在執行時判斷任意一個類所具有的方法和屬性;
在執行時呼叫任意一個物件的方法;
Java反射常用API:Class類,反射的核心類。Filed類,表示類的屬性。Method類,表示類的方法。Constructor類,表示類的構造方法。
Java程式中獲得Class物件通常有如下三種方法:
第一種:呼叫物件的getClass( )方法
Student stu=new Student();
Class cla=stu.getClass();
第二種:呼叫類的class屬性 Class cla1=Student.class;
第三種:使用Class類的forName( )靜態方法 Class cla2=Class.forName("某個類的全名");
相比之下呼叫某個類的class屬性來獲取該類對應的Class物件這種方式更有優勢。因為程式碼更安全,程式在編譯階段就可以檢查需要訪問的Class物件是否存在。而且程式效能更高,因為這種方式無需呼叫方法,所以效能更好。
獲得Class物件後,可以用Class物件獲得該類裡的成員,包括方法,構造方法以及屬性,方法由Method表示,構造方法由Constructor表示,屬性由Field表示。這個方法太繁雜了,我就不手打了,可以自己去看看API。
結合兩個例子來看看怎麼用。
這是隨手寫的一個學生類。
public class Student{
private String name;
private int age;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Student() {
}
public Student(int age, String name) {
this.age = age;
this.name = name;
}
使用Class物件建立一個該Class物件對應的物件,並直接獲取name屬性,然後修改name屬性
try {
Student stu=new Student();
Class cla=stu.getClass();
Field nameFiele=cla.getDeclaredField("name");
nameFiele.setAccessible(true);
nameFiele.set(stu,"熊大");
System.out.println(stu.getName());
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
提一句,getField( )方法只能獲取public訪問許可權的屬性,而使用getDeclaredField( )方法則可以獲取所有訪問許可權的屬性。
使用Class物件建立一個該Class物件對應的物件,並操作物件裡面設定姓名的方法,更改物件的姓名
Student stu=new Student();
Class cla=stu.getClass();
try {
Method nameMethod=cla.getMethod("setName",String.class);
nameMethod.invoke(stu,"熊二");
System.out.println(stu.getName());
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
通過Method的invoke( )方法呼叫方法時,Java會要求程式必須有呼叫該方法的許可權,如果程式確實需要呼叫某個物件的private方法,可以先呼叫setAccessible( )方法,將Method物件的accessible標誌設定為指定的布林值,值為true則代表該Method在使用時應該取消Java語言訪問許可權檢查,值為false則表示該Method在使用時應該進行Java語言訪問許可權檢查。
差不多就是這個樣子吧,動態操作嘛~
關於反射的詳細說明建議看看別的部落格,我這裡寫的比較粗淺。
練習:
1.編寫一個程式將file1.txt檔案中的內容複製到file2.txt檔案中。
FileReader fd=null;
BufferedReader br=null;
FileWriter fw=null;
BufferedWriter bw=null;
try {
fd=new FileReader("file1.txt");
br=new BufferedReader(fd);
fw=new FileWriter("file2.txt");
bw=new BufferedWriter(fw);
String s=null;
while((s=br.readLine())!=null){
bw.write(s);
bw.newLine();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
bw.close();
fw.close();
br.close();
fd.close();
} catch (IOException e) {
e.printStackTrace();
}
}
2.編寫一個Java程式讀取Windows目錄下的win.ini檔案,並輸出內容。
FileReader fd=null;
BufferedReader br=null;
try {
fd=new FileReader("c:\\Windows\\win.ini");
br=new BufferedReader(fd);
String line=br.readLine();
while(line!=null){
System.out.println(line);
line=br.readLine();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
br.close();
fd.close();
} catch (IOException e) {
e.printStackTrace();
}
}
3.編寫一個程式,執行Java控制檯程式,檢測本地是否存在學生物件(反序列化),如果儲存,則輸出學生資訊,如果沒有儲存,則通過學生類Student建立一個學生物件,將學生資訊輸出並儲存到本地檔案(序列化)中。
FileInputStream fis=null;
ObjectInputStream ois=null;
FileOutputStream fos=null;
ObjectOutputStream oos=null;
Student stu=new Student();
try {
fis=new FileInputStream("d:\\stu.txt");
if (fis.available()>0){
ois=new ObjectInputStream(fis);
stu=(Student)ois.readObject();
System.out.println("本地有學生資訊,學生資訊為:");
System.out.println("姓名:"+stu.getName());
System.out.println("年齡:"+stu.getAge());
System.out.println("性別:"+stu.getSex());
}else{
System.out.println("本地沒有學生資訊,自動新增一位學員資訊");
fos=new FileOutputStream("d:\\stu.txt");
oos=new ObjectOutputStream(fos);
stu=new Student(18,"張三",'男');
oos.writeObject(stu);
System.out.println("新增學員資訊為:");
System.out.println("姓名:"+stu.getName());
System.out.println("年齡:"+stu.getAge());
System.out.println("性別:"+stu.getSex());
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}finally {
try {
if(null!=oos){
oos.close();
}
if(null!=fos){
fos.close();
}
if(null!=ois){
ois.close();
}
if(null!=fis){
fis.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
這個題的finally如果不判斷是否開啟了對應的流,會邊執行邊報錯,雖然結果是對的,但是看起來不舒服,所以所有關閉流的時候建議都判斷一下。