Java中的輸入與輸出

本文內(nèi)容非原創(chuàng),你可以點(diǎn)擊此處查看內(nèi)容來源聲明

輸入/輸出流

在Java API中闷堡,可以從其中讀出一個(gè)字節(jié)序列的對(duì)象叫做輸入流隘膘,可以從其中寫入一個(gè)字節(jié)序列的對(duì)象叫做輸出流。這些字節(jié)序列的來源地和目的地可以是文件杠览,也可以是網(wǎng)絡(luò)連接弯菊,甚至是內(nèi)存塊

抽象類InputStream和OutputStream構(gòu)成了輸入輸出(I/O)類層次結(jié)構(gòu)的基礎(chǔ)。

讀寫字節(jié)

InpuStream類有一個(gè)抽象方法:
abstract int read()
這個(gè)方法將讀入一個(gè)字節(jié)踱阿,并返回讀入的字節(jié)管钳,或者在遇到輸入源結(jié)尾時(shí)返回-1。在設(shè)計(jì)具體的輸入流類時(shí)软舌,必須覆蓋這個(gè)方法已提供適當(dāng)?shù)墓δ堋?br> InputStream類還有若干個(gè)非抽象防范才漆,可以讀入一個(gè)字節(jié)數(shù)組,或者跳過大量的字節(jié)佛点。這些方法都要調(diào)用抽象的read方法醇滥,因此,各個(gè)子類都只需覆蓋這一個(gè)方法恋脚。
與此類似腺办,OutputStream類定義了下面的抽象方法:
abstract void write(int b)
它可以向某個(gè)輸出位置寫出一個(gè)字節(jié)。

read和write方法在執(zhí)行時(shí)都將阻塞糟描,直至 字節(jié)確實(shí)被讀入或?qū)懗銮娉 _@就意味著如果流不能被立即訪問,那么當(dāng)前的線程將被阻塞驯杜。這使得這兩個(gè)防范等待制定的流變?yōu)榭捎?的這段時(shí)間里吻育,其他的線程就有機(jī)會(huì)去執(zhí)行有用的工作。
available方法使我們可以去見檢查當(dāng)前可讀入的字節(jié)數(shù)量见间,這意味著下面的代碼片段不會(huì)被阻塞:

int bytesAvailable = in.available();
if(bytesAvailable > 0){
byte[] data = new byte[bytesAvailable];
in.read(data);
}

當(dāng)你完成對(duì)輸入輸出流的讀寫時(shí)聊闯,英國(guó)通過調(diào)用close方法來關(guān)閉它。

即使某個(gè)輸入輸出流類提供了原生的read和write功能的某些具體方法米诉,應(yīng)用系統(tǒng)的程序員還是很少使用他們菱蔬,因?yàn)榇蠹腋信d趣的數(shù)據(jù)可能包含數(shù)字、字符串和對(duì)象,而不是原生字節(jié)拴泌。
我們可以使用眾多的從基本的InputStream和OutputStream類導(dǎo)出的某個(gè)輸入/輸出類魏身,而不只是直接使用字節(jié)。

完整的流家族

組合輸入/輸出流過濾器

Tips:

所有在java.io中的類都將被相對(duì)路徑名解釋為以用戶工作目錄開始蚪腐,可以調(diào)用System.getProperty("user.dir");

Warning

由于反斜杠字符在java字符串中是轉(zhuǎn)義字符箭昵,因此要確保在Windows風(fēng)格的路徑名中使用\(eg:C:\Windows\win.ini)。在Windows中回季,可以使用單斜杠字符(C:/Windows/win.ini),因?yàn)榇蟛糠諻indows文件處理的系統(tǒng)調(diào)用都會(huì)將斜杠解釋成文件分隔符家制。但是并不推薦這么做,因?yàn)閃indows系統(tǒng)函數(shù)的行為會(huì)因與時(shí)俱進(jìn)而發(fā)生變化泡一。因此對(duì)于可移植的程序來說颤殴,應(yīng)該使用程序所運(yùn)行平臺(tái)的文件分隔符,可以通過個(gè)常量字符串java.io.File.separator獲得瘾杭。

FileInputStream和FileOutputStream可以提供附著在一個(gè)磁盤文件上的輸入流和輸出流诅病,而你只需向構(gòu)造器提供文件名或文件的完整路徑名。
FileInput fin = new FileInputStream("employee.dat");

與抽象類InutStream和Outputstream一樣粥烁,這些類只支持在字節(jié)級(jí)別上的讀寫贤笆。也就是說,我們只能從fin對(duì)象中讀入字節(jié)和字節(jié)數(shù)組讨阻。
byte b = (byte) fin.read();

DataInputStream芥永,就只能讀入數(shù)值類型:
DataInputStream din = ...;
double x = din.readDouble();

正如FileInputStream沒有任何讀入數(shù)值的方法一樣,DataInputStream也沒有任何從文件中獲取數(shù)據(jù)的方法钝吮。

Java使用了一種靈巧的機(jī)制來分離這兩種職責(zé)埋涧。某些輸入流(例如FileInputStream和由URL類的openStream方法返回的輸入流)可以從文件和其他更外部的位置上獲取字節(jié),而其他的輸入流(例如DataInputStream)可以將字節(jié)組裝到更有用的數(shù)據(jù)類型中奇瘦。Java程序員必須對(duì)二者進(jìn)行組合棘催。例如,為了從文件中讀入數(shù)字耳标,首先需要?jiǎng)?chuàng)建一個(gè)FileInputStream醇坝,然后將其傳遞給DataInputStream的構(gòu)造器:
FileInputStream fin = new FileInputStream("employee.dat");
DataInputStream din = new DataInputStream(fin);
double x = din.readDouble();

FilterInputStream和FilterOutputStream這些類的子類用于向處理字節(jié)的輸入/輸出流添加額外的功能。

可以通過嵌套過濾器添加多重功能次坡。例如呼猪,輸入流在默認(rèn)情況下是不被緩沖區(qū)緩存的。每個(gè)對(duì)read的調(diào)用都會(huì)請(qǐng)求操作系統(tǒng)再分發(fā)一個(gè)字節(jié)砸琅。相比之下宋距,請(qǐng)求一個(gè)數(shù)據(jù)塊并將置于緩沖區(qū)會(huì)顯得更加高效。如果使用緩沖機(jī)制症脂,以及用于文件的數(shù)據(jù)輸入方法谚赎,就需要使用下面這種相當(dāng)恐怖的構(gòu)造器序列:
DataInputStream din = new DataInputStream(
new BufferedInputStream(
new FileInputStream("employee.dat")));
注意淫僻,把DataInputStream置于構(gòu)造器的最后,是因?yàn)橄M褂肈ataInputStream的方法沸版,并且希望使用它們能夠使用帶緩沖機(jī)制的read方法嘁傀。
有時(shí)當(dāng)多個(gè)輸入流鏈接在一起,需要跟蹤各個(gè)中介輸入流(intermediate inputstream)视粮。例如,當(dāng)讀入輸入流時(shí)橙凳,經(jīng)常需要預(yù)覽下一個(gè)字節(jié)蕾殴,以了解它是否是想要的值。Java提供了用于此目的的PushbackInputStream:
PushbackInputStream pbin = new PushbackInputStream(
new BuffererInputStream(
new FileInputStream("employee.dat")));
現(xiàn)在可以預(yù)讀下一個(gè)字節(jié):
int b = pbin.read();
并且在它并非你所期望的值時(shí)將其推回流中岛啸。
if(b != '<') pbin.unread(b);
但是讀入和推回是可應(yīng)用于可推回(pushable)輸入流的僅有的方法钓觉。如果希望能夠預(yù)先瀏覽并且可以讀入數(shù)字,就需要一個(gè)既是可推回輸入流坚踩,又是一個(gè)數(shù)據(jù)輸入流的引用荡灾。
DataInputStream din = new DataInputStream(
pbin = new PushbackInputStream(
new BufferedInputStream(
new FileInputStream("employee.dat")))
);

