IO流的学习

流概念

概念

流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象。即 数据在两设备间的传输称为流,流的本质是数据传输,根据数据传输特性将流抽象为各种类,方便更直观的进行数据操作

分类

按处理数据类型的不同,分为字节流和字符流

按数据流向的不同,分为输入流和输出流(入和出是相对与内存说的)

按功能不同,分为节点流和处理流

      节点流;直接操作数据源

      处理流;对其他流进行处理

 抽象类定义

 InputStream

InputStream的继承关系 ;蓝色为节点流,黑色为处理流

 

 OutputStream

OutputStream的继承关系 为 蓝色为节点流,黑色为处理流

 

 

 Reader

 Reader的继承关系;蓝色为节点流,黑色为处理流

 

 Writer

writer的继承关系;蓝色为节点流,黑色为处理流

 

 文件流

 FileInputStream

package day_7_20;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

/**
 * 
 * I; Input输出,指向内存中导入数据
 * o;Output 输出,指从内存中写出数据
 * 
 * 流是对数据传输的总称,也就是两台设备之家进行数据传输成为流,根据流传输的特性,抽象为各个类
 * FileInoutStream;字节输入流
 * 
 * 
 * @author 啊超
 * @Date 2021年7月20日
 */
public class Io_1 {
	public static void main(String[] args) throws IOException {
		//读取文件首先需要找到文件
		//绝对定位;指在计算机中那个盘里存储的地址
		//相对地址;就是相对于这个文件的目录下的位置  ./../
		FileInputStream fis=new FileInputStream("D://a.java");
		//读取的,默认一次读取一个字节,并且返回值是下一个字节的值,到达文件末尾,返回-1
		
		
		//循环结构
		int temp=-1;
		while((temp=fis.read())!=-1){
			System.out.print((char)temp);
		}		
	}
}
package day_7_20;

import java.io.FileInputStream;
import java.io.IOException;


/**
 * 异常处理
 * 
 * 
 * @author 啊超
 * @Date 2021年7月20日
 */
public class IO_2 {
	public static void main(String[] args) {
		FileInputStream fis=null;
		try{
			fis=new FileInputStream("D://a.java");
			//循环读取
			int temp=-1;
			while((temp=fis.read())!=-1){
				System.out.print((char)temp);
			}
			
		}catch(IOException e){
			e.printStackTrace();
		}finally{                   //需要在这里强制关闭
			
			try{
				if(fis!=null){         //如果运行了就关闭
					fis.close();
				}
			}catch(IOException e){
				e.printStackTrace();
			}
		}
		//1.7自动关闭
		try(FileInputStream fis1=new FileInputStream ("D://a.java");){
			//循环读取
			int temp=-1;
			while((temp=fis1.read())!=-1){
				System.out.print((char)temp);
			}
			
		}catch(Exception e){
			e.printStackTrace();
		}
	}
}
package day_7_20;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;


/**
 * 
 * read; 方法重载,可以传入一个字节数组,把数据放到数组中;
 * @author 啊超
 * @Date 2021年7月20日
 */
public class IO_3FileInputStream {
	public static void main(String[] args)  {
		try(FileInputStream fis1=new FileInputStream("D://a.java")){
			循环读取
			byte [] bytes =new byte[100];
			int temp=-1;
			while((temp=fis1.read(bytes))!=-1){
				System.out.println(new String(bytes,0,temp));
			}
		}catch(Exception e){
			e.printStackTrace();
		}
	}
}

FileRader

package day_7_20;

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;



/**
 * 
 * 
 * FileReaderyi一次读取一个字符,可以解决乱码问题,一般纯文本文件使用FileReader
 * 向压缩包,图片,视频等,还是会使用字节流,字符流会出现问题
 * 
 * @author 啊超
 * @Date 2021年7月20日
 */
public class IO_4FileReader {

