Java知识之IO

一、Java中的IO的分类

  • 磁盘的操作: File
  • 字节的操作:InputStream 和 OutputStream
  • 字符的操作:Reader 和 Writer
  • 对象的操作:Serializable
  • 网络的操作:Socket,如服务端通过输入流读取客户端发送的请求信息 和 客户端通过输出流向服务端请求信息。
  • 非阻塞IO:NIO

Java中IO流的类结构图:

二、磁盘的操作

File类用于表示文件和目录的信息,但是它不表示文件的内容。

递归地列出一个目录下地所有文件:

// 递归 打印一个目录下面的所有文件   java7开始,可用Paths代替File
    public static void listAllFiles(File dir) {
        if(dir == null || !dir.exists()) return;
        if(dir.isFile()) {
            System.out.println(dir.getName());
            return ;
        }
        for(File file: dir.listFiles()) {
            listAllFiles(file);
        }
    }

 

三、字节的操作

读取数据到字节数组中:public int read(byte b[]) throws IOException.最常用方法

实现文件复制操作

每次读取一个字节,实现文件复制

// 每次读取一个字节
    public static void one_byte(File src, File dist) throws IOException {
        FileInputStream fileInputStream = new FileInputStream(src);
        FileOutputStream fileOutputStream = new FileOutputStream(dist);
        int byte_num = 0;
        // read() 返回的是实际读取的个数
        // 返回 -1 表示读到文件尾部
        while ((byte_num = fileInputStream.read()) != -1) {
            fileOutputStream.write(byte_num);
        }
        fileInputStream.close();
        fileOutputStream.close();
    }

一次性读取多个字节, 实现文件复制

// 一次读写多个字节
    public static void more_byte(File src, File dist) throws IOException {
        FileInputStream fileInputStream = new FileInputStream(src);
        FileOutputStream fileOutputStream = new FileOutputStream(dist);

        // 每次读取 50byte
        byte[] bytes = new byte[50];
        int byte_num = 0;

        // read() 最多读取 bytes.length 个字节
        // 返回的是实际读取的字节数
        // 在这里,每次 读取/写入 50byte
        while ((byte_num = fileInputStream.read(bytes, 0, bytes.length - 1)) != -1) {
            fileOutputStream.write(bytes);
        }

        fileInputStream.close();
        fileOutputStream.close();
    }

实例化具有缓冲功能的字节流对象时,只需要在FileInputStream对象上再套一层BufferedInputStream对象即可。

FileInputStream fileInputStream = new FileInputStream( filepath );

BufferedInputStream buffer = new BufferedInputStream( fileInputStream );

 

四、字符的操作

编码与解码

  编码:将字符转化为字节

  解码:将字节转化为字符

Java中的编码方式:

GBK, 中文字符2字节,英文字符1字节

UTF-8, 中文字符3字节,英文字符1字节

UTF-16eb, 中文字符和英文字符均占2字节

String的编码方式

String 可以看成是一个字符序列,可以指定一个编码方式将它编码为字节序列,亦可指定解码方式,将字节序列解码为String

String str = new String("周五");
        byte[] bytes = str.getBytes("GBK");
        String re = new String(bytes, "GBK");
        System.out.print(re);

字符操作 Reader 和 Writer

  • InputStreamReader 实现从字节流解码解码成字符流
  • OutputStreamWriter 实现从字符流编码成字节流

使用FileInputStream 和 InputStreamReader 读取文件内容

// 读取文件内容
    public static void get_content(File file) throws IOException {
        // 使用 FileInputStream 将文件读取到字节流
        FileInputStream fileInputStream = new FileInputStream(file);
        // 使用 InputStreamReader 从字节流转化为字符流
        InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream, "GBK");

        // 使用缓存 BufferReader
        BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
        String line = null;
        while ((line = bufferedReader.readLine()) != null) {
            System.out.println(line);
        }
        // 装饰者模式是的BufferedReader 组合了一个 Reader 对象
        // 在调用 BufferedRead 的close() 方法时会去调用 Reader 的 close() 方法
        bufferedReader.close();
        inputStreamReader.close();
    }

 

五、对象的序列化和反序列化

序列化:所谓序列化,就是将一个对象转化成字节序列,用于存储和运输。      注意,不会对静态变量进行序列化,静态变量属于类的状态。

反序列化:反序列化,自然就是将字节序列转化为原对象

序列化: ObjectOutputStream.writeObject()

反序列化:ObjectInputStream.readObject()

序列化类的实现,Serializable接口

Serializable接口只是一个标准,没有任何方法需要实现。

