阿里 - 面经学习 1

2020/01/24 Interview

面试题来自:链接

目录

一、网络

子网掩码是什么?有什么作用

使用的 IPv4 协议对 IP 地址强行定义了一些保留地址,即:“网络地址”和“广播地址”。所谓“网络地址”就是指“主机号”全为“0”的IP地址,如:125.0.0.0(A类地址);而“广播地址”就是指“主机号”全为“255”时的IP地址,如:125.255.255.255(A类地址)。

子网掩码是用来标识两个 IP 地址是否同属于一个子网。其每一位上的数值代表不同含义:为“1”则代表该位是网络位;若为“0”则代表该位是主机位。如果两个 IP 地址分别与同一个子网掩码进行按位“与”计算后得到相同的结果,即表明这两个 IP 地址处于同一个子网中。

TCP 三次握手,能两次握手嘛

  1. 客户端发送 SYN 连接请求。
  2. 服务端像客户端发送确认 ACK 报文,客户端 ESTABLISHED
  3. 客户端收到报文后想服务端发出 ACK 确认,服务端 ESTABLISHED

不能两次握手。因为客户端有超时重传机制。例如当出现网络滞留时,服务端返回的 ACK 客户端迟迟没有收到,就会再次发送建立连接指令。如果只有两次握手,那当服务端返回 ACK 时,服务端自己就已经认为自己建立连接了,这时如果有重传就会建立多个连接,但客户端其实只有一个连接。使用三次握手的话,客户端收到 ACK 后在发送 ACK 给服务端才建立连接,可以保证只有一个连接。

引申四次挥手

  1. 客户端发送完全部数据,发送给服务端 FIN 请求结束。
  2. 服务端返回 ACK 表示知道要结束了,同时此时依然发送未完的数据。
  3. 服务端数据发送完毕,发送 FIN 表示自己想要结束。
  4. 客户端收到 FIN,返回 ACK。服务端 CLOSED,客户端等待 2 倍最大报文存活时间结束。

四次挥手原因是服务端会有未发完数据,发完后才能通知客户端可以结束。等待最后一个 ACK 才关是因为不知道客户端会不会又没收到的数据等待重传。

TIME_WAIT 是因为要确认服务端收到了最后的确认报文(比如最后服务端没有收到客户端的确认 ACK 就无法关闭了,服务端会重传),同时让本连接所有报文都消失,使得下一个连接不会再有旧连接数据。

TCP 和 UDP 区别

  1. 基于连接与无连接;
  2. 对系统资源的要求(TCP较多,UDP少);
  3. UDP程序结构较简单;
  4. 流模式与数据报模式 ;
  5. TCP保证数据正确性,UDP可能丢包;
  6. TCP保证数据顺序,UDP不保证。

数据库

事务 ACID

  • 为数据库操作提供了一个从失败中恢复到正常状态的方法,同时提供了数据库即使在异常状态下仍能保持一致性的方法。
  • 当多个应用程序在并发访问数据库时,可以在这些应用程序之间提供一个隔离方法,以防止彼此的操作互相干扰。

Atomic 原子性:事务被视为不可分割的最小单元,事务的所有操作要么全部提交成功,要么全部失败回滚。

Consistently 一致性:ACID 中唯一由开发者保证的。一致性是指系统从一个正确的状态,迁移到另一个正确的状态。也就是事务前后状态都是正确的。

Isolation 隔离:一个事务所做的修改在最终提交以前,对其它事务是不可见的。分为四个等级:读未提交、读已提交、可重复读、串行化。

Durability 持久性:一旦事务提交,则其所做的修改将会永远保存到数据库中。即使系统发生崩溃,事务执行的结果也不能丢失。使用重做日志来保证持久性。

隔离特性

  • 未提交读 Read Uncommitted:可以读到别的事务没有提交的。所以会产生脏读(人家改完又回滚了,结果读到的是改完的)。
  • 已提交读 Read Committed:只能读取到已提交事物的修改,事务之内的修改是不可见的。因此也就解决了脏读,但无法解决不可重复读(一个事物查询两次某行的值中间,那一行被另一个事务修改并已提交)。
  • 可重复读 Repeatable Read:保证在同一个事务中多次读取同样数据的结果是一样的。
  • 串行 Serializable:强制事务串行执行。需要加锁实现,而其它隔离级别通常不需要。

MySQL 默认可重复读。Oracle 默认已提交读。

