面试整理

作者: wanggs | 来源:发表于2019-02-25 15:19 被阅读301次

    1. 金三银四跳槽季

    2. 两年Java的面试总结

    Java基础

    1. HashMap的源码,实现原理,JDK8中对HashMap做了怎样的优化。
    jdk1.6 1.7,HashMap 采用位桶+链表来实现的,链表用来处理hash冲突,
    
    参考

    1. HaspMap扩容是怎样扩容的,为什么都是2的N次幂的大小。

    1. HashMap,HashTable,ConcurrentHashMap的区别。
    HasTable 是jdk1 遗留下来的类初始值是11扩容newsize =oldsize*2+1,而HashMap是后来增加的初始容量是16;
    HashTable 是线程安全的类,操作效率低,而HashMap是线程不安全的,操作效率高;
    HashTable 不允许null键null值,HashMap 允许null键null值
    concurrentHashMap底层采用的是数组加链表线程安全,
    
    java 中数据存储方式分为两种:一种是数组,另外一种是链表,前者的特点内存是连续的空间,寻址快,但是增加删除的时候会有较大幅度的移动,所以数组的特点是查询快增删慢;而链表由于内存地址是不连续的,寻址困难,增删元素只修改指针,所以链表的贴的,查询慢,增删快
    
    参考

    1. 极高并发下HashTable和ConcurrentHashMap哪个性能更好,为什么,如何实现的。
     ConcurrentHashMap在多线程下效率更高
     
     HashTable使用一把锁处理并发问题,当有多个线程访问时,需要多个线程竞争一把锁,导致阻塞
    
    ConcurrentHashMap则使用分段,相当于把一个HashMap分成多个,然后每个部分分配一把锁,这样就可以支持多线程访问
    

    1. HashMap在高并发下如果没有处理线程安全会有怎样的安全隐患,具体表现是什么。
    Hashmap在并发环境下,可能出现的问题:
    1、多线程put时可能会导致get无限循环,具体表现为CPU使用率100%;
    原因:在向HashMap put元素时,会检查HashMap的容量是否足够,如果不足,则会新建一个比原来容量大两倍的Hash表,然后把数组从老的Hash表中迁移到新的Hash表中,迁移的过程就是一个rehash()的过程,多个线程同时操作就有可能会形成循环链表,所以在使用get()时,就会出现Infinite Loop的情况
    

    1. java中四种修饰符的限制范围。
    范围    private     default     protected       public
    同一类
    同一个包中的类
    不同包的子类
    其他类
    

    1. Object类中的方法。
    clone   getClass    toString    equalse haseCode    wait 是当前线程等待该对象的锁,wait()方法一直等直到获得说或者被中断
    notify 该方法唤醒该对象的某个线程 notifyall该方法唤醒该对象的所有线程
    

    1. 接口和抽象类的区别,注意JDK8的接口可以有实现。
    Java8中的接口中的默认方法是可以被多重继承的。而抽象类不行
    

    1. 动态代理的两种方式,以及区别。
    程序调用的是代理类而不是目标类 ,代理类和目标类对外具有相同的方法声明,有两种可以实现相同的方法声明,一个是实现相同的方法声明,一个是作为目标的子类
    jdk 中采用的是proxy类参数动态代理的方式为某个接口产生实现类,如果某个产生了子类就是CGLIB
    

    1. Java序列化的方式。
    序列化就是把Java对象储存在某一地方(硬盘、网络),也就是将对象的内容进行流化
    反序列化:就是把二进制数据反序列化成对象数据
    

    1. 传值和传引用的区别,Java是怎么样的,有没有传值引用。
    传值:传递的是值的副本。方法中对副本的修改,不会影响到调用方。
    
    传引用:传递的是引用的副本,共用一个内存,会影响到调用方。此时,形参和实参指向同一个内存地址。对引用副本本身(对象地址)的修改,如设置为null,重新指向其他对象,不会影响到调用方
    

    1. 一个ArrayList在循环过程中删除,会不会出问题,为什么。

    1. @transactional注解在什么情况下会失效,为什么。

    数据结构和算法

    • B+树

    • 快速排序,堆排序,插入排序(其实八大排序算法都应该了解

    • 一致性Hash算法,一致性Hash算法的应用

    JVM

    • JVM的内存结构。
    • image.png
    参考

    • JVM方法栈的工作过程,方法栈和本地方法栈有什么区别。

    • JVM的栈中引用如何和堆中的对象产生关联。

    • 可以了解一下逃逸分析技术。

    • GC的常见算法,CMS以及G1的垃圾回收过程,CMS的各个阶段哪两个是Stop the world的,CMS会不会产生碎片,G1的优势。

    • 标记 - 清除算法
    标记清除算法是最基础的收集算法,其他收集算法都是基于这种思想。标记清除算法分为“标记”和“清除”两个阶段:首先标记出需要回收的对象,标记完成之后统一清除对象。
    
    它的主要缺点:①.标记和清除过程效率不高 。
    
    ②.标记清除之后会产生大量不连续的内存碎片
    
    image.png

    image.png

    • 复制算法
    复制算法采用的方式为从根集合进行扫描,将存活的对象移动到一块空闲的区域
    
    image.png image.png
    • 标记 - 整理算法
    image.png image.png
    • 标记清除和标记整理算法的理解以及优缺点。

    • eden survivor区的比例,为什么是这个比例,eden survivor的工作过程。

    • JVM如何判断一个对象是否该被GC,可以视为root的都有哪几种类型。
    jvm要做垃圾回收时,首先要判断一个对象是否还有可能被使用。那么如何判断一个对象是否还有可能被用到?
    
    • Java是否可以GC直接内存。

    • Java类加载的过程。
    classLoader 的作用就是将class文件加载到jvm中,然后执行一系列的方法,加载二进制数据,在堆内存中创建class对象,验证准备初始化,验证文件格式,字节码准备是为为类的静态变量分配空间,然后使用,new出对象程序中使用,卸载,执行垃圾回收
    

    • 双亲委派模型的过程以及优势。
    启动类加载器: BootStrap
    扩展类加载器:
    应用程序类加载器:负责加载用户类路径
    

    • 常用的JVM调优参数。
    -Xms设置堆的最小空间大小。
    
    -Xmx设置堆的最大空间大小。
    
    -XX:NewSize设置新生代最小空间大小。
    
    -XX:MaxNewSize设置新生代最大空间大小。
    
    -XX:PermSize设置永久代最小空间大小。
    
    -XX:MaxPermSize设置永久代最大空间大小。
    
    -Xss设置每个线程的堆栈大小。
    

    • dump文件的分析。

    • Java有没有主动触发GC的方式(没有)。

    • 你能保证 GC 执行吗?

    不能,虽然你可以调用 System.gc() 或者 Runtime.gc(),但是没有办法保证 GC 的执行。

    • Java 中堆和栈有什么区别?

    JVM 中堆和栈属于不同的内存区域,使用目的也不同。栈常用于保存方法帧和局部变量,而对象总是在堆上分配。栈通常都比堆小,也不会在多个线程之间共享,而堆被整个 JVM 的所有线程共享。

    多线程>

    • Java实现多线程有哪几种方式。
    public class MyThreadDemo extends Thread {
        public MyThreadDemo(){}
        public MyThreadDemo(String name){
            super(name);
        }
    
        @Override
        public void run() {
            for (int i = 0; i <= 100; i++) {
                System.out.println(getName());
            }
        }
    }
    
    /**
     * 获取线程名称
     *public final String getName();
     */
    class Test{
        public static void main(String[] args) {
            MyThreadDemo myThreadDemo1 = new MyThreadDemo();
            myThreadDemo1.setName("tom");
    
            myThreadDemo1.run();
    
        }
    }
    
    
    
    public class MyRunnable implements Runnable {
    
        @Override
        public void run() {
            for (int x = 0; x < 100; x++) {
                // 由于实现接口的方式就不能直接使用Thread类的方法了,但是可以间接的使用
                System.out.println(Thread.currentThread().getName() + ":" + x);
            }
        }
    
    }
    
    /*
     * 方式2:实现Runnable接口
     * 步骤:
     *      A:自定义类MyRunnable实现Runnable接口
     *      B:重写run()方法
     *      C:创建MyRunnable类的对象
     *      D:创建Thread类的对象,并把C步骤的对象作为构造参数传递
     */
    class MyRunnableDemo {
        public static void main(String[] args) {
            // 创建MyRunnable类的对象
            MyRunnable my = new MyRunnable();
            Thread t1 = new Thread(my, "林青霞");
            Thread t2 = new Thread(my, "刘意");
    
            t1.start();
            t2.start();
        }
    }
    
    

    • Callable和Future的了解。

    • 线程池的参数有哪些,在线程池创建一个线程的过程。

    • volitile关键字的作用,原理。
    内存可见性: 所用线程都能看到共享内存的最新状态。
    每个线程都有自己的本地空间,线程执行时,会先把变量,从内存中,读取到自己的内存中,然后对变量进行操作,对变量操作完成后,在某个时间再把变量刷新到主内存中
    

    • synchronized关键字的用法,优缺点。
    同步代码块 同步方法
    
        synchronized(对象)  对象可以随意(使用上帝就行)
        {
            需要被同步的代码(哪些代码在操作共享数据)
        }
        
        public static synchronized Singleton getInstance(){
                ....
        }
    

    • Lock接口有哪些实现类,使用场景是什么。

    • 可重入锁的用处及实现原理,写时复制的过程,读写锁,分段锁(ConcurrentHashMap中的segment)。

    • 悲观锁,乐观锁,优缺点,CAS有什么缺陷,该如何解决。

    • ABC三个线程如何保证顺序执行。

    • 线程的状态都有哪些。
    准备就绪运行死亡
    

    • sleep和wait的区别。
    sleep()必须指定时间,不释放锁。wait() 可以指定时间和不指定时间,释放锁
    

    • notify和notifyall的区别。
    notify 该方法唤醒该对象的某个线程 notifyall该方法唤醒该对象的所有线程
    

    • ThreadLocal的了解,实现原理。
    线程局部变量,用于实现线程内的数据共享
    

    数据库相关

    • 常见的数据库优化手段
    - 索引
    - 分表
    - 读写分离
    - 缓存
    - 定位慢查询
    1.索引: 普通索引,允许重复键出现
             唯一索引,不允许重复键出现
             全文索引,针对表中的文本域
    缺点:   吃内存
    索引的数据结构:B-Tree物理文件大多都是以二叉树结构来存储到本地的,也就是说,实际所需要的数据库,都是存在树的叶子节点上,而且任何一个叶子节点,长度都是相同的
    
    2.分表:水平分表,垂直分表
      因为mysql表数据达到百万级别,查询效率会很低,容易造成死锁,甚至堆积很多连接,导致数据库挂掉
      垂直分表:如果一个表中的字段非常多(长文本),而且很少情况下回使用,这时可以通过外键关联一张表例如(考试表 分数 和 详情)
      水平分表:
              水平分表策略:
                     1. 按照时间分割:这种分表有一定的局限性,当数据有较强的时效性,如微信聊天记录,这种数据用户很少会查询几个月前的数据可以按照月份分割
                     2. 按照区间分割:一般有严格的Id自增,如果按照userId水平分割,table_1 1-100w数据
                     3.按照hash分割:
                     通过一个原始的目标ID或者名称通过一定的hash算法,计算数据库存储的表名,然后访问相应的表。
    数据库优化之读写分离:
                   一台数据库支持的最大并发是有限的,如果用户并发访问太多,一台服务器满足不了,就可以集群处理,mysql最常用的就是读写分离
                   1、主从同步,数据库最终会把数据持久化到硬盘中,如果集群必须确保,服务器的数据一致性,该表数据库操作的都是在主数据库,而其他数据库是从主数据库同步的
                   2. 读写分离,使用负载均衡来实现,写的操作,在主数据库读,在其他书库写
    数据库优化之缓存:
                    在持久层(dao)和数据库(db)之间加一个缓存层,如果用户访问的是缓存起来的数据,那么用户之间从缓存中取,不用访问数据库,而缓存的操作是内存级的,访问速度快
                    
                    作用: 减少数据库的压力,减少访问时间
                    常用的是hibernate 二级缓存,该缓存不能完成分布式缓存,可以使用redis作为主缓存
                     
                         
    

    • 索引的优缺点,什么字段上建立索引
    优点:索引可以加快数据的检索速度;加快表与表的连接;字段添加索引可以加快分组和排序的效率
    缺点: 创建索引和维护索引需要增加时间成本;数据越大越占据物理空间
    
    什么字段需要索引: 表的主键和外键;超过300的表的数据;经常出现where的子句
    

    • 数据库连接池。
    数据库连接不用每次创建和销毁,减少开销,增快响应速度
    限定连接个数,不用因为数据库连接过多导致,数据库运行缓慢和崩溃
    
    使用缓存,访问缓存(内存级的)的数据要比访问数据库的数据要快的多
    
    Ehcache Gava cache  redis
    

    • durid的常用配置。

    计算机网络

    • TCP,UDP区别。

    • 三次握手,四次挥手,为什么要四次挥手。

    • 长连接和短连接。

    • 连接池适合长连接还是短连接

    设计模式

    • 观察者模式

    • 代理模式

    • 单例模式,有五种写法,可以参考文章单例模式的五种实现方式

    • 可以考Spring中使用了哪些设计模式

    分布式相关

    • 分布式事务的控制。

    • 分布式锁如何设计。

    • 分布式session如何设计。

    • dubbo的组件有哪些,各有什么作用。

    • zookeeper的负载均衡算法有哪些。

    • dubbo是如何利用接口就可以通信的。

    缓存相关

    reids

    reids 是一个key-value的nosql数据库,先保存到内存中根据一定的策略,持久化到磁盘中,即使断电,数据也不会丢失,可以用来缓存数据,web集群,共享seesion
    用途:
    缓存:高性能,高并发,因为内存天然支持高并发,,需要查询的数据修改少, 
    计数器: redis的计数器,是原子的,内存操作
    分布式锁: setnx key value,当key不存在时,将 key 的值设为 value ,返回1。若给定的 key 已经存在,则setnx不做任何动作,返回0。
    
    
    
    当setnx返回1时,表示获取锁,做完操作以后del key,表示释放锁,如果setnx返回0表示获取锁失败,整体思路大概就是这样
    
    
    redis 淘汰机制(内存大小有限,保存有效数据):在redis中,允许用户设置最大内存,但是内存大小是有限的,redis内存数据,上升到一定的大小会运行淘汰机制,6种
    已设置失效时间的 最近最少使用的 任意淘汰
    

    • redis和memcached的区别。
    Memcached仅支持简单的key-value结构的数据
    

    • redis支持哪些数据结构。
    string:  string是最简单的类型,你可以理解成与Memcached一模一样的类型,一个key对应一个value,其上支持的操作与Memcached的操作类似。但它的功能更丰富。
    
    list: list是一个链表结构,主要功能是push、pop、获取一个范围的所有值等等。之所以说它是双向的,因为它可以在链表左,右两边分别操作
    
    set: set是集合,和我们数学中的集合概念相似,对集合的操作有添加删除元素,有对多个集合求交并差等操作。操作中key理解为集合的名字
    
    zset: zset是set的一个升级版本,他在set的基础上增加了一个顺序属性,这一属性在添加修改元素的时候可以指定,每次指定后,zset会自动重新按新的值调整顺序。 可以对指定键的值进行排序权重的设定,它应用排名模块比较多
    
    hash: Redis能够存储key对多个属性的数据(比如user1.uname user1.passwd),当然,你完成可以把这些属性以json格式进行存储,直接把它当作string类型进行操作,但这样性能上是对影响的,所以redis提出的Hash类型。
    

    • redis是单线程的么,所有的工作都是单线程么。
    redis 内部使用文件事件处理器 file event handler,这个文件事件处理器是单线程的,所以 redis 才叫做单线程的模型。它采用 IO 多路复用机制同时监听多个 socket,根据 socket 上的事件来选择对应的事件处理器进行处理
    

    • redis如何存储一个String的。

    • redis的部署方式,主从,集群。

    • redis的哨兵模式,一个key值如何在redis集群中找到存储在哪里。

    • redis持久化策略。
    Redis的数据是存在内存中的,如果Redis发生宕机,那么数据就会全部丢失,因此必须提供持久化机制。
    
    Redis持久化机制有两种,第一种是快照(RDB),第二种是AOF日志,快照是一次全量备份,AOF 日志是连续的增量备份。快照是内存数据的二进制序列化形式,在存储上非常紧凑,而 AOF 日志记录的是内存数据修改的指令记录文本。AOF 日志在长期的运行过程中会变的无比庞大,数据库重启时需要加载 AOF 日志进行指令重放,这个时间就会无比漫长。所以需要定期进行 AOF 重写,给 AOF 日志进行瘦身。
    
    RDB是通过Redis主进程fork子进程,让子进程执行磁盘 IO 操作来进行 RDB 持久化,AOF 日志存储的是 Redis 服务器的顺序指令序列,AOF 日志只记录对内存进行修改的指令记录。即RDB记录的是数据,AOF记录的是指令
    

    • RDB和AOF到底该如何选择?
    
    1、不要仅仅使用 RDB,因为那样会导致你丢失很多数据,因为RDB是隔一段时间来备份数据。
    
    2、也不要仅仅使用 AOF,因为那样有两个问题,第一,通过 AOF 做冷备没有RDB恢复速度快; 第二,RDB 每次简单粗暴生成数据快照,更加健壮,可以避免 AOF 这种复杂的备份和恢复机制的 bug。
    
    3、用RDB恢复内存状态会丢失很多数据,重放AOP日志又很慢。Redis4.0推出了混合持久化来解决这个问题。将 rdb 文件的内容和增量的 AOF 日志文件存在一起。这里的 AOF 日志不再是全量的日志,而是自持久化开始到持久化结束的这段时间发生的增量 AOF 日志,通常这部分 AOF 日志很小。于是在 Redis 重启的时候,可以先加载 rdb 的内容,然后再重放增量 AOF 日志就可以完全替代之前的 AOF 全量文件重放,重启效率因此大幅得到提升。
    

    • Redis在互联网公司一般有以下应用:
    String: 缓存,限流,计数器,分布式锁,分布式session
    
    Hash: 储存用户信息,用户主页访问量,组合查询
    
    List: 简单的队列,微博关注的人的时间轴列表
    
    set:赞、踩、标签、好友关系
    
    Zset:排行榜
    

    参考

    缓存雪崩是什么?

    假设有如下一个系统,高峰期请求为5000次/秒,4000次走了缓存,只有1000次落到了数据库上,数据库每秒1000的并发是一个正常的指标,完全可以正常工作,但如果缓存宕机了,每秒5000次的请求会全部落到数据库上,数据库立马就死掉了,因为数据库一秒最多抗2000个请求,如果DBA重启数据库,立马又会被新的请求打死了,这就是缓存雪崩。

    image.png

    如何解决缓存雪崩

    事前:redis高可用,主从+哨兵,redis cluster,避免全盘崩溃
    
    事中:本地ehcache缓存 + hystrix限流&降级,避免MySQL被打死
    
    事后:redis持久化,快速恢复缓存数据
    

    缓存穿透是什么?

    假如客户端每秒发送5000个请求,其中4000个为黑客的恶意攻击,即在数据库中也查不到。举个例子,用户id为正数,黑客构造的用户id为负数,
    如果黑客每秒一直发送这4000个请求,缓存就不起作用,数据库也很快被打死。

    image.png

    如何解决缓存穿透

    查询不到的数据也放到缓存,value为空,如set -999 “”

    总而言之,缓存雪崩就是缓存失效,请求全部全部打到数据库,数据库瞬间被打死。缓存穿透就是查询了一个一定不存在的数据,并且从存储层查不到的数据没有写入缓存,这将导致这个不存在的数据每次请求都要到存储层去查询,失去了缓存的意义
    

    框架相关

    • SpringMVC的Controller是如何将参数和前端传来的数据一一对应的。

    • Mybatis如何找到指定的Mapper的,如何完成查询的。

    • Quartz是如何完成定时任务的。

    • 自定义注解的实现。

    • Spring使用了哪些设计模式。

    • Spring的IOC有什么优势。

    • Spring如何维护它拥有的bean。

    一些较新的东西

    • JDK8的新特性,流的概念及优势,为什么有这种优势。

    • 区块链了解

    • 如何设计双11交易总额面板,要做到高并发高可用。

    相关文章

      网友评论

        本文标题:面试整理

        本文链接:https://www.haomeiwen.com/subject/ttrgyqtx.html