- 可以从其中读入一个字节序列的对象称作输入流,可以向其中写入一个字节序列的对象称作输出流
- 抽象类 InputStream 和 OutputStream 构成了输入/输出(I/O)类层次结构的基础
- 面向字节的流不便于处理以 Unicode 形式存储的信息,因为 Unicode 中每个字符都使用了多个字节表示
- read操作:
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
public class Test{
public static String s;
public static void main(String[] args) throws InterruptedException, IOException{
//test.txt内容为:hello \r\n hust
int c = 0;
byte[] b2 = new byte[10];
byte[] b3 = new byte[10];
try(
InputStream is1 = new FileInputStream(new File("./test.txt"));
InputStream is2 = new FileInputStream(new File("./test.txt"));
InputStream is3 = new FileInputStream(new File("./test.txt"));) {
while ((c = is1.read()) != -1) {
System.out.print(c + " ");//104 101 108 108 111 13 10 104 117 115 116
}
System.out.println();
/*
* 以下一个while循环输出为:
* [104, 101, 108, 108, 111, 13, 10, 104, 117, 115]
* [116, 101, 108, 108, 111, 13, 10, 104, 117, 115]
*/
while (is2.read(b2) != -1) {//循环填充b2
System.out.println(Arrays.toString(b2));
}
/*
* 以下一个while循环输出为:
* [0, 0, 0, 104, 101, 108, 108, 111, 13, 10]
* [0, 0, 0, 104, 117, 115, 116, 111, 13, 10]
*/
while (is3.read(b3, 3, 7) != -1) {//循环填充b3,注意有偏移和长度
System.out.println(Arrays.toString(b3));
}
}
}
}
- write 操作:(书上对于 void write(byte[] b)的解释是写出所有字节到数组b中,对此存在质疑:按着程序的结果,应该是将b写入到输出流对象中)
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
public class Test{
public static String s;
public static void main(String[] args) throws InterruptedException, IOException{
int c = 104;
byte[] b = {104,101,108,108,111,13,10,104,117,115,116};
try(OutputStream is1 = new FileOutputStream(new File("./test_write.txt"));) {
is1.write(c);//一次写入一个Byte
is1.write(b);//一次性将b全写入
is1.write(b, 3, 5);//一次性将b中从3开始,依次写入5个字节
}
}
}
- 嵌套过滤器添加多重功能:
DataInputStream dis = new DataInputStream(new BufferedInputStream(new FileInputStream("test.txt")));
- 使用 PrintWriter 类的 print 方法或 println 方法向输出流对象写入数据:
try(PrintWriter pw = new PrintWriter(new File("./test_write.txt"));) {
pw.println("hello ");
pw.print(5);
}
- 读入文本数据:
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;
public class Test{
public static void main(String[] args) throws InterruptedException, IOException{
String content = new String(Files.readAllBytes(Paths.get("test_write.txt")), "utf-8");//读取所有内容为一个 String
List<String> list = Files.readAllLines(Paths.get("test_write.txt"));//一行行读取存入一个 List
/*
* 通过调用BufferedReader对象的readLine方法依次读取每行数据
* 若数据读完,则返回null
*/
try(BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("test_write.txt") , StandardCharsets.UTF_8))){
String line = "";
while ((line = br.readLine()) != null) {
System.out.println(line);
}
}
}
}
- DataInputStream 和 DataOutputStream :
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class Test{
public static void main(String[] args) throws InterruptedException, IOException{
try(DataOutputStream dos = new DataOutputStream(new FileOutputStream("test.txt"));){
dos.writeInt(5);//写入一个整数
dos.writeChars("hello");//写入一个字符串
}
try(DataInputStream dis = new DataInputStream(new FileInputStream("test.txt"))){
System.out.println(dis.readInt());//读取一个整数
System.out.println(dis.readChar());//读取一个字符
}
}
}
- RandomAccessFile :
import java.io.IOException;
import java.io.RandomAccessFile;
public class Test{
public static void main(String[] args) throws InterruptedException, IOException{
try(RandomAccessFile raf = new RandomAccessFile("test.txt", "rw")){
raf.writeUTF("我");
raf.writeChars("love");
raf.seek(0);//将文件指针重新指至0处
System.out.println(raf.readUTF());//读取一个UTF字符,在此用这个函数读取一个中文
//当文件指针 == raf.length 时,文件读取结束
while (raf.getFilePointer() < raf.length()) {
System.out.print(raf.readChar());
}
}
}
}
- 对象输入/输出流与序列化:
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class Test{
public static void main(String[] args) throws InterruptedException, IOException, ClassNotFoundException{
Manager m1 = new Manager("ss", 1000);
Manager m2 = new Manager("cc", 1100);//m1,m2的声明类型必须为Manager而不能是Employee,因为setSecretary方法只存在于Manager类中
Employee s = new Employee("az", 500);
m1.setSecretary(s);
m2.setSecretary(s);
try(ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("test_object.txt"))){
oos.writeObject(m1);//保存对象e1,即对对象e1进行序列化 Employee类必须实现Serializable接口,无任何方法需要实现
oos.writeObject(m2);//保存对象e2,即对对象e2进行序列化 Employee类必须实现Serializable接口,无任何方法需要实现
}
Manager tmp = null;
try(ObjectInputStream ois = new ObjectInputStream(new FileInputStream("test_object.txt"))){
Object o = ois.readObject();//按对象写入的顺序从头开始读取对象
if(o instanceof Manager) {
tmp = (Manager) o;
System.out.println("manager1:" + tmp.getName() + " " + tmp.getSalary());
Employee s1 = tmp.getSecretary();
System.out.println("secretary:" + s1.getName() + " " + s1.getSalary());
}
o = ois.readObject();
if(o instanceof Manager) {
tmp = (Manager) o;
System.out.println("manager2:" + tmp.getName() + " " + tmp.getSalary());
Employee s1 = tmp.getSecretary();
System.out.println("secretary:" + s1.getName() + " " + s1.getSalary());
}
}
}
}
/*
以下为另一个文件的Employee类和Manager类
*/
import java.io.Serializable;
public class Employee implements Serializable{
private String name;
private int salary;
public Employee(String name, int salary) {
this.name = name;
this.salary = salary;
}
public String getName() {
return name;
}
public int getSalary() {
return salary;
}
}
class Manager extends Employee{
private Employee secretary;
public Manager(String name,int salary) {
super(name,salary);
}
public void setSecretary(Employee secretary) {
this.secretary = secretary;
}
public Employee getSecretary() {
return secretary;
}
}
- 针对对象序列化(将对象写入文件)注意:
- 每个对象都是用一个序列号保存
- 对于遇到的每一个对象引用,该引用都会关联一个序列号
- 对于每个对象,当第一次遇到时,保存其对象数据到输出流中
- 如果某个对象之前已经被保存过,那么只做“与之前保存过的序列号为x的对象相同”的标记
- 针对对象读取(从文件读取对象)注意:
- 对于对象输入流中的对象,在第一次遇到其序列号时,构建它,并使用输入流中的数据来初始化它,然后记录这个顺序号和新对象之间的关联
- 当遇到“与之前保存过的序列号为x的对象相同”标记时,获取与这个序列号相关联的对象引用
- 被关键字 transient 修饰的域不可被序列化
- Path:
import java.nio.file.Path;
import java.nio.file.Paths;
public class Test{
public static void main(String[] args) throws InterruptedException,ClassNotFoundException{
Path path = Paths.get("E:","learningNotes","test.txt");// 对所有参数使用当前操作系统的路径分隔符进行拼接,作为path
System.out.println(path.toAbsolutePath());// E:\learningNotes\test.txt
System.out.println(path.getParent());// E:\learningNotes
System.out.println(path.getFileName());// test.txt
System.out.println(path.getRoot());// E:\
}
}
- 注意:路径不必对应某个实际存在的文件,它仅仅只是一个抽象的名字序列
- 操作 Files 类读写文件:
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.Writer;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.List;
public class Test{
public static void main(String[] args) throws IOException{
Path p = Paths.get("test.txt");
//读取指定路径文件的所有字节,返回为byte[]
byte[] bytes = Files.readAllBytes(p);
String content = new String(bytes, "utf-8");//将byte[]类型对象转成String,需提供一个charset
System.out.println(content);
String contentForWrite = "hello java";
//按行读取并将每一行的内容保存至list,再输出list每一行
List<String> list = Files.readAllLines(p);
list.forEach(System.out::println);
//以追加的方式将字符串contentForWrite写入p指定的文件
Files.write(p, contentForWrite.getBytes("utf-8"),StandardOpenOption.APPEND);
//以追加的方式将一个包含行的集合写入文件
Files.write(p, list, StandardOpenOption.APPEND);
//使用Files类创建输入输出流或者读入写入器
try(
InputStream is = Files.newInputStream(p);
OutputStream os = Files.newOutputStream(p);
Reader r = Files.newBufferedReader(p);
Writer w = Files.newBufferedWriter(p)
){
//do something
}
}
}
- 创建文件和目录:
/*
创建新目录
第一种方法需要路径中除最后一个部件外,其他部分都必须是已存在的
第二中方法将会自动创建中间未存在的目录
*/
Files.createDirectory(path);
Files.createDirectories(path);
/*
创建一个空文件。如果该文件已经存在,则以下语句就会抛出异常
*/
Files.createFile(path);
- 复制、移动和删除文件:
//将文件从一个位置复制或移动到另一个位置。如果目标路径已经存在,那么复制或移动将失败
Files.copy(fromPath,toPath);
Files.copy(fromPath,toPath);
/*
如果想要覆盖已有的目标路径,可以使用REPLACE_EXISTING选项
如果想要所有的文件属性,可以使用COPY_ATTRIBUTES选项
*/
Files.copy(fromPath,toPath,StandardCopyOption.REPLACE_EXISTING,StandardCopyOption.COPY_ATTRIBUTES);
/*
删除文件
*/
Files.deleteIfExists(path);
- 访问目录中的项:
//使用list方法读取目录,但是list方法不会进入子目录
try(Stream<Path> entries = Files.list(pathOfDirectory)){
//do something
}
//使用wark方法会进入子目录,按深度优先遍历指定目录,可指定第二个参数为最大深度
try(Stream<Path> entries = Files.walk(path)){
//do somethhing
}