2019年_BATJ大厂面试题总结-阿里篇

1.知道spring AOP是如何实现的么,动态代理和CGlib分别是如何实现的?

答案:https://blog.csdn.net/weixin_38362455/article/details/91055939

2.JVM内存模型了解不,是否有过调优经验?

答案:https://blog.csdn.net/qq_39712188/article/details/84840613

3.已知 sqrt (2)约等于 1.414,要求不用数学库,求 sqrt (2)精确到小数点后 10 位。

参考答案:

  • 考察点

基础算法的灵活应用能力(二分法学过数据结构的同学都知道,但不一定往这个方向考虑;如果学过数值计算的同学,应该还要能想到牛顿迭代法并解释清楚)
退出条件设计

二分法
已知 sqrt(2)约等于 1.414,那么就可以在(1.4, 1.5)区间做二分

查找,如:
a) high=>1.5
b) low=>1.4
c) mid => (high+low)/2=1.45
d) 1.45*1.45>2 ? high=>1.45 : low => 1.45

public class Main {
    public static void main(String[] args) {    
        //手写二分估值
        System.out.println(myCalculate(1.4,1.4,1.5));
        //调用函数输出
        //System.out.println("----------sqrt= "+Math.sqrt(2.0));
    }
    //根号2约等于 1.414
    private static double myCalculate(double ans,double low,double high) {
        double mid = 0;
        // 二分法,结束条件:差值小于等于1e-10即可
        while(high-low>1e-10){
            
             mid = (high+low)/2.0;
           // System.out.println("-----------mid= "+mid+"  mid*mid= "+mid*mid);
            //二分,逐步向中间值收拢
            if(mid*mid <= 2.0){
                low=mid;
            }
            else{
                high=mid;
            }   
        }  
        return mid;
    } 
}

牛顿迭代法

牛顿迭代法的公式为:

xn+1 = xn-f(xn)/f’(xn)

对于本题,需要求解的问题为:f(x)=x2-2 的零点

EPSILON = 0.1 ** 10
def newton(x):
    if abs(x ** 2 - 2) > EPSILON:
        return newton(x - (x ** 2 - 2) / (2 * x))
    else:
        return x

4.NFS 和 SMB 是最常见的两种 NAS(Network Attached Storage)协议,当把一个文件系统同时通过 NFS 和 SMB 协议共享给多个主机访问时,以下哪些说法是错误的:(多选)

A. 不可能有这样的操作,即把一个文件系统同时通过 NFS 和 SMB协议共享给多个主机访问。

B. 主机 a 的用户通过NFS 协议创建的文件或者目录,另一个主机 b的用户不能通过 SMB 协议将其删除。

C. 在同一个目录下,主机 a 通过 NFS 协议看到文件 file.txt,主机b 通过 SMB 协议也看到文件 file.txt,那么它们是同一个文件。

D. 主机 a 通过 NFS 协议,以及主机 b 通过 SMB 协议,都可以通过主机端的数据缓存,提升文件访问性能。

参考答案:A,B,C

5.ConcurrentHashMap原理,JDK1.8都有什么新特性?

答案:
DK8中ConcurrentHashMap参考了JDK8 HashMap的实现,采用了数组+链表+红黑树的实现方式来设计,内部大量采用CAS操作,这里我简要介绍下CAS。

CAS是compare and swap的缩写,即我们所说的比较交换。cas是一种基于锁的操作,而且是乐观锁。在java中锁分为乐观锁和悲观锁。悲观锁是将资源锁住,等一个之前获得锁的线程释放锁之后,下一个线程才可以访问。而乐观锁采取了一种宽泛的态度,通过某种方式不加锁来处理资源,比如通过给记录加version来获取数据,性能较悲观锁有很大的提高。

CAS 操作包含三个操作数 —— 内存位置(V)、预期原值(A)和新值(B)。如果内存地址里面的值和A的值是一样的,那么就将内存里面的值更新成B。CAS是通过无限循环来获取数据的,若果在第一轮循环中,a线程获取地址里面的值被b线程修改了,那么a线程需要自旋,到下次循环才有可能机会执行。

