Java學(xué)習(xí)之IO流對象

一、對象序列化

ObjectOutputStram和ObjectInputStream

1、概述:

將堆內(nèi)存中的對象存入硬盤,保留對象中的數(shù)據(jù),稱之為對象的持久化(或序列化)

2、特有方法:

1、write(int val) ---> 寫入一個字節(jié)(最低八位)

2、writeInt(int vale) ---> 吸入一個32為int值

3、使用步驟:

說明:serialVersion
a、給類一個可被編譯器識別的的序列號,在編譯類時,會分配一個long型UID,通過序列號,將類存入硬盤中,并序列化,即持久化。序列號根據(jù)成員算出的。靜態(tài)不能被序列化。如果非靜態(tài)成員也無需序列化,可以用transien修飾。

b、接口Serializable中沒有方法,稱之為標(biāo)記接口

1、寫入流對象:

1)創(chuàng)建對象寫入流,與文件關(guān)聯(lián),即傳入目的

2)通過寫入writeObject()方法,將對象作為參數(shù)傳入,即可寫入文件

2、讀取流對象

1)創(chuàng)建對象讀取流,與文件關(guān)聯(lián),即傳入源

2)通過writeObject()方法,讀取文件中的對象,并返回這個對象

示例:

import java.io.*;  
//創(chuàng)建Person類,實現(xiàn)序列化  
class Person implements Serializable{  
    //定義自身的序列化方式  
    public static final long serialVersionUID = 42L;  
    //定義私有屬性  
    private String name;  
    private int age;  
    transient String id;  
    static String country = "cn";  
    //構(gòu)造Person類  
    Person(String name,int age,String id,String country){  
        this.name = name;  
        this.age = age;  
        this.id = id;  
        this.country = country;  
    }  
    //覆寫toString方法  
    public String toString(){  
        return name+ ":" + age + ":" + id + ":" + country;  
    }  
}  
//對象序列化測試  
class ObjectStreamDemo{  
    public static void main(String[] args){  
        //對象寫入流  
        writeObj();  
        //對象讀取流  
        readObj();  
    }  
    //定義對象讀取流  
    public static void readObj(){  
        ObjectInputStream ois = null;  
        try{  
            //創(chuàng)建對象讀取流  
            ois = new ObjectInputStream(new FileInputStream("obj.txt"));  
            //通過讀取文件數(shù)據(jù),返回對象  
            Person p = (Person)ois.readObject();  
            System.out.println(p);  
        }catch (Exception e){  
            throw new RuntimeException("寫入文件失敗");  
        }  
        //最終關(guān)閉流對象  
        finally{  
            try{  
                if(ois!=null)  
                    ois.close();  
                }catch (IOException e){  
                throw new RuntimeException("寫入流關(guān)閉失敗");  
            }  
        }  
    }  
    //定義對象寫入流  
    public static void writeObj(){  
        ObjectOutputStream oos = null;  
        try{  
            //創(chuàng)建對象寫入流  
            oos = new ObjectOutputStream(new FileOutputStream("obj.txt"));  
            //寫入對象數(shù)據(jù)  
            oos.writeObject(new Person("lisi",25,"01","cn"));             
        }catch (Exception e){  
            throw new RuntimeException("寫入文件失敗");  
        }  
        //關(guān)閉流資源  
        finally{  
            try{  
                if(oos!=null)  
                    oos.close();  
                }catch (IOException e){  
                throw new RuntimeException("寫入流關(guān)閉失敗");  
            }  
        }  
    }  
} 

二、管道流

1、概述:

1、管道流:PipedInputStream和PipedOutputStream

2、管道流涉及到多線程的問題

2、使用步驟:

1、要先創(chuàng)建一個讀和寫的兩個類,實現(xiàn)Runnable接口,因為是兩個不同的線程,覆蓋run方法,注意,需要在內(nèi)部拋異常

2、創(chuàng)建兩個管道流,并用connect()方法將兩個流連接

3、創(chuàng)建讀寫對象,并傳入兩個線程內(nèi),并start執(zhí)行

示例:

import java.io.*;  
  
