摘要:對象序列化對象序列化機(jī)制允許把內(nèi)存中的對象轉(zhuǎn)換成與平臺無關(guān)的二進(jìn)制流,從而可以保存到磁盤或者進(jìn)行網(wǎng)絡(luò)傳輸,其它程序獲得這個二進(jìn)制流后可以將其恢復(fù)成原來的對象。
對象序列化
對象序列化機(jī)制允許把內(nèi)存中的Java對象轉(zhuǎn)換成與平臺無關(guān)的二進(jìn)制流,從而可以保存到磁盤或者進(jìn)行網(wǎng)絡(luò)傳輸,其它程序獲得這個二進(jìn)制流后可以將其恢復(fù)成原來的Java對象。 序列化機(jī)制可以使對象可以脫離程序的運行而對立存在
序列化的含義和意義 序列化序列化機(jī)制可以使對象可以脫離程序的運行而對立存在
序列化(Serialize)指將一個java對象寫入IO流中,與此對應(yīng)的是,對象的反序列化(Deserialize)則指從IO流中恢復(fù)該java對象
如果需要讓某個對象可以支持序列化機(jī)制,必須讓它的類是可序列化(serializable),為了讓某個類可序列化的,必須實現(xiàn)如下兩個接口之一:
Serializable:標(biāo)記接口,實現(xiàn)該接口無須實現(xiàn)任何方法,只是表明該類的實例是可序列化的
Externalizable
所有在網(wǎng)絡(luò)上傳輸?shù)膶ο蠖紤?yīng)該是可序列化的,否則將會出現(xiàn)異常;所有需要保存到磁盤里的對象的類都必須可序列化;程序創(chuàng)建的每個JavaBean類都實現(xiàn)Serializable;
使用對象流實現(xiàn)序列化實現(xiàn)Serializable實現(xiàn)序列化的類,程序可以通過如下兩個步驟來序列化該對象:
1.創(chuàng)建一個ObjectOutputStream,這個輸出流是一個處理流,所以必須建立在其他節(jié)點流的基礎(chǔ)之上
// 創(chuàng)建個ObjectOutputStream輸出流 ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("object.txt"));
2.調(diào)用ObjectOutputStream對象的writeObject方法輸出可序列化對象
// 將一個Person對象輸出到輸出流中 oos.writeObject(per);
定義一個NbaPlayer類,實現(xiàn)Serializable接口,該接口標(biāo)識該類的對象是可序列化的
public class NbaPlayer implements java.io.Serializable { private String name; private int number; // 注意此處沒有提供無參數(shù)的構(gòu)造器! public NbaPlayer(String name, int number) { System.out.println("有參數(shù)的構(gòu)造器"); this.name = name; this.number = number; } // name的setter和getter方法 public void setName(String name) { this.name = name; } public String getName() { return this.name; } // number的setter和getter方法 public void setNumber(int number) { this.number = number; } public int getNumber() { return this.number; } }
使用ObjectOutputStream將一個NbaPlayer對象寫入磁盤文件
import java.io.*; public class WriteObject { public static void main(String[] args) { try( // 創(chuàng)建一個ObjectOutputStream輸出流 ObjectOutputStream oos = new ObjectOutputStream( new FileOutputStream("object.txt"))) { NbaPlayer player = new NbaPlayer("維斯布魯克", 0); // 將player對象寫入輸出流 oos.writeObject(player); } catch (IOException ex) { ex.printStackTrace(); } } }反序列化
從二進(jìn)制流中恢復(fù)Java對象,則需要使用反序列化,程序可以通過如下兩個步驟來序列化該對象:
1.創(chuàng)建一個ObjectInputStream輸入流,這個輸入流是一個處理流,所以必須建立在其他節(jié)點流的基礎(chǔ)之上
// 創(chuàng)建個ObjectInputStream輸入流 ObjectInputStream ois = new ObjectInputStream(new FileInputStream("object.txt"));
2.調(diào)用ObjectInputStream對象的readObject()方法讀取流中的對象,該方法返回一個Object類型的Java對象,可進(jìn)行強(qiáng)制類型轉(zhuǎn)換成其真實的類型
// 從輸入流中讀取一個Java對象,并將其強(qiáng)制類型轉(zhuǎn)換為Person類 Person p = (Person)ois.readObject();
從object.txt文件中讀取NbaPlayer對象的步驟
import java.io.*; public class ReadObject { public static void main(String[] args) { try( // 創(chuàng)建一個ObjectInputStream輸入流 ObjectInputStream ois = new ObjectInputStream( new FileInputStream("object.txt"))) { // 從輸入流中讀取一個Java對象,并將其強(qiáng)制類型轉(zhuǎn)換為NbaPlayer類 NbaPlayer player = (NbaPlayer)ois.readObject(); System.out.println("名字為:" + player.getName() + " 號碼為:" + player.getNumber()); } catch (Exception ex) { ex.printStackTrace(); } } }
反序列化讀取的僅僅是Java對象的數(shù)據(jù),而不是Java類,因此采用反序列化恢復(fù)Java對象時,必須提供Java對象所屬的class文件,否則會引發(fā)ClassNotFoundException異常;反序列化機(jī)制無須通過構(gòu)造器來初始化Java對象
如果使用序列化機(jī)制向文件中寫入了多個Java對象,使用反序列化機(jī)制恢復(fù)對象必須按照實際寫入的順序讀取。當(dāng)一個可序列化類有多個父類時(包括直接父類和間接父類),這些父類要么有無參的構(gòu)造器,要么也是可序列化的—否則反序列化將拋出InvalidClassException異常。如果父類是不可序列化的,只是帶有無參數(shù)的構(gòu)造器,則該父類定義的Field值不會被序列化到二進(jìn)制流中
對象引用的序列化如果某個類的Field類型不是基本類型或者String類型,而是另一個引用類型,那么這個引用類型必須是可序列化的,否則有用該類型的Field的類也是不可序列化的
public class AllStar implements java.io.Serializable { private String name; private NbaPlayer player; public AllStar(String name, NbaPlayer player) { this.name = name; this.player = player; } // name的setter和getter方法 public String getName() { return this.name; } public void setName(String name) { this.name = name; } // player的setter和getter方法 public NbaPlayer getPlayer() { return player; } public void setPlayer(NbaPlayer player) { this.player = player; } }Java特殊的序列化算法
所有保存到磁盤中的對象都有一個序列化編號
當(dāng)程序試圖序列化一個對象時,程序?qū)⑾葯z查該對象是否已經(jīng)被序列化過,只有該對象從未(在本次虛擬中機(jī))被序列化過,系統(tǒng)才會將該對象轉(zhuǎn)換成字節(jié)序列并輸出
如果某個對象已經(jīng)序列化過,程序?qū)⒅皇侵苯虞敵鲆粋€序列化編號,而不是再次重新序列化該對象
import java.io.*; public class WriteAllStar { public static void main(String[] args) { try( // 創(chuàng)建一個ObjectOutputStream輸出流 ObjectOutputStream oos = new ObjectOutputStream( new FileOutputStream("allStar.txt"))) { NbaPlayer player = new NbaPlayer("詹姆斯哈登", 13); AllStar allStar1 = new AllStar("西部全明星", player); AllStar allStar2 = new AllStar("首發(fā)后衛(wèi)", player); // 依次將四個對象寫入輸出流 oos.writeObject(allStar1); oos.writeObject(allStar2); oos.writeObject(player); oos.writeObject(allStar2); } catch (IOException ex) { ex.printStackTrace(); } } }
4個寫入輸出流的對象,實際上只序列化了3個,而且序列的兩個AllStar對象的player引用實際是同一個NbaPlayer對象。以下程序讀取序列化文件中的對象
import java.io.*; public class ReadAllStar { public static void main(String[] args) { try( // 創(chuàng)建一個ObjectInputStream輸出流 ObjectInputStream ois = new ObjectInputStream( new FileInputStream("allStar.txt"))) { // 依次讀取ObjectInputStream輸入流中的四個對象 AllStar star1 = (AllStar)ois.readObject(); AllStar star2 = (AllStar)ois.readObject(); NbaPlayer player = (NbaPlayer)ois.readObject(); AllStar star3 = (AllStar)ois.readObject(); // 輸出true System.out.println("star1的player引用和player是否相同:" + (star1.getPlayer() == player)); // 輸出true System.out.println("star2的player引用和player是否相同:" + (star2.getPlayer() == player)); // 輸出true System.out.println("star2和star3是否是同一個對象:" + (star2 == star3)); } catch (Exception ex) { ex.printStackTrace(); } } }
如果多次序列化同一個可變Java對象時,只有第一次序列化時才會把該Java對象轉(zhuǎn)換成字節(jié)序列并輸出
當(dāng)使用Java序列化機(jī)制序列化可變對象時,只有第一次調(diào)用WriteObject()方法來輸出對象時才會將對象轉(zhuǎn)換成字節(jié)序列,并寫入到ObjectOutputStream;即使在后面程序中,該對象的實例變量發(fā)生了改變,再次調(diào)用WriteObject()方法輸出該對象時,改變后的實例變量也不會被輸出
import java.io.*; public class SerializeMutable { public static void main(String[] args) { try( // 創(chuàng)建一個ObjectOutputStream輸入流 ObjectOutputStream oos = new ObjectOutputStream( new FileOutputStream("mutable.txt")); // 創(chuàng)建一個ObjectInputStream輸入流 ObjectInputStream ois = new ObjectInputStream( new FileInputStream("mutable.txt"))) { NbaPlayer player = new NbaPlayer("斯蒂芬?guī)炖?, 30); // 系統(tǒng)會player對象轉(zhuǎn)換字節(jié)序列并輸出 oos.writeObject(player); // 改變per對象的name實例變量 player.setName("塞斯庫里"); // 系統(tǒng)只是輸出序列化編號,所以改變后的name不會被序列化 oos.writeObject(player); NbaPlayer player1 = (NbaPlayer)ois.readObject(); //① NbaPlayer player2 = (NbaPlayer)ois.readObject(); //② // 下面輸出true,即反序列化后player1等于player2 System.out.println(player1 == player2); // 下面依然看到輸出"斯蒂芬?guī)炖?,即改變后的實例變量沒有被序列化 System.out.println(player2.getName()); } catch (Exception ex) { ex.printStackTrace(); } } }自定義序列化
在一些特殊的場景下,如果一個類里包含的某些實例變量是敏感信息,這時不希望系統(tǒng)將該實例變量值進(jìn)行實例化;或者某個實例變量的類型是不可序列化的,因此不希望對該實例變量進(jìn)行遞歸實例化,以避免引發(fā)java.io.NotSerializableException異常
當(dāng)對某個對象進(jìn)行序列化時,系統(tǒng)會自動把該對象的所有實例變量依次進(jìn)行序列化,如果某個實例變量引用到另一個對象,則被引用的對象也會被序列化;如果被引用的對象的實例變量也引用了其他對象,則被引用的對象也會被序列化,這種情況被稱為遞歸序列化
在實例變量前面使用transient關(guān)鍵字修飾,可以指定java序列化時無須理會該實例變量
public class NbaPlayer implements java.io.Serializable { private String name; private transient int number; // 注意此處沒有提供無參數(shù)的構(gòu)造器! public NbaPlayer(String name, int number) { System.out.println("有參數(shù)的構(gòu)造器"); this.name = name; this.number = number; } // name的setter和getter方法 public void setName(String name) { this.name = name; } public String getName() { return this.name; } // number的setter和getter方法 public void setNumber(int number) { this.number = number; } public int getNumber() { return this.number; } }
transient關(guān)鍵字只能用于修飾實例變量,不可修飾Java程序中的其他成分
import java.io.*; public class TransientTest { public static void main(String[] args) { try( // 創(chuàng)建一個ObjectOutputStream輸出流 ObjectOutputStream oos = new ObjectOutputStream( new FileOutputStream("transient.txt")); // 創(chuàng)建一個ObjectInputStream輸入流 ObjectInputStream ois = new ObjectInputStream( new FileInputStream("transient.txt"))) { NbaPlayer per = new NbaPlayer("克萊湯普森", 11); // 系統(tǒng)會per對象轉(zhuǎn)換字節(jié)序列并輸出 oos.writeObject(per); NbaPlayer p = (NbaPlayer)ois.readObject(); System.out.println(p.getNumber()); } catch (Exception ex) { ex.printStackTrace(); } } }
在序列化和反序列化過程中需要特殊處理的類應(yīng)該提供如下特殊簽名的方法,這些特殊的方法用以實現(xiàn)自定義:
private void writeObject(java.io.ObjectOutputStream out) throws IOException
private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException
private void readObjectNoData() throws ObjectStreamException
writeObject()方法負(fù)責(zé)寫入特定類的實例的狀態(tài),以便相應(yīng)的readObject()方法可以恢復(fù)它。通過重寫該方法,可以完全獲得對序列化機(jī)制的控制,自主決定哪些實例變量需要序列化,怎樣序列化。在默認(rèn)情況下,該方法會調(diào)用out.defaultWriteObject來保存Java對象的各實例變量,從而可以實現(xiàn)序列化Java對象狀態(tài)的目的
readObject()方法負(fù)責(zé)從流中讀取并恢復(fù)對象實例變量,通過重寫該方法,可以完全獲得對反序列化機(jī)制的控制,可以自主決定需要反序列化哪些實例變量,怎樣反序列化。在默認(rèn)情況下,該方法會調(diào)用in.defaultReadObject來恢復(fù)Java對象的非瞬態(tài)實例變量
通常情況下readObject()方法與writeObject()方法對應(yīng),如果writeObject()方法中對Java對象的實例變量進(jìn)行了一些處理,則應(yīng)該在readObject()方法中對該實例變量進(jìn)行相應(yīng)的反處理,以便正確恢復(fù)該對象
當(dāng)序列化流不完整時,readObjectNoData()方法可以用來正確地初始化反序列化的對象
import java.io.IOException; public class NbaPlayer implements java.io.Serializable { private String name; private int number; // 注意此處沒有提供無參數(shù)的構(gòu)造器! public NbaPlayer(String name, int number) { System.out.println("有參數(shù)的構(gòu)造器"); this.name = name; this.number = number; } // name的setter和getter方法 public void setName(String name) { this.name = name; } public String getName() { return this.name; } // number的setter和getter方法 public void setNumber(int number) { this.number = number; } public int getNumber() { return this.number; } private void writeObject(java.io.ObjectOutputStream out) throws IOException { // 將name實例變量值反轉(zhuǎn)后寫入二進(jìn)制流 out.writeObject(new StringBuffer(name).reverse()); out.writeInt(number); } private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException { // 將讀取的字符串反轉(zhuǎn)后賦給name實例變量 this.name = ((StringBuffer)in.readObject()).reverse().toString(); this.number = in.readInt(); } }
writeObject()方法存儲實例變量的順序應(yīng)該和readObject()方法中恢復(fù)實例變量的順序一致,否則將不能正?;謴?fù)該Java對象
ANY-ACCESS-MODIFIER Object writeReplace() 實現(xiàn)序列化某個對象時替換該對象
此writeReplace()方法將由序列化機(jī)制調(diào)用,只要該方法存在。因為該方法可以擁有私有(private),受保護(hù)的(protected)和包私有(package-private)等訪問權(quán)限,所以其子類有可能獲得該方法
下面程序的writeReplace()方法,這樣可以在寫入NbaPlayer對象時將該對象替換成ArrayList
// 重寫writeReplace方法,程序在序列化該對象之前,先調(diào)用該方法 private Object writeReplace() throws ObjectStreamException { ArrayList
Java的序列化機(jī)制保證在序列化某個對象之前,先調(diào)用該對象的writeReplace()方法,如果該方法返回另一個Java對象,則系統(tǒng)轉(zhuǎn)為序列化另一個對象。如下程序表面上是序列化NbaPlayer對象,但實際上序列化的是ArrayList
// 系統(tǒng)將player對象轉(zhuǎn)換字節(jié)序列并輸出 oos.writeObject(player); // 反序列化讀取得到的是ArrayList ArrayList list = (ArrayList)ois.readObject(); System.out.println(list);
系統(tǒng)在序列化某個對象之前,會先調(diào)用該對象的writeReplace()和writeObject()兩個方法,系統(tǒng)總是先調(diào)用被序列化對象的writeReplace()方法,如果該方法返回另一個對象,系統(tǒng)將再次調(diào)用另一個對象的writeReplace()方法,直到該方法不再返回另一個對象為止,程序最后將調(diào)用該對象的writeObject()方法來保存該對象的狀態(tài)
ANY-ACCESS-MODIFIER Object readResolve() throws ObjectStreamException實現(xiàn)保護(hù)性復(fù)制整個對象,緊挨著readObject()之后被調(diào)用,該方法的返回值將會代替原來反序列化的對象,而原來readObject()反序列化的對象將會立即丟棄
Java的序列化機(jī)制:首先調(diào)用writeReplace(),其次調(diào)用writeObject(),最后調(diào)用writeResolve()
readObject()方法在序列化單例類,枚舉類時尤其有用
反序列化機(jī)制在恢復(fù)java對象時無須調(diào)用構(gòu)造器來初始化java對象。從這個意義上來看,序列化機(jī)制可以用來"克隆"對象;所有單例類,枚舉類在實現(xiàn)序列化時都應(yīng)該提供readResolve()方法,這樣才可以保證反序列化的對象依然正常;readResolve()方法建議使用final修飾
另一種自定義序列化機(jī)制這種序列化方式完全由程序員決定存儲和恢復(fù)對象數(shù)據(jù)。要實現(xiàn)該目標(biāo),必須實現(xiàn)Externalizable接口,該接口里定義了如下兩個方法:
void readExternal(ObjectInput in):需要序列化的類實現(xiàn)readExternal()方法來實現(xiàn)反序列化。該方法調(diào)用DataInput(它是ObjectInput的父接口)的方法來恢復(fù)基本類型的實例變量值,調(diào)用ObjectInput的readObject()方法來恢復(fù)引用類型的實例變量值
void writeExternal(Object out):需要序列化的類實現(xiàn)該方法來保存對象的狀態(tài)。該方法調(diào)用DataOutput(它是ObjectOutput的父接口)的方法來保存基本類型的實例變量值,調(diào)用ObjectOutput的writeObject()方法來保存引用類型的實例變量值
import java.io.*; public class Player implements java.io.Externalizable { private String name; private int number; // 注意此處沒有提供無參數(shù)的構(gòu)造器! public Player(String name, int number) { System.out.println("有參數(shù)的構(gòu)造器"); this.name = name; this.number = number; } // 省略name與number的setter和getter方法 // name的setter和getter方法 public void setName(String name) { this.name = name; } public String getName() { return this.name; } // number的setter和getter方法 public void setNumber(int number) { this.number = number; } public int getNumber() { return this.number; } public void writeExternal(java.io.ObjectOutput out) throws IOException { // 將name實例變量的值反轉(zhuǎn)后寫入二進(jìn)制流 out.writeObject(new StringBuffer(name).reverse()); out.writeInt(number); } public void readExternal(java.io.ObjectInput in) throws IOException, ClassNotFoundException { // 將讀取的字符串反轉(zhuǎn)后賦給name實例變量 this.name = ((StringBuffer)in.readObject()).reverse().toString(); this.number = in.readInt(); } }
兩種序列化機(jī)制的對比
實現(xiàn)Serializable接口 | 實現(xiàn)Externalizable接口 |
---|---|
系統(tǒng)自動存儲必要信息 | 程序員決定存儲哪些信息 |
Java內(nèi)建支持,易于實現(xiàn),只需實現(xiàn)該接口即可,無須任何代碼支持 | 僅僅提供兩個空方法,實現(xiàn)該接口必須為兩個空方法提供實現(xiàn) |
性能略差 | 性能略好 |
對象序列化的注意事項:
對象的類名、實例變量(包括基本類型、數(shù)組、對其他對象的引用)都會被序列化;方法、類變量(即static修飾的成員變量)、transient實例變量(也被稱為瞬態(tài)實例變量)都不會被序列化
反序列化讀取的僅僅是Java對象的數(shù)據(jù),而不是Java類,因此采用反序列化恢復(fù)Java對象時,必須提供Java對象所屬的class文件,否則會引發(fā)ClassNotFoundException異常
實現(xiàn)Serializable接口的類如果需要讓某個實例變量不被序列化,則可以在該實例變量前加transient修飾符,而不是static關(guān)鍵字,雖然static關(guān)鍵字也可以達(dá)到這個效果,但static關(guān)鍵字不能這樣用
保證序列化對象的實例變量類型也是可序列化的,否則需要使用transient修飾該變量
當(dāng)通過文件、網(wǎng)絡(luò)來讀取序列化后的對象時,必須按照實際寫入的順序讀取
版本隨著項目的設(shè)計,系統(tǒng)的class文件也會升級,Java如何保證兩個class文件的兼容性?為了在反序列化時確保序列化版本的兼容性,最好在每個要序列化的類中加入private static final long serialVersionUID這個屬性,具體數(shù)值自定義。這樣,即使某個類在與之對應(yīng)的對象已經(jīng)序列化出去后做了修改,該對象依然可以被正確反序列化
如不顯式定義該變量值,這個變量值將由JVM根據(jù)類的相關(guān)信息計算,而修改后的類的計算結(jié)果與修改前的類的計算結(jié)果往往不同,從而造成對象的反序列化因為類版本不兼容而失敗
導(dǎo)致該類實例的反序列化失敗的類修改操作:
如果修改類時僅僅修改了方法,則反序列化完全不受任何影響,類定義無需修改serizlVersionUID屬性值
如果修飾類時僅僅修改了靜態(tài)屬性或瞬態(tài)(transient)屬性,則反序列化不受任何影響,類定義無需修改serialVersionUID屬性值
如果修改類時修飾了非靜態(tài)、非瞬態(tài)屬性,則可能導(dǎo)致序列化版本不兼容,如果對象流中的對象和新類中包含同名的屬性,而屬性類型不同,則反序列化失敗 ,類定義應(yīng)該更新serialVersionUID屬性值。如果新類比對象流中對象包含更多的 屬性,序列化版本也可以兼容,類定義可以不更新serialVersionUID屬性值;但反序列化得到的新對象中多出的屬性值都是null(引用類型屬性)或0(基本類型屬性)
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.hztianpu.com/yun/66592.html
摘要:在中,對象的序列化與反序列化被廣泛應(yīng)用到遠(yuǎn)程方法調(diào)用及網(wǎng)絡(luò)傳輸中。相關(guān)接口及類為了方便開發(fā)人員將對象進(jìn)行序列化及反序列化提供了一套方便的來支持。未實現(xiàn)此接口的類將無法使其任何狀態(tài)序列化或反序列化。 序列化與反序列化 序列化 (Serialization)是將對象的狀態(tài)信息轉(zhuǎn)換為可以存儲或傳輸?shù)男问降倪^程。一般將一個對象存儲至一個儲存媒介,例如檔案或是記億體緩沖等。在網(wǎng)絡(luò)傳輸過程中,可以...
摘要:序列化對象和平臺無關(guān),序列化得到的字節(jié)流可以在任何平臺反序列化。從文件中或網(wǎng)絡(luò)上獲得序列化的字節(jié)流后,根據(jù)字節(jié)流中所保存的對象狀態(tài)及描述信息,通過反序列化重建對象。因此意味著不要序列化靜態(tài)變量不屬于對象狀態(tài)的一部分,因此它不參與序列化。 一.序列化和反序列化(1)序列化:將內(nèi)存中的對象轉(zhuǎn)化為字節(jié)序列,用于持久化到磁盤中或者通過網(wǎng)絡(luò)傳輸。對象序列化的最主要的用處就是傳遞和保存對象,保證對...
摘要:使用對象序列化,在保存對象時,會把其狀態(tài)保存為一組字節(jié),在未來,再將這些字節(jié)組裝成對象。由此可知,對象序列化不會關(guān)注類中的靜態(tài)變量。對象的讀寫類中對象的序列化工作是通過和來完成的。這就是為什么在此序列化過程中的無參構(gòu)造器會被調(diào)用。 Java對象的序列化 Java平臺允許我們在內(nèi)存中創(chuàng)建可復(fù)用的Java對象,但一般情況下,只有當(dāng)JVM處于運行時,這些對象才可能存在,即,這些對象的生命周...
摘要:虛擬機(jī)讀取其他進(jìn)程的數(shù)據(jù)對象的方法可以運行平臺上的其他程序該方法產(chǎn)生一個對象對象代表由該程序啟動啟動的子進(jìn)程類提供如下三個方法用于和其子進(jìn)程通信獲取子進(jìn)程的錯誤流獲取子進(jìn)程的輸入流獲取子進(jìn)程的輸出流這里的輸入流輸出流容易混淆從程序的角度思考 Java虛擬機(jī)讀取其他進(jìn)程的數(shù)據(jù) Runtime對象的exec方法可以運行平臺上的其他程序,該方法產(chǎn)生一個Process對象,Process對象...
閱讀 1274·2021-11-17 09:33
閱讀 3697·2021-09-28 09:42
閱讀 3470·2021-09-13 10:35
閱讀 2686·2021-09-06 15:00
閱讀 2516·2021-08-27 13:12
閱讀 3670·2021-07-26 23:38
閱讀 2007·2019-08-30 15:55
閱讀 604·2019-08-30 15:53