java I/O
文件对象
文件和文件夹都是用File来表示
-
创建文件对象
package file; import java.io.File; public class TestFile { public static void main(String[] args) { // 绝对路径 File f1 = new File("d:/LOLFolder"); System.out.println("f1的绝对路径:" + f1.getAbsolutePath()); // 相对路径,相对于工作目录,如果在eclipse中,就是项目目录 File f2 = new File("LOL.exe"); System.out.println("f2的绝对路径:" + f2.getAbsolutePath()); // 把f1作为父目录创建文件对象 File f3 = new File(f1, "LOL.exe"); System.out.println("f3的绝对路径:" + f3.getAbsolutePath()); } }
结果如下
字节流
以字节流的形式读取和输出,实现复制内容的效果
因为内容是以字节的形式传输的,所以就算不是文本文件,诸如.exe文件都可以通过这种方式实现数据传输。
package top.faroz.file;
import java.io.*;
/**
* @ClassName FileDemo6
* @Description TODO
* @Author FARO_Z
* @Date 2020/8/19 5:39 下午
* @Version 1.0
**/
public class FileDemo6 {
public static void main(String[] args) {
File inFile = new File("/Users/faro_z/Documents/hahaha.txt");
File outFile = new File("/Users/faro_z/Documents/wdnmd.txt");
byte[] bytes = new byte[30];
try {
FileInputStream fileInputStream = new FileInputStream(inFile);
/**
* 通过read()方法将文件内容的字节流读入byte[] 数组
*/
fileInputStream.read(bytes);
fileInputStream.close();
FileOutputStream fileOutputStream = new FileOutputStream(outFile);
/**
* 然后在通过write方法,将字节数组中的内容写到新的文件 wdnmd.txt
* 如果wdnmd.txt不存在。会自动创建一个
*/
fileOutputStream.write(bytes);
fileOutputStream.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
-
关闭流的方式
-
在try中关闭
在try的作用域里关闭文件输入流,在前面的示例中都是使用这种方式,这样做有一个弊端;
如果文件不存在,或者读取的时候出现问题而抛出异常,那么就不会执行这一行关闭流的代码,存在巨大的资源占用隐患。 不推荐使用package stream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; public class TestStream { public static void main(String[] args) { try { File f = new File("d:/lol.txt"); FileInputStream fis = new FileInputStream(f); byte[] all = new byte[(int) f.length()]; fis.read(all); for (byte b : all) { System.out.println(b); } // 在try 里关闭流 fis.close(); } catch (IOException e) { e.printStackTrace(); } } }
-
在finally中关闭
这是标准的关闭流的方式
\1. 首先把流的引用声明在try的外面,如果声明在try里面,其作用域无法抵达finally.
\2. 在finally关闭之前,要先判断该引用是否为空
\3. 关闭的时候,需要再一次进行try catch处理这是标准的严谨的关闭流的方式,但是看上去很繁琐,所以写不重要的或者测试代码的时候,都会采用上面的有隐患try的方式,因为不麻烦~
package top.faroz.file; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; /** * @ClassName FileDemo7 * @Description 试试用finally关闭流的方法 * @Author FARO_Z * @Date 2020/8/24 10:24 上午 * @Version 1.0 **/ public class FileDemo7 { public static void main(String[] args) { File f1 = new File("/Users/faro_z/Documents/testFile1.txt"); FileInputStream fis1=null; try { byte[] bytes = new byte[(int) f1.length()]; fis1 = new FileInputStream(f1); fis1.read(bytes); for (byte aByte : bytes) { System.out.println(aByte); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { if (fis1!=null) { try { fis1.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
-
使用try()的方式
把流定义在try()里,try,catch或者finally结束的时候,会自动关闭
这种编写代码的方式叫做 try-with-resources, 这是从JDK7开始支持的技术所有的流,都实现了一个接口叫做 AutoCloseable,任何类实现了这个接口,都可以在try()中进行实例化。 并且在try, catch, finally结束的时候自动关闭,回收相关资源。
代码比较复制代码
package stream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; public class TestStream { public static void main(String[] args) { File f = new File("d:/lol.txt"); //把流定义在try()里,try,catch或者finally结束的时候,会自动关闭 try (FileInputStream fis = new FileInputStream(f)) { byte[] all = new byte[(int) f.length()]; fis.read(all); for (byte b : all) { System.out.println(b); } } catch (IOException e) { e.printStackTrace(); } } }
-
字符流
-
使用字符流读取文件
FileReader 是Reader子类,以FileReader 为例进行文件读取
package top.faroz.file; import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.util.Arrays; /** * @ClassName FileDemo8 * @Description 字符流读取文件 * 使用try()的方式关闭资源 * @Author FARO_Z * @Date 2020/8/24 10:39 上午 * @Version 1.0 **/ public class FileDemo8 { public static void main(String[] args) { File f1 = new File("/Users/faro_z/Documents/testFile1.txt"); try(FileReader fr1 = new FileReader(f1)) { final char[] chars = new char[(int) f1.length()]; /** * 使用read方法将字符流中的内容读取到字符数组 */ fr1.read(chars); String s=String.valueOf(chars); System.out.println(s); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } }
-
使用字符流写文件
使用类FileWriter,和上面的读文件操作差不多,这里不再赘述
package stream; import java.io.File; import java.io.FileWriter; import java.io.IOException; public class TestStream { public static void main(String[] args) { // 准备文件lol2.txt File f = new File("d:/lol2.txt"); // 创建基于文件的Writer try (FileWriter fr = new FileWriter(f)) { // 以字符流的形式把数据写入到文件中 String data="abcdefg1234567890"; char[] cs = data.toCharArray(); fr.write(cs); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
-
字符缓存流
-
使用缓存流的原因
以介质是硬盘为例,字节流和字符流的弊端:
在每一次读写的时候,都会访问硬盘。 如果读写的频率比较高的时候,其性能表现不佳。为了解决以上弊端,采用缓存流。
缓存流在读取的时候,会一次性读较多的数据到缓存中,以后每一次的读取,都是在缓存中访问,直到缓存中的数据读取完毕,再到硬盘中读取。就好比吃饭,不用缓存就是每吃一口都到锅里去铲。用缓存就是先把饭盛到碗里,碗里的吃完了,再到锅里去铲
缓存流在写入数据的时候,会先把数据写入到缓存区,直到缓存区达到一定的量,才把这些数据,一起写入到硬盘中去。按照这种操作模式,就不会像字节流,字符流那样每写一个字节都访问硬盘,从而减少了IO操作
- 使用缓存流读取数据
缓存字符输入流BufferedReader,可以一次读取一行数据
package stream; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.IOException; public class TestStream { public static void main(String[] args) { // 准备文件lol.txt其中的内容是 // garen kill teemo // teemo revive after 1 minutes // teemo try to garen, but killed again File f = new File("d:/lol.txt"); // 创建文件字符流 // 缓存流必须建立在一个存在的流的基础上 try ( FileReader fr = new FileReader(f);//这里必须要注意 //缓存流一定要建立在一个存在的流之上 BufferedReader br = new BufferedReader(fr); ) { while (true) { // 一次读一行 String line = br.readLine(); if (null == line) break; System.out.println(line); } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
- 使用缓存流写出数据
PrintWriter 缓存字符输出流, 可以一次写出一行数据
这里不使用BufferedWriter,原因如下:****BufferedWriter****:将文本写入字符输出流,缓冲各个字符从而提供单个字符,数组和字符串的高效写入。通过write()方法可以将获取到的字符输出,然后通过newLine()进行换行操作。BufferedWriter中的字符流必须通过调用flush方法才能将其刷出去。并且BufferedWriter只能对字符流进行操作。如果要对字节流操作,则使用BufferedInputStream。
****PrintWriter****:向文本输出流打印对象的格式化表示形式(Prints formatted representations of objects to a text-output stream)。PrintWriter相对于BufferedWriter的好处在于,如果PrintWriter开启了自动刷新,那么当PrintWriter调用println,prinlf或format方法时,输出流中的数据就会自动刷新出去。PrintWriter不但能接收字符流,也能接收字节流。
package stream; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; public class TestStream { public static void main(String[] args) { // 向文件lol2.txt中写入三行语句 File f = new File("d:/lol2.txt"); try ( // 创建文件字符流 FileWriter fw = new FileWriter(f); // 缓存流必须建立在一个存在的流的基础上 PrintWriter pw = new PrintWriter(fw); ) { pw.println("garen kill teemo"); pw.println("teemo revive after 1 minutes"); pw.println("teemo try to garen, but killed again"); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
-
当然,还有字节缓存流
分别是BufferedInputStream和BufferedOutputStream
使用方法和字符缓冲流差不多,这里不再演示
数据流
数据流可以用来进行格式化顺序读写
使用DataInputStream和DataOutputStream
注意: 要用DataInputStream 读取一个文件,这个文件必须是由DataOutputStream 写出的,否则会出现EOFException,因为DataOutputStream 在写出的时候会做一些特殊标记,只有DataInputStream 才能成功的读取。
下面这个例子体现了数据流在某些方面的便利性
用缓冲流输出两个int类型数字还要使用符号"@"隔开,读取才方便
但是数据流就不需要什么字符分隔,挨个读就好了,这也解释了为什么 要用DataInputStream 读取一个文件,这个文件必须是由DataOutputStream 写出的
package stream;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
public class TestStream {
static File f =new File("d:/data.txt");
static int x = 31;
static int y = 15;
public static void main(String[] args) {
//缓存流方式
method1();
//数据流方式
method2();
}
private static void method2() {
try (
FileInputStream fis = new FileInputStream(f);
DataInputStream dis =new DataInputStream(fis);
FileOutputStream fos = new FileOutputStream(f);
DataOutputStream dos =new DataOutputStream(fos);
){
dos.writeInt(x);
dos.writeInt(y);
int x = dis.readInt();
int y = dis.readInt();
System.out.printf("使用数据流读取出的x是 %d y是 %d%n",x,y);
} catch (IOException e) {
e.printStackTrace();
}
}
private static void method1() {
try (
FileWriter fw = new FileWriter(f);
PrintWriter pw = new PrintWriter(fw);
FileReader fr = new FileReader(f);
BufferedReader br = new BufferedReader(fr);
) {
pw.print(x+"@"+y);
pw.flush();
String str = br.readLine();
String[] ss =str.split("@");
int x = Integer.parseInt(ss[0]);
int y = Integer.parseInt(ss[1]);
System.out.printf("使用缓存流读取出的x是 %d y是 %d%n",x,y);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private static void read() {
File f =new File("d:/data.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("d:/data.txt");
try (
FileOutputStream fos = new FileOutputStream(f);
DataOutputStream dos =new DataOutputStream(fos);
){
dos.writeBoolean(true);
dos.writeInt(300);
dos.writeUTF("123 this is gareen");
} catch (IOException e) {
e.printStackTrace();
}
}
}
对象流
对象流指的是可以直接把一个对象以流的形式传输给其他的介质,比如硬盘
一个对象以流的形式进行传输,叫做序列化。 该对象所对应的类,必须是实现Serializable接口
这里直接写一个例子
准备一个长度是10,类型是Hero的数组,使用10个Hero对象初始化该数组
然后把该数组序列化到一个文件heros.lol
接着使用ObjectInputStream 读取该文件,并转换为Hero数组,验证该数组中的内容,是否和序列化之前一样
-
Hero类
注意,这个将要被序列化的Hero类必须实现Serializable接口package top.faroz.domain; import java.io.Serializable; /** * @ClassName Hero * @Description TODO * @Author FARO_Z * @Date 2020/8/26 1:03 上午 * @Version 1.0 **/ public class Hero implements Serializable { private String name; public Hero(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
-
主体
package top.faroz.file;
/**
* @ClassName FileDemo11
* @Description 对象流的测试
* @Author FARO_Z
* @Date 2020/8/26 1:03 上午
* @Version 1.0
**/
import top.faroz.domain.Hero;
import java.io.*;
/**
* 需求:
* 准备一个长度是10,类型是Hero的数组,使用10个Hero对象初始化该数组
* 然后把该数组序列化到一个文件heros.lol
* 接着使用ObjectInputStream 读取该文件,并转换为Hero数组,验证该数组中的内容,是否和序列化之前一样
*/
public class FileDemo11 {
public static void main(String[] args) {
Hero[] heroes = new Hero[10];
for (int i = 0; i < 10; i++) {
heroes[i]=new Hero("我是第"+i+"号英雄");
}
doTest(heroes);
}
public static void doTest(Hero[] heros) {
File file = new File("/Users/faro_z/Documents/testFile/hero.lol");
try(FileOutputStream fos = new FileOutputStream(file);
ObjectOutputStream oos = new ObjectOutputStream(fos)
) {
oos.writeObject(heros);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
try(FileInputStream fis = new FileInputStream(file);
ObjectInputStream ois = new ObjectInputStream(fis)
) {
Hero[] heroes2 = (Hero[]) ois.readObject();
for (Hero hero : heroes2) {
System.out.println(hero.getName());
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}