注释
注释分为3类:
单行注释:
//
其后的部分被视为注释。1
Sysout.out.print("context") //annotation
多行注释:
/* xxx */
内的xxx被视为注释,可以包含多行。1
2
3
4
5/*
annotation1
annotation2
annotation3
*/文档注释型:这种注释以
/**
开始,以*/
结束,这种注释可以用来自动生成文档。1
2
3
4
5/**
annotation1
annotation2
annotation3
*/
数据类型
Java有8中基本类型(primitive type),其中有4种整型、2种浮点类型、1种字符类型和一种表示真假值的Boolean类型。
整型
类型 | 存储需求 | 取值范围 |
---|---|---|
int |
4字节 | -2147483648~2147483647(刚好超过20亿) |
short |
2字节 | -32768~32767 |
long |
8字节 | -9223372036854775808~9223372036854775807 |
byte |
1字节 | -128~127 |
Note:
在Java中,整型的范围与运行Java代码得机器无关。
长整型数值有一个后缀L或l。
1
Long lVal = 999999999999999l;
从java7开始,可以为数字字面量添加下划线,如
9_999_999
即为9999999
。
浮点类型
类型 | 存储需求 | 取值范围 |
---|---|---|
float | 4字节 | 大约±3.402823E+38F(有效位数为6~7位) |
double | 8字节 | 大约±1.79769313486231570E+308(有效位数为15位) |
Note:
定义
float
类型时,其数值有一个后缀F
或者f
。没有后缀F
或f
会被默认视为double类型。(实际上也很少使用float
,大多使用double
)。常量
Double.POSITIVE_INFINITY
、Double.NEGATIVE_INFINITY
和Double.NaN
分别表示IEEE754规定的用于表示溢出和出错情况的三个特殊浮点数值:- 正无穷大
- 负无穷大
NaN
(Not a Number)
不能用
==
判断一个数是否为NaN
,而应该用Double.isNaN(param)
。浮点数存在误差,如经典的(2.0 - 1.1) = 0.899999999999, 而不是0.9。这是由于浮点数值采用二进制系统表示,而在二进制系统中无法精确的表示分数1/10。这就像无法用十进制精确的表达1/3一样。如果需要精确的计算,则应该采用
BigDecimal
类。
char类型
char类型原本表示单个字符,占2字节。不过现在一些Unicode字符需要两个char值来表达。
char类型表示的是十六进制值,其范围是从\u0000
到\uffff
。如\u2122
表示商标(™)。
除了转移序列\u
之外,还有一些用于表示特殊字符的转移序列。参见下表。
转移序列 | 名称 | Unicode值 |
---|---|---|
\b |
退格 | \u0008 |
\t |
制表 | \u0009 |
\n |
换行 | \u000a |
\r |
回车 | \u000d |
\" |
双引号 | \u0022 |
\' |
单引号 | \u0027 |
\\ |
反斜杠 | \u005c |
Note:
Unicode转义序列会在解析代码之前得到处理。也就是说,Java源代码的任意字符都可以使用 Unicode来编写。
以此会产生一些隐晦的语法错误(现在编译器都会提示),比如:
1
// look inside c:\users
由于
\u
并没有跟着4个十六进制数。
变量与常量
从jdk10开始,对于局部变量,如果可以从变量的初始值推断出它的类型,就可以不声明变量类型。如:
1
2var vacationDays = 12;
var greeting = "Hello";常量的定义使用关键字
final
,并且其只可以被赋值一次(注意并不是只可以在声明时赋值,可以在声明后在构造器方法中赋值,但是仅可以被赋值一次)。
运算符
在Java中,使用+,-,*,/表示加、减、乘、除运算。
- 对于/运算,当两个操作数都是整数时,表示整数除法(即只返回整数部分),否则表示浮点除法。
- 对于%运算,表示求余操作,其操作数可以为整型或者浮点型。
数学函数
在Math
类中,为了达到最佳的性能,所有的方法都是用计算机浮点单元中的例程。如果要求计算更加准确而不是在乎速度,则可以使用StrictMath
类,其API与Math
类完全相同。
数值类型的转换
隐式转换
图中展示出各种数值类型的转换,其中实线表示可以无损转换,而虚线表示转换会损失精度。
当一个二元操作符连接两个不同类型的值时,需要先将其转换为同一种类型才能进行运算。这种隐式转换的规则如下:
- 如果两个操作数中有一个是
double
类型,另一个操作数就会转换为double
类型。 - 否则,如果其中一个操作数是
flaot
类型,另一个操作数就会转换成为float
类型。 - 否则,如果其中一个操作数是
long
类型,另一个操作数将会转换为long
类型。 - 否则,两个操作数都将会被转化为
int
类型。
强制类型转换
在Java中,通过:(type) variable
来完成强制类型转换,如:
1 | double pi = 3.1415926; |
注意以上的转换会直接截断小数点。如果需要四舍五入,则可以使用Math.round()
函数来实现。
运算符优先级
如下表
优先级 | 运算符 | 结合性 |
---|---|---|
1 | ()、[]、{} | 从左向右 |
2 | !、+、-、~、++、-- | 从右向左 |
3 | *、/、% | 从左向右 |
4 | +、- | 从左向右 |
5 | «、»、>>> | 从左向右 |
6 | <、<=、>、>=、instanceof | 从左向右 |
7 | ==、!= | 从左向右 |
8 | & | 从左向右 |
9 | ^ | 从左向右 |
10 | | | 从左向右 |
11 | && | 从左向右 |
12 | || | 从左向右 |
13 | ?: | 从右向左 |
14 | =、+=、-=、*=、/=、&=、|=、^=、~=、«=、»=、>>>= | 从右向左 |
字符串
从概念上将,Java的字符串就是Unicode的字符序列。如,字符串”Java\u2122”由5个Unicode字符J、a、v、a和™组成。并且String并不是基础类型之一,所以在标准的Java类库中提供了一个预制类称为String。
String类没有提供修改字符串中某个字符的方法。如果希望修改字符串中某个位置的值,可以使用substring()
方法来实现。
不提供修改方法的原因主要在于可以共享字符串的值,将其存储在一个共享池中,提升效率。详细在jvm虚拟机的内存模型时可以更加详细的了解。
检测字符串是否相等
在检测字符串是否相等时,一般不使用==
,这是由于实际上只有通过字面量定义的字符串是共享的,而通过构造器或者substring()
得到的方法并不是共享的。所以:
- 如果两个字符串都是用字面量定义的,则可以使用
==
来判断。 - 其他情况下,都应当
equal()
方法来判断相等关系。 - 综上,还是尽量使用
equal()
方法来判断,防止不可预知的bug。
底层
jdk9之前,String底层是使用char值序列组成,而jdk9中,其底层变成了byte值序列。
构建字符串
有些时候,需要由较短的字符串来构建字符,这时如果采用字符串拼接会比较消耗性能,这时候可以采用StringBuilder
类来构建字符串。示例如下:
1 | StringBuilder builder = new StringBuilder(); |
输入输出
读取输入
1 | Scanner in = new Scanner(System.in); |
格式化输出
Sysout.print.out(x)
将数值x输出到控制台。
Sysout.out.printf()
可以进行格式化输出。如:Sysout.out.printf("%8.2f", x)
。这种语法沿用了C语言函数库中的printf()
方法。
其第一个参数格式如下:
分别解释每一项:
%
:表示格式化输出的标识符。argument index
+$
一起表示参数索引,即Sysout.out.printf(FORMAT, x,...restArg)
可以接受多个参数,其中1$
,2$
,n$
表示对第一个,第二个,第n个参数进行特定的格式化。flag
:指定控制格式化输出外观的各种标志,如下表:标志 目的 举例 + 打印数字前的符号 +3333.33 space 在正数之前加空格 | 3333.33| 0 在数字前补0 003333.33 - 左对齐 |3333.33 | ( 负数括在括号内 (3333.33) , 添加分组分隔符 3,333.33 # (for f ) 包含小数点 3,333. # (for x or o) 添加前缀 0x 或 0 0xcafe ^ 转化为大写 0XCAFE $ 指定格式化参数索引,如%1$d,%1$d表示以十进制
和十六进制打印第一个参数159 9F < 格式化前面参数,如%d%<x表示以十进制和十六进
制打印同一个参数159 9F width
:表示输出的位宽,如果原内容不足width
,则填空格。t
+conversion character
:转换符指示要格式化的数据类型。所有内容如下表:转换符 类型 举例 d 十进制整数 159 x 十六进制整数 9f o 八进制整数 237 f 定点浮点数 15.9 e 指数浮点数 1.59e+01 g 通常浮点数 a 十六进制浮点数 0x1.fccdp3 s 字符串 Hello c 字符 H b 布尔型 TRue h 散列码 42628b2 tx 日期时间 见时间介绍表 % 百分号 % n 分隔符 .
+precision
:表示数值输出的小数点位数,如果不足后面补0。
文件输入输出
读取文件
1 | Scanner in = new Scanner(Path.of("file.txt"), StanardCharsets.UTF_8); |
写入文件
1 | PrintWriter out = new PrintWriter("file.txt", StanardCharsets.UTF_8); |
流程控制
在Java中没有goto
语句,但是设计了另外一种语法来实现其一部分语法。
带标签的break
语法
可以使用带标签的break来讲程序跳转到确定位置。标签定义后加冒号,其后可以跟任何代码块。break
后加标签名字。例如:
1 | // 循环中使用 |
大数
如果基本的整数和浮点数精度不能满足需求,则可以使用java.Math
包中的两个类:
BigInteger
:大整数。BigDecimal
:大浮点数。可以使用vlaueOf方法将普通的数值转换为大数。
1
BigInteger big = BigInteger.valueOf(1000);
可以使用构造器传入字符串数值。
1
BigInteger big = new BigInteger("99999999999999999999999999999999999999999999999999999999999999999999999999999999")
但值得注意的是,大数不能使用普通的算术运算符(如+, -, *, /)。而是要使用大数类的add
,multiply
方法等来进行运算。如
1 | BigInteger c = a.add(b); |
数组
数组声明
1 | int[] a; // 一般使用这种,因为更容易区分变量名和数据类型 |
数组初始化
1 | // 声明和初始化分开 |
一旦创建了数组,就无法改变它的长度。
如果需要可改变大小的数组,可以使用
ArrayList
。注意通过字面量方法创建数组时,不需要提供数组大小,也不需要
new
操作符。创建一个数字数组时,所有元素都初始化为0;Boolean数组的元素会初始化为false;对象数组的元素则初始化为一个特殊值
null
。
数组拷贝
可以使用Arrays.copyOf(originalArray, newArrayLength)
来创建一个新的数组,并将原数组复制到新数组中,可以间接实现改变数组大小。
1 | int[] a = {1,2,3}; |
Note:当新数组的长度小于原数组,则会截断原始数组。
命令行参数
主函数main(String[] args)
的参数是作为命令行参数。如:
1 | java A -g param1 param2 |
则结果为:
1 | args[0]: "-g"; |