轉(zhuǎn)換流
- 輸入字節(jié)流的轉(zhuǎn)換流:InputStreamReader 是字節(jié)流通向字符流的橋InputStreamReader
- 輸出字節(jié)流的轉(zhuǎn)換流:OutputStreamWriter 可以把輸出字節(jié)流轉(zhuǎn)換成輸出字符流
- 轉(zhuǎn)換流的作用:
- 如果目前所 獲取到的是一個(gè)字節(jié)流需要轉(zhuǎn)換字符流使用么伯,這時(shí)候就可以使用轉(zhuǎn)換流瓢省。 字節(jié)流----> 字符流
- 使用轉(zhuǎn)換流可以指定編碼表進(jìn)行讀寫文件线梗。
public class Demo{
public static void main(String[] args) throws IOException {
// readTest();
// writeTest();'
// writeTest2();
readTest2();
}
//使用輸入字節(jié)流的轉(zhuǎn)換流指定碼表進(jìn)行讀取文件數(shù)據(jù)
public static void readTest2() throws IOException{
File file = new File("F:\\a.txt");
FileInputStream fileInputStream = new FileInputStream(file);
//創(chuàng)建字節(jié)流的轉(zhuǎn)換流并且指定碼表進(jìn)行讀取
InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream,"utf-8");
char[] buf = new char[1024];
int length = 0;
while((length = inputStreamReader.read(buf))!=-1){
System.out.println(new String(buf,0,length));
}
}
//使用輸出字節(jié)流的轉(zhuǎn)換流指定碼表寫出數(shù)據(jù)
public static void writeTest2() throws IOException{
File file = new File("F:\\a.txt");
//建立數(shù)據(jù)的輸出通道
FileOutputStream fileOutputStream = new FileOutputStream(file);
//把輸出字節(jié)流轉(zhuǎn)換成字符流并且指定編碼表。
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fileOutputStream, "utf-8");
outputStreamWriter.write("新中國(guó)好啊");
//關(guān)閉資源
outputStreamWriter.close();
}
public static void writeTest() throws IOException{
File file = new File("F:\\a.txt");
FileOutputStream fileOutputStream = new FileOutputStream(file);
//把輸出字節(jié)流轉(zhuǎn)換成輸出字符流淘菩。
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fileOutputStream);
outputStreamWriter.write("大家好");
outputStreamWriter.close();
}
public static void readTest() throws IOException{
InputStream in = System.in; //獲取了標(biāo)準(zhǔn)的輸入流。
// System.out.println("讀到 的字符:"+ (char)in.read()); //read()一次只能讀取一個(gè)字節(jié)削罩。
//需要把字節(jié)流轉(zhuǎn)換成字符流瞄勾。
InputStreamReader inputStreamReader = new InputStreamReader(in);
//使用字符流的緩沖類
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String line = null;
while((line = bufferedReader.readLine())!=null){
System.out.println("內(nèi)容:"+ line);
}
}
}
輸入字符流
-
Reader 輸入字符流的基類抽象類
- FileReader 讀取文件的輸入字符流
-
FileReader的用法:
- 找到目標(biāo)文件
- 建立數(shù)據(jù)的輸入通道
- 讀取數(shù)據(jù)
- 關(guān)閉資源
public class Demo {
public static void main(String[] args) throws IOException {
readTest2();
}
//使用緩沖字符數(shù)組讀取文件。
public static void readTest2() throws IOException{
//找到目標(biāo)文件
File file = new File("F:\\my.java");
// 建立數(shù)據(jù)的輸入通道
FileReader fileReader = new FileReader(file);
//建立緩沖字符數(shù)組讀取文件數(shù)據(jù)
char[] buf = new char[1024];
int length = 0 ;
while((length = fileReader.read(buf))!=-1){
System.out.print(new String(buf,0,length));
}
}
public static void readTest1() throws IOException{
//找到目標(biāo)文件
File file = new File("my.java");
//建立數(shù)據(jù)的輸入通道
FileReader fileReader = new FileReader(file);
int content = 0 ;
while((content = fileReader.read())!=-1){ //每次只會(huì)讀取一個(gè)字符弥激,效率低进陡。
System.out.print((char)content);
}
//關(guān)閉資源
fileReader.close();
}
}
輸出字符流
-
Writer 輸出字符流的基類 (抽象類)
- FileWriter 向文件數(shù)據(jù)數(shù)據(jù)的輸出字符流
-
FileWriter的使用步驟:
- 找到目標(biāo)文件
- 建立數(shù)據(jù)輸出通道
- 寫出數(shù)據(jù)
- 關(guān)閉資源
-
FileWriter要注意的事項(xiàng):
- 使用FileWriter寫數(shù)據(jù)的時(shí)候,F(xiàn)ileWriter內(nèi)部是維護(hù)了一個(gè)1024個(gè)字符數(shù)組的微服,寫數(shù)據(jù)的時(shí)候會(huì)先寫入到它內(nèi)部維護(hù)的字符數(shù)組中趾疚,如果需要把數(shù)據(jù)真正寫到硬盤上,需要調(diào)用flush或者是close方法或者是填滿了內(nèi)部的字符數(shù)組以蕴。
- 使用FileWriter的時(shí)候糙麦,如果目標(biāo)文件不存在,那么會(huì)自動(dòng)創(chuàng)建目標(biāo)文件丛肮。
3.使用FileWriter的時(shí)候赡磅, 如果目標(biāo)文件已經(jīng)存在了,那么默認(rèn)情況會(huì)先情況文件中的數(shù)據(jù)宝与,然后再寫入數(shù)據(jù) 焚廊, 如果需要在原來(lái)的基礎(chǔ)上追加數(shù)據(jù),需要使用“new FileWriter(File , boolean)”的構(gòu)造方法习劫,第二參數(shù)為true
練習(xí): 使用字符流拷貝一個(gè)文本文件(java文件)接著使用字符流拷貝一個(gè)圖片(觀察圖片的大小變化咆瘟,思考為什么會(huì)這樣子?)
public class Demo {
public static void main(String[] args) throws IOException {
writeTest1();
}
public static void writeTest1() throws IOException{
//找到目標(biāo)文件
File file = new File("F:\\a.txt");
//建立數(shù)據(jù)輸出通道
FileWriter fileWriter = new FileWriter(file,true);
//準(zhǔn)備數(shù)據(jù),把數(shù)據(jù)寫出
String data = "今天天氣非常好7汤铩袒餐!";
fileWriter.write(data); //字符流具備解碼的功能。
//刷新字符流
// fileWriter.flush();
//關(guān)閉資源
fileWriter.close();
}
}
對(duì)象的序列化
-
當(dāng)創(chuàng)建對(duì)象時(shí), 程序運(yùn)行時(shí)它就會(huì)存在, 但是程序停止時(shí), 對(duì)象也就消失了. 但是如果希望對(duì)象在程序不運(yùn)行的情況下仍能存在并保存其信息,將會(huì)非常有用灸眼,對(duì)象將被重建并且擁有與程序上次運(yùn)行時(shí)擁有的信息相同卧檐。可以使用對(duì)象的序列化
- 對(duì)象的序列化 : 將內(nèi)存中的對(duì)象直接寫入到文件設(shè)備中
- 對(duì)象的反序列化 : 將文件設(shè)備中持久化的數(shù)據(jù)轉(zhuǎn)換為內(nèi)存對(duì)象
-
基本的序列化由兩個(gè)方法產(chǎn)生 : 一個(gè)方法用于序列化對(duì)象并將它們寫入一個(gè)流幢炸,另一個(gè)方法用于讀取流并反序列化對(duì)象
- ObjectOutput -
writeObject(Object obj)
: 將對(duì)象寫入底層存儲(chǔ)或流 - ObjectInput -
readObject()
: 讀取并返回對(duì)象
- ObjectOutput -
-
ObjectOutputStream(對(duì)象的輸出流) / ObjectInputStream(對(duì)象的輸入流)
- 對(duì)象的輸入輸出流 : 對(duì)象的輸入輸出流主要的作用是用于寫對(duì)象的信息與讀取對(duì)象的信息, 對(duì)象信息一旦寫到文件上那么對(duì)象的信息就可以做到持久化了
- ObjectOutputStream的使用步驟:(對(duì)象輸入輸出流要注意的細(xì)節(jié))
- 如果對(duì)象需要被寫出到文件上泄隔,那么對(duì)象所屬的類必須要實(shí)現(xiàn)Serializable接口Serializable接口沒(méi)有任何的方法,是一個(gè)標(biāo)識(shí)接口而已
- 對(duì)象的反序列化創(chuàng)建對(duì)象的時(shí)候并不會(huì)調(diào)用到構(gòu)造方法的宛徊、
- serialVersionUID 是用于記錄class文件的版本信息的佛嬉,serialVersionUID這個(gè)數(shù)字是通過(guò)一個(gè)類的類名、成員闸天、包名暖呕、工程名算出的一個(gè)數(shù)字
- 使用ObjectInputStream反序列化的時(shí)候,ObjeectInputStream會(huì)先讀取文件中的serialVersionUID苞氮,然后與本地的class文件的serialVersionUID進(jìn)行對(duì)比湾揽,如果這兩個(gè)id不一致,那么反序列化就失敗了
- 如果序列化與反序列化的時(shí)候可能會(huì)修改類的成員笼吟,那么最好一開始就給這個(gè)類指定一個(gè)serialVersionUID库物,如果一類已經(jīng)指定的serialVersionUID,然后在序列化與反序列化的時(shí)候贷帮,jvm都不會(huì)再自己算這個(gè) class的serialVersionUID了
- 如果一個(gè)對(duì)象某個(gè)數(shù)據(jù)不想被序列化到硬盤上戚揭,可以使用關(guān)鍵字transient修飾
- 如果一個(gè)類維護(hù)了另外一個(gè)類的引用,那么另外一個(gè)類也需要實(shí)現(xiàn)Serializable接口
- 案例: 序列化和反序列化Cat對(duì)象
public class Demo3 {
public static void main(String[] args) throws IOException,
ClassNotFoundException {
Cat cat = new Cat("tom", 3);
FileOutputStream fos = new FileOutputStream(new File("c:\\Cat.txt"));
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(cat);
System.out.println(cat);
oos.close();
// 反序列化
FileInputStream fis = new FileInputStream(new File("c:\\Cat.txt"));
ObjectInputStream ois = new ObjectInputStream(fis);
Object readObject = ois.readObject();
Cat cat2 = (Cat) readObject;
System.out.println(cat2);
fis.close();
}
class Cat implements Serializable {
public String name;
public int age;
public Cat() {
}
public Cat(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Cat [name=" + name + ", age=" + age + "]";
}
}
- 例子關(guān)鍵點(diǎn):
1. 聲明Cat類實(shí)現(xiàn)了Serializable接口撵枢。是一個(gè)標(biāo)示器民晒,沒(méi)有要實(shí)現(xiàn)的方法
2. 新建Cat對(duì)象
3. 新建字節(jié)流對(duì)象(FileOutputStream)進(jìn)序列化對(duì)象保存在本地文件中
4. 新建ObjectOutputStream對(duì)象,調(diào)用writeObject方法序列化Cat對(duì)象
5. writeObject方法會(huì)執(zhí)行兩個(gè)工作:序列化對(duì)象锄禽,然后將序列化的對(duì)象寫入文件中
6. 反序列化就是調(diào)用ObjectInputStream的readObject()方法
7. 異常處理和流的關(guān)閉動(dòng)作要執(zhí)行
- Serializable
類通過(guò)實(shí)現(xiàn) java.io.Serializable 接口以啟用其序列化功能潜必。未實(shí)現(xiàn)此接口的類將無(wú)法使其任何狀態(tài)序列化或反序列化∥值可序列化類的所有子類型本身都是可序列化的磁滚。序列化接口沒(méi)有方法或字段,僅用于標(biāo)識(shí)可序列化的語(yǔ)義宵晚。所以需要被序列化的類必須是實(shí)現(xiàn)Serializable接口恨旱,該接口中沒(méi)有描述任何的屬性和方法,稱之為標(biāo)記接口坝疼。如果對(duì)象沒(méi)有實(shí)現(xiàn)接口Serializable,在進(jìn)行序列化時(shí)會(huì)拋出:NotSerializableException 異常
注意:保存一個(gè)對(duì)象的真正含義是什么谆沃?如果對(duì)象的實(shí)例變量都是基本數(shù)據(jù)類型钝凶,那么就非常簡(jiǎn)單。但是如果實(shí)例變量是包含對(duì)象的引用,會(huì)怎么樣耕陷?保存的會(huì)是什么掂名?很顯然在Java中保存引用變量的實(shí)際值沒(méi)有任何意義,因?yàn)镴ava引用的值是通過(guò)JVM的單一實(shí)例的上下文中才有意義哟沫。通過(guò)序列化后饺蔑,嘗試在JVM的另一個(gè)實(shí)例中恢復(fù)對(duì)象,是沒(méi)有用處的
-
如下:
- 首先建立一個(gè)Dog對(duì)象嗜诀,也建立了一個(gè)Collar對(duì)象猾警。Dog中包含了一個(gè)Collar(項(xiàng)圈)現(xiàn)在想要保存Dog對(duì)象,但是Dog中有一個(gè)Collar隆敢,意味著保存Dog時(shí)也應(yīng)該保存Collar发皿。假如Collar也包含了其他對(duì)象的引用,那么會(huì)發(fā)生什么拂蝎?意味著保存一個(gè)Dog對(duì)象需要清楚的知道Dog對(duì)象的內(nèi)部結(jié)構(gòu)穴墅。會(huì)是一件很麻煩的事情。
- Java的序列化機(jī)制可以解決該類問(wèn)題温自,當(dāng)序列化一個(gè)對(duì)象時(shí)玄货,Java的序列化機(jī)制會(huì)負(fù)責(zé)保存對(duì)象的所有關(guān)聯(lián)的對(duì)象(就是對(duì)象圖),反序列化時(shí)悼泌,也會(huì)恢復(fù)所有的相關(guān)內(nèi)容松捉。本例中:如果序列化Dog會(huì)自動(dòng)序列化Collar。但是券躁,只有實(shí)現(xiàn)了Serializable接口的類才可以序列化惩坑。如果只是Dog實(shí)現(xiàn)了該接口,而Collar沒(méi)有實(shí)現(xiàn)該接口也拜。會(huì)發(fā)生什么以舒?
Dog類和Collar類
import java.io.Serializable;
public class Dog implements Serializable {
private Collar collar;
private String name;
public Dog(Collar collar, String name) {
this.collar = collar;
this.name = name;
}
public Collar getCollar() {
return collar;
}
}
class Collar {
private int size;
public int getSize() {
return size;
}
public Collar(int size) {
this.size = size;
}
}
- 序列化
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
public class Demo4 {
public static void main(String[] args) throws IOException {
Collar coll = new Collar(10);
Dog dog = new Dog(coll, "旺財(cái)");
FileOutputStream fis = new FileOutputStream(new File("c:\\dog.txt"));
ObjectOutputStream os = new ObjectOutputStream(fis);
os.writeObject(dog);
}
}
- 執(zhí)行程序,出現(xiàn)了運(yùn)行時(shí)異常:
Exception in thread "main" java.io.NotSerializableException: Collar
- 所以我們也必須將Dog中使用的Collar序列化慢哈。但是如果我們無(wú)法訪問(wèn)Collar的源代碼蔓钟,或者無(wú)法使Collar可序列化,如何處理卵贱?
- 兩種解決方法:
- 繼承Collar類, 使子類可序列化, 但是如果Collar是final類, 就無(wú)法繼承了. 并且, 如果Collar引用了其他非序列化對(duì)象, 也無(wú)法解決該問(wèn)題
- transient : 此時(shí)就可以使用transient修飾符, 可以將Dog類中的成員變量標(biāo)識(shí)為transient, 那么在序列化Dog對(duì)象時(shí), 序列化就會(huì)跳過(guò)Collar
- 兩種解決方法:
public class Demo4 {
public static void main(String[] args) throws IOException,
ClassNotFoundException {
Collar coll = new Collar(10);
Dog dog = new Dog(coll, "旺財(cái)");
System.out.println(dog.getCollar().getSize());
FileOutputStream fis = new FileOutputStream(new File("c:\\d og.txt"));
ObjectOutputStream os = new ObjectOutputStream(fis);
os.writeObject(dog);
// 反序列化
FileInputStream fos = new FileInputStream(new File("c:\\dog.txt"));
ObjectInputStream ois = new ObjectInputStream(fos);
Object readObject = ois.readObject();
Dog dog2 = (Dog) readObject;
// Collar未序列化
dog2.getCollar().getSize();
}
}
- 這樣我們具有一個(gè)序列化的Dog和非序列化的Collar滥沫。此時(shí)反序列化Dog后,訪問(wèn)Collar键俱,就會(huì)出現(xiàn)運(yùn)行時(shí)異常 `` Exception in thread "main" java.lang.NullPointerException ``
- 注意:序列化不適用于靜態(tài)變量兰绣,因?yàn)殪o態(tài)變量并不屬于對(duì)象的實(shí)例變量的一部分。靜態(tài)變量隨著類的加載而加載编振,是類變量缀辩。由于序列化只適用于對(duì)象。
- 基本數(shù)據(jù)類型可以被序列化
public class Demo5 {
public static void main(String[] args) throws IOException {
// 創(chuàng)建序列化流對(duì)象
FileOutputStream fis = new FileOutputStream(new File("c:\\basic.txt"));
ObjectOutputStream os = new ObjectOutputStream(fis);
// 序列化基本數(shù)據(jù)類型
os.writeDouble(3.14);
os.writeBoolean(true);
os.writeInt(100);
os.writeInt(200);
// 關(guān)閉流
os.close();
// 反序列化
FileInputStream fos = new FileInputStream(new File("c:\\basic.txt"));
ObjectInputStream ois = new ObjectInputStream(fos);
System.out.println(ois.readDouble());
System.out.println(ois.readBoolean());
System.out.println(ois.readInt());
System.out.println(ois.readInt());
fos.close();
}
}
-
serialVersionUID : 用于給類指定一個(gè)UID。該UID是通過(guò)類中的可序列化成員的數(shù)字簽名運(yùn)算出來(lái)的一個(gè)long型的值臀玄。只要是這些成員沒(méi)有變化瓢阴,那么該值每次運(yùn)算都一樣。該值用于判斷被序列化的對(duì)象和類文件是否兼容健无。如果被序列化的對(duì)象需要被不同的類版本所兼容荣恐。可以在類中自定義UID累贤。
- 定義方式:
static final long serialVersionUID = 42L;
- 定義方式:
對(duì)象序列化示例:
class Address implements Serializable{
String country;
String city;
public Address(String country,String city){
this.country = country;
this.city = city;
}
}
class User implements Serializable{
private static final long serialVersionUID = 1L;
String userName ;
String password;
transient int age; // transient 透明
Address address ;
public User(String userName , String passwrod) {
this.userName = userName;
this.password = passwrod;
}
public User(String userName , String passwrod,int age,Address address) {
this.userName = userName;
this.password = passwrod;
this.age = age;
this.address = address;
}
@Override
public String toString() {
return "用戶名:"+this.userName+ " 密碼:"+ this.password+" 年齡:"+this.age+" 地址:"+this.address.city;
}
}
public class Demo {
public static void main(String[] args) throws IOException, Exception {
writeObj();
// readObj();
}
//把文件中的對(duì)象信息讀取出來(lái)-------->對(duì)象的反序列化
public static void readObj() throws IOException, ClassNotFoundException{
//找到目標(biāo)文件
File file = new File("F:\\obj.txt");
//建立數(shù)據(jù)的輸入通道
FileInputStream fileInputStream = new FileInputStream(file);
//建立對(duì)象的輸入流對(duì)象
ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
//讀取對(duì)象信息
User user = (User) objectInputStream.readObject(); //創(chuàng)建對(duì)象肯定要依賴對(duì)象所屬 的class文件叠穆。
System.out.println("對(duì)象的信息:"+ user);
}
//定義方法把對(duì)象的信息寫到硬盤上------>對(duì)象的序列化。
public static void writeObj() throws IOException{
//把user對(duì)象的信息持久化存儲(chǔ)畦浓。
Address address = new Address("中國(guó)","廣州");
User user = new User("admin","123",15,address);
//找到目標(biāo)文件
File file = new File("F:\\obj.txt");
//建立數(shù)據(jù)輸出流對(duì)象
FileOutputStream fileOutputStream = new FileOutputStream(file);
//建立對(duì)象的輸出流對(duì)象
ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
//把對(duì)象寫出
objectOutputStream.writeObject(user);
//關(guān)閉資源
objectOutputStream.close();
}
}
序列流 : SequenceInputStream
序列流痹束,對(duì)多個(gè)流進(jìn)行合并。SequenceInputStream 表示其他輸入流的邏輯串聯(lián)讶请。它從輸入流的有序集合開始祷嘶,并從第一個(gè)輸入流開始讀取,直到到達(dá)文件末尾夺溢,接著從第二個(gè)輸入流讀取论巍,依次類推,直到到達(dá)包含的最后一個(gè)輸入流的文件末尾為止风响。
合并兩個(gè)流 : 使用構(gòu)造函數(shù)
SequenceInputStream(InputStream s1, InputStream s2)
private static void testSequenceInputStream() throws IOException {
FileInputStream fis1 = new FileInputStream("c:\\a.txt");
FileInputStream fis2 = new FileInputStream("c:\\b.txt");
SequenceInputStream s1 = new SequenceInputStream(fis1, fis2);
int len = 0;
byte[] byt = new byte[1024];
FileOutputStream fos = new FileOutputStream("c:\\z.txt");
while ((len = s1.read(byt)) != -1) {
fos.write(byt, 0, len);
}
s1.close();
}
- 合并多個(gè)流:
public static void testSequenceInputStream() throws Exception {
InputStream in1 = new FileInputStream("c:/a.txt");
InputStream in2 = new FileInputStream("c:/b.txt");
InputStream in3 = new FileInputStream("c:/c.txt");
LinkedHashSet<InputStream> set = new LinkedHashSet<InputStream>();
set.add(in1);
set.add(in2);
set.add(in3);
final Iterator<InputStream> iter = set.iterator();
SequenceInputStream sin = new SequenceInputStream(
new Enumeration<InputStream>() {
@Override
public boolean hasMoreElements() {
return iter.hasNext();
}
@Override
public InputStream nextElement() {
return iter.next();
}
});
FileOutputStream out = new FileOutputStream("c:/z.txt");
for (int b = -1; (b = sin.read()) != -1;) {
out.write(b);
}
sin.close();
out.close();
}
- 案例:將map3歌曲文件進(jìn)行切割拷貝,并合并.
public class Demo2 {
public static void main(String[] args) throws IOException {
split(new File("c:\\a.mp3"), 10, new File("c:\\"));
System.out.println("切割完畢");
LinkedHashSet<InputStream> hs = new LinkedHashSet<InputStream>();
hs.add(new FileInputStream(new File("c:\\part.1.mp3")));
hs.add(new FileInputStream(new File("c:\\part.2.mp3")));
hs.add(new FileInputStream(new File("c:\\part.3.mp3")));
hs.add(new FileInputStream(new File("c:\\part.4.mp3")));
merage(hs, new File("c:\\merage.mp3"));
System.out.println("合并完畢");
}
private static void merage(LinkedHashSet<InputStream> hs, File dest)
throws IOException {
final Iterator<InputStream> it = hs.iterator();
FileOutputStream fos = new FileOutputStream(dest);
SequenceInputStream seq = new SequenceInputStream(
new Enumeration<InputStream>() {
@Override
public boolean hasMoreElements() {
return it.hasNext();
}
@Override
public InputStream nextElement() {
return it.next();
}
});
byte[] byt = new byte[1024 * 1024];
int len = 0;
while ((len = seq.read(byt)) != -1) {
fos.write(byt, 0, len);
}
seq.close();
fos.close();
}
// 1. 切割文件
/*
* 切割文件,切割份數(shù), 切割后保存路徑
*/
private static void split(File src, int count, File dir) throws IOException {
FileInputStream fis = new FileInputStream(src);
FileOutputStream fos = null;
byte[] byt = new byte[1024 * 1024];
int len = 0;
for (int i = 1; i <= count; i++) {
len = fis.read(byt);
if (len != -1) {
fos = new FileOutputStream(dir + "part." + i + ".mp3");
fos.write(byt, 0, len);
}
// fos.close();
}
fis.close();
}
}
- 合并文件
public class Demo {
public static void main(String[] args) throws IOException {
merge3();
}
// 把三個(gè)文件合并成一個(gè)文件
public static void merge3() throws IOException{
//找到目標(biāo)文件
File file1 = new File("F:\\a.txt");
File file2 = new File("F:\\b.txt");
File file3 = new File("F:\\c.txt");
File file4 = new File("F:\\d.txt");
//建立對(duì)應(yīng) 的輸入輸出流對(duì)象
FileOutputStream fileOutputStream = new FileOutputStream(file4);
FileInputStream fileInputStream1 = new FileInputStream(file1);
FileInputStream fileInputStream2 = new FileInputStream(file2);
FileInputStream fileInputStream3 = new FileInputStream(file3);
//創(chuàng)建序列流對(duì)象
Vector<FileInputStream> vector = new Vector<FileInputStream>();
vector.add(fileInputStream1);
vector.add(fileInputStream2);
vector.add(fileInputStream3);
Enumeration<FileInputStream> e = vector.elements();
SequenceInputStream sequenceInputStream = new SequenceInputStream(e);
//讀取文件數(shù)據(jù)
byte[] buf = new byte[1024];
int length = 0;
while((length = sequenceInputStream.read(buf))!=-1){
fileOutputStream.write(buf,0,length);
}
//關(guān)閉資源
sequenceInputStream.close();
fileOutputStream.close();
}
//使用SequenceInputStream合并文件
public static void merge2() throws IOException{
//找到目標(biāo)文件
File inFile1 = new File("F:\\a.txt");
File inFile2 = new File("F:\\b.txt");
File outFile = new File("F:\\c.txt");
//建立數(shù)據(jù)的輸入輸出通道
FileOutputStream fileOutputStream = new FileOutputStream(outFile);
FileInputStream fileInputStream1 = new FileInputStream(inFile1);
FileInputStream fileInputStream2 = new FileInputStream(inFile2);
//建立序列流對(duì)象
SequenceInputStream inputStream = new SequenceInputStream(fileInputStream1,fileInputStream2);
byte[] buf = new byte[1024];
int length = 0 ;
while((length = inputStream.read(buf))!=-1){
fileOutputStream.write(buf,0,length);
}
//關(guān)閉資源
inputStream.close();
fileOutputStream.close();
}
//需求:把a(bǔ).txt與b.txt 文件的內(nèi)容合并嘉汰。
public static void merge1() throws IOException{
//找到目標(biāo)文件
File inFile1 = new File("F:\\a.txt");
File inFile2 = new File("F:\\b.txt");
File outFile = new File("F:\\c.txt");
//建立數(shù)據(jù)的輸入輸出通道
FileOutputStream fileOutputStream = new FileOutputStream(outFile);
FileInputStream fileInputStream1 = new FileInputStream(inFile1);
FileInputStream fileInputStream2 = new FileInputStream(inFile2);
//把輸入流存儲(chǔ)到集合中,然后再?gòu)募现凶x取
ArrayList<FileInputStream> list = new ArrayList<FileInputStream>();
list.add(fileInputStream1);
list.add(fileInputStream2);
// 準(zhǔn)備一個(gè)緩沖數(shù)組
byte[] buf = new byte[1024];
int length = 0 ;
for(int i = 0 ; i< list.size() ; i++){
FileInputStream fileInputStream = list.get(i);
while((length = fileInputStream.read(buf))!=-1){
fileOutputStream.write(buf,0,length);
}
//關(guān)閉資源
fileInputStream.close();
}
fileOutputStream.close();
}
}
Properties
- 可以和流相關(guān)聯(lián)的集合對(duì)象Properties状勤。Map的子類:
Hashtable
&Properties
- Properties:該集合不需要泛型鞋怀,因?yàn)樵摷现械逆I值對(duì)都是String類型
- 存入鍵值對(duì) :
setProperty(key,value);
- 獲取指定鍵對(duì)應(yīng)的值 :
value getProperty(key);
- 獲取集合中所有鍵元素 :
Enumeration propertyNames()
; 在jdk1.6版本給該類提供一個(gè)新的方法 :Set<String> stringPropertyNames();
- 列出該集合中的所有鍵值對(duì),可以通過(guò)參數(shù)打印流指定列出到的目的地持搜。
list(PrintStream);
list(PrintWriter);
- 例:
list(System.out)
: 將集合中的鍵值對(duì)打印到控制臺(tái)
list(new PrintStream("prop.txt"))
: 將集合中的鍵值對(duì)存儲(chǔ)到prop.txt文件中
- 可以將流中的規(guī)則數(shù)據(jù)加載進(jìn)行集合密似,并稱為鍵值對(duì)。
load(InputStream):
jdk1.6版本提供了新的方法 -load(Reader):
- 注意:流中的數(shù)據(jù)要是"鍵=值" 的規(guī)則數(shù)據(jù)
- 可以將集合中的數(shù)據(jù)進(jìn)行指定目的的存儲(chǔ)葫盼。
store(OutputStram,String comment)
, jdk1.6版本提供了新的方法 -store(Writer ,String comment):
使用該方法存儲(chǔ)時(shí)残腌,會(huì)帶著當(dāng)時(shí)存儲(chǔ)的時(shí)間
- 存入鍵值對(duì) :
- 注意:Properties只加載key=value這樣的鍵值對(duì),與文件名無(wú)關(guān)贫导,注釋使用#
public static void sysPropList() throws IOException {
Properties prop = System.getProperties();
// prop.list(System.out);// 目的是控制臺(tái)抛猫。
// 需求是:將jvm的屬性信息存儲(chǔ)到一個(gè)文件中。
prop.list(new PrintStream("java.txt"));
}
public static void sysProp() {
Properties prop = System.getProperties();
Set<String> keys = prop.stringPropertyNames();
for (String key : keys) {
System.out.println(key + ":" + prop.getProperty(key));
}
}
- Properties類與配置文件
- Properties類是一個(gè)Map集合孩灯,該集合中的鍵值對(duì)都是字符串闺金。該集合通常用于對(duì)鍵值對(duì)形式的配置文件進(jìn)行操作, 提供了可以快速操作配置文件的方法
- 配置文件:將軟件中可變的部分?jǐn)?shù)據(jù)可以定義到一個(gè)文件中,方便以后更改峰档,該文件稱之為配置文件
- 優(yōu)勢(shì): Properties類提高代碼的維護(hù)性
- Properties類的基本方法
-
load()
: 將文件設(shè)備數(shù)據(jù)裝載為Map集合數(shù)據(jù) -
get(key)
: 獲取Map中的數(shù)據(jù) -
getProperty()
: 獲取Map中的數(shù)據(jù)特有方法
-
- 案例 : 將配置文件中的數(shù)據(jù)通過(guò)流加載到集合中
public static void loadFile() throws IOException {
// 1,創(chuàng)建Properties(Map)對(duì)象
Properties prop = new Properties();
// 2.使用流加載配置文件掖看。
FileInputStream fis = new FileInputStream("c:\\qq.txt");
// 3匣距。使用Properties 對(duì)象的load方法將流中數(shù)據(jù)加載到集合中。
prop.load(fis);
// 遍歷該集合
Set<Entry<Object, Object>> entrySet = prop.entrySet();
Iterator<Entry<Object, Object>> it = entrySet.iterator();
while (it.hasNext()) {
Entry<Object, Object> next = it.next();
Object key = next.getKey();
Object value = next.getValue();
}
// 通過(guò)鍵獲取指定的值
Object object = prop.get("jack");
System.out.println(object);
// 通過(guò)鍵修改值
prop.setProperty("jack", "888888");
// 將集合中的數(shù)據(jù)寫入到配置文件中哎壳。
FileOutputStream fos = new FileOutputStream("c:\\qq.txt");
// 注釋:
prop.store(fos, "yes,qq");
fos.close();
fis.close();
}
- 綜合示例: 獲取記錄程序運(yùn)行次數(shù):
public class Demo {
public static void main(String[] args) throws IOException {
int count = 0;
Properties pro = new Properties();
File file = new File("c:\\count.ini");
FileInputStream fis = null;
if (!file.exists()) {
file.createNewFile();
}
fis = new FileInputStream(file);
pro.load(fis);
String str = pro.getProperty("count");
if (str != null) {
count = Integer.parseInt(str);
}
if (count == 3) {
System.out.println("使用次數(shù)已到,請(qǐng)付費(fèi)");
System.exit(0);
}
count++;
System.out.println("歡迎使用本軟件" + "你已經(jīng)使用了:" + count + " 次");
pro.setProperty("count", count + "");
FileOutputStream fos = new FileOutputStream(new File("c:\\count.ini"));
pro.store(fos, "請(qǐng)保護(hù)知識(shí)產(chǎn)權(quán)");
fis.close();
fos.close();
}
}
打印流 (PrintStream)
- 打印流概述:
- 打印流可以接受文件和其他字節(jié)輸出流尚卫,所以打印流是對(duì)普通字節(jié)輸出流的增強(qiáng)归榕,其中定義了很多的重載的
print()
和println()
, 方便輸出各種類型的數(shù)據(jù). 打印流可以打印任意類型的數(shù)據(jù),而且打印數(shù)據(jù)之前都會(huì)先把數(shù)據(jù)轉(zhuǎn)換成字符串再進(jìn)行打印 - 示例:
- 打印流可以接受文件和其他字節(jié)輸出流尚卫,所以打印流是對(duì)普通字節(jié)輸出流的增強(qiáng)归榕,其中定義了很多的重載的
class Animal{
String name;
String color;
public Animal(String name,String color){
this.name = name;
this.color = color;
}
@Override
public String toString() {
return "名字:"+this.name+ " 顏色:"+ this.color;
}
}
public class Demo {
public static void main(String[] args) throws IOException {
/*FileOutputStream fileOutputStream = new FileOutputStream("F:\\a.txt");
fileOutputStream.write("97".getBytes());
fileOutputStream.close();*/
//打印流可以打印任何類型的數(shù)據(jù)吱涉,而且打印數(shù)據(jù)之前都會(huì)先把數(shù)據(jù)轉(zhuǎn)換成字符串再進(jìn)行打印刹泄。
File file = new File("F:\\a.txt");
//創(chuàng)建一個(gè)打印流
PrintStream printStream = new PrintStream(file);
/*
printStream.println(97);
printStream.println(3.14);
printStream.println('a');
printStream.println(true);
Animal a = new Animal("老鼠", "黑色");
printStream.println(a);
//默認(rèn)標(biāo)準(zhǔn)的輸出流就是向控制臺(tái)輸出的,
System.setOut(printStream); //重新設(shè)置了標(biāo)準(zhǔn)的輸出流對(duì)象
System.out.println("哈哈怎爵,猜猜我在哪里L厥!");
*/
//收集異常的日志信息鳖链。
File logFile = new File("F:\\2015年1月8日.log");
PrintStream logPrintStream = new PrintStream( new FileOutputStream(logFile,true) );
try{
int c = 4/0;
System.out.println("c="+c);
int[] arr = null;
System.out.println(arr.length);
}catch(Exception e){
e.printStackTrace(logPrintStream);
}
}
}
- PrintStream
- 打印流(PrintStream) : 是一個(gè)字節(jié)打印流姆蘸,System.out對(duì)應(yīng)的類型就是PrintStream, 它的構(gòu)造函數(shù)可以接收三種數(shù)據(jù)類型的值
- 字符串路徑
- File對(duì)象
- OutputStream
- 打印流(PrintStream) : 是一個(gè)字節(jié)打印流姆蘸,System.out對(duì)應(yīng)的類型就是PrintStream, 它的構(gòu)造函數(shù)可以接收三種數(shù)據(jù)類型的值
public static void main(String[] args) throws IOException {
PrintStream ps = System.out;
// 普通write方法需要調(diào)用flush或者close方法才會(huì)在控制臺(tái)顯示
// ps.write(100);
// ps.close();
// 不換行打印
ps.print(100);
ps.print('a');
ps.print(100.5);
ps.print("世界");
ps.print(new Object());
System.out.println("--------------");
// 換行
ps.println(100);
ps.println('a');
ps.println(100.5);
ps.println("世界");
ps.println(new Object());
// 重定向打印流
PrintStream ps2 = new PrintStream(new File("c:\\a.txt"));
System.setOut(ps2);
// 換行
ps2.println(100);
ps2.println('a');
ps2.println(100.5);
ps2.println("世界");
ps2.println(new Object());
// printf(); 格式化
ps2.printf("%d,%f,%c,%s", 100, 3.14, '中', "世界你好!!!");
ps2.printf("%4s和%8s 打價(jià)格戰(zhàn)", "京東", "蘇寧");
}
}
注意 : print 方法和write方法的卻別在于, print提供自動(dòng)刷新。普通的write方法需要調(diào)用flush或者close方法才可以看到數(shù)據(jù), JDK1.5之后Java對(duì)PrintStream進(jìn)行了擴(kuò)展, 增加了格式化輸出方式, 可以使用printf()重載方法直接格式化輸出. 但是在格式化輸出的時(shí)候需要指定輸出的數(shù)據(jù)類型格式
-
PrintWriter
- 是一個(gè)字符打印流, 構(gòu)造函數(shù)可以接收四種類型的值
- 字符串路徑芙委。
- File對(duì)象逞敷。
- OutputStream
- Writer
- 對(duì)于1/2類型的數(shù)據(jù), 還可以指定編碼表(也就是字符集); 對(duì)于3/4類型的數(shù)據(jù), 可以指定自動(dòng)刷新
- 注意:該自動(dòng)刷新值為true時(shí),只有三個(gè)方法可以用:println,printf,format.
如果想要既有自動(dòng)刷新灌侣,又可執(zhí)行編碼推捐。如何完成流對(duì)象的包裝?
PrintWrter pw = new PrintWriter(new OutputSteamWriter(new FileOutputStream("a.txt"),"utf-8"), true);
- 如果想要提高效率侧啼。還要使用打印方法:
PrintWrter pw = newPrintWriter(new BufferdWriter(new OutputSteamWriter( newFileOutputStream("a.txt"), "utf-8")), true);
- 是一個(gè)字符打印流, 構(gòu)造函數(shù)可以接收四種類型的值
public static void testPrintWriter() throws Exception {
PrintWriter pw = new PrintWriter("c:/b.txt", "gbk");
// pw.append("xxx");
// pw.println(55);
// pw.println('c');
// pw.printf("%.1s與%4s打價(jià)格戰(zhàn), %c", "京東","蘇寧", 'a');
pw.close();
}
- Scanner
public static void testScanner() throws Exception {
// Scanner scanner = new Scanner(new File("c:/test.txt"));
Scanner scanner = new Scanner(System.in);
System.out.println(scanner.nextInt());
System.out.println(scanner.nextBoolean());
scanner.close();
}
操作數(shù)組的流對(duì)象
- 操作字節(jié)數(shù)組: ByteArrayInputStream / ByteArrayOutputStream
- 具體使用方法:
- toByteArray();
- toString();
- writeTo(OutputStream);
- 具體使用方法:
public static void testByteArrayInputStream() throws Exception {
InputStream in = new ByteArrayInputStream(new byte[] { 65, 66, 67 });
ByteArrayOutputStream out = new ByteArrayOutputStream();
for (int b = -1; (b = in.read()) != -1;) {
out.write(b);
}
in.close();
out.close();
System.out.println(Arrays.toString(out.toByteArray()));
System.out.println(out);
}
- 操作字符數(shù)組 : CharArrayReader / CharArrayWriter
- 對(duì)于這些流: 源是內(nèi)存, 目的也是內(nèi)存牛柒。而且這些流并未調(diào)用系統(tǒng)資源, 使用的就是內(nèi)存中的數(shù)組, 所以在使用的時(shí)候無(wú)需close。操作數(shù)組的讀取流在構(gòu)造時(shí)痊乾,必須要明確一個(gè)數(shù)據(jù)源, 所以要傳入相對(duì)應(yīng)的數(shù)組皮壁。對(duì)于操作數(shù)組的寫入流,在構(gòu)造函數(shù)可以使用空參數(shù)符喝。因?yàn)樗鼉?nèi)置了一個(gè)可變長(zhǎng)度數(shù)組作為緩沖區(qū)
public static void testCharArrayReader() throws Exception {
CharArrayReader reader = new CharArrayReader(new char[] { 'A', 'b', 'c' });
CharArrayWriter writer = new CharArrayWriter();
for (int b = -1; (b = reader.read()) != -1;) {
writer.write(b);
}
reader.close();
writer.close();
System.out.println(writer.toCharArray());
}
- 這幾個(gè)流的出現(xiàn)其實(shí)就是通過(guò)流的讀寫思想在操作數(shù)組闪彼。類似的對(duì)象同理:StringReader / StringWriter。
public static void testStringReader() throws Exception {
StringReader reader = new StringReader("test 中國(guó)");
StringWriter writer = new StringWriter();
for (int b = -1; (b = reader.read()) != -1;) {
writer.write(b);
}
reader.close();
writer.close();
System.out.println(writer.toString());
}
操作基本數(shù)據(jù)類型的流對(duì)象
DataInputStream以及DataOutputStreamg概述 :
查看API文檔DataInputStream的信息 : 發(fā)現(xiàn)從底層輸入流中讀取基本 Java 數(shù)據(jù)類型协饲。查看方法, 有讀一個(gè)字節(jié), 讀一個(gè)char, 讀一個(gè)double 的方法
DataInputStream從數(shù)據(jù)流讀取字節(jié)畏腕,并將它們轉(zhuǎn)換為正確的基本數(shù)據(jù)類型值或字符串。DataInputStream類繼承FilterInputStream類并實(shí)現(xiàn)了DataInput接口
DataOutputStream將基本類型的值或字符串轉(zhuǎn)換為字節(jié)茉稠,并且將字節(jié)輸出到數(shù)據(jù)流描馅。DataOutputStream類繼承FilterOutputStream并實(shí)現(xiàn)了DataOutput接口
-
DataInputStream 的操作基本數(shù)據(jù)類型的方法:
-
int readInt()
: 一次讀取四個(gè)字節(jié),并將其轉(zhuǎn)成int值 -
boolean readBoolean()
: 一次讀取一個(gè)字節(jié) short readShort()
long readLong()
- 剩下的數(shù)據(jù)類型一樣
-
String readUTF()
: 按照utf-8修改版讀取字符而线。注意铭污,它只能讀writeUTF()寫入的字符數(shù)據(jù)
-
-
DataOutputStream 的操作基本數(shù)據(jù)類型的方法:
-
writeInt(int)
: 一次寫入四個(gè)字節(jié)恋日。注意和write(int)不同。write(int)只將該整數(shù)的最低一個(gè)8位寫入嘹狞。剩余三個(gè)8位丟棄 writeBoolean(boolean)
writeShort(short)
writeLong(long)
- 剩下是數(shù)據(jù)類型也也一樣
-
writeUTF(String)
: 按照utf-8修改版將字符數(shù)據(jù)進(jìn)行存儲(chǔ)岂膳。只能通過(guò)readUTF讀取
-
使用DataOutputStream寫數(shù)據(jù)文件
public static void testDataInputStream() throws Exception {
DataOutputStream out = new DataOutputStream(new FileOutputStream(
"c:/a.txt"));
out.writeBoolean(true);
out.writeByte(15); // 0x05 1 個(gè)字節(jié)
out.writeBytes("abc"); // 0x 0041 2個(gè)字節(jié)
out.writeChar('X'); // ??
out.writeChars("xyz");
out.writeLong(111);
out.writeUTF("中國(guó)");
out.close();
DataInputStream in = new DataInputStream(
new FileInputStream("c:/a.txt"));
System.out.println(in.readBoolean());
System.out.println(in.readByte());
System.out.println(in.readByte());
System.out.println(in.readByte());
System.out.println(in.readByte());
System.out.println(in.readChar());
System.out.println(in.readChar());
System.out.println(in.readChar());
System.out.println(in.readChar());
System.out.println(in.readLong());
System.out.println(in.readUTF());
in.close();
}