java基础面试题刷题笔记

语言基础

面向对象和面向过程的区别

面向过程:
优点:性能比面向对象高,因为类调用时需要实例化,开销比较大,比较消耗资源;比如单片机、嵌入式
开发、Linux/Unix等一般采用面向过程开发,能是最重要的因素。
缺点:没有面向对象易维护、易复用、易扩展
面向对象:
优点:易维护、易复用、易扩展,由于面向对象有封装、继承、多态性的特性,可以设计出低耦合的系
统,使系统更加灵活、更加易于维护
缺点:性能比面向过程低
面向过程是具体化的,流程化的,解决一个问题,你需要一步一步的分析,一步
一步的实现。

简述在System.out.println()里面,System, out, println分别是什么

System是系统提供的预定义的final类,
out是一个PrintStream对象,
println是out对象里面一个重载的方法。

描述行( serial )收集器和吞吐量( throughput )收集器的区别 ?

吞吐量收集器使用并行版本的新生代垃圾收集器,它用于中等规模和大规模数据的应用程序。而串行收集器对大多数的小应用(在现代处理器上需要大概100M左右的内存)就足够了。
1:串行GC:
整个扫描和复制过程均采用单线程的方式,相对于吞吐量GC来说简单;适合于单CPU、客户端级别。
2:吞吐量GC:
采用多线程的方式来完成垃圾收集。
适合于吞吐量要求较高的场合,比较适合中等和大规模的应用程序。

java and GC

Java堆是Java虚拟机所管理的内存中最大的一块,它被用来存储对象实例和数组。Java堆是所有线程共享的内存区域,在虚拟机启动时创建,并且可以动态地扩展或缩小。Java堆通常被划分为新生代、老年代和永久代(Java 8之前)/元空间(Java 8及以后)。

当Java堆中的对象不再被引用时,它们就变成了垃圾。Java虚拟机的垃圾回收器会自动检测并回收这些垃圾对象,以释放内存空间。垃圾回收器通过扫描Java堆中的所有对象,查找那些不再被任何其他对象引用的对象,并将其标记为可回收的垃圾对象。然后,垃圾回收器会回收这些垃圾对象占用的内存空间,并将其加入到空闲列表中,等待下一次分配给新的对象。

Java堆的垃圾回收过程称为GC(Garbage Collection)。GC具有自动化的特点,这意味着程序员无需手动管理内存,而只需要关注如何编写高效的代码和使用适当的数据结构来避免产生不必要的垃圾。不同的垃圾回收算法和策略可以对性能产生影响,因此需要合适地选取垃圾回收器和参数来优化系统性能。

32 位 JVM 和 64 位 JVM 的最大堆内存分别是多数?
理论上说上 32 位的 JVM 堆内存可以到达 2^32,即 4GB,但实际上会比这个小很多。不同操作系统之间不同,如 Windows 系统大约 1.5 GB,Solaris 大约 3GB。64 位 JVM允许指定最大的堆内存,理论上可以达到 2^64,这是一个非常大的数字,实际上你可以指定堆内存大小到 100GB。甚至有的 JVM,如 Azul,堆内存到 1000G 都是可能的

Java 中 WeakReference 与 SoftReference的区别

WeakReference适合用于只有在没有强引用时才能被回收的对象。
  SoftReference适合用于在内存不足时才被回收的对象,通常用于实现缓存等功能。
 1.WeakReference(弱引用)
  WeakReference用于引用那些只有在没有强引用时才能被回收的对象。当一个对象只有WeakReference指向它而没有其他强引用时,垃圾收集器会在下一次进行垃圾回收时将其回收。这意味着,如果只有弱引用指向对象,那么该对象会被认为是不可达的,并在垃圾收集时被回收。
  WeakReference通常用于构建高效的缓存、观察者模式等场景,其中需要及时回收对象而不会造成内存泄漏。当没有其他强引用指向对象时,这些弱引用指向的对象会被自动清理。
 SoftReference用于引用可能还有用但并非必需的对象。与弱引用不同的是,当垃圾收集器执行垃圾回收时,只有在内存不足的情况下,才会回收被软引用指向的对象。这使得软引用非常适合实现缓存。
  在内存充足的情况下,即使只有软引用指向对象,对象仍然保持在内存中。但当内存不足时,垃圾收集器会尝试回收这些被软引用指向的对象,以便释放更多内存

