Java与Spring全家桶面试题详解

Java与Spring全家桶面试题详解

一、Java基础

1. Java中的值传递与引用传递

Java只有值传递,没有引用传递。

  • 值传递:方法调用时,实参的值被复制给形参
  • 引用传递:方法调用时,实参的引用(地址)被传递给形参

在Java中传递对象时,传递的是对象引用的副本,而不是对象本身或引用本身。

public void test(Person p) {
    p.setName("Tom"); // 原对象被修改
    p = new Person(); // 只改变了形参引用,原对象不变
}

2. Java基本数据类型与包装类

基本数据类型 大小 默认值 包装类
byte 8位 0 Byte
short 16位 0 Short
int 32位 0 Integer
long 64位 0L Long
float 32位 0.0f Float
double 64位 0.0d Double
char 16位 '\u0000' Character
boolean 1位 false Boolean

区别

  • 基本类型存储在栈中,包装类是对象,存储在堆中
  • 基本类型不能为null,包装类可以为null
  • 基本类型不能调用方法,包装类可以调用方法

3. 自动装箱与拆箱

  • 装箱:基本类型转换为包装类(int → Integer
  • 拆箱:包装类转换为基本类型(Integer → int
Integer i = 10; // 自动装箱,底层使用Integer.valueOf(10)
int n = i;      // 自动拆箱,底层使用i.intValue()

性能影响

  • 频繁装箱拆箱会创建大量对象,增加GC压力
  • 在循环或算法中使用包装类会导致性能下降

4. == 与 equals()的区别

比较方式 比较内容 适用情况
== 基本类型比较值,引用类型比较引用(内存地址) 判断是否是同一个对象
equals() 默认与==相同,但很多类重写了此方法比较内容 判断对象内容是否相同

相同结果的情况

  • 基本类型使用==比较
  • 两个引用指向同一对象
  • 未重写equals()方法的类,equals()等同于==
  • String常量池中的相同字符串

5. String、StringBuilder与StringBuffer的区别

可变性 线程安全 性能 适用场景
String 不可变 安全 少量字符串操作
StringBuilder 可变 不安全 单线程大量拼接
StringBuffer 可变 安全(同步) 多线程大量拼接

6. final、finally、finalize的区别

关键字 用途
final 修饰类(不能被继承)、方法(不能被重写)、变量(不能被修改)
finally try-catch-finally结构中,保证一定会执行的代码块
finalize Object类方法,对象被垃圾回收前调用,不推荐使用

7. static关键字的作用

  • 修饰变量:所有实例共享一个静态变量
  • 修饰方法:不需要实例即可调用
  • 修饰代码块:类加载时执行
  • 修饰内部类:不需要外部类实例即可使用
  • 静态导入:导入静态成员,直接使用

执行顺序:静态变量/静态代码块(按编写顺序) → 实例变量/实例代码块(按编写顺序) → 构造方法

8. try-catch-finally执行顺序

  • 正常情况:执行try块 → 执行finally块
  • 异常情况:执行try块(出现异常处中断) → 匹配catch块 → 执行finally块

即使try或catch中有return语句,finally仍会执行,但finally中的修改对基本类型的return值无效(值传递),对引用类型有效(引用传递)。

9. Java反射机制

反射是在运行时检查、修改类、接口、方法和字段的机制。

原理:通过Class对象获取类的所有信息

应用场景

  • 框架开发(Spring IoC、ORM框架)
  • 动态代理
  • 注解处理
  • 测试工具
Class<?> clazz = Class.forName("java.lang.String");
Method[] methods = clazz.getDeclaredMethods();

10. 动态代理实现方式

实现方式 原理 优缺点
JDK动态代理 基于接口,通过InvocationHandler实现 只能代理实现了接口的类
CGLIB动态代理 基于子类,通过MethodInterceptor实现 可以代理未实现接口的类,但不能代理final类和方法
Javassist 直接操作字节码 更灵活,性能较好

11. Java泛型

特性

  • 类型安全
  • 消除类型转换
  • 支持泛型算法

泛型擦除:编译器在编译时去除泛型信息,替换为Object或上界,保留必要的类型转换。

使用限制

  • 不能用基本类型实例化
  • 不能创建泛型数组
  • 不能创建泛型异常类
  • 不能对静态变量使用泛型类型参数

12. Java序列化机制

序列化:将对象转换为字节流,便于存储或传输 反序列化:将字节流恢复为对象

实现自定义序列化

  • 实现Serializable接口(标记接口)
  • 添加serialVersionUID,保证版本兼容
  • 使用transient关键字排除不需要序列化的字段
  • 重写readObject/writeObject方法自定义序列化逻辑

Serializable与Externalizable区别

  • Serializable是标记接口,自动序列化
  • Externalizable需实现readExternal和writeExternal方法,手动控制序列化过程

13. IO与NIO

特性 BIO (传统IO) NIO
处理方式 阻塞IO 非阻塞IO
面向 流(Stream) 缓冲区(Buffer)
通道 Channel
选择器 Selector
适用场景 连接数少,IO操作密集 连接数多,延迟低

NIO核心组件

  • Buffer(缓冲区)
  • Channel(通道)
  • Selector(选择器)

14. 类加载机制

类加载过程:加载 → 连接(验证、准备、解析) → 初始化 → 使用 → 卸载

类加载器

  • 启动类加载器(Bootstrap ClassLoader):加载JDK核心类
  • 扩展类加载器(Extension ClassLoader):加载扩展库类
  • 应用类加载器(Application ClassLoader):加载应用程序类
  • 自定义类加载器:自定义加载逻辑

双亲委派模型:先委托父加载器加载,父加载器无法加载时子加载器才尝试加载。确保类的唯一性和安全性。

二、Java面向对象

1. 面向对象三大特性

特性 描述 示例
封装 隐藏实现细节,提供公共访问方法 私有属性,公共getter/setter
继承 子类继承父类的特性,实现代码复用 class Dog extends Animal {}
多态 同一方法不同表现形式 方法重写,接口多实现

2. 抽象类与接口的区别

特性 抽象类 接口
实现方式 extends implements
构造方法 可以有 不能有
成员变量 任意访问修饰符 默认public static final
成员方法 抽象或非抽象 默认public abstract(Java 8后有default和static)
多继承 单继承 多实现

使用场景

  • 抽象类:表示"是什么"关系,多个相关类有共同特性
  • 接口:表示"能做什么"关系,不相关类实现相同行为

3. Java 8接口默认方法与静态方法

默认方法:接口中带有实现的方法,用default关键字修饰 静态方法:接口中的静态方法,属于接口,不属于实现类

引入原因

  • 向已有接口添加方法而不破坏已有实现
  • 提供工具方法
interface Vehicle {
    default void start() {
        System.out.println("Default start");
    }
    
    static void compare(Vehicle v1, Vehicle v2) {
        // 比较两个车辆
    }
}

4. 方法重载与方法重写

特性 方法重载(Overloading) 方法重写(Overriding)
定义 同一个类中同名不同参数的方法 子类重新实现父类方法
条件 方法名相同,参数不同(类型、个数、顺序) 方法签名完全相同,返回类型兼容
访问修饰符 无限制 不能比父类更严格
异常 无限制 不能比父类更宽泛
多态类型 编译时多态(静态绑定) 运行时多态(动态绑定)

5. Java多态实现原理

多态通过动态绑定实现,具体过程:

  1. 编译器确定对象的静态类型和方法签名
  2. JVM查找实际对象的实际类型
  3. 根据实际类型找到方法表
  4. 调用方法表中对应的方法实现

虚方法表:每个类都有一个虚方法表(vtable),存储该类的所有虚方法(可被重写的方法)地址。子类继承父类的vtable,并替换重写的方法地址。

6. 继承与实现的区别

  • 继承(extends):子类与父类是"is-a"关系,获得父类的属性和方法
  • 实现(implements):类实现接口,承诺提供特定行为

多重继承:Java不支持类的多重继承,但支持接口的多重继承。可通过接口+内部类实现类似多重继承的效果。

7. Java内部类

类型 特点 使用场景
成员内部类 依赖外部类实例,可访问外部类所有成员 强关联的辅助类
静态内部类 不依赖外部类实例,只能访问外部类静态成员 分组相关类
局部内部类 定义在方法内部,只在方法内可见 一次性使用的辅助类
匿名内部类 没有名字的内部类,创建后立即使用 简化接口/抽象类实现

8. 匿名内部类

特点:没有名字,在创建时同时实例化

使用限制

  • 只能继承一个类或实现一个接口
  • 不能定义构造方法
  • 访问外部局部变量必须是final或effectively final
  • 不能是静态的
new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("匿名内部类");
    }
}).start();

