21-IO流进阶

IO流加强

字符流

整体框架

image-20230807090051516

FileReader(文件字符输入流)

作用:以内存为基准,可以把文件中的数据以字符的形式读入到内存中去。

构造方法
构造器说明
public FileReader**(File file)**创建字符输入流管道与源文件接通
public FileReader**(String pathname)**创建字符输入流管道与源文件接通
常用方法
方法名称说明
public int read()每次读取一个字符返回,如果发现没有数据可读会返回-1.
public int read(char[] buffer)每次用一个字符数组去读取数据,返回字符数组读取了多少个字符,如果发现没有数据可读会返回-1.
一个字符一个字符读取
    public static void main(String[] args) {
        try (
                Reader fr = new FileReader("src\\IoExpand\\text.txt");
            )
        {
            int c =0;
            while(c!=-1){
                c = fr.read();
                System.out.print((char) c);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

其中可以优化的点,这个代码是正确的,但是这个我们可以在简化一下比如说我们可以改成while((c==fr.read())!=-1)

            int c =0;
            while(c!=-1){
                c = fr.read();
                System.out.print((char) c);
            }
多个字符读取
            //字符数组读取
            char[] ch = new char[4];
            int length;
            while ((length=fr.read(ch))!=-1){
                String s = new String(ch,0,length);
                System.out.print(s);
            }

多个字符读取我们是返回的是成功读取的字符长度,并将其存放到字节数组中

FileWriter(文件字符输出流)

作用:以内存为基准,把内存中的数据以字符的形式写出到文件中去。

构造方法
构造器说明
public FileWriter(File file)创建字节输出流管道与源文件对象接通
public FileWriter(String filepath)创建字节输出流管道与源文件路径接通
public FileWriter(File file,boolean append)创建字节输出流管道与源文件对象接通,可追加数据
public FileWriter(String filepath,boolean append)创建字节输出流管道与源文件路径接通,可追加数据

boolean append如果是false我们就是将文件覆盖,如果是true,我们就是在将原有的内容上在添加内容

常用方法
方法名称说明
void write(int c)写一个字符
void write(String str)写一个字符串
void write(String str, int off, int len)写一个字符串的一部分
void write(char[] cbuf)写入一个字符数组
void write(char[] cbuf, int off, int len)写入字符数组的一部分
    public static void main(String[] args) {
        try ( Writer fw = new FileWriter("src/IoExpand/write.txt",true);){
           //写一个字符
            fw.write('a');;
            fw.write('b');
            fw.write('c');
            fw.write("\r\n");
            //写一个字符串出去
            fw.write("我爱你");
            fw.write("\r\n");
            //写字符串的一部分出去
            fw.write("郑丽霞爱我",0,3);
            fw.write("\r\n");
            //写一个字符数组出去
            fw.write(new char[]{'我','爱','你','就','爱','你'});
            fw.write("\r\n");
            //写字符数组的一部分
            fw.write(new char[]{'我','爱','你','就','爱','你'},0,5);

        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
注意事项

字符输出流写出数据后,必须刷新流,或者关闭流,写出去的数据才能生效

这是因为为了防止多次调用系统资源,我们是先将数据放到内存中的缓冲区的,缓冲区由于是内存所以读取的速度也很快,然后等这个输出流刷新或者关闭的时候在将这个数据放到文件中

关闭的时候自动就会刷新这个内存

close是关闭

flush是刷新

我们一般不用刷新,但是如果你想清空数据可以用刷新

当缓冲区满 的话我们就会自动将这个数据存放到文件中

方法名称说明
public void flush() throws IOException刷新流,就是将内存中缓存的数据立即写到文件中去生效!
public void close() throws IOException关闭流的操作,包含了刷新!

字节流、字符流的使用场景小结:
字节流适合做一切文件数据的拷贝(音视频,文本);字节流不适合读取中文内容输出。
字符流适合做文本文件的操作(读,写)。

缓冲流

image-20230807095425983

image-20230807095831024

原理:字节缓冲输入流自带了8KB缓冲池;字节缓冲输出流也自带了8KB缓冲池

字节缓冲输入输出流

构造器说明
public BufferedInputStream(InputStream is)把低级的字节输入流包装成一个高级的缓冲字节输入流,从而提高读数据的性能
public BufferedOutputStream(OutputStream os)把低级的字节输出流包装成一个高级的缓冲字节输出流,从而提高写数据的性能
  1. 字节缓冲流自带8KB缓冲区
  2. 可以提高原始字节流、字符流读写数据的性能

这个流的使用方法和字节输入输出流的方法是一样的,只是它的大小变化了

    public static void main(String[] args) {
        try (InputStream is = new FileInputStream("src\\IoExpand\\text.txt");
             BufferedInputStream bis= new BufferedInputStream(is);
             OutputStream os = new FileOutputStream("src\\IoExpand\\newtext.txt");
             BufferedOutputStream bos= new BufferedOutputStream(os);
             ){
            int c;
            while ((c=bis.read())!=-1){
                os.write(c);
                System.out.print((char) c);
            }
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }

需要注意的是

这几个构建方法千万别忘记,还有就是书写这个try catch的时候一定要注意不能漏写一个符号

InputStream is = new FileInputStream("src\\IoExpand\\text.txt");
BufferedInputStream bis= new BufferedInputStream(is);
OutputStream os = new FileOutputStream("src\\IoExpand\\newtext.txt");
BufferedOutputStream bos= new BufferedOutputStream(os);

字符缓冲输入输出流

BufferedReader(字符缓冲输入流)

作用:自带8K(8192)的字符缓冲池,可以提高字符输入流读取字符数据的性能。

构造器说明
public BufferedReader(Reader r)把低级的字符输入流包装成字符缓冲输入流管道,从而提高字符输入流读字符数据的性能

image-20230807103203895

字符缓冲输入流新增的功能:按照行读取字符

方法说明
public String readLine()读取一行数据返回,如果没有数据可读了,会返回null
    public static void main(String[] args) {
        try(Reader reader = new FileReader("src\\IoExpand\\csb.txt");
            BufferedReader br  = new BufferedReader(reader);
        ){
            String s;
            while (( s = br.readLine())!=null){
                System.out.println(s);
            }
        }catch (Exception e) {
            e.printStackTrace();
        }
    }
BufferedWriter(字符缓冲输出流)

作用:自带8K的字符缓冲池,可以提高字符输出流写字符数据的性能

构造器说明
public BufferedWriter(Writer r)把低级的字符输出流包装成一个高级的缓冲字符输出流管道,从而提高字符输出流写数据的性能

字节缓冲输出流新增的功能,换行

方法说明
public void newLine()换行
    public static void main(String[] args) {
        try (InputStream is = new FileInputStream("src\\IoExpand\\text.txt");
             BufferedInputStream bis= new BufferedInputStream(is);
             OutputStream os = new FileOutputStream("src\\IoExpand\\newtext.txt");
             BufferedOutputStream bos= new BufferedOutputStream(os);
             ){
            int c;
            while ((c=bis.read())!=-1){
                os.write(c);
                System.out.print((char) c);
            }
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }

出师表

出现问题

  1. list.sort((o1, o2) -> o1.charAt(0) - o2.charAt(0));

刚开始的提示我

java.lang.StringIndexOutOfBoundsException: String index out of range: 0
	at java.base/java.lang.StringLatin1.charAt(StringLatin1.java:48)
	at java.base/java.lang.String.charAt(String.java:1515)
	at IoExpand.csb.lambda$main$0(csb.java:21)
	at java.base/java.util.TimSort.binarySort(TimSort.java:296)
	at java.base/java.util.TimSort.sort(TimSort.java:221)
	at java.base/java.util.Arrays.sort(Arrays.java:1307)
	at java.base/java.util.ArrayList.sort(ArrayList.java:1721)
	at IoExpand.csb.main(csb.java:21)

仔细查阅发现这个出现了问题就是文件里面的首字母可能是空字符,所以出现错误

因此我们要加入一个判断语句,或者修改文件内容,让其不存在这个空字符开头的内容

            /* if (o1.length() == 0 || o2.length() == 0) {
                // 处理空字符串的情况,例如:抛出异常、放到末尾等
                // 这里假设将空字符串放到末尾
                return o1.isEmpty() ? 1 : -1;
            }*/
package IoExpand;

import java.io.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

public class csb {
    public static void main(String[] args) {
        try(Reader read = new FileReader("src\\IoExpand\\csb.txt");
            BufferedReader br = new BufferedReader(read);
            Writer writer = new FileWriter("src\\IoExpand\\newcsb.txt");
            BufferedWriter bw = new BufferedWriter(writer);
        ){
            ArrayList<String> list = new ArrayList<>();
            String s;
            while ((s=br.readLine())!=null){
                list.add(s);
            }
            //System.out.println(list);
            //离谱,这个有空字符
            /* if (o1.length() == 0 || o2.length() == 0) {
                // 处理空字符串的情况,例如:抛出异常、放到末尾等
                // 这里假设将空字符串放到末尾
                return o1.isEmpty() ? 1 : -1;
            }*/
            list.sort((o1, o2) -> o1.charAt(0) - o2.charAt(0));

            for (String s1 : list) {
                bw.write(s1);
                bw.newLine();
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

原始流和缓冲流的性能分析

测试用例:
分别使用原始的字节流,以及字节缓冲流复制一个很大视频。
测试步骤:

  1. 使用低级的字节流按照一个一个字节的形式复制文件。
  2. 使用低级的字节流按照字节数组的形式复制文件。
  3. 使用高级的缓冲字节流按照一个一个字节的形式复制文件。
  4. 使用高级的缓冲字节流按照字节数组的形式复制文件。

使用低级的字节流按照一个一个字节的形式复制文件。

    public static void copy1(){
        Long startTIme = System.currentTimeMillis();
        try(InputStream is = new FileInputStream(SRC_FILE);
            //BufferedInputStream bis = new BufferedInputStream(is);
            OutputStream os = new FileOutputStream(DEST_FILE+"1.zip");
            //BufferedOutputStream bos = new BufferedOutputStream(os);
        ) {
            int c ;
            while ((c=is.read())!=-1){
                os.write(c);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        long endTime = System.currentTimeMillis();
        System.out.println("字节流,一个字节一个字节耗时:"+(endTime-startTIme)/1000+"秒");
    }


使用低级的字节流按照字节数组的形式复制文件。

    public static void copy2(){
        Long startTIme = System.currentTimeMillis();
        try(InputStream is = new FileInputStream(SRC_FILE);
            //BufferedInputStream bis = new BufferedInputStream(is);
            OutputStream os = new FileOutputStream(DEST_FILE+"2.zip");
            //BufferedOutputStream bos = new BufferedOutputStream(os);
        ) {
            byte[] bytes = new byte[1024*8];
            int len;
            while ((len=is.read(bytes))!=-1){
                os.write(bytes,0,len);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        long endTime = System.currentTimeMillis();
        System.out.println("字节流,字节数组耗时:"+(endTime-startTIme)/1000+"秒");
    }

使用高级的缓冲字节流按照一个一个字节的形式复制文件。

    public static void copy3(){
        Long startTIme = System.currentTimeMillis();
        try(InputStream is = new FileInputStream(SRC_FILE);
            BufferedInputStream bis = new BufferedInputStream(is);
            OutputStream os = new FileOutputStream(DEST_FILE+"3.zip");
            BufferedOutputStream bos = new BufferedOutputStream(os);
        ) {
            int c ;
            while ((c=bis.read())!=-1){
                bos.write(c);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        long endTime = System.currentTimeMillis();
        System.out.println("缓冲字节流,一个字节一个字节耗时:"+(endTime-startTIme)/1000+"秒");
    }

使用高级的缓冲字节流按照字节数组的形式复制文件。

    public static void copy4(){
        Long startTIme = System.currentTimeMillis();
        try(InputStream is = new FileInputStream(SRC_FILE);
            BufferedInputStream bis = new BufferedInputStream(is);
            OutputStream os = new FileOutputStream(DEST_FILE+"4.zip");
            BufferedOutputStream bos = new BufferedOutputStream(os);
        ) {
            byte[] bytes = new byte[1024*8];
            int len;
            while ((len=bis.read(bytes))!=-1){
                bos.write(bytes,0,len);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        long endTime = System.currentTimeMillis();
        System.out.println("换种字节流,字节数组耗时:"+(endTime-startTIme)/1000+"秒");
    }

字节数组在某个范围中越大越好,但是达到一定程度,这个速度提高范围就不行了,因为你装数据和倒数据就要会变的很慢

同时并不是使用高级的缓冲字节流按照字节数组的形式复制文件就会快于其他的,当我们提高这个字节数组的大小,比如说我们扩大好多倍,我们的缓冲字节流和低级的字节流按照字节数组的形式复制文件速度是类似的

            byte[] bytes = new byte[1024*8];   //*32 *64 *128
            int len;
            while ((len=is.read(bytes))!=-1){
                os.write(bytes,0,len);
            }
        try(InputStream is = new FileInputStream(SRC_FILE,1024*8);  //*32 *64
            BufferedInputStream bis = new BufferedInputStream(is);
            OutputStream os = new FileOutputStream(DEST_FILE+"4.zip",1024*8);//*32 *64
            BufferedOutputStream bos = new BufferedOutputStream(os);
        ) {
            byte[] bytes = new byte[1024*8];  //*32 *64
            int len;
            while ((len=bis.read(bytes))!=-1){
                bos.write(bytes,0,len);
            }

推荐使用哪种方式提高字节流读写数据的性能?
建议使用字节缓冲输入流、字节缓冲输出流,结合字节数组的方式,目前来看是性能最优的组合。

不同编码读取会出现乱码

image-20230807122757380

image-20230807131704416

InputStreamReader(字符输入转换流)

我们之前学的转换是

String提供了如下方法说明
String(byte[] bytes)通过使用平台的默认字符集解码指定的字节数组来构造新的 String
String(byte[] bytes, String charsetName)通过指定的字符集解码指定的字节数组来构造新的 String
byte[] getBytes()使用平台的默认字符集将该 String编码为一系列字节,将结果存储到新的字节数组中
byte[] getBytes(String charsetName)使用指定的字符集将该 String编码为一系列字节,将结果存储到新的字节数组中

现在是直接通过这个文件进行转码

解决不同编码时,字符流读取文本内容乱码的问题。
解决思路:先获取文件的原始字节流,再将其按真实的字符集编码转成字符输入流,这样字符输入流中的字符就不乱码了。

构造器说明
public InputStreamReader(InputStream is)把原始的字节输入流,按照代码默认编码转成字符输入流(与直接用FileReader的效果一样)
public InputStreamReader (InputStream is , String charset)把原始的字节输入流,按照指定字符集编码转成字符输入流(重点)

OutputStreamWriter字符输出转换流

作用:可以控制写出去的字符使用什么字符集编码。
解决思路:获取字节输出流,再按照指定的字符集编码将其转换成字符输出流,以后写出去的字符就会用该字符集编码了。

构造器说明
public OutputStreamWriter(OutputStream os)可以把原始的字节输出流,按照代码默认编码转换成字符输出流。
public OutputStreamWriter(OutputStream os,String charset)可以把原始的字节输出流,按照指定编码转换成字符输出流(重点)

打印流

image-20230807131747849

PrintStream/PrintWriter(打印流)

作用:打印流可以实现更方便、更高效的打印数据出去,能实现打印啥出去就是啥出去。

PrintStream提供的打印数据的方案

构造方法
构造器说明
public PrintStream(OutputStream/File/String)打印流直接通向字节输出流/文件/文件路径
public PrintStream(String fileName, Charset charset)可以指定写出去的字符编码
public PrintStream(OutputStream out, boolean autoFlush)可以指定实现自动刷新
public PrintStream(OutputStream out, boolean autoFlush, String encoding)可以指定实现自动刷新,并可指定字符的编码
其他方法
方法说明
public void println(Xxx xx)打印任意类型的数据出去
public void write(int/byte[]/byte[]一部分)可以支持写字节数据出去
    public static void main(String[] args) {
        try(
                PrintStream ps = new PrintStream("src\\IoExpand\\print.ext");
                ){
                ps.print(67);
                ps.println(56);
                ps.print(45);
        }catch (Exception e){
            e.printStackTrace();
        }
    }

PrintWriter提供的打印数据的方案

构造器说明
public PrintWriter(OutputStream/Writer/File/String)打印流直接通向字节输出流/文件/文件路径
public PrintWriter(String fileName, Charset charset)可以指定写出去的字符编码
public PrintWriter(OutputStream out/Writer, boolean autoFlush)可以指定实现自动刷新
public PrintWriter(OutputStream out, boolean autoFlush, String encoding)可以指定实现自动刷新,并可指定字符的编码
方法说明
public void println(Xxxxx)打印任意类型的数据出去
public void write(int/String/char[]/…)可以支持写字符数据出去

PrintStream和PrintWriter的区别

  1. 打印数据的功能上是一模一样的:都是使用方便,性能高效(核心优势)
  2. PrintStream继承自字节输出流OutputStream,因此支持写字节数据的方法。
  3. PrintWriter继承自字符输出流Writer,因此支持写字符数据出去。
  4. 两者的唯一区别就是这个write方法不一样

注意事项

如果我们使用这个打印流的时候想要追加数据,我们可以并不能直接在高级流中进行追加,我们可以在低级流中进行追加之后在进行高级流的使用

PrintStream ps = new PrintStream(new FileOutputStream("src\\IoExpand\\print.ext",*true*));

打印流的一种应用:输出语句的重定向

如果我们将项目进行封装了之后我们就不能够直接进行输出到控制台了,我们这个时候就要将这个输出语句进行重定向了,将其输出到文件中.

System.out.println("老骥伏枥");
System.out.println("志在千里");
System.out.println("烈士暮年");
System.out.println("壮心不已");

可以把输出语句的打印位置改到某个文件中去。

PrintStream ps = new PrintStream("文件地址")
System.setOut(ps);

System.setOut(ps);

改变这个输出语句的默认输出位置,之后再使用这个System.out.println("壮心不已");就会输出到文件中去

数据流

image-20230807150315703

DataInputStream(数据输入流)

允许把数据和其类型一并写出去。

构造器说明
public DataOutputStream**(**OutputStream out)创建新数据输出流包装基础的字节输出流
方法说明
public final void writeByte(int v) throws IOException将byte类型的数据写入基础的字节输出流
public final void writeInt**(int v)** throws IOException将int类型的数据写入基础的字节输出流
public final void writeDouble**(Double v)** throws IOException将double类型的数据写入基础的字节输出流
public final void writeUTF**(String str)** throws IOException将字符串数据以UTF-8编码成字节写入基础的字节输出流
public void write(int/byte[]/byte[]一部分)支持写字节数据出去
    try (DataOutputStream dos= new DataOutputStream(new FileOutputStream("src\\IoExpand\\data.txt"));){
        dos.write(97);
        dos.writeDouble(12.3);
        dos.writeUTF("woaini");
        dos.writeBoolean(true);
    } catch (Exception e) {
        throw new RuntimeException(e);
    }

DataInputStream(数据输入流)

用于读取数据输出流写出去的数据。

构造器说明
public DataInputStream**(**InputStream is)创建新数据输入流包装基础的字节输入流
方法说明
Public final byte readByte() throws IOException读取字节数据返回
public final int readInt() throws IOException读取int类型的数据返回
public final double readDouble() throws IOException读取double类型的数据返回
public final String readUTF() throws IOException读取字符串数(UTF-8)据返回
public int readInt()/read(byte[])支持读字节数据进来
        try (DataOutputStream dos= new DataOutputStream(new FileOutputStream("src\\IoExpand\\data.txt"));
             DataInputStream dis = new DataInputStream(new FileInputStream("src\\IoExpand\\data.txt"));
        ){
            dos.write(97);
            dos.writeDouble(12.3);
            dos.writeUTF("woaini");
            dos.writeBoolean(true);
            int read = dis.read();
            System.out.println(read);
            double v = dis.readDouble();
            System.out.println(v);
            System.out.println(dis.readUTF());
            System.out.println(dis.readBoolean());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

通过这个数据流能够很方便的让我们能够从这个文件中读取变量赋值给数据吗???

序列化流

image-20230807151726665

ObjectOutputStream(对象字节输出流)

可以把Java对象进行序列化:把Java对象存入到文件中去。

构造器说明
public ObjectOutputStream**(**OutputStream out)创建对象字节输出流,包装基础的字节输出流
方法说明
public final void writeObject**(Object o)** throws IOException把对象写出去
public static void main(String[] args) {
    User u = new User("qiu",19);
    try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("src\\IoExpand\\user\\object.txt"));){
        oos.writeObject(u);
        System.out.println("win");
    } catch (IOException e) {
        e.printStackTrace();
    }
}

注意:对象如果要参与序列化,必须实现序列化接口(java.io.Serializable)

接口时一个标志,里面没有内容,是通知虚拟机这个对象能够序列化的

public class User implements Serializable {
    private  String name;
    private  int age;
}

ObjectInputStream(对象字节输入流)

可以把Java对象进行反序列化:把存储在文件中的Java对象读入到内存中来。

构造器说明
public ObjectInputStream(InputStream is)创建对象字节输入流,包装基础的字节输入流
方法说明
public final Object readObject()把存储在文件中的Java对象读出来

如果想要同时序列化多个对象,怎么办

用一个ArrayList集合存储多个对象,然后直接对集合进行序列化即可
注意:ArrayList集合已经实现了序列化接口!

不想序列化对象的某个属性

transientji加在对象属性前面,我们就可以让这个对象属性不用进行序列化

框架

解决某类问题,编写的一套类、接口等,可以理解成一个半成品,大多框架都是第三方研发的。
好处:在框架的基础上开发,可以得到优秀的软件架构,并能提高开发效率
框架的形式:一般是把类、接口等编译成class形式,再压缩成一个.jar结尾的文件发行出去。

image-20230807155155226

什么是IO框架

封装了Java提供的对文件、数据进行操作的代码,对外提供了更简单的方式来对文件进行操作,对数据进行读写等。

Commons-io

Commons-io是apache开源基金组织提供的一组有关IO操作的小框架,目的是提高IO流的开发效率。

FileUtils类提供的部分方法展示说明
public static void copyFile(File srcFile, File destFile)复制文件。
public static void copyDirectory(File srcDir, File destDir)复制文件夹
public static void deleteDirectory(File directory)删除文件夹
public static String readFileToString(File file, String encoding)读数据
public static void writeStringToFile(File file, String data, String charname, boolean append)写数据
IOUtils类提供的部分方法展示说明
public static int copy(InputStream inputStream, OutputStream outputStream)复制文件。
public static int copy(Reader reader, Writer writer)复制文件。
public static void write(String data, OutputStream output, String charsetName)写数据

添加外部框架

以Commons-io为例,如果只是想使用的话,我们只需要使用这个包就好了

image-20230807160216028

别的jat不导入也不影响你使用

之后我们在我们的主项目里面新建一个目录lib,专门用来存放这个外部的包

然后哦我们将这个jar包赋值粘贴过来,之后我们右击这个lib包,我们可以将这个包添加到库中

image-20230807160351147

g readFileToString(File file, String encoding) | 读数据 |
| public static void writeStringToFile(File file, String data, String charname, boolean append) | 写数据 |

IOUtils类提供的部分方法展示说明
public static int copy(InputStream inputStream, OutputStream outputStream)复制文件。
public static int copy(Reader reader, Writer writer)复制文件。
public static void write(String data, OutputStream output, String charsetName)写数据

添加外部框架

以Commons-io为例,如果只是想使用的话,我们只需要使用这个包就好了

[外链图片转存中…(img-cVw3Bk6q-1691455389419)]

别的jat不导入也不影响你使用

之后我们在我们的主项目里面新建一个目录lib,专门用来存放这个外部的包

然后哦我们将这个jar包赋值粘贴过来,之后我们右击这个lib包,我们可以将这个包添加到库中

[外链图片转存中…(img-jBoRa7Df-1691455389419)]

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值