[关闭]
@liayun 2016-06-19T16:29:20.000000Z 字数 17822 阅读 1393

Java IO(一)

java基础


IO(Input Output)流

IO流常用基类

注意:由这四个类派生出来的子类名称都是以其父类名作为子类名的后缀

字符流

先学习一下字符流的特点。

字符流——创建文件

既然IO流是用于操作数据的,那么数据的最常见体现形式是:文件,那么先以操作文件为主来演示。
例,需求:在硬盘上,创建一个文件并写入一些文字数据。
解:
分析:找到一个专门用于操作文件的Writer子类对象——FileWriter,后缀名是父类名,前缀名是该流对象的功能。

如果想在原有文件上继续加入新的数据呢?
创建一个FileWriter对象时,传递一个true参数,代表不覆盖已有的文件,即在已有文件的末尾处进行数据续写。如:

  1. import java.io.*;
  2. class FileWriterDemo {
  3. public static void main(String[] args) throws IOException {
  4. FileWriter fw = new FileWriter("demo.txt", true);
  5. fw.write("nihao\r\nxeixie");
  6. fw.close();
  7. }
  8. }

注意:\r\n在windows中表示行终止符

IO异常的处理方式

IO异常的处理方式:最后无论如何都应关闭资源,所以应放在finally代码块中。

  1. import java.io.*;
  2. class FileWriterDemo {
  3. public static void main(String[] args) {
  4. FileWriter fw = null;
  5. try {
  6. fw = new FileWriter("demo.txt");
  7. fw.write("abcdefg");
  8. } catch(IOException e) {
  9. System.out.println("catch:"+e.toString());
  10. } finally {
  11. try {
  12. if(fw!=null) // 写在外面也可
  13. fw.close();
  14. } catch(IOException e) {
  15. System.out.println(e.toString());
  16. }
  17. }
  18. }
  19. }

字符流——读取文件

例,需求:从硬盘的一个文件中读取内容。
解:
第一种方式读取:

第二种方式读取:通过字符数组进行读取。

练习1、读取一个.java文件,并打印在控制台上。
解:

  1. import java.io.*;
  2. class FileReaderTest {
  3. public static void main(String[] args) throws IOException {
  4. FileReader fr = new FileReader("DateDemo.java");
  5. char[] buf = new char[1024];
  6. int num = 0;
  7. while((num=fr.read(buf)) != -1) {
  8. System.out.print(new String(buf, 0, num));
  9. }
  10. fr.close();
  11. }
  12. }

练习2、将c盘一个文本文件复制到d盘中。
解:
分析:复制原理——其实就是将c盘下的文件数据存储到d盘的一个文件中。
步骤:

  1. 在d盘创建一个文件,用于存储c盘文件中的数据。
  2. 定义读取流和c盘文件关联。
  3. 通过不断的读写完成数据存储。
  4. 关闭资源。

第一种复制方式:从c盘读一个字符,就往d盘写一个字符。

  1. public static void copy_1() throws IOException {
  2. // 创建目的地
  3. FileWriter fw = new FileWriter("RuntimeDemo_copy.txt");
  4. // 与已有文件关联
  5. FileReader fr = new FileReader("RuntimeDemo.java");
  6. int ch = 0;
  7. while((ch=fr.read()) != -1) {
  8. fw.write(ch);
  9. }
  10. fw.close();
  11. fr.close();
  12. }

第二种复制方式:

  1. public static void copy_2() {
  2. FileReader fr = null;
  3. FileWriter fw = null;
  4. try {
  5. fw = new FileWriter("SystemDemo_copy.txt");
  6. fr = new FileReader("SystemDemo.java");
  7. char[] buf = new char[1024];
  8. int len = 0;
  9. while((len = fr.read(buf)) != -1) {
  10. fw.write(buf, 0, len);
  11. }
  12. } catch(IOException e) {
  13. throw new RuntimeException("读写失败");
  14. } finally {
  15. if(fr != null)
  16. try {
  17. fr.close();
  18. } catch(IOException e) {
  19. }
  20. if(fw != null)
  21. try {
  22. fw.close();
  23. } catch(IOException e) {
  24. }
  25. }
  26. }

原理:
复制文件

字符流的缓冲区

