网络IO
1. 网络IO
1.1 什么是IO流以及IO流的作用
I/O实际上是Input和Output,也就是输入和输出。而流其实是一种抽象的概念,它表示的是数据的无结构化传递。会被当成无结构的字节序列或字符序列。流可以当作是磁盘与内存之间的一个管道。
1.2 IO流的分类
在Java中I/O流操作很多,但是核心体系实际上就只有File(文件流)、InputStream(字节输入流)、OutputStream(字节输出流)、Reader(字符输入流)、Writer(字符输出流)。
- 字节流:操作的数据单元是8位的字节。InputStream、OutputStream作为抽象基类。可以处理所有的数据文件。
- 字符流:操作的数据单元是字符。以Writer、Reader作为抽象基类。只限于处理文本的数据文件。
- 访问管道处理流,是用来去完成管道的读写操作,用于线程间的通讯
- 访问数组处理流,是针对内存的操作
- 缓冲流是提供一个缓冲区,对于缓冲区的一个处理流,避免每次与磁盘的交互,提高输入输出的一个效率
- 对象流,主要用在序列化这个机制上,将一个对象序列化后转换成一个可存储可传输的对象,传输时用到的流。
- 转换流:将字符流转换成字节流
- 打印流
2. IO流的数据来源及操作的API
- 硬盘
- 内存
- 键盘
- 网络
2.1 File类简介
File类是Java中为文件进行创建、删除、重命名、移动等操作而设计的一个类
- File(File parent, String child):根据parent抽象路径名和child路径名字符串创建一个新的File实例。
- File(String pathname):将指定路径名转化为抽象路径名创建一个新的File实例。
- File(String parent, String child):根据parent路径名和child路径名创建一个File实例。
- File(URI uri):指定URI转化为抽象路径名。
2.2 基于文件的输入输出流
public static void main(String[] args) {
File file = new File("D:\\appdata\\IODemo\\Capture001.png");
try (
FileOutputStream fileOutputStream = new FileOutputStream("D:\\appdata\\IODemo\\Capture002.png");
FileInputStream fileInputStream = new FileInputStream(file)) { // 1.7之后,将流写入try()中,代码执行完毕后,会自动关闭流
int len = 0;
byte[] buffer = new byte[1024];
long start = System.currentTimeMillis();
while ((len = fileInputStream.read(buffer)) != -1) {
fileOutputStream.write(buffer, 0, len);
}
long end = System.currentTimeMillis();
System.out.println((end - start) / 1000);
} catch (IOException e) {
e.printStackTrace();
}
}
流一定要关闭,否则当前线程没执行完会一直使其被进程占用。
try (FileReader reader = new FileReader("/appdata/IODemo/IODemo");
FileWriter writer = new FileWriter("/appdata/IODemo/IODemo.txt")) {
int i = 0;
char[] chars = new char[1];
while ((i = reader.read(chars)) != -1) {
writer.write(new String(chars, 0, i));
}
} catch (Exception e) {
e.printStackTrace();
}
2.3 缓冲流
缓冲流是带缓冲区的处理流,他会提供一个缓冲区,缓冲区的作用主要目的是:避免每次和硬盘打交道,能够提高输入/输出的执行效率。
BufferedInputStream
private static int DEFAULT_BUFFER_SIZE = 8192; // 默认8Kb的缓冲区
private static int MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8; // 最大缓冲区大小
// 每次读取的8Kb size的字节会存储在buf[]数组中
//每次调用read()方法时,会首先去尝试从这个数组中读取,如果读取失败,会从数据源(磁盘上)去读取
protected volatile byte buf[];
// 两种构造方法最终调用该方法,带int参数的会覆盖默认的8Kb size
public BufferedInputStream(InputStream in, int size) {
super(in);
if (size <= 0) {
throw new IllegalArgumentException("Buffer size <= 0");
}
buf = new byte[size];
}
其实缓冲流原理上是帮我们封装了8Kb大小的数据,先从磁盘读8Kb到我们内存,后由我们自己去操作这8Kb的数据,当处理完8Kb缓冲区没有了,再加载数据到缓冲区,再读到内存去处理。当我们用普通流去处理文件,将buffer[]设置的稍微大一点,一样可以达到提高效率的结果。
public static void main(String[] args) {
try (BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream("/appdata/IODemo/IODemo"));
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream("/appdata/IODemo/IODemo.txt"))) {
int len = 0;
byte[] bytes = new byte[1024];
while ((len = bufferedInputStream.read(bytes)) != -1) {
// System.out.println(new String(bytes, 0, len));
bufferedOutputStream.write(bytes, 0, len);
bufferedOutputStream.flush();
}
} catch (IOException e) {
e.printStackTrace();
}
}
将创建InputStream写入到try()中,可以帮我们实现close()关闭流的操作,这个close中包含了buffred的flush操作,如果没有关闭流,又没有手动flush(),将会丢失数据。
public void close() throws IOException {
try (OutputStream ostream = out) {
flush();
}
}
try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream("/appdata/IODemo/IODemo"), StandardCharsets.UTF_8))) {
String str;
while ((str = reader.readLine()) != null) {
System.out.println(str);
}
} catch (Exception e) {
e.printStackTrace();
}
2.4 转换流
try (InputStream inputStream = new FileInputStream("/appdata/IODemo/IODemo");
InputStreamReader reader = new InputStreamReader(inputStream, StandardCharsets.UTF_8)) {
char[] chars = new char[1024];
int i;
while ((i = reader.read(chars)) != -1) {
System.out.println(new String(chars, 0, i));
}
} catch (Exception e) {
e.printStackTrace();
}
在这个转换流中,时可以指定字符集编码的。
2.5 对象流
关于序列化和反序列化这个问题,我在18年参加工作的时候,遇到过一个项目,之后就再没有用过了。当时架构还是分布式dubbo+zookeeper,但是传输报文竟然用到这个我是没想到的。
什么是序列化和反序列化?
- 序列化是把对象的状态信息转化为可存储或传输的形式的过程,也就是把对象转化为字节序列的过程成为对象的序列化
- 反序列化是序列化的逆向过程,把字节数组反序列化为对象。
public class UserSerializable implements Serializable {
private static final long serialVersionUID = 8160464260217334369L;
private String name;
private int age;
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "UserSerializable{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public static void main(String[] args) {
UserSerializable user = new UserSerializable();
user.setAge(26);
user.setName("Elian");
String fileName = "/appdata/IODemo/User";
try (FileInputStream fileInputStream = new FileInputStream(fileName);
FileOutputStream fileOutputStream = new FileOutputStream(fileName);
ObjectOutputStream outputStream = new ObjectOutputStream(fileOutputStream);
ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream)
) {
outputStream.writeObject(user);
outputStream.flush();
UserSerializable newUser = (UserSerializable) objectInputStream.readObject();
System.out.println(newUser);
} catch (Exception e) {
e.printStackTrace();
}
}
}
3. 本地IO和网络IO
3.0 本地I/O操作实例
public class NIOFirstDemo {
public static void main(String[] args) {
bio();
bufferBio();
nio();
mmap();
zeroCopy();
}
private static void bio() {
try (FileInputStream bioInputStream = new FileInputStream("/appdata/IODemo/jdk api 1.8_google.CHM");
FileOutputStream bioOutputStream = new FileOutputStream("/appdata/IODemo/jdk_bio.CHM")) {
// bio实现copy
long bioStart = System.currentTimeMillis();
int len = 0;
byte[] buffer = new byte[1024];
while ((len = bioIn