JDK8中彻底放弃了Segment转而采用的是Node,其设计思想也不再是JDK1.7中的分段锁思想。

Node:保存key,value及key的hash值的数据结构。其中value和next都用volatile修饰,保证并发的可见性。

<strong>class Node<K,V> implements Map.Entry<K,V> {
    final int hash;
    final K key;
    volatile V val;
    volatile Node<K,V> next;
    //... 省略部分代码
} </strong>
 

Java8 ConcurrentHashMap结构基本上和Java8的HashMap一样,不过保证线程安全性。

在JDK8中ConcurrentHashMap的结构,由于引入了红黑树,使得ConcurrentHashMap的实现非常复杂,我们都知道,红黑树是一种性能非常好的二叉查找树,其查找性能为O(logN),但是其实现过程也非常复杂,而且可读性也非常差,Doug
Lea的思维能力确实不是一般人能比的,早期完全采用链表结构时Map的查找时间复杂度为O(N),JDK8中ConcurrentHashMap在链表的长度大于某个阈值的时候会将链表转换成红黑树进一步提高其查找性能。

6.如何实现一个高效的单向链表逆序输出?

参考答案:下面是其中一种写法,也可以有不同的写法,比如递归等。供参考。

public class MyLinkedList {
    
    public int data;
    public MyLinkedList next;
    
    public MyLinkedList(int data) {
        this.data=data;
        this.next=null;
    }
    
