1. 程式人生 > >Java基礎-高階特性-簡單總結(I/O和反射機制)

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如果不判斷是否開啟了對應的流,會邊執行邊報錯,雖然結果是對的,但是看起來不舒服,所以所有關閉流的時候建議都判斷一下。