黑马程序员_Java基础_IO(1)

------- android培训java培训、期待与您交流! ----------

 

字符流两个基类:
Reader Writer
Reader 用于读取字符流的抽象类。子类必须实现的方法只有 read(char[], int, int) 和 close()。多数子类将重写此处定义的一些方法,以提供更高的效率和/或其他功能。 
要想打开文件读取字符,你得先用String或File对象创建一个FileInputReader。
为了提高速度,你应该对这个文件作缓冲,因此你得把FileInputReader的reference交给BufferedReader。
由于BufferedReader也提供了readLine( )方法,因此它就成为你最终要使用的那个对象,而它的接口也成为你使用的接口了。
当你读到了文件的末尾时,readLine( )会返回一个null,于是就退出while循环了。
   
常见子类: BufferedReader, InputStreamReader, LineNumberReader, FileReader
Reader  常用方法:
 
read()  读取单个字符
read(char[] cbuf)   将字符读入数组
read(char[] cbuf, int off, int len) 将字符读入数组的某一部分
关闭:
 
close()   关闭该流并释放与之关联的所有资源


 
  Writer 写入字符流的抽象类。子类必须实现的方法仅有 write(char[], int, int)、flush() 和 close()。多数子类将重写此处定义的一些方法,以提供更高的效率和/或其他功能。
常见子类:BufferedWriter,InputStreamWriter, PrintWriter,FileWriter
Writer 常用方法:
write(char[] cbuf) 写入字符数组
write(char[] cbuf, int off, int len)   写入字符数组的某一部分
write(int c)  写入单个字符
write(String str)  写入字符串
write(String str, int off, int len) 写入字符串的某一部分
append(char c) 将指定字符添加到此 writer
flush()   刷新该流的缓冲
close() 关闭此流,但要先刷新它

字节流两个基类:
InputStream OutputStream
 
InputStream   表示字节输入流的所有类的超类, 作用是标志那些从不同数据起源产生输入的类  
常见子类: ByteArrayInputStream , FileInputStream ,   PipedInputStream , SequenceInputStream
 常用方法:
int read()  从输入流中读取数据的下一个字节  
read(byte[] b)  从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b
read(byte[] b, int off, int len)  将输入流中最多 len 个数据字节读入 byte 数组

OutputStream 表示输出字节流的所有类的超类, 作用是将内存中的流文件输出到别的设备或者硬盘显示器等
常见子类: ByteArrayOutputStreamFileOutputStream , PipedOutputStream  , ObjectOutputStream  
 常用方法:
write(byte[] b)  将 b.length 个字节从指定的 byte 数组写入此输出流  
write(byte[] b, int off, int len)  将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此输出流
flush()  刷新此输出流并强制写出所有缓冲的输出字节
close()  关闭此输出流并释放与此流有关的所有系统资源
 
既然IO流是用于操作数据

那么数据最常见提现形式是:文件

FileReader类
   第一种读取方式:通过一个字符进行读取
例子1: 

public class FileReaderDemo {
    public static void main(String[] args) {
        FileReader fr = null;
        try {
            //创建一个文件读取流对象,和指定名称的文件相关联
            //要暴走该文件是已经存在的,如果不存在,会发生异常FileNotFoundException
            fr = new FileReader("demo.txt");
            int ch=0;
            
            //调用地区流对象的read方法
            //read();一次读取一个字符,而且会自动往下读
            //读取单个字符
            while((ch=fr.read()) != -1){
                System.out.println((char)ch);
            }
        } catch (IOException e) {
            System.out.println("读取文件出错!");
        } finally {
            try {
                if (fr != null)
                    fr.close();
            } catch (IOException e) {
                System.out.println("流文件关闭异常:" + e.toString());
            }
        }
    }
}

第二种读取方式:通过一个字符数组进行读取
 例子2:

