2023年Java核心技术面试第一篇(篇篇万字精讲)

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 2023年Java核心技术面试第一篇(篇篇万字精讲)

1.对于Java平台理解



1.1 "一次编译,到处运行"是Java语言跨平台的特性。


Java的跨平台特性与Java虚拟机的存在密不可分,可在不同的环境里面运行,window平台和Linux平台都有对应的JDK,安装JDK后就有了Java语言的运行环境,Java语言和其他编程语言并没有特别大的区别,并不是Java语言可以跨平台,而是在不同的平台都有让Java语言运行的环境,所以才有,Java一次编译,到处运行的结果。  


1.2跨平台的语言中Java是较为成熟的一种。


"一次编译,到处运行"这种效果与编译器有关,编程语言的处理需要编译器和解释器。

程序从源代码到运行的的三个阶段 编码-编译-运行-调试


1.3Java在编译阶段就体现了跨平台的特点。


编译过程:Java源代码转化成.class文件字节码,这是第一次编译。


.class文件就是可以到处运行的文件,然后Java字节码会被转化为目标机器代码,由JVM来执行,这是Java的第二次编译


"到处运行"的关键和前提就是JVM,在第二次编译的时候JVM起到关键的作用,在可以运行Java虚拟机的地方都内含一个JVM操作系统,使Java提供了各种平台的虚拟机制,实现了"到处运行"的效果。


1.4Java不是并不是编译机制,而是解释机制。


Java字节码的设计考虑到了JIT即时编译的方式,将字节码直接转化成高性能的本地机器码。


Java特性:


面向对象(封装,继承,多态)

平台无关性(JVM运行.class文件)

语言(泛型,Lambda)

类库(集合,并发,网络,IO/NIO)

JRE(Java运行环境,JVM,类库)

JDK(Java开发工具,包括JRE,Javac)


1.5Java不是解析运行的。


1.Java源代码通过Javac编译成.class文件。


2..class文件经JVM解析或编译运行。


(1)解析:class文件经过JVM内嵌的解析器解析执行。

(2)编译:存在JIT编译器(即时编译器) 把经常运行的代码作为”热点代码“编译与本地平台相关的机器码,并进行各种层次的优化。

(3)AOT编辑器:Java9提供的直接将所有代码编译成机器码执行。


1.6从不同角度看待Java


宏观角度:

与C/C++最大的不同点在于,C/C++编程是面向操作系统的,需要了解操作系统之间的差异。

Java平台通过虚拟机屏蔽了操作系统的底层细节,使得不用关心操作系统的之间差异,通过增加一个间接的中间层来进行解耦,虚拟机操作系统,HTTP,TCP/IP等。


微观角度:


Java语言本身,JDK中所提供的核心类库和相关工具

Java核心类库:集合类,线程相关类,IO,NIO,JUC并发包

大部分情况我们只需要考虑Java语言本身,无需关注底层细节,包括对内存的分配和回收,交给了GC。

Java虚拟机以及其他包含GC


解释JIT,AOT.


写个程序直接执行字节码就是解释执行。写个程序运行时把字节码动态翻译成机器码就是jit。

写个程序把java源代码直接翻译为机器码就是aot。造个CPU直接执行字节码,字节码就是机

器码。


jre为Java提供了必要的运行时环境,JDK为java提供了必要的开发环境


1.7理解扩展:


任何软件问题都可以通过加一层来解决。

有了编译器就屏蔽了不同机器语言的区别。

有了JVM就屏蔽了不同操作系统的区别。

有了TCP/IP就屏蔽了不同系统之间通讯的差异。

有了语音识别和翻译就屏蔽了不同语言的差异。

也许有一天人工智能可以直接把自然语言翻译成机器码直接生产可用的软件。


2 .Exception和Error的区别



2.1 Exception和Error 都是继承throwable类。


在Java里面只有Throwable类型的实例才可以被抛出(throw)和捕获(catch)


2.2 Exception。


程序正常运行中,可以预料的意外情况,并且应该被捕获,进行相应的处理。


