java面试

两年Java程序员面试经

工作两年有余,本人第一份工作是在一家外包公司,第二份工作是在一家做SAAS平台的公司,第一家公司让我入门,进入了软件开发的行业,了解了一些基础的东西;第二家公司由于规模不大,很多活儿都是一个人来做,从产品到开发、测试、运维我都了解了,对于整体的软件开发流程有了很大的了解。但是,为了自己的更好发展,决定去一家大公司,于5月初提出了离职,虽然领导挽留,但是我还是离职了,然后我自己面试了很多家公司,大公司小公司都有,积累了足够的经验,开始向大公司投递简历,面了几家之后,终于拿到了自己满意的offer,我是在积累了足够的经验之后才提出离职的,离职时一件很重大的事情,自己要慎重考虑,要从职业发展和个人规划上考虑清楚再去做出选择。
大公司面试考验基础和项目经验,小公司比较偏向于项目经验。这里我都总结下,给大家一个参考,希望可以帮助到正在面临跳槽的同志。
一、Java基础
1、Java基本数据类型
2、原始数据类型和封装类的区别
3、String、StringBuffer、StringBuilder区别
4、运行时异常和非运行时异常区别
5、简述一下面向对象的特征,并举例说明你对面向对象的理解
6、正则表达式的用法
7、Java 语言如何进行异常处理,关键字:throws、throw、try、catch、finally分别代表什么意义?finally代码是在return之后还是之前执行?
8、abstract class和interface有什么区别?接口可以继承接口吗?接口可以继承抽象类吗,为什么?
9、构造器(constructor)是否可被重写(override)?
10、是否可以继承String类?
11、Java 中的final关键字有哪些用法?
12、try{}里有一个return语句,那么紧跟在这个try后的finally{}里的代码会不会被执行,什么时候被执行,在return前还是后?
13、阐述final、finally、finalize的区别。

14、如何通过反射创建对象?

  三种    Class.Forname   类名.Class  对象.Getclass

15、Java 8的新特性

1Lambda 表达式
2方法引用
3函数式接口
4默认方法
5Stream
6Optional 类
7Nashorn, JavaScript 引擎
8新的日期时间 API
9Base64

16、Java数组和链表的两种结构的操作效率


17、Java的引用类型有哪几种

  

在JDK 1.2以前的版本中,若一个对象不被任何变量引用,那么程序就无法再使用这个对象。也就是说,只有对象处于可触及(reachable)状态,程序才能使用它。从JDK 1.2版本开始,把对象的引用分为4种级别,从而使程序能更加灵活地控制对象的生命周期。这4种级别由高到低依次为:强引用、软引用、弱引用和虚引用。

强引用(StrongReference)
强引用是使用最普遍的引用。如果一个对象具有强引用,那垃圾回收器绝不会回收它。当内存空间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足的问题。 ps:强引用其实也就是我们平时A a = new A()这个意思。

软引用(SoftReference)
如果一个对象只具有软引用,则内存空间足够,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存(下文给出示例)。
软引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被垃圾回收器回收,Java虚拟机就会把这个软引用加入到与之关联的引用队列中

弱引用(WeakReference)
弱引用与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象。
弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。

