IO流的再学习

这里写图片描述
流 Stream
InputStream / OutputStream 字节流的抽象父类
FileInputStream / FileOutputStream 文件字节流 低级流
BufferedInputStream / BufferedOutputStream 字节缓冲流 高级流
DataInputStream / DataOutputStream 固定字节格式 高级流
PrintStream 任何数据转为字符高级流
ByteArrayInputStream / ByteArrayOutputStream 字节数组流 低级流
ObjectInputStream / ObjectOutputStream 对象序列化与反序列化的字节操作流

Reader / Writer 字符流的抽象父类
InputStreamReader / OutputStreamWriter 字符编码转换流 高级流
FileReader / FileWriter 内部是字符编码转换流接文件字节流 高级流
BufferedReader / BufferedWriter 字符缓冲流 高级流
PrintWriter 带自动行刷新的字符缓冲输出流 高级流

BufferedReader in =
new BufferedReader(字符缓冲流
new InputStreamReader(字符转换流,可带编码
s.getInputStream()),”UTF-8”);文件字节流
PrintWriter out =
new PrintWriter(//带自动行刷新的字符缓冲流
new OutputStreamWriter(//字符转换流,可带编码
s.getOutputStream(),”UTF-8”));//文件字节流

流:========================================
* 将字节读写操作,抽象为字节在管道中流动
从头到尾读写,不能跳着来,单方向的,要么输入,要么输出
————————————————————————————————————————

这里写图片描述

流根据处理的数据单位不同分为:
字节流:以字节为单位读取 8位的字节
字符流:以字符为单位读取 16位的字符
流本质都是读写字节,字符流只不过在读写时将字节转换为了字符

这里写图片描述

流还分为高级流(节点流)与低级流(处理流)
低级流(节点流):数据的来源与去向是非常明确的 直接接数据源
高级流(处理流,过滤流):不能独立存在,内部必须接其他流,
通常会给我们提供额外的功能,用于简化读写
低级流
File 文件节点流 以文件为物理节点
ByteArray 字节数组节点流 以字节数组为物理节点
高级流
Buffered 缓冲区,单字节读写效率
Data 固定字节格式
Print 任何数据转为字符
Object 序列化

这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
Java流式输入、输出原理:
这里写图片描述
在java中对于数据的输入输出操作以流的方式进行,Java提供了各种各样的流类,用以获取不同种类的数据;程序中通过标准的方法输入输出数据;
流是水流的流,流氓的流,偏向于水流,流是用来读写数据的;
Java io包中定义了多个流类型(类或抽象类)来实现输入、输出功能;
可以从不同的角度对其进行分类;
按数据流的方向不同分为输入流和输出流;输入还是输出都是站在程序的角度说;
按处理数据单位不同分为字节流和字符流;最原始的一个流,读出来的数据就是0101最底层的数据;只不过它按照字节来读,一字节八位,不是一位一位的读,而是一字节八位八位的读;字符流就是一个字符一个字符往外读,在java中是UNICODE字符串,UTF-16,,所以一个字符就是二个字节;
按功能不同分为节点流和处理流;节点流为可以从一个特定的数据源(节点)(如文件、内存)读写数据;节点流是指这根管子直接接到数据源,对原始流不满意的话可以再套其他的管道,这些套在原始流上其他的流就是处理流;处理流是连接在已存在的流(节点流或者处理流)之上,通过对数据的处理为程序通过更为强大的读写功能;
JAVA中提供的所有流类型位于包JavaIO包内,都分别继承自以下四种抽象类;
字节流 字符流
输入流    InputStream Reader
输出流    OutputStream Writer
凡是以Stream结尾的都是原始的字节流;

