2019 Java面试基础知识整理——持续更新

文章目录

Java

HashMap

hash碰撞解决方法

(1) 开放定址法

Hash碰撞之后就向下寻找空的存储空间

(2) 链地址法(拉链法)

数组和链表结合,碰撞之后就插入链表(静态数组和动态数组的结合

(3) 再hash法

碰撞之后就再生成一hash表

HashMap扩容

(1)为什么要扩容,随着哈希表插入的数据越来越多,查找效率越来越低(哈希碰撞越来越多)

(2)直接扩大,重新生成新的最大值,重新生成hashcode

(3)Java大于数组长度的0.75,开始扩容,原来数组的两倍大

HashMap链表查找效率低怎么解决:

JDK1.8以后链表长度大于8时,自动转换成红黑树

HashCode

hashCode是jdk根据对象的地址或者字符串或者数字算出来的int类型的数值,方便字符串和类型进行比较

两个对象equal返回true,那么两个对象的hashCode()值也相同,反之不亦然!

HashCode 就是Hash值,Java中一般选用 2 N 2^N 2N来做模,好处就是取模速度快,原理如下:

static int Fun(int x,int length){
   
    return x & (length - 1);     //对(2^N-1)来说,取模直接&即可
}

为什么需要HashCode——方便!
总的来说,Java中的集合(Collection)有两类,一类是List,再有一类是Set。前者集合内的元素是有序的,元素可以重复;后者元素无序,但元素不可重复。这里就引出一个问题:要想保证元素不重复,可两个元素是否重复应该依据什么来判断呢?

这就是Object.equals方法了。但是,如果每增加一个元素就检查一次,那么当元素很多时,后添加到集合中的元素比较的次数就非常多了。也就是说,如果集合中现在已经有1000个元素,那么第1001个元素加入集合时,它就要调用1000次equals方法。这显然会大大降低效率。

于是,Java采用了哈希表的原理。哈希(Hash)实际上是个人名,由于他提出一哈希算法的概念,所以就以他的名字命名了。哈希算法也称为散列算法,是将数据依特定算法直接指定到一个地址上,初学者可以简单理解,hashCode方法实际上返回的就是对象存储的物理地址(实际可能并不是)。

这样一来,当集合要添加新的元素时,先调用这个元素的hashCode方法,就一下子能定位到它应该放置的物理位置上。如果这个位置上没有元素,它就可以直接存储在这个位置上,不用再进行任何比较了;如果这个位置上已经有元素了,就调用它的equals方法与新元素进行比较,相同的话就不存了,不相同就散列其它的地址,所以这里存在一个冲突解决的问题。这样一来实际调用equals方法的次数就大大降低了,几乎只需要一两次。

equals

equals在Object对象中直接比较两个类的引用地址,而在一些类型如String 、Math、Integer、Double中,对equals进行了重写,调用equals的时候是对内容进行比较,而不是简单的对引用地址进行比较。

  1. 自反性
x.equals(x) == true;
  1. 对称性
x.equals(y) == y.equals(x);
  1. 传递性
if(x.equals(y) && y.equals(z)){
   
    x.equals(z) == true;
}
  1. 一致性
x.equals(y) == x.equals(y);
  1. 与null比较
x.equals(null) == false;//x不为null

参考文章

equals 和 ==

  1. 对于基本类型,用==来判断两个值是否相等
  2. 对引用类型(对象),用==来判断是否是同一个引用,而用equals来判断二者内容是否相等(例如String,此时的equals被重写了)

sleep()和wait()的区别

(1)本质区别是sleep()不会释放同步锁,wait()释放同步锁

(2)sleep是Thread的方法,wait()是Object的方法

(3)wait(),notify 和 notifyAll 必须要在同步控制的方法中使用,而sleep()可以子任何地方使用

(4)sleep必须捕获异常,而wait,notify和notifyAll不需要捕获异常

(5)由于sleep不会释放同步锁,容易导致死锁问题,因此更推荐用wait

同步锁

产生原因

  1. 线程之间存在共享数据。
  2. 线程在具体执行时,会先拷贝主存数据到线程本地(CPU缓存),操作完成后再把结果从线程本地刷到主存。多线程情况下就会产生并发问题。

意义

当多个线程共用一块资源的时候,会出现资源抢占,加了同步锁之后,使得同一段时间只有一个线程会获得资源,其他线程处于等待状态。

主要实现方式有

synchronized,Lock

synchronized使用

synchronized是Java关键字,在JVM层面实现线程锁

当一个线程进入一个对象的synchronized方法A之后,其它线程是否可进入此对象的synchronized方法B?

不能。其它线程只能访问该对象的非同步方法,同步方法则不能进入。因为非静态方法上的synchronized修饰符要求执行方法时要获得对象的锁,如果已经进入A方法说明对象锁已经被取走,那么试图进入B方法的线程就只能在等锁池(注意不是等待池哦)中等待对象的锁。

对于一个类,在其synchronized方法A内是永远无法调用他的synchronized方法B的,因为同时只能有一个锁,B方法要等A方法释放锁才能调用。

Lock的使用

Lock是一个接口

public interface Lock {
   

    void lock();
 
    void lockInterruptibly() throws InterruptedException;
 
    boolean tryLock();
 
    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
 
    void unlock();
 
    Condition newCondition();
}

lock()

如果采用Lock,必须主动去释放锁,并且在发生异常时,不会自动释放锁。因此一般来说,使用Lock必须在try-catch块中进行,并且将释放锁的操作放在finally块中进行,以保证锁一定被被释放,防止死锁的发生。

Lock lock = new ReentrantLock();  
lock.lock();
try {
   
    for(int i=0;i<5;i++) {
   
        arrayList.add(i);   
    }
} catch (Exception e) {
   

}finally {
   
    lock.unlock();
}

tryLock()

tryLock()方法是有返回值的,它表示用来尝试获取锁,如果获取成功,则返回true,如果获取失败(即锁已被其他线程获取),则返回false,也就说这个方法无论如何都会立即返回。在拿不到锁时不会一直在那等待。

Lock lock = ...;
if(lock.tryLock()) {
   
     try{
   
         //处理任务
     }catch(Exception ex){
   
         
     }finally{
   
         lock.unlock();   //释放锁
     } 
}else {
   
    //如果不能获取锁,则直接做其他事情
}

tryLock(long time, TimeUnit unit)

方法和tryLock()方法是类似的,只不过区别在于这个方法在拿不到锁时会等待一定的时间,在时间期限之内如果还拿不到锁,就返回false。如果如果一开始拿到锁或者在等待期间内拿到了锁,则返回true。使用方法同上。

lockInterruptibly

lockInterruptibly()方法比较特殊,当通过这个方法去获取锁时,如果线程正在等待获取锁,则这个线程能够响应中断,即中断线程的等待状态。也就使说,当两个线程同时通过lock.lockInterruptibly()想获取某个锁时,假若此时线程A获取到了锁,而线程B只有在等待,那么对线程B调用threadB.interrupt()方法能够中断线程B的等待过程。

public void method() throws InterruptedException {
   
    lock.lockInterruptibly();
    try {
     
     //.....
    }
    finally {
   
        lock.unlock();
    }  
}

当一个线程获取了锁之后,是不会被interrupt()方法中断的。因为单独调用interrupt()方法不能中断正在运行过程中的线程,只能中断阻塞过程中的线程。因此当通过lockInterruptibly()方法获取某个锁时,如果不能获取到,只有进行等待的情况下,是可以响应中断的。

synchronized和Lock的区别

  1. synchronized是Java关键字,是在JVM层面控制同步的。Lock是一个接口,在代码JDK层面进行控制的
  2. synchro
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值