Java IO

Java.io 包提供了所有同步 IO 的功能;

File对象

java 标准库 Java.io 提供了 File 对象来操作文件和目录;

构造 File 对象时,既可以传入绝对路径,也可以传入相对路径;

如:

File f = new File(“C:\\windows\notepad”);

注意:Windows 使用 ‘\’ 作为路径分隔符(java中用 \\ 表示),Linux 中使用 ‘/’;
File 对象的3种路径表示方式:

  • ​getPath() # 返回构造方法传入的路径;

  • getAbsolutePath() # 返回绝对路径;

  • ​getCanonicalPath() # 返回规范路径,不包含 . 和 … 的绝对路径;

获取系统分隔符: File.separator;

文件和目录:

File 对象既可以表示文件,也可以表示目录,构造File对象时,即使传入的文件或目录不存在,代码也不会报错,因为不会导致任何磁盘操作;

只有调用 File 对象的某些方法时,才会真正进行磁盘操作;

如:

​ isFile() # 判断该 File 对象是否是一个已存在的文件;

​ isDirectory() # 判断是否是一个已存在的目录;

判断文件的权限和大小:

  • ​boolean canRead()

  • boolean canWrite()

  • ​boolean canExecute()

  • long length() : 文件字节大小

对目录而言,是否可执行表示能否列出它包含的文件和子目录;

创建和删除文件:

当File对象表示一个文件时,可以通过 createNewFile() 创建一个新文件,用 delete() 删除该文件;

例:

File file = new File(“/path/to/file”);

file.createNewFile();

file.delete();

创建临时文件:

​ File f = File.createTempFile(“tmp-”,“.txt”); // 提供临时文件的前缀和后缀;

​ JVM 退出时自动删除该文件 : f.deleteOnExit();

遍历文件和目录:

当 File 对象表示一个目录时,可以使用 list()listFiles() 列出目录下的文件和子目录名;

listFiles() 提供了一系列重载方法,可以过滤不想要的文件和目录;

例:

​ File f = new File(“C:\windows”);

​ File[] fs1 = f.listFiles();

过滤:

File[] fs2 = f.listFiles(new FilenameFilter(){ //仅列出 .exe 文件
    public boolean accept(File dir,String name){
        return name.endsWith(".exe"); //返回 true 表示接受该文件
    }
});

File 对象如果表示一个目录:

  • ​boolean mkdir() : 创建当前 File 对象表示的目录;

  • ​boolean mkdirs() : 创建当前File对象表示的目录,并在必要时将不存在的父目录也创建出来;

  • ​boolean delete() : 删除当前 File 对象表示的目录,当前目录必须为空才能删除成功;

Paths 工具类

java.nio.file 包,提供了一个 Path 对象;

Path p1 = Paths.get(".","project","study"); // 构造一个 Path 对象;
Path p2 = p1.toAbsolutePath(); //转换为绝对路径;
Path p3 = p2.normalize(); //转换为规范路径;
File f = p3.toFile(); // 转换为 File 对象;

Files工具类

java.nio 包中的 Files 工具类,可以方便我们读写文件;

常用的有:

把一个文件的全部内容读取为一个 byte[]:

​ byte[] data = Files.readAllBytes(Paths.get(“/path/file.txt”));

把一个文件的全部内容读取为 String:

​ String content = Files.readString(Paths.get(“/path/to/file.txt”), StandardCharsets.UTF-8);

按行读取并返回每行内容:

List lines = Files.readAllLines(Paths.get(“/path/file.txt”));

写入一个二进制文件

​ byte[] data = …

​ Files.write(Paths.get(“/path/file.txt”),data)

写入文本并指定编码

​ Files.writeString(Paths.get(“/path/file.txt”),“文本内容”, StandardCharsets.UTF-8);

按行写入文本

​ List lines = …

​ Files.write(Paths.get(“/path/file.txt”),lines);

注意:Files提供的读写方法,受内存限制,只能读写小文件,例如配置文件等;

InputStream

