一、流的概念和定义
流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象。即数据在两设备间的传输称为流,流的本质是数据传输,根据数据传输特性将流抽象为各种类,方便更直观的进行数据操作。
二、IO流分类
IO流用于处理设备之间的数据传输,Java对数据的操作时通过流的方式。
流按操作数据分为字节流和字符流。按流向分为输入流和输出流。
三、IO流区别
字符流的由来: 因为数据编码的不同,而有了对字符进行高效操作的流对象。本质其实就是基于字节流读取时,去查了指定的码表。
字节流和字符流的区别:
读写单位不同:字节流以字节(8bit)为单位,字符流以字符为单位,根据码表映射字符,一次可能读多个字节。
处理对象不同:字节流能处理所有类型的数据(如图片、avi等),而字符流只能处理字符类型的数据。
结论:只要是处理纯文本数据,就优先考虑使用字符流。 除此之外都使用字节流。
输入流和输出流:
对输入流只能进行读操作,对输出流只能进行写操作,程序中需要根据待传输数据的不同特性而使用不同的流。
四、流操作规律
(1)明确源和目的
源:输入流,InputStream. Reader
目的: 输出流, OutputStream Writer
(2)操作的数据是否是纯文本
是: 字符流
不是:字节流
(3)当体系明确后,再明确哪个对象。
通过设备来进行区分
源设备:内存,键盘,硬盘
目的设备: 内存,硬盘,控制台
扩展:System改变源和目的,System.setin( ) 和System.setout( )
五、Java IO流对象
1、输入字节流InputStream
输入字节流的继承图可见上图,可以看出:
(1)InputStream 是所有的输入字节流的父类,它是一个抽象类。
(2)ByteArrayInputStream、StringBufferInputStream、FileInputStream 是三种基本的介质流,它们分别从Byte数组、StringBuffer、和本地文件中读取数据。
(3)PipedInputStream 是从与其它线程共用的管道中读取数据,与Piped 相关的知识后续单独介绍。
(4)ObjectInputStream 和所有FilterInputStream的子类都是装饰流(装饰器模式的主角)。
(5)PrintStream字节打印流,该流提供了打印方法,可以将各种数据类型的数据都原样打印出来。
构造函数可以接受的类型:
1.file对象 File
2.字符串路径 String
3.字节输出流 OutputStream
2、输出字节流OutputStream
IO 中输出字节流的继承图可见上图,可以看出:
(1)OutputStream 是所有的输出字节流的父类,它是一个抽象类。
(2)ByteArrayOutputStream、FileOutputStream是两种基本的介质流,它们分别向Byte 数组、和本地文件中写入数据。
(3)PipedOutputStream 是向与其它线程共用的管道中写入数据,
(4)ObjectOutputStream 和所有FilterOutputStream的子类都是装饰流。
3、字节流的输入与输出的对应
图中蓝色的为主要的对应部分,红色的部分就是不对应部分。紫色的虚线部分代表这些流一般要搭配使用。从上面的图中可以看出Java IO 中的字节流是极其对称的。“存在及合理”我们看看这些字节流中不太对称的几个类吧!
(1)LineNumberInputStream 主要完成从流中读取数据时,会得到相应的行号,至于什么时候分行、在哪里分行是由改类主动确定的,并不是在原始中有这样一个行号。在输出部分没有对应的部分,我们完全可以自己建立一个LineNumberOutputStream,在最初写入时会有一个基准的行号,以后每次遇到换行时会在下一行添加一个行号,看起来也是可以的。好像更不入流了。
(2)PushbackInputStream 的功能是查看最后一个字节,不满意就放入缓冲区。主要用在编译器的语法、词法分析部分。输出部分的BufferedOutputStream 几乎实现相近的功能。
(3)StringBufferInputStream 已经被Deprecated,本身就不应该出现在InputStream部分,主要因为String 应该属于字符流的范围。已经被废弃了,当然输出部分也没有必要需要它了!还允许它存在只是为了保持版本的向下兼容而已。
(4)SequenceInputStream 可以认为是一个工具类,将两个或者多个输入流当成一个输入流依次读取。完全可以从IO 包中去除,还完全不影响IO 包的结构,却让其更“纯洁”――纯洁的Decorator 模式。
(5)PrintStream 也可以认为是一个辅助工具。主要可以向其他输出流,或者FileInputStream 写入数据,本身内部实现还是带缓冲的。本质上是对其它流的综合运用的一个工具而已。一样可以踢出IO 包!System.out 和System.out 就是PrintStream 的实例!
4、字节流练习
(1)FileInputStream和FileOutputStream
class InputStreamDemo
{
//1.读取文件方式一:一个字节一个字节的读取
publicstatic void read_1(){
FileInputStreamfis=null;
try{
fis=newFileInputStream("E:\\IO\\day\\BufferedWriter.txt");
intch=0;
while((ch=fis.read())!=-1){
System.out.print((char)ch);
}
}
catch(Exceptione){
thrownew RuntimeException("读取失败");
}
finally{
if(fis!=null)
try{
fis.close();
}
catch(Exceptione){
thrownew RuntimeException("关闭读取流失败");
}
}
}
//2.读取文件方式二:数组缓冲读取
publicstatic void read_2(){
FileInputStreamfis=null;
try{
fis=newFileInputStream("E:\\IO\\day\\文章摘抄.txt");
byte[]buff=new byte[1024];
intch=0;
while((ch=fis.read(buff))!=-1){
System.out.print(newString(buff,0,ch));
}
}
catch(Exceptione){
thrownew RuntimeException("读取失败");
}
finally{
if(fis!=null)
try{
fis.close();
}
catch(Exceptione){
thrownew RuntimeException("关闭读取流失败");
}
}
}
//3.读取方式三:指定数组读取
public static void read_3(){
FileInputStreamfis=null;
try{
fis=newFileInputStream("E:\\IO\\day\\文章摘抄.txt");
byte[]buff=new byte[fis.available()];
/**
返回的实际可读字节数,也就是总大小.但是需要注意的是。java虚拟机中的字符是Unicode编码,占两个字符,
假如一个txt文档内容为abcde是ASCII码,那么每个字符就是一个字节,所以共5个字节。
注意,class文件中的编码也不是Unicode的,而是utf8的
*/
intx=fis.read(buff);
System.out.print(new String(buff));
}
catch(Exceptione){
thrownew RuntimeException("读取失败");
}
finally{
if(fis!=null)
try{
fis.close();
}
catch(Exceptione){
thrownew RuntimeException("关闭读取流失败");
}
}
}
//4.字节流缓冲区拷贝图片。
publicstatic void copyPic(){
BufferedInputStreambis=null;
BufferedOutputStreambos=null;
try{
bis=newBufferedInputStream(new FileInputStream("E:\\IO\\tupian1.jpg"));
bos=newBufferedOutputStream(newFileOutputStream("E:\\IO\\day\\tupian1.jpg"));
intch=0;
while((ch=bis.read())!=-1){
bos.write(ch);
}
}
catch(Exceptione){
thrownew RuntimeException("拷贝失败");
}
finally{
if(bis!=null)
try{
bis.close();
}
catch(Exceptione){
thrownew RuntimeException("关闭读取流失败");
}
}
if(bos!=null)
try{
bos.close();
}
catch(Exceptione){
thrownew RuntimeException("关闭写入流失败");
}
}
}
class Test8
{
publicstatic void main(String[] args){
//InputStreamDemo.read_3();
InputStreamDemo.copyPic();
}
}
(2)自定义字节缓冲区
class MyBufferedInputStream
{
privateFileInputStream fis;//文件输入流
privateint count;//定义计数器
privateint pos;//定义坐标;
byte[]buff=new byte[1024];//定义缓冲数组。
//定于构造函数需传入,文件输入流对象。
MyBufferedInputStream(FileInputStreamfis ){
this.fis=fis;
}
publicint myread()throws Exception{
if(count==0){
count=fis.read(buff);
pos=0;
byteb=buff[pos];
count--;
pos++;
returnb;
/**
<span style="color:#ff0000;">当如果读取到二进制位数为11111111时,byte为 - 1,提升为int时前面16位补1,所以 int也为 -1;
只要前面补0那么可以保留原字节数据不变的情况下,又可以避免- 1的出现。
- 1与255的二进制位数相&的话,则可以实现。
11111111 11111111 1111111111111111
00000000 00000000 0000000011111111
两数相&得00000000 0000000000000000 11111111
按理说我们读取到byte为一个字节,提升 int为四个字节,那么把他输出到目的地中文件会比原文件大四倍的,为什么没有变大呢?
这是因为Write()方法会强制将前三个字节去除,保留最后一个字节8位。</span>
*/
}
if(count>0){
byteb=buff[pos];
count--;
pos++;
returnb;
}
return-1;
}
publicvoid myclose()throws Exception{
fis.close();
}
}
class Test8
{
publicstatic void main(String []args ){
MyBufferedInputStreammbis=null;
BufferedOutputStreambos=null;
try{
mbis=new MyBufferedInputStream(new FileInputStream("E:\\IO\\快捷键.txt"));
bos=newBufferedOutputStream(new FileOutputStream("E:\\IO\\快捷键11.txt"));
intch=0;
while((ch=mbis.myread())!=-1){
bos.write(ch);
}
}
catch(Exceptione){
thrownew RuntimeException("读取错误");
}
finally{
if(mbis!=null)
try{
mbis.myclose();
}
catch(Exceptione){
thrownew RuntimeException("关闭读取流失败");
}
if(bos!=null)
try{
bos.close();
}
catch(Exceptione){
thrownew RuntimeException("关闭读取流失败");
}
}
}
}
(3)PrintStream 创建异常日志
import java.util.*;
import java.text.SimpleDateFormat;
class Test8
{
publicstatic void logging(){
try{
int[]i=new int[2];
System.out.println(i[3]);//数组越界异常
}
catch(Exceptione){
try{
Dated=new Date();//创建时间对象。
SimpleDateFormatsd=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//对时间进行格式对象。
Strings=sd.format(d);//格式化时间。
PrintStreamps=new PrintStream("E:\\IO\\异常信息11.txt");
ps.print("异常时间"+s);//输出格式。
e.printStackTrace(ps);//将此异常信息 及其追踪输出到指定的输出流。
}
catch(Exceptione1){
thrownew RuntimeException("日志创建失败");
}
}
}
publicstatic void main(String[] args){
logging();
}
}
(4)合并流(SequenceInputStream)
public SequenceInputStream(Enumeration<?extends InputStream> e) 多个输入流
public SequenceInputStream(InputStream s1,InputStream s2) 两个输入流
//练习一:将多个文件整理生成一个文件。
public class SequenceInputStreamDemo {
public static void method_1() throws IOException{
Vector<FileInputStream> v= new Vector<FileInputStream>();
v.add( new FileInputStream("E:\\IO\\1.txt" ));
v.add( new FileInputStream("E:\\IO\\2.txt" ));
v.add( newFileInputStream("E:\\IO\\3.txt" ));
Enumeration<FileInputStream> e=v.elements();
SequenceInputStream sis= new SequenceInputStream(e);
FileOutputStream fo= new FileOutputStream("E:\\IO\\4.txt" );
byte [] buff=new byte[1024];
int num=0;
while ((num=sis.read(buff))!=-1){
fo.write(buff,0,num);
}
fo.close();
sis.close();
}
//练习二:把一个文件进行切割,然后合并到一起。
public class SplitFileDemo {
//切割文件
public static void method_1() throws IOException{
FileInputStream fi= new FileInputStream("E:\\IO\\1.jpg" );
FileOutputStream fo= null ;
byte[] buff= new byte [1024*1024];
int num=0;
int count=1;
while((num=fi.read(buff))!=-1){
fo= new FileOutputStream("E:\\IO\\aaa\\" +(count++)+".jpg");
fo.write(buff,0,num);
fo.close();
}
fi.close();
}
//合并文件
public static void method_2() throws Exception{
ArrayList<FileInputStream> list= newArrayList<FileInputStream>();
for (int x=1;x<=8;x++){
list.add( newFileInputStream("E:\\IO\\aaa\\" +x+".jpg" ));
}
finalIterator<FileInputStream> it=list.iterator();
Enumeration<FileInputStream> em= newEnumeration<FileInputStream>(){
public booleanhasMoreElements(){
return it.hasNext();
}
public FileInputStreamnextElement(){
return it.next();
}
};
SequenceInputStream sis= newSequenceInputStream(em);
FileOutputStream fos= newFileOutputStream("E:\\IO\\aaa\\0.jpg" );
byte [] buff=new byte[1024];
int len=0;
while((len=sis.read(buff))!=-1){
fos.write(buff,0,len);
}
fos.close();
sis.close();
}
public static void main(String[] args) throws Exception {
method_2();
}
}
(6)数据输入和输出流(DataInputStream、DataOutputStream)
public class DataStreamDemo {
public static void write(){
try {
DataOutputStream dos= newDataOutputStream(new FileOutputStream("E:\\IO\\DataStreamDemo.txt"));
dos.writeDouble(232.23);
dos.writeBoolean( true );
dos.writeInt(55);
dos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void read(){
try {
DataInputStream dis= newDataInputStream( new FileInputStream("E:\\IO\\DataStreamDemo.txt" ));
double d=dis.readDouble();
boolean b=dis.readBoolean();
int i=dis.readInt();
System. err.println("d=" +d);
System. err.println("b=" +b);
System. err.println("i=" +i);
dis.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void Write_1(){
try {
DataOutputStream dos= newDataOutputStream(new FileOutputStream("E:\\IO\\dataufl.txt" ));
dos.writeUTF( "你好" );
dos.close();
} catch (Exception e) {
// TODO Auto-generated catchblock
e.printStackTrace();
}
}
public static void read_1(){
try {
DataInputStream dis= newDataInputStream( new FileInputStream("E:\\IO\\dataufl.txt" ));
// byte[] buff=new byte[1024];
// dis.read(buff);
// String name=new String(buff);
//如果输出流用writeUTF输出的话必须用readUTF来读取。
String name=dis.readUTF();
System. err .println(name);
dis.close();
} catch (Exception e) {
// TODO Auto-generated catchblock
e.printStackTrace();
}
}
public static void main(String[] args) {
//read();
//write();
//Write_1();
read_1();
}
}
5、字符输入流Reader
在上面的继承关系图中可以看出:
(1)Reader 是所有的输入字符流的父类,它是一个抽象类。
(2)CharReader、StringReader 是两种基本的介质流,它们分别将Char 数组、String中读取数据。
(3)PipedReader 是从与其它线程共用的管道中读取数据。
(4)BufferedReader 很明显就是一个装饰器,它和其子类负责装饰其它Reader 对象。
(5)FilterReader 是所有自定义具体装饰流的父类,其子类PushbackReader 对Reader 对象进行装饰,会增加一个行号。
(6)InputStreamReader 是一个连接字节流和字符流的桥梁,它将字节流转变为字符流。FileReader 可以说是一个达到此功能、常用的工具类,在其源代码中明显使用了将FileInputStream 转变为Reader 的方法。我们可以从这个类中得到一定的技巧。Reader 中各个类的用途和使用方法基本和InputStream中的类使用一致。后面会有Reader 与InputStream 的对应关系。
(7) LineNumberReader装饰类
装饰类与继承的区别:
装饰模式比继承要灵活,避免了继承体系的臃肿。而且降低了类与类之间的联系。
装饰类因为增强已有对象,具备的功能和已有的是相同的,只不过提供了更强的功能。
6、字符输出流Writer
在上面的关系图中可以看出:
(1)Writer 是所有的输出字符流的父类,它是一个抽象类。
(2)CharArrayWriter、StringWriter 是两种基本的介质流,它们分别向Char 数组、String 中写入数据。
(3)PipedWriter 是向与其它线程共用的管道中写入数据,
(4)BufferedWriter 是一个装饰器为Writer 提供缓冲功能。
(5)PrintWriter 和PrintStream 极其类似,功能和使用也非常相似。
构造函数可以接受的类型:
a.file对象 File
b.字符串路径 String
c.字节输出流 OutputStream
d.字符输出流 Writer
(6)OutputStreamWriter 是OutputStream 到Writer 转换的桥梁,它的子类FileWriter其实就是一个实现此功能的具体类。功能和使用和OutputStream 极其类似,后面会有它们的对应。
7、字符流的输入和输出对比
8、字符流与字节流转换
转换流的特点:
(1)其是字符流和字节流之间的桥梁
(2)可对读取到的字节数据经过指定编码转换成字符
(3)可对读取到的字符数据经过指定编码转换成字节
何时使用转换流?
(1)当字节和字符之间有转换动作时;
(2)流操作的数据需要编码或解码时。
具体的对象体现:
(1)InputStreamReader:字节到字符的桥梁
(2)OutputStreamWriter:字符到字节的桥梁
这两个流对象是字符体系中的成员,它们有转换作用,本身又是字符流,所以在构造的时候需要传入字节流对象进来。
注意:
字符转字节,明明传了一个字节流对象进去,变成了一个字符流对象,为什么是字符转字节呢?
writer方法字符串将写入了到字符流。
OutputstreamWriter 将字符流进行编码(encodee 默认用系统平台的编码方式)这时候得到的是 一堆的byte[],然后将byte[]写入到 FileOutputStream
即FileOutputStream 是一个接受字节流的容器。只接受字节流。你明明输入的一个字符串,FileOutputStream 却成功的得到了byte[],这就是所谓的转换。
本质上:
String s --->Btye[]---->输出到字节流中去
只不过你看到的效果是 s---->字节流中有东西了。
中间过程,java给你封装了。
转换流的字符编码
InputStreamReader和OutputStreamReader,加入了编码表,可以在对对象构造时加入编码表。
注意:PrintStream和PintWriter 也可以加入编码表 ,不过只能打印,所以不常用。
编码:字符串变成字节数组。
解码:字节数组变成字符串。
public static void method_1(){
try {
String s= "你好" ;
//编码。
byte [] b=s.getBytes("UTF-8" );
System. err.println(Arrays.toString(b));
//解码。
String ss= new String(b,"UTF-8" );
System. err .println(ss);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
练习:
class Test8
{
publicstatic void conversion(){
InputStreamis=System.in;//键盘输入流
StringBuildersb=new StringBuilder();//字符串缓冲区
try{
while(true){
intch=is.read();//一个字节一个字节读取,然后判断该字节。
if(ch=='\r')//如果为'\r'则再读取下一个字节。
continue;
if(ch=='\n'){//如果是'\n',则输出出来字符串缓冲区中的数据。
String s=sb.toString();
if("over".equals(s))//判断数据是否为over,是则结束,并把该数据大写化。
break;
System.err.println(s.toUpperCase());
sb.delete(0,sb.length());//输出数据后,清空数据。否则会包含上次存入的数据。
}
else{
sb.append((char)ch);//如果不为'\r'和'\n'的话,则吧数据存入到字符串缓冲区中。
}
}
}
catch(Exceptione){
thrownew RuntimeException("错误");
}
finally{
if(is!=null)
try{
is.close();
}
catch(Exceptione){
thrownew RuntimeException("关闭读取流失败");
}
}
}
//1.字节流转字符流
//通过录入一行数据并转换成大写,发现其实就是读取一行数据的原理。也就是readLine( )方法。
//readline()方法是字符流bufferedReader类中的方法。而键盘录入的read方法是字节流InputStream中的方法。则需要将字节流转换为字符流。
publicstatic void conversion_1(){
InputStreamis=System.in;
BufferedReaderbr=null;
try{
br=newBufferedReader(new InputStreamReader(is));
Stringstr=null;
while((str=br.readLine())!=null){
if("over".equals(str))
break;
System.out.println(str.toUpperCase());
}
}
catch(Exceptione){
thrownew RuntimeException("读取数据失败");
}
finally{
if(br!=null)
try{
}
catch(Exceptione){
thrownew RuntimeException("关闭读取流失败");
}
}
}
//2.字符流转字节流
publicstatic void conversion_2(){
BufferedReader br=null;
BufferedWriter bw=null;
try{
br=new BufferedReader(new InputStreamReader(System.in));//字节流转字符流
bw=new BufferedWriter(newOutputStreamWriter(System.out));//字符流转字节流
Stringstr=null;
while((str=br.readLine())!=null){
if("over".endsWith(str))//判断输入的数据是否为over,是则退出。
break;
bw.write(str.toUpperCase());
bw.newLine();//换行
bw.flush();//实时刷新输出数据
}
}
catch(Exceptione){
thrownew RuntimeException("读取失败");
}
finally{
if(br!=null)
try{
br.close();
}
catch(Exceptione){
thrownew RuntimeException("关闭读取流失败");
}
if(bw!=null)
try{
bw.close();
}
catch(Exceptione){
thrownew RuntimeException("关闭写入流失败");
}
}
}
publicstatic void main(String[] args){
conversion_2();
}
}
9、字符流练习
(1)FileReader和FileWriter
class FileDemo
{
//1.写入文件
publicstatic void fileWriterDemo(){
try{
//创建一个FileWriter对象,该对象一初始化就需要明确要操作的文件,
//该文件会被创建到指定目录下,如果该目录下有同名文件的话则会被覆盖掉。
FileWriterfw=new FileWriter("E:\\IO\\day\\FileWriterDemo.txt");
//调用write写方法,把数据写入到流中。
fw.write("aaa");
//刷新流缓冲中的数据,刷到目的地中。
fw.flush();
//关闭流资源,关闭之前会刷新一下流缓冲中的数据。
//和flush的区别:flush刷新完后可以继续使用流资源,而close会刷新并关闭流资源,所以不能再使用流资源。
fw.close();
}
catch(Exceptione){
thrownew RuntimeException("写入失败");
}
}
//2.读取文件
publicstatic void fileReaderDemo(){
FileReaderfr=null;
try{
//创建一个fileReader对象,用于读取文本文件的字符流。该对象一初始化也需要明确被读取的文件。
fr=newFileReader("E:\\IO\\day\\FileWriterDemo.txt");
/**
第一种读取方式:一个字符一个字符的读取。
intch=0;
while((ch=fr.read())!=-1){//读取文件,当读到结尾就返回-1;
System.out.print((char)ch);
}
*/
//第二种读取方式:自定义一个字符缓冲区,读取的数据存入该缓冲区中,然后再输出该缓冲区中的数据。提高效率。
char[]buff=new char[1024];
intch=0;
while((ch=fr.read(buff))!=-1){
System.out.println(newString(buff,0,ch));
}
}
catch(Exceptione){
thrownew RuntimeException("读取失败");
}
finally{
if(fr!=null)//判断对象不为空,使程序更严谨。否则为空是会报空指针异常。
try{fr.close();}//关闭流资源。
catch(Exceptione){
thrownew RuntimeException("关闭读取流失败");
}
}
}
//3.拷贝文件
publicstatic void copyFile(){
FileWriterfw=null;
FileReaderfr=null;
try{
fw=newFileWriter("E:\\IO\\day\\快捷键.txt");
fr=newFileReader("E:\\IO\\快捷键.txt");
char[]buff=new char[1024];
intch=0;
while((ch=fr.read(buff))!=-1){
fw.write(buff,0,ch);
}
}
catch(Exceptione){
thrownew RuntimeException("关闭流失败");
}
finally{
if(fw!=null)
try{
fw.close();
}
catch(Exceptione){
thrownew RuntimeException("关闭写入流失败");
}
if(fr!=null)
try{
fr.close();
}
catch(Exceptione){
thrownew RuntimeException("关闭读取流失败");
}
}
}
}
class Test7
{
publicstatic void main(String[] args)
{
//FileDemo.fileWriterDemo();
//FileDemo.fileReaderDemo();
FileDemo.copyFile();
}
}
(2)BufferedReader和BufferedWriter
class BufferedDemo
{
//1.缓冲区写入文件
publicstatic void writer(){
FileWriterfw=null;
BufferedWriterbw=null;
try{
fw=newFileWriter("E:\\IO\\day\\BufferedWriter.txt");
//为提高流操作效率,加入缓冲区技术。
//只要将需要被提高效率的流对象作为参数传递给缓冲区的构造函数即可。
bw=newBufferedWriter(fw);
for(intx=0;x<10;x++){
bw.write("aaa");//将数据写入到缓冲区中。
bw.newLine();//缓冲区提提供了一个跨平台的特有方法:换行。如果用到缓冲区必须要刷新,才能将数据写入到目的地中。
// bw.flush();
}
}
catch(Exceptione){
thrownew RuntimeException("写入文件失败");
}
finally{
if(bw!=null)
try{
bw.close();//关闭缓冲区,就是关闭缓冲区中的流对象,缓冲区调用的是FileWriter对象。
}
catch(Exceptione){
thrownew RuntimeException("关闭写入流失败");
}
}
}
//2.缓冲区读取文件
publicstatic void read(){
FileReaderfr=null;
BufferedReaderbr=null;
try{
fr=newFileReader("E:\\IO\\day\\快捷键.txt");
br=newBufferedReader(fr);//创建一个字符流缓冲区
Stringstr=null;
while((str=br.readLine())!=null){//一行一行的读取。提高读取效率。当返回null时,表示读取到最后了。
System.out.println(str);
}
}
catch(Exceptione){
thrownew RuntimeException("读取文件失败");
}
finally{
if(br!=null)
try{
br.close();
}
catch(Exceptione){
thrownew RuntimeException("写入流失败");
}
}
}
//3.拷贝文件
publicstatic void copyFile(){
FileWriterfw=null;
FileReaderfr=null;
BufferedWriterbw=null;
BufferedReaderbr=null;
try{
br=newBufferedReader(new FileReader("E:\\IO\\快捷键.txt"));
bw=newBufferedWriter(new FileWriter("E:\\IO\\day\\快捷键11.txt"));
Stringstr=null;
while((str=br.readLine())!=null){
bw.write(str);//读取一行的方式时会读取不到换行符,所以需要每读取一行换行一次。
bw.newLine();//防止因意外导致的存入内存的数据丢失,没存入一行数据就写入到目的地中一行数据。
bw.flush();
}
}
catch(Exceptione){
thrownew RuntimeException("拷贝文件失败");
}
finally{
if(br!=null)
try{
br.close();
}
catch(Exceptione){
thrownew RuntimeException("关闭读取流失败失败");
}
if(bw!=null)
try{
bw.close();
}
catch(Exceptione){
thrownew RuntimeException("关闭写入流失败");
}
}
}
}
class Test7
{
publicstatic void main(String[] args)
{
//FileDemo.fileWriterDemo();
//FileDemo.fileReaderDemo();
//FileDemo.copyFile();
//BufferedDemo.writer();
//BufferedDemo.read();
BufferedDemo.copyFile();
}
}
扩展知识:
Readline原理:
模拟readline功能:
class MyBufferedReader
{
privateFileReader fr=null;
MyBufferedReader(FileReaderfr){
this.fr=fr;
}
publicString MyReadLine()throws Exception{
StringBuildersb=new StringBuilder();//定义一个临时容器,原BufferedReader中封装的是字符数组;
intch=0;
while((ch=fr.read())!=-1){
if(ch=='\r')//如果读到是'\r'则返回继续读取下一个字符。
continue;
if(ch=='\n') //如果是'\n'则代表一行读取完毕,存入到目的地中。
returnsb.toString();
else
sb.append((char)ch);//如果都不是则将字符添加到字符缓冲区中。
}
if(sb.length()!=0)//如果字符数组长度不等于空,则代表还有数据存在没有写入到目的地中。
returnsb.toString();
returnnull;
}
publicvoid myclose()throws Exception{
fr.close();
}
}
class Test7
{
publicstatic void read(){
FileReaderfr=null;
MyBufferedReadermbb=null;
try{
fr=newFileReader("E:\\IO\\day\\快捷键11.txt");
mbb=newMyBufferedReader(fr);
Stringstr=null;
while((str=mbb.MyReadLine())!=null){
System.out.println(str);
System.out.println("aaa");
}
}
catch(Exceptione){
thrownew RuntimeException("读取失败");
}
finally{
if(mbb!=null)
try{
mbb.myclose();
}
catch(Exception e){
thrownew RuntimeException("关闭读取流失败");
}
}
}
publicstatic void main(String [] args){
Test7.read();
}
}
(3)LineNumberReader装饰类
//1.数据前加行号和冒号
class LineNumberReaderDemo
{
publicstatic void read(){
FileReaderfr=null;
LineNumberReaderlnr=null;
try{
lnr=newLineNumberReader(new FileReader("E:\\IO\\day\\文章摘抄.txt"));
lnr.setLineNumber(0);//设置当前行号开始(不包含该行号)。
Stringstr=null;
while((str=lnr.readLine())!=null){
System.out.println(lnr.getLineNumber()+":"+str);
}
lnr.close();
}
catch(Exceptione){
throw new RuntimeException("读取失败");
}
}
}
class Test8
{
publicstatic void main(String[] args){
LineNumberReaderDemo.read();
}
}
(4)PrintWriter
public class PrintWriterDemo {
public static void method_1(){
BufferedReader br= null ;
PrintWriter pw= null ;
try {
br= new BufferedReader( newInputStreamReader(System.in ));
//注意,当前面参数是流时,后面可以添加一个参数,当为true时,
//如果输出有标记( println换行是标记)则自动刷新缓冲区
//pw =newPrintWriter(System.out,true);
pw= new PrintWriter( newBufferedWriter( new FileWriter("E:\\IO\\PrintWriter.txt" )), true);
String line= null ;
while ((line=br.readLine())!=null){
if(line.equals("over" ))
break ;
//pw.write(line.toUpperCase());
pw.println(line.toUpperCase());
//字符流需要刷新。
//pw.flush();
}
} catch (IOException e){
throw new RuntimeException("打印失败");
} finally {
try {
if (br!=null )
br.close();
} catch (IOException e) {
// TODO Auto-generatedcatch block
e.printStackTrace();
}
pw.close();
}
}
public static void main(String[] args) {
method_1();
}
}
(5)练习:有五个学生,每个学生有三门课的成绩,从键盘输入以上数据,输入的格式为:zhangsan,30,50,40计算出总成绩,并把学生信息和计算出来的总数高低顺序放在磁盘文件“stu.txt”中。
public class Student implementsComparable<Student> {
private String name;
private int sx ,yw ,wy ;
private int sum ;
Student(String name, int sx,int yw, int wy){
this .name =name;
this .sx =sx;
this .yw =yw;
this .wy =wy;
this .sum =sx+yw+wy;
}
public String getName(){
return name ;
}
public int getSum(){
return sum ;
}
public int compareTo(Student s) {
int num=new Integer( this. sum).compareTo( new Integer(s.sum ));
if (num==0)
return this .name .compareTo(s. name);
return num;
}
public int hashCode(){
return name .hashCode()*77;
}
public boolean equals(Object obj){
if (!(obj instanceof Student))
throw new RuntimeException("不是学生对象" );
Student stu=(Student)obj;
return this .name .equals(stu. name) && this .sum ==stu.sum ;
}
public String toString(){
return "学生" +name+"数学" +sx +"语文" +yw +"英语" +wy ;
}
}
public class StudentInfo {
public static Set<Student> getStudents() throws IOException{
return getStudents( null );
}
public static Set<Student> getStudents(Comparator<Student>cmp) throws IOException{
BufferedReader br= new BufferedReader( new InputStreamReader(System.in));
String line= null ;
Set<Student> set= null ;
if (cmp==null )
set= newTreeSet<Student>();
else
set= newTreeSet<Student>(cmp);
while ((line=br.readLine())!= null){
if ("over".equals(line))
break ;
String[] info=line.split("," );
Student stu= new Student(info[0],Integer.parseInt(info[1]),
Integer.parseInt(info[2]),Integer. parseInt(info[3])
);
set.add(stu);
}
br.close();
return set;
}
public static void WirteFile(Set<Student> stu) throws IOException{
BufferedWriter bw= new BufferedWriter( newFileWriter("E:\\IO\\students.txt" ));
for (Student s:stu){
bw.write(s.toString()+ "\t" );
bw.write(s.getSum()+ "" );
bw.newLine();
bw.flush();
}
bw.close();
}
}
public class StudentInfoTest {
public static void main(String[] args) throws IOException {
Comparator<Student> cmp=Collections. reverseOrder();
Set<Student> set=StudentInfo. getStudents(cmp );
StudentInfo. WirteFile(set);
}
}