9. Java访问修饰符

修饰符 类内部 同包 子类 其他包
public
protected ×
default(无) × ×
private × × ×

10. 构造方法特点

  • 名称与类名相同
  • 没有返回类型
  • 不能被static、final、abstract、native修饰
  • 如果没有显式定义,会提供默认无参构造
  • 可以重载但不能被继承,也不能被重写
  • 总是调用父类构造方法(显式或隐式super())

11. this关键字和super关键字

this

  • 引用当前对象
  • 调用当前类构造方法
  • 返回当前对象

super

  • 引用父类对象
  • 调用父类构造方法
  • 调用父类方法

12. 向上转型和向下转型

向上转型:子类对象转为父类类型,自动进行

Animal animal = new Dog(); // 向上转型

向下转型:父类对象转为子类类型,需要显式强制类型转换

Dog dog = (Dog) animal; // 向下转型

出现ClassCastException的原因:试图将父类对象转为子类类型时,实际对象不是子类实例。应使用instanceof进行检查。

三、Java集合框架

1. ArrayList与LinkedList区别

特性 ArrayList LinkedList
底层实现 动态数组 双向链表
访问方式 随机访问 顺序访问
查找性能 O(1) O(n)
增删性能 O(n) O(1)
内存消耗

适用场景

  • ArrayList:频繁随机访问,查询操作多
  • LinkedList:频繁增删,尤其是首尾操作多