當(dāng)然在其它編程語言的輸入輸出流類庫(kù)中,諸如緩沖機(jī)制和預(yù)覽等細(xì)節(jié)都是自動(dòng)處理的瞬铸。

文本輸入與輸出

在保存數(shù)據(jù)時(shí)批幌,可以選擇二進(jìn)制格式或文本格式。

在存儲(chǔ)文本字符串時(shí)嗓节,需要考慮編碼方式荧缘,在Java內(nèi)部使用的是UTF-16,字符串“1234”編碼為 00 31 00 32 00 33 0034 十六進(jìn)制。但是拦宣,許多程序都希望文本文件按照其他編碼方式編碼截粗。在UTF-8這種在互聯(lián)網(wǎng)上最常用的編碼方式中,這個(gè)字符串將寫出為 4A 6F 73 C3 A9,其中并沒有用于前3個(gè)字母的任何0字節(jié)鸵隧,而字符e(e頭上加一個(gè)二聲)占用了兩個(gè)字節(jié)绸罗。(最后一句話沒看懂 ,中文書上就這么翻譯的豆瘫,可能翻譯錯(cuò)了吧珊蟀。。靡羡。)

OutputStreamWriter類將使用選定的字符編碼方式系洛,把Unicode碼元的輸出流轉(zhuǎn)換為字節(jié)流。
InputStreamReader類將包含字節(jié)(用某種字符編碼方式表示的字符)的輸入流轉(zhuǎn)換為可以產(chǎn)生Unicode碼元的讀入器略步。

如何寫出文本輸出

對(duì)于文本輸出可以使用PrinterWriter描扯。這個(gè)類擁有以文本格式打印字符串和數(shù)字的方法,還有一個(gè)將Printerwriter鏈接到FileWriter的便捷方法趟薄,下面的語句:
PrintWriter out = new PrinterWriter("employee.txt","UTF-8");
等同于
PrintWriter out = new PrinterWriter(new FileOutStream("employee.txt"),"UTF-8");

為了輸出到打印寫出的器绽诚,需要使用與使用System.out時(shí)相同的print,println和printf方法。

如何讀入文本輸入

最簡(jiǎn)單的處理任意文本的方式是使用廣泛使用的Scanner類,可以從任何輸入流中構(gòu)建Scanner對(duì)象恩够。
或者,可以將短小的文本文件像下面這樣讀入一個(gè)字符串中:
Strng content = new String(File.readAllBytes(path),charset);
如果要將文件一行一行地讀入卒落,可以調(diào)用:
List<String> lines = Files.readAllLiines(path,charset);
如果文件太大,可以將行惰性處理為一個(gè)Stream<String>對(duì)象:
try (Stream<String> lines = Files.lines(path,charset)){...}

在早期的Java版本中蜂桶,處理文本的唯一輸入方式是通過BufferedReader類儡毕。它的readLine方法會(huì)產(chǎn)生一行文本,或者在無法獲得更多的輸入時(shí)返回null扑媚。典型的輸入循環(huán)看起來像下面這樣:

InputStream inputStream = ...;
try (BufferedReader in = new BufferedReader(new InputStreamReader(inputStream,StandardCharset.UTF_8))){
String line;
while((line = in.readLine() != null)){
do Something
}
}
如今腰湾,BufferedReader類又有了一個(gè)lines方法,可以產(chǎn)生一個(gè)Stream<String>對(duì)象。但是疆股,與Scanner不同费坊,BufferedReader沒有用于任何讀入數(shù)字的方法。

以文本格式存儲(chǔ)對(duì)象

字符編碼方式

輸入和輸出流都是用于處理字節(jié)序列的旬痹,但是在許多情況下附井,我們希望操作的是文本,即字符序列两残。于是永毅,字符如何編碼成為字節(jié)就成了問題。

Java針對(duì)字符使用的是Unicode標(biāo)準(zhǔn)磕昼。每個(gè)字符或“編碼點(diǎn)”都具有一個(gè)21位的整數(shù)卷雕。有多種不同的字符編碼方式,就是說票从,將這些21位數(shù)字包裝成字節(jié)的方法有多種漫雕。
最常見的編碼方式是UTF-8,它會(huì)將每個(gè)Unicode編碼點(diǎn)編碼為1到4個(gè)字節(jié) 的序列(如下表)。UTF-8的好處是傳統(tǒng)的包含了英語中用到的所有字符 的ASCII字符集中的每個(gè)字符都會(huì)占用一個(gè)字節(jié)峰鄙。

UTF-8編碼方式

字符范圍 編碼方式
0...7F 0a6a5a4a3a2a1a0
80...7FF 110a10a9a8a7a6??a5a4a3a2a1a0
800...FFFF 1110a15a14a13a12a11a10a9a8a7a6??a5a4a3a2a1a0
10000...10FFFF 11110a20a19a18a17a16a15a14a13a12a11a10a9a8a7a6??a5a4a3a2a1a0

另一種常用的編碼方式是UTF-16浸间,他會(huì)將每個(gè)Unicode編碼點(diǎn)編碼為1個(gè)或2個(gè)16位值。
(待補(bǔ)充吟榴。魁蒜。。)

StandardCharsets類具有類型為Charset的靜態(tài)變量吩翻,用于表示每種Java虛擬機(jī)都必須支持的字符編碼方式:
StandardCharsets.UTF_8
StandardCharsets.UTF_16
StandardCharsets.UTF_16BE
StandardCharsets.UTF_16LE
StandardCharsets.UTF_8859_1
StandardCharsets.UTF_US_ASCII

為了獲得另一種編碼方式的Charset,可以使用靜態(tài)的forName方法:
Charset shiftJIS = Charset.forName("Sift-JIS");
在讀入或?qū)懗鑫谋緯r(shí)兜看,應(yīng)該使用Charset對(duì)象∠料梗可以像下面這樣將一個(gè)字節(jié)數(shù)組轉(zhuǎn)換為字符串:
String str = new String(bytes,StandardCharsets.UTF_8);

