五、高级编程


[知识要点]:掌握异常处理;多线程机制;Java IO与文件处理;图形界面;用Java实现底层网络通讯。

一、掌握:Java的Swing GUI组件、容器、布局管理器的概念;图形界面上的事件响应

转载:这篇博客讲的很详细

二、掌握:Java 输入输出与文件处理

一、流的概念
流是指计算机各部件之间的数据流动。按照数据的传输方法,流可以分为输入流与输出流。从流的内容上划分,流分为字节流和字符流。

输入输出流
在Java语言中,把不同类型的输入输出源(键盘,屏幕等)抽象为流,其中输入或输出的数据被称为数据流。数据流分为输入流和输出流,从外设或外存传递到应用程序的流称为输入流,将数据从应用程序传递到外设或外存的流称为输出流。输入流只可以从其中读取数据,输出流只可以向其中写入数据。
缓冲流
对数据流的每次操作若都是以字节为单位进行操作,那么数据的传输效率显然很低,所以为了提高数据的传输效率,通常使用缓冲流(buffered stream),即为一个流配有一个缓冲区(buffer),这个缓冲区就是专门用于传送数据的一块内存。
当向一个缓冲流写入数据时,系统将数据发送到缓冲区,而不是直接发送到外部设备。缓冲区自动记录数据,当缓冲区满时,系统会将数据全部发送到相应的外部设备。
当从一个缓冲流读取数据时,系统实际是从缓冲区中读取数据,当缓冲区空时,系统就会从相关外部设备自动读取数据,并读取尽可能多的数据填满缓冲区,由此可见,缓冲区提高了内存与外部设备之间的数据传输效率。
二、输入输出流类库
为了方便流的处理,Java语言的流类都封装在java.io包之中,所以要是用流类,必须导入java.io包。
按照处理的数据单位分为字节流和字符流。

字节流:操作的数据单元是8位字节,InputStream、OutputStream作为抽象基类。
字符流:操作的数据单元是字符,Writer、Reader作为抽象基类。
字节流可以处理所有的数据文件,但是若处理纯文本数据,建议使用字符流。
三、使用InputStream和OutputStream流类
InputStream和OutputStream流类是Java语言中用来处理以位(bit)为单位的流,它除了可以用来处理二进制文件(binary file)的数据以外,也可以用来处理文本文件。
**注意:虽然说字节流可以操作文本文件,但是并不提倡这样做,因为字节流操作文本文件,若是文件中有汉字,可能会出现乱码,这是因为字节流不能够直接操作Unicode字符所导致,因此Java语言不提倡使用字节流读取文本文件,而是建议字符流操作文本文件。

InputStream流类
常用方法:

方法 功能
public int read() 从输入流中的当前位置读入一个字节的二进制数据,然后以此数据位地位字节,配上8个全0的高位字节合成一个16位的整型量(0-255)返回给调用此方法的语句,若输入流中的当前位置没有数据,则返回-1。
public int read(byte[] b) 从输入流中的当前位置连续读入多个字节保存在数组b中,同时返回所读到的字节数。
public int read(byte[] b, int off, int len) 从输入流中的当前位置连续读入len个字节,从数组b的第off+1个元素位置处开始存放,同时返回所读到的字节数。
public int available() 返回输入流中可以读取的字节数
public long skip(long n) 使位置指针从当前位置向后跳过n个字节
public void mark(int readlimit) 在当前位置处做一个标记,并且在输入流中读取readlimit个字节数后该标记失效
public void reset() 将位置指针返回到标记的位置
public void close() 关闭输入流与外设的链接并且释放所占用的系统资源

当Java程序需要从外设如键盘、磁盘文件等读入数据时,应该创建一个适当类型的输入流对象来完成与该设备的连接,由于InputStream是抽象类,所以程序中创建的输入流对象一般是InputStream某个子类的对象,通过调用该对象继承的read()方法就可以实现相应设备的输入操作。
流中的方法都声明抛出异常,所以程序中调用流方法时必须处理异常,否则编译不能通过,由于所有的I/O流都实现了AutoCloseable接口,而只是实现该接口的资源才可以使用try-with-resources语句自动关闭打开的资源。因此,在使用I/O流进行输入输出操作的时候,可以使用try-with-resources语句处理异常。

