JAVA中文件的讀寫 I/O 輸入輸出流


主要內容

1.編碼問題

2.File類的使用

3.RandomAccessFile的使用

4.I/O 輸入輸出流

編碼問題:

 1 import java.io.UnsupportedEncodingException;
 2 
 3 public class 編碼問題 {
 4     public static void main(String[] args) {
 5         // 我們項目的默認編碼是GBK
 6         String s = "測試 ABC";
 7         byte[] byte1 = s.getBytes();// 轉換成的字節序列用的是項目默認的編碼gbk
 8         for (byte b : byte1) {
 9             // 1 byte = 8 位 //toHexString這個函數是把字節(轉換成了Int)以16進制的方式顯示
10             System.out.print(Integer.toHexString(b & 0xff) + " ");// &
11                                                                     // 0xff是為了把前面的24個0去掉只留下后八位
12         }
13 
14         try {
15             // 也可以轉換成指定的編碼
16             byte[] bytes1 = s.getBytes("gbk");
17             System.out.println(new String(bytes1));
18         } catch (UnsupportedEncodingException e) {
19             // TODO Auto-generated catch block
20             e.printStackTrace();
21         }
22         /**
23          * gbk編碼: 中文占用兩個字節,英文占用一個字節 utf-8編碼:中文占用三個字節,英文占用一個字節
24          * java是雙字節編碼,是utf-16be編碼 utf-16be編碼:中文占用兩個字節,英文占用兩個字節
25          * 當你的字節序列是某種編碼時,這個時候想把字節序列變成字符串,也需要用這種編碼方式,否則會出現亂碼
26          */
27         try {
28             byte[] byte2 = s.getBytes("utf-16be");
29             String s2 = new String(byte2, "utf-16be");
30             System.out.println(s2);
31         } catch (UnsupportedEncodingException e) {
32             // TODO Auto-generated catch block
33             e.printStackTrace();
34         }
35         /**
36          * 文本文件就是字節序列,可以是任意編碼的字節序列
37          * 如果我們在中文機器上直接創建文本文件,那么該文件只認識ANSI編碼(例如直接在電腦中創建文本文件)
38          */
39 
40         /**
41          * byte轉int的時候為什么非要先&0xff計算出來才是正確答案?
42          * 首先,java中的二進制采用的是補碼形式,並非原碼或反碼,這3個概念要搞清楚;
43          * 其次,byte占8位,int占32位,將byte強制轉換為int型時,如果沒有做 &
44          * 0xff運算,且byte對應的值為負數的話,就會對高位3個字節進行補位,這樣就有可能出現補位誤差的錯誤。
45          * 舉例來說,byte型的-1,其二進制(補碼)為11111111(即0xff),轉換成int型,值也應該為-1,但經過補位后,
46          * 得到的二進制為11111111111111111111111111111111(即0xffffffff),這就不是-1了,對吧?
47          * 而0xff默認是int型,所以,一個byte跟0xff相與,會先將那個byte轉化成int型運算,這樣,結果中的高位3個字節就總會被清0,
48          * 於是結果就是我們想要的了~
49          */
50 
51     }
52 
53 }
View Code

File類的使用:

JAVA.io.File類用於表示文件(目錄)
File類只用於表示文件( 目錄)的信息(名稱、大小等),不能用於文件內容的訪問
File類的常用API:
1.創建File對象:File file=new File(String path);注意:File.seperater();獲取系統分隔符,如:”\“.
2.boolean file.exists();是否存在.
3.file.mkdir();或者file.mkdirs();創建目錄或多級目錄。
4.file.isDirectory()或者file.isFile()判斷是否是目錄或者是否是文件。
5.file.delete();刪除文件或目錄。
6.file.createNewFile();創建新文件。
7.file.getName()獲取文件名稱或目錄絕對路徑。
8.file.getAbsolutePath()獲取絕對路徑。
9.file.getParent();獲取父級絕對路徑。
10.file.getSize();獲取文件大小。
11.file.getFormat();獲取文件格式名。
 1 import java.io.File;
 2 import java.io.IOException;
 3 
 4 public class FileDemo {
 5 
 6     /**
 7      * @param args
 8      */
 9     public static void main(String[] args) {
10         // 了解構造函數的情況 查幫助ALT+/
11         File file = new File("E:\\javaio\\imooc");
12         // 判斷文件/文件夾是否存在
13         // System.out.println(file.exists());
14         if (!file.exists())
15             file.mkdir(); // file.mkdirs()如果文件不存在,直接創建文件夾
16         // mkdir創建的一級目錄,如果需要創建多級目錄可以使用mkdirs()
17         else
18             file.delete();
19 
20         // 是否是一個目錄 如果是目錄返回true,如果不是目錄or目錄不存在返回的是false
21         System.out.println(file.isDirectory());
22         // 是否是一個文件
23         System.out.println(file.isFile());
24 
25         // File file2 = new File("e:\\javaio\\日記1.txt");
26         File file2 = new File("e:\\javaio", "日記1.txt");
27         if (!file2.exists())
28             try {
29                 file2.createNewFile();
30             } catch (IOException e) {
31                 // TODO Auto-generated catch block
32                 e.printStackTrace();
33             }
34         else
35             file2.delete();
36         // 常用的File對象的API
37         System.out.println(file);// file.toString()的內容
38         System.out.println(file.getAbsolutePath());
39         System.out.println(file.getName());
40         System.out.println(file2.getName());
41         System.out.println(file.getParent());
42         System.out.println(file2.getParent());
43         System.out.println(file.getParentFile().getAbsolutePath());
44     }
45 
46 }
FileDemo