这里写图片描述
java.io.File磁盘文件路径字符串
这里写图片描述
类代表系统文件名/路径名《封装一个磁盘文件路径字符串》(包括完整的路径名和文件名)* 表示文件或文件夹,可以表示一个不存在的路径;不是物理上的文件,物理上的文件是硬盘中一块空间中装着很多数据;那些数据通过它读不出来;得通过IO输入输出才能读出来;封装的是这个文件的文件名,它是内存里的一个对象,但是真正的文件是在硬盘上的一块空间,在这个文件里存着各种各样的数据,我们想读这些数据是通过流的方式进行读;文件就是一个桶;也可以从网络读数据;用一根管道从文件往程序读取数据,也可以用另一根管道从程序里往文件中写入数据;
File类(java.io.*)可表示一个文件,也有可能是一个目录(在JAVA中文件和目录都属于这个类中,而且区分不是非常的明显)。Java.io.File下的方法是对磁盘上的文件进行磁盘操作,但是无法读取文件的内容。注意:创建一个文件对象和创建一个文件在JAVA中是两个不同的概念。前者是在虚拟机中创建了一个文件,但却并没有将它真正地创建到OS的文件系统中,随着虚拟机的关闭,这个创建的对象也就消失了。而创建一个文件才是在系统中真正地建立一个文件。
例如:File f=new File(“11.txt”);//创建一个名为11.txt的文件对象
f.CreateNewFile(); //真正地创建文件

f.CreateMkdir():创建目录
f.delete();删除文件
f.deleteOnExit();在进程退出的时候删除文件,这样的操作通常用在临时文件的删除。

对于命令:File f2=new file(“d:\abc\789\1.txt”)
这个命令不具备跨平台性,因为不同的OS的文件系统很不相同。
如果想要跨平台,在file类下有separtor(),返回锁出平台的文件分隔符。
File.fdir=new File(File.separator);
String str=”abc”+File.separator+”789”;
使用文件下的方法的时候一定注意是否具备跨平台性。

List():显示文件的名(相对路径)
ListFiles():返回Files类型数组,可以用getName()来访问到文件名。
使用isDirectory()和isFile()来判断究竟是文件还是目录。

   创建实例
   -------------------------------

public File(String pathname)以pathname为路径创建File对象,
如果pathname是相对路径,则默认的当前路径在系统属性user.dir中存储;
创建的只是在内存中名字叫pathname的一个file对象,而不是硬盘上真正的
物理文件;
File f = new File(“d:\a.txt”);//只表示路径:存在与否都可以,没有创建动作

public File(String parent,String child)以parent为父路径,child为子路径创建Filed对象;
File f = new File(“d:\”, “a.txt”);文件夹/目录名,文件/子目录名

路径分隔符:
Windows系统下用:\ 反斜杠
UNIX/liux系统下用:/ 正斜杠
顶层跨任何系统都可用:File的静态属性separator,它存储了当前系统的路径分隔符
public static final Stringseparator
与系统有关的默认名称分隔符,为了方便,它被表示为一个字符串。此字符串只包含一个字符,即separatorChar。
用于根据系统自动转义为相应的路径分隔符,有利于程序的跨平台性;
但是任何情况下写正斜杠/都不会错;
构造器:
File(String pathname)
File(String parent, String child)
File(File parent , String chile)
方法
——————————-
文件、目录属性
是否可读可写可执行:
canRead() 是否可读
canWrite() 是否可写
canExecute() 是否执行

                        isHidden()      是否是隐藏文件
                        exists()      表示的路径是否存在/硬盘上是否已经有该文件
                        getAbsolutePath() 完整路径<绝对路径,包含文件名>
                        getName() 得到文件名,不含路径名
                        getParent()得到父目录路径字符串<父文件对象的路径名>
                        getParentFile()得到父目录的 File 对象
                        lastModified() 最后修改时间,返回毫秒值 long类型
                        length()     文件字节量,对目录(文件夹)无效(里层进不去)
                          求目录大小需层层进入里层进行累加(树状结构遍历考虑递归)
                        isDirectory() 是否是文件夹(目录)得看磁盘上具体是什么
                        isFile()           是否是文件  得看磁盘上具体是什么
                        getTotalSpace()      空间总大小<文件所在磁盘分区大小>
                        getFreeSpace()       可用空间

文件、目录操作
通过File对象创建空文件或目录(在该对象所指的文件或目录不存在的情况下)

                        createNewFile()     创建文件<前提是该文件不存在,否则失败>
                        delete()                 删除文件或“空目录”
                        mkdir()                 创建单层目录
                        mkdirs()                创建多层目录
                        renameTo()            改名、移动
                        *)创建删除重命名方法返回 boolean,表示操作是否成功
                        File.createTempFile()     在系统临时目录创建临时文件

          目录列表:列出目录中所有文件和文件夹
                        无参:
                        list()   返回 String[],包含子文件、子目录名称
                        listFiles()  返回 File[],包含子文件、子目录的File对象
                        有参:          
                        list(FilenameFilter)
                        listFiles(FilenameFilter) 列表
                        listFiles(FileFilter)

                 只列出符合过滤条件的文件、目录,参数是:外接的过滤器