缓冲区的出现是为了提高流的操作效率而出现的,所以在创建缓冲区之前,必须要先有流对象。

BufferedWriter

该缓冲区中提供了一个跨平台的换行符:newLine()

BufferedReader

字符读取流缓冲区,该缓冲区提供了一个一次读一行的方法:readLine(),方便于对文本数据的获取,当返回null时,表示读取到文件末尾。
readLine()方法返回的时候只返回回车符之前的数据内容,并不返回回车符(行终止符)

  1. import java.io.*;
  2. class MyBufferedReader extends Reader {
  3. private Reader r;
  4. MyBufferedReader(Reader r) {
  5. this.r = r;
  6. }
  7. // 可以一次读一行数据的方法
  8. public String myReadLine() throws IOException {
  9. // 定义一个临时容器,原BufferReader封装的是一个字符数组
  10. // 为了演示方便,定义一个StringBuilder容器,因为最终还是要将数据变为字符串。
  11. StringBuilder sb = new StringBuilder();
  12. int ch = 0;
  13. while((ch=r.read()) != -1) {
  14. if(ch == '\r')
  15. continue;
  16. if(ch == '\n')
  17. return sb.toString();
  18. else
  19. sb.append((char)ch);
  20. }
  21. if(sb.length() != 0) // 如果文本数据最后的行终止符故意去掉,那么StringBuilder里面还是有数据的 ,也要给予返回
  22. return sb.toString();
  23. return null; // 如果已到达流末尾,则返回null
  24. }
  25. public void myClose() throws IOException {
  26. r.close();
  27. }
  28. /*
  29. 覆盖Reader类中的抽象方法。
  30. */
  31. public int read(char[] cbuf, int off, int len) throws IOException {
  32. return r.read(cbuf, off, len);
  33. }
  34. public void close() throws IOException {
  35. r.close();
  36. }
  37. }

测试自定义类MyBufferedReader

  1. class MyBufferedReaderDemo {
  2. public static void main(String[] args) throws IOException {
  3. FileReader fr = new FileReader("buf.txt");
  4. MyBufferedReader myBuf = new MyBufferedReader(fr);
  5. String line = null;
  6. while((line=myBuf.myReadLine()) != null) {
  7. System.out.println(line);
  8. }
  9. myBuf.myClose();
  10. }
  11. }
装饰设计模式

装饰设计模式:当想要对已有的对象进行功能增强时,可以定义类,将已有对象传入,基于已有的功能,并提供加强功能。那么自定义的该类称为装饰类。
装饰类通常会通过构造方法接收被装饰的对象。并基于被装饰的对象的功能,提供更强的功能。
举例来说明装饰设计模式:

  1. class Person {
  2. public void chiFan() {
  3. System.out.println("吃饭");
  4. }
  5. }
  6. class SuperPerson {
  7. private Person p;
  8. SuperPerson(Person p) {
  9. this.p = p;
  10. }
  11. public void superChiFan() {
  12. System.out.println("开胃酒");
  13. p.chiFan();
  14. System.out.println("甜点");
  15. System.out.println("来一根");
  16. }
  17. }
  18. class PersonDemo {
  19. public static void main(String[] args) {
  20. Person p = new Person();
  21. // p.chiFan();
  22. SuperPerson sp = new SuperPerson(p);
  23. sp.superChiFan();
  24. }
  25. }
装饰模式与继承之间的区别

假设有这样一个继承体系:

可发现这样的继承体系很臃肿,所以应该换一个继承体系。这样设计类呢?

  1. class MyBufferReader {
  2. MyBufferReader(MyTextReader text) {
  3. }
  4. MyBufferReader(MyMediaReader media) {
  5. }
  6. }

可发现该类扩展性很差,可以找到其参数的共同类型,通过多态的形式,可以提高扩展性。

  1. class MyBufferReader extends MyReader {
  2. private MyReader r;
  3. MyBufferReader(MyReader r) {
  4. }
  5. }

这样,最后的继承体系就是这个样子的:

结论:装饰模式比继承要灵活,避免了继承体系臃肿,而且降低了类与类之间的关系。装饰类因为增强已有对象,具备的功能和已有的是相同的,只不过提供了更强功能,所以装饰类和被装饰类通常都是属于一个体系中的。

