流的分类
按照流的方向分类
- 输入流:数据流向是输入源到程序(InputStream,Reader结尾的流)
- 输出流:数据流向是程序到目的地(OutputStream,Writer结尾的流)
按处理的数据单元分类
- 字节流:以字节为单位获取数据,命名一般以Stream为结尾
- 字符流:以字符为单位获取数据,命名一般是Reader/Writer为结尾
按处理的对象不同分类
- 节点流:可以直接从目的地或者数据源中读取数据(FileInputStream,FileReader,DataInputStream)
- 处理流:不直接连接到数据源或者目的地,是处理流的流,通过对其他流的处理来提高程序性能,如BufferedInputStream,BufferedReader等,处理流也叫包装流。
JavaIO四大基石
- 字节流接口InputStream,OutputStream
- 字符流:Reader,Writer
通过字节缓冲区进行文件的复制
package com.itbaizhan;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class TestFileByteBuffer {
public static void main(String[] args) {
final long l = System.currentTimeMillis();
copyFile("d:/1.png","d:/2.png");
final long l1 = System.currentTimeMillis();
System.out.println(l1-l);
}
/**
*
* @param source 源文件的路径
* @param destination 目标文件的路径
*/
public static void copyFile(String source,String destination){
//后开先关,try-with-source按照IO流被创建的顺序逆序关闭
try(FileInputStream fis = new FileInputStream(source);
FileOutputStream fos = new FileOutputStream(destination)) {
int temp;
//单个读取比较慢,添加一个缓冲区,通过缓冲区,很快,使用前5968ms,使用后0ms
byte[] buffer = new byte[1024];
while((temp = fis.read(buffer)) != -1){
//temp此时获得是数组的长度
fos.write(buffer,0,temp);
}
fos.flush();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
文件字节流
- FileInputStream通过字节的方式读取文件,适合读取所有类型的文件(图像、视频、文本、文件等)
try(FileInputStream fileInputStream = new FileInputStream("d:/a.txt")) {
StringBuilder stringBuilder = new StringBuilder();
int temp = 0;
while((temp = fileInputStream.read()) != -1){
char temp1 = (char) temp;
stringBuilder.append(temp1);
}
System.out.println(stringBuilder);
} catch (IOException e) {
throw new RuntimeException(e);
}
- FileOutputStream通过字节的方式写数据到文件中,适合所有类型的文件(图像、视频、文本文件等)
//构造函数有两个参数,第二boolean类型的参数:true代表在文件末尾添加内容,false表示覆盖文件中的内容,默认为false
try(FileOutputStream fileOutputStream = new FileOutputStream("d:/a.txt",true)) {
String str = "LiXinYan";
fileOutputStream.write(str.getBytes());
//刷新,将内存中的值写入文件
fileOutputStream.flush();
} catch (IOException e) {
throw new RuntimeException(e);
}
通过字节缓冲区提高效率
通过创建一个指定长度的数组作为缓冲区,以此来提高IO流的读写效率。该方式适用于读取较大文件时的缓冲区定义。注意:缓冲区的长度一定是2的整数幂。一般情况下1024长度较为合适。
package com.itbaizhan;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class TestFileByteBuffer {
public static void main(String[] args) {
final long l = System.currentTimeMillis();
copyFile("d:/1.png","d:/2.png");
final long l1 = System.currentTimeMillis();
System.out.println(l1-l);
}
/**
*
* @param source 源文件的路径
* @param destination 目标文件的路径
*/
public static void copyFile(String source,String destination){
//后开先关,try-with-source按照IO流被创建的顺序逆序关闭
try(FileInputStream fis = new FileInputStream(source);
FileOutputStream fos = new FileOutputStream(destination)) {
int temp;
//单个读取比较慢,添加一个缓冲区,通过缓冲区,很快,使用前5968ms,使用后0ms
byte[] buffer = new byte[1024];
while((temp = fis.read(buffer)) != -1){
//temp此时获得是数组的长度
fos.write(buffer,0,temp);
}
fos.flush();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
缓冲字节流
- BufferedInputStream
- BufferedOutputStream
Java缓冲流本身不具有IO流的读取和写入功能,只是在别的流(节点流和其他处理流)上加上缓冲功能提高效率,就像是把别的的流包装起来一样,因此缓冲流是一种处理流(包装流)
BufferedInputStream和BufferedOutputStream这两个流是缓冲字节流,通过内部缓存数组来提高操作流的效率。
使用缓冲流来实现文件的高效率复制
package com.itbaizhan;
import java.io.*;
public class TestFileBufferStream {
public static void main(String[] args) {
final long l = System.currentTimeMillis();
copyFile("d:/2.png","d:/3.png");
final long l1 = System.currentTimeMillis();
System.out.println(l1-l);
}
public static void copyFile(String source,String destination){
try(FileInputStream inputStream = new FileInputStream(source);
final FileOutputStream outputStream = new FileOutputStream(destination);
//通过BufferStream来进行读取,缓存区的大小默认是8192字节
final BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);
final BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(outputStream)) {
int temp = 0;
while((temp = bufferedInputStream.read()) != -1){
bufferedOutputStream.write(temp);
}
bufferedOutputStream.flush();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
文件字符流
前面介绍的文件字节流可以处理所有的文件,如果我们处理的是文本文件,也可以使用文件字符流,它以字符为单位进行操作
- FileReader
public static void main(String[] args) {
try(FileReader fileReader = new FileReader("d:/a.txt")) {
StringBuilder stringBuilder = new StringBuilder();
int temp;
while((temp = fileReader.read()) != -1){
stringBuilder.append((char)temp);
}
System.out.println(stringBuilder);
} catch (IOException e) {
throw new RuntimeException(e);
}
- FileWriter
package com.itbaizhan;
import java.io.FileWriter;
import java.io.IOException;
public class TestFileWriter {
public static void main(String[] args) {
//true:开启追加
try(FileWriter fileWriter = new FileWriter("d:/aa.txt",true)){
fileWriter.write("破绽,稍纵即逝\r\n");
fileWriter.write("随蝴蝶一起消散吧,旧日的幻影\r\n");
fileWriter.flush();
}catch (IOException e){
}
}
}
缓冲字符流
BufferedReader和BufferedWriter增加了缓存机制,大大提高了读写文本文件的效率。
- 字符输入缓冲流BufferedReader
package com.itbaizhan;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class TestBufferedReader {
public static void main(String[] args) {
try(final BufferedReader bufferedReader = new BufferedReader(new FileReader("d:/aa.txt"))){
StringBuilder stringBuilder = new StringBuilder();
String temp;
while((temp = bufferedReader.readLine()) != null){
stringBuilder.append(temp);
}
System.out.println(stringBuilder);
}catch (IOException e){
e.printStackTrace();
}
}
}
- 字符输出缓冲流BufferedWriter
package com.itbaizhan;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
public class TestBufferedWriter {
public static void main(String[] args) {
try(final BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter("d:/aaa.txt"))){
bufferedWriter.write("今日听君歌一曲");
bufferedWriter.newLine();
bufferedWriter.write("暂凭杯酒长精神");
bufferedWriter.newLine();
bufferedWriter.write("感时花溅泪");
bufferedWriter.newLine();
bufferedWriter.write("恨别鸟惊心");
bufferedWriter.flush();
//执行close()方法会自动flush
}catch (IOException e){
e.printStackTrace();
}
}
}
- 为文件中的内容添加行号
package com.itbaizhan;
import java.io.*;
public class TestLineNumber {
public static void main(String[] args) {
try(BufferedReader br = new BufferedReader(new FileReader("d:/aaa.txt"));
BufferedWriter bw = new BufferedWriter(new FileWriter("d:/a2.txt"))){
int i = 1;
String temp;
while((temp = br.readLine()) != null){
bw.write(i+"."+temp);
bw.newLine();
i++;
}
bw.flush();
}catch(IOException e){
e.printStackTrace();
}
}
}
转换流
InputStreamReader和OutputStreamWriter用来实现将字节流转化成字符流
通过转换流解决乱码
ANSI不是一种编码,而是对于对应的文字采取相对应的编码,比如中文就是GBK
package com.itbaizhan;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
/**
* 通过转换流解决乱码
*/
public class TestInputStreamReader {
public static void main(String[] args) {
try(FileInputStream fileInputStream = new FileInputStream("d:/a2.txt");
//字节流转换到字符流,如果文件设置为ANSI中文就是GBK编码格式,通过添加参数来改变编码格式,
InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream,"GBK")){
StringBuilder stringBuilder = new StringBuilder();
int temp = 0;
while((temp = inputStreamReader.read()) != -1){
stringBuilder.append((char) temp);
}
System.out.println(stringBuilder);
}catch (IOException e){
e.printStackTrace();
}
}
}
通过字节流读取文本并添加行号
public static void main(String[] args) {
//创建字符输入缓冲流、输入字节到字符转换流、文件字节输入流对象
try(BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream("d:/a2.txt"),"GBK"));
//创建字符输出缓冲流、输出字符到字节转换流、文件字节输出对象
BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("d:/a4.txt")))
){
int i = 1;
String temp;
while ((temp = bufferedReader.readLine()) != null){
bufferedWriter.write(i+" "+temp);
bufferedWriter.newLine();
i++;
}
bufferedWriter.flush();
}catch (IOException e){
e.printStackTrace();
}
通过转换流来实现键盘输入屏幕输出
package com.itbaizhan;
import java.io.*;
/**
* 接受键盘输出的流并输出
*/
public class TestKeyboard {
public static void main(String[] args) {
//System.in返回一个InputStream对象,创建键盘输入流对象
try(BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
//System.out返回一个OutputStream对象,创建屏幕输出的流对象
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out))
){
while (true){
bw.write("请输入:");
bw.flush();
String input = br.readLine();
if(input.equals("exit") || input.equals("quit")){
bw.write("Bye Bye!");
bw.flush();
break;
}
bw.write(input);
bw.newLine();
bw.flush();
}
}catch (IOException e){
}
}
}
字符输出流PrintWriter
在Java的IO中专门提供了用于字符输出的对象PrintWriter。该对象具有自动刷新缓冲字符输出流,特点是可以按行写出字符串,并可以通过println();方法实现自动换行。
package com.itbaizhan;
import java.io.IOException;
import java.io.PrintWriter;
public class TestPrintWriter {
public static void main(String[] args) {
try(PrintWriter printWriter = new PrintWriter("d:/sxt.txt")){
printWriter.print("abc");
printWriter.print("def");
//换行输出流
printWriter.println("oldLU");
printWriter.println("Linxinyan");
printWriter.flush();
}catch (IOException e){
e.printStackTrace();
}
}
}
通过字符输出流添加行号
package com.itbaizhan;
import java.io.*;
public class TestLineNumberInPrintWriter {
public static void main(String[] args) {
try(
BufferedReader bufferedReader = new BufferedReader(new FileReader("d:/aaa.txt"));
PrintWriter pw = new PrintWriter("d:/linenumber.txt")){
int i = 1;
String temp;
while((temp = bufferedReader.readLine()) != null){
//输出内容并带有换行效果
pw.println(i +" "+ temp);
i++;
}
//刷新
pw.flush();
}catch (IOException e){
e.printStackTrace();
}
}
}
数据流
- DataInputStream
- DataOutputStream
数据流将”基本数据类型与字符串类型“作为数据源,从而允许程序以机器无关的方式从底层输入输出流中操作Java基本数据类型与字符串。 DataInputStream和DataOutputStream提供了可以存取与机器无关的所有Java基础类型数据(如:int、double、String等)的方法。
package com.itbaizhan;
import java.io.*;
public class TestDataStream {
public static void main(String[] args) {
try(
DataOutputStream dos = new DataOutputStream(new FileOutputStream("d:/data"));
DataInputStream dis = new DataInputStream(new FileInputStream("d:/data"))
){
//写入以下数据
dos.writeChar('a');
dos.writeInt(10);
dos.writeDouble(Math.random());
dos.writeBoolean(true);
dos.writeUTF("北京尚学堂");
dos.flush();
//读取数据,顺序必须和写入一样,不然会出错
System.out.println("Char:"+dis.readChar());
System.out.println("Int:"+dis.readInt());
System.out.println("Double:"+dis.readDouble());
System.out.println("Boolean:"+dis.readBoolean());
System.out.println("String:"+dis.readUTF());
}catch (IOException e){
e.printStackTrace();
}
}
}
对象流
- ObjectInputStream
- ObjectOutputStream
我们前边学的数据流只能实现对基本数据类型和字符串类型的读写,并不能读取对象(字符串除外),如果要对某个对象进行读写操作,我们需要学习一对新的处理流。
处理基本数据类型的数据
package com.itbaizhan;
import java.io.*;
public class TestObjectStream {
public static void main(String[] args) {
try(ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("d:/data2"));
ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("d:/data2"))){
outputStream.writeInt(10);
outputStream.writeChar('a');
outputStream.writeBoolean(true);
outputStream.writeDouble(Math.random());
outputStream.writeUTF("北京故宫博物院");
outputStream.flush();
System.out.println(inputStream.readInt());
System.out.println(inputStream.readChar());
System.out.println(inputStream.readBoolean());
System.out.println(inputStream.readDouble());
System.out.println(inputStream.readUTF());
}catch (IOException e){
e.printStackTrace();
}
}
}
Java对象的序列化和反序列化
对象----序列化---->二进制数据
二进制数据----反序列化---->对象
序列化和反序列化是什么
当两个进程远程通信时,彼此可以发送各种类型的数据。无论是何种类型的数据,都会以二进制序列的形式在网络上传送。比如,我们可以通过http协议发送字符串的信息;我们也可以在网络上直接发送Java对象。发送方需要把这个Java对象转换为字节序列,才能在网络上传送,接收方则需要把字节序列恢复为Java对象才能正常读取。
把Java对象转为字节序列的过程称为对象的序列化。把字节序列恢复为Java对象的过程称为字节的反序列化。
序列化涉及的类和接口
ObjectOutputStream代表对象输出流,它的writeObject(Object obj)方法可对参数指定的obj对象进行序列化,把得到的字节序列写到一个目标输出流中
ObjectInputStream代表对象输入流,他的readObject()方法从一个源输入流中读取字节序列,再把它们反序列化为一个对象,并将其返回。
只有实现了Serializable接口的类的对象才能被序列化。Serializable接口是一个空接口,只有标记作用。
User代码:
import java.io.Serializable;
/**
* Serializable是标识接口,其中没有任何的抽象方法
*/
public class User implements Serializable {
private int id;
private String username;
private String userage;
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + ''' +
", userage='" + userage + ''' +
'}';
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getUserage() {
return userage;
}
public void setUserage(String userage) {
this.userage = userage;
}
}
ObjectOutputStream,写入到文件中,序列化
public static void main(String[] args) {
try(ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("d:/data3"))){
User user = new User();
user.setId(1);
user.setUsername("lixinyan");
user.setUserage("20");
oos.writeObject(user);
oos.flush();
}catch (Exception e){
e.printStackTrace();
}
}
ObjectInputStream,读取文件中的对象,反序列化
public static void main(String[] args) {
//创建对象字节输入流和文件字节输入流对象
try(ObjectInputStream ois = new ObjectInputStream(new FileInputStream("d:/data3"))){
//反序列化处理
User u = (User) ois.readObject();
System.out.println(u.getId());
System.out.println(u.getUserage());
System.out.println(u.getUsername());
}catch (Exception e){
e.printStackTrace();
}
}
File类在IO中的作用
当文件作为数据源或者目标时,除了可以使用字符串作为文件以及位置的指定以外,我们也可以使用File类指定。
package com.itbaizhan;
import java.io.*;
public class TestFile {
public static void main(String[] args) {
try(
BufferedReader br = new BufferedReader(new FileReader(new File("d:/aaa.txt")));
PrintWriter pr = new PrintWriter(new FileWriter(new File("d:/sxt.txt")))){
int i = 1;
String temp;
while((temp = br.readLine()) != null){
pr.println(i+","+temp);
i++;
}
pr.flush();
}catch (IOException e){
e.printStackTrace();
}
}
}
装饰器模式构建IO流体系
装饰器模式简介
装饰器模式是GOF23种设计模式中较为常用的一种模式。它可以实现对原有类的包装和装饰,使新的类具有更强的功能。
普通类和装饰器类
package com.itbaizhan.decoration;
public class Phone {
private String name;
public Phone(String name) {
this.name = name;
}
public Phone() {
}
public void show(){
System.out.println("我是"+name+"可以在屏幕上显示");
}
}
class Decoration{
Phone phone = new Phone();
public Decoration(Phone phone) {
this.phone = phone;
}
public void show(){
this.phone.show();
System.out.println("添加投影功能");
}
}
测试装饰器和普通类的方法
package com.itbaizhan.decoration;
public class TestDecoration {
public static void main(String[] args) {
Phone iPhone13 = new Phone("iPhone13");
Decoration decoration = new Decoration(iPhone13);
iPhone13.show();
System.out.println("===================使用装饰器===================");
decoration.show();
}
}
IO流体系中的装饰器模式
IO流体系中大量使用了装饰器模式,让流具有更强的功能、更强的灵活性。
显然BufferedInputStream装饰了原有的FileInputStream,让普通的FileInputStream也具备了缓存功能,提高了效率。
ApachIO介绍
FileUtils类中常用方法介绍
| 方法名 | 使用说明 |
|---|---|
| cleanDirectory | 清空目录,但不删除内容 |
| contentEquals | 比较两个文件的内容是否相同 |
| copyDirectory | 将一个目录内容拷贝到另一个目录,可以通过FileFilter过滤需要拷贝的文件 |
| copyFile | 将一个文件拷贝到一个新的地址 |
| copyFileToDirectory | 将一个文件拷贝到某个目录下 |
| copyInputStreamToFile | 将一个输入流中的内容拷贝到某个文件 |
| deleteDirectory | 删除目录 |
| deleteQuietly | 删除文件 |
| listFiles | 列出指定目录下的所有文件 |
| openInputStream | 打开指定文件的输入流 |
| readFileToString | 将文件内容作为字符串返回 |
| readLines | 将文件内容按行返回到一个字符串数组中 |
| size | 返回文件或目录的大小 |
| write | 将字符串内容直接写入文件中 |
| writeByteArrayToFile | 将字节数组的内容写入到文件中 |
| writeLines | 将容器中的元素的toString方法返回的内容依次写入文件中 |
| writeStringToFile | 将字符串内容写到文件中 |
读取文件中内容,并输出到控制台上,只需一行代码
public static void main(String[] args) throws IOException {
//通过工具类可以一下读出文件中的所有方法
String s = FileUtils.readFileToString(new File("d:/aaa.txt"), StandardCharsets.UTF_8);
System.out.println(s);
}
使用FileUtils工具类实现目录拷贝
我们可以使用FileUtils完成目录拷贝,在拷贝过程中可以通过文件过滤器FileFilter选择拷贝内容
public static void main(String[] args) throws IOException {
FileUtils.copyDirectory(new File("d:/aaa"), new File("d:/bbb"), new FileFilter() {
@Override
public boolean accept(File pathname) {
if(pathname.isDirectory() || pathname.getName().endsWith("html")){
return true;
}else {
return false;
}
}
});
}
IOUtils的妙用
| 方法名 | 使用方法 |
|---|---|
| buffer | 将传入的流进行包装,变成缓冲流。并可以通过参数指定缓冲大小 |
| closeQueitly | 关闭流 |
| contentEquals | 比较两个流中的内容是否一致 |
| copy | 将输入流中的内容拷贝到输出流中,并可以指定字符编码 |
| copyLarge | 将输入流中的内容拷贝到输出流中,适合大于2G内容的拷贝 |
| lineIterator | 返回可以迭代每一行内容的迭代器 |
| read | 将输入流中部分内容读入到字节数组中 |
| readFully | 将输入流中的所有内容读入到字节数组中 |
| readLine | 读入输入流内容中的一行 |
| toBufferedInputStream,toBufferedReader | 将输入转为带缓存的输入流 |
| toByteArray,toCharArray | 将输入流的内容转为字节数组、字符数组 |
| toString | 将输入流或者数组中的内容转化为字符串 |
| write | 向流里面写入内容 |
| writeLine | 向流里面写入一行内容 |
String s = IOUtils.toString(new FileInputStream("d:/a2.txt"), "GBK");
System.out.println(s);