	public static void main(String[] args) throws FileNotFoundException, IOException {
		try(FileReader fr=new FileReader("D://a.java");
				){
			int temp=-1;
			//read;返回下一个字符,到达文件末尾返回-1;
			while((temp=fr.read())!=-1){
				System.out.print((char)temp);
			}
			
		}catch(Exception e){
			e.printStackTrace();
		}
	}
}
package day_7_20;

import java.io.FileReader;


/**
 * 
 * FileReader; 一次读取一个字符,可以解决乱码问题,一般纯文本使用FileReader
 * 压缩包,图片、视频,需要使用字节流,字符流会出问题
 * 
 * @author 啊超
 * @Date 2021年7月20日
 */
public class IO_5FileReader {

	public static void main(String[] args) {
		try(FileReader fr=new FileReader("D://a.java");
				){
			char[] chars=new char[5]; 
			int temp=-1;
			while((temp=fr.read(chars))!=-1){
				System.out.print(new String(chars,0,temp));
			}
			
		}catch(Exception e){
			e.printStackTrace();
		}
	}
}

FileOutputStream

package day_7_20;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;


/**
 * 
 * FileOutoutStream;字节输出流,需要指定输出的文件路径,如果找不到该文件,会自动创建
 * 
 * write();
 * fluse();刷新
 * 构造方法;FileOutputStream(String) ;传入文件地址,默认覆盖写入
 *         覆盖写入就是先把文件中内容清空,在写入
 *         FileOutputStream(String,boolean); boolean类型为true就是追加写入,false就是覆盖写入
 * 
 * @author 啊超
 * @Date 2021年7月20日
 */
public class File_6OutpurStream {

	public static void main(String[] args) throws IOException {
		//覆盖写入,创建对象的时候会自动把源文件中内容清空
		
			FileOutputStream fos=new FileOutputStream("F://b.java");
			//FileOutputStream fos=new FileOutputStream("F://b.java",true);
			fos.write(97);
		//想要写出字符串,需要转换成字节数组
			String string="哈哈哈";
			byte [] bytes=string.getBytes();
			fos.write(bytes);
			//刷缓存
			fos.flush();
			//关闭资源
			fos.close();

	}

}

FileWrite

package day_7_20;

import java.io.FileWriter;



/**
 * 
 * 字符输出流
 * @author 啊超
 * @Date 2021年7月20日
 */
public class IO_7FileWrite {
	public static void main(String[] args) {
		//创建对象一运行就会清空文件!!!!,只要是输出流,创建对象就会清空,除非是追加
		try(FileWriter fw=new FileWriter("F://b.java");){
			fw.write("钱钱钱");
			char [] chars={'a','b','c','d'};
			fw.write(chars);
			fw.flush();
		}catch(Exception e){
			e.printStackTrace();
		}
	}
}

缓冲流

 特点

主要是为了提高效率而存在的,减少物理读取次数

提供read Line()、newLine()这样的便捷的方法(针对缓冲字符流)

在读取和写入时,会有缓存部分,调用flush为刷新缓存,将内存数据写入到磁盘

BufferedRead

package day_7_20;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;

public class IO_8BufferedReader {

	public static void main(String[] args) throws IOException, IOException {
	try(FileReader fr=new FileReader("F://b.java");
			BufferedReader br=new BufferedReader(fr);){
		String temp=null;
		//readerLine;读取下一行的数据,返回值就是读到的数据,如果达到文件默认,返回null
	while((temp=br.readLine())!=null){
		System.out.println(temp);
	}
	}catch(Exception e){
		e.printStackTrace();
	}

	}

}

BufferedWriter

package day_7_20;

import java.io.BufferedWriter;
import java.io.FileWriter;


/**
 * 
 * 输出流缓冲流
 * @author 啊超
 * @Date 2021年7月20日
 */
public class IO_9BufferedWriter {

	public static void main(String[] args) {
		try(FileWriter fw=new FileWriter("./src/day_7_20/call.txt");
			BufferedWriter bw= new BufferedWriter(fw);	
				){bw.write("你好吗");
				//换行
				bw.newLine();
				bw.write("脑壳疼");
			 bw.flush();
		}catch(Exception e){
			e.printStackTrace();
		}
	}
}