例,通过缓冲区复制一个.java文件。
解:
分析:readLine()方法的原理:无论是读一行,或者读取多个字符,其实最终都是在硬盘上一个一个读取,所以最终使用的还是read()方法,一次读一个的方法。

  1. import java.io.*;
  2. class CopyTextByBuf {
  3. public static void main(String[] args) {
  4. BufferedReader bufr = null;
  5. BufferedWriter bufw = null;
  6. try {
  7. bufr = new BufferedReader(new FileReader("BufferedWriterDemo.java"));
  8. bufw = new BufferedWriter(new FileWriter("bufWriter_Copy.txt"));
  9. String line = null; // 中转站
  10. while((line = bufr.readLine()) != null) {
  11. bufw.write(line);
  12. bufw.newLine();
  13. bufw.flush();
  14. }
  15. } catch(IOException e) {
  16. throw new RuntimeException("读写失败");
  17. } finally {
  18. try {
  19. if(bufr != null)
  20. bufr.close();
  21. } catch(IOException e) {
  22. throw new RuntimeException("读取关闭失败");
  23. }
  24. try {
  25. if(bufw != null)
  26. bufw.close();
  27. } catch(IOException e) {
  28. throw new RuntimeException("写入关闭失败");
  29. }
  30. }
  31. }
  32. }
LineNumberReader

一个带行号的缓冲区。

  1. import java.io.*;
  2. class LineNumberReaderDemo {
  3. public static void main(String[] args) throws IOException {
  4. FileReader fr = new FileReader("PersonDemo.java");
  5. LineNumberReader lnr = new LineNumberReader(fr);
  6. String line = null;
  7. lnr.setLineNumber(100);
  8. while((line=lnr.readLine()) != null) {
  9. System.out.println(lnr.getLineNumber()+":"+line);
  10. }
  11. lnr.close();
  12. }
  13. }

练习:模拟一个带行号的缓冲区对象。
解:

  1. class MyLineNumberReader {
  2. private Reader r;
  3. private int lineNumber;
  4. MyLineNumberReader(Reader r) {
  5. this.r = r;
  6. }
  7. public String myReadLine() throws IOException {
  8. lineNumber++; // myReadLine()方法读一次自增一次
  9. StringBuilder sb = new StringBuilder();
  10. int ch = 0;
  11. while((ch=r.read()) != -1) {
  12. if(ch=='\r')
  13. continue;
  14. if(ch=='\n')
  15. return sb.toString();
  16. else
  17. sb.append((char)ch);
  18. }
  19. if(sb.length() != 0)
  20. return sb.toString();
  21. return null;
  22. }
  23. public void setLineNumber(int lineNumber) {
  24. this.lineNumber = lineNumber;
  25. }
  26. public int getLineNumber() {
  27. return lineNumber;
  28. }
  29. public void myClose() throws IOException {
  30. r.close();
  31. }
  32. }

可发现myReadLine()方法与我们自定义类MyBufferedReader中的myReadLine()类似,所以我们可如此做:

  1. class MyLineNumberReader extends MyBufferedReader {
  2. private int lineNumber;
  3. MyLineNumberReader(Reader r) {
  4. super(r);
  5. }
  6. public String myReadLine() throws IOException {
  7. lineNumber++; // myReadLine()方法读一次自增一次
  8. return super.myReadLine();
  9. }
  10. public void setLineNumber(int lineNumber) {
  11. this.lineNumber = lineNumber;
  12. }
  13. public int getLineNumber() {
  14. return lineNumber;
  15. }
  16. }

测试自定义类MyLineNumberReader

  1. class MyLineNumberReaderDemo {
  2. public static void main(String[] args) throws IOException {
  3. FileReader fr = new FileReader("CopyTextByBuf.java");
  4. MyLineNumberReader mylnr = new MyLineNumberReader(fr);
  5. String line = null;
  6. mylnr.setLineNumber(100);
  7. while((line=mylnr.myReadLine()) != null) {
  8. System.out.println(mylnr.getLineNumber()+"::"+line);
  9. }
  10. mylnr.myClose();
  11. }
  12. }

字节流

字符流:

字节流:

基本操作与字符流类相同,但它不仅可以操作字符,还可以操作其他媒体文件。

