黑马程序员——Java IO流 1——字符流

------- android培训java培训、期待与您交流! ----------

IO流,英文I/O Stream,代表输入输出流。流表示一连串的数据。

当前程序用输入流(InputStream)从数据源(比如硬盘上的文件,键盘,条码扫描枪,其他程序等)读取数据,如下图所示:


当前程序用输出流(OutputStream)向目的地(比如硬盘上的文件,键盘,条码扫描枪,其他程序等)写入数据,如下图所示:


java中关于IO流的类主要都在java.io包中,四个主要基类如下表:


FileWrite

FIleWriter类继承Writer类,它有一个构造方法FileWriter(String FileName),用来创建FileWriter实例,FileWriter实例一但被初始化, 就必须要明确被操作的文件。如果FileName指定目录存在,但文件不存在,则创建新文件,如果文件已存在,则覆盖成空文件。如果FileName参数直接传文件名,不写路径,则创建在项目目录下。

FileWriter类还有继承来的write(String str)方法,用于将字符串写入到流中。

flush():将流对象缓冲中的数据刷到目的地中。

close():该方法用于关闭流资源,关闭之前会将流对象缓冲中的数据刷到目的地中。

上面的方法用法的例子:

import java.io.FileWriter;
import java.io.IOException;

public class FileWriterDemo {
	public static void main(String[] args) throws IOException {
		String fileName="F:\\Mytreasure\\study\\java\\IO\\a.txt";
		FileWriter fileWriter=new FileWriter(fileName);
		fileWriter.write("nihao");
		fileWriter.flush();
		fileWriter.write("haha");
		fileWriter.close();
		//在close()后调用write会报异常
		//fileWriter.write("after close invoke me");	
	}
}

IO异常处理

下面是标准的IO异常处理过程:

import java.io.FileWriter;
import java.io.IOException;

public class IOExceptionDemo {
	public static void main(String[] args) {
		FileWriter fw=null;
		try {
			fw=new FileWriter("test.txt");
			fw.write("你好");			
		} catch (IOException e) {
			System.err.println(e.toString());
		}finally {
			try {
				if(fw!=null)
				fw.close();
			} catch (IOException e) {
				System.err.println(e.toString());
			}		
		}
		System.out.println("文件写入成功");
	}
}
上面的方法虽然繁琐,但是可以保证程序最低的报错几率。注意上面的System.err.println()它的功能和System.out.println()一样,只不过,在MyEclipse中输出时显示红色字体,以突出是错误提示。

在文件中续写数据

下面的代码作用是在已有文件中续写数据:
import java.io.FileWriter;
import java.io.IOException;

public class ContinueWrite {
	public static void main(String[] args) {
		FileWriter fw=null;
		try {
			fw=new FileWriter("test.txt",true);
			fw.write("\r\n前面是windows换行");
			fw.write("\n前面是Linux换行");//用记事本打开时,此换行不好使
		} catch (IOException e) {
			System.err.println(e.toString());
		}finally{
			try {
				if(fw!=null){
					fw.close();
				}
			} catch (Exception e) {
				System.err.println(e.toString());
			}
		}
	}
}
其中,在FileWriter构造函数第二个参数如果传为true,表明不覆盖原来的文件内容,而是在原来的文件内容后边续写。
注意,在写入字符串时,如果字符串中有\n,在用记事本打开时,会显示为小黑方块,因为\n是Linux下换行,\r\n才是windows换行。

文本文件读取

读取文件主要使用FileReader类,下面的代码是一个字符一个字符读取文本文件:
import java.io.FileReader;
import java.io.IOException;

public class FileReaderDemo {
	public static void main(String[] args) {
		// 创建一个文件读取流对象,和指定名称的文件相关联
		// 要保证该文件已经存在,如不存在,会发生异常FileNotFoundException
		FileReader fileReader = null;
		try {
			fileReader = new FileReader("test.txt");
			int ch=0;
			while ((ch=fileReader.read())!=-1) {
				System.out.println((char) ch);
			}
		} catch (IOException e) {
			System.err.println(e.toString());
		} finally {
			try {
				if (fileReader != null) {
					fileReader.close();
				}
			} catch (IOException e) {
				System.err.println(e.toString());
			}
		}
	}
}
上述代码中的read()方法,一次读一个字符,且会自动往后读,如果读到文件末尾,则会返回-1;
还可以利用字符数组读取文本文件中的数据:
import java.io.FileReader;
import java.io.IOException;

