ToB企服应用市场:ToB评测及商务社交产业平台
标题:
Java IO
[打印本页]
作者:
水军大提督
时间:
2023-12-14 12:43
标题:
Java IO
IO(输入/输出)是每个程序都必须的部分。使用输入机制,程序可以读取到外部数据(例如来磁盘、光盘、网络等);使用输出机制,程序可以将数据输出到外部, 例如,把数据从内存写入到文件,把数据从内存输出到网络等等。
Java 的 IO 通过 java.io 包下的类和接口来支持,在java.io 包下主要包括输入、输出两种IO流,没中输入输出流又可分为字节流和字符流两大类。其中字节流以字节为单位来处理输入、输出操作,而字符流则以字符来处理输入、输出操作
File 类
使用File 类可以操作文件和目录。需要指出的是,File 类能新建、删除、重命名文件和目录,但File 类不能访问文件内容本身。如果需要访问文件内容本身,则需要通过IO来获取
File 类可以使用文件路径字符串来创建 File 实例,该文件路径字符串既可以是绝对路径,也可以是相对路径
// 通过绝对路径创建File 类
new File("C:\\Windows\\notepad.exe");
// 通过相对路径来创建File类
File f3 = new File(".\\sub\\javac");
复制代码
注意Windows平台使用\作为路径分隔符,在Java字符串中需要用\\表示一个\。Linux平台使用/作为路径分隔符:
创建 File 对象后,就可以通过File对象来操作文件和目录,下面列入一些比较常用的方法
1. 访问文件名相关方法
String getName() 返回此 File 对象所表示的文件名或路径名
String getPath() 返回此 File 对象所对应的路径名
File getAbsoluteFile() 返回此对象所对应的绝对路径
String getAbsoluteFile() 返回此File 对象所对应的绝对路径名
String getParent() 返回此 File 对象所对应目录的父目录名
boolean renameTo(File newName) 重命名此File 对象所对应的文件或目录,如果重命名成功,则返回true,否则返回false
2. 文件检测方法
boolean exists() 判断 File 对象所对应的文件或目录是否存在
boolean canWrite() 判断 File 对象所对应的文件和目录是否可写
boolean canRead() 判断 File 对象所对应的文件和目录是否刻度
boolean isFile() 判断 File 对象所对应的是否是文件
boolean isDirectory() 判断 File 对象所对应的是否是目录,而不是文件
boolean isAbsolute() 判断 File 对象所对应的文件或目录是否是绝对路径
3. 获取常规文件信息
long lastModified() 返回文件的最后修改时间
long length() 返回文件内容的长度
4. 文件操作相关方法
boolean createNewFile() 新建一个该 File 对象所指定的新文件,创建成功返回 true,否则返回false
boolean delete() 删除 File 对象所对应的文件或路径
static File createTempFile(String prefix,String suffix) 在默认的临时文件目录中创建一个临时的空文件,使用给定前缀和给定后缀作为文件名
static File createTempFile(String prefix,String suffix,File directory) 在 directory 所指定的目录中创建一个临时的空文件,使用给定前缀和给定后缀作为文件名
void deleteOnExit() 注册一个删除钩子,指定当 Java 虚拟机退出时,删除File对象所对应的文件和目录
5. 目录操作相关方法
boolean mkdir() 试图创建一个 File 对象所对应的目录,如果创建成功则返回true
String[] list() 列出 File 对象的所有子文件名和路径名
String[] list(FilenameFilter filter) FilenameFilter 是一个函数式接口,该接口包含了一个accept(File,String name) 方法,可以通过该参数列出符合条件的文件
File[] listFiles() 列出 File 对象所有的子文件和路径
static File[] listRoots() 列出系统所有的根路径
public class FileTest {
public static void main(String[] args) throws Exception {
// 以当前路径创建一个File 对象
File file = new File(".");
// 打印文件名
System.out.println(file.getName());
// 打印相对路径的上一级路径,由于相对路径只是一个点 所以输出null
System.out.println(file.getParent());
// 打印绝对路径
System.out.println(file.getAbsolutePath());
// 打印绝对路径的上一级路径
System.out.println(file.getAbsoluteFile().getParent());
// 创建一个临时文件
File temp = File.createTempFile("aaa", ".txt", file);
// 注册一个删除钩子,当JVM 退出时删除该文件
temp.deleteOnExit();
// 输出当前File 目录下所有的子文件和文件夹名
for (String filename : file.list()) {
System.out.print(filename + "\t");
}
// 换行
System.out.println();
// 输出系统所有的根目录
for (File root : File.listRoots()) {
System.out.println(root);
}
// 通过条件过滤出以.txt 结尾的文件
for (String filename : file.list(((dir, name) -> name.endsWith(".txt")))) {
System.out.print(filename + "\t");
}
}
}
复制代码
输出
.
null
F:\yfd\java\JaveTest\.
F:\yfd\java\JaveTest
.idea aaa8648461841651352798.txt Annotation IODemo JDBCDemo out pom.xml
C:\
D:\
E:\
F:\
aaa8648461841651352798.txt
复制代码
Java 的 IO 流
流的分类
按照不同的分类方式,可以将流分为不同类型
1. 输入流和输出流
输入流:只能从中读取数据,而不能向其写入数据,主要由 InputStream 和 Reader 作为基类
输出流:只能向其写入数据,而不能从中读取数据,主要由 OutputStream 和 Writer 作为基类
2. 字节流和字符流
字节流和字符流的用法几乎一样,区别在于操作的数据单元不同——字节流操作的数据单元是8位的字节,字符流操作的数据单元是16位的字符
3. 节点流和处理流
按照流的角色来分,可以分为节点流和处理流。
可以从/向一个特定的 IO 设备 读/写 数据的流,称为节点流。处理流则用于对一个已存在的流进行连接和封装,通过封装后的流来实现数据读/写功能
字节流和字符流
字节流和字符流的操作方式几乎一样,区别只是操作的数据单元不同
InputStream 和 Reader
InputStream 和 Reader 是所有输入流的抽象基类
InputStream 里包含如下三个方法
int read() 从输入流中读取单个字节
int read(byte[] b) 从输入流中最多读取 b.length 个字节的数据,并将其存储在字节数组 b 中,返回实际读取的字节数
int read(byte[] b,int off,int len) 从输入流中最多读取 len 个字节的数据,并将其存储在 数组b 中,放入数组b中时,是从 off 位置开始,返回实际读取的字节数
Reader 包含如下三个方法
int read() 从输入流中读取单个字符
int read(char[] cbuf) 从输入流中最多读取cbuf.length 个字符的数据,并将其存储在字符数组 cbuf中,返回实际读取的字符数
int read(char[] cbuf,int,int len) 从输入流中最多读取len 个字符的数据,并将其存储在字符数组cbuf 中,防暑数组cbuf中时,是从off 位置开始,返回实际读取的字符数
InputStream 实例
public static void main(String[] args) throws IOException {
try (
// 创建字节输入流,IO 流实现了AutoCloseable 接口,因此可通过try语句来关闭IO流
FileInputStream fis = new FileInputStream("test.txt");
) {
// 创建一个长度128的字节数组
byte[] data = new byte[128];
int len = 0;
// 向数组中装填数据
while ((len = fis.read(data)) > 0) {
// 将字节数组转换为字符串输入
System.out.print((new String(data, 0, len)));
}
}
}
复制代码
Reader 实例
public class ReaderTest {
public static void main(String[] args) throws IOException {
try (
// 创建一个字符输入流
FileReader fr = new FileReader("test.txt");
) {
// 表示当前字符
int data = 0;
// 通过read() 方法循环读取流,如果到底则返回-1
while ((data = fr.read()) != -1) {
System.out.print((char) data);
}
}
}
}
复制代码
除此之外, InputStream 和 Reader 还支持如下几个方法来移动记录指针
void mark(int readAheadLimit) 在记录指针当前位置记录一个标记(mark)
boolean markSupported() 判断此输入流是否支持 mark() 操作
void reset() 将此流的记录指针重新定位到上一次标记(mark)的位置
long skip(long n) 记录指针向后移动 n 个字节/字符
OutputStream 和 Writer
OutputStream 和 Writer 是输出流的基类,两个流都提供了如下三个方法
void write(int c) 将指定的 字节/字符 输出到输出流中,其中c既可以代表字节,也可以代表字符
void write(byte[]/char[] buf) 将 字节/字符 数组中的数据输出到指定输出流中
void write(byte[]/char[] buf,int off,int len) 将 字节/字符 数组从off 位置开始,长度为len 的 字节/字符 输出到输出流中
除此之外,Writer 还包含如下两个方法
void write(String str) 将 str 字符串里包含的字符输出到指定输出流中
void write(String str,int off,int len) 将 str 字符串里从 off 位置开始,长度为 len 的字符输出到指定输出流中
OutputStream 示例
public class FileOutputStreamTest {
public static void main(String[] args) throws IOException {
File file = new File("test2.txt");
// 如果文件不存在则创建
if (!file.exists()) {
file.createNewFile();
}
try (
// 创建字节输入流
FileInputStream fis = new FileInputStream("test.txt");
// 创建字节输出流
FileOutputStream fos = new FileOutputStream("test2.txt");
) {
byte[] buf = new byte[2];
int readLen = 0;
while ((readLen = fis.read(buf)) > 0) {
// 将字节数组写入文件流
fos.write(buf, 0, readLen);
}
}
}
}
复制代码
Writer 示例
public class FileWriterTest {
public static void main(String[] args) throws IOException {
try (
FileWriter fw = new FileWriter("test.txt");
) {
fw.write("锦瑟 \r\n");
fw.write("锦瑟无端五十弦, \r\n");
fw.write("一弦一柱思华年。 \r\n");
fw.write("庄生晓梦迷蝴蝶, \r\n");
fw.write("望帝春心托杜鹃。 \r\n");
}
}
}
复制代码
处理流的用法
处理流隐藏了底层设备上的节点流的差异,并对外提供了更方便的输入/输出方法,让程序员只需关心高级流的操作
下面通过使用 PrintStream 处理流来包装 OutputStream,使用处理流后的输出流在输出时将更加方便
public class PrintStreamTest {
public static void main(String[] args) throws IOException {
try (
FileOutputStream fos = new FileOutputStream("test.txt");
PrintStream ps = new PrintStream(fos);
) {
ps.println("Java");
}
}
}
复制代码
可以看到test.txt文件的内容已经变成了 Java
IO 流的体系结构
Java IO流体系中常用的流分类
分类字节输入流字节输出流字符输入流字符输出流抽象基类InputStreamOutputStreamReaderWriter访问文件FileInputStreamFileOutputStreamFileReaderFileWriter访问数组ByteArrayInputStreamByteArrayOutputStreamCharArrayReaderCharArrayWriter访问管道PipedInputStreamPipedOutputStreamPipedReaderPipedWriter访问字符串StringReaderStringWriter缓冲流BufferedInputStreamBufferedOutputStreamBufferedReaderPipedWriter转换流InputStreamReaderOutputStreamWriter对象流ObjectInputStreamObjectOutputStream抽象基类FilerInputStreamFilterOutputStreamFilterReaderFilterWriter打印流PrintStreamPrintWriter推回输入流PushBackInputStreamPushbackReader特殊流DataInputStreamDataOutputStream
转换流
IO 流体系中还提供了两个用于实现将字节流转换成字符流的转换流,其中 InputStreamReader 将字节输入流转换成字符输入流,OutputStreamWriter 将字节输出流转换成字符输出流
public class StreamTest {
public static void main(String[] args) throws IOException {
try (
FileInputStream fis = new FileInputStream("test.txt");
// 将字节流转换成字符流
InputStreamReader isr = new InputStreamReader(fis);
) {
char[] data = new char[32];
int len = 0;
while ((len = isr.read(data)) > 0) {
System.out.println(String.valueOf(data,0,len));
}
}
}
}
复制代码
推回输入流
PushbackInputStream 和 PushbackReader都带有一个推回缓冲区,当程序调用这两个退回输入流的 unread() 方法时,系统将会吧指定数组的内容退回到缓冲区里,而退回输入流每次调用 read() 方法时总是先从退回缓冲区读取
当程序创建一个退回流时,需要指定退回缓冲区的大小,默认的推回缓冲区的长度为1。如果程序中退回的长度超出了缓冲区的大小,将会引发异常
public class PushbackTest {
public static void main(String[] args) throws IOException {
try (
PushbackReader pr = new PushbackReader(new FileReader("test.txt"), 64);
) {
int c = 0;
while ((c = pr.read()) != -1) {
System.out.print((char) c);
}
// test.txt 最后一个字符是 a,将此字符推回输入流
pr.unread('a');
// 获取缓冲区的字符
System.out.println((char) pr.read());
}
}
}
复制代码
输出
Javaa
复制代码
读写其他进程数据
使用 Runtime 对象的 exec() 方法可以运行平台上的其他程序。Process 类提供了如下三个方法,用于让程序和其子进程进行通讯
InputStream getErrorStream() 获取子进程的错误流
InputStream getInputStream() 获取子进程的输入流
OutputStream getOutputStream() 获取子进程的输出流
public class ProcessStreamTest {
public static void main(String[] args) throws IOException {
Process p = Runtime.getRuntime().exec("javac");
try (
// 将字节流转换为字符流,再交给缓冲流(处理流)包装
BufferedReader br = new BufferedReader(new InputStreamReader(p.getErrorStream(),"GBK"))
){
String buff = null;
// 循环输出 p 进程的错误输出
while ((buff = br.readLine()) != null){
System.out.println(buff);
}
}
}
}
复制代码
输出
用法: javac <options> <source files>
其中, 可能的选项包括:
-g 生成所有调试信息
-g:none 不生成任何调试信息
-g:{lines,vars,source} 只生成某些调试信息
-nowarn 不生成任何警告
-verbose 输出有关编译器正在执行的操作的消息
-deprecation 输出使用已过时的 API 的源位置
-classpath <路径> 指定查找用户类文件和注释处理程序的位置
-cp <路径> 指定查找用户类文件和注释处理程序的位置
-sourcepath <路径> 指定查找输入源文件的位置
-bootclasspath <路径> 覆盖引导类文件的位置
-extdirs <目录> 覆盖所安装扩展的位置
-endorseddirs <目录> 覆盖签名的标准路径的位置
-proc:{none,only} 控制是否执行注释处理和/或编译。
-processor <class1>[,<class2>,<class3>...] 要运行的注释处理程序的名称; 绕过默认的搜索进程
-processorpath <路径> 指定查找注释处理程序的位置
-parameters 生成元数据以用于方法参数的反射
-d <目录> 指定放置生成的类文件的位置
-s <目录> 指定放置生成的源文件的位置
-h <目录> 指定放置生成的本机标头文件的位置
-implicit:{none,class} 指定是否为隐式引用文件生成类文件
-encoding <编码> 指定源文件使用的字符编码
-source <发行版> 提供与指定发行版的源兼容性
-target <发行版> 生成特定 VM 版本的类文件
-profile <配置文件> 请确保使用的 API 在指定的配置文件中可用
-version 版本信息
-help 输出标准选项的提要
-A关键字[=值] 传递给注释处理程序的选项
-X 输出非标准选项的提要
-J<标记> 直接将 <标记> 传递给运行时系统
-Werror 出现警告时终止编译
@<文件名> 从文件读取选项和文件名
Process finished with exit code 0
复制代码
序列化
序列化机制允许将实现序列化的 Java 对象转换成字节序列,这些字节序列可以保存在磁盘上,或通过网络传输,以备以后重新恢复成原来的对象
实现序列化
如果需要将某个对象实现序列化,则必须实现 Serializabe 接口,该接口只是一个标记接口,只是表明该类的示例是可序列化的
如果这个可序列化类中包含引用类型的成员变量,那么该成员变量也需要实现 Serializabe 接口,否则该类型的成员变量是不可序列化的,将会引发 NotSerializableException 异常
使用 transient 关键字修饰成员变量,可以指定 Java 序列化时无视该实例变量
// 课程类
public class Course implements Serializable {
String name;
public Course(String name) {
this.name = name;
}
...省略getter,setter,toString 方法
}
复制代码
// 学生类
public class Student implements Serializable {
String name;
int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
...省略getter,setter,toString 方法
}
复制代码
// 成绩类
public class Grade implements Serializable {
// 分数
int grade;
// 备注,使用 transient 使此成员变量不参与序列化
transient String memo;
Student student;
Course course;
public Grade(int grade, String memo, Student student, Course course) {
this.grade = grade;
this.memo = memo;
this.student = student;
this.course = course;
}
...省略getter,setter,toString 方法
}
复制代码
进行序列化对象
public class SerializeTest {
public static void main(String[] args) throws IOException {
Student stu = new Student("张三", 12);
Course course = new Course("语文");
Grade grade = new Grade(66, "备注", stu, course);
try (
// 1. 创建一个 ObjectOutputStream 输出流(处理流)
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("object.txt"));
) {
// 2. 调用 ObjectOutputStream 的 writeObject()方法将对象输出为可序列化对象
oos.writeObject(grade);
}
}
}
复制代码
反序列化
我们可以通过反序列化从二进制流中恢复 Java 对象
public class DeserializeTest {
public static void main(String[] args) throws Exception {
try (
// 1. 创建一个 ObjectInputStream 处理流
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("object.txt"))
) {
// 2. 调用 readObject() 读取流中的对象
Grade grade = (Grade) ois.readObject();
System.out.println(grade);
}
}
}
复制代码
输出
Grade{grade=66, memo='null', student=Student{name='张三', age=12}, course=Course{name='语文'}}
复制代码
NIO
从 JDK 1.4 开始 ,Java 提供了一系列改进的 输入/输出处理的新功能,这些功能被统称为新 IO(New IO,简称NIO),新增了许多用于处理IO的类,这些类都被放在java.nio 包以及子包下,并且对原 java.io 包中的很多类都以 NIO 为基础进行了改写
Channel(通道)和 Buffer(缓冲)是NIO 中的两个核心对象,Channel 是对传统的IO系统的模拟,在NIO 系统中所有的数据都需要通过通道传输
Buffer 可以被理解成一个容器,它的本质是一个数组,发送到 Channel 中的所有对象都必须首先放到 Buffer 中,而从 Channel 中读取的数据也必须先放到 Buffer 中
Buffer(缓冲区)
Buffer 的主要作用就是装入数据,然后输出数据,在Buffer 中有三个重要的概念:容量(capacity)、界限(limit)和位置(position)
容量(capacity) 缓冲区的容量(capacity)表示该 Buffer 的最大数据容量。缓冲区的容量不可能为负值,且创建后不能改变
界限(limit) 第一个不应该被读出或者写入的缓冲区位置索引。就是说,位于 limit 后的数据既不可被读,也不可被写
位置(position) 用于指明下一个可以被读出的或者写入的缓冲区索引
除此之外,Buffer 还支持一个可选的标记(mark),Buffer 允许直接将 position 定位到该 mark 处。
这些值满足如下关系
[code]0
欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/)
Powered by Discuz! X3.4