强引用
强引用(Strong Reference)是Java中最常见的引用类型,也是默认的引用类型。12

当一个对象被一个强引用变量所引用时,只要该变量还存在,垃圾收集器就不会回收这个对象。即使内存空间不足,Java虚拟机也宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会随意回收具有强引用的对象来解决内存不足的问题。强引用可以直接访问目标对象,且所指向的对象在任何时候都不会被系统回收。然而,如果强引用对象不再使用时,需要显式地将其设置为null,或让其超出对象的生命周期范围,这样垃圾回收器才能回收这个对象。

string为什么是不可变

Java 中的 String 不可变是因为 Java 的设计者认为字符串使用非常频繁,将字符串设置为不可变可以允许多个客户端之间共享相同的字符串

java堆和栈的区别

Java的堆是一个运行时数据区,类的(对象从中分配空间。这些对象通过new、newarray、anewarray和multianewarray等指令建立,它们不需要程序代码 来显式的释放。堆是由垃圾回收来负责的,堆的优势是可以动态地分配内存大小,生存期也不必事先告诉编译器,因为它是在运行时动态分配内存的,Java的垃圾收集器会自动收走这些不再使用的数据。但缺点是,由于要在运行时动态分配内存,存取速度较慢。
栈的优势是,存取速度比堆要快,仅次于寄存器,栈数据可以共享。但缺点是,存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。栈中主要存 放一些基本类型的变量(,int, short, long, byte, float, double, boolean, char)和对象句柄。

string stringbuffer stringbuilder

1、String与StringBuffer的区别的
简单地说,就是一个变量和常量的关系.StringBuffer对象的内容可以修改;而字符串对象一旦产生后就不可以被修改,重新赋值其实是两个对象
StringBuffer的内部实现方式和字符串不同,StringBuffer的在进行字符串处理时,不生成新的对象,在内存使用上要优于串类。所以在实际使用时,如果经常需要对一个字符串进行修改,例如插入,删除等操作,使用StringBuffer要更加适合一些。

字符串:
在String类中没有用来改变已有字符串中的某个字符的方法,由于不能改变一个Java 字符串中的某个单独字符,所以在JDK文档中称String类的对象是不可改变的,然而,不可改变的字符串具有一个很大的优点:编译器可以把字符串设为共享的。 [1]

2、变量:
在程序运行期间,随时可能产生一些临时数据,应用程序会将这些数据 保存在一些内存单元中,每个内存单元都用一个标识符来标识,这些内存单元被称为变量。定义的标识符就是变量名,内存单元中存储的数据就是变量的值。

3、StringBuffer 和 StringBuilder 的区别
当对字符串进行修改的时候,需要使用 StringBuffer 和 StringBuilder 类。
和 String 类不同的是,StringBuffer 和 StringBuilder 类的对象能够被多次的修改,并且不产生新的未使用对象。
StringBuilder 类在 Java 5 中被提出,它和 StringBuffer 之间的最大不同在于 StringBuilder 的方法不是线程安全的(不能同步访问)。
由于 StringBuilder 相较于 StringBuffer 有速度优势,所以多数情况下建议使用 StringBuilder 类。然而在应用程序要求线程安全的情况下,则必须使用 StringBuffer 类。

运算符

请计算 math.round(11.5)等于多少?math.round(-11.5)等于多少?

代码实现思路如下:
Math.round()对数字进行四舍五入
Math.round(11.5)=12
Math.round(-11.5)=11

类型转化

32 位和 64 位的 JVM,int 类型变量的长度是多数?

32 位和 64 位的 JVM 中,int 类型变量的长度是相同的,都是 32 位或者 4 个字节。

解释什么是不可变对象(immutable object)?

不可变对象(Immutable Objects)即对象一旦被创建它的状态(对象的数据,也即对象属性值)就不能改变,反之即为可变对象(Mutable Objects)。
不可变对象的类即为不可变类(Immutable Class)。Java 平台类库中包含许多不可变类,如 String、基本类型的包装类、BigInteger 和 BigDecimal 等。
不可变对象天生是线程安全的。它们的常量(域)是在构造函数中创建的。既然它们的状态无法修改,这些常量永远不会变。

程序控制

面向对象

说出几条 Java 中方法重载的最佳实践?

a)不要重载这样的方法:一个方法接收 int 参数,而另个方法接收 Integer 参数。
b)不要重载参数数量一致,而只是参数顺序不同的方法。
c)如果重载的方法参数个数多于 5 个,采用可变参数