例:
File[] files = f.listFiles(new FileFilter() {//外接过滤器

       @Override
       publicboolean accept(File f) {
          return f.isFile();//是文件的保留
       }
   });

java.io.RandomAccessFile 随机存储文件类
* 只能对文件进行读写操作,有个指针
* 将文件看做是一个byte字节数组,按下标读写文件中的字节
* 用处:
1)读写文件中一段数据
2)直接在文件数据中修改
创建实例


RandomAccessFile raf = new RandomAccessFile(文件,”r”);只读用全路径才能找到文件进行操作
字符串文件路径/File对象
RandomAccessFile raf = new RandomAccessFile(文件,”rw”);可读可写

方法(输出:从内存到磁盘,即把数据写到文件;输入:从磁盘到内存)


write(int b)将 int 4个字节中末尾字节输出到文件     [1][2][3][4] --> [4]

write(byte[] arr)输出数组中所有字节值

write(byte[] arr, int from, int n)输出数组中,从from 开始的 n 个字节

writeInt(int i)         输出int的4个字节值

writeDouble(double d)   输出double的8个字节值
……
writeUTF(String s)  先输出两个字节表示字符串的字节数量,再输出字符串的所有字节值
_______________________________________________________________________

read() 从文件读取一个字节,在前面补三个 0 字节,
转为int类型 [4] - [1][2][3][4] 如果没有数据了就返回-1
就算读出负数前面加三个0也变成正数了

read(byte[] buff) 用数组作为盛放字节数据的容器,一次可以读取数组长度个
字节保存在字节数组中,而该方法是返回读取的字节数量
(前几次就是数组的长度,最后一次余多少就是多少)
readInt() 读取4个字节值转成int

readDouble() 读取8个字节值转为double

readUTF() 先读两个字节,确定字符串的字节数量,再读取表示字符串的这些字节值
seek(int index) 将文件指针移动到指定下标位置

getFilePointer() 获得指针当前所在的下标位置

setLength(字节量) 根据现有磁盘空间为新文件分配空间;将文件扩充或裁剪为指定大小

java.io.InputStream / OutputStream 字节流的抽象父类
* 字节流的抽象父类,没有实例
void write(int c) 将指定的int c中末尾一个字节[1][2][3][4] –> [4] 这个字节输出到指定输出流
void write(byte[] arr) 将字节数组arr中的数据输出到指定输出流
void write(byte[] arr, from, n) 将字节数组中从start 开始的 n 个字节输出到指定输出流中
flush() 如果内部存在缓冲,刷出缓冲数据
close()

                        int read() throws IOException
                               每次读取一个字节,然后给前面补3个0拼成四字节,

转为 int返回 [4] –> [1][2][3][4];返回读到的值,如果没有数据,返回 -1
读取一个字节并以整数的形式返回(0-255)如果返回-1已到输入流的末尾
从输入流中读取单个字节数据(会直接自动转成int类型)并返回所读取的字节数据
int read(byte[] buffer)throws IOException
每次读取多个字节存入 buffer 数组,并返回这次读取的实际字节数量,
如果读取到末尾没有数据了返回 -1 buffer是内存里的一块缓冲区,接满一桶水再运走
请你把这个buffer数组填满我再处理;
PTOP的软件如电驴,容易毁硬盘,因为它读写硬盘的次数相当频繁,老得从文件读出数据传到网上去,天天传对硬盘确实是一个损伤,你不能说每从硬盘上读取一个字节你就访问一下硬盘,办法就是你在内存里分配一块空间,把它当成一个小桶,读一次就把这个小桶装满,这样对硬盘的访问次数就大大减少了;写的时候先把内存里的小桶写满了再一次写入到硬盘中,访问一次硬盘就把数据写进去;在内存中这个缓冲区(小桶)就用字节数组组织;
从输入流中最多读取buffer.length个字节的数据,并将其存储在字节数组buffer中,
返回实际读取的字节数
int read(byte[] buffer,int offset,int length)throws IOException
读取length个字节并存到一个字节数组buffer,从offset位置开始
返回实际读取的字节数,如果读取前已到输入流的末尾返回-1
从输入流中最多读取buffer.length个字节的数据,并将其存储在字节数组buffer中,
存入数组buffer中时并不是从数组起点开始,而是从off位置开始,返回实际读取的字节数
available()
返回剩余可读取字节量,
某些场景中,不可用
void close() throws IOException关闭释放内存资源
long skip(long n) throws IOException 跳过n个字节不读,返回实际跳过的字节数