转换流

 特点 

转换流是指将字节流向字符流的转换,主要有InputSteamReader和OutputStreamWriter

InputStreamReader主要是将字节流输入转换成字符流输入流

OutputStreamWriter主要是将字节流输出转换成字符输出流

InputStreamReader

package day_7_20;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;

/**
 * 字节流转换为  字符流
 * 
 * 
 * @author 啊超
 * @Date 2021年7月20日
 */
public class IO_10InputStreamReader {

	public static void main(String[] args) throws IOException, IOException {
		try(
			//得到字节流
		FileInputStream fis=file("D://a.java");		 //接收   //字节流可能出现乱码,需要转换成字符流
		InputStreamReader isr=new InputStreamReader(fis);   //转换为字符流
		//使用缓冲流
				BufferedReader br=new BufferedReader(isr);
				
				){
			String  temp=null;   //字符流缓冲流需要读一行,需要temp=null
			while((temp=br.readLine())!=null){
				System.out.println(temp);
			}
			
		}
		catch(Exception e){
			e.printStackTrace();
		}
	}
	//传递一个文件路径,返回该文件的输入流对象
	public static FileInputStream file(String filepath) throws FileNotFoundException{
		return  new FileInputStream(filepath);
	}
	

}

 OutputStreamWriter

参考InputStreamReader

打印流

特点

打印流是输出最方便的类

包含字节打印流PrintStream,字符打印流 PrintWriter

PrintStream是OutputStream的子类,把一个输出流的实例传递到打印流之后,可以更加方便地输出内容,相当于把输出流重新包装一下。

PrintStream类的print()方法被重载很多次print(int)、print(boolean b)、print(charc)

标准输入/输出

Java的标准输入/输出分别通过System.in和System.out来代表,在默认的情况下分别代表键盘和显示器,当程序通过System.in来获得输入时,实际上是通过键盘获得输入。当程序通过System.out执行输出时,程序总是输出到屏幕。

在System类中提供了三个重定向标准输入/输出的方法 

  1. static void setErr(PrintStream err) 重定向“标准”错误输出流
  2. static void setIn(InputStream in) 重定向“标准”输入流
  3. static void setOut(PrintStream out)重定向“标准”输出流
package day_7_20;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintStream;

public class IO_11printStream {

	public static void main(String[] args) throws FileNotFoundException {
		System.out.println(1);
		PrintStream ps=System.out;
		ps.println("xxx");
		//标准输出,默认打印到控制台
		//设置默认打印位置
		//创建字节输出流
		FileOutputStream fos= new FileOutputStream("D://qqq.txt");
		//封装为打印流
		PrintStream ps1=new PrintStream(fos);
		//设置到System中
		System.setOut(ps1);     //setOut 接收的是打印流,
		System.out.println("==========");
		System.out.println("m1开始执行");
		m1();
		System.out.println("m1执行结束");
		
	}
	public static void m1(){
		System.out.println("m1 method execute");
	}

}

数据流

为了方便操作java语言的基本数据类型和String的数据,可以使用数据流

数据流有两个类;(用于读取和写出基本数据类型、String类的数据)

DataInputStream和DataOutputStream

分别套接在InputStream和OutputStream子类的流上

DataInputStream中的方法

boolean readBoolean()  

char readChar()  

double readDouble()  

long readLong()

String readUTF()

byte readByte()  

float readFloat()  

short readShort()  

int readInt()

void readFully(byte[] b)

package day_7_20;

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

public class IO_12DataOutputStream {

	public static void main(String[] args) throws IOException {
		//为了解决不同语言的编码而产生的乱码
		//使用了两个轻量化方法
		DataOutputStream dos=new DataOutputStream(new FileOutputStream("./src/day_7_20/data"));
		byte b=10;
		short s=12;
		char c='a';
		int i=11;
		dos.writeByte(b);
		dos.writeShort(s);
		dos.writeChar(c);
		dos.writeInt(i);
		dos.writeUTF("脑壳疼");
		dos.flush();
		dos.close();
		
	}

}
package day_7_20;

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

