Java中IO相关整理

本文详细介绍了Java IO中的字节流、字符流及其子类,包括FileInputStream、FileOutputStream、BufferedInputStream和BufferedOutputStream等。同时,探讨了节点流与处理流的区别,如FileReader、FileWriter、BufferedReader和BufferedWriter。此外,文章还讨论了序列化的重要性及transient关键字。接着,转向NEW IO,重点讲解Buffer、Channel和Selector,强调了NIO的非阻塞特性和面向缓冲区的操作方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、Java IO

1.1字节流、字符流

  • 其中,字节流也有两个抽象类:InputStream OutputStream
    其对应子类有FileInputStream和FileOutputStream实现文件读写
    BufferedInputStream和BufferedOutputStream提供缓冲区功能
  • 同样,字符流有两个抽象类:Writer Reader 其对应子类FileWriter和FileReader可实现文件的读写操作 BufferedWriter和BufferedReader能够提供缓冲区功能,用以提高效率

FileInputStream Demo
其中新建了一个byte数组用来保存每次读取出来的数据,而c用来保存read方法的返回值,返回值为放入byte数组中字节的个数。

    public static void main(String[] args) throws IOException{
        File file = new File("./src/resourses/file.txt");
        System.out.println(file.exists());

        FileInputStream in = new FileInputStream("./src/resourses/file.txt");
        int c;
        byte[] b = new byte[1024];

        while ((c = in.read(b,0,b.length))!=-1){   // 将流中的数据读入放在byte数组的第off个位置先后的len个位置中
                                                       // 返回值为放入字节的个数。
            System.out.println(new String(b,0,c));
        }
        in.close();
    }

FileOutputStream Demo

    public static void main(String[] args) {
        try{
            FileInputStream is = new FileInputStream("./src/resourses/file.txt");
            FileOutputStream os = new FileOutputStream("./src/resourses/write.txt");

            byte[] b = new byte[1024];
            int hasRead;
            while ((hasRead = is.read(b,0,b.length))!=-1){
                os.write(b, 0, hasRead);
            }
        }catch (IOException e){
            e.printStackTrace();
        }
    }

reader Demo

    public static void main(String[] args) {
        try{
            FileReader fr = new FileReader("./src/resourses/file.txt");
            // 竹筒大小
            char[] c = new char[32];
            // 用来保存实际读取的字符数
            int hasRead = 0;
            //
            while ((hasRead = fr.read(c,0,c.length))!=-1){
                System.out.println(new String(c, 0, hasRead));
            }

        }catch (IOException e){
            e.printStackTrace();
        }
    }

writer Demo

    public static void main(String[] args) {
        try{
            FileReader fr = new FileReader("./src/resourses/file.txt");
            FileWriter fw = new FileWriter("./src/resourses/write.txt");

            char[] c = new char[32];
            int hasRead;

            while ((hasRead = fr.read(c,0,c.length))!=-1){
                fw.write(c,0,hasRead);
                fw.flush();
            }
            fw.close();
        }catch(IOException e){
            e.printStackTrace();
        }
    }

1.2 节点流 处理流

  • 节点流:可以从或向一个特定的地方(节点)读写数据。如FileReader

  • 处理流:是对一个已存在的流的连接和封装,通过所封装的流的功能调用实现数据读写。如BufferedReader。处理流的构造方法总是要带一个其他的流对象做参数。一个流对象经过其他流的多次包装,称为流的链接。

常用的节点流

  父 类 InputStream OutputStream Reader Writer

  文 件 *FileInputStream FileOutputStrean FileReader FileWriter 文件进行处理的节点流

  数 组 *ByteArrayInputStream ByteArrayOutputStream CharArrayReader CharArrayWriter 对数组进行处理的节点流(对应的不再是文件,而是内存中的一个数组)

  字符串 *无  无 StringReader StringWriter 对字符串进行处理的节点流

  管 道 *PipedInputStream PipedOutputStream PipedReader PipedWriter 对管道进行处理的节点流


  常用处理流(关闭处理流使用关闭里面的节点流)

  父 类 InputStream OutputStream Reader Writer

  缓冲流 *BufferedImputStrean BufferedOutputStream BufferedReader BufferedWriter —-需要父类作为参数构造,增加缓冲功能,避免频繁读写硬盘,可以初始化缓冲数据的大小,由于带了缓冲功能,所以就写数据的时候需要使用flush方法咯

  转换流 *InputStreamReader OutputStreamWriter- 要inputStream或OutputStream作为参数,实现从字节流到字符流的转换

  数据流 *DataInputStream DataOutputStream -提供将基础数据类型写入到文件中,或者读取出来,为什么要有这个流呢?看这样的分析,如果没有这种流的话,有一个long,本身只占8个字节,如果我要写入到文件,需要转成字符串,然后在转成字符数组,那空间会占用很多,但是有了这种流之后就很方便了,直接将这8个字节写到文件就完了。。是不是既节约了内存空间有让程序写起来更加方便简单了呐。写倒是很简单,但是读取的时候就注意了,根据读取的数据类型,指针会往下移,所以你写的顺序必须要和读的顺序一致才能完成你正确的需求。

  *ObjectInputStream ObjectOutputStream