public class ReadFileByCharArray {
	public static void main(String[] args) {
		char[] buffer=new char[1024];
		FileReader fr=null;
		try {
			
			fr = new FileReader("test.txt");
		    int	num=0;
			while ((num=fr.read(buffer))!=-1){
				System.out.println(new String(buffer,0,num));
			}
		} catch (IOException e) {
			System.err.println(e.toString());
		} finally {
			try {
				if (fr != null)
					fr.close();
			} catch (IOException e) {
				System.err.println(e.toString());
			}
		}
	}
}
其中当调用FileReader的read(char[] buffer)方法时,返回的是此轮读到的字符个数,如果一个字符都没读到,则返回-1。上面代码中利用String类的带三个参数的构造函数new String(char[],int,int),其中第一个参数表示构成字符串的字符数组,第二个参数表示导入字符串的第一个字符在字符数组中的索引,第三个参数表示导入字符串的最后一个字符在字符数组中的索引。这种方法要比上面的一种读取方法要好的多,因为是从文件一批一批的读数据,性能会高很多。另外,字符数组buffer一般都声明成1024个元素,这被证明是最合适的大小。
再举个例子,用FileReader从硬盘读取一个.java文件并打印出来,代码如下:
import java.io.FileReader;
import java.io.IOException;

public class FileReaderExercise {
	//从硬盘读取一个.java文件,并打印出来
	public static void main(String[] args) {
		FileReader fr=null;
		char[] buffer=new char[1024];
		int num=0;
		try {
			fr=new FileReader("Demo.java");
			while((num=fr.read(buffer))!=-1){
				System.out.print(new String(buffer,0,num));
			}
		} catch (IOException e) {
			System.err.println(e.toString());
		} finally {
			try {
				if (fr != null)
					fr.close();
			} catch (IOException e) {
				System.err.println(e.toString());
			}
		}
	}
}

拷贝文件

拷贝文件就是将文件用输入流输入到程序中,再用输出流输出到不同位置,举例来说就是将c盘目录下的test.txt复制到d盘目录下的test.txt的代码如下:
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

public class CopyFileDemo {
	public static void main(String[] args)  {
		copy("c:\\test.txt", "d:\\test.txt");
	}
	public static void copy(String source,String destination){
		FileWriter fw=null;
		FileReader fr=null;
		char[] buffer=new char[1024];
		int len=0;
		try {
			fw=new FileWriter(destination);
			fr=new FileReader(source);
			while ((len=fr.read(buffer))!=-1) {
				fw.write(buffer,0,len);
			}
		} catch (IOException e) {
			throw new RuntimeException(e);
		}finally{
			try {
				if(fw!=null)fw.close();
				if(fr!=null)fr.close();
			} catch (IOException e2) {
				throw new RuntimeException(e2);
			}
		}		
	}
}

BufferedWriter

缓冲区类的出现是为了提高流的操作效率而出现的。
所以在创建缓冲区前,必须先有流对象。
BufferedWriter类主要是为了缓冲输出流。
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;

public class BufferedWriterDemo {
	public static void main(String[] args) {
		FileWriter fw=null;
		BufferedWriter bw=null;
		try {
			fw = new FileWriter("test.txt",true);
			bw=new BufferedWriter(fw);
			for (int i = 0; i < 5; i++) {
				bw.write("测试文本");
				bw.newLine();//跨平台的换行
			}	
			bw.flush();
			//关闭缓冲区就是关闭流,所以此处不用再写fw.close()
			bw.close();
			
		} catch (IOException e) {
			throw new RuntimeException(e);
		} finally {
			try {
				if (fw != null)
					fw.close();
				if (bw != null)
					bw.close();
			} catch (IOException e) {
				throw new RuntimeException(e);
			}
		}
	}
}

BufferedReader