例,向一个文本文件中写入数据。

  1. public static void writeFile() throws IOException {
  2. FileOutputStream fos = new FileOutputStream("fos.txt");
  3. fos.write("abcde".getBytes());
  4. fos.close();
  5. }

例,从一个文本文件中读取数据。
第一种方式:

  1. public static void readFile_1() throws IOException {
  2. FileInputStream fis = new FileInputStream("fos.txt");
  3. int ch = 0;
  4. while((ch=fis.read()) != -1) {
  5. System.out.println((char)ch);
  6. }
  7. fis.close();
  8. }

第二种方式:

  1. public static void readFile_2() throws IOException {
  2. FileInputStream fis = new FileInputStream("fos.txt");
  3. byte[] buf = new byte[1024];
  4. int len = 0;
  5. while((len=fis.read(buf)) != -1) {
  6. System.out.println(new String(buf, 0, len));
  7. }
  8. fis.close();
  9. }

第三种方式(不建议使用,因为可能会有内存溢出异常)

  1. public static void readFile_3() throws IOException {
  2. FileInputStream fis = new FileInputStream("fos.txt");
  3. byte[] buf = new byte[fis.available()]; // 定义一个刚刚好的缓冲区,不用再循环了。不过慎用!!!
  4. fis.read(buf);
  5. System.out.println(new String(buf));
  6. fis.close();
  7. }

练习1、复制一个图片。
解:
思路:

  1. 用字节读取流对象和图片关联。
  2. 用字节写入流对象创建一个图片文件,用于存储获取到的图片数据。
  3. 通过循环读写,完成数据的存储。
  4. 关闭资源。
  1. import java.io.*;
  2. class CopyPic {
  3. public static void main(String[] args) {
  4. FileOutputStream fos = null;
  5. FileInputStream fis = null;
  6. try {
  7. fos = new FileOutputStream("c:\\2.jpg");
  8. fis = new FileInputStream("c:\\1.jpg");
  9. byte[] buf = new byte[1024];
  10. int len = 0;
  11. while((len=fis.read(buf)) != -1) {
  12. fos.write(buf, 0, len);
  13. }
  14. } catch(IOException e) {
  15. throw new RuntimeException("复制文件失败");
  16. } finally {
  17. try {
  18. if(fis != null)
  19. fis.close();
  20. } catch(IOException e) {
  21. throw new RuntimeException("读取关闭失败");
  22. }
  23. try {
  24. if(fos != null)
  25. fos.close();
  26. } catch(IOException e) {
  27. throw new RuntimeException("写入关闭失败");
  28. }
  29. }
  30. }
  31. }

练习2、复制一个MP3文件。
解:
分析:通过缓冲区(BufferedOutputStream/BufferedInputStream),演示MP3的复制。

  1. import java.io.*;
  2. class CopyMp3 {
  3. public static void main(String[] args) throws IOException {
  4. long start = System.currentTimeMillis();
  5. copy_1();
  6. long end = System.currentTimeMillis();
  7. System.out.println((end-start)+"毫秒");
  8. }
  9. // 通过字节流的缓冲区完成复制
  10. public static void copy_1() throws IOException {
  11. BufferedInputStream bufis = new BufferedInputStream(new FileInputStream("c:\\爱的秘密 蓝调口琴曲.mp3"));
  12. BufferedOutputStream bufos = new BufferedOutputStream(new FileOutputStream("c:\\爱的秘密 蓝调口琴曲_copy.mp3"));
  13. int by = 0;
  14. while((by=bufis.read()) != -1) {
  15. bufos.write(by);
  16. }
  17. bufos.close();
  18. bufis.close();
  19. }
  20. }

练习3、自定义一个类模拟BufferedInputStream,完成一个MP3文件的复制。
解:
分析:

mp3是由二进制数据组成的,如:11111111-1110000000000000000000101010110111010111010010110001。
问题:自定义的myRead()函数为什么会返回int类型,而不直接返回byte类型呢?
分析:
byte: -1  ----> int: -1
11111111(-1)提升为11111111 11111111 11111111 11111111(-1)
11111111(-1)--->提升为一个int类型,那还不是-1吗??是-1的原因是因为在8个1前面补的都是1导致的。那么我只要在前面补0,即可以保留原字节数据不变,又可以避免-1的出现。怎么补0呢?(&255)
    11111111 11111111 11111111 11111111
