第一章
一、jdk,jre,jvm
1.jdk
是Java程序的开发工具包,面向程序开发者,内包含了jre。
2.jre
是Java程序的运行环境,面向Java程序的使用者。提供了Java程序运行时所需要的类库和其他资源。
3.jvm
是Java虚拟机,是Java的核心组件,主要用于解释和执行字节码文件。它是Java跨平台的技术支持。程序通过在不同的操作系统上使用不同的JVM虚拟机运行相同的字节码文件,就会得到相同的运行效果。
二、环境变量
1.JAVA_HOME
配置jdk安装的根路径,它的作用主要是为其他软件读取使用。一些需要依赖Java环境运行的软件,在其启动时,会读取该环境变量值,以此得知Java的安装路径。
2.CLASSPATH
类加载器查找字节码文件的路径,变量名为 .
3.Path
是windows自带的环境变量。不需要新建path环境变量。其配置的只要作用是的命令窗口实现Java命令时不需要进入到Java的安装目录去执行,而是直接在命令窗口直接执行。
三、案例
1.程序运行的过程
- 创建源代码⽂件,Java源代码⽂件后缀为.java
- 编写源代码⽂件,定义类、⽅法和语句
- 使⽤javac⼯具编译源代码,得到字节码⽂件
- 运⾏字节码⽂件,启动Java程序
2.具体步骤
- 在磁盘某一路径下建立HelloWorld.java文件
- 写入以下代码
public class HelloWorld {
public static void main(String[] args) {
System.out.println("HelloWorld!");
}
打开cmd进行编译运行
小技巧:可以在资源管理器的地址栏输入cmd即可在当前文件夹下打开cmd,不需要再用cd去切换。
类使⽤public修饰有两个作⽤:
1)声明该类是当前源代码⽂件中的主类
⼀个源代码⽂件中可以定义多个类,但是最多只能有⼀个主类(最多只能有⼀个使⽤public修饰的类)。
如果某个类使⽤了public修饰,那么它的名称就必须与当前源代码⽂件的名称保持⼀致。
2)声明该类是⼀个公共类
main()⽅法是整个Java程序的⼊⼝,⽆论多么庞⼤的Java程序,都有且只有⼀个⼊⼝作为运⾏起点。
注意:此时的文件名必须与文件中的主类名一致,若文件中不存在主类,则可以不同。
- 编译源代码
javac HelloWorld.java
会生成HelloWorld.class文件。这个⽂件称之为bytecode
(字节码⽂件)或类⽂件。
字节码表示该⽂件中内容的格式,类⽂件表示该⽂件中内容的性质。
注意:此时生成的.class文件为java文件中的 类.class文件,有几个类,就会生成几个class文件。
- 运⾏程序,命令如下
java HelloWorld
注意:不要加.class后缀。此时java 后面跟的类名必须是包含有main方法的类名,才可以执行。
3.字符编码问题
什么是编码?
计算机中所有的⽂本信息/字符信息在处理和存储的时候都是以字符编码的形式进⾏。
计算机底层是基于⼆进制的,⽆法直接存储字符内容,所以需要制定⼀个编码表(也叫字符集),收录需要使⽤的
字符,并为每个字符指定⼀个数字编号。
具体包含两个过程:
- 字符编码
字符内容需要存储或传输,就到特定的编码表中找每个字符对应的码值,使⽤这些码值进⾏存储或传输。 - 字符解码
字符内容需要读取或接收,也到特定的编码表中找到码值对应的字符内容并显示出来。
注意:编码和解码需要采⽤相同的编码表。相同汉字在不同的编码表中位置不同。如果使⽤字符集A的码值到字符集B中解码,就会产⽣错乱的结果。
所有乱码问题的根本原因
- 存储字符(编码)和读取字符(解码)采⽤的编码表不⼀致所导致的。
解决方法
本案例中的编码和解码
- 编码
创建了.java源⽂件,⽤记事本打开编辑了内容并保存,记事本会有⼀个保存⽂件使⽤的编码格式设置。
保存的格式:UTF-8 - 解码
javac编译器读取源代码时候采⽤的解码格式
读取的格式:跟随操作系统的默认编码,⽽Windows的默认编码格式是ANSI。
ANSI编码并不是⼀种特定的字符编码,⽽是⼀种地区化编码的统称。例如,在美国ANSI就表示ASCII,
在中国ANSI就表示GBK。
可以简单理解为在中国Windows系统的默认编码就是GBK。
- 修改源代码⽂件保存的格式为GBK
记事本->⽂件->另存为->选择编码->保存->选择覆盖源⽂件 - 指定javac编译时使⽤UTF-8
在使⽤javac命令的时候添加⼀个选项及参数-encoding utf-8
javac -encoding utf-8 HelloWorld.java
javac HelloWorld.java -encoding utf-8
面试题
以下选项中,( A )是Java编程的最⼩单元。
A. 类 B. 对象
C. 变量 D. 数据
哪怕程序中只有⼀条语句,这条语句也不能单独存在。必须要定义在⼀个⽅法当中。哪怕程序中只有⼀个⽅
法,那么这个⽅法也不能单独出现,必须被定义在⼀个类中。
结论:哪怕程序中只需要运⾏⼀条语句,也需要为它定义⼀个Java类
4.源代码与字节码的关系
- ⼀个.class⽂件中保存的就是⼀个Java类的完整信息,.class⽂件的名称就与源代码中定义的类名是相同的,与源代码⽂件本身的名称没有关系。
- .class⽂件和Java类的数量⼀定是⼀⼀对应。不可能在⼀个.class⽂件中包含多个类的信息,也不可能把⼀个类的信息拆分为多个.class⽂件
笔试题
有⼀个源代码⽂件名为HelloWorld.java(不存在语法错误)。使⽤javac对该⽂件进⾏编译输出的结果是什么?
答案
1)输出的⽂件数量?
完全⽆法确定
2)输出的⽂件名称?
完全⽆法确定
四、顶级元素
顶级元素:直接定义在源代码⽂件中的内容,没有被其他内容所包含。
源代码中的顶级元素可以有三种:
- package
- import
- class
注意,上⾯三种顶级元素都可以不出现,但如果出现则必须按照上⾯的顺序定义。
1.package
package⽤来为当前源代码⽂件中的类定义包名前缀。
包:类似资源管理器中⽂件夹的概念,主要作⽤是对Java类进⾏分类存储和管理。
包的本质:在⽂件系统中就是⽬录的嵌套结构。
定义了包之后,类的名称就发⽣了改变。每个类现在拥有了两个名字:
- 简略类名(⼩名)
源代码中class关键字后边定义的名称。 - 完全限定性类名(⼤名)
包名.类名形式,也叫全限类名
包的定义规范
通常采⽤公司域名倒置形式作为包名前缀,例如:
百度:com.baidu.xxxx
阿⾥:alibaba.com
杰普:com.briup.xxxxx
源代码⽂件中定义了package之后,编译源代码的时候最好加上-d选项以及参数,有两个作⽤:
1、让编译器在编译的时候⾃动根据源代码中的package声明⽣成对应的⽬录结构
2、指定把这套包+类结构输出到什么地⽅
⼀旦类加上了package声明,那么在java命令中启动它的时候就要输⼊该类的全限类名。
2、import
需求:程序启动后,打印输出当前系统时间。
API中存在⼀个类Date,表示⽇期和时间。
只要创建⼀个Date类对象,并且将它打印输出,就可以获得当前系统时间
import:引⼊,把某个其他包下的类,暂时引⼊到当前命名空间
import java.util.Date;
可以理解为如下声明:但凡是在当前源代码⽂件中出现了名为Date的标识符,指的都是java.util.Date⽽不是其他
如果引用同一个包比较多,就可以使用import java.until.*;来引入包下所有的类。
五、Java程序完整生命周期
上图为Java代码的编译和运行过程图
一个程序经过编译以后得到的.class文件可以通过各种方式被加载到JVM内存中。
当一个程序被加载到JVM虚拟机内存后,首先要经过字节码验证,验证内容有:
- 验证当前class文件版本和当前JVM版本是否兼容
- 检查当前代码是否会破坏系统的完整性
- 检查当前代码是否有栈溢出的情况
- 检查当前代码中的参数类型是否正确
- 检查当前代码中的类型转换操作是否正确
验证完成后,确定哪些代码是解释执行的,哪些代码是JIT即时编译执行
- 解释执行
class文件的内容交给JVM虚拟机进行解释执行。解释一行就执行一行代码。 - JIT(Just In Time)即时编译
如果在Java代码中,相同的代码程序过多,则被称为热点代码。JVM可以Java中的热点代码直接编译成计算机可以可以运行的二进制指令,后续再调用这个热点代码的时候,可以直接运行编译好的指令。
六、GC(垃圾回收器)
Java代码中,开辟要使用的内存空间,使用new关键字即可完成。
使用完之后,对内存的释放,在JVM中,由垃圾回收器(Garbage Collection,GC)来完成。
不同类型的GC,在JVM中,会根据不同的算法,对不同的内存区域内标记为垃圾的空间,进行回收释放。在这个过中,是不需要编程人员干预的,它自己会主动的完成。
七 、java 和javac 的命令参数
javac -d 存放的文件目录 要编译的文件名.java
作用:编译源文件获得的字节码文件(存放到-d后的目录下),如果不跟-d,则会存放在当前目录下。
注意:如果源代码被打包,如不加-d 则字节码文件不会生成在对应的包下。
java -cp 字节码文件存储的目录 类的全包名
作用:用于找到字节码文件所在的文件,然后通过全包名查找解释执行该文件。
在工程化项目中往往将代码写在src文件中,而生成的字节码文件放在同文件夹下的bin文件中。对src文件中的代码进行编译有两种方式:
- 全部一次性编译
javac -d ./bin ./src/*.java
但是此种方法有一个弊端,如果某一个源文件中出现语法错误等,那么全部的编译工作将会失效。
- 逐个编译
注意:如果其中的一些源文件之间有调用的关联,那么编译就存在顺序问题。谁调用谁,被调用的先进行编译。假设World.java文件中调用了Student.java为一个新的对象,那么先编译Student.java。
javac -d ./bin ./src/Student.java
然后编译World.java ,此时必须找到Student.class生成的字节码文件
javac -d ./bin -cp ./bin ./src/World.java