Java的输入和输出通过数据流、序列化和文件系统提供;

输入/输出流?字节流与字符流?

  • 按流向分——
    输入流: 程序可以从中读取数据的流。
    输出流: 程序能向其中写入数据的流。

标准输入输出,文件的操作,网络上的数据流,字符串流,对象流,zip文件流等等,java中将输入输出抽象称为流,就好像水管,将两个容器连接起来。将数据冲外存中读取到内存中的称为输入流,将数据从内存写入外存中的称为输出流。

  • 按数据传输单位分——
    字节流: 以字节为单位传输数据的流,表示以字节为单位从stream中读取或往stream中写入信息,即io包中的inputstream类和outputstream类的派生类。通常用来读取二进制数据,如图象和声音。
    字符流: 以字符为单位传输数据的流,以Unicode字符为导向的stream,表示以Unicode字符为单位从stream中读取或往stream中写入信息,包括Reader/Writer。

常用类

字节流:InputStream/OutputStream(二进制格式操作);
字符流:Reader/Writer;
文件流:FileInputStream / FileOutputStream (字节流)、FileReader / FileWriter(字符流);
对象流:ObjectInputStream / ObjectOutputStream ;(序列化/反序列化);

eg:

1.字符流
int ch = [System.in](http://system.in/).read();
System.out.println(ch);2.字节流
BufferedReader bfr = new BufferedReader(new InputStreamReader([System.in](http://system.in/)));
System.out.println(bfr.readLine());3.文件流
(1)BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("test.txt")));
(2)BufferedReader br = new BufferedReader(new FileReader("test.txt"));

Java的输入,我们用到Scanner类,可以用它创建一个对象:Scanner reader=new Scanner([System.in](http://system.in/)); 然后reader对象调用nextBoolean(), nextByte(), nextShort(), nextInt(), nextLong(), nextFloat(), nextDouble()方法来从输入流中获取数据。这些方法在执行时都会阻塞,程序等待用户在输入流中输入enter键(\n)时继续执行;

操作文件?

Files类可以使得普通文件操作变得快捷:
byte[] bytes = Files.readAllBytes(path);

如果想将文件当作字符串读入,可以在调用readAllBytes之后执行:
String content = new String(bytes,charset);

如果希望文件当作行序列读入,可以调用:
List<String> lines = Files.readAllLines(path,charset);

相反,如果希望写出一个字符串到文件中,可以调用:
Files.write(path,content.getBytes(charset));

可以用下面的语句将一个行的集合写出到文件中:
Files.write(path,lines);
这些简便方法适用于处理中等长度的文本文件,如果要处理的文本长度较大,或者是二进制文件,还是应该使用所熟知的输入输出流或者读入器/写出器。

创建新目录可以调用:
Files.createDirectory(path);

可以使用下面的语句创建一个空文件:
Files.createFile(path);
如果文件已存在,那么这个调用就会异常。

将文件从一个位置复制到另一个位置可以直接调用
Files.copy(fromPath,toPath);

移动文件可以调用:
Files.move(fromPath,toPath);
如果目标路径已经存在,那么复制或移动将失败;
eg:

    @Testpublic void fuc_01() throws IOException{ // 测试 创建文件File mf = new File("Test_InAndOut_ABC.txt");System.out.println(mf.exists());if(mf.exists() == false){mf.createNewFile();}// 读文件String si = new String(Files.readAllBytes(Paths.get("Test_InAndOut_ABC.txt")), StandardCharsets.UTF_8);//前提是String不是很大System.out.println(si);List<String> lines = Files.readAllLines(Paths.get("Test_InAndOut_ABC.txt"), StandardCharsets.UTF_8);//读到List里面,按照行存StringBuilder sb = new StringBuilder();for(String line : lines){sb.append(line);}String fromFile = sb.toString();System.out.println(fromFile);// 写入文件PrintWriter pw = new PrintWriter(new FileWriter("Test_InAndOut_ABC.txt", true)); // true,**意味着为“追加内容”而非覆盖,否则 整个文件原内容都会被覆盖**pw.println("B");pw.println("结束");pw.close();}
    @Testpublic void fuc_02() throws IOException{//复制文件/移动文件/删除文件Path fromPath = Paths.get("D:\\IDEA\\workspace\\src\\JavaStudy_Test\\Test_InAndOut_ABC.txt");// 1."testPath";2."testPath\\Test_InAndOut_ABC_copy.txt"(可重命名文件);3."testPath\\Test_InAndOut_ABC_copy.txt"(可重命名文件);Path toPath = Paths.get("testPath\\Test_InAndOut_ABC_copy01.txt");//        Files.createDirectory(toPath);//若文件夹已存在,则FileAlreadyExistsException;
//        Files.copy(fromPath, toPath);//将文件从一个位置复制到另一个位置可以直接调用,可以在toPath里面重命名该文件
//        Files.move(fromPath,toPath);//移动文件可以调用,如果目标路径已经存在,那么复制或移动将失败;Files.deleteIfExists(toPath);//删除文件;}

Path类?目录遍历(目录流)?获取文件信息?

下面的Files下的静态方法(传入Path类参数)将返回boolean值,表示检查路径的某个属性的结果:

exists(path);
isHidden(path);
isReadadble(path),isWritable(path),isExecuutable(path);
isRegularFile(path), isDirectory(path), isSymbolicLink(path);
long filesize = Files.size(path); // size方法将返回文件的字节数

静态的Files.list方法会返回一个可以读取目录中各个项的Stream<Path>对象,list方法不会进入子目录。为了处理目录中的所有子目录,需要使用File.walk方法;

有时需要对遍历过程进行更加细粒度的控制。在这种情况下,应该使用File.newDirectoryStream对象,它会产生一个DirectoryStream。注意它不是java.util.stream.Stream的子接口,而是专门用于目录遍历的接口。它是Iterable的子接口,可以在增强的for循环中使用目录流。

try语句块用来确保目录流可以被正确关闭。访问目录中的项并没有具体的顺序。可以用glob模式来过滤文件try (DirectoryStream<Path> entries = Files.newDirectoryStream(dir, "*.java"))

eg:

    @Testpublic void fuc_03() throws IOException {//遍历目录,目录流;Path rootPath = Paths.get("testPath");Stream<Path> entries_01 = Files.list(rootPath);//entries_01.forEach(System.out::println); // 只能当前目录,不能遍历子目录Stream<Path> entries_02 = Files.walk(rootPath);//entries_02.forEach(System.out::println); // 按顺序遍历当前下所有子目录和文件try (DirectoryStream<Path> entries = Files.newDirectoryStream(rootPath)){ // 使用目录流DirectoryStream类,不是Stream而是Iterator的子类,不进入子目录;for (Path entry: entries){System.out.println(entry);}}//可以用glob模式来过滤文件:try (DirectoryStream<Path> entries = Files.newDirectoryStream(rootPath, "*.java"*)){for (Path entry: entries){System.out.println(entry);}}}

内存映射?

大多数操作系统都可以利用虚拟内存实现来将一个文件或文件的一部分“映射”到内存中。然后,这个文件就可以当作是内存数组一样访问,这比传统的文件操作要快得多。

与随机访问相比,性能提高总是很明显的。另一方面,对于中等尺寸文件的顺序读入则没有必要使用内存映射。

首先,从文件中获得一个通道(channel),,通道是用于磁盘文件的一种抽象,它使我们可以访问诸如内存映射、文件加锁机制以及文件间快速数据传递等操作系统特性。

FileChannel channel = FileChannel.open(path,options);

然后,通过调用FileChannel类的map方法从这个通道中获得一个ByteBuffer。可以指定想要映射的文件区域与映射模式,支持的模式options有三种:

  • FileChannel.MapMode.READ_ONLY: 所产生的缓冲区是只读的,任何对缓冲区写入的尝试都会导致ReadOnlyBufferedException异常。
  • FileChannel.MapMode.READ_WRTE:所产生的缓冲区是可写的,任何修改都会在某个时刻写回到文件中。注意:其他映射同一个文件的程序可能不能立即看到这些修改,多个程序同时进行文件映射的确切行为是依赖于操作系统的。
  • FileChannel.MapMode.PRIVATE: 所产生的缓冲区是科协的,但是任何修改过这个缓冲区来说是私有的,不会传播到文件中。

一旦有了缓冲区,就可以使用ByteBuffer类和Buffer超类的方法读写数据了。

缓冲区支持顺序和随机访问数据访问,它有一个可以通过get和put操作来移动的位置。
eg:

    @Testpublic void fuc_04() throws IOException { // 测试内存映射文件,以下为4种读文件的方式;Path path = Paths.get("testPath\\Test_InAndOut_ABC.txt");try (InputStream in = Files.newInputStream(path)) { // 第1种方式,InputStream字节流int c;while ((c = in.**read**()) != -1) {System.out.println(c);}}try (InputStream in = Files.newInputStream(path)) { // 第2种方式,BufferedReader字符BufferedReader br = new BufferedReader(new InputStreamReader(in));String s;while ((s = br.readLine()) != null) {System.out.println(s);}}try (RandomAccessFile file = new RandomAccessFile(path.toFile(), "r")) { // 第3种方式,随机访问文件RandomAccessFile(只读模式)long len = file.length();System.out.println("len:" + len);String s;while ((s = file.readLine()) != null) {s = new String(s.getBytes("ISO-8859-1"), "utf-8");// **解决中文乱码**System.out.println(s);}}try (FileChannel channel = FileChannel.open(path)) { // 第4种方式,内存映射FileChannel类// 创建缓冲区ByteBuffer buf = ByteBuffer.allocate(1024);// 从通道读取数据到缓冲区channel.read(buf);// 反转缓冲区(limit设置为position,position设置为0,mark设置为-1)buf.flip();// 就是判断position和limit之间是否有元素while (buf.hasRemaining()) {// 按照字节的格式获取数据,注意中文乱码https://aliahhqcheng.iteye.com/blog/1797041;System.out.print((char) buf.get());}// 读完将缓冲区还原(position设置为0,limit设置为capacity,mark设置为-1)buf.clear();}}

文件加锁?

考虑一下多个同时执行的程序需要修改同一个文件的情形,这些程序需要以某种方式进行通信,不然这个文件很容易被损坏。文件锁可以解决问题,它可以控制对文件或文件中某个范围的字节的访问。

假设你的应用程序将用户的偏好存储在一个配置文件中,当用户调用这个应用的两个实例时,这两个实例就有可能会同时希望写这个配置文件。这种情况下,第一个实例应该锁定这个文件,当第二个实例发现这个文件被锁定时,它必须决策是等待直至这个文件解锁,还是直接跳过这个文件写操作。

要锁定一个文件,可以调用FileChannel类的lock和tryLock方法:

FileChannel = FileChannel.open(path);
FileLock lock = channel.lock();

FileLock lock = channel.rtyLock();

第一个调用会阻塞直至可获得锁,而第二个调用将立即返回,要么返回锁,要么在锁不可获得的情况下返回null。这个文件将保持锁定状态,直至这个通道关闭,或者在锁上调用了release方法。

还可以通过下面的调用锁定文件的一部分:

FileLock lock (long start, long size, boolean shared)

FileLock tryLock (long start, long size, boolean shared)

如果shared标志为false,则锁定文件的目的是读写,而如果为true,则这是一个共享锁,它允许多个进程从文件中读入,并阻止任何进程获得独占的锁。并非所有的操作系统都支持共享锁,因此你可能会在请求共享锁的时候得到的是独占的锁。

注意:在某些系统中,文件加锁仅仅是建议性的,如果是一个应用未能得到锁,它仍旧可以向被另一个应用并发锁定的文件执行写操作;在网络文件系统上锁定文件时高度依赖与系统的,因此应尽量避免;

对象流?序列化与反射/Clone?

简单地说,序列化就是将对象的状态存储到特定存储介质中的过程,也就是将对象状态转换为可保持或传输格式的过程,在序列化过程中,会将对象的共有成员、私有成员包括雷明,转换为字节流,然后再把字节流写入数据流,存储到存储介质中,这里说的存储介质通常指的是文件。

使用序列化的意义在于,将对象序列化后,可以将其转换为字节序列,这样字节序列就可以被保存在磁盘上,也可以借助网络进行传输,同时序列化后的对象时二进制状态,这样实现了平台无关性。

序列化机制允许将实现了序列化的Java对象转化为字节序列,这个过程需要借助I/O流实现。Java中只有实现了java.io.Serializable接口的类的对象才能被序列化,Serializable表示可串行的、可序列化的,所有序列化有时也称作串行化。JDK中如String类、包装类、Date类等都实现了Serializable接口;

eg:

package cn.ming;import java.io.Serializable;public class Serial_Student<strong> implements Serializable {private String name;private int age;private String gender;private transient String password; // transient修饰,被声明为transient的password属性不会被序列化,static对象也不会被序列化public Serial_Student(String name, int age, String gender, String password) {this.name = name;this.age = age;this.gender = gender;this.password = password;}public String getName() {...} // getter and setter.public String toString() {return "姓名为:" + this.getName() + "\n" +"年龄为:" + this.getAge() + "\n" +"性别为:" + this.getGender() + "\n" +"密码为:" + this.getPassword();}
}
// test
package cn.ming;import java.io.*;
import java.util.List;
import java.util.ArrayList;public class Serial_Test_01 {public static void main(String[] args) throws IOException, ClassNotFoundException {ObjectOutputStream oos = null;ObjectInputStream ois = null;try {// 创建ObjectOutputStream输出流oos = new ObjectOutputStream(new FileOutputStream("testPath\\Test_StudentsInfo_ObjStream.txt"));Serial_Student stu = new Serial_Student("安娜", 30, "女", "aaaa");Serial_Student stu1 = new Serial_Student("李白", 18, "男", "123456");List<Serial_Student> list = new ArrayList();list.add(stu);list.add(stu1);System.out.println("write in:");list.forEach(System.out::println);// 对象序列化,写入输出流oos.writeObject(list);// 创建ObjectInputStream输入流ois = new ObjectInputStream(new FileInputStream("testPath\\Test_StudentsInfo_ObjStream.txt"));// 反序列化,强转类型List<Serial_Student> list_Students =(ArrayList)ois.readObject();System.out.println("read out:");// 输出生成后对象信息list_Students.forEach(System.out::println);} catch (IOException ex) {ex.printStackTrace();} finally {try {if (oos != null) {oos.close();}if (ois != null) {ois.close();}} catch (IOException ex) {ex.printStackTrace();}}}
}

使用反序列化获取对象信息?

反序列化就是从特定存储介质中读取数据并重新构成对象的过程。大致分为2步:

1)创建一个对象输入流(ObjectInputStream),它可以板状一个其他类型的输入流,如FileInputStream;

2)通过对象输入流的readObject()方法读取对象,该方法返回一个Object类型的对象,需要进行强制转换成真实的对象类型;

在对象序列化时需要注意

  • 序列化之后保存的是类的信息;
  • 被声明为transient的属性不会被序列化(出于安全考虑),这就是transient关键字的作用;
  • 被声明为static的属性不会被序列化,这个问题可以这么理解,序列化保存的是对象的状态,但是static修饰的变量是属于类的而不是属于变量的,因此序列化的时候不会序列化它;
  • 如果向文件中使用序列化写入多个对象,则反序列化恢复对象时必须按照写入的顺序读取;
  • 如果一个可序列化的类有多级父类,则这些父类要么也是可序列化的,要么有无参数的构造器,否则会抛出异常;
  • 如果一个类的成员中包含其他类的对象,如班级类中包含学生类型的对象,那么要序列化班级类时,则必须保证班级类和学生类都是可序列化的。即当需要序列化某个特定对象时,它的各个成员对象也必须是可序列化的;

将字节流格式的被序列化的对象经过传输,恢复成原来的对象,需要类的信息,会用到反射

关于反射的注意:使用反射虽然会很大程度上提高代码的灵活性,但是不能滥用反射,因为通过反射创建对象时性能稍微要低一些

实际上,只有当程序需要动态创建某个类的对象时才会考虑使用反射,通常在开发通用性比较强的框架、基础平台时可能会大量使用反射,因为很多Java框架中都需要根据配置问价信息来创建Java对象,从配置文件读取的只是某个类的字符串类名,程序需要根据字符串来创建对应的实例,就必须使用反射。

在实际开发中,没有必要使用反射来访问已知类的方法和属性,只有当程序需要动态创建某个类的对象时才会考虑使用。

正则表达式?(需要用的时候去查)

正则表达式可以用来搜索、编辑或处理文本,具体地说,用来匹配字符串的“某一段”,可以对其进行“判断匹配Matcher-返回true/false、删除、替换ReplaceAll、依次查找find...

如何使用?eg:

     boolean isMatch = Pattern.matches(patternStr, content);
     Pattern pattern = Pattern.compile(patternString, Pattern.CASE_INSENSITIVE);Matcher matcher = pattern.matcher(input);while (matcher.find()){String match = matcher.group();System.out.println(match);}

《Java核心技术 卷II》笔记——(7)输入/输出流文件流序列化相关推荐

  1. Java 核心技术卷 II(第 8 版) – 读书笔记 – 第 1 章(下)

    22.一旦获得了一个 Charset,就可以在 Java 的 Unicode 和指定的编码格式之间进行转化,下面以 GBK 和 Unicode 之间做为例子. 从 Unicode 到 GBK: imp ...

  2. 新书推荐 | Java核心技术 卷II 高级特性(原书第11版)

    新书推荐 <Java核心技术 卷II 高级特性(原书第11版)> 长按二维码 了解及购买 全新第11版!针对Java SE9.10.11全面更新!Java领域极具影响力和价值的著作之一,与 ...

  3. Java编程思想+Effective Java+Java核心技术+Java核心技术 卷II+Java语言程序设计(中文+英文+源码)

    Java四大名著(中文+英文+源码 ) 传说中的java四大名著,分享出来方便大家学习! 书名如下: Java编程思想 Effective Java(第2版) Java核心技术 卷I(第8版) Jav ...

  4. Java核心技术 卷II 高级特性 原书第9版pdf

    下载地址:网盘下载 内容简介  · · · · · · Java领域最有影响力和价值的著作之一,由拥有20多年教学与研究经验的资深Java技术专家撰写(获Jolt大奖),与<Java编程思想&g ...

  5. Java核心技术卷1——笔记(1)

    2019独角兽企业重金招聘Python工程师标准>>> 今天读到卷一中对象与类的相关内容,当中关于对象变量的描述对我还是很有启发的. 第一点: 那就是"一个对象变量并没有实 ...

  6. 《Java核心技术 卷Ⅰ》读书笔记一

    Java核心技术·卷 I(原书第10版) 作者: [美] 凯.S.霍斯特曼(Cay S. Horstmann) 出版社: 机械工业出版社 原作名: Core Java Volume I - Funda ...

  7. 《Java 核心技术 卷1》 笔记 第11章 异常、日志、断言和调试

    出现不可预计的问题时,需要进行如下处理: 报告错误 保存操作结果 允许用户退出 本章解决的问题: 验证程序正确性 记录程序错误 调试技巧 11.1 处理异常 程序出现错误时应该: 返回安全状态,能让用 ...

  8. java核心技术卷I 第1-3章 笔记

    java核心技术卷I 第1-3章 本书将详细介绍下列内容: ● 面向对象程序设计 ● 反射与代理 ● 接口与内部类 ● 异常处理 ● 泛型程序设计 ● 集合框架 ● 事件监听器模型 ● 使用Swing ...

  9. 《Java 核心技术 卷1》 笔记 第五章 继承(3)

    5.1.6 抽象类 有时候我们无法说出具体是什么,只能用于标识一个类型,比如图形,就可作为抽象类.虽然无法具体描述图形,但是图形通常都有面积.周长.这种时候就可用抽象类标识. 抽象类使用abstrac ...

最新文章

  1. Echart常用效果(一)
  2. 大型网站架构的发展演变过程
  3. HAProxy + Keepalived + Flume 构建高性能高可用分布式日志系统
  4. 发红包android
  5. Leetcode1143. 最长公共子序列(c#)
  6. CVS 客户端使用手册
  7. sqlite 模糊匹配日期_SQLite模糊查找(like) | 学步园
  8. verilog实现多周期处理器之——(六)简单算数操作指令的实现
  9. fedora core 7下如何安装Fcitx小企鹅输入法
  10. 如何使用 Siri 拨打电话并使用电话功能?
  11. 用JS实现人物走动动画效果
  12. Unity3D开发体验
  13. AutoCAD Civil 3D-部件-部件编辑器自定义边坡与材质
  14. 网络和共享中心 服务器运行失败,win10网络共享失败提示共享依赖服务无法启动的解决办法...
  15. 你真的理解devDependencies和dependencies区别吗?
  16. office2019怎么在同一个窗口显示多个文件
  17. Steve Jobs 2005年于 Stanford University 毕业典礼上的演讲
  18. NYIST汉诺塔(一)(三)问题以及汉诺塔的路径实现
  19. 利用VS安装项目打包软件的做法
  20. 2021年绵阳东辰中学高考成绩查询,2021年绵阳中考成绩和分数线什么时候公布(附查询入口)...

热门文章

  1. QQ2013协议分析(解密篇)
  2. 服务器显示na什么意思,游戏服务器na是什么意思
  3. c语言 编程结束怎么表示,c语言开头(c语言编程开头和结束代码)
  4. 我的500行代码 VS 资深大佬的50行代码
  5. (转载)WebAssembly,Web的新时代
  6. colorkey唇釉是否安全_colorkey空气唇釉怎么样 人气口红种草 – 爱打扮
  7. matlab e52pt,帮我看看Matlab怎么改这个错误?
  8. phpmyadmin爆破脚本练习
  9. Android 给EditText添加下划线
  10. 李阳疯狂英语-228句口语要素