JavaSE自学笔记021_Real(注解与反射 Annotation、Reflection)

JavaSE自学笔记021_Real(注解与反射)

一、概述

1、Annotation是从JDK5.0开始引入的新技术。
2、Annotation的作用:
(1)不是程序本身,可以对程序做出解释;
(2)可以被其他程序(比如编译器)读取。
3、Annotation的格式
注解是以“@注释名”在代码中存在的,还可以添加一些参数值,例如
@SuppressWarnings(value = "unlocked")
4、Annotation在哪里使用?
可以附加在pankage,class, method, field等上面,相当于给他们添加了额外的辅助信息,我们可以通过反射机制变成实现对这些元数据的访问。

二、内置注解

1、@Override:定义在Java.lang.Override中,此注释只适用于修辞方法,表示一个方法声明打算重写超类中的另一个方法声明。
2、@Deprecated:定义在Java.lang.Deprecated中,此注释可以用于修辞方法,属性,类,表示不鼓励程序使用这样的元素,通常是因为它很危险活着存在更好的选择。
3、@SuppressWarnings:定义在Java.lang.SuppressWarnings中,用来抑制编译时的警告信息。
@SuppressWarnings与前两者不同的是:你需要添加一个参数才能使用。
@SuppressWarnings(“all”)
@SuppressWarnings(“unlocked”)
@SuppressWarnings(value = (“unlocked, deprecation”))

三、元注解

1、元注解的作用是负责注解其他的注解,Java定义了4个标准的meta-annotation类型,他们被用来提供对其他annotation类型的说明。
2、这些类型和他们所支持的类在Java.lang.annotation包中可以找到(@Target, @Retention, @Documented, @Inherited)
@Target:用于描述注解的适用范围(即:被描述的注解可以用在什么地方)。
@Retention:表示需要在什么级别保存该注释信息,用于描述注解的生命周期。
(SOURCE < CLASS < RUNTIME)
@Documented:说明该注解将被包含在Javadoc中。
@Inherited:说明子类可以机车呢个父类中的注解。

四、自定义注解

1、使用@Interface自定义注解的时候,自动继承了Java.lang.annotation.Annotation接口
2、分析:
(1)@interface用来声明一个注解,格式:public @ interface 注解名(定义内容)
(2)其中的每一个方法实际上是声明一个配置参数
(3)方法的名称就是参数的名称
(4)返回值类型就是参数的类型(返回值只能是基础数据类型、Class、String、enum)
(5)可以通过default来声明参数的默认值
(6)如果只有一个参数成员,一般参数名为value

package com.AnnotationStudy;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

//自定义注解
public class CustomAnnotationsTest {
    @MyAnnotation2(name = "China")
    public void test(){

    }
    @MyAnnotation3("南理工")
    public void test2(){

    }
}

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation2{
    //注解的参数格式:参数类型 参数名();
    String name() default ""; //加上default之后,可以在调用的时候不写参数
    int age() default 0;
    int id() default -1; //如果默认值是-1,代表不存在,indexOf

    String[] schools() default {"Nanjing", "njust"};
}

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation3{
    String value();
}

五、反射机制概述(Reflection)

1、Reflection(反射)是Java被视为动态语言的关键,反射机制允许程序在执行期间借助于Reflection API取得任何内部信息,并能够直接操作任何类的内部信息,并能直接操作任意对象的内部属性及方法。
Class c = Class.forName("Java.lang.String")
2、加载完类之后,在堆内存的方法区中就会产生一个Class类型的对象(一个类型只有一个Class对象),这个对象就包含了完整的类的结构信息。我可以通过这个对象看到类的结构,这个对象就像是一面镜子,通过镜子看到类的结构,座椅形象的称之为:反射。
3、
正常方式:引入需要的“包类”名称->通过new实例化->获取实例化对象
反射方式:实例化对象->getClass()方法->得到完整的包类名称
4、反射相关的API
java.lang.Class:代表一个类
java.lang.reflect.Method:代表类的方法‘
java.lang.reflect.Field:代表类的成员变量
java.lang.reflect.Constructor:代表类的构造器

package com.ComntStudy;

//什么是反射
public class ReflectionTest {
    public static void main(String[] args) throws ClassNotFoundException {
        //通过反射获取该类的Class对象
        Class c1 = Class.forName("com.ComntStudy.User");
        System.out.println(c1);
    }
}

//实体类
class User{
    private String name;
    private int id;
    private int age;

    public User() {
    }

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

