[2017.11.26]IO流2

1.DataOutputStream和DataInputStream
都属于数据流:可以针对Java基本数据类型的数据进行读写操作

2.DataOutputStream构造方法
public DataOutputStream(OutputStream out)

3.演示:

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class DataStreamDemo {

    public static void main(String[] args) throws IOException {

//      write();

        read();
    }


    //读数据
    private static void read() throws FileNotFoundException, IOException {
        //读数据
        //创建数据输入流对象
        DataInputStream dis = new DataInputStream(
                new FileInputStream("dos.txt"));

        //读数据
        byte b = dis.readByte() ;
        int i = dis.readInt() ;
        short s = dis.readShort() ; 
        long l = dis.readLong() ;
        char ch = dis.readChar() ;
        boolean flag = dis.readBoolean() ;
        float f = dis.readFloat() ;
        double d = dis.readDouble() ;

        //释放资源
        dis.close() ;

        System.out.println(b);
        System.out.println(i);
        System.out.println(s);
        System.out.println(l);
        System.out.println(ch);
        System.out.println(flag);
        System.out.println(f);
        System.out.println(d);
    }


    //写数据
    private static void write() throws FileNotFoundException, IOException {
        //创建数据输出流对象
        DataOutputStream dos = new DataOutputStream(new FileOutputStream(
                "dos.txt"));

        //写数据
        dos.writeByte(100) ;
        dos.writeInt(1000) ;
        dos.writeShort(120) ;
        dos.writeLong(1000000L);
        dos.writeChar('A') ;
        dos.writeBoolean(true) ;
        dos.writeFloat(12.34F) ; 
        dos.writeDouble(12.56) ;

        //释放资源
        dos.close() ;
    }
}

4.ByteArrayInputStream和ByteArrayOutStream(重点)
内存操作流:针对内存的数据进行操作的,程序一结束,这些内存中的数据就消失掉了!
特点:针对小文件进行操作!(聊天室项目中使用它进行发送文件)

ByteArrayOutputStream:
public ByteArrayOutputStream():创建默认的缓冲区大小的内存操作输出流(单位是字节)
成员方法:
public byte[] toByteArray()创建一个新分配的 byte 数组(将内存操作输出流对象转换成字节数组)
void reset():重置内存操作输出流

ByteArrayInputStream:内存操作输入流:
public ByteArrayInputStream(byte[] buf):参数数一个字节数组(缓冲数组)

5.演示:

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;

public class ByteArrayStreamDemo {

    public static void main(String[] args) throws IOException {

        //创建内存操作输出流对象
        ByteArrayOutputStream baos = new ByteArrayOutputStream() ;

        //写数据
        for(int x = 0 ; x <5 ; x ++){
            baos.write(("hello"+x).getBytes()) ;
        }

        /**
         * 内存操作流:查看其释放流对象的源码:
         * public void close() throws IOException {}
         * 并没有具体的关闭流对象,所以此流对象可以不关闭
         */
        //关闭资源
//      baos.close() ;


        //将内存操作输出流对象转换成字节数组
        //public byte[] toByteArray()创建一个新分配的 byte 数组(将内存操作输出流对象转换成字节数组)
        byte[] buffer = baos.toByteArray() ;

        //创建内存操作输入流对象
        ByteArrayInputStream bais = new ByteArrayInputStream(buffer) ;

        //一次读取一个字节
        int by = 0 ;
        while((by=bais.read())!=-1){
            System.out.print((char)by);
        }

        //关闭
//      bais.close() ;
    }
}

6.打印流:
字节打印流:PrintStream
字符打印流:PrintWriter
打印流特点:
1)在复制文件的,打印流不能操作数据源,只能操作目的地的数据(只能输出数据)
2)打印流可以有自动刷新的功能
3)打印流可以直接针对文本文件进行操作:
什么情况下是直接可以针对文本文件进行操作?
查API的时候,看流中构造方法,只要构造方法的参数有File类或者是String类型,都可以针对文本文件进行操作
FileInputStream
FileOutputStream
FileReader
FileWriter

