9月24面试查漏补缺
包装类了解?Integer为什么超过127不能用==比较,其底层原理是什么
Integer类内部通过静态内部类提供了一个缓存池,范围在-128~127之间,如果超过这个范围 Integer 值都是new出来的对象
public static Integer valueOf(int paramInt) {
assert (IntegerCache.high >= 127);
if ((paramInt >= -128) && (paramInt <= IntegerCache.high))
return IntegerCache.cache[(paramInt + 128)];
return new Integer(paramInt);
}
volatile有什么特性?每个特性是怎么实现的?没有volatile多线程访问变量内部实现又是怎么样的?
volatile特性:可保证有序性、可见性
有序性原理:
通过插入内存屏障来禁止指令重排。
可见性原理:
- 每个处理器会通过嗅探总线上的数据来查看自己的数据是否过期,一旦处理器发现自己缓存对应的内存地址被修改,就会将当前处理器的缓存设为无效状态。此时,如果处理器需要获取这个数据需重新从主内存将其读取到本地内存。
- 当处理器写数据时,如果发现操作的是共享变量,会通知其他处理器将该变量的缓存设为无效状态
java内存模型:
Java内存模型的一个抽象概念,它包含了控制器、运算器、缓存等,本地内存并不是真实存在的。同时Java内存模型规定,线程对共享变量的操作必须在自己的本地内存中进行,不能直接在主内存中操作共享变量。这种内存模型会出现什么问题呢?,
|线程A|。。。。。。|线程B|
↓。。。。。。。。。。↓
|本地内存A|。。。|本地内存B|
↓。。。。。。。。。。↓
----------------主内存----------------
共享变量X
----------------主内存----------------
- 线程A获取到共享变量X的值,此时本地内存A中没有X的值,所以加载主内存中的X值并缓存到本地内存A中,线程A修改X的值为1,并将X的值刷新到主内存中,这时主内存及本地内存中的X的值都为1。
- 线程B需要获取共享变量X的值,此时本地内存B中没有X的值,加载主内存中的X值并缓存到本地内存B中,此时X的值为1。线程B修改X的值为2,并刷新到主内存中,此时主内存及本地内存B中的X值为2,本地内存A中的X值为1。
- 线程A再次获取共享变量X的值,此时本地内存中存在X的值,所以直接从本地内存中A获取到了X为1的值,但此时主内存中X的值为2,到此出现了所谓内存不可见的问题。
Linux中查询一个文件的某个字符怎么实现?
grep “要查找的字符串” 文件名
Linux中怎么将日志文件内容实时打印到控制台
tail -f xxx.log
Map使用put过程是怎么样的?为什么实际使用更多地用String类型来当key?
put的过程:
- HashMap底层是使用Entry对象数组存储的,而Entry是一个单项的链表,当调用一个put方法将一个键值对添加进来时先使用hash函数获取该对象的hash值。
- 然后调用indexFor方法查找该对象在数组中应该存储的下标。
- 如果该位置为空,就将value值插入。
- 如果该下标下不为空,如果这两个Entry的key通过equals比较返回true,新Entry的value将覆盖原有Entry的value,但key不会覆盖。
- 如果这两个Entry的key通过equals比较返回false,新添加的Entry将与集合中原有的Entry形成Entry链,而且新添加的Enetry位于Entry链的头部。
String类型当key:
- String 对象的 hashCode() 值是根据 String 对象的内容计算的,并不是根据对象的地址计算;
- String不可变的特性可以使得 hash 值也不可变,因此只需要进行一次计算。即使非String对象重写了hashCode和equals 方法也,不如String高效,因为String中缓存有个hash变量,它可以缓存hashCode,避免重复计算hashCode。
使用TreeSet时对自定义对象排序怎么实现?
自然排序(使用空参构造):
自定义类实现Comparable接口,并重写compareTo方法后,直接使用TreeSet对象进行添加。
比较器排序(使用有参构造):
另外提供一个实现了Comparator的接口的类,在new TreeSet时传入类对象。或直接使用匿名内部类实现。
class Mycompare implements Comparator<Person>{
@Override
public int compare(Person o1, Person o2) {
return 0;
}
}
Set<Person> set=new TreeSet<>(new Mycompare());
Set<Person> set = new TreeSet<>(new Comparator<Person>() {
//采用匿名内部类传入比较器对象;
@Override
public int compare(Person o1, Person o2) {
return 0;
}
});
Comparable和Comparator的区别:
Comparable来自java.lang包,Comparator来自java.util包
很多常用类都实现了Comparable接口,像Integer、String等,所以直接调用Collection.sort直接可以使用。当对类自带的自然排序不满意而又不能修改其源代码的情况,使用Comparator比较合适。
使用Comparator可以避免添加额外的代码与我们的目标类耦合,同时可以定义多种排序规则,这一点Comparable无法做到。
BIO,NIO,AIO?
BIO(blocking I/O):
同步阻塞IO,使用BIO当前线程只能进行IO操作。服务器实现模式为一个连接一个线程,即客户端有连接请求时服务就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,可以通过线程池机制改善。
NIO (non-blocking I/O):
同步非阻塞IO,基于Reactor模型。NIO利用了单线程轮询事件机制。服务器实现模式为一个请求一个线程,即课客户端发生的连接请求都会注册到多路复用器上,多路复用器轮询到连接有IO请求时才启动一个线程进行处理。
应用场景:聊天服务器,并发局限于应用中,编程比较复杂,JDK1.4开始支持
AIO(Asynchronous I/O) :
异步非阻塞IO,基于Proactor模型的。它不是在IO准备好时再通知线程,而是在IO操作已经完成以后由操作系统再给线程发出通知。因此AIO是不会阻塞的。服务器实现模式为一个有效请求一个线程。
应用场景:相册服务器,充分调用OS参与并发操作,编程比较复杂,JDK7开始支持。