码迷,mamicode.com
首页 > 编程语言 > 详细

2.熟悉Java基本类库系列——Java IO 类库

时间:2015-04-08 00:56:14      阅读:204      评论:0      收藏:0      [点我收藏+]

标签:

技术分享

Java中常用的IO操作基本上可以分为四大部分,分别是:File类操作、RandomAccessFile类操作、字节流操作、字符流操作。只要熟练掌握了本文中所列举的所有例子,基本上对于Java的IO流操作就可以说是掌握了。

下面将以JUnit测试用例的方式,用一个个例子的方式列出这四大部分中常用的操作例子。

一、File类操作

File类操作定义了最基本的、与操作系统的稳健系统相关的操作,可以对文件夹、文件进行一系列的操作。

 1、常用的一些用法

       //1.两个常量
	@Test public void twoConstant(){
		//输出: ‘/‘  ‘:‘
		System.out.println(File.separator); 
		System.out.println(File.pathSeparator);
	}	
	
	//2.创建新文件
	@Test public void createFile(){
		File f = new File("hello");
		try{
			f.createNewFile();
			System.out.println("创建了文件:" + f.getAbsolutePath());
		}catch(Exception e){
			e.printStackTrace();
		}
	}	
		
	//3.创建一个文件夹
	@Test public void createFolder(){
		try{
			File file = new File("HelloFolder");
			//当一个目录下既没有该名字对应的目录和文件时,才能建立该文件夹
			if(!file.isDirectory() && !file.isFile()){
				file.mkdir();
				//file.mkdirs(); //如果上级目录不存在,那么一并创建上级目录
			}else{
				System.out.println("已存在该名称的文件或文件夹");
			}
		}catch(Exception e){
			e.printStackTrace();
		}
	}
	
	//4.删除一个文件
	@Test public void deleteFile(){
		try{
			File file = new File("hello");
			if(file.exists()){
				file.delete();
			}else{
				System.out.println("文件不存在");
			}
		}catch(Exception e){
			e.printStackTrace();
		}
	}	
	
	//5.删除一个文件夹(与删除文件一样)
	@Test public void deleteFolder(){
		try{
			File file = new File("HelloFolder");
			if(file.exists()){
				file.delete();
			}else{
				System.out.println("该文件夹不存在");
			}
		}catch(Exception e){
			e.printStackTrace();
		}
	}	
	
	//6.获取指定目录下所有文件的文件名(包括隐藏文件和文件夹)
	@Test public void getAllFileName(){
		try{
			File folder = new File(".");  //当前目录
			String[] fileStrs = folder.list();
			for(String str : fileStrs){
				System.out.print(str + " ");
			}
		}catch(Exception e){
			e.printStackTrace();
		}
	}
	
	//7.获取指定目录下所有文件的路径
	@Test public void getAllFilePath(){
		try{
			File folder = new File(".");  //当前目录
			File[] files = folder.listFiles();
			for(File file : files){
				System.out.println(file.getCanonicalPath());
			}
		}catch(Exception e){
			e.printStackTrace();
		}
	}    

2、打印指定目录下的所有文件(递归调用)

package com.chanshuyi.io;

/**
 * 列出指定目录的全部内容
 * */

import java.io.*;

class ListAllFile{
    public static void main(String[] args) {
        File f = new File(".");
        print(f);
    }
    
    //递归打印
    public static void print(File f){
    	if(f != null){
    		if(f.isDirectory()){
    			File[] fileArray = f.listFiles();
    			if(fileArray != null){
    				for(int i = 0; i < fileArray.length; i++){
    					print(fileArray[i]);
    				}
    			}
    		}else{
    			System.out.println(f);
    		}
    	}
    }
} 

二、RandomAccessFile类操作

RandomAccessFile类可以对文件进行随机访问,比如可以指定读写指针到某一个字节处,也可以读写指针指定跳过指定字节数。简单地说,RandomAccessFile类提供了许多方法,使得我们可以对文件进行更加细致的读写操作。

