Java输入与输出流 知识点总结 想学IO看这里就对了!

文章说明

我一开始看书是《Java核心技术》,越看越晕乎。去看了一些视频后,决定写一篇学习总结,便于以后的复习。如有不足,还请指出。👍

一、File

作用
Java 使用 File 类来直接处理文件和文件系统,是文件和目录路径名的抽象表示。如果想要访问文件的属性或者目录结构,比如文件的大小、名称或者子目录结构等,可以选择File对象,但是File 类没有指定信息怎样从文件读取或向文件存储

1.常用构造方法

File(File parent, String child) 
从父抽象路径名和子路径名字符串创建新的 File实例。  
File(String pathname) 
通过将给定的路径名字符串转换为抽象路径名来创建新的 File实例。  
File(String parent, String child) 
从父路径名字符串和子路径名字符串创建新的 File实例。  

比如File file=new File("E:"+File.separator+"java");,表示E盘下名为java的文件夹,这里要说明一下路径的表示方法Windows系统):

  1. \\作为分隔符,比如“E:\\java”,用两个\的原因是反斜杠字符在Java字符串中是转义字符;
  2. /作为分隔符,比如E:/java
  3. File.separator作为分隔符,这样做的好处就是使程序具有可移植性,平常练习用前两种方便一些。

2.常用类库方法

方法解释
boolean createNewFile()创建由此抽象路径名命名的文件
boolean exists()测试此抽象路径名表示的文件或目录是否存在
String getPath()将此抽象路径名转换为路径名字符串
long length()返回由此抽象路径名表示的文件的大小
String[] list()返回一个字符串数组,命名由此抽象路径名表示的目录中的文件和目录
File[] listFiles()返回一个抽象路径名数组,表示由该抽象路径名表示的目录中的文件
boolean mkdir()创建由此抽象路径名命名的目录

备注
createNewFile()mkdir()虽然都是创建,但二者却有天壤之别,前者创建文件,后者创建文件夹。

3.示例

遍历一个目录下的所有一级子目录和文件

public void listDirectory(File file) throws IOException {
		//异常判断
        if(!file.exists())
            throw new IOException("目录"+file+"不存在");
        if(!file.isDirectory())
            throw new IOException(file+"不是目录");
        //list()方法的实际应用
        String[] fileNames=file.list();
        for(String each:fileNames)
            System.out.println(each);
    }

如果在一级子目录的基础上还想要遍历所有子目录,要运用listFiles(),得到所有File对象,再挨个儿判断,如果是目录,就递归调用本方法。

二、文件字节流

作用
字节流可以用来读写任何类型的文件,比如图片、视频、文本文件等,操作单位是byte

1.FileInputStream

1.1 常用构造方法
FileInputStream(File file) 
通过打开与实际文件的连接创建一个 FileInputStream ,该文件由文件系统中的 File对象 file命名。  
FileInputStream(String name) 
通过打开与实际文件的连接来创建一个 FileInputStream ,该文件由文件系统中的路径名 name命名。 
1.2 常用类库方法
方法解释
int available()返回从此输入流中剩余可以读取(或跳过)的剩余字节数的估计值
int read()从该输入流读取一个字节的数据
int read(byte[] b)从该输入流读取最多 b.length个字节的数据为字节数组
void close()关闭此文件输入流并释放与流相关联的任何系统资源

备注

  1. read()读取数据时,其实有一个指向字节的指针,当读到最后时,返回-1,缺点是一次只能读取一个字节,效率低下;
  2. read(byte[] b) 返回的是一次读取字节的长度,读到最后时同样返回-1。需要注意的是每次调用该方法,如果参数是同一个数组,都是从b[0]开始读入到b中;
  3. 每次创建一个流对象之后,如果非空,用完之后都要调用close(),便于释放有限的操作系统资源。
1.3 示例

