本篇笔记介绍了 Java 编程的基础知识,包括标识符规则、变量声明与初始化、final 关键字的使用、字面量与类型转换、选择语句(if-else、switch)、运算符结合性、字符与字符串处理、输入输出、循环语句、方法定义与重载、数组的创建与使用等内容。特别强调了 Java 与 C/C++ 的一些重要区别,如变量初始化要求、布尔类型使用规则等。同时详细讲解了 String 类的常用方法、Scanner 类的输入处理、格式化输出、方法重载的规则以及数组的内存分配特点。(由 claude-3.5-sonnet 生成摘要)
1. Ch02 Elementary Programming
1.1. Identifiers
- 标识符(indentifier) 是由字母、数字、下划线
_
和美元符号$
组成的字符序列。 - 规则:
- 必须以字母、下划线
_
或美元符号$
开头,不能以数字开头。 - 不能是 Java 的保留字。
- 不能是
true
、false
或null
。 - 区分大小写。
- 长度没有限制。
- 必须以字母、下划线
1.2. Variables
- 变量(variable) 是存储数据的容器。
- 可以在一步中完成变量的声明和初始化。
- Java 变量与 C/C++ 变量的区别
- 在 Java 中,变量的声明和定义没有区分。
- Java 不会为方法中的局部变量赋默认值,必须显式初始化,否则会报 CE。
1.3. The final
Keyword
- 使用
final
修饰变量得到 命名常量(named constant)。- final 变量是不可改变的,只能在初始化的时候被改变一次,但是这个初始化可以在运行时刻初始化,也可以在编译时刻初始化,甚至可以放在构造函数中初始化,而不必在声明的时候初始化。
- 举例:定义类时写
final int x;
然后在构造函数中在初始化x = 1;
是合法的。
- 举例:定义类时写
- 修饰对象引用时,不能改变引用指向的对象,但不意味着不能改变对象实例的内容。
- final 变量是不可改变的,只能在初始化的时候被改变一次,但是这个初始化可以在运行时刻初始化,也可以在编译时刻初始化,甚至可以放在构造函数中初始化,而不必在声明的时候初始化。
- 使用
final
修饰方法表示该方法不能被 重写(override)。 - 使用
final
修饰类表示该类不能被继承,里面的所有方法也自动会是final
的。
1.4. Literals
- 使用
f
结尾表示单精度浮点数字面量,使用d
结尾表示双精度浮点数字面量。 - 特殊的浮点数值:
Double.POSITIVE_INFINITY
:正无穷大。Double.NEGATIVE_INFINITY
:负无穷大。Double.NaN
:非数值。
1.5. Type Casting
- 隐式转换(从小范围到大范围):如
double d = 3;
- 显式转换(从大范围到小范围):如
int i = (int)3.9;
- 像这样的转换必须显示地写出,否则会 CE。
2. Ch03 Selections
2.1. The boolean
Data Type
boolean
类型只有两个值:true
和false
。- 与 C/C++ 不同,整型与布尔型不能直接转换,如当
even
是boolean
类型时写if (even != 0)
会编译错误,if (even)
可以通过编译。
2.2. if-else statment
else
语句采用就近原则,做题时不要被代码缩进欺骗。
2.3. switch statement
- 每一个
case
下的break
语句可以省略,这种情况下会继续执行下一个case
的语句。 - 参数的类型限制:
- 在 java 中 switch 后的表达式的类型只能为以下几种:byte、short、char、int、枚举类(在 Java1.6 中是这样),在 java1.7 后支持了对 string 的判断;
- 注意:boolean、long 类型不能作为 switch 参数。
2.4. Operator Associativity
- 所有二元运算符都是左结合的:
a – b + c – d
等价于((a – b) + c) – d
。 - 但是所有赋值运算符都是右结合的:
a = b += c = 5
等价于a = (b += (c = 5))
。
3. Ch04 Mathematical Functions, Characters, and Strings
3.1. Characters
- Java 中的字符默认采用 UTF-16 编码,每个字符占用 2 字节。
- Comment:对于超出基本字符集(BMP)的字符,会使用代理对(Surrogate Pair)来表示,每个代理对占用 4 字节。
char
Data Type- 表示单个字符,支持 ASCII 和 Unicode。
- 示例:
char letter = 'A';
(ASCII)char letter = '\u0041';
(Unicode)
- 可以使用递增或递减操作符获取下一个或前一个字符。
- 示例:
char ch = 'a'; System.out.println(++ch);
输出 b。
- 示例:
Character
类中的方法:isDigit(ch)
:判断是否为数字。isLetter(ch)
:判断是否为字母。isUpperCase(ch)
:判断是否为大写字母。isLowerCase(ch)
:判断是否为小写字母。toUpperCase(ch)
:将字符转换为大写。toLowerCase(ch)
:将字符转换为小写。
3.2. The String
Class
- Java 的
String
类是不可变类。- 坑点:后面的各种方法都不是在原串上做修改。
- 定义与基本操作
- 使用
String
对象表示字符串。 - 示例:
String message = "Welcome to Java";
- 使用
- 常用方法
length()
:获取字符串长度。- 示例:
"Hello".length()
返回 5。
- 示例:
charAt(index)
:获取指定索引的字符。- 示例:
"Hello".charAt(0)
返回H
。
- 示例:
concat(s1)
或+
:拼接字符串。- 示例:
"Hello".concat(" World")
返回"Hello World"
。
- 示例:
toUpperCase()
和toLowerCase()
:大小写转换。- 示例:
"Hello".toUpperCase()
返回"HELLO"
。
- 示例:
trim()
:去除字符串两端的空格。- 示例:
" Hello ".trim()
返回"Hello"
。
- 示例:
- 字符串比较
equals(s1)
:比较字符串内容是否相等。- 示例:
"Hello".equals("hello")
返回false
。
- 示例:
equalsIgnoreCase(s1)
:忽略大小写比较字符串内容是否相等。- 示例:
"Hello".equalsIgnoreCase("hello")
返回true
。
- 示例:
compareTo(s1)
:比较字符串的字典顺序。- 返回值:
- 大于 0:当前字符串大于
s1
。 - 等于 0:两字符串相等。
- 小于 0:当前字符串小于
s1
。
- 大于 0:当前字符串大于
- 返回值:
compareToIgnoreCase(s1)
:忽略大小写比较字符串的字典顺序。startsWith(s1)
:判断字符串是否以s1
开头。endsWith(s1)
:判断字符串是否以s1
结尾。
- 子字符串操作
substring(beginIndex)
:从指定索引开始截取到末尾。substring(beginIndex, endIndex)
:截取指定范围(左闭右开)的子字符串。- 示例:
"Hello".substring(1, 4)
返回"ell"
。
- 示例:
- 查找字符或子字符串
indexOf(ch)
:返回字符首次出现的索引。indexOf(ch, fromIndex)
:从指定索引开始返回字符首次出现的索引。indexOf(s1)
:返回子字符串首次出现的索引。indexOf(s1, fromIndex)
:从指定索引开始返回子字符串首次出现的索引。lastIndexOf(ch)
:返回字符最后一次出现的索引。- (类似地还有三个这里略。)
- 字符串转数字
Integer.parseInt()
:将字符串转换为整数。- 示例:
Integer.parseInt("123")
返回 123。
- 示例:
Double.parseDouble()
:将字符串转换为浮点数。- 示例:
Double.parseDouble("3.14")
返回 3.14。
- 示例:
- 数字转字符串
String.valueOf()
:会调用对应基础数据类型的包装类的toString()
静态方法。- 可以使用
+ ""
- 示例:
123 + ""
返回"123"
。
- 示例:
3.2.1. String Matching & Replacing & Splitting (Ch10)
replace(oldChar, newChar)
:字符替换replaceFirst(oldString, newString)
:子字符串替换,但只替换第一次replaceAll(oldString, newString)
:子字符串替换split(delimiter): String[]
:将字符串按指定串分割成数组- 这里的
match
、replace
系列、split
方法都支持正则表达式。
3.2.2. String Formatting (Ch10)
- 支持
String.format()
的静态方法,其中第一个参数是格式化字符串,支持的语法参见下面的 String-Formatting 部分。
3.3. Input: The Scanner
Class
Scanner sc = new Scanner(System.in);
- 使用
Scanner
类:nextInt()
、nextDouble()
、nextFloat()
、nextLong()
方法:读取整数、浮点数、长整数等等基本数据类型。next()
方法:读取单词。从遇到第一个有效字符(非空格、换行符)开始扫描,遇到第一个分隔符或结束符(空格\n
)时结束。nextLine()
方法:读取整行。扫描剩下的所有字符串直到遇到回车为止。(可以从空格开始)。
- 注意:
nextInt()
和nextLine()
的组合可能导致读取问题(即剩一个空的行末)。解决方法:在nextInt()
后添加一个nextLine()
。
3.4. Formatting Output
TODO:具体还是得看一下课件。
- 使用
printf
格式化输出- 格式:
%[index$][标识][最小宽度][.精度]转换符
。 - 常用转换符
%b
:布尔值。%c
:字符。%s
:字符串。%d
:整数。%f
:浮点数。%e
:科学计数法(4.556000e+01
)。- 日期和时间:
%tF
:完整日期(如 2024-12-17)。%tT
:完整时间(如 15:45:14)。- 还有各种,以
%t
开头。
- 标识
-
:左对齐。0
:用零填充。,
:千位分隔符。+
:显示正负号。
- 示例
- 将整数补零:
String.format("%04d", 5)
返回"0005"
。 - 千位分隔符:
String.format("%,d", 1000000)
返回"1,000,000"
。
- 将整数补零:
- 格式:
4. Ch05 Loops
4.1. do
-while
Loop
- 基本结构:
do { // Loop body; Statement(s); } while (loop-continuation-condition);
- 特点:至少执行一次循环体,然后再检查条件。
4.2. Break with Tag
- 使用带标签的
break
语句可以跳出多层嵌套循环。- 示例:
outer: for (int i = 0; i < 5; i++) { for (int j = 0; j < 5; j++) { if (j == 2) break outer; // 跳出 outer 循环 System.out.println("i = " + i + ", j = " + j); } }
- 示例:
- 可以将标签用到任意语句块中(即大括号括起来的一段语句),使用带标签的
break
语句可以跳出该语句块,即使这个语句块不是循环体。
4.3. For-in
- Java 5 引入了用于数组和容器的 for-in 语法,这类似于 C++ 14 的 range-based for 语法。
- 示例:
int[] numbers = {1, 2, 3, 4, 5}; for (int number : numbers) { System.out.println(number); }
- 示例:
5. Ch06 Methods
5.1. Methods
- 方法(method) 是一组语句的集合,用于完成某个特定的操作。通过方法可以实现代码的复用、模块化和易维护性。
- 方法签名(method signature) 定义为方法名和参数列表的组合,它是方法的唯一标识。
5.2. Formal & Actual Parameters
- 形式参数(formal parameter) 是方法头中定义的变量,用于接收调用方法时传递的值。
- 实际参数(actual parameter) 是在调用方法时传递给形式参数的值。
- Java 中的参数传递是 按值传递(pass by value) 的,即将实际参数的值复制一份传递给方法。
- 即方法内对参数的修改不会影响方法外的变量。
5.3. Overloading
- 方法的 重载(overload) 是指在同一个类中定义多个同名方法,但参数列表不同。
- 注意区分 overload 和 override。
- 可以重载静态方法,但不能重写静态方法。
- 注意区分 overload 和 override。
- 模糊调用(ambiguous invocation):在方法调用时,编译器可能发现有多个方法匹配,无法确定哪一个是最具体的,这种时候会直接 CE。
- 原因:
- 方法重载时,多个方法的参数列表可能存在一定的重叠,导致编译器无法确定调用哪个方法。
- 方法调用中涉及自动类型转换或可变参数时,可能会有多个方法同时符合调用条件。
- 举例:如果有
print(int); print(double);
两个方法,则在print(1);
时会认为前者更具体而不会报错,但如果同时有print(int, double);
和print(double, int);
两个方法,则print(1, 1);
时就无法确定调用哪个方法更具体,从而会报错。
- 原因:
6. Ch07 Single-Dimensional Arrays
6.1. Single-Dimensional Arrays
- 声明数组
- 语法:
datatype[] arrayRefVar;
datatype arrayRefVar[];
(不推荐)
- 语法:
- 创建数组
- 语法:
arrayRefVar = new datatype[arraySize];
- 示例:
myList = new double[10];
- 创建时会自动设定为默认值:
- 数值基本类型自动设定为
0
,字符类型默认为\u0000
,布尔类型默认为false
。 - 对象引用类型默认为
null
。
- 数值基本类型自动设定为
- 语法:
- 使用
arrayRefVar.length
获取数组大小。- 数组一旦创建,大小就固定不能改变。
- 数组初始化器(array initializer):使用时必须要在一个语句中完成数组的声明、创建和初始化,否则会导致 CE。
- 举例:
double[] myList = {1.9, 2.9, 3.4, 3.5};
- 可以用
new int[]{3, 1, 5, 2, 4}
的语法用数组初始化器创建 匿名数组(anonymous array)。
- 举例:
- Java 数组的内存分配在堆上。而在 C 中,使用
int a[100]
创建的数组分配在栈上,使用int *a = new int[100];
创建的数组分配在堆上。 - 边界检查:Java 的
[]
操作符会检查数组边界。 - 拷贝数组:不会重新创建数组,而是直接将引用复制。
- 正确的拷贝数组方式:
- 重新创建一个并依次赋值
- 使用
System.arraycopy
:System.arraycopy(sourceArray, 0, targetArray, 0, sourceArray.length);
。 - 使用
Arrays.copyOf
:int[] copiedArray = Arrays.copyOf(sourceArray, sourceArray.length);
- 正确的拷贝数组方式:
7. Ch08 Multidimensional Arrays
7.1. Two-dimensional Arrays
- 声明与创建:
// 声明数组引用变量 dataType[][] refVar; refVar = new dataType[10][10]; // 声明和创建结合 dataType[][] refVar = new dataType[10][10]; // 另一种语法 dataType refVar[][] = new dataType[10][10];
- 多维数组的创建时应从左到右指定维数,未指定的维数不会自动创建内容,需要再手动创建。
- 即
int a[][] = new int[3][0]
和new int[3][3]
都是合法的,但是前者要再使用a[0] = new int[3]
才能创建第二维的内容。
- 即
- 多维数组的创建时应从左到右指定维数,未指定的维数不会自动创建内容,需要再手动创建。
- Java 的数组每一维可以有不同的大小,大小不同的数组称为 不规则数组(ragged array)。
8. 杂项
System.currentTimeMillis()
返回自 1970 年 1 月 1 日以来的毫秒数。- Java 语言的
public static void main(String args[])
命令行参数中,从第一个位置开始存储命令行参数(即args[0]
),程序名没有存储在args
中。 - Java 中不能在 嵌套 代码块中重复声明同名变量,这点与 C/C++ 不同。
- 静态导入
import static
:可以引入类的静态成员,如import static java.lang.Math.PI
后可以直接用PI
而不用Math.PI
。
8.1. java.lang.Math
(Ch04)
- 常用常量
Math.PI
:圆周率。Math.E
:自然对数的底。
- 常用方法
- 三角函数:
Math.sin
、Math.cos
、Math.tan
、Math.toRadians
等。 - 指数函数:
Math.exp
、Math.log
、Math.log10
、Math.pow
、Math.sqrt
。 - 四舍五入方法:
Math.ceil
、Math.floor
、Math.rint
、Math.round
。Math.rint()
方法是向最接近的整数取整,如果出现 x.5 的情况则向 x 和 x+1 中的偶数取整。
- 最值和绝对值:
Math.max
、Math.min
、Math.abs
。 - 随机数生成:
Math.random()
生成 的随机数。
- 三角函数:
8.2. java.utils.Arrays
(Ch07)
copyOf(array, newLength)
静态方法:拷贝数组。binarySearch(array, key)
静态方法:二分搜索(要求数组有序)。sort(array)
静态方法:将数组排序。
8.3. java.util.Date
(Ch09)
- 构造函数:
Date()
:构造一个表示当前时间的 Date 对象。Date(elapseTime: long)
:构造一个 Date 对象,表示自 1970 年 1 月 1 日 GMT 以来指定毫秒数的时间。
- 方法:
toString() : String
:返回表示日期和时间的字符串。(例:Sun Mar 09 13:50:19EST 2003
)getTime() : long
:返回自 1970 年 1 月 1 日 GMT 以来的毫秒数。setTime(elapseTime: long) : void
:设置对象的新时间。
8.4. java.util.Random
(Ch09)
- 构造函数:
Random()
:使用当前时间作为种子构造一个 Random 对象。Random(seed: long)
:使用指定的种子构造一个 Random 对象。
- 方法:
nextInt() : int
:返回一个随机的 int 值。nextInt(n: int) : int
:返回一个 0(含)到 n(不含)之间的随机 int 值。nextLong() : long
:返回一个随机的 long 值。nextDouble() : double
:返回一个 0.0(含)到 1.0(不含)之间的随机 double 值。nextFloat() : float
:返回一个 0.0F(含)到 1.0F(不含)之间的随机 float 值。nextBoolean() : boolean
:返回一个随机的 boolean 值。