遍歷目錄

 1 import java.io.File;
 2 import java.io.IOException;
 3 
 4 //列出File的一些常用操作比如過濾,遍歷等操作
 5 public class FileUtils {
 6     /**
 7      * 列出指定指定目錄下(包括其子目錄)的所有文件
 8      * 
 9      * @param dir
10      * @throws IOException
11      */
12     public static void listDirectory(File dir) throws IOException {
13         if (!dir.exists()) {
14             throw new IllegalArgumentException("目錄:" + dir + "不存在");
15         }
16         if (!dir.isDirectory()) {
17             throw new IllegalArgumentException(dir + "不是目錄");
18         }
19 
20         // String[] fileNames =
21         // dir.list();//返回的是字符串數組,list()方法用於列出當前目錄下的子目錄和文件,直接子的名稱,不包含子目錄下的內容
22         // for (String string : fileNames) {
23         // System.out.println(dir+"\\"+string);
24         // }
25         //
26         // 如果要遍歷子目錄下的內容就需要構造成File對象做遞歸操作,File提供了直接返回File對象的API
27         File[] files = dir.listFiles();// 返回的是直接子目錄(文件)的抽象
28         if (files != null && files.length > 0) {// 確定存在子目錄
29             for (File file : files) {
30                 if (file.isDirectory()) {
31                     // 遞歸
32                     listDirectory(file);
33                 } else {
34                     System.out.println(file);
35                 }
36             }
37         }
38     }
39 }
FileUtils

RandomAccessFile的使用

RandomAccessFile JAVA提供的對文件內容的訪問,既可以讀文件,也可以寫文件。
RandomAccessFile支持隨機訪問文件,可以訪問文件的任意位置
(1)JAVA文件模型
在硬盤上的文件是byte byte byte存儲的,是數據的集合
(2)打開文件
有兩種模式"rw"(讀寫) "r"(只讀)
RandomAccessFile raf = new RandomeAccessFile(file,"rw")
文件指針,打開文件時指針在開頭 pointer = 0;
(3) 寫方法
raf.write(int)--->只寫一個字節(后8位),同時指針指向下一個位置,准備再次寫入
提供和很多方法能夠一次讀寫一個基本類型的數據
(4)讀方法
int b = raf.read()--->讀一個字節
(5)文件讀寫完成以后一定要關閉(Oracle官方說明)
 1 import java.io.File;
 2 import java.io.IOException;
 3 import java.io.RandomAccessFile;
 4 import java.util.Arrays;
 5 
 6 public class RafDemo {
 7 
 8     /**
 9      * @param args
10      */
11     public static void main(String[] args) throws IOException {
12         File demo = new File("demo");
13         if (!demo.exists())
14             demo.mkdir();
15         File file = new File(demo, "raf.dat");
16         if (!file.exists())
17             file.createNewFile();
18 
19         RandomAccessFile raf = new RandomAccessFile(file, "rw");
20         // 指針的位置
21         System.out.println(raf.getFilePointer());
22 
23         raf.write('A');// 只寫了一個字節
24         System.out.println(raf.getFilePointer());
25         raf.write('B');
26 
27         int i = 0x7fffffff;
28         // 用write方法每次只能寫一個字節,如果要把i寫進去就得寫4次
29         raf.write(i >>> 24);// 高8位
30         raf.write(i >>> 16);
31         raf.write(i >>> 8);
32         raf.write(i);
33         System.out.println(raf.getFilePointer());
34 
35         // 可以直接寫一個int
36         raf.writeInt(i);
37 
38         String s = "中";
39         byte[] gbk = s.getBytes("gbk");
40         raf.write(gbk);
41         System.out.println(raf.length());
42 
43         // 讀文件,必須把指針移到頭部
44         raf.seek(0);
45         // 一次性讀取,把文件中的內容都讀到字節數組中
46         byte[] buf = new byte[(int) raf.length()];
47         raf.read(buf);
48 
49         System.out.println(Arrays.toString(buf));
50         for (byte b : buf) {
51             System.out.println(Integer.toHexString(b & 0xff) + " ");
52         }
53         raf.close();
54     }
55 
56 }
RafDemo
import java.io.IOException;
import java.io.RandomAccessFile;

public class RafReadDemo {

