Java 基础概念

Java SE EE ME

Java SE 就是标准版,包含标准的 JVM 和标准库

Java EE 是企业版,它只是在 Java SE 的基础上加上了大量的 API 和库,以便方便开发 Web 应用、数据库、消息服务等。

Java ME 是 Java 的微型版本,主要用于开发嵌入式消费电子设备的应用程序,例如手机、PDA、机顶盒、冰箱、空调等。

Java版本

JVM & JDK & JRE

JVM(Java Virtual Machine, Java 虚拟机)并不是只有一种!只要满足 JVM 规范,每个公司、组织或者个人都可以开发自己的专属 JVM。 也就是说我们平时接触到的 HotSpot VM 仅仅是是 JVM 规范的一种实现而已。

维基百科上就有常见 JVM 的对比:Comparison of Java virtual machinesopen in new window ,感兴趣的可以去看看。并且,你可以在 Java SE Specificationsopen in new window 上找到各个版本的 JDK 对应的 JVM 规范。

JVM模型

JDK(Java Development Kit)是功能齐全的 Java SDK,是提供给开发者使用,能够创建和编译 Java 程序的开发套件。它包含了 JRE,还包含了 javac(编译 java 源码的编译器)以及一些其他工具比如 javadoc(文档注释工具)、jdb(调试器)、jconsole(基于 JMX 的可视化监控⼯具)、javap(反编译工具)等等。

JRE(Java Runtime Environment)是 Java 运行时环境。它是运行已编译 Java 程序所需的所有内容的集合,主要包括 Java 虚拟机(JVM)、Java 基础类库(Class Library)。

JDK、JRE 和 JVM 的包含关系

JDK = JRE + 开发工具集(例如 javac,java 编译工具等)

JRE = JVM + Java SE 标准类库(java 核心类库)

如果只想运行开发好的.class文件,只需要 JRE。

但对于某些需要使用 Java 特性的应用程序,如 JSP 转换为 Java Servlet、使用反射等,也需要 JDK 来编译和运行 Java 代码。因此,即使不打算进行 Java 应用程序的开发工作,也有可能需要安装 JDK。

JDK

不过,从 JDK 9 开始,就不需要区分 JDK 和 JRE 的关系了,取而代之的是模块系统(JDK 被重新组织成 94 个模块)+ jlink 工具 (随 Java 9 一起发布的新命令行工具,用于生成自定义 Java 运行时映像,该映像仅包含给定应用程序所需的模块) 。并且,从 JDK 11 开始,Oracle 不再提供单独的 JRE 下载。详见: Java 9 新特性概览

下载安装

官网:https://www.oracle.com/java/technologies/downloads/

配置环境变量path

  1. 环境变量 - 系统变量 - 添加 JAVA_HOME 环境变量,指向 jdk 的安装目录 D:\Program Files\jdk8\jdk1.8.0_333
  2. 编辑 path 环境变量,增加 %JAVA_HOME%\bin
  3. 为了确保 jre(运行)在你使用 javaIDE 的时候不产生问题,新增配置 %JAVA_HOME%\jre\bin

windows 操作系统是如何搜索硬盘上某个命令?

  1. 首先会从当前目录下搜索
  2. 当前目录搜索不到的话,会从环境变量path指定的路径当中搜索某个命令
  3. 如果都搜索不到,则报错

字节码

在 Java 中,JVM 可以理解的代码就叫做字节码(即扩展名为.class的文件),它不面向任何特定的处理器,只面向虚拟机。

Java 语言通过字节码的方式,在一定程度上解决了传统解释型语言执行效率低的问题,同时又保留了解释型语言可移植的特点。

由于字节码并不针对一种特定的机器,因此,Java 程序无须重新编译便可在多种不同操作系统的计算机上运行。

编译与运行

  1. Java 源码本质上是一个文本文件,需要先用javac把 Xxx.java 编译成字节码文件 Xxx.class,然后,用java命令执行这个字节码文件
  2. 给虚拟机(JVM)传递的参数Hello是我们定义的类名,JVM 会启动类加载器(ClassLoader),ClassLoader 会自动查找对应的 class 文件装载到 JVM 中执行。
  3. JVM 将 Xxx.class 字节码文件解释成二进制数据。
  4. 操作系统执行二进制和底层硬件平台进行交互。