7.PrintWriter:
构造方法: public PrintWriter(String fileName)

8.演示:

import java.io.IOException;
import java.io.PrintWriter;

public class PrintWriterDemo {

    public static void main(String[] args) throws IOException {

        //创建字符打印流对象
        PrintWriter pw = new PrintWriter("pw.txt") ;

        //写数据
        pw.write("hello") ;
        pw.write("world") ;
        pw.write("Java") ;

        //刷新
        pw.flush() ;

        //释放资源
        pw.close() ;
    }
}

9.PrintWriter:有自动刷新功能:
public PrintWriter(Writer out,boolean autoFlush)
第二个参数指定为true,则启动自动刷新
PrintWriter pw = new PrintWriter(new FileWriter(“pw.txt”),true) ;
加入自动刷新功能并且在写数据的时候,使用println():换行

println(“hello”)
相当于:
pw.write(“”) ;
pw.newLine() ;
pw.flush() ;

10.演示:

import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;

public class PrintWriterDemo2 {

    public static void main(String[] args) throws IOException {

        //创建字符打印流对象
        PrintWriter pw = new PrintWriter(new FileWriter("pw2.txt"), true) ;//启动自动刷新功能

        //写数据
    /*  pw.write("hello") ;
        pw.write("world") ;
        pw.write("java") ;*/

        //使用println
        //public void println(String x):打印字符串,并且终止该行
        pw.println("hello") ;
        pw.println("world") ;
        pw.println("java") ;

        //关闭流资源
        pw.close() ;


    }
}

11.演示:

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;

/**
 * 复制文件
 * 需求:将当前项目下的DataStreamDemo.java中的内容复制到当前项目下Copy.java文件中
 * 
 * 分析:
 *      数据源:DataStreamDemo.java---->BufferedReader---->读数据--->readLine()
 *      目的地:Copy.java------>BufferedWriter---->写数据
 * @author Apple
 */
public class CopyFileDemo {

    public static void main(String[] args) throws IOException {

        /*//1)封装数据源
        BufferedReader br = new BufferedReader(new FileReader("DataStreamDemo.java")) ;

        //2)封装目的地
        BufferedWriter bw = new BufferedWriter(new FileWriter("Copy.java")) ;

        //使用BufferedReader中的特有功能读写操作
        String line = null ;
        while((line=br.readLine())!=null){
            //写数据
            bw.write(line) ;
            //换行
            bw.newLine() ;
            //刷新流
            bw.flush() ;
        }
        //释放资源
        bw.close() ;
        br.close() ;*/

        //改进
//      1)封装数据源
        BufferedReader br = new BufferedReader(new FileReader("DataStreamDemo.java")) ;

        2)封装目的地
        //创建字符打印流对象,并且启动自动刷新功能
        PrintWriter pw = new PrintWriter(new FileWriter("Copy.java"), true) ;

        //读写操作
        String line = null ;
        while((line=br.readLine())!=null){
            //写数据
            pw.println(line) ;
        }

        //关闭资源
        pw.close() ;
        br.close() ;
    }
}

12.键盘录入的两种方式
1)Scanner类
Scanner sc = new Scanner(System.in) ;
2)使用IO流的形式进行录入数据
BufferedReader br = new BufferedReader(new InputStreamReader(System.in)) ;

13.演示:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

public class SystemInDemo {

