(1)Java基础
1.JDK 和 JRE 有什么区别?
JDK:Java 开发工具包, 提供了 Java 的开发环境和运行环境。
JRE:Java 运行环境, 为 Java 的运行提供了所需环境。
2.== 和 equals 的区别是什么?
==运算符比较的是两个引用是否指向相同的对象(即同一内存空间),也就是说在内存空间中的存储位置是否一致。
equals是判断两个变量或者实例指向同一个内存空间的值是不是相同
3. 两个对象的 hashCode() 相同,则 equals() 也一定为 true,对吗?
不对, 两个对象的 hashCode() 相同, equals() 不一定 true。
4. final 在 Java 中有什么作用?
final 修饰的类叫最终类, 该类不能被继承。
final 修饰的方法不能被重写。
final 修饰的变量叫常量, 常量必须初始化,初始化之后值就不能被 修改。
5. Java 中的 Math. round(-1. 5) 等于多少?
等于 -1。round()是四舍五入,注意负数5是舍的, 例如:Math.round(1.5)值是2,Math.round(-1.5)值是-1。
6. String 属于基础的数据类型吗?
String 不 属 于 基 础 类 型 , 基 础 类 型 有 8 种 : byte 、 boolean 、 char 、 short 、 int 、 float 、 long 、 double , 而 String 属于对象。
7. Java 中操作字符串都有哪些类?它们之间有什么区别?
操作字符串的类有:String、StringBuffer、StringBuilder。
区别一:
String是不可变字符串
StringBuffer和StringBuilder是可变字符串。
区别二:
StringBuffer是线程安全的,它的方法是支持线程同步,线程同步会操作串行顺序执行,在单线程环境 下会影响效率。
StringBuilder是StringBuffer单线程版本,它不是线程安全的,但它 的执行效率很高。
运行速度快慢为:StringBuilder > StringBuffer > String
8. String str="i"与 String str=new String(“i”)一样吗?
不一样, 因为内存的分配方式不一样。 String str=" i" 的方式, Java 虚拟 机会将其分配到常量池中;而 String str=new String(" i" ) 则会被分到堆 内存中。
9. 如何将字符串反转?
使用 StringBuilder 或者 stringBuffer 的 reverse() 方法。
10. String 类的常用方法都有那些?
indexOf():返回指定字符的索引。
charAt():返回指定索引处的字符。
replace():字符串替换。
trim():去除字符串两端空白。
split():分割字符串, 返回一个分割后的字符串数组。
getBytes():返回字符串的 byte 类型数组。
length():返回字符串长度。
toLowerCase():将字符串转成小写字母。
toUpperCase():将字符串转成大写字符。
substring():截取字符串。
equals():字符串比较。
11. 抽象类必须要有抽象方法吗?
不需要,抽象类不一定非要有抽象方法;但是包含一个抽象方法的类一定是抽象类。
12. 普通类和抽象类有哪些区别?
普通类不能包含抽象方法, 抽象类可以包含抽象方法。
抽象类不能直接实例化, 普通类可以直接实例化。
如果一个类继承于抽象类,则该子类必须实现父类的抽象方法。如果子类没有实现父类的抽象方法,则必须将子类也定义为abstract类。
13. 抽象类能使用 final 修饰吗?
不能, 定义抽象类就是让其他类继承的, 如果定义为 final 该类就不能被 继承, 这样彼此就会产生矛盾, 所以 final 不能修饰抽象类。
14. 接口和抽象类有什么区别?
实 现 : 抽 象 类 的 子 类 使 用 extends 来 继 承 ; 接 口 必 须 使 用 implements 来实现接口。
构造函数:抽象类可以有构造函数;接口不能有。
实现数量:接口支持多继承,而抽象类(包括具体类)只能继承一个父类。
成员变量:接口中不能有实例成员变量,接口所声明的成员变量全部是静态常量,抽象类与普通类一样各种形式的成员变量都可以声明。
15. Java 中 IO 流分为几种?
按功能来分:输入流(input)、输出流(output)。
按类型来分:字节流和字符流。
字节流和字符流的区别是:字节流按 8 位传输以字节为单位输入输出数据, 字符流按 16 位传输以字符为单位输入输出数据。
16. BIO、NIO、AIO 有什么区别?
BIO:Block IO 同步阻塞式 IO, 就是我们平常使用的传统 IO, 它 的特点是模式简单使用方便, 并发处理能力低。
NIO:Non IO 同步非阻塞 IO, 是传统 IO 的升级, 客户端和服务 器端通过 Channel(通道)通讯, 实现了多路复用。
AIO:Asynchronous IO 是 NIO 的升级, 也叫 NIO2, 实现了异 步非堵塞 IO , 异步 IO 的操作基于事件和回调机制。
17. Files 的常用方法都有哪些?
Files. exists():检测文件路径是否存在。
Files. createFile():创建文件。
Files. createDirectory():创建文件夹。
Files. delete():删除一个文件或目录。
Files. copy():复制文件。
Files. move():移动文件。
Files. size():查看文件个数。
Files. read():读取文件。
Files. write():写入文件。
(2)容器
18. Java 容器都有哪些?
Java 容器分为 Collection 和 Map 两大类, 其下又有很多子类, 如下所 示:
Collection
List
ArrayList
LinkedList
Vector
Stack
Set
HashSet
LinkedHashSet
TreeSet
Map
HashMap
LinkedHashMap
TreeMap
ConcurrentHashMap
Hashtable
19. Collection 和 Collections 有什么区别?
Collection 是一个集合接口, 它提供了对集合对象进行基本操作的 通用接口方法, 所有集合都是它的子类, 比如 List、Set 等。
Collections 是一个包装类, 包含了很多静态方法, 不能被实例化, 就像一个工具类, 比如提供的排序方法: Collections. sort(list)。
20. List、Set、Map 之间的区别是什么?
List:有序、允许重复
Set:无序、不允许重复
Map:key是set类型,不允许重复、value是Collection类型,可以重复
21. HashMap 和 Hashtable 有什么区别?
HashMap是继承自AbstractMap类,而HashTable是继承自Dictionary类
存储:HashMap的key-value支持key-value,null-null,key-null,null-value四种。而Hashtable只支持key-value一种
线程安全:Hashtable 是线程安全的, 而 HashMap 是非线程安全 的。
Hashtable比HashMap多提供了elments() 和contains() 两个方法。
22. 如何决定使用 HashMap 还是 TreeMap?
对于在 Map 中插入、删除、定位一个元素这类操作,选HashMap
要对一个 key 集合进行有序的遍历,选TreeMap
23. 说一下 HashMap 的实现原理?
HashMap 基 于 Hash 算 法 实 现 的 , 我 们 通 过 put(key,value) 存 储 , get(key)来获取。 当传入 key 时, HashMap 会根据 key. hashCode() 计算出 hash 值, 根据 hash 值将 value 保存在 bucket 里。 当计算出的 hash 值相同时, 我们称之为 hash 冲突, HashMap 的做法是用链表和 红黑树存储相同 hash 值的 value。 当 hash 冲突的个数比较少时, 使用 链表否则使用红黑树。
24. 说一下 HashSet 的实现原理?
HashSet 是基于 HashMap 实现的, HashSet 底层使用 HashMap 来 保存所有元素, 因此 HashSet 的实现比较简单, 相关 HashSet 的操作, 基本上都是直接调用底层 HashMap 的相关方法来完成, HashSet 不允 许重复的值。
25. ArrayList 和 LinkedList 的区别是什么?
数 据 结 构 实 现 : ArrayList 是 动 态 数 组 的 数 据 结 构 实 现 , 而 LinkedList是双向链表的数据结构实现。
随机访问效率:ArrayList 比 LinkedList 在随机访问的时候效率要 高, 因为LinkedList 是线性的数据存储方式, 所以需要移动指针从 前往后依次查找。
增加和删除效率:在非首尾的增加和删除操作,
LinkedList 要比 ArrayList 效率要高, 因为 ArrayList 增删操作要影响数组内的其他 数据的下标。
26. 如何实现数组和 List 之间的转换?
数组转 List:使用 Arrays. asList(array) 进行转换。
List 转数组:使用 List 自带的toArray() 方法。
27. ArrayList 和 Vector 的区别是什么?
线程安全:Vector 使用了 Synchronized 来实现线程同步, 是线 程安全的, 而 ArrayList 是非线程安全的。
性能:ArrayList 在性能方面要优于 Vector。
扩容:ArrayList 和 Vector 都会根据实际的需要动态的调整容量,只不过在 Vector 扩容每次会增加 1 倍, 而 ArrayList 只会增加 50%。
28. Array 和 ArrayList 有何区别?
Array 可以存储基本数据类型和对象, ArrayList 只能存储对象。
Array 是指定固定大小的, 而 ArrayList大小是自动扩展的。
Array 内 置 方 法 没 有 ArrayList 多 , 比如addAll、removeAll、iteration 等方法只有 ArrayList 有。
29. 在 Queue 中 poll()和 remove()有什么区别?
相同点:都是返回第一个元素, 并在队列中删除返回的对象。
不同点:如果没有元素 poll()会返回 null, 而
remove()会直接抛 出 NoSuchElementException 异常。
30. 哪些集合类是线程安全的?
Vector、Hashtable、Stack 都是线程安全的
31. 迭代器 Iterator 是什么?
Iterator 接 口 提 供 遍 历 任 何 Collection 的 接 口 。 我 们 可 以 从 一 个 Collection 中使用迭代器方法来获取迭代器实例。 迭代器取代了 Java 集 合框架中的 Enumeration, 迭代器允许调用者在迭代过程中移除元素。
32. Iterator 怎么使用?有什么特点?
Iterator 使用代码如下:
List<String> list = new ArrayList<>(); Iterator<String> it = list. iterator(); while(it. hasNext()){ String obj = it. next(); System. out.println(obj); }
Iterator 的特点是更加安全, 因为它可以确保, 在当前遍历的集合元素被 更改的时候, 就会抛出 ConcurrentModificationException 异常。
33. Iterator 和 ListIterator 有什么区别?
Iterator 可以遍历 Set 和 List 集合, 而 ListIterator 只能遍历 List。
Iterator 只能单向遍历, 而 ListIterator 可以双向遍历(向前/后 遍历)。
ListIterator 从 Iterator 接口继承, 然后添加了一些额外的功能, 比如添加一个元素、替换一个元素、获取前面或后面元素的索引位置。
34. 怎么确保一个集合不能被修改?
可以使用 Collections. unmodifiableCollection(Collection c) 方法来 创 建 一 个 只 读 集 合 , 这 样 改 变 集 合 的 任 何 操 作 都 会 抛 出 Java. lang. UnsupportedOperationException 异常。
(3)多线程
35. 并行和并发有什么区别?
并行:多个处理器或多核处理器同时处理多个任务。
并发:多个任务在同一个 CPU 核上, 按细分的时间片轮流(交替)执 行, 从逻辑上来看那些任务是同时执行。
并发 = 两个队列和一台咖啡机。
并行 = 两个队列和两台咖啡机。
36. 线程和进程的区别?
一个程序下至少有一个进程, 一个进程下至少有一个线程, 一个进程下也 可以有多个线程来增加程序的执行速度。
37. 守护线程是什么?
守护线程是运行在后台的一种特殊进程。 它独立于控制终端并且周期性地 执行某种任务或等待处理某些发生的事件。 在 Java 中垃圾回收线程就是 特殊的守护线程。
38. 创建线程有哪几种方式?
继承Thread类
实现Runnable接口
实现Callable接口通过FutureTask包装器来创建Thread线程
通过线程池创建线程,使用线程池接口ExecutorService结合Callable、Future实现有返回结果的多线程。
39. 说一下 runnable 和 callable 有什么区别?
runnable 没有返回值, callable 可以拿到有返回值, callable 可以看作 是 runnable 的补充。
40. 线程有哪些状态?
NEW 尚未启动
RUNNABLE 正在执行中
BLOCKED 阻塞的(被同步锁或者 IO 锁阻塞)
WAITING 永久等待状态
TIMED_WAITING 等待指定的时间重新被唤醒的状态
TERMINATED 执行完成
41. sleep() 和 wait() 有什么区别?
类的不同:sleep() 来自 Thread, wait() 来自 Object。
释放锁:sleep() 不释放锁;wait() 释放锁。
用法不同:sleep() 时间到会自动恢复;wait() 可以使用 notify()/ notifyAll()直接唤醒。
42. notify()和 notifyAll()有什么区别?
notifyAll()会唤醒所有的线程, notify()之后唤醒一个线程。 notifyAll() 调用后, 会将全部线程由等待池移到锁池, 然后参与锁的竞争, 竞争成功 则继续执行, 如果不成功则留在锁池等待锁被释放后再次参与竞争。 而 notify()只会唤醒一个线程, 具体唤醒哪一个线程由虚拟机控制。
43. 线程的 run() 和 start() 有什么区别?
start() 方法用于启动线程, run() 方法用于执行线程的运行时代码。 run() 可以重复调用, 而 start() 只能调用一次。
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 的封装。
45. 线程池都有哪些状态?
RUNNING:这是最正常的状态, 接受新的任务, 处理等待队列中 的任务。
SHUTDOWN:不接受新的任务提交, 但是会继续处理等待队列中 的任务。
STOP:不接受新的任务提交, 不再处理等待队列中的任务, 中断正 在执行任务的线程。
TIDYING:所有的任务都销毁了, workCount 为 0, 线程池的状 态在转换为 TIDYING 状态时, 会执行钩子方法 terminated()。
TERMINATED:terminated()方法结束后, 线程池的状态就会变 成这个。
46. 线程池中 submit() 和 execute() 方法有什么区别?
execute():只能执行 Runnable 类型的任务。
submit():可以执行 Runnable 和 Callable 类型的任务。
Callable 类型的任务可以获取执行的返回值, 而 Runnable 执行无返回 值。
47. 在 Java 程序中怎么保证多线程的运行安全?
方法一:使用安全类, 比如 Java. util. concurrent 下的类。
方法二:使用自动锁 synchronized。
方法三:使用手动锁 Lock。
48. 多线程中 synchronized 锁升级的原理是什么?
synchronized 锁升级原理:在锁对象的对象头里面有一个 threadid 字 段, 在第一次访问的时候 threadid 为空, jvm 让其持有偏向锁, 并将 threadid 设置为其线程 id, 再次进入的时候会先判断 threadid 是否与 其线程 id 一致, 如果一致则可以直接使用此对象, 如果不一致, 则升级 偏向锁为轻量级锁, 通过自旋循环一定次数来获取锁, 执行一定次数之后, 如果还没有正常获取到要使用的对象, 此时就会把锁从轻量级升级为重量 级锁, 此过程就构成了 synchronized 锁的升级。
锁的升级的目的:锁升级是为了减低了锁带来的性能消耗。
49. 什么是死锁?
当线程 A 持有独占锁 a, 并尝试去获取独占锁 b 的同时, 线程 B 持有独 占锁 b, 并尝试获取独占锁 a 的情况下, 就会发生 AB 两个线程由于互相 持有对方需要的锁, 而发生的阻塞现象, 我们称为死锁。
50. 怎么防止死锁?
尽 量 使 用 tryLock(long timeout, TimeUnit unit) 的 方 法 (ReentrantLock、ReentrantReadWriteLock) , 设置超时时间, 超时可以退出防止死锁。
尽量使用 Java. util. concurrent 并发类代替自己手写锁。
尽量降低锁的使用粒度, 尽量不要几个功能用同一把锁。
尽量减少同步的代码块。
51. ThreadLocal 是什么?有哪些使用场景?
ThreadLocal 为每个使用该变量的线程提供独立的变量副本, 所以每一个 线程都可以独立地改变自己的副本, 而不会影响其它线程所对应的副本。
ThreadLocal 的经典使用场景是数据库连接和 session 管理等。
52. 说一下 synchronized 底层实现原理?
synchronized 是 由 一 对 monitorenter/monitorexit 指 令 实 现 的 , monitor 对象是同步的基本实现单元。 在 Java 6 之前, monitor 的实现 完全是依靠操作系统内部的互斥锁, 因为需要进行用户态到内核态的切换, 所以同步操作是一个无差别的重量级操作, 性能也很低。 但在 Java 6 的 时 候 , Java 虚 拟 机 对 此 进 行 了 大 刀 阔 斧 地 改 进 , 提 供 了 三 种 不 同 的 monitor 实 现 , 也 就 是 常 说 的 三 种 不 同 的 锁 : 偏 向 锁 ( Biased Locking)、轻量级锁和重量级锁, 大大改进了其性能。
53. synchronized 和 volatile 的区别是什么?
volatile 是变量修饰符;synchronized 是修饰类、方法、代码段。
volatile 仅 能 实 现 变 量 的 修 改 可 见 性 , 不 能 保 证 原 子 性 ; 而 synchronized 则可以保证变量的修改可见性和原子性。
volatile 不会造成线程的阻塞;synchronized 可能会造成线程的 阻塞。
54. synchronized 和 Lock 有什么区别?
synchronized 可以给类、方法、代码块加锁;而 lock 只能给代码 块加锁。
synchronized 不需要手动获取锁和释放锁, 使用简单, 发生异常 会自动释放锁, 不会造成死锁;而 lock 需要自己加锁和释放锁, 如 果使用不当没有 unLock()去释放锁就会造成死锁。
通过 Lock 可以知道有没有成功获取锁, 而 synchronized 却无法 办到。
55. synchronized 和 ReentrantLock 区别是什么?
ReentrantLock 使用起来比较灵活, 但是必须有释放锁的配合动作;
ReentrantLock 必须手动获取与释放锁, 而 synchronized 不需 要手动释放和开启锁;
ReentrantLock 只适用于代码块锁, 而 synchronized 可用于修 饰方法、代码块等。
volatile 标记的变量不会被编译器优化;synchronized 标记的变 量可以被编译器优化。
56. 说一下 atomic 的原理?
atomic 主要利用 CAS (Compare And Wwap) 和 volatile 和 native 方法来保证原子操作, 从而避免 synchronized 的高开销, 执行效率大为 提升。
(4)反射
57. 什么是反射?
反射是在运行状态中, 对于任意一个类, 都能够知道这个类的所有属性和 方法;对于任意一个对象, 都能够调用它的任意一个方法和属性;这种动 态获取的信息以及动态调用对象的方法的功能称为 Java 语言的反射机制。
58. 什么是 Java 序列化?什么情况下需要序列化?
Java 序列化是为了保存各种对象在内存中的状态, 并且可以把保存的对象 状态再读出来。
以下情况需要使用 Java 序列化:
想把的内存中的对象状态保存到一个文件中或者数据库中时候;
想用套接字在网络上传送对象的时候;
想通过 RMI(远程方法调用)传输对象的时候。
59. 为什么要使用克隆?
克隆的对象可能包含一些已经修改过的属性, 而 new 出来的对象的属性 都还是初始化时候的值, 所以当需要一个新的对象来保存当前对象的“状 态”就靠克隆方法了。
60. 如何实现对象克隆?
实现 Cloneable 接口并重写 Object 类中的 clone() 方法。
实现 Serializable 接口, 通过对象的序列化和反序列化实现克隆, 可以实现真正的深度克隆。