    /**
     * @param args
     */
    public static void main(String[] args) throws IOException {
        // TODO Auto-generated method stub
        RandomAccessFile raf = new RandomAccessFile("demo/raf.dat", "r");
        raf.seek(2);
        int i = 0;
        int b = raf.read();// 讀取到一個字節
        System.out.println(raf.getFilePointer());
        i = i | (b << 24);
        b = raf.read();
        i = i | (b << 16);
        b = raf.read();
        i = i | (b << 8);
        b = raf.read();
        i = i | b;
        System.out.println(Integer.toHexString(i));
        raf.seek(2);
        i = raf.readInt();
        System.out.println(Integer.toHexString(i));
        raf.close();
    }
}
RafReadDemo
序列化與基本類型序列化
1)將類型int 轉換成byte或將其他數據類型轉換成byte的過程叫序列化
數據---->n byte
2)反序列化
將n個byte 轉換成一個數據的過程
nbyte ---> 數據
3)RandomAccessFile提供基本類型的讀寫方法,可以將基本類型數據
序列化到文件或者將文件內容反序列化為數據
 1 import java.io.File;
 2 import java.io.IOException;
 3 import java.io.RandomAccessFile;
 4 public class RandomAccessFileSeriaDemo {
 5 
 6     /**
 7      * @param args
 8      */
 9     public static void main(String[] args) throws IOException {
10         // TODO Auto-generated method stub
11         File demo = new File("demo1");
12         if (!demo.exists())
13             demo.mkdir();
14         File file = new File(demo, "raf.dat");
15         if (!file.exists())
16             file.createNewFile();
17         // 打開文件,進行隨機讀寫
18         RandomAccessFile raf = new RandomAccessFile(file, "rw");
19         /* 序列化 */
20         int i = 0x7ffffff;
21         raf.write(i >>> 24);
22         raf.write(i >>> 16);
23         raf.write(i >>> 8);
24         raf.write(i);
25         System.out.println(raf.getFilePointer());
26 
27         /* 反序列化 */
28         raf.seek(0);
29         int b = raf.read();
30         i = i | (b << 24);
31         b = raf.read();
32         i = i | (b << 16);
33         b = raf.read();
34         i = i | (b << 8);
35         b = raf.read();
36         i = i | b;
37         System.out.println(Integer.toHexString(i));
38         raf.close();
39     }
40 }
41 
42 RandomAccessFileSeriaDemo
RandomAccessFileSeriaDemo

I/O 輸入輸出流

流的定義:

流就是程序和設備之間嫁接起來的一根用於數據傳輸的管道,這個管道上有很多按鈕,不同的按鈕可以實現不同的功能。

這根用於數據傳輸的管道就是流,流就是一根管道

輸入時,程序在源(文件,網絡,內存)上打開一個流,然后如圖一個一個順序讀。寫也一樣。

流的分類和使用:

四大基本抽象流,文件流,緩沖流,轉換流,數據流,Print流,Object流。

JAVA.io 包中定義了多個流類型(類或抽象類)來實現輸入/輸出功能;可以從不同角度對其進行分類:

*按數據流的方向不用可以分為輸入流和輸出流

*按處理數據單位不同可以分為字節流和字符流

*按照功能不同可以分為節點流和處理流

JAVA中所提供的的所有流類型位於包JAVA.io內,都分別繼承自以下四種抽象流類型:

節點流與處理流:

節點流可以從一個特定的數據源(節點)讀取數據(如:文件,內存)

處理流是“連接”在已存在的流(節點流或處理流)之上,通過對數據的處理為程序提供更為強大的讀寫功能。

節點流也叫原始流,處理流也叫包裹流。

 

流與類的關系:

如果一個類是用作設備和程序之間的數據傳輸,則這個類有一個新的名字叫做流

流一定是類,但類不一定是流

 

四大基本流的介紹

輸入流,輸出流,字節流,字符流

InputStream和OutputStream讀寫數據的單位是一個字節

Reader和Writer讀寫數據的單位是一個字符

在JAVA中一個字符占兩個字節

InputStream,OutputStream,Reader,Writer都是抽象類,或者說都是抽象流,通常我們使用的都是它們的子類,凡是以Stream結尾的都是字節流。

 

InputStream 流中的常用方法:

 

 

OutputStream 流中的常用方法:

 

Reader 流中的常用方法:

 

 Writer 流中的常用方法:

 

 

文件流

 文件流包括:

FileInputStream FileOutputStream --字節流

FileReader FileWriter --字符流

實例:讀取一個文件的內容並將其輸出到顯示器上,並統計讀取的字節個數

 1 /*
 2     利用FileReader流來讀取一個文件中的數據,並在顯示器上輸出!
 3 */
 4 
 5 import java.io.*;
 6 
 7 public class TestFileReader {
 8     public static void main(String[] args) {
 9         FileReader fr = null;
10 
11         try {
12             fr = new FileReader("C:\\Documents and Settings\\others\\桌面\\java\\TestFileReader.java");
13             int cnt = 0;
14             int ch;
15 
16             while (-1 != (ch = fr.read())) // 20行
17             {
18                 System.out.print((char) ch); // System.out.print(int ch);
19                                                 // 這是在顯示器上輸出ch的整數值,所以必須的進行類型轉化,我們需要輸出的是ch所代表的整數對應的字符
20                 ++cnt;
21             }
22 
23             System.out.printf("總共從TestFileReader.java文件中讀取了%d個字符", cnt);
24         } catch (FileNotFoundException e) {
25             System.out.println("找不到文件!");
26             System.exit(-1);
27         } catch (IOException e) {
28             System.out.println("文件讀取失敗!");
29             System.exit(-1);
30         }
31     }
32 }
View Code

 

FileInputStream的使用

 

FileReader的使用

字節流與字符流的區別:

