Java中的输入输出流IO
- 1. Hello java.io
- 2. Java.io.file
- 3. 文件遍历案例
- 4. 文件过滤器FileFilter
- 5. 绝对与相对路径
- 6. IO流概述
- 7. java.io.OutputStream(字节输出)
- 8. java.io.FileOutputStream实体类(OutputStream子类)
- 9. java.io.InputStream(字节输入)
- 10. java.io.FileInputStream实体类(InputStream子类)
- 19. Properties类 和 .properties文件(Map子类)
- 20. try-with-resourcess(IO中的异常处理)
- 21. 序列化与反序列化技术
- 22. Serializable接口实现序列化和反序列化
- 23. 部分属性的序列化和反序列化(Serializable接口下)
- 24. Externalizable接口实现序列化和反序列化(Serializable子类)
- 25. Serializable和Externalizable的区别
1. Hello java.io
概念: io:in out。对计算机或网站资源的读取和写出。对文件的增删改查遍历操作,对文件字节上数据的读取和写出,对文本文件进行字符上的读取和写出。
2. Java.io.file
概念: file是文件和目录路径名的抽象表示。
构造方法:
构造器 | 描述 |
---|---|
File(String pathname) | 通过将给定的路径名字符串转换为抽象路径名来创建新的 File实例。 |
File(File parent, String child) | 从父抽象路径名和子路径名字符串创建新的 File实例。 |
File(String parent, String child) | 从父路径名字符串和子路径名字符串创建新的 File实例。 |
常用方法:
变量和类型 | 方法 | 描述 |
---|---|---|
boolean | createNewFile() | 当且仅当具有此名称的文件尚不存在时,以原子方式创建由此抽象路径名命名的新空文件。 |
boolean | mkdirs() | 创建此抽象路径名指定的目录,包括任何必需但不存在的父目录。 |
boolean | mkdirs() | 创建此抽象路径名指定的目录,包括任何必需但不存在的父目录。(即一次性创建多层文件夹。) |
boolean | delete() | 删除此抽象路径名表示的文件或目录。 |
void | deleteOnExit() | 请求在虚拟机终止时删除此抽象路径名表示的文件或目录。 |
String | getAbsolutePath() | 返回此抽象路径名的绝对路径名字符串。 |
String | getName() | 返回此抽象路径名表示的文件或目录的名称。 |
String | getParent() | 返回此抽象路径名父项的路径名字符串,如果此路径名未指定父目录,则返回 null 。 |
File | getParentFile() | 返回此抽象路径名父项的抽象路径名,如果此路径名未指定父目录,则返回 null 。 |
String | getPath() | 将此抽象路径名转换为路径名字符串。 |
long | length() | 返回此抽象路径名表示的文件的长度。(字节表示,可*1024转换为KB表示) |
boolean | exists() | 测试此抽象路径名表示的文件或目录是否存在。 |
boolean | isDirectory() | 测试此抽象路径名表示的文件是否为目录。 |
boolean | isFile() | 测试此抽象路径名表示的文件是否为普通文件。 |
File[] | listFiles() | 返回一个抽象路径名数组,表示此抽象路径名表示的目录中的文件。 |
String[] | list() | 返回一个字符串数组,用于命名此抽象路径名表示的目录中的文件和目录。 |
boolean | renameTo(File dest) | 重命名此抽象路径名表示的文件。(即剪切粘贴并重命名,可转移至别的路径里。) |
boolean | getName().endsWith(“.txt”) | 判断某个文件的后缀名是否是.txt(也可更换并判断其他后缀) |
eg:
在E盘下创建一个文件1.txt:
File file = new File(“E://1.txt”)
file.createNewFile();
创建一个文件夹dir:
File file = new File(“E://dir”)
file.mkdir();
用两种构造方法在文件夹dir中创建文件a.txt和b.txt:
File file = new File(dir, “a.txt”);
file.createNewFile();
File file = new File(“E://dir”, “b.txt”);
file.createNewFile();
字段
作用:跨平台系统时所有的路径符号应使用字段来替换以免不同系统的表示不同而出错。
变量和类型 | 字段 | 描述 |
---|---|---|
static String | pathSeparator | 与系统相关的路径分隔符,为方便起见,表示为字符串。 |
static char | pathSeparatorChar | 与系统相关的路径分隔符。 |
static String | separator | 系统相关的默认名称分隔符,为方便起见,表示为字符串。 |
static char | separatorChar | 系统相关的默认名称分隔符。 |
eg: 查看目前系统的字段表示符号:System.out.println(File. pathSeparator)
3. 文件遍历案例
eg: 写一个方法在E盘中遍历查找后缀为.avi的文件且大小大于200MB
public static void main(String[] args) throws IOException {
File e = new File("E:\\");
File[] files = e.listFiles();
listFiles(files);
}
public static void listFiles(File[] files){
if(files != null && files.length>0){
for(File file:files){
if(file.isFile()){
//是文件
if(file.getName().endsWith(".avi")){
//找到了一个avi文件
if(file.length()>200*1024*1024)
System.out.println("找到了一个avi文件:"+file.getAbsolutePath());
}
}else {
//文件夹
File[] files2 = file.listFiles();
listFiles(files2);
}
}
}
}
4. 文件过滤器FileFilter
格式:
FileFilter filter = new FileFilter() {
@Override
public boolean accept(File pathname) {
return false;
}
};
注意: return true表示保留该文件,return false表示剔除该文件。
创建一个匿名的内部类文件过滤器:
public static void main(String[] args) throws IOException {
File e = new File("E;\\");
listFiles(e);
}
public static void listFiles(File file){
//1.创建一个过滤器 并 描述规则
//2.通过文件获取子文件夹
File[] files = file.listFiles(new FileFilter() {
@Override
public boolean accept(File pathname) {
if(pathname.getName().endsWith(".avi") || pathname.isDirectory()){
return true;
}
return false;
}
});
if (files != null && files.length>0)
for (File f:files){
if (f.isDirectory()){
listFiles(f);
}else {
System.out.println("发现一个avi:"+f.getAbsolutePath());
}
}
}
5. 绝对与相对路径
绝对路径: 从盘符开始,是一个完整的路径。例如:E://a.txt。
相对路径: 在Java代码中是相对于项目目录路径,这是一个不完整的便捷路径,在Java开发中很常用。例如:a.txt。
6. IO流概述
IO流概述:
- 可以将这种数据传输操作,看作一种数据的流动,按照流动的方向分别为输入Input和输出Output。
- Java中的IO操作主要指的是Java.io包下的一些常用类的使用,通过这些常用类对数据进行读取(输入Input)和写出(输出Output)。
IO流的分类:
- 按照流的方向来分,可以分为:输入流和输出流。
- 按照流动的数据类型来分,可以分为:字节流和字符流。
字节流和字符流的顶级父类:- 字节流:
输入流:InputStream
输出流:OutputStream - 字符流:
输入流:Reader
输出流:Writer
注意:输入输出流最后记得尽可能早地使用close();对流进行关闭。
- 字节流:
7. java.io.OutputStream(字节输出)
一切皆字节:
- 8个二进制的bit位(即00000000)是一个字节。
- 计算机中的任何数据(文本,图片,视频,音乐等等)都是以二进制的形式存储的。
- 在数据传输时也都是以二进制的形式存储的。
- 后续学习的任何流,在传输时底层都是二进制。
构造方法: OutputStream()
常用方法:
变量和类型 | 方法 | 描述 |
---|---|---|
void | close() | |
void | flush() | 刷新此输出流并强制写出任何缓冲的输出字节。 |
static OutputStream | nullOutputStream() | 返回一个新的 OutputStream ,它丢弃所有字节。 |
void | write(byte[] b) | 将 b.length字节从指定的字节数组写入此输出流。 |
void | write(byte[] b, int off, int len) | 将从偏移量 off开始的指定字节数组中的 len字节写入此输出流。 |
abstract void | write(int b) | 将指定的字节写入此输出流。(仅获取int(4个字节)的最后一个字节(8位二进制)) |
8. java.io.FileOutputStream实体类(OutputStream子类)
构造方法:
构造器 | 描述 |
---|---|
FileOutputStream(File file) | 创建文件输出流以写入由指定的 File对象表示的文件。 |
FileOutputStream(FileDescriptor fdObj) | 创建要写入指定文件描述符的文件输出流,该文件描述符表示与文件系统中实际文件的现有连接。 |
FileOutputStream(File file, boolean append) | 创建文件输出流以写入由指定的 File对象表示的文件。 (append为true表示继续写,即追加流,为false或省略表示把原来文件数据清空再写) |
FileOutputStream(String name) | 创建文件输出流以写入具有指定名称的文件。 |
FileOutputStream(String name, boolean append) | 创建文件输出流以写入具有指定名称的文件。 (append为true表示继续写,即追加流,为false或省略表示把原来文件数据清空再写) |
eg:
public static void main(String[] args) throws IOException {
FileOutputStream fos = new FileOutputStream("E://a.txt");
fos.write(65); //写入一个字节,A
byte[] bytes = {65,66,67};
fos.write(bytes); //写入一组字节,A,B,C
byte[] bytes1 = "ABCD".getBytes(); //字符串转换成字节,不建议这么写,一般用字符流实现
fos.write(bytes, 1, 2); //写入一组字节从1下标开始的两个字节,B,C
fos.close();
}
9. java.io.InputStream(字节输入)
构造方法: InputStream()
常用方法:
变量和类型 | 方法 | 描述 |
---|---|---|
void | close() | 关闭此输入流并释放与该流关联的所有系统资源。 |
abstract int | read() | 从输入流中读取下一个数据字节。(从输入流中读取下一个数据字节。 值字节返回int ,范围为0至255 。 如果由于到达流末尾而没有可用字节,则返回值-1 。) |
int | read(byte[] b) | 从输入流中读取一些字节数并将它们存储到缓冲区数组 b 。 (返回值为实际读取的字节数。如果由于流位于文件末尾而没有可用字节,则返回值-1 ; 否则,至少读取一个字节并存储到b 。) |
10. java.io.FileInputStream实体类(InputStream子类)
构造方法:
构造器 | 描述 |
---|---|
FileInputStream(File file) | 通过打开与实际文件的连接来创建 FileInputStream ,该文件由文件系统中的 File对象 file命名。 |
FileInputStream(String name) | 通过打开与实际文件的连接来创建 FileInputStream ,该文件由文件系统中的路径名 name命名。 |
FileInputStream(FileDescriptor fdObj) | 使用文件描述符 fdObj创建 FileInputStream ,该文件描述符表示与文件系统中实际文件的现有连接。 |
eg:
```handlebars
```java
/**读取并打印字节*/
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("E://a.txt");
while(true){ //不断读取字节
byte b = (byte)fis.read(); //int强转为byte
if (b == -1) break; //读取完跳出
System.out.println((char)b); //输出字节表示的字符
}
fis.close();
}
/**读取并打印一组字节*/
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("E://a.txt");
while(true){ //不断读取10字节
byte[] bytes = new byte[10];
int len = fis.read(bytes);
if (len == -1) break; //读取完跳出
System.out.println(new String(bytes,0,len)); //转换为字符串并输出每次读到的len个字节
}
fis.close();
}
**注意:** 如果没有按len取出字节,如果数据中最后一组字节长度s小于字节数组长度n,取到的最后一组字节后几位n-s会是上一组取到字节的后几位n-s。
# 11. Writer字符输出
**字段:**
变量和类型|字段 |描述
--|--|--
protected Object|lock|用于同步此流上的操作的对象。
**构造方法:**
变量 |构造器| 描述
--|--|--
protected | Writer()|创建一个新的字符流编写器,其关键部分将在编写器本身上同步。
protected |Writer(Object lock) |创建一个新的字符流编写器,其关键部分将在给定对象上同步。
**常用方法:**
变量和类型 |方法| 描述
--|--|--
Writer|append(char c)|将指定的字符追加到此writer。
Writer|append(CharSequence csq)|将指定的字符序列追加到此writer。
Writer|append(CharSequence csq, int start, int end) |将指定字符序列的子序列追加到此writer。
abstract void| close()|关闭流,先冲洗它。
abstract void| flush()|刷新流。
static Writer|nullWriter()|返回一个新的 Writer ,它丢弃所有字符。
void |write(char[] cbuf)|写一个字符数组。
abstract void |write(char[] cbuf, int off, int len) |写一个字符数组的一部分。
void| write(int c)|写一个字符。
void| write(String str)|写一个字符串。
void |write(String str, int off, int len)|写一个字符串的一部分。
# 12. FileWriter(Writer子类)
**常用构造方法:**
构造器 |描述
--|--
FileWriter(File file)|给 File写一个 FileWriter ,使用平台的 default charset
FileWriter(File file, boolean append) |在给出要写入的 FileWriter下构造 File ,并使用平台的 default charset构造一个布尔值,指示是否附加写入的数据。
FileWriter(String fileName, Charset charset)|构造一个FileWriter给出文件名和charset (字符编码)。
# 13. Reader字符读取
**常用方法:**
变量和类型| 方法| 描述
--|--|--
abstract void |close()|关闭流并释放与其关联的所有系统资源。
int |read()|读一个字符。
int| read(char[] cbuf)|将字符读入数组。
abstract int| read(char[] cbuf, int off, int len) |将字符读入数组的一部分。
# 14. FileReader(Reader子类)
**构造方法:**
构造器 |描述
--|--
FileReader(File file)|使用平台 FileReader ,在 File读取时创建一个新的 FileReader 。
FileReader(FileDescriptor fd) |使用平台 default charset创建一个新的 FileReader ,给定 FileDescriptor进行读取。
FileReader(File file, Charset charset)|创建一个新的FileReader ,给出File读取和charset 。
FileReader(String fileName)| 使用平台 default charset创建一个新的 FileReader ,给定要读取的文件的 名称 。
FileReader(String fileName, Charset charset)|给定要读取的文件的名称和FileReader ,创建一个新的FileReader 。
# 15. flush刷新管道
**注意:** 字符在进行输出的时候会转换为字节然后先放在缓冲区中,待缓冲区刷新才会输出到文件中。close()操作也会刷新并关闭缓冲区,flush()则是主动刷新缓冲区,因此在字符输出的时候,如果是**循环输出字符,则在一个循环结束时要进行flush()刷新缓冲区将缓冲区**中的字符输出到文件中,再进行下一个循环。
# 16. InputStreamReader和OutputStreamWriter字节转换字符流(转换流)
**概念:** 字节流转换为字符流:即字节流“装饰”为字符流,使用了装饰者设计模式。例如将获取到的字节对象转换为字符对象进行输出或输入。
**InputStreamReader常用构造方法:**
构造器| 描述
--|--
InputStreamReader(**InputStream in**)|创建一个使用默认字符集的InputStreamReader。 (In为字节对象)
InputStreamReader(InputStream in, String charsetName) |创建一个使用指定charset的InputStreamReader。
**InputStreamReader常用方法:**
变量和类型| 方法 |描述
--|--|--
String|getEncoding()|返回此流使用的字符编码的名称。
Int| read()|读一个字符。
int |read(char[] cbuf, int offset, int length) |将字符读入数组的一部分。
boolean| ready()|判断此流是否可以读取。
**OutputStreamWriter常用构造方法:**
构造器| 描述
--|--
OutputStreamWriter(OutputStream out)|创建使用默认字符编码的OutputStreamWriter。 (out为字节对象)
OutputStreamWriter(OutputStream out, String charsetName)| 创建使用指定charset的OutputStreamWriter。
**OutputStreamWriter常用方法:**
变量和类型| 方法 |描述
--|--|--
Void| flush()|刷新流。
String|getEncoding()|返回此流使用的字符编码的名称。
Void |write(char[] cbuf, int off, int len) |写一个字符数组的一部分。
Void|write(int c)|写一个字符。
Void |write(String str, int off, int len)|写一个字符串的一部分。
# 17. PrintWriter(打印流)与BufferedReader(缓存读取流)(可以行操作)
**概念:**
1) **PrintWriter:** 打印流。
2) **BufferedReader:** 缓存读取流,将字符输入流转换为带有缓存,可以一次读取一行的缓存字符读取流。
**PrintWriter常用构建方法:**
构造器 |描述
--|--
PrintWriter(File file)|使用指定的文件创建没有自动行刷新的新打印流。
PrintWriter(OutputStream out)|创建新的打印流,==同时将字节流转换为字符流==。
**PrintWriter常用方法:**
变量和类型 |方法| 描述
--|--|--
void |println()| 打印一行字符。
**BufferedReader常用构建方法:**
构造器| 描述
--|--
BufferedReader(Reader in)|创建使用默认大小的输入缓冲区的缓冲字符输入流。
**注意:** ==InputStream类型的对象in要用InputStreamReader(in)转换为字符流对象才能传入BufferedReader中。==
**BufferedReader常用方法:**
变量和类型 |方法| 描述
--|--|--
String|readLine()|读一行文字。
# 18. 收集异常日志
**概念:** 将控制台的异常输出到文件中,但是一般不推荐这么写,而是利用已经包装好的相关类。
```java
```handlebars
/**打印异常日志*/
public static void main(String[] args) throws IOException {
try{
String s = null;
s.toString(); //会出现空指针异常
}catch (Exception e){
PrintWriter pw = new PrintWriter("E://bug.txt");
//以下两行用于打印时间
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");
pw.println(sdf.format(new Date()));
e.printStackTrace(pw); //打印异常到控制台,传入字符输出流对象则打印到对应文件
pw.close();
}
}
19. Properties类 和 .properties文件(Map子类)
概念: 存储和读取键=值的文件。
常用构造方法: Properties()
常用方法:
变量和类型 | 方法 | 描述 |
---|---|---|
String | getProperty(String key) | 在此属性列表中搜索具有指定键的属性。 |
void | load(InputStream inStream) | 从输入字节流中读取属性列表(键和元素对)。 |
void | load(Reader reader) | 以简单的面向行的格式从输入字符流中读取属性列表(键和元素对)。 |
void | store(OutputStream out, String comments) | |
void | store(Writer writer, String comments) | 将此 Properties表中的此属性列表(键和元素对)以适合使用 load(Reader)方法的格式写入输出字符流。(comments为该文件注释) |
20. try-with-resourcess(IO中的异常处理)
概念: 在try-catch代码块结束时自动调用close()。 使用try自动关闭的流对象必须有close()方法,如果是自定义的类对象,则需要实现Closeable接口并重写close()方法。
格式:
try(FileReader fr = new FileReader("E://book.txt")){}…
try(fr){}…
try(流对象1;流对象2,…){}…
自定义的类对象中:
static class CloseDemo implements Closeable{
@Override
public void close() throws IOException {
}
}
eg:
/**try-with-resources自动调用close()的写法*/
public static void main(String[] args) throws FileNotFoundException {
//try-with-resources
//1.7时:放在try()里
try(FileReader fr = new FileReader("E://book.txt")){
int c = fr.read();
System.out.println((char)c);
} catch (IOException e) {
e.printStackTrace();
}
//JDK9进行了优化:可以同时close()多个对象,用“;”隔开
FileReader fr = new FileReader("E://book.txt");
PrintWriter pw = new PrintWriter("E://book.txt");
try(fr;pw){
int c = fr.read();
System.out.println((char)c);
} catch (IOException e) {
e.printStackTrace();
}
//自定义对象类的try自动close():必须实现Closeable接口并重写close()方法
CloseDemo d = new CloseDemo();
try (d) {
} catch (Exception e){
}
}
static class CloseDemo implements Closeable{
@Override
public void close() throws IOException {
System.out.println("close方法被调用了");
}
}
21. 序列化与反序列化技术
概念:
Java序列化是指把Java对象转换为字节序列的过程,Java反序列化是指把字节序列恢复为Java对象的过 程。通过徐序列化和反序列化实现网络传输、本地存储的目的。序列化是指把对象以字节方式存储到文件中,反序列化是指把文件中的对象读取出来。由于文件中存储的是对象数据,所以直观上看是一堆乱码。由于未来该技术可能会被摒弃(出现bug),所以不太建议使用。
相关类:
ObjectOutputStream()
ObjectInputStream()
相关类的概念:
- ObjectOutputStream: 将Java对象的原始数据类型和图形序列化写入OutputStream。
- ObjectInputStream: 对先前使用ObjectOutputStream编写的原始数据和对象进行反序列化。
相关类的常用方法:
变量和类型 | 方法 | 描述 |
---|---|---|
Object | readObject() | 从ObjectInputStream中读取一个对象。 |
void | writeObject(Object obj) | 将指定的对象写入ObjectOutputStream。 |
序列化的对象对应的类必须实现一个标记接口: Serializable
22. Serializable接口实现序列化和反序列化
概念: 要实现Java对象的序列化,只要将类实现标识接口——Serializable接口即可,不需要我们重写任何方法就可以实现序列化。
注意: 如果序列化的类中存在其他类的实例化对象,则其他类也要实现Serializable接口!
如果是java内置类,如Scanner的实例化对象,则需要加上static或transient修饰符使其不参加序列化!否则会有NotSerializableException错误异常。
eg:
/**序列化对象和反序列化对象*/
public static void main(String[] args) throws IOException, ClassNotFoundException {
//序列化
Book b = new Book("书名","书的信息");
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("E://book.txt"));
oos.writeObject(b);
oos.close();
//反序列化
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("E://book.txt"));
Object o = ois.readObject();
ois.close();
}
static class Book implements Serializable{ //实现标记类
private String name;
private String info;
public Book(String name, String info) {
this.name = name;
this.info = info;
}
}
23. 部分属性的序列化和反序列化(Serializable接口下)
实现部分字段序列化的三种方式:
- 使用transient修饰符;
- 使用static修饰符;
- 默认方法writeObject和readObject;
使用transient修饰符: 修改实体类,将实体类中不想序列化的属性添加transient修饰词。
使用static修饰符: 修改实体类,将实体类中不想序列化的属性添加static修饰词。
默认方法writeObject和readObject: 修改实体类,将实体类中想序列化的属性添加到这两个方法中。
readObject的调用逻辑和writeObject一样。如果目标类中没有定义私有的writeObject或readObject方法,那么序列化和反序列化的时 候将调用默认的方法来根据目标类中的属性来进行序列化和反序列化,而如果目标类中定义了私有的 writeObject或readObject方法,那么序列化和反序列化的时候将调用目标类指定的writeObject或 readObject方法来实现。
private void writeObject(ObjectOutputStream objOut) throws IOException {
objOut.writeObject(目标类成员属性);
}
private void readObject(ObjectInputStream objIn) throws IOException, ClassNotFoundException {
目标类成员属性= objIn.readObject();
}
eg:
class Demo implements Serializable{
@Override
private void writeObject(ObjectOutputStream objOut) throws IOException {
objOut.writeObject(目标类成员属性);
}
@Override
private void readObject(ObjectInputStream objIn) throws IOException, ClassNotFoundException {
目标类成员属性= objIn.readObject();
}
}
24. Externalizable接口实现序列化和反序列化(Serializable子类)
概念:
- Externalizable继承自Serializable。使用Externalizable接口需要实现readExternal方法和 writeExternal方法来实现序列化和反序列化。
- Externalizable接口中定义了writeExternal和readExternal两个抽象方法,这两个方法其实对应 Serializable接口的writeObject和readObject方法。可以这样理解:Externalizable接口被设计出来的 目的就是为了抽象出writeObject和readObject这两个方法,但是目前这个接口使用的并不多。
class Demo implements Externalizable{
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeObject(目标类成员属性);
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
目标类成员属性= (String) in.readObject();
}
}
25. Serializable和Externalizable的区别
区别 | Serializable | Externalizable |
---|---|---|
实现复杂度 | 实现简单,Java对其有内建支持 | 实现复杂,由开发人员自己完成 |
执行效率 | 所有对象由Java统一保存,性能 较低 | 开发人员决定哪个对象保存,可能造成速度 提升 |
保存信息 | 保存时占用空间大 | 部分存储,可能造成空间减少 |
使用频率 | 高 | 偏低 |