天天看點

Java學習筆記——Java語言基礎(二十)(随機通路流、序列化和反序列化、Properties類、SequenceInputStream、切割合并檔案練習)

一、随機通路流RandomAccessFile

RandomAccessFile

RandomAccessFile:最大的特點就是可讀可寫。

RandomAccessFile類不屬于流,是Object類的子類。但它融合了InputStream和OutputStream的功能,這個對象可以用來讀取資料也可以用來寫資料,可以操作任意類型的資料。

構造方法

RandomAccessFile(File file, String mode)  建立從中讀取和向其中寫入(可選)的随機通路檔案流,該檔案由 File 參數指定
RandomAccessFile(String name, String mode)  建立從中讀取和向其中寫入(可選)的随機通路檔案流,該檔案具有指定名稱。
           

第一個參數為檔案或者檔案的路徑名。建立從中讀取和向其中寫入(可選)的随機通路檔案流,該檔案由 File 參數指定

第二個參數為打開檔案的通路模式,其可以選擇的值如下:

值        		含意
  
"r" 		以隻讀方式打開。調用結果對象的任何 write 方法都将導緻抛出 IOException。  
"rw" 		打開以便讀取和寫入。如果該檔案尚不存在,則嘗試建立該檔案。  
"rws" 		打開以便讀取和寫入,對于 "rw",還要求對檔案的内容或中繼資料的每個更新都同步寫入到底層儲存設備。  
"rwd"   	打開以便讀取和寫入,對于 "rw",還要求對檔案内容的每個更新都同步寫入到底層儲存設備。  

           

寫入資料

可以寫入指定類型的資料

public class Demo01 {
    public static void main(String[] args) throws IOException {
        RandomAccessFile rw = new RandomAccessFile("random.txt", "rw");
        rw.writeInt(1000);
        rw.writeBoolean(true);
        rw.writeDouble(3.1415);
        rw.writeUTF("你好");
    }
}
           

讀取資料

讀取上面寫入的資料,**注意:**讀取順序應當相同。

我們可以通過getFilePointer方法擷取檔案指針,并且可以通過seek方法設定檔案指針

public class Demo02 {
    public static void main(String[] args) throws IOException {
        RandomAccessFile rw = new RandomAccessFile("random.txt", "rw");
        //通過getFilePointer方法擷取檔案指針 指針位置與所存放資料類型占得位元組數有關
        int i = rw.readInt();
        long filePointer = rw.getFilePointer();
        System.out.println("檔案指針的位置:"+filePointer);//4
        boolean b = rw.readBoolean();
        filePointer = rw.getFilePointer();
        System.out.println("檔案指針的位置:"+filePointer);//5
        double v = rw.readDouble();
        filePointer = rw.getFilePointer();
        System.out.println("檔案指針的位置:"+filePointer);//13
        String s = rw.readUTF();
        filePointer = rw.getFilePointer();
        System.out.println("檔案指針的位置:"+filePointer);//24
        System.out.println(i);//1000
        System.out.println(b);//true
        System.out.println(v);//3.1415
        System.out.println(s);//你好
        //可以通過seek方法設定檔案指針 重新讀取字元串,将指針設定為字元串前面資料類型的指針位
        rw.seek(13);
        String s1 = rw.readUTF();
        System.out.println(s1);//你好;
    }
}
           

這裡面,使用編碼為UTF-8,一個漢字為三個位元組,但移動指針的位數為8,這是為什麼?

當在調用readUTF()方法時,會把兩個位元組寫入檔案中,移動的位數是實際的位元組數,而不是字元串的長度。在該長度之後,按順序輸出該字元串的每個字元,并對每個字元使用 UTF-8 修改版編碼。

使用RandomAccessFile斷點下載下傳

