[feat]Java基础知识&面试题总结——基础概念与常识部分完善
This commit is contained in:
parent
e6dcf0e47e
commit
b52f304bc9
File diff suppressed because one or more lines are too long
Binary file not shown.
After Width: | Height: | Size: 39 KiB |
File diff suppressed because one or more lines are too long
Binary file not shown.
After Width: | Height: | Size: 40 KiB |
|
@ -20,28 +20,22 @@ tag:
|
|||
|
||||
> **🐛 修正(参见: [issue#544](https://github.com/Snailclimb/JavaGuide/issues/544))** :C++11 开始(2011 年的时候),C++就引入了多线程库,在 windows、linux、macos 都可以使用`std::thread`和`std::async`来创建线程。参考链接:http://www.cplusplus.com/reference/thread/thread/?kw=thread
|
||||
|
||||
🌈 拓展一下:
|
||||
|
||||
“Write Once, Run Anywhere(一次编写,随处运行)”这句宣传口号,真心经典,流传了好多年!以至于,直到今天,依然有很多人觉得跨平台是 Java 语言最大的优势。实际上,跨平台已经不是 Java 最大的卖点了,各种 JDK 新特性也不是。目前市面上虚拟化技术已经非常成熟,比如你通过 Docker 就很容易实现跨平台了。在我看来,Java 强大的生态才是!
|
||||
|
||||
### JVM vs JDK vs JRE
|
||||
|
||||
#### JVM
|
||||
|
||||
Java 虚拟机(JVM)是运行 Java 字节码的虚拟机。JVM 有针对不同系统的特定实现(Windows,Linux,macOS),目的是使用相同的字节码,它们都会给出相同的结果。
|
||||
|
||||
**什么是字节码?采用字节码的好处是什么?**
|
||||
|
||||
> 在 Java 中,JVM 可以理解的代码就叫做`字节码`(即扩展名为 `.class` 的文件),它不面向任何特定的处理器,只面向虚拟机。Java 语言通过字节码的方式,在一定程度上解决了传统解释型语言执行效率低的问题,同时又保留了解释型语言可移植的特点。所以 Java 程序运行时比较高效,而且,由于字节码并不针对一种特定的机器,因此,Java 程序无须重新编译便可在多种不同操作系统的计算机上运行。
|
||||
|
||||
**Java 程序从源代码到运行一般有下面 3 步:**
|
||||
|
||||
![Java程序运行过程](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/Java%20%E7%A8%8B%E5%BA%8F%E8%BF%90%E8%A1%8C%E8%BF%87%E7%A8%8B.png)
|
||||
|
||||
我们需要格外注意的是 .class->机器码 这一步。在这一步 JVM 类加载器首先加载字节码文件,然后通过解释器逐行解释执行,这种方式的执行速度会相对比较慢。而且,有些方法和代码块是经常需要被调用的(也就是所谓的热点代码),所以后面引进了 JIT 编译器,而 JIT 属于运行时编译。当 JIT 编译器完成第一次编译后,其会将字节码对应的机器码保存下来,下次可以直接使用。而我们知道,机器码的运行效率肯定是高于 Java 解释器的。这也解释了我们为什么经常会说 Java 是编译与解释共存的语言。
|
||||
|
||||
> HotSpot 采用了惰性评估(Lazy Evaluation)的做法,根据二八定律,消耗大部分系统资源的只有那一小部分的代码(热点代码),而这也就是 JIT 所需要编译的部分。JVM 会根据代码每次被执行的情况收集信息并相应地做出一些优化,因此执行的次数越多,它的速度就越快。JDK 9 引入了一种新的编译模式 AOT(Ahead of Time Compilation),它是直接将字节码编译成机器码,这样就避免了 JIT 预热等各方面的开销。JDK 支持分层编译和 AOT 协作使用。但是 ,AOT 编译器的编译质量是肯定比不上 JIT 编译器的。
|
||||
|
||||
**总结:**
|
||||
|
||||
Java 虚拟机(JVM)是运行 Java 字节码的虚拟机。JVM 有针对不同系统的特定实现(Windows,Linux,macOS),目的是使用相同的字节码,它们都会给出相同的结果。字节码和不同系统的 JVM 实现是 Java 语言“一次编译,随处可以运行”的关键所在。
|
||||
|
||||
**JVM 并不是只有一种!只要满足 JVM 规范,每个公司、组织或者个人都可以开发自己的专属 JVM。** 也就是说我们平时接触到的 HotSpot VM 仅仅是是 JVM 规范的一种实现而已。
|
||||
|
||||
除了我们平时最常用的 HotSpot VM 外,还有 J9 VM、Zing VM、JRockit VM 等 JVM 。维基百科上就有常见 JVM 的对比:[Comparison of Java virtual machines](https://link.zhihu.com/?target=http%3A//en.wikipedia.org/wiki/Comparison_of_Java_virtual_machines) ,感兴趣的可以去看看。并且,你可以在 [Java SE Specifications](https://docs.oracle.com/javase/specs/index.html) 上找到各个版本的 JDK 对应的 JVM 规范。
|
||||
|
||||
![](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/javaguide/Java-SE-Specifications.png)
|
||||
|
||||
#### JDK 和 JRE
|
||||
|
||||
JDK 是 Java Development Kit 缩写,它是功能齐全的 Java SDK。它拥有 JRE 所拥有的一切,还有编译器(javac)和工具(如 javadoc 和 jdb)。它能够创建和编译程序。
|
||||
|
@ -50,14 +44,40 @@ JRE 是 Java 运行时环境。它是运行已编译 Java 程序所需的所有
|
|||
|
||||
如果你只是为了运行一下 Java 程序的话,那么你只需要安装 JRE 就可以了。如果你需要进行一些 Java 编程方面的工作,那么你就需要安装 JDK 了。但是,这不是绝对的。有时,即使您不打算在计算机上进行任何 Java 开发,仍然需要安装 JDK。例如,如果要使用 JSP 部署 Web 应用程序,那么从技术上讲,您只是在应用程序服务器中运行 Java 程序。那你为什么需要 JDK 呢?因为应用程序服务器会将 JSP 转换为 Java servlet,并且需要使用 JDK 来编译 servlet。
|
||||
|
||||
### 什么是字节码?采用字节码的好处是什么?
|
||||
|
||||
在 Java 中,JVM 可以理解的代码就叫做字节码(即扩展名为 `.class` 的文件),它不面向任何特定的处理器,只面向虚拟机。Java 语言通过字节码的方式,在一定程度上解决了传统解释型语言执行效率低的问题,同时又保留了解释型语言可移植的特点。所以, Java 程序运行时相对来说还是高效的(不过,和 C++,Rust,Go 等语言还是有一定差距的),而且,由于字节码并不针对一种特定的机器,因此,Java 程序无须重新编译便可在多种不同操作系统的计算机上运行。
|
||||
|
||||
**Java 程序从源代码到运行的过程如下图所示:**
|
||||
|
||||
![Java程序转变为机器代码的过程](./images/java程序转变为机器代码的过程.png)
|
||||
|
||||
我们需要格外注意的是 `.class->机器码` 这一步。在这一步 JVM 类加载器首先加载字节码文件,然后通过解释器逐行解释执行,这种方式的执行速度会相对比较慢。而且,有些方法和代码块是经常需要被调用的(也就是所谓的热点代码),所以后面引进了 JIT(just-in-time compilation) 编译器,而 JIT 属于运行时编译。当 JIT 编译器完成第一次编译后,其会将字节码对应的机器码保存下来,下次可以直接使用。而我们知道,机器码的运行效率肯定是高于 Java 解释器的。这也解释了我们为什么经常会说 **Java 是编译与解释共存的语言** 。
|
||||
|
||||
> HotSpot 采用了惰性评估(Lazy Evaluation)的做法,根据二八定律,消耗大部分系统资源的只有那一小部分的代码(热点代码),而这也就是 JIT 所需要编译的部分。JVM 会根据代码每次被执行的情况收集信息并相应地做出一些优化,因此执行的次数越多,它的速度就越快。JDK 9 引入了一种新的编译模式 AOT(Ahead of Time Compilation),它是直接将字节码编译成机器码,这样就避免了 JIT 预热等各方面的开销。JDK 支持分层编译和 AOT 协作使用。但是 ,AOT 编译器的编译质量是肯定比不上 JIT 编译器的。
|
||||
|
||||
### 为什么说 Java 语言“编译与解释并存”?
|
||||
|
||||
高级编程语言按照程序的执行方式分为编译型和解释型两种。简单来说,编译型语言是指编译器针对特定的操作系统将源代码一次性翻译成可被该平台执行的机器码;解释型语言是指解释器对源程序逐行解释成特定平台的机器码并立即执行。比如,你想阅读一本英文名著,你可以找一个英文翻译人员帮助你阅读,
|
||||
有两种选择方式,你可以先等翻译人员将全本的英文名著(也就是源码)都翻译成汉语,再去阅读,也可以让翻译人员翻译一段,你在旁边阅读一段,慢慢把书读完。
|
||||
其实这个问题我们讲字节码的时候已经提到过,因为比较重要,所以我们这里再提一下。
|
||||
|
||||
Java 语言既具有编译型语言的特征,也具有解释型语言的特征,因为 Java 程序要经过先编译,后解释两个步骤,由 Java 编写的程序需要先经过编译步骤,生成字节码(`*.class` 文件),这种字节码必须由 Java 解释器来解释执行。因此,我们可以认为 Java 语言编译与解释并存。
|
||||
我们可以将高级编程语言按照程序的执行方式分为两种:
|
||||
|
||||
### Oracle JDK 和 OpenJDK 的对比
|
||||
- **编译型** :[编译型语言](https://zh.wikipedia.org/wiki/%E7%B7%A8%E8%AD%AF%E8%AA%9E%E8%A8%80) 会通过[编译器](https://zh.wikipedia.org/wiki/%E7%B7%A8%E8%AD%AF%E5%99%A8)将源代码一次性翻译成可被该平台执行的机器码。一般情况下,编译语言的执行速度比较快,开发效率比较低。常见的编译性语言有 C、C++、Go、Rust 等等。
|
||||
- **解释型** :[解释型语言](https://zh.wikipedia.org/wiki/%E7%9B%B4%E8%AD%AF%E8%AA%9E%E8%A8%80)会通过[解释器](https://zh.wikipedia.org/wiki/直譯器)一句一句的将代码解释(interpret)为机器代码后再执行。解释型语言开发效率比较快,执行速度比较慢。常见的解释性语言有 Python、JavaScript、PHP 等等。
|
||||
|
||||
![编译型语言和解释型语言](./images/编译型语言和解释型语言.png)
|
||||
|
||||
根据维基百科介绍:
|
||||
|
||||
> 为了改善编译语言的效率而发展出的[即时编译](https://zh.wikipedia.org/wiki/即時編譯)技术,已经缩小了这两种语言间的差距。这种技术混合了编译语言与解释型语言的优点,它像编译语言一样,先把程序源代码编译成[字节码](https://zh.wikipedia.org/wiki/字节码)。到执行期时,再将字节码直译,之后执行。[Java](https://zh.wikipedia.org/wiki/Java)与[LLVM](https://zh.wikipedia.org/wiki/LLVM)是这种技术的代表产物。
|
||||
>
|
||||
> 相关阅读:[基本功 | Java 即时编译器原理解析及实践](https://tech.meituan.com/2020/10/22/java-jit-practice-in-meituan.html)
|
||||
|
||||
**为什么说 Java 语言“编译与解释并存”?**
|
||||
|
||||
这是因为 Java 语言既具有编译型语言的特征,也具有解释型语言的特征。因为 Java 程序要经过先编译,后解释两个步骤,由 Java 编写的程序需要先经过编译步骤,生成字节码(`.class` 文件),这种字节码必须由 Java 解释器来解释执行。
|
||||
|
||||
### Oracle JDK vs OpenJDK
|
||||
|
||||
可能在看这个问题之前很多人和我一样并没有接触和使用过 OpenJDK 。那么 Oracle JDK 和 OpenJDK 之间是否存在重大差异?下面我通过收集到的一些资料,为你解答这个被很多人忽视的问题。
|
||||
|
||||
|
@ -78,12 +98,12 @@ Java 语言既具有编译型语言的特征,也具有解释型语言的特征
|
|||
|
||||
🌈 拓展一下:
|
||||
|
||||
- BCL 协议(Oracle Binary Code License Agreement): 可以使用JDK(支持商用),但是不能进行修改。
|
||||
- OTN 协议(Oracle Technology Network License Agreement): 11 及之后新发布的JDK用的都是这个协议,可以自己私下用,但是商用需要付费。
|
||||
- BCL 协议(Oracle Binary Code License Agreement): 可以使用 JDK(支持商用),但是不能进行修改。
|
||||
- OTN 协议(Oracle Technology Network License Agreement): 11 及之后新发布的 JDK 用的都是这个协议,可以自己私下用,但是商用需要付费。
|
||||
|
||||
![](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/java-guide-blog/20210425151835918.png)
|
||||
|
||||
相关阅读👍:[《Differences Between Oracle JDK and OpenJDK》](https://www.baeldung.com/oracle-jdk-vs-openjdk)
|
||||
相关阅读 👍:[《Differences Between Oracle JDK and OpenJDK》](https://www.baeldung.com/oracle-jdk-vs-openjdk)
|
||||
|
||||
### Java 和 C++的区别?
|
||||
|
||||
|
@ -96,12 +116,6 @@ Java 语言既具有编译型语言的特征,也具有解释型语言的特征
|
|||
- C ++同时支持方法重载和操作符重载,但是 Java 只支持方法重载(操作符重载增加了复杂性,这与 Java 最初的设计思想不符)。
|
||||
- ......
|
||||
|
||||
### import java 和 javax 有什么区别?
|
||||
|
||||
刚开始的时候 JavaAPI 所必需的包是 java 开头的包,javax 当时只是扩展 API 包来使用。然而随着时间的推移,javax 逐渐地扩展成为 Java API 的组成部分。但是,将扩展从 javax 包移动到 java 包确实太麻烦了,最终会破坏一堆现有的代码。因此,最终决定 javax 包将成为标准 API 的一部分。
|
||||
|
||||
所以,实际上 java 和 javax 没有区别。这都是一个名字。
|
||||
|
||||
## 基本语法
|
||||
|
||||
### 字符型常量和字符串常量的区别?
|
||||
|
@ -154,8 +168,8 @@ Java 中的注释有三种:
|
|||
|
||||
### Java 中有哪些常见的关键字?
|
||||
|
||||
| 分类 | 关键字 | | | | | | |
|
||||
| :-------------------- | -------- | ---------- | -------- | ------------ | ---------- | --------- | ------ |
|
||||
| 分类 | 关键字 | | | | | | |
|
||||
| :------------------- | -------- | ---------- | -------- | ------------ | ---------- | --------- | ------ |
|
||||
| 访问控制 | private | protected | public | | | | |
|
||||
| 类,方法和变量修饰符 | abstract | class | extends | final | implements | interface | native |
|
||||
| | new | static | strictfp | synchronized | transient | volatile | |
|
||||
|
@ -686,22 +700,22 @@ public void f5(int a) {
|
|||
|
||||
不过,需要注意的是一般不建议使用 `对象.方法名` 的方式来调用静态方法。这种方式非常容易造成混淆,静态方法不属于类的某个对象而是属于这个类。
|
||||
|
||||
因此,一般建议使用 `类名.方法名` 的方式来调用静态方法。
|
||||
因此,一般建议使用 `类名.方法名` 的方式来调用静态方法。
|
||||
|
||||
```java
|
||||
|
||||
public class Person {
|
||||
public void method() {
|
||||
public void method() {
|
||||
//......
|
||||
}
|
||||
|
||||
|
||||
public static void staicMethod(){
|
||||
//......
|
||||
}
|
||||
public static void main(String[] args) {
|
||||
Person person = new Person();
|
||||
// 调用实例方法
|
||||
person.method();
|
||||
person.method();
|
||||
// 调用静态方法
|
||||
Person.staicMethod()
|
||||
}
|
||||
|
@ -1107,7 +1121,6 @@ public final void wait() throws InterruptedException//跟之前的2个wait方法
|
|||
protected void finalize() throws Throwable { }//实例被垃圾回收器回收的时候触发的操作
|
||||
```
|
||||
|
||||
|
||||
## 反射
|
||||
|
||||
### 何为反射?
|
||||
|
@ -1319,7 +1332,8 @@ try (BufferedInputStream bin = new BufferedInputStream(new FileInputStream(new F
|
|||
|
||||
`transient` 关键字的作用是:阻止实例中那些用此关键字修饰的的变量序列化;当对象被反序列化时,被 `transient` 修饰的变量值不会被持久化和恢复。
|
||||
|
||||
关于 `transient` 还有几点注意:
|
||||
关于 `transient` 还有几点注意:
|
||||
|
||||
- `transient` 只能修饰变量,不能修饰类和方法。
|
||||
- `transient` 修饰的变量,在反序列化后变量值将会被置成类型的默认值。例如,如果是修饰 `int` 类型,那么反序列后结果就是 `0`。
|
||||
- `static` 变量因为不属于任何对象(Object),所以无论有没有 `transient` 关键字修饰,均不会被序列化。
|
||||
|
@ -1371,4 +1385,3 @@ Java IO 流共涉及 40 多个类,这些类看上去很杂乱,但实际上
|
|||
- https://stackoverflow.com/questions/1906445/what-is-the-difference-between-jdk-and-jre
|
||||
- https://www.educba.com/oracle-vs-openjdk/
|
||||
- https://stackoverflow.com/questions/22358071/differences-between-oracle-jdk-and-openjdk 基础概念与常识
|
||||
|
||||
|
|
Loading…
Reference in New Issue