阐述Java抽象和封装的不同点?

抽象和封装是互补的概念。一方面,抽象关注对象的行为。另一方面,封装关注对象行为的细节。一般是通过隐藏对象内部状态信息做到封装,因此,封装可以看成是用来提供抽象的一种策略。两者的区别在于抽象是一种思维方式,而封装则是一种基于抽象的操作方法。

Java中的方法覆盖(Overriding)和方法重载(Overloading)的区别?

重载Override是一个类中多态性的一种表现。方法重载发生在同一个类里面两个或者多个方法的方法名相同但是参数不同的情况。父类与子类的多态性,对父类的函数进行重新定义,方法覆盖是说子类重新定义了父类的方法。方法覆盖必须有相同的方法名,参数列表和返回类型。

解释Java支持多继承么?

Java中类不支持多继承,只支持单继承(即一个类只有一个父类)。 但是java中的接口可以间接实现多继承,,即一个子接口可以有多个父接口。(接口的作用是用来扩展对象的功能,一个子接口继承多个父接口,说明子接口扩展了多个功能,当类实现接口时,类就扩展了相应的功能)。

简述Java接口和抽象类的区别

1:接口中所有的方法隐含的都是抽象的。而抽象类则可以同时包含抽象和非抽象的方法。
2:类可以实现很多个接口,但是只能继承一个抽象类
3:类如果要实现一个接口,它必须要实现接口声明的所有方法。但是,类可以不实现抽象类声明的所有方法,当然,在这种情况下,类也必须得声明成是抽象的。
4:抽象类可以在不提供接口方法实现的情况下实现接口。
5:Java接口中声明的变量默认都是final的。抽象类可以包含非final的变量。
6:Java接口中的成员函数默认是public的。抽象类的成员函数可以是private,protected或者是public。
7:接口是绝对抽象的,不可以被实例化。抽象类也不可以被实例化,但是,如果它包含main方法的话是可以被调用的。
设计层面的不同:抽象类作为很多子类的父类,它是一个个模板式设计。接口是一种行为规范,他是一种辐射式设计

finally和finalize()的区别

无论是否抛出异常,finally代码块都会执行,它主要是用来释放应用占用的资源。finalize()方法是Object类的一个protected方法,它是在对象被垃圾回收之前由Java虚拟机来调用的。

静态资源的加载时期

当类加载器将类加载到JVM中的时候就会创建静态变量,这跟对象是否创建无关。静态变量加载的时候就会分配内存空间。静态代码块的代码只会在类第一次初始化的时候执行一次。一个类可以有多个静态代码块,它并不是类的成员,也没有返回值,并且不能直接调用。静态代码块不能包含this或者super,它们通常被用初始化静态变量

线程

Java编写多线程程序的时候你会遵循哪些最佳实践?

a)给线程命名,这样可以帮助调试。
b)最小化同步的范围,而不是将整个方法同步,只对关键部分做同步。
c)如果可以,更偏向于使用 volatile 而不是 synchronized。
d)使用更高层次的并发工具,而不是使用 wait() 和 notify() 来实现线程间通信,如 BlockingQueue,CountDownLatch 及 Semeaphore。
e)优先使用并发集合,而不是对集合进行同步。并发集合提供更好的可扩展性
volatile 关键字在 Java 中的作用是确保变量的可见性和禁止指令重排序优化。

可见性:一个变量被 volatile 修饰后,当一个线程修改了这个变量的值,新值对其他线程是立即可见的,即其他线程读取这个变量时总是能获取到最新的值。

指令重排序优化:Java 编译器和处理器都能在不改变程序执行结果的前提下对指令进行重排序以优化性能,但是被 volatile 修饰的变量访问不会被重排序。