OutputStream流类
OutputStream流类中包含一套所有字节输出都需要的方法,可以完成最基本的向输出流写入数据的功能。

方法 功能
public void write(int b) 将参数b的低位字节写入到输出流
public void write(byte[] b) 将字节数组b的全部字节按顺序写入到输出流
public void write(byte[] b, int off, int len) 将字节数组b中的第off+1个元素开始的len个数据,顺序地写入到输出流
public void flush() 强制清空缓冲区并执行向外设写操作
public void close() 关闭输出流与外设的连接并且释放所占用的系统资源

例如:写入到文件中

import java.io.*;

class demo {
    public static void main(String[] args) throws IOException {
        char ch;
        FileInputStream fin = new FileInputStream(FileDescriptor.in);
        FileOutputStream fout = new FileOutputStream("text.txt");
        System.out.println("请输入一串字符!(以#号结束)");
        while ((ch = (char) fin.read()) != '#')
            fout.write(ch);
    }

}

读取文件的内容

 import java.io.*;
 
 class demo {
     public static void main(String[] args) throws IOException {
         char ch;
         int data;
 //        FileInputStream fin = new FileInputStream(FileDescriptor.in);
 //        FileOutputStream fout = new FileOutputStream("text.txt");
 //        System.out.println("请输入一串字符!(以#号结束)");
 //        while ((ch = (char) fin.read()) != '#')
 //            fout.write(ch);
         FileInputStream fin = new FileInputStream("text.txt");
         FileOutputStream fout = new FileOutputStream(FileDescriptor.out);
         while (fin.available() > 0) {
             data = fin.read();
             fout.write(data);
         }
     }
 }

FileInputStream和FileOutputStream主要用来处理二进制图像文件,例如:

import java.io.*;

class demo {
    public static void main(String[] args) throws IOException {
        FileInputStream fin = new FileInputStream("1.png");
        FileOutputStream fout = new FileOutputStream("2.png");
        System.out.println("文件大小"+fin.available());//利用available()方法获取文件的大小,并输出,以字节(B)为单位。
        byte[] b = new byte[fin.available()];//新建一个byte类型的数组
        fin.read(b);//将文件读入数组
        fout.write(b);//将数组中的数据写入新文件"2.png"中。
        System.out.println("文件复制更名成功!");
    }
}

顺序输入流
顺序输入流类SequenceInputStream是InputStream的直接子类,其功能是将多个输入流顺序连接在一起,形成单一的输入数据流,没有对应的输出数据流存在。再进行输入时,顺序输入流依次打开每个输入流并读取数据,也在读取完毕后将该流关闭,然后自动切换到下一个输入流。也就是说,由多个输入流构成的顺序输入流,当从一个流中中读取数据遇到EOF时,SequenceInputStream将会自动转向下一个输入流,直到构成SequenceInputStream类的最后一个输入流读取到EOF时为止。
SequenceInputStream类的构造方法、

构造方法 功能
public SequenceInputStream(Enumeration e) 创建一个串行输入流,连接枚举对象e中的所有输入流
public SequenceInputStream(InputStream s1, InputStream s2) 创建一个串行输入流,连接输入流s1和s2
SequenceInputStream类的常用方法
常用方法 功能
public int available() 返回流中可读取的字节数
public void close() 关闭输入流
public int read() 从输入流中读取字节,遇到EOF就转向下一输入流
public int read(byte[] b, int off, int len) 将len个数据读到一个字节数组从off开始的位置

管道输入输出流
Java之中管道字节输入流PipedInputSteam和管道字节输出流PipedOutputStream类提供了利用管道方式进行数据输入输出管理的类。管道流用来将一个程序或县城的输出连接到另外一个程序或线程作为输入,是的相互连接的线程能够通过PipedInputStream和PipedOutputStream流进行数据交换,从而可以实现程序内部线程间的通信或不同程序之间的通信。
在jdk1.8文档中明确说明了,不建议从单个线程中使用这两个对象,因为它可能会使线程死锁。
具体方法参考jdk文档

