目录
5,BufferedInputStream与BufferedOutputStream
一、认识文件
我们先来认识狭义上的文件(file)。针对硬盘这种持久化存储的I/O设备,当我们想要进行数据保存时, 往往不是保存成一个整体,而是独立成一个个的单位进行保存,这个独立的单位就被抽象成文件的概 念,就类似办公桌上的一份份真实的文件一般。
文件除了有数据内容之外,还有一部分信息,例如文件名、文件类型、文件大小等并不作为文件的数据 而存在,我们把这部分信息可以视为文件的元信息。
路径的两种表示方式
如何在文件系统中如何定位我们的一个唯一的文件就成为当前要解决的问题,但这难不倒计算机科学 家,因为从树型结构的角度来看,树中的每个结点都可以被一条从根开始,一直到达的结点的路径所描 述,而这种描述方式就被称为文件的绝对路径(absolute path)。
以C或者D开头的盘就是绝对路径
除了可以从根开始进行路径的描述,我们可以从任意结点出发,进行路径的描述,而这种描述方式就被 称为相对路径(relative path),相对于当前所在结点的一条路径。
以C:\Users\12556\Desktop\JavaEE\4.JavaEE初阶为例
相对路径:4.JavaEE初阶为例
二、Java 中操作文件
1,方法
修饰符及返回 值类型 | 方法签名 | 说明 |
String | getParent() | 返回 File 对象的父目录文件路径 |
String | getName() | 返回 FIle 对象的纯文件名称 |
String | getPath() | 返回 File 对象的文件路径 |
boolean | exists() | 判断 File 对象描述的文件是否真实存在 |
boolean | isDirectory() | 判断 File 对象代表的文件是否是一个目录 |
boolean | isFile() | 判断 File 对象代表的文件是否是一个普通文件 |
boolean | delete() | 根据 File 对象,删除该文件。成功删除后返回 true |
boolean | deleteOnExit() | 根据 File 对象,标注文件将被删除,删除动作会到 JVM 运行结束时才会进行 |
String[] | list() | 返回 File 对象代表的目录下的所有文件名 |
File[] | listFiles() | 返回 File 对象代表的目录下的所有文件,以 File 对象 表示 |
boolean | mkdir() | 创建 File 对象代表的目录 |
boolean | mkdirs() | 创建 File 对象代表的目录,如果必要,会创建中间目录 |
boolean | canRead() | 判断用户是否对文件有可读权限 |
boolean | canWrite() | 判断用户是否对文件有可写权限 |
示例1
public static void main(String[] args) {
File file = new File("D:/123.txt");
System.out.println(file.exists());
System.out.println(file.getParentFile());
System.out.println(file.getName());
System.out.println(file.getPath());
}
示例2
public static void main(String[] args) throws IOException {
File file = new File("D:/123.txt");
System.out.println(file.exists());
System.out.println(file.isDirectory());
System.out.println(file.isFile());
System.out.println(file.createNewFile());
System.out.println(file.exists());
System.out.println(file.isDirectory());
System.out.println(file.isFile());
System.out.println(file.createNewFile());
}
示例3
递归的罗列出一个目录中的所有文件
public static void main(String[] args) {
File file = new File("D:/练习");
listAllFile(file);
}
private static void listAllFile(File file) {
if (file.isDirectory()){
File[] files = file.listFiles();
for (File file1 : files){
listAllFile(file1);
}
}else {
System.out.println(file);
}
}
示例4
public static void main(String[] args) throws IOException {
File dir = new File("D:/some-dir"); // 要求该目录不存在,才能看到相同的现象
System.out.println(dir.isDirectory());
System.out.println(dir.isFile());
System.out.println(dir.mkdir());
System.out.println(dir.isDirectory());
System.out.println(dir.isFile());
}
三、文件内容的读写 —— 数据流
1,InputStream 概述
InputStream 只是一个抽象类,要使用还需要具体的实现类。关于 InputStream 的实现类有很多,基 本可以认为不同的输入设备都可以对应一个 InputStream 类,我们现在只关心从文件中读取,所以使 用 FileInputStream
InputStream:输入,从输入设备读取数据到内存中
2,OutputStream概述
OutputStream 同样只是一个抽象类,要使用还需要具体的实现类。我们现在还是只关心写入文件中, 所以使用 FileOutputStream
OutputStream:输出,把内存中的数据写入输出设备中
3,流对象操作
步骤:
1,打开文件(构造方法)
2,read:从文件中把数据读到内存中
3,write:把数据从内存中写入文件中
4,close:关闭文件(和打开相对应的)
public static void main(String[] args) throws IOException {
copyFile("D:/test/12.jpg","D:/test/123.jpg");
}
private static void copyFile(String srcPath, String destPath) throws IOException {
//打开文件,才能读写
FileInputStream fileInputStream = new FileInputStream(srcPath);
FileOutputStream fileOutputStream = new FileOutputStream(destPath);
//1,读取scrPath对应的内容
byte[] buffer = new byte[1024];
int len = -1;
//读到的len是-1说明读取结束,循环结束了
while ((len = fileInputStream.read(buffer)) != -1){
//读取成功,把读的内容写入另外一个文件
fileOutputStream.write(buffer,0,len);
}
fileInputStream.close();
fileOutputStream.close();
}
当一个进程中打开一个文件的时候,就会在PCB中进行一定的记录
代码中每次打开一个文件,本质就是在文件的描述符表中,创建了一个新的项,代码中每次关闭一个文件,本质就是把文件描述符表上的对应项给删除
文件描述符是PCB中的一个属性,一个进程可能会对应多个PCB(每个PCB对应一个线程)
同一个进程的若干个PCB之间共享同一个文件描述符表
4,代码优化
private static void copyFile2() throws IOException {
//创建示例
FileInputStream fileInputStream = null;
FileOutputStream fileOutputStream = null;
try {
fileInputStream = new FileInputStream("d:/test_dir/rose.jpg");
fileOutputStream = new FileOutputStream("d:/test_dir/rose2.jpg");
byte[] buffer = new byte[1024];
int len = -1;
while ((len = fileInputStream.read(buffer)) != -1) {
fileOutputStream.write(buffer, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (fileInputStream != null) {
fileInputStream.close();
}
if (fileOutputStream != null) {
fileOutputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
private static void copyFile3() {
//当代码写成这样的时候,就不需要显示调用close了
//try语句会在代码执行完毕后自动调用close方法
try {FileInputStream fileInputStream = new FileInputStream("d:/test_dir/rose.jpg");
FileOutputStream fileOutputStream = new FileOutputStream("d:/test_dir/rose2.jpg");
byte[] buffer = new byte[1024];
int len = -1;
while ((len = fileInputStream.read(buffer)) != -1){
fileOutputStream.write(buffer,0,len);
}
}catch (IOException e){
e.printStackTrace();
}
}
5,BufferedInputStream与BufferedOutputStream
内置缓冲区,本质就是一块内存空间,缓冲区存在的原因就是提高效率。
程序访问内存比程序访问磁盘,要快的多,IO涉及磁盘访问,IO越多,整体的程序效率就越低
读写磁盘的时候,每次读一个字节,读一百次肯定比每一次读100个字节,读一次效率低。
public static void main(String[] args) throws IOException {
copyFile();
}
private static void copyFile() throws IOException {
FileInputStream fileInputStream = new FileInputStream("路径");
FileOutputStream fileOutputStream = new FileOutputStream("路径");
BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream);
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream);
//用户指定的缓冲区
byte[] buffer = new byte[1024];
int len = -1;
while ((len = bufferedInputStream.read(buffer)) != -1) {
bufferedOutputStream.write(buffer,0,len);
}
bufferedInputStream.close();
bufferedOutputStream.close();
}
优化代码
private static void copyFile2() {
try (BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream("d:/test_dir/rose.jpg"));
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream("d:/test_dir/rose2.jpg"))) {
int len = -1;
byte[] buffer = new byte[1024];
while ((len = bufferedInputStream.read(buffer)) != -1) {
bufferedOutputStream.write(buffer, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
}
}
6,字符流
字符流对象的用法和字节流对象非常相似,只是有一些细节不一样
字节流的读写操作以byte为单位,缓冲区就是一个byte[]
字符流的读写操作以char为单位,缓存区就是一个char[]
private static void copyFile() throws IOException {
try{
FileReader fileReader = new FileReader("路径");
FileWriter fileWriter = new FileWriter("路径");
char[] buffer = new char[1024];
int len = -1;
while ((len = fileReader.read(buffer)) != -1) {
fileWriter.write(buffer,0,len);
}
} catch (IOException e){
e.printStackTrace();
}
}
优化代码
public static void main(String[] args) throws IOException {
copyFile1();
}
private static void copyFile1() throws IOException {
//带缓冲区的字符流有一种特殊的用法,按行读取
try{BufferedReader bufferedReader = new BufferedReader(new FileReader("路径"));
BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter("路径"));
String line = "";
while ((line = bufferedReader.readLine()) != null){
System.out.println("line:" + line);
bufferedWriter.write(line + "\n");
}
}catch (IOException e){
e.printStackTrace();
}
}
四、序列化与反序列化
序列化:把一个结构化数据(对象)变成一个二进制的bit流(可以把这个bit流保存到文件,或者通过网络传送)
反序列化:把二进制的bit流还原回原来的对象
目的:
就是为了让对象能通过网络传输/能够在文件中保存
序列化和反序列化的过程要保证“信息丢失”
Java自带序列化的方式,借助流对象来完成
static class Student implements Serializable{
public String name;
public int age;
public int score;
}
public static void main(String[] args) throws IOException {
Student s = new Student();
s.name = "张三";
s.age = 20;
s.score = 100;
seeializeStudet(s);
}
private static void seeializeStudet(Student s) throws IOException {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("D:/student.txt"));
objectOutputStream.writeObject(s);
objectOutputStream.close();
}
static class Student implements Serializable{
public String name;
public int age;
public int score;
}
public static void main(String[] args) throws IOException, ClassNotFoundException {
Student s = deserializeStudent();
System.out.println(s.name);
System.out.println(s.age);
System.out.println(s.score);
}
private static Student deserializeStudent() throws IOException, ClassNotFoundException {
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("D:/student.txt"));
Student s = (Student) objectInputStream.readObject();
return s;
}