一、IO流 —— BufferedWriter
1、字符流缓冲区:缓冲区的出现提高了对数据读写的效率。 2、对应类: BufferedWriter BufferedReader 3、缓冲区要结合流才可以使用;在流的基础上对流的功能进行了增强。 4、写入缓冲区: BufferedWriter -java.io.Writer -java.io.BufferedWriter 将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。 5、构造函数: BufferedWriter(Writer out) 在创建缓冲区之前,必须要先有字符写入流对象。 6、特有方法: 该缓冲区中提供了一个跨平台的换行符: newLine(); 该方法可以在任意平台下完成换行动作,而不需要我们根据平台的不同而使用不同的换行符。 代码示例:import java.io.*; class BufferedWriterDemo { public static void main(String[] args) throws IOException { //创建一个字符写入流对象 FileWriter fw = new FileWriter("buf.txt"); //为了提高字符写入流的效率,加入缓冲技术 //只要将需要被提高效率的流对象作为参数传递给缓冲区的构造函数即可。 BufferedWriter bufw = new BufferedWriter(fw); for (int x=1 ;x<6 ;x++ ) { bufw.write("abcde"+x); bufw.newLine(); bufw.flush();//只要用到缓冲区,就要刷新,刷新动作建议在写完一行刷新一次 } bufw.close();//关闭缓冲区,就是在关闭缓冲区中的流对象。 } }
二、 BufferedReader
1、读取缓冲区: BufferedReader
-java.io.Reader
-java.io.BufferedReader
2、构造函数:
BufferedReader(Reader in) 创建该缓冲区时,也需要先有字符读取流存在。
3、特有方法:
该缓冲区提供了一个一次读一行的方法: readLine(); 方便于对文本数据的获取。
读取时包含该行内容的字符串,但是不包含任何行终止符;如果已到达流末尾,则返回 null 。
代码示例:import java.io.*; class BufferedReaderDemo { public static void main(String[] args) throws IOException { //创建一个读取流对象和文件相关联 FileReader fr = new FileReader("buf.txt"); //为了提高效率,加入缓冲技术,将字符读取流对象作为参数传递给缓冲对象的构造函数。 BufferedReader bufr = new BufferedReader(fr); //String s = bufr.readLine(); //System.out.println("s: "+s); String line = null; while((line=bufr.readLine())!=null) { System.out.println(line); } bufr.close(); } }
三、通过缓冲区复制文本文件
需求:通过缓冲区复制一个文件。
思路:
1、分别创建一个读取流和一个写入流,读取流负责读取数据,写入流负责将数据写入文件;
2、加入缓冲技术;
3、读取一行数据就写入一行数据,并写入一个换行符;
4、关闭资源。
注意:
BufferedReader 中的readLine()方法返回时,只返回回车符之前的数据内容,并不返回回车符;
因此在将该读取到的数据写入到其他文件时,需要自己加上换行符。
代码:import java.io.*; class CopyTestByBuf { public static void main(String[] args) { BufferedReader bufr = null; BufferedWriter bufw = null; try { bufr = new BufferedReader(new FileReader("BufferedWriterDemo.java")); bufw = new BufferedWriter(new FileWriter("bufWriter_Copy.txt")); String line = null; while ((line=bufr.readLine())!=null) { bufw.write(line);//写入行内容 bufw.newLine();//换行 bufw.flush();//刷新 } } catch (IOException e) { throw new RuntimeException("读写失败"); } finally { if (bufr!=null) { try { bufr.close(); } catch (IOException e) { throw new RuntimeException("读取关闭失败"); } } if (bufw!=null) { try { bufw.close(); } catch (IOException e) { throw new RuntimeException("写入关闭失败"); } } } } }
四、readLine的原理图例
readLine()方法的原理:
无论是读一行,还是获取读取多个字符;其实最终都是在硬盘上一个一个读取。所以最终使用的还是read()方法一次读一个。五、MyBufferedReader
自定义一个类中包含一个功能和readLine一致的方法,来模拟一下 BufferedReader 。
思路:
1、因为字符读取流缓冲区会在创建时会接收到一个字符读取流的子类对象,所以可以定义定义一个Reader类型的成员变量,用于接收这个读取流;
2、有了读取流,就可以使用读取流中的read()方法,一次读取一个字符;
3、创建一个缓冲区,用于存储读取到的数据;
4、当读取到换行符时,将数据输出。
代码:import java.io.*; class MyBufferedReaderDemo { public static void main(String[] args) throws IOException { FileReader fr = new FileReader("buf.txt"); MyBufferedReader myBuf = new MyBufferedReader(fr); String line = null; while((line=myBuf.myReadLine())!=null) System.out.println(line); myBuf.myClose(); } } class MyBufferedReader { private Reader r ; MyBufferedReader(Reader r) { this.r = r; } public String myReadLine() 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(); else sb.append((char)ch); } //当最后一行结尾处没有回车符时,缓冲区中还有数据,但没有返回,所以需做如下判断,并返回。 if(sb.length()!=0) return sb.toString(); return null; } public void myClose() throws IOException { r.close(); } }
六、装饰设计模式
装饰设计模式:
当需要对已有的对象进行功能增强时,可以定义类,将已有对象传入,基于已有的功能,并提供加强功能。那么,自定义的该类就称为装饰类。
装饰类通常会通过构造方法接收被装饰的对象。并基于被装饰对象的功能,提供更强的功能。
BufferedReader 就是 FileReader 的装饰类。七、装饰和继承的区别
装饰模式比继承灵活,避免了继承体系的臃肿,而且降低了类与类之间的关系。
装饰类因为增强了已有对象,具备的功能和已有功能是相同的,只不过提供了更强的功能。所以装饰类和被装饰类通常都属于一个体系中。八、自定义装饰类
参阅第五节代码。九、 LineNumberReader
java.io.Reader
-BufferedReader
-LineNumberReader
1、构造方法:
LineNumberReader(Reader in)
使用默认输入缓冲区的大小创建新的行编号 reader。
2、特有方法:
该类定义了方法 setLineNumber(int) 和 getLineNumber(),它们可分别用于设置和获取当前行号。默认情况下,行编号从 0 开始。
代码:import java.io.*; class LineNumberReaderDemo { public static void main(String[] args) throws IOException { FileReader fr = new FileReader("buf.txt"); LineNumberReader lnr = new LineNumberReader(fr); String line = null; lnr.setLineNumber(100); while((line=lnr.readLine())!=null) System.out.println(lnr.getLineNumber()+"::"+line); lnr.close(); } }
十、 MyLineNumberReader
如同MyBufferedReader模拟BufferedReader一样,我们也可以定义一个类来模拟LineNumberReader
思路:
1、定义一个类型为Reader成员变量,用于接收创建MyLineNumberReader对象时传进来的Reader对象;
2、定义一个int类型的成员变量lineNumber,用于记录行号;
3、定义myReader()方法,并在每次读到一行时,行号自增一次;
4、定义getLineNumber和setLineNumber方法,用于获取和设置行号。
代码:import java.io.*; class MyLineNumberReaderDemo { public static void main(String[] args) throws IOException { FileReader fr = new FileReader("buf.txt"); MyLineNumberReader mylnr = new MyLineNumberReader(fr); String line = null; mylnr.setLineNumber(100); while((line=mylnr.myReadLine())!=null) { System.out.println(mylnr.getLineNumber()+"::"+line); } mylnr.myClose(); } } class MyLineNumberReader { private Reader r; private int lineNumber; MyLineNumberReader(Reader r) { this.r = r; } public String myReadLine() throws IOException { lineNumber++; 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; } public void setLineNumber(int lineNumber) { this.lineNumber = lineNumber; } public int getLineNumber() { return lineNumber; } public void myClose() throws IOException { r.close(); } }
优化后代码:class MyLineNumberReader extends MyBufferedReader { private int lineNumber; MyLineNumberReader(Reader r) { super(r); } public String myReadLine() throws IOException { lineNumber++; return super.myReadLine(); } public void setLineNumber(int lineNumber) { this.lineNumber = lineNumber; } public int getLineNumber() { return lineNumber; } }
十一、字节流 File 读写操作
字节流基类: InputStream OutputStream
java.io.InputStream
-FileInputStream
java.io.OutputStream
-FileOutputStream
FileOutputStream 写完不需要做刷新动作。
InputStream 中有一个特有方法:
available()该方法可以获取文件中字节的长度。
但是一般不用该方法获取到的长度来设置缓冲数组的长度,因为如果文件过大(比如4G、8G),那么在读取文件时会因为缓冲区过大而导致内存不足。
代码:import java.io.*; class FileStreamDemo { public static void main(String[] args) throws IOException { //writeFile(); //readFile_1(); readFile_2(); //readFile_3(); } public static void writeFile() throws IOException { FileOutputStream fos = new FileOutputStream("fos.txt"); fos.write("abcde".getBytes()); fos.close(); } public static void readFile_1() throws IOException { FileInputStream fis = new FileInputStream("fos.txt"); int ch = 0; while((ch=fis.read())!=-1) System.out.print((char)ch); fis.close(); } public static void readFile_2() throws IOException { FileInputStream fis = new FileInputStream("fos.txt"); byte[] buf = new byte[1024]; int len = 0; while((len=fis.read(buf))!=-1) { System.out.print(new String(buf,0,len)); } fis.close(); } public static void readFile_3() throws IOException { FileInputStream fis = new FileInputStream("fos.txt"); //int num = fis.available(); //System.out.println(num); byte[] buf = new byte[fis.available()]; fis.read(buf); System.out.println(new String(buf)); fis.close(); } }
十二、拷贝图片
需求:
复制一个图片
思路:
1、用字节读取流对象和图片关联。
2、用自己写入流对象创建一个图片文件,用于存储获取到的图片数据
3、通过循环读写,完成数据的存储。
4、关闭资源。
代码:import java.io.*; class CopyPic { public static void main(String[] args) { FileOutputStream fos = null; FileInputStream fis = null; try { fos = new FileOutputStream("d:\\2.jpg"); fis = new FileInputStream("d:\\1.jpg"); byte[] buf = new byte[1024]; int len = 0; while((len=fis.read(buf))!=-1) fos.write(buf,0,len); } catch (IOException e) { throw new RuntimeException("复制文件失败"); } finally { if(fis!=null) { try { fis.close(); } catch (IOException e) { throw new RuntimeException("读取关闭失败"); } } if(fos!=null) { try { fos.close(); } catch (IOException e) { throw new RuntimeException("写入关闭失败"); } } } } }
十三、字节流的缓冲区
java.io.InputStream
-FilterInputStream
-BufferedInputStream 在创建 BufferedInputStream 时,会创建一个内部缓冲区数组。
java.io.OutputStream
-FilterOutputStream
-BufferedOutputStream 该类实现缓冲的输出流。通过设置这种输出流,应用程序就可以将各个字节写入底层输出流中,而不必针对每次字节写入调用底层系统。
需求:
通过缓冲区复制 mp3
思路:
1、用字节读取流对象和MP3文件关联;并将该读取流对象传入缓冲对象中。
2、用自己写入流对象创建一个MP3文件,用于存储获取到的图片数据;并将该写入流对象传入缓冲对象中。
3、通过循环读写,完成数据的存储。
4、关闭资源。
代码:import java.io.*; class CopyMp3 { public static void main(String[] args) throws IOException { long start = System.currentTimeMillis(); copy_1(); long end = System.currentTimeMillis(); System.out.println((end-start)+"毫秒"); } //通过字节流的缓冲区完成复制 public static void copy_1() throws IOException { BufferedInputStream bufis = new BufferedInputStream(new FileInputStream("d:\\0.mp3")); BufferedOutputStream bufos = new BufferedOutputStream(new FileOutputStream("d:\\1.mp3")); //可以不用定义数组,因为缓冲区中已经有数组。 int by = 0; while((by=bufis.read())!=-1) bufos.write(by); bufos.close(); bufis.close(); } }
十四、自定义字节流缓冲区——read 和 write的特点
既然要自定义一个字节流缓冲区,那么我们就需要了解字节流缓冲区的基本原理:
BufferedInputStream :在创建 BufferedInputStream 时,会创建一个内部缓冲区数组。在读取时,会一次性从硬盘读取一堆数据来填充这个缓冲区(也就是数组)。
当BufferedInputStream调用read()方法时,是从缓冲区(数组)中读取数据,而不是从硬盘中读取。
原理图如下:
代码:import java.io.*; class MyCopyMp3 { public static void main(String[] args) throws IOException { long start = System.currentTimeMillis(); copy_1(); long end = System.currentTimeMillis(); System.out.println((end-start)+"毫秒"); } //通过字节流的缓冲区完成复制 public static void copy_1() throws IOException { MyBufferedInputStream bufis = new MyBufferedInputStream(new FileInputStream("d:\\0.mp3")); BufferedOutputStream bufos = new BufferedOutputStream(new FileOutputStream("d:\\1.mp3")); int by = 0; //System.out.println("第一个字节: "+bufis.myRead()); while((by=bufis.myRead())!=-1) bufos.write(by); //此处将int类型的by,强制转换为byte类型,以避免复制后的文件变为原文件的4倍。 //(read方法将byte提升为int类型,write方法将int类型强转成byte类型) bufos.close(); bufis.myClose(); } } class MyBufferedInputStream { private InputStream in; private byte[] buf = new byte[1024]; private int pos = 0, count = 0; MyBufferedInputStream(InputStream in) { this.in = in; } //一次读一个字节,从缓冲区(字节数组)获取。 public int myRead() throws IOException { if(count==0) { count = in.read(buf);//通过in对象读取硬盘上的数据,存储到buf中。并返回存入的字节数。 if(count<0) return -1; pos = 0; } byte b = buf[pos]; count--; pos++; return b&255; /* 因为b为byte类型,返回时需要返回的是int类型; 所以b在自动提升为int类型时会在8位二进制位的b前面补24个1; 那么将byte类型b,提升为int类型时,b的值将会改变。 所以,需要将byte类型的b和255进行 & ,得到的结果就相当于在b前24位补0,这样就不会改变b的值。 */ } public void myClose() throws IOException { in.close(); } }
十五、读取键盘录入
System 类中有一个字段:
static InputStream in
“标准”输入流。
System.in: 标准输入设备,默认是键盘。
System.in 返回的是一个 InputStream 对象。那么,我们就可以使用该对象的方法。
在windows系统中,回车符包含两个字符:\r 和 \n ;
需求:
通过键盘录入数据;
当录入一行数据后,就将该行数据进行打印;
如果录入over,则停止录入。
代码:import java.io.*; class ReadIn { public static void main(String[] args) throws IOException { InputStream in = System.in; StringBuilder sb = new StringBuilder(); while(true) { int ch = in.read(); if(ch=='\r') continue; else if(ch=='\n') { String s = sb.toString(); if("over".equals(s)) break; System.out.println(s.toUpperCase()); sb.delete(0,sb.length()); //sb = new StringBuilder(); } else sb.append((char)ch); } } }
十六、读取转换流
java.io.Reader
-InputStreamReader 字节流通向字符流的桥梁;它使用指定的charset读取字节并将其解码为字符。它使用的字符集可以由名称指定或显式给定,或者可以接受平台默认的字符集。
因为 InputStreamReader 是 Reader 的子类,那么它就可以使用 BufferedReader 进行包装,以提高效率。
构造方法:
InputStreamReader(InputStream in) 创建一个使用默认字符集的 InputStreamReader; 它接收的是一个字节流对象。
当然也可以在构造时指定编码表:
InputStreamReader(InputStream in, String charsetName) :创建使用指定字符集的 InputStreamReader。
那么,在读取键盘录入时,就可以通过 BufferedReader 的 readLine()方法一行一行的读取了。
代码:import java.io.*; class ReadIn2 { public static void main(String[] args) throws IOException { //获取键盘录入对象 InputStream in = System.in; //将字节流对象转成字符流对象,使用转换流: InputStreamReader InputStreamReader isr = new InputStreamReader(in); //为了提高效率,将字符流进行缓冲区技术的高效操作,使用 BufferedReader BufferedReader bufr = new BufferedReader(isr); String line = null; while (true) { line = bufr.readLine(); if("over".equals(line)) break; System.out.println(line); } bufr.close(); } }
十七、写入转换流
1、 FileReader 是 InputStreamReader 的子类,文件都是以字节的形式存在的,而通过转换流将这些字节读取出来时,经过转换流转换成字符。所以叫字节转字符。
FileReader 就是使用了默认字符编码的转换流,而 InputStreamReader 可以指定字符编码。
2、 FileWriter 是 OutputStreamWriter 的子类,文件都是以字节的形式存在的,而我们输入的文字、字母这些都是字符,对这些字符进行存储时,需先将其转换为字节。
所以叫字符转字节。 FileWriter 就是使用是默认字符编码的转换流,而 OutputStreamWriter 可以指定字符编码。
3、 System 类中还有一个字段:
static PrintStream out “标准”输出流。
System.out: 标准输出设备,默认是控制台。
System.out 返回的是一个 OutputStream 对象。那么,我们就可以使用该对象的方法。
4、 java.io.Writer
-OutputStreamWriter 字符流通向字节流的桥梁。可使用指定的 charset 将要写入流中的字符编码成字节。
构造方法:
OutputStreamWriter(OutputStream out) :创建使用默认字符编码的 OutputStreamWriter,也可以指定编码。它接收的是一个字节流。
5、 什么是字符通向字节?
我本身有一些我熟悉的文字,把他变成字节写出去,并写在文件当中;而文件是以字节的形式存储的。
然而录入的都是字符,存储的都是字节。所以在写的过程中需要将字符转成字节。
6、 键盘录入最常见写法:
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
代码:import java.io.*; class ReadIn3 { public static void main(String[] args) throws IOException { //获取键盘录入对象 InputStream in = System.in; //将字节流对象转成字符流对象,使用转换流: InputStreamReader InputStreamReader isr = new InputStreamReader(in); //为了提高效率,将字符流进行缓冲区技术的高效操作,使用 BufferedReader BufferedReader bufr = new BufferedReader(isr); //BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));//简写格式。 OutputStream out = System.out; OutputStreamWriter osw = new OutputStreamWriter(out); BufferedWriter bufw = new BufferedWriter(osw);//需换行操作,加入了缓冲技术。 //BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out));//简写格式。 String line = null; while (true) { line = bufr.readLine(); if("over".equals(line)) break; bufw.write(line.toUpperCase());//将输出语句变成字符流对象调用对应方法输出 bufw.newLine(); bufw.flush();//字符流写完需刷新 } bufr.close(); } }
十八、流操作规律-1
通过三个明确来确定要使用哪个流对象:
1、明确数据源和目的;
源:输入流。 InputStream Reader
目的:输出流。 OutputStream Writer
2、操作的数据是否是纯文本。
是:字符流;
不是:字节流。
3、当体系明确后,在明确要使用哪个具体对象。
通过设备来进行区分:
源设备包括:内存,硬盘,键盘;
目的设备:内存,硬盘,控制台。
练习一:
将一个文本文件中的数据存储到另一个文件中。
源: InputStream Reader
是否文本?是 Reader
明确对象--设备:硬盘文件: FileReader
FileReader fr = new FileReader("a.txt");
是否需要缓冲?
BufferedReader bufr = new BufferedReader(fr);
目的: OutputStream Writer
是否文本?是 Writer
明确对象--设备:硬盘文件: FileWriter
FileWriter fw = new FileWriter("b.txt");
BufferedWriter bufw = new BufferedWriter(fw);
练习二:
将一个图片文件中的数据存储到另一个文件中。
源: InputStream Reader
是否文本?否 InputStream
明确对象--设备:硬盘文件: FileInputStream
FileInputStream fis = new FileInputStream("1.jpg");
是否需要缓冲?
BufferedInputStream bufis = new BufferedInputStream(fis);
目的: OutputStream Writer
是否文本?否 OutputStream
明确对象--设备:硬盘文件: FileOutputStream
FileOutputStream fos = new FileOutputStream("2.jpg");
是否需要缓冲?
BufferedOutputStream bufos = new BufferedOutputStream(fos);
需求:把键盘录入的数据存储到一个文件中。
来源:键盘
目的地:文件代码:
import java.io.*; class TransStreamDemo2 { public static void main(String[] args) throws IOException { //键盘录入最常见写法。 BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in)); BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("out.txt"))); String line = null; while ((line=bufr.readLine())!=null) { if("over".equals(line)) break; bufw.write(line.toUpperCase()); bufw.newLine(); bufw.flush(); } bufr.close(); } }
需求:将一个文件的数据打印在控制台上。
来源:文件
目的地:控制台代码:import java.io.*; class TransStreamDemo21 { public static void main(String[] args) throws IOException { BufferedReader bufr = new BufferedReader(new InputStreamReader(new FileInputStream("out.txt"))); BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out)); String line = null; while ((line=bufr.readLine())!=null) { if("over".equals(line)) break; bufw.write(line.toUpperCase()); bufw.newLine(); bufw.flush(); } bufr.close(); } }
十九、流操作规律-2
练习一:
将键盘录入的数据保存到一个文件中。
源: InputStream Reader
是否文本?是 Reader (键盘录入属于文本)
明确对象--设备:键盘。 System.in
System.in 属于 InputStream 的具体对象,属于字节流。为了操作键盘的文本数据方便,转成字符流按照字符串操作是最方便的。
所以需要将 System.in 转成字符流 Reader; 需要用到 Reader 体系中的的转换流 InputStreamReader
InputStreamReader isr = new InputStreamReader(System.in);
加入缓冲技术:
BufferedReader bufr = new BufferedReader(isr);
目的: OutputStream Writer
是否文本?是 Writer
明确对象--设备:硬盘文件: FileWriter
FileWriter fw = new FileWriter("c.txt");
加入缓冲技术:
BufferedWriter bufw = new BufferedWriter(fw);
扩展:
将录入的数据按指定编码表(UTF-8),将数据存到文件中。
目的: OutputStream Writer
是否文本?是 Writer
明确对象--设备:硬盘文件: FileWriter (该对象使用的是默认编码表GBK)
但是存储时,需要加入指定编码表,而指定编码表只有转换流可以指定。
所以要使用的对象是 OutputStreamWriter
而该转换流对象要接收一个字节输出流,该字节输出流需可以操作文件: FileOutputStream
FileOutputStream fos = new FileOutputStream("d.txt");
字节流转换成字符流:
OutputStreamWriter osw = new OutputStreamWriter(fos,"UTF-8");
加入缓冲技术:
BufferedWriter bufr = new BufferedWriter(osw);
拓展示例代码:import java.io.*; class TransStreamDemo2 { public static void main(String[] args) throws IOException { BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in)); BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("d.txt","UTF-8"))); String line = null; while ((line=bufr.readLine())!=null) { if("over".equals(line)) break; bufw.write(line.toUpperCase()); bufw.newLine(); bufw.flush(); } bufr.close(); } }
注意:只有转换流可以指定编码表
OutputStreamWriter: 可以指定编码表
-FileWriter: 默认使用GBK
InputStreamReader: 可以指定编码表
-FileReader: 使用默认编码表
什么时候使用转换流?
字符和字节之间的桥梁;通常,涉及到字符编码转换时,需要用到转换流。
扩展知识:
InputStreamReader 的子类 FileReader 使用的是默认编码表GBK。因此 FileReader 只能读取用GBK编码表写入的文件。当需要读取指定编码表写入的文件时,需要用 InputStreamReader 指定编码表来读取文件。
课后练习:
//将一个文本数据打印到控制台上
分析:
源: InputStream Reader
是否文本?是 Reader
设备——硬盘文件: FileReader
加入缓冲:
BufferedReader bufr = new BufferedReader(new FileReader("buf.txt"));
目的: OutputStream Writer
是否文本?是 Writer
设备--控制台: System.out;
System.out 属于字节流 OutputStream ,
所以需转成字符流
OutputStreamWriter osw = new OutputStreamWriter(System.out);
代码:import java.io.*; class TransDemo { public static void main(String[] args) throws IOException { FileReader fr = new FileReader("buf.txt"); BufferedReader bufr = new BufferedReader(fr); OutputStream os = System.out; OutputStreamWriter osw = new OutputStreamWriter(os); BufferedWriter bufw = new BufferedWriter(osw); String line = null; while ((line=bufr.readLine())!=null) { bufw.write(line); bufw.newLine(); bufw.flush(); } bufr.close(); bufw.close(); } }
拓展练习:
需求:读取UTF-8编码表编写的文件d.txt,并打印在控制台上
分析:
源: InputStream Reader
文本: Reader
设备:硬盘文件: FileReader
FileReader 只能读取默认编码表GBK,需要读取指定编码表时,需使用到转换流: InputStreamReader ,同时在构造时传入指定编码表
InputStreamReader isr = new InputStreamReader(new FileInputStream("d.txt"),"UTF-8");
加入缓冲技术:
BufferedReader bufr = new BufferedReader(isr);
目的: OutputStream Writer
文本: Writer
设备:控制台: System.out;
System.out 属于字节流 OutputStream ,所以需要转换成字符流 OutputStreamWriter
OutputStreamWriter osw = new OutputStreamWriter(System.out);
加入缓冲技术:
BufferedWriter bufw = new BufferedWriter(osw);
代码:import java.io.*; class TransDemo2 { public static void main(String[] args) throws IOException { InputStreamReader isr = new InputStreamReader(new FileInputStream("d.txt"),"UTF-8"); BufferedReader bufr = new BufferedReader(isr); OutputStreamWriter osw = new OutputStreamWriter(System.out); BufferedWriter bufw = new BufferedWriter(osw); String line = null; while ((line=bufr.readLine())!=null) { bufw.write(line); bufw.newLine(); bufw.flush(); } bufr.close(); bufw.close(); } }
二十、改变标准输入输出设备
1、 System 类中有两个方法可以改变标准输入输出设备:
static void setIn(InputStream in) 重新分配“标准”输入流。接收一个 InputStream 类型的对象。
static void setOut(PrintStream out) 重新分配“标准”输出流。接收一个 PrintStream 类型的对象。
2、这两个方法都是静态的,可以直接用类名调用:
System.setIn(new FileInputStream("buf.txt"));
System.setOut(new PrintStream("out11.txt"));
3、 PrintStream 继承体系:
-java.lang.Object
-java.io.OutputStream
-java.io.FilterOutputStream
-java.io.PrintStream
从该继承体系可以看出: PrintStream 是 OutputStream 的子类,那么它就是一个字节写入流。
构造函数:
PrintStream(String fileName) :创建具有指定文件名称且不带自动行刷新的新打印流。
4、 System.out 返回的其实就是一个打印流: PrintStream
代码示例:import java.io.*; class TransStreamDemo { public static void main(String[] args) throws IOException { System.setIn(new FileInputStream("buf.txt")); System.setOut(new PrintStream("out11.txt")); BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in)); BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out)); String line = null; while ((line=bufr.readLine())!=null) { if("over".equals(line)) break; //System.out.println(line); bufw.write(line.toUpperCase()); bufw.newLine(); bufw.flush(); } bufr.close(); } }
二十一、异常的日志信息
需求:建立一个异常日志信息
思路:
Throwable 中有一个方法printStackTrace()可以输出打印异常信息,还有一个重载的方法printStackTrace(PrintStream s)可以指定异常信息的输出设备。
那么就可以创建一个PrintStream对象并指定一个文件,将这个对象作为参数传入该方法中就可以得到一个日志文件了。
或者先创建一个PrintStream对象并指定一个文件时;通过System.setOut改变标准的输出设备。
同时可以加上时间信息。
小知识:
网上有一个完整的日志信息工具:log4j,可导入直接使用。
代码示例:import java.io.*; import java.util.*; import java.text.*; class ExceptionInfo { public static void main(String[] args) { try { int[] arr = new int[2]; System.out.println(arr[4]); } catch (Exception e) { try { //日期格式化 Date d = new Date(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd E HH:mm:ss"); String s = sdf.format(d); //创建PrintStream对象 PrintStream ps = new PrintStream("exeception.log"); //改变输出设备 System.setOut(ps); //将时间信息打印输出 ps.println(s.toString());//ps.write(s.toString().getBytes()); } catch (IOException ex) { throw new RuntimeException("日志文件创建失败"); } //打印输出异常信息 e.printStackTrace(System.out); } } }
二十二、系统信息
System 类中有一个静态方法getProperties(),该方法返回一个Properties对象,该对象描述的是当前的系统信息;
Properties 有一个方法:
void list(PrintStream out) :将属性列表输出到指定的输出流。
那么就可以通过PrintStream对象将系统信息保存到一个文件中。
代码:import java.util.*; import java.io.*; class SystemInfo { public static void main(String[] args) throws IOException { Properties prop = System.getProperties(); prop.list(new PrintStream("sysinfo.txt"));//调用Properties类中的list(PrintStream ps)方法 //方式二: //System.setOut(new PrintStream("sysinfo.txt"));//改变输出路径 //prop.list(System.out); } }