欢迎访问悦橙教程(wld5.com),关注java教程。悦橙教程  java问答|  每日更新
页面导航 : > > 文章正文

【java】java BIO,

来源: javaer 分享于  点击 40497 次 点评:144

【java】java BIO,


一、文件

        在java.io包中,File类代表与平台无关的文件和目录,一旦成功创建了文件对象之后,就可以利用File对象的方法来访问,File的使用示例代码如下。

package file;

import java.io.File;
import java.io.IOException;

public class FileDemo {
	public static void main(String[] args) {
		/*
		 * 创建file
		 * File.separator表示路径的分隔符,windows和linux上不一样,为了程序有更好的移植行,建议使用
		 */
		File file = new File("d:"+File.separator+"xx.txt");
		if(!file.exists()){
			try {
				file.createNewFile();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		/*
		 * 创建目录
		 */
		File fileDir = new File("D:"+File.separator+"xx.txt");
		if(!file.exists()){
			fileDir.mkdir();
		}
		/*
		 * 创建多级目录
		 * mkdir:如果多级路径下有文件夹不存在,则创建失败;
		 * mkdirs:无论路径下的多级文件夹是否都存在,均创建;
		 */
		File fileDirs = new File("d:"+File.separator+"xx"+File.separator+"xxx"+File.separator+"xxxx");
		if(!fileDirs.exists()){
			fileDirs.mkdirs();
		}
		/*
		 * 删除文件
		 * 删除目录的时候,如果目录不为空,则删除失败
		 */
		if(file.exists())
			file.delete();
		if(fileDir.exists())
			fileDir.delete();
		if(fileDirs.exists()){
			System.out.println(fileDirs.delete());
		}
		/*
		 * 这句语句实际上并没有在硬盘上创建文件
		 */
		File files = new File("D:"+File.separator+"bb");
	}
}
        File类的简单操作:深度优先遍历文件夹。

package file;

import java.io.File;

public class DeepFirstTraversalFileDir {
	public static void dft(File dir){
		if(!dir.exists() || !dir.isDirectory())
			return;
		
		File[] files = dir.listFiles();
		for(int i=0; i<files.length; i++){
			if(files[i].isDirectory()){
				System.out.println(files[i]+"----------------");
				dft(files[i]);
			} else {
				System.out.println(files[i]);
			}
		}
	}
	
	public static void main(String[] args) {
		File dir = new File("d:"+File.separator+"demo");
		dft(dir);
	}
}

        File类的简单操作:广度优先过滤文件夹。

package file;

import java.io.File;
import java.io.FilenameFilter;
import java.util.ArrayList;

public class BreadthFirstFilterFile {
	public static void bfff(File dir){
		if(!dir.exists() || !dir.isDirectory())
			return;
		
		String[] fileNames = dir.list(new FilenameFilter() {
			@Override
			public boolean accept(File dir, String name) {
				// TODO Auto-generated method stub
				return name.endsWith(".java");
			}
		});
		File[] files = dir.listFiles();
		ArrayList<File> subDirsList = new ArrayList<>();
		
		System.out.println(dir + "包含了文件:");
		for(String i:fileNames)
			System.out.println(i);
		
		if(files.length > 0){
			for(int i=0; i<files.length; i++){
				if(files[i].isDirectory())
					subDirsList.add(files[i]);
			}
			if(subDirsList.size() > 0){
				for(File i:subDirsList)
					bfff(i);
			}
		}
		
	}
	
	public static void main(String[] args) {
		File dir = new File("d:"+File.separator+"xx");
		bfff(dir);
	}
}

        FIle类的简单操作:剪切文件夹(及该文件夹下面的所有目录和文件夹)。

package file;

import java.io.File;

public class CopyDir {
	/*
	 * 1. 先在目的中新建源一级目录 
	 * 2. listFiles源目录,如果是文件则renameTo,如果是目录就递归
	 */
	public static void copyDir(File sourceDir, File destDir) {
		if (!sourceDir.exists() || !sourceDir.isDirectory())
			return;

		File newDestDir = new File(destDir.getAbsolutePath(),
				sourceDir.getName());
		newDestDir.mkdir();

		File[] files = sourceDir.listFiles();
		for (int i = 0; i < files.length; i++) {
			if (files[i].isDirectory()) {
				copyDir(files[i], newDestDir);
			} else {
				files[i].renameTo(new File(newDestDir.getAbsolutePath(), files[i].getName()));
			}
		}
		sourceDir.delete();
	}

	public static void main(String[] args) {
		File sourceDir = new File("d:" + File.separator + "xx");
		File destDir = new File("d:" + File.separator + "demo" + File.separator
				+ "java");
		copyDir(sourceDir, destDir);
	}
}

二、输入输出体系

        按照流的流向来分,可以分为输入流和输出流,输入流读取文件内容,输出流向文件写入内容;按照操作的文件类型来分,分为字节流和字符流,字节流的数据单元是一个字节,而字符流的数据单元是2个字节;按照流的角色来分,分为节点流和处理流,直接操作一个I/O设备例如磁盘或内存等为节点流,对一个已存在的流进行封装或连接则称为处理流。字节流的基类为InputStream和OutputStream,字符流的基类为Reader和Writer,其中InputStream和Reader为输入流,OutputStream和Writer为输出流。



三、使用流

  • 文件流

        文件流是节点流,可以直接操作磁盘文件,其中字节流有FileInputStream/FileOutputStream,字符流有 FileReader/FileWriter,要创建文件流,只需要在构造方法中传入需要读取或写入的文件,或者个文件路径、名称即可。

        1. 对于输入流,常用的方法有read,可以一次读取一个字节或一个字符,也可以一次读取一个字节数组或字符数组,也可以一次读取一个字节数组或字符数组中的部分数据,read方法返回的都是读取的字节或字符的个数,如果都到文件末尾则返回-1;

        2. 对于输出流,常用的方法是write,可以一次写入一个字节或者字符,也可以一次写入一个字节数组或字符数组,也可以一次写入一个字节惑字符数组中的部分数据;如果把数据写入一个已经存在的文件,则文件已有的内容会被覆盖,如果想要追加,则只需要在文件输出流的构造方法中设置boolean append为true,形式为new FileOutputStream(existFile, true);/new FileWriter(existFile, true);。

        文件流的实例代码如下。

package io;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

/*
 * 字节文件流使用:FileInputStream/FileOutputStream
 */
public class CopyPicDemo {
	/*
	 * 传统的拷贝代码,需要显示的关闭流资源
	 * 图片文件的拷贝(二进制文件的拷贝,例如图片、mp3、视频等)
	 */
	public static <span >	</span>void copyPicture(File resourcePic, File destPic){
		if(!resourcePic.exists() || !destPic.exists())
			return;
		if((!resourcePic.isFile() || !destPic.isFile()))
			return;
		
		FileInputStream fis = null;
		FileOutputStream fos = null;
		
		try {
			fis = new FileInputStream(resourcePic);
			fos = new FileOutputStream(destPic);
			byte[] bArray = new byte[fis.available()/4];
			int hasRead = -1;
			/*
			 * while循环为拷贝代码
			 */
			while((hasRead = fis.read(bArray)) != -1){
				fos.write(bArray, 0, hasRead);
			}
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			/*
			 * 关闭流
			 */
			if(fis != null){
				try {
					fis.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			if(fos != null){
				try {
					fos.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}
	
	/*
	 * java7异常新特性:自动关闭流资源
	 * 形式:try(I/O流)
	 * 			{try块代码}
	 * 		catch
	 * 			{异常处理}
	 */
	public void copyBinaryFile(File sourceFile, File destFile){
		if(!sourceFile.exists() || !destFile.exists())
			return;
		if((!sourceFile.isFile() || !destFile.isFile()))
			return;
		
		try(
				FileInputStream fis = new FileInputStream(sourceFile);
				FileOutputStream fos = new FileOutputStream(destFile);
		){
			byte[] bArray = new byte[fis.available()/4];
			int hasRead = -1;
			/*
			 * while循环为拷贝代码
			 */
			while((hasRead = fis.read(bArray)) != -1){
				fos.write(bArray, 0, hasRead);
			}
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}
package io;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

/*
 * 自负文件流使用:FileReader/FileWriter
 */
public class CopyFile {
	public void copyFile(File sourceFile, File destFile){
		if(!sourceFile.exists() || !destFile.exists())
			return;
		if((!sourceFile.isFile() || !destFile.isFile()))
			return;
		
		/*
		 * java7新特性:自动关闭资源
		 * 前提:该类实现了Closeable或AutoClosable接口
		 */
		try(
				FileReader reader = new FileReader(sourceFile);
				FileWriter writer = new FileWriter(destFile);
		){
			/*
			 * 这里new char数组的时候,可以采用File类的length()方法获取源文件的大小
			 * 不过该方法的返回值是long类型,不是int类型,因此为了通用性和程序的健全
			 * 建议:不采用File类的length方法
			 */
			char[] cArray = new char[2048];
			int hasRead = -1;
			while((hasRead = reader.read(cArray)) != -1){
				writer.write(cArray, 0, hasRead);
			}
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

  • 缓冲流

        缓冲流是处理流,使用缓冲流封装其他节点流可以提高I/O的性能,其中BufferedInputStream/BufferedOuputStream是字节缓冲流,BufferedReader/BufferedWriter是字符缓冲流。缓冲流是处理流,因此建立缓冲流的时候需要传入其他流对象。当使字符输入缓冲流的时候,可以利用其特有的readLine方法来提高效率;当时用输出缓冲流的时候,每次write都会把数据输出到缓冲区而不是磁盘,等到缓冲区写满了在通过flush方法一次性刷新到磁盘,因此可以降低读写的时候操作I/O的次数从而提高效率。

        1. 在创建 BufferedInputStream 时,会创建一个内部byte的缓冲区数组。在读取或跳过流中的字节时,可根据需要从包含的输入流再次填充该内部缓冲区,一次填充多个字节。;

        2. 在创建BufferedOutputStream时,会创建要给内部byte的缓冲数组。通过设置这种输出流,应用程序就可以将各个字节写入底层输出流中,而不必针对每次字节写入调用底层系统;

        3. 通常,Reader 所作的每个读取请求都会导致对底层字符或字节流进行相应的读取请求。因此,建议用 BufferedReader 包装所有其 read() 操作可能开销很高的 Reader(如 FileReader 和 InputStreamReader;

        4. 通常 Writer 将其输出立即发送到底层字符或字节流。除非要求提示输出,否则建议用 BufferedWriter 包装所有其 write() 操作可能开销很高的 Writer(如 FileWriters 和 OutputStreamWriters)。该类提供了 newLine() 方法,它使用平台自己的行分隔符概念,此概念由系统属性line.separator 定义。并非所有平台都使用新行符 ('\n') 来终止各行。因此调用此方法来终止每个输出行要优于直接写入新行符。

        缓冲流的使用示例代码如下。

package io;

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;

/*
 * 字节缓冲流拷贝二进制文件提升效率:BufferedInputStream/BufferedOutputStream
 */
public class BufferInOutStream {
	public void copyBinaryDate(File sourceFile, File destFile){
		if(!sourceFile.exists() || !destFile.exists())
			return;
		if((!sourceFile.isFile() || !destFile.isFile()))
			return;
		
		try(
				BufferedInputStream bis = new BufferedInputStream(new FileInputStream(sourceFile));
				BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destFile));
		){
			byte[] bArray = new byte[bis.available()/4];
			int hasRead = -1;
			while((hasRead = bis.read(bArray)) != -1){
				bos.write(bArray, 0, hasRead);
			}
			/*
			 * 读取完文件之后,再一次性刷到磁盘
			 * 如果在while循环中flush,相当于读一次就写一次,那么就没有利用到缓冲流的缓冲区
			 * 这样的结果是缓冲流和FileOutputStream一样了
			 */
			bos.flush();
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}
package io;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

/*
 * 字符缓冲流:BufferedReader/BufferedWriter
 */
public class BufferReaderWriter {
	public void copyFile(File sourceFile, File destFile){
		if(!sourceFile.exists() || !destFile.exists())
			return;
		if((!sourceFile.isFile() || !destFile.isFile()))
			return;
		
		try(
				BufferedReader br = new BufferedReader(new FileReader(sourceFile));
				BufferedWriter bw = new BufferedWriter(new FileWriter(destFile));
		){
			/*
			 * 字符输入缓冲流特有的readLine方法
			 */
			String hasRead = null;
			while((hasRead = br.readLine()) != null){
				bw.write(hasRead);
			}
			bw.flush();
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}
        字符缓冲输入流的readLine方法原理:利用FileReader逐字符的读取数据,当读到"\r\n"的时候表示读到行尾,则返回数据。自定义实现readLine方法实例代码如下。

package io;

import java.io.File;
import java.io.FileReader;
import java.io.IOException;

public class MyBufferedReader {
	private FileReader reader;

	public MyBufferedReader(FileReader reader) {
		// TODO Auto-generated constructor stub
		this.reader = reader;
	}
	
	/*
	 * 自定义readLine
	 */
	public String readLine(){
		StringBuilder sb = new StringBuilder();
		int hasRead = -1;
		try {
			while((hasRead = reader.read()) != -1){
				if((char)hasRead == '\r')
					continue;
				if((char)hasRead == '\n')
					return sb.toString();
				sb.append((char)hasRead);			
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}
	
	public void close(){
		if(reader != null){
			try {
				reader.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
	
	public static void main(String[] args) throws Exception {
		FileReader reader = new FileReader(new File("D:"+File.separator+"demo"+File.separator+"mybuffer.txt"));
		MyBufferedReader myReader = new MyBufferedReader(reader);
		String hasRead = null;
		while((hasRead = myReader.readLine()) != null){
			System.out.println(hasRead);
		}
		myReader.close();
	}
}

  • 转换流

        转换流也是处理流,他将字节流转换成字符流,其中InputStreamReader是输入转换流,OutputStreamWriter是输出转换流。另外,在流的转换中,还可以进行字符编码操作。要想题号转换流的效率,还可以用缓冲流对字符流进行装饰。

        1. InputStreamReader 是字节流通向字符流的桥梁:它使用指定的charset 读取字节并将其解码为字符。它使用的字符集可以由名称指定或显式给定,或者可以接受平台默认的字符集,每次调用 InputStreamReader 中的一个 read() 方法都会导致从底层输入流读取一个或多个字节。要启用从字节到字符的有效转换,可以提前从底层流读取更多的字节,使其超过满足当前读取操作所需的字节;

        2. OutputStreamWriter 是字符流通向字节流的桥梁:可使用指定的charset 将要写入流中的字符编码成字节。它使用的字符集可以由名称指定或显式给定,否则将接受平台默认的字符集。每次调用 write() 方法都会导致在给定字符(或字符集)上调用编码转换器。在写入底层输出流之前,得到的这些字节将在缓冲区中累积。可以指定此缓冲区的大小,不过,默认的缓冲区对多数用途来说已足够大。注意,传递给 write() 方法的字符没有缓冲;

        3. 为了获得最高效率,可考虑将 OutputStreamWriter 包装到 BufferedWriter 中,以避免频繁调用转换器,同理可在 BufferedReader 内包装 InputStreamReader;

package io;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;

/*
 * 输入转换流InputStreamReader,在读取数据的时候,还可以对数据进行重新编码
 * 输出转换流OutputStreamWriter,在输出数据的时候,也可以对数据进行重新编码
 */
public class InOutStreamReaderWriterDemo {
	/*
	 * UTF-8方式读取键盘输入
	 */
	public void demo(){
		/*
		 * 键盘输入System.in为字节流,建立转换流并以UTF-8编码方式读取键盘输入
		 * 屏幕输出System.out为字节流(PrintWriter打印流封装了OutputStream),建立输出转换流,并以UTF-8方式输出
		 */
		InputStreamReader isReader = null;
		OutputStreamWriter osWriter = null;
		try {
			isReader = new InputStreamReader(System.in, "UTF-8");
			osWriter = new OutputStreamWriter(System.out, "UTF-8");
		} catch (UnsupportedEncodingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		/*
		 * 建立缓冲流装饰转换流提高效率
		 */
		BufferedReader br = new BufferedReader(isReader);
		BufferedWriter bw = new BufferedWriter(osWriter);
		String hasRead = null;
		try {
			while((hasRead = br.readLine()) != null){
				if(hasRead.equals("exit"))
					break;
				bw.write(hasRead);
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			if(isReader != null){
				try {
					isReader.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			if(br != null){
				try {
					br.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
		
	}
}

  • 打印流

        打印流没有输入只有输出,其中PrintStream是字节输出流,他可以直接关联文件,也可以封装其他OutputStream对象,PrintWriter是字符输出流,可以关联文件,也可以封装OutputStream或Writer对象,是唯一一个可以封装任意流对象的类,功能十分强大。

        打印流对外输出的时候,可以自动刷新,与 PrintStream 类不同,如果启用了自动刷新,则只有在调用 printlnprintfformat 的其中一个方法时才可能完成此操作,而不是每当正好输出换行符时才完成。这些方法使用平台自有的行分隔符概念,而不是换行符。要设置自动刷新,在构造打印流的时候设置autoFlush为true即可。示例代码如下。

package io;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;

public class PrintWriterDemo {
	/*
	 * PrintWriter:从键盘读取,输出到文件,自动flush
	 */
	public void autoFlushIntoFile(){
		try(
				BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
				PrintWriter pw = 
						new PrintWriter(
								new FileOutputStream(
										new File("D:"+File.separator+"demo"+File.separator+"xx.txt")), true)
		){
			String hasRead = null;
			while((hasRead = br.readLine()) != null){
				pw.println(hasRead);
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}
}

  • 序列流

        SequenceInputStream 表示其他输入流的逻辑串联。它从输入流的有序集合开始,并从第一个输入流开始读取,直到到达文件末尾,接着从第二个输入流读取,依次类推,直到到达包含的最后一个输入流的文件末尾为止。java序列流没有输出流,序列流可以把Enumeration中的多个InputStream输入流按照顺序合并成一个输入流来操作,使用示例代码如下。

package io;

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.SequenceInputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.Iterator;

/*
 * SequenceInputStream:序列流,将多个输入流对象合并成一个输入流
 */
public class SequenceInputStreamDemo {
	/*
	 * 将一个mp3文件切割成三部分存储到硬盘上
	 */
	public void splitFile(){
		File mp3File = new File("d:"+File.separator+"demo"+File.separator+"angel.mp3");
		if(!mp3File.exists() || !mp3File.isFile())
			return;
		
		try(
				BufferedInputStream bis = new BufferedInputStream(new FileInputStream(mp3File));
		){
			int count = 0;
			byte[] bArray = new byte[bis.available()/3];
			int hasRead = -1;
			while((hasRead = bis.read(bArray)) != -1){
				/*
				 * 采用多个输出流来切割文件
				 */
				BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(new File("d:"+File.separator+"demo"+File.separator+(count++)+".mp3")));
				bos.write(bArray, 0, hasRead);
				bos.close();
			}
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	/*
	 * 利用SequenceInputStream将多个输入流合并成一个输入流
	 */
	public void mergeFiles(Collection<? extends InputStream> c){
		if(c == null)
			return;
		
		/*
		 * 建立SequenceInputStream需要传入Enumeration对象,可以通过Collection的迭代器来重写Enum对象中的方法
		 * 从而使得所有的Collection都可以使用
		 */
		Iterator<? extends InputStream> it = c.iterator(); 
		SequenceInputStream sis = new SequenceInputStream(new Enumeration<InputStream>() {
			@Override
			public boolean hasMoreElements() {
				// TODO Auto-generated method stub
				return it.hasNext();
			}
			@Override
			public InputStream nextElement() {
				// TODO Auto-generated method stub
				return it.next();
			}
		});
		
		try(
				BufferedOutputStream bos = 
					new BufferedOutputStream(new FileOutputStream(new File("d:"+File.separator+"demo"+File.separator+"副本-angel.mp3")));
		){
			byte[] bArray = new byte[sis.available()/10];
			int hasRead = -1;
			while((hasRead = sis.read(bArray)) != -1){
				bos.write(bArray, 0, hasRead);
				bos.flush();
			}
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			if(sis != null){
				try {
					sis.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}
	
	public static void main(String[] args) throws Exception {
		SequenceInputStreamDemo demo = new SequenceInputStreamDemo();
		//demo.splitFile();
		ArrayList<FileInputStream> list = new ArrayList<>();
		list.add(new FileInputStream(new File("d:"+File.separator+"demo"+File.separator+"0.mp3")));
		list.add(new FileInputStream(new File("d:"+File.separator+"demo"+File.separator+"1.mp3")));
		list.add(new FileInputStream(new File("d:"+File.separator+"demo"+File.separator+"2.mp3")));
		demo.mergeFiles(list);
	}
}

  • 对象流

        由于对象流相对其他流来说比较复杂,因此在下一篇博文中详细介绍。

  • 管道流

        管道流也是java.io中比较特殊的流。管道输入流应该连接到管道输出流;管道输入流提供要写入管道输出流的所有数据字节。通常,数据由某个线程从PipedInputStream 对象读取,并由其他线程将其写入到相应的PipedOutputStream。不建议对这两个对象尝试使用单个线程,因为这样可能死锁线程。管道输入流包含一个缓冲区,可在缓冲区限定的范围内将读操作和写操作分离开。如果向连接管道输出流提供数据字节的线程不再存在,则认为该管道的。反过来,可以将管道输出流连接到管道输入流来创建通信管道,管道输出流是管道的发送端,如果某个线程正从连接的管道输入流中读取数据字节,但该线程不再处于活动状态,则该管道被视为毁坏的状态。

  • 数据流

        DataInputStream为java中的数据输入流,数据输入流允许应用程序以与机器无关方式从底层输入流中读取基本 Java 数据类型。应用程序可以使用数据输出流写入稍后由数据输入流读取的数据。DataOutputStream为java中的数据输出流,据输出流允许应用程序以适当方式将基本 Java 数据类型写入输出流中。然后,应用程序可以使用数据输入流将数据读入。代码示例如下。

        特点:能够直接操作java基本数据类型。另外,writeUTF方法不能用一般的UTF-8方式读取。

package io;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class DataInOutStreamDemo {
	public void writeBaseDataType(){
		DataOutputStream dos = null;
		try {
			dos = new DataOutputStream(new FileOutputStream(new File("D:"+File.separator+"demo"+File.separator+"baseDataType.txt")));
			dos.writeInt(4);
			dos.writeBoolean(true);
			dos.writeFloat(0.15f);
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	public void readBaseDataType(){
		DataInputStream dis = null;
		try {
			dis = new DataInputStream(new FileInputStream(new File("D:"+File.separator+"demo"+File.separator+"baseDataType.txt")));
			System.out.println(dis.readInt());
			System.out.println(dis.readBoolean());
			System.out.println(dis.readFloat());
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}
	
	public static void main(String[] args) {
		DataInOutStreamDemo demo = new DataInOutStreamDemo();
		demo.writeBaseDataType();
		demo.readBaseDataType();
	}
}

  • 数组流

        java中数组流有字节数组流和字符数组流,分别可以直接操作字节数组和字符数组,其中ByteArrayInputStream和ByteOutputStream表示字节数组流,CharArrayReader和CharArrayWriter则代表字符数组流。

        1. ByteArrayInputStream包含一个内部缓冲区,该缓冲区包含从流中读取的字节。内部计数器跟踪read 方法要提供的下一个字节。关闭ByteArrayInputStream 无效。此类中的方法在关闭此流后仍可被调用,而不会产生任何IOException;

        2. ByteArrayOutputStream实现了一个输出流,其中的数据被写入一个 byte 数组。缓冲区会随着数据的不断写入而自动增长。可使用toByteArray()toString() 获取数据,关闭ByteArrayOutputStream 无效。此类中的方法在关闭此流后仍可被调用,而不会产生任何IOException;

        3. 此类实现一个可用作 Writer 的字符缓冲区。缓冲区会随向流中写入数据而自动增长。可使用 toCharArray() 和 toString() 获取数据,在此类上调用 close() 无效,并且在关闭该流后可以调用此类中的各个方法,而不会产生任何 IOException;

        4. CharArrayReader实现一个可用作字符输入流的字符缓冲区,在此类上调用 close() 无效,并且在关闭该流后可以调用此类中的各个方法,而不会产生任何 IOException;

        数组流管理的物理设备是内存,因此在构造的时候需要接收数据源,数据源是字节或字符数组。示例代码如下。

        另外,和内存相关的类还有StringReader和StringWriter。

package io;

import java.io.CharArrayReader;
import java.io.CharArrayWriter;
import java.io.IOException;


public class CharArrayReaderWriterDemo {
	public static void main(String[] args) {
		CharArrayReader car = new CharArrayReader("welcom to java".toCharArray());
		CharArrayWriter caw = new CharArrayWriter();
		int hasRead = -1;
		try {
			while((hasRead = car.read()) != -1){
				caw.write((char)hasRead);
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println(caw.size());
		System.out.println(caw.toCharArray());
		System.out.println(caw.toString());
	}
}

  • Properties

        Properties是HashTable的子类,因此Properties是双列并具备了map的特点。Properties 类表示了一个持久的属性集。Properties 可保存在流中或从流中加载。属性列表中每个键及其对应值都是一个字符串。一个属性列表可包含另一个属性列表作为它的“默认值”;如果未能在原有的属性列表中搜索到属性键,则搜索第二个属性列表。因为Properties 继承于 Hashtable,所以可对 Properties 对象应用put putAll 方法。但不建议使用这两个方法,因为它们允许调用者插入其键或值不是 String 的项。相反,应该使用setProperty 方法。如果在“不安全”的Properties 对象(即包含非String 的键或值)上调用storesave 方法,则该调用将失败。类似地,如果在“不安全”的Properties 对象(即包含非String 的键)上调用propertyNameslist 方法,则该调用将失败。 代码示例如下。

package io;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Properties;
import java.util.Set;


/*
 * Properties加载流
 */
public class PropertiesDemo {
	public Properties loadInputStream(){
		Properties pro = new Properties();
		try {
			/*
			 * 加载流
			 */
			pro.load(new FileInputStream(new File("d:"+File.separator+"demo"+File.separator+"info.properties")));
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		Set<String> names = pro.stringPropertyNames();
		for(String name:names)
			System.out.println(name + " : " + pro.getProperty(name));
		
		return pro;
	}
	
	/*
	 * 将Properties对象存储到磁盘
	 */
	public void storeOutputStream(Properties pro){
		FileOutputStream fos = null;
		try {
			fos = new FileOutputStream("d:"+File.separator+"demo"+File.separator+"xx.properties");
			if(fos != null)
				pro.store(fos, "");
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	public static void main(String[] args) {
		PropertiesDemo demo = new PropertiesDemo();
		demo.storeOutputStream(demo.loadInputStream());
	}
}

四、编码

        因为不同区域对于文字的编码方式不一样,因此产生了各种各样的编码方式,其中java语言默认使用国际标准码unicode字符集,windows下简体中文默认使用GBK字符集,linux下简体中文默认使用UTF-8字符集。

       参考:编码(一)、编码(二)、编码(三)、编码(四)

        1. unicode:unicode.org组织制定的编码机制,愿景是希望能囊括全世界的文字,在unicode-1中是两个字节编码一个字符,在unicode-2中有增加相当于20bit编码;

        2. GBK:国标编码,由中华人民共和国信息技术标准化技术委员会制定,其包含了unicode-2的字符,并且增加了部分unicode没有的字符;

        3. UTF-8:是一种针对unicode的可变长度编码,每个字符占用1~3个字节,可以用来表示任何unicode中的字符,而且其编码容错性较高,因此逐渐成为网络应用优先采用的编码方式;

        编码:在windows上,当我们从键盘输入的时候,其实就是在编码,不过是以GBK的方式在编码,这个编码过程是操作系统帮我们完成,完成之后存储的在磁盘上的是一堆二进制数据,因此,编码实际上是一个按照编码方式生成二进制字节的过程;

        解码:实际上文件中存储的是二进制数据,当我们在windows打开一个例如txt文件的时候,是txt程序按照其默认编码方式,把文件中的二进制数据按照两个字节一组的方式对GBK码表进行查询,之后再返回字符展示,因此编码的过程其实就是按照指定编码方式的字节数查找码表的过程,之所以出现乱码,是因为指定的码表不正确,因此,解码就是一个按照指定方式查询码表的过程;

package io;

import java.io.UnsupportedEncodingException;

public class CharSetDemo {
	public static void main(String[] args) throws UnsupportedEncodingException {
		String str = "名字";
		/*
		 * unicode字符用utf-8方式编码
		 */
		byte[] utf_8Array = str.getBytes("UTF-8");
		System.out.println(utf_8Array.length);
		/*
		 * unicode字符采用GBK方式编码
		 */
		byte[] gbkArray = str.getBytes("GBK");
		System.out.println(gbkArray.length);
		/*
		 * unicode解码utf-8和gbk
		 */
		System.out.println(new String(utf_8Array));
		System.out.println(new String(gbkArray));
	}
	/*
	 * 输出结果:
	 * 6
	 * 4
	 * 名字
	 * ����
	 */
}

附注:

        本文如有错漏,烦请不吝指正,谢谢!

相关文章

    暂无相关文章
相关栏目:

用户点评