Java三年面试题

关于项目经验

关于专业技能 
1、基本语法 
static、final、transient等关键字的作用 
foreach循环的原理等等

static: 
1.静态变量 
2.静态方法 
3.静态代码块 
final: 
1.修饰类的属性,作用:修饰静态变量不可变,不建议修饰实例变量 
2.修饰类的方法,作用:可以被继承,但不能重写 
3.修饰类,作用:类不可以被继承 
transient: 
1.一旦变量被transient修饰,变量将不再是对象持久化(序列化)的一部分,该变量内容在序列化后没有值。 
2.transient关键字只能修饰变量,而不能修饰方法和类。注意,本地变量是不能被transient关键字修饰的。变量如果是用户自定义类变量,则该类需要实现Serializable接口。 
3.被transient关键字修饰的变量不再能被序列化,一个静态变量不管是否被transient修饰,均不能被序列化。

serialVersionUID的作用: 
如果没有明确指定serialVersionUID,序列化的时候会根据字段和特定的算法生成一个serialVersionUID,当属性有变化时这个id发生了变化,所以反序列化的时候 
就会失败。抛出“本地classd的唯一id和流中class的唯一id不匹配”。

2.集合 
List、Map、Set,问的是各种实现类的底层实现原理,实现类的优缺点。 
集合要掌握的是ArrayList、LinkedList、Hashtable、HashMap、ConcurrentHashMap、HashSet的实现原理, 
能流利作答,当然能掌握CopyOnWrite容器和Queue是再好不过的

ArrayList实现了可变大小的数组。它允许所有元素,包括null。ArrayList没有同步。 
Hashtable继承Map接口,Hashtable是同步的。实现一个key-value映射的哈希表。任何非空(non-null)的对象都可作为key或者value。 
HashMap和Hashtable类似,不同之处在于HashMap是非同步的,并且允许null,即null value和null key。 
HashMap解析:http://zhangshixi.iteye.com/blog/672697 
ArrayList解析:http://www.cnblogs.com/ITtangtang/p/3948555.html 
ArrayList源码中:private transient E[] elementData; //属性存放数据 
ArrayList是会开辟多余空间来保存数据的,而系列化和反序列化这些没有存放数据的空间是要消耗更多资源的, 
所以ArrayList的数组就声明为transient,自己实现write/readObject方法,仅仅系列化已经存放的数据。

ConcurrentHashMap的问题在面试中问得特别多 
ConcurrentHashMap与HashTable的区别主要是锁的粒度不同,HashTable当写数据时,锁住整个Hash表,而ConcurrentHashMap是所Hash表的段(桶)

(1)ConcurrentHashMap的锁分段技术 
答:ConcurrentHashMap将Hash表分成多个段(默认16个段),则有更新数据时,大部分是锁段,不锁整张Hash表,提高了性能 
每个segment则是一个传统意义上的hashtable