讀寫二進(jìn)制數(shù)據(jù)

文本格式對(duì)于測(cè)試和調(diào)試而言很方便细移,但是并不像二進(jìn)制格式傳遞數(shù)據(jù)那樣高效。

DadaInput 和 DataOutput接口

DataInput接口定義了下面用于二進(jìn)制格式寫數(shù)組熊锭、字符弧轧、Boolean值和字符串的方法:
writeCharts
writeByte
writeInt
writeShort
writeLong
writeFloat
writeDouble
writeChar
writeBoolean
writeUTF

例如,writeInt總是將一個(gè)整數(shù)寫出為4字節(jié)的二進(jìn)制數(shù)量值雪侥,而不管它有多少位,writeDouble總是將一個(gè)double值寫出為8字節(jié)的二進(jìn)制數(shù)量值精绎。這樣產(chǎn)生的結(jié)果并非人可閱讀的速缨,但是對(duì)于每個(gè)給定的每個(gè)值,所需的空間都是相同的代乃,而且將其讀回也比解析文本要快旬牲。
writeUTF方法使用修訂版的8位Unicode轉(zhuǎn)換格式寫出字符串。這種方式與直接使用標(biāo)準(zhǔn)的UTF-8編碼方式不同搁吓,其中引谜,Unicode碼元序列首先用UTF-16表示,其結(jié)果之后使用UTF-8規(guī)則進(jìn)行編碼擎浴。修訂后的編碼方式對(duì)于編碼大于oxFFF的字符的處理有所不同,這是為了向后兼容在Unicode還沒有超過16位時(shí)構(gòu)建的虛擬機(jī)毒涧。

為了讀回?cái)?shù)據(jù)贮预,可以使用DataInput接口中定義 的下列方法:
readInt
readShort
readLong
readFloat
readDouble
readChar
readBoolean
readUTF

DataInputStream類實(shí)現(xiàn)了DataInput接口,為了從文件中讀入二進(jìn)制數(shù)據(jù)契讲,可以將DataInputStream與某個(gè)字節(jié)源相結(jié)合仿吞,例如FileInputStream:
DataInputStream in = new DataInputStream(new FileInputStream("employee.txt"));

與此類似,想要寫出二進(jìn)制數(shù)據(jù)捡偏,可以使用實(shí)現(xiàn)了DataOutput接口的DataOutputStream類:
DataOutputStream out = new DataOutputStream(new FileOutputStream("employee.txt"));

隨機(jī)訪問文件

RandomAccessFile類可以在文件中的任何位置查找或?qū)懭霐?shù)據(jù)唤冈。磁盤文件都是隨機(jī)訪問的,但是與網(wǎng)絡(luò)套接字通信的輸入/輸出流卻不是银伟∧愫纾可以打開一個(gè)隨機(jī)訪問文件,只是用于讀入或同時(shí)用于讀寫彤避,可以通過使用字符串"r"(用于讀入訪問)或"rw"(用于讀入/寫出訪問)作為構(gòu)造器的第二個(gè)參數(shù)來指定這個(gè)選項(xiàng)傅物。
RandomAccessFile in = new RandomAccesFiel("employee.txt","r");
RandomeAccessFile inOut = new RandomAcce("employee.txt","rw");
當(dāng)你將已有文件作為RandomAccessFile打開時(shí),這個(gè)文件不會(huì)被刪除琉预。

隨機(jī)訪問已有文件有一個(gè)表示下一個(gè)將被讀入或?qū)懗龅淖止?jié)所處位置 的文件指針董饰,seek方法可以用來將這個(gè)文件指針設(shè)置到文件中的任意字節(jié)位置,seek的參數(shù)是一個(gè)long類型的整數(shù)圆米,它的值位于0到文件按照字節(jié)來度量的長(zhǎng)度之間卒暂。

getFilePointer方法將返回文件指針的當(dāng)前位置。

RandomAccessFile類同時(shí)實(shí)現(xiàn)了DataInput和DataOutput接口娄帖。為了讀寫隨機(jī)訪問文件也祠,可以使用在前面討論過的readInt/writeInt/和readChar/writeChar之類的方法。

(待補(bǔ)充块茁。齿坷。桂肌。)

ZIP文檔

ZIP文檔通常以壓縮格式存儲(chǔ)了一個(gè)或多個(gè)文件,每個(gè)ZIP文檔都有一個(gè)頭永淌,包含諸如每個(gè)文件名和使用的壓縮方法等信息崎场。在Java中,可以使用ZipInputStream來讀入ZIP文檔遂蛀√房纾可以需要瀏覽文檔中的每個(gè)單獨(dú)的項(xiàng),getNextEntry方法可以返回一個(gè)描述這些項(xiàng)的ZipEntry類型的對(duì)象李滴。向ZipInputStream的getInputStream方法傳遞該項(xiàng)可以獲取用于讀取該項(xiàng)的輸入流螃宙。然后調(diào)用closeEntry來讀入下一個(gè)項(xiàng)。下面是典型的通讀ZIP文件的代碼序列:

    ZipInputStream zin = new ZipInputStream(new FileInputStream(zipname));
    ZipEntry entry;
    while ((entry = zin.getNextEntry()) != null){
        InputStream in = zin.getInputStream(entry);
        //read the contents of in
        zin.closeEntry();
    }
    zin.close();

要寫出到ZIP文件所坯,可以使用ZipOutputStream谆扎,而對(duì)于你希望放入到ZIP文件中的每一項(xiàng),都應(yīng)該創(chuàng)建一個(gè)ZipEntry對(duì)象芹助,并將文件名傳遞給ZipEntry的構(gòu)造器堂湖,它將設(shè)置其他諸如文件日期和解壓縮方法等參數(shù)。如果需要状土,你可以 覆蓋這些設(shè)置无蜂。然后,你需要調(diào)用ZipOutputStream的putNextEntry方法來開始寫出新文件蒙谓,并將文件數(shù)據(jù)發(fā)送到ZIP 輸出流中斥季。完成時(shí),需要調(diào)用closeEntry累驮。然后酣倾,你需要對(duì)所有你希望存儲(chǔ)的文件都重復(fù)這個(gè)過程。下面是代碼框架:

FileOutputStream fout = new FileOutputStream("test.zip");
    ZipOutputStream zout = new ZipOutputStream(fout);
    //for all files
    {
        ZipEntry ze = new ZipEntry(filename);
        zout.putNextEntry(ze);
        //send data to zout
        zout.closeEntry();
    }
    zout.close();

JAR文件只是帶一個(gè)特殊項(xiàng)的ZIP文件慰照,這個(gè)項(xiàng)稱作清單灶挟。可以使用JarInputStream和JarOutputStream類來讀寫清單項(xiàng)毒租。