java.io.FileInputStream / FileOutputStream 文件字节节点流

  • 直接读取或写入到文件的流与文件相接的流(管道),只能插在文件上
  • 方法都是从父类继承的方法 创建实例
    ———————————-
    FileOutputStream out = new FileOutputStream(文件);用全路径才能找到文件进行操作

参数可以是路径字符串或者一个File对象总会新建文件,如果文件存在,会覆盖原文件

FileOutputStream out = new FileOutputStream(文件, true);
如果文件不存在会创建新的空文件true – 如果文件已经存在,向现有文件末尾追加数据

RandomAccessFile也可以直接读取文件,并且可以任意定位到文件某一位置开始读取数据;
而FileInputStream是流,只能顺序读写;

高级流、操作流、处理流、过滤流 <不接不行时才接入>
* 与其他已经存在的流相接对数据执行特定处理
* 对其他流进行二次加工操作,不接文件

输入:从相接的流读取数据,再加工; 读取
输出:对数据先加工,再向相接的流输出;写入

java.io.BufferedInputStream / BufferedOutputStream 字节缓冲流
* 提供内存缓冲区,缓冲一批数据,以提升单字节读写性能 对批量读取没有意义
创建实例 —————————–
BufferedOutputStream out = new BufferedOutputStream(其他流); 默认缓冲区8K 1024 2048 4096 8192
BufferedOutputStream out = new BufferedOutputStream(其他流, 缓冲区大小);指定缓冲区大小

flush():该方法用于将缓冲区的数据强制刷出,为了及时性需要
close():关流前首先自动会flush(),所以此方法中包含flush()方法的功能

java.io.DataInputStream / DataOutputStream 以固定字节格式读写数据高级处理流这里写图片描述
这里写图片描述
* 特殊的读取方法,当没有数据时,会出现 EOFException EOF - End Of File, End Of
创建实例 DataInputStream in = new DataInputStream(其他流);

方法——————————————
writeInt(int i) 输出整数的4个字节 该方法完成了二个操作:
将int值转为了4个字节;
通过文件输出流写入到了文件中;
第一步:将数据转换为字节的过程称之为序列化<这里就是基本数据序列化>
第二步:将数据写入到磁盘上做长久保存称之为持久化
writeDouble(double d) 输出double的8个字节
writeUTF(String s) 先输出两个字节表示字符串的字节长度,再输出表示字符串的这些字节
____________________________________________________________________
readInt() 先连续读取4个字节,转为int值,反序列化<从字节转换为数据的过程称之为反序列化>
readDouble() 读取8个字节,转为double
readUTF() 先读取2个字节确定字符串的字节数量,再读取这些字节转为字符串

java.io.PrintStream 将任何数据都转为字符串数据输出 高级处理流 * * 只能与字节流相接
创建实例 —————————
PrintStream out = new PrintStream(其他输出流);

PrintStream out = new PrintStream(文件); * 内部创建文件流与文件相接

PrintStream out = new PrintStream(文件, 字符编码); * 将字符转为指定字符编码再输出
方法—————————
print(数据)
println(数据) 末尾补换行;windows中有2个: \r 回车 \n换行 其他系统:\n
print(97) ‘9’ ‘7’:每个字变为一个字符输出 39 37 9字符和7字符
System.out.println((int)’9’);//57 对应16进制的39
Println(97) 39 37 0d 0a( /r /n 13 14) 二个一块是windows的换行
print(对象) 对象.toString()

java.io.ByteArrayInputStream / ByteArrayOutputStream 直接读写内存数组中的字节数据节点流 * 直接与内存中一个数组相接,是一个低级流没有磁盘读写,单字节读取效率都特别高
因为直接是在java程序内存中操作的没有占用任何系统资源close不close都没有关系

