文章目錄
- Java IO之鍵盤錄入
- Java File對象的常用方法
- File 之文件遍歷與刪除
- Java Properties類
- 實際使用過程中流對象的選擇
- Print類詳解
- SequenceInputStream 序列流
- IO流之綜合操作——文件的切割與合併
- Java之序列流ObjectOutputStream/ObjectInputStream
- Java之隨機訪問文件—RandomAccessFile
- Java之管道流PipedInputStream/PipedOutputStream
- Java之數據流DataInputStream/DataOutputStream
- Java之字節數組流ByteArrayInputStream/ByteArrayOutputStream
Java IO之鍵盤錄入
// An highlighted block
io流是java中很重要的一個部分,不用掃描器進行鍵盤錄入
InputStream in = System.in; //其中System是系統類,成員in默認爲鍵盤錄入
try {
int len = 0;
StringBuilder sb = new StringBuilder(); //臨時緩衝區
while((len = in.read())!=-1){ //read爲阻塞方法如果沒有中斷標記無法停止
char ch = (char)len;
//下面語段代碼爲BufferReader 的readLine中的實現方法
if(ch == '\r'){
continue;
}
if(ch == '\n'){
System.out.println(sb);
sb.delete(0, sb.length());
}else{
sb.append(ch);
}
if((char)len=='0') {
break;
}
}
} catch (IOException e) {
e.printStackTrace();
}
/*有一種改進方法就是對InputStream 的對象進行裝飾,其中會用到轉換流進行字符流轉字節流:
InputStreamReader //字節流轉字符流的橋樑
改進如下:
*/
public static void main(String[] args) {
InputStream in = System.in;
try {
String str = null;
StringBuilder sb = new StringBuilder();
BufferedReader br = new BufferedReader(new InputStreamReader(in)); //不斷進行裝飾的過程
while ((str = br.readLine())!=null){
System.out.println(str);
}
} catch (IOException e) {
e.printStackTrace();
}
}
Java File對象的常用方法
判斷方法
函數名稱 | 詳細說明 |
---|---|
canExecute() | 是否是執行文件 |
canRead | 是否允許讀操作 |
canWrite() | 是否允許讀操作 |
exists() | 目錄或文件是否存在 |
isFile() | 是否爲文件 |
isDirectory() | 是否爲目錄 |
isHidden() | 是否爲隱藏文件 |
以上方法返回值都爲boolean類型
獲取文件信息方法
返回值類型 | 函數名稱 | 詳細說明 |
---|---|---|
String | getName() | 獲取文件或目錄名稱 |
File | getAbsoluteFile() | 獲取絕對路徑的File對象形式,如果沒有則爲NULL |
String | getAbsolutePath() | 返回絕對路徑名字符串,如果沒有則爲NULL |
String | getParent() | 返回父目錄,如沒有則返回 null。(構造器傳入的父目錄,不一定是絕對路徑) |
File | getParentFile() | 返回父目錄的文件對象,如沒有則返回 null |
String | getPath() | 獲取相對路徑 |
long | lastModified() | 返回文件最後一次被修改的時間(毫秒值,需要Date轉換) |
long | length() | 返回文件的長度(字節數) |
static File[] | listRoots() | Windows下返回列出所有盤符 |
long | getFreeSpace() | 返回指定盤符的剩餘容量字節數 |
文件列表與篩選
返回值類型 | 函數名稱 | 詳細說明 |
---|---|---|
String[] | list() | 列出當前目錄下所有的文件夾和文件(包含隱藏文件)的列表,(類似於ls命令),如果調用list方法的file對象不是目錄或是一個無法訪問的系統目錄則返回一個null,如果有目錄,但目錄下子目錄爲空,則返回一個長度爲0的String數組 |
File[] | listFiles() | 返回指定目錄中的文件和目錄對象 |
String[] | list(FilenameFilter filter) | 返回目錄中滿足過濾器的文件和目錄的名稱。 (根據文件名稱過濾) |
File[] | listFiles(FilenameFilter filter) | 返回目錄中滿足指定過濾器的文件和目錄的對象 |
File[] | listFiles(FileFilter filter) | 返回滿足指定過濾器的目錄中的文件和目錄。 |
註釋:
對interface FilenameFilter
接口的解釋:
這裏的文件過濾是用到了策略設計模式,詳細用法我也不造車輪子了
引用鏈接:filenamefilter詳細說明
這裏我們會發現listFiles有重載形式:
filefilter和filenamefilter的區別
文件操縱方法
返回值類型 | 函數名稱 | 詳細說明 |
---|---|---|
boolean | createNewFile() | 創建新文件,如果文件已存在不覆蓋,返回false |
boolean | delete() | 刪除文件或目錄,不可刪除有內容的目錄 |
boolean | renameTo(File dest) | 重命名文件,也可以剪切(與linux mv命令類似)傳入的是想要更名的文件對象 |
boolean | mkdir() | 創建目錄 |
boolean | mkdirs() | 遞歸的創建目錄 |
未完待續…
File 之文件遍歷與刪除
文件遍歷
@Test
public void test3() {
File file = new File("E:\\");
getDir(file, 0);
}
private void getDir(File file, int count) {
File[] list = file.listFiles();
//count 循環是來控制縮進
for(int i = 0;i<count;i++){
System.out.print("|--");
}
System.out.println("dir:"+file.getName());
for (File file1 : list) {
if (file1.isDirectory()) {
getDir(file1, count + 1);
} else {
for(int i = 0;i<count;i++){
System.out.print("|--");
}
System.out.println("file::"+file1.getName());
}
}
}
文件刪除
public void test2() {
File file = new File("E:\\ccc");
getDir(file);
}
private void getDir(File file) {
File[] list = file.listFiles();
for (File file1 : list) {
if (file1.isDirectory()) {
getDir(file1);
} else {
//遞歸刪除文件
System.out.println("file:"+file1.delete());
}
}
//再刪除目錄
System.out.println("dir:"+file.delete());
}
Java Properties類
配置文件說明
配置文件一般與記錄用戶對程序設置的永久化存儲,每當你對手機電腦等系統或軟件進行字體、主題等其他設置的選擇都會保存在配置文件當中,每當程序啓動加載進內存,程序一般都會先讀取本地配置文件,加載用戶之前的自定義配置。
配置文件不僅用於持久化存儲用戶的一些配置,在Java中配合反射機制可以增強程序擴展性,這一點後面文章中在提及。
Properties說明
官方文檔說明:Properties 類表示了一個持久的屬性集。Properties 可保存在流中或從流中加載。屬性列表中每個鍵及其對應值都是一個字符串。
其實就是Properties是一個可以寫到硬盤上的Map集合,默認的鍵和值都是String類型,適合操縱鍵值對方式存儲的配置文件。
java.util class Properties 繼承體系:
java.lang.Object
|— java.util.Dictionary<K,V>
|—java.util.Hashtable<Object,Object>
|— java.util.Properties
從基礎體系可以得知,Properties類可以使用Hashtable中繼承過來的方法
常用方法
Properties對象操作方法
返回值 | 方法名稱 | 說明 | |
---|---|---|---|
Object | setProperty(String key, String value) | 調用 Hashtable 的方法 put,將鍵值寫入到HashTable集合中 | |
void | list(PrintWriter out) | 將Properties對象的map集合用打印流輸出(調試用) | |
String | getProperty(String key) | 獲取指key的value | |
Set<String> | stringPropertyNames() | 獲取配置文件中的所有Key的Set集合 |
持久化存取方法
- 存儲方法
返回值 | 方法名稱 | 說明 | |
---|---|---|---|
void | store(Writer writer, String comments) | 將內存中的Map集合數據通過字符流寫出 | |
void | store(OutputStream out, String comments) | 將內存中的Map集合數據通過字節流寫出 |
- store方法爲保存方法,第一個參數爲保存的流對象,第二個參數爲提示信息(不能有中文),在配置文件中是註釋。
public void test1() throws IOException {
Properties prop = new Properties();
prop.setProperty("Back color", "red");
prop.setProperty("found size", "25");
FileWriter fw = new FileWriter("E:\\b.txt");
prop.list(System.out);//先顯示到控制檯上
prop.store(fw,"Prompt information"); //再寫入到b.txt文件中
}
- 讀取方法
返回值 | 方法名稱 | 說明 | |
---|---|---|---|
void | load(InputStream inStream) | 從輸入流中讀取屬性列表(鍵和元素對) | |
void | load(Reader reader) | 按面向行的格式從輸入字符流中讀取屬性列表(鍵和元素對) |
- 通過load方法 將文件中的鍵值對讀取的內存中的HashTable中(也就是Properties對象中)
/* 對已有的配置文件中的信息進行修改。
* 讀取這個文件。
* 並將這個文件中的鍵值數據存儲到集合中。
* 在通過集合對數據進行修改。
* 在通過流將修改後的數據存儲到文件中。
*/
@Test
public void test() throws IOException{
//讀取這個文件。
File file = new File("info.txt");
FileReader fr = new FileReader(file);
//創建集合存儲配置信息。
Properties prop = new Properties();
prop.load(fr);
prop.setProperty("wangwu", "16");
// 一定要注意寫入文件對象的位置, 如果在load方法之前創建FileWriter對象會覆蓋原有文件的內容
FileWriter fw = new FileWriter(file);
prop.store(fw,"");
fw.close();
fr.close();
}
實際使用過程中流對象的選擇
-
輸入流和輸出流相對於內存設備而言.
將外設中的數據讀取到內存中:輸入
將內存的數寫入到外設中:輸出。 -
字符流的由來:
其實就是:字節流讀取文字字節數據後,不直接操作而是先查指定的編碼表。獲取對應的文字。
在對這個文字進行操作。簡單說:字節流+編碼表
字節流的兩個頂層父類:
1,InputStream 2,OutputStream.
字符流的兩個頂層父類:
1,Reader 2,Writer
這些體系的子類都以父類名作爲後綴。
而且子類名的前綴就是該對象的功能。
字符流緩衝區附加功能:
BufferedWriter
:newLine();
BufferedReader:
: readLine();
裝飾設計模式擴展功能與繼承模式擴展功能區別:
對一組對象的功能進行增強時,就可以使用該模式進行問題的解決。 裝飾和繼承都能實現一樣的特點:進行功能的擴展增強。
有什麼區別呢?
首先有一個繼承體系。
Writer
|–TextWriter:用於操作文本
|–MediaWriter:用於操作媒體。
想要對操作的動作進行效率的提高。
按照面向對象,可以通過繼承對具體的進行功能的擴展。
效率提高需要加入緩衝技術。
Writer
|–TextWriter:用於操作文本
|–BufferTextWriter:加入了緩衝技術的操作文本的對象。
|–MediaWriter:用於操作媒體。
|–BufferMediaWriter:
到這裏就哦了。但是這樣做好像並不理想。
如果這個體系進行功能擴展,有多了流對象。
那麼這個流要提高效率,是不是也要產生子類呢?是。這時就會發現只爲提高功能,進行的繼承,
導致繼承體系越來越臃腫。不夠靈活。
重新思考這個問題?
既然加入的都是同一種技術–緩衝。
前一種是讓緩衝和具體的對象相結合。
可不可以將緩衝進行單獨的封裝,哪個對象需要緩衝就將哪個對象和緩衝關聯。
class Buffer{
Buffer(TextWriter w)
{}
Buffer(MediaWirter w)
{
}
}
class BufferWriter extends Writer{
BufferWriter(Writer w)
{
}
}
Writer
|–TextWriter:用於操作文本
|–MediaWriter:用於操作媒體。
|–BufferWriter:用於提高效率。
裝飾比繼承靈活。
特點:裝飾類和被裝飾類都必須所屬同一個接口或者父類。
字節流:
InputStream
OutputStream
字節流:
FileInputStream
FileOutputStream
BufferedInputStream
BufferedOutputStream
字符流:
Writer Reader
FileReader
FileWriter
BufferedReader
BufferedWriter
轉換流:
InputStreamReader :字節到字符的橋樑。解碼。
OutputStreamWriter:字符到字節的橋樑。編碼。
流的操作規律:
之所以要弄清楚這個規律,是因爲流對象太多,開發時不知道用哪個對象合適。
想要知道開發時用到哪些對象。只要通過四個明確即可。
1,明確源和目的(匯)
源:InputStream Reader
目的:OutputStream Writer
2,明確數據是否是純文本數據。
源:是純文本:Reader
否:InputStream
目的:是純文本 Writer
否:OutputStream
到這裏,就可以明確需求中具體要使用哪個體系。
3,明確具體的設備。
源設備:
硬盤:File
鍵盤:System.in
內存:數組
網絡:Socket流
目的設備:
硬盤:File
控制檯:System.out
內存:數組
網絡:Socket流
4,是否需要其他額外功能。
1,是否需要高效(緩衝區);
是,就加上buffer.
2,轉換。
需求示例演示:
需求1:複製一個文本文件。
1,明確源和目的。
源:InputStream Reader
目的:OutputStream Writer
2,是否是純文本?
是!
源:Reader
目的:Writer
3,明確具體設備。
源:
硬盤:File
目的:
硬盤:File
FileReader fr = new FileReader("a.txt");
FileWriter fw = new FileWriter(“b.txt”);
4,需要額外功能嗎?
需要,需要高效。
BufferedReader bufr = new BufferedReader(new FileReader(“a.txt”));
BufferedWriter bufw = new BufferedWriter(new FileWriter(“b.txt”));
================================================
需求2:讀取鍵盤錄入信息,並寫入到一個文件中。
1,明確源和目的。
源:InputStream Reader
目的:OutputStream Writer
2,是否是純文本呢?
是,
源:Reader
目的:Writer
3,明確設備
源:
鍵盤。System.in
目的:
硬盤。File
InputStream in = System.in;
FileWriter fw = new FileWriter("b.txt");
將讀取的字節數據轉成字符串。再由字符流操作。這樣做可以完成,但是麻煩。
4,需要額外功能嗎?
需要。轉換。 將字節流轉成字符流。因爲名確的源是Reader,這樣操作文本數據做便捷。
所以要將已有的字節流轉成字符流。使用字節-->字符 。InputStreamReader
InputStreamReader isr = new InputStreamReader(System.in);
FileWriter fw = new FileWriter("b.txt");
還需要功能嗎?
需要:想高效。
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bufw = new BufferedWriter(new FileWriter("b.txt"));
需求3:將一個文本文件數據顯示在控制檯上。
1,明確源和目的。
源:InputStream Reader
目的:OutputStream Writer
2,是否是純文本呢?
是,
源:Reader
目的:Writer
3,明確具體設備
源:
硬盤:File
目的:
控制檯:System.out
FileReader fr = new FileReader(“a.txt”);
OutputStream out = System.out;//PrintStream
4,需要額外功能嗎?
需要,轉換。
FileReader fr= new FileReader(“a.txt”);
OutputStreamWriter osw = new OutputStreamWriter(System.out);
需要,高效。
BufferedReader bufr = new BufferedReader(new FileReader(“a.txt”));
BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out));
需求4:讀取鍵盤錄入數據,顯示在控制檯上。
1,明確源和目的。
源:InputStream Reader
目的:OutputStream Writer
2,是否是純文本呢?
是,
源:Reader
目的:Writer
3,明確設備。
源:
鍵盤:System.in
目的:
控制檯:System.out
InputStream in = System.in;
OutputStream out = System.out;
4,明確額外功能?
需要轉換,因爲都是字節流,但是操作的卻是文本數據。
所以使用字符流操作起來更爲便捷。
InputStreamReader isr = new InputStreamReader(System.in);
OutputStreamWriter osw = new OutputStreamWriter(System.out);
爲了將其高效。
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out));
5,將一箇中文字符串數據按照指定的編碼表寫入到一個文本文件中.
1,目的。OutputStream,Writer
2,是純文本,Writer。
3,設備:硬盤File
FileWriter fw = new FileWriter("a.txt");
fw.write("你好");
注意:既然需求中已經明確了指定編碼表的動作。
那就不可以使用FileWriter,因爲FileWriter內部是使用默認的本地碼錶。
只能使用其父類。OutputStreamWriter.
OutputStreamWriter接收一個字節輸出流對象,既然是操作文件,那麼該對象應該是FileOutputStream
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("a.txt"),charsetName);
需要高效嗎?
BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("a.txt"),charsetName));
什麼時候使用轉換流呢?
1,源或者目的對應的設備是字節流,但是操作的卻是文本數據,可以使用轉換作爲橋樑。
提高對文本操作的便捷。
2,一旦操作文本涉及到具體的指定編碼表時,必須使用轉換流 。
Print類詳解
PrintStream:字節打印流說明
- 提供了打印方法可以對多種數據類型值進行打印。並保持數據的表示形式。
注意:這裏說的打印方法是print, 此方法可以直接打印數據的原有形式不做編碼轉換,也就是下面介紹的print方法.
- 也就是說想要保存數據原樣性,推薦使用此類。
-
它不拋IOException.
-
構造函數接收三種類型的值:
字符串路徑。
File對象。
字節輸出流。
注意
PrintStream 打印的所有字符都使用平臺的默認字符編碼轉換爲字節。在需要寫入字符而不是寫入字節的情況下,應該使用 PrintWriter 類
常用函數
返回值 | 方法名 | 說明 |
---|---|---|
void | write(int b) | 將指定的字節轉換成默認字符集的字符寫出, 就是把int轉換成char再寫出。(int 類型是4個字節,而Write方法只寫入int的最低八位,前面的3個字節完全忽略) |
void | println(int x) | 把所有傳入此方法的內容轉換成字符串原樣打印出去 (此方法參數有多種重載形式,這裏只拿int來演示) |
PrintWriter:字符打印流。
- 構造函數參數:
字符串路徑。
File對象。
字節輸出流。
字符輸出流。
自動刷新功能
自動刷新功能,PrintWriter、PrintStream都支持,下面用PrintWriter的其中一個構造函數來解釋說明。
構造方法名 | 說明 |
---|---|
PrintWriter(OutputStream / Writer out, boolean autoFlush) | 通過現有的 OutputStream 創建新的 PrintWriter。autoFlush爲true是自動刷新(不用手動調用flash),只有在調用 println、printf 或 format 的其中一個方法時纔可能完成此操作,而不是每當正好輸出換行符時才完成。如果啓用了自動刷新,則這些方法使用平臺自有的行分隔符概念,而不是換行符。 |
SequenceInputStream 序列流
說明
與打印流不同的是,序列流只負責源的操縱,SequenceInputStream繼承於InputStream,其作用是爲多個源流的合併爲一個大流。
構造方法 | 說明 |
---|---|
SequenceInputStream(Enumeration<? extends InputStream> e) | 把集合Vector中存儲的流合併成一個大流, |
public static void main(String[] args) throws IOException {
Vector<InputStream> v = new Vector<>();
v.add(new FileInputStream("E:\\IO測試文件夾\\a.txt"));
v.add(new FileInputStream("E:\\IO測試文件夾\\b.txt"));
BufferedInputStream bis = new BufferedInputStream(
new SequenceInputStream(v.elements()));
PrintStream ps = new PrintStream("E:\\IO測試文件夾\\E.txt");
int len = 0;
while ((len = bis.read())!=-1){
ps.write(len);
}
ps.close();
bis.close();
}
但是Vector效率不如ArrayList高效,但是SequenceInputStream(Enumeration<? extends InputStream> e) 接收的是枚舉類型,ArrayList只有迭代器。因此我們需要集合中的工具類Collections中的一個函數:
返回值 | 函數名稱 | 說明 |
---|---|---|
static <T> Enumeration<T> | enumeration(Collection<T> c) | 傳入一個Collection集合,返回枚舉 |
- 改進如下:
public static void main(String[] args) throws IOException {
List<FileInputStream> list = new ArrayList<>();
list.add(new FileInputStream("E:\\IO測試文件夾\\a.txt"));
list.add(new FileInputStream("E:\\IO測試文件夾\\E.txt"));
//iterator 轉 enumeration
Enumeration<FileInputStream> enumeration = Collections.enumeration(list);
BufferedInputStream bis = new BufferedInputStream(
new SequenceInputStream(enumeration));
FileOutputStream ps = new FileOutputStream("E:\\IO測試文件夾\\b.txt");
int len = 0;
while ((len = bis.read())!=-1){
ps.write(len);
}
ps.close();
bis.close();
}
IO流之綜合操作——文件的切割與合併
import org.junit.Test;
import java.io.*;
import java.util.*;
public class 文件切割 {
private int FileMegabit = 1;
@Test
public void test1() throws IOException {
String srcDir = "F:\\JavaSourceCode\\TestModule\\files\\";
String srcFileName = "file.rar";
String outDri = "F:\\JavaSourceCode\\TestModule\\files\\切割目錄\\";
//cutfile(srcDir, srcFileName, outDri);
//合併
merge(outDri, outDri);
}
private void merge(String mergePath, String conDir) throws IOException {
Properties p = new Properties();
p.load(new FileReader(conDir + "partConf.ini"));
int count = Integer.parseInt(p.getProperty("count"));
String fileName = p.getProperty("fileName");
File srcDirFile = new File(mergePath);
if (!srcDirFile.isDirectory()) {
throw new RuntimeException("請選擇包含碎片文件的目錄");
}
File[] files = srcDirFile.listFiles((fil) -> fil.getName().endsWith(".part"));
if (files.length != count ) {
throw new RuntimeException("碎片文件不完整,無法合併");
}
ArrayList<FileInputStream> sofList = new ArrayList<>();
for (int i = 0; i < count; i++) {
sofList.add(new FileInputStream(new File(mergePath, fileName + i + ".part")));
}
Enumeration<FileInputStream> enumeration = Collections.enumeration(sofList);
SequenceInputStream sis = new SequenceInputStream(enumeration);
FileOutputStream fos = new FileOutputStream(mergePath + fileName);
byte[] flush = new byte[1024 * 1024];
int len = -1;
while ((len = sis.read(flush)) != -1) {
fos.write(flush, 0, len);
}
fos.close();
}
public void cutfile(String srcDir, String srcFileName, String outDri) throws IOException {
File srcFile = new File(srcDir, srcFileName);
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(srcFile));
byte[] b = new byte[1024 * 1024 * FileMegabit];
int len = 0;
int fileCuttingCounter = 0;
while ((len = bis.read(b)) != -1) {
FileOutputStream fos = new FileOutputStream(outDri + srcFileName + fileCuttingCounter + ".part");
fos.write(b, 0, len);
fileCuttingCounter++;
fos.close();
}
bis.close();
cutInfoStorage(fileCuttingCounter, srcFileName, outDri);
}
private void cutInfoStorage(int count, String fileName, String saveDir) throws IOException {
Properties p = new Properties();
p.setProperty("count", String.valueOf(count));
p.setProperty("fileName", fileName);
p.store(new FileWriter(saveDir + "partConf.ini"),
"cutting file information Not delete");
}
}
Java之序列流ObjectOutputStream/ObjectInputStream
說明
對象序列化就是把堆內存中的對象持久化處理(保存到硬盤上)。
對象序列化前提:對象想要被序列化,就必須實現Serializable接口,此接口是一個標記接口,沒有實現方法。
-
vransient關鍵字介紹:可以讓阻止非靜態數據(堆內存)的數據序列化。
-
Serializable接口介紹:實現此接口的類都有一個隱示的serialVersionUID (這個UID也可以手動定義)其定義格式爲:
ANY-ACCESS-MODIFIER static final long serialVersionUID
。這個UID用來判斷對象和類是否處於同一個版本,如果接收者加載的該對象的類的 serialVersionUID 與對應的發送者的類的版本號不同,則反序列化將會導致InvalidClassException
。
官方文檔上說,強烈建議自定義UID,因爲編譯器版本的不同,可能對同一個類進行序列化後導致ID不一致。
其他提示:靜態成員不能被序列化
演示
/**
* 自定義一個對象,用於測試對象序列化
*/
class Person implements Serializable {
public static final long serialVersionUID = 42L; //自定義序列化標號ID
//如果不寫,系統會根據類中的內容計算出一個序列化ID
//加載時根據ID判斷硬盤中的這個對象是否屬於這個類。
private String name;
transient int age; //transition 關鍵字:阻止堆內存中的數據序列化,數據不能寫入硬盤,
//只能在堆內存加載
static String country = "cn"; //靜態不能被序列化
Person(String name, int age, String country) {
this.name = name;
this.age = age;
Person.country = country;
}
@Override
public String toString() {
return name + ":" + age + ":" + country;
}
}
對象序列化(存儲)演示
//把一個對象寫入到硬盤。
public static void writeObj() throws IOException {
//創建寫對象流,和保存位置
ObjectOutputStream oos =
new ObjectOutputStream(new FileOutputStream("obj.object"));
//把一個對象寫入到硬盤中,如果有多次寫入,則順序存儲
oos.writeObject(new Person("lisi0", 399, "kr"));
//oos.writeObject(new Person("lisi0", 399, "kr"));
oos.close();
}
對象反序列化(讀取)演示
讀取的readObject方法會拋出一個ClassNotFoundException (類找不到異常),因爲要讀取對象文件有一個前提,你必須有這個對象的.class文件。如果你有這個對象文件而沒有類文件,這個對象就無法在內存中創建。
readObject方法是按照順序進行讀取的,也就是說,調用一次readObject就讀一次下一個對象。
還有一點就是要注意,readObject()方法好像沒有結束標記,讀到末尾會拋出EOFException,因此在catch中結束就好了,有知道的小夥伴可以留言評論一下。
//讀取硬盤的對象
public static void readObj() throws Exception {
ObjectInputStream ois = new ObjectInputStream(
new FileInputStream("files\\obj.object"));
Person person1 = (Person) ois.readObject();
System.out.println(person1.toString());
Person person2 = (Person) ois.readObject();
System.out.println(person2.toString());
}
Java之隨機訪問文件—RandomAccessFile
說明
RandomAccessFile不是io體系中的四大類的子類。它直接繼承與Object類。用此類存儲的文件字節數必須有規律,因爲他使用的是下標進行讀取的,有數組的特性。
- 總體來說就是通過seek方法可以對文件的任意位置進行讀寫。
其特點爲:
1,該對象即能讀,又能寫。
2,該對象內部維護了一個byte數組,並通過指針可以操作數組中的元素,
3,可以通過getFilePointer方法獲取指針的位置,和通過seek方法設置指針的位置。
4,其實該對象就是將字節輸入流和輸出流進行了封裝。
5,該對象的源或者目的只能是文件。通過構造函數就可以看出。
6,如果文件不存在,則創建,如果文件存在,則不覆蓋文件。(注意:這裏的不覆蓋指的是文件的覆蓋,與下面所說的內容覆蓋不同)
構造函數參數
- mode 參數指定用以打開文件的訪問模式。允許的值及其含意爲:
值 | 含意 |
---|---|
“r” | 以只讀方式打開。調用結果對象的任何 write 方法都將導致拋出 IOException。 |
“rw” | 打開以便讀取和寫入。如果該文件尚不存在,則嘗試創建該文件。 |
“rws” | 打開以便讀取和寫入,對於 “rw”,還要求對文件的內容或元數據的每個更新都同步寫入到底層存儲設備。 |
“rwd” | 打開以便讀取和寫入,對於 “rw”,還要求對文件內容的每個更新都同步寫入到底層存儲設備。 |
常用函數說明
- 此類提供了大量的各種形式的讀寫方法,由於方法太多這裏就不一一贅述了,詳情參看官方文檔。
其他
我們可以通過seek(long pos) 方法 設置指針的偏移位置,在通過其他的read方法去取數據,從而向數組一樣讀取文件的任意位置。
由於是通過指針進行操作的,因此如果用新的RandomAccessFile對已有的文件進行writer操作時不會記錄原有指針的位置,也就是說指針從0開始寫入,這樣會覆蓋原字節數組下標的數據。
由於他有隨機訪問的特性,因此可以用於多線程下載。
Java之管道流PipedInputStream/PipedOutputStream
說明
管道流應結合多線程使用。因爲官方文檔上說對這兩個對象使用單個線程,可能會造成該線程死鎖。
代碼示例
import java.io.*;
/**
* 管道流Piped:
* 說明:將輸入流和輸出流連接起來,(之前的方法是在內存中定義數組臨時存儲兩個流之間的變量)
* 注意:使用管道流,讀和寫不能在同一個線程執行,會出現死鎖。
*
*/
//read線程類
class Read implements Runnable {
private PipedInputStream in;
Read(PipedInputStream in) {
this.in = in;
}
public void run() {
try {
byte[] buf = new byte[1024];
System.out.println("讀取前。。沒有數據阻塞");
int len = in.read(buf); //假如讀線程先獲得cpu執行權,如果沒有數據,
// 線程就會進入掛起(阻塞/暫停)狀態,等待寫入完成
System.out.println("讀到數據。。阻塞結束");
String s = new String(buf, 0, len);
System.out.println(s);
in.close();
} catch (IOException e) {
throw new RuntimeException("管道讀取流失敗");
}
}
}
//write線程類
class Write implements Runnable {
private PipedOutputStream out;
Write(PipedOutputStream out) {
this.out = out;
}
public void run() {
try {
System.out.println("開始寫入數據,等待3秒後。");
Thread.sleep(3000);
out.write("piped lai la".getBytes());
out.close();
} catch (Exception e) {
throw new RuntimeException("管道輸出流失敗");
}
}
}
public class PipedStreamTest {
public static void main(String[] args) throws IOException {
PipedInputStream in = new PipedInputStream();
PipedOutputStream out = new PipedOutputStream();
in.connect(out); //把連個管道進行連接
Read r = new Read(in);
Write w = new Write(out);
new Thread(r).start();
new Thread(w).start();
}
}
Java之數據流DataInputStream/DataOutputStream
說明
- 此流用於操作基本數據類型,數據流是FilterOutputStream的子類。此類是一個裝飾類,它不能直接操作資源。需要從構造函數中傳遞資源對象。
構造方法:DataOutputStream(OutputStream out) 創建一個新的數據輸出流,將數據寫入指定基礎輸出流。 - 數據輸出流允許應用程序以適當方式將基本 Java 數據類型寫入輸出流中。然後,應用程序可以使用數據輸入流將數據讀入。
注意:
查看官方文檔的方法摘要就會發現。很多方法在管道流和RandomAccessFile等其他流中見到過,但是注意,管道流是操作管道的,RandomAccessFile是用於隨機訪問,雖然功能類似,但操作的對象和不一樣,而數據流是專門保存原始數據的。
如想要向文件寫入一個int類型的數據,普通流的write只寫入最低8位,而這個類中的方法提供了原樣寫入基本數據類型的功能。
Java之字節數組流ByteArrayInputStream/ByteArrayOutputStream
說明
此流用於操作內存。由於此對象沒有調用系統底層資源,因此此流不用關閉資源,且不會產生任何 IOException。
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
public class ByteArrayStreamDemo {
public static void main(String[] args) {
ByteArrayInputStream bis = new ByteArrayInputStream("abcedf".getBytes());
ByteArrayOutputStream bos = new ByteArrayOutputStream();
int ch = 0;
while((ch=bis.read())!=-1){
bos.write(ch);
}
System.out.println(bos.toString());
}
}
其他詳情請參考官方文檔