ZIP輸入流是一個(gè)能夠展示流的抽象化的強(qiáng)大之處的實(shí)例稚铣。當(dāng)你讀入以壓縮格式存儲(chǔ)的數(shù)據(jù)時(shí),不必?fù)?dān)心邊請(qǐng)求邊解壓數(shù)據(jù)的問題墅垮,而且ZIP格式的字節(jié)源并非是文件惕医,也可以是來自網(wǎng)絡(luò)連接的ZIP數(shù)據(jù)。事實(shí)上算色,當(dāng)Applet的類加載器讀入JAR文件時(shí)抬伺,它就是在讀入和解壓來自網(wǎng)絡(luò)的數(shù)據(jù)。

對(duì)象輸入/輸出流與序列化

當(dāng)你需要存儲(chǔ)相同類型的數(shù)據(jù)時(shí)灾梦,使用固定長(zhǎng)度的記錄格式是一個(gè)不錯(cuò)的選擇峡钓。但是妓笙,在面向?qū)ο蟪绦蛑袆?chuàng)建的對(duì)象很少全部都具有相同的類型。例如能岩,你可能有一個(gè)稱為staff的數(shù)組寞宫,它名義是一個(gè)Employee記錄數(shù)組,但是實(shí)際上卻包含諸如Manager這樣的子類實(shí)例拉鹃。
Java語言支持一種稱為對(duì)象序列化的非常通用的機(jī)制辈赋,它可以將任何對(duì)象寫出到輸出流中,并在之后將其讀回膏燕。

保存和加載序列化對(duì)象

為了保存對(duì)象數(shù)據(jù)钥屈,需要打開一個(gè)ObjectOupputStream對(duì)象:
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("employee.txt"));
為了保存對(duì)象,可以使用ObjectOutputStream的writeObject方法:

Employee harry = new Employee("Harry Hacker",5000,1998,10,1);
    Manager boss = new Manager("Carl Cracker",8000,1987,12,25);
    out.writeObject(harry);
    out.writeObbject(boss);

為了將這些對(duì)象讀回坝辫,首先需要獲得一個(gè)ObjectInputStream對(duì)象:
ObjectInputStream in = new ObjectInputStream(new FileInputStream("employee.txt"));
然后篷就,用readObject方法以這些對(duì)象被寫出時(shí)的順序獲得他們:
Employee e1 = (Employee) in.readObject();
Employee e2 = (Employee) in.readObject();
但是對(duì)希望在對(duì)象輸出流中存儲(chǔ)或從對(duì)象輸入流中恢復(fù)的所有類都應(yīng)進(jìn)行一些修改,這些類必須實(shí)現(xiàn)Serializable接口:
class Employee implements Serializable {... }

Serializable接口沒有任何方法近忙,因此不需要對(duì)這些類做任何改動(dòng)腻脏。在這一點(diǎn)上,與Cloneable接口很相似银锻。但是,為了使類可克隆做鹰,仍需要覆蓋object類中的clone方法击纬,而為了使類序列化,不需要做任何事钾麸。

只有需要寫出對(duì)象時(shí)才使用witeObject/readObject,對(duì)于基本類型值,使用諸如writeInt/readInt或writeDouble/readDouble (對(duì)象流類都實(shí)現(xiàn)了DataInput/DataOutput接口)

在幕后,是ObjectOutputStream在瀏覽對(duì)象的所有域,并存儲(chǔ)他們的內(nèi)容更振。

此處的序列化是將對(duì)象集合保存到文件中,并按照它們被存儲(chǔ)的樣子獲取他們饭尝。序列化的另一種非常重要的應(yīng)用是通過網(wǎng)絡(luò)將對(duì)象集合傳送到另一臺(tái)計(jì)算機(jī)上肯腕。正如在文件中保存原生的內(nèi)存地址無意義一樣,這些地址對(duì)于不同的處理器之間的通信也是毫無意義的钥平。因?yàn)樾蛄谢眯蛄刑?hào)代替了內(nèi)存地址实撒,所有它允許對(duì)象集合從一臺(tái)機(jī)器傳送到另一臺(tái)機(jī)器。

理解對(duì)象序列化的文件格式

參見原書

修改默認(rèn)的序列化機(jī)制

某些數(shù)據(jù)域是不可序列化的涉瘾,例如只對(duì)本地方法有意義的存儲(chǔ)文件句柄或窗口句柄的整數(shù)值知态,這種信息在稍后重新加載對(duì)象或?qū)⑵鋫魉偷狡渌麢C(jī)器上時(shí)都是沒用的。事實(shí)上立叛,這種域的值如果不恰當(dāng)负敏,還會(huì)引起本地方法崩潰。Java擁有一種很簡(jiǎn)單的機(jī)制來防止這種域被序列化秘蛇,那就是將它們標(biāo)記成transient的其做。如果這些域?qū)儆诓豢尚蛄谢念惗タ迹残枰獙⑺鼈儤?biāo)記成transient的。瞬時(shí)的域在對(duì)象被序列化時(shí)總是被跳過的妖泄。
序列化機(jī)制為單個(gè)的類提供了一種方式驹沿,去向默認(rèn)的讀寫行為添加驗(yàn)證或任何其他想要的行為「÷可序列化的類可以定義具有下列簽名的方法:
private void readObject(ObjectInputStream in) throws IOException,ClassNoutFoundException;
private void writeObject(ObjectOutputStream out) throws IOException;
之后數(shù)據(jù)域就再不會(huì)被自動(dòng)序列化甚负,取而代之的是調(diào)用這些方法。

除了讓序列化機(jī)制來保存和恢復(fù)對(duì)象數(shù)據(jù)审残,類還可以定義它自己的機(jī)制梭域。為了做到這一點(diǎn),這個(gè)類必須實(shí)現(xiàn)Externalizable接口搅轿,這需要定義兩個(gè)方法:
public void readExternal(ObjectInputStream in) throws IOException ,ClassNotFoundException;
Public void writeExternal(ObjectOutputStream out) throws IOException;

序列化單例和實(shí)例安全的枚舉

在序列化和反序列化時(shí)病涨,如果目標(biāo)對(duì)象時(shí)唯一的,必須加倍小心璧坟。
如果使用Java語言的enum結(jié)構(gòu)既穆,就不必?fù)?dān)心序列化。但是假設(shè)在維護(hù)遺留代碼雀鹃,其中包含下面這樣的枚舉類型:
public class Orientation {
public static final Orientation HORIZONTAL = new Orientation(1);
public static final Orientation VERTICAL = new Orientation(2);
}

為了解決這個(gè)問題幻工,需要定義另一種稱為readResolve的特殊序列化方法。如果定義了該方法黎茎,在對(duì)象被序列化后就會(huì)調(diào)用它囊颅。它必須返回一個(gè)對(duì)象,而該對(duì)象之后會(huì)成為一個(gè)readObject的返回值傅瞻。在上面的情況下踢代,readResolve方法將檢查value域并返回恰當(dāng)?shù)拿杜e常量:
protected Object readResolve() throws ObjectStreamException{
if(value == 1) return Orientation.HORIZONTAL;
if(value == 2) return Orientation.VERTICAL;
throw new ObjectStreamException();
}