FileInputStream 和FileOutputStream 可以完成所有格式文件的復制

FileReader和FileWriter只可以完成文本文件的復制,卻無法完成其他格式文件的復制

因為字節是不需要解碼和編碼的,將字節轉化為字符才存在解碼和編碼的問題

字節流可以從所有格式的設備中讀寫數據,但字符流只能從文本格式的設備中讀寫數據

實例:編程實現文件的復制

 1 /*
 2     利用FileInputStream 和 FileOutputStream 可以完成所有格式文件的賦值
 3     因為字節是不需要解碼和編碼的,將字節轉化為字符才存在解碼的問題
 4     本程序完成了音頻文件的復制
 5 */
 6 
 7 import java.io.*;
 8 
 9 public class TestFileInputStreamOutputStreamCopy {
10     public static void main(String[] args) {
11         FileInputStream fi = null;
12         FileOutputStream fo = null;
13 
14         try {
15             fi = new FileInputStream("E:\\綜藝\\歌曲\\卡農.mp3");
16             fo = new FileOutputStream("d:/share/Output.txt"); //使用播放器可正常播放該文件
17             int ch;
18 
19             while (-1 != (ch = fi.read())) {
20                 fo.write(ch);
21             }
22         } catch (FileNotFoundException e) {
23             System.out.println("文件沒有找到!");
24             System.exit(-1);
25         } catch (IOException e) {
26             System.out.println("文件讀寫錯誤!");
27             System.exit(-1);
28         } finally {
29             try {
30                 if (null != fi) {
31                     fi.close();
32                     fi = null;
33                 }
34                 if (null != fo) {
35                     fo.close();
36                     fo = null;
37                 }
38             } catch (Exception e) {
39                 e.printStackTrace();
40                 System.exit(-1);
41             }
42         }
43 
44         System.out.println("文件復制成功!");
45     }
46 }
TestFileInputStreamOutputStreamCopy
 1 /*
 2     本程序證明了 FileReader 和 FileWriter 只可以完成文本文件的復制,
 3             卻無法完成音頻格式文件的復制
 4 */
 5 
 6 import java.io.*;
 7 
 8 public class TestFileReaderWriterCopy {
 9     public static void main(String[] args) {
10         FileReader fi = null;
11         FileWriter fo = null;
12 
13         try {
14             fi = new FileReader("E:\\綜藝\\歌曲\\卡農.mp3");
15             fo = new FileWriter("d:/share/Output.txt"); // Output.txt使用播放器打開失敗!
16                                                         // 本程序證明了FileWriter 和
17                                                         // FileReader
18                                                         // 無法完成音頻文件的復制,實際上FileWriter
19                                                         // 和 FileReader
20                                                         // 只能完成文本文件的復制
21             int ch;
22 
23             while (-1 != (ch = fi.read())) {
24                 fo.write(ch);
25             }
26         } catch (FileNotFoundException e) {
27             System.out.println("文件沒有找到!");
28             System.exit(-1);
29         } catch (IOException e) {
30             System.out.println("文件讀寫錯誤!");
31             System.exit(-1);
32         } finally {
33             try {
34                 if (null != fi) {
35                     fi.close();
36                     fi = null;
37                 }
38                 if (null != fo) {
39                     fo.close();
40                     fo = null;
41                 }
42             } catch (Exception e) {
43                 e.printStackTrace();
44                 System.exit(-1);
45             }
46         }
47 
48         System.out.println("文件復制成功!");
49     }
50 }
TestFileReaderWriterCopy

 

緩沖流

緩沖流就是帶有緩沖區的輸入輸出流

緩沖流可以顯著的減少我們對IO訪問的次數,保護我們的硬盤

緩沖流本事就是處理流(包裹流),緩沖流必須得依附於節點流(原始流)

處理流包裹在原始節點流上的流,相當於包裹在管道上的管道

 

緩沖流要"套接"在相應的節點流之上,對讀寫的數據提供了緩沖的功能,提高了讀寫的效率,同時增加了一些新的方法。JAVA提供了四種緩沖流,其常用的構造方法為:

 

BufferedOutputStream 和 BufferedInputStream

BufferedOutputStream :帶有緩沖的輸出流,允許一次向硬盤寫入多個字節的數據。

BufferedInputStream:帶緩沖的輸入流,允許一次向程序中讀入多個字節的數據。

BufferedOutputStream 和 BufferedInputStream都是包裹流,必須依附於OutputStream和InputStream

例子:利用BufferedOutputStream 和 BufferedInputStream 完成大容量文件的復制,這遠比單純利用FileInputStream和FileOutputStream要快的多

 1 /*
 2     利用BufferedOutputStream 和 BufferedInputStream完成大容量文件的復制
 3     這遠比單純利用 FileInputStream  和 FileOutputStream 要快得多
 4 
 5     BufferedOutputStream 和 BufferedInputStream 都是包裹流,必須的依附於
 6     InputStream 和 OutputStream 
 7 */
 8 
 9 import java.io.*;
