什么是反射?

1 什么是反射?

Reflection(反射) 是 Java 程序开发语言的特征之一,它允许运行中的 Java 程序对自身进行检查,或者说“自审”,也有称作“自省”。
反射非常强大,它甚至能直接操作程序的私有属性。我们前面学习都有一个概念,被private封装的资源只能类内部访问,外部是不行的,但这个规定被反射赤裸裸的打破了。
反射就像一面镜子,它可以在运行时获取一个类的所有信息,可以获取到任何定义的信息(包括成员变量,成员方法,构造器等),并且可以操纵类的字段、方法、构造器等部分。

2 为什么需要反射?

如果想创建对象,我们直接new User(); 不是很方便嘛,为什么要去通过反射创建对象呢?

那我要先问你个问题了,你为什么要去餐馆吃饭呢?
例如:我们要吃个牛排大餐,如果我们自己创建,就什么都得管理。
好处是,每一步做什么我都很清晰,坏处是什么都得自己实现,那不是累死了。牛接生你管,吃什么你管,屠宰你管,运输你管,冷藏你管,烹饪你管,上桌你管。就拿做菜来说,你能有特级厨师做的好?
那怎么办呢?有句话说的好,专业的事情交给专业的人做,饲养交给农场主,屠宰交给刽子手,烹饪交给特级厨师。那我们干嘛呢?
我们翘起二郎腿直接拿过来吃就好了。
再者,饭店把东西做好,不能扔到地上,我们去捡着吃吧,那不是都成原始人了。那怎么办呢?很简单,把做好的东西放在一个容器中吧,如把牛排放在盘子里。

我们在后面的学习中,会学习框架,有一个框架Spring就是一个非常专业且功能强大的产品,它可以帮我们创建对象,管理对象。以后我无需手动new对象,直接从Spring提供的容器中的Beans获取即可。Beans底层其实就是一个Map<String,Object>,最终通过getBean(“user”)来获取。而这其中最核心的实现就是利用反射技术。

总结一句,类不是你创建的,是你同事或者直接是第三方公司,此时你要或得这个类的底层功能调用,就需要反射技术实现。有点抽象,别着急,我们做个案例,你就立马清晰。

3 反射需要用到的API

3.1 获取字节码对象

Class.forName(“类的全路径”);
类名.class
对象.getClass();

3.2 常用方法

获取包名 类名

clazz.getPackage().getName()//包名
clazz.getSimpleName()//类名
clazz.getName()//完整类名

获取成员变量定义信息

getFields()//获取所有公开的成员变量,包括继承变量
getDeclaredFields()//获取本类定义的成员变量,包括私有,但不包括继承的变量
getField(变量名)
getDeclaredField(变量名)

获取构造方法定义信息

getConstructor(参数类型列表)//获取公开的构造方法
getConstructors()//获取所有的公开的构造方法
getDeclaredConstructors()//获取所有的构造方法,包括私有
getDeclaredConstructor(int.class,String.class)

获取方法定义信息

getMethods()//获取所有可见的方法,包括继承的方法
getMethod(方法名,参数类型列表)
getDeclaredMethods()//获取本类定义的的方法,包括私有,不包括继承的方法
getDeclaredMethod(方法名,int.class,String.class)

反射新建实例

clazz.newInstance();//执行无参构造创建对象
clazz.newInstance(666,”海绵宝宝”);//执行含参构造创建对象
clazz.getConstructor(int.class,String.class)//获取构造方法

反射调用成员变量

clazz.getDeclaredField(变量名);//获取变量
clazz.setAccessible(true);//使私有成员允许访问
f.set(实例,值);//为指定实例的变量赋值,静态变量,第一参数给null
f.get(实例);//访问指定实例变量的值,静态变量,第一参数给null

反射调用成员方法

Method m = Clazz.getDeclaredMethod(方法名,参数类型列表);
m.setAccessible(true);//使私有方法允许被调用
m.invoke(实例,参数数据);//让指定实例来执行该方法

4 反射的应用

4.1 创建 : 测试物料类

创建包: cn.tedu. reflection
创建类: Student.java