//創(chuàng)建Read類,實現(xiàn)run方法  
class Read implements Runnable{  
    private PipedInputStream in;      
    Read(PipedInputStream in){  
        this.in = in;  
    }  
    //實現(xiàn)run方法  
    public void run(){  
        try{  
            //讀取寫入的數(shù)據(jù)  
            //System.out.println("開始寫入數(shù)據(jù),等待時間");//測試用  
            Thread.sleep(3000);  
            byte[] b = new byte[1024];  
            int len = in.read(b);  
            //System.out.println("讀取完畢");//測試用  
            String s = new String(b,0,len);  
            System.out.println(s);  
            in.close();  
        }catch (Exception e){  
            throw new RuntimeException("管道流讀取失敗");  
        }  
    }  
}  
//創(chuàng)建Write類  
class Write implements Runnable{  
    private PipedOutputStream out;  
    //Write構(gòu)造函數(shù)  
    Write(PipedOutputStream out){  
        this.out = out;  
    }  
    //實現(xiàn)run方法  
    public void run(){  
        try{  
            //寫入數(shù)據(jù)  
            //System.out.println("開始寫入數(shù)據(jù)");//測試用  
            out.write("管道流來嘍~~~~".getBytes());  
            out.close();  
        }catch (IOException e){  
            throw new RuntimeException("管道流寫入失敗");  
        }  
    }  
}  
class PipedStreamDemo{  
    public static void main(String[] args) throws IOException  
    {  
        //創(chuàng)建管道流對象  
        PipedInputStream in = new PipedInputStream();  
        PipedOutputStream out = new PipedOutputStream();  
        //將讀取流(輸入流)和寫入流(輸出流)關(guān)聯(lián)起來  
        in.connect(out);  
        //創(chuàng)建讀寫對象,并創(chuàng)建線程對象  
        Read r = new Read(in);  
        Write w = new Write(out);  
        new Thread(r).start();  
        new Thread(w).start();  
    }  
}

三、RandomAccessFile 類

1、概述:

1、RandomAccessFile稱之為隨機訪問文件的類,自身具備讀寫方法。

2、該類不算是IO體系中的子類,而是直接繼承Object,但是它是IO包成員,因為它具備讀寫功能,內(nèi)部封裝了一個數(shù)組,且通過指針對數(shù)組的元素進(jìn)行操作,同時可通過seek改變指針的位置。

3、可以完成讀寫的原理:內(nèi)部封裝了字節(jié)輸入流

4、構(gòu)造函數(shù):RandomAccessFile(File file,String mode),可已從它的構(gòu)造函數(shù)中看出,該類只能操作文件(也有字符串),而且操作文件還有模式。

模式傳入值:”r“:以只讀方式打開;”rw“:打開以便讀寫

如果模式為只讀,則不會創(chuàng)建文件,會去讀一個已存在的文件,若文件不存在,則會出現(xiàn)異常,如果模式為rw,且該對象的構(gòu)造函數(shù)要操作的文件不存在,會自動創(chuàng)建,如果存在,則不會覆蓋,也可通過seek方法修改。

2、特有方法:

1、seek(int n):設(shè)置指針,可以將指針設(shè)置到前面或后面

2、skipBytes(int n):跳過指定字節(jié)數(shù),不可往前跳

3、使用步驟:

1、創(chuàng)建RandomAccessFile對象

2、將數(shù)據(jù)寫入到指定文件中

3、讀取數(shù)據(jù),讀入到指定文件中

注意:堯都區(qū)后面的數(shù)據(jù),需要調(diào)用數(shù)組指針,通過改變角標(biāo)位置,取出相應(yīng)的數(shù)據(jù)

a.調(diào)整對象的指針:seek()
b.跳過指定字節(jié)數(shù)

示例:

import java.io.*;  
//注:這幾個函數(shù)內(nèi)部都需要try,為測試,在函數(shù)上拋異常  
class RanAccFileDemo{  
    public static void main(String[] args) throws IOException{  
        //readRaf();  
        readRaf2();  
        //writeRaf();  
    }  
    //寫入數(shù)據(jù)  
    public static void writeRaf()throws IOException{  
        //創(chuàng)建對象,寫入數(shù)據(jù)  
        RandomAccessFile raf = new RandomAccessFile("ran.txt","rw");  
        raf.write("王五".getBytes());  
        raf.writeInt(99);  
        raf.write("李四".getBytes());  
        raf.writeInt(97);  
        raf.close();  
    }  
    //讀取數(shù)據(jù)  
    public static void readRaf()throws IOException{  
        //創(chuàng)建對象,讀取數(shù)據(jù)  
        RandomAccessFile raf = new RandomAccessFile("ran.txt","r");  
        byte[] b = new byte[4];  
        raf.read(b);  
        String name = new String(b);  
        int age = raf.readInt();  
        System.out.println("name="+ name);  
        System.out.println("age=" + age);  
        raf.close();  
    }  
    //讀取數(shù)據(jù)  
    public static void readRaf2()throws IOException {  
        //創(chuàng)建對象,讀取數(shù)據(jù)  
        RandomAccessFile raf = new RandomAccessFile("ran.txt","r");  
        //調(diào)整對象中的指針  
        //raf.seek(8);  
        //跳過指定字節(jié)數(shù)  
        raf.skipBytes(8);  
        byte[] b = new byte[4];  
        raf.read(b);  
        String name = new String(b);  
        int age = raf.readInt();  
        System.out.println("name="+ name);  
        System.out.println("age=" + age);  
        raf.close();  
    }  
}