10 
11 public class TestBufferedInputStreamOutputStreamCopy {
12     public static void main(String[] args) {
13         BufferedOutputStream bos = null;
14         BufferedInputStream bis = null;
15 
16         try {
17             bos = new BufferedOutputStream(new FileOutputStream("e:/OutputView.txt")); // bos
18                                                                                         // 輸出流有個默認的緩沖區,大小為32個字節
19 
20             bis = new BufferedInputStream(new FileInputStream("c:\\[高清在線www.66ys.cn]海底總動員DVD中英字幕.rmvb")); // bis
21                                                                                                             // 輸入流有個默認的緩沖區,大小為32個字節
22             byte[] buf = new byte[1024];
23             int len = bis.read(buf, 0, 1024); // 一定要注意,這不是從buf中讀數據,而是從bis所關聯到的D:\\綜藝\\電影\\貓和老鼠\\CD4.rmvb文件中讀取數據,並將讀取的數據寫入bis自己的默認緩沖區中,然后再將緩沖區的內容寫入buf數組中,每次最多向buf數組中寫入1024個字節,返回實際寫入buf數組的字節個數,如果讀到了文件的末尾,無法再向buf數組中寫入數據,則返回-1
24             while (-1 != len) {
25                 bos.write(buf, 0, len); // 不是寫入buf數組,而是將buf數組中下標從0開始的到len-1為止的所有數據寫入bos所關聯到的"d:/share/OutputView.txt"文件中
26                 len = bis.read(buf); // bis.read(buf); 等價於 bis.read(buf, 0,
27                                         // buf.length);
28             }
29             bos.flush();
30             bis.close();
31             bos.close();
32         } catch (FileNotFoundException e) {
33             System.out.println("沒有找到文件!");
34             System.exit(-1);
35         } catch (IOException e) {
36             System.out.println("文件讀寫錯誤!");
37             System.exit(-1);
38         }
39 
40         System.out.println("文件復制成功!");
41     }
42 }
TestBufferedInputStreamOutputStreamCopy
 1 /*
 2     本程序讀寫速度要慢於  "TestBufferedInputStreamOutputStreamCopy.java" 程序
 3     即:
 4     利用BufferedOutputStream 和 BufferedInputStream完成大容量文件的復制
 5     這遠比單純利用 FileInputStream  和 FileOutputStream 要快得多
 6 
 7     BufferedOutputStream 和 BufferedInputStream 都是包裹流,必須的依附於
 8     OutputStream  和 OutputStream 
 9 */
10 
11 import java.io.*;
12 
13 public class TestBufferedInputStreamOutputStreamCopy_2 {
14     public static void main(String[] args) {
15         FileOutputStream bos = null;
16         FileInputStream bis = null;
17 
18         try {
19             bos = new FileOutputStream("e:/OutputView.txt");
20             bis = new FileInputStream("c:\\[高清在線www.66ys.cn]海底總動員DVD中英字幕.rmvb");
21 
22             byte[] buf = new byte[1024];
23             int len = bis.read(buf, 0, 1024);
24             while (-1 != len) {
25                 bos.write(buf, 0, len);
26                 len = bis.read(buf);
27             }
28             bos.flush();
29             bis.close();
30             bos.close();
31         } catch (FileNotFoundException e) {
32             System.out.println("沒有找到文件!");
33             System.exit(-1);
34         } catch (IOException e) {
35             System.out.println("文件讀寫錯誤!");
36             System.exit(-1);
37         }
38 
39         System.out.println("文件復制成功!");
40     }
41 }
TestBufferedInputStreamOutputStreamCopy_2

一定要注意,bis.read(buf,0,1024);這不是從buf中讀數據,而是從bis所關聯到的“D:\\綜藝\\電影\\貓和老鼠\\CD4.rmvb”文件中讀取數據,並將讀取的數據寫入bis自己的默認緩沖區中,然后再將緩沖區的內容寫入buf數組中,每次最多向buf數組中寫入1024個字節,返回實際寫入buf數組的字節個數,如果讀到了文件的末尾,無法再向buf數組中寫入數據,則返回-1

BufferedInputStream流中有public int read(byte[] b)方法用來把從當前流關聯到的設備中讀取出來的數據存入一個byte數組中

BufferedOutputStream 流中有public int write(byte[] b)方法用來把byte數組中的數據輸出來當前流所關聯到的設備中

如果我們希望用BufferedInputStream 和 BufferedOutputStream 完成“將一個設備中的數據導入另一個設備中”,我們就應該定義一個臨時的byte類型的數據,用這個臨時數組作為輸入流和輸出流進行交互的中轉樞紐。

 

BufferedReader 和 BufferedWriter

 

實例:利用BufferedReader 和 BufferedWriter完成文本文件的復制

 1 /*
 2     利用 BufferedReader 和 BufferedWriter 完成文本文件的復制
 3 */
 4 import java.io.*;
 5 
 6 public class TestBufferedReaderWriterCopy {
 7     public static void main(String[] args) {
 8         BufferedReader br = null;
 9         BufferedWriter bw = null;
10 
11         try {
12             br = new BufferedReader(
13                     new FileReader("C:\\Documents and Settings\\others\\桌面\\java\\TestBufferedReaderWriterCopy.java"));
14             bw = new BufferedWriter(new FileWriter("d:/share/Writer.txt"));
15             String str = null;
16 
17             while (null != (str = br.readLine())) // br.readLine()讀取一行字符,但會將讀取的換行符自動丟棄,即返回的String對象中並不包括換行符
18             {
19                 bw.write(str);
20                 bw.newLine(); // 寫入一個換行符 這行不能省
21             }
22             bw.flush();
23         } catch (FileNotFoundException e) {
24             e.printStackTrace();
25             System.exit(-1);
26         } catch (IOException e) {
27             e.printStackTrace();
28             System.exit(-1);
29         } finally {
30             try {
31                 bw.close();
32                 br.close();
33             } catch (IOException e) {
34                 e.printStackTrace();
35                 System.exit(-1);
36             }
37         }
38     }
39 }
TestBufferedReaderWriterCopy

 