伪共享

在Java中,伪共享(false sharing)是指多线程环境下,由于缓存一致性协议的影响,不同线程访问同一缓存行中的不同数据造成的性能下降现象。当多个线程同时访问不同变量,但这些变量存储在同一缓存行中时,每个线程只修改自己的变量,但由于缓存一致性协议的要求,需要将整个缓存行的数据进行更新,导致其他线程缓存的数据失效,从而影响了性能。

如何理解线程同步

线程同步:当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作, 其他线程才能对该内存地址进行操作,而其他线程又处于等待状态,实现线程同步的方法有很多

wait和sleep的不同

通常会在电话面试中经常被问到的Java线程面试问题。最大的不同是在等待时wait会释放锁,而sleep一直持有锁。Wait通常被用于线程间交互,sleep通常被用于暂停执行。

创建线程的几种方式

1.继承Thread类
总结:通过继承 Thread 类,重写 run() 方法,而不是 start() 方法
Thread 类底层实现 Runnable 接口
类只能单继承
接口可以多继承

2.实现Runnable接口
总结:通过实现 Runnable 接口,实现 run() 方法,依然要用到 Thread 类

3.实现Callable接口
通过实现 Callable 接口,实现 call() 方法,使用Thread+FutureTask配合可以得到异步线程的执行结果

4.利用线程池来创建线程
用 ExecutorService 创建线程
注意:不建议用 Executors 创建线程池,建议用 ThreadPoolExecutor 定义线程池。
用的无界队列,可能造成 OOM ;不能自定义线程名字,不利于排查问题。

以上四种方式底层都是基于 Runnable

同步方法和同步代码块的区别

Java同步方法和同步代码块的区别 :
同步方法默认用this或者当前类class对象作为锁;
同步代码块可以选择以什么来加锁,比同步方法要更细颗粒度,我们可以选择只同步会发生同步问题的部分代码而不是整个方法;
同步方法使用关键字 synchronized修饰方法,而同步代码块主要是修饰需要进行同步的代码,用 synchronized(object){代码内容}进行修饰;

什么是死锁

1:死锁的概念是什么?
死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。
多线程产生死锁需要四个条件,分别是互斥性,保持和请求,不可剥夺性还有要形成闭环

防止死锁

使用多线程的时候,一种非常简单的避免死锁的方式就是:指定获取锁的顺序,并强制线程按照指定的顺序获取锁。因此,如果所有的线程都是以同样的顺序加锁和释放锁,就不会出现死锁了
多线程产生死锁需要四个条件,分别是互斥性,保持和请求,不可剥夺性还有要形成闭环

IO流

集合

说明哪些Java集合类是线程安全的?

Vector、HashTable、Properties和Stack是同步类,所以它们是线程安全的,可以在多线程环境下使用。Java1.5并发API包括一些集合类,允许迭代时修改,因为它们都工作在集合的克隆上,所以它们在多线程环境中是安全的。

请解释为何Iterator接口没有具体的实现 ?

Iterator接口定义了遍历集合的方法,但它的实现则是集合实现类的责任。每个能够返回用于遍历的Iterator的集合类都有它自己的Iterator实现内部类。
这就允许集合类去选择迭代器是fail-fast还是fail-safe的。比如,ArrayList迭代器是fail-fast的,而CopyOnWriteArrayList迭代器是fail-safe的

简述Java集合框架机制与原理?
(1)使用核心集合类降低开发成本,而非实现我们自己的集合类。
(2)随着使用经过严格测试的集合框架类,代码质量会得到提高。
(3)通过使用JDK附带的集合类,可以降低代码维护成本。
(4)复用性和可操作性。

请说明 Java 中使用 Collections 的最佳实践

a)使用正确的集合类,例如,如果不需要同步列表,使用 ArrayList 而不是 Vector。
b)优先使用并发集合,而不是对集合进行同步。并发集合提供更好的可扩展性。
c)使用接口代表和访问集合,如使用List存储 ArrayList,使用 Map 存储 HashMap 等等。
d)使用迭代器来循环集合。
e)使用集合的时候使用泛型