&   00000000 00000000 00000000 11111111(255)
------------------------------------------
    00000000 00000000 00000000 11111111
所以应把11111111(-1)提升为00000000 00000000 00000000 11111111(255),避免返回-1这种情况。

自定义一个MyBufferedInputStream类,如下:

  1. import java.io.*;
  2. class MyBufferedInputStream {
  3. private InputStream in;
  4. private byte[] buf = new byte[1024*4];
  5. private int pos = 0, count = 0;
  6. MyBufferedInputStream(InputStream in) {
  7. this.in = in;
  8. }
  9. // 一次读一个字节,从缓冲区(字节数组)获取。
  10. public int myRead() throws IOException {
  11. // 通过in对象读取硬盘上数据,并存储到buf中。
  12. if(count == 0) {
  13. count = in.read(buf);
  14. if(count < 0)
  15. return -1;
  16. pos = 0;
  17. byte b = buf[pos];
  18. count--;
  19. pos++;
  20. return b & 255; // 必须&255(0xff),然后再返回
  21. } else if (count > 0) {
  22. byte b = buf[pos];
  23. count--;
  24. pos++;
  25. return b & 0xff; // 必须&255(0xff),然后再返回
  26. }
  27. return -1;
  28. }
  29. public void myClose() throws IOException {
  30. in.close();
  31. }
  32. }

复制MP3:

  1. class CopyMp3 {
  2. public static void main(String[] args) throws IOException {
  3. long start = System.currentTimeMillis();
  4. copy_2();
  5. long end = System.currentTimeMillis();
  6. System.out.println((end-start)+"毫秒");
  7. }
  8. public static void copy_2() throws IOException {
  9. MyBufferedInputStream bufis = new MyBufferedInputStream(new FileInputStream("c:\\爱的秘密 蓝调口琴曲.mp3"));
  10. BufferedOutputStream bufos = new BufferedOutputStream(new FileOutputStream("c:\\爱的秘密 蓝调口琴曲_copy_2.mp3"));
  11. int by = 0;
  12. // System.out.println("第一个字节:"+bufis.myRead()); // 第一个字节:-1,因为读到了连续的11111111
  13. while((by=bufis.myRead()) != -1) {
  14. bufos.write(by);
  15. }
  16. bufos.close();
  17. bufis.myClose();
  18. }
  19. }

System类对IO的支持

例,需求:通过键盘录入数据。当录入一行数据后,就将该行数据进行打印,如果录入的数据是over,那么录入停止。
解:

  1. import java.io.*;
  2. class ReadIn {
  3. public static void main(String[] args) throws IOException {
  4. InputStream in = System.in;
  5. StringBuilder sb = new StringBuilder();
  6. while(true) {
  7. int ch = in.read();
  8. if(ch == '\r')
  9. continue;
  10. if(ch == '\n') {
  11. String s = sb.toString();
  12. if("over".equals(s))
  13. break;
  14. System.out.println(s.toUpperCase());
  15. sb.delete(0, sb.length()); // 清空缓冲区
  16. } else {
  17. sb.append((char)ch);
  18. }
  19. }
  20. }
  21. }

通过以上的键盘录入一行数据并打印其大写,发现其实就是读一行数据的原理。也就是readLine()方法。
能不能直接使用readLine()方法来完成键盘录入的一行数据的读取呢?
readLine()方法是字符流BufferedReader类中的方法,而键盘录入的read()方法是字节流InputStream的方法,那么能不能将字节流转成字符流,再使用字符流缓冲区的readLine()方法呢?此时就需要用到转换流。

转换流