    public String getName() {
        return name;
    }

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

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public int getAge() {
        return age;
    }

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

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", id=" + id +
                ", age=" + age +
                '}';
    }
}

在这里插入图片描述

六、Class类

对象照镜子后可以得到的信息:某个类的属性、方法、构造器,某个类到底实现了哪些接口,对于每个类而言,JRE都为其保留了一个不变的Class类型的对象,一个Class对象包含了特定某个结构(class/interface/annotation/primitive type/void/[])的相关信息
(1)Class本身也是一个类;
(2)Class对象只能由系统建立对象;
(3)一个加载的类在JVM中指挥有一个实例;
(4)一个Class对象对应的是一个加载到JVM《 中的一个.class文件;
(5)每个类的实例都会集的自己是由那个Class实例所生成;
(6)通过Class可以完整得到一个类中的所有被加载的结构;
(7)Class类是Reflection的根源,针对任何你想动态加载,运行的类,唯有先获得相应的Class对象。

package com.ComntStudy;

//测试Class类的创建方式有哪些
public class Test03 {
    public static void main(String[] args) throws ClassNotFoundException {
        Person person = new Student();
        System.out.println("这个人是" + person.name);

        //创建方式一:通过实例化对象获得
        Class c1 = person.getClass();
        System.out.println(c1);

        //创建方式二:forName获得
        Class c2 = Class.forName("com.ComntStudy.Student");
        System.out.println(c2.hashCode());

        //创建方式三:通过类名.class获得
        Class c3 = Student.class;
        System.out.println(c3.hashCode());

        //创建方式四:基本数据类型的对应类里面有内置的TYPE属性可以获得基础数据类型的类对象
        Class c4 = Integer.TYPE;
        System.out.println(c4.hashCode());

        //======2、获得父类类型========
        Class c5 = c1.getSuperclass();
        System.out.println(c5);
    }
}

class Person{
    public String name;

    public Person() {
    }

    public Person(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                '}';
    }
}

class Student extends Person{
    public Student(){
        this.name = "学生";
    }
}

class Teacher extends Person{
    public Teacher(){
        this.name = "老师";
    }
}

七、哪些类型可以有Class对象?

class:外部类、成员(成员内部类, 静态内部类)
interface:接口
[]:数组
enum:枚举类型
annotation:注解@interface
primitive type:基本数据类型
void

package com.ComntStudy;

import java.lang.annotation.ElementType;

public class Test04 {
    public static void main(String[] args) {
        Class c1 = Object.class; //类
        Class c2 = Comparable.class; //接口
        Class c3 = String[].class; //一维数组
        Class c4 = int[][].class; //二维数组
        Class c5 = Override.class; //注解
        Class c6 = ElementType.class; //枚举
        Class c7 = Integer.class; //基本数据类型
        Class c8 = void.class; //void
        Class c9 = Class.class; //Class

        System.out.println(c1);
        System.out.println(c2);
        System.out.println(c3);
        System.out.println(c4);
        System.out.println(c5);
        System.out.println(c6);
        System.out.println(c7);
        System.out.println(c8);
        System.out.println(c9);
    }
}

八、类加载的内存分析

1、Java内存:
(1)堆:1)存放new的对象和数组。2)可以被所有的线程共享,不会存放别的对象引用。
(2)栈:1)村方基本变量类型(会包含这个基本类型的具体数值)。2)引用对象的变量(会存放这个引用在堆里面的具体地址)。
(3)方法去:1)可以被所有的线程共享。2)包含了所有的class和static变量。

2、类的加载与ClassLoader的理解
(1)加载:将class文件字节码内容加载到内存中,并将这些静态数据转换称为方法区的运行时数据结构,然后生成一个代表这个类的java.lang.Class对象。
(2)链接:将java类的二进制代码合并到JVM的运行状态之中的过程。
1)验证:确保加载的类信息符合JVM规范,没有安全方面的问题。
2)准备:正式为类变量(static)分配内存并设置类变量默认初始值的阶段,这些内存都将在方法区中进行分配。
3)解析:虚拟机常量池内的符号引用(常量名)替换为引用(地址)的过程。
(3)初始化:
1)执行类构造器<clinit>()方法的过程。类构造器<clinit>()方法是由编译器自动收集类中所有类变量的赋值动作和静态代码块中的语句合并产生的。(类的构造器是构造类信息的,不是构造类对象的构造器)。
2)当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先出发其父类的初始化。
3)虚拟机会保证一个类的<clinit>()方法在多线程环境中被正确的加锁和同步。

