目录
I/O就是Java可以读取磁盘中的文件,也可以将数据写到磁盘中的文件里
1 File类
java.io.File类表示磁盘上的文件或目录(文件夹)
1.1 特点
- 文件和目录都通过File类表示(目录实际上就是特殊的文件)
- File类提供了对文件和目录的基本操作,如查看文件名、文件大小、新建、删除文件等
- File类只能看文件的属性,而不能查看文件内容
1.2 构造方法
1.2.1 路径分类
路径分隔符:
Windows使用 “\” 反斜杠表示,Linux和MacOS使用 “/” 正斜杠表示。需要注意的是一个反斜杠在Java中代表转义符,例如 \n 表示回车,所以在Java中表示Windows路径时需要使用 “\ \” 或使用 “/” 表示路径分隔符
-
绝对路径:以根开始的路径
在Windows中根就是盘符,也就是平常说的C盘D盘。例如 D:\ \目录\ \文件名
在Linux和MacOS中根就用 “/” 表示。例如 /目录/文件名 -
相对路径:不是以根开始的路径,相对于某个路径的路径,例如 目录/文件名
. 表示当前目录, . . 表示上一级目录。Java中是相对于项目的根目录的路径
1.2.2 构造
直接new一个File对象即可,构造时参数为路径。需要注意的是这个路径所指向的文件、目录可以存在,也可以不存在,如果要判断文件是否存在可以调用exists()方法查看。创建一个File对象只是创建一个指向某个路径的对象,并不表示在硬盘中创建文件。
构造方法 | 作用 |
---|---|
File(String pathname) | 指定文件的路径 |
File(String parent, String child) | 指定父目录的路径和文件名 |
File(File parent , String child) | 指定父目录的路径(由File对象指定)和文件名 |
1.3 常用方法
- 访问属性:
方法 | 作用 |
---|---|
getName() | 文件名 |
gtePath() | 路径名(和创建对象时的参数一致) |
getAbsolutePath() | 绝对路径名 |
getParent() | 父目录(看创建对象时的参数,若没有写出则为null) |
getParentFile() | 父目录文件对象(返回File对象) |
length() | 文件大小(返回字节大小) |
lastModified() | 最后一次修改时间 |
exists() | 文件是否存在 |
canRead() | 是否可读 |
canWrite() | 是否可写(可通过右击文件选择属性,在其中可以修改文件是否可写可读) |
isFile() | 是否为普通文件(若文件不存在则为false) |
isDirectory() | 是否为目录 |
isHidden() | 是否为隐藏文件 |
- 文件目录的操作:
方法 | 作用 |
---|---|
createNewFile() | 创建空文件 |
mkdir() | 创建目录,如果父目录不存在则会创建失败 |
mkdirs() | 创建目录,如果父目录不存在会一同创建 |
renameTo(File dest) | 修改文件名,新名字用File对象传入 |
delete() | 删除文件 |
list() | 获取目录下的所有文件和目录名称,返回一个字符串数组 |
listFile() | 获取目录下的所有文件和目录对象,返回一个File数组 |
1.4 文件过滤
使用方法list(FilenameFilter filter)实现过滤,FilenameFilter是文件名字过滤接口,实现其中的accepty(File dir, String name)方法自定义过滤规则便可进行过滤
File a = new File("d:\\java") ;
String[] list = a.list(new FilenameFilter() {
public boolean accept(File dir, String name) {
if(name.endsWith(".txt")) //自定义过滤规则
return true ;
return false;
}
}) ;
2 IO流
- I :input,输入
- O :output,输出
- 流:stream,可理解为一组有顺序的、有起点和终点的动态数据集合。数据传输通过流的形式传输,例如将磁盘中的文件读取到Java程序中,那么起点就是磁盘文件,终点就是Java程序,数据按这个方向不断流动。文件是数据在磁盘中的静态存储,而流是数据在传输时的动态形态
通过IO流可以实现文件的输入输出功能(用于对文件进行读写操作)
2.1 文件的分类
- 文本文件 : 由字符组成的文件,可以使用记事本编辑的文件(.txt , .java)
- 二进制文件: 除了文本文件以外的所有文件都属于二进制文件(.jpg)
2.2 流的分类
- 按流的方向分(站在Java程序的角度看)
名字 | 作用 |
---|---|
输入流 | 读取数据,从磁盘文件读取数据到Java程序(父类InputStream、Reader) |
输出流 | 用于写出数据,将Java程序中的数据写出到磁盘文件中(父类OutputStream、Writer) |
- 按流中数据的单位分
一个英文字符占1个字节,一个汉字占2个字节(GBK)或3个字节(UTF-8)
名字 | 作用 |
---|---|
字节流byte | 所操作的最小数据单元为字节(父类InputStream、OutputStream) |
字符流char | 所操作的最小数据单位为字符(父类Reader、Writer) |
- 按数据的来源分
名字 | 作用 |
---|---|
节点流 | 直接对数据源进行操作,如操作文件 |
包装流 | 对一个节点流进行操作 |
3 字节流
3.1 简介
- InputStream是字节输入流的顶层父类,常用子类有FileInputStream、ByteArrayInputStream、ObjectInputStream
- OutputStream是字节输出流的顶层父类,常用子类有FileOutputStream、ByteArrayOutputStream、ObjectOutputStream
3.2 文件输入输出流
- FileInputStream:文件输入流,以字节为单位,从文件中读取数据
方法 | 作用 |
---|---|
构造方法 | 参数可直接传入文件路径,或者传入一个File对象 |
read() | 读取一个字节,返回int类型的字节值,每调用一次就往后读取一次,如果到文件末尾则返回-1 |
read(byte [] b) | 根据传入的字节数组控制每次读取的数据长度(b.length),返回每次读取字节数,如果到文件末尾则返回-1 |
close() | 关闭输入流 |
available() | 流中可读取的字节数 |
需要注意的点:
- 字节流读取数据是按字节读取,那么在读取中文时可能会出现乱码
- 只要程序打开了外部资源(文件、数据库、网络连接),使用完后一定要关闭资源,所以关闭资源一般要放在finally中
- 在使用close()方法之前一定要保证对象不为空,否则会出现空指针异常
- 使用字节数组控制一次读取长度,可减少对硬盘的读取次数,提高效率
public void input() {
FileInputStream fis = null ;
try {
fis = new FileInputStream("src\\data.properties") ;
// int data = -1 ;
// while((data=fis.read()) != -1) { //遍历文件内,按单个字节读取
// System.out.println((char)data);
// }
int num = -1 ;
byte [] buffer = new byte[3] ; //字节数组控制读取长度
while((num=fis.read(buffer)) != -1) { //num为读取的字节个数
System.out.println(new String(buffer, 0, num));
}
}
catch (FileNotFoundException e) {
e.printStackTrace();
}
catch (IOException e) {
e.printStackTrace();
}
finally {
if(fis != null) { //只有 不为空才能关闭资源
try {
fis.close() ;
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
- FileOutputStream:文件输出流,以字节为单位,从将数据写入文件中
用法和文件输入流类似,也要用try-catch-finally的格式
方法 | 作用 |
---|---|
write(byte [] b) | 将字节数组中的数据写入 |
flush() | 刷新输出流,完成数据的输出 |
close() | 关闭输入流,会自动调用flush()方法 |
available() | 流中可读取的字节数 |
需要注意的点:
- 使用write()方法只是将数据写入到内存的缓冲区中,并没有真正写入到文件中
- 当关闭资源时,会自动调用flush()方法
- 若写入文件不存在则会自动创建文件,若文件存在,则默认覆盖原有内容
FileOutputStream fos = null ;
try {
fos = new FileOutputStream("src\\data.properties", true) ;
//第二个参数代表写入数据是追加(true)还是覆盖(false),默认是覆盖
byte[] data = "hello world".getBytes() ; //要写入的数据用字节数组保存
fos.write(data); //写数据
}
catch (FileNotFoundException e) {
e.printStackTrace();
}
catch (IOException e) {
e.printStackTrace();
}
finally {
if(fos != null) {
try {
fos.close(); //自动调用flush()方法
} catch (IOException e) {
e.printStackTrace();
}
}
}
3.3 字节数组输入输出流
数据的来源、目的地不一定是文件,也可以是内存中的一块空间
- ByteInputStream:字节数组输入流,从字节数组中读取数据
- ByteOutputStream:字节数组输出流,将数据写出到内置的字节数组中
需要注意的点:
- 字节数组输入输出流的数据不是外部资源,是内存中的数组。没有必要调用close()方法
- 与文件输入输出流的区别就是数据的位置不一样,其他的操作类似
public class 字节数组输入输出流 {
public static void main(String[] args) {
input() ;
output() ;
}
public static void input() { //输入
byte[] data = "hello world".getBytes() ; //要读的数据来源
try {
InputStream bis = new ByteArrayInputStream(data) ;
int x = -1 ;
while((x=bis.read()) != -1) {
System.out.print((char)x);
}
}
catch (IOException e) {
e.printStackTrace();
}
}
public static void output() { //输出
try {
ByteArrayOutputStream bos = new ByteArrayOutputStream() ;
bos.write("world hello".getBytes());
bos.flush();
byte[] buffer = bos.toByteArray() ; //获取内置的字节数组中的数据
System.out.println(new String(buffer)) ;
}
catch (IOException e) {
e.printStackTrace();
}
}
}
3.4 对象输入输出流(对象的序列化和反序列化)
如果希望将Java对象写入IO流中,或从IO流中读取Java对象,则要使用对象输入输出流
- 序列化:将Java对象写入IO流,实现将对象保存在磁盘或在网络中传递对象。由于你的电脑不认识Java对象,所以要用序列化处理之后存储在磁盘中
- 反序列化:从IO流中读取Java对象,实现从磁盘或网络中恢复对象
对象必须要实现Serializable接口c才能被序列化,转换成二进制流,通过网络传输
这个接口中什么都没有,只是标志着实现该接口的类是可序列化的。
当类实现了Serializable接口之后会提示可以创建一个UID,用来判断对象序列化版本的一致性:在反序列化时会将流中的serialVersionUID与本地相应实体对象的serialVersionUID进行比较,如果相同则认为版本一致可进行反序列化,否则会出现版本不一致的异常。但是如果不创建这个UID还是可以执行序列化和反序列化。
- ObjectInputStream: 对象输入流,对应反序列化。属于包装流。
public class 对象输入输出流 {
public static void main(String[] args) {
User user = new User("小明", 123) ; //要写入的对象必须是可序列化的
ObjectOutputStream oos = null ;
try {
FileOutputStream fos = new FileOutputStream("user.data"); //作为包装流的参数
oos = new ObjectOutputStream(fos) ;
oos.writeObject(user); //将对象写出
oos.flush();
}
catch (FileNotFoundException e) {
e.printStackTrace();
}
catch (IOException e) {
e.printStackTrace();
}
finally {
if(oos != null) { //只需要关闭主要的流即可
try {
oos.close() ;
}
catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
打开写入的文件,可以看到里面的内容都是乱码,这就是二进制文件
- ObjectOutputStream: 对象输出流,对应序列化
public class 对象输入输出流 {
public static void main(String[] args) {
ObjectInputStream ois = null ;
try {
ois = new ObjectInputStream(new FileInputStream("user.data")) ;
//读取出来是Object类型要强转,读取顺序和写入顺序一致
User user = (User)ois.readObject() ;
System.out.println(user.getName()) ;
}
catch (FileNotFoundException e) {
e.printStackTrace();
}
catch (IOException e) {
e.printStackTrace();
}
catch (ClassNotFoundException e) {
e.printStackTrace();
}
finally {
if(ois != null) { //只需要关闭主要的流即可
try {
ois.close() ;
}
catch (IOException e) {
e.printStackTrace();
}
}
}
}
ObjectInputStream和ObjctOutputStream都属于包装流,用于对节点流进行功能扩展和包装。在创建包装流时要传入操作的节点流对象;关闭流时,只需要关闭包装流即可,节点流会自动关闭。
3.5 随机读写流
可以对文件进行随机读写,即可以定位到文件的任意位置进行读写操作,原理其实就是通过移动指针实现。该流既能读也能写
在创建随机读写流对象时,要指定具体的模式:
- “r”: 只读,如果文件不存在则会报错,不会自动创建文件
- “rw”: 读写,如果文件不存在会自动创建新文件;不会覆盖原文件中的内容
方法 | 功能 |
---|---|
getFilePointer() | 获取当前指针位置 |
seek(long pos) | 将指针移动到指定位置(以字节为单位) |
skipBytes(int n) | 将指针往后移动n个位置(以字节为单位) |
4 字符流
4.1 简介
- Reader是字符输入流的顶层父类,常用子类有:FileReader、 BufferedReader、 InputStreamReader
- Writer是字符输出流的顶层父类,常用子类有:FileWriter、BufferedWriter、 PrintWriter、 OutputStreamWriter
4.2 文件输入输出流
- FileReader: 文件字符输入流,以字符为单位从文件中读取数据。
- FileWriter: 文件字符输出流,以字符为单位将数据写出到文件中。
用法和FileInputStream、FileOutputStream类似,但由于每次操作的都是以字符为单位,所以在处理中文字符时会更方便一些,同时由于不以字节为单位,所以无法读写二进制文件
4.3 转换流
用于将字节流转换为字符流(Java不支持将字符流转换为字节流),同时可以实现编码的转换。在转换时需要指定使用的字符集,如果不指定的话默认使用JVM的字符集。
- InputStreamReader: 将字节输入流转换为字符输入流,属于包装流
- OutputStreamWriter: 将字节输出流转换为字符输出流
//首先要有一个被转换的字节流
FileInputStream fis = new FileInputStream("src//data.properties") ;
//然后再创建一个转换流,其中需要被转换的就是上面的字节流
InputStreamReader isr = new InputStreamReader(fis) ;
//最后用一个字符流得到转换的结果
BufferedReader reader = new BufferedReader(isr) ;
上面的写法也可以合并
BufferedReader reader = new BufferedReader( //缓冲流,包装流
new InputStreamReader( //转换流,包装流
new FileInputStream("src//data.properties") //字节流
, "gbk" //要转换的编码字符集
)
) ;