例,需求:通过键盘录入数据。当录入一行数据后,就将该行数据进行打印,如果录入的数据是over,那么录入停止。
解:

  1. import java.io.*;
  2. class TransStreamDemo {
  3. public static void main(String[] args) throws IOException {
  4. // 获取键盘录入对象。
  5. // InputStream in = System.in;
  6. // 将字节流对象转成字符流对象,使用转换流——InputStreamReader
  7. // InputStreamReader isr = new InputStreamReader(in);
  8. // 为了提高效率,将字符流进行缓冲区技术高效操作,使用BufferedReader
  9. // BufferedReader bufr = new BufferedReader(isr);
  10. // 简写格式,键盘录入最常见写法
  11. BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
  12. // OutputStream out = System.out; // 屏幕输出
  13. // OutputStreamWriter osw = new OutputStreamWriter(out); // 字符流输出对象转换成字节流输出对象
  14. // BufferedWriter bufw = new BufferedWriter(osw);
  15. BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out));
  16. String line = null;
  17. while((line=bufr.readLine()) != null) {
  18. if("over".equals(line))
  19. break;
  20. bufw.write(line.toUpperCase());
  21. bufw.newLine();
  22. bufw.flush();
  23. }
  24. bufr.close();
  25. bufw.close();
  26. }
  27. }

流操作的基本规律

最痛苦的就是流对象有很多,不知道该用哪一个。但通过三个明确来完成:

  1. 明确源和目的。
    • 源:输入流。InputStream Reader
    • 目的:输出流。 OutputStream Writer
  2. 操作的数据是否是纯文本。
    • 是:字符流。
    • 不是:字节流。
  3. 当体系明确后,再明确要使用哪个具体的对象。通过设备来进行区分:
    • 源设备:内存、硬盘、键盘。
    • 目的设备:内存、硬盘、控制台。

例1,将一个文本文件中的数据存储到里另一个文件中(复制文件)。
分析:

  1. 源:因为是源,所以使用读取流。InputStream Reader
  2. 是不是操作文本文件?
    • 是!这时就可以选择Reader,这样体系就明确了。
  3. 接下来要明确要使用该体系中的那个对象?
    • 明确设备:硬盘上的一个文件。Reader体系中可以操作文件的对象是FileReader。
  4. 是否需要提高效率?
    • 是!加入Reader体系中的缓冲区BufferedReader。
  1. FileReader fr = new FileReader("a.txt");
  2. BufferedReader bufr = new BufferedReader(fr);
  1. 目的:OutputStream Writer
  2. 目的是否是纯文本?
    • 是!Writer。
  3. 设备:硬盘上的一个文件。
    • Writer体系中可以操作文件的对象是FileWriter
  4. 是否需要提高效率?
    • 是!加入Reader体系中的缓冲区BufferedWriter。
  1. FileWriter fw = new FileWriter("b.txt");
  2. BufferedWriter bufw = new BufferedWriter(fw);

练习:将一个图片文件中的数据存储到另一个文件中。(复制图片)
解:

  1. import java.io.*;
  2. class MyCopyPicture {
  3. public static void main(String[] args) {
  4. BufferedInputStream bis = null;
  5. BufferedOutputStream bos = null;
  6. try {
  7. bis = new BufferedInputStream(new FileInputStream("c:\\1.jpg"));
  8. bos = new BufferedOutputStream(new FileOutputStream("c:\\ye.jpg"));
  9. // 方式一
  10. /*
  11. int by = 0;
  12. while((by=bis.read()) != -1) {
  13. bos.write(by);
  14. }
  15. */
  16. // 方式二
  17. byte[] buf = new byte[1024];
  18. int len = 0;
  19. while((len=bis.read(buf)) != -1) {
  20. bos.write(buf, 0, len);
  21. }
  22. } catch(IOException e) {
  23. throw new RuntimeException("复制图片失败");
  24. } finally {
  25. try {
  26. if(bis != null)
  27. bis.close();
  28. } catch(IOException e) {
  29. throw new RuntimeException("读取关闭失败");
  30. }
  31. try {
  32. if(bos != null)
  33. bos.close();
  34. } catch(IOException e) {
  35. throw new RuntimeException("写入关闭失败");
  36. }
  37. }
  38. }
  39. }

例2,需求:将键盘录入的数据保存到一个文件中。
分析:这个需求中有源和目的都存在,那么分别分析:

  1. 源:InputStream Reader
  2. 是不是纯文本?
    • 是!Reader
  3. 设备:键盘。对应的对象是System.in。
    不是选择Reader吗?System.in对应的不是字节流吗?为了操作键盘的文本数据方便,转成字符流,按照字符串操作是最方便的,所以既然明确了Reader,那么就将System.in转换成字符流Reader,用到了Reader体系中的转换流,InputStreamReader。

    1. InputStreamReader isr = new InputStreamReader(System.in);
  4. 需要提高效率吗?
    • 需要!BufferedReader
  1. BufferedReader bufr = new BufferedReader(isr);
  1. 目的:OutputStream Writer
  2. 是不是纯文本?
    • 是!Writer
  3. 设备:硬盘上的一个文件。使用FileWriter。

    1. FileWriter fw = new FileWriter("c.txt");
  4. 需要提高效率吗?
    • 需要!BufferedWriter
  1. BufferedWriter bufw = new BufferedWriter(fw);

