最新Java校招面试题及答案

本文作者在一年之内参加过多场面试,应聘岗位均为 Java 开发方向。在不断的面试中,分类总结了 Java 开发岗位面试中的一些知识点。

主要包括以下几个部分:

  1. Java 基础知识点

  2. Java 常见集合

  3. 高并发编程(JUC 包)

  4. JVM 内存管理

  5. Java 8 知识点

  6. 网络协议相关

  7. 数据库相关

  8. MVC 框架相关

  9. 大数据相关

  10. Linux 命令相关

面试,是大家从学校走向社会的第一步。

互联网公司的校园招聘,从形式上说,面试一般分为 2-3 轮技术面试 +1 轮 HR 面试。但是一些公司确实是没有 HR 面试的,直接就是三轮技术面。

技术面试中,面试官一般会先就你所应聘的岗位进行相关知识的考察,也叫基础知识和业务逻辑面试。只要你回答的不是特别差,面试官通常会说:“咱们写个代码吧”,这个时候就开始了算法面试。

也就是说,一轮技术面试 = 基础知识和业务逻辑面试 + 算法面试。

在本篇文章中,我们主要从技术面试聊起。技术面试包括:业务逻辑和基础知识面试。

首先是业务逻辑面试 ,也就是讲项目。

面试官会对你简历上写的若干个项目其中之一拿出来和你聊聊。在期间,会针对你所做的东西进行深度挖掘。

包括:为什么要这么做?优缺点分析,假如重新让你做一次,你打算怎么做? 等等。这个环节主要考察我们对自己做过的项目(实习项目或者校内项目)是否有一个清晰的认识。

关于业务逻辑面试的准备,建议在平时多多思考总结,对项目的数据来源、整体运行框架都应该熟悉掌握。

比如说你在某公司实习过程中,就可以进行总结,而不必等到快离职的时候慌慌张张的去总结该项目。

接下来是基础知识面试。

Java 开发属于后台开发方向,有人说后台开发很坑,因为需要学习的东西太多了。没错,这个岗位就是需要学习好多东西。包括:本语言(Java/C++/PHP)基础、数据库、网络协议、Linux 系统、计算机原理甚至前端相关知识都可以考察你,而且,并不超纲 。

有时候,你报的是后台开发岗,并且熟悉的是 Java 语言,但是面试官却是 C++ 开发方向的,就是这么无奈~

好了,闲话少说,让我们开始分类讲解常见面试知识点。

(一) Java 基础知识点

1)面向对象的特性有哪些?

答:封装、继承和多态。

2)Java 中覆盖和重载是什么意思?

解析:覆盖和重载是比较重要的基础知识点,并且容易混淆,所以面试中常见。
答:覆盖(Override)是指子类对父类方法的一种重写,只能比父类抛出更少的异常,访问权限不能比父类的小。

被覆盖的方法不能是 private 的,否则只是在子类中重新定义了一个方法;重载(Overload)表示同一个类中可以有多个名称相同的方法,但这些方法的参数列表各不相同。

面试官: 那么构成重载的条件有哪些?

答:参数类型不同、参数个数不同、参数顺序不同。

面试官: 函数的返回值不同可以构成重载吗?为什么?

答:不可以,因为 Java 中调用函数并不需要强制赋值。举例如下:

如下两个方法:

<div class=highlight> <div class=cnblogs_Highlighter sh-gutter> <div id=highlighter_303231 class=syntaxhighlighter  java> <table border=0 cellspacing=0 cellpadding=0><tr> <td class=gutter> <div class=line number1 index0 alt2>1<div class=line number2 index1 alt1>2 <code class=java plain>f(){}<div class=line number2 index1 alt1> <code class=java keyword>int <code class=java plain>f(){ <code class=java keyword>return <code class=java value>1<code class=java plain>;}

只要编译器可以根据语境明确判断出语义,比如在 int x = f();中,那么的确可以据此区分重载方法。不过, 有时你并不关心方法的返回值,你想要的是方法调用的其他效果 (这常被称为 “为了副作用而调用”),这时你可能会调用方法而忽略其返回值,所以如果像下面的调用:

<div class=highlight> <div class=cnblogs_Highlighter sh-gutter> <div id=highlighter_278773 class=syntaxhighlighter  java> <table border=0 cellspacing=0 cellpadding=0><tr> <td class=gutter> <div class=line number1 index0 alt2>1

此时 Java 如何才能判断调用的是哪一个 f() 呢?别人如何理解这种代码呢?所以,根据方法返回值来区分重载方法是行不通的。

3)抽象类和接口的区别有哪些?

答:

  1. 抽象类中可以没有抽象方法;接口中的方法必须是抽象方法;

  2. 抽象类中可以有普通的成员变量;接口中的变量必须是 static final 类型的,必须被初始化,接口中只有常量,没有变量。

  3. 抽象类只能单继承,接口可以继承多个父接口;

  4. Java8 中接口中会有 default 方法,即方法可以被实现。

面试官:抽象类和接口如何选择?

答:

  1. 如果要创建不带任何方法定义和成员变量的基类,那么就应该选择接口而不是抽象类。

  2. 如果知道某个类应该是基类,那么第一个选择的应该是让它成为一个接口,只有在必须要有方法定义和成员变量的时候,才应该选择抽象类。因为抽象类中允许存在一个或多个被具体实现的方法,只要方法没有被全部实现该类就仍是抽象类。

4)Java 和 C++ 的区别:

解析:虽然我们不太懂 C++,但是就是会这么问,尤其是三面(总监级别)面试中。

答:

  1. 都是面向对象的语言,都支持封装、继承和多态;

  2. 指针:Java 不提供指针来直接访问内存,程序更加安全;

  3. 继承: Java 的类是单继承的,C++ 支持多重继承; Java 通过一个类实现多个接口来实现 C++ 中的多重继承; Java 中类不可以多继承,但是!!!接口可以多继承;

  4. 内存: Java 有自动内存管理机制,不需要程序员手动释放无用内存。

5)Java 中的值传递和引用传递

解析:这类题目,面试官会手写一个例子,让你说出函数执行结果,详细举例请查阅我的博客:Java 值传递和引用传递基础分析。

答:值传递是指对象被值传递,意味着传递了对象的一个副本,即使副本被改变,也不会影响源对象。引用传递是指对象被引用传递,意味着传递的并不是实际的对象,而是对象的引用。

因此,外部对引用对象的改变会反映到所有的对象上。

6)JDK 中常用的包有哪些?

答:java.lang、java.util、<a class= external href=http://link.zhihu.com/?target=http%3A//java.io rel=nofollow noreferrer target=_blank><span class=invisible>http://<span class=visible>java.io、<a class= external href=http://link.zhihu.com/?target=http%3A//java.net rel=nofollow noreferrer target=_blank><span class=invisible>http://<span class=visible>java.net、java.sql。

7)JDK,JRE 和 JVM 的联系和区别:

答:JDK 是 java 开发工具包,是 java 开发环境的核心组件,并提供编译、调试和运行一个 java 程序所需要的所有工具,可执行文件和二进制文件,是一个平台特定的软件。

JRE 是 java 运行时环境,是 JVM 的实施实现,提供了运行 java 程序的平台。JRE 包含了 JVM,但是不包含 java 编译器 / 调试器之类的开发工具。

JVM 是 java 虚拟机,当我们运行一个程序时,JVM 负责将字节码转换为特定机器代码,JVM 提供了内存管理 / 垃圾回收和安全机制等。

这种独立于硬件和操作系统,正是 java 程序可以一次编写多处执行的原因。

区别:

  1. JDK 用于开发,JRE 用于运行 java 程序;

  2. JDK 和 JRE 中都包含 JVM;

  3. JVM 是 java 编程语言的核心并且具有平台独立性。

Others:限于篇幅,面试中 Java 基础知识点还有:反射、泛型、注解等。

小结:本节主要阐述了 Java 基础知识点,这些问题主要是一面面试官在考察,难度不大,适当复习下,应该没什么问题。

(二)Java 中常见集合

集合这方面的考察相当多,这部分是面试中必考的知识点。

1)说说常见的集合有哪些吧?

答:Map 接口和 Collection 接口是所有集合框架的父接口:

  1. Collection 接口的子接口包括:Set 接口和 List 接口;

  2. Map 接口的实现类主要有:HashMap、TreeMap、Hashtable、ConcurrentHashMap 以及 Properties 等;

  3. Set 接口的实现类主要有:HashSet、TreeSet、LinkedHashSet 等;

  4. List 接口的实现类主要有:ArrayList、LinkedList、Stack 以及 Vector 等。

(2)HashMap 和 Hashtable 的区别有哪些?(必问)

答:

  1. HashMap 没有考虑同步,是线程不安全的;Hashtable 使用了 synchronized 关键字,是线程安全的;

  2. 前者允许 null 作为 Key;后者不允许 null 作为 Key。

3)HashMap 的底层实现你知道吗?

答:在 Java8 之前,其底层实现是数组 + 链表实现,Java8 使用了数组 + 链表 + 红黑树实现。此时你可以简单的在纸上画图分析:

<img src=https://www.jb51.cc/res/2019/03-16/23/3ef2329fc0cba6b90c92db6599961d16.jpg alt=>

4)ConcurrentHashMap 和 Hashtable 的区别? (必问)

答:ConcurrentHashMap 结合了 HashMap 和 HashTable 二者的优势。HashMap 没有考虑同步,hashtable 考虑了同步的问题。但是 hashtable 在每次同步执行时都要锁住整个结构。 ConcurrentHashMap 锁的方式是稍微细粒度的。 ConcurrentHashMap 将 hash 表分为 16 个桶(默认值),诸如 get,put,remove 等常用操作只锁当前需要用到的桶。

面试官:ConcurrentHashMap 的具体实现知道吗?

答:

  1. 该类包含两个静态内部类 HashEntry 和 Segment;前者用来封装映射表的键值对,后者用来充当锁的角色;

  2. Segment 是一种可重入的锁 ReentrantLock,每个 Segment 守护一个 HashEntry 数组里得元素,当对 HashEntry 数组的数据进行修改时,必须首先获得对应的 Segment 锁。

5)HashMap 的长度为什么是 2 的幂次方?

答:

  1. 通过将 Key 的 hash 值与 length-1 进行 & 运算,实现了当前 Key 的定位,2 的幂次方可以减少冲突(碰撞)的次数,提高 HashMap 查询效率;

  2. 如果 length 为 2 的次幂 则 length-1 转化为二进制必定是 11111……的形式,在于 h 的二进制与操作效率会非常的快,而且空间不浪费;

  3. 如果 length 不是 2 的次幂,比如 length 为 15,则 length-1 为 14,对应的二进制为 1110,在于 h 与操作,最后一位都为 0,而 0001,0011,0101,1001,1011,0111,1101 这几个位置永远都不能存放元素了,空间浪费相当大。

更糟的是这种情况中,数组可以使用的位置比数组长度小了很多,这意味着进一步增加了碰撞的几率,减慢了查询的效率!这样就会造成空间的浪费。

6)List 和 Set 的区别是啥?

答:List 元素是有序的,可以重复;Set 元素是无序的,不可以重复。

7)List、Set 和 Map 的初始容量和加载因子

答:

1. List

  • ArrayList 的初始容量是 10;加载因子为 0.5; 扩容增量:原容量的 0.5 倍 +1;一次扩容后长度为 16。

  • Vector 初始容量为 10,加载因子是 1。扩容增量:原容量的 1 倍,如 Vector 的容量为 10,一次扩容后是容量为 20。

2. Set

HashSet,初始容量为 16,加载因子为 0.75; 扩容增量:原容量的 1 倍; 如 HashSet 的容量为 16,一次扩容后容量为 32

3. Map

HashMap,初始容量 16,加载因子为 0.75; 扩容增量:原容量的 1 倍; 如 HashMap 的容量为 16,一次扩容后容量为 32

8)Comparable 接口和 Comparator 接口有什么区别?

答:

  1. 前者简单,但是如果需要重新定义比较类型时,需要修改源代码。

  2. 后者不需要修改源代码,自定义一个比较器,实现自定义的比较方法。
    具体解析参考我的博客:Java 集合框架—Set

9)Java 集合的快速失败机制 “fail-fast”

答:它是 java 集合的一种错误检测机制,当多个线程对集合进行结构上的改变的操作时,有可能会产生 fail-fast 机制。

例如 :假设存在两个线程(线程 1、线程 2),线程 1 通过 Iterator 在遍历集合 A 中的元素,在某个时候线程 2 修改了集合 A 的结构(是结构上面的修改,而不是简单的修改集合元素的内容),那么这个时候程序就会抛出 ConcurrentModificationException 异常,从而产生 fail-fast 机制。

原因: 迭代器在遍历时直接访问集合中的内容,并且在遍历过程中使用一个 modCount 变量。集合在被遍历期间如果内容发生变化,就会改变 modCount 的值。

每当迭代器使用 hashNext()/next() 遍历下一个元素之前,都会检测 modCount 变量是否为 expectedmodCount 值,是的话就返回遍历;否则抛出异常,终止遍历。

解决办法:

  1. 在遍历过程中,所有涉及到改变 modCount 值得地方全部加上 synchronized;

  2. 使用 CopyOnWriteArrayList 来替换 ArrayList。

小结:本小节是 Java 中关于集合的考察,是 Java 岗位面试中必考的知识点,除了应该掌握以上的问题,包括各个集合的底层实现也建议各位同学阅读,加深理解。

(三)高并发编程-JUC 包

在 Java 5.0 提供了 java.util.concurrent(简称 JUC )包,在此包中增加了在并发编程中很常用的实用工具类,用于定义类似于线程的自定义子系统,包括线程池、异步 IO 和轻量级任务框架。

1)多线程和单线程的区别和联系:

答:

  1. 在单核 CPU 中,将 CPU 分为很小的时间片,在每一时刻只能有一个线程在执行,是一种微观上轮流占用 CPU 的机制。

  2. 多线程会存在线程上下文切换,会导致程序执行速度变慢,即采用一个拥有两个线程的进程执行所需要的时间比一个线程的进程执行两次所需要的时间要多一些。

结论:即采用多线程不会提高程序的执行速度,反而会降低速度,但是对于用户来说,可以减少用户的响应时间。

2)如何指定多个线程的执行顺序?

解析:面试官会给你举个例子,如何让 10 个线程按照顺序打印 0123456789?(写代码实现)

答:

  1. 设定一个 orderNum,每个线程执行结束之后,更新 orderNum,指明下一个要执行的线程。并且唤醒所有的等待线程。

  2. 在每一个线程的开始,要 while 判断 orderNum 是否等于自己的要求值!!不是,则 wait,是则执行本线程。

3)线程和进程的区别:(必考)

答:

  1. 进程是一个 “执行中的程序”,是系统进行资源分配和调度的一个独立单位;

  2. 线程是进程的一个实体,一个进程中拥有多个线程,线程之间共享地址空间和其它资源(所以通信和同步等操作线程比进程更加容易);

  3. 线程上下文的切换比进程上下文切换要快很多。

  • (1)进程切换时,涉及到当前进程的 CPU 环境的保存和新被调度运行进程的 CPU 环境的设置。

  • (2)线程切换仅需要保存和设置少量的寄存器内容,不涉及存储管理方面的操作。

4)多线程产生死锁的 4 个必要条件?

答:

  1. 互斥条件:一个资源每次只能被一个线程使用;

  2. 请求与保持条件:一个线程因请求资源而阻塞时,对已获得的资源保持不放;

  3. 不剥夺条件:进程已经获得的资源,在未使用完之前,不能强行剥夺;

  4. 循环等待条件:若干线程之间形成一种头尾相接的循环等待资源关系。

面试官:如何避免死锁?(经常接着问这个问题哦~)

答:指定获取锁的顺序,举例如下:

  1. 比如某个线程只有获得 A 锁和 B 锁才能对某资源进行操作,在多线程条件下,如何避免死锁?

  2. 获得锁的顺序是一定的,比如规定,只有获得 A 锁的线程才有资格获取 B 锁,按顺序获取锁就可以避免死锁!!!

5)sleep( ) 和 wait( n)、wait( ) 的区别:

答:

  1. sleep 方法:是 Thread 类的静态方法,当前线程将睡眠 n 毫秒,线程进入阻塞状态。当睡眠时间到了,会解除阻塞,进行可运行状态,等待 CPU 的到来。睡眠不释放锁(如果有的话);

  2. wait 方法:是 Object 的方法,必须与 synchronized 关键字一起使用,线程进入阻塞状态,当 notify 或者 notifyall 被调用后,会解除阻塞。但是,只有重新占用互斥锁之后才会进入可运行状态。睡眠时,释放互斥锁。

6)synchronized 关键字:

答:底层实现:

  1. 进入时,执行 monitorenter,将计数器 +1,释放锁 monitorexit 时,计数器-1;

  2. 当一个线程判断到计数器为 0 时,则当前锁空闲,可以占用;反之,当前线程进入等待状态。

含义:(monitor 机制)

Synchronized 是在加锁,加对象锁。对象锁是一种重量锁(monitor),synchronized 的锁机制会根据线程竞争情况在运行时会有偏向锁(单一线程)、轻量锁(多个线程访问 synchronized 区域)、对象锁(重量锁,多个线程存在竞争的情况)、自旋锁等。

该关键字是一个几种锁的封装。

7)volatile 关键字

答:该关键字可以保证可见性不保证原子性。

功能:

  1. 主内存和工作内存,直接与主内存产生交互,进行读写操作,保证可见性;

  2. 禁止 JVM 进行的指令重排序。

解析:关于指令重排序的问题,可以查阅 DCL 双检锁失效相关资料。

8)ThreadLocal(线程局部变量)关键字:

答:当使用 ThreadLocal 维护变量时,其为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立的改变自己的副本,而不会影响其他线程对应的副本。

ThreadLocal 内部实现机制:

  1. 每个线程内部都会维护一个类似 HashMap 的对象,称为 ThreadLocalMap,里边会包含若干了 Entry(K-V 键值对),相应的线程被称为这些 Entry 的属主线程;

  2. Entry 的 Key 是一个 ThreadLocal 实例,Value 是一个线程特有对象。Entry 的作用即是:为其属主线程建立起一个 ThreadLocal 实例与一个线程特有对象之间的对应关系;

  3. Entry 对 Key 的引用是弱引用;Entry 对 Value 的引用是强引用。

<img src=https://www.jb51.cc/res/2019/03-16/23/74713424e22bedbc89ac3d253baa6b00.jpg alt=>

9)Atomic 关键字:

答:可以使基本数据类型以原子的方式实现自增自减等操作。参考我的博客:concurrent.atomic 包下的类 AtomicInteger 的使用。

10)线程池有了解吗?(必考)