    public static void main(String[] args) throws IOException {

        //创建一个字节输入流对象
        /*InputStream is = System.in ; //标准输入流

        //要一次读取一行数据使用BufferedReader的readLine()
        //BufferedReader字符缓冲输入流只能针对字符流进行操作
        //将当前is对象转换字符流,使用字符转换输入流
        InputStreamReader isr = new InputStreamReader(is) ;

        //在创建BufferedReader对象
        BufferedReader br = new BufferedReader(isr) ;*/

        //另一种录入数据的方式
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        //录入数据
        System.out.println("请输入一个字符串:");
        String line = br.readLine() ;
        System.out.println("您录入的字符串是:"+line);

        System.out.println("请输入一个整数数据:");
        String s = br.readLine() ;

        int a = Integer.parseInt(s) ;//long l = Long.pareLong(s) ;
        System.out.println("您输入的整数是:"+a);
    }
}

14.标准输入和输出流
System类中有两个字段:
in:—–>InputStream is = System.in ;
out—–>PrintStream ps = System.out ;
System.out.println();

15.演示:

import java.io.PrintStream;

public class SystemOutDemo {

    public static void main(String[] args) {

        //经常使用输出语句
        System.out.println("helloworld") ;
        System.out.println("helloworld") ;


        //字节打印流
        PrintStream ps = System.out ;

        //字节打印流调用PrintStream类中的方法
//      ps.print() ;这个方法不存在
        ps.println("javaweb") ;//打印一个字符串数据并且终止当前行
        ps.println() ;


    }
}

16.使用BufferedReader完成了录入数据:
使该流封装字符转换输入流,然后使用字符转换输入流封装字节流

17.演示:按照上述方式,将BufferedWriter层层封装标准输出流,将数据打印控制台

import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintStream;

public class SystemOutDemo2 {

