(1)、文件对象
文件和文件夹都是用 File 代表。
[1]、创建文件对象(直接new)可以用绝对路径和相对路径两种
绝对路径不用说,从盘符开始,如:D:\MyFile\a.txt
相对路径,则是从项目目录开始,如:项目 MyProject 下的相对路径就在这个目录下
[2]、文件对象的常用方法:
方法名 | 功能 |
exists() | 判断文件是否存在 |
isDirectory() | 判断是否是文件夹 |
isFile() | 判断是否是文件(非文件夹) |
length() | 文件长度 |
lastModified() | 获取文件最后修改时间 |
setLastModified() | 设置文件修改时间 |
renameTo() | 文件重命名 |
list() | 以字符串数组的形式,返回当前文件夹下的所有文件(不包含子文件及子文件夹) |
listFiles() | 以文件数组的形式,返回当前文件夹下的所有文件(不包含子文件及子文件夹) |
getParent() | 以字符串形式返回获取所在文件夹 |
getParentFile() | 以文件形式返回获取所在文件夹 |
mkdir() | 创建文件夹,如果父文件夹不存在,创建就无效 |
mkdirs() | 创建文件夹,如果父文件夹不存在,就会创建父文件夹 |
createNewFile() | 创建一个空文件,如果父文件夹不存在,就会抛出异常 |
listRoots() | 列出所有的盘符c: d: e: 等等 |
delete() | 刪除文件 |
deleteOnExit() | JVM结束的时候,刪除文件,常用于临时文件的删除 |
(2)、流
流(Stream),就是一系列的数据。
当不同的介质之间有数据交互的时候,JAVA就使用流来实现。
数据源可以是文件,还可以是数据库,网络甚至是其他的程序;
比如读取文件的数据到程序中,站在程序的角度来看,就叫做输入流。
输入流: InputStream
输出流:OutputStream
(3)、字节流,用于以字节的形式读取和写入数据
InputStream 字节输入流的所有类的超类;
OutputStream 字节输出流的所有类的超类;
[1]、以字节流的形式读取文件内容
InputStream是字节输入流,同时也是抽象类,只提供方法声明,不提供方法的具体实现。
FileInputStream 是InputStream子类,以FileInputStream 为例进行文件读取
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
public class InputStreamTest {
public static void main(String[] args) {
try {
// 准备文件a.txt其中的内容是AB,对应的ASCII分别是65 66
File f = new File("a.txt");
// 创建基于文件的输入流
FileInputStream fis = new FileInputStream(f);
// 创建字节数组,其长度就是文件的长度
byte[] all = new byte[(int) f.length()];
// 以字节流的形式读取文件所有内容
fis.read(all);
for (byte b : all) {
// 打印出来是65 66
System.out.println(b);
}
// 每次使用完流,都应该进行关闭
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
[2]、以字节流的形式向文件写入数据
OutputStream是字节输出流,同时也是抽象类,只提供方法声明,不提供方法的具体实现。
FileOutputStream 是OutputStream子类,以FileOutputStream 为例向文件写出数据;
【注意: 如果文件 b.txt 不存在,写出操作会自动创建该文件;但是如果是文件 dir/b.txt,而目录 dir 又不存在,则会抛出异常】
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
public class OutputStreamTest {
public static void main(String[] args) {
try {
// 创建文件对象b.txt
File f = new File("b.txt");
// 准备长度是2的字节数组,用88,89初始化,其对应的字符分别是X,Y
byte data[] = { 88, 89 };
// 创建基于文件的输出流
FileOutputStream fos = new FileOutputStream(f);
// 把数据写入到输出流
fos.write(data);
// 关闭输出流
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
执行结果:
(4)、流的关闭
所有的流,无论是输入流还是输出流,使用完毕之后,都应该关闭。 如果不关闭,会产生对资源占用的浪费。 当量比较大的时候,会影响到业务的正常开展。
如果没有关闭流的话,eclipse会进行提醒(黄色下划线):↓ ↓ ↓
[1]、在 try 块中关闭流:
在try的作用域里关闭文件输入流,在前面的示例中都是使用这种方式,这样做有一个弊端;
如果文件不存在,或者读取的时候出现问题而抛出异常,那么就不会执行这一行关闭流的代码,存在巨大的资源占用隐患。 不推荐使用
[2]、在 finally 块中关闭流:
这是标准的关闭流的方式
1. 首先把流的引用声明在try的外面,如果声明在try里面,其作用域无法抵达finally.
2. 在finally关闭之前,要先判断该引用是否为空
3. 关闭的时候,需要再一次进行try catch处理
这是标准的严谨的关闭流的方式,但是看上去很繁琐,所以写不重要的或者测试代码的时候,都会采用上面的有隐患try的方式,因为不麻烦~
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
public class CloseStreamTest {
public static void main(String[] args) {
FileOutputStream fos = null;
try {
// 创建文件对象b.txt
File f = new File("b.txt");
// 准备长度是2的字节数组,用88,89初始化,其对应的字符分别是X,Y
byte data[] = { 88, 89 };
// 创建基于文件的输出流
fos = new FileOutputStream(f);
// 把数据写入到输出流
fos.write(data);
} catch (IOException e) {
e.printStackTrace();
} finally {
// 如果流引用不为空,则关闭
if (fos != null) {
try {
// 关闭输出流
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
[3]、使用 try() 的方式关闭流:
【这种方式叫做 try-with-resources, 这是从JDK7开始支持的技术;
把流定义在try()里,try,catch或者finally结束的时候,会自动关闭。】
所有的流,都实现了一个接口叫做 AutoCloseable,任何类实现了这个接口,都可以在try()中进行实例化。 并且在try, catch, finally结束的时候自动关闭,回收相关资源。
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
public class CloseStreamWithTryTest {
public static void main(String[] args) {
// 创建文件对象b.txt
File f = new File("b.txt");
// 把流定义在try()里,try,catch或者finally结束的时候,会自动关闭
try(FileOutputStream fos = new FileOutputStream(f)) {
// 准备长度是2的字节数组,用88,89初始化,其对应的字符分别是X,Y
byte data[] = { 88, 89 };
// 把数据写入到输出流
fos.write(data);
} catch (IOException e) {
e.printStackTrace();
}
}
}
(5)、字符流,专门用于以字符的形式读取和写入数据
Reader 字符输入流的所有类的超类;
Writer 字符输出流的所有类的超类;
[1]、使用字符流读取文件
FileReader 是Reader子类,以FileReader 为例进行文件读取
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
public class ReaderTest {
public static void main(String[] args) {
// 准备文件a.txt其中的内容是【每一个字都是一个字符,Java采用的是Unicode编码,中文汉字也是一个字符】
File f = new File("a.txt");
// 创建基于文件的Reader
try (FileReader fr = new FileReader(f)) {
// 创建字符数组,其长度就是文件的长度
char[] all = new char[(int) f.length()];
// 以字符流的形式读取文件所有内容
fr.read(all);
for (char b : all) {
// 打印出来是:【每 一 个 字 都 是 一 个 字 符 , J a v a 采 用 的 是 U n i c o d e 编 码 , 中 文 汉 字 也 是 一 个 字 符 】
System.out.print(b + " ");
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
输出结果:
[2]、使用字符流把字符串写入到文件
FileWriter 是Writer的子类,以FileWriter 为例把字符串写入到文件
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
public class WriterTest {
public static void main(String[] args) {
// 准备文件c.txt
File f = new File("c.txt");
// 创建基于文件的Writer
try (FileWriter fr = new FileWriter(f)) {
// 创建内容字符串,转换为字符数组
char[] all = new String("每一个汉字都是一个字符!").toCharArray();
// 以字符流的形式将所有内容写入文件
fr.write(all);
} catch (IOException e) {
e.printStackTrace();
}
}
}
执行结果:
(6)、编码问题
[1]、常见编码
工作后经常接触的编码方式有如下几种:
ISO-8859-1 ASCII 数字和西欧字母
GBK GB2312 BIG5 中文
UNICODE (统一码,万国码)
其中
ISO-8859-1 包含 ASCII
GB2312 是简体中文,BIG5是繁体中文,GBK同时包含简体和繁体以及日文。
UNICODE 包括了所有的文字,无论中文,英文,藏文,法文,世界所有的文字都包含其中
[2]、UNICODE和UTF
UNICODE每个数字都是很长的(4个字节),因为不仅要表示字母,还要表示汉字等。
如果完全按照UNICODE的方式来存储数据,就会有很大的浪费。
比如在ISO-8859-1中,a 字符对应的数字是0x61
而UNICODE中对应的数字是 0x00000061,倘若一篇文章大部分都是英文字母,那么按照UNICODE的方式进行数据保存就会消耗很多空间
在这种情况下,就出现了UNICODE的各种减肥子编码, 比如UTF-8对数字和字母就使用一个字节,而对汉字就使用3个字节,从而达到了减肥还能保证健康的效果
UTF-8,UTF-16和UTF-32 针对不同类型的数据有不同的减肥效果,一般说来UTF-8是比较常用的方式
[3]、Java使用的就是UNICODE编码
写在.java源代码中的汉字,在执行之后,都会变成JVM中的字符。
而这些中文字符采用的编码方式,都是使用UNICODE. "中"字对应的UNICODE是4E2D,所以在内存中,实际保存的数据就是十六进制的0x4E2D, 也就是十进制的20013。
[4]、用FileInputStream 字节流正确读取中文
为了能够正确的读取中文内容
1. 必须了解文本是以哪种编码方式保存字符的
2. 使用字节流读取了文本后,再使用对应的编码方式去识别这些数字,得到正确的字符
如本例,一个文件中的内容是字符 '中' ,编码方式是UTF-8,那么读出来的数据一定是E4B8AD。
再使用UTF-8编码方式识别E4B8AD,就能正确的得到字符 '中' 。
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
public class EncodingTest {
public static void main(String[] args) {
File f = new File("d.txt");
try (FileInputStream fis = new FileInputStream(f);) {
byte[] all = new byte[(int) f.length()];
fis.read(all);
//文件中读出来的数据是
System.out.println("文件中读出来的数据是:");
for (byte b : all)
{
int i = b&0x000000ff; //只取16进制的后两位
System.out.println(Integer.toHexString(i));
}
System.out.println("把这个数字,以UTF-8的编码方式读取:");
String str = new String(all,"UTF-8");
System.out.println(str);
} catch (IOException e) {
e.printStackTrace();
}
}
}
输出结果:
(7)、缓存流
以介质是硬盘为例,字节流和字符流的弊端:
在每一次读写的时候,都会访问硬盘。 如果读写的频率比较高的时候,其性能表现不佳。
为了解决以上弊端,采用缓存流!!!
缓存流在读取的时候,会一次性读较多的数据到缓存中,以后每一次的读取,都是在缓存中访问,直到缓存中的数据读取完毕,再到硬盘中读取。
【就好比吃饭,不用缓存就是每吃一口都到锅里去铲。用缓存就是先把饭盛到碗里,碗里的吃完了,再到锅里去铲 】
缓存流在写入数据的时候,会先把数据写入到缓存区,直到缓存区达到一定的量,才把这些数据,一起写入到硬盘中去。
【就好比做饭,不用每从米袋中抓一把米都倒到锅里;用缓存就是先把米抓到碗里,碗里装满了,再倒到锅里。】
按照这种操作模式,就不会像字节流,字符流那样每读/写一个字节都访问硬盘,从而减少了IO操作。
[1]、使用缓存流读取数据
缓存字符输入流 BufferedReader 可以一次读取一行数据;
缓存流必须建立在一个存在的流的基础上!!!
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
public class BufferedStreamTest {
public static void main(String[] args) {
// 准备文件e.txt其中的内容是
// 测试缓存流的文本,第一行
// 测试缓存流的文本,第二行
// 测试缓存流的文本,第三行
File f = new File("e.txt");
// 创建文件字符流
// 缓存流必须建立在一个存在的流的基础上
try ( FileReader fr = new FileReader(f);
BufferedReader br = new BufferedReader(fr);
) {
while (true) {
// 一次读一行
String line = br.readLine();
if (line == null) {
break;
} else {
System.out.println(line);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
输出结果:
[2]、使用缓存流写出数据
PrintWriter 缓存字符输出流, 可以一次写出一行数据;
缓存流必须建立在一个存在的流的基础上!!!
import java.io.File;
import java.io.FileWriter;
import java.io.PrintWriter;
public class PrintWriterStreamTest {
public static void main(String[] args) {
// 准备文件f.txt
File f = new File("f.txt");
// 创建文件字符流
// 缓存流必须建立在一个存在的流的基础上
try (FileWriter fw = new FileWriter(f); PrintWriter pw = new PrintWriter(fw);) {
// 向文件f.txt中写入5行数据
for (int i = 0; i < 5; i++) {
String line = "这是写入的第" + i + "行数据";
pw.println(line);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
执行结果:
[3]、flush
有的时候,需要立即把数据写入到硬盘,而不是等缓存满了才写出去。 这时候就需要用到 flush .
import java.io.File;
import java.io.FileWriter;
import java.io.PrintWriter;
public class StreamTest {
public static void main(String[] args) {
// 准备文件f.txt
File f = new File("f.txt");
// 创建文件字符流
// 缓存流必须建立在一个存在的流的基础上
try (FileWriter fw = new FileWriter(f); PrintWriter pw = new PrintWriter(fw);) {
// 向文件f.txt中写入5行数据
for (int i = 0; i < 5; i++) {
String line = "这是写入的第" + i + "行数据";
pw.println(line);
// 强制把缓存中的数据写入硬盘,无论缓存是否已满
pw.flush();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
执行结果:
(8)、数据流(属于字节流的一种)
DataInputStream 数据输入流
DataOutputStream 数据输出流
[1]、利用数据流进行字符串的读写
使用数据流的writeUTF()和readUTF() 可以进行数据的格式化顺序读写;
【注: 要用DataInputStream 读取一个文件,这个文件必须是由DataOutputStream 写出的,否则会出现EOFException,因为DataOutputStream 在写出的时候会做一些特殊标记,只有DataInputStream 才能成功的读取。】
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class DataStreamTest {
public static void main(String[] args) {
write();
read();
}
private static void read() {
File f =new File("g.txt");
try (
FileInputStream fis = new FileInputStream(f);
DataInputStream dis =new DataInputStream(fis);
){
boolean b= dis.readBoolean();
int i = dis.readInt();
String str = dis.readUTF();
System.out.println("读取到布尔值:"+b);
System.out.println("读取到整数:"+i);
System.out.println("读取到字符串:"+str);
} catch (IOException e) {
e.printStackTrace();
}
}
private static void write() {
File f =new File("g.txt");
try (
FileOutputStream fos = new FileOutputStream(f);
DataOutputStream dos =new DataOutputStream(fos);
){
dos.writeBoolean(true);
dos.writeInt(300);
dos.writeUTF("中文");
} catch (IOException e) {
e.printStackTrace();
}
}
}
执行结果:
(9)、对象流
对象流指的是可以直接把一个对象以流的形式传输给其他的介质,比如硬盘
一个对象以流的形式进行传输,叫做序列化。 该对象所对应的类,必须是实现Serializable接口。
序列化一个对象:
【注:把一个对象序列化有一个前提是:这个对象的类,必须实现了Serializable接口】
创建一个User对象 user , 把该对象序列化到一个文件user.user , 然后再通过序列化把该文件转换为一个User对象
User.java :
import java.io.Serializable;
public class User implements Serializable{
//表示这个类当前的版本,如果有了变化,比如新设计了属性,就应该修改这个版本号
private static final long serialVersionUID = 1L;
public String username;
public int age;
}
ObjectStreamTest.java :
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class ObjectStreamTest {
public static void main(String[] args) {
//创建一个User对象
//要把User对象直接保存在文件上,务必让User类实现Serializable接口
User user = new User();
user.username = "张三";
user.age = 18;
//准备一个文件用于保存该对象
File f = new File("h.user");
try(
//创建对象输出流
FileOutputStream fos = new FileOutputStream(f);
ObjectOutputStream oos = new ObjectOutputStream(fos);
//创建对象输入流
FileInputStream fis = new FileInputStream(f);
ObjectInputStream ois = new ObjectInputStream(fis);
)
{
oos.writeObject(user); // 写出对象
User user2 = (User) ois.readObject(); // 读入对象
System.out.println(user2.username + "," + user2.age);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
运行结果:
(10)、流关系图