谈一谈--Java的IO类及其模型

JAVA IO模型 JAVA IO模型常见的IO模型有:阻塞IO模型、非阻塞IO模型、多路复用IO模型、信号驱动IO模型、异步IO模型。 阅读详情

一、简述

对Java中I/O相关的基础知识和I/O模型进行系统化梳理总结,以便更有效的学习应用 。读者可以按三个层次来学习理解:首先是对基础io包中的类按模块进行学习理解 ,如有磁盘操作、字节操作、字符操作、对象操作、网络操作等,然后更进一步的了解不同工具类的方法和使用;其次对nio包的类安模块进行学习理解,如有通道、缓冲、选择器、选择键(包含关联关系)等;之后再通过了解操作系统层面的一些特性,进一步理解阻塞IO和非阻塞IO的内涵,学习理解在工程服务中是怎样用不同的模式构建通信处理模块及其相应优劣势。

希望我的知识总结梳理能给您的阅读带来收获 。

PS:由于内容较多,部分内容待更新补充。。。。

二、思维导图

三、知识要点

1、理解FilterInputStream

FilterInputStream 属于抽象装饰者,装饰者用于装饰组件,为组件提供额外的功能。例如 BufferedInputStream 为 FileInputStream 提供缓存的功能。实例化一个具有缓存功能的字节流对象时,只要在FileInputStream对象上再套一层BufferedInputStream即可使用 。

 public static void copyFile(String src, String dist) throws IOException {
        // 直接从磁盘读
        FileInputStream in = new FileInputStream(src);
        FileOutputStream out = new FileOutputStream(dist);
        // 从内存读  缓存大小 8192
        BufferedInputStream buffered= new BufferedInputStream(in);
        byte[] buffer = new byte[ 1024];
        int cnt;

        // read() 最多读取 buffer.length 个字节
        // 返回的是实际读取的个数
        // 返回 -1 的时候表示读到 eof,即文件尾
        while ((cnt = buffered.read(buffer)) != -1) {
            out.write(buffer, 0, cnt);
        }
         // 装饰着会去调用inputStream的close()方法
        buffered.close();
        out.close();
    }

2、编码和解码

由于存在各个国家地区对字符编码标准的不统一,因此在编码和解码所用的编码方式不是一致的情况下,会出现乱码

/**
GBK 编码中,中文字符占 2 个字节,英文字符占 1 个字节;
UTF-8 编码中,中文字符占 3 个字节,英文字符占 1 个字节;
UTF-16be 编码中,中文字符和英文字符都占 2 个字节, be 指的是 Big Endian, 大端 
UTF-16le,le 指的是 Little Endian ,小端 
Java 的内存编码使用双字节编码 UTF-16be,这不是指 Java 只支持这一种编码方式,而是说 char 这种类型使用 UTF-16be 进行编码。char 类型占 16 位,也就是两个字节,Java 使用这种双字节编码是为了让一个中文或者一个英文都能使用一个 char 来存储。
**/

/**String 的编码方式  一般为 UTF-8 **/
String str1 = "文本内容";
byte[] bytes = str1.getBytes("UTF-8");
String str2 = new String(bytes, "UTF-8");
System.out.println(str2);

3、字符流与字节流的转换

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

InputStreamReader 实现从字节流解码成字符流;

OutputStreamWriter 实现字符流编码成为字节流。

  public static void readFileContent(String src) throws IOException {

        BufferedReader reader = new BufferedReader(new FileReader(src));

        String line ;
        while ((line = reader.readLine()) != null) {
            System.out.println(  line);
        }
       
        reader.close();
    }

4、transient关键字

在序列化与反序列化场景中,transient 关键字可以使一些属性不会被序列化。