九、什么时候会发生类的初始化?

1、类的主动引用(一定会发生类的初始化)
(1)当虚拟机启动,先初始化main方法所在的类
(2)new一个类的对象
(3)调用类的静态成员(处理final常量)和静态方法
(4)使用java.lang.reflect包的方法对类进行反射调用
(5)当初始化一个类,如果其夫雷没有被初始化,则先会初始化它的父类
2、类的被动引用(不会发生类的初始化)
(1)当访问一个静态域的时候,只有真正声明这个域的类才会被初始化,例如:当通过子类引用父类的静态变量,不会导致子类初始化。
(2)通过数组定义父类引用,不会出发此类的初始化
(3)引用常量不会触发此类的初始化(常量再链接阶段九存入调用类的常量池中了)

十、类加载器的作用

1、作用:将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后再堆中生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口。
2、类缓存:标准的JavaSE类加载器可以按要求查找类,但是一旦某一个类被加载到类加载器中,他将维持加载(缓存)一段时间,不过JVM垃圾回收机制可以回收这些Class对象。
3、JVM定义了如下类型的加载器:
(1)引导类加载器:用C++编写的,是JVM自带的类加载器,负责Java平台核心库,用来装载核心类库,该加载器无法直接获取。
(2)扩展类加载器:富则ire/lib/ext目录下的jar包或-D java.ext.dirs指定目录下的jar包装入工作库。
(3)系统类加载器:负责java -classpath或-D java.class.path所指的目录下的类域jar包装入工作,是最常用的类加载器。

package com.ClassLoaderStudy;

public class ClassLoaderTest {
    public static void main(String[] args) throws ClassNotFoundException {

        //获取系统的而类加载器
        ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
        System.out.println(systemClassLoader);

        //获取系统类加载器的父类加载器-->扩展类加载器
        ClassLoader parent = systemClassLoader.getParent();
        System.out.println(parent);

        //获取扩展类加载器的父类加载器-->根加载器
        ClassLoader parent1 = parent.getParent();
        System.out.println(parent1);

        //测试当前类是那个加载器加载的
        ClassLoader classLoader = Class.forName("com.ClassLoaderStudy.ClassLoaderTest").getClassLoader();
        System.out.println(classLoader);

        //测试JDK内置的类是谁加载的
        classLoader = Class.forName("java.lang.Object").getClassLoader();
        System.out.println(classLoader);
        
        //如何获得系统类加载器可以加载的路径
        System.out.println(System.getProperty("java.class.path"));
        
    }
}
输出结果:
jdk.internal.loader.ClassLoaders$AppClassLoader@2437c6dc
jdk.internal.loader.ClassLoaders$PlatformClassLoader@57829d67
null
jdk.internal.loader.ClassLoaders$AppClassLoader@2437c6dc
null

最后一个的输出结果:
D:\NewSoftwares\Javasooftware\javaprojects\JavaSEAdvanceProjects\out\production\JavaSEAdvance;
C:\Users\xueli\.m2\repository\org\junit\jupiter\junit-jupiter\5.7.0\junit-jupiter-5.7.0.jar;
C:\Users\xueli\.m2\repository\org\junit\jupiter\junit-jupiter-api\5.7.0\junit-jupiter-api-5.7.0.jar;
C:\Users\xueli\.m2\repository\org\apiguardian\apiguardian-api\1.1.0\apiguardian-api-1.1.0.jar;
C:\Users\xueli\.m2\repository\org\opentest4j\opentest4j\1.2.0\opentest4j-1.2.0.jar;
C:\Users\xueli\.m2\repository\org\junit\platform\junit-platform-commons\1.7.0\junit-platform-commons-1.7.0.jar;
C:\Users\xueli\.m2\repository\org\junit\jupiter\junit-jupiter-params\5.7.0\junit-jupiter-params-5.7.0.jar;
C:\Users\xueli\.m2\repository\org\junit\jupiter\junit-jupiter-engine\5.7.0\junit-jupiter-engine-5.7.0.jar;
C:\Users\xueli\.m2\repository\org\junit\platform\junit-platform-engine\1.7.0\junit-platform-engine-1.7.0.jar

十一、创建运行时类的对象

package com.ComntStudy;

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

