IO流
IO流原理及流的分类
概念
- I/O技术用于处理数据传输,如读/写文件、网络通讯等。
- Java程序中,对于数据的输入/输出操作以"流(stream)"的方式进行。
流的分类
输入流和输出流
概念
Java 程序通过流来完成输入/输出,所有的输入/输出以流的形式处理。
输入就是将数据从各种输入设备(包括文件、键盘等)中读取到内存中,输出则正好相反,是将数据写入到各种输出设备(比如文件、显示器、磁盘等)。例如键盘就是一个标准的输入设备,而显示器就是一个标准的输出设备,但是文件既可以作为输入设备,又可以作为输出设备。
字节流和字符流
概念
字节流:java程序在输入和输出的时候是以字节来进行输入和输出的。(音视频文件、图片等用字节流比较好)
字符流:java程序在输入和输出的时候是以字符来进行输入和输出的。不同的字符编码,读取的格式也是不一样的,比如Unicode编码,汉字和字母都是用两个字节来存储的,但是utf-8编码,汉字用三个字节来存储,而字母用一个字节来存储。(word文件、txt文件等用字符流比较好)
节点流和处理流(包装流)
节点流:节点流可以从一个特定的数据源(文件、数组、管道等)读写数据。如FileReader、FileWriter等。
处理流(包装流):是用来包装节点流的,可以为程序提供更为强大的功能。如BufferedFileReader、BufferedFileWriter等。处理流用了修饰器设计模式。
IO流体系图
节点流和处理流
概念
节点流可以从一个特定的数据源(文件、数组、管道等)读写数据。
处理流(包装流):是用来包装节点流的,可以为程序提供更为强大的功能。处理流用了修饰器设计模式
节点流和处理流(图)
文件流
常用的文件操作(File)
创建文件
- new File(String filePath) // 根据路径构建一个File对象
- new File(File parent,String child) // 根据父目录文件+子路径构建
- new File(String parent,String child)//根据父目录 + 子路径构建
@Test
public void create02() {
File parentFile = new File("e:/");
String fileName = "news2.txt";
//这里的file对象,在java程序中,只是一个对象
//只有执行了createNewFile 方法,才会真正的,在磁盘创建该文件
File file = new File(parentFile, fileName);
try {
file.createNewFile();
System.out.println("创建成功~");
} catch (IOException e) {
e.printStackTrace();
}
}
获取文件的一些相关信息(文件的方法)
@Test
public void info() throws Exception{
String pathname = "/Users/zhaojiawei/Desktop/test/new1.txt";
File file = new File(pathname);
System.out.println("文件名:" + file.getName());//文件名:new1.txt
System.out.println("文件路径:" + file.getAbsolutePath());//文件路径:/Users/zhaojiawei/Desktop/test/new1.txt
System.out.println("父目录:" + file.getParent());//父目录:/Users/zhaojiawei/Desktop/test
System.out.println("文件大小(字节):" + file.length());//文件大小(字节):0
System.out.println("是否存在:" + file.exists());//是否存在:false
System.out.println("是不是一个文件:" + file.isFile());//是不是一个文件:false
System.out.println("是不是一个目录:" + file.isDirectory());//是不是一个目录:false
}
目录的操作和文件删除
mkdir创建一级目录、mkdirs创建多级目录、delete删除空目录或文件,这三个方法的返回值都是boolean类型,成功则返回true,失败返回false。
FileInputStream(字节输入流)
public class FileInputStream_ {
public static void main(String[] args) {
}
/**
* 演示读取文件...
* 单个字节的读取,效率比较低
* -> 使用 read(byte[] b)
*/
@Test
public void readFile01() {
String filePath = "e:\\hello.txt";
int readData = 0;
FileInputStream fileInputStream = null;
try {
//创建 FileInputStream 对象,用于读取 文件
fileInputStream = new FileInputStream(filePath);
//如果返回-1 , 表示读取完毕
while ((readData = fileInputStream.read()) != -1) {
System.out.print((char)readData);//转成char显示
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//关闭文件流,释放资源.
try {
fileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 使用 read(byte[] b) 读取文件,提高效率
*/
@Test
public void readFile02() {
String filePath = "e:\\hello.txt";
//字节数组
byte[] buf = new byte[8]; //一次读取8个字节.
int readLen = 0;
FileInputStream fileInputStream = null;
try {
//创建 FileInputStream 对象,用于读取 文件
fileInputStream = new FileInputStream(filePath);
//从该输入流读取最多b.length字节的数据到字节数组。 此方法将阻塞,直到某些输入可用。
//如果返回-1 , 表示读取完毕
//如果读取正常, 返回实际读取的字节数
while ((readLen = fileInputStream.read(buf)) != -1) {
System.out.print(new String(buf, 0, readLen));//显示
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//关闭文件流,释放资源.
try {
fileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
FileOutputStream(字节输出流)
public class FileOutputStream01 {
public static void main(String[] args) {
}
/**
* 演示使用FileOutputStream 将数据写到文件中,
* 如果该文件不存在,则创建该文件
*/
@Test
public void writeFile() {
//创建 FileOutputStream对象
String filePath = "e:\\a.txt";
FileOutputStream fileOutputStream = null;
try {
//得到 FileOutputStream对象 对象
//老师说明
//1. new FileOutputStream(filePath) 创建方式,当写入内容是,会覆盖原来的内容
//2. new FileOutputStream(filePath, true) 创建方式,当写入内容是,是追加到文件后面
fileOutputStream = new FileOutputStream(filePath, true);
//写入字符串
String str = "hsp,world!";
//str.getBytes() 可以把 字符串-> 字节数组
fileOutputStream.write(str.getBytes());
/*
write(byte[] b, int off, int len) 将 len字节从位于偏移量 off的指定字节数组写入此文件输出流
*/
//fileOutputStream.write(str.getBytes(), 0, 3);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
FileReader(字符输入流)
- new FileReader(File/String)
- read:每次读取单个字符,返回该字符,如果到文件末尾返回-1
- read(char[]):批量读取多个字符到数组,返回读取到的字符数,如果到文件末尾返回-1
FileWriter(字符输出流)
BufferedReader、BufferedWriter
字符处理文件输入流BufferedReader和字符处理文件输出流BufferedWriter演示文件拷贝,这两个流是处理流
注:在使用BufferedWriter时不会立即写入文件,只有在调用flush方法或close方法时,才会写入到文件,在网络传输中也是这样,不会立即放入到网络传输的管道中去,只有在调用flush方法或close方法是,才会放进网络传输的管道中去,对方才能读取到。
public class FileCopy {
public static void main(String[] args) {
String srcFilePath = "/Users/zhaojiawei/Desktop/b.txt";
String dstFilePath = "/Users/zhaojiawei/Desktop/a.txt";
FileCopy.fileCopy(srcFilePath,dstFilePath);
}
/**
* 演示将srcFilePath文件拷贝到dstFilePath
* @param srcFilePath 源文件
* @param dstFilePath 目标文件
*/
public static void fileCopy(String srcFilePath,String dstFilePath){
BufferedReader bufferedReader = null;
BufferedWriter bufferedWriter = null;
try {
//创建一个字符处理输入流
bufferedReader = new BufferedReader(new FileReader(srcFilePath));
//创建一个字符处理输出流
bufferedWriter = new BufferedWriter(new FileWriter(dstFilePath));
/*
* 将字符处理输入流按行读取,然后将读取到的内容放入到字符处理输出流中
*/
String content;
//当readLine读取不到内容的时候,会返回null,注:readLine读取不到换行
while((content = bufferedReader.readLine()) != null){
bufferedWriter.write(content);
//插入一个和系统相关的换行
bufferedWriter.newLine();
}
} catch (IOException e) {
e.printStackTrace();
} finally{
try {
if(bufferedReader != null) {
bufferedReader.close();
}
if(bufferedWriter != null){
bufferedWriter.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
BufferedInputStream、BufferedOutputStream
字节处理文件输入流BufferedInputStream和字节处理文件输出流BufferedOutputStream 演示文件拷贝,这两个流是处理流,
注:在使用BufferedOutputStream时不会立即写入文件,只有在调用flush方法或close方法时,才会写入到文件,在网络传输中也是这样,不会立即放入到网络传输的管道中去,只有在调用flush方法或close方法是,才会放进网络传输的管道中去,对方才能读取到。
public class FileCopy2 {
public static void main(String[] args) {
String srcFilePath = "/Users/zhaojiawei/Desktop/b.txt";
String dstFilePath = "/Users/zhaojiawei/Desktop/a.txt";
FileCopy.fileCopy(srcFilePath,dstFilePath);
}
/**
* 演示将srcFilePath文件拷贝到dstFilePath,一般来说,音视频、图片文件用字节流读取好,而文档用字符流读取好
* @param srcFilePath 源文件
* @param dstFilePath 目标文件
*/
public static void fileCopy(String srcFilePath,String dstFilePath){
BufferedInputStream bufferedInputStream = null;
BufferedOutputStream bufferedOutputStream = null;
try {
//创建一个字节处理文件输入流
bufferedInputStream = new BufferedInputStream(new FileInputStream(srcFilePath));
//创建一个字节处理文件输出流
bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(dstFilePath));
/*
* 将字节处理文件输入流读取到bytes数组中去,然后将读取到的内容放入到字节处理文件输出流中
*/
byte[] bytes = new byte[1024];
int len;
//read(byte[] b)可以读取到换行符
while((len = bufferedInputStream.read(bytes)) != -1){
bufferedOutputStream.write(bytes,0,len);
}
} catch (IOException e) {
e.printStackTrace();
} finally{
try {
if(bufferedInputStream != null) {
bufferedInputStream.close();
}
if(bufferedOutputStream != null){
bufferedOutputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
对象流(序列化、反序列化) (处理流)
ObjectInputStream(反序列化)和ObjectOutputStream(序列化),这两个流是处理流,是用来包装节点流的。主要将对象存储进文件中,或在网络中进行传输
序列化和反序列化
- 序列化就是在保存数据时,保存数据的值和数据类型。
- 反序列化就是在恢复数据时,恢复数据的值和数据类型。
- 要让某个类支持序列化,这该类必须实现下面两个接口:
- Serializable //这是一个标记接口,没有方法
- Externalizable //该接口有方法需要实现,因此我们一般实现上面的Serializable接口
注意事项
-
读写顺序要一致
-
序列化或反序列化对象时,需要实现Serializable
-
序列化的类中建议添加SerialVersionUID.为了提高版本的兼容性
-
//序列化的版本号,如果该类改变,则你应该显示的改变其版本号,反序列化的时候会检查该类的版本号是否一致,如果不一致,则反序列化失败 private static final long serialVersionUID = 1L;
-
-
序列化对象时,默认将里面所有属性都进行序列化,但除了static或transient修饰的成员。
-
序列化对象时,要求里面属性的类型也需要实现序列化接口。
-
序列化具备可继承性,也就是如果某类已经实现了序列化,则它的所有子类也已经默认实现了序列化。
对象流序列化
public class ObjectOutputStream01 {
public static void main(String[] args) throws Exception{
String filePath = "/Users/zhaojiawei/Desktop/c.txt";
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(filePath));
objectOutputStream.writeInt(100);
objectOutputStream.writeBoolean(true);
objectOutputStream.writeChar('a');
objectOutputStream.writeDouble(9.5);
objectOutputStream.writeUTF("hello world");
objectOutputStream.writeObject(new Dog("旺财",10));
objectOutputStream.close();
System.out.println("序列化完毕");
}
对象流反序列化
public class ObjectInputStream01 {
public static void main(String[] args) throws Exception{
String filePath = "/Users/zhaojiawei/Desktop/c.txt";
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filePath));
//读取的顺序和你保存数据一致
System.out.println(ois.readInt());
System.out.println(ois.readBoolean());
System.out.println(ois.readChar());
System.out.println(ois.readDouble());
System.out.println(ois.readUTF());
Object o = ois.readObject();
System.out.println((Dog)o);
ois.close();
}
}
标准输入和输出流
打印流
PrintStream
public class PrintStream_ {
public static void main(String[] args) throws IOException {
PrintStream out = System.out;
//在默认情况下,PrintStream 输出数据的位置是 标准输出,即显示器
/*
public void print(String s) {
if (s == null) {
s = "null";
}
write(s);
}
*/
out.print("john, hello");
//因为print底层使用的是write , 所以我们可以直接调用write进行打印/输出
out.write("韩顺平,你好".getBytes());
out.close();
//我们可以去修改打印流输出的位置/设备
//1. 输出修改成到 "e:\\f1.txt"
//2. "hello, 韩顺平教育~" 就会输出到 e:\f1.txt
//3. public static void setOut(PrintStream out) {
// checkIO();
// setOut0(out); // native 方法,修改了out
// }
System.setOut(new PrintStream("e:\\f1.txt"));
System.out.println("hello, 韩顺平教育~");
}
}
PrintWriter
public class PrintWriter_ {
public static void main(String[] args) throws IOException {
//PrintWriter printWriter = new PrintWriter(System.out);
PrintWriter printWriter = new PrintWriter(new FileWriter("e:\\f2.txt"));
printWriter.print("hi, 北京你好~~~~");
printWriter.close();//flush + 关闭流, 才会将数据写入到文件..
}
}
转换流(InputStreamReader和OutputStreamWriter) 转换流是节点流
作用:即将字节流转换为字符流。转换流可以在输入和输出的时候指定编码格式,例如InputStreamReader(InputStream in,String charset);
简介
InputStreamReader
public static void main(String[] args) throws Exception{
String filePath = "/Users/zhaojiawei/Desktop/b.txt";
InputStreamReader inputStreamReader = new InputStreamReader(new FileInputStream(filePath), StandardCharsets.UTF_8);
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String line;
while((line = bufferedReader.readLine()) != null){
System.out.println(line);
}
bufferedReader.close();
}
OutputStreamWriter
public static void main(String[] args) throws Exception{
String filePath = "/Users/zhaojiawei/Desktop/a.txt";
java.io.OutputStreamWriter outputStreamWriter = new java.io.OutputStreamWriter(new FileOutputStream(filePath), StandardCharsets.UTF_8);
BufferedWriter bufferedWriter = new BufferedWriter(outputStreamWriter);
bufferedWriter.write("你好啊,测试测试");
bufferedWriter.close();
}
Properties类
简介
常见方法
使用Properties 类来读取mysql.properties 文件
public class Properties02 {
public static void main(String[] args) throws IOException {
//使用Properties 类来读取mysql.properties 文件
//1. 创建Properties 对象
Properties properties = new Properties();
//2. 加载指定配置文件
properties.load(new FileReader("src\\mysql.properties"));
//3. 把k-v显示控制台
properties.list(System.out);
//4. 根据key 获取对应的值
String user = properties.getProperty("user");
String pwd = properties.getProperty("pwd");
System.out.println("用户名=" + user);
System.out.println("密码是=" + pwd);
}
}
使用Properties 类来创建 配置文件, 修改配置文件内容
public class Properties03 {
public static void main(String[] args) throws IOException {
//使用Properties 类来创建 配置文件, 修改配置文件内容
Properties properties = new Properties();
properties.setProperty("charset", "utf8");
properties.setProperty("user", "汤姆");//注意保存时,是中文的 unicode码值
properties.setProperty("pwd", "888888");
//将k-v 存储文件中即可
properties.store(new FileOutputStream("src\\mysql2.properties"), null);
System.out.println("保存配置文件成功~");
}
}
总结
通常来说,音视频文件、图片用字节输入流和输出流读取和写入比较好,而word文档、txt等文件用字符输入流和输出流读取比较好