答:java.util.concurrent.ThreadPoolExecutor 类就是一个线程池。客户端调用 ThreadPoolExecutor.submit(Runnable task) 提交任务,线程池内部维护的工作者线程的数量就是该线程池的线程池大小,有 3 种形态:

  • 当前线程池大小 :表示线程池中实际工作者线程的数量;

  • 最大线程池大小 (maxinumPoolSize):表示线程池中允许存在的工作者线程的数量上限;

  • 核心线程大小 (corePoolSize ):表示一个不大于最大线程池大小的工作者线程数量上限。

  1. 如果运行的线程少于 corePoolSize,则 Executor 始终首选添加新的线程,而不进行排队;

  2. 如果运行的线程等于或者多于 corePoolSize,则 Executor 始终首选将请求加入队列,而不是添加新线程;

  3. 如果无法将请求加入队列,即队列已经满了,则创建新的线程,除非创建此线程超出 maxinumPoolSize, 在这种情况下,任务将被拒绝。

小结:本小节内容涉及到 Java 中多线程编程,线程安全等知识,是面试中的重点和难点。

(四)JVM 内存管理

既然是 Java 开发面试,那么对 JVM 的考察当然也是必须的,面试官一般会问你对 JVM 有了解吗?

我通常都会把我所了解的都说一遍,包括:JVM 内存划分、JVM 垃圾回收的含义,有哪些 GC 算法,年轻代和老年代各自的特点统统阐述一遍。

1)JVM 内存划分:

  1. 方法区(线程共享):常量、静态变量、JIT(即时编译器) 编译后的代码也都在方法区;

  2. 堆内存(线程共享):垃圾回收的主要场所;

  3. 程序计数器: 当前线程执行的字节码的位置指示器;

  4. 虚拟机栈(栈内存):保存局部变量、基本数据类型变量以及堆内存中某个对象的引用变量;

  5. 本地方法栈 :为 JVM 提供使用 native 方法的服务。

2)类似-Xms、-Xmn 这些参数的含义:

答:

堆内存分配:

  1. JVM 初始分配的内存由-Xms 指定,默认是物理内存的 1/64;

  2. JVM 最大分配的内存由-Xmx 指定,默认是物理内存的 1/4;

  3. 默认空余堆内存小于 40% 时,JVM 就会增大堆直到-Xmx 的最大限制;空余堆内存大于 70% 时,JVM 会减少堆直到 -Xms 的最小限制;

  4. 因此服务器一般设置-Xms、-Xmx 相等以避免在每次 GC 后调整堆的大小。对象的堆内存由称为垃圾回收器的自动内存管理系统回收。

非堆内存分配:

  1. JVM 使用-XX:PermSize 设置非堆内存初始值,默认是物理内存的 1/64;

  2. 由 XX:MaxPermSize 设置最大非堆内存的大小,默认是物理内存的 1/4;

  3. -Xmn2G:设置年轻代大小为 2G;

  4. -XX:SurvivorRatio,设置年轻代中 Eden 区与 Survivor 区的比值。

3)垃圾回收算法有哪些?

答:

  1. 引用计数 :原理是此对象有一个引用,即增加一个计数,删除一个引用则减少一个计数。垃圾回收时,只用收集计数为 0 的对象。此算法最致命的是无法处理循环引用的问题;

  2. 标记-清除 :此算法执行分两阶段。第一阶段从引用根节点开始标记所有被引用的对象,第二阶段遍历整个堆,把未标记的对象清除;

此算法需要暂停整个应用,同时,会产生内存碎片;

  1. 复制算法 :此算法把内存空间划为两个相等的区域,每次只使用其中一个区域。垃圾回收时,遍历当前使用区域,把正在使用中的对象复制到另外一个区域中;

此算法每次只处理正在使用中的对象,因此复制成本比较小,同时复制过去以后还能进行相应的内存整理,不会出现 “碎片” 问题。当然,此算法的缺点也是很明显的,就是需要两倍内存空间;

  1. 标记-整理 :此算法结合了 “标记-清除” 和 “复制” 两个算法的优点。也是分两阶段,第一阶段从根节点开始标记所有被引用对象,第二阶段遍历整个堆,把清除未标记对象并且把存活对象 “压缩” 到堆的其中一块,按顺序排放。

此算法避免了 “标记-清除” 的碎片问题,同时也避免了 “复制” 算法的空间问题。

4)root 搜索算法中,哪些可以作为 root?

答:

  • 被启动类(bootstrap 加载器)加载的类和创建的对象;

  • JavaStack 中的引用的对象 (栈内存中引用的对象);

  • 方法区中静态引用指向的对象;

  • 方法区中常量引用指向的对象;

  • Native 方法中 JNI 引用的对象。

5)GC 什么时候开始?

答:GC 经常发生的区域是堆区,堆区还可以细分为新生代、老年代,新生代还分为一个 Eden 区和两个 Survivor 区。

  1. 对象优先在 Eden 中分配,当 Eden 中没有足够空间时,虚拟机将发生一次 Minor GC,因为 Java 大多数对象都是朝生夕灭,所以 Minor GC 非常频繁,而且速度也很快;

  2. Full GC,发生在老年代的 GC,当老年代没有足够的空间时即发生 Full GC,发生 Full GC 一般都会有一次 Minor GC。

大对象直接进入老年代,如很长的字符串数组,虚拟机提供一个;XX:PretenureSizeThreadhold 参数,令大于这个参数值的对象直接在老年代中分配,避免在 Eden 区和两个 Survivor 区发生大量的内存拷贝;

  1. 发生 Minor GC 时,虚拟机会检测之前每次晋升到老年代的平均大小是否大于老年代的剩余空间大小,如果大于,则进行一次 Full GC,如果小于,则查看 HandlePromotionFailure 设置是否允许担保失败,如果允许,那只会进行一次 Minor GC,如果不允许,则改为进行一次 Full GC。

6)内存泄漏和内存溢出

答:

概念:

  1. 内存溢出指的是内存不够用了;

  2. 内存泄漏是指对象可达,但是没用了。即本该被 GC 回收的对象并没有被回收;

  3. 内存泄露是导致内存溢出的原因之一;内存泄露积累起来将导致内存溢出。

内存泄漏的原因分析:

  1. 长生命周期的对象引用短生命周期的对象;

  2. 没有将无用对象置为 null。

小结:本小节涉及到 JVM 虚拟机,包括对内存的管理等知识,相对较深。除了以上问题,面试官会继续问你一些比较深的问题,可能也是为了看看你的极限在哪里吧。
比如:内存调优、内存管理,是否遇到过内存泄漏的实际案例、是否真正关心过内存等。由于本人实际项目经验不足,这些深层次问题并没有接触过,各位有需要可以上网查阅。

(五)Java 8 相关知识点

关于 Java8 中新知识点,面试官会让你说说 Java8 你了解多少,下边主要阐述我所了解,并且在面试中回答的 Java8 新增知识点。

1)HashMap 的底层实现有变化:HashMap 是数组 + 链表 + 红黑树(JDK1.8 增加了红黑树部分)实现。

2)JVM 内存管理方面,由元空间代替了永久代。

区别:

  1. 元空间并不在虚拟机中,而是使用本地内存;

  2. 默认情况下,元空间的大小仅受本地内存限制;

  3. 也可以通过-XX:MetaspaceSize 指定元空间大小。

3)Lambda 表达式(也称为闭包),允许我们将函数当成参数传递给某个方法,或者把代码本身当做数据处理。

4)函数式接口:指的是只有一个函数的接口,java.lang.Runnable 和 java.util.concurrent.Callable 就是函数式接口的例子;java8 提供了一个特殊的注解 @Functionallnterface 来标明该接口是一个函数式接口。

5)引入重复注解:Java 8 中使用 @Repeatable 注解定义重复注解。

6)接口中可以实现方法 default 方法。

7) 注解的使用场景拓宽: 注解几乎可以使用在任何元素上:局部变量、接口类型、超类和接口实现类,甚至可以用在函数的异常定义上。

8) 新的包 java.time 包

  1. 包含了所有关于日期、时间、时区、持续时间和时钟操作的类;

  2. 这些类都是不可变的、线程安全的。

小结:Java8 的一些新特性,面试官一般情况下不要求你有多么精通,主要是看看你有没有一些了解。

(六)网络协议相关

网络协议方面,考察最多的包括服务器和客户端在三次握手、四次挥手过程中的状态变化;还有网络拥塞控制,及其解决办法等。

1)三次握手、四次挥手示意图:

<img src=https://www.jb51.cc/res/2019/03-16/23/90da328b76ccddea4f60dd9cf5145b88.jpg alt=>

总共有四种状态:主动建立连接、主动断开连接、被动建立连和被动断开连接

两两组合还是 4 种组合:

  1. 主动建立连接、主动断开连接会经历的状态:
    SYNC_SENT——ESTABLISHED—-FIN_WAIT_1—-FIN_WAIT_2—-TIME_WAIT

  2. 主动建立连接、被动断开连接会经历的状态:
    SYNC_SENT——ESTABLISHED—-CLOSE_WAIT—-LAST_ACK

  3. 被动建立连接、主动断开连接会经历的状态:
    LISTEN—-SYN_RCVD—-ESTABLISHED—-FIN_WAIT_1—-FIN_WAIT_2—-TIME_WAIT

  4. 被动建立连接、被动断开连接会经历的状态:
    LISTEN—-SYN_RCVD—-ESTABLISHED—-CLOSE_WAIT—-LAST_ACK

2)滑动窗口机制

由发送方和接收方在三次握手阶段,互相将自己的最大可接收的数据量告诉对方。也就是自己的数据接收缓冲池的大小。这样对方可以根据已发送的数据量来计算是否可以接着发送。

在处理过程中,当接收缓冲池的大小发生变化时,要给对方发送更新窗口大小的通知。

3)拥塞避免机制

拥塞:对资源的需求超过了可用的资源。若网络中许多资源同时供应不足,网络的性能就要明显变坏,整个网络的吞吐量随之负荷的增大而下降。

拥塞控制:防止过多的数据注入到网络中,使得网络中的路由器或链路不致过载。

拥塞控制方法:

  • 慢开始 + 拥塞避免;

  • 快重传 + 快恢复。

<img src=https://www.jb51.cc/res/2019/03-16/23/263da2b28578374aae35b687ab769227.jpg alt=><img src=https://www.jb51.cc/res/2019/03-16/23/20f28c2fca25490dae6d42258c5bdccc.jpg alt=>

4)浏览器中输入:“www.xxx.com” 之后都发生了什么?请详细阐述。

解析:经典的网络协议问题。

答:

  1. 由域名→IP 地址
    寻找 IP 地址的过程依次经过了浏览器缓存、系统缓存、hosts 文件、路由器缓存、 递归搜索根域名服务器。

  2. 建立 TCP/IP 连接(三次握手具体过程)

  3. 由浏览器发送一个 HTTP 请求

  4. 经过路由器的转发,通过服务器的防火墙,该 HTTP 请求到达了服务器

  5. 服务器处理该 HTTP 请求,返回一个 HTML 文件

  6. 浏览器解析该 HTML 文件,并且显示在浏览器端

  7. 这里需要注意:

  • HTTP 协议是一种基于 TCP/IP 的应用层协议,进行 HTTP 数据请求必须先建立 TCP/IP 连接

  • 可以这样理解:HTTP 是轿车,提供了封装或者显示数据的具体形式;Socket 是发动机,提供了网络通信的能力。

  • 两个计算机之间的交流无非是两个端口之间的数据通信,具体的数据会以什么样的形式展现是以不同的应用层协议来定义的。

5)常见 HTTP 状态码

  1. 1xx(临时响应)

  2. 2xx(成功)

  3. 3xx(重定向):表示要完成请求需要进一步操作

  4. 4xx(错误):表示请求可能出错,妨碍了服务器的处理

  5. 5xx(服务器错误):表示服务器在尝试处理请求时发生内部错误

  6. 常见状态码:

  • 200(成功)

  • 304(未修改):自从上次请求后,请求的网页未修改过。服务器返回此响应时,不会返回网页内容

  • 401(未授权):请求要求身份验证

  • 403(禁止):服务器拒绝请求

  • 404(未找到):服务器找不到请求的网页

6)TCP 和 UDP 的区别:

答:

  1. 回答发送数据前是否存在建立连接的过程;

  2. TCP过确认机制,丢包可以重发,保证数据的正确性;UDP不保证正确性,只是单纯的负责发送数据包;

  3. UDP 是面向报文的。发送方的 UDP 对应用程序交下来的报文,在添加首部后就向下交付给 IP 层。既不拆分,也不合并,而是保留这些报文的边界,因 此,应用程序需要选择合适的报文大小;

  4. UDP 的头部,只有 8 个字节,相对于 TCP 头部的 20 个字节信息包的额外开销很小。

限于篇幅,更多网络协议相关知识,请参阅我的博客:TCP/IP 协议面试常问知识点,倾心总结

小结:必须熟练掌握 TCP 和 UDP 的区别、三次握手和四次挥手的状态切换,必考。

(七)数据库知识点

既然是后端开发,那么与数据库相关的知识点也是必不可少的。

1)MySQL 和 MongoDB 的区别有哪些?如何选择?

2)MongoDB 的优缺点有哪些?

(ps 本人对这一块不是很熟悉,就不附上参考答案了,请各位小伙伴自行学习哈~)

3)听说过事务吗?(必考)

答:作为单个逻辑工作单元执行的一系列操作,满足四大特性:

  1. 原子性(Atomicity):事务作为一个整体被执行 ,要么全部执行,要么全部不执行;

  2. 一致性(Consistency):保证数据库状态从一个一致状态转变为另一个一致状态;

  3. 隔离性(Isolation):多个事务并发执行时,一个事务的执行不应影响其他事务的执行;

  4. 持久性(Durability):一个事务一旦提交,对数据库的修改应该永久保存。

4)事务的并发问题有哪几种?

答:丢失更新、脏读、不可重复读以及幻读。

5)数据库中的锁有哪几种?
答:独占锁、排他锁以及更新锁。

6)事务的隔离级别有哪几种?

答:读未提交、读已提交、可重复读和序列化。

扩展问题:MySQL 事务默认隔离级别是哪个?

答:可重复读。

解析:关于问题(4)(5)(6)的详细解答,请参阅我的博客:数据库并发机制和事务的隔离级别详解

(ps,关于数据库事务方面的深层次考察还有分布式事务即两段提交和三段提交等,限于本人水平,请各位自行学习)

7)数据库的索引有什么作用?(必考) 底层数据结构是什么,为什么使用这种数据结构?

答:

  1. 索引 是对数据库表中一列或多列的值进行排序的一种结构,使用索引可快速访问数据库表中的特定信息;

  2. 底层数据结构是 B+ 树;

  3. 使用 B+ 树的原因:查找速度快、效率高,在查找的过程中,每次都能抛弃掉一部分节点,减少遍历个数。( 此时,你应该在白纸上画出什么是 B+ 树 )

扩展问题:聚簇索引和非聚簇索引的区别?

8)MyISAM 和 InnoDB 的区别有哪些?

答:

  • MyISAM 不支持事务,InnoDB 是事务类型的存储引擎;

  • MyISAM 只支持表级锁,BDB 支持页级锁和表级锁,默认为页级锁;而 InnoDB 支持行级锁和表级锁,默认为行级锁;

  • MyISAM 引擎不支持外键,InnoDB 支持外键;

  • MyISAM 引擎的表在大量高并发的读写下会经常出现表损坏的情况;

  • 对于 count( ) 查询来说 MyISAM 更有优势;

  • InnoDB 是为处理巨大数据量时的最大性能设计,它的 CPU 效率可能是任何其它基于磁盘的关系数据库引擎所不能匹敌的;

  • MyISAM 支持全文索引(FULLTEXT),InnoDB 不支持;

  • MyISAM 引擎的表的查询、更新、插入的效率要比 InnoDB 高。

最主要的区别是:MyISAM 表不支持事务、不支持行级锁、不支持外键。 InnoDB 表支持事务、支持行级锁、支持外键。(可直接回答这个)

9)数据库中 Where、group by、having 关键字:

答: 关键字作用:

  1. where 子句用来筛选 from 子句中指定的操作所产生的的行;

  2. group by 子句用来分组 where 子句的输出;

  3. having 子句用来从分组的结果中筛选行;

having 和 where 的区别:

  1. 语法类似,where 搜索条件在进行分组操作之前应用;having 搜索条件在进行分组操作之后应用;

  2. having 可以包含聚合函数 sum、avg、max 等;

  3. having 子句限制的是组,而不是行。

当同时含有 where 子句、group by 子句 、having 子句及聚集函数时,执行顺序如下:

  1. 执行 where 子句查找符合条件的数据;

  2. 使用 group by 子句对数据进行分组;对 group by 子句形成的组运行聚集函数计算每一组的值;最后用 having 子句去掉不符合条件的组。

10)还有一些问题,如 MySQL 和 SQL Server 用法上的区别、limit 关键字的使用等问题。

小结:数据库方面还是事务机制、隔离级别比较重要,当然了数据库索引是必考的问题。偶尔也会给你几个表,让你现场写 SQL 语句,主要考察 group by 和 having 等关键字。

(八)MVC 框架相关知识点

我在项目中使用的框架有 Spring MVC 和 MyBatis,所以在简历上只写了这两种框架,面试官主要针对这两种框架进行提问。以下问题供小伙伴们参考。

JavaWeb 开发经典的 3 层框架:Web 层、Service 层(业务逻辑层)和 Dao 层(数据访问层)

  • Web 层:包含 JSP 和 Servlet 等与 Web 相关的内容;

  • 业务层:只关心业务逻辑;

  • 数据层:封装了对数据库的访问细节。

Spring 知识点

1)Spring 的 IOC 和 AOP 有了解吗?

答:

  • IOC:控制反转,(解耦合)将对象间的依赖关系交给 Spring 容器,使用配置文件来创建所依赖的对象,由主动创建对象改为了被动方式;

  • AOP:面向切面编程,将功能代码从业务逻辑代码中分离出来。

2)AOP 的实现方式有哪几种?如何选择?(必考)

答:JDK 动态代理实现和 cglib 实现。

选择:

  1. 如果目标对象实现了接口,默认情况下会采用 JDK 的动态代理实现 AOP,也可以强制使用 cglib 实现 AOP;

  2. 如果目标对象没有实现接口,必须采用 cglib 库,Spring 会自动在 JDK 动态代理和 cglib 之间转换。

扩展:JDK 动态代理如何实现?(加分点)

答:JDK 动态代理,只能对实现了接口的类生成代理,而不是针对类,该目标类型实现的接口都将被代理。原理是通过在运行期间创建一个接口的实现类来完成对目标对象的代理。

  1. 定义一个实现接口 InvocationHandler 的类;

  2. 通过构造函数,注入被代理类;

  3. 实现 invoke( Object proxy,Method method,Object[] args)方法;

  4. 在主函数中获得被代理类的类加载器;

  5. 使用 Proxy.newProxyInstance( ) 产生一个代理对象;

  6. 通过代理对象调用各种方法。

解析:关于 IOC 和 AOP 的详细阐述,请各位参阅我的博客:Spring 核心 AOP(面向切面编程)总结,Spring 框架学习—控制反转(IOC)

