------<a href="http://www.itheima.com" target="_blank" rel="external nofollow" target="blank">Java教育訓練、Android教育訓練、iOS教育訓練、.Net教育訓練</a>、期待與您交流! -------
IO流的其他重要類
一.資料輸入輸出流:操作基本資料類型
DataInputStream:資料輸入流:
構造方法:DataInputStream(InputStream in):底層使用指定的InputStream 建立一個對象
DataOutputStream:資料輸出流:
構造方法:DataOutputStream(OutputStream out)建立一個新的資料輸出流,将資料寫入指定的基礎輸出流
可以讀寫任何的Java的基本資料類型;可以按照寫入的順序讀出資料,可以起到密碼保護的作用,檔案中根本看不出來是什麼。
二.記憶體操作流:(暫存)
ByteArrayOutputStream:向記憶體中寫入byte[]數組;
此類實作了一個輸出流,其中的資料被寫入一個 byte 數組。
緩沖區會随着資料的不斷寫入而自動增長。可使用 toByteArray() 和 toString() 擷取資料。
ByteArrayInputStream:從記憶體中讀取byte[]數組;
包含一個内部緩沖區,該緩沖區包含從流中讀取的位元組。
内部計數器跟蹤 read 方法要提供的下一個位元組。
作用:
緩存byte[]數組;就跟StringBuffer緩存String作用相同;将資料暫時存儲在記憶體中的緩存區中,以備使用,但是一旦斷電就沒有了。
public class Demo {
public static void main(String[] args) throws IOException{
//1.從檔案中讀取内容,一次讀取一個位元組數組;
//将每次讀取的位元組數組存儲到ByteArrayOutputStream中
FileInputStream in = new FileInputStream("demo08_1.txt");
//定義一個byte數組的緩存區
ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
//一次讀取一個位元組數組
byte[] byteArray = new byte[5];
int len = 0;
while((len = in.read(byteArray)) != -1){
//這個時候byte數組已經被填充,但這個數組現在沒有什麼用,不要轉換為字元串,也不需要寫入到檔案。可以将byte數組緩沖起來
byteOut.write(byteArray, 0, len);
}
//此循環結束後,所有的byte數組将會寫入到記憶體中;
System.out.println("寫入完畢!");
System.out.println("讀取byte數組:");
//取出所有的byte[]數組
byte[] byteArrayAll = byteOut.toByteArray();
//構造一個ByteArrayInputStream
ByteArrayInputStream byteIn = new ByteArrayInputStream(byteArrayAll);
//一次讀取一個位元組.此方式,同周遊byteArrayAll數組效果是一樣的。
int n = 0;
while((n = byteIn.read()) != -1){
System.out.println("讀取的位元組數:" + n + " 字元:" + (char)n);
}
}
}
注意:此例向記憶體中寫入的時候,是一個一個數組的寫入,而取出資料是,是一個位元組一個位元組的讀取。
三.列印流:(自動重新整理)
1.列印字元流:PrintWriter:
2.列印位元組流:PrintStream:
3.特點:
1).隻有輸出,沒有輸入;
2).可以輸出任何類型的資料;基本資料類和引用資料類型
3).有自動重新整理功能,如果啟用,部分功能将具有自動重新整理功能:
println(),printf(),format()隻有此三個方法有
4).可以操作檔案;可以寫入檔案
4.構造方法
PrintStream(OutputStream out)根據現有的 OutputStream 建立不帶自動行重新整理的新 PrintStream
PrintStream(OutputStream out,boolean autoFlush)過現有的 OutputStream 建立新的 PrintStream
PrintWriter(OutputStream out) :根據現有的 OutputStream 建立不帶自動行重新整理的新 PrintWriter。
PrintWriter(OutputStream out, boolean autoFlush) 通過現有的 OutputStream 建立新的 PrintWriter。
參數:autoFlush:true:自動重新整理;
PrintWriter(Writer out) 建立不帶自動行重新整理的新 PrintWriter。
PrintWriter(Writer out, boolean autoFlush) 建立新 PrintWriter。
注意:如果啟用了自動重新整理,則隻有在調用 println、printf 或 format 的其中一個方法時才可能完成此操作
★★★ println相當于write() + flush() + newLine()
5.使用列印流複制文本:
思路:
1.複制文本,就需要輸入流和輸出流;
2.列印流隻有輸出,沒有輸入;
3.用一個字元流做輸入;列印流做輸出
public class Demo {
public static void main(String[] args) throws IOException{
//1.輸入流:
BufferedReader in = new BufferedReader(new FileReader("demo16.txt"));
//2.輸出流
PrintWriter out = new PrintWriter(new FileWriter("demo16_copy.txt"),true);//帶自動重新整理
//一次讀取一行資料
String row = null;
while((row = in.readLine()) != null){
out.println(row);//writer() + flush() + newLine();
}
//關閉流
out.close();
in.close();
System.out.println("複制完畢");
}
}
四.标準輸入輸出流:(重要)(輸入方式,輸出見列印流)
1.System.in : 傳回的是InputStream類型的,實際上是InputStream子類對象的引用
Inputstream in=System.in
2.System.out: 傳回的是PrintStream類型的,這是一個OutputStream的子類
PrintStream out =System.out;
System.out.println("輸出流:" + out);//PrintStream
System.out.println("輸入流:" + in);//BufferedInputStream
通過列印可以知道輸出流的的确是printStream位元組列印流列印流
而輸入流卻是一個帶緩沖的位元組輸入流
out.println()方法列印字元數組名稱的,得到的是數組元素,而列印其他的卻是位址值。
3.将标準輸入流轉換為帶緩沖的字元輸入流:
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
如何實作的呢?
InputStreamReader streamReader = new InputStreamReader(System.in);
BufferedReader bufReader = new BufferedReader(streamReader);
4.三種方式實作擷取控制台資料:
1).Scanner();
2).main()方法的形參;
3).System.in;(可以經過轉換方法,一次讀取一行資料:帶緩沖的字元流。見下面執行個體)
5.一點小知識
InputStream in = System.in;
//一次讀取一個位元組。從控制台
int n = in.read();//程式會在read()方法阻塞,等待使用者輸入
System.out.println("讀取的位元組:" + n + " 讀取的字元:" + (char)n);
BufferedReader bufReader= new BufferedReader(new InputStreamReader(System.in));
String s = bufReader.readLine();
System.out.println("讀取的内容:" + s);
★★★ 若是在控制台輸入一些文字,按下Enter鍵,n會存儲第一個位元組的資訊,而s會接收之後的資訊。說明其内部有個光标機制,會自動移動讀取位置。
五.随機通路流:(指針特性)
1.RandomAccessFile:
2.它雖然在IO包但是不是流,直接繼承自Object
3.它内部結合了InputStream和OutputStream可以對檔案進行讀寫;
4.構造方法的mode參數:
RandomAccessFile(File file, String mode) 建立從中讀取和向其中寫入(可選)的随機通路檔案流,該檔案由 File 參數指定。
RandomAccessFile(String name, String mode):建立從中讀取和向其中寫入(可選)的随機通路檔案流,該檔案具有指定名稱。
"r" 以隻讀方式打開。調用結果對象的任何 write 方法都将導緻抛出 IOException。
"rw" 打開以便讀取和寫入。如果該檔案尚不存在,則嘗試建立該檔案。
"rws" 打開以便讀取和寫入,對于 "rw",還要求對檔案的内容或中繼資料的每個更新都同步寫入到底層儲存設備。
"rwd" 打開以便讀取和寫入,對于 "rw",還要求對檔案内容的每個更新都同步寫入到底層儲存設備。
5.getFilePointer()擷取檔案指針;
seek():設定檔案指針;
其檔案的寫入和讀出和Data資料操作流差不多,都具有檔案加密功能,按照順序來讀寫才可以,并且可以擷取和設定指針,來選擇讀取位置。
六.合并流
1.構造方法
public SequenceInputStream(InputStream s1,InputStream s2)
InputStream is1 = new FileInputStream("a.txt");
InputStream is2 = new FileInputStream("b.txt");
SequenceInputStream sis = new SequenceInputStream(is1, is2);
//将多個檔案寫入一個檔案中
InputStream is1 = new FileInputStream("a.txt");
InputStream is2 = new FileInputStream("b.txt");
Vector<InputStream> v = new Vector<InputStream>();
v.add(is1);
v.add(is2);
Enumeration<InputStream> en = v.elements();//傳回的 Enumeration 對象将生成此向量中的所有項。生成的第一項為索引 0 處的項,然後是索引 1 處的項,依此類推。
SequenceInputStream sis = new SequenceInputStream(en);//将集合中所有的輸入流都添加進來。
// 複制資料
FileOutputStream out = new FileOutputStream("copy.txt");
byte[] bys = new byte[1024];
int len = 0;
while ((len = sis.read(bys)) != -1) {
out.write(bys, 0, len);
}
out.close();
is1.close();
is2.close();
sis.close();
七.序列化和反序列化:(重要)
1.序列化和反序列化的概述
序列化:是可以将一個記憶體中的"對象"寫入到一個檔案中,包括這個對象中的所有屬性的值。
也可以将一個對象,通過網絡傳輸到另一台機器上。這個過程就叫:序列化;
反序列化:當将一個對象"序列化"後,我們可以将其"反序列化",将對象的資訊讀入到程式;例如:從一個檔案中讀取一個"對象的資訊";從網絡接收到一個對象的資訊。
2.序列化和反序列化的類:
ObjectOutputStream:序列化輸入流;
構造方法:public ObjectOutputStream(OutputStream out):
成員方法:public final void writeObject(Object obj):
注意:需要被序列化的類,必須實作Serializable接口。這個接口是一個"辨別性"的接口,沒有抽象方法,子類不需要重寫方法。
實作此接口的類,表示可以被序列化。
3.ObjectInputStream:反序列化輸出流:
構造方法:ObjectInputStream(InputStream in) 建立從指定 InputStream 讀取的 ObjectInputStream
成員方法:public final Object readObject(),傳回的是一個引用,用的時候需要強轉一下。可以取出對象的有關屬性。
注意:此時會産生一個全新的對象,和之前的對象毫無關系的,位址值并不一樣。
4.每個被序列化的類,都隐式的有一個屬性:serialVersionUID,而且有一個預設值。
如果我們不顯示定義這個屬性,java會自動添加一個這個屬性。
這時,如果我們将此類的對象序列化後,如果更改了類結構,這個屬性的值也會随之改變,那麼就與已經序列化的對象的這個屬性值不比對了。
當我們反序列化時,Java會驗證這個屬性值,如果不比對,則抛出:InvalidClassException異常;
建議:在需要序列化的類中顯示的定義serialVersionUID屬性,并賦予一個值。如果這個值不發生變化,反序列化就不會有問題。
5.transient關鍵字
防止有關成員變量被序列化。
沒有被序列化的變量,再次被取出時,其值就是個預設值,0或者null.
6.注意:
1).被序列化的對象,必須實作Serializable 接口;
2).被序列化的對象,建議顯示的定義成員常量:serialVersionUID
3).transient可以修飾成員變量不被序列化;
八.Properties類(及其重要)
1.它是Map的實作類,直接繼承自:Hashtable。是以它就是一個Map集合。
2.其内部直接繼承了輸入輸出流,可以用它來讀寫配置檔案資訊;
3.配置檔案:一般軟體運作時都會記錄一些使用者的"設定資訊"、"操作資訊"等一些資訊,記錄的目的是為了下次再啟動軟體時,
可以根據使用者上次的設定或使用情況,來顯示軟體;
配置檔案就是一個"文本檔案"。
它内部一般以"鍵值對"的形式存儲資訊。
4.其為map類
Map的方法:
put(Object key ,Object value):存儲元素
keySet():擷取鍵的集合
get(Object key):通過鍵擷取值
特有方法:
public Object setProperty(String key,String value): 用來存儲元素,類似于put(),傳回原來的值。
public String getProperty(String key): 擷取鍵的值相當于get()
public Set<String> stringPropertyNames(): 擷取所有鍵的值集合keySet()
注意,其中鍵和值的類型都是string類的。
5.與IO流一起使用,讀寫配置檔案:
public void load(Reader reader):從配置檔案中讀取屬性資訊
public void store(Writer writer,String comments):向配置檔案中寫入資訊。comments,檔案頭資訊。
private static void write() throws IOException{
Properties pro = new Properties();
//填充集合
pro.setProperty("疲勞值", "1200");
pro.setProperty("經驗", "5000");
pro.setProperty("金币", "500");
pro.setProperty("靈活", "20");
//寫入到檔案
FileWriter out = new FileWriter("demo23.properties");
pro.store(out, "demo23");
out.close();
private static void read() throws IOException{
Properties pro = new Properties();
//讀取
FileReader in = new FileReader("demo23.properties");
pro.load(in);//将會把檔案中的所有配置資訊,讀取到Properties中
in.close();
//解析Properties
Set<String> keySet = pro.stringPropertyNames();
for(String key : keySet){
System.out.println(key + "---" + pro.getProperty(key));
}
} 可見Properties隻是一個集合而已,若是要将檔案資訊寫入文本之中,還以要已開字元的輸入輸出流呢。
★★6.練習:修改指定鍵的值:
需求,将屬性寫入到配置檔案,周遊properties檔案看是否有什麼值,并修改
* 思路分析:
* 1.配置檔案一般是以鍵值對的形式存儲,那麼考慮使用Properties
* 2.建立字元輸入流,将檔案資訊讀入
* 3.周遊集合,尋找鍵值,然後修改
* 4.輸出修改後的,建立輸出流。
*
* */
public class Demo {
public static void main(String[] args) throws IOException {
//建立集合
Properties pro=new Properties();
//填充集合
String str1=(String)pro.setProperty("技術", "100");
pro.setProperty("力量","20001" );
pro.setProperty("速度", "2000");
System.out.println(str1+pro.setProperty("靈活", "2234"));
//建立輸入流
FileWriter out=new FileWriter("demo.properties");
//集合寫入檔案
pro.store(out, "demo--name");
//out.close();
//讀取配置檔案中的内容
//建立檔案輸入流
FileReader in =new FileReader("demo.properties");
//讀取檔案資訊進入集合
pro.load(in);
in.close();
//周遊集合
//建立鍵值對的集合
Set<String> proset=pro.stringPropertyNames();
for(String pstr:proset){
System.out.println("鍵為--"+pstr+"--的,值為--"+pro.getProperty(pstr));
if (pstr.equals("技術")){
pro.setProperty(pstr, "10000");
System.out.println("鍵為---"+pstr+"---的值修改為---"+pro.getProperty(pstr));
}
}
//将修改後的再次寫入檔案
pro.store(out,"修改後的技術");
out.close();
}
}
九.新IO的方法:
Path:與平台無關的路徑。
Paths類:包含了傳回Path的靜态方法。
public static Path get(URI uri):根據給定的URI來确定檔案路徑。
Files類:操作檔案的工具類。提供了大量的方法,簡單了解如下方法
public static long copy(Path source, OutputStream out) :複制檔案
public static Path write(Path path, Iterable<? extends CharSequence> lines,Charset cs,OpenOption... options):
把集合的資料寫到檔案。
public class Demo {
public static void main(String[] args) throws IOException{
OutputStream out = new FileOutputStream("demo25_copy.txt");
Files.copy(Paths.get("demo16_copy.txt"), out);
out.close();
System.out.println("拷貝完成");
//将集合的資料寫入到檔案
ArrayList<String> strList = new ArrayList<>();
strList.add("張學友");
strList.add("劉德華");
strList.add("章子怡");
Files.write(Paths.get("demo25_2.txt"), strList, Charset.forName("GBK"));
System.out.println("寫入完畢!");
}
}