四、操作基本數(shù)據(jù)類型的流對象

1、概述:

1、操作基本數(shù)據(jù)類型的流對象:DataInputStream和DataOutputStream

2、這兩個讀寫對象,可用于操作基本數(shù)據(jù)類型的流對象,包含讀寫各種基本數(shù)據(jù)類型的方法

2、特有方法:

int型 writeInt(int n) int readInt()
boolean型 writeBoolean(boolean b) boolean readBoolean()
double型 writeDouble(double d) double readDouble()

五、操作數(shù)組和字符串

1、操作字節(jié)數(shù)組的對象:ByteArrayInputStream和ByteArrayOutputStream

1、這個對象并沒有調(diào)用底層資源,所以不用關(guān)閉流資源

2、存入的是緩沖區(qū),并未用到鍵盤和硬盤燈,所以不需要拋任何IO異常

3、對象中封裝了數(shù)組

4、構(gòu)造函數(shù):
1)ByteArrayInputStream:在構(gòu)造函數(shù)的時候,需要接受數(shù)據(jù)源,而且數(shù)據(jù)源是一個字節(jié)數(shù)據(jù)。
2)ByteArrayOutputStream:在構(gòu)造函數(shù)的時候,不用定義數(shù)據(jù)目的,因為該對象中已經(jīng)在內(nèi)部封裝了可變長度的字節(jié)數(shù)組,這就是數(shù)據(jù)的目的地

4、因為兩個流對象都是操作的是數(shù)據(jù),并沒有使用系統(tǒng)資源,所以不用進(jìn)行close關(guān)閉。

6、其實就是用流的思想操作數(shù)組

7、特有方法:writeTo(OutputStream out) 這個方法用到了字節(jié)輸出流,有異常存在,需要拋IO異常

2、對應(yīng)的字符數(shù)組和字符串:

字符數(shù)組流對象:CharArrayReader和CharArrayWriter
字符串流對象: StringReader和StringWriter

示例:

import java.io.*;  
class ArrayStreamDemo  
{  
    public static void main(String[] args)  
    {  
        //數(shù)據(jù)源  
        ByteArrayInputStream bais = new ByteArrayInputStream("ABCDEFF".getBytes());  
        //數(shù)據(jù)目的  
        ByteArrayOutputStream baos = new ByteArrayOutputStream();  
        int by = 0;  
        //讀取和寫入數(shù)據(jù)  
        while((by=bais.read())!=-1)  
        {  
            baos.write(by);  
        }  
        System.out.println(baos.size());  
  
        System.out.println(baos.toString());  
          
        try  
        {  
            //方法,此處拋異常,所以上面需要拋出去  
            baos.writeTo(new FileOutputStream("a.txt"));  
        }  
        catch (IOException e)  
        {  
            throw new RuntimeException("寫入文件失敗");  
        }  
          
    }  
}

六、字符編碼

1、概述:

1、字符流的出現(xiàn)為了方便操作字符,更重要的是加入了編碼的轉(zhuǎn)換,即轉(zhuǎn)換流。

2、通過子類進(jìn)行轉(zhuǎn)換

3、在兩個對象進(jìn)行構(gòu)造時,可加入編碼表

4、可傳入編碼表的有:
1)轉(zhuǎn)換流:InuputStreamReader和OutputStreamWriter
2)打印流:PrintStream和PrintWriter,只有輸出流

5、常見的編碼表:
1)ASCII:美國標(biāo)準(zhǔn)信息交換碼表。用一個字節(jié)的7位表示
2)IOS8859-1:拉丁碼表;歐洲碼表。用一個字節(jié)的8位表示
3)GB2312:中國的中文編碼表
4)GBK:中國的中文編碼表升級,融合了更多的中文文字字符。打頭的是兩個高位為1的兩個字節(jié)編碼。為負(fù)數(shù)
5)Unicode:國際標(biāo)準(zhǔn)碼,融合了多種文字
6)UTF-8:最多用三個字節(jié)表示一個字符的編碼表,包括:一位、兩位、三位表示的字符
UTF-8有自己的字節(jié)碼:
一個字節(jié):0開頭
兩個字節(jié):
》字節(jié)一 ---> 110 位數(shù):10 ~ 6
》字節(jié)二 ---> 10 位數(shù):5 ~ 0
三個字節(jié):
》字節(jié)一 ---> 110 位數(shù):15 ~ 12
》字節(jié)二 ---> 10 位數(shù):11 ~ 6
》字節(jié)三 ---> 10 位數(shù):5 ~ 0