3)Spring MVC 的核心控制器是什么?消息处理流程有哪些?

答:核心控制器为 DispatcherServlet。消息流程如下:

<img src=https://www.jb51.cc/res/2019/03-16/23/fa0a33b96b08d7ffc6ded52cf4c2996a.jpg alt=>

4)其他问题包括:重定向和转发的区别、动态代理和静态代理的区别等。

Mybatis 知识点

关于 MyBatis 主要考察占位符#和 $ 的区别,区别如下:

  1. 符号将传入的数据都当做一个字符串,会对自动传入的数据加一个双引号;

  2. $ 符号将传入的数据直接显示生成 SQL 中;

  3. 符号存在预编译的过程,,对问号赋值,防止 SQL 注入;

  4. $ 符号是直译的方式,一般用在 order by ${列名}语句中;

  5. 能用#号就不要用 $ 符号。

小结:限于作者水平,MVC 框架方面了解不是太多,实战能力欠缺。面试官偶尔问框架底层实现原理等都知之甚少,有能力的小伙伴可以多加学习。

(九)大数据相关知识点

大数据相关是因为我的简历上写了 KafKa 相关项目,所以面试官会进行提问 KafKa 相关知识点,我也进行了一些简单概念总结,深层次的实现原理因为并没有特别多的实战经验,所以并不了解。

以下概念总结供小伙伴参考。

1)KafKa 基本特性:

答:快速持久化、支持批量读写消息、支持消息分区,提高了并发能力、支持在线增加分区、支持为每个分区创建多个副本。

扩展:为什么可以实现快速持久化?

答:KafKa 将消息保存在磁盘中,并且读写磁盘的方式是顺序读写,避免了随机读写磁盘(寻道时间过长)导致的性能瓶颈;磁盘的顺序读写速度超过内存随机读写。

2)核心概念:

答:

  • 生产者(Producer): 生产消息,并且按照一定的规则推送到 Topic 的分区中。

  • 消费者(Consumer): 从 Topic 中拉去消息,并且进行消费。

  • 主题(Topic): 用于存储消息的逻辑概念,是一个消息集合。

  • 分区(partition):

  1. 每个 Topic 可以划分为多个分区,每个消息在分区中都会有一个唯一编号 offset

  2. kafka 通过 offset 保证消息在分区中的顺序

  3. 同一 Topic 的不同分区可以分配在不同的 Broker 上

  4. partition 以文件的形式存储在文件系统中。

副本(replica):

  1. KafKa 对消息进行了冗余备份,每个分区有多个副本,每个副本中包含的消息是 “一样” 的。

  2. 每个副本中都会选举出一个 Leader 副本,其余为 Follower 副本,Follower 副本仅仅将数据从 Leader 副本拉去到本地,然后同步到自己的 Log 中。

消费者组(Consumer Group): 每个 consumer 都属于一个 consumer group,每条消息只能被 consumer group 中的一个 Consumer 消费,但可以被多个 consumer group 消费。

Broker:

  1. 一个单独的 server 就是一个 Broker;

  2. 主要工作:接收生产者发过来的消息,分配 offset,并且保存到磁盘中;

Cluster&Controller:

  1. 多个 Broker 可以组成一个 Cluster,每个集群选举一个 Broker 来作为 Controller,充当指挥中心

  2. Controller 负责管理分区的状态,管理每个分区的副本状态,监听 ZooKeeper 中数据的变化等工作

保留策略和日志压缩:

  1. 不管消费者是否已经消费了消息,KafKa 都会一直保存这些消息(持久化到磁盘);

  2. 通过保留策略,定时删除陈旧的消息;

  3. 日志压缩,只保留最新的 Key-Value 对。

关于副本机制:(加分点)

ISR 集合 :表示当前 “可用” 且消息量与 Leader 相差不多的副本集合。满足条件如下:

  1. 副本所在节点必须维持着与 ZooKeeper 的连接;

  2. 副本最后一条信息的 offset 与 Leader 副本的最后一条消息的 offset 之间的差值不能超过指定的阈值。

HW&LEO:

  1. HW 标记了一个特殊的 offset,当消费者处理消息的时候,只能拉取到 HW 之前的消息;

  2. HW 也是由 Leader 副本管理的;

  3. LEO(Log End Offset)是所有副本都会有的一个 offset 标记。

ISR、HW 和 LEO 的工作配合:

  1. producer 向此分区中推送消息;

  2. Leader 副本将消息追加到 Log 中,并且递增其 LEO;

  3. Follower 副本从 Leader 副本中拉取消息进行同步;

  4. Follower 副本将消息更新到本地 Log 中,并且递增其 LEO;

  5. 当 ISR 集合中的所有副本都完成了对 offset 的消息同步,Leader 副本会递增其 HW

KafKa 的容灾机制: 通过分区的副本 Leader 副本和 Follower 副本来提高容灾能力。

小结:请小伙伴根据自己的简历自行准备学习大数据相关知识点。

(十)Linux 常见命令

作者对这一方面不是很精通,知识点来源于网络总结以及面试官的提问,仅供小伙伴参考。

1)grep、sed 以及 awk 命令

解析:awk 命令如果可以掌握,是面试中的一个 加分点。

2)文件和目录:

pwd 显示当前目录

ls 显示当前目录下的文件和目录:

  1. ls -F 可以区分文件和目录;

  2. ls -a 可以把隐藏文件和普通文件一起显示出来;

  3. ls -R 可以递归显示子目录中的文件和目录;

  4. ls -l 显示长列表;

  5. ls -l test 过滤器,查看某个特定文件信息。可以只查看 test 文件的信息。

3)处理文件方面的命令有:touch、cp、 In、mv、rm、

4)处理目录方面的命令:mkdir

5)查看文件内容:file、cat、more、less、tail、head

6)监测程序命令:ps、top

eg. 找出进程名中包括 java 的所有进程:ps -ef | grep java

top 命令 实时监测进程

top 命令输出的第一部分:显示系统的概括。

  1. 第一行显示了当前时间、系统的运行时间、登录的用户数和系统的平均负载(平均负载有 3 个值:最近 1min 5min 15min);

  2. 第二行显示了进程的概要信息,有多少进程处于运行、休眠、停止或者僵化状态;

  3. 第三行是 CPU 的概要信息;

  4. 第四行是系统内存的状态。

7)ps 和 top 命令的区别:

  1. ps 看到的是命令执行瞬间的进程信息,而 top 可以持续的监视;

  2. ps 只是查看进程,而 top 还可以监视系统性能,如平均负载,cpu 和内存的消耗;

  3. 另外 top 还可以操作进程,如改变优先级 (命令 r) 和关闭进程 (命令 k);

  4. ps 主要是查看进程的,关注点在于查看需要查看的进程;

  5. top 主要看 cpu,内存使用情况,及占用资源最多的进程由高到低排序,关注点在于资源占用情况。

8) 压缩数据

  1. tar -xvf 文件名;

  2. tar -zxvf 文件名;

  3. tar -cvzf 文件名。

9)结束进程:kill PID 或者 kill all

至此,从十个不同的方面阐述了 Java 开发面试岗位中所涉及到的重要知识点。加上我上次发布的 关于算法面试的 chat,我大概将最近一年的时间内的面试笔试经验给大家做了总结分享。

接下来,为了给大家提供更多的帮助,我想针对简历方面和大家聊聊,主要包括: 制作简历和投递简历两方面。

制作简历:

首先,我想先介绍下我的简历都包括哪些部分:

  1. 个人信息

  2. 教育背景

  3. 个人亮点

  4. 实习项目

  5. 校内项目

  6. 奖励、语言及 IT 能力

  7. 个人综述

简历应该突出表现自己优秀的地方,所以我将个人技术博客和 github 地址放到了相对靠前的位置,使面试官或者 HR 可以一眼看到。

各位小伙伴如果在校期间参加过什么大型竞赛并且获奖,也可以写在明显的位置。

给大家一个小提示,那就是必须对简历上所写的知识点有一定了解,不懂的就不要写上去了,因为你会被问的很惨(即使一面面试官不问,三面面试官也会问的)。

比如说你在简历上写了一个技术 A,说自己在项目中使用过技术 A,那么面试官就会问该技术 A 的底层实现、原理等等。

如果你回答出来,确实,这是加分项,但是很多时候我们是回答不出来的。

投递简历

投递简历同样很重要,对于并不是出身名校的小伙伴来说,在投递一些互联网公司的时候,可能会面临着简历被刷的可能。这时候内推的重要性就体现出来了。

内推是内部推荐的意思, 内推的好处一般有两种:

  1. 内推免简历筛选,直接进入笔试环节;

  2. 内推可以直接获得面试资格,面试失败可以继续参加后续校招大流程。

内推时间点:

  1. 暑期实习校园招聘的内推一般从每年 3 月份开始;

  2. 秋招的内推从每年 8 月份开始,持续到九月份。

如何内推?

内推,意味着你要找到公司内部人员进行推荐,内推渠道主要是找自己的师兄师姐。另外可以时常关注号称 “全国最大的高校论坛”- 北邮人论坛 。

北邮人论坛资源相当丰富,每年招聘季的内推帖子数不胜数,各位如果有需要,可以关注一下。在牛客网以及赛码网的讨论区内也存在着大量的内推消息,大家可以关注。

结束语

这是一篇很长的文章,然而,再长的文章也道不尽我这一年中面试笔试的所有经历。找工作是一场持久战,坚持到最后的才是胜利者。

对于各位志在投身 Java 开发岗位的小伙伴们来说,本文所提到的知识点绝对是面试中的重点,希望各位可以有效掌握。

(限于作者水平,文中如有错误之处,烦请各位指出,我们一起进步。)

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


