java語言基礎入門——各類流的介紹

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提供的各類庫中有更爲豐富的類和接口,使用起來也很方便。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章