阿里巴巴21年校招面经题目解答

2 篇文章 0 订阅

今天已然已是23年,发现CSDN中有些文章草稿还未发布,整理了发布下,原文如下


突然接到了阿里巴巴的面试通知,我四月初面过,当时面完以后过几天回复我说我基础不好,把我拒绝了。不管进不进,多面面,检验自己的能力也好。于是,又开始准备阿里巴巴的校招,从网上搜集一些面经多复习下。这篇文章中记录了很多的面试题:阿里巴巴面经、面试流程、面试题。如下,我将以自己的角度去简要解答部分面试题。

为什么重写equals()方法就必须重写hashCode()方法?

答:
Java中的规定:两个对象使用equals方法判断相等,hashCode方法得到的值也一定相等,如果不重写hashCode方法,那么在equals方法相等的情况,hashCode方法不一定相等。但相反,两个对象hashCode相等,equals方法判断时不一定相等。其实,hashCode方法主要是在Map等需要hash值的场景中使用,如果非常明确绝对不会使用到该对象的hash值,倒是也可以不重写hashCode()方法,但是这种场景太少,所以还是重写吧,不要节省这一点代码量。

Object有哪些方法?

答:
Object中主要有如下方法。
在这里插入图片描述

  • Object() 是一个空的构造方法。
  • clone() 方法使用了 protected 修饰(protected 的作用范围 = default + 子类。default的可见范围是同一个包可见。)。利用protected修饰clone方法,是为了安全考虑。Object类中的clone方法是浅拷贝,如果是对象,它拷贝的只是这个对象的一个引用,而这个引用仍然指向那个对象,当我们改变这个引用的属性时,原来对象也会跟着改变,这不是我们希望看到的,但是Object类肯定做不到深拷贝,因为它不知道你的类里有哪些引用类型,所以把修饰符定义为protected,这样想要在其他任何地方调用这个类的clone方法,这个类就必须去重写clone方法并且把修饰符改为public,并且把修饰符修改为public(继承可以扩大权限控制符的范围),这样在任何地方都可以调用这个类的clone方法了。
  • equals()方法用来判断两个对象是否相等,我们通常都需要override equals方法。
  • finalize() 从 Java 9以后已经被标价为 Deprecated 了。它的设计目的是保证对象在被垃圾收集前完成特定资源的回,finalize 被设计成在对象被垃圾收集前调用,这就意味着实现了 finalize 方法的对象是个“特殊公民”,JVM 要对它进行额外处理。finalize 本质上成为了快速回收的阻碍者,可能导致你的对象经过多个垃圾收集周期才能被回收。
  • getClass() 方法,返回这个对象运行时对应的类,反射中经常使用。
  • hashCode() 方法用于计算对象的hashCode,用在Map等需要hash值的场景。
  • registerNatives() 方法的作用是当包含registerNatives()方法的类被JVM加载时,注册的方法就是该类所包含的除了registerNatives()方法以外的所有本地方法。
  • notify 和 notifyAll 方法的作用都是唤醒在某个对象上等待的线程。不同的是notify随机唤醒一个线程,notifyAll 是唤醒所有线程。
  • toString() 是将一个对象输出为一个字符串的形式,便于查看,通常都需要进行重写。
  • 上图的三个wait方法都是使某个线程在某个对象(monitor)上陷入等待,不过有的有超时值,有的没有。

Arraylist、HashMap的初始容量、加载因子、扩容增量?

答:
ArrayList
ArrayList的底层是基于数组,初始容量位10,扩容时一般扩大为原来容量的1.5倍。加载因子感觉就是扩容因子,应该是指当元素达到容量的百分之多少的时候开始扩容。当 ArrayList 中实际存储的元素的个数 = ArrayList 的容量大小时,才开始进行扩容,可以理解为扩容因子=1。
在这里插入图片描述
在这里插入图片描述
当 ArrayList 中实际存储的元素的个数 = ArrayList 的容量大小时,开始进行扩容,可以理解为扩容因子=1。
在这里插入图片描述
HashMap
初始容量。1左移4位,相当是2的4次方,即16。
在这里插入图片描述
最大容量。
在这里插入图片描述

加载因子,也可以称作为扩容因子,为0.75,可以在创建HashMap时进行调整,但是一般不建议修改。
在这里插入图片描述
当进行扩容时,需要再进行Hash,这样就会减少Hash冲突的概率,如果某个Hash桶中的元素过多,会将其右链表的形式转化为红黑树,进行扩容时,如果该Hash桶中的元素个数小于6,就会取消树化。
在这里插入图片描述
HashMap 进行扩容时,也会考虑到很多情况,在正常情况下的扩容(oldCap < MAXIMUM_CAPACITY && oldCap >= DEFAULT_INITIAL_CAPACITY),容量扩大为原来容量的2倍。
在这里插入图片描述
需要注意的是 HashMap 中存放元素的最大个数并不像ArrayList一样等于它的容量大小,其扩容时的阈值 = capacity * loadfactor(0.75f)。也就是说当HashMap 中的元素个数等于容量的0.75倍时就开始扩容,并不是等到容量都已经被占用完毕了才开始扩容。
在这里插入图片描述
在这里插入图片描述