數據流DataInputStream DataOutputStream

DataInputStream能夠以一種與機器無關的方式,直接從底層字節輸入流讀取JAVA基本類型和String類型的數據。常用方法包括:

DataInputStream 是包裹流,必須依附於InputStream

DataOutputStream能夠以一種機器無關的方式,直接將JAVA基本類型和String類型數據寫出到其他的字節輸出流。常用方法包括:

DataOutputStream 是包裹流,它必須依附於OutputStream

數據流實例:

編程實現將long類型數據寫入byte數組,然后再從byte數組中吧該數據讀出來{

  *這是Socket編程中經常要完成的功能。

  *因為網絡編程中經常要把數據存入byte數組中,然后把byte數組打包成數據包(DatagramPacket),再把數據包經過網絡傳輸到目的機,目的機再從byte數組中把原數值型數據還原回來。

}

本程序要使用到:

DataInputStream 

DataOutputStream

ByteArrayInputStream

ByteArrayOutputStream

 1 /*
 2     功能:把一個long類型的數據寫入byte數組中,然后再從byte數組中讀取出
 3           這個long類型的數據
 4           
 5           因為網絡編程中經常要把數值型數據存入byte數組中然后打包成
 6           DatagramPacket經過網絡傳輸到目的機,目的機再從byte數組中
 7           把原數值型數據還原回來
 8     
 9     目的: ByteArrayOutputStream   DataOutputStream  ByteInputStream DataInputStream 流的使用
10           記住: DataOutputStream流中的writeLong(long n)是把n變量在內存
11                   中的二進制代碼寫入該流所連接到的設備中
12                   
13     注意:查API文檔得知:
14             構造  ByteArrayOutputStream 對象時不需要也不能指定緩沖數組,因為緩沖數組默認已經內置好了         
15               構造  ByteArrayInputStream 對象時必須的指定緩沖數組是誰!
16 */
17 
18 import java.io.*;
19 
20 public class TestByteArrayOutputStream1
21 {
22     public static void main(String args[]) throws Exception
23     {
24         long n = 9876543210L;
25         ByteArrayOutputStream baos = new ByteArrayOutputStream();  //9行  API:"public ByteArrayOutputStream(): 創建一個新的 byte 數組輸出流。緩沖區的容量最初是 32 字節,如有必要可增加其大小。 "
26                                     //9行代碼一旦執行完畢,意味着兩點: 1、在內存中生成了一個大小為32個字節的byte數組   2、有一根叫做baos的管道已鏈接到了該byte數組中,並且可以通過這個管道向該byte數組中寫入數據
27                                     //雖然此時可以通過baos向baos所連接到的在內存中分配好的byte數組中寫入數據,但是ByteArrayOutputStream流並沒有提供可以直接把long類型數據直接寫入ByteArrayOutputStream流所連接到的byte數組中的方法, 簡單說我們沒法通過baos向baos所連接到的byte數組中寫入long類型的數據, 查API文檔可以發現: ByteArrayOutputStream流中並沒有類似writeLong()這樣的方法,但是DataOutputStream流中卻有writeLong() writeFloat()等方法
28         DataOutputStream dos = new DataOutputStream(baos);
29         
30         dos.writeLong(n);  //把n變量所代表的10000L在內存中的二進制代碼寫入dos所依附的baos管道所連接到的內存中的大小為32字節的byte數組中,由運行結果來看,這是二進制寫入,既不是把10000L轉化為字符'1' '0' '0' '0' '0'寫入byte數組中,而是把10000L在內存中的總共8個字節的二進制代碼寫入byte數組中
31         
32         dos.flush();
33         byte[] buf = baos.toByteArray();  //DataOutputStream 流中並沒有toByteArray()方法,但是ByteArrayOutputStream 流中卻有toByteArray()方法, 所以不可以把baos 改為dos,否則編譯時會出錯! ByteArrayOutputStream流中toByteArray()方法的含義,摘自API“創建一個新分配的 byte 數組。其大小是此輸出流的當前大小,並且緩沖區的有效內容已復制到該數組中”
34 
35         //利用ByteArrayInputStream 和 DataInputStream 可以從byte數組中得到原long類型的數值10000L
36         ByteArrayInputStream bais = new ByteArrayInputStream(buf);
37         DataInputStream dis = new DataInputStream(bais);
38         long l = dis.readLong();
39 
40         System.out.println("l = " + l);
41         dos.close();
42     }
43 }
44 /*
45     在JDK 1.6中的運行結果是:
46 ----------------
47 l = 9876543210
48 ----------------
49 */
TestByteArrayOutputStream1
 1 /*
 2     功能:
 3         將long類型數據寫入byte數組,然后在從byte數組中把該數據讀出來
 4 */
 5 
 6 import java.io.*;
 7 
 8 public class TestByteArrayOutputStream2
 9 {
10     public static void main(String[] args) throws Exception
11     {
12         long n = 1234567;
13         ByteArrayOutputStream baos = new ByteArrayOutputStream();
14         DataOutputStream dos = new DataOutputStream(baos);
15         dos.writeLong(n);
16         
17         byte[] buf = baos.toByteArray();
18         ByteArrayInputStream bis = new ByteArrayInputStream(buf);
19         DataInputStream dis = new DataInputStream(bis);
20         long n2 = dis.readLong();
21         System.out.println("n2 = " + n2);
22         
23         dos.close(); 
24         dis.close();
25     }
26 }
TestByteArrayOutputStream2

 