public class IO_13DataInputStream {

	public static void main(String[] args) throws IOException {
		DataInputStream dis=new DataInputStream(new FileInputStream("./src/day_7_20/data"));
          //读写顺序与12一样
		System.out.println(dis.readByte());
		System.out.println(dis.readShort());
		System.out.println(dis.readChar());
		System.out.println(dis.readInt());
		System.out.println(dis.readUTF());
		
	}

}

File类

File类特点

  1. java.io.File类:文件和文件目录路径的抽象表示形式,与平台无关
  2. File 能新建、删除、重命名文件和目录,但 File 不能访问文件内容本身。

如果需要访问文件内容本身,则需要使用输入/输出流。

  1. 想要在Java程序中表示一个真实存在的文件或目录,那么必须有一个File对 象,但是Java程序中的一个File对象,可能没有一个真实存在的文件或目录。
  2. File对象可以作为参数传递给流的构造器

File类构造方法

  1. public File(String pathname)以pathname为路径创建File对象,可以是绝对路径或者相对路径,如果pathname是相对路径,则默认的当前路径在系统属性user.dir中存储。
    • 绝对路径:是一个固定的路径,从盘符开始
    • 相对路径:是相对于某个位置开始
  1. public File(String parent,String child)以parent为父路径,child为子路径创建File对象。
  1. public File(File parent,String child)根据一个父File对象和子文件路径创建File对象

File使用

  1. 路径中的每级目录之间用一个路径分隔符隔开。
  2. 路径分隔符和系统有关:
    • windows和DOS系统默认使用“\”来表示
    • UNIX和URL使用“/”来表示
  3. Java程序支持跨平台运行,因此路径分隔符要慎用。
  4. 为了解决这个隐患,File类提供了一个常量:public static final String separator。根据操作系统,动态的提供分隔符

 File类常用方法

  • public String getAbsolutePath():获取绝对路径
  • public String getPath() :获取路径
  • public String getName() :获取名称
  • public String getParent():获取上层文件目录路径。若无,返回null
  • public long length() :获取文件长度(即:字节数)。不能获取目录的长度。
  • public long lastModified() :获取最后一次的修改时间,毫秒值
  • public String[] list() :获取指定目录下的所有文件或者文件目录的名称数组
  • public File[] listFiles() :获取指定目录下的所有文件或者文件目录的File数组

重命名功能:

  • public boolean renameTo(File dest):把文件重命名为指定的文件路径

判断功能:

  • public boolean isDirectory():判断是否是文件目录
  • public boolean isFile() :判断是否是文件
  • public boolean exists() :判断是否存在
  • public boolean canRead() :判断是否可读
  • public boolean canWrite() :判断是否可写
  • public boolean isHidden() :判断是否隐藏

创建删除功能:

  • public boolean createNewFile() :创建文件。若文件存在,则不创建,返回false
  • public boolean mkdir() :创建文件目录。如果此文件目录存在,就不创建了。 如果此文件目录的上层目录不存在,也不创建。
  • public boolean mkdirs() :创建文件目录。如果上层文件目录不存在,一并创建

注意事项:如果你创建文件或者文件目录没有写盘符路径,那么,默认在项目 路径下。

  • public boolean delete():删除文件或者文件夹

删除注意事项:

Java中的删除不走回收站

要删除一个文件目录,请注意该文件目录内不能包含文件或者文件目录

package day_7_20;

import java.io.File;
import java.io.IOException;


/**
 * 
 * 文件操作
 * 
 * @author 啊超
 * @Date 2021年7月20日
 */
public class File_1 {