简述Java用哪两种方式来实现集合的排序?.

你可以使用有序集合,如 TreeSet 或 TreeMap,你也可以使用有顺序的的集合,如 list,然后通过 Collections.sort() 来排序

linkedhashmap & prioityqueue

PriorityQueue 保证最高或者最低优先级的的元素总是在队列头部,但是 LinkedHashMap 维持的顺序是元素插入的顺序。当遍历一个 PriorityQueue 时,没有任何顺序保证,但是 LinkedHashMap 课保证遍历顺序是元素插入的顺序。

list set map queue

4.Map(映射):
-使用键值对(key-value)的方式存储元素。一键是唯一的,不允许重复,每个键关联一个值。 -提供了高效的查找和关联功能。
-常见实现类有HashMap、TreeMap和 LinkedHashMap等。
总结:
-List允许重复元素,维护插入顺序,可以通过索引访问和操作元素。
-Set不允许重复元素,不维护插入顺序,提供高效的查找和去重功能。
-Queue以队列方式存储元素,遵循先进先出原则。 -Map使用键值对存储元素,键唯一,提供高效的查找和关联功能。

collection和collections的区别

Collection是集合类的父类,继承它的主要由set和list
Collections是针对集合类的帮助类,它提供了一系列针对集合的搜索,排序,线程安全化等操作

Collection是一个集合接口。它提供了对集合对象进行基本操作的通用接口方法。Collection接口在Java类库中有很多具体的实现。Collection接口的意义是为各种具体的集合提供最大化的统一操作方式。
以下接口实现了Collection接口:
map、set、list、vector

Collections是一个包装类。它包含各种有关集合操作的静态多态方法。此类不能实例化,就像一个工具类,服务于Java的Collection框架。
(1)排序(Sort)
(2)反转(Reverse)
(3)替换所有元素(Fill)
(4)拷贝(copy)
(5)返回Collections中最小元素(min)
(6)返回Collections中最小元素(max)

Java为什么不直接实现Iterator接口,而是实现Iterable?

1.Iterable接口是各Java类集框架的最高级接口;
2.iterator接口作为Iterable接口中一个方法的返回类型。
Iterator接口的核心方法next()或者hasNext() 是依赖于迭代器的当前迭代位置的。如果Collection直接实现Iterator接口,势必导致集合对象中包含当前迭代位置的数据(指针)。
而Iterable则不然,每次调用都会返回一个从头开始计数的迭代器。多个迭代器是互不干扰的。

集合类为什么没有实现cloneable和serializable接口

其实不难看出,Cloneable是复制对象的,序列化也是针对对象的操作,集合类只是管理对象的一个工具,就好比说list能够线性的管理对象,set集合能够对对象去重等,这些集合类都是针对与为管理对象而产生的。
其实,着两个接口都是针对真是的对象,而不是集合类这样的管理对象的对象。这个从语义上就是集合类的Cloneable接口和Serializable接口

iterator和listiterator的区别

Iterator 和 ListIterator 都是 Java 集合框架中的迭代器,其中 Iterator 是普遍适用于所有实现了 Iterable 接口的集合类的通用迭代器,而 ListIterator 则是专门用于遍历 List 集合的迭代器,它比 Iterator 更加强大,而且只适用于 List 集合。

以下是 Iterator 和 ListIterator 的主要区别:

  1. 支持遍历方向不同:Iterator 只支持从前向后遍历集合,而 ListIterator 支持从前向后遍历和从后向前遍历两个方向。
  2. 支持修改元素的方法不同:Iterator 只支持使用 remove() 方法删除集合中的元素,不支持修改和添加操作;而 ListIterator 则支持使用 set() 修改当前元素,以及使用 add() 方法在当前元素之前添加元素。
  3. 支持元素索引不同:Iterator 没有提供获取元素索引的方法,而 ListIterator 可以通过 nextIndex() 和 previousIndex() 方法获取下一个元素和上一个元素的索引值。

hashmap和hashtable的区别