//8.随机读写文件类
@Test public void operateRandom(){
	try{
		//1.跳过两个字节读取文件内容
		//文件内容是:Hello, this is demo File.
		RandomAccessFile randomRW = new RandomAccessFile(new File("demo.txt"), "rw"); 
		randomRW.skipBytes(2);  //跳过两个字节,即跳过了‘he‘两个英文字符(一个英文字符占用1个字节)
		byte b[] = new byte[100];
		int length = randomRW.read(b);
		System.out.println("总共读取了" + length + "个字节,读取的内容是:" + new String(b));
		//总共读取了23个字节,读取的内容是:llo, this is demo File.
		
		//2.将读写指针跳回文件头重新读取
		randomRW.seek(0);
		length = randomRW.read(b);
		System.out.println("总共读取了" + length + "个字节,读取的内容是:" + new String(b));
		//总共读取了25个字节,读取的内容是:Hello, this is demo File.
	
		//3.获取读写指针所在地址
		long pointer = randomRW.getFilePointer();
		System.out.println("文件指针地址:" + pointer);  //文件指针地址:25
		randomRW.seek(2);
		pointer = randomRW.getFilePointer();
		System.out.println("文件指针地址:" + pointer);	//文件指针地址:2
		randomRW.seek(77);
		pointer = randomRW.getFilePointer();
		System.out.println("文件指针地址:" + pointer);	//文件指针地址:77
		
		randomRW.close();
	}catch(Exception e){
		e.printStackTrace();
	}

三、字节流读写

FileInputStream / FileOutputStream - 实现了对文件的读写操作

ObjectInpuStream / ObjectOutputStream - 实现了对序列化对象的读写操作

ByteArrayInputStream / ByteArrayOutputStream - 实现了对字节数组的读写操作

PipedInputStream / PipedOutputStream - 实现不同线程之间的通信

SequenceInputStream - 实现不同输入流的合并(此对象没有对应的OutputStream类)

BufferedInputStream / BufferedOutputStream - 实现读写缓存层

1、字节流读取 - byte(表示读取的数据类型是byte或byte[])

//15.字节流读取 - byte
@Test public void readByte(){
	try{
		File file = new File("hello.txt");
		InputStream fos = new FileInputStream(file);
		
		byte[] bytes = new byte[fos.available()];
		fos.read(bytes);
		String str = new String(bytes);
		System.out.println("文件内容是:\n" + str);
		fos.close();
		
	}catch(Exception e){
		e.printStackTrace();
	}
}

2、字节流读取(缓存) - byte

//字节流读取(缓存) - byte
@Test public void writeByteWithBuffere(){
	try{
		File file = new File("hello1.txt");
		OutputStream fos = new FileOutputStream(file);
		BufferedOutputStream out = new BufferedOutputStream(fos);
		
		out.write("你好\n".getBytes());
		out.write("吃饭了么!".getBytes());
		out.flush();
			
		fos.close();
	}catch(Exception e){
		e.printStackTrace();
	}
}

3、字节流写入 - byte

//17.字节流写入 - byte
@Test public void writeByte(){
	try{
		File file = new File("hello.txt");
		OutputStream fos = new FileOutputStream(file);
		fos.write("Hello Mac.\n你好,Mac.".getBytes());
		fos.close();
	}catch(Exception e){
		e.printStackTrace();
	}
}

4、字节流写入(缓存) - byte

//18.字节流写入(缓存) - byte
@Test public void writeByteWithBuffer(){
	try{
		File file = new File("hello.txt");
		OutputStream fos = new FileOutputStream(file);
		BufferedOutputStream bos = new BufferedOutputStream(fos);
			
		bos.write("Hello Mac.\n你好,Mac.".getBytes());
		bos.close();
		fos.close();
	}catch(Exception e){
		e.printStackTrace();
	}
}

如果你仔细敲过上面4个例子的代码你会发现,其实好像字节流的读取和写入,好像加了缓存和没加缓存,它们的代码好像都差不多啊,至少再写入数据的时候是一样的。而字符流的读取在加了缓存层之后,至少还能直接读取整行数据,字符流的写入加了缓存之后,可以写入换行符。那字节流的缓存究竟有什么必要性呢?

确实,从代码以及其方法上看,其实他们并没有什么区别,但是从官方的API文档来看,缓存最重要的一个地方就是减少程序对于磁盘的IO次数。加了缓存的程序再读取的时候会一次性读取很多个字节,之后提供给程序使用,但如果你不加缓存,那么程序就只会读取代码中指定的字节数。这在你一个字节一个字节从文件中读取数据的时候,其差别就凸现出来了。如果你没有使用缓存进行数据读取,那么你每读一个字节的数据,程序就去磁盘读取一次文件,这样会造成磁盘的频繁IO读取,减少磁盘的寿命。

5、ObjectInputStream / ObjectOutputStream - 序列化一个对象

要被序列化的POJO对象:

package com.chanshuyi.io.po;

import java.io.Serializable;

public class Student implements Serializable{
	
	/**
	 * 序列化ID
	 */
	private static final long serialVersionUID = 7288449352920655248L;

	private String name;
	
	private int age;
	
	private String phone;
	
	public Student(){
		
	}
	
	public Student(String name, int age, String phone){
		this.name = name;
		this.age = age;
		this.phone = phone;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public String getPhone() {
		return phone;
	}

	public void setPhone(String phone) {
		this.phone = phone;
	}
	
	public String toString(){
		return name + "," + age + "," + phone;
	}

}

被序列化的POJO对象需要实现Serializable接口。

序列化对象方法:

//20.ObjectOutputStream - 序列化对象 - 将对象属性序列化保存
@Test public void serialized(){
	try{
		Student std = new Student("Tommy", 13, "18923923876");
		ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("StudentObject.obj"));
		oos.writeObject(std);
		oos.close();
	}catch(Exception e){
		e.printStackTrace();
	}
}

反序列化对象方法:

//21.ObjectInputStream - 反序列化对象 - 读取序列化后的对象
@Test public void deSerialized(){
	try{
		ObjectInputStream ois = new ObjectInputStream(new FileInputStream("StudentObject.obj"));
		Student std = (Student)ois.readObject();
		System.out.println(std);
		ois.close();
	}catch(Exception e){
		e.printStackTrace();
	}
}

6、ByteArrayInputStream / ByteArrayOutputStream - 操作内存字节数据

//19.ByteArrayInputStream - 内存操作流 - 将内存数据转化为流
@Test public void random2Stream(){
	try{
		String str = "你好";
		ByteArrayInputStream input = new ByteArrayInputStream(str.getBytes());
		ByteArrayOutputStream output = new ByteArrayOutputStream();
		int temp = 0;
		while((temp = input.read()) != (-1)){
			char ch = (char)temp;
			output.write(Character.toLowerCase(ch));
		}
		String outputStr = output.toString();
		input.close();
		output.close();
		System.out.println(outputStr);
		
	}catch(Exception e){
		e.printStackTrace();
	}
}

7、PipedInputStream / PipedOutputStream - 实现进程间的管道通信

接收者:

package com.chanshuyi.io.pineStream;

import java.io.PipedInputStream;

/**
 * 管道流 - 接收者
 * @author yurongchan
 *
 */
public class Receiver implements Runnable{
	
	private PipedInputStream in = null;
	
	public Receiver(){
		in = new PipedInputStream();
	}
	
	public PipedInputStream getIn(){
		return this.in;
	}

	public void run(){
		byte[] b = new byte[1000];
		int length = 0;
		try{
			length = this.in.read(b);
		}catch(Exception e){
			e.printStackTrace();
		}
		System.out.println("接收到的消息:" + new String(b, 0, length));
	}
}

发送者:

package com.chanshuyi.io.pineStream;

import java.io.PipedOutputStream;

/**
 * 管道流 - 发送者
 * @author yurongchan
 *
 */
public class Send implements Runnable{
	private PipedOutputStream out = null;
	
	public Send(){
		out = new PipedOutputStream();
	}
	
	public PipedOutputStream getOut(){
		return this.out;
	}
	
	public void run(){
		String msg = "Hello, I‘m outputer.\n你好,我是发送者。";
		try{
			out.write(msg.getBytes());
			out.close();
		}catch(Exception e){
			e.printStackTrace();
		}
	}
}

测试方法:

//22.PipedInputStream - 在不同线程之间进行通信
@Test public void pipeContact(){
	try{
		Send send = new Send();
		Receiver receiver = new Receiver();
		try{
			send.getOut().connect(receiver.getIn()); 
		}catch(Exception e){
			e.printStackTrace();
		}
		new Thread(send).start();
		new Thread(receiver).start();
	}catch(Exception e){
		e.printStackTrace();
	}
}

8、SequenceInputStream - 合并输入流

//23.SequenceInputStream - 合并几个输入流
@Test public void sequenceInputStream(){
	try{
		InputStream is1 = new FileInputStream(new File("sequence1.txt"));
		InputStream is2 = new FileInputStream(new File("sequence2.txt"));
		OutputStream os = new FileOutputStream(new File("sequence3.txt"));
		SequenceInputStream sis = new SequenceInputStream(is1, is2);
		int temp = 0;
		while((temp = sis.read()) != -1){
			os.write(temp);
		}
		sis.close();
		is1.close();
		is2.close();
		os.close();
	}catch(Exception e){
		e.printStackTrace();
	}
}

之后打开sequence3.txt会发现,1、2文本中的内容都到了sequence3.txt中了。

四、字符流读写

字符流的读写实际上还是基于字节流实现的,而且数组存储时更多是以字节为单位存储,因此更多时候还是使用字节流进行读写操作。

因此对于字符流的读写,我们只需要掌握常用的几个操作即可。

InputStreamReader / OutputStreamWriter - 实现字符流的读写

BufferedReader / BufferedWriter - 实现字符流的缓存层

FileReader / FileWriter - 字符流的工具类

1、字符流读取 - char(表示读取的数据类型是char或char[]) 

//9.字符流读取 - char
@Test public void writeChar(){
try{
    InputStreamReader reader = new InputStreamReader(new FileInputStream("OutputStreamWriter.txt"));
    char[] chars = new char[1000];
    int length = reader.read(chars);
    System.out.println("一共读取了" + length + "个字符,内容是:" + new String(chars));
    reader.close();
  }catch(Exception e){
    e.printStackTrace();
  }
}

2、字符流读取(缓存) - char

//10.字符流读取(缓存)
@Test public void writeCharWithBuffer(){
	try{
		InputStreamReader isr = new InputStreamReader(new FileInputStream("BufferedWriter.txt"));
		BufferedReader reader = new BufferedReader(isr);
		String str = ""; 
		while((str = reader.readLine()) != null && str.length() != 0){
			System.out.println(str);
		}
		reader.close();
	}catch(Exception e){
		e.printStackTrace();
	}
}

3、字符流写入 - char / char[] / String

//11.字符流写入 
	@Test public void readChar(){
	try{
	  OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream("OutputStreamWriter.txt"));
	  writer.write("AllFileTest.fileReadUtil -> 字符流写入文件");
	  writer.close();
	}catch(Exception e){
	  e.printStackTrace();
	}
}

