java io流 nio

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 包区别就是

  1. io包中是面向流的,数据通过流直接写入或取出

    nio包中视面向缓冲的,数据都是写入缓冲中然后再更新

  2. io流都是单向的,一个input,一个output

    nio是通道channel,可以是双向的

  3. 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();
            }
        }

    }
}
发布了22 篇原创文章 · 获赞 2 · 访问量 881

猜你喜欢

转载自blog.csdn.net/weixin_41685373/article/details/104973527