聊聊SpringBoot和传统的SSM的区别? SpringBoot是Spring的扩展,在Spring的基础上,简化了传统的SSM开发繁琐的配置; 在部署上,SpringBoot内置了Tomcat,
在面试当中,有时候会问到你在项目中用过多线程么? 对于普通的应届生或者工作时间不长的初级开发 ???—— crud仔流下了没有技术的眼泪。 博主这里整理了项目中用到了多线程的一个简单的实例,希望能对你
@ 介绍一下你做的某些模块,有些什么比较复杂的地方? 略。 你们的文件怎么存储的? 我们的文件是存储在MongoDB中的。 MongoDB单个文档的存储限制是16M,如果要存储大于16M的文件,就要用
前言 本文分为20多个问题,通过问题的方式,来逐渐理解jvm,由浅及深。希望帮助到大家。 1.&#160;Java类实例化时,JVM执行顺序? 正确的顺序如下: 1父类静态代码块 2父类静态变量 3子
1.&#160;Java线程的创建方式 (1)继承thread类 thread类本质是实现了runnable接口的一个实例,代表线程的一个实例。启动线程的方式start方法。start是一个本地方法,
1.&#160;SpringBoot简介 SpringBoot是简化Spring应用开发的一个框架。他整合了Spring的技术栈,提供各种标准化的默认配置。使得我们可以快速开发Spring项目,免掉x
1.&#160;List List 是有序的 Collection。Java List 一共三个实现类: 分别是 ArrayList、Vector 和 LinkedList ArrayList Arr
一、Spring面试题1、Spring 在ssm中起什么作用?Spring:轻量级框架作用:Bean工厂,用来管理Bean的生命周期和框架集成。两大核心:①. IOC/DI(控制反转/依赖注入) :把dao依赖注入到service层,service层反转给action层,Spring顶层容器为BeanFactory。②. AOP:面向切面编程2、Spring的事务?编程式事务管理:编程方式管理事务,极大灵活性,难维护。声明式事务管理:可以将业务代码和事务管理分离,用注解和xml配置来管理事务。3、IOC 在项目中的作用?作用:Ioc解决对象之间的依赖问题,把所有Bean的依赖关系通过配置文件或注解关联起来,降低了耦合度。4、Spring的配置文件中的内容?开启事务注解驱动事务管理器开启注解功能,并配置扫描包配置数据库配置SQL会话工厂,别名,映射文件不用编写Dao层的实现类5、Spring下的注解?注册:@Controller @Service @Component注入:@Autowired @Resource请求地址:@RequestMapping返回具体数据类型而非跳转:@ResponseBody6、Spring DI 的三种方式?构造器注入:通过构造方法初始化<constructor-arg index="0" type="java.lang.String" value="宝马"></constructor-arg>setter方法注入:通过setter方法初始化<property name="id" value="1111"></property>接口注入7、Spring主要使用了什么模式?工厂模式:每个Bean的创建通过方法单例模式:默认的每个Bean的作用域都是单例代理模式:关于Aop的实现通过代理模式8、IOC,AOP的实现原理?IOC:通过反射机制生成对象注入AOP:动态代理二、SpringMvc面试题1、SpringMvc 的控制器是不是单例模式,如果是,有什么问题,怎么解决?问题:单例模式,在多线程访问时有线程安全问题解决方法:不要用同步,在控制器里面不能写字段2、SpringMvc 中控制器的注解?@Controller:该注解表明该类扮演控制器的角色3、@RequestMapping 注解用在类上的作用?作用:用来映射一个URL到一个类或者一个特定的处理方法上4、前台多个参数,这些参数都是一个对象,快速得到对象?方法:直接在方法中声明这个对象,SpringMvc就自动把属性赋值到这个对象里面5、SpringMvc中函数的返回值?String,ModelAndView,List,Set 等一般String,Ajax请求,返回一个List集合6、SpringMvc中的转发和重定向?转发: return:“hello”重定向 :return:“redirect:hello.jsp”7、SpringMvc和Ajax之间的相互调用?通过JackSon框架把java里面对象直接转换成js可识别的json对象,具体步骤如下:加入JackSon.jar在配置文件中配置json的映射在接受Ajax方法里面直接返回Object,list等,方法前面需要加上注解@ResponseBody8、SpringMvc的工作流程图? 9、Struts2 和 SpringMvc的区别?入口不同:Struts2:filter过滤器SpringMvc:一个Servlet即前端控制器开发方式不同:Struts2:基于类开发,传递参数通过类的属性,只能设置为多例SpringMvc:基于方法开发(一个url对应一个方法),请求参数传递到方法形参,可以为单例也可以为多例(建议单例)请求方式不同:Struts2:值栈村塾请求和响应的数据,通过OGNL存取数据SpringMvc:通过参数解析器将request请求内容解析,给方法形参赋值,将数据和视图封装成ModelAndView对象,最后又将ModelAndView中的模型数据通过request域传输到页面,jsp视图解析器默认使用的是jstl。三、Mybatis面试题1、Ibatis和Mybatis?Ibatis:2010年,apache的Ibatis框架停止更新,并移交给了google团队,同时更名为MyBatis。从2010年后Ibatis在没更新过,彻底变成了一个孤儿框架。一个没人维护的框架注定被mybatis拍在沙滩上。Mybatis:Ibatis的升级版本。2、什么是Mybatis的接口绑定,有什么好处?Mybatis实现了DAO接口与xml映射文件的绑定,自动为我们生成接口的具体实现,使用起来变得更加省事和方便。3、什么情况用注解,什么情况用xml绑定?注解使用情况:Sql语句简单时xml绑定使用情况:xml绑定 (@RequestMap用来绑定xml文件)4、Mybatis在核心处理类叫什么?SqlSession5、查询表名和返回实体Bean对象不一致,如何处理?映射键值对即可<result column="title" property="title" javaType="java.lang.String"/>column:数据库中表的列名property:实体Bean中的属性名6、Mybatis的好处?把Sql语句从Java中独立出来。封装了底层的JDBC,API的调用,并且能够将结果集自动转换成JavaBean对象,简化了Java数据库编程的重复工作。自己编写Sql语句,更加的灵活。入参无需用对象封装(或者map封装),使用@Param注解7、Mybatis配置一对多?<collection property="topicComment" column="id" ofType="com.tmf.bbs.pojo.Comment" select="selectComment" />property:属性名column:共同列ofType:集合中元素的类型select:要连接的查询8、Mybatis配置一对一?<association property="topicType" select="selectType" column="topics_type_id" javaType="com.tmf.bbs.pojo.Type"/>property:属性名select:要连接的查询column:共同列javaType:集合中元素的类型9 、${} 和 #{}的区别?${}:简单字符串替换,把${}直接替换成变量的值,不做任何转换,这种是取值以后再去编译SQL语句。#{}:预编译处理,sql中的#{}替换成?,补全预编译语句,有效的防止Sql语句注入,这种取值是编译好SQL语句再取值。总结:一般用#{}来进行列的代替10、获取上一次自动生成的主键值?select last _insert_id()11、Mybatis如何分页,分页原理?RowBounds对象分页在Sql内直接书写,带有物理分页12、Mybatis工作原理? 原理:通过SqlSessionFactoryBuilder从mybatis-config.xml配置文件中构建出SqlSessionFactory。SqlSessionFactory开启一个SqlSession,通过SqlSession实例获得Mapper对象并且运行Mapper映射的Sql语句。完成数据库的CRUD操作和事务提交,关闭SqlSession。四、结语前面如有不正确的地方还希望大家多多指教,希望和志同道合的朋友一起学习,一起进步,先更新到这里,下次继续补充。
基础篇一、基本功面向对象特征封装,继承,多态和抽象1. 封装封装给对象提供了隐藏内部特性和行为的能力。对象提供一些能被其他对象访问的方法来改变它内部的数据。在 Java 当中,有 3 种修饰符: public, private 和 protected。每一种修饰符给其他的位于同一个包或者不同包下面对象赋予了不同的访问权限。下面列出了使用封装的一些好处:通过隐藏对象的属性来保护对象内部的状态。提高了代码的可用性和可维护性,因为对象的行为可以被单独的改变或者是扩展。禁止对象之间的不良交互提高模块化2. 继承继承给对象提供了从基类获取字段和方法的能力。继承提供了代码的重用行,也可以在不修改类的情况下给现存的类添加新特性。3. 多态多态是编程语言给不同的底层数据类型做相同的接口展示的一种能力。一个多态类型上的操作可以应用到其他类型的值上面。4. 抽象抽象是把想法从具体的实例中分离出来的步骤,因此,要根据他们的功能而不是实现细节来创建类。 Java 支持创建只暴漏接口而不包含方法实现的抽象的类。这种抽象技术的主要目的是把类的行为和实现细节分离开。final, finally, finalize 的区别1. final修饰符(关键字)如果一个类被声明为final,意味着它不能再派生出新的子类,不能作为父类被继承。因此一个类不能既被声明为 abstract的,又被声明为final的。将变量或方法声明为final,可以保证它们在使用中不被改变。被声明为final的变量必须在声明时给定初值,而在以后的引用中只能读取,不可修改。被声明为final的方法也同样只能使用,不能重载。2. finally在异常处理时提供 finally 块来执行任何清除操作。如果抛出一个异常,那么相匹配的 catch 子句就会执行,然后控制就会进入 finally 块(如果有的话)。3. finalize方法名。Java 技术允许使用 finalize() 方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。这个方法是由垃圾收集器在确定这个对象没有被引用时对这个对象调用的。它是在 Object 类中定义的,因此所有的类都继承了它。子类覆盖 finalize() 方法以整理系统资源或者执行其他清理工作。finalize() 方法是在垃圾收集器删除对象之前对这个对象调用的。int 和 Integer 有什么区别int 是基本数据类型Integer是其包装类,注意是一个类。为什么要提供包装类呢???一是为了在各种类型间转化,通过各种方法的调用。否则 你无法直接通过变量转化。比如,现在int要转为Stringint a=0;String result=Integer.toString(a);在java中包装类,比较多的用途是用在于各种数据类型的转化中。我写几个demo//通过包装类来实现转化的int num=Integer.valueOf("12");int num2=Integer.parseInt("12");double num3=Double.valueOf("12.2");double num4=Double.parseDouble("12.2");//其他的类似。通过基本数据类型的包装来的valueOf和parseXX来实现String转为XXString a=String.valueOf("1234");//这里括号中几乎可以是任何类型String b=String.valueOf(true);String c=new Integer(12).toString();//通过包装类的toString()也可以String d=new Double(2.3).toString();再举例下。比如我现在要用泛型List<Integer> nums;这里<>需要类。如果你用int。它会报错的。重载和重写的区别override(重写)1. 方法名、参数、返回值相同。2. 子类方法不能缩小父类方法的访问权限。3. 子类方法不能抛出比父类方法更多的异常(但子类方法可以不抛出异常)。4. 存在于父类和子类之间。5. 方法被定义为final不能被重写。overload(重载)1. 参数类型、个数、顺序至少有一个不相同。2. 不能重载只有返回值不同的方法名。3. 存在于父类和子类、同类中。抽象类和接口有什么区别接口是公开的,里面不能有私有的方法或变量,是用于让别人使用的,而抽象类是可以有私有方法或私有变量的,另外,实现接口的一定要实现接口里定义的所有方法,而实现抽象类可以有选择地重写需要用到的方法,一般的应用里,最顶级的是接口,然后是抽象类实现接口,最后才到具体类实现。还有,接口可以实现多重继承,而一个类只能继承一个超类,但可以通过继承多个接口实现多重继承,接口还有标识(里面没有任何方法,如Remote接口)和数据共享(里面的变量全是常量)的作用。说说反射的用途及实现Java反射机制主要提供了以下功能:在运行时构造一个类的对象;判断一个类所具有的成员变量和方法;调用一个对象的方法;生成动态代理。反射最大的应用就是框架Java反射的主要功能:确定一个对象的类取出类的modifiers,数据成员,方法,构造器,和超类.找出某个接口里定义的常量和方法说明.创建一个类实例,这个实例在运行时刻才有名字(运行时间才生成的对象).取得和设定对象数据成员的值,如果数据成员名是运行时刻确定的也能做到.在运行时刻调用动态对象的方法.创建数组,数组大小和类型在运行时刻才确定,也能更改数组成员的值.反射的应用很多,很多框架都有用到spring 的 ioc/di 也是反射….javaBean和jsp之间调用也是反射….struts的 FormBean 和页面之间…也是通过反射调用….JDBC 的 classForName()也是反射…..hibernate的 find(Class clazz) 也是反射….反射还有一个不得不说的问题,就是性能问题,大量使用反射系统性能大打折扣。怎么使用使你的系统达到最优就看你系统架构和综合使用问题啦,这里就不多说了。来源:http://uule.iteye.com/blog/1423512说说自定义注解的场景及实现(此题自由发挥,就看你对注解的理解了!==)登陆、权限拦截、日志处理,以及各种Java框架,如Spring,Hibernate,JUnit 提到注解就不能不说反射,Java自定义注解是通过运行时靠反射获取注解。实际开发中,例如我们要获取某个方法的调用日志,可以通过AOP(动态代理机制)给方法添加切面,通过反射来获取方法包含的注解,如果包含日志注解,就进行日志记录。HTTP 请求的 GET 与 POST 方式的区别GET方法会把名值对追加在请求的URL后面。因为URL对字符数目有限制,进而限制了用在客户端请求的参数值的数目。并且请求中的参数值是可见的,因此,敏感信息不能用这种方式传递。POST方法通过把请求参数值放在请求体中来克服GET方法的限制,因此,可以发送的参数的数目是没有限制的。最后,通过POST请求传递的敏感信息对外部客户端是不可见的。参考:https://www.cnblogs.com/wangli-66/p/5453507.htmlsession 与 cookie 区别cookie 是 Web 服务器发送给浏览器的一块信息。浏览器会在本地文件中给每一个 Web 服务器存储 cookie。以后浏览器在给特定的 Web 服务器发请求的时候,同时会发送所有为该服务器存储的 cookie。下面列出了 session 和 cookie 的区别:无论客户端浏览器做怎么样的设置,session都应该能正常工作。客户端可以选择禁用 cookie,但是, session 仍然是能够工作的,因为客户端无法禁用服务端的 session。JDBC 流程1、 加载JDBC驱动程序:在连接数据库之前,首先要加载想要连接的数据库的驱动到JVM(Java虚拟机),这通过java.lang.Class类的静态方法forName(String className)实现。例如:try{//加载MySql的驱动类Class.forName("com.mysql.jdbc.Driver") ;}catch(ClassNotFoundException e){System.out.println("找不到驱动程序类 ,加载驱动失败!");e.printStackTrace() ;}成功加载后,会将Driver类的实例注册到DriverManager类中。2、 提供JDBC连接的URL连接URL定义了连接数据库时的协议、子协议、数据源标识。书写形式:协议:子协议:数据源标识协议:在JDBC中总是以jdbc开始 子协议:是桥连接的驱动程序或是数据库管理系统名称。数据源标识:标记找到数据库来源的地址与连接端口。例如:jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=gbk;useUnicode=true;(MySql的连接URL)表示使用Unicode字符集。如果characterEncoding设置为 gb2312或GBK,本参数必须设置为true 。characterEncoding=gbk:字符编码方式。3、创建数据库的连接要连接数据库,需要向java.sql.DriverManager请求并获得Connection对象, 该对象就代表一个数据库的连接。使用DriverManager的getConnectin(String url , String username , String password )方法传入指定的欲连接的数据库的路径、数据库的用户名和 密码来获得。例如: //连接MySql数据库,用户名和密码都是rootString url = "jdbc:mysql://localhost:3306/test" ;String username = "root" ;String password = "root" ;try{Connection con = DriverManager.getConnection(url , username , password ) ;}catch(SQLException se){System.out.println("数据库连接失败!");se.printStackTrace() ;}4、 创建一个Statement•要执行SQL语句,必须获得java.sql.Statement实例,Statement实例分为以下3 种类型:1、执行静态SQL语句。通常通过Statement实例实现。2、执行动态SQL语句。通常通过PreparedStatement实例实现。3、执行数据库存储过程。通常通过CallableStatement实例实现。具体的实现方式:Statement stmt = con.createStatement() ; PreparedStatement pstmt = con.prepareStatement(sql) ; CallableStatement cstmt = con.prepareCall(“{CALL demoSp(? , ?)}”) ;5、执行SQL语句Statement接口提供了三种执行SQL语句的方法:executeQuery 、executeUpdate 和execute1、ResultSet executeQuery(String sqlString):执行查询数据库的SQL语句 ,返回一个结果集(ResultSet)对象。2、int executeUpdate(String sqlString):用于执行INSERT、UPDATE或 DELETE语句以及SQL DDL语句,如:CREATE TABLE和DROP TABLE等3、execute(sqlString):用于执行返回多个结果集、多个更新计数或二者组合的 语句。 具体
面向对象编程的基本理念与核心设计思想解释下多态性(polymorphism),封装性(encapsulation),内聚(cohesion)以及耦合(coupling)。继承(Inheritance)与聚合(Aggregation)的区别在哪里。你是如何理解干净的代码(Clean Code)与技术负载(Technical Debt)的。描述下常用的重构技巧。阐述下 SOLID 原则。其他的譬如 KISS,DRY,YAGNI 等原则又是什么含义。什么是设计模式(Design Patterns)?你知道哪些设计模式?你有了解过存在哪些反模式(Anti-Patterns)吗?你会如何设计登陆舰/数学表达式计算程序/一条龙?你知道哪些基本的排序算法,它们的计算复杂度如何?在给定数据的情况下你会倾向于使用哪种算法呢?尝试编写如下代码:计算指定数字的阶乘开发 Fizz Buzz 小游戏倒转句子中的单词回文字符串检测枚举给定字符串的所有排列组合Java 核心概念equals 与 hashCode 的异同点在哪里?Java 的集合中又是如何使用它们的。描述下 Java 中集合(Collections),接口(Interfaces),实现(Implementations)的概念。LinkedList 与 ArrayList 的区别是什么?基础类型(Primitives)与封装类型(Wrappers)的区别在哪里?final 与 static 关键字可以用于哪里?它们的作用是什么?阐述下 Java 中的访问描述符(Access Modifiers)。描述下 String,StringBuilder 以及 StringBuffer 区别。接口(Interface)与抽象类(Abstract Class)的区别在哪里。覆盖(Overriding)与重载(OverLoading)的区别在哪里。异常分为哪几种类型?以及所谓的handle or declare原则应该如何理解?简述垃圾回收器的工作原理。你是如何处理内存泄露或者栈溢出问题的?如何构建不可变的类结构?关键点在哪里?什么是 JIT 编译?Java 8 / Java 7 为我们提供了什么新功能?即将到来的 Java 9 又带来了怎样的新功能?Hibernate / 数据库请解释下 ORM。简述下 Hibernate 的优劣特性。Hibernate 与 JPA 区别在哪?Hibernate 最新版提供了哪些特性?什么是懒加载(Lazy Loading)?什么是 N+1 难题?介绍一些熟悉的 Hibernate 注释。简介下 Hibernate Session 与 SessionFactory。Entity Beans 的状态有哪些。Hibernate 中的缓存分为几层。Hibernate 中事务的支持分为几级?什么是乐观锁(Optimistic Locking)?简述下 ACID 原则。简述下数据库正则化(Normalizations)。请介绍下你日常工作中优化慢查询(Slow Query)的策略。Spring新版的 Spring 中有哪些新特性?介绍下 Spring 的优势与缺陷。什么是控制反转(Inversion of Control)与依赖注入(Dependency Injection)?你用过哪些 Spring 的模块?Spring 中是如何使用依赖注入的?Spring 中提供了几种自动注入的机制?介绍下 Spring MVC。Spring 中 Scopes 有哪些?Spring 中 Bean 的生命周期包含哪些步骤?Spring Bean 与 EJB Bean 的区别在哪里?其他主题介绍下切面编程(Aspect Oriented Programming)。概述下 GET 与 POST 的区别。Web Server、Web Container 与 Application Server 的区别是什么?简要介绍下从浏览器输入 URL 开始到获取到请求界面之后 Java Web 应用中发生了什么。什么是 N 层架构?微服务(MicroServices)与巨石型应用(Monolithic Applications)之间的区别在哪里?你知道哪些商业级设计模式?你是如何测试一个应用的?知道哪些测试框架?你是如何测试单个方法的?在你的职业生涯中,算得上最困难的技术挑战是什么?什么是领域驱动开发(Domain Driven Development)?介绍下一些你最爱的 IDE 的常用插件。除了 IDE 之外,你的日常工作中还会用到哪些工具?你使用什么版本管理工具?分支(Branch)与标签(Tag)之间的区别在哪里?你常用的持续集成(Continuous Integration)、静态代码分析(Static Code Analysis)工具有哪些?
1、什么是线程池线程池的基本思想是一种对象池,在程序启动时就开辟一块内存空间,里面存放了众多(未死亡)的线程,池中线程执行调度由池管理器来处理。当有线程任务时,从池中取一个,执行完成后线程对象归池,这样可以避免反复创建线程对象所带来的性能开销,节省了系统的资源。2、使用线程池的好处减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务。运用线程池能有效的控制线程最大并发数,可以根据系统的承受能力,调整线程池中工作线线程的数目,防止因为消耗过多的内存,而把服务器累趴下(每个线程需要大约1MB内存,线程开的越多,消耗的内存也就越大,最后死机)。对线程进行一些简单的管理,比如:延时执行、定时循环执行的策略等,运用线程池都能进行很好的实现3、线程池的主要组件  一个线程池包括以下四个基本组成部分:线程池管理器(ThreadPool):用于创建并管理线程池,包括 创建线程池,销毁线程池,添加新任务;工作线程(WorkThread):线程池中线程,在没有任务时处于等待状态,可以循环的执行任务;任务接口(Task):每个任务必须实现的接口,以供工作线程调度任务的执行,它主要规定了任务的入口,任务执行完后的收尾工作,任务的执行状态等;任务队列(taskQueue):用于存放没有处理的任务。提供一种缓冲机制。4、ThreadPoolExecutor类讲到线程池,要重点介绍java.uitl.concurrent.ThreadPoolExecutor类,ThreadPoolExecutor线程池中最核心的一个类,ThreadPoolExecutor在JDK中线程池常用类UML类关系图如下: 我们可以通过ThreadPoolExecutor来创建一个线程池 new ThreadPoolExecutor(corePoolSize, maximumPoolSize,keepAliveTime,milliseconds,runnableTaskQueue, threadFactory,handler);1. 创建一个线程池需要输入几个参数corePoolSize(线程池的基本大小):当提交一个任务到线程池时,线程池会创建一个线程来执行任务,即使其他空闲的基本线程能够执行新任务也会创建线程,等到需要执行的任务数大于线程池基本大小时就不再创建。如果调用了线程池的prestartAllCoreThreads方法,线程池会提前创建并启动所有基本线程。maximumPoolSize(线程池最大大小):线程池允许创建的最大线程数。如果队列满了,并且已创建的线程数小于最大线程数,则线程池会再创建新的线程执行任务。值得注意的是如果使用了无界的任务队列这个参数就没什么效果。runnableTaskQueue(任务队列):用于保存等待执行的任务的阻塞队列。ThreadFactory:用于设置创建线程的工厂,可以通过线程工厂给每个创建出来的线程设置更有意义的名字,Debug和定位问题时非常又帮助。RejectedExecutionHandler(拒绝策略):当队列和线程池都满了,说明线程池处于饱和状态,那么必须采取一种策略处理提交的新任务。这个策略默认情况下是AbortPolicy,表示无法处理新任务时抛出异常。以下是JDK1.5提供的四种策略。n AbortPolicy:直接抛出异常。keepAliveTime(线程活动保持时间):线程池的工作线程空闲后,保持存活的时间。所以如果任务很多,并且每个任务执行的时间比较短,可以调大这个时间,提高线程的利用率。TimeUnit(线程活动保持时间的单位):可选的单位有天(DAYS),小时(HOURS),分钟(MINUTES),毫秒(MILLISECONDS),微秒(MICROSECONDS, 千分之一毫秒)和毫微秒(NANOSECONDS, 千分之一微秒)。2. 向线程池提交任务我们可以通过execute()或submit()两个方法向线程池提交任务,不过它们有所不同execute()方法没有返回值,所以无法判断任务知否被线程池执行成功threadsPool.execute(new Runnable() {@Overridepublic void run() {// TODO Auto-generated method stub}});submit()方法返回一个future,那么我们可以通过这个future来判断任务是否执行成功,通过future的get方法来获取返回值try {Object s = future.get();} catch (InterruptedException e) {// 处理中断异常} catch (ExecutionException e) {// 处理无法执行任务异常} finally {// 关闭线程池executor.shutdown();}3. 线程池的关闭我们可以通过shutdown()或shutdownNow()方法来关闭线程池,不过它们也有所不同shutdown的原理是只是将线程池的状态设置成SHUTDOWN状态,然后中断所有没有正在执行任务的线程。shutdownNow的原理是遍历线程池中的工作线程,然后逐个调用线程的interrupt方法来中断线程,所以无法响应中断的任务可能永远无法终止。shutdownNow会首先将线程池的状态设置成STOP,然后尝试停止所有的正在执行或暂停任务的线程,并返回等待执行任务的列表。4. ThreadPoolExecutor执行的策略  线程数量未达到corePoolSize,则新建一个线程(核心线程)执行任务线程数量达到了corePools,则将任务移入队列等待队列已满,新建线程(非核心线程)执行任务队列已满,总线程数又达到了maximumPoolSize,就会由(RejectedExecutionHandler)抛出异常新建线程 -> 达到核心数 -> 加入队列 -> 新建线程(非核心) -> 达到最大数 -> 触发拒绝策略5. 四种拒绝策略AbortPolicy:不执行新任务,直接抛出异常,提示线程池已满,线程池默认策略DiscardPolicy:不执行新任务,也不抛出异常,基本上为静默模式。DisCardOldSetPolicy:将消息队列中的第一个任务替换为当前新进来的任务执行CallerRunPolicy:拒绝新任务进入,如果该线程池还没有被关闭,那么这个新的任务在执行线程中被调用)5、Java通过Executors提供四种线程池CachedThreadPool():可缓存线程池。线程数无限制有空闲线程则复用空闲线程,若无空闲线程则新建线程 一定程序减少频繁创建/销毁线程,减少系统开销FixedThreadPool():定长线程池。可控制线程最大并发数(同时执行的线程数)超出的线程会在队列中等待ScheduledThreadPool():定时线程池。支持定时及周期性任务执行。SingleThreadExecutor():单线程化的线程池。有且仅有一个工作线程执行任务所有任务按照指定顺序执行,即遵循队列的入队出队规则1. newCachedThreadPoolnewCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程 public class ThreadPoolExecutorTest1 {public static void main(String[] args) {ExecutorService cachedThreadPool = Executors.newCachedThreadPool();for (int i = 0; i < 1000; i++) {final int index = i;try {Thread.sleep(index * 1000);} catch (Exception e) {e.printStackTrace();}cachedThreadPool.execute(new Runnable() {public void run() {System.out.println(Thread.currentThread().getName()+":"+index);}});}}} 2. newFixedThreadPoolnewFixedThreadPool创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待,指定线程池中的线程数量和最大线程数量一样,也就线程数量固定不变示例代码如下public class ThreadPoolExecutorTest {public static void main(String[] args) {ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);// 每隔两秒打印3个数for (int i = 0; i < 10; i++) {final int index = i;fixedThreadPool.execute(new Runnable() {public void run() {try {System.out.println(Thread.currentThread().getName()+":"+index);//三个线程并发Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}}});}}} 3. newscheduledThreadPoolnewscheduledThreadPool创建一个定长线程池,支持定时及周期性任务执行。延迟执行示例代码如下.表示延迟1秒后每3秒执行一次public class ThreadPoolExecutorTest3 {public static void main(String[] args) {ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);scheduledThreadPool.scheduleAtFixedRate(new Runnable() {public void run() {System.out.println(Thread.currentThread().getName() + ": delay 1 seconds, and excute every 3 seconds");}}, 1, 3, TimeUnit.SECONDS);// 表示延迟1秒后每3秒执行一次}}  4. newSingleThreadExecutornewSingleThreadExecutor创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行public class ThreadPoolExecutorTest4 {public static void main(String[] args) {ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();for (int i = 0; i < 10; i++) {final int index = i;singleThreadExecutor.execute(new Runnable() {public void run() {try {System.out.println(Thread.currentThread().getName() + ":" + index);Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}});}}}结果依次输出,相当于顺序执行各个任务。使用JDK自带的监控工具来监控我们创建的线程数量,运行一个不终止的线程
List和Set比较,各自的子类比较对比一:Arraylist与LinkedList的比较1、ArrayList是实现了基于动态数组的数据结构,因为地址连续,一旦数据存储好了,查询操作效率会比较高(在内存里是连着放的)。2、因为地址连续, ArrayList要移动数据,所以插入和删除操作效率比较低。3、LinkedList基于链表的数据结构,地址是任意的,所以在开辟内存空间的时候不需要等一个连续的地址,对于新增和删除操作add和remove,LinedList比较占优势。4、因为LinkedList要移动指针,所以查询操作性能比较低。适用场景分析:当需要对数据进行对此访问的情况下选用ArrayList,当需要对数据进行多次增加删除修改时采用LinkedList。对比二:ArrayList与Vector的比较1、Vector的方法都是同步的,是线程安全的,而ArrayList的方法不是,由于线程的同步必然要影响性能。因此,ArrayList的性能比Vector好。2、当Vector或ArrayList中的元素超过它的初始大小时,Vector会将它的容量翻倍,而ArrayList只增加50%的大小,这样。ArrayList就有利于节约内存空间。3、大多数情况不使用Vector,因为性能不好,但是它支持线程的同步,即某一时刻只有一个线程能够写Vector,避免多线程同时写而引起的不一致性。4、Vector可以设置增长因子,而ArrayList不可以。适用场景分析:1、Vector是线程同步的,所以它也是线程安全的,而ArrayList是线程异步的,是不安全的。如果不考虑到线程的安全因素,一般用ArrayList效率比较高。2、如果集合中的元素的数目大于目前集合数组的长度时,在集合中使用数据量比较大的数据,用Vector有一定的优势。对比三:HashSet与TreeSet的比较1.TreeSet 是二叉树实现的,Treeset中的数据是自动排好序的,不允许放入null值 。2.HashSet 是哈希表实现的,HashSet中的数据是无序的,可以放入null,但只能放入一个null,两者中的值都不能重复,就如数据库中唯一约束 。3.HashSet要求放入的对象必须实现HashCode()方法,放入的对象,是以hashcode码作为标识的,而具有相同内容的String对象,hashcode是一样,所以放入的内容不能重复。但是同一个类的对象可以放入不同的实例。适用场景分析:HashSet是基于Hash算法实现的,其性能通常都优于TreeSet。我们通常都应该使用HashSet,在我们需要排序的功能时,我们才使用TreeSet。HashMap和ConcurrentHashMap的区别1、HashMap不是线程安全的,而ConcurrentHashMap是线程安全的。2、ConcurrentHashMap采用锁分段技术,将整个Hash桶进行了分段segment,也就是将这个大的数组分成了几个小的片段segment,而且每个小的片段segment上面都有锁存在,那么在插入元素的时候就需要先找到应该插入到哪一个片段segment,然后再在这个片段上面进行插入,而且这里还需要获取segment锁。3、ConcurrentHashMap让锁的粒度更精细一些,并发性能更好。JVM的内存结构根据 JVM 规范,JVM 内存共分为虚拟机栈、堆、方法区、程序计数器、本地方法栈五个部分。1、Java虚拟机栈:线程私有;每个方法在执行的时候会创建一个栈帧,存储了局部变量表,操作数栈,动态连接,方法返回地址等;每个方法从调用到执行完毕,对应一个栈帧在虚拟机栈中的入栈和出栈。2、堆:线程共享;被所有线程共享的一块内存区域,在虚拟机启动时创建,用于存放对象实例。3、方法区:线程共享;被所有线程共享的一块内存区域;用于存储已被虚拟机加载的类信息,常量,静态变量等。4、程序计数器:线程私有;是当前线程所执行的字节码的行号指示器,每条线程都要有一个独立的程序计数器,这类内存也称为“线程私有”的内存。5、本地方法栈:线程私有;主要为虚拟机使用到的Native方法服务。强引用,软引用和弱引用的区别强引用:只有这个引用被释放之后,对象才会被释放掉,只要引用存在,垃圾回收器永远不会回收,这是最常见的New出来的对象。软引用:内存溢出之前通过代码回收的引用。软引用主要用户实现类似缓存的功能,在内存足够的情况下直接通过软引用取值,无需从繁忙的真实来源查询数据,提升速度;当内存不足时,自动删除这部分缓存数据,从真正的来源查询这些数据。弱引用:第二次垃圾回收时回收的引用,短时间内通过弱引用取对应的数据,可以取到,当执行过第二次垃圾回收时,将返回null。弱引用主要用于监控对象是否已经被垃圾回收器标记为即将回收的垃圾,可以通过弱引用的isEnQueued方法返回对象是否被垃圾回收器标记。springmvc的核心是什么,请求的流程是怎么处理的,控制反转怎么实现的核心:控制反转和面向切面请求处理流程:1、首先用户发送请求到前端控制器,前端控制器根据请求信息(如URL)来决定选择哪一个页面控制器进行处理并把请求委托给它,即以前的控制器的控制逻辑部分;2、页面控制器接收到请求后,进行功能处理,首先需要收集和绑定请求参数到一个对象,并进行验证,然后将命令对象委托给业务对象进行处理;处理完毕后返回一个ModelAndView(模型数据和逻辑视图名);3、前端控制器收回控制权,然后根据返回的逻辑视图名,选择相应的视图进行渲染,并把模型数据传入以便视图渲染;4、前端控制器再次收回控制权,将响应返回给用户。控制反转如何实现:我们每次使用spring框架都要配置xml文件,这个xml配置了bean的id和class。spring中默认的bean为单实例模式,通过bean的class引用反射机制可以创建这个实例。因此,spring框架通过反射替我们创建好了实例并且替我们维护他们。A需要引用B类,spring框架就会通过xml把B实例的引用传给了A的成员变量。BIO、NIO和AIO的区别Java BIO : 同步并阻塞,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,当然可以通过线程池机制改善。Java NIO : 同步非阻塞,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。Java AIO: 异步非阻塞,服务器实现模式为一个有效请求一个线程,客户端的I/O请求都是由OS先完成了再通知服务器应用去启动线程进行处理。NIO比BIO的改善之处是把一些无效的连接挡在了启动线程之前,减少了这部分资源的浪费(因为我们都知道每创建一个线程,就要为这个线程分配一定的内存空间) AIO比NIO的进一步改善之处是将一些暂时可能无效的请求挡在了启动线程之前,比如在NIO的处理方式中,当一个请求来的话,开启线程进行处理,但这个请求所需要的资源还没有就绪,此时必须等待后端的应用资源,这时线程就被阻塞了。适用场景分析:BIO方式适用于连接数目比较小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中,JDK1.4以前的唯一选择,但程序直观简单易理解,如之前在Apache中使用。NIO方式适用于连接数目多且连接比较短(轻操作)的架构,比如聊天服务器,并发局限于应用中,编程比较复杂,JDK1.4开始支持,如在 Nginx,Netty中使用。AIO方式使用于连接数目多且连接比较长(重操作)的架构,比如相册服务器,充分调用OS参与并发操作,编程比较复杂,JDK7开始支持,在成长中,Netty曾经使用过,后来放弃。为什么要用线程池那先要明白什么是线程池线程池是指在初始化一个多线程应用程序过程中创建一个线程集合,然后在需要执行新的任务时重用这些线程而不是新建一个线程。使用线程池的好处1、线程池改进了一个应用程序的响应时间。由于线程池中的线程已经准备好且等待被分配任务,应用程序可以直接拿来使用而不用新建一个线程。2、线程池节省了CLR 为每个短生存周期任务创建一个完整的线程的开销并可以在任务完成后回收资源。3、线程池根据当前在系统中运行的进程来优化线程时间片。4、线程池允许我们开启多个任务而不用为每个线程设置属性。5、线程池允许我们为正在执行的任务的程序参数传递一个包含状态信息的对象引用。6、线程池可以用来解决处理一个特定请求最大线程数量限制问题。悲观锁和乐观锁的区别,怎么实现悲观锁:一段执行逻辑加上悲观锁,不同线程同时执行时,只能有一个线程执行,其他的线程在入口处等待,直到锁被释放。乐观锁:一段执行逻辑加上乐观锁,不同线程同时执行时,可以同时进入执行,在最后更新数据的时候要检查这些数据是否被其他线程修改了(版本和执行初是否相同),没有修改则进行更新,否则放弃本次操作。悲观锁的实现:begin;/begin work;/start transaction; (三者选一就可以)//1.查询出商品信息select status from t_goods where id=1 for update;//2.根据商品信息生成订单insert into t_orders (id,goods_id) values (null,1);//3.修改商品status为2update t_goods set status=2;//4.提交事务commit;/commit work;乐观锁的实现:1.查询出商品信息select (status,status,version) from t_goods where id=#{id}2.根据商品信息生成订单3.修改商品status为2update t_goodsset status=2,version=version+1where id=#{id} and version=#{version};什么是线程死锁?死锁如何产生?如何避免线程死锁?死锁的介绍:线程死锁是指由于两个或者多个线程互相持有对方所需要的资源,导致这些线程处于等待状态,无法前往执行。当线程进入对象的synchronized代码块时,便占有了资源,直到它退出该代码块或者调用wait方法,才释放资源,在此期间,其他线程将不能进入该代码块。当线程互相持有对方所需要的资源时,会互相等待对方释放资源,如果线程都不主动释放所占有的资源,将产生死锁。死锁的产生的一些特定条件:1、互斥条件:进程对于所分配到的资源具有排它性,即一个资源只能被一个进程占用,直到被该进程释放 。2、请求和保持条件:一个进程因请求被占用资源而发生阻塞时,对已获得的资源保持不放。3、不剥夺条件:任何一个资源在没被该进程释放之前,任何其他进程都无法对他剥夺占用。4、循环等待条件:当发生死锁时,所等待的进程必定会形成一个环路(类似于死循环),造成永久阻塞。如何避免:1、加锁顺序:当多个线程需要相同的一些锁,但是按照不同的顺序加锁,死锁就很容易发生。如果能确保所有的线程都是按照相同的顺序获得锁,那么死锁就不会发生。当然这种方式需要你事先知道所有可能会用到的锁,然而总有些时候是无法预知的。2、加锁时限:加上一个超时时间,若一个线程没有在给定的时限内成功获得所有需要的锁,则会进行回退并释放所有已经获得的锁,然后等待一段随机的时间再重试。但是如果有非常多的线程同一时间去竞争同一批资源,就算有超时和回退机制,还是可能会导致这些线程重复地尝试但却始终得不到锁。3、死锁检测:死锁检测即每当一个线程获得了锁,会在线程和锁相关的数据结构中(map、graph等等)将其记下。除此之外,每当有线程请求锁,也需要记录在这个数据结构中。死锁检测是一个更好的死锁预防机制,它主要是针对那些不可能实现按序加锁并且锁超时也不可行的场景。 
本文作者在一年之内参加过多场面试,应聘岗位均为 Java 开发方向。在不断的面试中,分类总结了 Java 开发岗位面试中的一些知识点。主要包括以下几个部分:Java 基础知识点Java 常见集合高并发编程(JUC 包)JVM 内存管理Java 8 知识点网络协议相关数据库相关MVC 框架相关大数据相关Linux 命令相关面试,是大家从学校走向社会的第一步。互联网公司的校园招聘,从形式上说,面试一般分为 2-3 轮技术面试 +1 轮 HR 面试。但是一些公司确实是没有 HR 面试的,直接就是三轮技术面。技术面试中,面试官一般会先就你所应聘的岗位进行相关知识的考察,也叫基础知识和业务逻辑面试。只要你回答的不是特别差,面试官通常会说:“咱们写个代码吧”,这个时候就开始了算法面试。也就是说,一轮技术面试 = 基础知识和业务逻辑面试 + 算法面试。在本篇文章中,我们主要从技术面试聊起。技术面试包括:业务逻辑和基础知识面试。首先是业务逻辑面试 ,也就是讲项目。面试官会对你简历上写的若干个项目其中之一拿出来和你聊聊。在期间,会针对你所做的东西进行深度挖掘。包括:为什么要这么做?优缺点分析,假如重新让你做一次,你打算怎么做? 等等。这个环节主要考察我们对自己做过的项目(实习项目或者校内项目)是否有一个清晰的认识。关于业务逻辑面试的准备,建议在平时多多思考总结,对项目的数据来源、整体运行框架都应该熟悉掌握。比如说你在某公司实习过程中,就可以进行总结,而不必等到快离职的时候慌慌张张的去总结该项目。接下来是基础知识面试。Java 开发属于后台开发方向,有人说后台开发很坑,因为需要学习的东西太多了。没错,这个岗位就是需要学习好多东西。包括:本语言(Java/C++/PHP)基础、数据库、网络协议、Linux 系统、计算机原理甚至前端相关知识都可以考察你,而且,并不超纲 。有时候,你报的是后台开发岗,并且熟悉的是 Java 语言,但是面试官却是 C++ 开发方向的,就是这么无奈~好了,闲话少说,让我们开始分类讲解常见面试知识点。(一) Java 基础知识点1)面向对象的特性有哪些?答:封装、继承和多态。2)Java 中覆盖和重载是什么意思?解析:覆盖和重载是比较重要的基础知识点,并且容易混淆,所以面试中常见。答:覆盖(Override)是指子类对父类方法的一种重写,只能比父类抛出更少的异常,访问权限不能比父类的小。被覆盖的方法不能是 private 的,否则只是在子类中重新定义了一个方法;重载(Overload)表示同一个类中可以有多个名称相同的方法,但这些方法的参数列表各不相同。面试官: 那么构成重载的条件有哪些?答:参数类型不同、参数个数不同、参数顺序不同。面试官: 函数的返回值不同可以构成重载吗?为什么?答:不可以,因为 Java 中调用函数并不需要强制赋值。举例如下:如下两个方法:12void f(){}int f(){ return 1;}只要编译器可以根据语境明确判断出语义,比如在 int x = f();中,那么的确可以据此区分重载方法。不过, 有时你并不关心方法的返回值,你想要的是方法调用的其他效果 (这常被称为 “为了副作用而调用”),这时你可能会调用方法而忽略其返回值,所以如果像下面的调用:1fun();此时 Java 如何才能判断调用的是哪一个 f() 呢?别人如何理解这种代码呢?所以,根据方法返回值来区分重载方法是行不通的。3)抽象类和接口的区别有哪些?答:抽象类中可以没有抽象方法;接口中的方法必须是抽象方法;抽象类中可以有普通的成员变量;接口中的变量必须是 static final 类型的,必须被初始化 , 接口中只有常量,没有变量。抽象类只能单继承,接口可以继承多个父接口;Java8 中接口中会有 default 方法,即方法可以被实现。面试官:抽象类和接口如何选择?答:如果要创建不带任何方法定义和成员变量的基类,那么就应该选择接口而不是抽象类。如果知道某个类应该是基类,那么第一个选择的应该是让它成为一个接口,只有在必须要有方法定义和成员变量的时候,才应该选择抽象类。因为抽象类中允许存在一个或多个被具体实现的方法,只要方法没有被全部实现该类就仍是抽象类。4)Java 和 C++ 的区别:解析:虽然我们不太懂 C++,但是就是会这么问,尤其是三面(总监级别)面试中。答:都是面向对象的语言,都支持封装、继承和多态;指针:Java 不提供指针来直接访问内存,程序更加安全;继承: Java 的类是单继承的,C++ 支持多重继承; Java 通过一个类实现多个接口来实现 C++ 中的多重继承; Java 中类不可以多继承,但是!!!接口可以多继承;内存: Java 有自动内存管理机制,不需要程序员手动释放无用内存。5)Java 中的值传递和引用传递解析:这类题目,面试官会手写一个例子,让你说出函数执行结果,详细举例请查阅我的博客:Java 值传递和引用传递基础分析。答:值传递是指对象被值传递,意味着传递了对象的一个副本,即使副本被改变,也不会影响源对象。引用传递是指对象被引用传递,意味着传递的并不是实际的对象,而是对象的引用。因此,外部对引用对象的改变会反映到所有的对象上。6)JDK 中常用的包有哪些?答:java.lang、java.util、http://java.io、http://java.net、java.sql。7)JDK,JRE 和 JVM 的联系和区别:答:JDK 是 java 开发工具包,是 java 开发环境的核心组件,并提供编译、调试和运行一个 java 程序所需要的所有工具,可执行文件和二进制文件,是一个平台特定的软件。JRE 是 java 运行时环境,是 JVM 的实施实现,提供了运行 java 程序的平台。JRE 包含了 JVM,但是不包含 java 编译器 / 调试器之类的开发工具。JVM 是 java 虚拟机,当我们运行一个程序时,JVM 负责将字节码转换为特定机器代码,JVM 提供了内存管理 / 垃圾回收和安全机制等。这种独立于硬件和操作系统,正是 java 程序可以一次编写多处执行的原因。区别:JDK 用于开发,JRE 用于运行 java 程序;JDK 和 JRE 中都包含 JVM;JVM 是 java 编程语言的核心并且具有平台独立性。Others:限于篇幅,面试中 Java 基础知识点还有:反射、泛型、注解等。小结:本节主要阐述了 Java 基础知识点,这些问题主要是一面面试官在考察,难度不大,适当复习下,应该没什么问题。(二)Java 中常见集合集合这方面的考察相当多,这部分是面试中必考的知识点。1)说说常见的集合有哪些吧?答:Map 接口和 Collection 接口是所有集合框架的父接口:Collection 接口的子接口包括:Set 接口和 List 接口;Map 接口的实现类主要有:HashMap、TreeMap、Hashtable、ConcurrentHashMap 以及 Properties 等;Set 接口的实现类主要有:HashSet、TreeSet、LinkedHashSet 等;List 接口的实现类主要有:ArrayList、LinkedList、Stack 以及 Vector 等。(2)HashMap 和 Hashtable 的区别有哪些?(必问)答:HashMap 没有考虑同步,是线程不安全的;Hashtable 使用了 synchronized 关键字,是线程安全的;前者允许 null 作为 Key;后者不允许 null 作为 Key。3)HashMap 的底层实现你知道吗?答:在 Java8 之前,其底层实现是数组 + 链表实现,Java8 使用了数组 + 链表 + 红黑树实现。此时你可以简单的在纸上画图分析:4)ConcurrentHashMap 和 Hashtable 的区别? (必问)答:ConcurrentHashMap 结合了 HashMap 和 HashTable 二者的优势。HashMap 没有考虑同步,hashtable 考虑了同步的问题。但是 hashtable 在每次同步执行时都要锁住整个结构。 ConcurrentHashMap 锁的方式是稍微细粒度的。 ConcurrentHashMap 将 hash 表分为 16 个桶(默认值),诸如 get,put,remove 等常用操作只锁当前需要用到的桶。面试官:ConcurrentHashMap 的具体实现知道吗?答:该类包含两个静态内部类 HashEntry 和 Segment;前者用来封装映射表的键值对,后者用来充当锁的角色;Segment 是一种可重入的锁 ReentrantLock,每个 Segment 守护一个 HashEntry 数组里得元素,当对 HashEntry 数组的数据进行修改时,必须首先获得对应的 Segment 锁。5)HashMap 的长度为什么是 2 的幂次方?答:通过将 Key 的 hash 值与 length-1 进行 & 运算,实现了当前 Key 的定位,2 的幂次方可以减少冲突(碰撞)的次数,提高 HashMap 查询效率;如果 length 为 2 的次幂 则 length-1 转化为二进制必定是 11111……的形式,在于 h 的二进制与操作效率会非常的快,而且空间不浪费;如果 length 不是 2 的次幂,比如 length 为 15,则 length-1 为 14,对应的二进制为 1110,在于 h 与操作,最后一位都为 0,而 0001,0011,0101,1001,1011,0111,1101 这几个位置永远都不能存放元素了,空间浪费相当大。更糟的是这种情况中,数组可以使用的位置比数组长度小了很多,这意味着进一步增加了碰撞的几率,减慢了查询的效率!这样就会造成空间的浪费。6)List 和 Set 的区别是啥?答:List 元素是有序的,可以重复;Set 元素是无序的,不可以重复。7)List、Set 和 Map 的初始容量和加载因子答:1. ListArrayList 的初始容量是 10;加载因子为 0.5; 扩容增量:原容量的 0.5 倍 +1;一次扩容后长度为 16。Vector 初始容量为 10,加载因子是 1。扩容增量:原容量的 1 倍,如 Vector 的容量为 10,一次扩容后是容量为 20。2. SetHashSet,初始容量为 16,加载因子为 0.75; 扩容增量:原容量的 1 倍; 如 HashSet 的容量为 16,一次扩容后容量为 323. MapHashMap,初始容量 16,加载因子为 0.75; 扩容增量:原容量的 1 倍; 如 HashMap 的容量为 16,一次扩容后容量为 328)Comparable 接口和 Comparator 接口有什么区别?答:前者简单,但是如果需要重新定义比较类型时,需要修改源代码。后者不需要修改源代码,自定义一个比较器,实现自定义的比较方法。具体解析参考我的博客:Java 集合框架—Set9)Java 集合的快速失败机制 “fail-fast”答:它是 java 集合的一种错误检测机制,当多个线程对集合进行结构上的改变的操作时,有可能会产生 fail-fast 机制。例如 :假设存在两个线程(线程 1、线程 2),线程 1 通过 Iterator 在遍历集合 A 中的元素,在某个时候线程 2 修改了集合 A 的结构(是结构上面的修改,而不是简单的修改集合元素的内容),那么这个时候程序就会抛出 ConcurrentModificationException 异常,从而产生 fail-fast 机制。原因: 迭代器在遍历时直接访问集合中的内容,并且在遍历过程中使用一个 modCount 变量。集合在被遍历期间如果内容发生变化,就会改变 modCount 的值。每当迭代器使用 hashNext()/next() 遍历下一个元素之前,都会检测 modCount 变量是否为 expectedmodCount 值,是的话就返回遍历;否则抛出异常,终止遍历。解决办法:在遍历过程中,所有涉及到改变 modCount 值得地方全部加上 synchronized;使用 CopyOnWriteArrayList
Arraylist 与 LinkedList 异同1. 是否保证线程安全: ArrayList 和 LinkedList 都是不同步的,也就是不保证线程安全;2. 底层数据结构: Arraylist 底层使用的是Object数组;LinkedList 底层使用的是双向循环链表数据结构;3. 插入和删除是否受元素位置的影响: ① ArrayList 采用数组存储,所以插入和删除元素的时间复杂度受元素位置的影响。 比如:执行add(E e)方法的时候, ArrayList 会默认在将指定的元素追加到此列表的末尾,这种情况时间复杂度就是O(1)。但是如果要在指定位置 i 插入和删除元素的话(add(int index, E element))时间复杂度就为 O(n-i)。因为在进行上述操作的时候集合中第 i 和第 i 个元素之后的(n-i)个元素都要执行向后位/向前移一位的操作。 ② LinkedList 采用链表存储,所以插入,删除元素时间复杂度不受元素位置的影响,都是近似 O(1)而数组为近似 O(n)。4. 是否支持快速随机访问: LinkedList 不支持高效的随机元素访问,而ArrayList 实现了RandmoAccess 接口,所以有随机访问功能。快速随机访问就是通过元素的序号快速获取元素对象(对应于get(int index)方法)。5. 内存空间占用: ArrayList的空 间浪费主要体现在在list列表的结尾会预留一定的容量空间,而LinkedList的空间花费则体现在它的每一个元素都需要消耗比ArrayList更多的空间(因为要存放直接后继和直接前驱以及数据)。补充:数据结构基础之双向链表双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱。所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点。一般我们都构造双向循环链表,如下图所示,同时下图也是LinkedList 底层使用的是双向循环链表数据结构。   ArrayList 与 Vector 区别Vector类的所有方法都是同步的。可以由两个线程安全地访问一个Vector对象、但是一个线程访问Vector的话代码要在同步操作上耗费大量的时间。Arraylist不是同步的,所以在不需要保证线程安全时时建议使用Arraylist。HashMap的底层实现JDK1.8之前JDK1.8 之前 HashMap 由 数组+链表 组成的(“链表散列” 即数组和链表的结合体),数组是 HashMap 的主体,链表则是主要为了解决哈希冲突而存在的(HashMap 采用 “拉链法也就是链地址法” 解决冲突),如果定位到的数组位置不含链表(当前 entry 的 next 指向 null ),那么对于查找,添加等操作很快,仅需一次寻址即可;如果定位到的数组包含链表,对于添加操作,其时间复杂度依然为 O(1),因为最新的 Entry 会插入链表头部,急需要简单改变引用链即可,而对于查找操作来讲,此时就需要遍历链表,然后通过 key 对象的 equals 方法逐一比对查找.所谓 “拉链法” 就是将链表和数组相结合。也就是说创建一个链表数组,数组中每一格就是一个链表。若遇到哈希冲突,则将冲突的值加到链表中即可。   JDK1.8之后相比于之前的版本, JDK1.8之后在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为8)时,将链表转化为红黑树,以减少搜索时间。   TreeMap、TreeSet以及JDK1.8之后的HashMap底层都用到了红黑树。红黑树就是为了解决二叉查找树的缺陷,因为二叉查找树在某些情况下会退化成一个线性结构。推荐阅读:《Java 8系列之重新认识HashMap》 :zhuanlan.zhihu.com/p/21673805HashMap 和 Hashtable 的区别线程是否安全: HashMap 是非线程安全的,HashTable 是线程安全的;HashTable 内部的方法基本都经过 synchronized 修饰。(如果你要保证线程安全的话就使用 ConcurrentHashMap 吧!);效率: 因为线程安全的问题,HashMap 要比 HashTable 效率高一点。另外,HashTable 基本被淘汰,不要在代码中使用它;对Null key 和Null value的支持: HashMap 中,null 可以作为键,这样的键只有一个,可以有一个或多个键所对应的值为 null。。但是在 HashTable 中 put 进的键值只要有一个 null,直接抛出 NullPointerException。初始容量大小和每次扩充容量大小的不同 : ①创建时如果不指定容量初始值,Hashtable 默认的初始大小为11,之后每次扩充,容量变为原来的2n+1。HashMap 默认的初始化大小为16。之后每次扩充,容量变为原来的2倍。②创建时如果给定了容量初始值,那么 Hashtable 会直接使用你给定的大小,而 HashMap 会将其扩充为2的幂次方大小。也就是说 HashMap 总是使用2的幂作为哈希表的大小,后面会介绍到为什么是2的幂次方。底层数据结构: JDK1.8 以后的 HashMap 在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为8)时,将链表转化为红黑树,以减少搜索时间。Hashtable 没有这样的机制。HashMap 的长度为什么是2的幂次方为了能让 HashMap 存取高效,尽量较少碰撞,也就是要尽量把数据分配均匀,每个链表/红黑树长度大致相同。这个实现就是把数据存到哪个链表/红黑树中的算法。这个算法应该如何设计呢?我们首先可能会想到采用%取余的操作来实现。但是,重点来了:“取余(%)操作中如果除数是2的幂次则等价于与其除数减一的与(&)操作(也就是说 hash%length==hash&(length-1)的前提是 length 是2的 n 次方;)。” 并且 采用二进制位操作 &,相对于%能够提高运算效率,这就解释了 HashMap 的长度为什么是2的幂次方。HashSet 和 HashMap 区别   ConcurrentHashMap 和 Hashtable 的区别ConcurrentHashMap 和 Hashtable 的区别主要体现在实现线程安全的方式上不同。底层数据结构: JDK1.7的 ConcurrentHashMap 底层采用 分段的数组+链表 实现,JDK1.8 采用的数据结构跟HashMap1.8的结构一样,数组+链表/红黑二叉树。Hashtable 和 JDK1.8 之前的 HashMap 的底层数据结构类似都是采用 数组+链表 的形式,数组是 HashMap 的主体,链表则是主要为了解决哈希冲突而存在的;实现线程安全的方式(重要): ① 在JDK1.7的时候,ConcurrentHashMap(分段锁) 对整个桶数组进行了分割分段(Segment),每一把锁只锁容器其中一部分数据,多线程访问容器里不同数据段的数据,就不会存在锁竞争,提高并发访问率。(默认分配16个Segment,比Hashtable效率提高16倍。) 到了 JDK1.8 的时候已经摒弃了Segment的概念,而是直接用 Node 数组+链表+红黑树的数据结构来实现,并发控制使用 synchronized 和 CAS 来操作。(JDK1.6以后 对 synchronized锁做了很多优化) 整个看起来就像是优化过且线程安全的 HashMap,虽然在JDK1.8中还能看到 Segment 的数据结构,但是已经简化了属性,只是为了兼容旧版本;② Hashtable(同一把锁) :使用 synchronized 来保证线程安全,效率非常低下。当一个线程访问同步方法时,其他线程也访问同步方法,可能会进入阻塞或轮询状态,如使用 put 添加元素,另一个线程不能使用 put 添加元素,也不能使用 get,竞争会越来越激烈效率越低。两者的对比图:图片来源:www.cnblogs.com/chengxiao/p…HashTable:  JDK1.7的ConcurrentHashMap: JDK1.8的ConcurrentHashMap(TreeBin: 红黑二叉树节点 Node: 链表节点):ConcurrentHashMap线程安全的具体实现方式/底层具体实现JDK1.7(上面有示意图)首先将数据分为一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据时,其他段的数据也能被其他线程访问。ConcurrentHashMap 是由 Segment 数组结构和 HahEntry 数组结构组成。Segment 实现了 ReentrantLock,所以 Segment 是一种可重入锁,扮演锁的角色。HashEntry 用于存储键值对数据。 static class Segment<K,V> extends ReentrantLock implements Serializable { }一个 ConcurrentHashMap 里包含一个 Segment 数组。Segment 的结构和HashMap类似,是一种数组和链表结构,一个 Segment 包含一个 HashEntry 数组,每个 HashEntry 是一个链表结构的元素,每个 Segment 守护着一个HashEntry数组里的元素,当对 HashEntry 数组的数据进行修改时,必须首先获得对应的 Segment的锁。JDK1.8 (上面有示意图)ConcurrentHashMap取消了Segment分段锁,采用CAS和synchronized来保证并发安全。数据结构跟HashMap1.8的结构类似,数组+链表/红黑二叉树。synchronized只锁定当前链表或红黑二叉树的首节点,这样只要hash不冲突,就不会产生并发,效率又提升N倍。集合框架底层数据结构总结Collection1. ListArraylist: Object数组Vector: Object数组LinkedList: 双向循环链表2. SetHashSet(无序,唯一): 基于 HashMap 实现的,底层采用 HashMap 来保存元素LinkedHashSet: LinkedHashSet 继承与 HashSet,并且其内部是通过 LinkedHashMap 来实现的。有点类似于我们之前说的LinkedHashMap 其内部是基于 Hashmap 实现一样,不过还是有一点点区别的。TreeSet(有序,唯一): 红黑树(自平衡的排序二叉树。)MapHashMap: JDK1.8之前HashMap由数组+链表组成的,数组是HashMap的主体,链表则是主要为了解决哈希冲突而存在的(“拉链法”解决冲突).JDK1.8以后在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为8)时,将链表转化为红黑树,以减少搜索时间LinkedHashMap: LinkedHashMap 继承自 HashMap,所以它的底层仍然是基于拉链式散列结构即由数组和链表或红黑树组成。另外,LinkedHashMap 在上面结构的基础上,增加了一条双向链表,使得上面的结构可以保持键值对的插入顺序。同时通过对链表进行相应的操作,实现了访问顺序相关逻辑。详细可以查看:《LinkedHashMap 源码详细分析(JDK1.8)》HashTable: 数组+链表组成的,数组是 HashMap 的主体,链表则是主要为了解决哈希冲突而存在的TreeMap: 红黑树(自平衡的排序二叉树)推荐阅读:jdk1.8中ConcurrentHashMap的实现原理HashMap? ConcurrentHashMap? 相信看完这篇没人能难住你!HASHMAP、HASHTABLE、CONCURRENTHASHMAP的原理与区别ConcurrentHashMap实现原理及源码分析java-并发-ConcurrentHashMap高并发机制-jdk1.8
整理了下阿里近几年的java面试题目,大家参考下吧,希望对大家有帮助,可以帮大家查漏补缺。答对以下这些面试题,可以淘汰掉 80 % 的求职竞争者。1.hashcode相等两个类一定相等吗?equals呢?相反呢?2.介绍一下集合框架?3.hashmap hastable 底层实现什么区别?hashtable和concurrenthashtable呢?4.hashmap和treemap什么区别?低层数据结构是什么?5.线程池用过吗都有什么参数?底层如何实现的?6.sychnized和Lock什么区别?sychnize 什么情况情况是对象锁? 什么时候是全局锁为什么?7.ThreadLocal 是什么底层如何实现?写一个例子呗?8.volitile的工作原理?9.cas知道吗如何实现的?10.请用至少四种写法写一个单例模式?11.请介绍一下JVM内存模型??用过什么垃圾回收器都说说呗12.线上发送频繁full gc如何处理? CPU 使用率过高怎么办?13.如何定位问题?如何解决说一下解决思路和处理方法14.知道字节码吗?字节码都有哪些?Integer x =5,int y =5,比较x =y 都经过哪些步骤?15.讲讲类加载机制呗都有哪些类加载器,这些类加载器都加载哪些文件?16.手写一下类加载Demo17.知道osgi吗? 他是如何实现的???18.请问你做过哪些JVM优化?使用什么方法达到什么效果???19.classforName("java.lang.String")和String classgetClassLoader() LoadClass("java.lang.String") 什么区别啊?20.探查Tomcat的运行机制即框架?21.分析Tomcat线程模型?22.Tomcat系统参数认识和调优?23.MySQL底层B+Tree机制?24.SQL执行计划详解?25.索引优化详解?26.SQL语句如如如何优化?27.spring都有哪些机制啊AOP底层如何实现的啊IOC呢??28.cgLib知道吗?他和jdk动态代理什么区别?手写一个jdk动态代理呗?29.使用mysq1索引都有哪些原则? ?索引什么数据结构? 3+tree 和B tree 什么区别?30.MySQL有哪些存储引擎啊?都有啥区别? 要详细!31.设计高并发系统数据库层面该怎么设计??数据库锁有哪些类型?如何实现呀?32.数据库事务有哪些?33.如何设计可以动态扩容缩容的分库分表方案?34.用过哪些分库分表中间件,有啥优点和缺点?讲一下你了解的分库分表中间件的底层实现原理?35.我现在有一个未分库分表的系统,以后系统需分库分表,如何设计,让未分库分表的系统动态切换到分库分表的系统上?TCC? 那若出现网络原因,网络连不通怎么办啊?36.分布式事务知道吗? 你们怎么解决的?37.为什么要分库分表啊?38.RPC通信原理,分布式通信原理39.分布式寻址方式都有哪些算法知道一致性hash吗?手写一下java实现代码??你若userId取摸分片,那我要查一段连续时间里的数据怎么办???40.如何解决分库分表主键问题有什么实现方案??41.redis和memcheched 什么区别为什么单线程的redis比多线程的memched效率要高啊?42.redis有什么数据类型都在哪些场景下使用啊?43.reids的主从复制是怎么实现的redis的集群模式是如何实现的呢redis的key是如何寻址的啊?44.使用redis如何设计分布式锁?使用zk可以吗?如何实现啊这两种哪个效率更高啊??45.知道redis的持久化吗都有什么缺点优点啊? ?具体底层实现呢?46.redis过期策略都有哪些LRU 写一下java版本的代码吧??47.说一下dubbo的实现过程注册中心挂了可以继续通信吗??48.dubbo支持哪些序列化协议?hessian 说一下hessian的数据结构PB知道吗为啥PB效率是最高的啊??49.知道netty吗'netty可以干嘛呀NIO,BIO,AIO 都是什么啊有什么区别啊?50.dubbo复制均衡策略和高可用策略都有哪些啊动态代理策略呢?51.为什么要进行系统拆分啊拆分不用dubbo可以吗'dubbo和thrift什么区别啊?52.为什么使用消息队列啊消息队列有什么优点和缺点啊?53.如何保证消息队列的高可用啊如何保证消息不被重复消费啊54.kafka ,activemq,rabbitmq ,rocketmq都有什么优点,缺点啊???55.如果让你写一个消息队列,该如何进行架构设计啊?说一下你的思路56.说一下TCP 'IP四层?57.的工作流程?? ?http1.0 http1.1http2.0 具体哪些区别啊?58.TCP三次握手,四层分手的工作流程画一下流程图为什么不是四次五次或者二次啊?59.画一下https的工作流程?具体如何实现啊?如何防止被抓包啊??60.源码中所用到的经典设计思想及常用设计模式61.系统架构如何选择合适日志技术(log4j、log4j2、slf4j、jcl…….)62.springAOP的原理,springAOP和Aspectj的关系,springAOP的源码问题63.dubbo框架的底层通信原理64.RPC通信原理,分布式通信原理65.如何利用springCloud来架构微服务项目66.如何正确使用docker技术67.springMVC的底层原理、如何从源码来分析其原理68.mybaits的底层实现原理,如何从源码来分析mybaits69.mysql的索引原理,索引是怎么实现的70.索引的底层算法、如何正确使用、优化索引71.springboot如何快速构建系统72.zk原理知道吗zk都可以干什么Paxos算法知道吗?说一下原理和实现?73.如果让你写一个消息队列,该如何进行架构设计啊?说一下你的思路74.分布式事务知道吗? 你们怎么解决的?75.请问你做过哪些JVM优化?使用什么方法达到什么效果?
1、webservice是什么?webservice是一种跨编程语言和跨操作系统的远程调用技术,遵循SOPA/WSDL规范。2、springCloud是什么?springcloud是一个微服务框架,并提供全套分布式系统解决方案。支持配置管理,熔断机制,leader选举,服务治理,分布式session,微代理,控制总线,智能路由,一次性token。3、Java中堆和栈有什么不同?每个线程都有自己的栈内存,用于存储本地变量,方法参数和栈调用,一个线程中存储的变量对其它线程是不可见的。而堆是所有线程共享的一片公用内存区域。对象都在堆里创建,为了提升效率线程会从堆中弄一个缓存到自己的栈,如果多个线程使用该变量就可能引发问题,这时volatile 变量就可以发挥作用了,它要求线程从主存中读取变量的值。堆:(对象)引用类型的变量,其内存分配在堆上或者常量池(字符串常量、基本数据类型常量),需要通过new等方式来创建。堆内存主要作用是存放运行时创建(new)的对象。(主要用于存放对象,存取速度慢,可以运行时动态分配内存,生存期不需要提前确定)栈:(基本数据类型变量、对象的引用变量)基本数据类型的变量(int、short、long、byte、float、double、boolean、char等)以及对象的引用变量,其内存分配在栈上,变量出了作用域就会自动释放。4、Spring的Scope有以下几种,通过@Scope注解来实现:(1)Singleton:一个Spring容器中只有一个Bean的实例,此为Spring的默认配置,全容器共享一个实例。(2)Prototype:每次调用新建一个Bean实例。(3)Request:Web项目中,给每一个 http request 新建一个Bean实例。(4)Session:Web项目中,给每一个 http session 新建一个Bean实例。(5)GlobalSession:这个只在portal应用中有用,给每一个 global http session 新建一个Bean实例。5、Spring事务传播行为所谓事务的传播行为是指,如果在开始当前事务之前,一个事务上下文已经存在,此时有若干选项可以指定一个事务性方法的执行行为。在TransactionDefinition定义中包括了如下几个表示传播行为的常量:TransactionDefinition.PROPAGATION_REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。这是默认值。TransactionDefinition.PROPAGATION_REQUIRES_NEW:创建一个新的事务,如果当前存在事务,则把当前事务挂起。TransactionDefinition.PROPAGATION_SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起。TransactionDefinition.PROPAGATION_NEVER:以非事务方式运行,如果当前存在事务,则抛出异常。TransactionDefinition.PROPAGATION_MANDATORY:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。TransactionDefinition.PROPAGATION_NESTED:如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED。6、Spring的声明式事务管理力度是什么级别?Struts2是类级别的,Spring是方法级别的spring事务可以分为编程式事务和声明式事务7、spring MVC与struts2的区别:参考:  http://blog.csdn.net/chenleixing/article/details/44570681① Struts2是类级别的拦截, 一个类对应一个request上下文,SpringMVC是方法级别的拦截② SpringMVC的方法之间基本上独立的,独享request response数据③ 由于Struts2需要针对每个request进行封装,把request,session等servlet生命周期的变量封装成一个一个Map,供给每个Action使用,并保证线程安全,所以在原则上,是比较耗费内存的④ 拦截器实现机制上,Struts2有以自己的interceptor机制,SpringMVC用的是独立的AOP方式⑤ SpringMVC的入口是servlet,而Struts2是filter⑥ SpringMVC集成了Ajax⑦ SpringMVC验证支持JSR303,处理起来相对更加灵活方便,而Struts2验证比较繁琐,感觉太烦乱⑧ Spring MVC和Spring是无缝的。从这个项目的管理和安全上也比Struts2高⑨ Struts2更加符合OOP的编程思想, SpringMVC就比较谨慎,在servlet上扩展⑩ SpringMVC开发效率和性能高于Struts28、Spring框架中的核心思想包括什么?主要思想是IOC控制反转,DI依赖注入,AOP面向切面9、ArrayList和LinkedList的大致区别如下:1.ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。 2.对于随机访问get和set,ArrayList觉得优于LinkedList,因为LinkedList要移动指针。 3.对于新增和删除操作add和remove,LinedList比较占优势,因为ArrayList要移动数据。10、ArrayList,Vector主要区别为以下几点: (1):Vector是线程安全的,源码中有很多的synchronized可以看出,而ArrayList不是。导致Vector效率无法和ArrayList相比; (2):ArrayList和Vector都采用线性连续存储空间,当存储空间不足的时候,ArrayList默认增加为原来的50%,Vector默认增加为原来的一倍; 11、HashSet与HashMap的区别:12、HashMap和Hashtable的区别:HashMap和Hashtable都实现了Map接口,但决定用哪一个之前先要弄清楚它们之间的分别。主要的区别有:线程安全性,同步(synchronization),以及速度。HashMap几乎可以等价于Hashtable,除了HashMap是非synchronized的,并可以接受null(HashMap可以接受为null的键值(key)和值(value),而Hashtable则不行)。HashMap是非synchronized,而Hashtable是synchronized,这意味着Hashtable是线程安全的,多个线程可以共享一个Hashtable;而如果没有正确的同步的话,多个线程是不能共享HashMap的。Java 5提供了ConcurrentHashMap,它是HashTable的替代,比HashTable的扩展性更好。另一个区别是HashMap的迭代器(Iterator)是fail-fast迭代器,而Hashtable的enumerator迭代器不是fail-fast的。所以当有其它线程改变了HashMap的结构(增加或者移除元素),将会抛出ConcurrentModificationException,但迭代器本身的remove()方法移除元素则不会抛出ConcurrentModificationException异常。但这并不是一个一定发生的行为,要看JVM。这条同样也是Enumeration和Iterator的区别。由于Hashtable是线程安全的也是synchronized,所以在单线程环境下它比HashMap要慢。如果你不需要同步,只需要单一线程,那么使用HashMap性能要好过Hashtable。HashMap不能保证随着时间的推移Map中的元素次序是不变的。13、线程安全是什么?线程不安全是什么?线程安全就是多线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时,进行保护,其他线程不能进行访问直到该线程读取完,其他线程才可使用。不会出现数据不一致或者数据污染。(Vector,HashTable) 线程不安全就是不提供数据访问保护,有可能出现多个线程先后更改数据造成所得到的数据是脏数据。(ArrayList,LinkedList,HashMap等)14、线程和进程的区别?进程和线程都是一个时间段的描述,是CPU工作时间段的描述,不过是颗粒大小不同;(1)进程是资源的分配和调度的一个独立单元,而线程是CPU调度的基本单元(2)同一个进程中可以包括多个线程,并且线程共享整个进程的资源(寄存器、堆栈、上下文),一个进行至少包括一个线程。(3)进程的创建调用fork或者vfork,而线程的创建调用pthread_create,进程结束后它拥有的所有线程都将销毁,而线程的结束不会影响同个进程中的其他线程的结束(4)线程是轻两级的进程,它的创建和销毁所需要的时间比进程小很多,所有操作系统中的执行功能都是创建线程去完成的(5)线程中执行时一般都要进行同步和互斥,因为他们共享同一进程的所有资源(6)线程有自己的私有属性TCB,线程id,寄存器、硬件上下文,而进程也有自己的私有属性进程控制块PCB,这些私有属性是不被共享的,用来标示一个进程或一个线程的标志15、黑盒测试、灰盒测试、白盒测试、单元测试有什么区别?黑盒测试关注程序的功能是否正确,面向实际用户;白盒测试关注程序源代码的内部逻辑结构是否正确,面向编程人员;灰盒测试是介于白盒测试与黑盒测试之间的一种测试。单元测试(Unit Testing)是对软件基本组成单元进行的测试,如函数或是一个类的方法。这里的单元,就是软件设计的最小单位。16、怎么对数据库百万级数据进行优化?使用读写分离技术(让主数据库(master)处理事务性增、改、删操作(INSERT、UPDATE、DELETE),而从数据库(slave)处理SELECT查询操作)17、Spring Bean的生命周期:Bean的建立, 由BeanFactory读取Bean定义文件,并生成各个实例Setter注入,执行Bean的属性依赖注入BeanNameAware的setBeanName(), 如果实现该接口,则执行其setBeanName方法BeanFactoryAware的setBeanFactory(),如果实现该接口,则执行其setBeanFactory方法BeanPostProcessor的processBeforeInitialization(),如果有关联的processor,则在Bean初始化之前都会执行这个实例的processBeforeInitialization()方法InitializingBean的afterPropertiesSet(),如果实现了该接口,则执行其afterPropertiesSet()方法Bean定义文件中定义init-methodBeanPostProcessors的processAfterInitialization(),如果有关联的processor,则在Bean初始化之前都会执行这个实例的processAfterInitialization()方法DisposableBean的destroy(),在容器关闭时,如果Bean类实现了该接口,则执行它的destroy()方法Bean定义文件中定义destroy-method,在容器关闭时,可以在Bean定义文件中使用“destory-method”定义的方法简单回答springbean生命周期:(1)实例化(必须的)构造函数构造对象(2)装配(可选的)为属性赋值(3)回调(可选的)(容器-控制类和组件-回调类)(4)初始化(init-method=" ")(5)就绪(6)销毁(destroy-method=" "
下列面试题都是在网上收集的,本人抱着学习的态度找了下参考答案,有不足的地方还请指正。1、面向对象的特征有哪些方面?抽象:将同类对象的共同特征提取出来构造类。继承:基于基类创建新类。封装:将数据隐藏起来,对数据的访问只能通过特定接口。多态性:不同子类型对象对相同消息作出不同响应。2、访问修饰符public,private,protected,以及不写(默认)时的区别?protected 当前类,同包,异包子类。3、String 是最基本的数据类型吗?答:不是。Java中的基本数据类型只有8个:byte、short、int、long、float、double、char、boolean;除了基本类型(primitive type),剩下的都是引用类型(reference type),Java 5以后引入的枚举类型也算是一种比较特殊的引用类型。4、float f=3.4;是否正确?答:不正确。3.4是双精度数,将双精度型(double)赋值给浮点型(float)属于下转型(down-casting,也称为窄化)会造成精度损失,因此需要强制类型转换float f =(float)3.4; 或者写成float f =3.4F;5、short s1 = 1; s1 = s1 + 1;有错吗?short s1 = 1; s1 += 1;有错吗?答:对于short s1 = 1; s1 = s1 + 1;由于1是int类型,因此s1+1运算结果也是int 型,需要强制转换类型才能赋值给short型。而short s1 = 1; s1 += 1;可以正确编译,因为s1+= 1;相当于s1 = (short)(s1 + 1);其中有隐含的强制类型转换。6、Java有没有goto?答:goto 是Java中的保留字,在目前版本的Java中没有使用。(根据James Gosling(Java之父)编写的《The Java Programming Language》一书的附录中给出了一个Java关键字列表,其中有goto和const,但是这两个是目前无法使用的关键字,因此有些地方将其称之为保留字,其实保留字这个词应该有更广泛的意义,因为熟悉C语言的程序员都知道,在系统类库中使用过的有特殊意义的单词或单词的组合都被视为保留字)7、int和Integer有什么区别?答:Java是一个近乎纯洁的面向对象编程语言,但是为了编程的方便还是引入了基本数据类型,但是为了能够将这些基本数据类型当成对象操作,Java为每一个基本数据类型都引入了对应的包装类型(wrapper class),int的包装类就是Integer,从Java 5开始引入了自动装箱/拆箱机制,使得二者可以相互转换。Java 为每个原始类型提供了包装类型:原始类型: boolean,char,byte,short,int,long,float,double包装类型:Boolean,Character,Byte,Short,Integer,Long,Float,Double1 class AutoUnboxingTest {23 public static void main(String[] args) {4 Integer a = new Integer(3);5 Integer b = 3; // 将3自动装箱成Integer类型6 int c = 3;7 System.out.println(a == b); // false 两个引用没有引用同一对象8 System.out.println(a == c); // true a自动拆箱成int类型再和c比较9 }10 }最近还遇到一个面试题,也是和自动装箱和拆箱有点关系的,代码如下所示:1 public class Test03 {23 public static void main(String[] args) {4 Integer f1 = 100, f2 = 100, f3 = 150, f4 = 150;56 System.out.println(f1 == f2);7 System.out.println(f3 == f4);8 }9 }如果不明就里很容易认为两个输出要么都是true要么都是false。首先需要注意的是f1、f2、f3、f4四个变量都是Integer对象引用,所以下面的==运算比较的不是值而是引用。装箱的本质是什么呢?当我们给一个Integer对象赋一个int值的时候,会调用Integer类的静态方法valueOf,如果看看valueOf的源代码就知道发生了什么。1 public static Integer valueOf(int i) {2 if (i >= IntegerCache.low && i <= IntegerCache.high)3 return IntegerCache.cache[i + (-IntegerCache.low)];4 return new Integer(i);5 }IntegerCache是Integer的内部类,其代码如下所示:1 /**2 * Cache to support the object identity semantics of autoboxing for values between3 * -128 and 127 (inclusive) as required by JLS.4 *5 * The cache is initialized on first usage. The size of the cache6 * may be controlled by the {@code -XX:AutoBoxCacheMax=<size>} option.7 * During VM initialization, java.lang.Integer.IntegerCache.high property8 * may be set and saved in the private system properties in the9 * sun.misc.VM class.10 */1112 private static class IntegerCache {13 static final int low = -128;14 static final int high;15 static final Integer cache[];1617 static {18 // high value may be configured by property19 int h = 127;20 String integerCacheHighPropValue =21 sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");22 if (integerCacheHighPropValue != null) {23 try {24 int i = parseInt(integerCacheHighPropValue);25 i = Math.max(i, 127);26 // Maximum array size is Integer.MAX_VALUE27 h = Math.min(i, Integer.MAX_VALUE - (-low) -1);28 } catch( NumberFormatException nfe) {29 // If the property cannot be parsed into an int, ignore it.30 }31 }32 high = h;3334 cache = new Integer[(high - low) + 1];35 int j = low;36 for(int k = 0; k < cache.length; k++)37 cache[k] = new Integer(j++);3839 // range [-128, 127] must be interned (JLS7 5.1.7)40 assert IntegerCache.high >= 127;41 }4243 private IntegerCache() {}44 }简单的说,如果整型字面量的值在-128到127之间,那么不会new新的Integer对象,而是直接引用常量池中的Integer对象,所以上面的面试题中f1==f2的结果是true,而f3==f4的结果是false。提醒:越是貌似简单的面试题其中的玄机就越多,需要面试者有相当深厚的功力。8、&和&&的区别?答:&运算符有两种用法:(1)按位与;(2)逻辑与。&&运算符是短路与运算。逻辑与跟短路与的差别是非常巨大的,虽然二者都要求运算符左右两端的布尔值都是true整个表达式的值才是true。&&之所以称为短路运算是因为,如果&&左边的表达式的值是false,右边的表达式会被直接短路掉,不会进行运算。很多时候我们可能都需要用&&而不是&,例如在验证用户登录时判定用户名不是null而且不是空字符串,应当写为:username != null &&!username.equals(""),二者的顺序不能交换,更不能用&运算符,因为第一个条件如果不成立,根本不能进行字符串的equals比较,否则会产生NullPointerException异常。注意:逻辑或运算符(|)和短路或运算符(||)的差别也是如此。补充:如果你熟悉JavaScript,那你可能更能感受到短路运算的强大,想成为JavaScript的高手就先从玩转短路运算开始吧。9、解释内存中的栈(stack)、堆(heap)和方法区(method area)的用法。答:通常我们定义一个基本数据类型的变量,一个对象的引用,还有就是函数调用的现场保存都使用JVM中的栈空间;而通过new关键字和构造器创建的对象则放在堆空间,堆是垃圾收集器管理的主要区域,由于现在的垃圾收集器都采用分代收集算法,所以堆空间还可以细分为新生代和老生代,再具体一点可以分为Eden、Survivor(又可分为From Survivor和To Survivor)、Tenured;方法区和堆都是各个线程共享的内存区域,用于存储已经被JVM加载的类信息、常量、静态变量、JIT编译器编译后的代码等数据;程序中的字面量(literal)如直接书写的100、"hello"和常量都是放在常量池中,常量池是方法区的一部分,。栈空间操作起来最快但是栈很小,通常大量的对象都是放在堆空间,栈和堆的大小都可以通过JVM的启动参数来进行调整,栈空间用光了会引发Stack
上一篇:近5年常考Java面试题及答案整理(一)31、String s = new String("xyz");创建了几个字符串对象?答:两个对象,一个是静态区的"xyz",一个是用new创建在堆上的对象。32、接口是否可继承(extends)接口?抽象类是否可实现(implements)接口?抽象类是否可继承具体类(concrete class)?答:接口可以继承接口,而且支持多重继承。抽象类可以实现(implements)接口,抽象类可继承具体类也可以继承抽象类。举一个多继承的例子,我们定义一个动物(类)既是狗(父类1)也是猫(父类2),两个父类都有“叫”这个方法。那么当我们调用“叫”这个方法时,它就不知道是狗叫还是猫叫了,这就是多重继承的冲突。而接口没有具体的方法实现,所以多继承接口也不会出现这种冲突。33、一个".java"源文件中是否可以包含多个类(不是内部类)?有什么限制?答:可以,但一个源文件中最多只能有一个公开类(public class)而且文件名必须和公开类的类名完全保持一致。34、Anonymous Inner Class(匿名内部类)是否可以继承其它类?是否可以实现接口?答:可以继承其他类或实现其他接口,在Swing编程和Android开发中常用此方式来实现事件监听和回调。35、内部类可以引用它的包含类(外部类)的成员吗?有没有什么限制?答:一个内部类对象可以访问创建它的外部类对象的成员,包括私有成员。36、Java 中的final关键字有哪些用法?答:(1)修饰类:表示该类不能被继承;(2)修饰方法:表示方法不能被重写;(3)修饰变量:表示变量只能一次赋值以后值不能被修改(常量)。37、指出下面程序的运行结果。1 class A {23 static {4 System.out.print("1");5 }67 public A() {8 System.out.print("2");9 }10 }1112 class B extends A{1314 static {15 System.out.print("a");16 }1718 public B() {19 System.out.print("b");20 }21 }2223 public class Hello {2425 public static void main(String[] args) {26 A ab = new B();27 ab = new B();28 }2930 }答:执行结果:1a2b2b。创建对象时构造器的调用顺序是:先初始化静态成员,然后调用父类构造器,再初始化非静态成员,最后调用自身构造器。提示:如果不能给出此题的正确答案,说明之前第21题Java类加载机制还没有完全理解,赶紧再看看吧。38、数据类型之间的转换:如何将字符串转换为基本数据类型?如何将基本数据类型转换为字符串?答:调用基本数据类型对应的包装类中的方法parseXXX(String)或valueOf(String)即可返回相应基本类型;一种方法是将基本数据类型与空字符串("")连接(+)即可获得其所对应的字符串;另一种方法是调用String 类中的valueOf()方法返回相应字符串39、如何实现字符串的反转及替换?答:方法很多,可以自己写实现也可以使用String或StringBuffer/StringBuilder中的方法。有一道很常见的面试题是用递归实现字符串反转,代码如下所示:1 public static String reverse(String originStr) {2 if(originStr == null || originStr.length() <= 1)3 return originStr;4 return reverse(originStr.substring(1)) + originStr.charAt(0);5 }40、怎样将GB2312编码的字符串转换为ISO-8859-1编码的字符串?答:代码如下所示:1 String s1 = "你好";2 String s2 = new String(s1.getBytes("GB2312"), "ISO-8859-1");41、日期和时间:如何取得年月日、小时分钟秒?如何取得从1970年1月1日0时0分0秒到现在的毫秒数?如何取得某月的最后一天?如何格式化日期?答:问题1:创建java.util.Calendar 实例,调用其get()方法传入不同的参数即可获得参数所对应的值。Java 8中可以使用java.time.LocalDateTimel来获取,代码如下所示。1 public class DateTimeTest {2 public static void main(String[] args) {3 Calendar cal = Calendar.getInstance();4 System.out.println(cal.get(Calendar.YEAR));5 System.out.println(cal.get(Calendar.MONTH)); // 0 - 116 System.out.println(cal.get(Calendar.DATE));7 System.out.println(cal.get(Calendar.HOUR_OF_DAY));8 System.out.println(cal.get(Calendar.MINUTE));9 System.out.println(cal.get(Calendar.SECOND));1011 // Java 812 LocalDateTime dt = LocalDateTime.now();13 System.out.println(dt.getYear());14 System.out.println(dt.getMonthValue()); // 1 - 1215 System.out.println(dt.getDayOfMonth());16 System.out.println(dt.getHour());17 System.out.println(dt.getMinute());18 System.out.println(dt.getSecond());19 }20 }问题2:以下方法均可获得该毫秒数。1 Calendar.getInstance().getTimeInMillis();2 System.currentTimeMillis();3 Clock.systemDefaultZone().millis(); // Java 8问题3:代码如下所示。1 Calendar time = Calendar.getInstance();2 time.getActualMaximum(Calendar.DAY_OF_MONTH);问题4:利用java.text.DataFormat 的子类(如SimpleDateFormat类)中的format(Date)方法可将日期格式化。Java 8中可以用java.time.format.DateTimeFormatter来格式化时间日期,代码如下所示。1 import java.text.SimpleDateFormat;2 import java.time.LocalDate;3 import java.time.format.DateTimeFormatter;4 import java.util.Date;56 class DateFormatTest {78 public static void main(String[] args) {9 SimpleDateFormat oldFormatter = new SimpleDateFormat("yyyy/MM/dd");10 Date date1 = new Date();11 System.out.println(oldFormatter.format(date1));1213 // Java 814 DateTimeFormatter newFormatter = DateTimeFormatter.ofPattern("yyyy/MM/dd");15 LocalDate date2 = LocalDate.now();16 System.out.println(date2.format(newFormatter));17 }18 }补充:Java的时间日期API一直以来都是被诟病的东西,为了解决这一问题,Java 8中引入了新的时间日期API,其中包括LocalDate、LocalTime、LocalDateTime、Clock、Instant等类,这些的类的设计都使用了不变模式,因此是线程安全的设计。42、打印昨天的当前时刻。1 import java.util.Calendar;23 class YesterdayCurrent {4 public static void main(String[] args){5 Calendar cal = Calendar.getInstance();6 cal.add(Calendar.DATE, -1);7 System.out.println(cal.getTime());8 }9 }在Java 8中,可以用下面的代码实现相同的功能。1 import java.time.LocalDateTime;23 class YesterdayCurrent {45 public static void main(String[] args) {6 LocalDateTime today = LocalDateTime.now();7 LocalDateTime yesterday = today.minusDays(1);89 System.out.println(yesterday);10 }11 }43、比较一下Java和JavaSciprt。答:JavaScript 与Java是两个公司开发的不同的两个产品。Java 是原Sun Microsystems公司推出的面向对象的程序设计语言,特别适合于互联网应用程序开发;而JavaScript是Netscape公司的产品,为了扩展Netscape浏览器的功能而开发的一种可以嵌入Web页面中运行的基于对象和事件驱动的解释性语言。JavaScript的前身是LiveScript;而Java的前身是Oak语言。下面对两种语言间的异同作如下比较:基于对象和面向对象:Java是一种真正的面向对象的语言,即使是开发简单的程序,必须设计对象;JavaScript是种脚本语言,它可以用来制作与网络无关的,与用户交互作用的复杂软件。它是一种基于对象(Object-Based)和事件驱动(Event-Driven)的编程语言,因而它本身提供了非常丰富的内部对象供设计人员使用。解释和编译:Java的源代码在执行之前,必须经过编译。JavaScript是一种解释性编程语言,其源代码不需经过编译,由
 上一篇:近5年常考Java面试题及答案整理(二)68、Java中如何实现序列化,有什么意义?答:序列化就是一种用来处理对象流的机制,所谓对象流也就是将对象的内容进行流化。可以对流化后的对象进行读写操作,也可将流化后的对象传输于网络之间。序列化是为了解决对象流读写操作时可能引发的问题(如果不进行序列化可能会存在数据乱序的问题)。要实现序列化,需要让一个类实现Serializable接口,该接口是一个标识性接口,标注该类对象是可被序列化的,然后使用一个输出流来构造一个对象输出流并通过writeObject(Object)方法就可以将实现对象写出(即保存其状态);如果需要反序列化则可以用一个输入流建立对象输入流,然后通过readObject方法从流中读取对象。序列化除了能够实现对象的持久化之外,还能够用于对象的深度克隆(可以参考第29题)。69、Java中有几种类型的流?答:字节流和字符流。字节流继承于InputStream、OutputStream,字符流继承于Reader、Writer。在 java.io 包中还有许多其他的流,主要是为了提高性能和使用方便。关于Java的I/O需要注意的有两点:一是两种对称性(输入和输出的对称性,字节和字符的对称性);二是两种设计模式(适配器模式和装潢模式)。另外Java中的流不同于C#的是它只有一个维度一个方向。面试题 - 编程实现文件拷贝。(这个题目在笔试的时候经常出现,下面的代码给出了两种实现方案)1 import java.io.FileInputStream;2 import java.io.FileOutputStream;3 import java.io.IOException;4 import java.io.InputStream;5 import java.io.OutputStream;6 import java.nio.ByteBuffer;7 import java.nio.channels.FileChannel;89 public final class MyUtil {1011 private MyUtil() {12 throw new AssertionError();13 }1415 public static void fileCopy(String source, String target) throws IOException {16 try (InputStream in = new FileInputStream(source)) {17 try (OutputStream out = new FileOutputStream(target)) {18 byte[] buffer = new byte[4096];19 int bytesToRead;20 while((bytesToRead = in.read(buffer)) != -1) {21 out.write(buffer, 0, bytesToRead);22 }23 }24 }25 }2627 public static void fileCopyNIO(String source, String target) throws IOException {28 try (FileInputStream in = new FileInputStream(source)) {29 try (FileOutputStream out = new FileOutputStream(target)) {30 FileChannel inChannel = in.getChannel();31 FileChannel outChannel = out.getChannel();32 ByteBuffer buffer = ByteBuffer.allocate(4096);33 while(inChannel.read(buffer) != -1) {34 buffer.flip();35 outChannel.write(buffer);36 buffer.clear();37 }38 }39 }40 }41 }注意:上面用到Java 7的TWR,使用TWR后可以不用在finally中释放外部资源 ,从而让代码更加优雅。70、写一个方法,输入一个文件名和一个字符串,统计这个字符串在这个文件中出现的次数。答:代码如下:1 import java.io.BufferedReader;2 import java.io.FileReader;34 public final class MyUtil {56 // 工具类中的方法都是静态方式访问的因此将构造器私有不允许创建对象(绝对好习惯)7 private MyUtil() {8 throw new AssertionError();9 }1011 /**12 * 统计给定文件中给定字符串的出现次数13 *14 * @param filename 文件名15 * @param word 字符串16 * @return 字符串在文件中出现的次数17 */18 public static int countWordInFile(String filename, String word) {19 int counter = 0;20 try (FileReader fr = new FileReader(filename)) {21 try (BufferedReader br = new BufferedReader(fr)) {22 String line = null;23 while ((line = br.readLine()) != null) {24 int index = -1;25 while (line.length() >= word.length() && (index = line.indexOf(word)) >= 0) {26 counter++;27 line = line.substring(index + word.length());28 }29 }30 }31 } catch (Exception ex) {32 ex.printStackTrace();33 }34 return counter;35 }3637 }71、如何用Java代码列出一个目录下所有的文件?答:如果只要求列出当前文件夹下的文件,代码如下所示:1 import java.io.File;23 class Test12 {45 public static void main(String[] args) {6 File f = new File("/Users/nnngu/Downloads");7 for(File temp : f.listFiles()) {8 if(temp.isFile()) {9 System.out.println(temp.getName());10 }11 }12 }13 }如果需要对文件夹继续展开,代码如下所示:1 import java.io.File;23 class Test12 {45 public static void main(String[] args) {6 showDirectory(new File("/Users/nnngu/Downloads"));7 }89 public static void showDirectory(File f) {10 _walkDirectory(f, 0);11 }1213 private static void _walkDirectory(File f, int level) {14 if(f.isDirectory()) {15 for(File temp : f.listFiles()) {16 _walkDirectory(temp, level + 1);17 }18 }19 else {20 for(int i = 0; i < level - 1; i++) {21 System.out.print("t");22 }23 System.out.println(f.getName());24 }25 }26 }在Java 7中可以使用NIO.2的API来做同样的事情,代码如下所示:1 class ShowFileTest {23 public static void main(String[] args) throws IOException {4 Path initPath = Paths.get("/Users/nnngu/Downloads");5 Files.walkFileTree(initPath, new SimpleFileVisitor<Path>() {67 @Override8 public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)9 throws IOException {10 System.out.println(file.getFileName().toString());11 return FileVisitResult.CONTINUE;12 }1314 });15 }16 }72、用Java的套接字编程实现一个多线程的回显(echo)服务器。答:1 import java.io.BufferedReader;2 import java.io.IOException;3 import java.io.InputStreamReader;4 import java.io.PrintWriter
我通过两个月的复习拿到了阿里巴巴的 offer,有一些运气,也有一些心得,借着跳槽季来临特此分享出来。简单梳理一下我的复习思路,同时也希望和大家一起交流讨论,一起学习,如果不对之处欢迎指正一起学习。本文即是复习思路,亦可当做学习思路。我大致把 JAVA 的复习分为如下几个方向。JVM;排序算法和 Java 集合&工具类;多线程和并发包;存储相关:Redis 、Elastic Search、MySQL;框架:Spring,SpringMVC,Spring Boot分布式:Dubbo;设计模式;下面简单说一下如何复习上面的知识,首先明确,我不会讲解具体的知识点,而是一个思路,纵观互联网上面的帖子、文章误人子弟的多一些,所以就不误人子弟了,而是推荐分析出知识点然后以看书为主。毕竟书是多方校对权威出版的读物。JVMJVM 是每一个开发人员必备的技能,推荐看国内比较经典的 JVM 书籍,里面包含JVM的内存接口,类的加载机制等基础知识,是不是觉得这些在面试中似曾相识?所以对于 JVM 方面的知识的巩固与其在网上看一些零零碎碎的文章不如啃一下这本书。《深入理解 Java 虚拟机:JVM 高级特性与最佳实践(第 2 版)》,当然了如果你的英文好强烈推荐看 Oracle 最新发布的 JAVA 虚拟机规范。在啃书的时候切记不能图快,你对知识的积累不是通过看书的数量来决定,而是看书的深度。所以在看每一章节的时候看到不懂的要配合网上的文章理解,并且需要看几篇文章理解,因为一篇文章很可能是错误的,我认为文章的可信度顺序自建域名>*.github.io>SF>简书=博客园>CSDN>转载排序算法和 Java 集合、工具类这一个分类是每一个人必须掌握的并熟练使用的,那么为什么我把他们放在一起呢?  因为工具和集合类都源于算法,在准备算法复习之前你要理解,为什么要必考算法。正式因为排序算法和我们编程息息相关。举两个“栗子”。你可以看一下Collections 中的mergeSort和sort 方法,你会发现 mergeSort 就是归并排序的实现,而 sort 方法结合了归并排序和插入排序,这样使得 sort 方法最差O(NlogN)最好可以达到O(N)的效果。那么只有你自己理解了排序方法的实现,才能更好的使用 JAVA 中的集合类啊?第二个“栗子”,大家都听闻过 TopN 问题吧,经常在面试中遇到请写一下 TopN 的实现,说到算法它就是一个大顶堆,说到 JAVA 它是一个 PriorityQueue 的实现,那么你理解了 TopN 问题,知道他的时间复杂度,优缺点了,那么是不是就可以熟练运用 JAVA 的工具类写更高效的程序了?之所以排序算法和 JAVA  集合&工具类 一样重要是因为它们和我们每天的编程息息相关。面试官总是问排序算法也不是在难为你,而是在考察你的编程功底。所以你需要对着排序算法和基本的算法配合 JAVA 的集合类、工具类仔细的研究一番,这样才能更深入的理解他们的关联关系。  多线程和并发包多线程和并发包,重要性就不累述了,直接说一下学习方法。你首先要理解多线程不仅仅是 Thread 和 Runnable 那么简单,整个并发包下面的工具都是在为多线程服务。对于多线程的学习切不可看几篇面试文章,或者几个关键字 CountDownLatch,Lock 巴拉巴拉就以为理解了多线程的精髓,我整理了一个大图你需要针对这个大图或者自己梳理一个大图,对里面的类各个击破,他们的使用场景,优缺点。当然你需要配合源码看,源码就是大图里面的每一个源码,和上面讲的 JVM 一样,不要着急马上看完,而是看懂每一个地方是为什么。看的差不多你就会发现,其实他和 JAVA 集合类、工具类密不可分。那么自然把它列为重要知识点的原因不言而喻。Redis、MySQL、ElasticSearch存储相关相关都是我们平时常用的工具,Redis,MySQL,ElasticSearch。它的知识点分为两方面,一方面是你平时使用过程中积累的经验,另一方面是你对其的深入理解。所以对这个地方的建议就是通过书籍来巩固技术知识, 《Redis设计与实现 (数据库技术丛书)》,《高性能 MySQL》,《ElasticSearch 权威指南》这三本书不一定是该领域最好的书籍,但是如果你吃透了,对于你对知识的理解和程序的设计必定有很大帮助。书里面的内容太多,还是举两个“栗子”。第一个“栗子”,使用 Redis 切不可只用他当做 key-value 缓存数据库。我了解到它的5种基本类型中一种类型叫做 sorted set。sorted set 里 items 内容大于 64 的时候同时使用了 hash 和 skiplist 两种设计实现。这也会为了排序和查找性能做的优化。添加和删除都需要修改 skiplist,所以复杂度为 O(log(n))。 但是如果仅仅是查找元素的话可以直接使用 hash,其复杂度为 O(1) ,其他的 range 操作复杂度一般为 O(log(n)),当然如果是小于 64 的时候,因为是采用了 ziplist 的设计,其时间复杂度为 O(n)。这样以后查询和更新阅读都变得简单,那是不是可以用其实现 TopN 的需求呢?这样类似的需求就不需要你查数据,再在内存里面计算和操作了。比如我们简单的周排行,月排行都可以考虑使用这个数据结构实现,当然并不一定这是最好的解决方案,而是提供了一种解题思路。 另一个“栗子”,PriorityQueue 是优先队列我们上文已经了解,那么 ElasticSearch 的 query 也是用的优先队列分别在每一个分片上面获取,然后再合并优先队列你了解吗?这个“栗子”告诉我们其实算法是想通的,你理解一个便可以举一反三触类旁通。框架一谈框架就想起来 Spring,一说 Spring 就想起来 IOC,AOP。因为大家都在用这个框架,所以对于框架也不需要看一些其他的,直接就深入了解一下 Spring 就可以了。通过上面的叙述你已经了解了我的思路,看什么都要看他的实现原理,所以直接推荐你一本书《Spring 技术内幕》然后对着自己现有的 Spring 项目 Debug,从请求的流转梳理知识点。Spring 出来这么久大家对基本的知识已经了然于胸,重要的是看其解决问题的思路和原理,栗子又来了。  比如需要实现在 Bean 刚刚初始化的时候做一些操作,是不是需要使用InitializingBean?那么具体怎么使用,它的原理是什么,Spring Bean 的生命周期是什么样子,通过具体的使用场景逐步展开说明。这样复习效果会更好一些,然后再逐步的思考每一个知识点里面涉及的更多的知识点,比如 AOP 里面的 Proxy 都是基于什么原理实现,有什么优缺点。分布式这是一个老生常谈的话题,也是这几年比较火的话题,说起分布式就一定和 Dubbo 有关系,但是不能仅仅就理解到 Dubbo。首先我们需要思考它解决的问题,为什么要引入 Dubbo 这个概念。随着业务的发展、用户量的增长,系统数量增多,调用依赖关系也变得复杂,为了确保系统高可用、高并发的要求,系统的架构也从单体时代慢慢迁移至服务SOA时代,应运而生的 Dubbo 出现了,它作为 RPC 的出现使得我们搭建微服务项目变得简单,但是我们不仅仅要思考 Dubbo带来的框架支撑。同时需要思考服务的幂等、分布式事务、服务之间的 Trace 定位、分布式日志、数据对账、重试机制等,与此同时考虑 MQ 对系统的解耦和压力的分担、数据库分布式部署和分库分表、限流、熔断等机制。所以最终总结是不仅仅要看 Dubbo 的使用、原理同时还要思考上下游和一些系统设计的问题,这块相对的知识点较多,可以针对上面抛出来的点各个击破。设计模式设计模式很多,但是常用的就几种,这个地方可以分两个地方准备。1. 学以致用,设计模式不是背出来的,而是用出来了。平时多注意思考当前项目的设计,是否可以套用设计模式,当然必须先理解每一个设计模式存在的意义。  2. 在现有框架中思考设计模式的体现,上面已经讲过框架怎么学习,用 Spring 距离,它里面用了超过9种设计模式,你都知道用到哪里了吗?如果不知道,试着把他们找出来,同时思考为什么这么设计,全部找到以后,基本的设计模式的用法和原理你也就都理解了。