Java 面试题

305 阅读14分钟

JavaStudy

前言

自2014年从南昌到上海参加实习工作以来,到现在的杭州工作也有三年了。行业有话说,三年一个坎,我觉得我的坎就要到了。所以我想总结一下自己的技术,写写文档总结。禅宗有语云:“时时勤拂拭,勿使惹尘埃。”,因为我们不是圣贤,所以以勤为本,才是我们技术人员的出路。

Java 基础篇

1、HashMap 的源码,实现原理,JDK 8 对HashMap做了什么优化?

 原理:HashMap 是基于哈希表的 Map 接口实现类。
 优化:JDK 8 对 HashMap 底层的实现进行了优化,例如引入了红黑树数据接口和扩容的优化。

2、HaspMap扩容是怎样扩容的,为什么都是2的N次幂的大小?

每次 HashMap 在调用put()方法的时候,都会去判断 length 是不是超过极限值,如果超过了极限值,
就会调用扩容方法。
如果 length 为2 的次幂,那么length-1 转化为二进制必定是111... 的形式,这样的话操作效率会非常快而且不会浪费空间

3、HashMap、HashTable 和ConcurrentHashMap 有什么区别?

1、HashMap 是非线程安全的,而 HashMap 和 ConcurrentHashMap 是线程安全的;
2、HashMap 是可以有键值为空(null)的存在,而HashTable 和 ConcurrentHashMap 有空会抛出空指针异常;
3、因为线程安全、哈希效率的问题,HashMap 效率比HashTable 的要高。
4、ConcurrentHashMap 对 整个桶书进行了分割分段(Segment),然后再没一点都使用了lock锁进行保护,相对于HashTable 的 syn 关键字锁的粒度更精细了一些
,并发性能更好,而HashMap 没有锁机制,不是线程安全的。

4、极高并发下的HashMap 和 concurrentHashMap 那个性能好,为什么,如何实现?

HashTable使用一把锁处理并发问题,当有多个线程访问时,需要多个线程竞争一把锁,导致阻塞
ConcurrentHashMap则使用分段,相当于把一个HashMap分成多个,然后每个部分分配一把锁,这样就可以支持多线程访问

5、HashMap在高并发下如果没有处理线程安全会有怎样的安全隐患,具体表现是什么。

HashMap事实上并非线程安全的,在高并发的情况下,是非常可能发生死循环的,由此造成CPU 100%,这是非常可怕的。
所以在多线程的情况下,用HashMap是非常不妥当的行为,应採用线程安全类ConcurrentHashMap进行取代。

6、java中四种修饰符的限制范围。

public: Java语言中访问限制最宽的修饰符,一般称之为“公共的”。被其修饰的类、属性以及方法不仅可以跨类访问,而且允许跨包(package)访问。
private: Java语言中对访问权限限制的最窄的修饰符,一般称之为“私有的”。被其修饰的类、属性以及方法只能被该类的对象访问,其子类不能访问,更不能允许跨包访问。
protect: 介于public 和 private 之间的一种访问修饰符,一般称之为“保护形”。被其修饰的类、属性以及方法只能被类本身的方法及子类访问,即使子类在不同的包中也可以访问。
default:即不加任何访问修饰符,通常称为“默认访问模式“。该模式下,只允许在同一个包中进行访问。

7、Object类中的方法

1,构造函数
2,hashCode和equale函数用来判断对象是否相同,
3,wait(),wait(long),wait(long,int),notify(),notifyAll()
4,toString()和getClass,
5,clone()函数的用途是用来另存一个当前存在的对象。
6,finalize()用于在垃圾回收

8、接口和抽象类的区别,注意JDK8的接口可以有实现。