读取文件中的全部字节

		//假设目录下存在文件
     public static void main(String[] args) {
        FileInputStream fileIn = null;
        byte[] data=new byte[10];
        try {
            fileIn = new FileInputStream("E:/IO练习/字节输入流.txt");
            //适合读取小文件,因为byte数组不能过大
            byte[] data=new byte[fileIn.available()];
            fileIn.read(data);
            System.out.print(new String(data));
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fileIn != null) {
                try {
                    fileIn.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

2.FileOutputStream

2.1 常用构造方法
FileOutputStream(File file) 
创建文件输出流以写入由指定的 File对象表示的文件。  
FileOutputStream(File file, boolean append) 
创建文件输出流以写入由指定的 File对象表示的文件。   
FileOutputStream(String name) 
创建文件输出流以指定的名称写入文件。  
FileOutputStream(String name, boolean append) 
创建文件输出流以指定的名称写入文件。  

备注

  1. boolean append决定了是否以追加的方式对文件进行写入。如果没有这个参数,相当于第一次创建输出流进行写入时,先对原来文件的内容进行清空再写入
  2. 创建FileOutputStream对象时,如果对应的目录文件不存在,会自动创建。
2.2 常用类库方法
方法解释
void write(byte[] b)将 b.length个字节从指定的字节数组写入此文件输出流
void write(byte[] b, int off, int len)将 len字节从位于偏移量 off的指定字节数组写入此文件输出流
void write(int b)将指定的字节写入此文件输出流

应用
向文件写入的关键代码如下:

fileOut = new FileOutputStream("E:/IO练习/字节输入流.txt",true);
byte[] data = {'x', 'z', 'y'};
fileOut.write(data);
fileOut.flush();
2.3 示例

复制文件

public static void main(String[] args) {
	FileInputStream fileIn = null;
        FileOutputStream fileOut = null;
        try {
            fileIn = new FileInputStream("E:/IO练习/字节输入流.txt");
            //不用追加的原因在于每次写入都是同一个输出流,不会清空重写
            fileOut = new FileOutputStream("E:/IO练习/字节输出流.txt");
            byte[] data=new byte[2];
            int count=0;
            while((count=fileIn.read(data))!=-1){
                fileOut.write(data,0,count);
            }
            fileOut.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
        	//分开捕获异常,使得二者相互独立
            if (fileIn != null) {
                try {
                    fileIn.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (fileOut != null) {
                try {
                    fileOut.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

容易产生疑问的地方
.txt文件为例,如果文件中有汉字,发现复制过去的文件可能出现乱码问题。原因在于编码方式,比如源文件是GBK编码,Java工程的编码方式是UTF-8,就会产生这种问题,改正的话让二者编码方式一致就好。

三、文件字符流

作用
操作文件,只对普通文本进行读写,如.txt、.java文件,注意word不是普通文件,平时判断的依据可以是能否用记事本打开

1.FileReader

备注
InputStreamReader的子类。

1.1 常用构造方法
FileReader(File file) 
创建一个新的 FileReader ,给出 File读取。    
FileReader(String fileName) 
创建一个新的 FileReader ,给定要读取的文件的名称。  
1.2 常用类库方法
方法解释
void close()关闭流并释放与之相关联的任何系统资源
String getEncoding()返回此流使用的字符编码的名称
int read()读一个字符
int read(char[] cbuf, int offset, int length)将字符读入数组的一部分

备注

方法的返回值可以参考FileInputStream~

1.3 示例

读取文件中的全部字节

public static void main(String[] args) {
	    FileReader fRead = null;
        try {
            //假设文件存在
            fRead = new FileReader("E:/IO练习/字符输入流.txt");
            char[] data = new char[16];
            int count = 0;
            while ((count = fRead.read(data)) != -1)
                System.out.print(new String(data, 0, count));
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (fRead != null) {
                    fRead.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
}

2.FileWriter

备注
OutputStreamWriter的子类。

2.1 常用构造方法
FileWriter(File file) 
给一个File对象构造一个FileWriter对象。  
FileWriter(File file, boolean append) 
给一个File对象构造一个FileWriter对象。    
FileWriter(String fileName) 
构造一个给定文件名的FileWriter对象。  
FileWriter(String fileName, boolean append) 
构造一个FileWriter对象,给出一个带有布尔值的文件名,表示是否附加写入的数据。  
2.2 常用类库方法
方法解释
void close()关闭流,先刷新
void flush()刷新流
String getEncoding()返回此流使用的字符编码的名称
void write(char[] cbuf, int off, int len)写入字符数组的一部分
void write(int c)写一个字符
void write(String str, int off, int len)写一个字符串的一部分

应用
向文本文件写入字符的关键代码如下:

FileWriter fWrite=new FileWriter("E:/IO练习/字符输出流.txt");
char[] data = {'字','符','流'};
fWrite.write(data);
fWrite.flush();
2.3 示例

复制普通文本文件的关键代码

FileReader fRead=new FileReader("E:/IO练习/字符输入流.txt");
FileWriter fWrite=new FileWriter("E:/IO练习/字符输出流.txt");
char[] data=new char[1024*512];
int count=0;
while((count=fRead.read(data))!=-1)
	fWrite.write(data,0,count);
fWrite.flush();

四、缓冲流

作用
前面介绍的几种IO流都是直接进行内存和硬盘间的交互,读取效率较低,而缓冲流内部实现了缓冲机制—

  • 进行读取时,先储存到缓冲区,利用时直接从缓冲区读到内存。当缓冲区为空时,会从硬盘中入一个新的数据块;
  • 进行写入时,先储存到缓冲区中,直到缓冲区满才会将数据到送到硬盘。当然可以运用flush()人为地清空缓冲区。

1.BufferedReader

作用
从字符输入流读取文本,缓冲字符,以提供字符,数组和行的高效读取。

1.1 常用构造方法
BufferedReader(Reader in) 
创建使用默认大小的输入缓冲区的缓冲字符输入流。  
BufferedReader(Reader in, int sz) 
创建使用指定大小的输入缓冲区的缓冲字符输入流。  

备注
Reader是抽象类,无法被实例化,所以我们传入的时候为了方便,可以传入Reader的子类对象,比如FileReader类的对象。

1.2 常用类库方法
方法解释
void close()关闭流并释放与之相关联的任何系统资源
String readLine()读一行文字

备注

  1. 调用close()时,不仅会关闭该缓冲流(包装流),而且会关闭构造时作为参数的流(节点流);
  2. readLine()不会读取每行的换行符,返回的是读取到的字符串,没有字符可读时,会返回NULL
1.3示例

读取文件的全部字符

	public static void main(String[] args) {
        BufferedReader bRead = null;
        try {
            FileReader fRead = new FileReader("E:/IO练习/缓冲输入流.txt");
            bRead = new BufferedReader(fRead);
            String lineData;
            while ((lineData = bRead.readLine()) != null)
                System.out.println(lineData);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (bRead != null) {
                    bRead.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

2.BufferedWriter

作用
将文本写入字符输出流,缓冲字符,以提供单个字符,数组和字符串的高效写入。

2.1 常用构造方法
BufferedWriter(Writer out) 
创建使用默认大小的输出缓冲区的缓冲字符输出流。  
BufferedWriter(Writer out, int sz) 
创建一个新的缓冲字符输出流,使用给定大小的输出缓冲区。  

备注
Writer是抽象类,无法被实例化,所以我们传入的时候为了方便,可以传入Writer的子类对象,比如FileWriter类的对象。

2.2 常用类库方法
方法解释
void close()关闭流并释放与之相关联的任何系统资源
void flush()刷新流,即清空缓冲区
void newLine()写入行分隔符

备注

  1. 调用close()时,不仅会关闭该缓冲流(包装流),而且会关闭构造时作为参数的流(节点流);
  2. newLine()向输出流中写入一个行结束标志,它不是简单地换行符 \n 或回车符\r,而是系统定义的行隔离标志(line separator)

3.小结

以上两种缓冲流都是以字符为单位进行操作的,还有两种字节缓冲流—BufferedInputStreamBufferedOutputStream(可通过后缀进行区分),最好参照文件字节流进行学习,不会有太大差别,这里就不过多介绍~

五、数据流

作用
数据流除了可处理字节和字节数组外,还可以处理 int、float、boolean 等基本数据类型,也就是对二进制数据进行读写。

1.DataOutputStream

1.1 常用构造方法
DataOutputStream(OutputStream out) 
创建一个新的数据输出流,以将数据写入指定的底层输出流。
1.2 常用类库方法
方法解释
int size()返回计数器的当前值 written ,到目前为止写入此数据输出流的字节数
void writeInt(int v)将底层输出流写入 int作为四字节
void writeUTF(String str)使用 modified UTF-8编码以机器无关的方式将字符串写入基础输出流

备注

  1. DataOutputStream有一个实例域written ,记录了到目前为止写入数据输出流的字节数;
  2. 可以按顺序将数据和数据类型一并写入,但数据是对外不可见的,读取时也要按照写入的顺序依据数据类型读,否则得不到想要的结果,就好像加密文件一样。
    应用
	//仅展示关键代码
	FileOutputStream fOut=new FileOutputStream("E:/IO练习/数据输出流.txt");
    DataOutputStream dOut=new DataOutputStream(fOut);
    dOut.writeUTF("你好");
    dOut.writeByte(1);
    dOut.writeBoolean(true);

结果我打开E:/IO练习/数据输出流.txt是这样的:你好

2.DataInputStream

2.1 常用构造方法
DataInputStream(InputStream in) 
创建使用指定的底层InputStream的DataInputStream。
2.2 常用类库方法
方法解释
boolean readBoolean()读取一个输入字节,并返回 true如果该字节不为零, false如果该字节是零
byte readByte()读取并返回一个输入字节
String readUTF()读取已使用 modified UTF-8格式编码的字符串

备注
最好是从利用DataOutputStream进行写入的文件中进行读取。
应用

	FileInputStream fIn=new FileInputStream("E:/IO练习/数据输出流.txt");
	DataInputStream dIn=new DataInputStream(fIn);
    System.out.print(dIn.readUTF()+dIn.readByte()+dIn.readBoolean());

控制台输出结果是这样的:你好1true,与存入一致。

六、标准输出流

1.PrintStream

作用
我们常用的System.out就属于PrintStream,能够方便地打印各种数据值的表示。

1.1 常用构造方法
PrintStream(File file) 
使用指定的文件创建一个新的打印流,而不需要自动换行。   
PrintStream(OutputStream out) 
创建一个新的打印流。  
PrintStream(String fileName) 
使用指定的文件名创建新的打印流,无需自动换行。  
1.2 常用类库方法

我们最开始学习Java运用的print()、printf()、println()方法就源于这里,由于方法过多,这里就不展开叙述了,需要注意的是我们不需要手动关闭close()方法。

1.3 示例

修改System.out输出方向

	//仅展示关键代码
	PrintStream pStream=new PrintStream("E:/IO练习/标准输出流.txt");
	System.setOut(pStream);
	System.out.println(12);

这样一来,12不会再输出到控制台,而是打印到了文件中,至于怎么修改回来,目前我的方法就是删除System.setOut(pStream);这一代码…

七、对象流

作用
用来操作Java中的对象,完成对象在内存和硬盘间的传递。

1、ObjectOutputStream

注释
Java对象的原始数据类型和图形写入OutputStream,过程是把一个对象分成若干个序列,再依次写入,称为序列化(Serialize)

  • 只有支持java.io.Serializable接口的对象才能写入流中;
  • java.io.Serializable是标志性接口,不含方法,用来给虚拟机提供参考,当虚拟机看到此接口时,会为该类自动生成一个序列化版本号。
  • 序列化版本号是用来区分类的,如果之后修改了类的源代码,导致序列号版本号不同,反序列化失败。为了避免这种错误,可以手动生成,例如private static final long serialVersionUID = 1336136411266510208L;
1.1 常用构造方法
ObjectOutputStream(OutputStream out) 
创建一个写入指定的OutputStream的ObjectOutputStream。 
1.2 常用类库方法
方法解释
void writeObject(Object obj)将指定的对象写入ObjectOutputStream
1.3 示例
//创建一个商品类
public class Item implements Serializable {
    private String name;
    private int price;
    Item(String name,int price){
        this.name=name;
        this.price=price;
    }
    //重载toString()方法,便于验证
    @Override
    public String toString() {
        return "Item:"+'\n'+"name-"+name+'\n'+"price-"+price;
    }
}
//添加类对象到文件中
public class TestItem {
    public static void main(String[] args) {
       try {
            FileOutputStream fOut=new FileOutputStream("E:/IO练习/对象输出流.txt");
            ObjectOutputStream oOUt=new ObjectOutputStream(fOut);
            Item aItem=new Item("菠萝",10);
            oOUt.writeObject(aItem);
            oOUt.flush();
            oOUt.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

打开文件之后是一堆乱码: sr From0To100.Item?e樨k焞 I priceL namet Ljava/lang/String;xp t 鑿犺悵

2、ObjectInputStream

注释
过程是把若干个序列重组成对象再进行读取,称为反序列化(Deserialize)

  • 只能从流中读取支持java.io.Serializablejava.io.Externalizable接口的对象
2.1常用构造方法
ObjectInputStream(InputStream in) 
创建从指定的InputStream读取的ObjectInputStream。 
2.2常用类库方法
方法解释
Object readObject()ObjectInputStream读取一个对象
2.3 示例
	//建立在上一个程序上的基础上,展示关键代码
	FileInputStream fIn=new FileInputStream("E:/IO练习/对象输出流.txt");
	ObjectInputStream oIn=new ObjectInputStream(fIn);
	Object goods=oIn.readObject();
	System.out.println(goods.toString());

输出结果为:

Item:
name-菠萝
price-10

备注

  • 要想对多个对象序列化,可将对象放在同一个集合中,因为集合也是一个对象,成功的前提是集合和集合中对象对应的类都要实现java.io.Serializable接口;
  • 如果不想让某一实例域序列化,可利用关键词transient(游离的),比如private transient int price,输出的结果中price=0,也就是默认值。

八、IO与Properties联合使用

  Properties类继承了Hashtable<Object,Object> ,可以利用输入流获取文件的信息。这里的文件比较特殊,格式都是key=value,称为配置文件。以.properties作为后缀的称为属性配置文件,通过Properties类可以获取其中的键值对,直接修改配置文件就起到了修改程序的作用。

	src/class.properties文件信息如下:
	classname=java.lang.String
	
    public static void main(String[] args) throws IOException {
        //新建文件输入流对象
        FileReader reader=new FileReader("src/class.properties");
        //新建Map集合
        Properties ppt=new Properties();
        //加载配置信息到Map集合中
        ppt.load(reader);
        //关闭流
        reader.close();
        //通过key获取value
        String s=ppt.getProperty("classname");
    }

总结

其实我几个月前已经学习过一次IO了,但是当时只是看了书没记笔记,现在回想起来发现全给忘记了,思绪比较混乱。现在通过这篇博客记录学习过程,以后复习时直接拿来看就好,顺便还能给自己增加访问量,一举两得😏
(欢迎评论区交流~)

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值