代碼演示需要提前在項目根目錄下創建一個 file.txt ,並在其中輸入一些信息
不習慣這樣看代碼的話,我把代碼上傳到了 github 上:https://github.com/Mr1wangjiabin/java-io
文章目錄
IO流的劃分
按數據來源或者操作對象劃分
按數據傳輸方式或者說是運輸方式劃分
文本、文件、文本文件
- 文本:Java 的文本是 16 位無符號整數,是字符的
unicode
編碼 - 文件:文件時
byte byte byte ...
的數據序列 - 文本文件:是文本按照某種編碼方式序列化爲
byte
的存儲結果
編碼
- GBK
- UTF-8
- UTF-16be
public class EncodeDemo {
public static void main(String[] args) throws Exception {
/**
* 文本文件,可以是任意編碼的字節序列
* 如果我們在中文機器上直接創建文本文件,那麼該文件只認識ANSI編碼
*
* 字節序列只能進行同種編碼間的傳輸,如果編碼方式不一致,會出現亂碼
*/
//UTF-8 輸出e6 b5 8b e8 af 95 41 42 43
//一個漢字 3 個字節,字母 1 個字節
String s = "測試ABC";
byte[] bytes1 = s.getBytes();//轉換成字節是項目默認的編碼 UTF-8
System.out.println("UTF-8編碼:");
for (byte b : bytes1) {
System.out.print(Integer.toHexString(b & 0xff) + " ");//將字節轉換爲 16進制 int型輸出
}
System.out.println();
//GBK輸出b2 e2 ca d4 41 42 43
//一個漢字 2 個字節,字母 1 個字節
System.out.println("GBK編碼:");
byte[] bytes2 = s.getBytes("gbk");
for (byte b : bytes2) {
System.out.print(Integer.toHexString(b & 0xff) + " ");
}
System.out.println();
//UTF-16be 輸出6d 4b 8b d5 0 41 0 42 0 43
//一個漢字 2 個字節,字母 2 個字節
System.out.println("UTF-16be編碼:");
byte[] bytes3 = s.getBytes("utf-16be");
for (byte b : bytes3) {
System.out.print(Integer.toHexString(b & 0xff) + " ");
}
//關閉流
raf.close
}
}
File
java.io.file
用來表示文件或者目錄File
類用於表示文件(目錄)的信息(名稱、大小等)。不能用於文件內容的訪問
File 類的基本API
/**
* File 的構造函數:
* File(filepath) filepath爲文件全目錄
* File(filepath,filepath1,filepath2...) filepath1爲filepath的子目錄
* File.separator 文件分隔符
* exists 驗證file是否存在
* mkdir 創建一個文件夾/目錄
* mkdir 創建多級目錄
* delete 刪除文件
* isDirectory 判斷file 是否是文件夾
* isFile 判斷file 是否是文件
* getAbsolutePath 獲取全路徑
* getName 獲取文件名稱
* createNewFile 創建一個新文件
*/
public class FileDemo {
public static void main(String[] args) throws IOException {
File file = new File("E:\\Project\\io\\test");
File file1 = new File("E:" + File.separator
+ "Project" + File.separator
+ "io"+ File.separator
+ "test.text");
if(!file.exists()){
file.mkdir();
}else {
file.delete();
}
System.out.println(file.isDirectory());//判斷file 是否是文件夾
System.out.println(file.isFile());//判斷file 是否是文件
System.out.println(file.getAbsolutePath());//獲取全路徑
System.out.println(file.getName());//獲取文件名稱
if (!file1.exists()){
file1.createNewFile();
}else {
file1.delete();
}
}
}
File 的 list 與 listFile
import java.io.File;
/**
* list 返回目錄/文件下的所有文件名
* listFiles 返回文件下的所有文件
*
* 練習,完成目錄下所有子目錄的查詢
*/
public class FileUtils {
public static void main(String[] args) throws IllegalAccessException {
File file = new File("E:/Project/io");
FileUtils.listFile(file);
}
public static void listFile(File file) throws IllegalAccessException {
//首先判斷文件是否存在以及文件是文件夾
if(!file.exists()){
throw new IllegalAccessException(file + " 不存在");
}
if (!file.isDirectory()){
throw new IllegalAccessException(file + " 不是一個文件夾");
}
File[] files = file.listFiles();
if (files.length > 0 && files != null){
for (File file1 : files) {
if (file1.isDirectory()){
FileUtils.listFile(file1);
}else {
System.out.println(file);
}
}
}
}
}
RandomAccessFile
- 即可以讀取文件內容,也可以向文件中寫入內容。但是和其他輸入/輸入流不同的是,程序可以直接跳到文件的任意位置來讀寫數據。
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.Arrays;
/**
** java 文件模型
* 在硬盤上是 byte byte 存儲的,是數據的集合
* 打開文件
* 有兩種模式:"rw"(讀寫) "r"(只讀)
* RandomAccessFile raf = new RandomAccessFile("rw")
* 文件指針,打開文件時,文件指針在開頭 pointer = 0
* 寫方法
* raf.writer() -->只寫一個字節(後8位)同時指針指向下一個位置,準備再次寫入
* 讀方法
* int b = raf.read() --> 讀一個字節
* 文件讀寫完成一定要關閉
*
* 其他API
* getFilePointer 獲取指針當前所在位置
*
*
*/
public class RandomAccessFileDmo {
public static void main(String[] args) throws IOException {
//創建文件
File file = new File("demo");
if(!file.exists()){
file.mkdir();
}
File file1 = new File(file, "raf.dat");
if(!file1.exists()){
file1.createNewFile();
}
//對文件的讀寫操作
RandomAccessFile raf = new RandomAccessFile(file1, "rw");
System.out.println("指針當前所在位置爲:" + raf.getFilePointer());//獲取指針當前所在位置
//寫入一個字節
//默認編碼UTF-8,一個漢字 3 個字節,一個字母 1 個字節
raf.write('A');
System.out.println("指針當前所在位置爲:" + raf.getFilePointer());//獲取指針當前所在位置
int i = 0x7fffffff;
//用write方法每次只能寫一個字節,如果要把i寫進去就得寫4次
raf.write((i >>> 24) & 0xff);//右移 24 位,並清空其餘的0值
raf.write((i >>> 16) & 0xff);
raf.write((i >>> 8) & 0xff);
raf.write((i) & 0xff);
System.out.println("指針當前所在位置爲:" + raf.getFilePointer());//獲取指針當前所在位置
//可以直接寫一個 int 型變量,底層實現和上面的方法一樣
raf.writeInt(i);
//寫一個漢字
String s = "中";
byte[] bytes = s.getBytes();
raf.write(bytes);
System.out.println("指針當前所在位置爲:" + raf.getFilePointer());//獲取指針當前所在位置
//讀文件,必須把指針移動到頭部
raf.seek(0);
//把文件中的內容都讀取到字節數組中
byte[] bytes1 = new byte[(int) raf.length()];
raf.read(bytes1);
System.out.println(Arrays.toString(bytes1));//將數組輸出
//將字節數組轉換爲字符串輸出
String s1 = new String(bytes1);
System.out.println(s1);
}
}
字節流
輸入輸出流
FileInputStream
- 實現了在文件上讀取數據
package byteStream;
import java.io.FileInputStream;
import java.io.IOException;
/**
* 讀取指定文件內容,按照16進制輸出到控制檯。並且每輸出10個byte換行
* 批量讀取
*/
public class FileInputStreamTest {
public static void main(String[] args) throws IOException {
printHex("file.txt");
}
public static void printHex(String filename) throws IOException{
FileInputStream fis = new FileInputStream(filename);
byte[] bytes = new byte[8 * 1024];
int b = 0;
int j = 1;
while ((b = fis.read(bytes,0,bytes.length)) != -1){
for (int i = 0; i < b; i++) {
System.out.print(Integer.toHexString(bytes[i] & 0xff) + " ");
if(((j++)%10)==0){
System.out.println();
}
}
}
fis.close();
}
}
FileOutputStream
package byteStream;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* 從文件中讀取數據
*/
public class FileOutPutStreamTest {
public static void main(String[] args) throws IOException {
FileOutputStream fos = new FileOutputStream("out.dat");
byte[] bytes = "你好".getBytes();
fos.write(bytes);
fos.close();
FileInputStreamTest.printHex("out.dat");
}
}
練習:實現文件的複製
package byteStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
/**
* 完成文件的複製
*/
public class FileCopyTest {
public static void main(String[] args) throws Exception {
fileCopy(new File("file.txt"),new File("fileout.txt"));
}
/**
* 文件複製
* 問什麼傳入參數不用文件名?
* 需要判斷源文件是否存在
* @param srcFile
* @param destFile
*/
public static void fileCopy(File srcFile,File destFile) throws Exception{
if(!srcFile.exists()){
throw new IllegalAccessException("文件" + srcFile + "不存在");
}
if(!srcFile.isFile()){
throw new IllegalAccessException("文件" + srcFile + "不是文件");
}
FileInputStream fis = new FileInputStream(srcFile);
FileOutputStream fos = new FileOutputStream(destFile);
byte[] bytes = new byte[8 * 1024];
int b = 0;
if((b = fis.read(bytes,0,bytes.length)) != -1){
fos.write(bytes,0,b);
fos.flush();
}
fis.close();
fos.close();
}
}
緩衝流 BufferedInputStream && BufferedOutputStream 進行文件複製
- 提供了緩衝區操作,提高的IO的性能
package byteStream;
import java.io.*;
public class BufferedTest {
public static void main(String[] args) throws Exception{
copyFile(new File("E:\\Project\\io\\src\\main\\java\\file\\FileDemo.java"),
new File("fileOut2.txt"));
}
public static void copyFile(File srcFile, File destFile) throws Exception{
if(!srcFile.exists()){
throw new IllegalAccessException("文件" + srcFile + "不存在");
}
if(!srcFile.isFile()){
throw new IllegalAccessException("文件" + srcFile + "不是文件");
}
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(srcFile));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destFile));
int c;
while ((c = bis.read())!= -1){
bos.write(c);
bos.flush();
}
bis.close();
bos.close();
}
}
字符流
- 有了字節流爲什麼還需要字符流?
字符流是由Java虛擬機將字節轉換得到的,問題就出在這個過程還算是非常耗時,並且,如果我們不知道編碼類型就很容易出現亂碼問題。所以,I/O 流就乾脆提供了一個直接操作字符的接口,方便我們平時對字符進行流操作。 - 字符的處理
一次處理一個字符,底層仍然是最基本的字節序列
InputStreamReader && OutputStreamWriter 實現文件複製
InputStreamReader
按照編碼將byte
流解析爲char
流OutputStreamWriter
默認項目的編碼,操作的時候要寫文件本身的編碼
package charStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
/**
* InputStreamReader && OutputStreamWriter 實現文件複製
*/
public class FileCopyTest {
public static void main(String[] args) throws Exception{
InputStreamReader isr = new InputStreamReader(new FileInputStream("file.txt"));
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("fileOut3.txt"));
char[] chars = new char[8 * 1024];
int c;
while ((c = isr.read(chars,0,chars.length)) != -1){
osw.write(chars,0,c);
osw.flush();
}
isr.close();
osw.close();
}
}
FileReader && FileWriter 實現文件複製
package charStream;
import java.io.FileReader;
import java.io.FileWriter;
/**
* InputStreamReader && OutputStreamWriter 實現文件複製
*/
public class FileCopyByFileTest {
public static void main(String[] args) throws Exception{
FileReader fr = new FileReader("file.txt");
FileWriter fw = new FileWriter("fileOut4.txt");
char[] chars = new char[8 * 1024];
int d;
while ((d = fr.read(chars,0,chars.length)) != -1){
fw.write(chars,0,d);
}
fr.close();
fw.close();
}
}
BufferedReader && BufferedWriter/PrintWriter 實現文件複製
PrintWriter
用起來比BufferedWriter
方便不少,因此我不再寫BufferedWriter
相關代碼,感興趣的自己查吧
package charStream;
import java.io.*;
public class FileCopyByBuffer {
public static void main(String[] args) throws Exception{
BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("file.txt")));
PrintWriter pw = new PrintWriter(
new FileOutputStream("fileOut5.txt"),true);//在文件參數爲 OutputStream時,可以設置自動清空緩存
String line;
while ((line = br.readLine()) != null){
pw.println(line);
}
br.close();
pw.close();
}
}
序列化與反序列化
- 將對象轉換爲
byte
存儲稱爲序列化,反之爲反序列化 - 序列化流
ObjectOutputStream
方法writeObject
- 反序列化流
ObjectInputStream
方法readObject
- 只有實現了
Serializable
接口的類才能序列化 - 類中
transient
修飾的變量不會被 JVM 自動序列化,但可以手動,用以提高性能(這部分不懂,就簡單的提一下)
package SerializableTest;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class SerializableTest {
public static void main(String[] args) throws Exception{
//對象序列化
ObjectSerializable();
//對象反序列化
ObjectInSerializable();
}
/**
* 對象序列化
* @throws Exception
*/
public static void ObjectSerializable() throws Exception{
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("obj.dat"));
Student student = new Student(1L, "小王");
oos.writeObject(student);
oos.flush();
oos.close();
}
public static void ObjectInSerializable() throws Exception{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("obj.dat"));
Student student = (Student) ois.readObject();
System.out.println(student);
ois.close();
}
}