    public static void main(String[] args)  {

        /*PrintStream ps = System.out ;

        //写入一个分隔符:newLine()是BufferedWriter类中的方法
//      BufferedWriter bw = new BufferedWriter(ps) ;

        //将字节打印流转换成字符流
        OutputStreamWriter osw = new OutputStreamWriter(ps) ;
        //创建字符缓冲输出流对象
        BufferedWriter bw = new BufferedWriter(osw) ;*/

            BufferedWriter bw = null ;
        try {
            bw = new BufferedWriter(new OutputStreamWriter(
                    System.out));

            //写数据
            bw.write("hello") ;
            bw.newLine() ;

            bw.write("world") ;
            bw.newLine() ;

            bw.write("Javaweb") ;
            bw.newLine()  ;

            //刷新
            bw.flush() ;

        } catch (IOException e) {
            e.printStackTrace();
        }finally{
            //对流对象进行判断
            if( bw!=null){
                try {
                    bw.close() ;
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }



    }
}

18.java.io
RandomAccessFile:随机访问流:此类的实例支持对随机访问文件的读取和写入
不是实际意义上的流,因为它继承自Object类

常用的构造方法:
public RandomAccessFile(String name, String mode)
参数一:指定该文件的路径
参数二:指定的一种模式:常用的模式:”rw”,这种模式是可以读也可以写

19.演示:

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;

public class RandomAccessFileDemo {

    public static void main(String[] args) throws IOException {

//      write();


        read();
    }

    //读数据
    private static void read() throws FileNotFoundException, IOException {
        RandomAccessFile raf = new RandomAccessFile("raf.txt", "rw") ;

        ////读数据
        //public long getFilePointer():返回此文件中的当前偏移量。 文件开头的偏移量(以字节为单位

        byte b = raf.readByte() ;
        System.out.println(b);
        System.out.println("getFilePointer:"+raf.getFilePointer());

        char ch = raf.readChar() ;
        System.out.println(ch);

        String  str  = raf.readUTF() ;
        System.out.println(str);

        //关闭资源
        raf.close() ;
    }

    //写数据
    private static void write() throws FileNotFoundException, IOException {
        //创建随机访问流对象
        RandomAccessFile raf = new RandomAccessFile("raf.txt", "rw") ;

        //写数据
        raf.writeByte(100) ;
        raf.writeChar('a') ;
        raf.writeUTF("中国") ;

        //关闭资源
        raf.close() ;
    }
}

20.SequenceInputStream:合并流(读数据):表示其他输入流的逻辑串联
合并流在复制文件的时候,只能操作数据源,不能操作目的地

之前的操作:
a.txt–>b.txt
c.txt—>d.txt

现在有合并流(将两个文件中的内容复制到另一个文件中)
a.txt+b.txt—>c.txt文件中
构造方法:
public SequenceInputStream(InputStream s1,InputStream s2)

21.演示:

import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.SequenceInputStream;

/**
 * 需求:将DataStreamDemo.java和ByteArrayStreamDemo.java两个java文件中的内容复制到
 * Copy.java文件中
 * 
 * 分析:
 *      1)数据源:DataStreamDemo.java和ByteArrayStreamDemo.java----->SequenceInputStream----->分别读取第一个和第二个java文件
 *      2)目的地:Copy.java----->BufferedOutputStream---->写数据
 */
public class SequenceInputStreamDemo {

    public static void main(String[] args) throws IOException {
        //分别封装这个两个java文件
        InputStream s1 = new FileInputStream("DataStreamDemo.java") ;
        InputStream s2 = new FileInputStream("ByteArrayStreamDemo.java") ;

        //创建合并流对象
        SequenceInputStream sis = new SequenceInputStream(s1, s2) ;

        //封装目的地
        BufferedOutputStream bos = new BufferedOutputStream(
                new FileOutputStream("Copy.java"));

        //一次一个字节数组
        byte[] bys = new byte[1024] ;
        int len = 0 ;
        while((len = sis.read(bys))!=-1){
            //写数据
            bos.write(bys, 0, len) ;
            bos.flush() ;
        }

//      释放资源
        sis.close() ;
        bos.close() ;
    }
}

22.SequenceInputStream的另一种构造方法:复制多个文件
public SequenceInputStream(Enumeration


import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.SequenceInputStream;
import java.util.Enumeration;
import java.util.Vector;


/**
 * 需求:将DataStreamDemo.java,ByteArrayStreamDemo.java,以及CopyFileDemo.java将这三个文件中的内容复制到当前项目下:Copy.java文件中
 * 
 * 分析:数据源:DataStreamDemo.java,ByteArrayStreamDemo.java,以及CopyFileDemo.java--->SequenceInputStream--->读数据
 *     目的地:Copy.java----->BufferedOutStream---->写数据
 */
public class SequenceInputStreamDemo2 {

    public static void main(String[] args) throws IOException {

        //创建一个Vector集合,泛型数据类型<InputStream>
        Vector<InputStream> v = new Vector<InputStream>() ;

        //封装者三个java文件
        InputStream s1 = new FileInputStream("DataStreamDemo.java") ;
        InputStream s2 = new FileInputStream("ByteArrayStreamDemo.java") ;
        InputStream s3 = new FileInputStream("CopyFileDemo.java") ;

        //添加到集合中
        v.add(s1) ;
        v.add(s2) ;
        v.add(s3) ;

        //调用特有功能:
        //public Enumeration<E> elements()
        Enumeration<InputStream> en = v.elements() ;
        //创建合并刘对象
        SequenceInputStream sis = new SequenceInputStream(en) ;
        //封装目的地
        BufferedOutputStream bos = new BufferedOutputStream(
                new FileOutputStream("Copy.java"));

        //一次读取一个字节数组
        byte[] bys = new byte[1024] ;
        int len = 0 ;
        while((len=sis.read(bys))!=-1){
            //写数据
            bos.write(bys, 0, len) ;
            bos.flush() ;
        }

        //释放资源
        bos.close() ;
        sis.close() ;
    }
}

24.序列化流:将对象像流的方式或者网络传输中的数据写数据.对象—->流数据:ObjectOutputStream
反序列化:将流数据或者网络传输中的流数据读取出来.流数据—->还原成对象:ObjectInputStream

ObjectOutputStream中的成员方法:
public final void writeObject(Object obj):将obj对象写入到当前的序列化流中

ObjectInputStream中的成员方法:
public final Object readObject():从当前反序列化流中读取一个对象

25.报异常了:NotSerializableException(没有实现序列化接口的异常) org.westos_07.Person

Serializeable:序列化接口
类通过实现 java.io.Serializable 接口以启用其序列化功能。未实现此接口的类将无法使其任何状态序列化或反序列化,如果自定义一个类没有实现这个接口,就不能使用
序列化或者是反序列化!如果一个接口中没有字段,没有构造方法,没有成员方法,叫做标记接口

更改了Pereson类中的一个成员变量,重新去反序列化:出现异常:
java.io.InvalidClassException
org.westos_07.Person; local class incompatible:
stream classdesc serialVersionUID = -1389484787812115752,
local class serialVersionUID = -8105095612190351088

如果一个类实现了标记接口:Serializable,那么对应的该类加载的时候,会产生一个ID
Person.class——->id ,假设id=100
成员变量:name和age—->对应一个固定id= 100

后面重新改动了Person类的成员,Person.class —->id = 200 ;
name,age——–>id = 200 ;

26.序列化和反序列化版本号不一致,会出现InvalidClassException
解决这个异常两种方案:
1)要么改动当前某个类中的数据之后,然后重新序列化和反序列化
这种做法不符实实际要求,在实际开发中,可能直接读数据:将流数据—>还原成对象
2)发现黄色警告线,点击它:生成一个固定ID

注意事项:
比如一个类中有很多成员变量,并不想让一些成员变量被序列化,Java提供了一个关键字:
transient:不用被序列化的时候用它修饰

27.序列化和反序列化考点:
1)将对象—>流数据或者流数据—>对象,该对象所在的类要实现一个标记接口:serializable 多线程有一个关键字:同步机制(synchronized)
2)序列化和反序列化生产的版本Id不一致的时候,会出现异常,所以使用生产随机ID或者固定ID解决
3)transient:修饰的变量不会被序列化…