2. Vector与ArrayList区别

特性 Vector ArrayList
线程安全 安全(方法有synchronized) 不安全
性能
扩容 默认2倍 默认1.5倍
初始版本 JDK 1.0 JDK 1.2

3. HashMap底层实现原理

JDK 1.7:数组+链表

  1. 计算key的hashCode
  2. 对hashCode进行处理得到数组索引
  3. 如遇哈希冲突,将元素链接到链表尾部
  4. 链表采用头插法

JDK 1.8优化:数组+链表+红黑树

  1. 当链表长度大于8且数组长度大于64,链表转为红黑树
  2. 当红黑树节点数小于6,红黑树转为链表
  3. 链表采用尾插法,避免JDK 1.7在并发环境下形成环形链表
  4. 优化了高位运算的hash算法

4. HashMap、Hashtable与ConcurrentHashMap区别

特性 HashMap Hashtable ConcurrentHashMap
线程安全 是(同步方法) 是(分段锁/CAS)
性能 中(并发高)
允许null键值
初始容量 16 11 16
扩容方式 2倍 2倍+1 2倍

ConcurrentHashMap实现线程安全

  • JDK 1.7:分段锁(Segment)
  • JDK 1.8:CAS + synchronized

5. Comparable与Comparator接口区别

特性 Comparable Comparator
包位置 java.lang java.util
排序方法 compareTo compare
实现位置 待比较类内部 独立比较器类
灵活性 低(一种排序) 高(多种排序)

使用示例

// Comparable
public class Person implements Comparable<Person> {
    private String name;
    private int age;
    
    @Override
    public int compareTo(Person o) {
        return this.age - o.age; // 按年龄升序
    }
}

// Comparator
Comparator<Person> ageComparator = new Comparator<Person>() {
    @Override
    public int compare(Person p1, Person p2) {
        return p1.getAge() - p2.getAge();
    }
};

// Java 8 Lambda
Comparator<Person> nameComparator = 
    (p1, p2) -> p1.getName().compareTo(p2.getName());

6. 线程安全的集合类

集合类 实现方式 特点
Vector, Hashtable 同步方法 性能较差,一次仅一个线程访问
Collections.synchronizedXXX 同步代码块 性能一般,一次仅一个线程访问
ConcurrentHashMap 分段锁/CAS 性能好,并发性高
CopyOnWriteArrayList 写时复制 适用于读多写少场景
CopyOnWriteArraySet 基于CopyOnWriteArrayList 适用于读多写少场景
ConcurrentSkipListMap 跳表 适用于高并发环境下有序map