有序的Map有哪些?为什么TreeMap是有序的?

答:
LikedHashMap(key遍历的顺序就是key插入的顺序),TreeMap都是有序的,在保证线程安全的情况下,可以采用ConcurrentSkipListMap(可以根据key的自然顺序或者是根据 Comparator 的顺序来进行排序)。

TreeMap 则是基于红黑树的一种提供顺序访问的 Map,和 HashMap 不同,它的 get、put、remove 之类操作都是 O(log(n))的时间复杂度,具体顺序可以由指定的 Comparator 来决定,或者根据键的自然顺序来判断

我们可以使用LinkedHashMap 实现一个简单的LRU算法(淘汰最近最少使用的元素),构造 LinkedHashMap 时,指定 accessOrder = true,且自定义淘汰策略,删除最久不被访问的元素。

public static void main(String[] args) {
    // accessOrder = true 表示按照访问顺序进行排序,accessOrderedMap = false 表示按照插入顺序排序
    LinkedHashMap<String, String> accessOrderedMap = new LinkedHashMap<>(16, 0.75F, true) {
        // 实现自定义删除策略,否则行为就和普遍Map没有区别,当size() > 3 时,触发删除
        @Override
        protected boolean removeEldestEntry(Map.Entry<String, String> eldest) {
            return size() > 3;
        }
    };
    // 放入元素
    accessOrderedMap.put("Project1", "Valhalla");
    accessOrderedMap.put("Project2", "Panama");
    accessOrderedMap.put("Project3", "Loom");
    System.out.println("-----------第一次遍历-----------");
    accessOrderedMap.forEach((k, v) -> System.out.println(k + ":" + v));

    // 模拟访问
    accessOrderedMap.get("Project3");
    accessOrderedMap.get("Project2");
    accessOrderedMap.get("Project1");
    System.out.println("-----------第二次遍历-----------");
    accessOrderedMap.forEach((k, v) -> System.out.println(k + ":" + v));

    accessOrderedMap.get("Project2");
    System.out.println("-----------第三次遍历-----------");
    accessOrderedMap.forEach((k, v) -> System.out.println(k + ":" + v));

    // 插入元素
    accessOrderedMap.put("Project4", "Mission Control");

    System.out.println("-----------第四次遍历-----------");
    accessOrderedMap.forEach((k, v) -> System.out.println(k + ":" + v));
}

输出结果

-----------第一次遍历-----------
Project1:Valhalla
Project2:Panama
Project3:Loom
-----------第二次遍历-----------
Project3:Loom
Project2:Panama
Project1:Valhalla
-----------第三次遍历-----------
Project3:Loom
Project1:Valhalla
Project2:Panama
-----------第四次遍历-----------
Project1:Valhalla
Project2:Panama
Project4:Mission Control

在这里插入图片描述

谈谈 synchronized和ReentrantLock 的区别?

答:
ReentrantLock是Lock的实现类,是一个互斥的同步器,在多线程高竞争条件下,ReentrantLock比synchronized有更加优异的性能表现,synchronized 在 1.6 以后进行了优化,在低竞争的情况下表现可能会优于 ReentrantLock。

  1. 用法比较
  • Lock使用起来比较灵活,但是必须有释放锁的配合动作
  • Lock必须手动获取与释放锁,而synchronized不需要手动释放和开启锁
  • Lock只适用于代码块锁,而synchronized可用于修饰方法、代码块等
  1. 特性比较
    ReentrantLock的优势体现在:
  • 具备尝试非阻塞地获取锁的特性:当前线程尝试获取锁,如果这一时刻锁没有被其他线程获取到,则成功获取并持有锁
  • 能被中断地获取锁的特性:与synchronized不同,获取到锁的线程能够响应中断,当获取到锁的线程被中断时,中断异常将会被抛出,同时锁会被释放。
  • 超时获取锁的特性:在指定的时间范围内获取锁;如果截止时间到了仍然无法获取锁,则返回
  1. 注意事项
    在使用ReentrantLock类的时,一定要注意三点:
  • 在finally中释放锁,目的是保证在获取锁之后,最终能够被释放
  • 不要将获取锁的过程写在try块内,因为如果在获取锁时发生了异常,异常抛出的同时,也会导致锁无故被释放。
  • ReentrantLock提供了一个newCondition的方法,以便用户在同一锁的情况下可以根据不同的情况执行等待或唤醒的动作
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值