	public static void main(String[] args) throws IOException {
		//创建文件对象
		File f1=new File("D://work//works");
		//活得全路径
		System.out.println(f1.getAbsolutePath());
		//判断是否存在
		System.out.println(f1.exists());  
		//判断是否是文件
		System.out.println(f1.isFile());
		//判断是否是目录
		System.out.println(f1.isDirectory());
		//上级路径
		System.out.println(f1.getParent());
		//上级对象
		System.out.println(f1.getParentFile());
		//获取所有子文件对象
		File []subFiles=f1.listFiles();
		for(File file:subFiles){
			System.out.println(file.getAbsolutePath());
		}
		//删除就找不回来了
		File f2=new File("D:/.a.java");
		//判断是否存在
		System.out.println(f2.exists());
		//新建文件,不能新建文件夹,如果有就不创建,创建返回true,不创建返回false
		System.out.println(f2.createNewFile());
		//删除成功,返回true,否则返回false
		System.out.println(f2.delete());
		
		//文件夹创建和删除
		File f3 = new File("D:/com/zrz/");
		System.out.println(f3.exists());
		// 只能创建zrz 不能创建com,
		// f3.mkdir();
		// 递归创建整个目录
		f3.mkdirs();
		// 删除,只会删除zrz文件夹,因为f3是zrz的文件夹对象,所以不会删除com
		f3.delete();
	}
}
package day_7_20;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;


/**
 * 
 * 递归复制
 * 
 * 1 单个文件复制
 * 
 * 2 递归获取所有文件
 * 
 * 3 整合
 * 
 * @author 啊超
 * @Date 2021年7月20日
 */
public class File_2 {

	public static void main(String[] args) {
		File f = new File("D:\\1work\\work");
		checkMunu(f);
		System.out.println("复制完成");
	}
	public static void checkMunu(File f) {
		if (f.isFile()) {
			// 原文件路径
			String filePath = f.getAbsolutePath();
			// 目标路径,把D盘的这个文件,复制到E盘
			// 注意 !!!!   原目录 和 目标目录 不能一致,否则 如果是覆盖写入 所有数据就清空了
			// 如果是追加写入,导致全是重复数据,因为写一行,读一行,导致永远读不完
			String newFilePath = "E"+filePath.substring(1);
			
			// 判断目标路径中,是否有对应的文件夹
			File file = new File(newFilePath).getParentFile();
			// 不存在就创建
			if (!file.exists()) {
				file.mkdirs();
			}
			// 复制
			try (FileInputStream fis = new FileInputStream(filePath);
					BufferedInputStream bis = new BufferedInputStream(fis);
					FileOutputStream fos = new FileOutputStream(newFilePath);
					BufferedOutputStream bos = new BufferedOutputStream(fos);
					) {
				byte[] bytes = new byte[1024000];
				int temp = -1;
				while( (temp = bis.read(bytes)) != -1 ){
					bos.write(bytes, 0, temp);
					
				}
				bos.flush();
			} catch (Exception e) {
				e.printStackTrace();
			}
		
			return;
		}
		// 能到这里 说明是 目录
		// 获取所有子文件
		File[] fs = f.listFiles();
		for (File file : fs) {
			checkMunu(file);
		}
	}

}

对象流

定义

  1. ObjectInputStream和OjbectOutputSteam
  • 用于存储和读取基本数据类型数据或对象的处理流。它的强大之处就是可 以把Java中的对象写入到数据源中,也能把对象从数据源中还原回来。
  1. 序列化:用ObjectOutputStream类保存基本类型数据或对象的机制
  2. 反序列化:用ObjectInputStream类读取基本类型数据或对象的机制
  3. ObjectOutputStream和ObjectInputStream不能序列化static和transient修

饰的成员变量

对象的序列化

  1. 对象序列化机制允许把内存中的Java对象转换成平台无关的二进制流,从 而允许把这种二进制流持久地保存在磁盘上,或通过网络将这种二进制流传 输到另一个网络节点。//当其它程序获取了这种二进制流,就可以恢复成原 来的Java对象
  2. 序列化的好处在于可将任何实现了Serializable接口的对象转化为字节数据, 使其在保存和传输时可被还原