创建方式—————————————-
ByteArrayInputStream in = new ByteArrayInputStream(byte[]数组);//从内存数组读取数据 得指定一个数组

ByteArrayOutputStream out = new ByteArrayOutputStream();//输出数据到内部数组不需要给参数
拿到输出的字节值会直接摆在自己内部维护的数组中数组满了会复制成更长数组
ByteArrayOutputStream 方法 —————————————-
toByteArray() 输出结束后可以获得内部生成的 byte[] 数组 取出内部维护的装数据的数组

java.io.ObjectInputStream / ObjectOutputStream对象被序列化成一串字节值还可以恢复的字节高级处理流创建的学生对象(包含姓名性别年龄这些属性)被序列化成一串字节值输出保存到磁盘文件,
不是学生这个类被序列化了;这一串包含三部分:克隆技术在java中的体现
完整类型名称(即什么类型的对象)
变量信息变量名和变量类型,不包含变量的值
成员变量的值
之后可以读取或者恢复这个对象
手游正在玩,电话进来了,游戏退出时的状态封装在一个对象里,然后把这个对象可以保存成对象存到文件里,
之后游戏恢复时再把这个对象恢复出来;
对象序列化的含义:
Java 序列化技术可以使你将一个对象的状态写入一个Byte 流里,并且可以从其它地方把该Byte 流里的数据读出来。重新构造一个相同的对象。这种机制允许你将对象通过网络进行传播,并可以随时把对象持久化到数据库、文件等系统里。Java的序列化机制是RMI、EJB、JNNI等技术的技术基础。

这里写图片描述
这里写图片描述
这里写图片描述
* 只允许实现了 Serializable 接口的子类进行对象序列化: Serializable接口没有抽象方法
* Serializable,可序列化 * 标识接口:
* 空接口,不需要实现任何方法,
* 标识 Student 类型对象可以允许序列化
异常:NotSerializableException不能序列化 InvalidClassException

这里写图片描述

  • 不能对象序列化:
    static 静态属于对象它在类所控制的内存空间中不随对象序列化输出
    transient:临时、瞬间、瞬态
    (临时只在程序运行内存中存在,不会被序列化保存到文件)用于修饰属性,在序列化时将被忽略
    • 序列化头部数据
    • 新建ObjectOutputStream对象时,会自动先输出4个头部字节值
    • 新建ObjectIntputStream对象时会自动先读取4个头部字节值,来确认后面的数据是否是对象序列化数据
    • private static final long serialVersionUID = 1L; 前面的访问范围可以任意改
      反序列化恢复一个对象时, 会比对保存的版本 id 和当前类的版本 id,版本不一致不允许恢复

这里写图片描述

意思就是对象的序列化和反序列化都得依赖于创造该对象的那个类;
创建实例 ——————————————
ObjectOutputStream out = new ObjectOutputStream(字节流);
*) 创建 OOS 时,会输出 4 个字节,表示后续数据是对象序列化数据
ObjectInputStream in = new ObjectInputStream(字节流);
*) 先读取 4 个字节,确认后续数据是否是对象序列化数据
方法——————————————
out.writeObject(数据到字节:对象序列化,对象序列化成一串字节值)
in.readObject(字节到数据:对象反序列化,读取并恢复对象)

例:将person实例对象写入磁盘文件分二步(输出:out.writeObject(person)):
先将对象转换为字节(对象序列化成一串字节值)
将这些字节写入到磁盘文件(对象持久化)
例:
//发送文件对象:把文件名及文件大小封装成对象
ObjectOutputStream out =new ObjectOutputStream(s.getOutputStream());
File f = new File(dir,fileName);
Response resp = new Response();
resp.setFileName(fileName);
resp.setLength(f.length());
out.writeObject(resp);
out.flush();

//接收客户端发送过来的请求对象
ObjectInputStream in =new ObjectInputStream(s.getInputStream());
return (Requst) in.readObject();

