一、IO概述
1、IO:是Input、Output的缩写。
2、特点:
(1)用于处理设备间的数据传输。
(2)Java对数据的处理是通过“流”完成的。
(3)Java用于操作流的对象都在IO包中。
(4)流按操作分为两种:字节流和字符流。
(5)流按流向分为:输入流和输出流(输入输出是相对于“内存”而言的)。
3、IO流常用基类
(1)字节流abstract基类:InputStream、OutputStream;
(2)字符流抽象基类:Reader、Writer;
二、字符流(Reader、Writer)
(一)概述
字符流只用于处理文字数据,是字节流读取文字字节数据后,不直接操作而是先查指定的编码表,获取对应的文字。简单说:字符流 = 字节流+编码表
(二)写入字符流(Writer)
1、“写入字符流”操作步骤
(1)创建时,要明确文件存储路径。如果文件不存在,则自动创建文件。如果文件存在,则会覆盖原文件。代码示例:
FileWtriter fw = new FileWriter("d:\\d.txt");
(2)调用Writer对象中的write(String s)方法,将数据写入到“临时存储缓冲区”中。代码示例:
fw.write("abcde");
(3)调用flush()方法,刷新该流的缓冲,将数据刷新到目的地中。代码示例:
fw.flush(); //可以用多次
(4)调用close()方法,关闭流资源。代码示例:
fw.close();//但是关闭前会刷新一次内部的缓冲数据,并将数据刷新到目的地中,只能用一次。
注意:close()和flush()区别:flush()刷新后,流可以继续使用;而close()刷新后,将会关闭流,不可再写入字符流。
2、FileWriter细节
(1)换行
Windows系统中的换行是\r\n,而Linux系统中的换行是\n。为了能够适应不同的操作系统,java可以获取系统换行。格式如下
System.getProperty(“line.sepatator”);
(2)续写
如果再FileWriter的构造函数中加入true,可以实现对文件实行续写。格式如下:
FileWtriter fw = new FileWriter("d:\\d.txt" , true);
(3)IO异常处理
将FileWriter对象(FileWriter fw = null)声明在try{}catch(){}外面。
在finally中,关闭流对象,判断fw是否为空,不为空时,才可以用try{}catch(){}来处理fw.close()。否则,会出现java.lang.NullPointerException。
3、FileWriter总结
(1)其实java自身不能写入数据,而是调用系统内部方式完成数据的书写,使用系统资源后,一定要关闭资源。
(2)文件的数据的续写是通过构造函数 FileWriter(Strings,boolean append),在创建对象时,传递一个true参数,代表不覆盖已有的文件。并在已有文件的末尾处进行数据续写。(windows系统中的文件内换行用\r\n两个转义字符表示,在linux系统中只用\n表示换行)
(3)由于在创建对象时,需要指定创建文件位置,如果指定的位置不存在,就会发生IOException异常,所以在整个步骤中,需要对IO异常进行try处理。
(三)读取字符流(Reader)
1、“读取字符流”操作步骤
(1)创建一个文件读取流对象,和指定名称的文件相关联。要保证该文件已经存在,若不存在,将会发生异常FileNotFoundException。代码示例:
FileReader fr = new FileReader(“d:\\demo.txt”);
(2)调用读取流对象的read()方法。read()有两种读取方式:一种是读取单个字符。另一种是通过字符数组进行读取。两种读取方式的代码示例:
① 读取单个字符方式:int ch = fr.read();
② 读取字符数组方式:
1 char[] buf = new char[1024];
2 Int len = 0;
3 While(( len = fr.read(buf))!=-1){
4 System.out.print( new String(buf , 0 , len));
5 }
(3)读取后调用close()方法,关闭流资源。代码示例:
fr.close();
2、FileReader总结
(1)定义文件路径时,可以用“/”或者“\\”。
(2)在创建一个文件时,如果目录下有同名文件将被覆盖。
(3)在读取文件时,必须保证该文件已存在,否则出异常。
(四)字符流练习:
/*
需求:将c盘一个文本文件复制到d盘。
思路:1、需要读取源;2、将读到的源写入到目的地;
步骤:
1、读一个文件,是字符读取流和文件关联;
2、创建一个目的文件,用于存储读到的数据;
3、频繁的读写;
4、关闭流资源;
*/
1 import java.io.*;
2 class ReaderWriterTest {
3 public static void main(String[] args) {
4 // 调用复制方法
5 // copy_1();
6 copy_2();
7 }
8 // 用第一种读取方式进行复制
9 public static void copy_1() {
10 FileWriter fw = null;
11 FileReader fr = null;
12 try {
13 // 关联读取和写入的文件
14 fw = new FileWriter("D:\\HelloWorld.java");
15 fr = new FileReader("C:\\HelloWorld.java");
16 for (int ch = 0; (ch = fr.read()) != -1;) {
17 fw.write(ch);// 一个字符一个字符写入
18 }
19 } catch (IOException e) {
20 throw new RuntimeException("读写失败");
21 } finally {
22 if (fr != null)
23 try {
24 fr.close();// 对读取流和写入流分别关闭
25 } catch (IOException e) {
26 }
27 if (fw != null)
28 try {
29 fw.close();
30 } catch (IOException e) {
31 }
32 }
33 }
34 // 第二种读取方式进行复制
35 public static void copy_2() {
36 FileWriter fw = null;
37 FileReader fr = null;
38 try {
39 // 关联读取和写入的文件
40 fw = new FileWriter("D:\\HelloWorld.java");
41 fr = new FileReader("C:\\HelloWorld.java");
42 char[] arr = new char[1024];
43 for (int len = 0; (len = fr.read(arr)) != -1;) {
44 fw.write(arr, 0, len);// 利用数组一次存入数组中的字符
45 }
46 } catch (IOException e) {
47 throw new RuntimeException("读写失败");
48 } finally {
49 try {
50 if (fr != null)
51 fr.close();
52 } catch (IOException e) {
53 } finally {
54 if (fw != null)
55 try {
56 fw.close();
57 } catch (IOException e) {
58 }
59 }
60 }
61 }
62 }
三、字符流缓冲区
(一)概述
1、字符流缓冲区的作用:提高了对数据的读写效率。
2、对应的类:BufferedWriter以及BufferedReader,要结合流,才可以使用。
(二)BufferedWriter
1、操作步骤
(1)创建一个字符写入流对象,代码示例:
FileWriter fw = new FileWriter("demo.txt");
(2)为了提高字符写入流效率,加入缓冲技术,将需要被提高效率的流对象作为参数传递给缓冲区的构造函数,代码示例:
BufferedWriter bufw = new BufferedWriter(fw);
(3)调用write方法写入数据到指定文件,代码示例:
bufw.write ("abcde");
(4)刷新缓冲区,代码示例:
bufw.flush();
(5)关闭流资源,,代码示例:
bufw.close();
2、小细节:BufferedWriter提供了跨平台的换行符newLine()方法,格式:bufw.newLine();
(三)BufferedReader
1、作用
从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。
2、特点
BufferedReader提供了一次读一行的方法readLine(),方便于文本数据的获取,当返回null时表示读到文件末尾。readLine方法返回的时候,只返回回车符之前的数据内容。并不返回回车符。
3、readLine()原理
使用了读取缓冲区的read()方法,将读取到的字符进行缓冲并判断换行标记,将标记前的缓存数据变成字符串返回。
4、BufferedReader操作步骤
(1)创建一个读取流对象和文件相关联,
FileReader fr = new FileReader("demo.txt");
(2)为了提高效率、加入缓冲技术、将字符读取流对象作为参数传递给缓冲区对象的构造函数。
BufferedReader bufr = new BufferedReader(fr);
(3)调用该readLine()方法一行一行读取,如果到达文件末尾,则返回null
String str = bufr.readLine();
(4)关闭流资源
bufr.close();
(四)练习
/* 需求:使用缓冲技术copy一个文本文件 */
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class CopyTextByBufTest {
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("buf.txt");
BufferedReader bufr = new BufferedReader(fr);
FileWriter fw = new FileWriter("buf_copy.txt");
BufferedWriter bufw = new BufferedWriter(fw);
String line = null;
while((line=bufr.readLine())!=null){
bufw.write(line);
bufw.newLine();
bufw.flush();
}
//单个字符读取方式
//int ch = 0;
//while((ch=bufr.read())!=-1){
// bufw.write(ch);
//}
bufw.close();
bufr.close();
}
}
(五)自定义缓冲区
1 /*需求:模拟BufferedReader,自定义一个MyBufferedReader缓冲区 */
2 import java.io.*;
3 //自定义缓冲类
4 class MyBufferedReader extends Reader {
5 private Reader r;// 定义接收的流对象
6 MyBufferedReader(Reader r) {
7 this.r = r;
8 }
9 // 自定义整行读取
10 public String myReadLine() throws IOException {
11 // 创建一个容器,用来存储一行的字符
12 StringBuilder sb = new StringBuilder();
13 // 一个字符一个字符读取
14 for (int ch = 0; (ch = r.read()) != -1;) {
15 if (ch == '\r')// 如果遇到换行符,则继续
16 continue;
17 if (ch == '\n')// 如果遇到回车符,表示该行读取完毕
18 return sb.toString();
19 else
20 sb.append((char) ch);// 将该行的字符添加到容器
21 }
22 if (sb.length() != 0)// 如果读取结束,容器中还有字符,则返回元素
23 return sb.toString();
24 return null;
25 }
26 // 复写父类中的抽象方法
27 public int read(char[] cbuf, int off, int len) throws IOException {
28 return r.read(cbuf, off, len);
29 }
30 // 复写父类的close方法
31 public void close() throws IOException {
32 r.close();
33 }
34 }
35
36 // 测试MyBufferedReader
37 class MyBufferedReaderDemo {
38 public static void main(String[] args) {
39 MyBufferedReader mbr = null;
40 try {
41 mbr = new MyBufferedReader(new FileReader("d:\\HelloWorld.java"));
42 for (String line = null; (line = mbr.myReadLine()) != null;) {
43 System.out.println(line);// 显示效果
44 }
45 } catch (IOException e) {
46 throw new RuntimeException("读取数据失败");
47 } finally {
48 try {
49 if (mbr != null)
50 mbr.close();
51 } catch (IOException e) {
52 throw new RuntimeException("读取流关闭失败");
53 }
54 }
55 }
56 }
(六)LineNumberReader类
在BufferedReader中有个直接的子类LineNumberReader,其中有特有的方法获取和设置行号:
setLineNumber();//设置初始行号
getLineNumber();//获取行号
(七)装饰设计模式
1、对一组对象的功能进行增强时,就可以使用装饰模式来解决问题。
2、特点:装饰类通常都会通过构造方法接收被装饰的对象,基于被装饰的对象的功能,提供更强的功能。
3、“装饰和继承”的异同?
(1)相同点:进行功能的扩展和增强,
(2)区别:装饰模式比继承要灵活、避免了继承体系的臃肿,降低了类与类之间的继承关系。装饰类和被装饰的类通常都是属于一个体系,有同一个接口或父类。
注:在定义类的时候,不要以继承为主;可通过装饰设计模式进行增强类功能。灵活性较强,当装饰类中的功能不适合,可再使用被装饰类的功能。
四、字节流(FileInputStream、FileOutputStream)
(一)概述
1、字节流和字符流的基本操作是相同的,它不仅可以操作文本数据,还可以操作其他媒体类文件。
2、由于硬盘上的数据都是以“字节”存储的,所以字节流对象可直接将数据写入到文件中,而可以不用再进行刷流动作。
3、读写字节流:
(1)InputStream:输入流(读)
(2)OutputStrea:输出流(写)
4、InputStream特有方法:
int available();//返回文件中的字节个数
注:可以利用此方法来指定读取方式中传入数组的长度,从而省去循环判断。但是如果文件较大,而虚拟机启动分配的默认内存一般为64M。当文件过大时,此数组长度所占内存空间就会溢出。所以,此方法慎用,当文件不大时,可以使用。
(二)FileInputStream、FileOutputStream
1、FileInputStream:InputStream常用子类之一,从文件系统中的某个文件中获得输入字节。
2、FileOutputStream:OutputStream常用子类之一,用于写入诸如图像数据之类的原始字节的流。
3、练习:复制图片
1 import java.io.*;
2 class CopyPic {
3 public static void main(String[] args) {
4 // 常用方法复制
5 byteArrayCopy();
6
7 // 利用输入流的available方法进行复制
8 // 利用available方法对复制文件大小有限制,慎用
9 availableCopy();
10 }
11 // 使用available方法进行复制
12 public static void availableCopy() {
13 FileInputStream fis = null;
14 FileOutputStream fos = null;
15 try {
16 // 关联要复制的文件
17 fis = new FileInputStream("C:\\1.jpg");
18 // 指定复制的路径
19 fos = new FileOutputStream("C:\\copy_1.jpg");
20 // 利用available方法指定数组长度
21 byte[] buf = new byte[fis.available()];
22 fis.read(buf);// 复制关联文件数据
23 fos.write(buf);// 粘贴到指定路径
24 } catch (IOException e) {
25 throw new RuntimeException("图片复制失败");
26 } finally {
27 try {
28 if (fis != null)
29 fis.close();// 关闭输入字节流
30 } catch (IOException e) {
31 throw new RuntimeException("读取字节流关闭失败");
32 }
33 try {
34 if (fos != null)
35 fos.close();// 关闭输出字节流
36 } catch (IOException e) {
37 throw new RuntimeException("写入字节流关闭失败");
38 }
39 }
40 }
41 // 使用读数组方式进行复制
42 public static void byteArrayCopy() {
43 FileInputStream fis = null;
44 FileOutputStream fos = null;
45 try {
46 // 关联要复制的文件
47 fis = new FileInputStream("C:\\2.jpg");
48 // 指定复制的路径
49 fos = new FileOutputStream("C:\\copy_2.jpg");
50 // 利用数组的读取方式
51 byte[] buf = new byte[1024];
52 int len = 0;
53 while ((len = fis.read(buf)) != -1){
54 fos.write(buf, 0, len);// 粘贴到指定路径
55 }
56 } catch (IOException e) {
57 throw new RuntimeException("图片复制失败");
58 } finally {
59 try {
60 if (fis != null)
61 fis.close();// 关闭输入字节流
62 } catch (IOException e) {
63 throw new RuntimeException("读取字节流关闭失败");
64 }
65 try {
66 if (fos != null)
67 fos.close();// 关闭输出字节流
68 } catch (IOException e) {
69 throw new RuntimeException("写入字节流关闭失败");
70 }
71 }
72 }
73 }
五、字节流缓冲区(BufferedInputStream、BufferedOutputStream)
1、作用:为了提高字节流的读写效率。
2、练习:分别用一般方式和字节缓冲区方式实现mp3文件的复制
1 import java.io.BufferedInputStream;
2 import java.io.BufferedOutputStream;
3 import java.io.FileInputStream;
4 import java.io.FileOutputStream;
5 import java.io.IOException;
6 public class CopyMp3Test {
7 public static void main(String[] args) throws IOException {
8 //普通方式复制mp3方法
9 OrdinayMethod();
10
11 //字节缓冲方式复制mp3方法
12 BufferedCopyMethod();
13 }
14 //字节缓冲方式复制功能定义
15 public static void BufferedCopyMethod() throws IOException {
16 FileInputStream fis = new FileInputStream("c:\\0.mp3");
17 BufferedInputStream bufis = new BufferedInputStream(fis);
18
19 FileOutputStream fos = new FileOutputStream("c:\\2.mp3");
20 BufferedOutputStream bufos = new BufferedOutputStream(fos);
21
22 int ch = 0;
23 while ((ch = bufis.read()) != -1) {
24 bufos.write(ch);
25 }
26
27 bufos.close();
28 bufis.close();
29 }
30 ////普通方式复制功能定义
31 public static void OrdinayMethod() throws IOException {
32 FileInputStream fis = new FileInputStream("c:\\0.mp3");
33 FileOutputStream fos = new FileOutputStream("c:\\1.mp3");
34
35 byte[] buf = new byte[1024];
36 int len = 0;
37 while ((len = fis.read(buf)) != -1) {
38 fos.write(buf, 0, len);
39 }
40
41 fos.close();
42 fis.close();
43 }
44 }
六、流操作(键盘输入流、转换流)
(一)标准输入输出流
1、System.in:是“标准”输入流,其类型是static InputStream类型,通常对应于键盘输入或者由主机环境或用户指定的另一个输入源。。
2、System.out:是“标准”输出流,其类型是PrintStrea,此流对应于显示器输出或者由主机环境或用户指定的另一个输出目标。
(二)读取键盘录入
InputStream in = System.in.
int ch = in.read(); //此句代码是阻塞式方法
in.close();
(三)整行录入
当使用输入流进行键盘录入时,只能一个字节一个字节进行录入。为了提高效率,可以自定义一个数组将一行字节进行存储。当一行录入完毕,再将一行数据进行显示。这种正行录入的方式,和字符流读一行数据的原理是一样的。也就是readLine方法。
那么能不能直接使用readLine方法来完成键盘录入的一行数据的读取呢?readLine方法是字符流BufferedReader类中方法。而键盘录入的read方法是字节流InputStream的方法。
那么能不能将字节流转成字符流再使用字符流缓冲区的readLine方法呢?这就需要用到转换流。
(四)转换流(InputStreamReader、OutputStreamWriter)
1、转换流:字符流与字节流之间的桥梁,方便了字符流与字节流之间的转换操作。
2、InputStreamReader:将字节流通向字符流的桥梁:
(1)定义键盘录入
InputStream in = System.in;
(2)定义转换流:字节流——>字符流
InputStreamReader isr = new InputStreamReader(in);
(3)定义字符流缓冲区,使用BufferedReader
BufferedReader br = new BufferedReader(isr);
3、OutputStreamWriter:字符流通向字节流的桥梁
字符通向字节:录入的是字符,存到硬盘上的是字节,操作与InputStreamReader转换流类似。
4、总结:
(1)谨记:键盘录入写法:
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
(2)谨记:控制台输出写法
BufferedWriter bufw = new BufferedRWriter(new OutputStreamWriter(System.out));
注:System.in可以替换为new FileInputStream(“file”)。System.out可以替换为new FileOutputStream(“file”)。
5、什么时候使用转换流呢?
(1)源或者目的对应的设备是“字节流”,但是操作的确实“文本数据”,可以使用转换流作为桥梁。
(2)一旦操作文本涉及到具体的指定编码表时,必须使用转换流。
6、转换流应用代码示例:
1 /*
2 需求:将键盘录入的数据,显示在控制台,当输入over时,表示结束。
3 源:键盘录入。
4 目的:控制台。
5 */
6 import java.io.*;
7 class TransStreamDemo{
8 public static void main(String[] args)throws IOException{
9
10 //键盘录入装饰写法
11 BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
12
13 //控制台输出装饰写法
14 BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out));
15
16 String line = null;
17 while(( line = bufr.readLine())! = null){
18 if("over".equals(s))
19 break;
20 bufw.write(s.toUpperCase());//写入数据
21 bufw.newLine();//换行
22 bufw.flush();//刷新
23 }
24 bufw.close();//关闭流资源
25 bufr.close();
26 }
27 }
(五)流操作的基本规律
1、要知道开发时用那些对象,只要通过四个明确即可:
(1)明确源和目的
---源:InputStream、Reader
---目的:OutputStream、Writer
(2)明确诗句是否是文本数据
---源:是纯文本:Reader
否:InputStream
---目的:是纯文本:Writer
否:OutputStream
* 到此,可以明确具体要用到哪个体系。
(3)明确具体的设备
---源设备:硬盘:File
键盘:System.in
内存:数组
网络:Socket流
---目的设备:硬盘:File
键盘:System.in
内存:数组
网络:Socket流
(4)是否需要其他额外功能
* 是否要高效(缓冲区):是添加前缀Buffered
* 是否转换流:用InputStreamReader / OutputStreamWriter
2、基本规律应用练习
需求1:复制一个文本文件。
1、明确源和目的。
源:InputStream Reader
目的:OutputStream Writer
2、是否是纯文本?
是!
源:Reader
目的:Writer
3、明确具体设备。
源:硬盘:File
目的:硬盘:File
FileReader fr = new FileReader("a.txt");
FileWriter fw = new FileWriter("b.txt");
4、需要额外功能吗?
需要,需要高效。
BufferedReader bufr = new BufferedReader(new FileReader("a.txt"));
BufferedWriter bufw = new BufferedWriter(new FileWriter("b.txt"));
================================================
需求2:读取键盘录入信息,并写入到一个文件中。
1、明确源和目的。
源:InputStream Reader
目的:OutputStream Writer
2、是否是纯文本呢?
是,
源:Reader
目的:Writer
3、明确设备
源:
键盘。System.in
目的:硬盘。File
InputStream in = System.in;
FileWriter fw = new FileWriter("b.txt");
这样做可以完成,但是麻烦。将读取的字节数据转成字符串。再由字符流操作。
4、需要额外功能吗?
需要。转换。将字节流转成字符流。因为名确的源是Reader,这样操作文本数据做便捷。
所以要将已有的字节流转成字符流。使用字节-->字符 。InputStreamReader
InputStreamReader isr = new InputStreamReader(System.in);
FileWriter fw = new FileWriter("b.txt");
还需要功能吗?
需要:想高效。
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bufw = new BufferedWriter(new FileWriter("b.txt"));
===================================================
需求3:将一个文本文件数据显示在控制台上。
1、明确源和目的。
源:InputStream Reader
目的:OutputStream Writer
2、是否是纯文本呢?
是,
源:Reader
目的:Writer
3、明确具体设备
源:硬盘:File
目的:控制台:System.out
FileReader fr = new FileReader("a.txt");
OutputStream out = System.out;//PrintStream
4、需要额外功能吗?
需要,转换。
FileReader fr= new FileReader("a.txt");
OutputStreamWriter osw = new OutputStreamWriter(System.out);
需要,高效。
BufferedReader bufr = new BufferedReader(new FileReader("a.txt"));
BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out));
================================================================
需求4:读取键盘录入数据,显示在控制台上。
1、明确源和目的。
源:InputStream Reader
目的:OutputStream Writer
2、是否是纯文本呢?
是,
源:Reader
目的:Writer
3、明确设备。
源:
键盘:System.in
目的:控制台:System.out
InputStream in = System.in;
OutputStream out = System.out;
4、明确额外功能?
需要转换,因为都是字节流,但是操作的却是文本数据。
所以使用字符流操作起来更为便捷。
InputStreamReader isr = new InputStreamReader(System.in);
OutputStreamWriter osw = new OutputStreamWriter(System.out);
为了将其高效。
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out));
============================================================
5、将一个中文字符串数据按照指定的编码表写入到一个文本文件中.
1、目的。OutputStream,Writer
2、是纯文本,Writer。
3、设备:硬盘File
FileWriter fw = new FileWriter("a.txt");
fw.write("你好");
注意:既然需求中已经明确了指定编码表的动作。那就不可以使用FileWriter,因为FileWriter内部是使用默认的本地码表。只能使用其父类OutputStreamWriter。OutputStreamWriter接收一个字节输出流对象,既然是操作文件,那么该对象应该是FileOutputStream。
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("a.txt"),charsetName);
4、需要高效吗?
BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("a.txt"),charsetName));