    public MyLinkedList() {
        this.data=-1;
        this.next=null;
        
    }

7.请评估一下程序的执行结果?

public class SynchronousQueueQuiz {
    public static void main(String[] args) throws Exception {
        BlockingQueue<Integer> queue = new
        SynchronousQueue<>();
        System. out .print(queue.offer(1) + " ");
        System. out .print(queue.offer(2) + " ");
        System. out .print(queue.offer(3) + " ");
        System. out .print(queue.take() + " ");
        System. out .println(queue.size());
    }
}

A. true true true 1 3

B. true true true (阻塞)

C. false false false null 0

D. false false false (阻塞)

参考答案:D

8、了解Dubbo框架不,看过源码没,了解实现原理不?

答案:https://blog.csdn.net/u011277123/article/details/88717206

9.假设有一亿个用户每个用户订阅了一个手机闹铃闹铃类型不同,都是7点钟的闹铃,如何保证7点时数据库的负载最小,简述你的方案

答案:https://blog.csdn.net/v1v1wang/article/details/8771974

10.关于 epoll 和 select 的区别,哪些说法是正确的?(多选)

A. epoll 和 select 都是 I/O 多路复用的技术,都可以实现同时监听多个 I/O 事件的状态。

B. epoll 相比 select 效率更高,主要是基于其操作系统支持的I/O事件通知机制,而 select 是基于轮询机制。

C. epoll 支持水平触发和边沿触发两种模式。

D. select 能并行支持 I/O 比较小,且无法修改。

参考答案:A,B,C

【延伸】那在高并发的访问下,epoll使用那一种触发方式要高效些?当使用边缘触发的时候要注意些什么东西?
参考:https://blog.csdn.net/h490516509/article/details/88108890

11.给定一个整数数组和一个整数,返回两个数组的索引,这两个索引指向的数字的加和等于指定的整数。需要最优的算法,分析算法的空间和时间复杂度?

参考答案:

public int[] twoSum(int[] nums, int target) {
    if(nums==null || nums.length<2)
        return new int[]{0,0};
 
    HashMap<Integer, Integer> map = new HashMap<Integer, Integer>();
    for(int i=0; i<nums.length; i++){
        if(map.containsKey(nums[i])){
            return new int[]{map.get(nums[i]), i};
        }else{
            map.put(target-nums[i], i);
        }
    }
    return new int[]{0,0};
}

12.给定一个链表,删除链表的倒数第 N 个节点,并且返回链表的头结点。

示例:
给定一个链表: 1->2->3->4->5, 和 n = 2.
当删除了倒数第二个节点后,链表变为 1->2->3->5.
说明:
给定的 n 保证是有效的。
要求:
只允许对链表进行一次遍历。

参考答案:

我们可以使用两个指针而不是一个指针。第一个指针从列表的开头向前移动 n+1 步,而第二个指针将从列表的开头出发。现在,这两个指针被 n 个结点分开。我们通过同时移动两个指针向前来保持这个恒定的间隔,直到第一个指针到达最后一个结点。此时第二个指针将指向从最后一个结点数起的第 n 个结点。我们重新链接第二个指针所引用的结点的 next 指针指向该结点的下下个结点。

参考代码:

public ListNode removeNthFromEnd(ListNode head, int n)
{
    ListNode dummy = new ListNode(0);
    
    dummy.next = head;
    ListNode first = dummy;
    ListNode second = dummy;
    // Advances first pointer so that the gap between first
    and second is n nodes apart
    for (int i = 1; i <= n + 1; i++) {
        first = first.next;
    }
    // Move first to the end, maintaining the gap
    while (first != null) {
        first = first.next;
        second = second.next;
    }
    second.next = second.next.next;
    return dummy.next;
}

复杂度分析:
时间复杂度:O(L),该算法对含有 L 个结点的列表进行了一次遍历。因此时间复杂度为 O(L)。
空间复杂度:O(1),我们只用了常量级的额外空间。

13.多线程,AtomicInteger底层用的啥?cas的原理,AtomicInteger用了Voliate么?voliate的原理,变量加Voliate被修改了其他线程能立刻知道么?

参考:https://mp.weixin.qq.com/s/k6fnsM5-pZyKLVlAhpO1GQ
https://blog.csdn.net/seulzz/article/details/77930800
https://www.cnblogs.com/wuxun1997/p/10974472.html

14.对象存在java内存的那块区域里面?

java中标识符对应的值可以改变的叫做变量,不可以改变的叫做常量。如:

//标识符a的值可以改变,叫做变量
int a=3;
a=4;
//标识符b的值不可以改变,叫做常量
final int b=3;

java语言支持的类型有引用类型和基本类型。
对象,是类的一个实例。不仅可以存在于堆中,也可以存在方法区。如:

public calss A{
	//常量存在于方法区中的运行时常量池中
	public static final int C=2;
	//反射时的java.lang.Class对象也存在于方法区
	public static void main(String args[]){
		//a标识符指向的对象存在于堆中
		A a=new A();
	}
}

15.从 innodb 的索引结构分析,为什么索引的 key 长度不能太长?

参考答案:key 太长会导致一个页当中能够存放的 key 的数目变少,间接导致索引树的页数目变多,索引层次增加,从而影响整体查询变更的效率。

16.string类的用法,string a=Hello string a = new string(hello)创建的流程?

参考:https://blog.csdn.net/kk123k/article/details/80752476

17.MySQL 的数据如何恢复到任意时间点?

参考答案
恢复到任意时间点以定时的做全量备份,以及备份增量的 binlog 日志为前提。恢复到任意时间点首先将全量备份恢复之后,再此基础上回放增加的 binlog 直至指定的时间点。

18.jdk hashmap 底层存储?红黑树的特点,为啥不用数组用红黑树?

1.答案:https://my.oschina.net/u/2307589/blog/1800587
2.红黑树有几种特性:

    1.每个节点只能是红色或者黑色。

    2.根节点必须是黑色。

    3.红色的节点,它的叶节点只能是黑色。

