前言
做了这么久的Java开发,也非常喜欢这门语言,却对它本身了解不多。而我又报名参加了2020年的蓝桥杯Java组,并且打算参加软件杯和C4,为了能把这些比赛打好,在下学期的Java程序设计课里拿个高分(虽然能不能选上这门课还不一定),也为了能对Java语言有一个更深入、系统的了解,不在以后的开发中对语言本身感到困惑,所以打算写这个系列,从零开始重新学习Java基础知识,并对第一次学习掌握得不好的部分重新记录。
参考资料
javac和java命令做了什么?
我们在安装好JDK之后开始写HelloWorld
程序时,都会用到javac
和java
两条命令。那么这两条命令为我们做了些什么呢?
javac
命令启动了一个Java编译器,它把后缀名为.java
的Java源码文件编译成了后缀名为.class
的字节码文件。java
命令则启动了JRE
——Java运行时环境(Java Runtime Environment)和JVM
——Java虚拟机(Java Virtual Machine),装载刚才编译好的.class
文件并以解释的方式执行其中的字节码。
(图片来自Java300集)
所以,我们可以很清楚地看出为什么Java是半编译半解释的语言——因为源码需要先编译成字节码,字节码在JVM中又是解释执行的。
这里如果展开了说还会涉及到Java语言、JVM的底层原理,比如JVM是怎么加载这个类,怎么执行其中的字节码,还有JIT即时编译、双委派模型之类的,这里先不考虑这些细节,在后面学习JVM基础知识的部分再去详细展开。
JDK、JRE和JVM的关系
JRE并不等于JVM。
除了JVM之外,JRE其实还包含一些库函数和必须的文件,所以JRE其实是包含JVM的。
JDK则在JRE的基础上又增加了编译器、调试器等开发需要的东西。
JDK的目录结构
- bin:二进制文件,比如
java
、javac
之类的,也就是我们用到的命令 - db:存放一些数据
- include:一些需要包含的头文件
- jre:就是JRE
- lib:库目录,存放了一些jar包
- src.zip:JDK源码,不过为啥我macOS版的Java11没这个呢?
标识符
标识符其实就是一个名字,用来给变量、包、类等命名。
标识符有一定的规则——
- 所有的标识符都应该以字母(A-Z 或者 a-z),美元符($)、或者下划线(_)开始
- 首字符之后可以是字母(A-Z 或者 a-z),美元符($)、下划线(_)或数字的任何字符组合
-
class
等Java关键字不能用作标识符
注意:Java使用的是Unicode标准字符集,所以可以用中文命名变量。
数据类型
在Java中,一共有8种基本类型(primitive type),其中有4种整型、2种浮点类型、1种用于表示Unicode 编码的字符单元的字符类型char和1种用于表示真值的boolean类型。
除此之外,都是引用数据类型。它们占四个字节,用来表示一个对象的地址(类似于C++的指针)。
注意:Java有一个能够表示任意精度的算术包,通常被称为大数值(BigDecimal),但它并不是一种新的Java类型,而是一个Java对象。
变量的类型、长度与范围
整数类型
类型 | 长度 | 取值范围 |
---|---|---|
int | 4字节 | -2147483648~2147483647 |
short | 2字节 | -32768~32767 |
long | 8字节 | -9223372036854775808~9223372036854775807(-264~264-1) |
byte | 1字节 | -128~127 |
注意:
长整型数值有一个后缀 L 或 l (如 4000000000L) 十六进制数值有一个前缀 0x 或 0X ( 如 0xCAFEL 八进制有一个前缀0,例如,010 对应八进制中的 8。 很显然, 八进制表示法比较容易混淆, 所以建议最好不要使用八进制常数。并且,由于l容易和数字1、大写的I混淆,所以一般都使用大写的L表示长整型数。
从 Java 7 开始, 加上前缀 0b 或 0B 就可以写二进制数。 例如, 0b1001就是 9。 另外, 同样是 从Java7开始,还可以为数字字面量加下划线,如用1_000_000表示一百万,不会影响到编译结果。
关于boolean的长度问题
Java规范中并没有明确地指出boolean类型的长度。但是,并不能说boolean的长度就不确定了:
在《Java虚拟机规范》一书中的描述:“虽然定义了boolean这种数据类型,但是只对它提供了非常有限的支持。在Java虚拟机中没有任何供boolean值专用的字节码指令,Java语言表达式所操作的boolean值,在编译之后都使用Java虚拟机中的int数据类型来代替,而boolean数组将会被编码成Java虚拟机的byte数组,每个元素boolean元素占8位”。也就是说JVM规范指出boolean当做int处理,也就是4字节,boolean数组当做byte数组处理,这样我们可以得出boolean类型占了单独使用是4个字节,在数组中是确定的1个字节。
from: https://blog.csdn.net/YuanMxy/article/details/74170745
虽说单独使用的boolean
是跟int
一样占用四个字节,但是boolean
和int
不能相互转换。
浮点类型
类型 | 长度 | 取值范围 |
---|---|---|
float | 4字节 | 大约 ± 3.402 823 47E+38F (有效位数为 6 ~ 7 位) |
double | 8字节 | 大约 ± 1.797 693 134 862 315 70E+308 (有效位数为 15 位) |
浮点数不仅可以用小数点表示,也可以用科学计数法来表示,例如314e2
、314E2
。
注意:float类型的数值有一个后缀F或f (例如,3.14F) 没有后缀F的浮点数值(如3.14) 默 认为 double 类型。当然, 也可以在浮点数值后面添加后缀 D 或 d ( 例如,3.14D)。
注意2:Java中有三个特殊的浮点数值,它们可以用一些特殊的常量来表示:
- 正无穷大——
Double.POSITIVE_INFINITY
- 负无穷大——
Double.NEGATIVE_INFINIT
- NaN(不是一个数字)——
Double.NaN
可以用Double.isXxx
方法,例如Double.isNaN
来检测需要检测的数是不是这三个常数。
注意3:与C/C++一样,浮点数类型在计算时可能会产生误差,因为浮点数并不是精确存储的,可能产生舍入误差,所以也最好不要比较两个浮点数。如果业务要求不能产生误差或者必须要比较两个浮点数,需要使用BigInteger
(整数)或者BigDecimal
(浮点数)类。
注意4:Java提供了PI与E的近似值常量——Math.PI
、Math.E
。
字符类型
char
类型可以表示的字符范围为\u0000
到\uffff
。
注意:Unicode转义序列会在解析代码之前被处理,所以必须小心注释中的\u
,它们可能会引起一些意想不到的错误。
自动类型转换
下图为Java不同数值类型之间的合法转换(实线代表不会损失精度,虚线代表会损失精度。图片来自《Java核心技术》):
强制类型转换
Java的强制类型转换与C/C++基本相同,但是在面向对象部分,有些情况下不可以使用强制类型转换,这一点在后面复习面向对象时再详细总结。
变量
变量的本质
变量的本质其实就是内存中的一个可操作、固定长度的存储空间(不过据说boolean的长度是不确定的),在内存中的地址是确定的,但是里面放置什么值是由程序运行决定的。我们可通过变量名来访问对应的存储空间,从而操纵这个存储空间里存储的值。
Java是一种强类型语言,声明变量必须在变量名前先声明其类型,但是在Java11中,能自动推断出类型的可以不显示指定类型而使用var
关键字指定,非常方便。
变量在使用前必须声明, 只有在变量声明以后,才能为其分配相应长度的存储空间,所以在使用变量之前必须声明。
注意:使用没有初始化局部的变量会报错,但是声明了变量不使用则不会报错(这跟Go语言不一样)。
变量的分类
Java的变量与C++一样,分为三种:
- 局部变量
- 静态变量
- 成员变量/实例变量
成员变量的初值
成员变量从属于对象,生命周期伴随对象始终。
成员变量如果没有初始化,会有一个默认值:
类型 | 默认值 |
---|---|
byte | 0 |
short | 0 |
int | 0 |
long | 0 |
float | 0.0 |
double | 0.0 |
char | '\u0000' |
boolean | false |
String等引用类型 | null |
静态变量
静态变量(使用static
声明的变量)从属于类,是生命周期最长的,生命周期为从类的加载到类的卸载。
常量
与C/C++不同,常量使用final
关键字声明,一旦有值,其值就不能发生变化。
final
关键字除了用于常量(不能修改的变量)之外,还有以下作用:
- 使用
final
修饰的类不能被继承 - 使用
final
修饰的方法不能被重写
这在后面复习面向对象的时候再详细学习。
虽然Java不使用const
修饰常量,但是const
是Java的一个保留关键字,不能使用。
运算符
Java中有以下运算符(图片来自Java300集):
算术运算符
除了++
和--
以外都属于二元运算符,二元运算符运算时遵守以下规则:
- 如果两个操作数有一个为Long, 则结果也为long。
- 没有long时,结果为int。即使操作数全为short,byte,结果也是int。
- 如果两个操作数有一个为double,则结果为double。
- 只有两个操作数都是float,则结果才为float。
- 取模操作其操作数可以为浮点数,但是一般使用整数。结果是“余数”,“余数”符号和左边操作数相同,如:7%3=1,-7%3=-1,7%-3=1。
++
与--
的运算规则与C/C++等语言相同。
赋值运算与结合赋值运算
Java一共有以下几种结合赋值运算符:
+=
-=
*=
\=
%=
用法与C/C++相同。
注意:如果整数除以0会抛出一个异常,而浮点数除以0则会得到无穷大或者是NaN。
关系运算符
Java的关系运算符一共有以下几种:
==
!=
>
<
>=
<=
用法同样与C/C++相同。
逻辑运算符
Java 沿用了 C++ 的做法,使用&&
表示逻辑“与”运算符,使用||
表示逻辑“或”运算符。从 != 运算符可以想到,感叹号!
就是逻辑非运算符。&&和||运算符是按照“短路”方式来求值的: 如果第一个操作数已经能够确定表达式的值,第二个操作数就不必计算了。
位运算符
Java中的位运算符与C/C++的基本相同,个人感觉这些东西在实际开发中很难用到,除非是在性能要求非常高的地方。
(图片来自Java300集)
运算符的优先级
Java运算符的优先级也与C/C++基本相同。在实际使用中一般很少会遇到需要考虑优先级的问题,一般都使用小括号来解决运算符的优先级带来的问题,因为小括号的优先级最高。
(图片来自Java300集)