1、什麼是流?流是一個抽象的概念,代表一串數據集合,當java程序需要從數據源讀取數據時,就開啓了一個到數據源的流,同樣,當數據需要輸出數據到達目的地時,也需要開啓一個流。流是用來處理數據的通道。流有字節流,字符流;輸入流,輸出流。
2、InputStream和OutputStream是以字節爲單位的輸入輸出抽象流類(輸入是指從字節到流(可讀),輸出是指從流到字節(可寫))。
3、Reader和Writer是以字符爲單位的輸入、輸出抽象流類(輸入是指從字節到流(可讀),輸出是指從流到字節(可寫))
4、FileInputStream和FileOutputStream是以字節爲操作單位的文件輸入流和文件輸出流。FileReader和FileWriter是以字符爲操作單位的文件輸入流和文件輸出流。一般來講,以字節爲單位的適合用來對圖片、聲音、視頻等文件進行操作。而已字符爲單位的適合對文本文件進行操作。
使用I/O流類來操作對象時一般步驟如下:
(1)創建連接到指定數據源的I/O流對象
(2)利用I/O流類提供的方法進行數據的讀取和寫入,整個過程中都需要操作java.io.IOException異常。另外,如果是向輸入流寫入數據,還需要在寫入操作完成後調用flush()方法強制寫出所有緩衝的數據。
(3)操作完畢後,一定要調用close()方法關閉I/O流對象,後創建的先關閉。
package stream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class FileRWTest {
public static void main(String[] args) {
try {
/**
* 使用以字節爲單位的可以有效減少亂碼產生
*/
FileReader fileReader=new FileReader("test1.txt");
FileWriter fileWriter=new FileWriter("test_new.txt");
char charArray[]=new char[20];
int length=0;
while((length=fileReader.read(charArray))!=-1){
fileWriter.write(charArray,0,length);
}
fileWriter.flush();
System.out.println("複製完成");
fileReader.close();
fileWriter.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
package stream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import commonly_class.string;
public class FileStreamTest {
/**
* US-ASCII Seven-bit ASCII, a.k.a. ISO646-US, a.k.a. the Basic Latin block of the Unicode character set
ISO-8859-1 ISO Latin Alphabet No. 1, a.k.a. ISO-LATIN-1
UTF-8 Eight-bit UCS Transformation Format
UTF-16BE Sixteen-bit UCS Transformation Format, big-endian byte order
UTF-16LE Sixteen-bit UCS Transformation Format, little-endian byte order
UTF-16 Sixteen-bit UCS Transformation Format, byte order identified by an optional byte-order mark
* @param args
*/
public static void main(String[] args) {
try {
FileInputStream fileInputStream=new FileInputStream("tset.mp4");
FileOutputStream fileOutputStream=new FileOutputStream("test_new.mp4");
byte byteArray[]=new byte[21];//使用字節流讀取數據,容易出現亂碼
int length=0;
while ((length=fileInputStream.read(byteArray))!=-1) {//在文件結尾處返回-1
fileOutputStream.write(byteArray,0,length);//用length控制複製範圍,防止在最後一次多複製
//String string=new String(byteArray,0,length);
//System.out.println(string);
}
fileOutputStream.flush();//刷新數據流
System.out.println("複製完成");
fileInputStream.close();//關閉
fileOutputStream.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
5、緩衝流:爲了提高讀寫速度,java提供了緩衝功能的流類,在使用這些帶緩衝功能的流類時,它會創建一個內部緩存衝區數組。在讀取字節或字符時,會先從數據源取到數據填充到緩衝區,然後在返回。在寫入字節和字符時,會先以寫入的數據填充到該內部緩衝區,然後依次性將目標寫入數據源中。簡而言之就是在數據源(磁盤)和流之間建立一個區域,用於一次性取較多數據並緩存起來,流再從該區域取數據,這樣就減少訪問數據源次數,提高效率。
緩衝流也分爲兩類,針對字節的緩衝輸入和輸出流:BufferedInputStream和BufferedOutputStream;針對字符的緩衝輸入流和輸出流:BufferedReader和BufferedWriter
package stream;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class BufferedIOTest {
public static void main(String[] args) {
/**
* 使用緩衝區處理大型文件可以提高很大的效率
* 具體什麼時候能達到效率最高,
* 需要反覆調試BufferedOutputStream(fileOutputStream,50000);和new byte[3000];的數值匹配
*/
try {
FileInputStream fileInputStream=new FileInputStream("tset.mp4");
FileOutputStream fileOutputStream=new FileOutputStream("test_new.mp4");
BufferedInputStream bufferedInputStream=new BufferedInputStream(fileInputStream,50000);
BufferedOutputStream bufferedOutputStream=new BufferedOutputStream(fileOutputStream,50000);
byte byteArray[]=new byte[3000];//使用字節流讀取數據,容易出現亂碼
int length=0;
while ((length=bufferedInputStream.read(byteArray))!=-1) {
bufferedOutputStream.write(byteArray, 0, length);
}
System.out.println("複製成功");
bufferedOutputStream.flush();//刷新緩衝區
bufferedInputStream.close();//依次關閉流
bufferedOutputStream.close();
fileInputStream.close();
fileOutputStream.close();
}
catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
6、轉換流用於字節流和字符流之間的轉換。InputStreamReader用於將字節流讀取的字節按指定字符集解碼成字符,他需要與InputStream套接。OutputStreamWriter用於將寫入到字節流的字符按照指定字符集編碼成字節,它需要與OutputStream套接。
package stream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class ByteToChar {
public static void main(String[] args) {
System.out.println("請輸入信息(退出輸入e)");
//將在標準的輸入流的字節流轉化爲字符流,再包裝成緩衝流
BufferedReader bufferedReader=new BufferedReader(
new InputStreamReader(System.in));
String string=null;
try {
while ((string=bufferedReader.readLine())!=null) {
if (string.equalsIgnoreCase("e")) {//輸入e退出
System.out.println("安全退出!");
break;
}
System.out.println("---->:"+string.toUpperCase());//將字符串轉化爲大寫
System.out.println("請繼續輸入信息");
}
bufferedReader.close();//關閉緩衝流,會自動關閉它包裝的底層的字節流和字符流
} catch (IOException e) {
e.printStackTrace();
}
}
}
7、數據流是用來專門把Double、Int等基本數據類型寫入文本或從文本中讀取數據。數據流主要有兩類,DataInputStream和DataOutputStream,分別用來讀取和寫入基本數據類型的數據。,例如他們分別對應readInt()方法和writeBoolean()分別代表讀取一個int類型數據和寫入一個Boolean類型數據。
package stream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
public class TestDataStream {
public static void main(String[] args) {
ByteArrayOutputStream baos=new ByteArrayOutputStream();
DataOutputStream dos=new DataOutputStream(baos);//數據流
try {
dos.writeInt(1);
dos.writeBoolean(true);
dos.writeChars("中國china\t");
dos.writeUTF("中國china");
ByteArrayInputStream bais=new ByteArrayInputStream(baos.toByteArray());
System.out.println(bais.available());//長度
DataInputStream dis=new DataInputStream(bais);
System.out.println(dis.readInt());//讀取一個32爲整數
System.out.println(dis.readBoolean());
char temp[]=new char[200];
int length=0;
char c=0;
while ((c=dis.readChar())!='\t'){
temp[length]=c;
length++;
}
String string=new String(temp,0,length);
System.out.println(string);
System.out.println(dis.readUTF());//讀取一個由UTF格式字符組成的字符串
dos.close();
dis.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
8、打印流,PrintStream和PrintWriter流都屬於打印流,他們提供了一系列的print和println方法,可以實現將基本數據類型的數據格式轉化爲字符串輸出。我們常用的Systeam.out.println語句中的Systeam.out就是PrintStream類的一個實例
package stream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import javax.crypto.NullCipher;
/**
* 把標準的輸出改爲文件的輸出
* @author kepu
*
*/
public class PrintStreamTest {
public static void main(String[] args) {
FileOutputStream fos=null;
try {
fos=new FileOutputStream("test1.txt");
//創建打印輸出流,設置爲自動刷新模式
PrintStream ps=new PrintStream(fos, true);
if(ps!=null){
//把標準輸出流改爲文件
System.setOut(ps);
}
for (int i = 0; i <255; i++) {//輸入ASCII字符
System.out.print((char)i);
if (i%50==0) {
System.out.println();
}
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}
finally{
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
9、對象流。JDk中提供了ObjectOutputStream和ObjectInputStream類是用於存儲和讀取基本數據類=類型數據或對象的流,他的強大之處在於可以把java中的對象寫到數據源中,也能把對象中還原回來。用ObjectOutputStream類保存基本數據類型或對象的機制叫做序列化;用ObjectInputStream類讀取基本數據類型或對象的機制叫做反序列化。
需要注意的是ObjectOutputStream和ObjectInputStream不能序列化static或transient修飾的成員變量。另外,能被序列化的對象所對應的類必須實現java.io.Serializable這個標示性接口。
package stream;
public class Student implements java.io.Serializable {
/**
* 凡是實現 Serializable接口的類都有一個表示序列化版本的標識符
* erialVersionUID用來表示類的不同版本間的兼容性
* erialVersionUID可以顯示的表示出來,也可以採用系統默認的值,
* 對代碼進行修改再重新編譯時,erialVersionUID的值可能會改變
* 顯示的定義erialVersionUID的值主要有以下兩種用途:
* 在某些場合,希望類的不同版本對序列化兼容,因此需要確保不同版本具有相同的erialVersionUID值
* 在某些場合,希望類的不同版本對序列化不兼容,因此需要確保不同版本具有不同的erialVersionUID值
*/
private static final long erialVersionUID = 858606830804257830L;
private int id;
private String name;
private transient int age;//不需要序列化的屬性
public Student(int id,String name ,int age) {
this.age=age;
this.id=id;
this.name=name;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public String toString() {
return "id: "+id+"name: "+name+"age: "+age;
}
}
package stream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class ObjectIOStreamTest {
public static void main(String[] args) {
ObjectOutputStream oos=null;
ObjectInputStream ois=null;
try {
//創建連接到指定文件的對象流實例
oos=new ObjectOutputStream(new FileOutputStream("test1.txt"));
ois=new ObjectInputStream(new FileInputStream("test1.txt"));
oos.writeObject(new Student(22, "張三", 18));
//把Student對象序列化到文件中
oos.flush();//刷新輸出流
System.out.println("序列化成功");
Student student=(Student) ois.readObject();//讀取對象
System.out.println(student);
System.out.println("反序列化成功");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
finally{
try {
oos.close();
ois.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
10、ZIP文件流,由於網絡寬帶速度有限,所以數據文件的壓縮有利於數據在Internet上快速傳輸,同時也節省空間。java實現了I/O數據流與網絡數據流的單一接口,因此數據的壓縮、網絡傳輸和解壓文件的實現比較容易。java支持GIZP和ZIP兩種格式。以ZIP爲例來講,主要有java.util.zip包中ZipEntry、ZipInputStream、ZipoutputStream三個類實現ZIP數據壓縮方法的實現。
ZipEntry代表ZIP文件條目,要壓縮的文件都要轉化爲一個個條目
ZipInputStream實現了ZIP壓縮文件的讀輸入流,支持壓縮和非壓縮entry
ZipOutputStrem實現了ZIP壓縮文件的輸出流,支持壓縮和非壓縮entry
package stream;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
public class ZipOutDemo {
public static void main(String[] args) {
/**
* 對單一文件壓縮
*/
ZipOutputStream zos=null;
FileInputStream fis=null;
BufferedInputStream bis=null;
BufferedOutputStream bos=null;
ZipEntry ze=null;//條目
String tarstr = "tset.mp4";//源文件
String destrtr = "zzz.zip";//目標文件
try {
//創建文件輸入流對象
fis = new FileInputStream(tarstr);
//創建Zip輸出流
zos=new ZipOutputStream(new FileOutputStream(destrtr));
//創建條目
ze=new ZipEntry(tarstr);
//寫入條目
zos.putNextEntry(ze);
//創建輸出輸入緩衝流
bos=new BufferedOutputStream(zos,10000);
bis=new BufferedInputStream(fis,10000);
//byte c=0;
byte b[]=new byte[500];
int c=0;
while ((c= bis.read(b))!=-1) {
bos.write(b,0,c);
}
bos.flush();//刷新zip輸出流
zos.closeEntry();//關閉當前zip條目
System.out.println("壓縮完成");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
finally{
try {
fis.close();//關閉各種流
zos.close();
bis.close();
bos.close();
} catch (IOException e) {
// TODO 自動生成的 catch 塊
e.printStackTrace();
}
}
/**
* 對單一文件解壓
*/
ZipInputStream zis=null;
FileOutputStream fos=null;
ZipEntry ze2=null;
String tarstr2 = "zzz.zip";//源文件
String destsr2 = "zzz.mp4";//目標文件
try {
//創建文件輸出流對象
fos=new FileOutputStream(destsr2);
//創建zip輸入流
zis=new ZipInputStream(new FileInputStream(tarstr2));
//寫入條目
ze2=zis.getNextEntry();
//創建輸出輸入緩衝流
bos=new BufferedOutputStream(fos,10000);
bis=new BufferedInputStream(zis,10000);
byte b[]=new byte[500];
int c=0;
while ((c= bis.read(b))!=-1) {//邊讀邊寫
fos.write(b,0,c);
}
zis.closeEntry();//關閉當前zip條目
System.out.println("解壓完成");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
finally{
try {
bis.close();//關閉各種流
bos.close();
fos.close();
zis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
package stream;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
import try_catch.ThrowsDemo;
/**
* zip壓縮文件夾與解壓文件夾
* @author kepu
*
*/
public class ZipTest {
public static void main(String[] args) {
zipFile("g:\\www", "g:\\abc.zip");
System.out.println("壓縮完成");
upZipFile("g:\\abc.zip","g:\\www_new");
System.out.println("解壓完成");
}
//zip壓縮功能。壓縮baseDir(文件目錄下)的所有文件,包含子目錄
public static void zipFile(String baseDir,String fileName) {
List<File> fileList =getSubFiles(new File(baseDir));
ZipOutputStream zos =null;//zip輸出流
ZipEntry ze=null;//條目
byte buf[]=new byte[1000];//緩衝區
try {
zos = new ZipOutputStream(new FileOutputStream(fileName));
for(File f: fileList){
//條目的名只能使用相對於基目錄的相對路徑
ze = new ZipEntry(getAbsFileName(baseDir, f));
//文件當做條目使用
ze.setSize(f.length());
ze.setTime(f.lastModified());
zos.putNextEntry(ze);//開始一個新文件寫入
InputStream is = new BufferedInputStream(new FileInputStream(f));
//創建連接到指定文件的輸入流
int readLen=-1;
while((readLen=is.read(buf))!=-1){//從文件中讀取數據
zos.write(buf, 0, readLen);//往ZIp輸出流中寫
}
zos.closeEntry();//關閉當前條目
is.close();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
try {
zos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
//給定根目錄,返回另一個文件名的相對路徑,使用ZIP文件的路徑
public static String getAbsFileName(String baseDir,File file)throws IOException{
String reult=file.getName();//記住文件名
File base=new File(baseDir);
File temp=file;
while (true) {
temp=temp.getParentFile();
if((temp==null)||(temp.equals(base))){
break;
}else {
reult=temp.getName()+"/"+reult;
}
}
reult=base.getName()+"/"+reult;
return reult;
}
//遞歸獲取指定目錄下的所有子孫文件、目錄列表
private static List<File> getSubFiles(File baseDir) {
List<File> list =new ArrayList<File>();
File tmp[]=baseDir.listFiles();
for (int i = 0; i < tmp.length; i++) {
if (tmp[i].isFile()) {
list.add(tmp[i]);
}
if (tmp[i].isDirectory()) {
list.addAll(getSubFiles(tmp[i]));//遞歸
}
}
return list;
}
/**
* 解壓縮功能. 將zipName文件解壓到destDir目錄下.
*/
@SuppressWarnings("unchecked")
public static void upZipFile(String zipName, String destDir) {
ZipFile zfile = null;
ZipEntry ze = null;
byte[] buf = new byte[8192];
try{
zfile = new ZipFile(zipName);
Enumeration zList = zfile.entries();
while (zList.hasMoreElements()) { //遍歷zip中的條目
ze = (ZipEntry) zList.nextElement();
if (ze.isDirectory()) { //如果條目是目錄
File f = new File(destDir + "/" + ze.getName());
f.mkdir();
}else{
OutputStream os = new BufferedOutputStream(
new FileOutputStream(
getRealFileName(destDir, ze.getName())));
InputStream is = new BufferedInputStream(zfile.getInputStream(ze));
int readLen = -1;
while ((readLen = is.read(buf)) != -1) {
os.write(buf, 0, readLen);
}
is.close();
os.close();
}
}
}catch(IOException e){
e.printStackTrace();
}finally{
try {
zfile.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 給定根目錄,返回一個相對路徑所對應的實際文件名.
* @param baseDir 指定根目錄
* @param absFileName 相對路徑名,來自於ZipEntry中的name
* @return java.io.File 實際的文件
*/
public static File getRealFileName(String baseDir, String absFileName) {
String[] dirs = absFileName.split("/");
File dest = new File(baseDir);
if (dirs.length > 1) {
for (int i = 0; i < dirs.length - 1; i++) {
dest = new File(dest, dirs[i]);
}
if (!dest.exists()){
dest.mkdirs();
}
dest = new File(dest, dirs[dirs.length - 1]);
return dest;
}
return dest;
}
}
11、隨機存儲流類,RandomAccessFile是一個特殊的流類,它可以在文件的任何地方讀取或寫入數據。打開一個隨機存儲文件後,要麼對他進行只讀操作,要麼同時進行讀寫操作。可以通過他的構造方法的第二個參數進行設置,r爲只讀。rw爲讀寫,還要rws,rwd。
隨機存取文件的類似於存儲文件系統中的一個大型的byte數組,它提供了一個指向該數組的光標或索引,成爲文件指針,該文件指針用來標識將要進行的讀寫操作的下一個字節位置,getFilePointer方法可以返回文件指針的當前位置。使用seek方法可以將文件指針移動到該文件內部的任意字節位置處。
package stream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
public class RandomAccessFileTest {
public static void main(String[] args) {
File file = new File("g:"+File.separator+"test.txt");
try {
RandomAccessFile raf=new RandomAccessFile(file, "rw");
//同時讀寫方式,文件如果不存在,會創建文件
String name = null;
int age=0;
name="dds sh";//字符串長度爲8
age=22;//數字長度爲4
raf.writeBytes(name);//寫字符串
raf.writeInt(age);//寫整型
name="dddd ";//字符串長度爲8
age=10;//數字長度4
raf.writeUTF(name);//寫字符串
raf.writeInt(age);//寫整型
name="lilidqqqq";//字符串長度8
age=19;//數字長度4
raf.writeUTF(name);//寫字符串
raf.writeInt(age);//寫整型
System.out.println("寫入完成");
byte b[]=new byte[8];
raf.seek(0);
//raf.skipBytes(12);
for (int i = 0; i <b.length; i++) {
b[i]=raf.readByte();
}
name=new String(b);
age=raf.readInt();
System.out.println("第一個學員信息爲: name:+"+name+ " age:"+age);
raf.seek(12);
//raf.skipBytes(12);
name=raf.readUTF();
age=raf.readInt();
System.out.println("第二個學員信息爲: name:+"+name+ " age:"+age);
raf.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
另外,在Apache提供的各類庫中有更爲豐富的類和接口,使用起來也很方便。