数据库脏读、幻读是什么?隔离特性有什么影响?

  • 脏读:T1 修改一个数据,T2 随后读取这个数据。如果 T1 撤销了这次修改,那么 T2 读取的数据是脏数据。
  • 幻读:T1在一个事务内读取某个范围的数据,T2在这个范围内插入新的数据(已提交),T1再次读取这个范围的数据,此时读取的结果和和第一次读取的结果不同。串行可以解决这一问题。

索引的类型

  • 数据结构的角度
    • B+ 树索引(O(log(n)))
    • HASH 索引
    • FULLTEXT 索引
    • 空间数据索引(R-Tree索引)
  • 从物理存储角度
    • 聚集索引(clustered index):存储全部数据
    • 非聚集索引(non-clustered index):存储主键
  • 从逻辑角度(人为角度划分的)
    • 主键索引:主键索引是一种特殊的唯一索引,不允许有空值
    • 普通索引或者单列索引
    • 多列索引(复合索引):复合索引指多个字段上创建的索引,只有在查询条件中使用了创建索引时的第一个字段,索引才会被使用。使用复合索引时遵循最左前缀集合
    • 唯一索引或者非唯一索引
    • 空间索引:空间索引是对空间数据类型的字段建立的索引,MYSQL中的空间数据类型有4种,分别是GEOMETRY、POINT、LINESTRING、POLYGON。

B 树

平衡树,所有叶子结点位于同一层。

B 树的平衡条件则有三点:

  • 叶子节点都在同一层
  • 每个节点的关键字数为子树个数减一(子树个数 k 是 [M/2, M]。M是树的阶数)
  • 子树的关键字保证左小右大的顺序

B+ 树

一棵 B+ 树需要满足以下条件:

  • 节点的子树数和关键字数相同(B 树是关键字数比子树数少一)
  • 节点的关键字表示的是子树中的最大数,在子树中同样含有这个数据
  • 叶子节点包含了全部数据,同时符合左小右大的顺序

JAVA

HashMap 原理

内部包含了一个 Entry 类型的数组 table。Entry 存储着键值对。它包含了四个字段,从 next 字段我们可以看出 Entry 是一个链表。即数组中的每个位置被当成一个桶,一个桶存放一个链表。HashMap 使用拉链法来解决冲突,同一个链表中存放哈希值和散列桶取模运算结果相同的 Entry。

从 JDK 1.8 开始,一个桶存储的链表长度大于等于 8 时会将链表转换为红黑树。

红黑树

一种二叉查找树,但在每个结点上增加一个存储位表示结点的颜色,可以是 Red 或 Black。

通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出俩倍,因而是接近平衡的。

但它是如何保证一棵n个结点的红黑树的高度始终保持在 logn 的呢?这就引出了红黑树的 5 个性质:

  • 每个结点要么是红的要么是黑的。
  • 根结点是黑的。
  • 每个叶结点(叶结点即指树尾端NIL指针或NULL结点)都是黑的。
  • 如果一个结点是红的,那么它的两个儿子都是黑的。
  • 对于任意结点而言,其到叶结点树尾端NIL指针的每条路径都包含相同数目的黑结点。

红黑树

AVL 树

平衡二叉树递归定义如下:

  • 左右子树的高度差小于等于 1。
  • 其每一个子树均为平衡二叉树。

为了保证二叉树的平衡, AVL 树引入了所谓监督机制,就是在树的某一部分的不平衡度超过一个阈值后触发相应的平衡操作。保证树的平衡度在可以接受的范围内。

HashTable

与 HashMap 类似,但线程安全,用 synchronized 进行同步。

ConcurrentHashMap

jdk 1.7

用 segment 做到局部加锁(基于 ReentrantLock),在 segment 里再放 table。每次加锁只对局部也就是 segment 加锁。操作需要两次定位,先定位到 segment,在定位到 table 数组上。

执行 size 操作要累加所有 segment 中的 count 数量。会先尝试不加锁,尝试三次在加锁。

jdk 1.8

又恢复成了一个 Node 数组。通过 CAS+synchronized 方式线程安全。CAS 创建 table 和 Node。锁 Table 对应的第一个 Node 添加值。

核心机制i++在java中是否线程安全

不安全,i++ 不是原子操作,他涉及三个操作:

  1. 从内存中把i的值取出来放到CPU的寄存器中

  2. CPU寄存器的值 +1

  3. 把CPU寄存器的值写回内存

