Java语言基础13—IO
Posted xuliang-daydayup
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java语言基础13—IO相关的知识,希望对你有一定的参考价值。
I/O(输入/输出)
参考资料:《Java从入门到精通》/明日科技编著. 4版. 北京:清华大学出版社,2016
一、流概述
- 流是一种有序的数据序列,根据操作的类型,可分为输入流和输出流。
- I/O(Input/Output,输入/输出)流提供了一条通道程序,可以用这条通道把源中的字节序列送到目的地。
- 虽然I/O流通常与磁盘有关,但是程序的源和目的地也可以是键盘、鼠标、内存或显示器窗口等。
Java由数据流处理输入/输出模式,程序从指向源的输入流中读取数据,数据源可以是文件、网络、压缩包或其他数据源,这个过程也称为数据的读取,如下图所示:
输出流是指向数据要到达的目的地,程序通过向输出流中写入数据,把信息传递到目的地,输出流的目的地可以是文件、网络、压缩包或其他输出目标,该过程也称为数据的写入,如下图所示:
总的来说,在Java程序中,要想从文件中读取数据或者将数据写入文件中,都需要在程序和文件之间建立一条数据的传输通道,这个通道就是输入/输出流。
- 当程序创建输入流对象时,Java会自动创建输入流通道;
- 当程序创建输出流对象时,Java会自动创建输出流通道。
二、输入/输出流
Java语言在java.io包中定义了很多处理各种输入/输出的类。
- 字节流
- 抽象类InputStream(字节输入流)是所有输入字节流类的父类;
- 抽象类OutputStream(字节输出流)是所有输出字节流类的父类;
- 字符流
- 抽象类Reader(字符输入流)是所有输入字符流类的父类;
- 抽象类Writer(字符输出流)是所有输出字符流类的父类;
字节流与字符流的区别:
简而言之,字节流在操作文件时不会用到缓存(内存),而是直接操作文件本身。而字符流在操作的时候使用了缓存区,而不是直接操作文件。例如,使用字符流执行写入数据操作,如果使用close()方法没有关闭输出流,则文件中看不到任何写入的内容,除非使用flush()方法强行将缓存数据写入目标文件。
本质上来说,一切都是字节流,其实没有字符流这个东西。字符只是根据编码集对字节进行解码的产物。
字节流:01010100010010100101
字符流:aah65Khannskm3bhbasv //本质上还是字节流
【技巧】:字节流可以处理所有数据文件,若处理的是纯文本数据,建议使用字符流。
1、输入流
(1)字节输入流
InputStream类是字节输入流的抽象类,是所有字节输入流的父类,其层次结构如下所示:
该类中所有方法遇到错误时都会引发IOException异常,下面是该类的一些常用方法:
- read ():从输入流中读取数据的下一个字节。
- read (byte[] b):从输入流中读入一定长度的字节,并以整数形式返回字节数。
- mark (int readlimit):在输入流的当前位置放置一个标记,readlimit参数告知此输入流在标记位置失效之前允许读取的字节数。
- reset ():将输入指针返回到当前所做的标记处。
- skip (long n):跳过输入流上的n个字符并返回实际跳过的字节数。
- close() :关闭此输入流,并释放与该流相关的所有系统资源。
说明:并不是所有的InputStream类的子类都支持InputStream中定义的所有方法,如skip()、mark()、reset()等方法只对某些子类有用。
(2)字符输入流
Java中的字符是Unicode编码,是双字节的,即一个字符占用两个字节。InputStream是用来处理字节的,并不适合处理字符文本。Java为字符文本的输入专门提供了一套单独的类Reader,但是Reader类并不是InputStream类的替换者,只是在处理字符串时简化了编程。
Reader类是字符输入流的抽象类,是所有字符输入流的父类。
2、输出流
(1)字节输出流
OutputStream类是字节输出流的抽象类,该类是所有表示字节输出流类的父类,其层次结构如下所示:
OutputStream类中的所有方法均返回void,在遇到错误时会引发IOException异常,下面对OutputStream类中的方法做简单的介绍:
- write (int b):将指定的字节写入此输出流。
- write (byte[] b):将b个字节从指定的byte数组写入此输出流。
- write (byte[] b, int off, int len):将指定byte数组中从偏移量off开始的len个字节写入此输出流。
- flush():彻底完成输出并清空缓存区。
- close():关闭输出流。
(2)字符输出流
Writer类是字符输出流的抽象类,此类为所有字符输出流类的父类,其层次结构如下所示:
三、File类
- File类是java.io包中唯一代表磁盘文件本身的对象。
- 可以通过调用File类中的方法,实现创建、删除、重命名文件等操作。
- File类的对象主要用来获取文件本身的一些信息,如文件所在的目录、文件的长度、文件的读写权限等,但是不能对文件执行读取或写入数据的操作。
- 数据流可以将数据写入到文件中,文件也是数据流最常用的数据媒介。
1、文件的创建与删除
可以使用File类创建一个文件对象。通常使用以下3种构造方法来创建文件对象:
- File (String pathname)
该构造方法通过将给定的路径名字符串,并转换为抽象路径名来创建一个新的File实例。语法格式如下:
new File(String pathname)
其中,pathname指路径名称(包含文件名),例如:
File file = new File("C:UsersXULIANGabc.txt")
- File (String parent, String child)
该构造方法根据定义的父路径和子路径字符串(包含文件名)创建一个新的File对象。语法格式如下:
new File (String parent, String child)
- parent:父路径字符串。例如,"C:Users"。
- child:子路径字符串。例如,"letter.txt"。
【问】那何为父路径与子路径呢?
这里举个例子,关于"C:UsersXULIANGDocuments"这个目录,"C:"是"C:Users"的父目录,"C:Users"是"C:UsersXULIANG"的父目录。简而言之,父目录就是当前目录的上级目录。
- File (File f, String child)
该构造方法根据parent抽象路径名和子路径字符串创建一个新的File对象。语法格式如下:
new File (File f, String child)
- f:父路径对象。例如,"D:/doc/"。
- child:子路径字符串。例如,"letter.txt"。
import java.io.File;
public class FileTest {
public static void main(String[] args) {
//创建文件对象,并指定文件名
File file = new File("test.txt");
//判断该文件是否存在
if (file.exists()){
//若存在,则删除文件
file.delete();
System.out.println("文件已删除");
}else {
//捕捉可能出现的异常
try {
//若不存在,则创建该文件
file.createNewFile();
System.out.println("文件已创建");
}catch (Exception e){
e.printStackTrace();
}
}
}
}
运行结果:
文件已创建
上述代码执行后,会在该项目所在的路径下创建一个名为"test.txt"的文本文件。
3、获取文件信息
File类提供了很多方法用于获取文件的一些信息,常用的方法如下所示:
方法 | 返回值 | 说明 |
---|---|---|
getName() | String | 获取文件的名称 |
canRead() | boolean | 判断文件是否可读 |
canWrite() | boolean | 判断文件是否可写 |
exits() | boolean | 判断文件是否存在 |
length() | long | 获取文件的长度(以字节为单位) |
下面通过实例来介绍使用方法来获取文件的信息:
import java.io.File;
public class FileTest2 {
public static void main(String[] args) {
File file = new File("test.txt");
if(file.exists()){
String name = file.getName();
long length = file.length();
Boolean hidden = file.isHidden();
System.out.println("文件的名字:" + name);
System.out.println("文件的长度:" + length);
System.out.println("是否为隐藏文件:" + hidden);
}
}
}
运行结果:
文件的名字:test.txt
文件的长度:0
是否为隐藏文件:false
四、文件输入/输出流
在程序的运行期间,大部分数据都保存在内存中进行操作,当程序结束后或关闭时,这些数据将消失。如果需要将数据永久保存,可使用文件输入/输出流与指定的文件建立连接,将需要的数据永久保存到文件中。
1、FileInputStream和FileOutputStream类
FileInputStream和FileOutputStream类都是用来操作磁盘文件。
(1)FileInputStream类
FileInputStream类继承于InputStream类,满足比较简单的文件读取要求。FileInputStream类常用的构造方法有两种:
- FileInputStream (String name)
- FileInputStream (File file)
第一种构造方法使用给定文件名name,创建一个FileInputStream对象。第二种构造方法使用File对象创建FileInputStream对象,该构造方法允许在把文件连接输入流之前对文件作进一步分析。
(2)FileOutputStream类
FileOutputStream类与FileInputStream类具有相同的构造方法,创建一个FileOutputStream
对象时,可以指定不存在的文件名,但此文件不能是一个被其他程序打开的文件。
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class FileTest3 {
public static void main(String[] args) {
File file = new File("word.txt");
System.out.println("---------------写入操作----------------");
try{
FileOutputStream fileOutputStream = new FileOutputStream(file);
//字符串转化成字节码形式,采用Java平台默认字符集进行编码
byte[] bytes = "Java基础班学习".getBytes();
System.out.println(""Java基础班学习"字符串的字节码形式:");
//遍历数组
for (int i = 0; i < bytes.length; i++) {
System.out.print(bytes[i] + " ");
}
System.out.println("
总共 " + bytes.length + " 个字节");
//执行写入文件操作
fileOutputStream.write(bytes);
//关闭输出流
fileOutputStream.close();
}catch (IOException e){
e.printStackTrace();
}
System.out.println("---------------读取操作----------------");
try{
FileInputStream inputStream = new FileInputStream(file);
byte bytes[] = new byte[100];
//读取文件的数据内容,然后保存到bytes数组中,返回字节的个数
int str = inputStream.read(bytes);
System.out.println("该文件内容所占用的字节个数为:" + str);
/*
* 将数组中的字节进行解码,并转化为字符串
* 采用Java平台默认字符集进行解码
* 形参0表示起始索引位置,str表示截取长度
*/
String content = new String(bytes,0, str);
System.out.println("文件中的内容为:" + content);
//关闭输入流
inputStream.close();
}catch (IOException e){
e.printStackTrace();
}
}
}
运行结果:
---------------写入操作----------------
"Java基础班学习"字符串的字节码形式:
74 97 118 97 -27 -97 -70 -25 -95 -128 -25 -113 -83 -27 -83 -90 -28 -71 -96
总共 19 个字节
---------------读取操作----------------
该文件内容所占用的字节个数为:19
文件中的内容为:Java基础班学习
【注意】虽说Java在程序结束时自动关闭所有打开的流,但是当使用完流后,应该显示地关闭所有打开地流仍是一个良好的习惯,一个被打开的流有可能耗尽系统资源。
2、FileReader和FileWriter类
使用FileOutputStream类向文件中写入数据与FileInputStream类从文件中读取内容,都存在一点不足,即这两个类都只提供了对字节或字节数组的读取方法。由于汉字在文件中占两个字节,如果使用字节流,读取不好可能会出现乱码现象,此时采用字符流Reader或Writer类即可避免这种现象。
FileReader和FileWriter字符流对应了FileInputStream和FileOutputStream类。FileReader流顺序地读取文件,只要不关闭流,每次调用read()方法就会顺序地读取源中地内容,直到源的末尾或流被关闭。
五、带缓存的输入/输出流
- 缓存是I/O的一种性能优化。
- 缓存流为了I/O流增加了内存缓存区,有了缓存区,使得在流上执行skip()mark()和reset()方法都成为可能。
1、BufferedInputStream和BufferedOutputStream类
BufferedInputStream类可以对所有InputStream类进行缓存区的包装以达到性能优化的目的。BufferedInputStream类有两个构造方法:
- BufferedInputStream (InputStream in)
- BufferedInputStream (InputStream in, int size)
第一种形式的构造方法创建了一个带有32个字节的缓存流,第二种形式的构造方法按指定的大小来创建缓存区。BufferedInputStream读取文件的过程如下所示:
数据流 数据流 数据流
文件 -------> InputStream -------> BufferedInputStream -------> 目的地
使用BufferedOutputStream输出信息和OutputStream输出信息完全一样,只不过BufferedOutputStream有一个flush()方法将缓存区的数据强制写入文件中。BufferedOutputStream也有两个构造方法:
- BufferedOutputStream (InputStream in)
- BufferedOutputStream (InputStream in, int size)
第一种形式的构造方法创建了一个带有32个字节的缓存流,第二种形式的构造方法按指定的大小来创建缓存区。
【注意】flush()方法用于即使在缓存区没有满的情况下,也将缓存区的内容强制写入到外设,习惯上将这个过程称为刷新。
2、BufferedReader与BufferedWriter类
BufferedReader与BufferedWriter类分别继承于Reader和Writer类,这两个类同样具有内部缓存机制,并可以以行为单位进行输入/输出。
BufferedReader类常用的方法:
- read() 方法:读取单个字符。
- readLine()方法:读取一个文本行,并将其返回为字符串。若无数据可读则返回null。
BufferedWriter类的方法都返回void,常用的方法:
- write(String s, int off, int len)方法:写入字符串的某一个部分。
- flush()方法:刷新该流的缓存。
- newLine()方法:写入一个行分隔符。
在使用BufferedWriter类的write()方法时,数据并没有立刻被写入输出流,而是首先进入缓存区中。如果想立即将缓存区中的数据写入输出流时,必须调用flush()方法。
import java.io.*;
public class Student {
public static void main(String[] args) {
String content[] = {"你好吗","好久不见","记得常联系"};
File file = new File("word.txt");
try{
FileWriter fw = new FileWriter(file);
BufferedWriter bw = new BufferedWriter(fw);
for (int i = 0; i < content.length; i++) {
bw.write(content[i]); //将字符数组的元素写入磁盘文件
bw.newLine(); //写入行分隔符
}
bw.close(); //关闭BufferedWriter流
fw.close(); //关闭FileWriter流
}catch (IOException e){
e.printStackTrace();
}
try {
FileReader fr = new FileReader(file);
BufferedReader bufr = new BufferedReader(fr);
String str = null;
int i = 0;
//读取文件的一行,若不为null,则进入循环
while ((str = bufr.readLine()) != null){
i++;
System.out.println("第" + i + "行:" + str);
}
bufr.close(); //关闭BufferedReader流
fr.close(); //关闭FileReader流
}catch (IOException e){
e.printStackTrace();
}
}
}
运行结果:
第1行:你好吗
第2行:好久不见
第3行:记得常联系
以上是关于Java语言基础13—IO的主要内容,如果未能解决你的问题,请参考以下文章