一、Java基础
1、谈谈你对Java平台的理解?【第1讲】
答:
- 1、相较C语言,Java面向对象,有GC(垃圾收集),不用关心内存分配和回收;
- 2、JVM(跨平台)、JRE(Java基础运行环境)、JDK(Java开发工具包)
1)JVM + 【Java类库、某些模块等】= JRE
2)JRE + 【编译器、诊断等工具】 = JDK
- 3、拓展
……
追问1:面向过程和面向对象有什么区别?
角度 | 面向过程 | 面向对象 |
---|---|---|
特点 | 以分步骤解决问题,用方法组织代码 | 以分类解决问题,用类组织代码。 |
优点 | 性能好 | 工程更加模块化,实现更低耦合和高内聚 |
缺点 | 适应性差、可拓展性与可维护性差 | 类调用需要实例化,开销大 |
特征 | 步骤 | 封装、继承、多态 |
关系:细节用面向过程解决,整体面向对象把控,相辅相成。
追问3:Java和Python的区别
角度 | Java | Python |
---|---|---|
类型 | 静态类型,变通实现运行时修改 | 全动态 |
语法 | 面向对象 | 结合函数式编程与面向对象 |
开发周期 | 慢 | 有丰富库,快 |
执行效率 | 性能稳定,扩展性好 | 较差 |
领域 | 服务器、Web、安卓 | 脚本处理、AI、图形图像 |
追问5:“Java 是解释执行”,这个说法正确吗?
答:不太准确。 Java 的源代码首先通过 Javac 编译成为字节码(bytecode),在运行时,JVM内嵌的解释器将字节码转换成为最终的机器码。
但常见 JVM(如 Hotspot JVM)都提供了 JIT(Just-In-Time)编译器,即动态编译器,能在运行时将热点代码编译成机器码,这种情况下部分热点代码就属于编译执行,而不是解释执行了。
2、Java提供了哪些IO方式(*2)? NIO如何实现多路复用?【第11讲】
Java IO 方式有很多种,基于不同的 IO 抽象模型和交互方式,可以进行简单区分。
首先,传统的 java.io 包,它基于流模型实现,提供了我们最熟知的一些 IO 功能,比如 File 抽象、输入输出流等。交互方式是同步、阻塞的方式,也就是说,在读取输入流或者写入输出流时,在读、写动作完成之前,线程会一直阻塞在那里,它们之间的调用是可靠的线性顺序。
java.io 包的好处是代码比较简单、直观,缺点则是 IO 效率和扩展性存在局限性,容易成为应用性能的瓶颈。
很多时候,人们也把 java.net 下面提供的部分网络 API,比如 Socket、ServerSocket、HttpURLConnection 也归类到同步阻塞 IO 类库,因为网络通信同样是 IO 行为。
第二,在 Java 1.4 中引入了 NIO 框架(java.nio 包),提供了 Channel、Selector、Buffer 等新的抽象,可以构建多路复用的、同步非阻塞 IO 程序,同时提供了更接近操作系统底层的高性能数据操作方式。
第三,在 Java 7 中,NIO 有了进一步的改进,也就是 NIO 2,引入了异步非阻塞 IO 方式,也有很多人叫它 AIO(Asynchronous IO)。异步 IO 操作基于事件和回调机制,可以简单理解为,应用操作直接返回,而不会阻塞在那里,当后台处理完成,操作系统会通知相应线程进行后续工作。
3、Java有几种文件拷贝方式?哪一种最高效?【第12讲】
Java 有多种比较典型的文件拷贝实现方式,比如:
利用 java.io 类库,直接为源文件构建一个 FileInputStream 读取,然后再为目标文件构建一个 FileOutputStream,完成写入工作。
public static void copyFileByStream(File source, File dest) throws
IOException {
try (InputStream is = new FileInputStream(source);
OutputStream os = new FileOutputStream(dest);){
byte[] buffer = new byte[1024];
int length;
while ((length = is.read(buffer)) > 0) {
os.write(buffer, 0, length);
}
}
}
或者,利用 java.nio 类库提供的 transferTo 或 transferFrom 方法实现。
public static void copyFileByChannel(File source, File dest) throws
IOException {
try (FileChannel sourceChannel = new FileInputStream(source)
.getChannel();
FileChannel targetChannel = new FileOutputStream(dest).getChannel
();){
for (long count = sourceChannel.size() ;count>0 ;) {
long transferred = sourceChannel.transferTo(
sourceChannel.position(), count, targetChannel); sourceChannel.position(sourceChannel.position() + transferred);
count -= transferred;
}
}
}
当然,Java 标准类库本身已经提供了几种 Files.copy 的实现。
对于 Copy 的效率,这个其实与操作系统和配置等情况相关,总体上来说,NIO transferTo/From 的方式可能更快,因为它更能利用现代操作系统底层机制,避免不必要拷贝和上下文切换。
4、写正则表达式进行手机号匹配(*2)
# 号码规则:
1、第 1 位: 1
2、第 2 位: 3~9
3、第 3 到第 11 位只要是数字就行.
答案: /^[1]([3-9])[0-9]{9}$/
(1) /^ - 表示文本开始;
(2) () - 子表达式的开始与结束;
(3) [] - 要匹配里面内容,[0-9]匹配数字范围为0/1/2/3/4/5/6/7/8/9;
(4) {n} - 匹配n次,{9}是匹配9次;
(5) ^ - 字符串开始,\^ - 匹配 ^ 字符本身; $ - 字符串结束。
5、什么情况下Java程序会产生死锁?(*2)如何定位、修复?(*2)【第18讲】(死锁处理办法)
死锁是一种特定的程序状态,在实体之间,由于循环依赖导致彼此一直处于等待之中,没有任何个体可以继续前进。死锁不仅仅是在线程之间会发生,存在资源独占的进程之间同样也可能出现死锁。通常来说,我们大多是聚焦在多线程场景中的死锁,指两个或多个线程之间,由于互相持有对方需要的锁,而永久处于阻塞的状态。
你可以利用下面的示例图理解基本的死锁问题:
定位死锁最常见的方式就是利用 jstack 等工具获取线程栈,然后定位互相之间的依赖关系,进而找到死锁。如果是比较明显的死锁,往往 jstack 等就能直接定位,类似 JConsole 甚至可以在图形界面进行有限的死锁检测。
如果程序运行时发生了死锁,绝大多数情况下都是无法在线解决的,只能重启、修正程序本身问题。所以,代码开发阶段互相审查,或者利用工具进行预防性排查,往往也是很重要的。
追问1:Java调试命令。看线程运行状态用什么?看堆栈信息用什么?
……
6、如何终止一个正在运行的线程?
stop():一剑封喉,被终止线程没机会料理后事,不建议使用。
interrupt():可以将休眠线程转换为RUNNAVBLE状态,设置中断位,然后判断是否中止。
……
追问1:如何优雅终止线程池?
方法:shutdown()和shutdownNow()
shutdown():保守,执行后,不接收新任务,但会等待正在执行和阻塞任务执行完,才最终关闭。
shutdownNow():激进,拒绝新任务,同时强制停止正在执行与阻塞队列任务,优雅结束需要正确处理线程中断。
三、JVM
1、Java内存模型中的happen-before是什么?【第29讲】
真正含义即关闭所有的编译器、操作系统和处理器的优化,所有指令顺序全部按照程序代码书写的顺序执行。 去掉CPU高速缓存,让CPU的每次读写操作都直接与主存交互。
……
补充:Happen-before规则内容
程序次序规则:在单线程中,代码的执行是有序的,虽然可能会存在运行指令的重排序,但最终执行的结果和顺序执行的结果是一致的;
锁定规则:一个锁处于被一个线程锁定占用状态,那么只有当这个线程释放锁之后,其它线程才能再次获取锁操作;
volatile 变量规则:如果一个线程正在写 volatile 变量,其它线程读取该变量会发生在写入之后;
线程启动规则:Thread 对象的 start() 方法先行发生于此线程的其它每一个动作;
线程终结规则:线程中的所有操作都先行发生于对此线程的终止检测;
对象终结规则:一个对象的初始化完成先行发生于它的 finalize() 方法的开始;
传递性:如果操作 A happens-before 操作 B,操作 B happens-before 操作 C,那么操作 A happens-before 操作 C;
线程中断规则:对线程 interrupt() 方法的调用先行发生于被中断线程的代码检测到中断事件的发生。
2、你用过哪些JVM参数?
JVM参数 | 作用 |
---|---|
-Xms | 堆的最小空间大小 |
-Xmx | 设置堆最大空间大小 |
-XX:NewSize | 设置新生代最小空间大小 |
-XX:MaxNewSize | 设置新生代最小空间大小 |