28.演示:

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class ObjectStreamDemo {

    public static void main(String[] args) throws Exception {

//      write() ;
        read();
    }

    //读数据
    private static void read() throws IOException, FileNotFoundException,
            ClassNotFoundException {
        //反序列化:将当前流数据--->对象
        //创建一个反序列化流对象
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(
                "oos.txt"));

        //使用反序列化:将流数据--->对象
        Object obj = ois.readObject() ;

        //关闭资源
        ois.close() ;
        System.out.println(obj);
    }

    //序列化
    private static void write() throws IOException {


        //创建一个对象
        Person p = new Person("高圆圆", 27) ;
        //创建一个序列化流对象
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(
                "oos.txt"));

        //写数据
//      writeObject(Object obj)
        oos.writeObject(p) ;

        //释放资源
        oos.close() ;
    }
}
import java.io.Serializable;

//如果启用序列化功能,那么必须实现一个接口:Serializable
public class Person implements Serializable {

    private static final long serialVersionUID = 1L;
    /**
     * default Servial ID
     */
    //生成一个ID
    private String name ;
//  private int age ;
//  int age ;
    public transient int age ;
//  public int age ;
    public Person() {
        super();
    }

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person [name=" + name + ", age=" + age + "]";
    }   


}

29.Properties:属性集合类,该类继承自Hashtable

import java.util.Properties;
import java.util.Set;

public class PropertiesDemo {

    public static void main(String[] args) {

        //创建属性集合类对象
//      Properties<String,String> prop = new Properties<String,String>() ;
        Properties prop = new Properties() ;

        //既然是Map集合怎么添加元素?
        prop.put("张三", "30") ;
        prop.put("李四", "25") ;
        prop.put("王五", "40") ;
        prop.put("赵六", "35") ;

        //遍历集合
        //获取所有的键的集合
        Set<Object> keySet = prop.keySet() ;
        //增强for遍历
        for(Object key :keySet){
            Object value = prop.get(key) ;
            System.out.println(key+"="+value);
        }
    }
}