过滤输入输出流
过滤字节输入流类FilterInputStream和过滤字节输出流类FilterOuputStream,分别实现了在数据的读写操作的同时进行数据处理,他们是InputStram和OutputStream类的直接子类。过滤字节输入输出流类的主要特点是,顾虑字节输入输出流时间里在基本输入输出流之上,并在输入输出数据的同时能对所传输的数据做指定类型或格式的转换,即可实现对二进制字节数据的理解和编码转换。
具体使用方法见jdk文档。
整理如下:

DataInputStream类的常用方法

常用方法 说明
public boolean readBoolean() 从流中读1字节,但是字节值非0返回true,否则返回false
public byte readByte() 从流中读1自字节,返回该字节值
public char readChar() 从流中读取a,b2字节,形成Unicode字符(char)((a<<8)|(b&0xff))
public short readShort() 从六中读入2字节的short值并返回
public int readInt() 从流中读入4字节的int值并返回
public float readFloat() 从流中读入4字节的float值并返回
public long readLong() 从流中读入8字节的long值并返回
public double readDouble() 从流中读入8字节的double值并返回
DataOutputStream类的常用方法

常用方法 说明
public void writeBoolean(boolean v) 将boolean写入底层输出流作为1字节值,值true被写为(byte)1,值false作为值(byte)0
public void writeByte(int v) 向流中写入1字节,最低的1字节,其他的字节丢弃
public writeChar(int v) 向流中写入参数v的高2字节,其他的字节丢弃
public writeShort(int v) 向流中写入参数v的高2字节,其他的字节丢弃
public writeInt(int v) 向流中写入参数v的4字节
public writeFloat(float v) 向流中写入参数v的4字节
public writeLong(long v) 向流中写入参数v的8字节
public writeDouble(double v) 向流中写入参数v的8字节

package sample;

import java.io.*;

class demo {

    public static void main(String[] args) throws IOException {
        FileOutputStream fout = new FileOutputStream("text");
        DataOutputStream dout = new DataOutputStream(fout);

        dout.writeInt(10);
        dout.writeLong(12345);
        dout.writeFloat(3.1415626f);
        dout.writeDouble(987654321.123);
        dout.writeBoolean(true);
        dout.writeChars("GoodBye!\0");//此处不加上\0后面会抛出IO异常EOFException;
        FileInputStream fin = new FileInputStream("text");
        DataInputStream din = new DataInputStream(fin);

        System.out.println(din.readInt());
        System.out.println(din.readLong());
        System.out.println(din.readFloat());
        System.out.println(din.readDouble());
        System.out.println(din.readBoolean());

        char ch;
        while ((ch = din.readChar()) != '\0')
            System.out.print(ch);
    }
}

writeChars()里面需要加上一个\0,不然的话会抛出异常,亲测,EOFException3表示输入过程中意外地到达文件尾或流尾的信号,导致异常。

标准输入输出流
当程序对标准输入输出设备进行操作的时候,则不需要创建输入或输出流类的对象。
对于一般的系统来说,标准输入设备通常指键盘,标准输出设备通常指屏幕显示器。为了方便程序对键盘输入和屏幕输出进行操作。Java系统事先在System类中定义静态流对象System.in、System.out和System.err。System.in对应着输入流,通常指键盘输入设备。System.out对应着输出流,指显示器等信息输出设备,System.err对应着标准错误输出设备,使得程序的运行错误,可以由固定的输出位置,通常来说该对象对应着显示器。
1.标准输入
Java语言的标准输入是System.in是BufferedInputStream类的对象,当程序需要从键盘上读入数据时,只需要调用System.in的read()方法即可,该方法从键盘缓冲区读入一个字节的二进制数据,并将它们存储到缓冲区b,实际读取的字节数作为整数返回。其方法:public int read (byte[] b) throws IOException。第一个字节存入b[0],以此类推。
2.标准输出
Java语言的标准输出,是打印输出流PrintStream类的对象。详见jdk手册。

import java.io.*;