扩展一下,想要把录入的数据按照指定的编码表(如UTF-8),将数据存到文件中,怎么办呢?

  1. 目的:OutputStream Writer
  2. 是不是纯文本?
    • 是!Writer
  3. 设备:硬盘上的一个文件,使用FileWriter。但是FileWriter是使用的默认编码表(GBK),但是存储时,需要加入指定的编码表(UTF-8),而指定的编码表只有转换流可以指定,所以要使用的对象是OutputStreamWriter,而该转换流对象要接收一个字节输出流,而且还可以操作文件的字节输出流,FileOutputStream。

    1. OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("d.txt"), "UTF-8");
  4. 需要提高效率吗?
    • 需要!BufferedWriter
  1. BufferedWriter bufw = new BufferedWriter(osw);

所以,记住,转换流什么时候使用?字符和字节之间的桥梁,通常涉及到字符编码转换时,需要用到转换流
练习:将一个文本数据打印在控制台上。
解:

  1. import java.io.*;
  2. class MyTest {
  3. public static void main(String[] args) {
  4. BufferedReader bufr = null;
  5. BufferedWriter bufw = null;
  6. try {
  7. bufr = new BufferedReader(new FileReader("MyCopyPicture.java"));
  8. bufw = new BufferedWriter(new OutputStreamWriter(System.out));
  9. String line = null;
  10. while((line=bufr.readLine()) != null) {
  11. bufw.write(line);
  12. bufw.newLine();
  13. bufw.flush();
  14. }
  15. } catch(IOException e) {
  16. throw new RuntimeException("打印文件到控制台失败");
  17. } finally {
  18. try {
  19. if(bufr != null)
  20. bufr.close();
  21. } catch(IOException e) {
  22. throw new RuntimeException("读取文件关闭失败");
  23. }
  24. try {
  25. if(bufw != null)
  26. bufw.close();
  27. } catch(IOException e) {
  28. throw new RuntimeException("写入关闭失败");
  29. }
  30. }
  31. }
  32. }

标准输入输出流

System类中的字段:inout,它们各代表了系统标准的输入和输出设备,默认输入设备是键盘,输出设备是显示器。

可通过System类的setInsetOut方法对默认设备进行改变,不建议使用。

  1. System.setIn(new FileInputStream("PersonDemo.java")); // 将源改成文件PersonDemo.java
  2. System.setOut(new PrintStream("zzz.txt")); // 将目的改成文件zzz.txt

异常的日志信息

log4j:记录日志信息的一个工具。

  1. import java.io.*;
  2. import java.util.*;
  3. import java.text.*;
  4. class ExceptionInfo {
  5. public static void main(String[] args) throws IOException {
  6. try {
  7. int[] arr = new int[2];
  8. System.out.println(arr[3]);
  9. } catch(Exception e) {
  10. try {
  11. Date d = new Date();
  12. SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
  13. String s = sdf.format(d);
  14. PrintStream ps = new PrintStream("exception.log");
  15. // ps.write(d.toString().getBytes());
  16. ps.println(s);
  17. System.setOut(ps);
  18. } catch(IOException ex) {
  19. throw new RuntimeException("日志文件创建失败");
  20. }
  21. e.printStackTrace(System.out);
  22. }
  23. }
  24. }

系统信息

  1. import java.util.*;
  2. import java.io.*;
  3. class SystemInfo {
  4. public static void main(String[] args) throws IOException {
  5. Properties prop = System.getProperties();
  6. // System.out.println(prop);
  7. prop.list(new PrintStream("sysinfo.txt"));
  8. }
  9. }

通过sysinfo.txt文本文件可以知道平台默认的字符集,即默认字符编码是GBK
sysinfo

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注