相同点:
1. 接口和抽象类都能定义方法和属性。
2. 接口和抽象类都是看作是一种特殊的类。大部分的时候,定义的方法要子类来实现
3. 抽象类和接口都可以不含有抽象方法。接口没有方法就可以作为一个标志。比如可序列化的接口Serializable,没有方法的接口称为空接口。没有抽象方法的抽象类,小编不知道有什么作用,总之是可以通过编译的。
4. 抽象类和接口都不能创建对象。
5. 抽象类和接口都能利用多态性原理来使用抽象类引用指向子类对象。
6. 继承和实现接口或抽象类的子类必须实现接口或抽象类的所有的方法,抽象类若有没有实现的方法就继续作为抽象类,要加abstract修饰。若接口的子类没有实现的方法,也要变为抽象类。
不同点:
1. 接口能够多实现,而抽象类只能单独被继承,其本质就是,一个类能继承多个接口,而只能继承一个抽象类。
2. 方法上,抽象类的方法可以用abstract 和public或者protect修饰。而接口默认为public abttact 修饰。
3. 抽象类的方法可以有需要子类实现的抽象方法,也可以有具体的方法。而接口在老版本的jdk中,只能有抽象方法,但是Java8版本的接口中,接口可以带有默认方法。
4. 属性上,抽象类可以用各种各样的修饰符修饰。而接口的属性是默认的public static final
5. 抽象类中可以含有静态代码块和静态方法,而接口不能含有静态方法和静态代码块。
6. 抽象类可以含有构造方法,接口不能含有构造方法。
7. 设计层面上,抽象类表示的是子类“是不是”属于某一类的子类,接口则表示“有没有”特性“能不能”做这种事。如飞机和鸟都能飞,但是他们在设计上实现一个Fly接口,实现fly()方法。远比两个类继承飞行物抽象类好得多。因为,飞机和鸟有太多的属性不一样。
8. 设计层面上,另外一点,抽象类可以是一个模板,因为可以自己带集体方法,所以要加一个实现类都能有的方法,直接在抽象类中写出并实现就好,接口在以前的版本则不行。新版本Java8才有默认方法。
9. 既然说到Java 8 那么就来说明,Java8中的接口中的默认方法是可以被多重继承的。而抽象类不行。
10. 另外,接口只能继承接口。而抽象类可以继承普通的类,也能继承接口和抽象类。

9、动态代理两种方式以及区别

常见的实现代理的两种方式:(1)JDK动态代理(2)使用cglib产生代理
这两种方法各有好坏。jdk动态代理是由java内部的反射机制生成字节码并生成对象来实现的,而cglib代理底层是借助
asm来实现的,这个asm就是一个java字节码操纵框架,它能用来动态生成类或者增强类的功能,ASM从类文件中读入
信息后,改变类的行为,分析类的信息,这就跟aop实现方式中的静态织入的是一样的,就是相当于把切面织入类的
字节码文件中,以此达到拦截的作用。一般jdk动态代理用于目标类都是基于统一的接口,cglib多用于对类的代理,
这些类不需要实现统一的接口。

10、Java序列化的方式。

一、是相应的对象实现了序列化接口Serializable,这个使用的比较多,对于序列化接口Serializable
接口是一个空的接口,它的主要作用就是标识这个对象时可序列化的,jre对象在传输对象的时候会进行相关的封装;
二、实现序列化的第二种方式为实现接口Externalizable,Externalizable继承了Serializable 接口;

11、传值和传引用的区别,Java是怎么样的,有没有传值引用。

值传递:方法调用时,实际参数把它的值的副本传递给对应的形式参数。特点:此时内存中存在两个相等的基本类型,
即实际参数和形式参数,后面方法中的操作都是对形参这个值的修改,不影响实际参数的值。
引用传递:方法调用时,实际参数的引用(地址,而不是参数的值)被传递给方法中相对应的形式参数,
函数接收的是原始值的内存地址;特点:在方法执行中,形参和实参内容相同,指向同一块内存地址,
方法执行中对引用的操作将会影响到实际对象。

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

会出现问题

13、@Transactional注解在什么情况下会失效,为什么。

框架篇

1、简单的谈一下SpringMVC的工作流程?

1、用户发送请求到前端控制器DisPathcherServlet;
2、DisPathcherServlet 收到请求调用HandleMapping 处理器映射器;
3、处理器映射器找到具体的处理器,生成处理器及处理器拦截器一并返回给DisPathcherServlet;
4、DisPathcherServlet 调用HandleAdapter 处理器适配器;
5、HandleAdapter 经过适配找到具体的Controller ;
6、Controller 执行完成返回ModelAndView ;
7、HandleAdapter 将Controller 的执行结果返回给DisPathcherServlet;
8、DisPathcherServlet 将ModelAndView 传给 ViewReslover 视图解释器;
9、ViewReslover 解析返回具体的view ;
10、DisPathcherServlet 根据view 进行渲染数据;
11、DisPathcherServlet 响应用户;

2、Spring中用到的设计模式

简单工厂、工厂方法、单例模式、适配器、包装器、代理、观察者、策略、模板方法

3、Spring中IOC的作用与原理?对象创建的过程。

IOC--Inversion of Control控制反转。当某个角色需要另外一个角色协助的时候,在传统的程序设计过程中,
通常由调用者来创建被调用者的实例对象。但在spring中创建被调用者的工作不再由调用者来完成,因此称为控制反转。
创建被调用者的工作由spring来完成,然后注入调用者 直接使用。

4、Spring管理事务有几种方式?