package cn.tedu.reflection;
/**本类用于测试反射,先准备的物料类*/
public class Student {
	//1.定义成员变量
	String name;
	int age;
<span class="token comment">//2.定义构造方法</span>
<span class="token comment">//快捷方式:右键--&gt;Source--&gt;倒数第三个--&gt;OK</span>
<span class="token keyword">public</span> <span class="token class-name">Student</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span><span class="token punctuation">}</span><span class="token comment">//注意要先手动添加无参构造,防止被覆盖</span>
<span class="token keyword">public</span> <span class="token class-name">Student</span><span class="token punctuation">(</span><span class="token class-name">String</span> name<span class="token punctuation">,</span><span class="token keyword">int</span> age<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
	<span class="token keyword">super</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token keyword">this</span><span class="token punctuation">.</span>name <span class="token operator">=</span> name<span class="token punctuation">;</span>
	<span class="token keyword">this</span><span class="token punctuation">.</span>age <span class="token operator">=</span> age<span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token comment">//3.定义成员方法</span>
<span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">eat</span><span class="token punctuation">(</span><span class="token keyword">int</span> n<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
	<span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"饿了吃点火锅吧"</span><span class="token operator">+</span>n<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token comment">//4.提供重写的toString()</span>
<span class="token comment">//目的:为了方便查看对象的属性值</span>
<span class="token annotation punctuation">@Override</span>
<span class="token keyword">public</span> <span class="token class-name">String</span> <span class="token function">toString</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
	<span class="token keyword">return</span> <span class="token string">"Student [name="</span><span class="token operator">+</span> name <span class="token operator">+</span><span class="token string">",age="</span><span class="token operator">+</span> age <span class="token operator">+</span><span class="token string">"]"</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

4.2 练习 : 获取类对象

创建包: cn.tedu. reflection
创建类: Test1Reflect.java

package cn.tedu.reflection;

import java.lang.reflect.Constructor;
import java.util.Arrays;

import org.junit.Test;

/*本类用来测试反射/
public class TestReflect1 {
//1.创建入口函数main()–不用
/**单元测试方法:是java测试的最小单位,使用灵活,推荐使用
* 语法要求: @Test + void + 没有参数 + public
* 注意使用时需要导包:Add JUnit 4 library to the build path:import org.junit.Test;
* 单元测试方法执行方式:选中方法名–>右键运行(Run As–>JUnit Test)–>出现小绿条说明执行成功
*/

//2.通过单元测试来测试如何获取类对象
@Test
public void getClazz() throws Exception {
/*右键要获取字节码对象的类名,选择Copy Quailfied Name复制类的全路径名/
Class<?> student1 = Class.forName(“cn.tedu.reflection.Student”);//此处的参数是类的全路径名[包名+类名]
Class<?> student2 = Student.class;
Class<?> student3 = new Student().getClass();//先创建匿名对象,匿名对象没有名字,然后对象的字节码对象
System.out.println(student1);//反射得到的字节码Class对象
System.out.println(student2.getName());//获取类的全路径名[包名+类名]
System.out.println(student3.getSimpleName());//只获取类名
System.out.println(student3.getPackage().getName());//获取包名
}
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

4.3 练习 : 类获取构造方法

package cn.tedu.reflection;

import java.lang.reflect.Constructor;
import java.util.Arrays;

import org.junit.Test;

/*本类用来测试反射/
public class TestReflect1 {
//3.通过单元测试来测试如何获取构造方法
@Test
public void getConstruct() {
//1.获取字节码Class对象
Class<?> clazz = Student.class;
//2.获取构造方法们,并放入cs数组中
Constructor<?>[] cs = clazz.getConstructors();
//3.获取每个构造
//使用增强for循环完成
//for(1 2 : 3){循环体}其中3是要遍历的数据,1是遍历后每个元素的数据类型 2是遍历后每个数据的变量名
for (Constructor c : cs) {
//拿到每个构造方法以后可以获取构造方法的相关信息
System.out.println(c.getName());//获取构造方法的名称
Class[] cp = c.getParameterTypes();//获取构造方法的参数类型,可能有多个
System.out.println(Arrays.toString(cp));
}
}
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

4.4 练习 : 获取成员方法

package cn.tedu.reflection;

import java.lang.reflect.Method;
import java.util.Arrays;

import org.junit.Test;

/*本类用来测试反射/
public class TestReflect1 {
//4.通过单元测试来测试获取成员方法
@Test
public void getFunction() throws Exception {
//1.获取Class字节码对象
Class<?> clazz = Class.forName(“cn.tedu.reflection.Student”);

		<span class="token comment">//2.获取所有成员方法</span>
		<span class="token class-name">Method</span><span class="token punctuation">[</span><span class="token punctuation">]</span> ms <span class="token operator">=</span> clazz<span class="token punctuation">.</span><span class="token function">getMethods</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
		
		<span class="token comment">//3.遍历数组,获取每个方法的信息</span>
		<span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token class-name">Method</span> m <span class="token operator">:</span> ms<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
			<span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>m<span class="token punctuation">.</span><span class="token function">getName</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">//获取方法名</span>
			<span class="token class-name">Class</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token operator">?</span><span class="token punctuation">&gt;</span></span><span class="token punctuation">[</span><span class="token punctuation">]</span> pt <span class="token operator">=</span> m<span class="token punctuation">.</span><span class="token function">getParameterTypes</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">//获取方法参数类型</span>
			<span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token class-name">Arrays</span><span class="token punctuation">.</span><span class="token function">toString</span><span class="token punctuation">(</span>pt<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
		<span class="token punctuation">}</span>
	<span class="token punctuation">}</span>

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

4.5 练习 : 获取成员变量

package cn.tedu.reflection;

import java.lang.reflect.Field;

import org.junit.Test;

/本类用来测试反射/
public class TestReflect1 {
//5.通过单元测试来测试获取成员变量
@Test
public void getFields(){
//1.获取Class字节码对象
/* Class<?>中的"?“是泛型约束的通配符,类似于”*" */
Class<?> clazz = Student.class;

		<span class="token comment">//2.获取所有的成员变量,公共的!!!</span>
		<span class="token comment">/**!!!注意目前成员变量的修饰符必须是public才能获取到,采用默认修饰符就反射不到*/</span>
		<span class="token class-name">Field</span><span class="token punctuation">[</span><span class="token punctuation">]</span> fs <span class="token operator">=</span> clazz<span class="token punctuation">.</span><span class="token function">getFields</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
		
		<span class="token comment">//3.遍历数组,获取每个成员变量的信息</span>
		<span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token class-name">Field</span> f<span class="token operator">:</span> fs<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
			<span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>f<span class="token punctuation">.</span><span class="token function">getName</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">//获取变量名</span>
			<span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>f<span class="token punctuation">.</span><span class="token function">getType</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getName</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">//获取变量类型</span>
		<span class="token punctuation">}</span>
	<span class="token punctuation">}</span>

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