7. HashSet与TreeSet区别

特性 HashSet TreeSet
底层实现 HashMap TreeMap
有序性 无序 有序(自然顺序或比较器)
性能 O(1) O(log n)
是否允许null 允许一个 不允许

HashSet保证元素不重复

  • 利用HashMap的key不能重复特性
  • 插入元素时,以元素为key,固定对象PRESENT为value
  • 判断重复依据equals()和hashCode()方法,须同时重写

8. LinkedHashMap和LinkedHashSet底层实现原理

LinkedHashMap

  • 继承自HashMap,在HashMap基础上增加了双向链表
  • 维护插入顺序或访问顺序(可配置)
  • 可实现LRU缓存

LinkedHashSet

  • 基于LinkedHashMap实现
  • 保证插入顺序的Set

不同点

  • 比HashMap/HashSet多维护了元素顺序
  • 迭代性能更好,但插入/删除性能略差

9. CopyOnWriteArrayList原理

核心思想:写时复制,读写分离

实现机制

  1. 读操作不加锁,直接读取旧数组
  2. 写操作加锁,复制一份新数组,在新数组上修改,然后原子替换旧数组
  3. 适用于读多写少的场景

缺点

  • 内存占用高
  • 写操作性能较差
  • 数据一致性是最终一致性,不保证实时一致性

10. Collection和Collections区别

Collection:集合框架的根接口,定义了集合的基本操作

Collections:工具类,提供集合操作的静态方法

Collections常用方法

  • 排序:sort, reverse, shuffle
  • 查找:binarySearch, min, max
  • 批量操作:fill, copy, addAll
  • 线程安全包装:synchronizedXXX
  • 不可修改包装:unmodifiableXXX
  • 单例集合:singletonXXX

四、Java多线程与并发

1. 进程和线程的区别

特性 进程 线程
定义 程序的一次执行 进程中的执行单元
资源 独立的内存空间 共享进程的内存空间
通信 进程间通信(IPC) 共享内存,简单
切换开销
健壮性 一个进程崩溃不影响其他进程 一个线程崩溃整个进程崩溃

Java线程与操作系统线程

  • JDK 1.2前:绿色线程,由JVM管理
  • JDK 1.2后:原生线程,1:1映射到操作系统线程
  • JDK 19+:虚拟线程(纤程),M:N映射,由JVM调度到操作系统线程

2. 创建线程的方式

方式 实现步骤 优缺点
继承Thread类 继承Thread,重写run方法 简单,但不能继承其他类
实现Runnable接口 实现接口,实例作为Thread参数 可继承其他类,更好的封装
实现Callable接口 实现call方法,结合FutureTask使用 可获取返回值,可抛出异常
线程池 提交任务到线程池 复用线程,控制并发数,推荐使用

3. 线程的生命周期

状态 描述 转换条件
NEW 线程创建但未启动 Thread对象创建后
RUNNABLE 可运行状态 调用start(),等待CPU分配时间片
BLOCKED 阻塞状态 等待获取synchronized锁
WAITING 等待状态 调用wait(), join(), LockSupport.park()
TIMED_WAITING 超时等待 调用sleep(), wait(timeout), join(timeout)
TERMINATED 终止状态 线程执行完毕或抛出异常

4. synchronized关键字原理

原理:基于监视器(Monitor)实现,在字节码中使用monitorenter和monitorexit指令

锁升级过程

  1. 无锁 → 偏向锁 → 轻量级锁 → 重量级锁
  2. 通过对象头中的Mark Word记录锁状态
  3. 偏向锁:只有一个线程访问
  4. 轻量级锁:多个线程交替执行
  5. 重量级锁:多个线程竞争,阻塞等待

对象锁与类锁区别

  • 对象锁:锁实例,不同实例互不影响
  • 类锁:锁类,影响所有实例

5. volatile关键字作用

作用

  • 保证可见性:修改立即对其他线程可见
  • 禁止指令重排:保证有序性
  • 不保证原子性:复合操作仍需加锁

底层实现

  • 内存屏障(Memory Barrier)
  • 写操作时,强制将修改刷新到主内存
  • 读操作时,强制从主内存读取最新值
  • 插入屏障防止指令重排

6. ThreadLocal原理和应用场景