Exception分为可检查(checked异常)和不检查(unchecked)异常,可检查异常在源代码里必须显式地进行捕获处理,不检查异常就是类似 NullPointerException,ArrayIndexOutOfBoundsException 之类,根据具体需要来进行判断是否需要捕获


2.3 Error。


正常情况下,不太可能出现的问题,绝大部分的Error都会导致程序处于非正常的不可恢复的状态,因为是非正常情况,所以不便于也不需要捕获,常见OutOfMemoryError类,都是Error的子类。


2.4 考点分析:


分析Exception和区别,从概念的角度考察Java的处理机制

第一:理解Throwable,Exception,Error的设计和分类,掌握应用最为广泛的子类,以及如何自定义异常。


扩展猜想:


如:你了解哪些Error,Exception或者RuntimeException?

NoClassDefFoundError 和ClassNotFoundException 有什么区别?


使用场景:

属于类加载相关的异常,在Java中用于表示类无法被找到的情况。


区别如下:

1.NoClassDefFoundError是Error类型的异常,而ClassNotFoundException是一个Checked Exception(受检异常),

并且NoClassDefFoundError是在运行时发生,通常是虚拟机在加载某个类的过程中,找不到该类的定义,可能是由于编译期间缺少了该类的依赖项,或者在运行时环境中缺少了该类的相关库文件。

ClassNotFoundException 是在编译时或运行时明确引发的,通常是因为尝试使用 Class.forName() 或 ClassLoader.loadClass() 方法来动态加载类时找不到该类。


2.NoClassDefFoundError 是一个致命错误,意味着虚拟机无法找到类的定义,导致程序无法继续执行。一旦出现 NoClassDefFoundError,通常需要检查类路径设置、类文件是否存在以及依赖关系是否正确,


ClassNotFoundException 也表示找不到类,但它是一个异常,可以通过 try-catch 块进行捕获和处理。可以根据具体的情况采取适当的措施,如提供正确的类路径、检查类名拼写、确认依赖是否正确等


小结:NoClassDefFoundError 是一个致命的错误,表示虚拟机在运行时找不到类的定义;ClassNotFoundException 则是一个 Checked Exception,表示编译时或运行时明确引发的找不到类的异常


第二,理解 Java 语言中操作 Throwable 的元素和实践。掌握最基本的语法,如

try-catch-finally 块,throw、throws 关键字等,要懂得处理典型场景。


try-catch-finally 块:

  • try-catch-finally 块用于捕获和处理异常。在 try 块中编写可能引发异常的代码,如果发生异常,则会跳转到与之匹配的 catch 块进行处理。
  • catch 块声明了捕获的异常类型,并在块内部提供处理逻辑。可以有多个 catch 块,每个 catch 块用于处理不同类型的异常。
  • finally 块是可选的,它包含的代码无论是否发生异常都会执行,通常用于释放资源或执行清理操作。


try {
    // 可能引发异常的代码
} catch (ExceptionType1 e1) {
    // 处理 ExceptionType1 异常的逻辑
} catch (ExceptionType2 e2) {
    // 处理 ExceptionType2 异常的逻辑
} finally {
    // 清理操作或释放资源
}


2.5  throw 关键字:


  • throw 关键字用于手动抛出一个异常对象。通过 throw 关键字可以在代码中显式地抛出异常,使程序进入异常处理流程。
    throw 后面跟着一个异常对象,该对象必须是 Throwable 类或其子类的实例
throw new ExceptionType("异常信息");


2.6  throws关键字:


  • throws 关键字用于在方法声明中指定可能会抛出的异常。当一个方法可能引发某种类型的异常时,可以使用 throws 关键字在方法签名中声明该异常。
    throws 关键字后面跟着一个或多个异常类型,表示该方法有可能抛出这些异常,通知调用者处理这些异常。
1. public void methodName() throws ExceptionType1, ExceptionType2 {
2. // 方法体
3. }