虚引用(PhantomReference)
“虚引用”顾名思义,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收
虚引用主要用来跟踪对象被垃圾回收器回收的活动。虚引用与软引用和弱引用的一个区别在于:虚引用必须和引用队列 (ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之 关联的引用队列中

ReferenceQueue queue = new ReferenceQueue ();

PhantomReference pr = new PhantomReference (object, queue);
程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。如果程序发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动。

(一)为什么使用软引用 (缓存)
SoftReference的特点是它的一个实例保存对一个Java对象的软引用, 该软引用的存在不妨碍垃圾收集线程对该Java对象的回收。也就是说,一旦SoftReference保存了对一个Java对象的软引用后,在垃圾线程对 这个Java对象回收前,SoftReference类所提供的get()方法返回Java对象的强引用。另外,一旦垃圾线程回收该Java对象之 后,get()方法将返回null。软引用的对象容易操控,不会强制驻守在内存,是缓存的极佳方式
(二)使用软引用
例:用Map集合缓存软引用的Bitmap对象

Map<String, SoftReference<Bitmap>> imageCache = new new HashMap<String, SoftReference<Bitmap>>();
//强引用的Bitmap对象
Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
//软引用的Bitmap对象
SoftReference<Bitmap> bitmapcache = new SoftReference<Bitmap>(bitmap);
//添加该对象到Map中使其缓存
imageCache.put("1",softRbitmap);
..
.
//从缓存中取软引用的Bitmap对象
SoftReference<Bitmap> bitmapCache = imageCache.get("1");
//取出Bitmap对象,如果由于内存不足Bitmap被回收,将取得空
Bitmap bm = bitmapCache .get();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

利用软引用缓存图片到内存中,是安卓图片缓存的常用方法,它相对灵活,不会强占内存,容易回收


二、多线程、IO、集合

1、ArrayList、Vector、LinkedList的存储性能和特性


2、List、Set、Map是否继承自Collection接口?


3、List、Map、Set三个接口存取元素时,各有什么特点?


4、请说出与线程同步以及线程调度相关的方法。

    

5、编写多线程程序有几种实现方式?


6、简述synchronized 和java.util.concurrent.locks.Lock的异同?

             1.Lock能完成几乎所有synchronized的功能,并有一些后者不具备的功能,如锁投票、定时锁等候、可中断锁等候等

   2.synchronized 是Java 语言层面的,是内置的关键字;Lock 则是JDK 5中出现的一个包,在使用时,synchronized 同步的代码块可以由JVM自动释放;Lock 需要程序员在finally块中手工释放,如果不释放,可能会引起难以预料的后果(在多线程环境中)。


   synchronized 快速回顾:

   1.当代码块 加上 synchrozized之后,代码会发生什么改变?

    答案:有两条改变。一个是原子性(atomicity),一个是可见性(visibility)原子性意味着一次只能有一个线程获得代码锁,进入synchronized 包围的代码块中执行。而可见性则是对不同范围内对变量的修改做出的一致性。强调变量的可见性与一致性,是因为在Java 内存中,内存缓存编译器优化在多线程条件下会造成各种反常行为。一般来说,线程以某种不必让其他线程立即可以看到的方式(不管这些线程在寄存器(register)中、在处理器特定的缓存(CPU cache)中,还是通过指令重排或者其他编译器优化),不受缓存变量值的约束,但是加人synchronized关键字之后,那么运行库将确保某一线程对变量所做的更新先于对现有 synchronized 块所进行的更新,当进入由同一监控器(lock)保护的另一个 synchronized 块时,将立刻可以看到这些对变量所做的更新。类似的规则也存在于 volatile 变量

  2. 为什么需要java.util.concurrent.lock?

  主要是synchronized是比较古老的实现机制,设计较早,有一些功能上的限制:

—— 它无法中断一个正在等候获得锁的线程

——也无法通过投票得到锁,如果不想等下去,也就没法得到锁。

——同步还要求锁的释放只能在与获得锁所在的堆栈帧相同的堆栈帧中进行

  

 3. 重入锁 ReentrantLock

java.util.concurrent.lock 中的 Lock 框架是锁定的一个抽象,它允许把锁定的实现作为 Java 类,而不是作为语言的特性来实现。

这就为 Lock 的多种实现留下了空间,各种实现可能有不同的调度算法、性能特性或者锁定语义。 

ReentrantLock 类实现了Lock ,它拥有与 synchronized 相同的并发性和内存语义,但是添加了类似锁投票定时锁等候可中断锁等候的一些特性。

此外,它还提供了在激烈争用情况下更佳的性能。(换句话说,当许多线程都想访问共享资源时,JVM 可以花更少的时候来调度线程,把更多时间用在执行线程上。)

reentrant 锁意味着什么呢?简单来说,它有一个与锁相关的获取计数器,如果拥有锁的某个线程再次得到锁,那么获取计数器就加1,然后锁需要被释放两次才能获得真正释放。这模仿了 synchronized 的语义;如果线程进入由线程已经拥有的监控器保护的 synchronized 块,就允许线程继续进行,当线程退出第二个(或者后续) synchronized 块的时候,不释放锁,只有线程退出它进入的监控器保护的第一个 synchronized 块时,才释放锁。

[java] view plain copy
  1. Lock lock = new ReentrantLock();  
  2. lock.lock();  
  3. try {   
  4.   // update object state  
  5. }  
  6. finally {  
  7.   lock.unlock();   
  8. }  

上面的代码可以看出,   Lock 和 synchronized 有一点明显的区别 —— lock 必须在 finally 块中释放。否则,如果受保护的代码将抛出异常,锁就有可能永远得不到释放!这一点区别看起来可能没什么,但是实际上,它极为重要。忘记在 finally 块中释放锁,可能会在程序中留下一个定时炸弹,当有一天炸弹爆炸时,您要花费很大力气才有找到源头在哪。而使用同步,JVM 将确保锁会获得自动释放。

    

[java] view plain copy
  1. package com.mahoutchina.java.concurrent.lock;  
  2.   
  3. import java.util.concurrent.ExecutorService;  
  4. import java.util.concurrent.Executors;  
  5. import java.util.concurrent.TimeUnit;  
  6. import java.util.concurrent.locks.Condition;  
  7. import java.util.concurrent.locks.ReentrantLock;  
  8.   
  9. public class LockSample {  
  10.   
  11.     /** 
  12.      * @param args 
  13.      */  
  14.     public static void main(String[] args) {  
  15.         final ExecutorService exec = Executors.newFixedThreadPool(4);  
  16.         final ReentrantLock lock = new ReentrantLock();  
  17.         final Condition con = lock.newCondition();  
  18.         final int time = 5;  
  19.         final Runnable add = new Runnable() {  
  20.   
  21.             @Override  
  22.             public void run() {  
  23.                 System.out.println("Pre" + lock.toString());  
  24.                 lock.lock();  
  25.   
  26.                 try {  
  27.                     con.await(time, TimeUnit.SECONDS);  
  28.                 } catch (InterruptedException e) {  
  29.                     e.printStackTrace();  
  30.                 } finally {  
  31.                     System.out.println("Post " + lock.toString());  
  32.                     lock.unlock();  
  33.                 }  
  34.   
  35.             }  
  36.         };  
  37.   
  38.         for (int i = 0; i < 4; i++) {  
  39.             exec.submit(add);  
  40.         }  
  41.         exec.shutdown();  
  42.   
  43.     }  
  44.   
  45. }  

运行结果:

    

Pre java.util.concurrent.locks.ReentrantLock@5829428e[Unlocked]

Pre java.util.concurrent.locks.ReentrantLock@5829428e[Unlocked]

Pre java.util.concurrent.locks.ReentrantLock@5829428e[Unlocked]

Pre java.util.concurrent.locks.ReentrantLock@5829428e[Unlocked]

Post java.util.concurrent.locks.ReentrantLock@5829428e[Locked by thread pool-1-thread-1]

Post java.util.concurrent.locks.ReentrantLock@5829428e[Locked by thread pool-1-thread-3]

Post java.util.concurrent.locks.ReentrantLock@5829428e[Locked by thread pool-1-thread-2]

Post java.util.concurrent.locks.ReentrantLock@5829428e[Locked by thread pool-1-thread-4]


7、hash碰撞以及hash算法、如何解决哈希冲突

     

算法解决冲突的方法


Hash算法解决冲突的方法一般有以下几种常用的解决方法
1, 开放定址法:
所谓的开放定址法就是一旦发生了冲突,就去寻找下一个空的散列地址,只要散列表足够大,空的散列地址总能找到,并将记录存入
公式为:fi(key) = (f(key)+di) MOD m (di=1,2,3,……,m-1)
用开放定址法解决冲突的做法是:当冲突发生时,使用某种探测技术在散列表中形成一个探测序列。沿此序列逐个单元地查找,直到找到给定的关键字,或者
碰到一个开放的地址(即该地址单元为空)为止(若要插入,在探查到开放的地址,则可将待插入的新结点存人该地址单元)。查找时探测到开放的地址则表明表
中无待查的关键字,即查找失败。

比如说,我们的关键字集合为{12,67,56,16,25,37,22,29,15,47,48,34},表长为12。 我们用散列函数f(key) = key mod l2
当计算前S个数{12,67,56,16,25}时,都是没有冲突的散列地址,直接存入:
这里写图片描述
计算key = 37时,发现f(37) = 1,此时就与25所在的位置冲突。
于是我们应用上面的公式f(37) = (f(37)+1) mod 12 = 2。于是将37存入下标为2的位置:
这里写图片描述

2, 再哈希法:
再哈希法又叫双哈希法,有多个不同的Hash函数,当发生冲突时,使用第二个,第三个,….,等哈希函数
计算地址,直到无冲突。虽然不易发生聚集,但是增加了计算时间

3, 链地址法:
链地址法的基本思想是:每个哈希表节点都有一个next指针,多个哈希表节点可以用next指针构成一个单向链表,被分配到同一个索引上的多个节点可以用这个单向
链表连接起来,如:
键值对k2, v2与键值对k1, v1通过计算后的索引值都为2,这时及产生冲突,但是可以通道next指针将k2, k1所在的节点连接起来,这样就解决了哈希的冲突问题
这里写图片描述
4, 建立公共溢出区:
这种方法的基本思想是:将哈希表分为基本表和溢出表两部分,凡是和基本表发生冲突的元素,一律填入溢出表


8、ArrayList和HsahSet的区别,HashMap和Hashtable的区别?

  

9、HashMap的存储原理,需要了解HashMap的源码。


10、ArrayList和LinkedList的各自实现和区别
11、HashMap和HashTable区别
12、Hashtable,HashMap,ConcurrentHashMap 底层实现原理与线程安全问题
13、Hash冲突怎么办?哪些解决散列冲突的方法?
14、讲讲IO里面的常见类,字节流、字符流、接口、实现类、方法阻塞。
15、讲讲NIO。
16、递归读取文件夹下的文件,代码怎么实现
17、常用的线程池模式以及不同线程池的使用场景
18、newFixedThreadPool此种线程池如果线程数达到最大值后会怎么办,底层原理。
19、了解可重入锁的含义,以及ReentrantLock 和synchronized的区别
20、atomicinteger和volatile等线程安全操作的关键字的理解和使用
21、进程和线程的区别
22、同步和异步,阻塞和非阻塞
三、设计模式
1、简述一下你了解的设计模式。
2、写出单利模式,懒汉和饿汉
四、JVM
1、描述一下JVM加载class文件的原理机制?
2、Java 中会存在内存泄漏吗,请简单描述。
3、GC是什么?为什么要有GC?
4、JVM的内存模型(重要、GC算法、新生代、老年代、永久代等需要详细了解)
5、GC的工作原理
五、数据库
1、事务的ACID是指什么?
2、悲观锁和乐观锁的区别
3、Left join、right join、inner join区别
4、SQL优化
5、redis缓存数据库,需要了解,什么是内存数据库,支持的数据类型
6、单个索引、联合索引、主键索引
7、索引的数据结构
8、数据库的锁,行锁、表锁、悲观锁、乐观锁
六、框架
1、web Service 常用注解 客户端如何生成,还是手写
2、mybatis处理大数据
3、AOP IOC优点缺点
4、spring事务传播属性和隔离级别
5、Web Service 客户端和服务端实现技术
6、Spring Mvc返回json技术
7、Hibernate悲观锁和乐观锁
8、Hibernate三种状态
9、hibernate和ibatis的区别
10、讲讲mybatis连接池
11、SpringMVC的工作原理
12、Spring的几种注入方式
13、Spring如何实现事务管理
14、Spring IOC和AOP的原理
七、算法和数据结构
1、写出快速排序和冒泡排序算法
八、Linux基础
1、常用命令
2、Linux文件权限
3、端口占用
九、项目经验面试真题
1、浏览器访问www.taobao.com,经历了怎样的过程。
2、高并发情况下,我们系统是如何支撑大量的请求的
3、集群如何同步会话状态
4、负载均衡的原理
5、如果有一个特别大的访问量,到数据库上,怎么做优化(DB设计,DBIO,SQL优化,Java优化)
设计缓存,使用memcached、redis,读写分离,数据库优化(优化表结构、索引、查询语句等),使用集群,升级硬件,
6、手写斐波那契数列、递归查找文件
7、Mybatis的# $的区别
8、prototype作用域的范围
9、Spring的动态代理
10、手写生产者消费者模式
11、分布式锁
12、死锁的原因以及如何避免
13、内存溢出的原因
14、秒杀系统的设计
15、100万条记录实现导出
16、字符串的比较、反转
17、CountDownLatch的应用场景

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值