2、編碼和解碼:

1、編解碼:
編碼:字符串變成字節(jié)數(shù)組
解碼:字節(jié)數(shù)組變成字符串

2、轉(zhuǎn)換:
1)默認(rèn)字符集:
String ---> byte[] :srt.getBytes()
byte[] ---> String :new String(byte[])
2)指定字符集:
String ---> byte[] :srt.getBytes(charsetName)
byte[] ---> String :new String(byte[],charsetName)

3、對于編碼和解碼的字符集轉(zhuǎn)換

1、如果編碼失敗,解碼就沒意義了。

2、如果編碼成功,解碼出來的是亂碼,,則需對亂碼通過再次編碼(用解錯碼的編碼表),然后再通過正確的編碼表解碼。針對于IOS8859-1是通用的。

3、如果用的是GBK編碼,UTF-8解碼,那么再通過2的方式,就不能成功了,因為UTF-8也支持中文,在UTF-8解的時候,會將對應(yīng)的字節(jié)數(shù)改變,所以不會成功。

編解碼示意圖

4、特別注意:

對于中文的”聯(lián)通“,這兩個字比較特別,它的二進(jìn)制位正好是和在UTF-8中兩個字節(jié)打頭的相同,可以找到對應(yīng)的符號,但不再是”聯(lián)通“了。

示例:

import java.util.*;  
  
class EncodeDemo  
{  
    public static void main(String[] args) throws Exception{  
        CodeDemo();  
        //編譯成功,解碼失敗后的解決方式  
        CodeBack();  
    }  
    public static void CodeDemo()throws Exception{  
        String s = "你好";  
        byte[] b1 = s.getBytes();  
        String s1 = new String(b1);  
        System.out.println(Arrays.toString(b1));  
  
        byte[] b2 = s.getBytes("GBK");//默認(rèn)編碼  
        String s2 = new String(b2);  
        System.out.println("s1=" + s1 + ",s2=" + s2);  
        System.out.println(Arrays.toString(b2));  
  
        byte[] b3 = s.getBytes("UTF-8");//國際編碼  
        String s3 = new String(b3);  
        System.out.println("s3=" + s3);  
        System.out.println(Arrays.toString(b3));  
  
        byte[] b4 = s.getBytes("ISO8859-1");//歐洲編碼  
        String s4 = new String(b4);  
        System.out.println("s4=" + s4);  
        System.out.println(Arrays.toString(b4));  
    }  
    //編碼與解碼  
    public static void CodeBack()throws Exception{  
        String s = "你好";  
        System.out.println("原數(shù)據(jù):" + s);  
        byte[] b1 = s.getBytes("GBK");//默認(rèn)編碼  
        System.out.println(Arrays.toString(b1));  
        String s1 = new String(b1,"ISO8859-1");  
        System.out.println("s1=" + s1);  
          
  
        System.out.println("----對s1進(jìn)行ISO8859-1編碼-----");  
        //對s1進(jìn)行ISO8859-1編碼  
        byte[] b2 = s1.getBytes("ISO8859-1");//歐洲編碼  
        System.out.println(Arrays.toString(b2));  
          
        String s2 = new String(b2,"GBK");  
        System.out.println("s2=" + s2);  
    }  
  
}

七、練習(xí)

五個學(xué)生,每個學(xué)生有3門課程的成績,從鍵盤輸入以上數(shù)據(jù)(姓名,三門課成績),

輸入格式:如:zahngsan,30,40,60計算出總成績,并把學(xué)生的信息和計算出的總分?jǐn)?shù)高低按順序存放在磁盤文件stud.txt中

步驟:
1、描述學(xué)生對象
2、定義一個可操作學(xué)生對象的工具類

思路:
1、通過獲取鍵盤錄入一行的數(shù)據(jù),并將該行數(shù)據(jù)的信息取出,封裝成學(xué)生對象
2、因為學(xué)生對象很多,則需要存儲,使用集合,因為要對學(xué)生總分排序
所以可以使用TreeSet
3、將集合中的信息寫入到一個文件中