一个静态变量不管是否被transient修饰,均不能被序列化(如果反序列化后类中static变量还有值,则值为当前JVM中对应static变量的值)。序列化保存的是对象状态,静态变量保存的是类状态,因此序列化并不保存静态变量。

 public static void main(String[] args) {
        User user = new User() ;
        user.setVersion("1.1");
        user.setUsername("zhangsan_ sxx");
        user.setCode("fsjjkjsfs");
        user.setGender("male");
        serializeToFile(FileConfig.FILE_SERIALIZE_OBJECT_FILE,user);

        User user1 = deserializeFromFile(FileConfig.FILE_SERIALIZE_OBJECT_FILE);
        System.out.println("version = " + user1.getVersion());
        System.out.println("userName = " + user1.getUsername());
        System.out.println("code = " + user1.getCode());
        System.out.println("gender = " + user1.getGender());

        user.setVersion("2.3");
        User user2 = deserializeFromFile(FileConfig.FILE_SERIALIZE_OBJECT_FILE);
        System.out.println("version = " + user2.getVersion());
        System.out.println("userName = " + user2.getUsername());
        System.out.println("code = " + user2.getCode());
        System.out.println("gender = " + user2.getGender());

    }

    public static void serializeToFile(String fileName,User user) {
        try{
            ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream(fileName));
            os.writeObject(user);
            os.flush();
            os.close();
        } catch (FileNotFoundException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public static <T> T deserializeFromFile(String fileName ) {
        try{
            ObjectInputStream in = new ObjectInputStream(new FileInputStream(fileName));
            T t = (T)in.readObject();
            in.close();
            return t ;
        } catch (FileNotFoundException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
    }

5、流与块的理解

I/O 与 NIO 最重要的区别是数据打包和传输的方式,I/O 以流的方式处理数据,而 NIO 以块的方式处理数据。

面向流的 I/O 一次处理一个字节数据:一个输入流产生一个字节数据,一个输出流消费一个字节数据。为流式数据创建过滤器非常容易,链接几个过滤器,以便每个过滤器只负责复杂处理机制的一部分。不利的一面是,面向流的 I/O 通常相当慢。

面向块的 I/O 一次处理一个数据块,按块处理数据比按流处理数据要快得多。但是面向块的 I/O 缺少一些面向流的 I/O 所具有的优雅性和简单性。

I/O 包和 NIO 已经很好地集成了,java.io.* 已经以 NIO 为基础重新实现了,所以现在它可以利用 NIO 的一些特性。例如,java.io.* 包中的一些类包含以块的形式读写数据的方法,这使得即使在面向流的系统中,处理速度也会更快。

6、缓冲区的读写操作

class Buffer {

  // Invariants: mark <= position <= limit <= capacity
    private int mark = -1;
    private int position = 0; // 当前已经读写的字节数
    private int limit; // 还可以读写的字节数
    private int capacity; // 容量 

    public Buffer reset();
    public Buffer clear(); 
    public Buffer flip() ;// 翻转  读写模式翻转 
    public abstract boolean isDirect(); 
    public abstract boolean isReadOnly();
    public final boolean hasRemaining() {
        return position < limit;
    }
}

    public static void main(String[] args) {
        // 演示复制资源文件
        try {
            fileCopy(FileConfig.FILE_RESOURCE_SRC_FILE,FileConfig.FILE_RESOURCE_DEST_FILE);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static void fileCopy(String src, String dist) throws IOException {
        /* 获得源文件的输入字节流 */
        FileInputStream fin = new FileInputStream(src);

        /* 获取输入字节流的文件通道 */
        FileChannel fcin = fin.getChannel();

        /* 获取目标文件的输出字节流 */
        FileOutputStream fout = new FileOutputStream(dist);

        /* 获取输出字节流的文件通道 */
        FileChannel fcout = fout.getChannel();

        /* 为缓冲区分配 1024 个字节 */
        ByteBuffer buffer = ByteBuffer.allocateDirect(1024);

        while (true) {

            /* 从输入通道中读取数据到缓冲区中 */
            int r = fcin.read(buffer);

            /* read() 返回 -1 表示 EOF */
            if (r == -1) {
                break;
            }

            /* 切换读写 */
            buffer.flip();

            /* 把缓冲区的内容写入输出文件中 */
            fcout.write(buffer);

            /* 清空缓冲区 */
            buffer.clear();
        }
    }

7、内存映射文件

内存映射文件 I/O 是一种读和写文件数据的方法,它可以比常规的基于流或者基于通道的 I/O 快得多。

向内存映射文件写入可能是危险的,只是改变数组的单个元素这样的简单操作,就可能会直接修改磁盘上的文件。修改数据与将数据保存到磁盘是没有分开的。

下面代码行将文件的前 1024 个字节映射到内存中,map() 方法返回一个 MappedByteBuffer,它是 ByteBuffer 的子类。因此,可以像使用其他任何 ByteBuffer 一样使用新映射的缓冲区,操作系统会在需要时负责执行映射。

MappedByteBuffer mbb = fc.map(FileChannel.MapMode.READ_WRITE, 0, 1024);

8、网络编程

四、应用场景

  1. 文件读写

FileInputStream \ FileOutputStream ; FileReader \ FileWriter

2、获取网络地址对应资源

public static void main(String[] args) {
        try {
            getUrl("https://leetcode.cn/u/wang-chao-xk/");
        }catch (IOException e ){
            e.printStackTrace();
        }
    }

    private static void getUrl(String urls) throws IOException  {
        URL url = new URL(urls);

        /* 字节流 */
        InputStream is = url.openStream();

        /* 字符流 */
        InputStreamReader isr = new InputStreamReader(is, "utf-8");

        /* 提供缓存功能 */
        BufferedReader br = new BufferedReader(isr);

        String line;
        int l =0 ;
        while ((line = br.readLine()) != null) {
            System.out.printf("line : %s , content: %s ,\n",l++,line);
        }

        br.close();
    }

3、BIO 方式 ServerSocket

public static void main(String[] args) {
        try {
            ServerSocket server = new ServerSocket(10000);
            Socket socket = server.accept();
            BufferedReader is = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            String line = is.readLine();
            System.out.println("received from client : " + line);
            PrintWriter pw = new PrintWriter(socket.getOutputStream());
            pw.println("received data : " + line);
            pw.flush();
            pw.close();
            is.close();
            socket.close();
            server.close();
        }catch (Exception e){
            log.error("ServerBIO error" ,e);
        }
    }

4、NIO 方式 ServerSocket

 public static void main(String[] args) {
        try {
            ServerSocketChannel ssc = ServerSocketChannel.open();
            ssc.socket().bind(new InetSocketAddress(10000));
            ssc.configureBlocking(false);
            Selector selector = Selector.open();
            ssc.register(selector, SelectionKey.OP_ACCEPT);

            Handler handler = new Handler(1024);
            while (true) {
                if (selector.select(3000) == 0) {
                    System.out.println("等待超时");
                    continue;
                }
                System.out.println("处理请求。。");
                Iterator<SelectionKey> keyIterator = selector.selectedKeys().iterator();
                while (keyIterator.hasNext()) {
                    SelectionKey key = keyIterator.next();
                    try {
                        if (key.isAcceptable()) {
                            handler.handleAccept(key);
                        }

                        if (key.isReadable()) {
                            handler.handleRead(key);
                        }
                    } catch (IOException e) {
                        keyIterator.remove();
                        continue;
                    }
                    keyIterator.remove();
                }
            }
        } catch (Exception e) {
            log.error("ServerBIO error", e);
        }
    }

五、 参考资料

1、Java I/O

2、Java NIO浅析

02【Java的IO模型】_java io包是属于哪种io模型 如果连接大多是长连接,则会导致连接无法释放,新的请求将无法得到处理,另外,BIO这种一个请求对应一个线程的方式在应对高并发的情况下,服务器必须也要创建同等量的线程来处理客户端的请求,这样对系统的消耗是非常大的;在NIO模型中,每个请求都会有一个与服务器做数据交互的通道(Channel),所有的通道都被注册到一个选择器中(selector),当需要与服务器做数据交互时,数据通过管道写入到一个缓冲区(Buffer)中,服务器通过往缓冲区中读取数据,如果当前通道没有数据时,就什么都不会获取, 阅读详情

相关推荐

Java IO模型&NIO

Java IO模型&NIO Java IO模型NIO 楔子 概述 网络服务 经典的服务设计 经典的SocketServer循环阻塞 可伸缩目标 分而治之 事件驱动设计 背景知识AWT 事件 Reactor 模式 Reactor基础模式 Java NIO 支持 Channels Buffers Selectors SelectionKeys Reactor 模式实践 第一步初始化 第二步循环分发 第三

Schelor的专栏 6277

常见JAVA IO/NIO模型

我们常见的IO模型有:阻塞 IO 模型、非阻塞 IO 模型、多路复用 IO 模型、信号驱动 IO 模型、异步 IO 模型;下面我们就简单介绍一下以上IO模型。 1、阻塞 IO 模型 最传统的一种IO 模型,即在读写数据过程中会发生阻塞现象。当用户线程发出IO 请求之后,内核会去查看数据是否就绪,如果没有就绪就会等待数据就绪,而用户线程就会处于阻塞状态,用户线程交出CPU。当数据就绪之后,内核...

北漂码农的博客 9万+

Java 五种IO模型

Java 五种IO模型

qq_63353796的博客 739

Java 四种主要的 IO 模型

不同的IO模型在处理并发IO请求时有各自的特点和适用场景。阻塞IO模型简单易用,但在高并发情况下性能较差。非阻塞IO模型通过轮询IO状态来实现并发处理,适用于中等并发场景。IO多路复用模型通过select、poll、epoll等机制实现高并发处理。异步IO模型通过回调函数实现高并发处理,性能最佳。

qq_45607784的博客 1778

USB2.0物理层实战:手把手教你用示波器抓取D+/D-信号(附波形分析)

本文是一份针对硬件工程师的USB2.0物理层信号调试实战指南。文章详细讲解了如何使用示波器捕获和分析D+/D-差分信号,从基础设置、核心信号状态解读,到信号完整性分析与常见问题排查,手把手教你定位USB通信故障。内容涵盖USB2.0协议物理层关键概念,并附有基础解码思路,旨在提升工程师的底层调试能力。

s8t9u0v1w的博客 448

原恒星尘埃生长的三维MHD模拟与湍流效应研究

星际尘埃演化是恒星形成过程中的关键环节,其动力学行为直接影响行星系统的构建。通过磁流体力学(MHD)模拟可以揭示尘埃颗粒从微米级到行星胚胎尺度的生长机制,其中湍流效应通过改变碰撞频率和密度结构发挥双重作用。最新研究采用COALA-RAMSES耦合框架,实现了包含自洽尘埃生长的三维非理想MHD模拟,量化了不同物理环境下尘埃尺寸分布的演化规律。这些发现不仅为解释ALMA观测数据提供理论依据,也对行星形成初期条件约束具有重要意义,特别是在湍流参数化与尘埃生长速率关系方面取得突破。

weixin_30988079的博客 382

Java中的三种IO模型

模型类型优点缺点适用场景BIO同步阻塞简单易用高并发下性能差低连接数场景NIO同步非阻塞支持高并发,效率高实现复杂高并发网络应用AIO异步非阻塞高性能,资源利用率高编程复杂,调试困难高性能实时应用。

weixin_72973409的博客 1689

LEF 5.8 Language Reference学习笔记

您可以在一个 LEF 文件中定义所有元件库信息,但这样会产生一个复杂且难以管理的大文件。相反,您可以将信息分为两个文件,一个是 "technology "LEF 文件,另一个是 "cell library "LEF 文件。

yajuan的专栏 4098

Java IO 模型详解:从传统 IO 到异步 IO

IO 模型主要有五种:阻塞 I/O、非阻塞 I/O、多路复用、信号驱动和异步 I/O。阻塞 I/O:应用程序执行 I/O 操作时,会一直等待数据传输完成,期间无法执行其他任务。非阻塞 I/O:应用程序执行 I/O 操作时,如果数据未准备好,立即返回错误状态,不等待数据传输完成,可执行其他任务。多路复用:允许一个线程同时管理多个 I/O 连接,适用于高并发、低延迟和高吞吐量场景,减少线程数量和上下文切换开销。信号驱动。

专注Java和MySQL领域知识分享 1256

Dbeaver连接TDengine时序数据库

也没啥好总结的,就一句话:Dbeaver真香,而且还是我大JAVA实现的,赞。希望能帮到大家,uping。

zwrlj527的专栏 1900

Echats-wordcloud 文字云图的踩坑点【Unknown series wordCloud】

一直都是报 【ECharts Unknown series wordCloud】这玩意可能要刷脸,祝你好运吧~

Sky_Bug的博客 2417

Java中五中IO模型详解

一、前言 在《Unix网络编程》一书中提到了五种IO模型,5种IO模型分别为: 阻塞IO模型、非阻塞IO模型、IO复用模型、信号驱动的IO模型、异步IO模型。下面就分别来介绍一下这5种IO模型的异同。 二、五种IO模型 1、阻塞IO模型(BIO) 这是最传统的一种IO模型,即在读写数据过程中会发生阻塞现象。 阻塞IO模型: 当用户线程发出IO请求之后,内核会去查看数据是否就绪,如果没有就绪就会等待数据就绪,而用户线程就会处于阻塞状态,用户线程交出CPU。当数据就绪之后,内核会将数据拷贝到用户线程,并返回结果

Thinkingcao的专栏 2396

【目标跟踪】基于Kalman滤波实现无人机运动轨迹跟踪附matlab代码

1 简介 在近代控制中,需要对某一特定的运动目标进行跟踪监测,并需要对其下一时间的运动位置,运动方向,速度等信息进行预先估算,才能达到对目标的即时控制.本文简要讨论了用卡尔曼滤波算法对单个目标航迹进行预测,并借助于Matlab仿真工具,对实验的效果进行评估. 2 部分代码 clc; close all; %initial state xo=25; vox=2; %Observations **** % X-Direction Calcula

qq_59747472的博客 4731

Java的File类和IO流

File类和IO流基础

2302_81034736的博客 1349

十七、SAP-采购订单(STO)

业务逻辑、功能讲解、图文解析、章节总结

m0_72403327的博客 2065

静态时序分析(STA)—— 标准单元库

目录 1、时序库 概览 2、时序模型 1、基本概念 2、影响因素 3、线性延时模型 4、非线性延时模型 5、单元时序弧 1、组合逻辑单元 2、时序逻辑单元 6、线负载模型 参考说明 上一篇,学习了 STA相关的基本概念,本篇继续学习 STA相关的 标准单元库。 1、时序库 概览 库文件概览: 2、时序模型 1、基本概念 2、影响因素 对于组合逻辑的时序弧而言,其延时的大小取决于: 1、输出负载,即,输出端的电容负载的大小。容性负载越大,...

记录所学,分享知识,结识挚友 2852

02【Java的IO模型】

Java共支持3种网络编程模型/IO模式,分别是:我们可以根据不同的业务场景来决定选择不同I/O处理模型;在BIO模式中,服务器会为每个客户端的请求创建一个对应的线程来处理,由该线程单独负责处理一个客户请求,如果这个连接不做任何事情会造成不必要的线程开销,为此我们可以通过线程池机制改善性能;虽然使用线程池可以一定程度上改善BIO的性能,但依旧无法冲本质上解决BIO同步阻塞的的问题;如果连接大多是长连接,则会导致连接无法释放,新的请求将无法得到处理,另外,BIO这种一个请求对应一个线程的方式在应对高并发的情况

緑水長流*z 6004

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

从BIO到NIO到AIO: Java全面IO模型解析

在Java中,IO操作是通过使用java.io包中的类和接口执行的。java.io包提供了非常丰富的流(Stream)类别进行数据读写,这些流类别主要分为两大部分:字节流(例如InputStream和OutputStream)用于处理RAW数据如二进制文件,字符流(例如Reader和Writer)用于处理字符和字符串,更适用于文本数据。在传统的阻塞IO模型中,每个网络连接都需要一个线程去处理,大量的并发连接可能会导致系统过多的线程开销,从而影响性能。

逆流的小鱼 1376

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

Java IO 模型详解

即输入/输出,描述了计算机系统与外部设备之间通信的过程。阻塞(Blocking):在阻塞I/O操作中,调用者请求I/O操作并等待操作完成。在操作完成之前,调用者被挂起,无法执行其他操作。非阻塞(Non-blocking):非阻塞I/O操作允许调用者发起I/O请求后立即继续执行,不需要等待I/O操作完成。同步(Synchronous):同步I/O操作要求程序在I/O操作完成之前保持等待状态。同步操作可以是阻塞的,也可以是非阻塞的(通过轮询实现)。异步(Asynchronous)

Yaml4的博客 1375

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

【Java】网络通信IO模型

模型BIONIOAIO阻塞性同步阻塞同步非阻塞异步非阻塞线程模型1 连接 1 线程多路复用(单线程/线程池)回调驱动复杂度低中高高适用场景低并发、简单应用高并发、实时响应(如 Netty)特定场景(如文件操作)接口SocketBIO:仅用于教学或低负载场景。NIO:大多数高并发场景的首选,结合 Netty 等框架简化开发。AIO:谨慎使用,需评估操作系统支持与业务需求。为什么主流框架(如 Netty)选择 NIO 而非 AIO?

athicket 1558

Linux网络IO模型及JAVA中IO模型

文章目录1.概述2.Linux网络IO模型2.1.阻塞IO模型2.2.非阻塞IO模型2.3.IO复用模型2.3.1.select2.3.2.poll2.3.3.epoll2.4.信号驱动IO模型2.5.异步IO3.JAVA中IO模型 1.概述 在jdk1.4之前JAVA只有同步阻塞IO,在jdk1.4版本更新同步非阻塞的NIO,在jdk1.7又更新了异步的NIO2.0,本文将简单介绍下Linux有哪些网络IO模型,以及JAVA中的IO所对应的网络IO模型。 理解本文之前需要先知道一个概念:用户空间与内核空间

凶猛的小蜜蜂 734

IO模型和java IO

常见IO模型有以下几种 1.最简单的阻塞IO模型:用户线程请求数据,内核准备数据,将数据从内核复制到用户空间,从请求阻塞到获取数据,显然对资源利用率低     用餐馆的例子来解释:用户点菜,厨师准备好菜品,将菜给到用户,期间用户一直在等待,无法做别的事 2.非阻塞IO模型:用户线程请求数据,并且直接返回内核准备数据结果,如为准备好,则轮询结果,当数据准备好后,再将数据复制到用户空间,

strawberrynick的博客 549

Java之IO模型演进

在先前的文章《Unix之IO模型》已经讲述到5种IO模型以及对应的同步异步和阻塞非阻塞相关核心概念,接下来看下Java的IO模型在服务端的网络编程中是如何演进,注意这里用启动Java程序表示一个JVM进程,而JVM进程中以多线程方式进行协作,这里讲述以线程为主展开 Java中的BIO演进 BIO 概述 在上篇文章中讲述到阻塞式IO是应用进程等待内核系统接收到数据报并将数据报复制到内核再返回的...

疾风先生的博客 644

JAVA的IO模型的学习

参考: https://www.zhihu.com/question/337609338 https://blog.youkuaiyun.com/ty497122758/article/details/78979302 https://blog.youkuaiyun.com/weixin_41563161/article/details/104242190 https://blog.youkuaiyun.com/crazymakercircle/article/details/85858596 这个对IO模型讲的比较好:https://mp.

Fuly的博客 197
上一篇: 谈一谈--Java集合类
下一篇: 谈一谈--JVM的内存管理及执行子系统
 汪超
博客等级 码龄17年 65粉丝 38原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值