读取字节的源头被称为输入流,字节的目的地被称为输出流。
//从文件获取流 InputStream in = Files.newInputStream(path); OutputStream out = Files.newOutputStream(path); //如果对象是网页 URL url = new URL("https://www.baidu.com/"); InputStream in = url.openStream(); //从一个字节数组读取数据 byte[] bytes = ... InputStream in = new ByteArrayInputStream(bytes);
输入和输出流按顺序读/写字节,文本是要按字符读/写,常用的字符编码是UTF-8或GBK。
一些方法允许通过Charset对象或一个字符串指定字符编码,建议使用StandardCharsets常量。
对于较短的文本文件,可以将文件读取到一个字符串
String content = new String(Files.readAllBytes(path), "GBK");
按行读取文件:
List<String> lines = Files.readAllLines(path, StandardCharsets.UTF_8);
按流处理:
try(Stream<String> lines = Files.lines(path, StandardCharsets.UTF_8)){ }catch (Exception ex){ }
如果输入源并非来自文件:
URL url = new URL("https://www.bilibili.com/"); try(BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(url.openStream()))){ Stream<String> lines = bufferedReader.lines(); }
try(PrintWriter out = new PrintWriter(Files.newBufferedWriter(path, StandardCharsets.UTF_8))){ out.write("开始写入..."); out.flush(); } catch (Exception ex) { }
如果你已经有一个变量包含了要写的文本:
String content = "内容1"; Files.write(path, content.getBytes(StandardCharsets.UTF_8)); //或者 List<String> lines = new ArrayList<>(); Files.write(path, lines, StandardCharsets.UTF_8);
向文件追加内容:
Files.write(path, content.getBytes(StandardCharsets.UTF_8), StandardOpenOption.APPEND); //或者 Files.write(path, lines, StandardCharsets.UTF_8, StandardOpenOption.APPEND);
当多个同时执行的程序对同一个文件进行修改时,它们需要协调;否则,文件很容易被损坏。使用文件锁机制可以解决这类问题。
例如一个简单例子:
FileChannel channel = FileChannel.open(path); try(FileLock lock = channel.lock()){ }catch (Exception ex){ }
从根路径开始的路径被称为绝对路径,否则就是相对路径。
例子:
//绝对路径 Path path = Paths.get("C://Users//HP//Desktop//新建文本文档.txt"); //或者 Path path = Paths.get("C://","Users","HP","Desktop","新建文本文档.txt"); //相对路径,路径为文件在工程中的相对路径 Path path = Paths.get("src","in.txt"); //对路径进行组合,调用p.resolve(q)会根据这些规则返回一个路径对象.如果q是绝对路径,则返回结果就是q,否则,返回结果就是先p然后q。 //假设你的程序需要在主目录的相对路径下找配置文件: Path path = homeDirectory.resolve("myapp/work");
//创建一个空文件 Path path = Paths.get("src","in.txt"); if(!Files.exists(path)){ Files.createFile(path); } //创建一个空目录 Path path = Paths.get("src","in"); if(!Files.exists(path)){ Files.createDirectory(path); }
如果移动或复制的目标已经存在,则必须指定StandardCopyOption的字段,否则会失败。
//移动,ATOMIC_MOVE选项指定移动应该是原子操作,这样才能确保移动保存成功完成,或继续保留在当前位置 Path fromPath = Paths.get("src","in.txt"); Path toPatch = Paths.get("src","in","in.txt"); Files.move(fromPath, toPatch, StandardCopyOption.ATOMIC_MOVE); //复制,COPY_ATTRIBUTES选项能覆盖已经存在的目标文件或目录 Files.copy(fromPath, toPatch, StandardCopyOption.COPY_ATTRIBUTES);
删除一个文件
boolean deleted = Files.deleteIfExists(path);
URL类能提供的控制有限,如果想写数据,或获取web资源的额外信息可以使用
--add-modules jdk.incubator.httpclient
对象的序列化是一种将一个对象转化成 字节 方便传送到别处或储存在硬盘上,并且能再从转化成的字节重构对象的机制。
为了一个对象能被序列化,即把它转化成一段字节,它必须是一个实现了Serializable接口的类的实例。这是一个没有方法的标记接口。
例子:
//所有实例的变量都是基本类型或枚举类型,或者引用其他可序列化对象 class Employee implements Serializable{ private String name; private double salary; public Employee(String name, double salary) { this.setName(name); this.setSalary(salary); } public String getName() { return name; } public void setName(String name) { this.name = name; } public double getSalary() { return salary; } public void setSalary(double salary) { this.salary = salary; } }
要序列化对象,你需要一个从接收实际字节的OutputStream构造的ObjectOutputStream对象。
Path path = Paths.get("C://","Users","HP","Desktop","新建文本文档.txt"); try(ObjectOutputStream out = new ObjectOutputStream(Files.newOutputStream(path))){ Employee peter = new Employee("Peter", 90000); Employee paul = new Employee("paul", 190000); //写入对象 out.writeObject(peter); out.writeObject(paul); }catch (Exception ex){ } //读回对象 try (ObjectInputStream in = new ObjectInputStream(Files.newInputStream(path))){ //然后按照写入的顺序用readObject方法读取对象 Employee e1 = (Employee)in.readObject(); Employee e2 = (Employee)in.readObject(); } catch (Exception ex){ }
为了实现某些实例变量不被序列化,简单的方法就是给这个变量打一个transient修饰符标记。
private transient double salary;