import java.io.*;  
import java.util.*;  
//定義學(xué)生類  
class Student implements Comparable<Student>  
{  
    //定義私有屬性  
    private String name;  
    private int ma,cn,en;  
    private int sum;  
    //構(gòu)造Student函數(shù),初始化  
    Student(String name,int ma,int cn,int en)  
    {  
        this.name = name;  
        this.ma = ma;  
        this.cn = cn;  
        this.en = en;  
        sum = ma+cn+en;  
    }  
    //覆寫compareTo方法,按學(xué)生總成績排序  
    public int compareTo(Student s)  
    {  
        int num = new Integer(this.sum).compareTo(new Integer(s.sum));  
        if(num==0)  
            return this.name.compareTo(s.name);  
        return num;  
    }  
    //獲取學(xué)生信息  
    public String getName()  
    {  
        return name;  
    }  
    public int getSum()  
    {  
        return sum;  
    }  
    //覆寫hasdCode()和equals()方法,排除相同的兩個學(xué)生  
    public int hashCode()  
    {  
        return name.hashCode() + sum*39;  
    }  
    public boolean equals(Object obj)  
    {  
        if(obj instanceof Student)  
            throw new ClassCastException("類型不匹配");  
        Student s = (Student)obj;  
        return this.name.equals(s.name) && this.sum==s.sum;  
    }  
    //定義學(xué)生信息顯示格式  
    public String toString()  
    {  
        return "student[" + name + ", " + ma + ", " + cn + ", " + en + "]";  
    }  
}  
//工具類,將鍵盤錄入的輸入存入集合,并將集合的元素寫入文件中  
class StudentInfoTool  
{  
    //無比較器的學(xué)生集合  
    public static Set<Student> getStudents()  
    {  
        return getStudents(null);  
    }  
    //具備比較器的學(xué)生集合  
    public static Set<Student> getStudents(Comparator<Student> cmp)  
    {  
        BufferedReader bufr = null;  
        Set<Student> stus = null;  
        try  
        {  
            //創(chuàng)建讀取流對象緩沖區(qū),鍵盤錄入  
            bufr = new BufferedReader(new InputStreamReader(System.in));  
            String line = null;  
            //選擇集合是否有比較器  
            if(cmp==null)  
                stus = new TreeSet<Student>();  
            else  
                stus = new TreeSet<Student>(cmp);  
            //循環(huán)讀取鍵盤錄入的數(shù)據(jù)  
            while((line=bufr.readLine())!=null)  
            {  
                if("over".equals(line))  
                    break;  
                //對讀取的數(shù)據(jù)進(jìn)行分割并存入集合  
                String[] info = line.split(",");  
                Student stu = new Student(info[0],Integer.parseInt(info[1]),  
                                                Integer.parseInt(info[2]),  
                                                Integer.parseInt(info[3]));  
                stus.add(stu);  
            }  
        }  
        catch (IOException e)  
        {  
            throw new RuntimeException("學(xué)生信息讀取失敗");  
        }  
        //關(guān)閉流資源  
        finally  
        {  
            try  
            {  
                if(bufr!=null)  
                    bufr.close();  
            }  
            catch (IOException e)  
            {  
                throw new RuntimeException("讀取流關(guān)閉失敗");  
            }  
            return stus;  
        }  
    }  
    //將數(shù)據(jù)寫入指定文件  
    public static void write2File(Set<Student> stus,String fileName)  
    {  
        BufferedWriter bufw = null;  
        try  
        {  
            //創(chuàng)建寫入流對象  
            bufw = new BufferedWriter(new FileWriter(fileName));  
            //循環(huán)寫入數(shù)據(jù)  
            for(Student stu : stus)  
            {  
                bufw.write(stu.toString() + "\t");  
                bufw.write(stu.getSum() + "");  
                bufw.newLine();  
                bufw.flush();  
            }  
        }  
        catch (IOException e)  
        {  
            throw new RuntimeException("讀取流關(guān)閉失敗");  
        }  
        //關(guān)閉流資源  
        finally  
        {  
            try  
            {  
                if(bufw!=null)  
                    bufw.close();  
            }  
            catch (IOException e)  
            {  
                throw new RuntimeException("寫入流關(guān)閉失敗");  
            }  
        }  
    }  
}  
  
class Demo  
{  
    public static void main(String[] args)   
    {  
        //反轉(zhuǎn)比較器,將成績從大到小排  
        Comparator<Student> cmp = Collections.reverseOrder();  
        //將錄入的學(xué)生信息存入集合  
        Set<Student> stus = StudentInfoTool.getStudents(cmp);  
        //將信息寫入指定文件中  
        StudentInfoTool.write2File(stus,"sudentinfo.txt");  
    }  
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容