第一章、IO流
一、概述
1、IO流概念
主要用于保存数据,将内存中的数据保存到硬盘上,方便读取,实现类似于数据库的功能
2、IO流的分类
在Java中,I/O(输入/输出)流是处理数据输入和输出的机制。它们用于从文件、网络连接、内存等源读取数据,或将数据写入到这些目标中。I/O流以字节流和字符流的形式存在。
Java中的I/O流分为两个基本类型:字节流和字符流。字节流以字节为单位进行读取和写入,而字符流以字符为单位进行读取和写入。每种类型又分为输入流和输出流。
- 按照流的方向进行分类(以内存作为参照物)
- 输入流:往内存中区,叫做输入(Input)。或者叫做读(Read)
- 输出流:从内存中出来,叫做输出(Ootput)。或者叫做写(Write)
- 按照读取数据方式不同进行分类
- 字节流:按照字节的方式读取数据,一次读取1个字节byte,等同于一次读取8个二进制位。这种流是万能的,什么类型的文件都可以读取。包括:文本文件,图片,声音文件,视频文件等
- 中文字符在Windows中占用3个字节
- 假设文件file.txt,采用字节流的话是这样读的:
- a中国
- 第一次读:一个字节(正好读到“a”)
- 第二次读:一个字节(正好读到“中”字符的 1/ 3)
- 第三次读:一个字节(正好读到“中”字符的 2/ 3)
- 第四次读:一个字节(正好读到“中”字符的 3/ 3)
- 字符流:按照字符的方式读取数据的,一次读取一个字符,这种流是为了方便读取普通文本文件而存在的,这种流不能读取:图片,声音,视频等文件,只能读取纯文本文件,连word文件都无法读取
- 假设文件file.txt,采用字符流的话是这样读的:
- a中国
- 第一次读:“a”字符(“a”字符在wndows系统中占用1个字节)
- 第二次读:“中”字符(“中”字符在windows系统中占用2个字节)
3、java中所有的流都在:java.io.*;下
java中所有的IO流都已经写好了,我们不需要关心,我们最主要还是掌握,在java中已经提供了哪些流,每个流的特点是什么,怎么new流对象,每个流对象上常用的方法
二、java IO流的四大家族
1、概述
以下四个都是顶级类:都是抽象类(public abstract class)
- java.io.InputStream:字节输入流
- java.io.OutputStream:字节输出流
- java.io.Reader:字符输入流
- java.io.Writer:字符输出流
注意:在java中只要“类名”以Stream结尾的都是字节流。以“Reader/Writer”结尾的都是字符流
- java.io.*Stream:字节输入流(看结尾)
- java.io.*Stream:字节输出流(看结尾)
- java.io.*Reader:字符输入流(看结尾)
- java.io.*Writer:字符输出流(看结尾)
2、所有的流都实现了java.io.Closeable.java接口
java.io.Closeable.java接口,都是可关闭的,都有close方法。
流毕竟是一个管道,这个是内存和硬盘之间的通道,用完之后一定要关闭,不然会耗费(浪费)很多资源。
3、所有的输出流都实现了java.io.Flushable.java接口
- java.io.Flushable接口,都是可刷新的,都有flush()方法。
- 养成一个好习惯,输出流在最终输出之后,一定要记得flush(),刷新一下。
- 这个刷新表示将通道/管道当中剩余未输出的数据强行进行输出完(情空管道!)
- 刷新的作用就是清空管道
注意:如果没有flush()可能会导致丢失数据
三、java.io包下需要掌握的流(16个)
1、文件专属
java文件也是普通文本文件,只要能用记事本打开编辑的都是普通文本文件,并不一定是.txt后缀文件
- java.io.FileInputStream
- java.io.FileOutputStream
- java.io.FileReader
- java.io.FileWriter
2、转换流(将字节流转换成字符流)
- java.io.InputStreamReader
- java.io.OutputStreamWriter
3、缓冲流专属
- java.io.BufferedReader
- java.io.BufferedWriter
- java.io.BufferedInputStream
- java.io.BufferedOutputStream
4、数据流专属
- java.io.DataInputStream
- java.io.DataOutputStream
5、标准输出流
- java.io.PrintWriter
- java.io.PrintStream
6、对象专属流
- java.io.ObjectInputStream
- java.io.ObjectOutputStream
四、IOException异常与流的基本使用
使用I/O流时,通常的流程是创建适当的流对象,然后使用读取和写入方法对数据进行操作。完成操作后,应关闭流以释放系统资源。
注意:在处理I/O流时,需要处理可能抛出的异常,例如
IOException
。通常使用try-catch
块来捕获和处理这些异常。
第二章、FileInputStream文件字节输入流,FileReader文件字符输入流(硬盘 -> 内存)
一、概述
FileInputStream
是Java中用于从文件中读取字节的输入流类。它继承自InputStream
类,并提供了一些额外的方法来支持文件读取操作
二、创建FileInputStream对象
使用FileInputStream
可以打开一个文件并从中读取数据。以下是FileInputStream
的常用构造方法:
1、FileInputStream(String fileName)
:根据指定的文件名创建一个FileInputStream
对象。
1.1、绝对路径
//首先声明一个FileInputStream类型的变量为null,方便后期进行判断
FileInputStream fis = null;
try{
//创建文件字节输入流对象
//文件路径 C:\Users\13476\Desktop\JavaSE\Java进阶\新建 文本文档.txt (IDEA会自动把\变成\\)
// FileInputStream fis = new FileInputStream("C:\\Users\\13476\\Desktop\\JavaSE\\Java进阶\\新建 文本文档.txt");
//写成这个/也是可以的
fis = new FileInputStream("C:/Users/13476/Desktop/JavaSE/Java进阶/新建 文本文档.txt");
}catch (FileNotFoundException e){
e.printStackTrace();
}
1.2、相对路径
在IEDA中默认当前路径为工程project的根目录下
//尝试相对路径:相对路径一定是从当前的位置作为起点开始找
//IDEA中默认当前路径是:工程project的根
// fis = new FileInputStream("tempfile");
// fis = new FileInputStream("java_advanced/tempfile2");
// fis = new FileInputStream("java_advanced/src/tempfile3");
fis = new FileInputStream("java_advanced/src/com/javase/io/tempfile4");
三、常用实例方法
一旦创建了FileInputStream
对象,就可以使用其提供的方法读取文件的内容。以下是一些常用的方法:
1、void close() throws IOException:关闭输入流,释放与其关联的系统资源
一般在finally语句块中进行关闭
finally {
if(null != fis){
//关闭流的前提是:流不是空
//流是null的时候没必要关闭
try {
fis.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
2、int read() throws IOException 从输入流中读取一个字节的数据,并返回读取的字节值(0-255)。如果已经达到文件末尾,则返回-1
FileInputStream
类的 read
方法返回的 int
类型是在 0 到 255 之间的,是因为它是用来读取字节数据的方法,而字节是一个 8 位的数据类型,范围是从 0 到 255(2^8 - 1)。
具体来说,FileInputStream
的 read
方法的工作方式如下:
- 当它成功读取一个字节时,它将返回表示该字节的整数值(0 到 255),这意味着一个字节的所有可能值都可以通过正常的
int
类型来表示。 - 当已经到达文件的末尾时,
read
方法将返回 -1,表示没有更多的数据可供读取。
这种设计的好处是,你可以用 int
类型来接收 read
方法的返回值,并且能够轻松地检查是否已经到达文件的末尾(通过检查是否返回 -1)。这种方法允许你有效地读取文件中的每个字节,同时提供了一种方式来检测文件的结束。
要注意的是,如果你需要将读取的字节数据用于其他目的(例如构建字符串或解析二进制数据),你可能需要将返回的 int
值强制转换为 byte
类型,以便正确处理字节数据。
//开始读 读取a字符
int readData = fis.read(); //这个方法的返回值是:读取到一个字节所对应数据的字节值(0~255)
System.out.println(readData); //97
案例一:
package com.javase.io;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
/**
* java.io.FileInputStram
* 1、文件字节流,万能流,任何类型的文件都可以采用这个流来读
* 2、字节的方式,完成输入的操作,完成流的操作(硬盘 --》 内存)
*/
public class FileInputStreamTest01 {
public static void main(String[] args) {
//提前声明一个FileInputStream类型的变量,并赋值为null
FileInputStream fis = null;
try{
//创建文件字节输入流对象
//文件路径 C:\Users\13476\Desktop\JavaSE\Java进阶\新建 文本文档.txt (IDEA会自动把\变成\\)
// FileInputStream fis = new FileInputStream("C:\\Users\\13476\\Desktop\\JavaSE\\Java进阶\\新建 文本文档.txt");
//写成这个/也是可以的
fis = new FileInputStream("C:/Users/13476/Desktop/JavaSE/Java进阶/新建 文本文档.txt");
//开始读 读取a字符
int readData = fis.read(); //这个方法的返回值是:读取到一个字节所对应数据的字节值(0~255)
System.out.println(readData); //97
readData = fis.read(); //这个方法的返回值是:读取到一个字节所对应数据的字节值(0~255)
System.out.println(readData); //98
readData = fis.read(); //这个方法的返回值是:读取到一个字节所对应数据的字节值(0~255)
System.out.println(readData); //99
readData = fis.read(); //这个方法的返回值是:读取到一个字节所对应数据的字节值(0~255)
System.out.println(readData); //100
readData = fis.read(); //这个方法的返回值是:读取到一个字节所对应数据的字节值(0~255)
System.out.println(readData); //101
readData = fis.read(); //这个方法的返回值是:读取到一个字节所对应数据的字节值(0~255)
System.out.println(readData); //102
//已经读到文件末尾了,再读都是返回-1
readData = fis.read(); //这个方法的返回值是:读取到一个字节所对应数据的字节值(0~255)
System.out.println(readData); //-1
}catch (FileNotFoundException e){
e.printStackTrace();
}catch (IOException e){
e.printStackTrace();
}
finally {
if(null != fis){
//关闭流的前提是:流不是空
//流是null的时候没必要关闭
try {
fis.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
}
案例二:
package com.javase.io;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
/**
* 对第一个程序进行改进
*
* 分析这个程序的缺点:
* 一次读取一个字节byte,这样内存和硬盘交互太频繁,基本上时间都耗费在交互上了
* 所以尽可能一次读取多个字节
*/
public class FileInputStreamTest02 {
public static void main(String[] args) {
FileInputStream fis = null;
int readData = 0;
try {
fis = new FileInputStream("C:/Users/13476/Desktop/JavaSE/Java进阶/新建 文本文档.txt");
// while (true){
// int readData = fis.read();
// if(readData == -1){
// break;
// }
// System.out.println(readData);
// }
//改进while循环
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) {
throw new RuntimeException(e);
}
}
}
}
}
3、int read(byte[ ] b) throws IOException 从输入流中读取最多b.length
个字节的数据,并将其存储在给定的字节数组b
中。返回实际读取的字节数量(不是字节值),如果已经达到文件末尾,则返回-1。
1、数组中元素类型一致,每个元素所占的空间大小相同,是根据元素的类型所确定的,不一定是1byte
2、在该方法中byte[ ]数组中每个元素占1byte,因为字节输入流就是一个字节一个字节读取的。
3、该方法返回值不是具体的数据所对应的字节值,而是将数据拆分需要几个字节(总的字节数),返回的是本次读取到的字节的数量,且最大数量为b.length
汉字占三个字节,因此在初始化byte数组时最好是3的倍数,一般创建容量为1024的byte数组,这样会保证对同一个汉字可以完全读取不会乱码。
package com.javase.io;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
/**
* public int read(byte[] b) throws IOException;
* 一次最多读取 b.length 个字节
* 减少硬盘和内存的交互,提高程序的执行效率
* 往byte[]数组中读
*/
public class FileInputStreamTest03 {
public static void main(String[] args) {
FileInputStream fis = null;
try {
//尝试相对路径:相对路径一定是从当前的位置作为起点开始找
//IDEA中默认当前路径是:工程project的根
// fis = new FileInputStream("tempfile");
// fis = new FileInputStream("java_advanced/tempfile2");
// fis = new FileInputStream("java_advanced/src/tempfile3");
fis = new FileInputStream("java_advanced/src/com/javase/io/tempfile4");
//开始读,采用byte[]数组,一次读取多个字节,最多读取b.length个字节
byte[] bytes = new byte[4]; //准备一个容量为4的byte[]数组
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);
System.out.println(readCount); //第二次读取2个字节
// System.out.println(new String(bytes)); //efcd
//不应该全部转换,应该是读取了多少字节,转换多少字节
System.out.println(new String(bytes,0,readCount));
readCount = fis.read(bytes);
System.out.println(readCount); //第三次读取,一个字节都读不到,返回-1
// System.out.println(new String(bytes));//efcd
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
}
重点案例:
package com.javase.io;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
/**
* public int read(byte[] b) throws IOException;
* 一次最多读取 b.length 个字节
* 减少硬盘和内存的交互,提高程序的执行效率
* 往byte[]数组中读
*/
public class FileInputStreamTest04 {
public static void main(String[] args) {
FileInputStream fis = null;
try {
fis = new FileInputStream("java_advanced/src/com/javase/io/tempfile4");
byte[] bytes = new byte[4];
int readCount = 0;
while ((readCount = fis.read(bytes)) != -1){
//将byte数组转换为字符串,读到多少个转多少个
System.out.print(new String(bytes,0,readCount));
}
// 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();
}
}
}
}
}
4、int available() throws IOException 返回剩下未读的字节数量
可以获取目标数据的总字节数,创建指定数量的byte[]数组,就可以不使用循环了
不适用于大文件,因为byte[]数组不能太大
fis = new FileInputStream("java_advanced/src/com/javase/io/tempfile4");
//获取总字节数量
System.out.println("总字节数量:" + fis.available()); //6
//创建总字节数量的byte[]数组,就不需要循环了
byte[] bytes = new byte[fis.available()];
fis.read(bytes);
System.out.println(new String(bytes)); //abcdef
5、long skip(long n) throws IOException 跳过n个字节不读取,从n+1个字节开始读取
//跳过n个字节不读取
fis.skip(3l);
System.out.println(fis.read()); //100
四、FileReader
1、用法和FileInputStream完全一样,只是有byte[]变为char[]
package com.javase.io.FileReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
/**
* FileReader:
* 文件字符输入流,只能读取普通文本
* 读取文本内容时比较方便,快捷
*/
public class FileReaderTest {
public static void main(String[] args) {
FileReader reader = null;
try {
//创建文件字符输入流
reader = new FileReader("C:\\java文件拷贝测试\\test.txt");
/*//开始读
char[] chars = new char[4];
int readCount = 0;
while ((readCount = reader.read(chars)) != -1) {
System.out.print(new String(chars,0,readCount));
}*/
//遍历char[]数组
char[] chars = new char[4];
//将字符读取到char[]数组中
reader.read(chars);
for (char ele:
chars) {
System.out.println(ele);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
第三章、FileOutputStream 文件字节输出流,FileWriter文件字符输出流(内存 --> 硬盘)
一、概述
在Java中,FileOutputStream是用于将数据写入文件的类。它继承自OutputStream类,并提供了一组用于写入字节数据的方法。
1、文件字节输出流特点:没有指定的文件时会自动新建
没有指定的文件时会自动新建
2、文件名可跟绝对路径或相对路径
实例字节输出流对象时传入的文件名可以是绝对路径也可以是相对路径
3、输出流一定要在写入完成后进行flush()刷新
在写入完成后使用该方法void flush() throws IOException进行刷新
4、 输出流只会创建对应的目标文件而不会创建目标文件的父文件夹
需要提前创建出目标文件的父文件夹,否则会报FileNotFoundException异常
二、创建FileOutputStream对象
1、FileOutputStream(String name) throws FileNotFoundException 使用指定的File对象创建文件输出流。它将创建一个与指定文件关联的输出流(会将原文件内容清空再写入)
//myFile文件不存在时会新建
//这种方式慎用,会将原文件清空,然后重新写入
//使用相对路径,IDEA默认在工程根目录下
fos = new FileOutputStream("myFile");
2、FileOutputStream(File file, boolean append)
: 使用指定的File对象创建文件输出流,并指定是否在写入数据时追加到文件末尾。如果append
参数为true
,则数据将追加到文件末尾;如果为false
,则会覆盖文件中的内容
//以追加的方式在文件末尾写入,不会清空原文件内容
fos = new FileOutputStream("java_advanced/src/com/javase/io/FileOutputStream/jzq1",true);
三、常用实例方法
1、void write(byte[] b) throws IOException 将字节数组全部写入文件
byte[] data = "Hello, World!".getBytes();
fos.write(data);
byte[] bytes = {97,98,99,100};
//将byte[]数组全部写入
fos.write(bytes);
2、void write(byte[] b, int off, int len) throws IOException 将字节数组的一部分写入文件,从指定的偏移量开始,写入指定长度的字节。
byte[] data = "Hello, World!".getBytes();
fos.write(data, 0, 5); // 写入 "Hello"
3、void flush() throws IOException: 刷新缓冲区,将缓冲区中的数据立即写入文件。
//写完之后一定要进行刷新
fos.flush();
四、FileWriter
java文件也是普通文本文件,只要能用记事本打开编辑的都是普通文本文件,并不一定是.txt后缀文件
1、用法和FileOutputStream一样,只是将byte[]转换为char[]数组
package com.javase.io.FileWriter;
import java.io.FileWriter;
import java.io.IOException;
/**
* FileWriter
* 文件字符输出流,写
* 只能输出普通文本
*/
public class FileWriterTest {
public static void main(String[] args) {
FileWriter writer = null;
try {
//创建文件字符输出流对象
writer = new FileWriter("D:\\JavaSE\\JavaSE_demo1\\java_advanced\\src\\com\\javase\\io\\FileWriter\\测试.txt",true);
//开始写
char[] chars = {'贾','卓','群'};
//可以将char[]数组直接写入
writer.write(chars);
writer.write(chars,1,2);
//也可以直接写入字符串
writer.write("我是一名java软件工程师");
//写入换行符
writer.write("\n");
writer.write("999999999999");
writer.flush();
} catch (IOException e) {
e.printStackTrace();
}finally {
if (writer != null) {
try {
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
五、文件拷贝
1、文件字节流完成文件拷贝
1、原理
2、思路
使用FileInputSteam和