class demo {

public static void main(String[] args) throws IOException {
    byte[] b = new byte[128];
    System.out.println("请输入字符:");
    int count = System.in.read(b);
    System.out.println("输入的是:");
    for(int i = 0; i < count; ++i){
        System.out.println(b[i] + " ");//输出的是ASCII值
    }
    for(int i = 0; i < count; ++i)
        System.out.println((char)b[i] + " ");//以字符的方式输出数组b元素
}

四、使用Reader和Writer流类
Reader和Writer类则是用来处理“字符流”的,也就是文本文件,与字节输入输出流的功能一样,字符输入输出流类知识建立了一条通往文本文件的通道。
Reader类的常用方法

常用方法 说明
public int read() 从输入流中读一个字符
publici int read(char[] cbuf) 从输入流中读最多cbuf.length个字符,存入字符数组cbuf中
public int read(char[] cbuffer, int off, int len) 从输入流中读最多len个字符,存入字符数组cbuffer中从off开始的位置
public long skip(long n) 从输入流中最多向后跳n个字符
public boolean ready() 判断流是否最好读的准备
public void mark(int readAheadLimit) 标记输入流的当前位置
public boolean markSupported() 测试输入流是否支持mark
public void reset() 重定位输入流
public void close() 关闭输入流

Writer类的常用方法

常用方法 说明
public void write(int c) 将单一字符c输出到流中
public void write(String str) 将字符串输出到流中
public void write(char[] cbuf) 将字符数组cbuf输出到流
public void write(char[] cbuf, int off, int len) 将字符数组按指定格式输出
public void flush() 将缓冲区的数组写到文件
public void close() 关闭输出流

使用FileReader类读取文件
文件字符输入流类FileReader是继承自InputStreamReader类,而InputStreamReader类又是继承自Reader类,因此,Reader类与InputStreamReader类所提供的方法均可供FileReader类所创建对象使用。
使用FileReader类读取文件时,必须先调用FileReader()构造方法创建FileReader类的对象,再利用它来调用read()方法。
其构造方法:public FileReader(String name),根据文件名称创建一个可以读取的输入流对象。

import java.io.*;

class demo {

    public static void main(String[] args) throws IOException {
        char[] ch = new char[500];//创建可以容纳500字符的数组
        FileReader file = new FileReader("text.txt");
        int num = file.read(ch);//将数据读取到数组之中,并且返回读取的字符数量
        String str = new String(ch, 0, num);//将字符数组转换成为字符串
        System.out.println("读取的字符个数有:" + num + "个。内容如下:");
        System.out.println(str);
    }
}

这样子会将文件中的内容读出来,输出。但是可能会有人遇到乱码问题,记住,windows默认创建的txt文件时ANSI编码格式,所以,你需要保存为UTF-8格式。

使用FileWriter类写入文件
文件字符输出流类FileWriter继承自OutputStreamWriter类,但是OutputStreamWriter类又是从Writer类中继承得到的,所以说,Writer类与OutputStreamWriter类suotigong7的方法均可提供给FileWriter类所创建的对象使用。
FileWriter类的构造方法

构造方法 功能说明
public FileWriter(String filename) 根据所给的文件名创建一个可以供写入字符数据的输出流对象,原先的文件会被覆盖
public FileWriter(String filename, boolean a) 同上,如果说a设置为true,则会被数据追加在原先文件之后

import java.io.*;