4、字符流的写入(缓存)- char/ String

//12.字符流写入(缓存)
@Test public void readCharWithBuffer(){
try{
	OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("BufferedWriter.txt"));
	BufferedWriter writer = new BufferedWriter(osw);
	writer.write("AllFileTest.fileReadUtil -> 字符流写入文件");
	writer.newLine();	//换行
	writer.write("第二行");
	writer.close();
}catch(Exception e){
	e.printStackTrace();
    }
}

总结一下上面四种方式的字符流写入写出,我们会发现两个规律,一个是读写规律,一个是缓存层的规律:

· 读写规律。字符流的读取,其读取的数据都是char(字符型)的单个或者数组。而字符流的写入除了可以写入单个或多个char类型的字符歪,还可以直接写入String(字符串)类型。

· 缓存层规律。字符流的读写,增加了缓存层之后的一个明显区别就是:加了缓存层(Buffer)之后,字符流读取可以实现整行读取(readLine),而字符流写入可以实现写入换行符(writeLine)。

5、文件读取工具类

//13.FileReader - 文件读取工具类(这是加了缓存的。但也可以不加缓存)
@Test public void fileReadUtil(){
    try{
		FileReader fr = new FileReader(new File("demo.txt"));
		BufferedReader reader = new BufferedReader(fr);
		String str = "";
		while((str = reader.readLine()) != null && str.length() != 0){
			System.out.println(str);
		}
		reader.close();
	}catch(Exception e){
		e.printStackTrace();
	}
}

