day31 天笔记(Io流部分)
IO 流
基本概念
IO流,什么是IO?
I : Input
O : Output
通过 IO 可以完成硬盘文件的读和写。

- IO流的分类
有多种分类方式:
一种方式是按照流的方向进行分类:
以内存作为参照物,<br /> 往内存中去,叫做输入(Input)。或者叫做读(Read)。<br /> 从内存中出来,叫做输出(Output)。或者叫做写(Write)。
另一种方式是按照读取数据方式不同进行分类:
有的流是按照字节的方式读取数据,一次读取1个字节byte,等同于一次读取8个二进制位。这种流是万能的,什么类型的文件都可以读取。包括:文本文件,图片,声音文件,视频文件等….
假设文件file1.txt,采用字节流的话是这样读的:
a中国bc张三fe
第一次读:一个字节,正好读到’a’
第二次读:一个字节,正好读到’中’字符的一半。
第三次读:一个字节,正好读到’中’字符的另外一半。有的流是按照字符的方式读取数据的,一次读取一个字符,这种流是为了方便读取。普通文本文件而存在的,这种流不能读取:图片、声音、视频等文件。只能读取纯文本文件,连word文件都无法读取。
假设文件file1.txt,采用字符流的话是这样读的:
a中国bc张三fe
第一次读:’a’字符(’a’字符在windows系统中占用1个字节。)
第二次读:’中’字符(’中’字符在windows系统中占用2个字节。)
注意: 空格是一个字符 Asall值是32
综上所述:流的分类
输入流、输出流
字节流、字符流
Java中的IO流
Java中的IO流都已经写好了,我们程序员不需要关心,我们最主要还是掌握,在java中已经提供了哪些流,每个流的特点是什么,每个流对象上的常用方法有哪些????
java中所有的流都是在:java.io.*;下。
java中主要还是研究:怎么new流对象 与 调用流对象的哪个方法是读、哪个方法是写。
java IO 流这块有四大家族:
四大家族的首领:
java.io.InputStream 字节输入流
java.io.OutputStream 字节输出流java.io.Reader 字符输入流<br /> java.io.Writer 字符输出流四大家族的首领都是抽象类。(abstract class)
所有的流都实现了:java.io.Closeable接口
都是可关闭的,都有close()方法
流毕竟是一个管道,这个是内存和硬盘之间的通道,用完之后一定要关闭,不然会耗费(占 用)很多资源。养成好习惯,用完流一定要关闭。
- 所有的输出流都实现了:java.io.Flushable接口
都是可刷新的,都有flush()方法。
养成一个好习惯,输出流在最终输出之后,一定要记得flush()刷新一下。这个刷新表示将 通道/管道当中剩余未输出的数据强行输出完(清空管道!)刷新的作用就是清空管道。
注意:如果没有flush()可能会导致丢失数据。
- 注意:在java中只要“类名”以Stream结尾的都是字节流。以“Reader/Writer”结尾的都是字符流。
java.io包下需要掌握的流
java.io包下需要掌握的流有16个:
文件专属:
java.io.FileInputStream(掌握)
java.io.FileOutputStream(掌握)
java.io.FileReader
java.io.FileWriter转换流:(将字节流转换成字符流)
java.io.InputStreamReader
java.io.OutputStreamWriter缓冲流专属:
java.io.BufferedReader
java.io.BufferedWriter
java.io.BufferedInputStream
java.io.BufferedOutputStream数据流专属:
java.io.DataInputStream
java.io.DataOutputStream标准输出流:
java.io.PrintWriter
java.io.PrintStream(掌握)对象专属流:
java.io.ObjectInputStream(掌握)
java.io.ObjectOutputStream(掌握)
File(重点)
java.io.FileInputStream
构造方法:
| FileInputStream(String name) 通过打开与实际文件的连接来创建一个 FileInputStream ,该文件由文件系统中的路径名 name命名。 |
|---|
常用方法:
| int read(byte[ ] b)
从该输入流读取最多 b.length个字节的数据为字节数组
int read(byte[] b, int off, int len)
从该输入流读取最多 len字节的数据为字节数组
int available():返回流当中剩余的没有读到的字节数量
long skip(long n):跳过几个字节不读 | | —- | | |
基本概念:
1、文件字节输入流,万能的,任何类型的文件都可以采用这个流来读。
2、字节的方式,完成输入的操作,完成读的操作(硬盘—-> 内存)Java中的文件路径:
D:\course\JavaProjects\02-JavaSE\temp (IDEA会自动把\编程\,因为java中\表示转义)
以下都是采用了:绝对路径的方式。
FileInputStream fis = new FileInputStream(“D:\course\JavaProjects\02-JavaSE\temp”);
这里的 \ 写成这个 / 也是可以的。实例(程序有缺陷):
注意:
- 读到文件末尾时 返回 -1- 在finally语句块当中,确保流一定关闭。关闭流的前提是:流不是空。流是null的时候没必要关闭- FileInputStream 源码中抛异常了 需要对异常进行处理- FileInputStream 创建对象时抛异常了需要对异常进行处理- read()方法源文件抛异常了 需要对异常进行处理- 关闭 close()的时候也需要 异常处理
public class FileInputStreamTest01 {public static void main(String[] args) {FileInputStream fis = null;try {fis = new FileInputStream("D:/course/JavaProjects/02-JavaSE/temp");// 开始读int readData = fis.read(); // 这个方法的返回值是:读取到的“字节”本身。System.out.println(readData); //97readData = fis.read();System.out.println(readData); //98readData = fis.read();System.out.println(readData); //99// 已经读到文件的末尾了,再读的时候读取不到任何数据,返回-1.readData = fis.read();System.out.println(readData);readData = fis.read();System.out.println(readData);readData = fis.read();System.out.println(readData);} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();} finally {// 在finally语句块当中确保流一定关闭。if (fis != null) { // 避免空指针异常!// 关闭流的前提是:流不是空。流是null的时候没必要关闭。try {fis.close();} catch (IOException e) {e.printStackTrace();}}}}}
- 对上面程序的改进
对第一个程序进行改进,循环方式。
分析以下这个程序的缺点:
一次读取一个字节byte,这样内存和硬盘交互太频繁,基本上时间/资源都耗费在交互上面了。能不能一次读取多个字节呢?可以。
FileInputStream fis = null;try {fis = new FileInputStream("D:\\course\\JavaProjects\\02-JavaSE\\temp");/*while(true) {int readData = fis.read();if(readData == -1) {break;}System.out.println(readData);}*/// 改造while循环int readData = 0;while((readData = fis.read()) != -1){System.out.println(readData);}} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();} finally {if (fis != null) {try {fis.close();} catch (IOException e) {e.printStackTrace();}
int read(byte[ ] b)
read (byte[] b, int off, int len)
往byte[]数组当中读:一次最多读取 b.length 个字节,减少硬盘和内存的交互,提高程序的执行效率。
将 byte 数组转换成String类型的字符串: 调用: String 类 的构造方法 传入byte数组 转换为 String类型
语法格式:
String(Byte,0,length-1)
FileInputStream fis = null;try {// 开始读,采用byte数组,一次读取多个字节。最多读取“数组.length”个字节。byte[] bytes = new byte[4]; // 准备一个4个长度的byte数组,一次最多读取4个字节。// 这个方法的返回值是:读取到的字节数量。(不是字节本身)int readCount = fis.read(bytes);System.out.println(readCount); // 第一次读到了4个字节。// 将字节数组全部转换成字符串//System.out.println(new String(bytes)); // abcd// 不应该全部都转换,应该是读取了多少个字节,转换多少个。System.out.println(new String(bytes,0, readCount));readCount = fis.read(bytes); // 第二次只能读取到2个字节。System.out.println(readCount); // 2// 将字节数组全部转换成字符串//System.out.println(new String(bytes)); // efcd// 不应该全部都转换,应该是读取了多少个字节,转换多少个//不这样处理的话输出efcdSystem.out.println(new String(bytes,0, readCount));readCount = fis.read(bytes); // 1个字节都没有读取到返回-1System.out.println(readCount); // -1} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();} finally {if (fis != null) {try {fis.close();} catch (IOException e) {e.printStackTrace();}}}
文件里面的内容:abcdef
下面这个需要掌握
public class FileInputStreamTest04 {public static void main(String[] args) {FileInputStream fis = null;try {fis = new FileInputStream("chapter23/src/tempfile3");// 准备一个byte数组byte[] bytes = new byte[4];/*while(true){int readCount = fis.read(bytes);if(readCount == -1){break;}// 把byte数组转换成字符串,读到多少个转换多少个。System.out.print(new String(bytes, 0, readCount));}*/int readCount = 0;while((readCount = fis.read(bytes)) != -1) {System.out.print(new String(bytes, 0, readCount));}} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();} finally {if (fis != null) {try {fis.close();} catch (IOException e) {e.printStackTrace();}}}}}
IDEA中的路径
相对路径的话呢? 相对路径一定是从当前所在的位置作为起点开始找!
IDEA默认的当前路径是哪里? 工程Project的根就是IDEA的默认当前路径。
如果该路径下 没有此文件会报错
fis = new FileInputStream("tempfile3");//fis = new FileInputStream("chapter23/tempfile2");//fis = new FileInputStream("chapter23/src/tempfile3");fis = new FileInputStream("chapter23/src/com/bjpowernode/java/io/tempfile4");
FileInputStream类的其它常用方法
int available(): 返回 流 当中剩余的没有读到的字节数量
long skip(long n): 跳过几个字节不读
列子:
public class FileInputStreamTest05 {public static void main(String[] args) {FileInputStream fis = null;try {fis = new FileInputStream("tempfile");System.out.println("总字节数量:" + fis.available());// 读1个字节//int readByte = fis.read();// 还剩下可以读的字节数量是:5//System.out.println("剩下多少个字节没有读:" + fis.available());// 这个方法有什么用?//byte[] bytes = new byte[fis.available()]; // 这种方式不太适合太大的文件,因为byte[]数组不能太大。// 不需要循环了。// 直接读一次就行了。//int readCount = fis.read(bytes); // 6//System.out.println(new String(bytes)); // abcdef// skip跳过几个字节不读取,这个方法也可能以后会用!fis.skip(3);System.out.println(fis.read()); //100} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();} finally {if (fis != null) {try {fis.close();} catch (IOException e) {e.printStackTrace();}}}}}
FileOutputStream
文件的写入 内存———> 硬盘
文件字节输出流,负责写。
- 构造方法:
FileOutputStream(String name)
FileOutputStream(String name, true) 这个构造方法表示在文件的末尾追加
- 案例:
注意:
- 文件不存在的时候会自动新建- 写完之后一定要刷新- fos = new FileOutputStream("chapter23/src/tempfile3");
这种方式谨慎使用,这种方式会先将原文件清空,然后重新写入。
- fos = new FileOutputStream("chapter23/src/tempfile3", true);
以追加的方式在文件末尾写入。不会清空原文件内容。
这里的true 表示的含义是 是否以文件追加的方式 在文件末尾写入
不加 true 这个默认参数的话是 默认将文件清空的
将String类型转换为数组:
String类型的对象 . getBytes();
public class FileOutputStreamTest01 {public static void main(String[] args) {FileOutputStream fos = null;try {// myfile文件不存在的时候会自动新建!// 这种方式谨慎使用,这种方式会先将原文件清空,然后重新写入。//fos = new FileOutputStream("myfile");//fos = new FileOutputStream("chapter23/src/tempfile3");// 以追加的方式在文件末尾写入。不会清空原文件内容。fos = new FileOutputStream("chapter23/src/tempfile3", true);// 开始写。byte[] bytes = {97, 98, 99, 100};// 将byte数组全部写出!fos.write(bytes); // abcd// 将byte数组的一部分写出!fos.write(bytes, 0, 2); // 再写出ab// 字符串String s = "我是一个中国人,我骄傲!!!";// 将字符串转换成byte数组。byte[] bs = s.getBytes();// 写fos.write(bs);// 写完之后,最后一定要刷新fos.flush();} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();} finally {if (fos != null) {try {fos.close();} catch (IOException e) {e.printStackTrace();}}}}}
文件的复制
- 文件的复制原理
图
- 案例:
FileReadr
能用记事本编辑的都是普通文本文件,普通文本文件和后缀无关
字节流是 Byte 数组 字符流是 char 数组
copy1
copy2
缓冲专属
BufferedRead
1.基本概念
带有缓冲区的字符输入流。
使用这个流的时候不需要自定义char数组,或者说不需要自定义byte数组。自带缓冲。
- 构造方法与其它流不同
语法格式:
BufferedReader( Reader in )
说明:
看Api得出的:
Reader 是一个抽象类
FileReader类是Reader类的子类 它的父类是InputStreamReader类
InputStreamReader类的父类是Reader
- 特有的方法:
- readerLine()
这个方法读到文件末尾结束
作用:读取一个文本行 (读一行) 不带换行符
案例
说明:
- 当一个流的构造方法中需要一个流的时候,这个被传进来的流叫做:节点流
- 外部负责包装的这个流,叫做:包装流,还有一个名字叫做:处理流。
看源代码得出的:
- BufferedReader中有一个实例变量 : Reader in- Reader in 就是一个节点,in 是一个属性,通过构造方法进行赋值- bufferedReader就是一个包装- 传入的节点的原理:- 将传入的节点流赋值给 Reader in 这个属性- bufferReader这个流里面有一个close(),这个close ()里面有一个in在调用close(),所以当BufferedReader流 去关闭的时候里面的流会自动关闭
得出的结论:
- 对一个包装流来说,只需要关闭最外层的就行,里面的节点流会自动关闭。 ```java public class BufferedReaderTest01 { public static void main(String[] args) throws Exception{
FileReader reader = new FileReader(“Copy02.java”); // 当一个流的构造方法中需要一个流的时候,这个被传进来的流叫做:节点流。 // 外部负责包装的这个流,叫做:包装流,还有一个名字叫做:处理流。 // 像当前这个程序来说:FileReader就是一个节点流。BufferedReader就是包装流/处理流。 BufferedReader br = new BufferedReader(reader);
// 读一行 /*String firstLine = br.readLine(); System.out.println(firstLine);
String secondLine = br.readLine(); System.out.println(secondLine);
String line3 = br.readLine(); System.out.println(line3);*/
// br.readLine()方法读取一个文本行,但不带换行符。 String s = null; while((s = br.readLine()) != null){
System.out.print(s);
}
// 关闭流 // 对于包装流来说,只需要关闭最外层流就行,里面的节点流会自动关闭。(可以看源代码。) br.close(); } } ```
转换流
BufferedReader02
通过对应的构造方法将字节流
数据专属流
标准输出流
println
logger
日志工具
File类
- 基本概念
File类和四大家族没有关系,所以File类不能完成文件的读和写。
File对象代表什么?
文件和目录路径名的抽象表示形式。
C:\Drivers 这是一个File对象
C:\Drivers\Lan\Realtek\Readme.txt 也是File对象。
一个File对象有可能对应的是目录,也可能是文件。
File只是一个路径名的抽象表示形式。需要掌握File类中常用的方法
filetest02
拷贝目录
对象专属流