序列化是 RMI(Remote Method Invoke – 远程方法调用)过程的参数和返 回值都必须实现的机制,而 RMI 是 JavaEE 的基础。因此序列化机制是 JavaEE 平台的基础

  1. 如果需要让某个对象支持序列化机制,则必须让对象所属的类及其属性是可 序列化的,为了让某个类是可序列化的,该类必须实现如下两个接口之一。 否则,会抛出NotSerializableException异常
    • Serializable
    • Externalizable
  2. 凡是实现Serializable接口的类都有一个表示序列化版本标识符的静态变量:
    • private static final long serialVersionUID;
    • serialVersionUID用来表明类的不同版本间的兼容性。简言之,其目的是以序列化对象 进行版本控制,有关各版本反序列化时是否兼容。
    • 如果类没有显示定义这个静态常量,它的值是Java运行时环境根据类的内部细节自 动生成的。若类的实例变量做了修改,serialVersionUID 可能发生变化。故建议,  显式声明。
    • 简单来说,Java的序列化机制是通过在运行时判断类的serialVersionUID来验 证版本一致性的。在进行反序列化时,JVM会把传来的字节流中的 serialVersionUID与本地相应实体类的serialVersionUID进行比较,如果相同 就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异 常。(InvalidCastException)

使用对象流序列化对象

  1. 若某个类实现了 Serializable 接口,该类的对象就是可序列化的:
    • 创建一个 ObjectOutputStream
    • 调用 ObjectOutputStream 对象的 writeObject(对象) 方法输出可序列化对象
    • 注意写出一次,操作flush()一次
  2. 反序列化
    • 创建一个 ObjectInputStream
    • 调用 readObject() 方法读取流中的对象
  3. 强调:如果某个类的属性不是基本数据类型或 String 类型,而是另一个 引用类型,那么这个引用类型必须是可序列化的,否则拥有该类型的 Field 的类也不能序列化

 

package day_7_20;

import java.io.Serializable;
/**
 * 要被序列化的类,必须实现 Serializable接口
 * 
 * Serializable接口 没有任何方法和功能,就是一个标识
 * 
 * serialVersionUID 是控制版本的
	 * 
	 * 如果不加,每次更改都会重新 生成一个UID,如果序列化对象中的UID和类中的UID不同,则不兼容
	 * 
	 * 所以我们一般手动指定一个UID.比如现在更改内容了,之前的序列化对象不想让他兼容了,可以把UID更改
	 * 
	 * 如果我们更改了内容.之前的序列化对象还需要兼容,则不需要更改UID的值
	 */
	
	/**
	 * transient : 防止该变量进行序列化
	 * 
	 * 如果使用transient修饰之后,该变量的值,不会被序列化
	 * 
	 * 可以把不必要的数据 使用 transient 进行修饰,这样可以提升序列化和反序列化的效率
 * @author 啊超
 * @Date 2021年7月20日
 */
public class User implements Serializable{
	private static final long serialVersionUID = 1L;
	String name;
	transient int age;

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

	public void m1(){
		System.out.println("m1------");
	}
	public void m2(){
		System.out.println("m2------");
	}
	@Override
	public String toString() {
		return "User [name=" + name + ", age=" + age + "]";
	}
	

}

 

package day_7_20;

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



/**
 * 
 * 序列化;对象-->硬盘中进行持久化存储
 * 反序列化; 硬盘中对象-->堆内存对象
 * 创建对象    
 *        1 new 最常用
 *        2反射机制
 *        3反序列化
 *        4不用管clone,被序列化代替
 *应用场景 ; 序列化就是将数据转换为二进制流,然后能够进行持久话存储和进行网络传输
 *         数据-->序列化-->加密处理-->网络传输--->解密处理--->反序列化--->数据     
 * 
 * @author 啊超
 * @Date 2021年7月20日
 */
public class IO_14ObjectOutputStream {

	public static void main(String[] args)  throws FileNotFoundException, IOException {
		User user = new User("张三", 18);
		ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("./src/com/obj"));
		oos.writeObject(user);
		oos.flush();
		oos.close();

	}

}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值