并且 java 中,每个线程有自己的工作内存,各工作内存只有各自内存可见。

可以用 AtomicIntger 解决,或者加 synchronized。

valotile 关键字如何使用

保证线程可见行,工作内存肯定从主内存刷新。多线程保证每个线程看到他的值都是最新的。但不能保证线程安全,因为他只能保证原子性。

volatile 如果修饰 list,只有当 list 被赋新值才有作用,因为 volatile 只是保证 reference 的可见性。

get post delet put在rest风格如何使用的,以及区别?

  • get:请求资源,幂等
  • post:提交资源,创建资源,不幂等
  • put:通过替换方式更新资源,幂等
  • delete:删除资源,幂等
import java.util.ArrayList;
import java.util.List;
 
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
import org.xdemo.example.springrestful.entity.User;
/**
 * @作者 Goofy
 * @邮件 252878950@qq.com
 * @日期 2014-4-2下午1:28:07
 */
@Controller
@RequestMapping("/user")
public class UserController {
    public List<User> list=null;
    /**
     * user路径下默认显示用户列表
     * @return
     */
    @RequestMapping(method=RequestMethod.GET)
    public ModelAndView index(){
        if(list==null){
            list=getUserList();
        }
        ModelMap model=new ModelMap();
        model.addAttribute("list",list);
        return new ModelAndView("user/index",model);
    }
    /**
     * 跳转到添加用户页面,约定优于配置,默认匹配文件/WEB-INF/views/user/add.jsp
     */
    @RequestMapping("add")
    public void add(){}
    /**
     * 新增保存用户
     * @param user
     * @return ModelAndView
     */
    @RequestMapping(method=RequestMethod.POST)
    public ModelAndView addUser(User user){
        if(list==null){
            list=getUserList();
        }
        list.add(user);
        ModelMap model=new ModelMap();
        model.addAttribute("list",list);
        return new ModelAndView("user/index",model);
    }
    /**
     * 查看用户详细信息
     * @param id
     * @return ModelAndView
     */
    @RequestMapping(method=RequestMethod.GET,value="{id}")
    public ModelAndView viewUser(@PathVariable("id")String id){
        User user=findUserById(id);
        ModelMap model=new ModelMap();
        model.addAttribute("user",user);
        return new ModelAndView("user/view",model);
    }
     
    /**
     * 删除用户
     * @param id
     */
    @ResponseBody
    @RequestMapping(method=RequestMethod.DELETE,value="{id}")
    public String deleteUser(@PathVariable("id")String id){
        if(list==null){
            list=getUserList();
        }
        removeUserByUserId(id);
        return "suc";
    }
     
    /**
     * 跳转到编辑页面
     * @param id
     * @return ModelAndView
     */
    @RequestMapping("{id}/edit")
    public ModelAndView toEdit(@PathVariable("id")String id){
         
        User user=findUserById(id);
        ModelMap model=new ModelMap();
        model.addAttribute("user",user);
         
        return new ModelAndView("user/edit",model);
    }
     
    /**
     * 更新用户并跳转到用户列表页面
     * @param user
     * @return ModelAndView
     */
    @RequestMapping(method=RequestMethod.PUT)
    public ModelAndView edit(User user){
        updateUser(user);
        return new ModelAndView("redirect:/user/");
    }
     
/********************下面方法是操作数据的*********************/
    /**
     * 造10个用户
     * @return List<User>
     */
    private List<User> getUserList(){
        List<User> list=new ArrayList<User>();
        for(int i=0; i<10;i++){
            list.add(new User((i+1)+"","李四"+(i+1)));
        }
        return list;
    }
    /**
     * 删除用户
     * @param id
     * @return List<User>
     */
    private List<User> removeUserByUserId(String id){
        if(list==null)return null;
        for(User user:list){
            if(user.getUserId().equals(id)){
                list.remove(user);break;
            }
        }
        return list;
    }
    /**
     * 查找用户
     * @param id
     * @return User
     */
    private User findUserById(String id){
        User user=null;
        if(list==null)return null;
        for(User _user:list){
            if(_user.getUserId().equals(id)){
                user=_user;break;
            }
        }
        return user;
    }
    /**
     * 更新用户
     * @param user
     */
    private void updateUser(User user){
        for(User _user:list){
            if(_user.getUserId().equals(user.getUserId())){
                _user.setUserName(user.getUserName());break;
            }
        }
    }
     
     
}

Search

    Table of Contents