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()方法。