IO流
IO流是Java處理設備之間的數據傳輸的重要方式,文件的讀取和寫入實現數據的獲取和存儲,按照不同的劃分標準流可以分爲輸入流和輸出流、字節流和字符流,節點流和處理流。系統的學習IO流的原理對於數據的寫入和寫出有很重要的作用。同時這也是菜鳥級別的程序員慢慢走向成熟的表現,慢慢理解編程的概念,伴隨着知識的積累,逐漸積攢開發經驗。
一、File 類的使用
① File類介紹:
② File類常用構造器:
③ 路徑分隔符:
④ File類常用方法
二、IO流原理及流的分類
① IO流的原理
1.1 I/O是Input/Output的縮寫,用於處理設備之間的數據傳輸。
1.2 Java程序中,對於數據的輸入/輸出操作以“流(stream)” 的方式進行。
1.3 java.io包下提供了各種“流”類和接口,用以獲取不同種類的數據。
② IO流的分類
③ IO流體系
三、IO流基類概述(inputStream、Reader、outputStream、Writer)
① InputStream & Reader
② OutputStream & Writer
四、節點流 (文件流) (FileInputStream、FileoutputStream、FileReader、FileWriter)
① 讀取文件
//1.建立一個流對象,將已存在的一個文件加載進流。
FileReader fr = new FileReader(new File(“Test.txt”));
//2.創建一個臨時存放數據的數組。
char[] ch = new char[1024];
//3.調用流對象的讀取方法將流中的數據讀入到數組中。
fr.read(ch);
//4. 關閉資源。
fr.close();
② 寫入文件
//1.創建流對象,建立數據存放文件
FileWriter fw = new FileWriter(new File(“Test.txt”));
//2.調用流對象的寫入方法,將數據寫入流
fw.write(“atguigu-songhongkang”);
//3.關閉流資源,並將流中的數據清空到文件中。
fw.close();
③ 節點流 ( 文件流 ) 注意點
五、緩衝流( BufferedInput(output)Stream、BufferedReader 、BufferedWriter )
代碼示例:
BufferedReader br = null;
BufferedWriter bw = null;
try {
// 創建緩衝流對象:它是處理流,是對節點流的包裝
br = new BufferedReader(new FileReader("d:\\IOTest\\source.txt"));
bw = new BufferedWriter(new FileWriter("d:\\IOTest\\dest.txt"));
String str;
while ((str = br.readLine()) != null) { // 一次讀取字符文本文件的一行字符
bw.write(str); // 一次寫入一行字符串
bw.newLine(); // 寫入行分隔符
}
bw.flush(); // 刷新緩衝區
} catch (IOException e) {
e.printStackTrace();
} finally {
// 關閉IO流對象
try {
if (bw != null) {
bw.close(); // 關閉過濾流時,會自動關閉它所包裝的底層節點流
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if (br != null) {
br.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
六、轉換流 (InputStreamReader、OutputStreamWriter)
代碼示例:
public void testMyInput() throws Exception {
FileInputStream fis = new FileInputStream("dbcp.txt");
FileOutputStream fos = new FileOutputStream("dbcp5.txt");
InputStreamReader isr = new InputStreamReader(fis, "GBK");
OutputStreamWriter osw = new OutputStreamWriter(fos, "GBK");
BufferedReader br = new BufferedReader(isr);
BufferedWriter bw = new BufferedWriter(osw);
String str = null;
while ((str = br.readLine()) != null) {
bw.write(str);
bw.newLine();
bw.flush();
}
bw.close();
br.close();
}
七、標準輸入、輸出流(System.in、System.out)
代碼示例:
System.out.println("請輸入信息(退出輸入e或exit):");
// 把"標準"輸入流(鍵盤輸入)這個字節流包裝成字符流,再包裝成緩衝流
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String s = null;
try {
while ((s = br.readLine()) != null) { // 讀取用戶輸入的一行數據 --> 阻塞程序
if ("e".equalsIgnoreCase(s) || "exit".equalsIgnoreCase(s)) {
System.out.println("安全退出!!");
break;
}
// 將讀取到的整行字符串轉成大寫輸出
System.out.println("-->:" + s.toUpperCase());
System.out.println("繼續輸入信息");
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (br != null) {
br.close(); // 關閉過濾流時,會自動關閉它包裝的底層節點流
}
} catch (IOException e) {
e.printStackTrace();
}
}
八、打印流( PrintStream、PrintWriter )
代碼示例:
PrintStream ps = null;
try {
FileOutputStream fos = new FileOutputStream(new File("D:\\IO\\text.txt"));
// 創建打印輸出流,設置爲自動刷新模式(寫入換行符或字節 '\n' 時都會刷新輸出緩衝區)
ps = new PrintStream(fos, true);
if (ps != null) {// 把標準輸出流(控制檯輸出)改成文件
System.setOut(ps);
}
for (int i = 0; i <= 255; i++) { // 輸出ASCII字符
System.out.print((char) i);
if (i % 50 == 0) { // 每50個數據一行
System.out.println(); // 換行
}
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
if (ps != null) {
ps.close();
}
}
九、數據流(DataInputStream、DataOutputStream)
代碼示例:
DataOutputStream dos = null;
try { // 創建連接到指定文件的數據輸出流對象
dos = new DataOutputStream(new FileOutputStream("destData.dat"));
dos.writeUTF("我愛北京天安門"); // 寫UTF字符串
dos.writeBoolean(false); // 寫入布爾值
dos.writeLong(1234567890L); // 寫入長整數
System.out.println("寫文件成功!");
} catch (IOException e) {
e.printStackTrace();
} finally { // 關閉流對象
try {
if (dos != null) {
// 關閉過濾流時,會自動關閉它包裝的底層節點流
dos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
DataInputStream dis = null;
try {
dis = new DataInputStream(new FileInputStream("destData.dat"));
String info = dis.readUTF();
boolean flag = dis.readBoolean();
long time = dis.readLong();
System.out.println(info);
System.out.println(flag);
System.out.println(time);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (dis != null) {
try {
dis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
十、對象流( ObjectInputStream、OjbectOutputSteam )
序列化和反序列化實例:
//序列化:將對象寫入到磁盤或者進行網絡傳輸。
//要求對象必須實現序列化
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(“data.txt"));
Person p = new Person("韓梅梅", 18, "中華大街", new Pet());
oos.writeObject(p);
oos.flush();
oos.close();
//反序列化:將磁盤中的對象數據源讀出。
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(“data.txt"));
Person p1 = (Person)ois.readObject();
System.out.println(p1.toString());
ois.close();
面試題: 談談你對java.io.Serializable接口的理解,我們知道它用於序列化,是空方法接口,還有其它認識嗎?
答:
① 實現了Serializable接口的對象,可將它們轉換成一系列字節,並可在以後完全恢復回原來的樣子。這一過程亦可通過網絡進行。這意味着序列化機制能自動補償操作系統間的差異。換句話說,可以先在Windows機器上創建一個對象,對其序列化,然後通過網絡發給一臺Unix機器,然後在那裏準確無誤地重新“裝配”。不必關心數據在不同機器上如何表示,也不必關心字節的順序或者其他任何細節。
② 由於大部分作爲參數的類如String、Integer等都實現了java.io.Serializable的接口,也可以利用多態的性質,作爲參數使接口更靈活。
十一、隨機存取文件流(RandomAccessFile)
RandomAccessFile使用:
- RandomAccessFile直接繼承於java.lang.Object類,實現了DataInput和DataOutput接口。
- RandomAccessFile既可以作爲一個輸入流,又可以作爲一個輸出流。
- 如果RandomAccessFile作爲輸出流時,寫出到的文件如果不存在,則在執行過程中自動創建。如果寫出到的文件存在,則會對原有文件內容進行覆蓋。(默認情況下,從頭覆蓋)
- 可以通過相關的操作,實現RandomAccessFile“插入”數據的效果。
實例1: 讀取文件內容
RandomAccessFile raf = new RandomAccessFile(“test.txt”, “rw”);
raf.seek(5);
byte [] b = new byte[1024];
int off = 0;
int len = 5;
raf.read(b, off, len);
String str = new String(b, 0, len);
System.out.println(str);
raf.close();
實例2: 寫入文件內容
RandomAccessFile raf = new RandomAccessFile("test.txt", "rw");
raf.seek(5);
//先讀出來
String temp = raf.readLine();
raf.seek(5);
raf.write("xyz".getBytes());
raf.write(temp.getBytes());
raf.close();
實例3: 使用RandomAccessFile實現數據的插入效果
@Test
public void test3() throws IOException {
RandomAccessFile raf1 = new RandomAccessFile("hello.txt","rw");
raf1.seek(3);//將指針調到角標爲3的位置
//保存指針3後面的所有數據到StringBuilder中
StringBuilder builder = new StringBuilder((int) new File("hello.txt").length());
byte[] buffer = new byte[20];
int len;
while((len = raf1.read(buffer)) != -1){
builder.append(new String(buffer,0,len)) ;
}
//調回指針,寫入“xyz”
raf1.seek(3);
raf1.write("xyz".getBytes());
//將StringBuilder中的數據寫入到文件中
raf1.write(builder.toString().getBytes());
raf1.close();
}
十二、NIO.2中Path、Paths、Files類的使用
① Java NIO 概述
② Path、Paths和Files核心API
③ Path接口常用方法
④ Files類常用方法
實例一:Path常用方法實例
//Path中的常用方法
@Test
public void test2() {
//實例化Path
Path path1 = Paths.get("d:\\", "nio\\nio1\\nio2\\hello.txt");
Path path2 = Paths.get("hello.txt");
// String toString() : 返回調用 Path 對象的字符串表示形式
System.out.println(path1);
// boolean startsWith(String path) : 判斷是否以 path 路徑開始
System.out.println(path1.startsWith("d:\\nio"));
// boolean endsWith(String path) : 判斷是否以 path 路徑結束
System.out.println(path1.endsWith("hello.txt"));
// boolean isAbsolute() : 判斷是否是絕對路徑
System.out.println(path1.isAbsolute() + "~");
System.out.println(path2.isAbsolute() + "~");
// Path getParent() :返回Path對象包含整個路徑,不包含 Path 對象指定的文件路徑
System.out.println(path1.getParent());
System.out.println(path2.getParent());
// Path getRoot() :返回調用 Path 對象的根路徑
System.out.println(path1.getRoot());
System.out.println(path2.getRoot());
// Path getFileName() : 返回與調用 Path 對象關聯的文件名
System.out.println(path1.getFileName() + "~");
System.out.println(path2.getFileName() + "~");
// int getNameCount() : 返回Path 根目錄後面元素的數量
// Path getName(int idx) : 返回指定索引位置 idx 的路徑名稱
for (int i = 0; i < path1.getNameCount(); i++) {
System.out.println(path1.getName(i) + "*****");
}
// Path toAbsolutePath() : 作爲絕對路徑返回調用 Path 對象
System.out.println(path1.toAbsolutePath());
System.out.println(path2.toAbsolutePath());
// Path resolve(Path p) :合併兩個路徑,返回合併後的路徑對應的Path對象
Path path3 = Paths.get("d:\\", "nio");
Path path4 = Paths.get("nioo\\hi.txt");
path3 = path3.resolve(path4);
System.out.println(path3);
// File toFile(): 將Path轉化爲File類的對象
File file = path1.toFile();//Path--->File的轉換
Path newPath = file.toPath();//File--->Path的轉換
}
附錄1: FIles工具類的使用
@Test
public void test1() throws IOException{
Path path1 = Paths.get("d:\\nio", "hello.txt");
Path path2 = Paths.get("atguigu.txt");
// Path copy(Path src, Path dest, CopyOption … how) : 文件的複製
//要想複製成功,要求path1對應的物理上的文件存在。path1對應的文件沒有要求。
// Files.copy(path1, path2, StandardCopyOption.REPLACE_EXISTING);
// Path createDirectory(Path path, FileAttribute<?> … attr) : 創建一個目錄
//要想執行成功,要求path對應的物理上的文件目錄不存在。一旦存在,拋出異常。
Path path3 = Paths.get("d:\\nio\\nio1");
// Files.createDirectory(path3);
// Path createFile(Path path, FileAttribute<?> … arr) : 創建一個文件
//要想執行成功,要求path對應的物理上的文件不存在。一旦存在,拋出異常。
Path path4 = Paths.get("d:\\nio\\hi.txt");
// Files.createFile(path4);
// void delete(Path path) : 刪除一個文件/目錄,如果不存在,執行報錯
// Files.delete(path4);
// void deleteIfExists(Path path) : Path對應的文件/目錄如果存在,執行刪除.如果不存在,正常執行結束
Files.deleteIfExists(path3);
// Path move(Path src, Path dest, CopyOption…how) : 將 src 移動到 dest 位置
//要想執行成功,src對應的物理上的文件需要存在,dest對應的文件沒有要求。
// Files.move(path1, path2, StandardCopyOption.ATOMIC_MOVE);
// long size(Path path) : 返回 path 指定文件的大小
long size = Files.size(path2);
System.out.println(size);
}
@Test
public void test2() throws IOException{
Path path1 = Paths.get("d:\\nio", "hello.txt");
Path path2 = Paths.get("atguigu.txt");
// boolean exists(Path path, LinkOption … opts) : 判斷文件是否存在
System.out.println(Files.exists(path2, LinkOption.NOFOLLOW_LINKS));
// boolean isDirectory(Path path, LinkOption … opts) : 判斷是否是目錄
//不要求此path對應的物理文件存在。
System.out.println(Files.isDirectory(path1, LinkOption.NOFOLLOW_LINKS));
// boolean isRegularFile(Path path, LinkOption … opts) : 判斷是否是文件
// boolean isHidden(Path path) : 判斷是否是隱藏文件
//要求此path對應的物理上的文件需要存在。纔可判斷是否隱藏。否則,拋異常。
// System.out.println(Files.isHidden(path1));
// boolean isReadable(Path path) : 判斷文件是否可讀
System.out.println(Files.isReadable(path1));
// boolean isWritable(Path path) : 判斷文件是否可寫
System.out.println(Files.isWritable(path1));
// boolean notExists(Path path, LinkOption … opts) : 判斷文件是否不存在
System.out.println(Files.notExists(path1, LinkOption.NOFOLLOW_LINKS));
}
/**
* StandardOpenOption.READ:表示對應的Channel是可讀的。
* StandardOpenOption.WRITE:表示對應的Channel是可寫的。
* StandardOpenOption.CREATE:如果要寫出的文件不存在,則創建。如果存在,忽略
* StandardOpenOption.CREATE_NEW:如果要寫出的文件不存在,則創建。如果存在,拋異常
*
*/
@Test
public void test3() throws IOException{
Path path1 = Paths.get("d:\\nio", "hello.txt");
// InputStream newInputStream(Path path, OpenOption…how):獲取 InputStream 對象
InputStream inputStream = Files.newInputStream(path1, StandardOpenOption.READ);
// OutputStream newOutputStream(Path path, OpenOption…how) : 獲取 OutputStream 對象
OutputStream outputStream = Files.newOutputStream(path1, StandardOpenOption.WRITE,StandardOpenOption.CREATE);
// SeekableByteChannel newByteChannel(Path path, OpenOption…how) : 獲取與指定文件的連接,how 指定打開方式。
SeekableByteChannel channel = Files.newByteChannel(path1, StandardOpenOption.READ,StandardOpenOption.WRITE,StandardOpenOption.CREATE);
// DirectoryStream<Path> newDirectoryStream(Path path) : 打開 path 指定的目錄
Path path2 = Paths.get("e:\\teach");
DirectoryStream<Path> directoryStream = Files.newDirectoryStream(path2);
Iterator<Path> iterator = directoryStream.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
}
附錄2: FileUtils類
1.//拷貝文件 --這裏會覆蓋--而非追加
File src = new File("D:\\workspace\\j2ee\\mycommon\\src\\com\\commons\\io\\abc.txt");
File dest = new File("D:\\workspace\\j2ee\\mycommon\\src\\com\\commons\\io\\abcd.txt");
FileUtils.copyFile(src, dest);
2.//拷貝文件到某一路徑
File src = new File("D:\\workspace\\j2ee\\mycommon\\src\\com\\commons\\io\\abcd.txt");
File dir = new File("D:\\");
FileUtils.copyFileToDirectory(src, dir);
3.//寫字符串到一個文件--此種爲覆蓋的方法
String string = "Blah blah blah";
File dest = new File("D:\\workspace\\j2ee\\mycommon\\src\\com\\commons\\io\\abcd.txt");
FileUtils.writeStringToFile(dest, string, "ISO-8859-1");
4. 刪除文件實例
File file = new File( ("D:\\workspace\\j2ee\\mycommon\\src\\com\\commons\\io\\abcd.txt") );
FileUtils.forceDelete(file);
5. 讀取文件每一行數據的功能
File file = new File("D:\\workspace\\j2ee\\mycommon\\src\\com\\commons\\io\\abc.txt");
List lines = FileUtils.readLines(file, "UTF-8");
for(int i=0;i<lines.size();i++) {
System.out.println(lines.get(i));
}```
##IOUtils類
IOUtils主要提供更便捷的操作流的方法
主要方法
toXxx/read- these methods read data from a stream
write -these methods write data to a stream
copy -these methods copy all the data from one stream to another
contentEquals- these methods compare the content of two streams
```java
1.比較兩個輸入流的內容是否相等
IOUtils.contentEquals(InputStream input1, InputStream input2)
(URLurl = new URL(url);
InputStream is = url.openStream();ur的內容讀出來)
InputStream in = new URL("http://www.apache.org").openStream();
InputStream in2 = new URL("http://www.apache.org").openStream();
IOUtils.contentEquals(in, in2)
IOUtils.toString將緩衝區的內容以utf-8的編碼方式以字符串的形式輸出
2.將字節從 InputStream複製到OutputStream中
IOUtils.copy(InputStream input, OutputStream output)
ByteArrayOutputStream out = **new** ByteArrayOutputStream();
IOUtils.copy(in2, out);
3.返回一個裝有輸入字節行數的Iterator對象,使用特定的字符編碼(如果沒用聲明的話則用默認編碼)
IOUtils.lineIterator(InputStream input, Charset encoding)
InputStream in3 = **new** URL("http://www.apache.org").openStream();
try {
LineIterator it = IOUtils.lineIterator(in3, "UTF-8");
System.out.println(it.hasNext());
if (it.hasNext()) {
String line = it.nextLine();
//System.out.println(line);
}
} finally {
IOUtils.closeQuietly(in);
}
4、從輸入流中讀取字節(通常返回輸入流的字節數組的長度)
IOUtils.read(InputStream input, byte[] buffer)
InputStream in4 = new URL("http://www.apache.org").openStream();
byte[] buffer = new byte[100000];
System.out.println(IOUtils.read(in4, buffer));
5、獲得輸入流的內容放回一個List<String>類型的容器,每一行爲這個容器的一個入口,使用特定的字符集(如果爲空就使用默認的字符集)
IOUtils.readLines(InputStream input, Charset encoding)
InputStream in5 = new URL("http://www.apache.org").openStream();
List<String> list = IOUtils.readLines(in5, "UTF-8");
Iterator<String> iter = list.iterator();
while(iter.hasNext()){
String s = iter.next();
//System.out.println(s);
}
好了,JAVA IO 流就是這麼多的知識了,積少成多,我們首先要理解各自的基礎知識,厚積薄發,慢慢的弄懂其中的原理,終將可簡化開發,實現程序員之夢。