為克隆使用序列化

序列化機(jī)制是一種很有趣的用法:即提供了一種克隆對(duì)象的漸變途徑,只要對(duì)應(yīng)的類是可序列化的即可嗅骄。做法很簡(jiǎn)單:直接將對(duì)象序列化到輸出流中胳挎,然后將其讀回。這樣產(chǎn)生的新對(duì)象是對(duì)現(xiàn)有對(duì)象的一個(gè)深拷貝溺森。在此過程中慕爬,我們不必將對(duì)象寫出到文件中,因?yàn)榭梢杂肂yteArrayOutputStream將數(shù)據(jù)保存到字節(jié)數(shù)組中屏积。

操作文件

Path接口和Files類封裝了在用戶機(jī)器上處理文件系統(tǒng)所需的所有功能澡罚,他們是在JavaSE7中新添加進(jìn)來的,比自JDK1.0以來一直使用的File類方便很多肾请。

Path

Path表示的是一個(gè)目錄名序列留搔,其后還可以跟著一個(gè)文件名。路徑中的第一個(gè)部件可以是根部件铛铁,例如 / 或 C:\ ,而允許訪問的根部件取決于文件系統(tǒng)隔显。
Path absolute = Paths.get("/home","harry");
Path relative = Paths.get("myprog","conf","user.properties");
靜態(tài)的Paths.get方法接受一個(gè)或多個(gè)字符串却妨,并將它們用默認(rèn)的文件系統(tǒng)路徑分隔符(/,\)連接起來,然后解析連接起來的結(jié)果括眠,如果表示的不是給定文件系統(tǒng)的合法路徑彪标,就拋出InvalidPathException異常。這個(gè)連接起來的結(jié)果就是一個(gè)Path對(duì)象掷豺。
get方法可以獲取多個(gè)部件構(gòu)成的單個(gè)字符串捞烟。

組合或解析路徑是常見的操作,調(diào)用p.resolve(q)將按照下列規(guī)則返回一個(gè)路徑:

  • 如果q是絕對(duì)路徑当船,結(jié)果就是q
  • 否則题画,根據(jù)文件系統(tǒng)規(guī)則,將“p后跟q"作為結(jié)果

resolve方法有一種快捷方式德频,它接受一個(gè)字符串而不是路徑:
Path workPath = basePath.resolve("work");
還有一個(gè)很方便的方法:resolveSibling苍息,它通過解析指定路徑的父路徑產(chǎn)生其兄弟路徑。例如壹置,如果workPath是/opt/myapp/work,那么下面的調(diào)用
Path tempPath = workPath.resolveSibling("temp");
將創(chuàng)建/opt/myapp/temp.

resolve的對(duì)立面是relativize竞思,即調(diào)用p.relativize(r)將產(chǎn)生路徑q,而對(duì)q進(jìn)行的解析的結(jié)果正是r 。例如钞护,以”/home/cay"為目標(biāo)對(duì)"/home/fred/myprog"進(jìn)行相對(duì)化操作盖喷,會(huì)產(chǎn)生“../fred/yprog",其中假設(shè)..表示的是文件系統(tǒng)的父目錄。

normalize方法將移除所有冗余的.和..部件(或文件系統(tǒng)認(rèn)為冗余的部件)
toAbsolutePath方法將產(chǎn)生給定路徑的絕對(duì)路徑难咕,該絕對(duì)路徑從根部件開始传蹈。

Path類有許多有用的方法用來將路徑斷開。
Path p = Paths.get("/home","fred","myproperties");
Path parent = p.getParent();
Path file = p.getFileName();
Path root = p.getRoot();

還可以從Path對(duì)象中構(gòu)建Scanner對(duì)象:
Scanner in = new Scanner(Paths.get("/home/fred/input.txt"));

需要與遺留系統(tǒng)交互時(shí)步藕,可以使用Path的toFile方法,或者File類的toPath方法

讀寫文件

Files類可以使得普通文件操作變得快捷挑格。例如可以使用下面方式很容易地讀取文件的所有內(nèi)容:
byte[] bytes = Files.readAllBytes(path);
如果想將文件當(dāng)作字符串讀入咙冗,可以在調(diào)用readAllBytes之后執(zhí)行:
String content = new String(bytes,charset);
如果希望文件當(dāng)作行序列讀入,可以調(diào)用:
List<String> lines = Files.readAllLines(path,charset);
相反漂彤,如果希望寫出一個(gè)字符串到文件中雾消,可以調(diào)用:
Files.write(path,content.getBytes(charset));
向指定文件追加內(nèi)容:可以調(diào)用:
Files.write(path,content,getBytes(charset),StandardOpenOption.APPEND);
可以用下面的語句將一個(gè)行的集合寫出到文件中:
Files.write(path,lines);
這些簡(jiǎn)便方法適用于處理中等長(zhǎng)度的文本文件,如果要處理的文本長(zhǎng)度較大挫望,或者是二進(jìn)制文件立润,還是應(yīng)該使用所熟知的輸入輸出流或者讀入器/寫出器。

創(chuàng)建文件和目錄

創(chuàng)建新目錄可以調(diào)用:
Files.createDirectory(path);
其中路徑中除最后一個(gè)部件外媳板,其他部分都必須是已存在的桑腮。要?jiǎng)?chuàng)建路徑中的中間目錄,應(yīng)該使用:
Files.createDirectories(path);
可以使用下面的語句創(chuàng)建一個(gè)空文件:
Files.createFile(path);
如果文件已存在蛉幸,那么這個(gè)調(diào)用就會(huì)異常破讨。

有些便捷方法可以用來在指定位置或者系統(tǒng)指定位置創(chuàng)建臨時(shí)文件和臨時(shí)目錄:
Path newPath = Files.createTempFile(dir,prefix,suffix);
Pah newPath = Files.createTempFile(prefix,suffix);
Pah newPath = Files.createTempDirectory(dir,prefix);
Pah newPath = Files.createTempDirectory(prefix);

其中丛晦,dir是一個(gè)Path對(duì)象,prefix和suffix是可以為null的字符串提陶。
創(chuàng)建文件或目錄烫沙,可以制定屬性,例如文件的擁有者和權(quán)限隙笆。锌蓄。

復(fù)制、移動(dòng)和刪除文件

將文件從一個(gè)位置復(fù)制到另一個(gè)位置可以直接調(diào)用
Files.copy(fromPath,toPath);
移動(dòng)文件可以調(diào)用:
Files.move(fromPath,toPath);
如果目標(biāo)路徑已經(jīng)存在撑柔,那么復(fù)制或移動(dòng)將失敗瘸爽。如果想要覆蓋已有的目標(biāo)路徑,可以使用REPLACE_EXISTING選項(xiàng)乏冀。如果想要復(fù)制所有的文件屬性蝶糯,可以使用COPY_ATTRIBUTES選項(xiàng)×韭伲可同時(shí)選擇這兩個(gè)選項(xiàng)昼捍。
可以使用ATOMIC_MOVE 將移動(dòng)操作定義為原子性的。
還可以將一個(gè)輸入流復(fù)制到Path中肢扯,這表示你想要將該輸入流存儲(chǔ)到硬盤上妒茬。類似地,可以將一個(gè)Path復(fù)制到輸出流中蔚晨≌ё辏可以使用下面的調(diào)用:
Files.copy(inputStream,toPath);
Files.copy(fromPath,outputStream);

