IO流概述
一、IO即Input、Ouput组合;
二、IO流特点:
1、IO流用来处理设备之间的数据传输
2、Java对数据的操作是通过流的方式
3、Java用于操作流的对象都在IO包中
三、IO流分类
1、按照操作数据分:字符流和字节流
字节流:字节流可以操作任何数据,因为在计算机中任何数据都是以字节的形式存储的(如图片,音频)
字符流:字符流只能操作纯字符数据,比较方便(不能操作音频和图片)。
2、按照流向分为:输入流和输出流
四、字节流和字符流
1、字节流的抽象基类
InputStram(输入流),OutputSteam(输出流)
2、字符流的抽象基类
Reader(输入流)、Writer(输出流)
住:由这四个类派生出来的子类的名称都是以其父类名称作为后缀来命名的
如:InputStream的子类FileInputSteam
OutputStram的子类FileOutputStram
Reader的子类FileReader
Writer的子类FileWriter
字符流
一、字符流是什么?
1、字符流是可以直接读取字符的IO流
2、字符流读取字符, 就要先读取到字节数据, 然后转为字符. 如果要写出字符, 需要把字符转为字节再写出.
二、FielReader和FileWriter
FileReader
<span style="font-size:14px;">/*FileReader 步骤:1、创建文件读取流对象,并指定路径下的文件名称,如果没指定路径, 则默认是读取该源文件所在的路径下的文件 2、调用读取流对象的read方法,返回读取的字符在ASCII中对应的数字, 如果读取到则返回-1 3、因为没调用一次read方法,仅仅只是读取一个字符,所以要使用循环 4、关闭流 */ import java.io.*; class FileReaderDemo { public static void main(String[] args) { /*FileReader是IO流中的字符输入流,是从硬盘上读取数据用的*/ //步骤1 FileReader fr = null; try{ fr = new FileReader("d:\\file.txt"); //会抛出FileNotFoundException异常 /*因为是读取文件,所以要指定文件以及文件所在的路径,必须保证文件存在 如果要读取得文件不存在,则会发生FileNotFoundException文件找不到异常 */ //步骤2 int charNum = 0; //调用read方法,循环读取文件 while((charNum = fr.read())!=-1){ //read会抛出IOExcepion异常 System.out.print((char)charNum); } } catch(IOException e){ System.out.println(e.toString()); } finally{ //流是必须要关闭的,所以放在finally语句块中 try { if(fr!=null) fr.close(); //会抛出IOException异常 } catch (IOException e) { System.out.println(e.toString()); } } } } </span>
注:输出流中flush和close的区别:
flush是用来刷新缓冲区的,刷完之后还可以写出
close方法是用来关闭流的,在关闭之前会刷新一次缓冲区,刷完之后关闭,不可以再写出
FileWriter
<span style="font-size:14px;">/*FileReader 步骤:1、创建文件写入流对象,并指定路径下的文件名称,如果该路径下没有指定文件, 则会在该路径下创建一个文件,如果有则会覆盖 2、调用输入流对象的write方法,将数据写入文件中 3、清空缓存或关闭流 */ import java.io.*; class FileWriterDemo { public static void main(String[] args) { /*FileWriter是IO流中的字符输出流,是将读取到的数据写入指定文件中*/ //步骤1 FileWriter fw = null; try{ fw = new FileWriter("d:\\file_Copy.txt"); //会抛出IOException异常 //步骤2 fw.write("abcdefg"); } catch(IOException e){ System.out.println(e.toString()); } finally{ //流是必须要关闭的,所以放在finally语句块中 try { if(fw!=null) fw.close(); //会抛出IOException异常 } catch (IOException e) { System.out.println(e.toString()); } } } } </span>
三、什么情况下使用字符流
1、字符流也可以拷贝文本文件,但不推荐使用,因为读取时会把字节转换成字符,写入时会把字符转回字节
2、程序需要读取一段文本,或者需要写出一段文件的时候可以使用字符流
四、字符流是否可以拷贝非纯文本的文件
1、不可以拷贝非纯文本的文件
2、因为在读的时候会将字节转换为字符,在转换过程中,可能找不到对应的字符,就会用?代替,写出的时候会将字符转换成字节写出去
3、如果是?,直接写出,这样写出之后的文件就乱了,看不了了
五、自定义的数组拷贝
<span style="font-size:14px;">/*自定义数组拷贝 提高效率 思路:当使用FileReader的read方法时,一次只读取一个字符调用一次输出一次, 比较麻烦,所以我们能不能把读取的数据存入数组中,等达到一定数量后,在输出呢? 步骤:1、定义输入输出流 2、定义数组,用于保存读取到的数据 3、将数组中的数据写入到指定文件中 */ import java.io.*; class FileReaderFileWriterDemo { public static void main(String[] args) { //定义输入流 FileReader fr = null; //定义输出流 FileWriter fw = null; try { fr = new FileReader("d:\\file.txt"); fw = new FileWriter("d:\\file_copy.txt"); //定义数组存放字符 char [] ch = new char[10]; //这里一般把数组的长度定义成1024也就是2kB int index = 0; //把读到的字符存入数组 while((index = fr.read(ch))!=-1){ //从字符中读取数据写入文件中 fw.write(ch,0,index); fw.flush(); } } catch (IOException e) { System.out.println(e.toString()); } finally{ //分别关闭输入输出流 try { if(fr!=null) fr.close(); } catch (IOException e) { System.out.println(e.toString()); } finally{ try { if(fw!=null) fw.close(); } catch (IOException e) { System.out.println(e.toString()); } } } } } </span>
六、带缓冲的字符流
1、BufferedReader的read()方法读取字符时会一次读取若干字符到缓冲区, 然后逐个返回给程序, 降低读取文件的次数, 提高效率
2、BufferedWriter的write()方法写出字符时会先写到缓冲区, 缓冲区写满时才会写到文件, 降低写文件的次数, 提高效率
注:1、BufferedReader中有一个特有的方法 readerLine() 用于读取一行文本,还有就是用readerLine读取的数据不会自动换行,那是因为readerLine方法返回的时候只返回回车符之前的数据内容,并不返回回车符
2、 BufferedWriter中也有一个特有的方法 newLine() 是一个跨平台的换行符
<span style="font-size:14px;">/*带缓冲的字符流 */ import java.io.*; class BufferedReaderBufferedWriterDemo { public static void main(String[] args) { BufferedReader br = null; BufferedWriter bw = null; try { br = new BufferedReader(new FileReader("d:\\file.txt")); bw = new BufferedWriter(new FileWriter("d:\\file_copy.txt")); /*缓冲区的作用是为了提高流的操作效率而出现的,所以在创建缓冲区 之前,必须要先有流对象,所以要把流对象作为实际参数传递给缓冲区 的构造函数*/ String str = ""; while((str=br.readLine())!=null){ bw.write(str); bw.newLine(); bw.flush(); } } catch (IOException e) { System.out.println(e.toString()); } finally{ //分别关闭输入输出流 try { if(br!=null) br.close(); } catch (IOException e) { System.out.println(e.toString()); } finally{ try { if(bw!=null) bw.close(); } catch (IOException e) { System.out.println(e.toString()); } } } } } </span>
七、写一个自己的缓冲区
<span style="font-size:14px;">/* 明白了BufferedReader类中特有方法readLine的原理后, 可以自定义一个类中包含一个功能和readLine一致的方法。 来模拟一下BufferedReader */ import java.io.*; class MyBufferedReader { private FileReader fr; MyBufferedReader(FileReader fr){ this.fr = fr; } public String readLine()throws IOException{ int charNum = 0; String str = ""; while((charNum = fr.read())!=-1){ if(charNum=='\r') continue; if(charNum=='\n') return str; else str+=(char)charNum; } if(str.length()!=0) return str; return null; } public void close()throws IOException{ fr.close(); } } class MyBufferedReaderMyBufferedWriterDemo { public static void main(String[] args) { MyBufferedReader br = null; BufferedWriter bw = null; try { br = new MyBufferedReader(new FileReader("d:\\file.txt")); bw = new BufferedWriter(new FileWriter("d:\\file_copy.txt")); String str = ""; while((str=br.readLine())!=null){ bw.write(str); bw.newLine(); bw.flush(); } } catch (IOException e) { System.out.println(e.toString()); } finally{ //分别关闭输入输出流 try { if(br!=null) br.close(); } catch (IOException e) { System.out.println(e.toString()); } finally{ try { if(bw!=null) bw.close(); } catch (IOException e) { System.out.println(e.toString()); } } } } } </span>
八、装饰类
1、定义:装饰类其实和继承的复写差不多,但是有一点是装饰并不是继承这个类复写该类方法,而是继承这个类的父类,把需要增强功能的类作为实际参数传递给装饰类的构造函数
如:
使用继承完成的功能增强后体系表现
MyReader//专门用于读取数据的类。
|--MyTextReader(需要增强的类)
|--MyBufferTextReader(子类继承父类,增强功能)
|--MyMediaReader(需要增强的类)
|--MyBufferMediaReader(子类继承父类,增强功能)
|--MyDataReader(需要增强的类)
|--MyBufferDataReader(子类继承父类,增强功能)发现:当超类下面有多个子类时,而子类复写父类方法后共性方法还需要增强时,可以在定义一个装饰类继承超类,把子类传入,挺高功能增强,通过多态的形式。可以提高扩展性。
如:
MyReader//专门用于读取数据的类。
|--MyTextReader(需要增强的类)
|--MyMediaReader(需要增强的类)
|--MyDataReader(需要增强的类)
|--MyBufferReader(用于增强子类)
以前是通过继承将每一个子类都具备缓冲功能。
那么继承体系会复杂,并不利于扩展。
现在优化思想。单独描述一下缓冲内容。
将需要被缓冲的对象。传递进来。也就是,谁需要被缓冲,谁就作为参数传递给缓冲区。
这样继承体系就变得很简单。优化了体系结构。
装饰模式比继承要灵活。避免了继承体系臃肿。
而且降低了类于类之间的关系。
装饰类因为增强已有对象,具备的功能和已有的是相同的,只不过提供了更强功能。
所以装饰类和被装饰类通常是都属于一个体系中的。写一个自己的装饰类
<span style="font-size:14px;">//基础Reader类 同时必须复写抽象方法close和read(char[] cbuf, int off, int len)方法 class MyBufferedReader extends Reader { private Reader r; MyBufferedReader(Reader r) { this.r = r; } //可以一次读一行数据的方法。 public String myReadLine()throws IOException { //定义一个临时容器。原BufferReader封装的是字符数组。 //为了演示方便。定义一个StringBuilder容器。因为最终还是要将数据变成字符串。 StringBuilder sb = new StringBuilder(); int ch = 0; while((ch=r.read())!=-1) { if(ch=='\r') continue; if(ch=='\n') return sb.toString(); else sb.append((char)ch); } if(sb.length()!=0) return sb.toString(); return null; } /* 覆盖Reader类中的抽象方法。 */ public int read(char[] cbuf, int off, int len) throws IOException { return r.read(cbuf,off,len) ; } public void close()throws IOException { r.close(); } }</span>
九、.LineNumberReader
特有方法:获取行号 getLineNumber()
设置行号 setLineNumber()
运行结果:<span style="font-size:14px;">import java.io.*; class LineNumberReaderDemo { public static void main(String[] args)throws IOException { FileReader fr = new FileReader("PersonDemo.java"); LineNumberReader lnr = new LineNumberReader(fr); String line = null; //设置行号 //lnr.setLineNumber(100); while((line=lnr.readLine())!=null) { //给拷贝的文件添加行号 System.out.println(lnr.getLineNumber()+":"+line); } lnr.close(); } } </span>
十、写一个自己的LineNumberReader
<span style="font-size:14px;">import java.io.*; //继承BufferedReader是为了方便调用BufferedReader的readline方法 class MyLineNumberReader extends BufferedReader { private int lineNumber; MyLineNumberReader(Reader r) { super(r); } //没从硬盘上读取一行数据就让lineNumber+1 public String myReadLine()throws IOException { lineNumber++; return super.readLine(); } public void setLineNumber(int lineNumber) { this.lineNumber = lineNumber; } public int getLineNumber() { return lineNumber; } } class MyLineNumberReaderDemo { public static void main(String[] args) throws IOException { FileReader fr = new FileReader("copyTextByBuf.java"); MyLineNumberReader mylnr = new MyLineNumberReader(fr); String line = null; mylnr.setLineNumber(100); while((line=mylnr.myReadLine())!=null) { System.out.println(mylnr.getLineNumber()+"::"+line); } mylnr.myClose(); } } </span>
字节流
一、概述
1、字节流和字符流的基本操作是相同的,但字节流还可以操作其他媒体文件。
2、由于媒体文件数据中都是以字节存储的,所以,字节流对象可直接对媒体文件的数据写入到文件中,而可以不用再进行刷流动作。
3、读写字节流:InputStream 输入流(读)
OutputStream 输出流(写)
4、常用方法:
InputStream:
1、read() 读取一个字节,返回
0到255范围内的int字节值2、read(byte[] b) 从输入流中读取一定数量的字节,并将其存储在缓冲区数组
b中3、available() 可以获取读取文件中的所有字节数
4、close() 关闭流,释放资源
OutputStream:
1、write(int b) 写出一个字节
2、write(byte[]) 写出数组中的所有字节
3、writer(byte[],int strat,int end) 从指定位置写出数组中的字节
4、close() 关闭流,释放资源
5、读取文件
1、创建FileINputStream对象,指定一个文件,该文件必须存在,不存在则会抛出FileNotFoundException异常6、写出文件
2、使用read方法从文件中读取一个字节,如果读取到文件末尾则会返回-1
3、读取完毕后,使用close方法释放资源
为什么read方法返回的是int而不是byte类型呢?<span style="font-size:14px;">import java.io.*; class FileInputStreamDemo { public static void main(String[] args) { //创建流对象,关联file.txt文件 FileInputStream fis = new FileInputStream("D:\\file.txt"); int by = 0; //循环读取 while((by=fis.read())!=-1){ System.out.println(char(by)); fis.close(); } } </span>
原因:因为如果读取的是视频文件或音频文件或图片文件等,在读取过程中很有可能会遇到11111111,也就是byte类型的-1,那么遇到-1程序就会停止读取,会漏掉文件,为了防止这种情况出现,把byte类型提升为int类型,在这个字节前补上24个0,把遇到的-1变成255,这样可以保证将整个文件读完
1、创建FileOutputStream对象,指定一个文件,如果文件不存在,会自动创建该文件,如果需要在该文件后面追加,则需要在创建FileOutputStream对象时传入true7、拷贝文件
2、使用write方法进行写入
3、使用close方法关闭资源
<span style="font-size:14px;">import java.io.*; class FileOutputStreamDemo { public static void main(String[] args) { //创建输出流对象,与文件关联 FileOutputStream fos = new FileOutputStream("D:\\file.txt"); fos.write(100); //写出数据的时候会将前面24个0去掉,写出的是一个字节 fos.write(65); fos.write(97); fos.write("AABBBCCCDDDEEEFFFF".getBytes()); //写出字符串时,需要转换 fos.close(); //关闭 } }</span>
1、琢个字节拷贝
2、自定义数组拷贝<span style="font-size:14px;">import java.io.*; class CopyMp3 { public static void main(String[] args) { long start = System.currentTimeMillis(); FileInputStream fis =null; FileOutputStream fos =null; try { fis = new FileInputStream("D:\\KuGou\\陈奕迅 - 斗战神.mp3"); fos = new FileOutputStream("D:\\KuGou\\陈奕迅 - 斗战神_copy.mp3"); int by = 0; while((by=fis.read())!=-1){ fos.write(by); } } catch (IOException e) { throw new RuntimeException("文件复制失败"); } finally{ try { if(fis!=null) fis.close(); } catch (IOException e) { throw new RuntimeException("文件读取流关闭失败"); } finally{ try { if(fos!=null) fos.close(); } catch (IOException e) { throw new RuntimeException("文件输出流关闭失败"); } } } long end = System.currentTimeMillis(); System.out.println("拷贝使用的毫秒数:"+(start-end)); } } </span>
<span style="font-size:14px;">import java.io.*; class BytesCopyMp3 { public static void main(String[] args) { long start = System.currentTimeMillis(); FileInputStream fis =null; FileOutputStream fos =null; try { fis = new FileInputStream("D:\\KuGou\\陈奕迅 - 斗战神.mp3"); fos = new FileOutputStream("D:\\KuGou\\陈奕迅 - 斗战神_copy.mp3"); byte [] bys = new byte[1024]; int by = 0; while((by=fis.read(bys))!=-1){ fos.write(bys,0,by); } } catch (IOException e) { throw new RuntimeException("文件复制失败"); } finally{ try { if(fis!=null) fis.close(); } catch (IOException e) { throw new RuntimeException("文件读取流关闭失败"); } finally{ try { if(fos!=null) fos.close(); } catch (IOException e) { throw new RuntimeException("文件输出流关闭失败"); } } } long end = System.currentTimeMillis(); System.out.println("拷贝使用的毫秒数:"+(end-start)); } } </span>
3、定义一个和文件字符数相同的数组拷贝import java.io.*;
4、带缓冲流拷贝<span style="font-size:14px;">import java.io.*; class CopyMp3_available { public static void main(String[] args) { long start = System.currentTimeMillis(); FileInputStream fis =null; FileOutputStream fos =null; try { fis = new FileInputStream("D:\\KuGou\\陈奕迅 - 斗战神.mp3"); fos = new FileOutputStream("D:\\KuGou\\陈奕迅 - 斗战神_copy.mp3"); //因为数组的长度和文件字节长度是一样的,所以不需要使用循环了 byte [] bys = new byte[fis.available()]; fis.read(bys); fos.write(bys); } catch (IOException e) { throw new RuntimeException("文件复制失败"); } finally{ try { if(fis!=null) fis.close(); } catch (IOException e) { throw new RuntimeException("文件读取流关闭失败"); } finally{ try { if(fos!=null) fos.close(); } catch (IOException e) { throw new RuntimeException("文件输出流关闭失败"); } } } long end = System.currentTimeMillis(); System.out.println("拷贝使用的毫秒数:"+(end-start)); } } </span>
<span style="font-size:14px;">import java.io.*; class CopyMp3Buffered { public static void main(String[] args) { long start = System.currentTimeMillis(); FileInputStream fis =null; FileOutputStream fos =null; try { fis = new FileInputStream("D:\\KuGou\\陈奕迅 - 斗战神.mp3"); fos = new FileOutputStream("D:\\KuGou\\陈奕迅 - 斗战神_copy.mp3"); //创建缓冲区 BufferedInputStream bis = new BufferedInputStream(fis); BufferedOutputStream bos = new BufferedOutputStream(fos); int by = 0; while((by=bis.read())!=-1){ bos.write(by); } } catch (IOException e) { throw new RuntimeException("文件复制失败"); } finally{ try { if(fis!=null) fis.close(); } catch (IOException e) { throw new RuntimeException("文件读取流关闭失败"); } finally{ try { if(fos!=null) fos.close(); } catch (IOException e) { throw new RuntimeException("文件输出流关闭失败"); } } } long end = System.currentTimeMillis(); System.out.println("拷贝使用的毫秒数:"+(end-start)); } } </span>
8、自定义字节流缓冲区
BufferedInputStream的特点:
BufferedInputStream内置了一个缓冲区(数组),从BufferedInputStream中读取一个字节时,BufferedInputStream会一次性从文件中读取8192个,存在缓冲区中,返回给程序一个,当程序再次读取时,就不用找文件了,直接从缓冲区读取,知道缓冲区中的所有的都被使用过,才重新从文件中读取8192个
BufferedOutputStream的特点:
BufferedOutputStream也内置了一个缓冲区(数组),当程序向流中写出字节时,不会直接写到文件,先写到缓冲区,知道缓冲区写满,BufferedOutputStream才会把缓冲区中的数据一次性写到文件中
自定义BufferedOutputStream
/*自定义BufferedInputStream缓冲区 原理:BufferedInputStream内置了一个缓冲区(数组),从BufferedInputStream中 读取一个字节时,BufferedInputStream会一次性从文件中读取8192个,存在缓冲区 中,返回给程序一个,当程序再次读取时,就不用找文件了,直接从缓冲区读取, 直到缓冲区中的所有的都被使用过,才重新从文件中读取8192个 思路:1、既然是缓冲区,肯定要有底层读取流,所以要有FileInputStream 2、缓冲区肯定要有缓存的地方,即数组 类型byte 3、由于已经读取了一批数据并存入了数组中,所以需要把数组中的数据读取完毕后, 才能从文件中再次读取一批文件存入数组,所以要先把数组中的数据写出去 4、要想把数组中的数据清空,只能是当调用缓冲区的read方法时,利用指针来获取 */ import java.io.*; class MyBufferedInputStream { private byte [] bys = new byte[1024*4]; //缓存区 private int count = 0; //用于记录数组中数据的长度 private int pos=0; //指针 private FileInputStream fis; public MyBufferedInputStream(FileInputStream fis){ this.fis = fis; } public int myRead()throws IOException{ if(count==0) //如果count为0,从文件中读取一批数据存入数组中 { count = fis.read(bys); //给count赋值 if((count==-1)) //如果成立表示没有文件中没有数据,直接返回-1 return -1; pos = 0; //数组中的元素被读完,把pos初始化 byte b = bys[pos]; //用b记录bys数组中的pos位置上的元素 count--; //每调用一次myRead方法,条件成立,让count-1,当count等于0时,再从文件中读取一批数据 pos++; //每调用一次myRead方法,条件成立,让pos+1,知道把bys数组中的元素读完 return b&255; //因为read方法返回的是int类型,而b是byte类型,类型提升 //由原先的8位变成了32位,而b的二进制有可能是11111111的情况 //也就是byte类型的-1,那么遇到-1程序就会停止读取,会漏掉文件, //为了防止这种情况出现,把byte类型提升为int类型,需要在这个字节 //前补上24个0,把遇到的-1变成255,所以需要&255 } else if(count>0) { byte b = bys[pos]; count--; pos++; return b&0xff; } return -1; } public void myClose()throws IOException{ fis.close(); } } class MyBufferedInputStreamDemo { public static void main(String[] args) { long start = System.currentTimeMillis(); MyBufferedInputStream bis =null; BufferedOutputStream bos =null; try { //创建缓冲区 bis = new MyBufferedInputStream(new FileInputStream("D:\\KuGou\\陈奕迅 - 斗战神.mp3")); bos = new BufferedOutputStream(new FileOutputStream("D:\\KuGou\\陈奕迅 - 斗战神_copy.mp3")); int by = 0; while((by=bis.myRead())!=-1){ bos.write(by); } } catch (IOException e) { throw new RuntimeException("文件复制失败"); } finally{ try { if(bis!=null) bis.myClose(); } catch (IOException e) { throw new RuntimeException("文件读取流关闭失败"); } finally{ try { if(bos!=null) bos.close(); } catch (IOException e) { throw new RuntimeException("文件输出流关闭失败"); } } } long end = System.currentTimeMillis(); System.out.println("拷贝使用的毫秒数:"+(end-start)); } }
流操作规律
一、键盘录入
1、标准输入输出流
System.in:对应的标准输入设备,键盘。
Ssytem.out:对应的是标准的输出设备,控制台。
System.in的返回值类型是InputStream.
System.out的返回值类型是PrintStream,它是OutputStream的子类FilterOutputStream的子类。
2、整行录入
当使用输入流进行键盘录入时,只能一个字节一个字节进行录入。为了提高效率,可以自定义一个数组将一行字节进行存储。当一行录入完毕,再将一行数据进行显示。这种正行录入的方式,和字符流读一行数据的原理是一样的。也就是readLine方法。
那么能不能直接使用readLine方法来完成键盘录入的一行数据的读取呢?readLine方法是字符流BufferedReader类中方法。而键盘录入的read方法是字节流InputStream的方法。
那么能不能将字节流转成字符流再使用字符流缓冲区的readLine方法呢?这就需要用到转换流了。
3、转换流
3.1 转换流的由来:
a、字符流与字节流之间的桥梁
b、方便了字符流与字节流之间的操作
转换流的应用:
字节流中的数据都是字符时,转成字符流操作更高效。
3.2 InputStreamReader将字节流通向字符流
a、获取键盘录入对象。
InputStream in=System.in;
b、将字节流对象转成字符流对象,使用转换流。
InputStreamReade risr = new InputStreamReader(in);
c、为了提高效率,将字符串进行缓冲区技术高效操作。使用BufferedReader
BufferedReaderbr=new BufferedReader(isr);
//键盘录入最常见写法
BufferedReaderin=new BufferedReader(new InputStreamReader(System.in));
3.3 OutputStreamWriter字符流通向字节流
字符通向字节:录入的是字符,存到硬盘上的是字节。步骤和InputStreamReader转换流一样。
示例:
/*
需求:将键盘录入的数据,显示在控制台,当输入over时,表示结束
源:键盘录入。
目的:控制台。
*/
import java.io.*;
class TransStreamDemo
{
public static void main(String[] args)throws IOException
{
//获取键盘录入对象。
//InputStream in=System.in;
//将字节流对象转成字符流对象,使用转换流。
//InputStreamReader isr=new InputStreamReader(in);
//为了提高效率,将字符串进行缓冲区技术高效操作。使用BufferedReader
//BufferedReader br=new BufferedReader(isr);
//键盘录入最常见写法
BufferedReader in=new BufferedReader(new InputStreamReader(System.in));
//字符流通向字节流
BufferedWriter bw =new BufferedWriter(new OutputStreamWriter(System.out));
String s=null;
while((s=in.readLine())!=null)
{
if("over".equals(s))
break;
bw.write(s.toUpperCase());//写入数据
bw.newLine();//换行
bw.flush();//刷新
}
bw.close();//关闭流资源
in.close();
}
}
二、流操作规律
1、
源:键盘录入。
目的:控制台。
2、需求:想把键盘录入的数据存储到一个文件中。
源:键盘
目的:文件。
使用字节流通向字符流的转换流(桥梁):InputStreamReader
3、需求:想要将一个文件的数据打印在控制台上。
源:文件
目的:控制台
使用字符流通向字节流的转换流(桥梁):OutputStreamWriter
4、流操作的基本规律:
最痛苦的就是流对象有很多,不知道该用哪一个。
通过三个明确来完成:
4.1 明确源和目的。
源:输入流。InputStream Reader
目的:输出流。OutputStream Writer
4.2 操作的数据是否是纯文本。
是:字符流
否:字节流
4.3 当体系明确后,再明确要使用哪个具体的对象。通过设备来进行区分:
源设备:内存,硬盘,键盘
目的设备:内存,硬盘,控制台
5、规律体现
5.1 将一个文本文件中数据存储到另一个文件中。复制文件。
1)源:因为是源,所以使用读取流:InputStream和Reader
明确体系:是否操作文本:是,Reader
明确设备:明确要使用该体系中的哪个对象:硬盘上的一个文件。Reader体系中可以操作文件的对象是FileReader
是否需要提高效率:是,加入Reader体系中缓冲区 BufferedReader.
FileReader fr = new FileReader("a.txt");
BufferedReader bufr = new BufferedReader(fr);
2)目的:输出流:OutputStream和Writer
明确体系:是否操作文本:是,Writer
明确设备:明确要使用该体系中的哪个对象:硬盘上的一个文件。Writer体系中可以操作文件的对象FileWriter。
是否需要提高效率:是,加入Writer体系中缓冲区 BufferedWriter
FileWriter fw = new FileWriter("b.txt");
BufferedWriter bufw = new BufferedWriter(fw);
练习:将一个图片文件中数据存储到另一个文件中。复制文件。要按照以上格式自己完成三个明确。
1)源:输入流,InputStream和Reader
是否是文本?否,InputStream
源设备:硬盘上的一个文件。InputSteam体系中可以操作文件的对象是FileInputSteam
是否需要提供效率:是,BufferedInputStream
BufferedInputSteambis=newBufferedInputStream(newFileInputStream("c:/users/asus/desktop/1.jpg"));
2)目的:输出流,OutputStream和Writer
是否是文本?否,OutputStream
源设备:硬盘上的文件,FileOutputStream
是否需要提高效率:是,加入BufferedOutputStream
BufferedOutputStreambos=newBufferedOutputStream(newFileOutputStream("c:/users/asus/desktop/2.jpg"));
5.2 需求:将键盘录入的数据保存到一个文件中。
1)源:InputStream和Reader
是不是纯文本?是,Reader
设备:键盘。对应的对象是System.in。——为了操作键盘的文本数据方便。转成字符流按照字符串操作是最方便的。所以既然明确了Reader,那么就将System.in转换成Reader。用Reader体系中转换流,InputStreamReader
InputStreamReaderisr = new InputStreamReader(System.in);
需要提高效率吗?需要,BufferedReader
BufferedReaderbufr = new BufferedReader(isr);
2)目的:OutputStream Writer
是否是存文本?是!Writer。
设备:硬盘。一个文件。使用 FileWriter。
FileWriter fw = new FileWriter("c.txt");
需要提高效率吗?需要。
BufferedWriter bufw = new BufferedWriter(fw);
5.3 扩展:想要把录入的数据按照指定的编码表(UTF-8)(默认编码表是GBK),将数据存到文件中。
目的:OutputStream Writer
是否是存文本?是!Writer。
设备:硬盘上的一个文件。使用 FileWriter。——但是FileWriter是使用的默认编码表:GBK。而存储时,需要加入指定编码表utf-8。而指定的编码表只有转换流可以指定。所以要使用的对象是OutputStreamWriter。
该转换流对象要接收一个字节输出流,而且还可以操作的文件的字节输出流:FileOutputStream
OutputStreamWriter osw =new OutputStreamWriter(newFileOutputStream("d.txt"),"UTF-8");
需要高效吗?需要,BufferedWriter
BufferedWriter bufw = new BufferedWriter(osw);
记住:
转换流什么使用?
字符和字节之间的桥梁。通常,涉及到字符编码转换时,需要用到转换流。
练习:将一个文本数据打印在控制台上。要按照以上格式自己完成三个明确。
1)源:InputStreamReader
是文本?是:Reader
设备:硬盘。上的文件:FileReader
是否需要提高效率?是:BufferedReader
BufferedReader br=new BufferedReader(newFileReader("1.txt"));
2)目的:OutputStream Writer
是文本?是:Writer
设备:控制台。对应对象System.out。由于System.out对应的是字节流,所以利用OutputSteamWriter转换流
是否提高效率?是:BufferedWriter
BufferedWriter bw =new BufferedWriter(newOutputStreamWriter(system.out));
示例:
/*
2、需求:想把键盘录入的数据存储到一个文件中。
源:键盘
目的:文件
把录入的数据按照指定的编码表(UTF-8),将数据存到文件中。
3、需求:想要将一个文件的数据打印在控制台上。
源:文件
目的:控制台
*/
import java.io.*;
class TransStreamDemo2
{
public static void main(String[] args)throws IOException
{
//键盘录入
BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
//存入文件中,按照指定的编码表(UTF-8)
BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(new FileOutputStream("readin1.txt"),"UTF-8"));
String line=null;
while((line=br.readLine())!=null)
{
if("over".equals(line))
break;
bw.write(line);
bw.newLine();
bw.flush();
}
/*
//录入文件数据
BufferedReader br=new BufferedReader(new InputStreamReader(new FileInputStream("TransStreamDemo2.java")));
//显示在控制台
BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(System.out));
String line=null;
while((line=br.readLine())!=null)
{
if("over".equals(line))
break;
bw.write(line);
bw.newLine();
bw.flush();
}
*/
}
} 小知识:
1、异常的日志信息:
当程序在执行的时候,出现的问题是不希望直接打印给用户看的,是需要作为文件存储起来,方便程序员查看,并及时调整的。
示例:
import java.io.*;
import java.text.*;
import java.util.*;
class ExceptionInfo
{
public static void main(String[] args)
{
try
{
int[] arr =new int[2];
System.out.println(arr[3]);
}
catch (Exception e)
{
try
{
Date d=new Date();//创建时间对象
//时间模块格式对象
SimpleDateFormat sdf=new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
String s=sdf.format(d);
PrintStream ps=new PrintStream("info.log");//打印流对象
System.setOut(ps);//修改输出流设备
ps.println(s);//输出时间
}
catch (IOException ex)
{
throw new RuntimeException("文件创建失败");
}
e.printStackTrace(System.out);//将异常信息输出指定输出流
}
}
} 2、系统属性信息存入文本
获取系统信息:
Properties getProperties()
将信息输出到指定输出流中
void list(PrintStream out)
将输出流中数据存入指定文件中
new PrintStream("systeminfo.txt")
示例:
//将系统属性信息保存到指定文本中
import java.util.*;
import java.io.*;
class SystemInfo
{
public static void main(String[] args)
{
PrintStream ps = null;
try
{
//获取系统信息:
Properties pop = System.getProperties();
//创建输出流对象,将输出流中数据存入指定文件中
ps = new PrintStream("systeminfo.txt");
//将属性列表输出到指定的输出流
pop.list(ps);
}
catch (Exception e)
{
throw new RuntimeException("获取系统信息失败。");
}
}
} 3、通过System类的setIn,setOut方法可以对默认设备进行改变
System.setIn(new InputStream(“1.txt”));//将源改成文件1.txt。
System.setOut(new OutputStream(“2.txt”));//将目的改成文件2.txt
流的基本应用小结:
- 流是用来处理数据的。
- 处理数据时,一定要先明确数据源,与数据目的地(数据汇)。
- 数据源可以是文件,可以是键盘。
- 数据目的地可以是文件、显示器或者其他设备。
- 而流只是在帮助数据进行传输,并对传输的数据进行处理,比如过滤处理.转换处理等。
字符流继承体系简图
字节流继承体系简图
本文详细介绍了Java中的IO流,包括IO流的概述、分类、字节流与字符流的基类及其子类。重点讨论了字符流的使用场景,如FileReader和FileWriter,并对比了字符流与字节流在拷贝文件时的差异。还提到了缓冲区在字符流中的应用,如BufferedReader和BufferedWriter,并介绍了装饰模式在增强流功能中的应用。此外,还讲解了LineNumberReader的使用和自定义装饰类的实现。最后,探讨了流操作的规律和转换流在不同场景下的应用。
1970

被折叠的 条评论
为什么被折叠?



