华为
-
父线程如何捕获子线程的异常
若线程组有异常处理器就使用线程组的异常处理器,若没有就使用全局的UncaughtExceptionHandler,通过回调UncaughtExceptionHandler接口的public void uncaughtException(Thread t, Throwable e) 方法来处理异常。
(虽然是在回调方法中处理异常,但这个回调方法在执行时依然还在抛出异常的这个线程中!另外还要特别说明一点:如果线程是通过线程池创建,线程异常发生时UncaughtExceptionHandler接口不一定会立即回调。)
https://www.cnblogs.com/jpfss/p/10272885.html
https://blog.csdn.net/weixin_40803329/article/details/89316009
-
λ 表达式
-
Java IO流使用的设计模式
适配器模式和装饰者模式
https://blog.csdn.net/nicewuranran/article/details/51779060 -
如何捕获文件流的异常,以及文件流如何关闭
-
计算机网络:三次握手四次挥手,停止等待,拥塞控制,滑动窗口
-
Java optional类
Optional 类是一个可以为null的容器对象。如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象。
Optional 是个容器:它可以保存类型T的值,或者仅仅保存null。Optional提供很多有用的方法,这样我们就不用显式进行空值检测。
Optional 类的引入很好的解决空指针异常。
https://www.runoob.com/java/java8-optional-class.html -
Java 8 新特性
-
::是什么?
在Java中,方法引用通过一对冒号 :: 来表示,方法引用是一种函数式接口的另一种书写方式,分为三种形式:
A.静态方法引用,通过类名 :: 静态方法名,如 Integer::parseInt;
B.实例方法引用,通过实例对象 :: 实例方法名,如 str::substring;
C.构造方法引用,通过类名 :: new,如 User::new。方法引用与 λ 表达式的区别:
A.λ 表达式也是一种函数式接口,它通常只调用一次,为了简洁就不新建方法,而是直接将方法体作为参数直接传入,与方法引用的区别在于:λ 表达式一般用于自己提供方法体,而方法引用一般直接引用现成的方法。
小米
-
虚拟内存机制
每个程序都拥有自己的地址空间,这个地址空间被分成大小相等的页,这些页被映射到物理内存;但不需要所有的页都在物理内存中,当程序引用到不在物理内存中的页时,由操作系统将缺失的部分装入物理内存。这样,对于程序来说,逻辑上似乎有很大的内存空间,只是实际上有一部分是存储在磁盘上,因此叫做虚拟内存。
通俗地讲:操作系统有一块物理内存(中间的部分),有两个进程(实际会更多)P1和P2,操作系统偷偷地分别告诉P1和P2,我的整个内存都是你的,随便用,管够。可事实上呢,操作系统只是给它们画了个大饼,这些内存说是都给了P1和P2,实际上只给了他们一个序号而已。只有当P1和P2真正开始使用这些内存时,系统才开始使用辗转挪移,拼凑出各个块给进程用,P2以为自己在用A内存,实际上已经被系统悄悄重定向到真正的B去了,甚至,当P1和P2共用了C内存,他们都是不知道。
https://mp.weixin.qq.com/s?src=11×tamp=1600529709&ver=2594&signature=J5JiwhJgVDs51**vt9DDUkaMGBXKszavY9R*1G89grfQnsoaazdjBwNcrAt7o4IIFwd2JBdlVeBEwBRBBCw7EQVpbfcJH3zLXANRIXP9z0O4mjswokAj2t8DnEovt3zB&new=1虚拟内存的优点是让程序可以获得更多的可用内存。
虚拟内存提供了三个重要的能力:
1)它将主存看成是一个存储在磁盘上的地址空间的高速缓存,在主存中只保存活动区域,并根据需要在磁盘和主存之间来回传送数据,通过这种方式,它高效使用了主存;
2)它为每个进程提供了一致的地址空间,从而简化了内存管理;
3)它保护了每个进程的地址空间不被其他进程破坏。扩展:如何进行地址空间到物理内存的映射
内存管理单元(MMU)管理着逻辑地址和物理地址的转换,其中的页表(Page table)存储着页(逻辑地址)和页框(物理内存空间)的映射表,页表中还包含包含有效位(是在内存还是磁盘)、访问位(是否被访问过)、修改位(内存中是否被修改过)、保护位(只读还是可读写)。逻辑地址:页号+页内地址(偏移);每个进程一个页表,放在内存,页表起始地址在PCB/寄存器中。
-
死锁是什么?怎样处理和预防死锁?
死锁:在两个或者多个并发进程中,每个进程持有某种资源而又等待其它进程释放它们现在保持着的资源,在未改变这种状态之前都不能向前推进,称这一组进程产生了死锁(deadlock)。
死锁条件:
互斥:一个资源一次只能被一个进程使用;
请求和保持:一个进程至少占有一个资源,并在等待另一个被其它进程占用的资源;
非抢占:已经分配给一个进程的资源不能被强制性抢占,只能由进程完成任务之后自愿释放;
循环等待:若干进程之间形成一种头尾相接的环形等待资源关系,该环路中的每个进程都在等待下一个进程所占有的资源。死锁处理:鸵鸟策略:什么也不做。
死锁预防:
破坏互斥条件:允许某些资源同时被多个进程访问,实用性有限;
破坏请求并保持条件:
·实行资源预先分配策略(当一个进程开始运行之前,必须一次性向系统申请它所需要的全部资源,否则不运行);
·或者只允许进程在没有占用资源的时候才能申请资源(申请资源前先释放占有的资源);
·缺点:很多时候无法预知一个进程需要的全部资源;会降低资源利用率,降低系统的并发性;
破坏非抢占条件:允许进程强行抢占被其他进程占有的资源。会降低系统性能;
破坏循环等待条件:对所有资源统一编号,所有进程对资源的请求必须按照序号递增的顺序提出。死锁避免:
动态地检测资源分配状态,以确保系统处于安全状态,只有处于安全状态时才会进行资源分配。(所有安全状态是指:即使所有进程突然请求需要的所有资源,也能存在某种对进程的资源分配顺序,使得每个进程运行完毕。)死锁解除:
·利用抢占:挂起某些进程,并抢占它的资源。但应防止进程被饿死。
·利用回滚:让某些进程回退到足以解除死锁的地步,进程资源释放资源。
·利用杀死进程:强制杀死某些进程直到死锁解除为止,可以按照优先级进行。 -
Java里,byte[] 和 Sring 互相转换。
String 转 byte[] 用 String 的实例方法 getBytes() ;
byte[] 转 String 用 new String(byte[]) ;当然,可以指定码制的:
getBytes(“UTF-8”) ;
new String(byte[],“UTF-8”); -
Integer 和 int 哪个更占内存?
Integer 更占内存。实际上,在 Java 里, int 占据4字节,Integer 占据16字节。
细想一下,Integer作为包装类,它除了存储相关的数据之外,额外的方法、引用这些也是需要内存的。 -
Java容器了解多少?
List、Set、Queue、Map挨个枪毙。
-
Java如何实现一个双向链表
回答LinkedList底层原理即可。
private static class Node<E>{
E item;
Node<E> next;
Node<E> prev;
}
//每个链表再存储队头和队尾指针
transient Node<E> first;
transient Node<E> last;
-
如何用一个数组实现一个双向链表?
我回答的是用三个数组实现静态链表的操作。实际上想考察的应该是一个类数组如何实现双向链表。
-
equals() 和 == 区别
= = 比较的是对象在内存中的存放地址,因此,除非是同一个 new 出来的对象,他们比较后的结果为 true ,其余的都为 false 。
equals() 是 Object 类的方法,如果不重写该方法的话,也是比较两个对象的地址是否相同。但是我们可以重写该方法,来比较两个对象的数据内容是否相同。比如 String、 Integer 这些类里面的 equals() 都被重写了的,可以比较两个字符串内容是否相等,两个 Integer 对象值是否相等。
-
hashCode()
hashCode() 放回哈希值,而 equals() 是用来判断两个对象是否等价的。等价的两个对象 hashCode 一定相等,但是 hashCode 相同的两个对象不一定等价。
在覆盖 equals() 方法时应当总是覆盖 hashCode() 方法,保证等价的两个对象哈希值也是相等的。
-
编译期常量(没答上来)
编译期常量就是程序在编译时就能确定这个常量的具体值,非编译期常量就是程序在运行时才能确定常量的值,因此也称为运行时常量。
定义上来说,声明为 final 类型的基本类型和 String 类型并直接赋值的变量就是编译期常量。
扩展:编译期常量的风险
因为编译期常量在编译时就确定了值,使用编译期常量的地方在编译时就会替换成对应的值。即使我们将编译期常量定义成 public static final 类型(静态常量),引用该值也不会导致类的初始化。
这个特性就导致了:假定A类定义了一个编译期常量,B类使用了这个常量,两者都进行了编译。然后修改了A中常量的值,重新进行编译时,系统只会重新编译改动的A类,而旧代码B类并不会被重新编译。导致B中的常量值与A中的常量值不一致。 -
深拷贝和浅拷贝
浅拷贝就是拷贝获得的对象和原始对象引用了同一个对象,浅拷贝获得的拷贝对象进行修改之后,原始对象也会被修改。
深拷贝就是拷贝获得的对象和原始对象引用不同的对象,它们只是值相等而已。深拷贝获得的拷贝对象进行修改之后,原始对象不会被修改。
-
算法题:10亿个 double 精度的数,如何找到前10个最小的数。
答案:维护一个大小为 10 的大顶堆,然后遍历这10亿个数,大于堆顶的元素被丢弃,小于堆顶的元素,将其和堆顶交换,然后重新调整堆。遍历完成后留下的就是最小的 10 个数。