大一下有过 Java 课程,但是当时主要是为了应试,现在已经忘完了(
这学期的算法课作业要求用 Java 完成,于是打算复习复习 Java 的基础部分(够用就行)。
基础
Java 程序基础
Java 程序基本结构
以 Hello, world!
为例。
public class Exercise {
public static void main(String[] args) {
System.out.println("Hello, world!");
}
}
因为 Java 是面向对象的语言,一个程序的基本单位就是 class
,class
是关键字,这里定义的 class
名字就是 Exercise
。
类名要求:
- 必须以英文字母开头,后接字母,数字和下划线的组合。
- 习惯以大写字母开头。
其中,public
是修饰符,表示该 class
是公开的。
在 class
内部,可以定义若干方法(method),如上代码中,有方法 main
,返回值是 void
,表示没有任何返回值。
注意到 public
除了可以修饰 class
外,也可以修饰方法。而关键字 static
是另一个修饰符,它表示静态方法。
方法命名要求与类名要求一样,但是首字母一般小写。
变量和数据类型
变量
在 Java 中,变量必须先定义再使用,定义时需要指定初始值。
public class Exercise {
public static void main(String[] args) {
int x = 10;
System.out.println("x="+x);
x=100;
System.out.println("x="+x);
}
}
如上代码,其输出为
x=10
x=100
基本数据类型
- 整数类型:
byte, short, int, long
- 浮点数类型:
float, double
- 字符类型:
char
- 布尔类型:
boolean
整型
Java 只定义了带符号的整型。
public class Exercise {
public static void main(String[] args) {
int a = 2_000_000_000; // 加下划线更容易识别
int b = 0xff0000; // 十六进制表示的 16711680
int c = 0b1000000000; // 二进制表示的 512
long d = 90000000000000L; // long 型结尾要加 L
long e = 900; // 没有加 L, 此处 900 为 int 类型,但是 int 类型可以赋值给 long
}
}
上面是一些正确的定义整型的例子。
但是 long
型无法赋值给 int
,如 int g=900L
就是错误的。
浮点型
一些浮点数定义的例子:
float f1 = 3.14f;
float f2 = 3.14e38f; // 科学计数法表示的3.14x10^38
float f3 = 1.0; // 错误:不带f结尾的是double类型,不能赋值给float
double d = 1.79e308;
double d2 = -1.79e308;
double d3 = 4.9e-324; // 科学计数法表示的4.9x10^-324
对于 float
类型,需要加上 f
后缀。
布尔类型
布尔类型 boolean
只有 true
和 false
两个值,布尔运算总是关系运算的计算结果。
字符类型
字符类型 char
表示一个字符。Java的 char
类型除了可表示标准的 ASCII 外,还可以表示一个 Unicode 字符。
引用类型
除了上述基本类型的变量,剩下的都是引用类型。例如,引用类型最常用的就是 String
字符串。
引用类型的变量类似于C语言的指针,它内部存储一个“地址”,指向某个对象在内存的位置。
常量
定义变量的时候,如果加上final
修饰符,这个变量就变成了常量,类似于 c++
的 const
。
为了与变量区分开来,常量名通常全部大写。
var
关键字
有时候类型名字太长,写起来很麻烦,例如
StringBuilder sb = new StringBuilder();
这时可以使用 var
关键字:
var sb = new StringBuilder();
整数运算
感觉与 C++
差不多,一样的部分不写了。
类型自动提升与强制转型
在运算过程中,如果参与运算的两个数类型不一致,那么计算结果为较大类型的整型。例如,short
和 int
计算,结果总是 int
,原因是 short
首先自动被转型为 int
。
也可以将结果强制转型,即将大范围的整数转型为小范围的整数。强制转型使用 (类型)
,例如,将 int
强制转型为 short
。
要注意,超出范围的强制转型会得到错误的结果,原因是转型时,int
的两个高位字节直接被扔掉,仅保留了低位的两个字节。
浮点数运算
浮点数会产生误差,所以判断两浮点数是否相等时,通常要设一个 eps
,作差后与 eps
比较。
类型提升
如果参与运算的两个数其中一个是整型,那么整型可以自动提升到浮点型。
溢出
整数运算除数为 0
时会报错,但是浮点数运算在除数为 0
时不会报错,会返回几个特殊值:
NaN
表示 Not a NumberInfinity
表示无穷大-Infinity
表示负无穷大
强制转型
可以将浮点数强制转型为整数。在转型时,浮点数的小数部分会被丢掉。如果转型后超过了整型能表示的最大范围,将返回整型的最大值。
一个应用:如果要进行四舍五入,可以将浮点数加上 0.5 后再强制转型。
布尔运算
运算符与 C++
一致,不写了。
短路运算
布尔运算的一个重要特点是短路运算。如果一个布尔运算的表达式能提前确定结果,则后续的计算不再执行,直接返回结果。
因为 false && x
的结果总是 false
,无论 x
是 true
还是 false
,因此,与运算在确定第一个值为 false
后,不再继续计算,而是直接返回 false
。
public class Exercise {
public static void main(String[] args) {
boolean b = 5 < 3;
boolean result = b && (5 / 0 > 0);
System.out.println(result);
}
}
如上代码,如果没有短路运算, &&
后面的表达式会因为除数为 0
而报错,但是 b
的值为 false
,由于短路运算,提前计算出了结果 false
。
||
类似。
三元运算符
类似 C++
的三目运算符,不写了。
字符串
字符串类型 String
是引用类型。
字符串连接
Java
中,可以用 +
连接任意字符串和其他数据类型。
如果用 +
连接字符串和其他数据类型,会将其他数据类型先自动转型为字符串,再连接。
多行字符串
从 Java 13
开始,字符串可以用
...
来表示多行字符串。
如:
public class Exercise {
public static void main(String[] args) {
String s = """
SELECT * FROM
users
WHERE id > 100
ORDER BY name DESC
""";
System.out.println(s);
}
}
输出为
SELECT * FROM
users
WHERE id > 100
ORDER BY name DESC
会以最短的行首空格为基准。
不可变特性
即“引用”特性。
public class Exercise {
public static void main(String[] args) {
String s = "hello";
String t = s;
s = "world";
System.out.println(t);
}
}
由于引用特性,该代码输出为 hello
。
空值 null
引用类型的变量可以指向一个空值 null
,表示不存在,即该变量不指向任何对象。
注意 null
和 空字符串 ""
的区别,空字符串是一个有效的字符串对象,它不等于 null
。
数组类型
定义一个数组类型的变量,使用数组类型“类型[]”,例如, int[]
。和单个基本类型变量不同,数组变量初始化必须使用 new int[5]
表示创建一个可容纳 5 个 int
元素的数组。
几个特点:
- 数组所有元素初始化为默认值,整型都是
0
,浮点型是0.0
,布尔型是false
; - 数组一旦创建后,大小就不可改变。
可以用 数组变量.length
获取数组大小。
数组是引用类型,在使用索引访问数组元素时,如果索引超出范围,运行时将报错。
也可以在定义数组时直接指定初始化的元素,这样就不必写出数组大小,而是由编译器自动推算数组大小,例如 int[] ns = new int[] { 68, 79, 91, 85, 62 }
,还可以进一步简写为 int[] ns = { 68, 79, 91, 85, 62 }
。
流程控制
输入和输出
输出
上面的例子中,总是使用 System.out.println()
来输出。
println
是 print line 的缩写,表示输出并换行。如果输出后不想换行,可以用 print()
。
格式化输出
格式化输出使用 System.out.printf()
,占位符有如下几种:
%d
,格式化输出整数%x
,格式化输出十六进制整数%f
,格式化输出浮点数%e
,格式化输出科学计数法表示的浮点数%s
,格式化字符串
感觉用法和 C++
的 printf
差不多。
输入
输入较为复杂。
这是从控制台读取一个字符串和一个整数的例子:
public class Exercise {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in); // 创建Scanner对象
System.out.print("Input your name: "); // 打印提示
String name = scanner.nextLine(); // 读取一行输入并获取字符串
System.out.print("Input your age: "); // 打印提示
int age = scanner.nextInt(); // 读取一行输入并获取整数
System.out.printf("Hi, %s, you are %d\n", name, age); // 格式化输出
}
}
首先,我们通过 import
语句导入 java.util.Scanner
,import
是导入某个类的语句,必须放到 Java 源代码的开头,后面我们在 Java 的 package
中会详细讲解如何使用 import
。
然后,创建 Scanner
对象并传入 System.in
。System.out
代表标准输出流,而 System.in
代表标准输入流。直接使用 System.in
读取用户输入虽然是可以的,但需要更复杂的代码,而通过 Scanner
就可以简化后续的代码。
有了 Scanner
对象后,要读取用户输入的字符串,使用 scanner.nextLine()
,要读取用户输入的整数,使用 scanner.nextInt()
。Scanner
会自动转换数据类型,因此不必手动转换。
if
条件判断
基本用法与 C++
相同,不写了。
判断引用类型相等
无法直接用 ==
来判断。
因为 ==
只能用来判断两个引用类型的变量是否指向同一个对象,有时候它们的内容相同,但是指向不同的对象。
判断引用类型的变量内容是否相等,必须使用 equals()
方法。
例如判断字符串 s1
与 s2
,s1.equals(s2)
。
switch
多重选择
与 C++
差不多,不写了。
which
循环
与 C++
差不多,不写了。
do while
循环
与 C++
差不多,不写了。
for
循环
与 C++
差不多,不写了。
for each
循环
int[] ns = { 1, 4, 9, 16, 25 };
for (int n : ns) {
System.out.println(n);
}
和 C++
高级版本里面更新的用法差不多。
break
和 continue
与 C++
差不多,不写了。
数组操作
遍历数组
用前面的 for
循环和 for each
循环都可以实现对数组的遍历。
数组排序
可以用 Java 标准库内的 Arrays.sort()
函数来实现对数组的从小到大排序。
int[] ns = { 28, 12, 89, 73, 65, 18, 96, 50, 8, 36 };
Arrays.sort(ns);
多维数组
一个定义二维数组的例子:
public class Exercise {
public static void main(String[] args) {
int[][] ns = {
{ 1, 2, 3, 4 },
{ 5, 6, 7, 8 },
{ 9, 10, 11, 12 }
};
System.out.println(ns.length); // 3
}
}
要打印一个二维数组,可以用两层嵌套的 for
循环:
public class Exercise {
public static void main(String[] args) {
int[][] ns = {
{ 1, 2, 3, 4 },
{ 5, 6 },
{ 7, 8, 9 }
};
for (int[] arr : ns) {
for (int n : arr) {
System.out.print(n);
System.out.print(", ");
}
System.out.println();
}
}
}
或者使用 Java 标准库的 Arrays.deepToString()
:
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
int[][] ns = {
{ 1, 2, 3, 4 },
{ 5, 6 },
{ 7, 8, 9 }
};
System.out.println(Arrays.deepToString(ns));
}
}
类似的,也可知三维数组的定义与遍历。