将这个例子与上面的第2个例子,即加了缓存的字符流读取例子相比,你会发现其实这两个例子的区别就只是声明FileReader、InputStreamReader的区别而已。声明FileReader只需要再加上File类型参数即可,而声明InputStreamReader则需要再加上一个FileInputStream类,之后才能跟上File类型的参数。因此,我们才说FileReader是一个文件读取工具类。

6、文件写入工具类

//14.FileWriter - 文件写入工具类(这是加了缓存的。但也可以不加缓存)
@Test public void fileWriteUtil(){
	try{
		FileWriter fw = new FileWriter(new File("FileWriteUtil1.txt"));
		BufferedWriter writer = new BufferedWriter(fw);
		writer.write("HelloMan1");
		writer.newLine();
		writer.write("HelloMan2");
		writer.close();
	}catch(Exception e){
		e.printStackTrace();
	}
}

同样的,其实FileWriter与OutputStreamWriter也只是声明上的区别而已。

五、其他

这里会收集一些不怎么常用,但是有时也会用到的例子,遇到的时候可以快速的查询到。

1、追加新的内容 

//9.字节流 - 追加新内容
//无论是字节流还是字符流,要追加新的内容都是再FileOutputStream中指定第二个参数为true
@Test public void appendFile(){
	try{
		File file = new File("hello.txt");
		OutputStream fos = new FileOutputStream(file, true);
		
		String str = "\n这是新增加的内容";
		fos.write(str.getBytes());
		fos.close();
			
	}catch(Exception e){
		e.printStackTrace();
	}
}