序列化和反序列化的使用示例

private static A a = new A(666, "Hello World!");
private static String objectFile = "obj.txt";

// 对象的序列化 Serializable
public static void ojb_to_byte() throws IOException{
    FileOutputStream fileOutputStream = new FileOutputStream(objectFile);
    ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
    objectOutputStream.writeObject(a);
    // 将对象转化为字节序 写入文件
    objectOutputStream.close();
}

// 对象反序列化
public static void byte_to_obj() throws IOException, ClassNotFoundException {
    FileInputStream fileInputStream = new FileInputStream(objectFile);
    ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
    // 使用ObjectInputStream 从文件读取字节序,再转化为对象
    A a2 = (A) objectInputStream.readObject();
    objectInputStream.close();
    System.out.println(a2.str);
}

// 一个类
private static class A implements Serializable {
    private int x;
    private String str;
    A(int x, String str) {
        this.x = x;
        this.str = str;
    }
}

补充:transient 关键字, transient可以使一些属性不被序列化。

 

六、通道和缓冲

1. 通道

通道Channel是对原IO包中的流的模拟,可以通过它读取和写入数据。

通道与流:流只能在一个方向上移动,而通道是双向的,可以读、写或者同时用于读写

通道的类型:

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

2. 缓冲区

发送给一个通道的所有数据均要首先放到缓冲区,同样,从通道中读取任何数据也要先读到缓冲区中。

不会直接对通道进行读写数据,而是先经过缓冲区。

Java中7类缓冲区:

ByteBuffer、ShortBuffer、IntBuffer、CharBuffer、FloatBuffer、DoubleBuffer、LongBuffer

缓冲区实质上是一个数组

缓冲区的状态变量

  • capacity : 最大容量
  • position:  当前已经读写的字节数
  • limit:  还可以读写的字节数

 

// 1. 创建容量大小为 10 的ByteBuffer

ByteBuffer bf = ByteBuffer.allocate(10);

此时position = 0, limit = capacity = 10。

// 2. 往缓冲区中 put() 5个字节

bf.put((byte)'H').put((byte)'e').put((byte)'l').put((byte)'l').put((byte)'o');

此时,position = 5, limit = capacity = 10;

// 3.调用 flip() 方法,切换为读就绪状态

bf.flip();

在缓冲区的数据写到通道之前,需要调用flip() 方法,该方法将limit 设置为当前position,并将position设置为0

// 4. 从缓冲区中读取两个元素

从缓冲区中读取2个字节到输出缓冲中,此时position = 2

// 5. 最后调用clear() 方法清空缓冲区,此时 position 和 limit 都被设置为初始值。

 

七、非阻塞IO

NIO也叫做非阻塞IO,NIO在网络通信中的非阻塞特性应用广泛。

IO与NIO最重要的区别是数据打包和传送的方式,IO以流的方式处理数据,而NIO是以块的形式处理数据。

面向流的IO一次处理一个字节数据,而面向块的NIO一次处理一个数据块。

1. 阻塞IO通信模型

阻塞I/O在调用InputStream.read()方法时是阻塞的,它会一直等到数据到来时(或超时)才会返回;同样,在调用ServerSocket.accept()方法时,也会一直阻塞到有客户端连接才会返回,每个客户端连接过来后,服务端都会启动一个线程去处理该客户端的请求。阻塞I/O的通信模型示意图如下:

阻塞IO的缺点:

  • 客户端过多时,会创建大量处理线程。占用栈空间和CPU
  • 阻塞可能带来频繁上下文切换,且大部分上下文切换可能是无意义的。

2. Java NIO原理及通信模型

  • 一个专门的线程处理所有的IO事件,并负责并发。
  • 事件驱动机制:事件到的时候触发,而不是同步去监视事件。
  • 线程通讯:线程之间通过 wait, notify 等方式通讯。保证上下文切换都是有意义的,减少无意义切换。

工作原理图:

进行通信时,客户端和服务端各自维护一个管道的对象,即选择器Selector,而Selector通过轮询的方式去监听多个通道Channel上的事件,从而让一个线程处理多个事件。

通过配置监听的通道Channel为非阻塞,当Channel上的IO事件还未到达时,就不会进入阻塞状态,而是继续轮询其他Channel,找到IO事件已经到达的Channel执行。

只有Channel才能配置非阻塞,而FileChannel不能。

 

参考博客:

https://blog.csdn.net/shimiso/article/details/24990499

https://www.cnblogs.com/chenpi/p/6475510.html

https://www.cnblogs.com/ylspace/p/8128112.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值