BufferedReader主要是为了缓冲输入流。基本用法例子如下:
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class BufferedReaderDemo {
	public static void main(String[] args) {
		FileReader fr=null;
		BufferedReader br=null;
		String line="";
		try {
			fr=new FileReader("test.txt");
			br=new BufferedReader(fr);
			while((line=br.readLine())!=null){
				System.out.println(line);
			}
		} catch (IOException e) {
			throw new RuntimeException(e);
		} finally {
			try {
				if (br != null)
					br.close();
			} catch (IOException e) {
				throw new RuntimeException(e);
			}
		}
	}
}
注意上面的readLine()方法并不返回换行符,所以需要自己加换行。readLine方法的原理:其实从硬盘到内存还是一个字符一个字符读入,但是从内存到当前程序时,是先读取完一行(以遇到\r或\n为标志),然后传给当前程序使用。

通过缓冲区复制文本文件

代码如下:
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

public class BufferedCopyDemo {
	public static void main(String[] args) {
		BufferedReader br=null;
		BufferedWriter bw=null;
		try {
			br=new BufferedReader(new FileReader("test.txt"));
			bw=new BufferedWriter(new FileWriter("test_copy.txt"));
			String line="";
			while ((line=br.readLine())!=null) {
				bw.write(line);
				bw.newLine();
			}
		} catch (IOException e) {
			throw new RuntimeException(e);
		} finally {
			try {
				if (br != null)
					br.close();
				if (bw != null)
					bw.close();
			} catch (IOException e) {
				throw new RuntimeException(e);
			}
		}
	}
}

MyBufferedReader

为了更深入的理解BufferedReader的原理,我们自定义一个MyBufferedReader类来模拟BufferedReader的功能。
代码如下:
import java.io.FileReader;
import java.io.IOException;

class MyBufferedReader{
	FileReader r;
	public MyBufferedReader(FileReader r) {
		this.r=r;
	}
	public String readLine() throws IOException{
		//原生的BufferedReader方法封装的是字符数组,我们为了演示方便,这里用StringBuilder
		StringBuilder sb=new StringBuilder();
		int ch=0;
		while((ch=r.read())!=-1){
			if (ch=='\r') {
				continue;
			}
			if (ch=='\n') {
				return sb.toString();
			}
			sb.append((char)ch);
		}
		//这里是防止读到最后一行时,无行终止符,导致最后一行文本没读进来的问题
		if (sb.length()>0) {
			return sb.toString();
		}
		return null;	
	}
	public void close() throws IOException{
		r.close();
	}
}

public class MyBufferReaderDemo {
	public static void main(String[] args) {
		MyBufferedReader mybr=null;
		try {
			mybr=new MyBufferedReader(new FileReader("test.txt"));
			String line="";
			while ((line=mybr.readLine())!=null) {
				System.out.println(line);
			}
		} catch (IOException e) {
			throw new RuntimeException(e);
		} finally {
			try {
				if (mybr != null)
					mybr.close();
			} catch (IOException e) {
				throw new RuntimeException(e);
			}
		}
	}
}

装饰设计模式

英文名Decorator Pattern,含义是给对象添加一些额外的职责。从上面的MyBufferedReader类可以看出,它对FileReader类进行了增强,方法是把FileReader对象通过构造函数赋给私有成员,再通过MyBufferedReader类的方法去增强FileReader类的相应方法,这种面向对象设计的模式很常用,所以技术界把它称为装饰设计模式。由此可以看出BufferedReader就是对FileReader类的装饰。下面用Person类举例说明装饰设计模式的实现方法。
//被装饰的类
class Person{
	public void eat(){
		System.out.println("吃饭");
	}
}
//装饰类
class DecoratorPerson{
	private Person p;
	public DecoratorPerson(Person p) {
		this.p=p;
	}
	public void enjoyEat(){
		System.out.println("开胃菜");
		p.eat();
		System.out.println("水果");
	}
}

public class DecoratorPattern {
	public static void main(String[] args) {
		Person person=new Person();//定义被装饰的对象
		DecoratorPerson dp=new DecoratorPerson(person);
		dp.enjoyEat();
	}
}
当想要对已有的对象进行功能增强时,可以先定义一个类,然后将已有对象传入,通过定义新方法调用已有对象的方法并进行增强,该自定义类称为装饰类。
装饰类通常会通过构造方法接受被装饰的对象

