前言
学习IO
流是为了更好的理解序列化
和反序列化
学习IO流还得先从File讲起
文件:保存数据的地方
文件流:文件在程序中是以流的形式来操作的
- 流:数据在数据源(文件)和程序(内存)之间经历的路径
- 输入流:数据从数据源(文件)到程序(内存)的路径
- 输出流:数据从程序(内存)到数据源(文件)的路径
从三个常用的API着手
public File(String pathname)
:通过将给定的路径名字符串转换为抽象路径名来创建新的 File实例。
public File(String parent, String child)
:从父路径名字符串和子路径名字符串创建新的 File实例。
public File(File parent, String child)
:从父抽象路径名和子路径名字符串创建新的 File实例。
下面给出三种创建File的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| package IO;
import java.io.File; import java.io.IOException;
public class FileCreate { public static void main(String[] args) {
create03(); }
public static void create01() throws IOException { String filepath = "d:\\news1.txt"; File file = new File(filepath); file.createNewFile(); }
public static void create02(){ File parentfile = new File("d:\\"); String fileName = "news2.txt"; File file = new File(parentfile,fileName); try { file.createNewFile(); System.out.println("创建02成功"); } catch (IOException e) { throw new RuntimeException(e); } }
public static void create03(){ String parentPath = "d:\\"; String fileName = "news3.txt"; File file = new File(parentPath,fileName);
try { file.createNewFile(); System.out.println("03创建成功"); } catch (IOException e) { throw new RuntimeException(e); } }
}
|
常用的方法
public String getAbsolutePath()
:返回此File的绝对路径名字符串。
public String getPath()
:将此File转换为路径名字符串。
public String getName()
:返回由此File表示的文件或目录的名称。
public long length()
:返回由此File表示的文件的长度。
public boolean exists()
:此File表示的文件或目录是否实际存在。
public boolean isDirectory()
:此File表示的是否为目录。
public boolean isFile()
:此File表示的是否为文件。
给出相应的方法用法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| package IO;
import java.io.File;
public class FileInformation { public static void main(String[] args) { info(); } public static void info(){ File file = new File("d:\\new1.txt"); System.out.println("文件名字= "+file.getName()); System.out.println("文件的绝对路径= "+file.getAbsolutePath()); System.out.println("文件父目录= "+file.getParent()); System.out.println("文件的大小(字节) "+file.length()); System.out.println("文件是否存在 "+file.exists()); System.out.println("是不是一个文件 "+file.isFile()); System.out.println("是不是一个目录 "+file.isDirectory());
} }
|
文件的增加删除
public boolean createNewFile()
:文件不存在,创建一个新的空文件并返回true,文件存在,不创建文件并返回false。
public boolean delete()
:删除由此File表示的文件或目录。
public boolean mkdir()
:创建由此File表示的目录。
public boolean mkdirs()
:创建由此File表示的目录,包括任何必需但不存在的父目录。
其中,mkdirs()和mkdir()方法类似,但mkdir(),只能创建一级目录,mkdirs()可以创建多级目录比如//a//b//c,所以开发中一般用mkdirs();
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
| package IO;
import java.io.File; import java.io.IOException;
public class Directory_ {
public static void main(String[] args) { m1(); m2(); m3(); } public static void m1(){ String filePath = "d:\\new1.txt"; File file = new File(filePath); if (file.exists()){ if (file.delete()) { System.out.println("删除成功"); } else { System.out.println(" 删除失败"); } } else { System.out.println("NO"); } }
public static void m2(){ String filePath = "d:\\demo"; File file = new File(filePath); if (file.exists()) { if (file.delete()) { System.out.println("删除成功"); } else { System.out.println("删除失败"); } } else { System.out.println("不存在"); } }
public static void m3(){ String filePath = "d:\\demo\\a\\c"; File file = new File(filePath); if (file.exists()){ System.out.println("存在");
} else { if (file.mkdirs()) { System.out.println("创建成功"); } else { System.out.println("创建失败"); } } } }
|
目录的遍历
public String[] list()
:返回一个String数组,表示该File目录中的所有子文件或目录。
public File[] listFiles()
:返回一个File数组,表示该File目录中的所有的子文件或目录。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| public class FileFor { public static void main(String[] args) { File dir = new File("G:\光标"); String[] names = dir.list(); for(String name : names){ System.out.println(name); } File[] files = dir.listFiles(); for (File file : files) { System.out.println(file); } } }
|
listFiles
在获取指定目录下的文件或者文件夹时必须满足下面两个条件
递归遍历文件夹下所有文件以及子文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| package File;
import java.io.File;
public class RecursionDirectory { public static void main(String[] args) { File file=new File("D:\\java专属IO测试"); Recursion(file); } public static void Recursion(File file){ if(!file.isDirectory()){ return; } File[] files = file.listFiles(); for (File f: files) { if(f.isDirectory()){ Recursion(f); }else { System.out.println(f.getName()); }
} } }
|
IO流
IO流分类
按操作数据单位不同分为:字节流(8bit),字符流(按字符,对于几个字节)
按数据流的流向不同分为:输入流,输出流
按流的角色的不同分为:节点流,处理流/包装流
|
输入流 |
输出流 |
字节流 |
InputStream |
OutputStream |
字符流 |
Reader |
Writer |
字节流 Stream
字节输出流 OutputStream
字节输出流的基本共性功能方法:
public void close()
:关闭此输出流并释放与此流相关联的任何系统资源。
public void flush()
:刷新此输出流并强制任何缓冲的输出字节被写出。
public void write(byte[] b)
:将 b.length个字节从指定的字节数组写入此输出流。
public void write(byte[] b, int off, int len)
:从指定的字节数组写入 len字节,从偏移量 off开始输出到此输出流。 也就是说从off个字节数开始读取一直到len个字节结束
public abstract void write(int b)
:将指定的字节输出流。
以上五个方法则是字节输出流都具有的方法,由父类OutputStream定义提供,子类都会共享以上方法
FileOutputStream构造方法
public FileOutputStream(File file)
:根据File对象为参数创建对象。
public FileOutputStream(String name)
: 根据名称字符串为参数创建对象。
FileOutputStream写出字节数据
使用FileOutputStream写出字节数据主要通过Write方法,而write方法分如下三种
public void write(int b)
public void write(byte[] b)
public void write(byte[] b,int off,int len)
//从off
索引开始,len
个字节
FileOutputStream实现数据追加续写、换行
public FileOutputStream(File file, boolean append)
public FileOutputStream(String name, boolean append)
第二个参数中都需要传入一个boolean类型的值,true 表示追加数据,false 表示不追加也就是清空原有数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| package IO.inputStream;
import java.io.FileNotFoundException; import java.io.IOException;
public class FileOutputStream { public static void main(String[] args) { try { out(); } catch (IOException e) { throw new RuntimeException(e); } }
public static void out() throws IOException {
String filePath = "d:\\1.txt"; java.io.FileOutputStream file = null; try { file = new java.io.FileOutputStream(filePath,true); String str = "CQJKL"; file.write(str.getBytes()); System.out.println("Success!"); } catch (IOException e) { throw new RuntimeException(e); }finally { file.close(); } } }
|
字节输出流的基本共性功能方法:
public void close()
:关闭此输入流并释放与此流相关联的任何系统资源。
public abstract int read()
: 从输入流读取数据的下一个字节。
public int read(byte[] b)
: 该方法返回的int值代表的是读取了多少个字节,读到几个返回几个,读取不到返回-1
FileInputStream(File file)
: 通过打开与实际文件的连接来创建一个 FileInputStream ,该文件由文件系统中的 File对象 file命名。
FileInputStream(String name)
: 通过打开与实际文件的连接来创建一个 FileInputStream ,该文件由文件系统中的路径名name命名。
- 读取字节:read方法,每次可以读取一个字节的数据,提升为int类型,读取到文件末尾,返回-1
- 使用字节数组读取:read(byte[] b),每次读取b的长度个字节到数组中,返回读取到的有效字节个数,读取到末尾时,返回-1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| package IO.inputStream;
import java.io.IOException;
public class FileInputStream {
public static void main(String[] args) throws IOException { read1(); read2(); }
public static void read1() throws IOException { String filePath = "d:\\1.txt"; int readData = 0; java.io.FileInputStream fileInputStream = null; try { fileInputStream = new java.io.FileInputStream(filePath); while ((readData = fileInputStream.read()) != -1){ System.out.println((char)readData); } } catch (IOException e) { throw new RuntimeException(e); }finally { fileInputStream.close(); } }
public static void read2() throws IOException { String filePath = "d:\\1.txt"; int readData = 0; byte[] bytes = new byte[8]; int readlen = 0; java.io.FileInputStream fileInputStream = null; try { fileInputStream = new java.io.FileInputStream(filePath); while ((readlen = fileInputStream.read(bytes)) != -1){ System.out.println(new String(bytes,0, readlen)); } } catch (IOException e) { throw new RuntimeException(e); }finally { fileInputStream.close(); } } }
|
字符流 Reader/Writer
字符流的由来:因为数据编码的不同,因而有了对字符进行高效操作的流对象,字符流本质其实就是基于字节流读取时,去查了指定的码表,而字节流直接读取数据会有乱码的问题(读中文会乱码),字节流读取中文字符时,可能不会显示完整的字符,那是因为一个中文字符占用多个字节存储。
可以通过new String()
来解决乱码问题,但是很麻烦,因此就引入了字符流,专门用来处理文本文件
字符输入流 Reader
字符输入流的共性方法
public void close()
:关闭此流并释放与此流相关联的任何系统资源。
public int read()
: 从输入流读取一个字符。
public int read(char[] cbuf)
: 从输入流中读取一些字符,并将它们存储到字符数组 cbuf中
构造方法
FileReader(File file)
: 创建一个新的 FileReader ,给定要读取的File对象。
FileReader(String fileName)
: 创建一个新的 FileReader ,给定要读取的文件的字符串名称。
FileReader读取字符数据
- 读取字符:read方法,每次可以读取一个字符的数据,提升为int类型,读取到文件末尾,返回-1,循环读取
- 使用字节数组读取:read(byte[] b),每次读取b的长度个字节到数组中,返回读取到的有效字节个数,读取到末尾时,返回-1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
| package IO.reader;
import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException;
public class Filereader {
public static void main(String[] args) { read2(); }
public static void read1(){ String path = "d:\\1.txt"; FileReader filereader = null; try { filereader = new FileReader(path); int len = 0; while ((len = filereader.read()) != -1) { System.out.print((char)len); }
} catch (IOException e) { throw new RuntimeException(e); } finally { try { filereader.close(); } catch (IOException e) { throw new RuntimeException(e); } } }
public static void read2(){ String path = "d:\\1.txt"; FileReader filereader = null; try { filereader = new FileReader(path); int len = 0; char[] buf = new char[1024]; while ((len = filereader.read(buf)) != -1) { System.out.print(new String(buf,0,len)); }
} catch (IOException e) { throw new RuntimeException(e); } finally { try { filereader.close(); } catch (IOException e) { throw new RuntimeException(e); } } } }
|
字符输出流 Writer
字符输出流的基本共性功能方法
void write(int c)
写入单个字符。
void write(char[] cbuf)
写入字符数组。
abstract void write(char[] cbuf, int off, int len)
写入字符数组的某一部分,off数组的开始索引,len写的字符个数。
void write(String str)
写入字符串。
void write(String str, int off, int len)
写入字符串的某一部分,off字符串的开始索引,len写的字符个数。
void flush()
刷新该流的缓冲。
void close()
关闭此流,但要先刷新它。
构造方法
FileWriter(File file)
: 创建一个新的 FileWriter,给定要读取的File对象。
FileWriter(String fileName)
: 创建一个新的 FileWriter,给定要读取的文件的名称。
FileWriter写出数据
write(int b)
方法,每次可以写出一个字符数据
public void write(char[] b)
,以数组的方式加快
关闭close和刷新flush
因为内置缓冲区的原因,如果不关闭输出流,无法写出字符到文件中。但是关闭的流对象,是无法继续写出数据的。如果我们既想写出数据,又想继续使用流,就需要flush
方法了。
flush
:刷新缓冲区,流对象可以继续使用。
close
:先刷新缓冲区,然后通知系统释放资源。流对象不可以再被使用了。
[注意]关闭资源时,与FileOutputStream不同。 如果不关闭,数据只是保存到缓冲区,并未保存到文件。
flush()
这个函数是清空的意思,用于清空缓冲区的数据流,进行流的操作时,数据先被读到内存中,然后再用数据写到文件中,那么当你数据读完时,我们如果这时调用close()
方法关闭读写流,这时就可能造成数据丢失,为什么呢?因为,读入数据完成时不代表写入数据完成,一部分数据可能会留在缓存区中,这个时候flush()
方法就格外重要了。
缓冲流 Buffered
缓冲流的基本原理:
- 使用了底层流对象从具体设备上获取数据,并将数据存储到缓冲区的数组内。
- 通过缓冲区的read()方法从缓冲区获取具体的字符数据,这样就提高了效率。
- 如果用read方法读取字符数据,并存储到另一个容器中,直到读取到了换行符时,将另一个容器临时存储的数据转成字符串返回,就形成了readLine()功能。
字节缓冲流
构造方法
字符缓冲流
构造方法
字符缓冲流特有方法
字符缓冲流的基本方法与普通字符流调用方式一致,这里不再阐述,我们来看字符缓冲流具备的特有方法
。
BufferedReader:public String readLine()
: 读一行数据。 读取到最后返回null
BufferedWriter:public void newLine()
: 换行,由系统属性定义符号。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| package IO.Buffer;
import IO.buff.BufferReader_; import IO.reader.Filereader;
import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException;
public class BufferedReader { public static void main(String[] args) throws IOException {
String filePath = "d:\\1.txt"; java.io.BufferedReader bufferedReader = new java.io.BufferedReader(new FileReader(filePath)); String line = ""; while ((line = bufferedReader.readLine()) != null){ System.out.println(line); } bufferedReader.close();
} }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| package IO.Buffer;
import java.io.BufferedReader; import java.io.FileWriter; import java.io.IOException;
public class BufferedWriter { public static void main(String[] args) throws IOException {
String filePath = "d:\\ok.txt"; java.io.BufferedWriter bufferedWriter = new java.io.BufferedWriter(new FileWriter(filePath)); bufferedWriter.write("Hello, CQJKL1"); bufferedWriter.newLine(); bufferedWriter.write("Hello, CQJKL2"); bufferedWriter.newLine(); bufferedWriter.write("Hello, CQJKL3");
System.out.println("操纵完毕"); bufferedWriter.close(); } }
|
转换流
构造方法
InputStreamReader(InputStream in)
: 创建一个使用默认字符集的字符流。
InputStreamReader(InputStream in, String charsetName)
: 创建一个指定字符集的字符流。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| package IO.exchangeStream;
import java.io.*;
public class input { public static void main(String[] args) throws IOException { String path = "d:\\a.txt";
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream(path),"UTF-8"));
String s = ""; while ((s = bufferedReader.readLine()) !=null) { System.out.println(s); }
bufferedReader.close();
} }
|
OutputStreamWriter类
构造方法
OutputStreamWriter(OutputStream in)
: 创建一个使用默认字符集的字符流。
OutputStreamWriter(OutputStream in, String charsetName)
: 创建一个指定字符集的字符流。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| package IO.exchangeStream;
import java.io.*;
public class output { public static void main(String[] args) throws IOException {
String path = "d:\\b.txt";
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(new FileOutputStream(path),"UTF-8"); outputStreamWriter.write("Hello, world!~~"); outputStreamWriter.close(); System.out.println("保存成功!"); } }
|
序列化流
序列化
Java 提供了一种对象序列化的机制。用一个字节序列可以表示一个对象,该字节序列包含该对象的数据
、对象的类型
和对象中存储的属性
等信息。字节序列写出到文件之后,相当于文件中持久保存了一个对象的信息。
反之,该字节序列还可以从文件中读取回来,重构对象,对它进行反序列化。对象的数据
、对象的类型
和对象中存储的数据信息
,都可以用来在内存中创建对象
相关操作
一个对象要想序列化,必须满足两个条件:
该类必须实现java.io.Serializable
接口,Serializable
是一个标记接口,不实现此接口的类将不会使任何状态序列化或反序列化,会抛出NotSerializableException
。
该类的所有属性必须是可序列化的。如果有一个属性不需要可序列化的,则该属性必须注明是瞬态的,使用transient
关键字修饰。
ObjectOutputStream类
构造方法
public ObjectOutputStream(OutputStream out)
: 创建一个指定OutputStream的ObjectOutputStream。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| package IO.Object;
import java.io.*;
public class outStream { public static void main(String[] args) throws IOException { String path = "d:\\data.dat";
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(path));
objectOutputStream.writeInt(100); objectOutputStream.writeUTF("CQJLK"); objectOutputStream.writeBoolean(true);
objectOutputStream.writeObject(new Dog("小白",5));
objectOutputStream.close(); }
}
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| package IO.Object;
import java.io.Serializable;
public class Dog implements Serializable { private String name; private int age;
public Dog(String name, int age) { this.name = name; this.age = age; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
@Override public String toString() { return "Dog{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
|
构造方法
public ObjectInputStream(InputStream in)
: 创建一个指定InputStream的ObjectInputStream。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| package IO.Object;
import java.io.FileInputStream; import java.io.IOException; import java.io.ObjectInputStream;
public class inputStream { public static void main(String[] args) throws IOException, ClassNotFoundException {
String path = "d:\\data.dat"; ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(path));
System.out.println(objectInputStream.readInt()); System.out.println(objectInputStream.readUTF()); System.out.println(objectInputStream.readBoolean()); System.out.println(objectInputStream.readObject());
objectInputStream.close();
} }
|
注意
- 读写顺序要一致
- 要求序列化或者反序列化对象,需要实现
Serializable
- 序列化的类中 建议添加SeralVersionUID,为了提高版本的兼容性
- 序列化对象时,默认将里面所有属性都进行序列化,但是除了static或者
transient
修饰的成员
- 序列化对象时,要求里面属性的类型也需要实现序列化接口
- 序列化具备可继承性,也就是如果某类已经实现了序列化,则他的所有子类也已经默认实现了序列化
打印流
何谓打印流
平时我们在控制台打印输出,是调用print
方法和println
方法完成的,各位用了这么久的输出语句肯定没想过这两个方法都来自于java.io.PrintStream
类吧,哈哈。该类能够方便地打印各种数据类型的值,是一种便捷的输出方式。
打印流分类:
字节打印流PrintStream,字符打印流PrintWriter
打印流特点:
- 只操作目的地,不操作数据源
- 可以操作任意类型的数据
- 如果启用了自动刷新,在调用println()方法的时候,能够换行并刷新
- 可以直接操作文件
直接操作文件
:如果该流的构造方法能够同时接收File和String类型的参数,一般都是可以直接操作文件的!
PrintStream是OutputStream的子类,PrintWriter是Writer的子类,两者处于对等的位置上,所以它们的API是非常相似的。二者区别无非一个是字节打印流,一个是字符打印流。
Properties属性类
基本介绍
- 专门用来读写配置文件的集合类
键=值
- 注意:键值对不需要有空格,值不需要用引号。默认类型String
构造方法
public Properties()
:创建一个空的属性列表。
基本的存储方法
public Object setProperty(String key, String value)
: 保存一对属性。
public String getProperty(String key)
:使用此属性列表中指定的键搜索属性值。
Set stringPropertyNames()``` :所有键的名称的集合。1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
|
```java package IO.properties;
import java.io.*; import java.util.Properties;
public class Propertity { public static void main(String[] args) throws IOException { //1.c创建一个对象 Properties properties = new Properties(); //2.加载指定的配置文件 properties.load(new FileReader("src\\mysql.properties")); //3.显示到控制台 properties.list(System.out); //4. 根据key获取对应的值 System.out.println("======================="); String user = properties.getProperty("user"); String pwd = properties.getProperty("pwd"); System.out.println(user); System.out.println(pwd);
} }
|
借鉴
<B站韩顺平IO流>
https://www.cnblogs.com/yichunguo/p/11775270.html
https://juejin.cn/post/6844903910348603405