所使用的JDK版本为Open JDK 11.0.20,不保证所有的内容在高版本的JDK和旧版的JDK同样适用。
快速入门
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("Hello, Java!");
System.out.print("请输入你的名字: ");
String name = scanner.nextLine();
System.out.println("你好, " + name + "! 欢迎学习 Java!");
System.out.print("请输入两个整数 (用空格分隔): ");
int a = scanner.nextInt();
int b = scanner.nextInt();
int sum = a + b;
System.out.println("它们的和是: " + sum);
scanner.close();
}
}
将上述代码保存到Main.java中,打开控制台,输入javac Main.java
和java Main
,就能看到输出的结果。
注释
单行注释://
// 这是一行注释
多行注释:/* .. */
/*
这是多行注释
*/
注释不会被执行,在用编译为class字节码文件后就会被移除。
注意事项:
Java 解析 .java
文件时,会先处理Unicode 转义字符,然后再进行词法分析。如果在单行注释 //
里放 \u000A
(换行符),会导致下一行代码“恢复可执行”。
public class Demo {
public static void main(String[] args) {
// System.out.println("这行被注释了"); \u000A System.out.println("但这行仍然执行!");
}
}
最终导致 System.out.println("但这行仍然执行!"); 仍然会被执行。
其它的,比如多行注释的*/
对应的unicode码,提前结束多行注释。
标识符和命名规范
标识符
标识符是用于命名变量、方法、类、接口等的名称。Java 标识符的规则如下:
- 组成:字母(A-Z, a-z)、数字(0-9)、下划线(_)、美元符号($)。
- 开头:必须以字母、下划线或美元符号开头,不能以数字开头。
- 区分大小写:
myVar
和myvar
是不同的标识符。 - 长度:无限制,但应保持简洁。
- 关键字:不能使用 Java 关键字(如
class
,public
等)。
Java命名规范
Java 遵循驼峰命名法(CamelCase),具体规范如下:
- 类名:大驼峰法,每个单词首字母大写。
- 示例:
MyClass
,StudentRecord
- 示例:
- 方法名:小驼峰法,首字母小写,后续单词首字母大写。
- 示例:
calculateSum()
,getUserName()
- 示例:
- 变量名:小驼峰法,与方法名相同。
- 示例:
totalAmount
,userName
- 示例:
- 常量名:全大写,单词间用下划线分隔。
- 示例:
MAX_VALUE
,PI
- 示例:
- 包名:全小写,单词间用点号分隔。
- 示例:
com.example.myproject
- 示例:
- 接口名:与类名相同,使用大驼峰法。
- 示例:
Runnable
,Serializable
- 示例:
变量
数据类型
基本数据类型:
- 整形:
byte
、short
、int
、long
- 浮点型:
float
、double
- 布尔型:
bool
- 字符型:
char
引用数据类型:
- 类
- 接口
- 数组
基本数据类型保存的是值,引用数据类型保存的是地址。
基本数据类型在把一个变量赋值给另一个变量时,传递的是值;引用数据类型传递的是地址。
在System.out.println(args)
中,如果是System.out.println("" + args)
,出现了字符串,则+
表示拼接;如果没有出现字符串,则是数值相加。
public class Main {
public static void main(String[] args) {
int age = 18;
System.out.println("age:" + age); // -->输出age:18
System.out.println(age + 2); // -->输出20
}
}
变量的使用
所有的变量在使用时都包含两部分:声明和初始化。声明不会为变量分配内存,只有在初始化后才会。
声明的语法如下:
数据类型 变量名;
int age;
String name;
char[] s;
初始化的语法如下:
变量名 = xxXXX;
age = 12;
name = "小明";
s = new char[]{'a', 'b', 'c'};
也可以声明的同时直接初始化:
数据类型 变量名 = xxXXX;
int age = 12;
String name = "小明";
char[] s = new char[]{'a', 'b', 'c'};
基本数据类型
整形
整形所占大小和表示的范围:
类型 | 占用存储空间 | 范围 |
---|---|---|
byte |
1 字节 | −27 到 27 - 1 |
short |
2 字节 | −216 到 216 - 1 |
int |
4 字节 | −232 到 232 - 1 |
long |
8 字节 | −264 到 264 - 1 |
Java和C语言不同,具有固定的占用存储空间。
整形的使用
public class Main {
public static void main(String[] args) {
byte a = 1;
short b = 3;
int age = 18;
long d = 100L;
}
}
java默认使用int类型,long类型必需在后面加I或L。
浮点型
浮点型所占大小和表示的范围:
类型 | 占用存储空间 | 范围 |
---|---|---|
float |
4 字节 | ±2±126 |
double |
8 字节 | ±2±1022 |
表示的范围只是大概的范围,并非范围中的每个数都能被表示,这和浮点数的表示有关。
浮点数的使用
public class Main {
public static void main(String[] args) {
float pi = 3.1415f;
double source = 89.5;
}
}
java默认使用double类型,float类型必需在后面加f或F。
字符型
字符型所占大小和表示的范围:
类型 | 占用存储空间 | 范围 |
---|---|---|
char |
2字节 | −216 到 216 - 1 |
字符型的使用
public class Main {
public static void main(String[] args) {
char ch = 'A';
char ch1 = '韩';
char ch2 = '你';
System.out.println(ch);
System.out.println(ch1);
System.out.println(ch2);
// A
// 韩
// 你
}
}
字符型被定义的时候必需使用'',不能是"";String类型必须是"",不能是''。
字符型的本质是一个数,是字符对应的unicode码,在定义的时候也可以通过数字定义,输出对应的字符,也可以进行运算,进行运算时,被当作int类型。
public class Main {
public static void main(String[] args) {
char ch = 'c';
System.out.println((int) ch);
// 输出99,即c对于的unicode码
char ch1 = 99;
System.out.println(ch1);
// 输出c
System.out.println(ch + 1);
// 输出100
}
}
布尔类型
布尔类型的使用
public class Main {
public static void main(String[] args) {
boolean bool = true;
if (bool) {
System.out.println("bool if true");
}
bool = false;
if (!bool) {
System.out.println("bool is false");
}
}
// 输出bool is true
// 输出bool is false
}
Java中的布尔类型不能转换成整形,也不能从1或0转换成布尔型
类型转换
当Java进行赋值或运算时,范围小的类型会自动转换成范围大的类型。当把范围大的类型赋值给范围小的类型的时候,就会报错。
byte-->short-->int-->long-->float-->double
char--> int
注意事项:
- char不会自动转换成byte和short类型,byte和short类型也不会自动转换成char类型。
byte ch = 99;
char ch1 = ch;
// 会报错
-
byte、short和char类型可以进行运算,在运算时会先转换成int类型。
-
boolean类型不参与基本类型之间的转换。
-
表达式的结果会自动提升为操作数中的最大类型。
-
char类型可以通过数字赋值。
对于范围大的类型赋值给范围小的类型,需要强制类型转换,在进行强制类型转换时可能会发生数据溢出,需要格外注意。
float f = 10f;
int b = (int) f;
基本数据类型和String类型的转换
基本类型转String类型:直接+""
即可
int a = 1;
double b = 2.0;
boolean d = true;
String s_a = "" + a;
String s_b = "" + b;
String s_d = "" + d;
String类型转基本类型:调用对应的包装类型的parseXXX
方法
String s_a = "1";
String s_b = "2.0";
String s_d = "true";
int a = Integer.parseInt(s_a);
double b = Double.parseDouble(s_b);
boolean d = Boolean.parseBoolean(s_d);
对于字符,使用str.charAt(index)
方法获取字符串中指定位置的字符。
String str = "Hello";
char ch = str.charAt(0); // 获取第一个字符 'H'
System.out.println(ch); // 输出: H
运算符
算数运算符
+
:加法-
:减法*
:乘法/
:除法%
:取模(求余数)++
:自增(变量值加1)--
:自减(变量值减1)
对于++
和--
,如果时右++
,是先赋值在运算;如果是左--
,是先运算在赋值。
int number = 1;
int count = number++;
System.out.println(number); // 2
System.out.println(count); // 1
int number = 1;
int count = ++number;
System.out.println(number); // 2
System.out.println(count); // 2
关系运算符
==
:等于!=
:不等于>
:大于<
:小于>=
:大于等于<=
:小于等于
逻辑运算符
&&
:逻辑与(两个条件都为真时返回真)||
:逻辑或(至少一个条件为真时返回真)!
:逻辑非(反转布尔值)
对于&&
和||
,多个条件进行判断时,如果在中间的某个条件就已经能判断出结果,就会直接停止判断。
int number = 1;
if(number < 0 && ++number > 1) {
System.out.println("Hello World"); // 不会输出
}
System.out.println(number); // 1
// 因为number < 0可以判断出最终结果为false,后面的++number > 1就没有在进行。
int number = 1;
if(++number > 1 || ++number > 2) {
System.out.println("Hello World"); // 会执行,输出Hello World
}
System.out.println(number); // 2
// 因为++number > 1可以判断出最终结果为true,后面的++number > 1就没有在进行。
赋值运算符和复合赋值运算符
=
:赋值+=
:加后赋值-=
:减后赋值*=
:乘后赋值/=
:除后赋值%=
:取模后赋值
复合赋值运算符如a+=b
,相当于a=a+b
,复合赋值运算符会发生类型转换,如byte a = 1;a += 2;
等同于a = (byte)(a + 2)
。
位运算符
-
&
:按位与,对两个操作数的每一位进行与操作,只有当两个对应位都为1时,结果位才为1,否则为0。int a = 5; // 二进制:0101 int b = 3; // 二进制:0011 int result = a & b; // 结果:0001 (十进制 1)
-
|
:按位或,对两个操作数的每一位进行或操作,只要有一个对应位为1,结果位就为1。int a = 5; // 二进制:0101 int b = 3; // 二进制:0011 int result = a | b; // 结果:0111 (十进制 7)
-
^
:按位异或,对两个操作数的每一位进行异或操作,当两个对应位不同时,结果位为1,否则为0。int a = 5; // 二进制:0101 int b = 3; // 二进制:0011 int result = a ^ b; // 结果:0110 (十进制 6)
-
~
:按位取反,对操作数的每一位进行取反操作,即1变0,0变1。int a = 5; // 二进制:0000 0101 int result = ~a; // 结果:1111 1010 (十进制 -6)
-
>>
:右移,将操作数的二进制位向右移动指定的位数,左侧空出的位用符号位填充(正数补0,负数补1)。int a = 10; // 二进制:0000 1010 int result = a >> 1; // 结果:0000 0101 (十进制 5)
-
<<
:左移,将操作数的二进制位向左移动指定的位数,右侧空出的位用0填充。int a = 5; // 二进制:0000 0101 int result = a << 1; // 结果:0000 1010 (十进制 10)
-
>>>
:无符号右移,将操作数的二进制位向右移动指定的位数,左侧空出的位用0填充(无论正负),没有<<<
。int a = -10; // 二进制:1111 1111 1111 1111 1111 1111 1111 0110 int result = a >>> 1; // 结果:0111 1111 1111 1111 1111 1111 1111 1011 (正数)
三元运算符
格式条件 ? 表达式1 : 表达式2
,如果条件为真,返回表达式1的值,否则返回表达式2的值。
int a = 10;
int b = 22;
int c = a > b ? a : b; // 22
// a > b不为真,返回b的值,可以用来求两个数的最大值。
控制结构
分支控制
if语句
基本语法如下:
if (条件1) {
// 当条件1为 true 时执行的代码
} else if (条件2) {
// 当条件1为 false 且条件2为 true 时执行的代码
} else if (条件3) {
// 当条件1和条件2为 false 且条件3为 true 时执行的代码
} else {
// 当所有条件都为 false 时执行的代码
}
其中至少需要有一个if(条件)
,可以在内部嵌套if
。
int score = 85;
if (score >= 90) {
System.out.println("成绩等级:A");
} else if (score >= 80) {
System.out.println("成绩等级:B");
} else if (score >= 70) {
System.out.println("成绩等级:C");
} else if (score >= 60) {
System.out.println("成绩等级:D");
} else {
System.out.println("成绩等级:F");
}
// 成绩等级:B
switch语句
基本语法如下:
switch (表达式) {
case 值1:
// 当表达式等于值1时执行的代码
break;
case 值2:
// 当表达式等于值2时执行的代码
break;
case 值3:
// 当表达式等于值3时执行的代码
break;
// 可以有任意数量的 case
default:
// 当表达式不匹配任何 case 时执行的代码
break;
}
表达式的类型可以是 int
、char
、byte
、short
、String
或枚举类型。
每个 case
后面跟一个常量值,表示与表达式可能匹配的值。
break
用于终止 switch
语句,如果没有 break
,程序会继续执行后续的 case
代码块。
default
是可选的,当表达式的值不匹配任何 case
时,执行 default
后的代码块。
int day = 3;
String dayName;
switch (day) {
case 1:
dayName = "星期一";
break;
case 2:
dayName = "星期二";
break;
case 3:
dayName = "星期三";
break;
case 4:
dayName = "星期四";
break;
case 5:
dayName = "星期五";
break;
case 6:
dayName = "星期六";
break;
case 7:
dayName = "星期日";
break;
default:
dayName = "无效的星期数";
}
System.out.println(dayName); // 星期三
switch和if的选择
如果判断的数值不多,而且是 int
、char
、byte
、short
、String
或枚举类型,虽然都可以用,建议使用switch
,否则,则使用if
。
在底层的实现上,switch
的速度相较于if
更快。
循环控制
for循环
基本语法如下:
for (初始化表达式; 条件表达式; 迭代表达式) {
// 循环体
}
初始化表达式:在循环开始时执行一次(通常用于定义并初始化循环变量)。
条件表达式:每次循环迭代前都会检查,如果为 true
,则执行循环体;否则循环结束。
迭代表达式:每次循环迭代后执行,通常用于更新循环变量。
每个表达式部分可以为空,但是必需要写上";"。
for (int i = 0; i < 5; i++) {
System.out.println("当前 i 的值:" + i);
}
/* 输出:
当前 i 的值:0
当前 i 的值:1
当前 i 的值:2
当前 i 的值:3
当前 i 的值:4
*/
高级for循环:
用于遍历数组或集合,语法如下:
for (元素类型 变量名 : 数组或集合) {
// 操作变量
}
示例:
int[] numbers = {1, 2, 3, 4, 5};
for (int num : numbers) {
System.out.println(num);
}
while循环
基本语法如下:
while (条件表达式) {
// 循环体
}
条件表达式:在每次循环迭代前检查,如果为 true
,则执行循环体;否则循环结束。
int i = 0;
while (i < 5) {
System.out.println("当前 i 的值:" + i);
i++; // 迭代
}
/*
当前 i 的值:0
当前 i 的值:1
当前 i 的值:2
当前 i 的值:3
当前 i 的值:4
*/
注意事项:
必须注意循环条件,防止无限循环即死循环。
do...while 循环
基本语法如下:
do {
// 循环体
} while (条件表达式)
先执行循环体,再检查条件表达式,如果为 true
,则继续执行循环;否则退出。
int i = 0;
do {
System.out.println("当前 i 的值:" + i);
i++;
} while (i < 5);
/*
当前 i 的值:0
当前 i 的值:1
当前 i 的值:2
当前 i 的值:3
当前 i 的值:4
*/
break
和 continue
关键字
break
:终止 当前循环,并跳出循环结构。
continue
:跳过 本次循环,直接进入下一次迭代。
for (int i = 0; i < 10; i++) {
if (i == 5) {
break; // 当 i == 5 时,终止整个循环
}
System.out.println(i);
}
int j = 0;
while (j < 10) {
j++;
if (j == 5) {
continue; // 当 j == 5 时,跳过本次循环
}
System.out.println(j);
}
此外,return
也会终止掉循环,实质是终止掉了方法的执行并返回结果。
数组
数组的声明
结构如下:
数据类型[] 数组名; // 推荐写法
数据类型 数组名[]; // 也可以这样写,但不推荐
示例:
int[] numbers; // 定义一个整型数组
double[] prices; // 定义一个浮点型数组
String[] names; // 定义一个字符串数组
数组的声明不会分配内存,必须显式初始化才能使用。
数组的初始化
静态初始化
在定义数组的同时,直接指定数组元素:
int[] numbers = {1, 2, 3, 4, 5};
String[] names = {"Alice", "Bob", "Charlie"};
动态初始化
在定义数组时,只指定长度,然后逐个赋值:
int[] arr = new int[5]; // 创建长度为 5 的 int 数组,默认值为 0
arr[0] = 10;
arr[1] = 20;
默认初始化:int
为 0,double
为 0.0,boolean
为 false,char
为\u0000,引用类型为null。
访问数组元素
数组中的元素通过 索引访问,索引从 0
开始,直到数组的长度减一,索引为i的元素实际对应第i+1个元素:
int[] arr = {10, 20, 30, 40};
System.out.println(arr[0]); // 输出 10
arr[2] = 100; // 修改第 3 个元素
访问数组的索引越界,即超过了索引的最大值,会发生异常。
数组的遍历
使用for循环
:
int[] numbers = {1, 2, 3, 4, 5};
for (int i = 0; i < numbers.length; i++) {
System.out.println(numbers[i]);
}
使用while循环
:
int[] numbers = {1, 2, 3, 4, 5};
int i = 0;
while (i < numbers.length) {
System.out.println(numbers[i]);
i++;
}
多维数组
声明和初始化
静态初始化:
int[][] matrix = {
{1, 2, 3},
{4, 5, 6}
};
// 定义一个2行3列的二维数组
动态初始化:
int[][] matrix = new int[2][3]; // 2 行 3 列的数组
matrix[0][1] = 10; // 赋值
遍历多维数组
int[][] matrix = {
{1, 2, 3},
{4, 5, 6}
};
for (int i = 0; i < matrix.length; i++) {
for (int j = 0; j < matrix[i].length; j++) {
System.out.print(matrix[i][j] + " ");
}
System.out.println();
}
数组的操作
获取数组长度
使用数组名.length
获取数组的长度
数组拷贝
int[] arr1 = {1, 2, 3, 4, 5};
int[] arr2 = new int[arr1.length];
System.arraycopy(arr1, 0, arr2, 0, arr1.length);
System.arraycopy()
方法参数:
- 源数组
- 源起始索引
- 目标数组
- 目标起始索引
- 复制长度
Arrays工具类
-
Arrays.toString(Object[] a)
:将数组转换成字符串。String[] chars = {"aa", "ac", "ab", "da", "ea"}; System.out.println(Arrays.toString(chars)); // [aa, ac, ab, da, ea]
-
Arrays.sort(Oject[] a)
:为数组排序,按照从小到大的顺序。对于字符类型的数组,则是按照首字母的顺序。对于字符串类型的数组,则是依次比较字母的顺序(仅限英文单词)。String[] chars = {"aa", "ac", "ab", "da", "ea"}; Arrays.sort(chars); System.out.println(Arrays.toString(chars)); // [aa, ab, ac, da, ea]
-
Arrays.binarySearch(int[] a, int key)
:二分查找元素的索引,必需传入一个排序好的数组,如果找到则返回元素索引,找不到则返回负数。int[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; Arrays.sort(arr); int index = Arrays.binarySearch(arr, 5); System.out.println(index); // 5