装饰与继承的区别

假设要建立一个类继承体系,有两种方式,一种是继承方式,一种是用装饰设计模式的方式,如下所示:
继承方式:
MyReader//专门用于读取数据的类
|--MyTextReader
|--MyBufferTextReader
|--MyMediaReader
|--MyBufferMediaReader
|--MyDataReader
|--MyBufferDataReader
装饰设计模式:
MyReader
|--MyTextReader
|--MyMediaReader
|--MyDataReader
|--MyBufferReader
class MyBufferReader extends MyReader
{
MyBufferReader(MyReader r)
{}
}
可以看出继承的体系结构臃肿,但是装饰设计模式的体系结构很优雅,并且更灵活,更具扩展性(比如假设今后再加一个类MyMobieReader,那么在继承体系结构中,就需要再创建一个MyBufferMobieReader类,而装饰设计模式只需加MyMobieReader类,在调用时,修改一下传进MyBufferReader装饰类的参数即可)。
因为装饰类只不过是对已有类的增强,所以一般在同一个体系中。

优化自定义装饰类

下面让我们根据装饰设计模式优化一下MyBufferedReader
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;

class MyBufferedReader extends Reader{//这里继承Reader以使MyBufferedReader与Reader在一个体系中
	Reader r;//这里把FileReader改为Reader,从而使本类不只可以装饰FileReader,还可以装饰其他Reader
	public MyBufferedReader(Reader r) {
		this.r=r;
	}
	public String readLine() throws IOException{
		//原生的BufferedReader方法封装的是字符数组,我们为了演示方便,这里用StringBuilder
		StringBuilder sb=new StringBuilder();
		int ch=0;
		while((ch=r.read())!=-1){
			if (ch=='\r') {
				continue;
			}
			if (ch=='\n') {
				return sb.toString();
			}
			sb.append((char)ch);
		}
		//这里是防止读到最后一行时,无行终止符,导致最后一行文本没读进来的问题
		if (sb.length()>0) {
			return sb.toString();
		}
		return null;	
	}
	public void close() throws IOException{
		r.close();
	}
	@Override
	public int read(char[] cbuf, int off, int len) throws IOException {
		return r.read(cbuf, off, len);
	}
}

LineNumberReader

带行号的BufferedReader,是对BufferedReader类的装饰,用法例子如下:
import java.io.FileReader;
import java.io.IOException;
import java.io.LineNumberReader;

public class LineNumberReaderDemo {
	public static void main(String[] args) {
		FileReader fr=null;
		LineNumberReader lnr=null;
		
		try {
			fr=new FileReader("AutoBoxing.java");
			lnr=new LineNumberReader(fr);
			lnr.setLineNumber(100);
			String buf=null;
			while((buf=lnr.readLine())!=null){
				System.out.println(lnr.getLineNumber()+":"+buf);
			}
		} catch (IOException e) {
			throw new RuntimeException(e);
		} finally {
			try {
				if (lnr != null)
					lnr.close();
			} catch (IOException e) {
				throw new RuntimeException(e);
			}
		}
	}
}

MyLineNumberReader

模仿LineNumberReader自定义一个MyLineNumberReader,要求主要功能和LineNumberReader一样,并通过此练习熟悉装饰设计模式的用法和作用。
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;

class MyLineNumberReader extends MyBufferedReader{
	int lineNumber;
	public MyLineNumberReader(Reader r){
		super(r);
	}
	public String readLine() throws IOException{
		lineNumber++;
		return super.readLine();
	}
	public void setLineNumber(int number){
		this.lineNumber=number;
	}
	public int getLineNumber(){
		return this.lineNumber;
	}
}
public class MyLineNumberDemo{
	public static void main(String[] args)throws IOException {
		FileReader fr=new FileReader("AutoBoxing.java");
		MyLineNumberReader mnd=new MyLineNumberReader(fr);
		String line="";
		mnd.setLineNumber(10);
		while((line=mnd.readLine())!=null){
			System.out.println(mnd.getLineNumber()+":"+line);
		}
		mnd.close();
	}
}











你可能感兴趣的:(IO,IO流)