2.7 典型场景的处理包括:


  • 使用 try-catch-finally 块捕获和处理特定类型的异常,以防止程序意外终止,同时提供合理的异常处理逻辑。
  • 在 catch 块中根据具体的业务需求进行异常处理,例如打印日志、回滚事务、给用户友好的提示等。
  • 使用 throw 关键字手动抛出自定义异常,在需要的情况下主动中断程序流程,并传递异常信息。
  • 在方法声明中使用 throws 关键字声明可能会抛出的异常,以告知调用者方法可能引发的异常,让其选择相应的处理方式。


注意点:


第一,尽量不要捕获类似 Exception 这样的通用异常,而是应该捕获特定异常。

第二,不要生吞(swallow)异常。可能会导致非常难以诊断的诡异情况。

注意finally的使用。


3.final,finally,finalize的不同的地方?



3.1 final。


用来修饰类,方法,变量,进行修饰的class代表不可继承,final的变量代表不可以修改,并且final的方法不可以被重写(override)


finally:则是Java保证重点代码一定要被执行的一种机制,可以使用try-finallly或者try-catch-finally来进行关闭JDBC连接,保证unlock锁等动作。


finalize是基础类java.lang.Object的一个方法,保证对象在垃圾收集前完成特定资源的回收。


不推荐使用了,JDK9中被标记deprecated


3.2 考点分析:


经典的Java基础问题,上面都是从语法和使用实践的角度出发的。

扩展方向往:性能,并发,对象周期或者垃圾收集过程


final:避免API使用者进行更改,保证平台安全的必要手段,使用final进行修饰参数或者变量,避免意外赋值导致的编程错误。

利用final可能有助于JVM将方法进行内联,可以进行改善编译器进行条件编译的能力


3.3 final方法的内联优化可能包括以下方面:


3.3.1直接插入方法体:


编译器可以直接将final方法的代码插入到调用处,避免了方法调用的开销。

例子:


printMessage()是一个被final修饰的方法,它打印一条消息到控制台。由于编译器知道该方法不会被重写,它可以直接将调用处的代码替换为方法体的内容,即System.out.println("Hello, World!");


通过这种直接插入方法体的优化,避免了实际的方法调用和方法体代码的执行开销,提高了程序的执行效率。

public class Example {
    public final void printMessage() {
        System.out.println("Hello, World!");
    }
    public static void main(String[] args) {
        Example example = new Example();
        // 调用final方法
        example.printMessage();
        // 编译器可以将上述代码替换为方法体的直接插入
        System.out.println("Hello, World!");
    }
}


3.3.2编译期间绑定:


由于final方法不可被重写,编译器可以在编译期间确定要调用的确切方法,而不需要等到运行时进行动态绑定。


例子:

printMessage()是一个被final修饰的方法。由于它是final方法,编译器在编译时就可以确定要调用的确切方法是Example类中的printMessage()方法,而不需要在运行时进行动态绑定。


通过编译期间绑定的优化,避免了在运行时进行动态查找的开销,提高了程序的执行效率。


public class Example {
    public final void printMessage() {
        System.out.println("Hello, World!");
    }
}
public class Main {
    public static void main(String[] args) {
        Example example = new Example();
        // 调用final方法
        example.printMessage();
    }
}

3.3.3内联常量引用


如果final方法返回常量值,编译器可以将调用该方法的地方替换为常量值的直接引用,从而消除方法调用和返回值的相关操作。


例子:

getConstantValue()是一个被final修饰的方法,并且它始终返回一个常量值10。由于编译器知道该方法不会被重写,它可以直接将调用处的代码替换为常量值的引用,即int optimizedValue = 10;


通过这种内联常量引用的优化,避免了实际的方法调用和返回值操作,提高了程序的执行效率。

public class Example {
    public final int getConstantValue() {
        return 10;
    }
    public static void main(String[] args) {
        Example example = new Example();
        // 调用final方法并获取返回值
        int value = example.getConstantValue();
        // 编译器可以将上述代码替换为直接使用常量值
        int optimizedValue = 10;
        System.out.println(value);           // 输出:10
        System.out.println(optimizedValue);  // 输出:10
    }
}