public class FileReaderDemo2 {
    public static void main(String[] args) {
        FileReader fr = null;
        try {
            fr = new FileReader("demo.txt");
            
            //定义一个字符数组,用于存储读取字符
            //该read[char[]]返回的是读到字符个数
            char[] buf = new char[1024];
            int num = 0;
            while((num=fr.read(buf)) != -1){
                System.out.println(new String(buf,0,num));
            }
        } catch (IOException e) {
            System.out.println("读取文件出错!");
        } finally {
            try {
                if (fr != null)
                    fr.close();
            } catch (IOException e) {
                System.out.println("流文件关闭异常:" + e.toString());
            }
        }
    }
}

 练习:  读取一个.java文件,打印在控制台上

例子3: 


<strong>public class FileReaderTest {
    public static void main(String[] args) {
        FileReader fr = null;
        try {
            fr = new FileReader(
                    "D:\\JavaWeb\\workroomHeima\\Heima\\src\\day08_IO\\FileReaderDemo2.java");
            char[] buf = new char[1024];
            int num = 0;
            while ((num = fr.read(buf)) != -1) {
                System.out.print(new String(buf, 0, num));
            }
        } catch (IOException e) {
            System.out.println("文件读取异常:" + e.toString());
        } finally {
            try {
                if (fr != null)
                    fr.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
</strong>
需求:在硬盘上,创建一个文件并写入一些文字数据;

找到一个专门用于操作文件的Writer子类对象,FileWriter。
后缀名是父类名。前缀名是该流对象的功能。
 

 例子4: 
 

public class FileWriterDemo {
    public static void main(String[] args) throws IOException {
        
        //创建一个FileWriter对象,该对象一被初始化就必须要明确被操作的文件。
        //而且该文件会被创建到指定目录下。如果该目录下已有同名的文件,将被覆盖
        //其实该步就是在明确数据要存放的目的地
        FileWriter fw = new FileWriter("c:\\demo.txt");
        
        //调用write方法,将字符串写入到流中
        fw.write("时间让我们都回不了过去!");
        
        //刷新流对象中的缓冲数据
        //将数据刷到目的地中
        fw.flush();
        
        fw.write("怎么伤心怎么放弃,我都浑然不知!");
        fw.flush();
        
        fw.write("我们重新开始吧");
        
        //关闭刘资源,但是关闭之前会刷新一次内部的缓冲数据
        //将数据刷到目的地中
        //和flush区别:flush刷新后,流可以继续使用,close刷新后,会将流关闭
        fw.close();
    }
}


 IO异常抛出方式:

 一定要保证代码的健壮性 

例子5:


<strong>public class FileWriterDemo2 {
    public static void main(String[] args) {
        Writer fw = null;
        try {
            fw = new FileWriter("demo02.txt");
            fw.write("demo02");
        } catch (IOException e) {
            System.out.println("创建或写入异常:" + e.toString());
        } finally {
            try {
                if(fw != null)    //要保证代码健壮性
                    fw.close();
            } catch (IOException e) {
                System.out.println("关闭异常:" + e.toString());
            }
        }
    }
}</strong>





演示:对已有文件的数据续写






例子6:  

public class FileWriterDemo3 {
    public static void main(String[] args) {
        Writer fw = null;
        try {
            //传递一个true参数,代表不覆盖已有的文件,并在已有文件的末尾进行数据续写
            fw = new FileWriter("demo02.txt",true);   //对已有文件的数据续写,第二个参数为true
            fw.write("\r\ndemo03");   //\r\n表示换行
        } catch (IOException e) {
            System.out.println("创建或写入异常:" + e.toString());
        } finally {
            try {
                if (fw != null) // 要保证代码健壮性
                    fw.close();
            } catch (IOException e) {
                System.out.println("关闭异常:" + e.toString());
            }
        }
    }
}
  BufferedReader 字符读取流缓冲区:

 该缓冲区提供了一个一次读取一行的方法 readLine(),方便对文本数据的获取
 当返回null时,表示读取到文本末尾
例子7:  

public class BufferedReaderDemo {
    public static void main(String[] args) {
        FileReader fr = null;
        BufferedReader bufr = null;
        try {
            // 创建一个读取流对象和文件关联
            fr = new FileReader("BufferedWriterDemo.txt");
            // 为了提供效率。加入缓冲区技术,将字符读取流对象作为参数传递个缓冲区对象的构造函数
            bufr = new BufferedReader(fr);
            String context = null;
            while ((context = bufr.readLine()) != null) {
                System.out.println(context);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                bufr.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

  BufferedWriter 缓冲区的出现是为了提高流的操作效率而出现的
 所以在创建缓冲区之前,必须要先有流对象
 该缓冲区提供了一个跨平台的换行符号
 newLine();
  例子8:

public class BufferedWriterDemo {
    public static void main(String[] args) {
        FileWriter fw = null;
        BufferedWriter bufw = null;
        try {
            // 创建一个字符写入流对象
            fw = new FileWriter("BufferedWriterDemo.txt");
            // 为了提供字符写入流效率,加入缓冲技术
            // 只要将需要被提高效率的流对象作为参数传递给缓冲区的构造函数即可
            bufw = new BufferedWriter(fw);
            for (int i = 0; i <= 5; i++) {
                bufw.write("abcde" + i);
                // 换行,对不同的平台都有作用,使用\r\n是有平台限制的,linux和window平台是不一样过的,使用newLine()方法能解决平台的问题
                bufw.newLine();
                
                //记住,只要用到缓冲区,就要记得刷新
                bufw.flush();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (bufw != null)   //保证程序健壮性
                    bufw.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

需求:自定义一个和BufferedReader类中特有的方法readLine,模拟一下BufferedReader
  例子9:  
 
class MyBufferedReader extends Reader{
    private Reader r;
    public MyBufferedReader(Reader r) {
        this.r = r;
    }
    //可以一次读取一行数据的方法
    public String myReadLined() throws IOException {
        
        //定义一个临时容器,原BufferedReader封装的是字符数组
        //我们定义StringBuilder来作为容器,因为最终还是要将数据变成字符串
        StringBuilder sb = new StringBuilder();
        int num = 0;
        while ((num = r.read()) != -1) {
            if (num == '\r')
                continue;
            if (num == '\n')
                return sb.toString();
            else
                sb.append((char) num);
        }
        if (sb.length() != 0)
            return sb.toString();
        return null;
    }
    public void myClose() throws IOException {
        r.close();
    }
    //覆盖父类中的抽象方法
    @Override
    public int read(char[] cbuf, int off, int len) throws IOException {
        return r.read(cbuf, off, len);
    }
    @Override
    public void close() throws IOException {
        r.close();
    }
}
public class MyBufferedReaderDemo {
    public static void main(String[] args) {
        MyBufferedReader reader = null;
        
        try {
            reader = new MyBufferedReader(new FileReader("BufferedWriterDemo.txt"));
            String tepm = null;
            while((tepm = reader.myReadLined()) != null){
                System.out.println(tepm);
            }
        }catch (IOException e) {
            System.out.println("读取文件错误");
        }
        finally{
            try {
                reader.myClose();
            } catch (IOException e) {
                System.out.println("关闭文件错误");
            }
        }
    }
}


LineNumberReader类是BufferedReader的子类 
跟踪行号的缓冲字符输入流。
此类定义了方法   setLineNumber(int)  和   getLineNumber(),它们可分别用于设置和获取当前行号。
 其主要方法有:
 setLineNumber(int)    设置行号
 getLineNumber()      获取行号

 例子10: 

public class LineNumberReaderDemo {
    public static void main(String[] args) {
        LineNumberReader lnr = null;
        try {
            lnr = new LineNumberReader(new FileReader("BufferedWriterDemo.txt"));
            lnr.setLineNumber(100); // 设置行号开始的位置
            String str = null;
            while ((str = lnr.readLine()) != null) {
                // getLineNumber()方法是获取行号的
                System.out.println(lnr.getLineNumber() + ":" + str);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (lnr != null)
                try {
                    lnr.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
        }
    }
}


需求:
 将一个文本文件拷贝到另外一个文件夹下
 
 复制的原理:
 其实就是将原来的文件数据存储到另一个文件中

 例子11:  

public class CopyTest {
    public static void main(String[] args) {
        FileReader fr = null;
        FileWriter fw = null;
        try {
            fr = new FileReader("D:\\JavaWeb\\workroomHeima\\Heima\\src\\day08_IO\\FileReaderDemo2.java");
            fw = new FileWriter("FileReaderDemo2_Copt.txt");  //默认在工作目录下
            char[] buf = new char[1024];
            int num = 0;
            while ((num = fr.read(buf)) != -1) {
                fw.write(buf,0,num);
            }
        } catch (IOException e) {
            System.out.println("文件读取或写入异常:" + e.toString());
        } finally {
            try {
                if (fr != null)
                    fr.close();
                if (fw != null)
                    fw.close();
            } catch (IOException e) {
                System.out.println("关闭异常:" + e.toString());
            }
        }
    }
}

需求: 通过缓冲区复制一个.java文件    
  例子12:  

public class CopyTextByBuf {
    public static void main(String[] args) {
        BufferedReader br = null;
        BufferedWriter bw = null;
        try {
            br = new BufferedReader(
                    new FileReader(
                            "D:\\JavaWeb\\workroomHeima\\Heima\\src\\day08_IO\\FileReaderDemo2.java"));
            bw = new BufferedWriter(new FileWriter(
                    "FileReaderDemo_copyByBuf.txt"));
            String str = null;
            while ((str = br.readLine()) != null) {
                bw.write(str);
                bw.newLine(); // 换行
                bw.flush();
            }
        } catch (IOException e) {
            System.out.println("读写失败" + e.toString());
        } finally {
            if (br != null)
                try {
                    br.close();
                } catch (IOException e) {
                    System.out.println("关闭读缓冲失败");
                }
            if (bw != null)
                try {
                    bw.close();
                } catch (IOException e) {
                    System.out.println("关闭写缓冲失败");
                }
        }
    }
}
 

装饰设计模式:

 当想要对已有的对象进行功能增强是时
 可以定义类,将已有对象传入, 基于已有的功能,并提供加强功能
 那么自定义的该类称之为装饰类

 装饰类通常会通过构造方法接受被装饰对象
 并基于被装饰的对象的功能,提供更强的功能
 
 ****************************
  装饰模式比继承要灵活,避免了继承体系臃肿
 而且降低了类与类之间的关系
 
 装饰类因为增强已有对象,具备的功能和已有的是相同的
 所以装饰类和被装饰类通常都属于一个体系中。

例子13:

class Person {
    public void chifan() {
        System.out.println("吃饭");
    }
}
class SuperPerson {
    private Person p;
    public SuperPerson(Person p) {
        this.p = p;
    }
    public void superChifan(){
        System.out.println("开胃酒");
        p.chifan();
        System.out.println("甜点");
        System.out.println("来一根烟");
    }
}
public class PersonDemo {
    public static void main(String[] args) {
        Person  p = new Person();
        SuperPerson sp = new SuperPerson(p);
        sp.superChifan();
    }
}

用于操作字节数组的流对象


ByteArrayInputStream : 在构造方法的时候,需要接受数据源,而且数据源是一个字节数组

ByteArrayOutputStream : 在构造的时候,不用定义数据目的,因为该对象中已经内部封装了可变长度的字节数组

在流操作规律讲解时:

源设备:
键盘 System.in 硬盘 FileStream 内存:ArrayStream

目的设备:
键盘 System.in 硬盘 FileStream 内存:ArrayStream
例子14:  
public class ByteArrayStreamDemo {



    public static void main(String[] args) {


        //源


        ByteArrayInputStream in = new ByteArrayInputStream("abcdef".getBytes());


        


        //目的


        ByteArrayOutputStream out  = new ByteArrayOutputStream();


        int len = 0 ;


        while((len = in.read()) != -1){


            out.write(len);


        }


        


        System.out.println(out.size());


        System.out.println(out.toString());


    }



}

  DataInputStream与DataOutputStream
 
 数据输入流允许应用程序以与机器无关方式从底层输入流中读取基本 Java 数据类型。
应用程序可以使用数据输出流写入稍后由数据输入流读取的数据
DataInputStream 对于多线程访问不一定是安全的  
 
 可以用于 操作基本数据类型的数据的流对象  
 例子15:
public class DataStreamDemo {


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


        // writeData();


//      readData();


//      writeUTFData();


        readUTFData();



    }



    public static void readUTFData() throws IOException {


        DataInputStream in = new DataInputStream(new FileInputStream(


                "dataStream1.txt"));


        System.out.println(in.readUTF());


        in.close();



    }



    public static void writeUTFData() throws IOException {


        DataOutputStream out = new DataOutputStream(new FileOutputStream(


                "dataStream1.txt"));


        out.writeUTF("你好  我的名字叫");


        out.close();


    }



    public static void readData() throws IOException {


        DataInputStream in = new DataInputStream(new FileInputStream(


                "dataStream.txt"));


        System.out.println(in.readInt());


        System.out.println(in.readBoolean());


        System.out.println(in.readDouble());


        in.close();


    }



    public static void writeData() throws IOException {


        DataOutputStream out = new DataOutputStream(new FileOutputStream(


                "dataStream.txt"));


        out.writeInt(122);


        out.writeBoolean(true);


        out.writeDouble(12.56);


        out.close();


    }



}

序列化 就是一种用来处理对象流的机制,所谓对象流也就是将对象的内容进行流化。
 可以对流化后的对象进行读写操作,也可将流化后的对象传输于网络之间。
 序列化是为了解决在对对象流进行读写操作时所引发的问题。
 
 序列化的实现:将需要被序列化的类实现Serializable接口
 该接口没有需要实现的方法,implements Serializable只是为了标注该对象是可被序列化的,
 然后使用一个输出流(如:FileOutputStream)来构造一个ObjectOutputStream(对象流)对象,
 接着,使用ObjectOutputStream对象的writeObject(Object obj)方法就可以将参数为obj的对象写出(即保存其状态)
 ,要恢复的话则用输入流。

对象的序列化 
 
例子16: 
public class ObjectStreamDemo {


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


        // writeObject();


        readObject();


    }


    public static void readObject() throws Exception {


        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(


                "person.object"));


        Person p1 = (Person) ois.readObject();


        System.out.println(p1.toString());


        Person p2 = (Person) ois.readObject();


        System.out.println(p2.toString());


    }



    public static void writeObject() throws Exception {


        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(


                "person.object"));


        oos.writeObject(new Person("zhangshan", 12));


        oos.writeObject(new Person("xiaoming", 22));


        oos.close();


    }



}



class Person implements Serializable {



    // public static final long serialVersionUID = 42L;


    private String name;


    int age;



    Person(String name, int age) {


        this.name = name;


        this.age = age;


    }



    public String toString() {


        return name + ":" + age;


    }


}

管道流技术(使用到线程)----------》 PipedInputStream   和   PipedOutputStream

管道输入流应该连接到管道输出流;管道输入流提供要写入管道输出流的所有数据字节  
不建议对这两个对象尝试使用单个线程,因为这样可能死锁线程
如果向连接管道输出流提供数据字节的线程不再存在
 

 需求:两个线程,一个读一个写

例子17:
 
class Read implements Runnable {


    private PipedInputStream pis;



    Read(PipedInputStream pis) {


        this.pis = pis;


    }



    public void run() {


        try {


            byte[] bt = new byte[1024];


            System.out.println("读取前。。。要是没有数据就会堵塞");


            int len = pis.read(bt);


            System.out.println(new String(bt,0,len));


            System.out.println("读取结束。。");


        } catch (Exception e) {


            throw new RuntimeException("管道读取流失败");


        }


    }


}



class Write implements Runnable {


    private PipedOutputStream pos;



    Write(PipedOutputStream pos) {


        this.pos = pos;


    }



    public void run() {


        try {


            System.out.println("开始写入");


            Thread.sleep(6000);


            pos.write("piped come to ".getBytes());


            pos.close();


        } catch (Exception e) {


            throw new RuntimeException("管道输出失败");


        }


    }


}



// 管道流  使用到多线程技术


public class PipedStreamDemo {



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


        PipedOutputStream out = new PipedOutputStream();


        PipedInputStream in = new PipedInputStream();


        in.connect(out);


        Read r = new Read(in);


        Write w = new Write(out);


        new Thread(r).start();


        new Thread(w).start();


        


    }



}

  例子18 : (联通的Bug
<strong>public class EncodeDemo2 {

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

        // 在记事本输入“联通” ,有时会出现乱码现象,那是因为记事本在读取时使用utf-8 但是我们的系统默认是GBK

        String s = "联通";

        byte[] bt = s.getBytes("GBK");

        for (byte b : bt) {

            System.out.println(Integer.toBinaryString(b & 255));

        }

    }

}</strong>

练习:有五个学生,每个学生有3们成绩
从键盘输入以上数据(包括姓名,三门课程成绩)
输入的格式: 如:   zhangsan,30,50,80计算出总成绩
并把学生的信息和计算出的总分数高低存放在磁盘文件“studentInof.txt”中

1.描述学学生对象
2.定义一个可操作学生对象的工具类

思想:
1.通过获取键盘录入一行数据,并将该行中的信息取出封装到学生对象中,
2.因为学生很多,那么就需要存储,使用到集合,因为要对学生的总分数进行排序 ,所以可以使用TreeSet
3.将集合的信息写入到一个文件中

例子19: 
public class StudentInfoTest {

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

//      Comparator<Student> cmp = Collections.reverseOrder();比较器排列成绩

//      Set<Student> set = StudentInfoUtil.getStudents(cmp);  

        Set<Student> set = StudentInfoUtil.getStudents();  

        StudentInfoUtil.writetoFile(set);

    }

}

class StudentInfoUtil {

    public static Set<Student> getStudents() throws IOException {

        return getStudents(null);

    }

    public static Set<Student> getStudents(Comparator<Student> cmp)

            throws IOException {

        BufferedReader buf = new BufferedReader(

                new InputStreamReader(System.in));

        String line = null;

        Student stu = null;

        Set<Student> set = null;

        if (cmp != null)

            set = new TreeSet<Student>(cmp);

        else

            set = new TreeSet<Student>();

        while ((line = buf.readLine()) != null) {

            if ("over".equals(line)) {

                break;

            }

            String[] arr = line.split(",");

            stu = new Student(arr[0], Integer.parseInt(arr[1]),

                    Integer.parseInt(arr[2]), Integer.parseInt(arr[3]));

            // System.out.println(student.toString());

            set.add(stu);

        }

        System.out.println(set.size());

        buf.close();

        return set;

    }

    public static void writetoFile(Set<Student> set) throws IOException {

        BufferedWriter out = new BufferedWriter(new FileWriter(

                "studentInfo.txt"));

        System.out.println(set);

        for (Student s : set) {

            out.write(s.toString() + "\t");

            out.write(s.getSum() + "");

            out.newLine();

            out.flush();

        }

        out.close();

    }

}

class Student implements Comparable<Student> {

    private String name;

    private int ma, ch, en;

    private int sum;

    Student(String name, int ma, int ch, int en) {

        this.name = name;

        this.ma = ma;

        this.ch = ch;

        this.en = en;

        this.sum = ma + ch + en;

    }

    public int getSum() {

        return this.sum;

    }

    @Override

    public int compareTo(Student o) {

        int num = new Integer(this.sum).compareTo(o.sum);

        if (num == 0)

            return this.name.compareTo(o.name);

        return num;

    }

    @Override

    public int hashCode() {

        return name.hashCode() + sum * 87;

    }

    @Override

    public boolean equals(Object obj) {

        if (!(obj instanceof Student))

            throw new ClassCastException("对象错误");

        Student s = (Student) obj;

        return this.name.equals(s.name) && this.sum == s.sum;

    }

    @Override

    public String toString() {

        return "Student [name=" + name + ", ma=" + ma + ", ch=" + ch + ", en="

                + en + "]";

    }

}




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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值