(2)ConcurrentHashMap的读是否要加锁,为什么 
答:读大部分情况下不加锁。当在get的时候,经过Hash,找到Hash表中的段(桶),再找到key在该段对应的index值,后会进行遍历数据,详见代码readValueUnderLock(): 
在判断存在hash值的节点,且key也存在,而值为null,则需要重新上锁再读。 
这里当v为空时,可能是一个线程正在改变节点,而之前的get操作都未进行锁定,根据bernstein条件,读后写或写后读都会引起数据的不一致, 
所以这里要对这个e重新上锁再读一遍,以保证得到的是正确值 
V get(Object key, int hash) { 
if (count != 0) { // read-volatile 
HashEntry e = getFirst(hash); 
while (e != null) { 
if (e.hash == hash && key.equals(e.key)) { 
V v = e.value; 
if (v != null) 
return v; 
return readValueUnderLock(e); // recheck 

e = e.next; 


return null; 

V readValueUnderLock(HashEntry e) { 
lock(); 
try { 
return e.value; 
} finally { 
unlock(); 


(3)ConcurrentHashMap的迭代器是强一致性的迭代器还是弱一致性的迭代器 
答:弱一致性的迭代器 
public static void main(String[] args) {

    Map<String, String> testMap = new ConcurrentHashMap<String, String>();

    testMap.put("pengjie1", "pengjie test 1");
    testMap.put("pengjie2", "pengjie test 2");
    testMap.put("pengjie3", "pengjie test 3");
    testMap.put("pengjie4", "pengjie test 4");
    testMap.put("pengjie5", "pengjie test 5");
    testMap.put("pengjie6", "pengjie test 6");
    testMap.put("pengjie7", "pengjie test 7");

    Collection<String> values  = testMap.values();

    Iterator<String> iterator = values.iterator();

    while (iterator.hasNext()) {
        String name = (String) iterator.next();
        System.out.println("name :"+name);

        testMap.remove("pengjie1");//pengjie1被删除,但不影响执行
    }
    System.out.print("Map size:"+testMap.size());
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

结果: 
name :pengjie test 7 
name :pengjie test 2 
name :pengjie test 5 
name :pengjie test 3 
name :pengjie test 6 
name :pengjie test 4 
Map size:6

3、设计模式 
http://www.cnblogs.com/maowang1991/archive/2013/04/15/3023236.html 
简单工厂模式、静态工厂方法模式、抽象工厂模式、单例模式、装饰模式、代理模式、观察者模式 
面试中关于设计模式的问答主要是三个方向: 
(1)你的项目中用到了哪些设计模式,如何使用 
(2)知道常用设计模式的优缺点 
(3)能画出常用设计模式的UML图

装饰模式:顾名思义,装饰模式就是给一个对象增加一些新的功能,而且是动态的,要求装饰对象和被装饰对象实现同一个接口,装饰对象持有被装饰对象的实例 
代理模式:就是多一个代理类出来,替原对象进行一些操作,比如我们在租房子的时候回去找中介,为什么呢?因为你对该地区房屋的信息掌握的不够全面,希望找一个更熟悉的人去帮你做 
装饰模式与代理模式的区别:装饰模式持有对被装饰对象的实例,而代理模式没有。 
装饰模式是对被装饰对象的增强。代理模式是对被代理类的限制 
当使用代理模式的时候,我们常常在一个代理类中创建一个对象的实例。 
当我们使用装饰器模式的时候,我们通常的做法是将原始对象作为一个参数传给装饰者的构造器。

4、多线程 
这也是必问的一块了。因为三年工作经验,所以基本上不会再问你怎么实现多线程了, 
会问得深入一些比如说Thread和Runnable的区别和联系、多次start一个线程会怎么样、线程有哪些状态。 
(一个Thread的实例一旦调用start()方法,这个实例的started标记就标记为true) 
当然这只是最基本的,出乎意料地,几次面试几乎都被同时问到了一个问题,问法不尽相同,总结起来是这么一个意思: 
问题: 
假如有Thread1、Thread2、Thread3、Thread4四条线程分别统计C、D、E、F四个盘的大小, 
所有线程都统计完毕交给Thread5线程去做汇总,应当如何实现?

5、JDK源码 
要想拿高工资,JDK源码不可不读。上面的内容可能还和具体场景联系起来,JDK源码就是实打实地看你平时是不是爱钻研了。 
LZ面试过程中被问了不少JDK源码的问题,其中最刁钻的一个问了LZ,String的hashCode()方法是怎么实现的,幸好LZ平时String源代码看得多, 
答了个大概。JDK源码其实没什么好总结的,纯粹看个人,总结一下比较重要的源码: 
(1)List、Map、Set实现类的源代码 
(2)ReentrantLock、AQS的源代码 
(3)AtomicInteger的实现原理,主要能说清楚CAS机制并且AtomicInteger是如何利用CAS机制实现的 
(4)线程池的实现原理 
(5)Object类中的方法以及每个方法的作用 
这些其实要求蛮高的,LZ去年一整年基本把JDK中重要类的源代码研究了个遍,真的花费时间、花费精力,当然回头看,是值得的—-不仅仅是为了应付面试。

AtomicInteger的实现原理:

    private volatile int value;//取内存中的最新值

public final int incrementAndGet() {
    for (;;) {
        int current = get();
        int next = current + 1;
        //关键函数,将current与内存中的值进行比较,若相同,则更新,并返回true;
        //若内存中的值已经被修改,则返回false,进入下一次循环
        if (compareAndSet(current, next))
            return next;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

多线程分析: 
在使用中我们一般使用Executors类的静态方法来创建线程池,除非我们对于线程池非常理解才能自己去灵活的规划线程池类(可以用来继承ThreadPoolExecutor) 
1:核心线程:简单来讲就是线程池中能否允许同时并发运行的线程的数量 
2:线程池大小:线程池中最多能够容纳的线程的数量。 
3:队列:对提交过来的任务的处理模式。 
如果队列发过来的任务,发现线程池中正在运行的线程的数量小于核心线程,则立即创建新的线程,无需进入队列等待。 
如果正在运行的线程等于或者大于核心线程,则必须参考提交的任务能否加入队列中去 
任务进入队列总共只有三种情况: 
1.能加入,且队列无界,则最多运行核心线程池数 ,最大线程池数没有作用 
2.能加入,且队列有界,则队列满后,创新新的线程运行任务,超过最大线程池数后拒绝任务 
3.不能加入,直接提交到线程池。创建新的线程运行任务,超过最大线程池数后拒绝任务

参考多线程分析:http://my.oschina.net/u/1398304/blog/376827 
队列的三种策略: 
SynchronousQueue 直接提交,也就是上面讲到的所有任务不进入队列去等待。此时小于核心线程就增加,多于或等于核心线程数时,还是增加线程,最大为线程池中的最大允许。超出就拒绝。 
LinkedBlockingQueue 无界队列 此时超过核心线程后的任务全部加入队列等待,系统最多只能运行核心线程数量的线程。这种方法相当于控制了并发的线程数量。 
ArrayBlockingQueue 有界队列 此时超过核心线程后的任务先加入队列等待,超出队列范围后的任务就生成线程,但创建的线程最多不超过线程池的最大允许值。

1、线程池刚创建时,里面没有一个线程。任务队列是作为参数传进来的。不过,就算队列里面有任务,线程池也不会马上执行它们。 
2、当调用 execute() 方法添加一个任务时,线程池会做如下判断: 
a. 如果正在运行的线程数量小于 corePoolSize,那么马上创建线程运行这个任务; 
b. 如果正在运行的线程数量大于或等于 corePoolSize,那么将这个任务放入队列。 
c. 如果这时候队列满了,而且正在运行的线程数量小于 maximumPoolSize,那么还是要创建线程运行这个任务; 
d. 如果队列满了,而且正在运行的线程数量大于或等于 maximumPoolSize,那么线程池会抛出异常,告诉调用者“我不能再接受任务了”。 
3、当一个线程完成任务时,它会从队列中取下一个任务来执行。 
4、当一个线程无事可做,超过一定的时间(keepAliveTime)时,线程池会判断,如果当前运行的线程数大于 corePoolSize,那么这个线程就被停掉。所以线程池的所有任务完成后,它最终会收缩到 corePoolSize 的大小。 
这个过程说明,并不是先加入任务就一定会先执行。假设队列大小为 4,corePoolSize为2,maximumPoolSize为6, 
那么当加入15个任务时,执行的顺序类似这样:首先执行任务 1、2,然后任务3~6被放入队列。 
这时候队列满了,任务7、8、9、10 会被马上执行,而任务 11~15 则会抛出异常。 
最终顺序是:1、2、7、8、9、10、3、4、5、6。当然这个过程是针对指定大小的ArrayBlockingQueue来说, 
如果是LinkedBlockingQueue,因为该队列无大小限制,所以不存在上述问题。

6、框架 
老生常谈,面试必问的东西。一般来说会问你一下你们项目中使用的框架,然后给你一些场景问你用框架怎么做, 
比如我想要在Spring初始化bean的时候做一些事情该怎么做、想要在bean销毁的时候做一些事情该怎么做、MyBatis中$和#的区别等等,这些都比较实际了,平时积累得好、有多学习框架的使用细节自然都不成问题。 
如果上面你的问题答得好,面试官往往会深入地问一些框架的实现原理。 
问得最多的就是Spring AOP的实现原理,当然这个很简单啦,两句话就搞定的的事儿,即使你不会准备一下就好了。 
LZ遇到的最变态的是让LZ画一下Spring的Bean工厂实现的UML图,当然面对这样一个有深度的问题,LZ是绝对答不出来的/(ㄒoㄒ)/~~

7、数据库

8、数据结构和算法分析 
可以不了解它们的具体实现,但是要知道什么是二叉查找树、什么是平衡树,AVL树和红黑树的区别。 
记得某次面试,某个面试官和我聊到了数据库的索引,他问我: 
你知道索引使用的是哪种数据结构实现吗? 
为什么要使用树吗?

9、Java虚拟机 
出乎LZ的意料,Java虚拟机应该是很重要的一块内容,结果在这几家公司中被问到的概率几乎为0。

10、Web方面的一些问题 
Java主要面向Web端,因此Web的一些问题也是必问的。LZ碰到过问得最多的两个问题是: 
谈谈分布式Session的几种实现方式 
常用的四种能答出来自然是让面试官非常满意的,另外一个常问的问题是: 
讲一下Session和Cookie的区别和联系以及Session的实现原理 
这两个问题之外,web.xml里面的内容是重点,Filter、Servlet、Listener,不说对它们的实现原理一清二楚吧,至少能对它们的使用知根知底。 
另外,一些细节的方面比如get/post的区别、forward/重定向的区别、HTTPS的实现原理也都可能会被考察到。

关于HR面试 
这轮的面试也必须重视起来,HR面试主要问的是几点: 
1、简历中写的过去工作经历的离职原因 
2、当前公司薪资待遇 
3、期望能到怎样的一家公司 
4、个人未来的发展方向

猜你喜欢

转载自blog.csdn.net/yangaliang/article/details/79807206
今日推荐