InputStream 是一个抽象类,是所有输入流的超类;

Java 的 IO 标准提供的 InputStream 根据来源不同可以分为:

  • FileInputStream: 从文件读取数据;

  • ServletInputStream : 从 HTTP 请求读取数据;是一个抽象类;

  • Socket.getInputStream() : 从 TCP 连接读取数据;

最重要的方法:

read() : 读取输入流的下一个字节,返回字节表示的int值(0-255),如果读到末尾,则返回 -1;
缓冲

对于文件和网络流来说,利用缓冲区一次性读取多个字节,效率往往高很多;

InputStream 提供了两个重载方法,来支持读取多个字节:

​ int read(byte[] b) : 读取若干字节并填充到 byte[] 数组,返回读取的字节数(注意是字节数!与read方法不同);

​ int read(byte[] b , int off , int len ) : 指定 byte[] 数组的偏移量和最大填充数;

注意:

  • 如果返回 -1 , 表示没有更多数据了;

  • java.io 中的流都是同步的,也就是说调用方法时会阻塞;

InputStream 的子类:

​ 1、FileInputStream : 用来从文件流中读取数据;

​ InputStream input = new FileInputStream(“src/readme.txt”);

​ 2、ByteArrayInputStream:用来在内存中模拟一个 InputStream;

​ byte[] data = {1,2,3,4};

​ InputStream input = new ByteArrayInputStream(data);

流的关闭:

打开的流,使用完毕后应该及时通过 close() 来关闭,释放底层资源;

方式一:

InputStream input = null;
try{
    input = new FileInputStream("src/readme.txt");
    int n ; 
    while(n = input.read() != -1){
        
    }
}finally{
    if(input!=null){
        input.close();
    }
}

方式二:

使用 try(resource) 的语法,编译器会自动帮我们关闭资源( 在 try(resource) 块的结束处自动关闭);

原理:编译器只看 try(resource=…) 中的对象是否实现了 java.lang.AutoCloseable 接口,如果实现了,就自动加上 finally 语句并调用 close() 方法;

try(InputStream input = new FileInputStream("src/readme.txt")){}

同时操作多个 AutoCloseable 资源时,在 try(resource){…} 语句中可以同时写出多个资源,用 ;隔开。

OutputStream

最重要的方法:

​1、void write(int b) # 写入一个字节到输出流;

​注意:虽然传入的是 int ,但只会写入 int 的最低 8 位;

​2、void write( byte[] ) # 写入若干字节到输出流;// output.write(“hello”.getBytes(“UTF-8”));

​3、close() # 关闭输出流;

​4、flush() # 将缓冲区中的内容真正输出到目的地;

注意:缓冲区写满,或者调用 close() 都会自动触发 flush() 方法的调用;

OutputStream 的实现类:

​ 1、FileOutputStream : 文件输出流;

​ 例:

​ OutputStream outPut = new FileOutputStream(“out/read.txt”);

​ outPut.write(97);

​ 2、ByteArrayOutputStream : 在内存中模拟一个输出流,把 byte 数组在内存中变成一个OutputStream;

​ 例:

​ ByteArrayOutputStream outPut = new ByteArrayOutputStream();

​ byte[] data = null;

​ outPut.write(“hello”.getBytes(“UTF-8”));

​ data = outPut.toByteArray();

​ // outPut.toString(StandardCharsets.UTF-8);

​ sout(new String(data,“UTF-8”));

Filter 模式

JDK 中 InputStream 的继承关系:

InputStream : 顶级接口

​ 基础输入流:

​ FileInputStream

​ ByteArrayInputStream

​ ServletInputStream【抽象类】

​ 提供附加功能的输入流:

​ FilterInputStream:

​ BufferedInputStream : 提供缓冲;

​ DigestInputStream : 计算签名;

​ CipherInputStream:加密/解密;
使用:

​ InputStream file = new FileInputStream(“test.gz”);

​ 增加缓冲区:

​ InputStream buffered = new BufferedInputStream(file);

