Java面试问题总结

1. 简述TCP/IP的三次握手。
第一次握手:建立连接时,客户端发送syn包到服务器,并进入SYN_SEND状态,等待服务器确认。
第二次握手:服务器收到syn包,必须确认客户的SYN,同时自己也发送一个SYN ACK包,此时服务器进入到SYN_RECV状态。
第三次握手:客户端收到服务器的SYN ACK包,向服务器发送确认包ACK,此包发送完毕,客户端和服务器进入就绪状态,完成三次握手。
2. 线程调度的方法。
wait():命令线程等待,释放锁。等待时间结束后,会和其它线程抢占资源。
sleep():命令线程睡眠,不释放锁。即使没有其他线程抢占资源也需要等待睡眠时间到了以后才能继续运行。
yield():命令线程释放所有资源。会立即和其它线程抢占资源。
join():join()方法在哪个线程被调用,则就插入到哪个线程前面。
notify():唤醒一个线程等待JVM调度,但是不能指定唤醒某个线程。
notifyAll():唤醒所有线程等待JVM调度。
3. Thread的start()方法和run()方法的区别,并说明原因。
start()来启动线程,真正实现了多线程运行。此时无需等待run()结束,可直接继续运行下面的代码。通过调用Thread类的start()方法来启动一个线程。此时线程处于就绪状态,并未运行。然后通过Thread类调用run()来完成其运行操作。
run()方法当做普通方法的方式调用。程序要顺序执行,要等待run()方法体执行完毕。只用run()未起到多线程的目的,程序只有一个线程(主线程)。
4. synchronized与static synchronized 的区别
synchronized是对类的当前实例进行加锁,防止其他线程同时访问该类的该实例的所有synchronized块,注意这里是“类的当前实例”,类的两个不同实例就没有这种约束了。那么static synchronized恰好就是要控制类的所有实例的访问了,static synchronized是限制线程同时访问jvm中该类的所有实例同时访问对应的代码快。实际上,在类中某方法或某代码块中有 synchronized,那么在生成一个该类实例后,改类也就有一个监视快,放置线程并发访问改实例synchronized保护快,而static synchronized则是所有该类的实例公用一个监视快了,也也就是两个的区别了,也就是synchronized相当于 this.synchronized,而static synchronized相当于Something.synchronized.
5. Java的常用设计模式。
单例模式
工厂模式
观察者模式
装饰者模式
6. 手写单例模式。解释单例模式,及其用法。
懒汉式
饿汉式
7. 简述其他你知道的设计模式及使用方式和你在开发中在哪使用了此设计模式。
………………………
8. String、StringBuilder、StringBuffer的区别。
1. String是只读字符串,String引用的字符串内容不能改变。
2. StringBuilder/StringBuffer表示的字符串可以直接修改
3. StringBuilder/StringBuffer的方法完全相同,但StringBuilder是在单线程环境下使用
的,因为他的所有方法都未被
Synchronized修饰,因此效率会比StringBuffer高。
9. 简述Ajax及为什么要使用Ajax。
Ajax是一种创建交互式网页应用的网页开发技术
优势:
异步模式,提升用户体验
优化浏览器和服务器之间的传输,减少不必要的数据往返,减少了带宽占用
Ajax引擎在客户端运行,承担了一部分服务器工作,减少大用户量下的服务器负载
10. Ajax的常用参数都有什么。
data:传输的数据
datatype:返回数据类型,json,text
success:回调函数
type:请求方式“GET”,“POST”
url:请求的url
11. MySQL的性能优化方式。
1. 只有一行数据时使用limit 1。
2. 选择正确的引擎:MyISAM,InnoDB。(修改my.ini)。
3. 用not exists代替not in(not in 不能使用索引)。
4. 对操作的优化,尽量不采取不利于索引的操作符,如:in、not in、is null、is not null、<>等。
5. 对经常用来搜索的列,为其建立索引。
12. MySQL的锁。
表级锁:开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低;
行级锁:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高;
页面锁:开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般。
适用:从锁的角度来说,表级锁更适合于以查询为主,只有少量按索引条件更新数据的应用,如Web应用;而行级锁则更适合于有大量按索引条件并发更新少量不同数据,同时又有并发查询的应用,如一些在线事务处理(OLTP)系统。
13. MySQL引擎MyISAM 和 InnoDB 的区别有哪些。
区别:
1. InnoDB支持事务,MyISAM不支持,对于InnoDB每一条SQL语言都默认封装成事务,自动提交,这样会影响速度,所以最好把多条SQL语言放在begin和commit之间,组成一个事务。
2. InnoDB支持外键,而MyISAM不支持。对一个包含外键的InnoDB表转为MYISAM会失败。
3. InnoDB是聚集索引,数据文件是和索引绑在一起的,必须要有主键,通过主键索引效率很高。但是辅助索引需要两次查询,先查询到主键,然后再通过主键查询到数据。因此,主键不应该过大,因为主键太大,其他索引也都会很大。而MyISAM是非聚集索引,数据文件是分离的,索引保存的是数据文件的指针。主键索引和辅助索引是独立的。
4. InnoDB不保存表的具体行数,执行select count(*) from table时需要全表扫描。而MyISAM用一个变量保存了整个表的行数,执行上述语句时只需要读出该变量即可,
速度很快。
5. InnoDB不支持全文索引,而MyISAM支持全文索引,查询效率上MyISAM要高。
如何选择:
1. 是否要支持事务,如果要请选择InnoDB,如果不需要可以考虑MyISAM。
2. 如果表中绝大多数都只是读查询,可以考虑MyISAM,如果既有读写也挺频繁,请使用InnoDB。
3. 系统奔溃后,MyISAM恢复起来更困难,能否接受。
4. MySQL5.5版本开始InnoDB已经成为MySQL的默认引擎(之前是MyISAM),说明其优势是有目共睹的,如果你不知道用什么,那就用InnoDB,至少不会差。
14. MySQL索引。
优点:
1. 快速读取数据。
2. 保证数据记录的唯一性。
3. 实现表与表之间的参照完整性。
4. 使用order by和group by检索时,可以减少时间。
缺点:
1. 本身占用物理空间。
2. 索引也要动态的维护,降低了数据的维护速度。
索引的类型:
普通索引,唯一索引,主键索引,聚集索引
创建索引的原则:
1. 在进程使用过滤器的字段上建立索引。
2. 在经常order by和order by的字段上建立索引。
3. 在不同值比较的字段不要建立索引,如 性别。
4. 不在经常存取的列建立索引。
5. 用于联接的列(主/外键)建立索引。
6. 在经常存取的多个列上建立复合索引。
7. 缺省情况下建立的是非簇集索引。
15. 简述事务特性。
原子性:整个事务中的所有操作。
一致性:在事务开始结束后,完整性约束不被破坏。
隔离性:隔离状态执行操作。
持久性:事务完成后,不会被回滚。
16. 事务的隔离级别。 脏读 不可重复读 幻读
①Read Uncommitted :读未提交 . . .
②Read Committed :读已提交 . .
③Repeatable Read :可重复读 .
④Serialized :序列化
①事务可以读取其它事务未提交的数据
②事务未提交之前所做的修改其它事务不可见
③保证事务多次相同的查询结果一致(MySQL默认)
④保证读取的范围设有新的数据填入
17. JVM的GC机制的目的。
安全性考虑
减少内存泄漏
减少编码工作量
18. JVM内存分为哪几部分。
程序计数器、虚拟机栈、本地方法栈(不考虑回收问题)、方法区、堆(由GC管理)
19. 简述JVM的垃圾回收机制。
可达性分析:
从一个根(GC Roots)向下搜索。搜索的路径称为引用链。当一个对象到GC Roots设有引用链可达时,就认为这个对象是不可达的,就可以被回收了。
引用计数器:
给每个对象分配一个计算器,当有引用指向这个对象时,计数器加1,当指向该对象的引用失效时,计数器减1。最后如果该对象的计算器为0时,java垃圾回收器会认为该对象是可回收的。
结论:
1.对于堆中的对象,用可达性分析判断对象是否存在引用,没有则回收,然后根据引用的类型(强引用、软引用、弱引用、虚引用)不同判断回收机制。
2.对于方法区中的常量和类,当一个常量没有任何对象引用,就可以回收了,而对于类,判断为无用类,就可以回收了。
20. 简述JVM的垃圾回收算法。
1. 标记——清除算法:
标记需要回收的对象,再直接清除被标记的对象,但是内吨的碎片化严重。
2. 复制算法:
将内存分为两块,每次只使用一块,回收的时候把有用的对象复制到另一块,再将原来的那块内存整个清掉。内存的利用率较低。
3. 标记——整理算法:
先标记有用的对象,将有用的对象向内存的一段移动,然后直接回收边界以外的内存。
4. 分代收集算法:
根据对象存活时间分为新生代和老年代,根据其特点,分别采用不同的算法回收,新生代使用复制算法,老年代采用标记——整理算法。
21. JVM中的堆与栈的区别。
1. 申请方式:
stack:由系统自动分配。例如,声明在函数中一个局部变量 int b; 系统自动在栈中为b开辟空间
heap:需要程序员自己申请,并指明大小,在c中malloc函数,对于Java需要手动new Object()的形式开辟
2. 申请后系统的响应:
stack:只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢出。
heap:首先应该知道操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时, 会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序。另外,由于找到的堆结点的大小不一定正好等于申请的大小,系统会自动的将多余的那部分重新放入空闲链表中。
3. 申请大小的限制:
stack:栈是向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意思是
栈顶的地址和栈的最大容量是系统预先规定好的,在
WINDOWS下,栈的大小是2M(也有的说是1M,总之是一个编译时就确定的常数),如果申请的空间超过栈的剩余空间时,将提示overflow。因此,能从栈获得的空间较小。
heap:堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。
4. 申请效率的比较:
stack:由系统自动分配,速度较快。但程序员是无法控制的。
heap:由new分配的内存,一般速度比较慢,而且容易产生内存碎片,不过用起来最方便。
5. heap和stack中的存储内容:
stack:在函数调用时,第一个进栈的是主函数中后的下一条指令(函数调用语句的下一条可执行语句)的地址,然后是函数的各个参数,在大多数的C编译器中,参数是由右往左入栈的,然后是函数中的局部变量。注意静态变量是不入栈的。 当本次函数调用结束后,局部变量先出栈,然后是参数,最后栈顶指针指向最开始存的地址,也就是主函数中的下一条指令,程序由该点继续运行。
heap:一般是在堆的头部用一个字节存放堆的大小。堆中的具体内容有程序员安排。
6. 数据结构层面的区别:
还有就是数据结构方面的堆和栈,这些都是不同的概念。这里的堆实际上指的就是(满足堆性质的)优先队列的一种数据结构,第1个元素有最高的优先权;
栈实际上就是满足先进后出的性质的数据结构。
22. 创建对象时,JVM将对象信息存放到堆中,为什么不存放在栈中,栈的速度比堆快,不考虑内存大小限制。
………………………………
23. Array与ArrayList的区别
存储内容比较:
Array数组可以包含基本类型和对象类型。
ArrayList却只能包含对象类型。
但是需要注意的是:Array数组在存放的时候一定是同种类型的元素。ArrayList就不一定了,因为ArrayList可以存储Object。
空间大小比较:
Array它的空间大小是固定的,空间不够时也不能再次申请,所以需要事前确定合适的空间大小。
ArrayList的空间是动态增长的,如果空间不够,它会创建一个空间比原空间大一倍的新数组,然后将所有元素复制到新数组中,接着抛弃旧数组。而且,每次添加新的元素的时候都会检查内部数组的空间是否足够。
方法上的比较:
ArrayList作为Array的增强版,当然是在方法上比Array更多样化,比如添加全部addAll()、删除全部removeAll()、返回迭代器iterator()等。
24. List集合——ArrayList的底层实现及原理。
1. ArrayList是List接口的可变数组非同步实现,并允许包括null在内的所有元素。
2. 底层使用数组实现。
3. 该集合是可变长度数组,数组扩容时,会将老数组中的元素重新拷贝一份到新的数组
中,每次数组容量增长大约是其容量的
1.5倍,这种操作的代价很高。
4. 采用了Fail-Fast机制,面对并发的修改时,迭代器很快就会完全失败,而不是冒着在将来某个不确定时间发生任意不确定行为的风险。
5. remove方法会让下标到数组末尾的元素向前移动一个单位,并把最后一位的值置空。
25. Map集合——HashMap、HashTable的底层实现及原理。
HashMap
1. HashMap是基于哈希表的Map接口的非同步实现,允许使用null值和null键,但不保证映射的顺序。
2. 底层使用数组实现,数组中每一项是个单向链表,即数组和链表的结合体;当链表长度大于一定阈值时,链表转换为红黑树,这样减少链表查询时间。
3. HashMap在底层将key-value当成一个整体进行处理,这个整体就是一个Node对象。HashMap底层采用一个Node[]数组来保存所有的key-value对,当需要存储一个Node对象时,会根据key的hash算法来决定其在数组中的存储位置,在根据equals方法决定其在该数组位置上的链表中的存储位置;当需要取出一个Node时,也会根据key的hash算法找到其在数组中的存储位置,再根据equals方法从该位置上的链表中取出该Node。
4. HashMap进行数组扩容需要重新计算扩容后每个元素在数组中的位置,很耗性能。
5. 采用了Fail-Fast机制,通过一个modCount值记录修改次数,对HashMap内容的修改都将增加这个值。迭代器初始化过程中会将这个值赋给迭代器的expectedModCount,在迭代过程中,判断modCount跟expectedModCount是否相等,如果不相等就表示已经有其他线程修改了Map,马上抛出异常。
HashTable
1. Hashtable是基于哈希表的Map接口的同步实现,不允许使用null值和null键。
2. 底层使用数组实现,数组中每一项是个单链表,即数组和链表的结合体。
3. Hashtable在底层将key-value当成一个整体进行处理,这个整体就是一个Entry对象。Hashtable底层采用一个Entry[]数组来保存所有的key-value对,当需要存储一个Entry对象时,会根据key的hash算法来决定其在数组中的存储位置,在根据equals方法决定其在该数组位置上的链表中的存储位置;当需要取出一个Entry时,也会根据key的hash算法找到其在数组中的存储位置,再根据equals方法从该位置上的链表中取出该Entry。
4. synchronized是针对整张Hash表的,即每次锁住整张表让线程独占。
26. Set集合——HashSet的底层实现及原理。
1. HashSet由哈希表(实际上是一个HashMap实例)支持,不保证set的迭代顺序,并允许使用null元素。
2. 基于HashMap实现,API也是对HashMap的行为进行了封装,可参考HashMap
27. HashMap数据存储的原理。
HashMap底层就是一个数组结构,数组中的每一项又是一个链表。当新建一个HashMap的时候,就会初始化一个数组。Entry就是数组中的元素,每个 Map.Entry 其实就是一个key-value对,它持有一个指向下一个元素的引用,这就构成了链表。简单地说,HashMap 在底层将 key-value 当成一个整体进行处理,这个整体就是一个 Entry 对象。HashMap 底层采用一个 Entry[] 数组来保存所有的 key-value 对,当需要存储一个 Entry 对象时,会根据hash算法来决定其在数组中的存储位置,在根据equals方法决定其在该数组位置上的链表中的存储位置;当需要取出一个Entry时,也会根据hash
算法找到其在数组中的存储位置,再根据
equals方法从该位置上的链表中取出该Entry。从HashMap中get元素时,首先计算key的hashCode,找到数组中对应位置的某一元素,然后通过key的equals方法在对应位置的链表中找到需要的元素。
28. FastDFS的上传及下载流程及原理。
上传:
1. Client客户端询问tracker服务器上传到的storage服务器的地址
2. tracker服务器返回一台可用的storage服务器地址
3. Client直接和storage服务器建立连接,完成上传
4. storage返回文件信息,将信息存在数据库中
下载:
1. Client客户端询问tracker服务器,查询可用的storage服务器地址值
2. 使用数据库中的filed_id查询文件
3. Client直接和storage服务器建立连接完成下载
29. RabbitMQ的简述及原理。
30. 简述Dubbox及Zookeeper及其原理,zookeeper使用了哪种设计模式。
Zookeeper使用观察者设计模式
Zookeeper收集需要的数据,接受观察者注册,当数据变化,Zookeeper会通知观察者做对应的反应。
Dubbox可以抽象一个服务提供方,一个服务消费方两方面:
1. 服务提供方发布服务到注册中心
2. 服务消费方以注册中心订阅服务
3. 服务消费方调用已经注册的可用服务
31. Redis简述,Redis的几种数据类型,你是如何使用的。
数据类型:String,Set,List,Hash,ZSet
32. 你知道LRU算法么?
LRU(Least recently used,最近最少使用)算法根据数据的历史访问记录来进行淘汰数据,其核心思想是“如果数据最近被访问过,那么将来被访问的几率也更高”
33. 简述SSM框架。
Spring + SpringMVC + MyBatis
34. 简述IOC、AOP及实现原理。
IOC:Spring中的IOC的实现原理就是工厂模式加反射机制。把IOC容器的工作模式看做是工厂模式的升华,可以把IOC容器看作是一个工厂,这个工厂里要生产的对象都在配置文件中给出定义,然后利用编程语言提供的反射机制,根据配置文件中给出的类名生成相应的对象。
AOP:Spring AOP使用动态代理技术在运行期织入增强代码。使用两种代理机制:基于JDK的动态代理(JDK本身只提供接口的代理);基于CGlib的动态代理。
1. JDK的动态代理主要涉及java.lang.reflect包中的两个类:Proxy和InvocationHandler。其中InvocationHandler只是一个接口,可以通过实现该接口定义横切逻辑,并通过反射机制调用目标类的代码,动态的将横切逻辑与业务逻辑织在一起。而Proxy利用InvocationHandler动态创建一个符合某一接口的实例,生成目标类的代理对象。(只能为接口创建代理实例)。
2. CGLib采用底层的字节码技术,为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类的调用方法,并顺势织入横切逻辑。
35. 你是如何搭建SSM框架的。
…………
36. 简述SpringMVC的工作原理。
1. 用户向服务器发送请求,请求被springMVC前端控制器DispatchServlet捕获。
2. DispatcherServle对请求URL进行解析,得到请求资源标识符(URL),然后根据该URL调用HandlerMapping将请求映射到处理器HandlerExcutionChain。
3. DispatchServlet根据获得Handler选择一个合适的HandlerAdapter适配器处理。
4. Handler对数据处理完成以后将返回一个ModelAndView()对象给DisPatchServlet。
5. Handler返回的ModelAndView()只是一个逻辑视图并不是一个正式的视图,DispatcherSevlet通过ViewResolver试图解析器将逻辑视图转化为真正的视图View。
6. DispatcherServle通过model解析出ModelAndView()中的参数进行解析最终展现出完整的view并返回给客户端。
37. 简述Struts2的工作原理。
1. 客户端(Client)向Action发用一个请求(Request)。
2. Container通过web.xml映射请求,并获得控制器(Controller)的名字。
3. 容器(Container)调用控制器StrutsPrepareAndExecuteFilter。
4. 控制器(Controller)通过ActionMapper获得Action的信息。
5. 控制器(Controller)调用ActionProxy。
6. ActionProxy读取struts.xml文件获取action和interceptor stack的信息。
7. ActionProxy把request请求传递给ActionInvocation。
8. ActionInvocation依次调用action和interceptor。
9. 根据action的配置信息,产生result。
10. Result信息返回给ActionInvocation。
11. 产生一个HttpServletResponse响应。
12. 产生的响应行为发送给客服端。
38. 简述SpringMVC及Struts2的区别。
1. SpringMVC入口为Servlet,Struts2为一个Filter过滤器。
2. SpringMVC基于方法开发,Struts2基于类开发。
3. Struts使用值栈,前台使用OGNL/JSTL表达式,SpringMVC用ModelAndView用request域传到页面。
39. 手写MyBatis的mapping.xml配置文件。
…………
40. 简述JDBC、Hibernate及MyBatis,并谈谈他们的区别。
JDBC进行编程步骤:
1. 使用jdbc编程需要连接数据库,注册驱动和数据库信息。
2. 操作Connection,打开Statement对象。
3. 通过Statement对象执行SQL,返回结果到ResultSet对象。
4. 使用ResultSet读取数据,然后通过代码转化为具体的POJO对象。
5. 关闭数据库相关的资源。
区别:
1)从层次上看,JDBC是较底层的持久层操作方式,而Hibernate和MyBatis都是在JDBC的基础上进行了封装使其更加方便程序员对持久层的操作。
2)从功能上看,JDBC就是简单的建立数据库连接,然后创建statement,将sql语句传给statement去执行,如果是有返回结果的查询语句,会将查询结果放到ResultSet对象中,通过对ResultSet对象的遍历操作来获取数据;Hibernate是将数据库中的数据
表映射为持久层的
Java对象,对sql语句进行修改和优化比较困难;MyBatis是将sql语句中的输入参数和输出参数映射为java对象,sql修改和优化比较方便.
3)从使用上看,如果进行底层编程,而且对性能要求极高的话,应该采用JDBC的方式;如果要对数据库进行完整性控制的话建议使用Hibernate;如果要灵活使用sql语句的话建议采用MyBatis框架。
41. 重写与重载的区别。
重载:方法同名不同参
重写:子类覆写父类
重载(overload):
1. 参数类型、个数、顺序至少有一个不同
2. 重载与返回值类型无关
3. 存在同类中
重写(override):
1. 方法名、参数、返回值必须相同
2. 子类方法不能缩小父类方法的访问资源权限
3. 子类方法不能抛出比父类方法更多的异常
4. 方法被定义为final不能被访问
5. 存在于父类与子类中
42. 如何使用JS判断当前对象是否是undefined。
使用 typeOf
43. 说说final关键字。
1. 修饰类:
表明此类不可被继承,final类中的所有成员都会被隐式指定为final。
2. 修饰方法:
类的private方法会隐式的指定为final方法。
原因:1. 锁定方法,以防任何继承类修改它的定义。
2. 效率,final方法会转为内嵌调用。
只有在想明确禁止该方法在子类中被覆盖的情况下。
3. 修饰变量:
修饰基本数据类型,在初始化后,不能改变。
修饰引用类型,在初始化后,不能再让其指定另一对象。
44. 说说static关键字。
1. static方法:
静态方法不能引用非静态方法和非静态成员变量。非静态可以引用静态的。
使用 类.方法名 调用方法(工具类)。
2. static变量:
静态变量被所有的对象共享。在内存中只有一个副本,它当且仅当在类初始加载时会初始化,static成员变量的初始化顺序按照定义顺序进行初始化。
3. static代码块:
static代码块可以放在类的任何地方,类可以有多个static代码块。在类加载时,按顺序执行static,并且只会执行一次。
45. 事务知道吧。说一下原生的事务开启及提交方式。
1. 关闭事务自动提交:
connection.setAutoCommit(false);
2. 提交事务:
connection.commit();
3. 回滚事务:
connection.rollback();
46. 简述Synchronized关键字。
synchronized是Java中的关键字,是一种同步锁。它修饰的对象有以下几种:
1. 修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象。
2. 修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象。
3. 修改一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象。
4. 修改一个类,其作用的范围是synchronized后面括号括起来的部分,作用主的对象是这个类的所有对象。
47. JDK的动态代理与CGLIB的区别。
(1)JDK动态代理只能对实现了接口的类生成代理,而不能针对类。
(2)CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法因为是继承,所以该类或方法最好不要声明成final。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值