【Java常见问题】基础知识(四)流

117 阅读4分钟

在这篇文章中,不仅会讲到Java流概念本身,还会涉及与之相关的工作机制。

Java 流基本问题

分类

流分为两种:字节流和字符流。

区别

字节流在处理输入输出时不用到缓存,字符流会。

设计理念

Decorator设计模式(指在不改变现有对象结构的情况下,动态地给该对象增加一些职责(即增加其额外功能)的模式)

Java序列化

Java有两种对象持久化的方式,序列化和外部序列化。

序列化含义

将对象转换为一连串字节流

为什么要序列化

进行远程通信时,无论何种数据,都是以二进制序列的形式在网络中传输的。对象等数据类型并不能直接被识别,因此需要转化。

序列化注意事项

  • 能被序列化的类的子类也能被序列化
  • 有两种声明类型的对象无法被序列化:static和transient

什么时候应该使用序列化?

  • 网络传输数据
  • 对象持久化到数据库或文件
  • 为了实现深拷贝

Java实现序列化的具体步骤?

  • implements Serializable接口
  • 使用一个输出流来构造一个对象流对象
  • 该对象流的writeObject方法可用于保存目标对象状态(写出目标对象)
  • 要恢复目标对象可使用第二步对应的输入流

Java外部序列化

自行实现一个extends Serializable的外部序列化接口,且其中的读写方法也要自己写。

如何只序列化部分属性呢?

对于不想序列化的属性,可以用transient修饰。

并发与并行的区别是什么?

  • 并发:concurrency,宏观上多个进程在同时执行的现象,此时微观上指令实际上是快速交替进行的
  • 并行,parallel,由于多核机制,在微观上指令也是并行执行的

了解同步、异步、阻塞、非阻塞的概念和区别及联系吗?

多线程IO
同步一个执行块同一时间只有一个线程可访问发起IO操作并调用结束后,获取返回值后才执行下一步操作
异步一个执行块同一时间多个线程可访问发起IO操作后,结果由发起者轮询或执行者发起回调
阻塞线程挂起发起者发起IO操作后期间不能再处理其他业务
非阻塞线程无挂起发起者无需等待IO操作完成

几大IO模型

BIO(block,最经典)(方式:同步阻塞)

一个连接一个线程

缺点:允许创建的最大线程数可能远小于要处理的连接数

NIO(nonblock)(方式:异步阻塞)

多个连接一个线程,一个请求一个线程

通过Selector、Channels、Buffers实现。

后两个在学习操作系统和Go时都已经比较熟悉了,接下来讲讲Selector。

Selector直译为选择器,在这里的作用是轮询检查多个channel的状态,广义地讲就是判断通道是否可读或可写。

以下是关于Selector的示例代码:

服务端

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

public class Server {
    public static void main(String[] args) {
        Selector selector=null;
        try{
            ServerSocketChannel ssc=ServerSocketChannel.open();
            ssc.socket().bind(new InetSocketAddress(9000));
            //NIO
            ssc.configureBlocking(false);
            selector=Selector.open();
            //register channel
            ssc.register(selector, SelectionKey.OP_ACCEPT);
            
            //read buffer && write buffer
            ByteBuffer rb=ByteBuffer.allocate(1024);
            ByteBuffer wb=ByteBuffer.allocate(1024);
            
            rb.put("hello client".getBytes());
            wb.flip();
            
            while(true){
                int ready=selector.select();
                if(ready==0) continue;

                Set<SelectionKey> keys=selector.selectedKeys();
                Iterator<SelectionKey> it=keys.iterator();
                
                //iterator ready channel
                while(it.hasNext()){
                    SelectionKey key=it.next();
                    if(key.isAcceptable()){
                        SocketChannel sc= ssc.accept();
                        sc.configureBlocking(false);
                        //register new connection to channel,read only
                        sc.register(selector,SelectionKey.OP_READ);
                        
                    }else if(key.isReadable()){
                        SocketChannel sc=(SocketChannel)key.channel();
                        rb.clear();
                        sc.read(rb);
                        
                        rb.flip();
                        System.out.println("Server receive:"+new String(rb.array()));
                        
                        //mark as write,to send message to client
                        key.interestOps(SelectionKey.OP_WRITE);
                    }else if(key.isWritable()){
                        wb.rewind();
                        SocketChannel sc=(SocketChannel)key.channel();
                        sc.write(wb);
                        //mark as read,to receive message from client
                        key.interestOps(SelectionKey.OP_READ);
                    }
                    it.remove();
                }
                
            } 
        }catch (IOException e){
            e.printStackTrace();
        }finally {
            if(selector!=null){
                try{
                    selector.close();
                }catch (IOException e){
                    
                }
            }
        }
    }
}

client


package pra;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;

public class client {
    public static void main(String[] args) {
        SocketChannel sc=null;
        try{
            sc=SocketChannel.open();
            sc.connect(new InetSocketAddress(9000));
            ByteBuffer wb=ByteBuffer.allocate(1024);
            ByteBuffer rb=ByteBuffer.allocate(1024);

            wb.put("Hello server".getBytes());
            wb.flip();

            while (true){
                wb.rewind();
                sc.write(wb);
                rb.clear();
                sc.read(rb);
                System.out.println("client receive:"+new String(rb.array()));
            }
        }catch (IOException e){
            e.printStackTrace();
        }
        finally {
            if(sc!=null){
                try{
                    sc.close();
                }catch (IOException e){

                }
            }
        }
    }
}

AIO(Asynchronous I/O)

AIO基于Proactor模型实现。

首先讲讲什么是Proactor。

在进行IO读写相关的操作时,为了将请求与操作分离,会用到“事件分离器”,Reactor(响应器)和Proactor(直译为前摄器,可理解为主动)

  • Reactor是非阻塞同步模式,感知就绪可读写事件;
  • Proactor是异步模式,感知已完成的读写事件。

类比取快递。