2、模拟打印流输出数据

//18.打印流 - 模拟打印的方式输出数据
@Test public void printStream(){
	try{
		PrintStream print = new PrintStream(new FileOutputStream(new File("PrintStream.txt")));
		print.println(true);
		print.println("您好,我是打印输出流");
		print.printf("名字:%s.年龄:%d", "Tom", 32); //格式化输出
		print.close();
		//这里的数据要再PrintStream.txt文件中才能看到
	}catch(Exception e){
		e.printStackTrace();
	}
}

3、使用OutputStream向屏幕输出内容

//19.使用OutputStream向屏幕输出内容
@Test public void systemOutStream(){
	try{
		OutputStream out = System.out;
		out.write("你好".getBytes());
		out.close();
	}catch(Exception e){
		e.printStackTrace();
	}
}

4、标准输出重定向

//20.标准输出重定向
@Test public void redirectOutput(){
	try{
		System.out.println("Print in the Screen. 你好");
		System.setOut(new PrintStream(new FileOutputStream(new File("RedirectOutput.txt"))));
		//下面的输出都将重定向到文件中
		System.out.println("=== 重定向后的输出 ===");
		System.out.println("Hello, Eclipse in Mac.");
	}catch(Exception e){
		e.printStackTrace();
	}
}

5、标准输入重定向

//21.标准输入重定向
@Test public void redirectInput(){
	try{
		File file = new File("RedirectOutput.txt");
		if(!file.exists()){
			System.out.println("文件不存在!");
			return;
		}else{
			System.setIn(new FileInputStream(file));
			byte b[] = new byte[1000];
			int length = System.in.read(b);
			System.out.println("读入的内容为:" + new String(b, 0, length));
		}
	}catch(Exception e){
		e.printStackTrace();
	}
}

6、错误输出重定向

//22.错误输出重定向
@Test public void redirectErrOutput(){
	try{
		System.err.println("Print in the Screen. 你好");
		System.setErr(new PrintStream(new FileOutputStream(new File("RedirectErrOutput.txt"))));
		//下面的输出都将重定向到文件中
		System.err.println("=== 重定向后的错误输出 ===");
		System.err.println("Hello, Eclipse in Mac.");
	}catch(Exception e){
		e.printStackTrace();
	}
}

 

感谢以下博文的参考:

1、http://www.cnblogs.com/rollenholt/archive/2011/09/11/2173787.html

2、http://www.cnblogs.com/oubo/archive/2012/01/06/2394638.html

 

2.熟悉Java基本类库系列——Java IO 类库

标签:

原文地址:http://www.cnblogs.com/chanshuyi/p/4387524.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!