    4.从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点

从根基节点到最远叶节点的路径(最远路径)肯定不会大于根基节点到最近节点的路径(最短路径)的两倍长。这是因为性质3保证了没有相连的红色节点,性质4保证了从这个节点出发的不管是哪一条路径必须有相同数目的黑色节点,这也保证了有一条路径不能比其他任意一条路径的两倍还要长
3.暂时无解

19.数据库用过么?用的啥数据库,mysql用的啥引擎?为啥数据库底层用B+树不用红黑树?

用过,Mysql;使用InnoDB 支持外键
1、 B+树的磁盘读写代价更低:B+树的内部节点并没有指向关键字具体信息的指针,因此其内部节点相对B树更小,如果把所有同一内部节点的关键字存放在同一盘块中,那么盘块所能容纳的关键字数量也越多,一次性读入内存的需要查找的关键字也就越多,相对IO读写次数就降低了。

2、B+树的查询效率更加稳定:由于非终结点并不是最终指向文件内容的结点,而只是叶子结点中关键字的索引。所以任何关键字的查找必须走一条从根结点到叶子结点的路。所有关键字查询的路径长度相同,导致每一个数据的查询效率相当。

3、由于B+树的数据都存储在叶子结点中,分支结点均为索引,方便扫库,只需要扫一遍叶子结点即可,但是B树因为其分支结点同样存储着数据,我们要找到具体的数据,需要进行一次中序遍历按序来扫,所以B+树更加适合在区间查询的情况,所以通常B+树用于数据库索引。

B树在提高了IO性能的同时并没有解决元素遍历的我效率低下的问题,正是为了解决这个问题,B+树应用而生。B+树只需要去遍历叶子节点就可以实现整棵树的遍历。而且在数据库中基于范围的查询是非常频繁的,而B树不支持这样的操作或者说效率太低。

20.java内存模型分为了几块区域?元空间里有些啥?

1.根据 JVM 规范,JVM 内存共分为虚拟机栈、堆、方法区、程序计数器、本地方法栈五个部分。
2.元空间的本质和永久代类似,都是对JVM规范中方法区的实现。不过元空间与永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存。因此,默认情况下,元空间的大小仅受本地内存限制,但可以通过以下参数来指定元空间的大小:

-XX:MetaspaceSize,初始空间大小,达到该值就会触发垃圾收集进行类型卸载,同时GC会对该值进行调整:如果释放了大量的空间,就适当降低该值;如果释放了很少的空间,那么在不超过MaxMetaspaceSize时,适当提高该值。
  -XX:MaxMetaspaceSize,最大空间,默认是没有限制的。

除了上面两个指定大小的选项以外,还有两个与 GC 相关的属性:
  -XX:MinMetaspaceFreeRatio,在GC之后,最小的Metaspace剩余空间容量的百分比,减少为分配空间所导致的垃圾收集   -XX:MaxMetaspaceFreeRatio,在GC之后,最大的Metaspace剩余空间容量的百分比,减少为释放空间所导致的垃圾收集

21.HBase底层数据存储的结构?

因为Hbase采用的是LSM树的结构,这种结构的关键是:
每一次的插入操作都会先进入MemStore(内存缓冲区),
当 MemStore达到上限的时候,Hbase会将内存中的数据输出为有序的StoreFile文件数据(根据Rowkey、版本、列名排序,这里已经和列 簇无关了因为Store里都属于同一个列簇)。
这样会在Store中形成很多个小的StoreFile,当这些小的File数量达到一个阀值的时 候,Hbase会用一个线程来把这些小File合并成一个大的File。
这样,Hbase就把效率低下的文件中的插入、移动操作转变成了单纯的文件输出、 合并操作。

由上可知,在Hbase底层的Store数据结构中,

每个StoreFile内的数据是有序的,
但是StoreFile之间不一定是有序的,
Store只 需要管理StoreFile的索引就可以了。
这里也可以看出为什么指定版本和Rowkey可以加强查询的效率,因为指定版本和Rowkey的查询可以利用 StoreFile的索引跳过一些肯定不包含目标数据的数据。

22.NIO知道么? nio底层调用了啥?啥是非阻塞IO?

参考https://blog.csdn.net/u013967628/article/details/84253976
https://www.cnblogs.com/crazymakercircle/p/10225159.html

23.

发布了52 篇原创文章 · 获赞 86 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_43107323/article/details/104696472