刪除文件可以調(diào)用
Files.delete(path);
如果要?jiǎng)h除的文件不存在,這個(gè)方法就會(huì)拋出異常铭腕。因此可轉(zhuǎn)而使用:
boolean deleted = Files.deleteIfExits(path);

獲取文件信息

下面的靜態(tài)方法將返回boolean值银择,表示檢查路徑的某個(gè)屬性的結(jié)果:

  • exists
  • isHidden
  • isReadadble,isWritable,isExecuutable
  • isRegularFile, isDirectory, isSymbolicLink

size方法將返回文件的字節(jié)數(shù):
long filesize = Files.size(path);

getOwner方法將文件的擁有者作為java.nio.file.attribute.UserPrincipal的一個(gè)實(shí)例返回。
所有的文件兄臺(tái)那個(gè)都會(huì)報(bào)告一個(gè)基本屬性集累舷,他們被封裝在BasicFileAttributes接口中浩考,這些屬性與上述信息有部分重疊”挥基本文件屬性包括:

  • 創(chuàng)建文件析孽、最后一次訪問、最后一次修改的時(shí)間只怎,這些時(shí)間都表示成java.nio.file.attribute.FileTime
  • 文件是常規(guī)文件袜瞬、目錄還是符號(hào)鏈接,或者都不是
  • 文件尺寸
  • 文件主鍵身堡,這是某種類的對(duì)象邓尤,具體所屬類與文件系統(tǒng)相關(guān),有可能是文件的唯一標(biāo)識(shí)符,也可能不是
    要獲取這些屬性,可以調(diào)用:
    BasicFileAttributes attributes = Files .readAttributes(path,BasicFileAttributes.class);
    如果了解到用戶的文件兄臺(tái)那個(gè)兼容POSIX,你可以獲取一個(gè)PosixFileAttributes實(shí)例:
    PosixFileAttributes attributes = Files .readAttributes(path,PosixFilesAttributes.class);
    然后從中找到擁有者裁赠,以及文件的擁有者殿漠、組或訪問權(quán)限。

訪問目錄的項(xiàng)

靜態(tài)的Files.list方法會(huì)返回一個(gè)可以讀取目錄中各個(gè)項(xiàng)的Stream<Path>對(duì)象佩捞。目錄是被惰性讀取的绞幌,這使得處理具有大量項(xiàng)的目錄可以變得更高效。
因?yàn)樽x取目錄設(shè)涉及需要關(guān)閉的系統(tǒng)資源一忱,所以應(yīng)使用try塊:
try( Stream<Path> entries = Files.list(pathToDirectory){...}
list 方法不會(huì)進(jìn)入子目錄莲蜘。為了處理目錄中的所有子目錄,需要使用File.walk方法帘营。

使用目錄流

Files.walk方法會(huì)產(chǎn)生一個(gè)可以遍歷目錄中所有子目錄的Stream<Path>對(duì)象票渠。有時(shí)需要對(duì)遍歷過程進(jìn)行更加細(xì)粒度的控制。在這種情況下芬迄,應(yīng)該使用File.newDirectoryStream對(duì)象问顷,它會(huì)產(chǎn)生一個(gè)DirectoryStream。注意它不是java.util.stream.Stream的子接口禀梳,而是專門用于目錄遍歷的接口杜窄。它是Iterable的子接口,可以在增強(qiáng)的for循環(huán)中使用目錄流算途。
try (DirectoryStream<Path> entries = Files.newDirectoryStream(dir)){
for (Path entry: entries){
Process entries
}}

try語句塊用來確保目錄流可以被正確關(guān)閉塞耕。訪問目錄中的 項(xiàng)并沒有具體的順序∽烊浚可以用glob模式來過濾文件:
try (DirectoryStream<Path> entries = Files.newDirectoryStream(dir,"*.java"))
glob模式待完善扫外。。

ZIP文件系統(tǒng)

Paths類會(huì)在默認(rèn)文件系統(tǒng)中查找路徑廓脆,即在用戶本地磁盤中的文件筛谚,最有用之一的是ZIP文件系統(tǒng)。如果zipname 是某個(gè)ZIP文件的名字停忿,那么下面的調(diào)用
FileSystem fs = FileSystem.newFileSystem(Path.get(zipname),null);
將建立一個(gè)文件系統(tǒng)驾讲,它包含ZIP文檔中的所有文件。如果知道文件名瞎嬉,那么從ZIP文檔中復(fù)制出這個(gè)文件就會(huì)變得很容易:
Files.copy(fs.getPath(soureceName),targetPath);
其中,fs.getPath對(duì)于任意文件系統(tǒng)來說厚柳,都與Paths.get類似
要列出ZIP文檔中的所有文件氧枣,可以遍歷文件樹:
FileSystem fs = FileSystem.newFileSystem(Path.get(zipname),null);
Files.walkFileTreee(fs.getPath("/"),new SimpleFileVisitor<Path>(){
public FileVisitResult visitFile(Path file,BasicFileAttributes attrs) throws IOException{
System.out.pintln(file);
return FileVisitResult.CONTINUE;}
});

內(nèi)存映射文件

大多數(shù)操作系統(tǒng)都可以利用虛擬內(nèi)存實(shí)現(xiàn)來將一個(gè)文件或文件的一部分“映射”到內(nèi)存中。然后别垮,這個(gè)文件就可以當(dāng)作是內(nèi)存數(shù)組一樣訪問便监,這比傳統(tǒng)的文件操作要快得多。

內(nèi)存映射文件的性能

與隨機(jī)訪問相比,性能提高總是很明顯的烧董。另一方面毁靶,對(duì)于中等尺寸文件的順序讀入則沒有必要使用內(nèi)存映射。

java.nio包使用內(nèi)存映射變得十分簡(jiǎn)單逊移。
首先预吆,從文件中獲得一個(gè)通道(channel),通道是用于磁盤文件的一種抽象,它使我們可以訪問諸如內(nèi)存映射胳泉、文件加鎖機(jī)制以及文件間快速數(shù)據(jù)傳遞等操作系統(tǒng)特性拐叉。
FileChannel channel = FileChannel.open(path,optios);
然后,通過調(diào)用FileChannel類的map方法從這個(gè)通道中獲得一個(gè)ByteBuffer扇商》锸荩可以指定想要映射的文件區(qū)域與映射模式,支持的模式有三種:

  • FileChannel.MapMode.READ_ONLY: 所產(chǎn)生的緩沖區(qū)是只讀的案铺,任何對(duì)緩沖區(qū)寫入的嘗試都會(huì)導(dǎo)致ReadOnlyBufferedException異常蔬芥。
  • FileChannel.MapMode.READ_WRTE:所產(chǎn)生的緩沖區(qū)是可寫的,任何修改都會(huì)在某個(gè)時(shí)刻寫回到文件中控汉。注意:其他映射同一個(gè)文件的程序可能不能立即看到這些修改笔诵,多個(gè)程序同時(shí)進(jìn)行文件映射的確切行為是依賴于操作系統(tǒng)的。
  • FileChannel.MapMode.PRIVATE: 所產(chǎn)生的緩沖區(qū)是科協(xié)的暇番,但是任何修改過這個(gè)緩沖區(qū)來說是私有的嗤放,不會(huì)傳播到文件中。

一旦有了緩沖區(qū)壁酬,就可以使用ByteBuffer類和Buffer超類的方法讀寫數(shù)據(jù)了次酌。
緩沖區(qū)支持順序和隨機(jī)訪問數(shù)據(jù)訪問,它有一個(gè)可以通過get和put操作來移動(dòng)的位置舆乔。例如岳服,可以像下面這樣順序遍歷緩沖區(qū)的所有字節(jié):

while (buffer.hasRemaining()){
byte b = buffer.get();
}
或者下下面這樣進(jìn)行隨機(jī)訪問:
for ( int i=0;i < buffer.limit(); i++){
byte b = buffer.get(i);
}

可以用下面 的方法讀寫字節(jié)數(shù)組:
get(byte[] byte)
get(byte[], int offset, int length)

最后,還有下面的方法:
getInt
getLong
getShort
getChar
getFloat
getDouble
用來讀入文件中存儲(chǔ)為二進(jìn)制的基本類型值希俩。
Java對(duì)二進(jìn)制數(shù)據(jù)使用高位在前 的排序機(jī)制吊宋,但是,如果需要以地位在前的排序方式處理包含二進(jìn)制數(shù)字的文件颜武,只需調(diào)用
buffer.order(ByteOrder.LITTLE_ENDIAN);
要查詢緩沖區(qū)內(nèi)當(dāng)前的字節(jié)順序璃搜,可以調(diào)用:
ByteOrder b = buffer.order();
要向緩沖區(qū)寫數(shù)字,可以使用下列方法:
putInt
putLong
putShort
putChar
putFloat
putDouble
在恰當(dāng)?shù)臅r(shí)候鳞上,以及當(dāng)通道關(guān)閉時(shí)这吻,會(huì)將這些修改寫回到文件中。

