每天10道Java面试题,小伙伴们保持节奏,加油!
1.Java基础
1.1:JDK和JRE有什么区别?
JDK中包含了程序运行环境和开发环境,JRE中包含了程序运行环境。可以说JDK包含了JRE,同时还包含了程序运行中的编译器javac,程序调试和分析的工具。简单来说,如果需要运行程序,只装JRE也是可以的,如果要开发程序,则需要装JDK。
1.2:==和equals的区别是什么?
如果是基本数据类型,==比较的是值;如果是引用数据类型,==比较的是地址。equals默认是地址的比较,很多Object类都会重写equals方法,比如Integer和String把它变成了值的比较,所以equals通常情况为值的比较。
1.3:两个对象的hashCode()相等值一定会相等吗?
不一定。hashCode值相等说明两个键值对的哈希值相等,不一定说明两个值一定相等。
1.4:final在Java中有什么作用?
final修饰在类上,该类不可继承
final修饰在方法,该方法不能被重写
final修饰在变量上,该变量为常量,并且值必须初始化,初始化后不能被修改
1.5:Java中的Math.round(-1.5)的结果是什么?
-1,如果数值为正数则向上取整,如果为负数,直接舍弃0.5.
1.6:String属于基础的数据类型吗?
不属于,String属于对象。基本数据类型:int byte short long char boolean double float
1.7:Java中操作字符串的有哪些类,他们有什么区别?
String StringBuffer StringBuild的区别是String是不可变的对象,不可在原来的字符串上直接操作,而是又生成了一个新的字符串;StringBuffer是线程安全的,StringBuild是非线程安全的,所以StringBuild的性能高,单线程模式下建议使用StringBuild,多线程下使用StringBuffer.
1.8:String str="i"与String str=new String(“i”);一样吗
不一样。内存分配方式不一样,String str="i"分配在对象常量池中,而String str=new String(“i”)分配在堆中。
1.9:如何将字符串反转?
使用StringBuffer或者StringBuild中的reverse方法。
1.10:String类的常用方法有哪些?
indexOf ():返回指定字符的索引。
charAt ():返回指定索引处的字符。
replace ():字符串替换。
trim ():去除字符串两端空白。
split ():分割字符串,返回一个分割后的字符串数组。
getBytes ():返回字符串的 byte 类型数组。
length ():返回字符串长度。
toLowerCase ():将字符串转成小写字母。
toUpperCase ():将字符串转成大写字符。
substring ():截取字符串。
equals ():字符串比较。
1.11:抽象类必须有抽象方法吗?
不一定,抽象类可以有其他方法。
1.12:抽象类和普通类有哪些区别?
- 抽象类可以有其他方法,普通类不能有抽象方法。
- 抽象类不能直接实例化,普通类可以直接实例化。
1.13:抽象类能使用final修饰吗?
不能,定义抽象类就是让其他的类继承的,final修饰过的类不能被继承。
1.14:抽象类和接口有什么区别?
1.抽象类要使用extends关键字继承,而接口要通过implement实现
2.抽象类中可以有其他的构造方法,接口中不行。
3.类只能继承一个抽象类,而类可以实现多个接口。
4.接口只能用public修饰,抽象类可以是任意访问修饰符。
1.15:Java中IO流分为几种?
按功能分:输出流(output) 输入流(input)
按字符分:字节流和字符流。字节流是以8个字节为单位传输的,而字符流是以16个字节传输的。
1.16:BIO ,NIO ,AIO 有什么区别?
BIO:同步阻塞式IO,它的特点是模式简单使用方便,并发使用效率低
NIO:同步非阻塞式IO,BIO的升级版本,客户端和服务端通过道路通讯实现了多路复用
AIO:也可以叫做NIO2,包含了同步非阻塞,异步的操作基于事件和回调机制
1.17:File的常用方法有哪些?
File.exitis() 检查文件路径是否存在
File.createFile() 创建文件
File.createDirectory() 创建文件夹
File.delete() 删除
File.move() 移动
File.copy() 复制
File.read() 读取文件
File.write() 写入文件
2.容器
2.18:Java有哪些容器?
分为两大类:collection和Map
- Collection
- List
- ArrayList
- LinkedList
- Vector
- Stack
- Set
- HashSet
- LinkedHashSet
- TreeSet
- Map
- HashMap
- TreeMap
- HashTable
2.19:collection和collections有什么区别?
collection:所有的接口都是它的子类,collection提供了对象进行基本操作的通用接口方法。
collections:是一个包装类,包含了许多静态方法,不能被实例化,就像一个工具类。
2.20:List ,Set ,Map 之间的区别是什么?
从是否有序和值是否重复考虑
|
是否有序 |
值是否可重复 |
List |
是 |
是 |
AbstractSet |
否 |
否 |
LinkedSet |
否 |
否 |
TreeSet |
是 (用二叉树排序) |
否 |
AbstractMap |
否 |
是(key值唯一) |
HashMap |
否 |
是(key值唯一) |
TreeMap |
是(用二叉树排序) |
是(key值唯一) |
2.21: HashMap 和 Hashtable 有什么区别?
- HashMap运行key和Value为null,而HashTable不允许
- HashTable是线程安全的,HashMap不是
- 在HashTable的类注释中可以看到,HashTable是保留类,不建议使用,单线程下可以用HashMap代替,多线程可以用ConcurrentHashTable代替
2.22: 如何决定使用 HashMap 还是 TreeMap?
对于在Map中插入,删除,定位一个元素这类的操作,HashMap是更合适的选择。如果对集合进行有序的遍历,TreeMap比较适合。
2.23: 说一下 HashMap 的实现原理?
HashMap基于Hash算法实现的,我们通过put(key,value)存储,get(key)来获取。当传入key时,HashMap会根据key.HashCode()计算出hash值,根据hash值将value保存在bucket里。当计算出hash值相同时,我们称之为hash冲突,HashMap的做法是用链表和红黑树存储相同hash值的value。当Hash冲突的个数比较少时,使用链表否则使用红黑树。
2.24: 说一下 HashSet 的实现原理?
HashSet是基于HashMap实现的,HashSet底层使用HashMap保存所有的元素,HashSet的基本操作都是直接调用底层HashMap的相关方法来完成,HashSet中值不可以重复。
2.25: ArrayList 和 LinkedList 的区别是什么?
- ArrayList动态数组的数据结构,LinkedList是双向链表结构
- ArrayList比LinkedList在随机访问的时候效率高,因为LinkedList是线性的数据存储方式。
- 在非首尾的增加和删除操作,LinkedList 要比 ArrayList 效率要高,因为 ArrayList 增删操作要影响数组内的其他数据。
2.26: 如何实现数组和 List 之间的转换?
- 数组转List:Arrays.asList(array)
- List转数组: List中的toArray()方法
2.27: ArrayList 和 Vector 的区别是什么?
- Vector使用了锁来实现线程同步,是线程安全的。ArrayList不是线程安全的。
- ArratList性能高于Vector。
- ArrayList扩容每次会增加50%,Vector每次增加一倍。
2.28: Array 和 ArrayList 有何区别?
- Array可以存储基本数据类型和对象,ArrayList只能存储对象
- Array是固定大小,ArrayList可以自动扩容
- Array内置方法没ArrayList多,例如addAll,removeAll,iteration等
2.29: 在 Queue 中 poll () 和 remove () 有什么区别?
- 都是返回第一个元素,并在队列中删除返回的对象
- 如果没有元素poll()会返回null,而remove()会直接抛出NoSuchElementException异常
2.30: 哪些集合类是线程安全的?
Vector,HashTable,Stack都是线程安全的,HashMap在JDK1.5后有了对应的线程安全类,ConcurrentHashMap。
2.31: 迭代器iterator是什么?
Iterator接口提供了遍历任何Collection的接口。我们可以从一个Collection中使用迭代器方法来获取迭代器的实例。迭代器取代了Java集合框架中的Enumeration,迭代器允许调用者在迭代过程中移除元素。
2.32: iterator怎么使用,有什么特点?
List list = new ArrayList<>();
Iterator it = list. iterator();
while(it. hasNext()){
String obj = it. next();
System. out. println(obj);
}
Iterator 的特点是更加安全,因为它可以确保,在当前遍历的集合元素被更改的时候,就会抛出ConcurrentModificationException 异常。
2.33: iterator和ListIterator有什么区别?
- Iterator可以遍历List和Set集合,而ListIterator只能遍历List集合
- Iterator只能单向遍历,ListIterator可以进行双向遍历(向前/向后遍历)
- ListIterator从Iterator接口继承,然后添加了一些额外的功能,比如添加或替换一个元素
2.34: 怎么确保一个集合不能被修改?
可以使用Collections.unmodifiableCollection(Collection c)方法来创建一个只读集合,这样改变集合的任何操作都会抛出 Java. lang. UnsupportedOperationException 异常。
3.多线程
3.35: 并行和并发有什么区别?
- 并行:多个处理器或多核任务处理器同时处理多个任务
- 并发:多个任务在同一个CPU上,按细分的时间片段轮流(交替)执行,从逻辑上来看,这些任务是同时执行的
并发 = 两个队列和一台咖啡机。
并行 = 两个队列和两台咖啡机。
3.36:线程和进程的区别?
一个程序下至少有一个进程,一个进程下至少有一个线程,一个线程下也可以有多个进程来加快执行的速度。
3.37:守护线程是什么?
守护线程是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。在 Java 中垃圾回收线程就是特殊的守护线程。
3.38:创建线程有哪几种方式?
- 继承Thread重写run 方法
- 实现Runnable 接口
- 实现Callable接口
3.39:说一下Runnable和callable的区别?
runnable没有返回值,callable有返回值并且可以拿到返回值,callable可以看作是runnable的补充。
3.40:线程有哪些状态?
- NEW 尚未启动
- RUNNABLE 正在执行中
- BLOCKED 阻塞的(同步锁或IO锁阻塞)
- WAITING 永久等待状态
- TIMED_WAITING 等待指定的时间重新被唤醒的状态
- TERMINATED 执行完成
3.41:sleep()和wait()有什么区别?
- sleep()来自Thread,wait()来自Object
- sleep()不释放锁,wait()释放锁
- sleep()到时间会自动恢复,wait()可以使用notify()/notifyAll()直接唤醒
3.42:notify()和notifyAll()有什么区别?
notifyAll()会唤醒所有的线程,notify()唤醒一个线程。notifyAll()调用后,所有的线程会从等待池中转移到锁池,然后参与锁池中的竞争,成功后可以继续执行,不成功继续竞争。notify()只会唤醒一个线程,有虚拟机控制唤醒那个线程。
3.43:线程中的run()和start()有什么区别?
start()方法用于启动线程,run()方法用于执行线程的运行时代码。run()可以重复调用,start()只能调用一次。
3.44:创建线程池有哪些方式?
线程池创建有七种方式,最核心的是最后一种:
- newSingleThreadExecutor ():它的特点在于工作线程数目被限制为 1,操作一个无界的工作队列,所以它保证了所有任务的都是被顺序执行,最多会有一个任务处于活动状态,并且不允许使用者改动线程池实例,因此可以避免其改变线程数目;
- newCachedThreadPool ():它是一种用来处理大量短时间工作任务的线程池,具有几个鲜明特点:它会试图缓存线程并重用,当无缓存线程可用时,就会创建新的工作线程;如果线程闲置的时间超过 60 秒,则被终止并移出缓存;长时间闲置时,这种线程池,不会消耗什么资源。其内部使用 SynchronousQueue 作为工作队列;
- newFixedThreadPool (int nThreads):重用指定数目(nThreads)的线程,其背后使用的是无界的工作队列,任何时候最多有 nThreads 个工作线程是活动的。这意味着,如果任务数量超过了活动队列数目,将在工作队列中等待空闲线程出现;如果有工作线程退出,将会有新的工作线程被创建,以补足指定的数目 nThreads;
- newSingleThreadScheduledExecutor ():创建单线程池,返回 ScheduledExecutorService,可以进行定时或周期性的工作调度;
- newScheduledThreadPool (int corePoolSize):和 newSingleThreadScheduledExecutor () 类似,创建的是个 ScheduledExecutorService,可以进行定时或周期性的工作调度,区别在于单一工作线程还是多个工作线程;
- newWorkStealingPool (int parallelism):这是一个经常被人忽略的线程池,Java 8 才加入这个创建方法,其内部会构建 ForkJoinPool,利用 Work-Stealing 算法,并行地处理任务,不保证处理顺序;
- ThreadPoolExecutor ():是最原始的线程池创建,上面 1-3 创建方式都是对 ThreadPoolExecutor 的封装。
3.45:线程池有哪些状态?
- RUNNING:接受新的任务,处理等待队列中的任务
- SHUTDOWN:不接受新的任务提交,但是会继续处理等待队列中的任务
- STOP:不接受新的任务提交,也不处理等待队列中的任务,中断正在执行任务的线程
- TIDYING:所有的任务销毁,workCount为0,线程池的状态在转换为 TIDYING 状态时,会执行钩子方法 terminated ()。
- TERMINATED:terminated()方法执行结束后,线程池的状态就会变成这个
3.46:线程池中submit()和execute()方法有什么区别?
- execute():只能执行Runnable类型的任务
- submit():可以执行Runnale和Callable类型的任务
Callable类型的任务可以获取执行的返回值,Runable执行无返回值
3.47:在Java程序中怎么保证多线程的运行安全?
- 使用安全类,不如java.util.concurrent下的类
- 使用自动锁synchronized
- 使用手动锁Lock
手动锁 Java 示例代码如下:
Lock lock = new ReentrantLock();
lock. lock();
try {
System. out. println("获得锁");
} catch (Exception e) {
// TODO: handle exception
} finally {
System. out. println("释放锁");
lock. unlock();
}
3.48:多线程中synchronized锁升级的原理是什么?
- 在锁对象的对象头里面有一个 threadid 字段,在第一次访问的时候 threadid 为空,jvm 让其持有偏向锁,并将 threadid 设置为其线程 id,再次进入的时候会先判断 threadid 是否与其线程 id 一致,如果一致则可以直接使用此对象,如果不一致,则升级偏向锁为轻量级锁,通过自旋循环一定次数来获取锁,执行一定次数之后,如果还没有正常获取到要使用的对象,此时就会把锁从轻量级升级为重量级锁,此过程就构成了 synchronized 锁的升级。
- 锁的升级的目的:锁升级是为了减低了锁带来的性能消耗。在 Java 6 之后优化 synchronized 的实现方式,使用了偏向锁升级为轻量级锁再升级到重量级锁的方式,从而减低了锁带来的性能消耗。
3.49:死锁是什么?
当程序A持有独占锁a,并尝试获取锁b的同时,线程 B 持有独占锁 b,并尝试获取独占锁 a 的情况下,就会发生 AB 两个线程由于互相持有对方需要的锁,而发生的阻塞现象,我们称为死锁。
3.50:怎么防止死锁?
- 尽量使用 tryLock (long timeout, TimeUnit unit) 的方法 (ReentrantLock、ReentrantReadWriteLock),设置超时时间,超时可以退出防止死锁。
- 尽量使用 Java. util. concurrent 并发类代替自己手写锁。
- 尽量降低锁的使用粒度,尽量不要几个功能用同一把锁。
- 尽量减少同步的代码块。
3.51:ThreadLocal 是什么?有哪些使用场景?
ThreadLocal为每一个使用该变量的线程提供一个独立的变量副本,所以每个线程都可以独立的改变自己的副本,而不影响其他的线程所对应的副本。
ThreadLocal 的经典使用场景是数据库连接和 session 管理等。
3.52:说一下 synchronized 底层实现原理?
synchronized 是由一对 monitorenter/monitorexit 指令实现的,monitor 对象是同步的基本实现单元。在 Java 6 之前,monitor 的实现完全是依靠操作系统内部的互斥锁,因为需要进行用户态到内核态的切换,所以同步操作是一个无差别的重量级操作,性能也很低。但在 Java 6 的时候,Java 虚拟机 对此进行了大刀阔斧地改进,提供了三种不同的 monitor 实现,也就是常说的三种不同的锁:偏向锁(Biased Locking)、轻量级锁和重量级锁,大大改进了其性能。
3.53:synchronized 和 volatile 的区别是什么?
- volatile 是变量修饰符;synchronized 是修饰类、方法、代码段。
- volatile 仅能实现变量的修改可见性,不能保证原子性;而 synchronized 则可以保证变量的修改可见性和原子性。
- volatile 不会造成线程的阻塞;synchronized 可能会造成线程的阻塞。
3.54: synchronized 和 Lock 有什么区别?
- synchronized 可以给类、方法、代码块加锁;而 lock 只能给代码块加锁。
- synchronized 不需要手动获取锁和释放锁,使用简单,发生异常会自动释放锁,不会造成死锁;而 lock 需要自己加锁和释放锁,如果使用不当没有 unLock () 去释放锁就会造成死锁。
- 通过 Lock 可以知道有没有成功获取锁,而 synchronized 却无法办到。
3.55: synchronized 和 ReentrantLock 区别是什么?
synchronized 早期的实现比较低效,对比 ReentrantLock,大多数场景性能都相差较大,但是在 Java 6 中对 synchronized 进行了非常多的改进。
主要区别如下:
- ReentrantLock 使用起来比较灵活,但是必须有释放锁的配合动作;
- ReentrantLock 必须手动获取与释放锁,而 synchronized 不需要手动释放和开启锁;
- ReentrantLock 只适用于代码块锁,而 synchronized 可用于修饰方法、代码块等。
3.56: 说一下 atomic 的原理?
atomic 主要利用 CAS (Compare And Wwap) 和 volatile 和 native 方法来保证原子操作,从而避免 synchronized 的高开销,执行效率大为提升。
4.反射
4.57: 什么是反射?
反射是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为 Java 语言的反射机制。
4.58: 什么是 Java 序列化?什么情况下需要序列化?
Java 序列化是为了保存各种对象在内存中的状态,并且可以把保存的对象状态再读出来。
以下情况需要使用 Java 序列化:
- 想把的内存中的对象状态保存到一个文件中或者数据库中时候;
- 想用套接字在网络上传送对象的时候;
- 想通过 RMI(远程方法调用)传输对象的时候。
4.59: 动态代理是什么?有哪些应用?
动态代理是运行时动态生成代理类。
动态代理的应用有 spring aop、hibernate 数据查询、测试框架的后端 mock、rpc,Java 注解对象获取等。
4.60: 怎么实现动态代理?
JDK 原生动态代理和 cglib 动态代理。JDK 原生动态代理是基于接口实现的,而 cglib 是基于继承当前类的子类实现的。
5.对象拷贝
5.61:为什么要使用克隆?
克隆的对象包含一些已经修好过的属性,而new出来的对象的属性都还是初始化时候的值,所以当需要一个新的对象来保存当前的对象的“状态”就靠克隆方法了。
5.62:如何实现对象克隆?
- 实现Cloneable接口并重写object类中的clone()方法
- 实现Serializable接口,通过对象的序列化和反序列化实现克隆,可以实现真正的深度克隆
5.63:深拷贝和浅拷贝的区别是什么?
- 浅克隆:当对象被复制时只复制它本身和其中包含的值类型的成员变量,而引用类型的成员对象没有被复制
- 深克隆:除了对象本身被复制之外,对象所包含的所有成员变量也将复制
6.Java Web
6.64:JSP和Servlet有什么区别?
JSP是Servlet技术的扩展,本质上就是servlet的简易方式。servlet和JSP最主要的不同点在于,servlet的应用逻辑是在Java文件中,并且完全从表示层中的html中分离开来,而JSP的情况是Java和html可以组合成一个扩展名为JSP的文件。JSP侧重于视图,servlet主要用于控制逻辑。
6.65:JSP有哪些内置对象?作用分别是什么?
JSP有9大内置对象:
- request:封装客户端的请求,其中包含来自get和post请求的参数
- response:封装服务器对客户端的响应
- pageContext:通过该对象可以获取到其他对象
- session:封装用户会话的对象
- application:封装服务器运行环境的对象
- out:输出服务器响应的输出流对象
- config:web应用的配置对象
- page:JSP页面本身(相当于Java中的this)
- exception:封装页面抛出异常的对象
6.66:说一下JSP的4种作用域?
- page:代表一个与页面相关的对象和属性
- request:代表与客户端发出的一个请求相关的对象和属性。一个请求可能跨越多个页面,涉及多个Web组件,需要在页面显示的临时数据可以置于此作用域
- session:代表与某个用户与服务器建立的一次会话相关的对象和属性。跟某个用户相关的数据应该放在用户自己的session中
- application:代表与整个Web应用程序相关的对象和属性,它实质上是跨越整个Web应用程序,包括多个页面,请求和会话的一个全局作用域
6.67:session和cookie有什么区别?
- session存储在服务器端;cookie存储在浏览器端
- cookie安全性一般,在浏览器中存储,可以被伪造和修改
- cookie有容量限制,每个站点下的cookie也有个数限制
- session可以存储在Redis中,数据库中,应用程序中,cookie只能存储在浏览器中
6.68:说一下session的工作原理?
session的工作原理是客户端登录完成之后,服务器会创建对应的session,session创建完成之后,会将session的id发送给客户端,客户端再存储在浏览器中。这样客户端每次访问服务器时,都会带着sessionid,服务器拿到sessionid之后,在内存找到与之对应的session这样就可以正常工作了。
6.69:如果客户端禁止cookie实现session还能用吗?
可以用,session只是依赖cookie存储sessionid,如果cookie被禁用了,可以使用url中添加sessionid的方式保证session能正常使用。
6.70:spring mvc 和 struts 的区别是什么?
- 拦截级别:struts2 是类级别的拦截;spring mvc 是方法级别的拦截。
- 数据独立性:spring mvc 的方法之间基本上独立的,独享 request 和 response 数据,请求数据通过参数获取,处理结果通过 ModelMap 交回给框架,方法之间不共享变量;而 struts2 虽然方法之间也是独立的,但其所有 action 变量是共享的,这不会影响程序运行,却给我们编码和读程序时带来了一定的麻烦。
- 拦截机制:struts2 有以自己的 interceptor 机制,spring mvc 用的是独立的 aop 方式,这样导致 struts2 的配置文件量比 spring mvc 大。
- 对 ajax 的支持:spring mvc 集成了 ajax,所有 ajax 使用很方便,只需要一个注解 @ResponseBody 就可以实现了;而 struts2 一般需要安装插件或者自己写代码才行。
自己总结的,有什么错误的地方或者好的建议欢迎大家补充。