编译与运行

编译

  1. 编译阶段主要的任务是检查 Java 源程序是否符合Java语法
  2. 符合 Java 语法则生成正常的字节码文件(xxx.class

字节码文件中不是纯粹的二进制,这种文件无法在操作系统中直接执行。

示例:

1
javac Hello.java
  1. java 源文件,通过编译器编译成JVM可识别的字节码文件
  2. 通过javac.exe编译工具对Hello.java文件进行编译
  3. 若程序没错,则没有任何提示,并在源文件同目录出现一个Hello.class文件,该文件称为字节码文件,是可以执行的 java 程序

运行

java.exe主要负责运行阶段

示例

1
java Hello
  1. 有了可执行的 java 程序(.class字节码文件)
  2. 通过运行工具java.exe对字节码文件进行执行,本质是.class装载到 JVM 执行

注:dos控制台默认简体中文 GBK 编码,若源码有中文,则需要以 chinese 编码保存

开发注意事项

  1. Java应用程序的执行入口是main()方法。有固定书写格式:public static void main(String[] args){…}
  2. Java语言严格区分大小写。
  3. 类首字母大写。
  4. 一个源文件中最多只有一个public类。其他类个数不限。[示例1]
  5. 如果源文件中包含一个public类,则文件名必须按该类名命名。
  6. 也可以将mian方法写在非public类中,然后指定运行非public类,这样入口方法就是非public类的main方法。[示例1]

示例1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Hello {

public static void main(String[] args) {
System.out.println("yo my nigga!");
}
}

//编译后,每个类都对应一个.class
class Cat {
public static void main(String[] args) {
System.out.println("我是皮蛋");
}
}

class Dog {
}
# 编译 public 类命名的文件
javac Hello.java

# 运行里面的 Cat 类
java Cat

在java编程思想(第四版)一书中有这样3段话(6.4 类的访问权限):

  1.每个编译单元(文件)都只能有一个public类,这表示,每个编译单元都有单一的公共接口,用public类来表现。该接口可以按要求包含众多的支持包访问权限的类。如果在某个编译单元内有一个以上的public类,编译器就会给出错误信息。

  2.public类的名称必须完全与含有该编译单元的文件名相同,包含大小写。如果不匹配,同样将得到编译错误。

  3.虽然不是很常用,但编译单元内完全不带public类也是可能的。在这种情况下,可以随意对文件命名。

一个编译单元(java文件)可以存在多个类,在编译时产生多个不同的.class文件, .class文件便是程序运行的数据来源。

java 将 public 类作为每个编译单元的数据接口,只能有一个,不然不能处理存在多个类的 java 文件。当一个编译单元(java文件)有多个非 public 类时,运行时需要对数据来源进行选择。

Java API文档

API(Application Programming Interface)是Java提供的基本编程接口(java提供的类和相关方法)。中文在线文档:https://www.matools.com

为什么说 Java 语言“编译与解释并存”?

高级编程语言按照程序的执行方式分为两种:

  • 编译型:编译型语言open in new window 会通过编译器open in new window将源代码一次性翻译成可被该平台执行的机器码。一般情况下,编译语言的执行速度比较快,开发效率比较低。常见的编译性语言有 C、C++、Go、Rust 等等。
  • 解释型:解释型语言open in new window会通过解释器open in new window一句一句的将代码解释(interpret)为机器代码后再执行。解释型语言开发效率比较快,执行速度比较慢。常见的解释性语言有 Python、JavaScript、PHP 等等。

.class -> 机器码这一步:

  • JVM 类加载器首先加载.class字节码文件,然后通过解释器逐行解释执行,这种方式的执行速度会相对比较慢。

JIT

有些方法和代码块是经常需要被调用的(也就是所谓的热点代码),所以后面引进了 JIT(Just in Time Compilation) 编译器,而 JIT 属于运行时编译。当 JIT 编译器完成第一次编译后,其会将字节码对应的机器码保存下来,下次可以直接使用。

而我们知道,机器码的运行效率肯定是高于 Java 解释器的。这也解释了我们为什么经常会说 Java 是编译与解释共存的语言

🌈 拓展:有关 JIT 的实现细节: JVM C1、C2 编译器

Java程序转变为机器代码的过程-图片来自JavaGuide

Java程序转变为机器代码的过程

HotSpot 采用了惰性评估(Lazy Evaluation)的做法,根据二八定律,消耗大部分系统资源的只有那一小部分的代码(热点代码),而这也就是 JIT 所需要编译的部分。JVM 会根据代码每次被执行的情况收集信息并相应地做出一些优化,因此执行的次数越多,它的速度就越快。

这是因为 Java 语言既具有编译型语言的特征,也具有解释型语言的特征。因为 Java 程序要经过先编译,后解释两个步骤,由 Java 编写的程序需要先经过编译步骤,生成字节码(.class 文件),这种字节码必须由 Java 解释器来解释执行。

AOT

什么是AOT?

JDK 9 引入了一种新的编译模式 AOT(Ahead of Time Compilation) 。和 JIT 不同的是,这种编译模式会在程序被执行前就将其编译成机器码,属于静态编译(C、 C++,Rust,Go 等语言就是静态编译)。AOT 避免了 JIT 预热等各方面的开销,可以提高 Java 程序的启动速度,避免预热时间长。并且,AOT 还能减少内存占用和增强 Java 程序的安全性(AOT 编译后的代码不容易被反编译和修改),特别适合云原生场景。

JIT 与 AOT 两者的关键指标对比:

AOTvsJIT

可以看出,AOT 的主要优势在于启动时间、内存占用和打包体积。JIT 的主要优势在于具备更高的极限处理能力,可以降低请求的最大延迟。

提到 AOT 就不得不提 GraalVMopen in new window 了!GraalVM 是一种高性能的 JDK(完整的 JDK 发行版本),它可以运行 Java 和其他 JVM 语言,以及 JavaScript、Python 等非 JVM 语言。 GraalVM 不仅能提供 AOT 编译,还能提供 JIT 编译。感兴趣的同学,可以去看看 GraalVM 的官方文档。如果觉得官方文档看着比较难理解的话,也可以找一些文章来看看,比如:

为什么不全部使用 AOT 呢?

既然 AOT 这么多优点,那为什么不全部使用这种编译方式呢?

我们前面也对比过 JIT 与 AOT,两者各有优点,只能说 AOT 更适合当下的云原生场景,对微服务架构的支持也比较友好。

除此之外,AOT 编译无法支持 Java 的一些动态特性,如反射、动态代理、动态加载、JNI(Java Native Interface)等。然而,很多框架和库(如 Spring、CGLIB)都用到了这些特性。如果只使用 AOT 编译,那就没办法使用这些框架和库了,或者说需要针对性地去做适配和优化。

举个例子,CGLIB 动态代理使用的是 ASM 技术,而这种技术大致原理是运行时直接在内存中生成并加载修改后的字节码文件也就是 .class 文件,如果全部使用 AOT 提前编译,也就不能使用 ASM 技术了。为了支持类似的动态特性,所以选择使用 JIT 即时编译器。

Oracle JDK vs OpenJDK

首先,2006 年 SUN 公司将 Java 开源,也就有了 OpenJDK。2009 年 Oracle 收购了 Sun 公司,于是自己在 OpenJDK 的基础上搞了一个 Oracle JDK。Oracle JDK 是不开源的,并且刚开始的几个版本(Java8 ~ Java11)还会相比于 OpenJDK 添加一些特有的功能和工具。

其次,对于 Java 7 而言,OpenJDK 和 Oracle JDK 是十分接近的。 Oracle JDK 是基于 OpenJDK 7 构建的,只添加了一些小功能,由 Oracle 工程师参与维护。

下面这段话摘自 Oracle 官方在 2012 年发表的一个博客:

问:OpenJDK 存储库中的源代码与用于构建 Oracle JDK 的代码之间有什么区别?答:非常接近 - 我们的 Oracle JDK 版本构建过程基于 OpenJDK 7 构建,只添加了几个部分,例如部署代码,其中包括 Oracle 的 Java 插件和 Java WebStart 的实现,以及一些闭源的第三方组件,如图形光栅化器,一些开源的第三方组件,如 Rhino,以及一些零碎的东西,如附加文档或第三方字体。展望未来,我们的目的是开源 Oracle JDK 的所有部分,除了我们考虑商业功能的部分。

最后,简单总结一下 Oracle JDK 和 OpenJDK 的区别:

  • 是否开源:OpenJDK 是一个参考模型并且是完全开源的,而 Oracle JDK 是基于 OpenJDK 实现的,并不是完全开源的(个人观点:众所周知,JDK 原来是 SUN 公司开发的,后来 SUN 公司又卖给了 Oracle 公司,Oracle 公司以 Oracle 数据库而著名,而 Oracle 数据库又是闭源的,这个时候 Oracle 公司就不想完全开源了,但是原来的 SUN 公司又把 JDK 给开源了,如果这个时候 Oracle 收购回来之后就把他给闭源,必然会引起很多 Java 开发者的不满,导致大家对 Java 失去信心,那 Oracle 公司收购回来不就把 Java 烂在手里了吗!然后,Oracle 公司就想了个骚操作,这样吧,我把一部分核心代码开源出来给你们玩,并且我要和你们自己搞的 JDK 区分下,你们叫 OpenJDK,我叫 Oracle JDK,我发布我的,你们继续玩你们的,要是你们搞出来什么好玩的东西,我后续发布 Oracle JDK 也会拿来用一下,一举两得!)OpenJDK 开源项目:https://github.com/openjdk/jdkopen in new window
  • 是否免费:Oracle JDK 会提供免费版本,但一般有时间限制。JDK17 之后的版本可以免费分发和商用,但是仅有 3 年时间,3 年后无法免费商用。不过,JDK8u221 之前只要不升级可以无限期免费。OpenJDK 是完全免费的。
  • 功能性:Oracle JDK 在 OpenJDK 的基础上添加了一些特有的功能和工具,比如 Java Flight Recorder(JFR,一种监控工具)、Java Mission Control(JMC,一种监控工具)等工具。不过,在 Java 11 之后,OracleJDK 和 OpenJDK 的功能基本一致,之前 OracleJDK 中的私有组件大多数也已经被捐赠给开源组织。
  • 稳定性:OpenJDK 不提供 LTS 服务,而 OracleJDK 大概每三年都会推出一个 LTS 版进行长期支持。不过,很多公司都基于 OpenJDK 提供了对应的和 OracleJDK 周期相同的 LTS 版。因此,两者稳定性其实也是差不多的。
  • 协议:Oracle JDK 使用 BCL/OTN 协议获得许可,而 OpenJDK 根据 GPL v2 许可获得许可。

作用

  1. 区分相同名字的类
  2. 当类很多时,可以很好的管理类(详见Java API文档)
  3. 控制访问范围

基本规则

  1. package 的作用是声明当前类所在的包,需要放在类的最上面,一个类中最多只有一句package
  2. import 指令位置放在 package 下面,在类定义前面,可以有多句且没有顺序要求

基本语法

1
2
3
4
5
6
7
8
// 声明
package com.rwj;
// package关键字:表示打包
// com.rwj:表示包名

// 导入/引入
import java.util.Scanner; //表示只会引入java.util包下的Scanner类
import java.util.*; //表示将java.util包下的所有类都引入

本质

创建不同的目录/文件夹来保存类文件。

命名规则

只能包含数字、字母、下划线、小圆点,不能用数字开头,不能是关键字或保留字

命名规范

1
com.公司名.项目名.业务模块名

com.sina.crm.user //用户模块

com.sina.crm.order //订单模块

com.sina.crm.utils //工具类

常用包

java.lang.*:lang 包是基本包,默认引入,不需要再手动引入

java.util.*:util 包,系统提供的工具包,工具类,使用 Scanner

java.net.*:网络包,网络开发

java.awt.*:做 java 界面开发,GUI


参考

Java基础常见面试题总结(上)

Java历史 - Java教程 - 廖雪峰的官方网站


Java 基础概念
https://blog-21n.pages.dev/2022/05/24/Java-基础概念/
作者
Neo
发布于
2022年5月24日
许可协议