 class demo {
     public static void main(String[] args) throws IOException {
         FileWriter file = new FileWriter("text.txt");
         String str = "Java牛皮!";
         file.write(str);
         file.close();
     }
 }

利用BufferedReader类读取文件
缓冲字符输入流类BufferedReader继承自Reader类,BufferedReader类是用来读取缓冲区李的数据,使用该类读取缓冲区中的数据之前,必须创建FileReader类对象,再将以该对象来创建BufferedReader类的对象,然后才可以利用该对象来读取缓冲区中的数据。
构造方法

构造方法 功能说明
public BufferedReader(Reader in) 创建缓冲区字符输入流
public BufferedReader(Reader in, int size) 创建缓冲区字符输入流,并设置缓冲区大小
常用方法

常用方法 功能说明
public int read() 读取单一字符
public int read(char[] ch) 从流中读取字符并且写入到字符数组之中
public int read(char[] ch, int off, int len) 从流中读取字符存放到字符数组之中,off代表数组下标,len代表的是数组长度
public long skip(long n) 跳过n个字符不读取
public String readLine() 读取一行字符串
public void close() 关闭流

import java.io.*;

class demo {
    public static void main(String[] args) throws IOException {
        FileReader file = new FileReader("text.txt");
        BufferedReader bfile = new BufferedReader(file);

        String tmpString;
        while((tmpString = bfile.readLine())!=null){
            System.out.println(tmpString);
        }
    }
}

使用BufferedWriter类写入文件
与BufferedReader类类似,BufferedWriter继承自Writer类,BufferedWriter类,是用来将数据写入缓冲区中,首先来说,我必须创建FileWriter类对象,再以该对象为参数创建BufferedWriter类的对象,然后就可以利用此对象来讲数据写入缓冲区中,所以不同的是,缓冲区中的数据最后必须都要用flush()方法将缓冲区清空,也就是将缓冲区的数据全部写到文件之内。
缓冲字符输出流类BuffereWriter的构造方法:

构造方法 功能说明
public BufferedWriter(Writer out) 创建缓冲区字符输出流
public BufferedWriter(Writer out, int size) 创建缓冲区字符输出流

BufferedWriter类的常用方法

常用方法 功能说明
public void writer(int c) 将单一字符写入缓冲区
public void write(char[] ch. int off, int len) 将字符数组ch按照指定格式写入到输出缓冲区中(off表示数组下标,len表示写入的字符数)
pulic void write(String str, int off, int len) 写入字符串(off表示下标,len表示写入的字符数)
public void newLine() 写入回车换行字符
public void close() 关闭流

import java.io.*;

class demo {
    public static void main(String[] args) throws IOException {
        BufferedWriter out = new BufferedWriter(new FileWriter("text.txt"));
        String str = new String("Hello!Java!我是新一代程序员!");
        out.write(str);
        out.newLine();
        out.write("我必定会登顶巅峰!");
        out.close();
    }
} 

五、文件的处理与随机访问
对于文件以及文件夹的管理

创建一个File类的对象,每个File类的对象对应着系统的一个磁盘文件或者文件夹,所以说创建File类对象需要给出它所对应的文件名或者文件夹名。
File类的构造方法

构造方法 功能说明

public File(String path)	利用path参数创建对象
public File(String path, String name)	以path为路径,创建name的文件或文件夹
public File(File dir. String name)	用一个已经存在代表某磁盘的文件夹的File对象dir作为文件夹,以name作为文件或文件夹名来创建File对象

因为在不同的操作系统中使用的文件夹的分隔符不一样,如Windows操作系统使用的是反斜线,但是在UNIX操作系统上使用的是正斜线,但是为了保证Java程序能够在不同的平台上运行,可以使用File类的一个静态变量File.separator。该属性中保存了当前系统规定的文件夹分隔符,使用它会组合成在不同操作系统上都能够通用的路径。

文件或文件夹属性
常用方法

用方法 功能说明

public boolean exists()	判断文件或文件夹是否存在
public boolean isFile()	判断对象是否代表有效文件
public boolean isDirectory()	判断对象是否代表有效文件夹
public String getName()	返回文件名或文件夹名
public String getPath()	返回文件或者文件夹的路径
public long length()	返回文件的字节数
public boolean canRead()	判断文件是否可读
public boolean canWriter()	判断文件是否可写
public String[] list()	将文件夹中所有文件名保存在字符串数组中返回
public boolean equals(File file)	比较两个文件或文件夹是否相同

文件或文件夹操作
管理操作方法
常用方法 功能说明

public boolean renameTo(File newFile) 将文件重命名成newFile对应的文件名 public
boolean delete() 将当前文件删除,若删除成功返回true,否则返回false public boolean
mkdir() 创建当前文件夹的子文件夹,若成功返回true,否则返回false

对文件的随机访问
RandomAccessFile类的构造方法

构造方法 功能说明

public RandomAccessFile(String name, String mode)	以name来指定随机文件流对象所对应的文件名,以mode表示对文件的访问模式
public RandomAccessFile(File file, String mode)	以file来指定随机文件流对象所对应的文件名,以mode表示对文件的访问模式
访问模式:r:表示只读方式打开文件,rw:表示以读写方式打开文件。

RandomAccessFile类的常用方法

常用方法 功能说明

public void close()	关闭随机访问文件并释放系统资源
public final FIleDescriptorgetFD()	获取文件描述符
public long getFilePointer()	返回文件指针的当前位置
public long length()	返回文件长度
public int skipBytes(int n)	跳过输入流中n个字符,并返回跳过实际的字节数
public int read()	从文件输入流中读取一个字节的数据
public int read(byte[] b, int off, int len)	从文件输入流的当前指针位置开始读取长度为len字节的数据存放到字节数组b中,存放的便宜位置为off,若遇到文件结束符,则返回值-1
public final void readFully(byte[] b)	从文件输入流的当前指针位置开始读取b.length字节的数据存放到字节数组b中,若遇文件结束符,则抛出EOFException类异常
public final void readFully(byte[] b, int off, int len)	从文件输入流的当前指针位置开始读取长度为len字节的数据,放到b数组中,存放的位置开始为off,遇到文件结束符,则抛出EOFException类异常
public final boolean readBoolean()	读取文件的逻辑值
public final byte readByte()	从文件中读取带符号的字节值
public final char readChar()	从文件中读取一个Unicode字符
public final String readLine()	从文本文件中读取一行
public void seek(long pos)	设置文件指针位置
RandomAccessFile类用于写入操作的常用方法

用方法 功能说明

public void write(int b)	在文件指针的当前位置写入一个int型数据b
public void writeBoolean(boolean v)	在文件指针的当前位置写入一个boolean型数据v
public void writeByte(int v)	在文件指针的当前位置写入一个字节值
public void writeBytes(String s)	以字节形式写入一个字符串到文件
public void writeChar(int v)	在文件指针的当前位置写入v的两字节,高字节优先
public void writeChars(String s)	以字符形式写一个字符串到文件
public void writeDouble(double v)	在文件当前指针位置写入8字节数据v
public void writeFloat(float v)	在文件当前指针位置写入4字节数据v
public void writeInt(int v)	在整形数作为4字节写入文件
public void writeLong(long v)	将长整型数作为8字节写入文件
public void writeShort(int v)	在文件指针当前位置写入2字节,高位字节
public void writeUTF(String str)	作为UTF格式向文件之中写入一个字符串

三、掌握:Java多线程的概念和实现方法

线程和多线程
程序:是一段静态的代码,是应用软件执行的蓝本

进程:是程序的一次动态执行过程,它对应了从代码加载、执行至执行完毕的一个完整过程,这个过程也是进程本身从产生、发展至消亡的过程

线程:是比进程更小的执行单位。进程在其执行过程中,可以产生多个线程,形成多条执行线索,每条线索,即每个线程也有它自身的产生、存在和消亡的过程,也是一个动态的概念

在这里插入图片描述
主线程:(每个Java程序都有一个默认的主线程)

当JVM加载代码发现main方法之后,就会立即启动一个线程,这个线程称为主线程

注意:主线程不一定是最后完成执行的线程,各个线程运行时完全独立,争夺cpu,很可能主线程执行完了,子进程没有。

单线程:如果main方法中没有创建其他的线程,那么当main方法执行完最后一个语句,JVM就会结束Java应用程序

多线程:如果main方法中又创建了其他线程,那么JVM就要在主线程和其他线程之间轮流切换,JVM要等程序中所有线程都结束之后才结束程序。

多线程的优势:

减轻编写交互频繁、涉及面多的程序的困难

程序的吞吐量会得到改善

由多个处理器的系统,可以并发运行不同的线程

“同时”执行是人的感觉,在线程之间实际上轮换执行

线程生命周期(五个状态):新建、就绪、运行、阻塞、死亡

新建状态:线程对象已经创建,还没有在其上调用start()方法

就绪状态:当线程调用start方法,但调度程序还没有把它选定为运行线程时线程

运行状态:线程调度程序从可运行池中选择一个线程作为当前线程时线程所处的状态。(是线程进入运行状态的唯一方式)

阻塞(等待/睡眠)状态:线程仍旧是活的,但是当前没有条件运行。当某件事件出现,他可能返回到可运行状态

死亡状态:当线程的run()方法完成时就认为它死去。线程一旦死亡,就不能复生。 一个死去的线程上调用start()方法,会抛出java.lang.IllegalThreadStateException异常
在这里插入图片描述

多线程的6种实现方式

参考这篇博客

四、网络通讯及对网上资源的访问方法

待续

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值