轉換流:OutputStreamWriter InputStreamReader

OutputStreamWriter 流是把OutputStream流 轉化成Writer流的流

InputStreamReader 流是把InputStream流轉化為Reader

OutputStreamWriter 和 InputStreamReader都是包裹流

實例:如何將鍵盤輸入的字符組成字符串直接賦給String對象。

 1 /*
 2     如何將鍵盤輸入的字符組成字符串直接賦給String 對象
 3     
 4     預備知識:
 5 --------------------------------
 6 Reader FileReader InputStream FileInputStream BufferedInputStream 
 7 流中都沒有 readLine 方法
 8     DataInputStream 流中有 readLine方法,但已經 被標記為過時
 9     BufferedReader 流中有readLine方法,並且該方法是可以正確被使用的
10 --------------------------------
11 */
12 
13 import java.io.*;
14 
15 public class TestStringInput
16 {
17     public static void main(String[] args) 
18     {
19         String str = null;
20         BufferedReader br = new BufferedReader (   //21行
21                     new InputStreamReader(System.in)
22                 ); //23行  查API:從21行到23行的代碼是不會拋出任何異常的
23                 
24         try
25         {
26             str = br.readLine(); //會拋出IOException異常
27         }
28         catch (IOException e)
29         {
30             e.printStackTrace();
31             System.exit(-1);
32         }    
33         
34         System.out.println("str = " + str);        
35         try
36         {
37             br.close(); //會拋出IOException異常
38         }
39         catch (IOException e)
40         {
41             e.printStackTrace();
42             System.exit(-1);
43         }        
44     }
45 }
46 /*
47     在JDK 1.6中的運行結果是:
48 --------------------------------
49 sadd行政村123Asd?asd撒旦
50 str = sadd行政村123Asd?asd撒旦
51 --------------------------------
52 */
TestStringInput

 

readLine()與回車符的問題:

 

Print流  PrintWriter PrintStream

Print 流只有輸出,沒有輸入

分類:

PrintWriter輸入字符

PrintStream輸出字符

 

PrintWriter在OutputStream基礎之上提供了增強的功能,既可以方便地輸出各種類型數據(而不僅限於byte型)的格式化表示形式。

PrintStream重載了print和println方法,用於各種不同類型數據的格式化輸出。

格式化輸出是指將一個數據用其字符串格式輸出。

 

DataOutputStream 中的 WriteXXX(data)方法是把data在內存中的二進制數據寫入文件

PrintStream 中的println(data)是該數據格式化后的字符串寫入文件

 1 /*
 2     DataOutputStream 中的 writeXXX(data)方法
 3  4     PrintStream 中的 println(data)的區別
 5     
 6     總結:
 7         DataOutputStream 中的 writeXXX(data)方法是把data在內存中的二進制數據寫入文件
 8         PrintStream 中的 println(data)寫出的是該數據的格式化后的字符串        
 9 */
10 
11 import java.io.*;
12 
13 public class TestPrintStream_1
14 {
15     public static void main(String[] args) throws Exception 
16     {
17         DataOutputStream dos = new DataOutputStream(new FileOutputStream("d:/share/kk.txt"));
18         dos.writeLong(12345);  //實際寫入文件的是00 00 00 00 00 00 30 39
19         dos.close();
20         System.out.printf("%#X\n", 12345);
21         
22         PrintStream ps = new PrintStream(new FileOutputStream("d:/share/kk2.txt"), true);
23         ps.println(12345);  //實際寫入文件的是'1' '2' '3' '4' '5'
24         ps.close();
25     }
26 }
TestPrintStream_1

 

PrintWriter 提供了PrintStream的所有打印方法, 其方法也從不拋出IOException。

與PrintStream的區別:

 

標准輸入輸出的重定向:

實例:編程實現將鍵盤輸入的數據輸入A文件中,如果輸入有誤,則把出錯信息輸出到B文件

 1 import java.io.*;
 2 
 3 public class TestSetSystemOut {
 4     public static void main(String[] args) {
 5         PrintStream ps_out = null;
 6 
 7         try {
 8             ps_out = new PrintStream(new FileOutputStream("d:/share/ww.txt"));
 9             System.setOut(ps_out); // 將System.out的值重新設置為ps_out,即System.out不在關聯到顯示器,而是關聯到"d:/share/ww.txt"文件
10             System.out.println(12); // 這實際上是把12輸出到了System.out所關聯的d:/share/ww.txt中
11             System.out.println(55.5); // 同上
12         } catch (Exception e) {
13             e.printStackTrace();
14         } finally {
15             try {
16                 ps_out.close();
17             } catch (Exception e) {
18                 e.printStackTrace();
19             }
20 
21         }
22     }
23 }
TestSetSystemOut
 1 /*
 2     功能: 將鍵盤輸入的數據輸入A文件中,如果輸入有誤,
 3            則把出錯信息輸出到B文件中
 4            
 5            標准輸入輸出流的重定向    
 6 */
 7 
 8 import java.io.*;
 9 import java.util.*;