緩沖區(qū)數(shù)據(jù)結(jié)構(gòu)

本節(jié)簡(jiǎn)要介紹Buffer對(duì)象上的基本操作篙议。
緩沖區(qū)是由具有相同類型的數(shù)值構(gòu)成的數(shù)組唾糯,Buffer類是一個(gè)抽象類怠硼,它由眾多的具體子類,包括ByteBufer移怯,CharBuffer,DoubleBuffer,IntBuffer,LongBuffer和ShortBuffer香璃。

StringBuffer類與這些緩沖區(qū)沒有關(guān)系。

在實(shí)踐中舟误,最常用的是ByteBuffer和CharBuffer葡秒,如圖,每個(gè)緩沖區(qū)都具有:

  • 一個(gè)容量,它永遠(yuǎn)不能改變
  • 一個(gè)讀寫位置脐帝,下一個(gè)值將在此進(jìn)行讀寫
  • 一個(gè)界限同云,超過它進(jìn)行讀寫沒有意義
  • 一個(gè)可選的標(biāo)記,用于重復(fù)一個(gè)讀入或?qū)懗霾僮?/li>
一個(gè)緩沖區(qū)

這些值滿足下面的條件:
0<=標(biāo)記<=位置<=界限<=容量
使用緩沖區(qū)的主要目的是執(zhí)行“寫堵腹,然后讀入”循環(huán)炸站。假設(shè)我們有一個(gè)緩沖區(qū),在一開始疚顷,他的位置為0旱易,界限等于容量。我們不斷地調(diào)用put將值添加到這個(gè)緩沖區(qū)中腿堤,當(dāng)我們耗盡所有的數(shù)據(jù)或者寫出的數(shù)據(jù)量達(dá)到容量大小時(shí)阀坏,就該切換到讀入操作了。
這時(shí)調(diào)用flip方法將界限設(shè)置到當(dāng)前位置笆檀,并把位置復(fù)位到0.現(xiàn)在在remaining方法返回正整數(shù)時(shí)忌堂,不斷地調(diào)用get。在我們緩沖區(qū)素養(yǎng)的值都讀入之后酗洒,調(diào)用clear使緩沖區(qū)為下一次寫循環(huán)做好準(zhǔn)備士修。clear方法將位置復(fù)位到0,并將界限復(fù)位到容量樱衷。

如果想重讀緩沖區(qū)棋嘲,可以使用rewined或mark/reset方法。
要獲取緩沖區(qū)矩桂,可以調(diào)用諸如ByteBuffer.allocate或ByteBuffer.wrap這樣的靜態(tài)方法沸移。
然后,可以用來自某個(gè)通道的數(shù)據(jù)填充緩沖區(qū)侄榴,或者將緩沖區(qū)的內(nèi)容寫出通道中雹锣。例如:
ByteBuffer buffer = ByteBuffer.allocate(RECORD_SIZE);
channel.read(buffer);
channel.position(newpos);
buffer.flip();
channel.write(buffer);
這是一種非常有用的方法,可以替代隨機(jī)訪問文件癞蚕。

文件加鎖機(jī)制

考慮一下多個(gè)同時(shí)執(zhí)行的程序需要修改同一個(gè)文件的情形蕊爵,很明顯,這些程序需要以某種方式進(jìn)行通信涣达,不然這個(gè)文件很容易被損壞在辆。文件鎖可以解決問題,它可以控制對(duì)文件或文件中某個(gè)范圍的字節(jié)的訪問度苔。

假設(shè)你的應(yīng)用程序?qū)⒂脩舻钠么鎯?chǔ)在一個(gè)配置文件中匆篓,當(dāng)用戶調(diào)用這個(gè)應(yīng)用的兩個(gè)實(shí)例時(shí),這兩個(gè)實(shí)例就有可能會(huì)同時(shí)希望寫這個(gè)配置文件寇窑。這種情況下鸦概,第一個(gè)實(shí)例應(yīng)該鎖定這個(gè)文件,當(dāng)?shù)诙€(gè)實(shí)例發(fā)現(xiàn)這個(gè)文件被鎖定時(shí)甩骏,它必須決策是等待直至這個(gè)文件解鎖窗市,還是直接跳過這個(gè)文件寫操作。

要鎖定一個(gè)問價(jià)饮笛,可以調(diào)用FileChannel類的lock和tryLock方法:
FileChannel = FileChannel.open(path);
FileLock lock = channel.lock();

FileLock lock = channel.rtyLock();