1:HashMap和Hashtable都实现了Map接口,因此很多特性非常相似。但是,他们有以下不同点:
2:HashMap允许键和值是null,而Hashtable不允许键或者值是null。
3:Hashtable是同步的,而HashMap不是。因此,HashMap更适合于单线程环境,而Hashtable适合于多线程环境。
4:HashMap提供了可供应用迭代的键的集合,因此,HashMap是快速失败的。另一方面,Hashtable提供了对键的列举(Enumeration)。
5:一般认为Hashtable是一个遗留的类。
concurrenthashmap详解和hashmap出现死锁的情概况
fail-fast 机制是java集合(Collection)中的一种错误机制。当多个线程对同一个集合的内容进行操作时,就可能会产生fail-fast事件。例如:当某一个线程A通过iterator去遍历某集合的过程中,若该集合的内容被其他线程所改变了;那么线程A访问集合时,就会抛出ConcurrentModificationException异常,产生fail-fast事件。
总结
快速失败和安全失败是对迭代器而言的。
快速失败:当在迭代一个集合的时候,如果有另外一个线程在修改这个集合,就会抛出ConcurrentModification异常,java.util下都是快速失败。
安全失败:在迭代时候会在集合二层做一个拷贝,所以在修改集合上层元素不会影响下层。在java.util.concurrent下都是安全失败

treeset和hashset的区别

1、速度和内部实现不同
HashSet:用于搜索,插入和删除等操作。这些操作平均需要花费固定时间。HashSet比TreeSet快。HashSet是使用哈希表实现的。
TreeSet:TreeSet以O(Log n)进行搜索,插入和删除,该值高于HashSet。但是TreeSet保留排序的数据。此外,它支持诸如high()(返回最低的较高元素),floor(),ceiling()等操作。这些操作在TreeSet中也是O(Log n),在HashSet中不受支持。 TreeSet是使用自平衡二进制搜索树(红黑树)实现的。 TreeSet由Java中的TreeMap支持。

2、排序方式不同
HashSet中的元素未排序。 TreeSet按照Java中的Comparable或Comparator方法定义的排序顺序维护对象。默认情况下,TreeSet元素以升序排序。它提供了多种方法来处理有序集,例如first(),last(),headSet(),tailSet()等。

3、空对象不同
HashSet允许空对象。 TreeSet不允许null对象并抛出NullPointerException,为什么,因为TreeSet使用compareTo()方法比较键,而compareTo()会抛出java.lang.NullPointerException。

4、比较方式不同
HashSet使用equals()方法比较Set中的两个对象并检测重复项。 TreeSet使用compareTo()方法实现相同目的。如果equals()和compareTo()不一致,即对于两个相等的对象,equals应该返回true,而compareTo()应该返回零,这将打破Set接口的协定,并允许在Set实现中重复使用,例如TreeSet。
如果要排序的Set,较好将元素添加到HashSet中,然后将其转换为TreeSet,而不是创建TreeSet并向其中添加元素。

设计模式

关联聚合组合的区别

关联关系
可以理解为引用关系
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
部分对象可以脱离整体存在

在这里插入图片描述
部分不能脱离整体单独存在,整体控制部分的生命周期

enumeration和iterator的区别

Enumeration速度是Iterator的2倍,同时占用更少的内存。但是,Iterator远远比Enumeration安全,因为其他线程不能够修改正在被iterator遍历的集合里面的对象。同时,Iterator允许调用者删除底层集合里面的元素,这对Enumeration来说是不可能的。

网络

genericservlet和httpservlet的区别

GenericServlet是一个通用的协议无关的Servlet,它实现了Servlet和ServletConfig接口。继承自GenericServlet的Servlet应该要覆盖service()方法。最后,为了开发一个能用在网页上服务于使用HTTP协议请求的Servlet,你的Servlet必须要继承自HttpServlet。

反射

Class.forName:

返回与给定的字符串名称相关联类或接口的Class对象。

Class.forName是一个静态方法,同样可以用来加载类。该方法有两种形式:Class.forName(String name, boolean initialize, ClassLoader loader)和 Class.forName(String className)。第一种形式的参数 name表示的是类的全名;initialize表示是否初始化类;loader表示加载时使用的类加载器。第二种形式则相当于设置了参数 initialize的值为 true,loader的值为当前类的类加载器

什么是反射机制?

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个
对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为
java语言的反射机制。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值