1.3 序列化

  • transient关键字–修饰成员变量,不写入该成员变量

  • Serializable接口–表示接口,怎样实现的呢,在序列化之前,首先判断 (对象 instanceof Serializable)如果返回true则执行序列化,否者抛出异常,并且里面有一个ID,是用来快速查找某个对象的时候使用的

  • Externalizable接口–外部化接口;他是Serializable接口的子接口,能手动控制序列化的方式

定义是个Person类,实现了Serializable接口

public class Person implements Serializable{

    private String name;
    private int age;

    public Person(String name, int age){
        this.name = name;
        this.age = age;
    }
}

将这个对象序列化

    public static void main(String[] args) {
        try{
            //序列化 写入
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("./src/resourses/obj.txt"));
            Person p1 = new Person("sunwukong", 500);
            oos.writeObject(p1);
            //读取 
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream("./src/resourses/obj.txt"));
            Person p2 = (Person)ois.readObject();
            System.out.println(p2.getName()+p2.getAge());
        }catch(IOException e){

        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

二、NEW IO

最主要的三个类是Buffer,Channel和Selector。与传统的IO比较主要有亮点不同。

  • IO是面向流的,而NIO是面向缓冲区的。传统的IO面向流意味着每次从流中读取一个或者多个字节,这些数据没有缓冲在任何一个地方。如果要前后移动流中的数据,要先将其还存在一个缓冲区中。而面向缓冲区的NIO有先天的前后操纵数据的优势。
  • IO是阻塞的,即是,当调用read()或者write()方法时,当没有数据读出或者没有数据写入时,线程将是阻塞的;不同的是NIO是非阻塞的,当一个线程发送读的请求时,其只能读取当前可返回的数据,如果当前没有可用的数据,就什么都不会返回。写请求时,不用等到所写的数据要全部写入时,线程就可以去做其他的事情。线程通常将非阻塞的IO用于在其他的通道上进行读写操作,所以一个IO可以管理多个通道。

Buffer

每种类型都有对应的Buffer,但是常用的Buffer为ByteBufferCharBufferBuffer具体的实例由XXXBuffer.allocate(int capacity)来获取。ByteBuffer还有一个子类MappedByteBuffer,这个子类通常通过Channelmap()方法返回,该缓冲区将磁盘的全部或部分内容映射到内存(虚拟内存)。读写性能较高。

其有几个比较重要的标志,capacity,limit和position。其中capacity是指缓冲区的容量,由allocate初始化的时候指定。limit是指读取和写入时的上界限,position是指读取和写入时下一个位置。读取之前,调用flip()使position回到0 (读取的下一位置),limit回到定位到最后一个元素的下一位置 (读取的上界限),capacity不变。写入时,首先调用clear()方法,此时缓冲区中的元素并没有真正被clear,使用buffer.get(int pos)仍然能获取到缓冲区的内容,只是将position定位到0,将limit定位到capacity的位置,写入时,将覆盖原缓冲区的内容。

Channel

一般需要用流的getChannel()方法来初始化Channel,例如new FileInputStream(f).getChannel()new FileOutputStream(f).getChannel()new RandomAccessFile(f,"rw").getChannel()。读取文件到Buffer里可以用channelread()方法或channelmap(***)方法,写入则使用channelwrite()方法。

使用read()的具体形式为

...;
File f = new File("afile.txt");
FileChannel channel = new FileInputStream(f).getChannel();
ByteBuffer buffer = ByteBuffer.allocate(f.length());
channel.read(buffer);
...;

使用map()的具体形式为

...;
File f = new File("afile.txt");
FileChannel channel = new FileInputStream(f).getChannel();
MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_ONLY, 0, f.length());
...;

Selector

selector使用单线程处理多个channel,当应用打开了多个channel且每个channel的吞吐量不是很大时,使用channel就比较方便。使用selector分如下的几步:

  • 创建:使用Selector selector = Selector.open()
  • 将channel注册到selector:channel.register(selector, **),该方法可返回一个SelectorKey,代表该channel在selector中的”代号”。与selector一起使用时,channel一定处于非阻塞模式下。所以需要提前设置channel.configureBlocking(false)
  • 使用selector几个select的重载方法测试通道有多少已经准备好了:
    • int select()阻塞到至少有一个对象在通道上准备好了。
    • int select(long timeout)最长会阻塞timeout毫秒
    • int selectNow()不会阻塞,不管什么情况会直接返回,如果自上次调用选择操作以来,没有就绪的通道则直接返回0。
  • 调用select方法之后,如果有返回值,则说明已经有selector就绪了。此时可以通过selectedKey来选择已选择键集中就绪的通道。使用Set<SelectionKey> selectedKey = selector.selectedKeys();,之后在得到的SelectionKey的Set中可以通过SelectionKey提供的方法来操作channel。得到Channel的基本方法为selectionKey.channel()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值