1. IO概述
- IO流用来处理设备之间的数据传输
- Java对数据的操作是通过流的方式
- Java用于操作流的对象都在IO包中
- 流按操作对象分为两种:字节流与字符流,字节流可以操作任何数据,字符流只能操作纯字符数据比较方便。
- 流按流向分为:输入流,输出流。
2. IO流常用基类
2.1 字节流的抽象基类:
InputStream
,OutputStream
2.2 字符流的抽象基类
Reader
,Writer
2.3 由这四个类派生出来的子类名称都是以其父类名作为子类名的后缀。
- 如:InputStream的子类FileInputStream。
- 如:Reader的子类FileReader。
- InputStreamReader是Reader的子类
3. 字符流的操作
3.1 读取文件
1 2 3 4 5 6
| //定义字符流关联指定文件 FileReader reader = new FileReader("Test.txt"); //读取一个字符,返回int,该字符的码表值 int ch = reader.read(); //关闭流,释放资源 reader.close();
|
3.2 写出文件
1 2 3 4 5 6
| //定义字符输出流关联指定文件 FileWriter writer = new FileWriter("Test.txt"); //写出一个字符,接收int码表值 writer.write(97); //关闭流,释放资源 writer.close();
|
3.3 注意事项
- 文件路径
- 定义文件路径时Windows中的目录符号为“\”,但这个符号在Java中是特殊字符,需要转义。可以用“\”或“/”表示。
- 读取文件
- 读取文件时必须保证文件存在,否则将抛出FileNotFoundException。
- 写出文件
- 写出时文件如不存在时程序会创建新文件,如文件已存在则会清空原文件内容重新写入。
- 如需追加内容可调用FileWriter构造函数FileWriter(String fileName, boolean append)
4. 字符流缓冲区的读写操作
4.1 自定义缓冲区读写
- 为什么定义缓冲区
- 由于单个字符读写需要频繁操作文件,所以效率非常低。我们可以定义缓冲区将要读取或写出的数据缓存,减少操作文件次数。
- 缓冲区读取
- 先定义一个数组,然后调用FileReader读取一个数组的方法。int read(char[] cbuf)
- 缓冲区写出
- 将要写出的数据存放在数组中,调用FileWriter方法,一次写出一个数组。void write(char[] cbuf, int off, int len)
4.2 内置缓冲区的BufferedReader和BufferedWriter
- Java提供了带缓冲功能的Reader和Writer类:BufferedReader,BufferedWriter
- 这两个类都是提供包装功能,需要提供其他流来使用,给其他流增加缓冲功能
- 当我们调用BufferedReader读取数据时,程序会从文件中一次读取8192个字符用来缓冲
- 当我们调用BufferedWriter写出数据时,程序会先将数据写出到缓冲数组,直到写满8192个才一次性刷出到文件
- 特有方法 newLine(), readLine()
注意:BufferedReader 读取时 不会读取换行符,所以写入时会出现乱序,在写入后加newLine();可以解决
==Eg1.==
需求:在硬盘上,创建一个文件并写入一些文字数据。找到一个专门用于操作文件的Writer子类对象。FileWriter。 后缀名是父类名。 前缀名是该流对象的功能。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| import java.io.*; class FileWriterDemo { public static void main(String[] args) throws IOException { //创建一个FileWriter对象。该对象一被初始化就必须要明确被操作的文件。 //而且该文件会被创建到指定目录下。如果该目录下已有同名文件,将被覆盖。 //其实该步就是在明确数据要存放的目的地。 FileWriter fw = new FileWriter("demo.txt");
//调用write方法,将字符串写入到流中。 fw.write("abcde");
//刷新流对象中的缓冲中的数据。 //将数据刷到目的地中。 //fw.flush();
//关闭流资源,但是关闭之前会刷新一次内部的缓冲中的数据。 //将数据刷到目的地中。 //和flush区别:flush刷新后,流可以继续使用,close刷新后,会将流关闭。 fw.close(); } }
|
==Eg2.==
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| /* 演示对已有文件的数据续写。 */ import java.io.*; class FileWriterDemo3 { public static void main(String[] args) throws IOException {
//传递一个true参数,代表不覆盖已有的文件。并在已有文件的末尾处进行数据续写。 FileWriter fw = new FileWriter("demo.txt",true);
fw.write("nihao\r\nxiexie");
fw.close(); } }
|
==Eg.3==
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
| //将C盘一个文本文件复制到D盘。
/* 复制的原理: 其实就是将C盘下的文件数据存储到D盘的一个文件中。
步骤: 1,在D盘创建一个文件。用于存储C盘文件中的数据。 2,定义读取流和C盘文件关联。 3,通过不断的读写完成数据存储。 4,关闭资源。 */
import java.io.*;
class CopyText { public static void main(String[] args) throws IOException { copy_2(); }
public static void copy_2() { FileWriter fw = null; FileReader fr = null; try { fw = new FileWriter("SystemDemo_copy.txt"); fr = new FileReader("SystemDemo.java");
char[] buf = new char[1024];
int len = 0; while((len=fr.read(buf))!=-1) { fw.write(buf,0,len); } } catch (IOException e) { throw new RuntimeException("读写失败");
} finally { if(fr!=null) try { fr.close(); } catch (IOException e) { } if(fw!=null) try { fw.close(); } catch (IOException e) { } } } //从C盘读一个字符,就往D盘写一个字符。 public static void copy_1()throws IOException { //创建目的地。 FileWriter fw = new FileWriter("RuntimeDemo_copy.txt");
//与已有文件关联。 FileReader fr = new FileReader("RuntimeDemo.java");
int ch = 0;
while((ch=fr.read())!=-1) { fw.write(ch); } fw.close(); fr.close();
} }
|
5. 装饰设计模式
5.1 什么情况下使用装饰设计模式
当我们需要对一个类的功能进行改进、增强的时候
5.2 装饰模式的基本格式。
- 含有被装饰类的引用
- 通过构造函数传入被装饰类对象
- 和被装饰类含有同样的方法,其中调用被装饰类的方法,对其进行改进、增强
- 和被装饰类继承同一个类或实现同一个接口,可以当做被装饰类来使用
5.3 了解BufferedReader、BufferedWriter的原理。
BufferedReader
、BufferedWriter
都是装饰类,他们可以装饰一个Reader
或Writer
,给被装饰的Reader和Writer提供缓冲的功能。就像我们用BufferedReader
、BufferedWriter
装饰FileReader
和FileWriter
,使用的读写功能还是FileReader
和FileWriter
的,但给这两个类的读写添加了缓冲功能。
6 字节流
基本操作与字符流相同,字节流可以操作任意类型数据
6.1 自定义缓冲区读写
- 原理和字符流相同,都是为了提高效率
- 定义数组缓冲数据,一次读取一个数组,一次写出一个数组,减少操作文件的次数
- 和
BufferedReader
、BufferedWriter
原理相同,都是包装类
BufferedInputStream
、BufferedOutputStream
包装InputStream``和OutputStream
提供缓冲功能
6.3 FileOutputStream
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| public class IoDemo03 { public static void main(String[] args) { FileOutputStream outputStream=null; try { //创建源 //选择流 outputStream = new FileOutputStream("test.txt",true); //操作 String s="i love you hunan"; byte[] bytes=s.getBytes(); //字符==>字节 编码 outputStream.write(bytes,0,bytes.length); FileInputStream inputStream = new FileInputStream("test02.txt"); byte[] bytes1=new byte[1024]; int read ; while ((read=inputStream.read(bytes1))!=-1) { String str=new String(bytes1,0,read); //字节==>字符 解码 System.out.print(str ); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { if (outputStream!=null){ try { outputStream.close(); } catch (IOException e) { e.printStackTrace(); }
} } } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| public class IoDemo01 { public static void main(String[] args) { //1.确定源 file 或者 字节数组流 File file=new File("test.txt"); //System.out.println(file.getAbsolutePath()); //2.选择流 InputStream inputStream=null; try { inputStream=new FileInputStream(file); //3.操作 int temp=0; while((temp=inputStream.read())!=-1){ System.out.print((char) temp); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }finally{ if(inputStream!=null){ try { inputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } //4.释放资源 } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| public class IoDemo02 { public static void main(String[] args) { //1.确定源 File file =new File("test.txt"); //2.选择流 FileInputStream inputStream=null; try { //3.操作 inputStream = new FileInputStream(file); byte[] bytes = new byte[1024]; //缓冲区 int temp; while((temp=inputStream.read(bytes))!=-1){ System.out.print(new String(bytes)); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); //4.释放资源 }finally{ try { inputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } }
|
7. 转换流
字符流与字节流之间的桥梁,方便了字符流与字节流之间的操作,字节流中的数据都是字符时,转成字符流操作更高效
==注==:转换流的输出流可以指定编码表
InputStreamReader
,OutputStreamWriter
8. 标准输入,输出
System
类中的成员变量:in
,out
。
- 它们各代表了系统标准的输入和输出设备。
- 默认输入设备是键盘,输出设备是显示器。
- System.in的类型是InputStream.
- System.out的类型是PrintStream是OutputStream的子类FilterOutputStream 的子类.
9. 流操作小结
通过三个明确来完成。
1,明确源和目的。
源:输入流。InputStream Reader
目的:输出流。OutputStream Writer。
2,操作的数据是否是纯文本。
是:字符流。
不是:字节流。
3,当体系明确后,在明确要使用哪个具体的对象。
通过设备来进行区分:
源设备:内存,硬盘。键盘
目的设备:内存,硬盘,控制台。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| Eg. 1,将一个文本文件中数据存储到另一个文件中。复制文件。 源:因为是源,所以使用读取流。InputStream Reader 是不是操作文本文件。 是!这时就可以选择Reader 这样体系就明确了。
接下来明确要使用该体系中的哪个对象。 明确设备:硬盘。上一个文件。 Reader体系中可以操作文件的对象是 FileReader
是否需要提高效率:是!。加入Reader体系中缓冲区 BufferedReader.
FileReader fr = new FileReader("a.txt"); BufferedReader bufr = new BufferedReader(fr);
目的:OutputStream Writer 是否是纯文本。 是!Writer。 设备:硬盘,一个文件。 Writer体系中可以操作文件的对象FileWriter。 是否需要提高效率:是!。加入Writer体系中缓冲区 BufferedWriter FileWriter fw = new FileWriter("b.txt"); BufferedWriter bufw = new BufferedWriter(fw);
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| 练习:将一个图片文件中数据存储到另一个文件中。复制文件。要按照以上格式自己完成三个明确。
---------------------------------------
2,需求:将键盘录入的数据保存到一个文件中。 这个需求中有源和目的都存在。 那么分别分析 源:InputStream Reader 是不是纯文本?是!Reader 设备:键盘。对应的对象是System.in. 不是选择Reader吗?System.in对应的不是字节流吗? 为了操作键盘的文本数据方便。转成字符流按照字符串操作是最方便的。 所以既然明确了Reader,那么就将System.in转换成Reader。 用了Reader体系中转换流,InputStreamReader
InputStreamReader isr = new InputStreamReader(System.in);
需要提高效率吗?需要!BufferedReader 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。 但是FileWriter是使用的默认编码表。GBK. 但是存储时,需要加入指定编码表utf-8。而指定的编码表只有转换流可以指定。 所以要使用的对象是OutputStreamWriter。 而该转换流对象要接收一个字节输出流。而且还可以操作的文件的字节输出流。FileOutputStream
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("d.txt"),"UTF-8");
需要高效吗?需要。 BufferedWriter bufw = new BufferedWriter(osw);
所以,记住。转换流什么使用。字符和字节之间的桥梁,通常,涉及到字符编码转换时, 需要用到转换流。
|