导图社区 java基础知识
Java是一门面向对象编程语言,不仅吸收了C++语言的各种优点,还摒弃了C++里难以理解的多继承、指针等概念,因此Java语言具有功能强大和简单易用两个特征。
编辑于2021-07-13 17:54:27java知识(持续更新)
数值类型
将string转换成数字
处理浮点数不精确的问题
BigDecimal
用字符串参数,方式用浮点数创建的时候出现精度问题String.valueOf
字符串
实现方式utf-16,如果2个字节不够就用两个也就是4个字节
版本区别
1.8版本重要方法及字段
CASE_INSENSITIVE_ORDER
用于不关心大小写排序
equal()和equalIgnoreCase()
join()使用StringJoiner用来拼接字符串
internal()返回常量池对应版本的引用
String和字符数组的区别
char和codePoint
关键字
transient
对于实现Serializable的java序列化该字段会被忽略
集合
Collections
相关方法
asLifoQueue
转换成后进先出的队列
排序
sort
默认采用Array.sort在插入排序,快排,归并之间做选择
默认以自然顺序升序
reverse
反转
shuffle
打乱
swap
交换
同步控制
synchronizedXxx
Collection
List
RandomAccess
ArrayList实现,LinkedList未实现
对实现该接口的List,会采用索引遍历,未实现的会用迭代器遍历,从来减少访问的开销
Map
HashMap
造成cpu100%原因
版本区别
1.8版本使用红黑树替换链表
ConcurrentHashMap参考juc
Set
Array
java.util.Arrays.parallelSort(array)使用forkjoin利用并行排序提高效率
队列
时间
1.8之前
Date
Calendar
SimpleDateFormat
线程不完全
维护了Calendar
需要在同一个线程中使用
1.8及之后
LocalDate
LocalTime
LocalDateTime
Object
getClass
hashCode
equals
==
基本数据类型比较的就是值是否相等
引用类型比较的是是不是同一个对象
clone
使用clone方法需要对应类实现Cloneable,并实现clone方法
toString
notify
notifyAll
wait
finalize
对象被回收前被执行
错误捕获
SocksSocketIml重写了finalize来释放资源
因为其相对来说执行优先级比较低,所以有可能在吃紧的时候反而不容易被执行而造成内存泄露
链接
1.5特性
自动拆装箱
枚举
静态导入
可变参数
methodName([argumentList], dataType... argumentName);
内省(Introspector)
与反射的区别
反射是在运行状态把java中各种成分分别映射到对应的java对象,可以动态获取属性调用对象等
内省机制是通过反射来实现的,BeanInfo用来暴露一个bean的属性、方法和时间
重要类型
Introspector
PropertyDescriptor
MethodDescriptor
泛型
for-each
1.6特性
AWT
Desktop
SystemTray
JAXB2实现XML映射
StAX
用Console开发控制台程序
对脚本语言的支持如: ruby, groovy, javascript
使用Compiler API
插入式注解处理 API
轻量级Http Server API
Common Annotations
将javaEE5的一部分移到了javaSE
1.7特性
Objects
equals(Object,Object)
deepEquals(Object,Object)
有对数组元素的判断
compare(T,T,Comparator)
requireNonNull(T obj)
requireNonNull(T obj, String errMessage)
switch可以处理字符串
<>可以类型推断
新增加一些取环境信息的工具方法
File System.getJavaIoTempDir() // IO临时文件夹 File System.getJavaHomeDir() // JRE的安装目录 File System.getUserHomeDir() // 当前用户目录 File System.getUserDir() // 启动java进程时所在的目录 .......
Boolean类型反转,空指针安全,参与位运算
Character.equalsIgnoreCase(char ch1, char ch2)
安全的加减乘除
Math.safe....
对集合加强
数值可加下划线
支持二进制文字
Collections
自定义自动关闭 try-with-resource
try-catch修改
try { ...... } catch(ClassNotFoundException|SQLException ex) { ex.printStackTrace(); }
fork/join
见并发
其他1.8新特性
接口可以添加默认和静态方法
Lambda及函数式接口
函数式接口
定义:只有一个抽象方法的接口(可以定义默认方法),可以用@FunctionalInterface标记接口
@FunctionalInterface public interface FunctionClassTest<T> { String handle(T t); default void defaultTest() { System.out.println("default"); } }
函数式接口创建实现对象
(方法参数)-> 抽象方法实现
如果只有一条语句return可省略:(String s) -> String.join(s,"test1", "test2")
抽象方法实现
静态方法调用,并与抽象方法参数一致,则可以将 (Integer param) -> String.valueOf(param) 写成 String::valueOf
构造器引用
private Person construntorRef(Supplier<Person> sup){ Person p=sup.get(); return p; } @Test public void testConstructorRef(){ Person p=construntorRef(Person::new); System.out.println(p); }
成员方法调用
private Person construntorRef(Supplier<Person> sup){ Person p=sup.get(); return p; } @Test public void testConstructorRef(){ Person p=construntorRef(Person::new); System.out.println(p); }
只要成员的参数列表和FI参数一致
实例方法:可以直接创建函数接口实现类对象的方法 也可以采用::的方式,lamda表达式的第一个参数是是实例方法的调用者,第二个参数是实例方法的参数 所以该实例方法是有要求的
@FunctionalInterface public interface FunctionClassTest { boolean handle(Integer s1, Integer i1); default void defaultTest() { System.out.println("default"); } } public static void main(String[] args) { FunctionClassTest f = Integer::equals; }
抽象方法注意点
抽象方法中可以使用局部变量但是但是最好使用是final类型或者事实上final类型的,不然可能发生意外情况
关于lambda的一些讨论
缺点
序列化相关异常见上面的讨论
影响描述
spring低版本无法使用
Optional
创建对象
empty()
生成一个EMPTY
of(T value)
如果是null会直接抛出异常
ofNullable(T value)
如果是null会放EMPTY
主要方法
get()
返回值或者null直接报错
isPresent()
判断值是否为null
ifPresent(Consumer)
只有值不会null的时候才调用Consumer处理 其中Consumer有一个嵌套的方法andThen 通过andThen可以组合成一套处理逻辑,按个执行
filter(Predicate)
判断是否符合条件
Predicate提供一般方法的定义和可以实现的test方法
map(Funciont)
Function
apply
主要方法
compose
先执行一个Function
andThen
后执行一个Function
identity
相当于t->t,也就是一个返回自身的Function,主要是在参数是Function的方法中可以直接使用该方法,就不用写t->t这种代码
如果有值调用Function,没有返回empty()
flatMap(Function)
与上面相同只是Function的返回值就是Optional
orElse(T other)
有值返回否则返回other值
orElseGet(Supplier<? extends T> other)
有值返回否则返回supplier提供的值
orElseThrow(Supplier<? extends X> exceptionSupplier)
有值返回否则返回supplier提供的异常
数值类型
OptionalInt
OptionalDouble
OptionalLong
Stream
相关方法 一般不直接返回值而是返回Optional和Stream
filter()
map()
mapToInt()
mapToLong()
mapToDouble()
flatMap,flat...
就是传入的Function返回值是对应的Stream
distinct()
sorted()
sorted(Comparator)
peek(Consumer)
limit(Long)
skip(Long)
forEach(Consumer)
forEachOrdered(Consumer)
toArray()
toArray(IntFunction)
reduce(T,BinaryOperator)
BinaryOperator继承BiFunction,BiFunction可以传两个参数返回一个结果
collect(Supplier<R>,BiConsumer<R, ? super T>,BiConsumer<R, R> )
collect(Collector<? super T, A, R> collector)
min
max
count
anyMatch(Predicate<? super T> predicate)
allMatch(Predicate<? super T> predicate)
noneMatch(Predicate<? super T> predicate)
findFirst()
findAny()
builder()
Builder继承了Consumer
empty()
of(T...)
iterate(final T seed, final UnaryOperator<T> f)
继承Function,增identity
generate(Supplier<T> s)
Supplier只有get()
concat(Stream<? extends T> a, Stream<? extends T> b)
不推荐效率低
并行流
推荐,可以简单的进行并行处理,底层用的forkjoinpool
重复注解
更好的类型推断
拓宽注解的应用场景
Base64
JUC增强
ConcurrentHashMap添加聚焦支持
ConcurrentForkJoinPool添加新的方法
新增StampedLock
可以替换ReadWriteLock
atomic
DoubleAccmulator
DoubleAdder
LongAccumulator
LongAdder
伪共享
Annotation 注解
io
具体参考io思维导图
io分类
网路io
文件io
linux的五种io模型
阻塞io
非阻塞io
io多路复用
信号驱动io
异步io
bio(传统io)
java.io包下
linux 下read/write
nio
java.nio包下
linux 下select/epoll
存在的相关问题
bio
java.nio包下,又进行了上层封装,不推荐使用
linux 下select/epoll
io资源限制
主要方面
监控
反射
java反射在运行时提供的功能
判断对象所属类
构造任意一个类的对象
判断一个类所具有的成员变量和方法
调用一个对象的成员变量和方法
生成动态代理
java反射相关api
Class
Method
Field
Constructor
代理
静态代理
Aspectj
动态代理
spring中的动态代理
jdk动态:如果目标对象实现了接口
需要类实现接口,调用的时候需要指定实例
cglib: 如果目标对象没有实现接口
基于asm
如果目标对象强制cglib,则使用cglib
javassist
dubbo
java Agent
SPI机制
高并发与多线程
多线程基本知识
线程安全的三个特性
原子性
原子访问
读写引用变量和基础数据变量除了long和double是原子的
读写申明为volatile的变量是原子的
原子操作间是不能交替的执行,所以不用担心其他线程的干扰,但还是不能排除cpu缓存一致性错误
Atomic actions cannot be interleaved, so they can be used without fear of thread interference.
内存模型lock/unlok
unlock前必须要把变量同步到主内存中
字节码monitorenter/monitorexit
实现
synchronized
顺序性
Out-Of-Order Execution
为了提高处理器内部运算单元的利用率,对输入代码乱序执行
java虚拟机即时编译也有指令重排序优化
happen-before
java实现
volatile
synchronized
可见性
高速缓存
解决内存和处理器速度的差值
出现了缓存一致性问题
java实现
volatile
synchronized
final
构造器中一旦初始化完成,并且构造器没有把this引用传递出去
线程的实现方式
内核线程实现
1:1
内核线程,内核通过调度器将线程任务映射到各个处理器上,这里可能是多个内核线程对应一个处理器
轻量级进程(我通常意义上的线程)与内核线程一般是1:1的关系
基于内核线程操作,所以线程的一般操作都要进行用户态和内核态的切换
用户线程实现
1:N
建立在用户空间的线程库,系统内核无法感知
现在的Golang,Erlang,曾经的Java,Ruby
用户线程加轻量级进程混合实现
N:M
在Solaris、HP_UX等系统中支持这种线程模型
java线程实现
一般主流操作系统上的hotspot用内核线程实现
缓存一致性问题
MSI
MESI
MOSI
Synapse
Firefly
Dragon protocol
等
java内存模型
目的
定义程序中各种变量的访问规则
包括实例字段、静态字段和构成数组对象的元素
不包括局部变量与方法参数等
说明
与java内存区域的划分基本没太大关系
组成
主内存线程共有
工作内存线程私有
1. 工作内存可能会有对象的引用和用到字段复制值,一般不会复制整个对象到工作线程 2. 工作内存需要通过主内存访问共享数据,不能直接堆放工作内存中的变量
内存交互
变量如何从主内存拷贝到工作内存,从工作内存同步回主内存。
除了double和long的load、store、read、write操作外都具有原子性
操作
lock
unlock
read/load
use
assign
store/write
线程状态
Thread.State
NEW
线程还没开始执行
RUNABLE
这个线程虚拟机执行中,但是可能在等待操作系统的其他资源,比如处理器
BLOCKED
此状态,线程正在等待monitor锁进入或重入synchronized block/method 在调用Object.wait()之后
WAITING
Object.wait with no timeout Thread.join with no timeout LockSupport.park
需要等待其他线程的的特殊动作,比如其他线程调用Object.notify(),Object.notifyAll()等。Thread.join(),需要等待特定线程结束。
TIMED_WAITING
Thread.sleep Object.wait with timeout Thread.join with timeout LockSupport.parkNanos LockSupport.parkUntil
TERMINATED
方法方式
java中并发中常用的方式不外乎cas和锁
CAS:Compare and Swap
一般可以通过循环的方式去调用native的比较交换方法一般在unsafe类
unsafe只有启动类加载器加载的才能访问
ABA问题
锁
公平性
ReentrantLock可以设置公平性,但一旦设置公平则性能急剧下降
Synchronized非公平锁
锁优化技术
适应性自旋
自旋锁占用cpu时间减少内核线程的操作
jdk默认自旋次数为10,可以通过-XX:PreBlockSpin修改
不过自适应自旋之后,会根据前一次同一个锁自旋时间及锁拥有者状态等进行自旋次数的选择
一般是针对重量级锁?
锁消除
一般是即时编译器将逃逸分析等数据进行判断是否要对锁进行消除
锁粗化
虚拟机将需要进行锁同步区域扩大的时候进行扩大处理
比如几行代码不停的加锁释放锁
锁膨胀
比如下面的轻量级锁膨胀为重量级锁
偏向锁
偏向第一个线程,不需要再cas去操作,直到有线程尝试去获取该锁
当一个对象被计算过hash,则无法进入偏向锁,并直接膨胀为重量级锁
可以通过-XX:UseBiasedLocking禁用偏向锁
轻量级锁
cas修改mark word的值
修改成功,或者指向当前线程,代表拥有该锁
失败,如果出现两条以上竞争则膨胀为重量级锁
cas释放锁
成功释放锁
失败唤醒挂起线程
有竞争的情况下该锁更慢
重量级锁
操作系统互斥量实现
实现方式
Thread
Synchronized
不公平实现
jdk6之前直接用的操作系统内部的互斥锁
现在会有三种不同Monitor实现
偏斜锁
撤销偏斜锁使用轻量级锁是比较重的行为,可以通过-XX:-UseBiasedLocking关闭偏斜锁
通过CAS设置Mark Word
轻量级锁
重量级锁
ThreadLocal
Future和CompletableFuture
Future
方法
cancel
isCancelled
isDone
get
get(long timeout, TimeUnit unit)
局限
无法将两个异步计算合并为一个(这两个异步计算之间相互独立,同时第二个又依赖于第一个的结果)
无法等待 Future 集合中的所有任务都完成
无法仅等待 Future 集合中最快结束的任务完成,并返回它的结果。
CompletableFuture
方法
简介
async的方法是异步方法
创建方式
CompletableFuture()
CompletableFuture<U> supplyAsync(Supplier<U> supplier)
CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor)
异步返回结果
CompletableFuture<Void> runAsync(Runnable runnable)
CompletableFuture<Void> runAsync(Runnable runnable, Executor executor)
异步返回void
CompletableFuture<U> completedFuture(U value)
allOf
当次需要所有任务完成
anyOf
当次只要一个任务完成
获取结果
T get()
T get(long timeout, TimeUnit unit)
T join()
T getNow(T valueIfAbsent)
子主题
设置
boolean complete(T value)
boolean completeExceptionally(Throwable ex)
组合创建
thenApply
对上一次结果进行处理生成新的结果
thenAccept
对上一次结果进行处理,没有返回值
thenRun
上一次结果完成后执行该任务,与上次任务的结果不相关
thenCombine
新组合一个CF,并且对两个返回值进行处理生成新的结果
thenAcceptBoth
新组合一个CF,并且对两个返回值进行处理不生成新的结果
runAfterBoth
在某两个任务之后执行该任务
*Either*方法
任务一个正常返回后执行
volatile
保证修改的可见性
禁止指令重排
lock addl
使后面的指令不会提前到这句指令前
juc
AbstractQueuedSynchronizer
主要内容
依赖一个先进先出的等待队列
为根据单个int原子量状态的同步器作为一个基础
需要实现protected方法来改变状态,并且需要定义状态在对象被获取和释放的时候代表什么
其他方法实现排队和阻塞机制
AQS的子类应该被定义为非公开的内部类
AQS中队列的操作均是按照CAS操作实现的
ReentrantLock
替代Syncronized
可以控制公平性
公平则再次调用的时候偏向等待时间长的线程
但公平性有开销所以尽量选择不公平的
需要unlock释放不然会一直持有该锁
Condition
将wait,notify,notifyAll等操作封装起来
ReadWriteLock
性能不好,StampedLock替换
允许读在没有写的时候并发
不允许写并发
使用方法
用读写锁的读锁锁住读部分,写锁锁住写部分
StampedLock
多了乐观读
public class Point { private final StampedLock stampedLock = new StampedLock(); private double x; private double y; public void move(double deltaX, double deltaY) { long stamp = stampedLock.writeLock(); // 获取写锁 try { x += deltaX; y += deltaY; } finally { stampedLock.unlockWrite(stamp); // 释放写锁 } } public double distanceFromOrigin() { long stamp = stampedLock.tryOptimisticRead(); // 获得一个乐观读锁 // 注意下面两行代码不是原子操作 // 假设x,y = (100,200) double currentX = x; // 此处已读取到x=100,但x,y可能被写线程修改为(300,400) double currentY = y; // 此处已读取到y,如果没有写入,读取是正确的(100,200) // 如果有写入,读取是错误的(100,400) if (!stampedLock.validate(stamp)) { // 检查乐观读锁后是否有其他写锁发生 stamp = stampedLock.readLock(); // 获取一个悲观读锁 try { currentX = x; currentY = y; } finally { stampedLock.unlockRead(stamp); // 释放悲观读锁 } } return Math.sqrt(currentX * currentX + currentY * currentY); } }
在写少的时候采用这种方式效率更高
Semaphore
信号量限制可以访问资源的数量
比如new Semaphore(3)则表示同时只能3个线程访问资源
CountDownLatch
解决线程池时不能用thead.join的方式,等待线程都执行完继续
代码示例
// 创建2个线程的线程池 Executor executor = Executors.newFixedThreadPool(2); while(存在未对账订单){ // 计数器初始化为2 CountDownLatch latch = new CountDownLatch(2); // 查询未对账订单 executor.execute(()-> { pos = getPOrders(); latch.countDown(); }); // 查询派送单 executor.execute(()-> { dos = getDOrders(); latch.countDown(); }); // 等待两个查询操作结束 latch.await(); // 执行对账操作 diff = check(pos, dos); // 差异写入差异库 save(diff); }
CyclicBarrier
等待所有线程就绪才开始执行
所有线程执行完后执行回调方法
执行完回调方法之后会自动将前面
ThreadPoolExecutor
多线程框架
fork/join
ForkJoinPool
接受外部任务的提交
invoke
同步提交,有返回值,任务执行完成后返回
submit
异步提交,有返回值,调用线程立即返回
execute
异步提交,无返回值,调用线程立即返回
接受ForkJoinTask自身fork出的子任务的提交
任务队列数组(WorkQueue[])的初始化和管理
工作线程(Worker)的创建/管理
FIFO_QUEUE:先进先出,异步模式
LIFO_QUEUE:(默认)先进后出,同步模式
任务类继承
RecursiveTask
用于有返回结果的任务
RecursiveAction
用于没有返回结果的任务
示例
public static void main(String[] args) { System.out.println(new ParallelLearn().testForkJoin(1, 10)); } private int testForkJoin(int start, int end) { ForkJoinPool pool = new ForkJoinPool(); MyFokJoinTask task = new MyFokJoinTask(start, end); return pool.invoke(task); } static class MyFokJoinTask extends RecursiveTask<Integer> { int start; int end; MyFokJoinTask(int start, int end) { this.start = start; this.end = end; } @Override protected Integer compute() { if (end - start <= 1) { if (end == start) { return start; } else { return start + end; } } else { int mid = (end + start) / 2; MyFokJoinTask m1 = new MyFokJoinTask(start, mid); MyFokJoinTask m2 = new MyFokJoinTask(mid + 1, end); m1.fork(); m2.fork(); int m1Result = m1.join(); int m2Result = m2.join(); return m1Result + m2Result; } } }
高并发指标
tps
每秒事务数
请求服务器
处理请求
客户端就收到返回值
qps
每秒查询数
引用类型
其他常用类
LocalThread
jvm
启动参数
文档
内存区域
概念模型
方法区
内容
类型信息
常量
静态变量
即时编译器编译后的代码缓存
运行时常量池
hotspot实现
1.6,1.7使用永久代
1.8使用元空间使用直接内存
1.7开始将字符串常量池移动到了堆中
堆
所有的对象实例和数组都应当在堆上分配,但慢慢变的不那么绝对
垃圾回收的主区域,可以分代设计但不绝对
主要参数
-Xmx
-Xms
异常
OutOfMemoryError
虚拟机栈
生命周期同线程
方法执行的时候jvm创建栈帧
局部变量表
基本数据类型
对象引用
returnAddress类型
存储在slot变量槽中,具体槽占用多个多少字节不规定, 一般double和long占用两个槽
操作数栈
动态连接
方法出口
等
一次方法执行就是栈帧入栈出栈过程
异常
StackOverFlowError
栈深度超出虚拟机允许
OutOfMemoryError
动态扩容申请不到内存
本地方法栈
类似虚拟机栈,hotspot将两者合一
程序计数器
字节码解释器通过它获取下一条字节码指令
执行引擎
本地库接口
本地方法库
直接内存
NIO
具体实现
堆
垃圾回收
三个问题
哪些内存需要回收
栈、方法区、程序计数器随线程生灭不需要自动回收
一般主要讨论的是堆和方法区
什么时候回收
判定对象已死
引用计数法
无法解决互相引用问题
可达性分析算法
GC Roots是否可达
GC Roots包含
栈中引用的对象
类静态属性引用的对象
常量引用的对象
JNI引用的对象
虚拟机内部的引用
被同步锁持有的对象
反映虚拟机内部情况的JMXBean、JVMTI中注册的回调、本地代码缓存等
等等
引用类型
强引用
不会回收
软引用
内存溢出前可以先做回收
弱引用
下次垃圾回收的对象
虚引用
不影响回收,只是为了收到系统通知
怎么回收
回收步骤
GC Roots没有相连
是否需要执行finalize()
如果需要,放入优先级低的线程去执行,不保证能执行结束
如果执行中又做了其他关联可以逃脱被回收,但下一次不会再执行finalize()
等待被回收
方法区回收
回收条件苛刻
hotspot可以有参数控制
在自定义类加载器,大量使用反射等等技术的时候最好还是开启方法区的回收
分代回收
Minor GC
Major GC
Mixed GC
G1
Full GC
垃圾收集算法
标记-清除
内存碎片化
效率低
hotspot中的CMS,但CMS在碎片过多的时候还是会用标记整理
标记-复制
复制开销大
内存浪费
商用虚拟机大多的新生代都采用该方式
Appel式回收
hotspot的Serial、ParNew等新生代均采用
Eden和2个Survivor
1个Eden和1个Survivor为8:1浪费10%的空间作为复制
如果存活超过10%,则用逃生门设计
标记-整理
总吞吐量增加,但可能会出现延迟现象
Hotspot中的Parallel Scavenge收集器
jdk自带的工具
调优
class文件结构
类加载
代码执行
内存模型
语法糖
参数
-XX:+ PrintAssembly
JIT编译后的汇编
分布式锁
分布式事务
事务
特点
原子性
一致性或最终归一致性
隔离性
耐久性
redis
简介
官网
BSD协议
ANSI C编写
key-value数据库
基于内存可持久化
redis一般单机几万可以,需要压测
用自己的分离器不用锁
redis单个操作具有原子性,也支持事务
采用多路复用
安装部署
单机安装
官方上下载对应包
安装步骤可以参考官方文档
配置
Redis.conf
高级配置
理论
常用数据类型
持久化方式
定时快照snapshot
全部写入磁盘,代价高
基于语句追加aof
只追踪变化,回复速度慢
缺点
数据结构类型简单
内存比较耗,需要持久化
应用
订阅和发布
消息系统
Redis事务
Redis数据淘汰策略
Redis持久化
计数器
分布式会话
社交网络中的点赞关注等
最新列表
Redis缓存与数据库
缓存穿透
缓存雪崩
redis锁
热点key
memcache
简介
allocator
slab_class 具有相同大小chunk的slab组成
slab
page
trunk
这里真正存储数据
slab
...
...
路由策略
一致性hash
虚拟节点
命令
stats查看状态
elastic search
zookeeper
ZooKeeper is a centralized service for maintaining configuration information, naming, providing distributed synchronization, and providing group services.
安装部署
配置
理论
Paxos
ZAB
RAFT
应用
分布式锁
常用数据存储
mysql
dubbo
spring boot
spring cloud
Sharding-Sphere
mq
kafka
rocketMq
rabitMq
pulsar
...
流处理框架
flink
应该考虑的问题
服务器的增配减配
服务器的增加减少
服务器问题查询
设计模式
颜色含义
重要
可忽略
内容不太确认