3.3.4 finall 不执行的特例


例子:

try块中调用了System.exit(1)方法,它会终止Java虚拟机的运行,并传递一个退出状态码(这里是1)。当System.exit(1)被调用时,Java程序会立即终止,不再执行后续的代码,包括finally块。


try {
System.exit(1);
} finally{
System.out.println(“Print from finally”);
}


3. 4 finalize


Java9中明确将Object.finalize()标记为deprecated,没有特别原因不要去实现finalize方法,不能指望finalize进行资源回收,因为无法保证finalize什么时候执行,执行是否符合预期,使用不当会影响性能,导致程序死锁,挂起。


分析:


3.4.1执行时机不确定:


Java虚拟机对于finalize()方法的调用时机是不确定的,无法保证在对象不再被引用且即将被回收时立即执行。这意味着不能指望finalize()方法能及时地释放资源。


3.4.2不可靠性:


由于无法控制finalize()方法的执行时机,也无法确保其是否会被执行。例如,如果JVM在垃圾回收过程中遇到问题或程序正在运行耗尽内存,finalize()方法可能永远不会被调用。


3.4.3性能影响:


finalize()方法的执行会导致垃圾回收器的延迟和开销增加,可能会影响程序的性能。频繁地创建对象并依赖finalize()方法进行资源回收可能会导致系统性能下降。


3.4.4安全问题:


finalize()方法的执行过程是单线程的,在它执行期间,其他线程可能被挂起或发生死锁。这是因为垃圾回收器执行finalize()方法时会暂停其他的线程

解释:

3.4.5  finalize()方法可能永远不会被调用的原因是由于以下几个方面:


3.4.5.1垃圾回收器优化:


为了提高垃圾回收的效率,JVM的垃圾回收器通常采用了各种优化策略。其中一个优化策略是针对没有覆盖finalize()方法的对象,直接跳过其finalize()方法的调用,将其视为“不需要清理”的对象。这样可以减少垃圾回收的时间和开销。


3.4.5.2内存耗尽问题:


当程序运行过程中耗尽了内存资源,导致无法分配新的内存时,JVM会抛出OutOfMemoryError异常。在这种情况下,JVM处于一种极限状态,无法正常执行垃圾回收操作,包括finalize()方法的调用。因此,finalize()方法可能永远不会被调用。


3.4.5.3垃圾回收过程中的问题:


在垃圾回收过程中,可能会发生意外的错误或异常。例如,如果垃圾回收器本身存在缺陷、遭遇死循环或崩溃等问题,就无法完成垃圾回收操作,包括finalize()方法的执行。  

                                                                                                                                                 

3.4.5.4优化策略解析:                    


将没有覆盖finalize()方法的对象视为“不需要清理”的对象,


主要是出于性能和效率的考虑。当对象没有覆盖finalize()方法时,垃圾回收器可以假设该对象不需要进行任何额外的清理工作。


因此,在垃圾回收过程中,它可以直接跳过对这些对象的finalize()方法的调用,从而减少了垃圾回收的时间和开销。


通常情况下,如果一个对象覆盖了finalize()方法,那么在垃圾回收器执行垃圾回收操作时,会将该对象标记为“需要清理”。垃圾回收器会在回收该对象之前,先调用其finalize()方法进行清理操作。这个过程需要消耗额外的时间,并且会导致垃圾回收的延迟。


然而,由于finalize()方法的不确定性和性能问题,JVM引入了一种优化策略,即对没有覆盖finalize()方法的对象进行特殊处理。


这些对象被认为不需要进行额外的清理操作,因此在垃圾回收过程中会被直接回收,而不会调用其finalize()方法。