public class downLoad {
    public static void main(String[] args) throws IOException {
        RandomAccessFile read = new RandomAccessFile(new File("mixMusic.java"), "rw");
        RandomAccessFile write = new RandomAccessFile(new File("mixMusicdowmLoad.java"), "rw");
        /*
        read.seek();
        wirte.seek();
        設定暫停位置的指針
         */
        int lem=0;
        byte[] bytes=new byte[1];
        while ((lem=read.read(bytes))!=-1){
            write.write(bytes,0,lem);
            //暫停後,擷取檔案的指針位置,把這個檔案的指針位置,存到文本檔案中,下次使用者開始複制的時候,将指針設定到在這個位置開始複制
        }
        write.close();
        read.close();
    }
}
           

具體代碼,有待完善。

二、序列化和反序列化

Java 提供了一種對象序列化的機制。用一個位元組序列可以表示一個對象,該位元組序列包含該 對象的資料 、 對象的類型 和 對象中存儲的屬性 等資訊。位元組序列寫出到檔案之後,相當于檔案中持久儲存了一個對象的資訊。反之,該位元組序列還可以從檔案中讀取回來,重構對象,對它進行反序列化。 對象的資料 、 對象的類型 和 對象中存儲的資料 資訊,都可以用來在記憶體中建立對象。

簡單來說:就是序列化就是把對象通過流的方式存儲到檔案中。反之就是将檔案中存儲的對象以流的形式還原成對象

注意:對象 要重寫Serializable 接口才能被序列化

序列化流:ObjectOutputStream

反序列化流:ObjectInputStream

當一個類沒有實作Serializable接口時,序列化和反序列化的時候,會抛出NotSerializableException沒有序列化異常。通過實作接口啟動序列化的功能,未實作接口的無法進行序列化功能S。erializable接口叫做标記型接口。會給類添加一個标記

public class MyTest {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        writeObj();
        //讀取檔案中的資料
        ObjectInputStream is = new ObjectInputStream(new FileInputStream("student.txt"));
        //類型轉換
        Student s1 = (Student) is.readObject();
        Student s2 = (Student) is.readObject();
        Student s3 = (Student) is.readObject();
        System.out.println(s1);
        System.out.println(s2);
        System.out.println(s3);
        is.close();
    }
    private static void writeObj() throws IOException {
        ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream("student.txt"));
        Student s1 = new Student("張三", 23);
        Student s2 = new Student("李四", 24);
        Student s3 = new Student("王五", 25);
        /*将對象寫入到文本檔案中儲存起來。NotSerializableException 不能序列化的異常。*/
        os.writeObject(s1);
        os.writeObject(s2);
        os.writeObject(s3);
        os.close();
    }
}
class Student implements Serializable {
    private static final long serialVersionUID = -903491141342695531L;
    private String name;
    //transient private int age; //  transient 阻止這個屬性被序列化
    private int age;
    public Student() {
    }
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
    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;
    }
    @Override
    public String toString() {
        return "Student{" + "name='" + name + '\'' + ", age=" + age + '}';
    }
}

           

當我們在序列化之後,修改一些代碼。然後進行反序列化時,會抛出InvalidClassException異常()

**原因:**完成序列化以後,序列化檔案中還存在一個标記,然後在進行反序列化的時候,會驗證這個标記和序列化前的标記是否一緻,如果一緻就正常進行反序列化,如果不一緻就報錯了. 而現在我們把這個類做了修改,将相當于更改了标記,而導緻這兩個标記不一緻,就報錯了.

編譯完成之後形成的class檔案,添加一個序列号。在修改代碼之後,不進行序列化的情況下,直接進行反序列化會抛InvalidClassException異常()。我們可以自己加上一個序列号,保障不會發生改變 private static final long serialVersionUID=10l;。值可以任意給定。

另一種存取的方式:

将該類對象放入一個ArrayList集合中,然後對ArrayList集合進行序列化

public class MyTest2 {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        writeObj();
        //讀取檔案中的資料
        ObjectInputStream is = new ObjectInputStream(new FileInputStream("student.txt"));
        //類型轉換
        ArrayList<Student> list = (ArrayList<Student>) is.readObject();
        for (Student student : list) {
            System.out.println(student);
        }
        is.close();
    }
    private static void writeObj() throws IOException {
        ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream("student.txt"));
        Student s1 = new Student("張三", 23);
        Student s2 = new Student("李四", 24);
        Student s3 = new Student("王五", 25);
        //建立一個集合
        ArrayList<Student> list= new ArrayList<>();
        list.add(s1);
        list.add(s2);
        list.add(s3);
        //ArrayList實作了Serializable,是以可以被序列化
        os.writeObject(list);
    }
}
           