31.属性集合类:Properties有自己的遍历和添加元素的功能
给属性列表中添加元素:
public Object setProperty(String key,String value)
public Set stringPropertyNames():获取当前属性列表中所有的键的集合,键值都是String类型
public String getProperty(String key)用指定的键在此属性列表中搜索属性值

32.演示:

import java.util.Properties;
import java.util.Set;

public class PropertiesDemo2 {

    public static void main(String[] args) {

        //创建属性集合类对象
        Properties prop = new Properties() ;

        //添加元素
        //public Object setProperty(String key,String value)
        prop.setProperty("张三", "30") ;
        prop.setProperty("李四", "40") ;
        prop.setProperty("王五", "50") ;
        prop.setProperty("赵六", "60") ;

        //遍历属性列表:public Set<String> stringPropertyNames()
        Set<String> keySet = prop.stringPropertyNames() ;
        for(String key:keySet){
            //通过键搜索值
            //public String getProperty(String key)
            String value = prop.getProperty(key) ;
            System.out.println(key+"="+value);
        }

    }
}

33.Properties 可保存在流中或从流中加载。
将文件中的数据加载到属性集合中:public void load(Reader reader)
将属性集合中的数据保存到文件中:public void store(Writer writer,String comments)
第二个参数:comments:对当前属性列表 的描述

34.演示:


import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Properties;

/**
 * 举例:玩单机游戏
 *      保存进度和进度的加载
 *      "吕布"="方天画戟"
 */
public class PropertiesDemo {

    public static void main(String[] args) throws IOException {

//      myStore();
        myLoad() ;
    }

    //将文件中的数据加载到属性集合中
    private static void myLoad() throws IOException {
        //public void load(Reader reader)
        //创建属性集合类对象
        Properties prop = new Properties() ;

        //创建字符输入流对象
        FileReader fr = new FileReader("name.txt") ;
        //调用方法
        prop.load(fr) ;


        //释放资源
        fr.close() ;

        //显示输出
        System.out.println(prop);
    }

    private static void myStore() throws IOException {
        //创建属性集合类对象
        Properties prop = new Properties();

        //给属性集合中添加数据
        prop.setProperty("张三", "30") ;
        prop.setProperty("李四", "40") ;
        prop.setProperty("王五", "50") ;

        //将属性列表中的数据保存到文件中
//      public void store(Writer writer,String comments)
        //创建字符输出流
        FileWriter fw = new FileWriter("name.txt") ;
        //调用方法
        prop.store(fw, "names content") ;

        //释放资源
        fw.close() ;
    }
}

35.演示:


import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
import java.util.Properties;
import java.util.Set;

/**
 * 我有一个文本文件(user.txt),我知道数据是键值对形式的,但是不知道内容是什么。
    请写一个程序判断是否有“lisi”这样的键存在,如果有就改变其值为”100”

    分析:
        1)将user.txt文件中的内容加载到属性集合类中
        2)遍历属性列表:获取所有的键的集合:stringPropertyNames(String key );
                判断:如果"lisi"这个键中key值相等
                    修改:setProperty(key,"100") ;

        3)重新将属性集合中的数据写到user.txt中
 */
public class PropertiesTest {

    public static void main(String[] args) throws IOException {
        //创建属性集合类对象
        Properties prop = new Properties() ;

        //创建字符输入流对象
        Reader r = new FileReader("user.txt") ;
        //加载到属性集合中
        prop.load(r) ;

        //关闭流资源
        r.close() ;

        //遍历属性列表
        Set<String> keySet = prop.stringPropertyNames() ;
        for(String key:keySet){
            //判断
            if("lisi".equals(key)){
                //修改
                prop.setProperty(key, "100") ;
            }
        }

        //创建字符输出流丢向
        Writer w = new FileWriter("user.txt") ;
        //调用功能
        prop.store(w, "content") ;

        //释放资源
        w.close() ;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值