相关文章
|
2天前
|
SQL 监控 Java
技术前沿:Java连接池技术的最新发展与应用
本文探讨了Java连接池技术的最新发展与应用,包括高性能与低延迟、智能化管理和监控、扩展性与兼容性等方面。同时,结合最佳实践,介绍了如何选择合适的连接池库、合理配置参数、使用监控工具及优化数据库操作,为开发者提供了一份详尽的技术指南。
17 7
|
4天前
|
移动开发 前端开发 Java
过时的Java技术盘点:避免在这些领域浪费时间
【10月更文挑战第14天】 在快速发展的Java生态系统中,新技术层出不穷,而一些旧技术则逐渐被淘汰。对于Java开发者来说,了解哪些技术已经过时是至关重要的,这可以帮助他们避免在这些领域浪费时间,并将精力集中在更有前景的技术上。本文将盘点一些已经或即将被淘汰的Java技术,为开发者提供指导。
31 7
|
2天前
|
Java 数据库连接 数据库
优化之路:Java连接池技术助力数据库性能飞跃
在Java应用开发中,数据库操作常成为性能瓶颈。频繁的数据库连接建立和断开增加了系统开销,导致性能下降。本文通过问题解答形式,深入探讨Java连接池技术如何通过复用数据库连接,显著减少连接开销,提升系统性能。文章详细介绍了连接池的优势、选择标准、使用方法及优化策略,帮助开发者实现数据库性能的飞跃。
14 4
|
2天前
|
SQL Java 数据库连接
打破瓶颈:利用Java连接池技术提升数据库访问效率
在Java应用中,数据库访问常成为性能瓶颈。连接池技术通过预建立并复用数据库连接,避免了频繁的连接建立和断开,显著提升了数据库访问效率。常见的连接池库包括HikariCP、C3P0和DBCP,它们提供了丰富的配置选项和强大的功能,帮助优化应用性能。
16 2
|
6天前
|
存储 Java 程序员
Java面试加分点!一文读懂HashMap底层实现与扩容机制
本文详细解析了Java中经典的HashMap数据结构,包括其底层实现、扩容机制、put和查找过程、哈希函数以及JDK 1.7与1.8的差异。通过数组、链表和红黑树的组合,HashMap实现了高效的键值对存储与检索。文章还介绍了HashMap在不同版本中的优化,帮助读者更好地理解和应用这一重要工具。
21 5
|
4天前
|
前端开发 Java API
过时Java技术的退役:这些技能你不再需要掌握!
【10月更文挑战第22天】 在快速变化的技术领域,一些曾经流行的Java技术已经逐渐被淘汰,不再适用于现代软件开发。了解这些过时的技术对于新手开发者来说尤为重要,以避免浪费时间和精力学习不再被行业所需的技能。本文将探讨一些已经或即将被淘汰的Java技术,帮助你调整学习路径,专注于那些更有价值的技术。
15 1
|
5天前
|
移动开发 前端开发 JavaScript
java家政系统成品源码的关键特点和技术应用
家政系统成品源码是已开发完成的家政服务管理软件,支持用户注册、登录、管理个人资料,家政人员信息管理,服务项目分类,订单与预约管理,支付集成,评价与反馈,地图定位等功能。适用于各种规模的家政服务公司,采用uniapp、SpringBoot、MySQL等技术栈,确保高效管理和优质用户体验。
|
5天前
|
存储 Java
[Java]面试官:你对异常处理了解多少,例如,finally中可以有return吗?
本文介绍了Java中`try...catch...finally`语句的使用细节及返回值问题,并探讨了JDK1.7引入的`try...with...resources`新特性,强调了异常处理机制及资源自动关闭的优势。
14 1
|
14天前
|
Java 程序员
Java 面试高频考点:static 和 final 深度剖析
本文介绍了 Java 中的 `static` 和 `final` 关键字。`static` 修饰的属性和方法属于类而非对象,所有实例共享;`final` 用于变量、方法和类,确保其不可修改或继承。两者结合可用于定义常量。文章通过具体示例详细解析了它们的用法和应用场景。
21 3
|
15天前
|
存储 Java 大数据
该到重点啦—java的核心技术
该到重点啦—java的核心技术
11 1