transient關鍵字:阻止該這個屬性被序列化。存儲檔案時不會發生改變

static修飾的不能夠被序列化

三、Properties類

Properties 類繼承Hashtable。表示了一個持久的屬性集。可儲存在流中或從流中加載。屬性清單中每個鍵和值都是一個字元串

3.1 Properties作為Map集合的使用

public class Demo01 {
    public static void main(String[] args) {
        Properties properties = new Properties();
       /* 父類方法
        properties.put("username","張三");
        properties.put("password","123456");
        String username = (String) properties.get("username");
        System.out.println(username);*/
       //特有方法
        properties.setProperty("username", "張三");
        properties.setProperty("password", "123456");
        //get方法1 通過鍵擷取值
        String username = properties.getProperty("username");
        System.out.println(username);
        //get方法2 一個預設值,如果通過鍵,沒有擷取到值,就傳回預設值
        String username2 = properties.getProperty("username", "李四");
        System.out.println(username2);//預設值為張三
    }
}

           

3.2Properties的load()和store()功能

public class Demo02 {
    public static void main(String[] args) throws IOException {
        Properties properties = new Properties();
        properties.setProperty("username", "張三");
        properties.setProperty("password", "123456");
        //使用集合的store方法,可以将集合中的鍵值存到檔案中。鍵值之間用"="連接配接
        //第二個參數為提示
        properties.store(new FileWriter("user.properties"),null);
        //使用幾個的load方法,可以将檔案中的鍵值資料讀取回集合
        //對于讀取的檔案有要求:1.鍵值之間使用"="連接配接。2.檔案的字尾名是 .properties
        Properties properties2 = new Properties();
        properties2.load(new FileReader("user.properties"));
        //輸出集合
        System.out.println(properties2);
    }
}
           

properties檔案稱為配置檔案

3.3 Properties練習

判斷Properties集合中是否存在鍵,如果存在,就修改該鍵的值

public class Test01 {
    public static void main(String[] args) throws IOException {
        Properties properties = new Properties();
        properties.load(new FileReader("user.properties"));
        //判斷是否含有該鍵
        if (properties.containsKey("username")){
            properties.setProperty("username","李四");//鍵相同,值覆寫
            //重寫回檔案覆寫資料
            properties.store(new FileWriter("user.properties"), null);
            //列印回控制台
            properties.load(new FileReader("user.properties"));
            System.out.println(properties);
        }
    }
}
           

四、SequenceInputStream

SequenceInputStream :表示其他輸入流的邏輯串聯。它從輸入流的有序集合開始,并從第一個輸入流開始讀取,直到到達檔案末尾,接着從第二個輸入流讀取,依次類推,直到到達包含的最後一個輸入流的檔案末尾為止。

4.1 構造方法

SequenceInputStream(InputStream s1, InputStream s2)  通過記住這兩個參數來初始化新建立的 SequenceInputStream(将按順序讀取這兩個參數,先讀取 s1,然後讀取 s2),以提供從此 SequenceInputStream 讀取的位元組。
SequenceInputStream(Enumeration<? extends InputStream> e) 通過記住參數來初始化新建立的 SequenceInputStream,該參數必須是生成運作時類型為 InputStream 對象的 Enumeration 型參數。

           

第一種構造,将兩個輸入流作為參數進行傳遞,合并兩個文本檔案

public class Demo01 {
    public static void main(String[] args) throws IOException {
        //給定兩個檔案位元組輸入流FileInputStream
        FileInputStream fis1=new FileInputStream("C:\\Users\\asus\\Desktop\\許巍 - 藍蓮花.mp3");
        FileInputStream fis2=new FileInputStream("C:\\Users\\asus\\Desktop\\許巍歌曲合集.mp3");
        //使用SequenceInputStream(InputStream s1, InputStream s2)的構造 傳遞兩個流
        SequenceInputStream sequenceInputStream = new SequenceInputStream(fis1, fis2);
        //給定一個檔案位元組輸出流FileOutputStream
        FileOutputStream fos = new FileOutputStream("C:\\Users\\asus\\Desktop\\許巍歌曲大合集.mp3");
        //讀寫資料
        int len=0;
        byte[] bytes=new byte[1024];
        while ((len=sequenceInputStream.read(bytes))!=-1){
            fos.write(bytes,0,len);
        }
        fos.close();
        sequenceInputStream.close();
    }
}
           