原理

  • 每个Thread维护一个ThreadLocalMap
  • 以ThreadLocal对象为key,存储线程私有数据
  • 不同线程访问同一ThreadLocal,获取自己线程的数据

应用场景

  • 线程上下文数据传递(用户身份、事务控制)
  • 数据库连接、Session管理
  • 线程安全的单例模式

防止内存泄漏

  • 显式调用remove()方法
  • 使用try-finally代码块确保清理
  • 避免使用static修饰ThreadLocal

7. 线程池核心参数

七大核心参数

  1. corePoolSize:核心线程数,长期保持
  2. maximumPoolSize:最大线程数
  3. keepAliveTime:空闲线程存活时间
  4. unit:时间单位
  5. workQueue:任务队列
  6. threadFactory:线程工厂
  7. handler:拒绝策略

合理设置

  • CPU密集型:核心数+1
  • IO密集型:核心数*2或CPU核心数/(1-阻塞系数)
  • 混合型:根据监控和性能测试调整

8. 线程池执行流程

  1. 核心线程未满,创建新线程执行
  2. 核心线程已满,任务入队列
  3. 队列已满,创建临时线程(不超过最大线程数)
  4. 临时线程已满,触发拒绝策略

拒绝策略

  • AbortPolicy:抛出异常(默认)
  • DiscardPolicy:丢弃任务
  • DiscardOldestPolicy:丢弃最早任务
  • CallerRunsPolicy:调用者线程执行

9. Java常见线程池

线程池 特点 适用场景
FixedThreadPool 固定数量线程 负载稳定的服务
CachedThreadPool 弹性线程数 短暂异步小任务
ScheduledThreadPool 定时执行任务 周期性任务
SingleThreadExecutor 单线程执行 顺序执行任务

10. 线程安全的实现方式

  1. 互斥同步:synchronized、ReentrantLock
  2. 非阻塞同步:CAS操作、原子类
  3. 无同步方案:栈封闭、ThreadLocal、不可变对象
  4. 并发容器:ConcurrentHashMap、CopyOnWriteArrayList
  5. 线程安全类库:java.util.concurrent包

11. Lock接口与synchronized区别

特性 Lock synchronized
实现 接口,需显式实现 JVM层面实现
获取/释放 显式调用lock/unlock 自动获取/释放
响应中断 支持 不支持
超时尝试 支持 不支持
公平锁 支持 不支持
非阻塞获取 支持 不支持
灵活性
性能 JDK 1.6前更好,后续差异不大 JDK 1.6后优化显著

ReentrantLock特性

  • 可重入:同一线程可多次获取锁
  • 公平/非公平:控制获取锁的顺序
  • 可中断:lockInterruptibly()支持中断
  • 超时获取:tryLock(timeout)
  • 条件变量:newCondition()

12. 读写锁ReentrantReadWriteLock

特点

  • 读锁共享:多个线程可同时读
  • 写锁独占:一个线程写,其他线程阻塞
  • 写锁可降级为读锁:写锁->获取读锁->释放写锁
  • 读锁不能升级为写锁

适用场景:读多写少的高并发场景,如缓存

13. 死锁

死锁条件(四个必要条件):

  1. 互斥:资源不能同时被多个线程使用
  2. 占有并等待:持有资源的同时等待其他资源
  3. 不可剥夺:资源只能由持有者自愿释放
  4. 循环等待:形成环形等待链

预防死锁

  • 破坏占有并等待:一次性申请所有资源
  • 破坏不可剥夺:等待超时机制
  • 破坏循环等待:按顺序申请资源
  • 使用tryLock避免无限等待

14. Callable与Runnable区别

特性 Callable Runnable
返回值 有(泛型)
异常 可抛出 不可抛出
执行方法 call() run()
使用方式 结合Future/FutureTask Thread构造或线程池

获取线程执行结果

  • Future接口的get()方法
  • FutureTask同时实现Future和Runnable接口
  • CompletableFuture异步回调

15. CompletableFuture实现异步编程

优势

  • 链式调用,避免回调地狱
  • 组合多个异步操作
  • 异常处理机制
  • 支持延迟计算
  • 丰富的超时和取消操作
