目录主要来自于《尚硅谷Java教程》
关键字(keyword)是被Java赋予了特殊含义、用作特殊用途的字符串,全部为小写,如定义数据类型的class
, boolean
和用于定义流程控制的switch
, while
等。
保留字(reserved word)在现有Java版本尚未使用,但以后版本可能会作为关键字使用,需要在命名标识符时避免使用,包括:goto
, const
。
标识符(identifier)是对变量、方法和类等命名时使用的字符序列。
由26个英文字母、0-9、_
或$
组成。
严格区分大小写。
不能以数字开头、不能包含空格。
命名规范:
helloworldtest
。HelloWorldTest
。helloWorldTest
。HELLO_WORLD_TEST
。变量用于在内存中保存数据,概念:
需要注意:
按照数据类型可以分为:
byte
, short
, int
, long
)、浮点(float
, double
)、字符(char
)、布尔(boolean
)。class
)、接口(interface
)、数组([]
)。按照声明位置的不同可以分为:
static
修饰)。具体内容参考面向对象。
类型 | 占用空间 | 范围 |
---|---|---|
byte |
1 byte = 8 bit | \(-128\) ~ \(127\) |
short |
2 bytes | \(-2^{15}\) ~ \(2^{15}-1\) |
int |
4 bytes | \(-2^{31}\) ~ \(2^{31}-1\) |
long |
8 bytes | \(-2^{63}\) ~ \(2^{63}-1\) |
主要注意,Java各个整数类型有固定的字段长度和表达范围,不受OS影响,以保证可移植性。
int
。long
类型时,如果数字大小超过int
的范围,数字后须加l
或者L
,这是因为Java默认把数字当成int
类型来处理。类型 | 占用空间 | 范围 |
---|---|---|
单精度float |
4 bytes | \(-3.403\times 10^{38}\) ~ \(3.403\times 10^{38}\) |
双精度double |
8 bytes | \(-1.798\times 10^{308}\) ~ \(1.798\times 10^{308}\) |
Java的浮点类型常量默认为double
,声明float
类型,需要加f
或F
。浮点型常量有两种表现形式:
char
类型用来表示通常意义上的字符,占用2 bytes。
\u000a
表示\n
,其中000a
为十六进制整数。char
类型可以运算,因为它有对应的Unicode码。boolean
只能取ture
和false
。
String
属于引用数据类型,声明时使用一对""
。
String
类型可以和8种基本数据类型变量做运算,且只能是连接运算+
,运算的结果仍然是String
。不能通过强制类型转换将String
转换为其他基本数据类型。
String str1 = 123 + ""; // int num1 = (int) str1; // 编译不通过 int num1 = Integer.parseInt(str1); // 123
字符串连接:
char c = 'a'; int num = 10; String str = "hello"; System.out.println(c + num + str); // output: 107hello System.out.println(c + str + num); // output: ahello10 System.out.println(c + (num + str)); // output: a10hello System.out.println(str + num + c); // output: hello10a
只讨论7种基本数据类型变量间的运算,不包含boolean
。
byte
/ short
/ char
< int
< long
< float
< double
,这里的容量指表示数据范围的大小而不是占用空间的大小,例如:long
占用8个字节,float
占用4个字节,但是float
的表示范围比long
更大。short s1 = 123; double b1 = s1; System.out.println(d1); // output: 123.0
byte
/ short
/ char
这3种类型做运算时,结果为int
型,包括相同种类(如2个byte
变量相加)。byte b1 = 1; short s1 = 10; // short s3 = b1 + s1; // 编译不通过 int i1 = b1 + s1;
将容量大的数据类型变量强制赋值给容量小的数据变量,可能会导致精度损失。
// 没有精度损失 long l1 = 123; short s1 = (short) l1; // 123 // 精度损失,截断 double d1 = 12.9; int i1 = (int) d1; // 12 // 精度损失,溢出 int i2 = 128; byte b = (byte) i2; // -128
对于整数类型,有4种表达方式:
0b
或0B
开头。0
开头。0x
或0X
开头,A-F不区分大小写,0x21af + 1 == 0x21b0
。Java整数常量默认是int
类型,当用二进制定义整数时,第32位是符号位。正数的原码、补码、反码相同,负数的补码为其反码+1。 计算机底层都以补码的方式存储数据。
结果的符号与被模数相同。
int m1 = 12; int n1 = 5; System.out.println(m1 % n1); // 2 int m2 = -12; int n2 = 5; System.out.println(m2 % n2); // -2 int m3 = 12; int n3 = -5; System.out.println(m3 % n3); // 2 int m4 = -12; int n4 = -5; System.out.println(m4 % n4); // -2
自增/自减运算符不会改变变量的数据类型。
short s1 = 100; s1 -= 1; s1--; // s1 = s1 - 1; // 编译失败,需要强制类型转换 System.out.println(s1); // 98
与自增运算符类似,+=
, -=
, *=
, /=
等赋值运算符不会改变数据类型。
比较运算符的结果都是boolean
类型,包括==
, !=
, <
, >
, <=
, >=
, instanceof
,其中instanceof
用来检查是否是类的对象,用法如下:
System.out.println("Hello" instanceof String); // ture
运算符 | 含义 |
---|---|
& |
逻辑与 |
&& |
短路与 |
| |
逻辑或 |
|| |
短路或 |
! |
逻辑非 |
^ |
逻辑异或 |
注意,没有<<<
运算符。
运算符 | 含义 | 示例 |
---|---|---|
<< |
左移 | 3 << 2 => 12 |
>> |
右移 | 3 >> 1 => 1 |
>>> |
无符号右移 | 3 >>> 1 => 1 |
& |
与运算 | 6 & 3 => 2 |
| |
或运算 | 6 | 3 => 7 |
^ |
异或运算 | 6 ^ 3 => 5 |
~ |
取反运算 | ~6 => -7 |
<<
:空位补0,被移除的高位丢弃,在一定范围内,每向左移动1位,相当于* 2
。>>
:正数右移后空缺位补0,负数补1,在一定范围内,每向右移动1位,相当于/ 2
。>>>
:最高位无论是1还是0,右移空缺位都补0。~
:按照补码,各位取反。2 << 3
和 8 << 1
的复杂度是\(O(1)\),比2 * 8
效率更高。
结构:(条件表达式) ? 表达式1 : 表达式2
。
if-else
语句,反之则不一定,参见下一条。表达式1
和表达式2
要求是一致的,例如(m > n) ? 2 : "n is bigger"
编译不通过。import java.util.Scanner;
Scanner
类。next()
等方法读取数据。import java.util.Scanner; public class HelloWorld{ public static void main(String[] args){ Scanner scan = new Scanner(System.in); int num = scan.nextInt(); String str = scan.next(); System.out.println(num + str); } }
除了if-else
结构外,有关switch-case
结构需要注意:
switch
中的表达式只能是这6种数据类型之一:byte
、short
、char
、int
、枚举(JDK5.0新增)、String
(JDK7.0新增)。case
之后只能声明常量,不能声明范围。default
结构是可选的,而且位置灵活。另外switch-case
结构比if-else
结构效率稍高。
label: for(int i = 1; i <= 4; i++){ for(int j = 1; j <= 10; j++){ System.out.println(j); if (j % 4 == 0){ // continue; // 结束包裹此关键字最近的一层循环结构 continue label; // 结束指定标签的一层循环结构 } } }
数组(array)是多个相同类型数据按一定顺序排列的集合。
数组一旦初始化完成,其长度就确定了。
// 静态初始化:数组的初始化和数组元素的赋值操作同时进行 int[] ids = new int[] {1001, 1002, 1003, 1004}; // 动态初始化:数组的初始化和元素赋值分开进行 String[] names = new String[5]; names[0] = "Wang"; // 获取数组长度 System.out.println(ids.length + " " + names.length);
char
型:0或\u0000
,而非'0'
boolean
型:false
null
JVM中的内存结构简单分为:
ids
和names
(仅存放数组的地址)。new
出来的对象、数组等,如上述代码中ids
和names
对应数组开辟的连续空间。初始化方法如下:
// 静态初始化:数组的初始化和数组元素的赋值操作同时进行 int[][] arr1 = new int[][] {{1, 2, 3}, {4, 5}, {6}}; // 动态初始化:数组的初始化和元素赋值分开进行 String[][] arr2 = new String[5][3]; String[][] arr3 = new String[6][]; // 也是正确的写法 int[][] arr4 = {{1, 2, 3}, {4, 5}, {6}}; // 类型推断 int arr5[][] = new int[][] {{1, 2, 3}, {4, 5}, {6}};
针对动态初始化方法一,例如int[][] arr = new int[3][4];
,则初始化值为
int[][] arr = new int[3][4]; System.out.println(arr[0]); // 地址值:[I@28d93b30 System.out.println(arr[0][0]); // 0
针对动态初始化方法二,例如int[][] arr = new int[3][];
,则初始化值为
null
。(int) (Math.random() * (99 - 10 + 1) + 10); /* * Math.random() => [0.0, 1.0) * Math.random() * 90 => [0.0, 90.0) * (int) (Math.random() * 90) => [0, 89] * (int) (Math.random() * 90 + 10) => [10, 99] */
在空间复杂度为常数级的情况下,反转数组。主要思想为交换头尾元素。
// 随机生成20个两位数 int[] arr = new int[20]; for(int i = 0; i < arr.length; i++){ arr[i] = (int) (Math.random() * 90 + 10); System.out.print(arr[i] + " "); } System.out.println(); // reverse 1 /* for (int i = 0; i < arr.length / 2; i++){ int temp = arr[i]; arr[i] = arr[arr.length - 1 - i]; arr[arr.length - 1 - i] = temp; } */ // reverse 2: 双指针交换,实际操作上与方法一相同 for (int i = 0, j = arr.length - 1; i < j; i++, j--){ int temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; }
// 二分查找(必须是有序的) int [] arr2 = new int[] {-98, -34, 2, 34, 54, 66, 79, 105, 210, 333}; int head = 0; // 初始前索引 int end = arr2.length - 1; // 初始后索引 boolean isFound = false; // 是否被找到 int target = 210; // 查找的目标值 while (head < end){ int middle = (head + end) / 2; if (target == arr2[middle]){ System.out.println("Find " + target + ", index = " + middle + "."); isFound = true; break; } else if (target < arr2[middle]){ end = middle - 1; } else { head = middle + 1; } } if (!isFound){ System.out.println("Can't find " + target + "."); }
衡量排序算法:
for (int i = 0; i < arr.length - 1; i++){ for (int j = 0; j < arr.length - 1 - i; j++){ if (arr[j] > arr[j+1]){ int temp = arr[j]; arr[j] = arr[j+1]; arr[j+1] = temp; } } }
public class QuickSort { // 交换数组的两个索引上的值 private static void swap(int[] data, int i, int j){ int temp = data[i]; data[i] = data[j]; data[j] = temp; } // 子方法 private static void subSort(int[] data, int start, int end){ if (start < end){ int base = data[start]; int low = start; int high = end + 1; while (true){ // 通过两个while循环,向右移动low,向左移动high // 直到找到大于base的low和小于base的high while (low < end && data[++low] <= base); while (high > start && data[--high] >= base); // low和high没有碰面,需要交换,循环继续 if (low < high){ swap(data, low, high); } else { break; } } // 将base移到中间,base左边全部是比它小的数字,右边全部是比它大的数字 swap(data, start, high); // 递归地对base左半部分和右半部分排序 subSort(data, start, high - 1); subSort(data, high + 1, end); } } // 快速排序 public static void quickSort(int[] data){ subSort(data, 0, data.length - 1); } }
java.util.Arrays
是操作数组的工具类,里面定义了很多操作数组的方法。
// 1. 判断两个数组是否相等 int[] arr1 = new int[] {1, 2, 3, 4, 5}; int[] arr2 = new int[] {1, 2, 3, 4, 5}; System.out.println(Arrays.equals(arr1, arr2)); // true // 2. 输出数组 System.out.println(Arrays.toString(arr1)); // [1, 2, 3, 4, 5] // 3. 将数组填充为指定值 Arrays.fill(arr1, 10); System.out.println(Arrays.toString(arr1)); // [10, 10, 10, 10, 10] // 4. 对数组排序 int [] arr3 = new int[] {98, 34, 2, 34, 54, -66, -79, 105, -210, 333}; Arrays.sort(arr3); System.out.println(Arrays.toString(arr3)); // [-210, -79, -66, 2, 34, 34, 54, 98, 105, 333] // 5. 二分查找 System.out.println(Arrays.binarySearch(arr3, 105)); // 如果找到,输出索引 System.out.println(Arrays.binarySearch(arr3, -2)); // 负数表示没找到 // 8 // -4