​ 读取解压缩的内容:

​ InputStream bufferded = new GZIPIputStream(buffered);
DigestInputStream使用示例

MessageDigest md = MessageDigest.getInstance(digestName);
InputStream input = new DigestInputStream(request.getInputStream(), md);
byte[] result = md.digest()
//将一个字节数组转换成 BigInteger ,然后转换成十六进制字符串
// BigInteger(int signum, byte[] magnitude) # 将一个字节数组转换为BigeInteger, signum=-1表示负, 0 表示 0, 1表示正;
new BigInteger(1,result).toString(16);

这种通过一个 “基础” 组件再叠加各种 “附加” 功能组件的模式,称之为装饰器模式;

优势:可以让我们通过少量的类来实现各种功能的组合(避免子类爆炸);

在叠加多个 FilterInputStream 时,我们只需要持有最外层的 InputStream, 当最外层的 InputStream 关闭时,内层 InputStream 的close( ) 方法也会被自动调用;

读取 classpath 资源

把资源存储在 classpath 中可以避免文件路径依赖;

try( InputStream input = getClass().getResourceAsStream(“/default.properties”) ){//TODO}

在 classpath 中的资源文件,路径总是以 / 开头;

如果资源文件不存在,它将返回 null;

序列化

序列化是指把一个 Java 对象变成二进制内容,本质上就是一个 byte[] 数组;

序列化后可以把 byte[] 保存到文件中,或者把 byte[] 通过网络传输到远程;

反序列化: 把一个 byte[] 数组,变回 java 对象;

如何序列化:

1、一个 java 对象要能序列化,必须实现一个特殊的 java.io.Serializable 接口,此接口没有任何方法,是一个空接口,又称标记接口;

2、使用: ObjectOutputStream 把一个 java 对象写入一个字节流;【ObjectOutputStream(OutputStream out)】

​ ByteArrayOutputStream buffer = new ByteArrayOutputStream();

​ ObjectOutputStream ouput = new ObjectOutputStream(buffer);

​ //写入 int

​ output.writeInt(123);

​ //写入String

​ output.writeUTF(“hello”)

​ //写入 Object

​ output.writeObject(Double.valueOf(“123.1”));

如何反序列化:

使用 ObjectInputStream : 从一个字节流读取 Java 对象;【ObjectInputStream(InputStream in)】

try(ObjectInputStream input = new ObjectInputStream(…)){

​ int n = input.readInt();

​ String s = input.readUTF();

​ Double d = (Double) input.readObject();

}

readObject 可能抛出的异常有:

​ ClassNotFoundException : 没有找到对应的 class;

​ InvalidClassException:Class不匹配;

为了避免class定义变动导致的不兼容,Java 的序列化允许class定义一个特殊的 serialVersionUID 静态变量,用于标识 Java 类的序列化 “版本”, 通常可以由 IDE 自动生成,如果增加或修改了字段,可以改变 serialVersionUID的值,这样就能自动阻止不匹配的 class 版本;

注意:反序列化时,由 JVM 直接构造出 Java对象,不调用构造方法,构造方法内部的代码,在反序列化时根本不可能执行;

更好的序列化方法是通过 JSON 这样的通用数据结构来实现,只输出基本类型(包括 String)的内容,而不存储任何与代码相关的信息

Reader(抽象类)

java.io.Reader : 是所有字符输入流的超类;

InputStream 是字节流,以 byte(0-255)为单位读取,读到字节数组 int read(byte[] b);

而 Reader 是 字符流,以 char(2个字节0-65535)为单位读取,读到字符数组 int read(char[] c);

InputStreamReader 类继承于 Reader 抽象类,FileReader 继承于 InputStreamReader;

主要方法:

​ read() : 读取字符流的下一个字符,并返回字符表示的 int, 范围 0-65535,读到末尾返回-1;

​ int read( char[] c ):一次读取若干字符,并填充到 char[] 数组;返回实际读入的字符个数

​ int read(char cbuf[] , int off, int len) : off:从字符数组的哪里开始存储字符,len:读取的字符数,

Reader的子类:

1、FileReader:

​ Reader reader = new FileReader(“src/reademe.txt”,StandardCharsets.UTF_8); // 需要指定字符编码;

2、CharArrayReader : 在内存中模拟一个 Reader,把一个 char[] 数组变成一个 Reader;

​ Reader reader = new CharArrayReader(“hello”.toCharArray);

3、StringReader : 可以直接把 String作为数据源,它和 CharArrayReader 几乎一样;

​ Reader reader = new StringReader(“Hello”)

4、BufferedReader 额外定义的方法;

​ String readLine() : 读取一行,不包含行终止符 和 null;

​ Stream lines() # Return a Stream, the elements of which are lines read from this BufferedReader;

注意:

readLine() 是一个阻塞函数,当没有数据读取时,就一直会阻塞在那,而不是返回 null;

readLine() 使用的 buffer 有 8192 个字符。

使用 socket 之类的数据流时,要避免使用 readLine(), 以免为了等待一个换行/回车符而一直阻塞;

InputStreamReader

普通的 Reader 实际上是基于 InputStream 构造的,因为 Reader 需要从 InputStream 中读入字节流(byte),然后,根据编码设置,再转换为 char 就可以实现字符流;

Reader 本质上是一个基于 InputStream 的 byte 到char的转换器;

InputStreamReader 就是这样一个转换器,它可以把任何 InputStream 转换为 Reader ;

例:

InputStream input = new FileInputStream(“src/readme.txt”);

//变换为 Reader,需要传入 InputStream ,还需要指定编码;

Reader reader = new InputStreamReader(input,“UTF-8”);

Writer

Writer 是带编码转换器的 OutPutStream, 把 char 转换为 byte 并输出;

Writer 是所有字符输出流的超类;

OutputStream:

​ 字节流,以 byte 为单位;

​ 写入字节:void write( int b )

​ 写入字节数组:void write( byte[] b )

Writer:

​ 字符流,以char为的那位;

​ 写入字符(0-65535): void write(int c)

​ 写入字符数组:void write(char[] c)

写入String: void write(String s);

Writer的实现类:

1、FileWriter : 向文件中写入字符流;

​ Writer writer = new FileWriter(“readme.txt”,StandardCharsets.UTF-8);

​ writer.write(“H”); 写入字符

​ writer.write(“hello”.toCharArray()); 写入 char[]

​ writer.write(“Hello”); 写入 String;

2、CharArrayWriter: 可以在内存中创建一个Writer,构造一个缓冲区,可以写入 char,最后得到写入的char[]数组;

​ CharArrayWriter writer = new CharArrayWriter();

​ writer.write(65);

​ writer.write(66);

​ char[] data = writer.toCharArray();

3、StringWriter : 基于内存的Writer;

OutputStreamWriter

1、Writer 实际上是基于 OutputStream 构造的,接收char, 然后在内部自动转换成byte,并写入 OutputStream;

2、OutputStreamWriter 是一个将任意 OutputStream 转换为 Writer 的转换器;

Writer writer = new OutputStreamWriter(new FileOutputStream(“readme.txt”),“UTF-8”);

PrintStream/PrintWriter

PrintStream

PrintStream 是一种 FilterOutputStream (继承了OutputStream),额外提供了一些写入各种数据类型的方法;

写入int: print(int)

写入 boolean : print(boolean)

写入String:print(String)

写入 Object:print(Object) // 相当于print(object.toString())

println() : 会自动加上换行符;

System.out : 是系统默认提供的 PrintStream;

System.err : 是系统默认提供的标准错误输出;

PrintWriter

PrintWriter 扩展了 Writer 接口,它的print() /println() 方法最终输出的是char数据;

StringWriter buffer = new StringWriter();
try(PrintWriter pw = new PrintWriter(buffer)){
    pw.prinln("hello");
    pw.println(1234);
    pw.println(true);
}
System.out.println(buffer.toString());
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值