第一個(gè)調(diào)用會(huì)阻塞直至可獲得鎖咨察,而第二個(gè)調(diào)用將立即返回,要么返回鎖福青,要么在鎖不可獲得的情況下返回null摄狱。這個(gè)文件將保持鎖定狀態(tài),直至這個(gè)通道關(guān)閉无午,或者在鎖上調(diào)用了release方法媒役。

還可以通過下面的調(diào)用鎖定文件的一部分:
FileLock lock (long start,long size,boolean shared)

FileLock tryLock(long start,long size,boolean shared)

如果shared標(biāo)志為false,則鎖定文件的目的是讀寫,而如果為true,則這是一個(gè)共享鎖宪迟,它允許多個(gè)進(jìn)程從文件中讀入酣衷,并阻止任何進(jìn)程獲得獨(dú)占的鎖。并非所有的操作系統(tǒng)都支持共享鎖次泽,因此你可能會(huì)在請(qǐng)求共享鎖的時(shí)候得到的是獨(dú)占的鎖穿仪。
調(diào)用FileLock類的isShared方法可以查詢所持有的鎖的類型。

如果鎖定了文件的尾部箕憾,而這個(gè)文件的長(zhǎng)度隨后增長(zhǎng)了超過鎖定的部分牡借,那么增長(zhǎng)出來的額外區(qū)域是未鎖定的,要想鎖定所有字節(jié)袭异,可以使用Long.MAX_VALUE來表示尺寸钠龙。
要確保在操作完成時(shí)釋放鎖,與往常一樣,最好在一個(gè)try語句中執(zhí)行釋放鎖的操8作:
try(FileLock lock = channel.lock()){
access the locked file or segment
}
文件加鎖機(jī)制時(shí)依賴操作系統(tǒng)的

  • 在某些系統(tǒng)中御铃,文件加鎖僅僅是建議性的碴里,如果是一個(gè)應(yīng)用未能得到鎖,它仍舊可以向被另一個(gè)應(yīng)用并發(fā)鎖定的文件執(zhí)行寫操作上真。
  • 在某些系統(tǒng)中咬腋,不能在鎖定一個(gè)文件的同時(shí)將其映射到內(nèi)存中。
    文件是由整個(gè)Java虛擬機(jī)持有的睡互。如果有兩個(gè)程序是由同一個(gè)虛擬機(jī)啟動(dòng)的根竿,那么它們不可能每一個(gè)都獲得一個(gè)在同一個(gè)文件上的鎖陵像。當(dāng)調(diào)用lock和tryLock方法時(shí),如果虛擬機(jī)已經(jīng)在同一個(gè)文件上持有了另一個(gè)重疊的鎖寇壳,那么這兩個(gè)方法將拋出OverlappingFileLockException醒颖。
  • 在一些系統(tǒng)中,關(guān)閉一個(gè)通道會(huì)釋放由Java虛擬機(jī)持有的底層文件上的所有鎖壳炎。因此迄薄,在同一個(gè)鎖定文件山海關(guān)應(yīng)避免使用多個(gè)通道板甘。
  • 在網(wǎng)絡(luò)文件系統(tǒng)上鎖定文件時(shí)高度依賴與系統(tǒng)的,因此應(yīng)盡量避免。

正則表達(dá)式

這部分單獨(dú)作為一個(gè)章節(jié)列出徒爹。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末栅盲,一起剝皮案震驚了整個(gè)濱河市野蝇,隨后出現(xiàn)的幾起案子谁尸,更是在濱河造成了極大的恐慌,老刑警劉巖稼病,帶你破解...
    沈念sama閱讀 211,290評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件挠阁,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡溯饵,警方通過查閱死者的電腦和手機(jī)侵俗,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來丰刊,“玉大人隘谣,你說我怎么就攤上這事∽那桑” “怎么了寻歧?”我有些...
    開封第一講書人閱讀 156,872評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)秩仆。 經(jīng)常有香客問我码泛,道長(zhǎng),這世上最難降的妖魔是什么澄耍? 我笑而不...
    開封第一講書人閱讀 56,415評(píng)論 1 283
  • 正文 為了忘掉前任噪珊,我火速辦了婚禮,結(jié)果婚禮上齐莲,老公的妹妹穿的比我還像新娘痢站。我一直安慰自己,他們只是感情好选酗,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,453評(píng)論 6 385
  • 文/花漫 我一把揭開白布阵难。 她就那樣靜靜地躺著,像睡著了一般芒填。 火紅的嫁衣襯著肌膚如雪呜叫。 梳的紋絲不亂的頭發(fā)上空繁,一...
    開封第一講書人閱讀 49,784評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音朱庆,去河邊找鬼家厌。 笑死,一個(gè)胖子當(dāng)著我的面吹牛椎工,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播蜀踏,決...
    沈念sama閱讀 38,927評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼维蒙,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了果覆?” 一聲冷哼從身側(cè)響起颅痊,我...
    開封第一講書人閱讀 37,691評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎局待,沒想到半個(gè)月后斑响,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,137評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡钳榨,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,472評(píng)論 2 326
  • 正文 我和宋清朗相戀三年舰罚,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片薛耻。...
    茶點(diǎn)故事閱讀 38,622評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡营罢,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出饼齿,到底是詐尸還是另有隱情饲漾,我是刑警寧澤,帶...
    沈念sama閱讀 34,289評(píng)論 4 329
  • 正文 年R本政府宣布缕溉,位于F島的核電站考传,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏证鸥。R本人自食惡果不足惜僚楞,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,887評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望枉层。 院中可真熱鬧镜硕,春花似錦、人聲如沸返干。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽矩欠。三九已至财剖,卻和暖如春悠夯,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背躺坟。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來泰國(guó)打工沦补, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人咪橙。 一個(gè)月前我還...
    沈念sama閱讀 46,316評(píng)論 2 360
  • 正文 我出身青樓夕膀,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親美侦。 傳聞我的和親對(duì)象是個(gè)殘疾皇子产舞,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,490評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)菠剩,斷路器易猫,智...
    卡卡羅2017閱讀 134,629評(píng)論 18 139
  • 一、基礎(chǔ)知識(shí):1具壮、JVM准颓、JRE和JDK的區(qū)別:JVM(Java Virtual Machine):java虛擬機(jī)...
    殺小賊閱讀 2,369評(píng)論 0 4
  • 昨晚,我聽一個(gè)好朋友說棺妓,她失眠了攘已,最后終于睡著了,卻做了一個(gè)噩夢(mèng)怜跑。 她夢(mèng)見我和她兒子一起乘公司的電梯贯被,準(zhǔn)備上五樓,...
    瘋狂風(fēng)雨中閱讀 353評(píng)論 0 0
  • abaeccdce206閱讀 1,671評(píng)論 0 0
  • 最近的心態(tài)越來越差了妆艘,不管工作如何都感受不到意義彤灶,總感受不到自己的價(jià)值意義,一眼看去所有的人和事都是灰色的批旺,心情也...
    人生清茶閱讀 212評(píng)論 0 0