第二種構造,有時不滿足于兩個文本檔案的合并,那麼可以使用Enumeration作為參數進行傳遞,合并多個文本檔案

public class Demo02 {
    public static void main(String[] args) throws IOException {
        FileInputStream fis1=new FileInputStream("C:\\Users\\asus\\Desktop\\許巍 - 藍蓮花.mp3");
        FileInputStream fis2=new FileInputStream("C:\\Users\\asus\\Desktop\\許巍歌曲合集.mp3");
        FileInputStream fis3=new FileInputStream("C:\\Users\\asus\\Desktop\\許巍歌曲大合集.mp3");
        //建立一個Vector集合 泛型FileInputStream
        Vector<FileInputStream> vector = new Vector<>();
        //添加檔案
        vector.add(fis1);
        vector.add(fis2);
        vector.add(fis3);
        //擷取Enumeration類型的對象
        Enumeration<FileInputStream> elements = vector.elements();
        //構造方法
        SequenceInputStream sequenceInputStream = new SequenceInputStream(elements);
        FileOutputStream out = new FileOutputStream("C:\\Users\\asus\\Desktop\\許巍.mp3");
        int len = 0;
        byte[] bytes = new byte[1024 * 8];

        while ((len = sequenceInputStream.read(bytes)) != -1) {
            out.write(bytes, 0, len);
            out.flush();
        }
        out.close();
        sequenceInputStream.close();
    }
}
           

4.2 SequenceInputStream練習

把一個xxxx.mp3檔案分割成多份 每份大小是 1M 然後再把分割後的小檔案,再合并成一首歌。

public class Day4Test2 {
    public static void main(String[] args) throws IOException {
        /*
        把一個xxxx.mp3檔案分割成多份 每份大小是 1M 然後再把分割後的小檔案,再合并成一首歌。
         */
        FileInputStream fis = new FileInputStream(new File("C:\\Users\\asus\\Desktop\\許巍 - 曾經的你.mp3"));
        qiegeMp3(fis);
        hebing();
    }
    private static void hebing() throws IOException {
        File targetFile = new File("C:\\Users\\asus\\Desktop\\mp32");
        //周遊存放切割歌曲的檔案夾
        File[] files = targetFile.listFiles();
        Vector<FileInputStream> vector=new Vector<>();
        for (File file : files) {
            FileInputStream fis = new FileInputStream(file);
            vector.add(fis);
        }
        //構造方法
        SequenceInputStream sequenceInputStream = new SequenceInputStream(vector.elements());
        FileOutputStream fos = new FileOutputStream("C:\\Users\\asus\\Desktop\\許巍 - 曾經的你hebing.mp3");
        //讀寫資料
        int len=0;
        byte[] bytes=new byte[1024];
        while ((len=sequenceInputStream.read(bytes))!=-1){
            fos.write(bytes,0,len);
        }
        fos.close();
        sequenceInputStream.close();
    }
		 private static void qiegeMp3(FileInputStream fis) throws IOException {
		   //目标檔案夾
        File targetFile = new File("C:\\Users\\asus\\Desktop\\mp32");
        if (!targetFile.exists()){
            targetFile.mkdirs();
        }
        //定義切割的大小 一次為1M
        byte[] bytes=new byte[1024*1024];
        int len=0;
        int j=1;
        while ((len=fis.read(bytes))!=-1){
            FileOutputStream fos = new FileOutputStream(new File(targetFile,(j++)+"曾經的你.mp3"));
            fos.write(bytes,0,len);
            fos.close();
        }
        fis.close();
    }
}

           

繼續閱讀