JAVA IO

一、简介

Java对数据的操作是通过流的方式,通过IO流来处理设备之间的数据传输,上传和下载

二、说明

Java 的 IO 操作有以下几类

  • 字节IO:InputStream 和 OutputStream
  • 字符IO:Reader 和 Writer
  • 对象IO:Serializable
  • 磁盘IO:File
  • 网络IO:Socket
  • 非阻塞IO:NIO

三、使用

字节IO

采用装饰者模式实现

  • InputStream:字节输入流,抽象类
  • OutputStream:字节输出流,抽象类
  • FileInputStream:文件输入流
  • BufferedInputStream:字节缓冲输入流

示例

public class ByteIO {
    public static void main(String[] args) throws IOException {
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(Paths.get("E:", "test.txt").toString()));
        byte[] bs = new byte[1024];
        int len = 0;
        while ((len = bis.read(bs)) != -1) {
            System.out.print(new String(bs, 0, len));
        }
        bis.close();
    }
}

字符IO

编码是把字符转换为字节,解码是把字节重新组合成字符。
不管是磁盘还是网络传输,最小的存储单元都是字节,而不是字符。
但是在程序中操作的通常是字符形式的数据,因此需要提供对字符进行操作的方法。

  • Reader:读取字符流
  • Writer:写入字符流
  • InputStreamReader:实现从字节流解码成字符流;
  • BufferedReader:字符缓冲流

示例

public class CharIO {
    public static void main(String[] args) throws IOException {
        BufferedReader bufferedReader = new BufferedReader(new FileReader(Paths.get("E:", "test.txt").toString()));
        String line;
        while (true) {
            if (!((line = bufferedReader.readLine()) != null)) break;
            System.out.println(line);
        }
        bufferedReader.close();
    }
}

对象IO

  • 序列化是将一个对象转换成字节序列,方便存储和传输
  • 它不会对静态变量进行序列化,因为序列化只是保存对象的状态,静态变量属于类的状态。
  • transient 关键字可以使一些属性不会被序列化
  • ObjectInputStream:对象输入流
  • ObjectOutputStream:对象输出流

示例

public class ObjectIO {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        Person person1 = new Person("赵士杰", "男", 23);
        Path path = Paths.get("D:", "test.txt");
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(path.toFile()));
        objectOutputStream.writeObject(person1);
        ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(path.toFile()));
        System.out.println(objectInputStream.readObject());
        objectOutputStream.close();
        objectInputStream.close();
    }

    private static class Person implements Serializable {
        private String name;
        private String gender;
        private transient Integer age;
        
        Person(String name, String gender, Integer age) {
            this.name = name;
            this.gender = gender;
            this.age = age;
        }

        @Override
        public String toString() {
            return "name = " + name + " gender = " + gender + " age = " + age;
        }
    }
}

磁盘IO

  • File:用来表示文件和目录的信息,不表示文件的内容
  • Path:用来取代File,可以File相互转换
  • Paths:提供创建Path的功能
  • Files:提供大量处理文件的功能,如复制、读取、写入等

示例

public class FileIO {
    public static void main(String[] args) {
        Path source = Paths.get("D:", "test.txt");
        Path target = Paths.get("E:", "test.txt");
        System.out.println(copyFile(source, target));
    }

    /**
     * 复制文件
     */
    public static Path copyFile(Path source, Path target) {
        try {
            System.out.println("source路径:" + source);
            //复制文件
            return Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return source;
    }

    /**
     * 列出path下所有文件路径
     */
    public static void listAllFiles(Path path) {
        if (Files.notExists(path)) {
            return;
        }
        if (Files.isDirectory(path)) {
            try {
                Files.list(path).forEach(MyFile::listAllFiles);
            } catch (IOException e) {
                e.printStackTrace();
            }
        } else {
            System.out.println(path);
            System.out.println(path.getFileName() + " isReadable: " + Files.isReadable(path)
                    + " isWritable: " + Files.isWritable(path)
                    + " isExecutable: " + Files.isExecutable(path)
            );
        }
    }
}

网络IO

  • InetAddress:网络上的硬件资源,即 IP 地址
  • URL:统一资源定位符
  • Sockets:使用 TCP 协议实现网络通信
  • Datagram:使用 UDP 协议实现网络通信

示例

public class NetworkIO {
    public static void main(String[] args) throws IOException {
        URL url = new URL("http://www.baidu.com");
        InputStream is = url.openStream();
        InputStreamReader isr = new InputStreamReader(is, "utf-8");
        BufferedReader br = new BufferedReader(isr);
        String line;
        while ((line = br.readLine()) != null) {
            System.out.println(line);
        }
        br.close();
    }
}

非阻塞IO

工作原理