4.6 练习 : 创建对象

package cn.tedu.reflection;

import java.lang.reflect.Constructor;

import org.junit.Test;

/本类用来测试反射/
public class TestReflect1 {
//6.通过单元测试来测试反射创建对象
/*
* 方式一:通过字节码对象直接调用newInstance(),触发无参构造来创建对象
* 方式二:先获取指定的构造函数,再通过构造函数对象调用newInstance(),触发对应的构造函数来创建对象
* */

@Test
public void getObject () throws Exception {
//1.获取Class字节码对象
Class<?> clazz = Student.class;

		<span class="token comment">//2.创建对象</span>
		<span class="token class-name">Object</span> obj <span class="token operator">=</span> clazz<span class="token punctuation">.</span><span class="token function">newInstance</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">//触发无参构造</span>
		<span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>obj<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">//Student [name=null,age=0]</span>
		
		<span class="token comment">//3.可以指定去调用哪个构造方法,注意根据参数来指定,而且传入的是参数的字节码对象</span>
		<span class="token class-name">Constructor</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token operator">?</span><span class="token punctuation">&gt;</span></span> c <span class="token operator">=</span> clazz<span class="token punctuation">.</span><span class="token function">getConstructor</span><span class="token punctuation">(</span><span class="token class-name">String</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">,</span><span class="token keyword">int</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
		
		<span class="token comment">//4.触发指定的构造方法</span>
		<span class="token class-name">Object</span> obj2 <span class="token operator">=</span> c<span class="token punctuation">.</span><span class="token function">newInstance</span><span class="token punctuation">(</span><span class="token string">"海绵宝宝"</span><span class="token punctuation">,</span><span class="token number">3</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
		<span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>obj2<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">//Student [name=海绵宝宝,age=3]</span>
		
		<span class="token comment">//5.查看对象具体的属性值,或者调用方法,需要把Object强转成指定的子类类型</span>
		<span class="token comment">/**为什么要把Object强转成子类类型?因为想要使用子类的特有功能,父类无法使用子类的特有功能
		 * obj是Object类型,Object中没有Student的属性与功能
		 * 这个操作叫做向下转型--想使用子类特有功能时需要做此操作
		 * */</span>
		<span class="token class-name">Student</span> s <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token class-name">Student</span><span class="token punctuation">)</span>obj2<span class="token punctuation">;</span>
		<span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>s<span class="token punctuation">.</span>name<span class="token punctuation">)</span><span class="token punctuation">;</span>
		<span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>s<span class="token punctuation">.</span>age<span class="token punctuation">)</span><span class="token punctuation">;</span>
		s<span class="token punctuation">.</span><span class="token function">eat</span><span class="token punctuation">(</span><span class="token number">666</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token punctuation">}</span>

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

4.7 熟悉API

自己创建类练习,获取类中的所有资源,熟悉反射中涉及的API

5 暴力反射

指可以将程序中的私有的属性或者方法通过反射技术,暴力的获取到资源。需要使用的常见方法如下:
暴力反射API

5.1 创建 : 测试物料类

创建包: cn.tedu. reflection
创建类: Person.java

package cn.tedu.reflection;

/*本类用来测试暴力反射/
public class Person {
//1.提供私有属性
private String name;
private int age;

<span class="token comment">//2.提供私有方法</span>
<span class="token keyword">private</span> <span class="token keyword">void</span> <span class="token function">save</span><span class="token punctuation">(</span><span class="token keyword">int</span> m<span class="token punctuation">,</span><span class="token class-name">String</span> n<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
   <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"save().."</span><span class="token operator">+</span>m<span class="token operator">+</span>n<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token keyword">private</span> <span class="token keyword">void</span> <span class="token function">update</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
   <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"update().."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

5.2 练习 : 创建测试类

创建包: cn.tedu. reflection
创建类: TestReflect2.java

package tedu.reflection;

import org.junit.Test;

import java.lang.reflect.Field;
import java.lang.reflect.Method;

/本类用于测试暴力反射/
public class TestReflect2 {
/*单元测试方法的格式:@Test + public + void + 无参 */
/1.单元测试1:暴力反射获取和设置私有属性/
@Test
public void getFields() throws Exception {
//1.获取Class字节码对象
Class<?> clazz = Person.class;
//2.获取私有属性
Field field = clazz.getDeclaredField(“name”);
//3.根据获取到的属性对象获取属性的类型
System.out.println(field.getType().getName());//java.lang.String
System.out.println(field.getType());//class java.lang.String
//4.设置属性的值
//4.1 没有对象就通过反射的方式创建对象
Object obj = clazz.newInstance();
//4.2暴力反射!!!需要设置私有可见
field.setAccessible(true);
//4.3通过字段对象来给刚刚创建的对象obj设置name属性值为“派大星”
//set(m,n)-- m:要给哪个对象设置属性值,n:是要设置的属性的具体值
field.set(obj,“派大星”);
//4.4通过字段对象获取刚刚给obj对象设置的私有属性的值
System.out.println(field.get(obj));
}
/2.单元测试2:暴力反射获取和设置私有方法/
@Test
public void getFunction() throws Exception {
//1.获取Class字节码对象
Class<?> clazz = Person.class;
//2.通过暴力反射获取私有方法
/*getDeclaredMethod(m,x,y,z…)
* m:要获取的方法名
* x,y,z…可变参数,是这个方法的参数类型,但注意要加“.class”
* */

Method method = clazz.getDeclaredMethod(“save”,int.class,String.class);
//3.1没有对象就通过反射的方式创建对象
Object obj = clazz.newInstance();
//3.2 想要执行私有方法,也需要先设置私有可见
method.setAccessible(true);
/*invoke(o,x,y,z…),表示通过反射技术执行方法
* o :要执行的是哪个对象的方法
* x,y,z…:执行这个方法【method对象代表的之前获取到的save()】时需要传入的参数
* */

//3.3 通过反射技术invoke(),执行目标对象obj的目标方法method【save()】
//save()被调用时传入的参数是100,“海绵宝宝”
method.invoke(obj,100,“海绵宝宝”);
}
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56

恭喜你,又学会了一个新知识,反射的API比较多,要多多练习哦

下一节 内部类 点这里哦

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值