Java IO/NIO 一文
首先要明确的是,在JAVA中的输入输出流都是数据流,数据流都在内存中
java.io.*
在这个包下
最简单最常用的两个就是
Reader/Writer 字符流
InputStream/OutputStream 字节流
在IDEA下查看java.io.*下的所有类的diagram图即可
不过图不好看,太小了.
用思维导图软件自己做一个即可
什么是Java序列化 ,怎么实现
序列化就是把数据对象变成流,这样的数据流就可以读写,网络IO,存储等等
NOSQL中必须要实现Serializable接口,因为NOSQL中没有对应的数据类型
具体就是实现Serializable接口即可。实现接口,但是不需要实现该接口的任何方法,相当于打上标志。
实现Serializable 一般还需要一个serialVersionUID 来保证传输对象的一致性
private static final long serialVersionUID = 1L;
用下面这段代码就可以很好的解释
注意事项:
1. 只有实现Serializable接口才能变成二进制文本存储或者传输
2.必须保证serialVersionUID前后的一致性
3.序列化对象的成员对象也应该是序列化的
有一些类不能序列化,Thread等等都是瞬间变换的,可以加上transient
4.不需要序列化字段的加上关键字transient
5.单例类序列化,需要重写readResolve()方法;否则会破坏单例原则。
6.同一对象序列化多次,只有第一次序列化为二进制流,以后都只是保存序列化编号,不会重复序列化。
package io;
import java.io.*;
public class SerializableTest {
public static void main(String[] args) {
//writePerson();
// 如果写完Person ,把Person的serialVersionUID 改掉,再读会报错
//readPerson();
}
public static void writePerson(){
Person person = new Person(1000,"Mark");
try {
ObjectOutputStream objectOutputStream= new ObjectOutputStream(new FileOutputStream("/person.txt"));
objectOutputStream.writeObject(person);
objectOutputStream.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("OK");
}
public static void readPerson() {
ObjectInputStream objectInputStream = null;
try {
objectInputStream = new ObjectInputStream(new FileInputStream("/person.txt"));
} catch (IOException e) {
e.printStackTrace();
}
Object person = null;
try {
person = objectInputStream.readObject();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
System.out.println((Person) person);
}
}
package io;
import java.io.Serializable;
public class Person implements Serializable {
private Integer id;
private String name;
private static final long serialVersionUID = 1L;
public Person(Integer id, String name) {
this.id = id;
this.name = name;
}
@Override
public String toString() {
return "Person{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
字节流和字符流的区别
字节流操作ASCII编码的,为了使用Unicode编码的文件,引入字符流。
System.out.println()的本质
这个问题很多人都不知道,只知道用
System是一个类
out是类型为PrintStream的成员变量
println是PrintStream类的一个方法
特殊的类 RandomAccessFile.class
RandomAccessFile 是java.io包下一个特殊的类随机文件读写
类似于Python 中的读写 写代码方式非常的像
可以读也可以写 好像有点老了,没人用了。
都改用nio包了
这里不介绍了,有兴趣自己百度看看别人的
java.nio.*
java nio 是解决java 传统io流阻塞性问题的 java 1.4 以上才出现
nio 核心就3个东西
buffer 缓存
channel 通道
selector 选择器
选择器是重中之中
nio 和 io 包区别就是
-
io包中是面向流的,数据通过流直接写入或取出
nio包中视面向缓冲的,数据都是写入缓冲中然后再更新
-
io流都是单向的,一个input,一个output
nio是通道channel,可以是双向的
-
io实现是阻塞的,nio是非阻塞的
用一个实例来看一下他们使用区别
package io;
import java.io.*;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
public class IOandNIO {
public static void main(String[] args) {
IOmethod();
System.out.println();
NIOmethod();
}
public static void IOmethod(){
InputStream inputStream =null;
try {
inputStream = new BufferedInputStream(new FileInputStream("a.txt"));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
byte[] buff = new byte[1024];
try {
int res = inputStream.read(buff);
while (res!=-1){
for(int i=0;i<res;i++){
System.out.print(((char) buff[i]));
}
res =inputStream.read(buff);
}
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void NIOmethod(){
try {
FileChannel fileChannel = new FileInputStream("a.txt").getChannel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
int res = fileChannel.read(buffer);
while (res!=-1){
//翻转buffer回到 0位置
buffer.flip();
while (buffer.hasRemaining()){
System.out.print(((char) buffer.get()));
}
buffer.clear();
res = fileChannel.read(buffer);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
buffer的读取实现方式类似于滑动窗口协议的滑动窗口
nio服务器和客户端的阻塞与非阻塞
以下代码来源于java3y
阻塞的写法
客户端
package io;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.SocketChannel;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
public class Client {
public static void main(String[] args) throws IOException {
SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1",8099));
FileChannel fileChannel = FileChannel.open(Paths.get("xx.txt"), StandardOpenOption.READ);
ByteBuffer buffer = ByteBuffer.allocate(1024);
while (fileChannel.read(buffer)!=-1){
buffer.flip();
socketChannel.write(buffer);
buffer.clear();
}
// 显式的告诉服务端数据结束了
socketChannel.shutdownOutput();
socketChannel.close();
fileChannel.close();
}
}
服务端
package io;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
public class Server {
public static void main(String[] args) throws IOException {
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
FileChannel fileChannel = FileChannel.open(Paths.get("b.txt"), StandardOpenOption.WRITE,StandardOpenOption.CREATE);
serverSocketChannel.bind(new InetSocketAddress(8099));
SocketChannel client = serverSocketChannel.accept();
ByteBuffer buffer = ByteBuffer.allocate(1024);
while (client.read(buffer)!=-1 &&client.isConnected()){
buffer.flip();
fileChannel.write(buffer);
buffer.clear();
}
System.out.println("收到!");
}
}
非阻塞的写法(其实是多路复用IO)
客户端
package io;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.SocketChannel;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
public class NoBlockClient {
public static void main(String[] args) throws IOException {
SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 6666));
socketChannel.configureBlocking(false);
FileChannel fileChannel = FileChannel.open(Paths.get("a.txt"), StandardOpenOption.READ);
ByteBuffer buffer = ByteBuffer.allocate(1024);
while (fileChannel.read(buffer) != -1) {
buffer.flip();
socketChannel.write(buffer);
buffer.clear();
}
fileChannel.close();
socketChannel.close();
}
}
服务端
package io;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.Iterator;
public class NoBlockServer {
public static void main(String[] args) throws IOException {
// 1.获取通道
ServerSocketChannel server = ServerSocketChannel.open();
// 2.切换成非阻塞模式
server.configureBlocking(false);
// 3. 绑定连接
server.bind(new InetSocketAddress(6666));
// 4. 获取选择器
Selector selector = Selector.open();
// 4.1将通道注册到选择器上,指定接收“监听通道”事件
server.register(selector, SelectionKey.OP_ACCEPT);
// 5. 轮训地获取选择器上已“就绪”的事件--->只要select()>0,说明已就绪
while (selector.select() > 0) {
// 6. 获取当前选择器所有注册的“选择键”(已就绪的监听事件)
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
// 7. 获取已“就绪”的事件,(不同的事件做不同的事)
while (iterator.hasNext()) {
SelectionKey selectionKey = iterator.next();
// 接收事件就绪
if (selectionKey.isAcceptable()) {
// 8. 获取客户端的链接
SocketChannel client = server.accept();
// 8.1 切换成非阻塞状态
client.configureBlocking(false);
// 8.2 注册到选择器上-->拿到客户端的连接为了读取通道的数据(监听读就绪事件)
client.register(selector, SelectionKey.OP_READ);
} else if (selectionKey.isReadable()) {
// 读事件就绪
SocketChannel client = (SocketChannel) selectionKey.channel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
FileChannel outChannel = FileChannel.open(Paths.get("1.txt"), StandardOpenOption.WRITE, StandardOpenOption.CREATE);
while (client.read(buffer) > 0) {
// 在读之前都要切换成读模式
buffer.flip();
outChannel.write(buffer);
// 读完切换成写模式,能让管道继续读取文件的数据
buffer.clear();
}
}
// (已经处理过的事件,就应该取消掉了)
iterator.remove();
}
}
}
}