  • 1、获取用于连接IO设备的通道Channel以及用于容纳数据的缓冲区Buffer
  • 2、通过选择器Selector监控多个Channel的IO状况
  • 3、操作缓冲区来对数据进行处理

相比于传统IO

  • NIO

    • 面向缓冲区
    • 双向
    • 非阻塞
    • 多路复用
    • 支持字符集编解码、锁
  • IO

    • 面向流
    • 单向
    • 阻塞

通道

Channel 是对流的模拟,负责管道的连接和数据的传输,可通过它读取和写入数据。
流只能在一个方向上移动,而通道是双向的,可以用于读、写或者同时用于读写。

  • FileChannel:从文件中读写数据
  • DatagramChannel:通过 UDP 读写网络中数据
  • SocketChannel:通过 TCP 读写网络中数据,用于客户端
  • ServerSocketChannel:可以监听新进来的 TCP 连接,对每一个新进来的连接都会创建一个 SocketChannel,用于服务端

缓冲区

向 Channel 中写数据或从 Channel 中读数据都会先经过 Buffer,
缓冲区提供了对数据的结构化访问,而且可以跟踪系统的读/写进程。

  • capacity:最大容量
  • position:当前已经读写的字节数
  • limit:还可读写的字节数
  • mark:记录当前position的位置,可重置到mark
  • put():写数据到 Buffer
  • flip():切换到读模式
  • get():读数据到 Buffer
  • allocate(int capacity):非直接缓冲区,在JVM中分配
  • allocateDirect(int capacity):非直接缓冲区,在物理内存分配

选择器

Selector 通过轮询的方式去监听多个通道 Channel 上的事件,从而让一个线程可以处理多个事件

  • 工作流程

    • 1、创建选择器
    • 2、将通道注册到选择器
    • 3、监听事件
    • 4、获取到达的事件
    • 5、循环获取
  • 监听事件类型

    • 读取操作:SelectionKey.OP_READ,数值为1
    • 写入操作:SelectionKey.OP_WRITE,数值为4
    • socket连接操作:SelectionKey.OP_CONNECT,数值为8
    • socket接受操作:SelectionKey.OP_ACCEPT,数值为16

非阻塞示例

//服务端
public class NIOServer {
    public static void main(String[] args) throws IOException {
        Selector selector = Selector.open();
        ServerSocketChannel ssChannel = ServerSocketChannel.open();
        ssChannel.configureBlocking(false);
        ssChannel.register(selector, SelectionKey.OP_ACCEPT);

        ServerSocket serverSocket = ssChannel.socket();
        InetSocketAddress address = new InetSocketAddress("127.0.0.1", 8888);
        serverSocket.bind(address);

        while (true) {
            selector.select();
            Set<SelectionKey> keys = selector.selectedKeys();
            Iterator<SelectionKey> keyIterator = keys.iterator();

            while (keyIterator.hasNext()) {
                SelectionKey key = keyIterator.next();
                if (key.isAcceptable()) {
                    ServerSocketChannel ssChannel1 = (ServerSocketChannel) key.channel();
                    //服务器会为每个新连接创建一个 SocketChannel
                    SocketChannel sChannel = ssChannel1.accept();
                    sChannel.configureBlocking(false);
                    //从客户端读取数据
                    sChannel.register(selector, SelectionKey.OP_READ);
                } else if (key.isReadable()) {
                    SocketChannel sChannel = (SocketChannel) key.channel();
                    System.out.println(readDataFromSocketChannel(sChannel));
                    sChannel.close();
                }
                keyIterator.remove();
            }
        }
    }

    private static String readDataFromSocketChannel(SocketChannel sChannel) throws IOException {
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        StringBuilder data = new StringBuilder();

        while (true) {
            buffer.clear();
            int n = sChannel.read(buffer);
            if (n == -1) {
                break;
            }
            buffer.flip();
            int limit = buffer.limit();
            char[] dst = new char[limit];
            for (int i = 0; i < limit; i++) {
                dst[i] = (char) buffer.get(i);
            }
            data.append(dst);
            buffer.clear();
        }
        return data.toString();
    }
}
//客户端
public class NIOClient {
    public static void main(String[] args) throws IOException {
        Socket socket = new Socket("127.0.0.1", 8888);
        OutputStream out = socket.getOutputStream();
        String s = "hello world";
        out.write(s.getBytes());
        out.close();
    }
}

四、连接

jenkov的JAVA IO教程

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值