CompletableFuture.supplyAsync(() -> fetchData())
                 .thenApply(data -> processData(data))
                 .thenAccept(result -> useResult(result))
                 .exceptionally(ex -> handleException(ex));

16. 线程间通信方式

通信方式 实现机制 特点
wait/notify Object类方法,配合synchronized 必须先获得锁,易造成死锁
park/unpark LockSupport类方法 可在任何地方调用,不需要获取锁
Condition Lock接口方法创建条件变量 更精确的线程唤醒,可有多个条件
BlockingQueue 阻塞队列实现生产者消费者 高级抽象,使用简单
CountDownLatch 计数器,等待所有线程完成 一次性使用
CyclicBarrier 循环屏障,等待所有线程到达 可重复使用
Semaphore 信号量,控制并发访问数 控制资源并发访问
Exchanger 线程间交换数据 适用于两个线程交换数据

17. CountDownLatch、CyclicBarrier、Semaphore区别

工具类 作用 应用场景
CountDownLatch 一个或多个线程等待其他线程完成操作 主线程等待所有子线程执行完毕
CyclicBarrier 多个线程相互等待,到达同一同步点 多线程计算,最后合并结果
Semaphore 控制同时访问特定资源的线程数量 限流,控制并发访问数

18. CAS原理

CAS(Compare And Swap):比较并交换,原子操作

原理

  1. 读取旧值A
  2. 计算新值B
  3. 比较内存中的值是否仍为A
  4. 如果是,则更新为B;否则重试或返回失败

问题

  • ABA问题:值从A变成B又变回A,CAS无法感知
  • 循环时间长开销大:自旋CAS长时间不成功,CPU消耗大
  • 只能保证一个变量的原子操作

解决ABA

  • AtomicStampedReference:添加版本号
  • AtomicMarkableReference:添加标记位

五、Java 8及以上新特性

1. Lambda表达式语法

Lambda表达式是函数式编程的核心,语法简洁:

// 基本语法:(参数) -> {表达式}
Runnable r = () -> System.out.println("Hello Lambda!");

// 单参数无需括号
Consumer<String> c = s -> System.out.println(s);

// 多条语句需要花括号
Comparator<Integer> comp = (a, b) -> {
    int result = a.compareTo(b);
    return result;
};

// 返回值自动推断
Function<String, Integer> f = s -> s.length();

2. 函数式接口

函数式接口:只有一个抽象方法的接口,使用@FunctionalInterface注解标记

内置函数式接口

  • Consumer<T>:接收参数,无返回值
  • Supplier<T>:提供数据,无参数
  • Function<T, R>:输入T,输出R
  • Predicate<T>:输入T,返回boolean
  • UnaryOperator<T>:一元操作,类型相同
  • BinaryOperator<T>:二元操作,类型相同

3. 方法引用

类型 语法 示例
静态方法 类名::静态方法 Integer::parseInt
实例方法 实例::方法 str::length
对象方法 类名::实例方法 String::compareToIgnoreCase
构造方法 类名::new ArrayList::new

4. Stream API核心概念

Stream:元素序列的抽象,支持串行或并行聚合操作

特点

  • 不存储数据
  • 不修改源数据
  • 延迟执行
  • 可消费性:只能遍历一次

与集合区别

  • 集合:数据结构,存储元素
  • Stream:计算机制,处理元素

5. Stream操作

中间操作(返回新Stream):

  • filter:过滤元素
  • map:转换元素
  • flatMap:压平嵌套流
  • distinct:去重
  • sorted:排序
  • peek:查看元素
  • limit:限制数量
  • skip:跳过元素

终端操作(执行计算):

  • forEach:遍历元素
  • collect:收集结果
  • reduce:归约为单值
  • count:计数
  • min/max:最小/最大值
  • anyMatch/allMatch/noneMatch:匹配判断
  • findFirst/findAny:查找元素

懒加载特性:中间操作不立即执行,直到遇到终端操作才开始计算

6. 并行流与串行流

区别

  • 串行流:单线程处理
  • 并行流:多线程处理(使用ForkJoinPool)

正确使用并行流

  • 数据量大
  • 操作耗时长
  • 无状态操作
  • 避免共享可变状态
  • 有限使用limit/findFirst等依赖顺序的操作
// 串行流
list.stream().forEach(System.out::printl
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值