//获得类的信息
public class Test08 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
        Class c1 = Class.forName("com.ComntStudy.User");

        //获得类的名字
        System.out.println(c1.getName());
        System.out.println(c1.getSimpleName());

        //获得类的属性
        System.out.println("====================================");
        Field[] fields = c1.getFields(); //只能找到public属性

        fields = c1.getDeclaredFields(); //能够哦获得所有属性
        for(Field field : fields){
            System.out.println(field);
        }

        //获得指定属性的值
        Field name = c1.getDeclaredField("name");
        System.out.println(name);

        //获得类的方法
        System.out.println("===========================");
        Method[] methods = c1.getMethods(); //获得奔雷及其父类的public方法
        for (Method method: methods) {
            System.out.println("正常的" + method);
        }
        methods = c1.getDeclaredMethods(); //获得被雷得所有方法
        for (Method method: methods) {
            System.out.println("getDeclareMethods:" + method);
        }
        //获得指定得方法
        //重载
        Method getName = c1.getMethod("getName", null);
        Method setName = c1.getMethod("setName", String.class);
        System.out.println(getName);
        System.out.println(setName);

        //获得指定得构造器
        System.out.println("====================================");
        Constructor[] constructors = c1.getConstructors();
        for (Constructor constructor: constructors) {
            System.out.println(constructor);
        }
        constructors = c1.getDeclaredConstructors();
        for (Constructor constructor: constructors) {
            System.out.println(constructor);
        }
                //获得指定得构造器
        Constructor declaredConstructor = c1.getDeclaredConstructor(String.class, int.class, int.class);
        System.out.println("指定得构造器:" + declaredConstructor);
    }
}

输出结果:
com.ComntStudy.User
User
====================================
private java.lang.String com.ComntStudy.User.name
private int com.ComntStudy.User.id
private int com.ComntStudy.User.age
private java.lang.String com.ComntStudy.User.name
===========================
正常的public java.lang.String com.ComntStudy.User.getName()
正常的public java.lang.String com.ComntStudy.User.toString()
正常的public void com.ComntStudy.User.setName(java.lang.String)
正常的public int com.ComntStudy.User.getId()
正常的public int com.ComntStudy.User.getAge()
正常的public void com.ComntStudy.User.setId(int)
正常的public void com.ComntStudy.User.setAge(int)
正常的public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
正常的public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
正常的public final void java.lang.Object.wait() throws java.lang.InterruptedException
正常的public boolean java.lang.Object.equals(java.lang.Object)
正常的public native int java.lang.Object.hashCode()
正常的public final native java.lang.Class java.lang.Object.getClass()
正常的public final native void java.lang.Object.notify()
正常的public final native void java.lang.Object.notifyAll()
getDeclareMethods:public java.lang.String com.ComntStudy.User.getName()
getDeclareMethods:public java.lang.String com.ComntStudy.User.toString()
getDeclareMethods:public void com.ComntStudy.User.setName(java.lang.String)
getDeclareMethods:public int com.ComntStudy.User.getId()
getDeclareMethods:public int com.ComntStudy.User.getAge()
getDeclareMethods:public void com.ComntStudy.User.setId(int)
getDeclareMethods:public void com.ComntStudy.User.setAge(int)
public java.lang.String com.ComntStudy.User.getName()
public void com.ComntStudy.User.setName(java.lang.String)
====================================
public com.ComntStudy.User()
public com.ComntStudy.User(java.lang.String,int,int)
public com.ComntStudy.User()
public com.ComntStudy.User(java.lang.String,int,int)
指定得构造器:public com.ComntStudy.User(java.lang.String,int,int)

十二、利用反射创建对象并使用对象中的方法域属性

package com.ComntStudy;

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

//通过反射,动态创建对象
public class Test09 {
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, ClassNotFoundException, NoSuchFieldException {
        //获得class对象
        Class c1 = Class.forName("com.ComntStudy.User");

        //构造一个对象
        Constructor constructor = c1.getDeclaredConstructor(String.class, int.class, int.class);
        User user1 = (User)constructor.newInstance("ZhongZi", 1, 18);
        System.out.println(user1);

        //通过反射调用方法
        User user2 = (User)c1.getDeclaredConstructor(String.class, int.class, int.class).newInstance("", 0, 0);
        //通过反射获取一个方法
        Method setName = c1.getDeclaredMethod("setName", String.class);
        //invoke:激活得意思
        //对象,“方法得值”)
        setName.invoke(user2, "ZhongZi_Real");
        System.out.println(user2.getName());

        //通过反射操作属性
        System.out.println("===========================");
        User user4 = (User)c1.getDeclaredConstructor(String.class, int.class, int.class).newInstance("", 0, 0);
        Field name = c1.getDeclaredField("name");
        //不能直接操作私有属性,我们需要关闭程序得安全检测,属性活着方法的setAccessible(true)
        name.setAccessible(true);
        name.set(user4, "NJUST");
        System.out.println(user4.getName());
    }
}