有两种方式:
1、编程式事务,在代码中硬编码。(不推荐使用);
2、声明式事务,在配置文件中配置(推荐使用).
声明式事务又分为两种:
a、基于XML的声明式事务;
b、基于注解的声明式事务.

5、Spring中AOP的原理、好处?

原理:AOP是面向切面编程,是通过动态代理的方式为程序添加统一功能,集中解决一些公共问题。
优点:1.各个步骤之间的良好隔离性耦合性大大降低
      2.源代码无关性,再扩展功能的同时不对源码进行修改操作

多线程篇

1、Java实现多线程有哪几种方式。

1、继承Thread类实现多线程;
2、实现Runnable接口方式实现多线程;
3、使用ExecutorService、Callable、Future实现有返回结果的多线程;

2、Callable和Future的了解

Callable接口代表一段可以调用并返回结果的代码;
Future接口表示异步任务,是还没有完成的任务给出的未来结果。
所以说Callable用于产生结果,Future用于获取结果。

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

参数:
1、corePoolSize:核心线程数
2、queueCapacity:任务队列容量(阻塞队列)
3、maxPoolSize:最大线程数
4、keepAliveTime:线程空闲时间
5、allowCoreThreadTimeout:允许核心线程超时
6、rejectedExecutionHandler:任务拒绝处理器

Java通过Executors提供四种线程池,分别为:
newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。
newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

4、volitile关键字的作用,原理。

修饰变量,每次取值都是直接到内存去读取。
编译器不能对它进行缓存等优化操作。
都用于对原子性操作要求不强,要求内存可见的地方。
比如只有一个线程对其进行修改,多个线程读取该变量。

5、synchronized关键字的用法,优缺点。

synchronized修饰方法和synchronized 修饰代码块
优点:
多线程同时访问只有一个线程能进入synchronized 方法或者代码块
缺点:
当某个线程进入同步方法获得对象锁,那么其他线程访问这里对象的同步方法时,必须等待或者阻塞,
这对高并发的系统是致命的,这很容易导致系统的崩溃。如果某个线程在同步方法里面发生了死循环,
那么它就永远不会释放这个对象锁,那么其他线程就要永远的等待。这是一个致命的问题。

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

一个是ReentrantLock,另两个是ReentrantReadWriteLock类中的两个静态内部类ReadLock和WriteLock。
场景1:如果已加锁,则不再重复加锁
场景2:如果发现该操作已经在执行,则尝试等待一段时间,等待超时则不执行(尝试等待执行)
场景3:如果发现该操作已经加锁,则等待一个一个加锁(同步执行,类似synchronized)
场景4:可中断锁

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

1. 乐观锁是一种思想,具体实现是,表中有一个版本字段,第一次读的时候,获取到这个字段。处理完业务
逻辑开始更新的时候,需要再次查看该字段的值是否和第一次的一样。如果一样更新,反之拒绝。之所
以叫乐观,因为这个模式没有从数据库加锁。2. 悲观锁是读取的时候为后面的更新加锁,之后再来的读
操作都会等待。这种是数据库锁乐观锁优点程序实现,不会存在死锁等问题。他的适用场景也相对乐观。
阻止不了除了程序之外的数据库操作。悲观锁是数据库实现,他阻止一切数据库操作。再来说更新数据
丢失,所有的读锁都是为了保持数据一致性。乐观锁如果有人在你之前更新了,你的更新应当是被拒绝
的,可以让用户从新操作。悲观锁则会等待前一个更新完成。这也是区别。具体业务具体分析

8、ABC三个线程如何保证顺序执行

主要通过join方法来实现顺序输出ABC

9、线程的状态都有哪些

1、新建状态
2、就绪状态
3、运行状态(running)
4、阻塞状态(blocked)
5、死亡状态(dead)

10、sleep和wait的区别

sleep
sleep方法是使线程停止一段时间的方法。
在sleep 时间间隔期满后,线程不一定立即恢复执行。
这是因为在那个时刻,其它线程可能正在运行而且没有被调度为放弃执行,除非“醒来”的线程具有更高的优先级,正在运行的线程因为其它原因而阻塞。
wait
wait是线程交互时,如果线程对一个同步对象x 发出一个wait调用,该线程会暂停执行,被调对象进入等待状态,直到被唤醒或等待时间到。

11、notify和notifyall的区别

notify表示唤醒一个线程,notifyAll也表示唤醒一个线程,但它会notify所有的线程,具体唤醒哪一个线程,由jvm来决定.

12、ThreadLocal的了解,实现原理

ThreadLocal提供一个线程(Thread)局部变量,访问到某个变量的每一个线程都拥有自己的局部变量。说白了,ThreadLocal就是想在多线程环境下去保证成员变量的安全。