//发送请求对象
Requst rep = new Requst();
rep.setWhat(Requst.WHAT_LIST);
ObjectOutputStream out = newObjectOutputStream(s.getOutputStream());
out.writeObject(rep);
out.flush();
java.io.Reader / Writer 字符流抽象父类 * 读写字符数据的流(没有实例)

   方法----------------------------------
                 Writer
   -----------------------------
   write(int c) 十进制97 是十六进制(四位二进制表示一位)00 00 00 61    00 61        
   将 int c中4 个字节中末尾 2 个字节作为1个16位char字符数据输出到指定输出流
                            [1][2][3][4] --> [3][4] --》转为其他编码字符输出
                      也就是一次写一个char值,所以是写给定的int值的后16位《低16位》

   write(char[] arr)将字符数组中的数据输出到指定输出流中可以直接接char数组的封装类型String

   write(char[] arr, start, n) 输出数组中,从 start 开始的 n 个字符
  //因为字符流直接以字符为操作单位,所以writer可以用字符串代替字符数组,即以String对象作为参数
   write(String s)   输出字符串中的字符到指定输出流中
   write(String s,int off,int len) 输出字符串中从 start 开始的 n 个字符
   flush()         刷出缓存

                 Reader
   -----------------------------     
   int read()                      
   读取单个字符 --> [3][4] --> [1][2][3][4]返回 int 类型,没有数据返回 -1

也就是一次读一个char值(即一个字符),所以是读给定的int值的后16位《低16位》
字符:是一个16位无符号整数,是一个16进制数,数值是对应的unicode编码
从输入流中读取单个字符(会自动转换成int类型),返回读取的字符数据
int read(char[] buff) 读取多个字符
从输入流中最多读取buff.length个字符的数据,
并将其存储在字符数组buff中,返回实际读取的字符数
int read(char[] chuf,int off,int len) 读取多个字符
从输入流中最多读取buff.length个字符的数据,并将其存储在字符数组chuf中,
放入数组chuf时并不是从起点开始,而是从off位置开始,返回实际读取的字符数
java.io.InputStreamReader / OutputStreamWriter 基于unicode编码的字符编码转换流 高级处理转换流这里写图片描述
编码转换流 转换指转换的是基于unicode编码的转换
* 高级流,接字节流 将字节转换成字符
指定要读取的文件的字符编码 根据指定的字符集读取字符
* 从字节流读取的字节数据,编码转换为 Unicode 字符 InputStreamReader
如:将文件中一个字节的a和三个字节的中是 UTF-8编码转为Unicode字符编码
读取其他编码字节值转成unicode
* 将 Unicode 转为其他编码字节值输出 OutputStreamWriter 指定要写入数据的文件的字符编码
如:中文的中在内存中是2个字节输出后转为了UTF-8字符变成了三个字节
将内存中unicode编码字符转成其他编码字节数据输出
* 默认存在 8k 缓冲区

   创建实例
   ----------------------------------------

OutputStreamWriter out = new OutputStreamWriter(相接的字节流);
将内存中 UNICODE 字符转为系统默认字符编码

OutputStreamWriter out = new OutputStreamWriter(相接的字节流, 字符编码);
将内存中 UNICODE 字符转为指定的字符编码
这里写图片描述
这里写图片描述

java.io.FileReader / FileWriter文件字符节点流(和文件直接关联)内部是文件字节流接字符编码转换流

  • 不能指定编码,只能使用默认编码(中文windows中默认GBK编码)
    创建实例————————————-
    FileWriter out = new FileWriter(文件);//只是一种代码简化方式用不用二可
    类似于:
    OutputStreamWriter out =
    new OutputStreamWriter(//字符编码转换流
    new FileOutputStream(文件));//文件字节流

java.io.BufferedReader / BufferedWriter 字符缓冲流 readLine() * 提供字符缓冲区
BufferedReader 方法


   readLine() - 读取一行字符串,不含末尾的换行符;
             返回字符串(换行符之前的一行字符串)如果没有数据,返回 null

write.witer(line);
write.newline();//换行用(不同平台通用)

java.io.PrintWriter 带自动行刷新的字符打印流
* 应用、用法与 PrintStream 完全相同,将任何类型数据转为字符数据
* 不同的是PrintStream只能接字节流,而PrintWriter字节流和字符流都可以用
效率低(刷新次数频繁,所以低)
方法—————————————-
print() println()

它的构造方法:
PrintWriter(File file)
基于文件的文件对象创建对该文件写操作的输出流
PrintWriter(String fileName)
基于给定的文件全路径限定名创建基于该文件写操作

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值