10 
11 public class TestSetOutErr {
12     public static void main(String[] args) {
13         PrintStream psOut = null;
14         PrintStream psError = null;
15         Scanner sc = null;
16 
17         try {
18             psOut = new PrintStream("d:/Out.txt");
19             psError = new PrintStream("d:/error.txt");
20             sc = new Scanner(System.in);
21             int num;
22             System.setOut(psOut);
23             System.setErr(psError);
24 
25             while (true) {
26                 num = sc.nextInt();
27                 System.out.println(num);
28             }
29         } catch (Exception e) {
30             System.err.println("出錯的信息是:"); // 不可以寫成System.out.println("出錯的信息是:");
31             e.printStackTrace(); // e.printStackTrace(); 默認是輸出到System.err所關聯的設備中
32         }
33     }
34 }
TestSetOutErr

 

對象的序列化 ObjectOutputStream ObjectInputStream
所謂序列化是指:把一個Object對象直接轉化為字節流,然后把這個字節流直接寫入本地硬盤或網絡中
序列化流(ObjectOutputStream)----writeObject
反序列化流(ObjectInputStream)---readObject
如果想把某個對象序列化,則必須實現Serializable接口
transient關鍵字用用它修飾變量后該變量不會進行jvm默認的序列化
當我們要把一個對象在網絡上傳輸轉化成字節序列,我們有些變量沒必要使用,放在網絡上傳輸會浪費資源,這個時候我們就會用transient關鍵字
transient修飾的屬性我們可以自己完成序列化
transient關鍵字在有些情況下可以提高性能

實例:

 1 import java.io.*;
 2 
 3 public class TestObjectIO
 4 {
 5     public static void main(String[] args)
 6     {
 7         ObjectOutputStream oos = null;
 8         ObjectInputStream ois = null;
 9         Student ss = new Student("zhansan", 1000, 88.8f);  //注意88.8f不能改為88.8 
10         Student ss2 = null;    
11                 
12         try
13         {
14             FileOutputStream fos = new FileOutputStream("d:/share/java/ObjectOut.txt");
15             oos = new ObjectOutputStream(fos);
16             oos.writeObject(ss);
17             
18             ois = new ObjectInputStream(new FileInputStream("d:/share/java/ObjectOut.txt"));    
19             ss2 = (Student)ois.readObject();  //(Student)不能省   ois.readObject();如果ois中的某個成員是transient,則該成員是不會被讀取的,因為該成員不會被保存,何來讀取之說?!
20             
21             System.out.println("ss2.sname = " + ss2.sname);
22             System.out.println("ss2.sid = " + ss2.sid);
23             System.out.println("ss2.sscore = " + ss2.sscore);
24         }
25         catch (FileNotFoundException e)
26         {
27             System.out.println("文件沒有找到!");
28             System.exit(-1);
29         }
30         catch (Exception e)
31         {
32             e.printStackTrace();
33             System.exit(-1);
34         }
35         finally
36         {
37             try
38             {
39                 oos.close();
40                 ois.close();
41             }
42             catch (Exception e)
43             {
44                 e.printStackTrace();
45                 System.exit(-1);
46             }
47         }        
48     }
49 }
50 
51 class Student implements Serializable  //如果將implements Serializable 注釋掉,則程序編譯時就會報錯
52 {
53     public String sname = null;
54     public int sid = 0;
55     transient public float sscore = 0; //表示sscore成員不能被序列化,所謂不能被序列化就是指:“該成員調用ObjectOutputStream 的writeOnbject()時不會被保存,調用ObjectInputStream的readObject()方法時不會被讀取”
56     
57     public Student(String name, int id, float score)
58     {
59         this.sname = name;
60         this.sid = id;
61         this.sscore =  score;
62     }
63 }
TestObjectIO
 
分析ArrayList源碼中序列化和反序列化的問題
ArrayList的底層雖然是一個數組,但是這個數組不一定放滿,沒有放滿的數組元素是不需要進行序列化的,我們必須自己完成序列化,把有效元素一個一個自己完成序列化 ,反序列化也一樣,所以ArrayList源碼中的序列化和反序列化的作用就是把ArrayList中的有效元素進行序列化 無效元素不進行序列化,可以提高性能。
對ArrayList的源碼有個初步的了解,能夠進行序列化的優化問題
 
序列化中 子類和父類構造函數的調用問題
當一個子類的父類實現了序列化接口,子類可以直接進行序列化 ,子類序列化時會遞歸調用父類的構造方法。
對子類對象進行反序列化操作時,如果其父類沒有實現序列化接口,那么其父類的構造方法會被調用


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM