IO流
IO流
IO流图解
1.文件
文件:文件就是保存数据的地方,类似word,text等都是文件。
- 文件流:文件在程序中,是以流的形式来进行操作的。
- 流:数据在数据源(文件)与程序(内存)之间的路径
- 输入流:数据从程序源(文件)到程序(内存)的路径
- 输出流:数据从程序(内存)到数据源(文件)的路径

1.创建文件的几种方式
package io;
import java.io.File;
import java.io.IOException;
public class CreateFile {
public static void main(String[] args) {
testCreateFileOne();
testCreateFileTwo();
testCreateFileThree();
testCreateFileFour();
}
/**
* 创建文件的第一种方式 填入完整路径+文件名
*/
public static void testCreateFileOne(){
String path = "D:\\new1.txt";
//此步骤只是创建了一个文件对象,并没有向硬盘写入任何数据
File file = new File(path);
try {
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("创建成功!");
}
/**
* 创建文件的第一种方式 父文件+子路径
*/
public static void testCreateFileTwo(){
String parentPath = "D:\\";
File parentFile = new File(parentPath);
//此步骤只是创建了一个文件对象,并没有向硬盘写入任何数据
File file = new File(parentFile,"new2.txt");
try {
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("创建成功2!");
}
/**
* 创建文件的第一种方式 父文件路径+子路径
*/
public static void testCreateFileThree(){
String parentPath = "D:\\";
String path = "new3.txt";
//此步骤只是创建了一个文件对象,并没有向硬盘写入任何数据
File file = new File(parentPath,path);
try {
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("创建成功3!");
}
/**
* 路径填写可以不使用转义符
*/
public static void testCreateFileFour(){
String parentPath = "D:/";
String path = "new4.txt";
//此步骤只是创建了一个文件对象,并没有向硬盘写入任何数据
File file = new File(parentPath,path);
try {
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("创建成功4!");
}
}
2.文件常见的方法
package io;
import java.io.File;
/**
* 测试文件类的一些方法
*/
public class TestFileMethod {
public static void main(String[] args) {
File file = new File("d://new1.txt");
//判断文件是否存在
System.out.println("文件是否存在:"+file.exists());
//判断是否是文件
System.out.println("是否是文件:"+file.isFile());
//判断是否是文件夹
System.out.println("是否是文件夹"+file.isDirectory());
//获取文件名称
System.out.println("文件名称:"+file.getName());
//获取文件路径
System.out.println("文件路径:"+file.getPath());
//获取文件的绝对路径
System.out.println("文件绝对路径:"+file.getAbsolutePath());
//获取文件的长度
//文本内容:hello 哈哈哈 UTF-8格式 英文一个字节 中文三个字节 空格也占一个字节
System.out.println("文件长度(字节):"+file.length());
mkdirs
}
}
3.文件目录的创建、删除
在java中,文件目录实际上就是一种特殊的文件,删除和创建方式基本同文件。
操作方法:
- delete() 只能删除空文件夹,文件夹中有子文件夹或文件时,此方法都无法删除成功
- mkdirs() 创建一层或多层文件夹
- mkdir() 只能创建一层文件夹
package io;
import java.io.File;
/**
* 测试文件目录操作
*/
public class TestFileDirectory {
public static void main(String[] args) {
//判断D盘下是否有文件 new1.txt,有的话删除
// String filePath ="D:\\new1.txt";
// File file = new File(filePath);
// if(file.exists()){
// if(file.delete()){
// System.out.println(filePath+"删除成功!");
// }else{
// System.out.println(filePath+"删除失败!");
// }
// }else{
// System.out.println(filePath + "文件不存在!");
// }
//判断D盘下是否有文件目录,有的话提示已有目录,没有的话,创建目录
// String filePath ="D:\\demo\\a";
// File file = new File(filePath);
// if(file.exists()){
// System.out.println(filePath+"文件目录已存在");
// }else{
// if(file.mkdirs()){
// System.out.println(filePath+"文件目录创建成功!");
// }else{
// System.out.println(filePath+"文件目录创建失败!");
// }
// }
//目录删除
String filePath ="D:\\demo\\a";
File file = new File(filePath);
if(file.exists()){
if(file.delete()){
System.out.println("删除成功!");
}else{
System.out.println("删除失败!");
}
}else{
System.out.println(filePath + "文件不存在!");
}
}
}
2.文件常用流
1. 节点流
1.FileInputStram
输入流 --> 字节流 -->文件输入流
/**
* 读取D盘根目录下的hello.txt文件中的内容并输出
*
* read() 方法
*/
public static void testRead(){
String filePath = "d:\\hello.txt";
FileInputStream fileInputStream = null;
try {
//创建文件对应的输入流对象
fileInputStream = new FileInputStream(filePath);
//从该输入流读取一个字节的数据。 如果没有输入可用,此方法将阻止。
//数据的下一个字节,如果达到文件的末尾, 返回-1
int read = 0;
while (read != -1){
read = fileInputStream.read();
//读取的字节转化为字符输出
System.out.print((char)read);
}
} catch (IOException e) {
e.printStackTrace();
}finally {
//使用完成后,关闭流,防止资源浪费
try {
fileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 读取D盘根目录下的hello.txt文件中的内容并输出
*
* read(byte[] byte)方法 此方法通过定义字节数组提高读取数据的效率,读取到的数据,存储在定义好的数组中,返回值为实际读取到的字节数
*/
public static void testRead2(){
String filePath = "d:\\hello.txt";
byte[] bytes = new byte[5];
FileInputStream fileInputStream = null;
try {
//创建文件对应的输入流对象
fileInputStream = new FileInputStream(filePath);
//从该输入流读取指定字节数组长度的数据。 如果没有输入可用,此方法将阻止。
//数据的下一个字节数组长度,如果达到文件的末尾, 返回-1
int read = 0;
while (read != -1){
read = fileInputStream.read(bytes);
//读取的字节转化为字符输出
if(read!=-1){
System.out.print(new String(bytes,0,read));
}
}
} catch (IOException e) {
e.printStackTrace();
}finally {
//使用完成后,关闭流,防止资源浪费
try {
fileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
由于按照一个或多个字节读取数据,对于UTF-8格式的汉字,每个汉字3个字节,所读结果必然会乱码,这也是为什么字节流更适合读取二进制文件(声音、视频等)的原因。
2.FileOutputStream
输出流 --> 字节流 -->文件输出流
/**
* 在D盘下a.txt文件中输出指定的内容
*
* write 文件不存在时,此方法会自动创建文件,但前提是文件目录存在;
*/
public static void testWrite(){
String filePath="d://a.txt";
FileOutputStream fileOutputStream = null;
try{
//创建输出流对象
//此构造器会覆盖文件原有内容,如果不想覆盖原内容,追加时,使用另一个构造器,设置append属性为true
fileOutputStream = new FileOutputStream(filePath,true);
fileOutputStream = new FileOutputStream(filePath);
//输出一个字符
//fileOutputStream.write('H');
//输出多个字符
String str = "hello,world!";
//fileOutputStream.write(str.getBytes());
//输出多个字符中的指定长度字符
fileOutputStream.write(str.getBytes(),0,5);
}catch (IOException exception){
exception.printStackTrace();
}finally {
try {
fileOutputStream.close();
} catch (IOException exception) {
exception.printStackTrace();
}
}
}
2.文件拷贝
public static void testCopyFile(){
String filePath = "d:\\backGround.jpg";
String outPath = "d:\\backGround2.jpg";
FileInputStream fileInputStream = null;
FileOutputStream fileOutputStream = null;
try {
byte[] bytes = new byte[1024];
fileInputStream = new FileInputStream(filePath);
fileOutputStream = new FileOutputStream(outPath,true);
//读取图片文件
int read = 0;
while ((read = fileInputStream.read(bytes)) !=-1){
//每次读取完成后,输出读取到的字节数据追加到文件中,循环,直至读取完成
fileOutputStream.write(bytes,0,read);
}
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
fileInputStream.close();
fileOutputStream.close();
} catch (IOException exception) {
exception.printStackTrace();
}
}
}
3.FileReader
输入流 --> 字符流
/**
* 读取D盘根目录下的story.txt的信息并输出
*/
public static void testFileReader1(){
String filePath = "D:\\story.txt";
FileReader fileReader = null;
try {
fileReader = new FileReader(filePath);
int read = 0;
while ((read = fileReader.read()) != -1){
//循环读取文本,int类型转换为char类型,由于按字符读取,不用考虑中英文问题
System.out.print((char) read);
}
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
fileReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 读取D盘根目录下的story.txt的信息并输出,使用char数组,优化效率
*/
public static void testFileReader2(){
String filePath = "D:\\story.txt";
FileReader fileReader = null;
try {
fileReader = new FileReader(filePath);
int read = 0;
char[] buf = new char[15];
while ((read = fileReader.read(buf)) != -1){
//循环读取文本,int类型转换为char类型,由于按字符读取,不用考虑中英文问题
System.out.print(new String(buf,0,read));
}
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
fileReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
4. FileWriter
输出流 --> 字符流
注意:此IO流使用完成后,必须调用close()或flush()方法,否则内容不会写入到文件中
package com.lzl.io.writer;
import java.io.FileWriter;
import java.io.IOException;
/**
*
* 输出流 --> 字符流 --> FileWriter
*
* @author liangzilong
* @date 2022-9-26 10:30
*/
public class TestFileWriter {
/**
* 向D盘根目录下创建note.txt文件并写入内容
* @param args
*/
public static void main(String[] args) {
String filePath = "d:\\note.txt";
FileWriter fileWriter = null;
try {
//默认覆盖文件内容模式创建
fileWriter = new FileWriter(filePath);
//单个字符创建方式
fileWriter.write('H');
//字符数组创建
char[] chars = {'a','b','c'};
fileWriter.write(chars);
//字符数组指定偏移量创建
fileWriter.write(chars,0,2);
//字符串创建
fileWriter.write("哈哈哈");
//字符串指定偏移量创建
fileWriter.write("哈酒地哦啊接搜到怕是都怕卡萨丁",0,5);
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
//使用完成后必须调用close()或flush()方法,否则内容不会写入到文件中
fileWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
}
System.out.println("程序结束。。。");
}
}
2.节点流和处理流
节点流:从一个特定数据源读写数据的流,如FileReader、FileWriter等
处理流(也叫包装流):连接已存在的流,可以是节点流,也可以是处理流,为程序提供更强大的读写功能,如BufferReader、BufferWriter
节点流和处理流的区别与联系:
- 节点流是底层流/低级流,直接和数据源相接
- 处理流(包装流)是高级流,不与数据源相接,对节点流或处理流进行包装调用;处理流的实现,使用了修饰器设计模式
处理流的优点:
- 性能的提高:主要以增加缓冲的方式来提高输入输出的效率
- 操作的便捷:处理流可能提供了一系列便捷的方法来一次输入输出大批量的数据,使用更加灵活方便
3.处理流(包装流)
1.BufferReader
package io.reader;
import java.io.BufferedReader;
import java.io.FileReader;
/**
* 处理流 --> 输入流 --> 字符流 BufferReader
*
*/
public class TestBufferReader {
/**
* 使用BufferReader读取d盘根目录下 a.txt文本中的内容
*/
public static void main(String[] args) throws Exception {
String filePath = "d:\\a.txt";
BufferedReader bufferedReader = new BufferedReader(new FileReader(filePath));
//按行读取数据,读取不到数据时,返回null
String data= "";
while ((data = bufferedReader.readLine()) != null){
System.out.println(data);
}
//关闭流,对于处理流(包装流)而言,关闭外层流即可,节点流会自动关闭
bufferedReader.close();
}
}
2.BufferWriter
package io.writer;
import java.io.BufferedWriter;
import java.io.FileWriter;
/**
* 处理流 --> 输出流 --> 字符流
*/
public class TestBufferWriter {
/**
* 使用BufferWriter向d盘根目录下的a.txt文档中写入指定内容
*/
public static void main(String[] args) throws Exception{
String filePath = "d:\\a.txt";
//创建处理流对象,此处如果需要追加文件内容时,修改节点流的追加属性即可
BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(filePath));
/*
南有樛木,葛藟累之。乐只君子,福履绥之。
南有樛木,葛藟荒之。乐只君子,福履将之。
南有樛木,葛藟萦之。乐只君子,福履成之
*/
bufferedWriter.write("南有樛木,葛藟累之。乐只君子,福履绥之。");
//插入一个换行(使用不同的系统)
bufferedWriter.newLine();
bufferedWriter.write("南有樛木,葛藟荒之。乐只君子,福履将之。");
bufferedWriter.newLine();
bufferedWriter.write("南有樛木,葛藟萦之。乐只君子,福履成之");
bufferedWriter.newLine();
//关闭流
bufferedWriter.close();
}
}
文件拷贝:使用处理流BufferReader、BufferWriter实现一个文本文件拷贝
package io.writer;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
/**
* 测试使用BufferReader、BufferWriter实现数据拷贝
* 说明:以上两个处理流(包装流)是字符流,拷贝的文件应是文本文件;如果拷贝二进制文件,可能会导致文件损毁
*/
public class TestCopyByBuffer {
public static void main(String[] args) throws Exception{
String filePath = "D:\\a.txt";
String filePath2 = "D:\\a2.txt";
//创建处理流(包装流)读取对象
BufferedReader bufferedReader = new BufferedReader(new FileReader(filePath));
//创建处理流(包装流)写出对象
BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(filePath2,true));
//定义一个每次读取字节长度的数组
char[] chars = new char[1024];
//读取到的数据
String data = "";
while ((data = bufferedReader.readLine()) != null){
//写出读取到的实际数据
bufferedWriter.write(data);
//换行
bufferedWriter.newLine();
}
//关闭流
bufferedReader.close();
bufferedWriter.close();
System.out.println("程序结束。。。");
}
}
3.BufferedInputStream、BufferedOutputStream
package io.outStream;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
/**
* 测试通过BufferedInputStream、BufferedOutputStream处理流实现二进制文件拷贝
*/
public class TestCopyFileByBuffer {
public static void main(String[] args)throws Exception {
//文件读取、输出路径
String filePath = "D:\\迅雷下载\\XMPSetup-xl11.exe";
String outPath = "D:\\迅雷下载\\XMPSetup-xl112.exe";
//创建处理流读取对象
BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(filePath));
//创建处理流输出对象
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(outPath, true));
//每次读取数据的字节数组
byte[] bytes = new byte[1024];
//实际读取到的数据长度
int len = 0;
while ((len = bufferedInputStream.read(bytes)) != -1){
bufferedOutputStream.write(bytes,0,len);
}
bufferedInputStream.close();
bufferedOutputStream.close();
System.out.println("程序结束。。。。。");
}
}
3.对象处理流
当需求不仅需要保存数据,还需要保存数据类型时,需要使用对象处理流(ObjectInputStream、ObjectOutputStream)
序列化与反序列化:
-
序列化:将对象的数据和数据类型保存至数据源中的操作,称为序列化
-
反序列化:将数据源中的数据类型和数据还原为对象的操作,称之为反序列化
-
实现序列化:一个对象如果想要被序列化,必须实现Serializable或Externalizable接口,由于Serializable接口是标记接口,建议使用
-
标记接口:没有定义任何方法和常量的接口称之为标记接口
1.ObjectOutputStream
package io.outStream;
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
/**
* 测试通过ObjectOutputStream处理流序列化数据类型、对象
*/
public class TestObjectOutputStream {
public static void main(String[] args)throws Exception {
//此处文件命名不能以.java文件作为后缀
String outputPath = "d:\\a.dat";
//创建对象处理流对象
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(outputPath,true));
//写入数据
//Integer包装类实现了Serializable接口,可以被直接序列换,其他实现接口的包装类,同理
objectOutputStream.writeInt(100);
objectOutputStream.writeDouble(0.01);
objectOutputStream.writeUTF("哈哈哈哈");
//序列化自定义对象
objectOutputStream.writeObject(new Dog("大黄",3));
//关闭流
objectOutputStream.close();
System.out.println("序列化完成");
}
}
//建议将此类建在测试类同一个包下,并作为公开类
class Dog implements Serializable{
private String name;
private Integer age;
public Dog() {
}
public Dog(String name, Integer age) {
this.name = name;
this.age = age;
}
}
2.ObjectInputStream
package io.inputStream;
import io.outStream.Dog;
import java.io.FileInputStream;
import java.io.ObjectInputStream;
/**
* 测试通过ObjectInputStream反序列化数据、对象
*/
public class TestObjectInputStream {
public static void main(String[] args) throws Exception {
String filePath = "d:\\a.dat";
/**
* 对象反序列化注意事项:
* 1.反序列化的顺序需要和序列化的顺序保持一致
* 2.反序列化的对象需要公开,在序列化和反序列化时都能被访问到
*
*/
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filePath));
System.out.println(ois.readInt());
System.out.println(ois.readDouble());
System.out.println(ois.readUTF());
//反序列化对象
Dog dog =(Dog) ois.readObject();
System.out.println(dog);
//关闭流
ois.close();
}
}
3.序列化与反序列化注意事项
- 读写顺序要一致
- 要求序列化与反序列化对象,需要实现Serializable接口
- 序列化的类中建议添加SerialVersionUID,为了提高版本的兼容性
- 序列化对象时,默认将里面所有属性都进行序列化,但除了static或transient修饰的成员
- 序列化对象时,要求对象中的参数属性类型也都实现了序列化接口,否则会报错
- 序列化具备可继承性,也就是说如果某类可序列化,那么它的所有子类都可以被序列化
//序列化的版本号,可以提高兼容性
private static final long serialVersionUID = 1L;
//static、transient修饰的不会被序列化
private static String nation;
private transient String color;
4.标准输入输出流
类型 默认设备
System.in 标准输入 InputStream 键盘
System.out 标准输出 PrintStream 显示器
package io.standard;
import java.util.Scanner;
public class TestStandard {
public static void main(String[] args) {
//public final static InputStream in = null;
//System.in 的编译类型是 InputStream
//System.out 的运行类型是 BufferedInputStream
//表示的是标准输入 键盘
System.out.println(System.in.getClass());
//public final static PrintStream out = null;
//System.out 的编译运行类型都是 PrintStream
//表示的是标准输出 屏幕
System.out.println(System.out.getClass());
//用户交互的案例解读
//通过扫描传入的标准输入流,获取键盘输入信息,再通过标准输出流,显示在显示器上
Scanner scanner = new Scanner(System.in);
System.out.println("请输入:");
String next = scanner.next();
System.out.println(next);
}
}
5.转换流
package io.transForm;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
/**
* 测试字符流读取文件产生的乱码
*/
public class TestErrorCode {
public static void main(String[] args) throws IOException {
//读取d盘下hello.txt文件中的内容
String filePath = "D:/hello.txt";
BufferedReader reader = new BufferedReader(new FileReader(filePath));
//默认读取的编码格式是utf-8,当文件为其他编码格式时会产生乱码
String s = reader.readLine();
System.out.println(s);
//关闭流
reader.close();
}
}
1.InputStreamReader
package io.transForm;
import java.io.*;
/**
* 测试使用InputStreamReader 解决读取文件的乱码问题
*/
public class TestInputStreamReader {
public static void main(String[] args) throws IOException {
String filePath = "d:/hello.txt";
//读取文件
FileInputStream fileInputStream = new FileInputStream(filePath);
//转换字节流
InputStreamReader streamReader = new InputStreamReader(fileInputStream, "gbk");
//转换为字符流
BufferedReader bufferedReader = new BufferedReader(streamReader);
//读取数据
String s = bufferedReader.readLine();
System.out.println(s);
//关闭流
bufferedReader.close();
}
}
2.OutputStreamWriter
package io.transForm;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
/**
* 测试使用转换流 OutputStreamWriter 指定编码格式输出文件
*/
public class TestOutputStreamWriter {
public static void main(String[] args) throws IOException {
//文件路径
String filePath = "d:\\osw.txt";
//编码格式
String charset = "utf8";
//使用转换流创建文件
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(new FileOutputStream(filePath), charset);
//查看创建的文件格式的编码,会根据输出的编码改变
outputStreamWriter.write("hello,小白!");
System.out.println("文件创建成功!");
//关闭流
outputStreamWriter.close();
}
}
6.打印流
1.PrintStream
package io.print;
import java.io.IOException;
import java.io.PrintStream;
import java.nio.charset.StandardCharsets;
/**
* 测试使用 PrintStream 打印流输出数据
*/
public class TestPrintStream {
public static void main(String[] args) throws IOException {
//获取打印流对象
PrintStream out = System.out;
//默认的输出位置是控制台
out.println("hello,world!");
out.write("梁子龙".getBytes(StandardCharsets.UTF_8));
//指定输出位置
System.setOut(new PrintStream("d:/f1.txt"));
System.out.println("hello,打印流数据~");
//关闭流
out.close();
}
}
2.PrientWriter
package io.print;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
/**
* 测试使用字符打印流PrintWriter输出数据
*/
public class TestPrintWriter {
public static void main(String[] args) throws IOException {
//根据构造器中传入的输出对象不同,数据输出的位置不相同
//传入标准输出流对象,内容显示在控制台中
PrintWriter printWriter = new PrintWriter(System.out);
printWriter.println("hello,World!");
//传入文件输出字符流,内容输出在文件中
PrintWriter printWriter1 = new PrintWriter(new FileWriter("d:/a1.txt"));
printWriter1.write("hello,字符打印流数据");
//关闭流
//此处需要注意,无论构造器中传入的是字节流,还是字符流,此处都需要关闭流,数据才会输出,并且与输出位置无关
//底层真正调用,同FileWriter,close时才会调用writer方法
printWriter.close();
printWriter1.close();
}
}
7.Properties类
这个类出现的目的,就是为了简化对配置文件的读写操作
package io.print;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
/**
* 测试使用字符打印流PrintWriter输出数据
*/
public class TestPrintWriter {
public static void main(String[] args) throws IOException {
//根据构造器中传入的输出对象不同,数据输出的位置不相同
//传入标准输出流对象,内容显示在控制台中
PrintWriter printWriter = new PrintWriter(System.out);
printWriter.println("hello,World!");
//传入文件输出字符流,内容输出在文件中
PrintWriter printWriter1 = new PrintWriter(new FileWriter("d:/a1.txt"));
printWriter1.write("hello,字符打印流数据");
//关闭流
//此处需要注意,无论构造器中传入的是字节流,还是字符流,此处都需要关闭流,数据才会输出,并且与输出位置无关
//底层真正调用,同FileWriter,close时才会调用writer方法
printWriter.close();
printWriter1.close();
}
}
由于不同版本的IDEA编码配置,保存到配置文件中的汉字可能会显示汉字,也可能会显示Unicode编码
学习视频地址:韩顺平教育