输出结果:
User{name='ZhongZi', id=1, age=18}
ZhongZi_Real
===========================
NJUST
setAccessible

1、Method和Field,Constructor对象都有setAccessible()方法
2、setAccessible作用是启动和金庸访问安全检查的开关
3、参数值为true则指示反射的对象在使用的时候应该取消Java语言访问检查
(1)提高反射的效率,如果代码中必须使用反射,而该句代码需要频繁的被调用,那么设置为true。
(2)使得原本无法访问的私有成员也可以访问。
4、参数值为false则知识反射的对象应该实施java语言访问检查。

十三、反射操作泛型

1、Java采用泛型擦除的机制来引入泛型,Java中的泛型仅仅是给编译器javac使用的,确保数据的安全性和免去强制类型转换问题,但是,一旦编译完成,所有和泛型有关的类型全部擦除。
2、为了通过反射操作这些类型,Java新增了ParameterizedType,GenericArrayType,TypeVariable和WildcardType几种类型来代表不能被归一到Class类中的类型但是又和原始类型齐名的类型。

(1)ParameterizedType:表示一种参数化类型,比如Collection<String\>
(2)GenericArrayType:表示一种元素类型是参数化类型活着类型变量的数组类型
(3)TypeVariable:是各种类型变量的而公共父接口
(4)WildcardType:代表一种通配符类型表达式
package com.ComntStudy;

import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;
//通过反射获取泛型
public class Test11 {

    public void test01(Map<String, User> map, List<User> list){
        System.out.println("test01");
    }

    public Map<String, User> test02(){
        System.out.println("test02");
        return null;
    }

    public static void main(String[] args) throws NoSuchMethodException {
        Method method = Test11.class.getMethod("test01", Map.class, List.class);
        for (Type genericParameterType : method.getGenericParameterTypes()) {
            System.out.println("#" + genericParameterType);
            if(genericParameterType instanceof ParameterizedType){
                for (Type actualTypeArgument : ((ParameterizedType) genericParameterType).getActualTypeArguments()) {
                    System.out.println(actualTypeArgument);
                }
            }
        }
        method = Test11.class.getMethod("test02", null);
        Type genericReturnType = method.getGenericReturnType();

        if(genericReturnType instanceof ParameterizedType) {
            Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();
            for (Type actualTypeArgument : actualTypeArguments) {
                System.out.println(actualTypeArgument);
            }
        }

    }
}
输出结果:
#java.util.Map<java.lang.String, com.ComntStudy.User>
class java.lang.String
class com.ComntStudy.User
#java.util.List<com.ComntStudy.User>
class com.ComntStudy.User
class java.lang.String
class com.ComntStudy.User

十四、反射操作注解

package com.ComntStudy;

import java.lang.annotation.*;
import java.lang.reflect.Field;

//练习反射操作注解
public class Test12 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
        Class c1 = Class.forName("com.ComntStudy.Student2");

        //通过反射获得注解
        Annotation[] annotations = c1.getAnnotations();
        for (Annotation annotation : annotations) {
            System.out.println(annotation);
        }

        //获得注解的value的值
        TableXlz tableXlz = (TableXlz) c1.getAnnotation(TableXlz.class);
        String value = tableXlz.value();
        System.out.println(value);

        //获得类指定的注解
        Field f = c1.getDeclaredField("name");
        FieldXlz annotation = f.getAnnotation(FieldXlz.class);
        System.out.println(annotation.column());
        System.out.println(annotation.type());
        System.out.println(annotation.length());
    }
}

@TableXlz("db-Student")
class Student2{
    @FieldXlz(column = "db-id", type = "int", length = 10)
    private int id;
    @FieldXlz(column = "db-age", type = "int", length = 10)
    private int age;
    @FieldXlz(column = "db-name", type = "varchar", length = 3)
    private String name;

    public Student2(){

    }

    public Student2(int id, int age, String name) {
        this.id = id;
        this.age = age;
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public int getAge() {
        return age;
    }

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

    public String getName() {
        return name;
    }

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

    @Override
    public String toString() {
        return "Student2{" +
                "id=" + id +
                ", age=" + age +
                ", name='" + name + '\'' +
                '}';
    }
}

//类名的注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface TableXlz{
    String value();
}

//属性的注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface FieldXlz{
    String column();
    String type();
    int length();
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

仲子_real

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值