JAVAの旅
JAVAの旅
因为必须选修JAVA课,因此特地来学习听闻已久的JAVA了。
其实也有以下原因:
Java的广泛应用:
1. 跨平台特性
Java是基于Java虚拟机(JVM)的编程语言,这使得它具有“一次编写,到处运行”的特性。程序员编写的Java代码只需编译成字节码,然后在任何支持JVM的设备上运行。这种跨平台的能力使Java在各种操作系统和硬件平台上都能高效地运行,大大提高了其普及度。
2. 内存管理和易用性
Java语言设计时考虑到了程序员的开发便利性。它提供了内置的垃圾回收机制,自动管理内存,这样开发者就不必手动分配和释放内存,减少了内存泄漏和溢出的风险。这种简化的内存管理使得Java程序更易于编写和维护。
3. 稳定性和性能
Java虚拟机经过了长时间的开发和优化,具有工业级的稳定性和高效的性能。JVM不仅在性能上进行了高度优化,还提供了丰富的调试和监控工具,使得Java在大型和复杂的应用程序中表现优异。
4. 广泛的开源社区支持
Java拥有一个庞大而活跃的开源社区。开发者可以利用各种开源库和框架,如Spring、Hibernate、Apache Commons等,这些工具能够加速开发过程,并提高软件的质量和稳定性。开源社区的支持也确保了Java技术的不断更新和完善。
5. 主导市场的领域
Java长期以来在多个关键领域占据主导地位:
互联网和企业应用:Java EE(Enterprise Edition)作为一个成熟的企业级解决方案,广泛应用于企业级应用开发,如Web应用、分布式系统等。它提供了一整套规范和工具,帮助企业开发复杂的业务应用。
大数据平台:许多大数据处理框架,如Hadoop、Spark、Flink等,都是用Java或Scala(另一种运行于JVM的语言)开发的。Java在大数据领域的应用进一步巩固了其在技术领域的地位。
Android移动平台:虽然Android在逐渐转向Kotlin作为官方语言,但Java仍然是Android应用开发的主要语言。大量的现有应用和开发者的知识积累,使Java在Android平台上仍然保持强大的影响力。
JAVA版本
Java SE(Standard Edition)
- 核心平台:Java SE是Java的标准版本,包括基础的Java语言、核心库和JVM(Java虚拟机)。它提供了开发普通桌面应用和基本的Java程序所需的功能。
Java EE(Enterprise Edition)
- 企业扩展:Java EE是在Java SE的基础上扩展的版本,添加了用于开发企业级应用的额外API和库,如Web应用、数据库访问、消息服务等。Java EE应用程序运行在与Java SE相同的JVM上,但提供了更多的企业级功能。
Java ME(Micro Edition)
- 嵌入式系统:Java ME是Java的“瘦身版”,专为资源有限的嵌入式设备(如手机和嵌入式系统)设计。它的库和虚拟机都经过优化,适合小型设备,但不包含Java SE和Java EE的全部功能。
学习路线图
- 学习Java SE:掌握Java语言、核心技术和标准库。
- 进阶Java EE:学习Web开发、数据库开发和分布式架构,如Spring框架。
- 大数据开发:学习Hadoop、Spark等基于Java的技术。
- Android开发:深入学习Android平台,掌握移动应用开发。
Java SE是学习其他版本的基础,而Java EE、Java ME、以及大数据和Android开发是根据具体需求进一步扩展的方向。
一些名词解释
JDK vs. JRE
JDK (Java Development Kit): 包含了Java编译器(
javac
)、调试器、文档生成器等开发工具,以及JRE。适合开发Java程序。JRE (Java Runtime Environment): 只包括JVM(Java虚拟机)和运行Java程序所需的标准库。适合运行Java程序,但不包含开发工具。
JSR 和 JCP
JSR (Java Specification Request): 是Java平台的功能规范,请求添加新功能或修改现有功能的标准。每个JSR定义了一组接口和功能。
JCP (Java Community Process): 负责审核和制定JSR规范的组织。确保Java平台的标准和兼容性。
RI 和 TCK
RI (Reference Implementation): 规范的参考实现代码,展示如何实现JSR规范,但不一定是最优的。
TCK (Technology Compatibility Kit): 兼容性测试套件,用于验证实现是否符合JSR规范。确保不同实现之间的一致性。
总结: - JDK:开发工具包(包含JRE) - JRE:运行环境 - JSR:功能规范 - JCP:规范制定组织 - RI:参考实现 - TCK:兼容性测试工具
第一个JAVA程序
代码示例:
1
2
3
4
5public class Hello {
public static void main(String[] args) {
System.out.println("Hello, world!");
}
}解释:
public class Hello
:定义一个公开的类,类名是Hello
。public static void main(String[] args)
:这是程序的入口方法,main
方法是Java程序的起点。System.out.println("Hello, world!");
:输出“Hello, world!”到屏幕。
文件名:
- 文件名必须与类名相同,且区分大小写:
Hello.java
。
- 文件名必须与类名相同,且区分大小写:
格式:
- Java源码的缩进并非强制,但通常使用4个空格或一个tab进行缩进,以提高代码的可读性。
如何运行Java程序
编译源码:
- 使用
javac
命令将Java源码(.java
文件)编译成字节码(.class
文件)。 - 命令:
1
javac Hello.java
- 编译后目录下会生成
Hello.class
文件。
- 使用
运行字节码:
- 使用
java
命令运行编译后的字节码文件。 - 命令:
1
java Hello
- 输出:
1
Hello, world!
- 使用
单文件运行(Java 11+):
- 直接运行
.java
文件,不需要预先编译:1
java Hello.java
- 但在实际项目中,通常会涉及多个文件和依赖库,这种单文件运行不适用。
- 直接运行
javac
:将.java
编译为.class
。java
:运行.class
文件。- Java 11起支持直接运行单个
.java
文件,但在复杂项目中常需编译和管理多个文件及库。
Java基础
Java程序的基本结构包括以下几个部分:
- 类定义:
1
2
3public class Hello {
// 类的内容
}- 类名:应以字母开头,后接字母、数字或下划线。习惯上首字母大写,如
Hello
。
- 类名:应以字母开头,后接字母、数字或下划线。习惯上首字母大写,如
- 方法定义:
1
2
3public static void main(String[] args) {
// 方法的代码
}- 方法名:应以小写字母开头,符合驼峰命名法,如
main
。 - 返回值:
void
表示方法没有返回值。 static
:表示静态方法,Java程序入口方法必须是静态的。
- 方法名:应以小写字母开头,符合驼峰命名法,如
- 语句:
- 每行语句以分号(
;
)结束。1
System.out.println("Hello, world!");
- 每行语句以分号(
- 注释:
- 单行注释:以
//
开头。1
// 这是单行注释
- 多行注释:以
/*
开头,以*/
结束。1
2
3/*
这是多行注释
*/ - 文档注释:以
/**
开头,以*/
结束,通常用于生成文档。1
2
3
4/**
* 这是文档注释
* @author liaoxuefeng
*/
- 单行注释:以
- 格式化:
- Java代码格式化遵循社区约定,使用IDE(如Eclipse)的格式化功能(快捷键Ctrl+Shift+F)帮助保持代码整洁。
变量
在Java中,变量用于存储数据。变量分为两种类型:基本类型和引用类型。以下是基本类型变量的简单介绍:
定义变量:
1
int x = 1; // 定义一个整型变量x,并赋初值1
使用变量:
1
2
3
4
5
6
7// 定义并打印变量
public class Main {
public static void main(String[] args) {
int x = 100; // 定义变量x,初始值100
System.out.println(x); // 打印x的值
}
}重新赋值:
1
2
3
4
5
6
7
8
9// 重新赋值变量
public class Main {
public static void main(String[] args) {
int x = 100; // 初始值100
System.out.println(x); // 输出100
x = 200; // 重新赋值为200
System.out.println(x); // 输出200
}
}变量之间的赋值:
1
2
3
4
5
6
7
8
9
10
11// 变量之间的赋值
public class Main {
public static void main(String[] args) {
int n = 100; // n = 100
n = 200; // n = 200
int x = n; // x = 200
x = x + 100; // x = 300
System.out.println("x = " + x); // 输出300
System.out.println("n = " + n); // 输出200
}
}
- 变量必须先定义后使用,并可以重新赋值。
=
是赋值操作符,不表示数学上的相等。- 变量的值可以被覆盖和更新,影响变量的存储内容。
Java的基本数据类型分为四类:整数类型、浮点数类型、字符类型和布尔类型。它们各自有不同的内存占用和取值范围,理解这些类型有助于编写高效且可靠的Java程序。下面我们来详细探讨这些基本数据类型。
数据类型
1. 整数类型
Java中的整数类型是带符号的,表示正数和负数。它们的存储空间和取值范围如下:
数据类型 | 大小 (字节) | 取值范围 |
---|---|---|
byte |
1 | -128 ~ 127 |
short |
2 | -32768 ~ 32767 |
int |
4 | -2147483648 ~ 2147483647 |
long |
8 | -9223372036854775808 ~ 9223372036854775807 |
- 符号位:在二进制表示中,最高位是符号位,
0
表示正数,1
表示负数。 - 表示范围:数据类型的取值范围与其存储字节数成正比,字节数越多,能表示的范围越大。
示例: 1
2int i = 2147483647;
long n = 9000000000000000000L;long
类型的数值末尾需要加L
,否则默认为int
。
2. 浮点数类型
浮点数用于表示带小数的数值,它们可以表示非常大的数或非常小的数。Java定义了两种浮点类型:
数据类型 | 大小 (字节) | 有效位数 | 取值范围 |
---|---|---|---|
float |
4 | 6-7位 | 1.4E-45 ~ 3.4E+38 |
double |
8 | 15-16位 | 4.9E-324 ~ 1.8E+308 |
- 科学计数法:浮点数通常用科学计数法表示,如
3.14e38
表示3.14 × 10^38
。 - 后缀
f
:float
类型的数值需要在末尾加f
或F
,否则默认为double
。
示例: 1
2float f = 3.14f;
double d = 1.79e308;
3. 布尔类型
布尔类型boolean
只有两个值:true
和false
。它通常用于逻辑判断或条件控制。
- 存储:理论上,布尔类型仅需1个bit,但在JVM中通常以4个字节来表示。
示例: 1
2boolean isGreater = 5 > 3; // true
boolean isAdult = age >= 18; // false
4. 字符类型
char
用于表示单个字符,支持Unicode编码,可以表示全世界的文字。
数据类型 | 大小 (字节) | 取值范围 |
---|---|---|
char |
2 | 0 ~ 65535 |
- Unicode:
char
类型可以表示的字符包括ASCII和Unicode标准中的所有字符。
示例: 1
2char a = 'A';
char zh = '中';
5. 引用类型
除了基本数据类型,Java还有引用类型。引用类型存储的是对象的内存地址,常见的引用类型包括String
、数组和自定义的类等。
示例: 1
String s = "hello";
6. 常量
使用final
关键字修饰的变量称为常量,一旦赋值后不可修改,通常用来避免魔术数字,提高代码的可读性。
示例: 1
final double PI = 3.14;
7. var
关键字
var
可以让编译器根据上下文自动推断变量的类型,简化代码书写,但仍然强类型。
示例: 1
var sb = new StringBuilder();
8. 变量的作用范围
变量的作用范围由定义的语句块{}
决定,超出作用域引用变量会导致编译错误。
示例: 1
2
3
4
5
6
7
8
9
10{
int i = 0;
{
int x = 1;
{
String s = "hello";
}
String s = "hi"; // 新的变量s,与上面的s不同
}
}
Java的整数运算包括加、减、乘、除、求余、移位和位运算等,遵循与初等数学一致的四则运算规则。整数运算是精确的,包括除法,只返回整数部分。关键点如下:
基本运算
- 加法与减法:遵循基本的数学规则,可以嵌套括号来明确计算顺序。例如:
1 | int i = (100 + 200) * (99 - 88); // 3300 |
- 除法与求余:除法会丢弃小数部分,只保留整数部分。求余运算返回除法的余数。例如:
1 | int x = 12345 / 67; // 184 |
- 运算溢出:整数在运算时,如果结果超出了数据类型的范围(例如
int
),会发生溢出。溢出不会抛出错误,而是返回一个错误的值:
1 | int x = 2147483640; |
要避免溢出,可以使用更大范围的 long
类型:
1 | long x = 2147483640L; |
自增/自减运算
- 自增 (
++
) 和自减 (--
):可以对变量进行加1或减1操作。++n
是先加1再使用变量,而n++
是先使用变量再加1。建议避免将自增运算混入复杂的表达式中,以防引起混淆。
1 | int n = 3300; |
移位运算
移位运算基于整数的二进制表示形式。 - 左移
(<<
):将所有位左移,低位补0,实际上是乘以2的幂。
- 右移
(>>
):将所有位右移,高位补符号位,实际上是除以2的幂。
- 无符号右移 (>>>
):与
>>
类似,但不保留符号位,高位补0。
示例:
1 | int n = 7; // 00000000 00000000 00000000 00000111 |
对于负数,右移运算保持符号位:
1 | int n = -536870912; |
位运算
- 与
(
&
):按位与运算,只有两个位都为1时结果才为1。 - 或
(
|
):按位或运算,只要任一位为1,结果就是1。 - 非
(
~
):按位取反运算,0变1,1变0。 - 异或
(
^
):按位异或运算,两个位不同结果为1,相同结果为0。
示例:
1 | int i = 167776589; // 00001010 00000000 00010001 01001101 |
运算优先级
运算符的优先级从高到低如下: 1. ()
(括号) 2.
!
~
++
--
(逻辑非,按位取反,自增,自减) 3. *
/
%
(乘,除,取余) 4. +
-
(加,减)
5. <<
>>
>>>
(左移,右移,无符号右移) 6. &
(按位与) 7.
|
(按位或) 8. +=
-=
*=
/=
(赋值运算符)
如果不确定优先级,使用括号 ()
来确保运算顺序正确。
类型自动提升与强制转型
在表达式中,较小范围的类型(如
short
)会自动提升为较大范围的类型(如
int
)。强制类型转换可以将一个大范围的类型转换为小范围类型,但需注意可能发生的数据丢失:
1 | short s = 1234; |
超出范围的强制转型会导致不正确的结果:
1 | int i1 = 1234567; |
一些例题:
例题1:计算商品折扣价格
编写一个程序计算商品的折扣价格。已知商品的原价为int price = 1200
,折扣为int discount = 15
(表示15%)。要求计算出折扣后的价格并打印出来。
提示:
计算折扣后的价格可以使用公式:折扣价格 = 原价 * (1 - 折扣 / 100)
。注意在计算中可能涉及整数除法的精度问题。
例题2:判断奇偶性
编写一个程序判断一个整数是否为偶数。输入一个整数int num
,如果该整数为偶数,打印"Even"
,否则打印"Odd"
。
提示:
使用位运算来判断奇偶性,具体方法是检查整数的最低位是否为0。
例题3:处理溢出问题
编写一个程序,演示整数溢出问题。定义两个变量int a = 1000000
和int b = 2000
,然后计算a * b
并打印结果。接着,修改代码使计算结果不发生溢出。
提示:
计算过程中可能会发生溢出,考虑使用long
类型进行计算。
例题4:IP地址网段判断
编写一个程序判断某个IP地址是否在给定的网段内。已知IP地址int ip = 3232235777
(对应192.168.1.1
),网段的网络地址int network = 3232235776
(对应192.168.1.0
),子网掩码int mask = 4294967040
(对应255.255.255.0
)。要求判断该IP是否在这个网段内。
提示:
使用按位与运算&
判断IP地址是否在网段内。
例题5:实现简单的计算器
编写一个简单的四则运算计算器,用户输入两个整数int a
和int b
以及一个操作符char operator
(+
、-
、*
、/
)。根据输入的操作符执行对应的运算,并打印出结果。
提示:
考虑输入除数为0的情况,并进行错误处理。
例题6:类型转换陷阱
编写一个程序,演示类型转换的陷阱。定义一个int i = 987654321
,然后将其强制转换为short
类型,并打印转换后的结果。
提示:
注意int
转换为short
时会丢失高位数据,分析为什么输出结果会与原值不同。
例题7:左移与右移操作
编写一个程序,演示整数的左移和右移操作。定义一个整数int n = 1
,依次对其左移1位、2位、3位,并分别打印结果。然后对一个负数int m = -4
进行右移操作,并观察结果。
提示:
注意负数右移操作时符号位的变化。
例题8:计算二进制中1的个数
编写一个程序,输入一个整数int n
,计算并打印该整数的二进制表示中1
的个数。
提示:
可以通过循环和移位操作逐位检查二进制数中的每一位。
解答:
例题1:计算商品折扣价格
1 | public class DiscountPrice { |
输出:
折扣后的价格为: 1020
例题2:判断奇偶性
1 | public class EvenOdd { |
输出:
Odd
例题3:处理溢出问题
1 | public class OverflowExample { |
输出:
发生溢出的结果: -727379968
防止溢出的结果: 2000000000
例题4:IP地址网段判断
1 | public class IPAddressCheck { |
输出:
IP在网段内
例题5:实现简单的计算器
1 | import java.util.Scanner; |
输入/输出示例:
输入第一个整数: 10
输入第二个整数: 2
输入操作符 (+, -, , /):
结果: 20
例题6:类型转换陷阱
1 | public class TypeConversionTrap { |
输出:
原值: 987654321
转换后的short值: 7225
例题7:左移与右移操作
1 | public class ShiftOperations { |
输出:
n << 1: 2
n << 2: 4
n << 3: 8
m >> 1: -2
m >> 2: -1
m >> 3: -1
例题8:计算二进制中1的个数
1 | public class CountBits { |
输出:
二进制中1的个数: 4
这些例题展示了Java整数运算的各种常见场景及其处理方式,理解这些问题有助于掌握Java编程中的关键概念。
浮点数运算的细节
在计算机中,浮点数的运算由于表示方法的原因会产生误差,这与整数运算不同。
1. 浮点数的表示和误差
浮点数在计算机中的表示方式导致其不能精确表示某些值,例如0.1。因为0.1在二进制中是一个无限循环小数,所以存储时只能近似表示。运行以下代码可以观察浮点数的误差:
1 | public class Main { |
由于浮点数无法精确表示,直接比较两个浮点数是否相等可能会导致错误。因此,比较两个浮点数时,通常使用以下方法:
1 | double r = Math.abs(x - y); |
2. 类型提升
在混合运算中,若参与运算的两个数中有一个是浮点型,整数会自动提升为浮点型进行运算。例如:
1 | public class Main { |
但需要注意,在复杂的四则运算中,如果都是整数,编译器仍会按整数进行运算。例如:
1 | double d = 1.2 + 24 / 5; // 结果是5.2而非6.0 |
解决办法是将整数转为浮点数:
1 | double d = 1.2 + 24.0 / 5; // 结果是6.0 |
3. 溢出处理
浮点数在除数为0时不会报错,而是返回特殊值:
NaN
(Not a Number)Infinity
(正无穷大)-Infinity
(负无穷大)
示例代码:
1 | public class Main { |
4. 强制转型与四舍五入
浮点数可以强制转型为整数,小数部分会被直接丢弃。例如:
1 | int n1 = (int) 12.3; // 12 |
如果想实现四舍五入,可以在转型前加上0.5:
1 | public class Main { |
一些例题:
例题 1: 浮点数精度问题
计算以下表达式并判断结果是否为预期值: 1
2
3
4
5
6
7public class Main {
public static void main(String[] args) {
double a = 0.1 + 0.2;
double b = 0.3;
System.out.println("a == b: " + (a == b));
}
}
例题 2: 浮点数与整数运算
计算以下表达式并输出结果: 1
2
3
4
5
6
7
8public class Main {
public static void main(String[] args) {
int x = 5;
double y = 3.0;
double result = y / x + 2.5;
System.out.println("result: " + result);
}
}
例题 3: 处理浮点数比较
比较两个浮点数是否相等,允许一定的误差范围: 1
2
3
4
5
6
7
8
9
10
11
12public class Main {
public static void main(String[] args) {
double num1 = 1.0001;
double num2 = 1.0002;
double epsilon = 0.001;
if (Math.abs(num1 - num2) < epsilon) {
System.out.println("num1 and num2 are considered equal.");
} else {
System.out.println("num1 and num2 are not equal.");
}
}
}
例题 4: 浮点数与类型提升
观察以下代码中类型提升的影响: 1
2
3
4
5
6
7
8public class Main {
public static void main(String[] args) {
int a = 10;
double b = 3.5;
double result = a + b;
System.out.println("result: " + result);
}
}
例题 5: 浮点数的强制转型
执行以下代码,并输出结果: 1
2
3
4
5
6
7
8
9
10public class Main {
public static void main(String[] args) {
double d1 = 9.7;
double d2 = -9.7;
int n1 = (int) d1;
int n2 = (int) d2;
System.out.println("n1: " + n1);
System.out.println("n2: " + n2);
}
}
布尔类型和布尔运算
布尔类型
布尔类型(boolean
)在 Java
中只有两个值:true
和
false
。布尔类型用于表示逻辑值和结果。
布尔运算
布尔运算主要包括比较运算符、与运算符、或运算符、非运算符等。
比较运算符 比较运算符用于比较两个值,结果是布尔值:
>
大于>=
大于或等于<
小于<=
小于或等于==
等于!=
不等于
示例:
1
2
3
4
5
6boolean isGreater = 5 > 3; // true
int age = 12;
boolean isZero = age == 0; // false
boolean isNonZero = !isZero; // true
boolean isAdult = age >= 18; // false
boolean isTeenager = age > 6 && age < 18; // true逻辑运算符
- 与运算符 (
&&
): 如果两个操作数都为true
,结果为true
,否则为false
。 - 或运算符 (
||
): 如果至少一个操作数为true
,结果为true
,否则为false
。 - 非运算符 (
!
): 取反操作符,将true
变为false
,将false
变为true
。
示例:
1
2
3boolean result1 = true && false; // false
boolean result2 = true || false; // true
boolean result3 = !true; // false- 与运算符 (
短路运算 短路运算是一种优化机制,当可以确定整个表达式的结果时,后续部分的计算会被跳过:
- 对于
&&
(与运算),如果第一个操作数为false
,结果必定为false
,后续部分不会计算。 - 对于
||
(或运算),如果第一个操作数为true
,结果必定为true
,后续部分不会计算。
示例:
1
2
3
4
5
6
7
8// 短路运算
public class Main {
public static void main(String[] args) {
boolean b = 5 < 3; // false
boolean result = b && (5 / 0 > 0); // 5 / 0 不会报错,因为 b 为 false
System.out.println(result); // false
}
}如果
b
为true
,则(5 / 0 > 0)
会抛出异常,因为短路机制未起作用。1
2
3
4
5
6
7// 短路运算
public class Main {
public static void main(String[] args) {
boolean result = true || (5 / 0 > 0); // 5 / 0 不会报错,因为第一个操作数为 true
System.out.println(result); // true
}
}- 对于
三元运算符 三元运算符用于基于布尔表达式的结果选择两个值中的一个。语法为
b ? x : y
,其中b
为布尔表达式,x
和y
为待返回的值。- 如果
b
为true
,返回x
。 - 如果
b
为false
,返回y
。
示例:
1
2
3
4
5
6
7
8// 三元运算
public class Main {
public static void main(String[] args) {
int n = -100;
int x = n >= 0 ? n : -n; // 如果 n >= 0 为 true,则 x = n;否则 x = -n
System.out.println(x); // 100
}
}- 如果
布尔运算符优先级
布尔运算符的优先级从高到低如下: 1. !
(非运算) 2.
>
、>=
、<
、<=
(比较运算符)
3. ==
、!=
(相等和不等运算符) 4.
&&
(与运算符) 5. ||
(或运算符)
示例问题和解答
例题 1: 判断一个数是否在 10 到 20 之间(包括10和20)。
1 | public class Main { |
例题 2: 判断一个数是否为正数或零,并且不是 100。
1 | public class Main { |
例题 3: 使用三元运算符计算一个数的绝对值。
1 | public class Main { |
Java中的字符和字符串
在Java中,字符(char
)和字符串(String
)是两种不同的类型,分别用于处理单个字符和字符序列。
字符类型 char
- 定义:
char
是基本数据类型,用于表示单个 Unicode 字符。 - 大小: 一个
char
占用 2 个字节。 - Unicode: Java 使用 Unicode 编码来表示字符,无论是英文字符还是中文字符。
示例: 1
2
3
4
5
6char c1 = 'A'; // 英文字符 A
char c2 = '中'; // 中文字符 中
// 输出字符的 Unicode 编码
int n1 = 'A'; // 65
int n2 = '中'; // 20013
使用转义字符: - \u
加上四位十六进制数表示 Unicode 字符。
示例: 1
2char c3 = '\u0041'; // 'A'
char c4 = '\u4e2d'; // '中'
字符串类型 String
- 定义:
String
是引用类型,用双引号"
包含字符序列。 - 大小: 一个
String
对象可以存储任意长度的字符序列。
示例: 1
2
3
4String s = ""; // 空字符串
String s1 = "A"; // 包含一个字符
String s2 = "ABC"; // 包含三个字符
String s3 = "中文 ABC"; // 包含六个字符
转义字符: - 用于在字符串中插入特殊字符: -
\"
表示双引号字符 "
- \'
表示单引号字符 '
- \\
表示反斜杠字符
\
- \n
表示换行符 - \r
表示回车符
- \t
表示制表符 - \u####
表示 Unicode
编码字符(四位十六进制)
示例: 1
2
3String s1 = "abc\"xyz"; // 包含字符: a, b, c, ", x, y, z
String s2 = "abc\\xyz"; // 包含字符: a, b, c, \, x, y, z
String s3 = "ABC\n\u4e2d\u6587"; // 包含字符: A, B, C, 换行符, 中, 文
字符串连接
- 使用
+
连接: 可以将字符串与其他数据类型连接,自动转换其他数据类型为字符串。
示例: 1
2
3
4
5
6
7
8String s1 = "Hello";
String s2 = "world";
String s = s1 + " " + s2 + "!"; // 连接结果: "Hello world!"
System.out.println(s);
int age = 25;
String s3 = "age is " + age; // 连接结果: "age is 25"
System.out.println(s3);
多行字符串(Text Blocks)
- Java 13 引入: 使用
"""..."""
语法来表示多行字符串(Text Blocks),使代码更加清晰易读。 - 自动去除共同空格: 多行字符串会自动去除前面共同的空格。
示例: 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15String s = """
SELECT * FROM
users
WHERE id > 100
ORDER BY name DESC
""";
System.out.println(s);
// 如果不希望末尾有换行符,可以去掉最后的换行符:
String s2 = """
SELECT * FROM
users
WHERE id > 100
ORDER BY name DESC""";
System.out.println(s2);
不规则排版: 只会去除最短行首的空格。
示例: 1
2
3
4
5
6
7String s = """
SELECT * FROM
users
WHERE id > 100
ORDER BY name DESC
""";
System.out.println(s);
字符串的不可变性
- 不可变性: Java 中的字符串是不可变的。这意味着一旦创建,字符串内容不能被改变。修改字符串实际上是创建了一个新的字符串对象。
示例: 1
2
3
4String s = "hello";
String t = s;
s = "world";
System.out.println(t); // 输出 "hello",因为 t 仍然指向原来的字符串
解释: - 初始时,s
和 t
都指向字符串 "hello"
。 - 当 s
被赋值为
"world"
时,t
仍然指向原来的字符串
"hello"
。 - 字符串 "hello"
并没有被修改,而是创建了一个新的字符串对象 "world"
。
空值 null
- 定义:
null
表示一个变量不指向任何对象。与空字符串""
不同,空字符串是一个有效的字符串对象。
示例: 1
2
3String s1 = null; // s1 是 null
String s2 = s1; // s2 也是 null
String s3 = ""; // s3 指向一个空字符串对象,不是 null
区分: - null
是一个特殊的值,表示“没有对象”。 - 空字符串 ""
是一个有效的
String
对象,长度为0。
数组基础
定义和初始化数组
在Java中,数组用于存储相同类型的数据集合。你可以用以下方式定义和初始化一个数组:
1
2
3
4int[] ns = new int[5]; // 创建一个包含5个元素的整型数组
ns[0] = 68; // 设置第一个元素为68
ns[1] = 79; // 设置第二个元素为79
// 依此类推...你也可以在定义数组时直接初始化其元素:
1
int[] ns = {68, 79, 91, 85, 62}; // 创建一个包含5个初始化元素的整型数组
数组的特点
大小不可变:一旦创建,数组的大小不能更改。例如:
1
2int[] ns = {68, 79, 91, 85, 62}; // 数组大小为5
ns = new int[] {1, 2, 3}; // 指向一个新的大小为3的数组索引从0开始:数组索引范围从0到
length-1
,超出索引范围会引发ArrayIndexOutOfBoundsException
。元素初始化:数组创建时,基本数据类型的元素会被初始化为默认值,如整型为0,布尔型为false。
字符串数组
在Java中,字符串是引用类型,每个字符串对象在内存中有其自己的存储位置。当你创建一个字符串数组时,数组的每个元素实际上都是对某个字符串对象的引用:
1 | String[] names = {"ABC", "XYZ", "zoo"}; |
这行代码创建了一个字符串数组,其中包含三个字符串对象的引用。每个引用指向一个独立的字符串对象。
字符串数组的引用特性
当你修改字符串数组中的元素时,实际上是改变了数组中对字符串对象的引用,而不是改变字符串对象本身:
1 | public class Main { |
在这个代码片段中:
String s = names[1];
这行代码将names
数组中索引为1的元素(即"XYZ")的引用赋给变量s
。- 当你执行
names[1] = "cat";
时,你实际上是将names
数组中索引为1的元素的引用从"XYZ"更改为"cat"。 s
仍然指向原来的"XYZ"字符串,因为s
和names[1]
分别保存了不同的引用。
这表明对数组中元素的修改不会影响已保存的其他引用的值。s
指向的字符串依然是"XYZ",即使 names[1]
现在指向了"cat"。
要点:
- 数组:数组的大小一旦确定就不能更改,数组中的元素可以通过索引访问,数组的元素初始化为默认值。
- 字符串数组:字符串是引用类型,对数组元素的修改实际上是改变了数组中引用的对象。数组的元素在被修改时,原有的字符串对象不会改变,只是引用发生了变化。
在Java中,输入和输出(I/O)是程序与外界交互的重要方式。下面是对你提供的代码以及相关概念的详细解释。
输出
System.out.println()
和
System.out.print()
用于在控制台输出信息:
System.out.println()
:输出信息并自动换行。System.out.print()
:输出信息但不换行。
示例代码:
1 | public class Main { |
执行效果: 1
2A,B,C
END
格式化输出
格式化输出使用 System.out.printf()
方法,它允许你根据指定的格式输出信息。格式化输出通常用在需要以特定格式呈现数据时,例如对浮点数的精确度控制或者将整数转换为不同的进制。
格式化字符串使用占位符(如 %d
, %f
等)来指定输出的格式。常用占位符及其说明:
%d
:格式化整数%x
:格式化为十六进制整数%f
:格式化浮点数%e
:格式化为科学计数法表示的浮点数%s
:格式化字符串
示例代码:
1 | public class Main { |
执行效果: 1
2
33.14
3.1416
n=12345000, hex=00bc614e
输入
Java中用于从控制台读取输入的主要工具是
Scanner
类。Scanner
类提供了便捷的方法来读取各种类型的输入数据,例如字符串和整数。
使用 Scanner
类的步骤:
- 导入
Scanner
类:通过import
语句导入java.util.Scanner
。 - 创建
Scanner
对象:将System.in
传递给Scanner
构造函数,表示从标准输入流中读取数据。 - 读取数据:使用
Scanner
对象的方法(如nextLine()
和nextInt()
)读取用户输入的数据。
示例代码:
1 | import java.util.Scanner; |
执行步骤:
编译:使用
javac
命令编译代码:1
$ javac Main.java
执行:使用
java
命令运行编译后的程序:1
$ java Main
然后根据提示输入数据,例如:
1
2
3Input your name: Bob
Input your age: 12
Hi, Bob, you are 12
- 输出:
System.out.print()
:不换行输出。System.out.println()
:输出并换行。System.out.printf()
:格式化输出,使用占位符定义输出格式。- 输入:
- 使用
Scanner
类从标准输入流读取数据。- 通过
nextLine()
读取字符串,nextInt()
读取整数等。
在Java中,条件控制语句(如 if
、else if
和
else
)用于根据条件的真假来控制程序的执行路径。以下是对if
语句及相关条件判断的详细解释,包括常见的陷阱和最佳实践。
基本 if
语句
if
语句的基本结构如下:
1 | if (条件) { |
当条件为 true
时,if
语句块中的代码将被执行。如果条件为
false
,则跳过该代码块。
示例代码:
1 | public class Main { |
输出:
1 | 及格了 |
多语句的 if
语句块
当 if
语句块中有多条语句时,必须使用花括号
{}
将其括起来,以确保所有语句都属于 if
语句块的一部分。
示例代码:
1 | public class Main { |
输出:
1 | 及格了 |
省略花括号的情况
当 if
语句块只有一条语句时,可以省略花括号,但这种做法不推荐,因为它可能导致代码难以维护和出错。
示例代码:
1 | public class Main { |
输出:
1 | 及格了 |
使用 else
和
else if
else
和
else if
语句用于处理 if
条件为 false
时的情况。
示例代码:
1 | public class Main { |
输出:
1 | 及格了 |
可以使用多个 else if
来处理多个条件:
1 | public class Main { |
输出:
1 | 及格了 |
判断顺序
在使用多个 if ... else if ...
语句时,确保条件的顺序是合理的。上面的示例展示了如何从高到低逐步判断条件。错误的顺序会导致逻辑错误:
1 | public class Main { |
输出:
1 | 及格了 |
边界条件和浮点数比较
当使用边界条件时,要注意 >
和 >=
的区别:
1 | public class Main { |
输出:
1 | 及格了 |
浮点数比较
浮点数比较时,直接使用 ==
可能不可靠,因为浮点数计算可能产生微小的误差。应使用一个误差范围来比较浮点数:
1 | public class Main { |
输出:
1 | x is 0.1 |
引用类型的比较
对于引用类型(如 String
),==
比较的是引用是否相同(即是否指向同一个对象)。要比较内容是否相等,应使用
.equals()
方法:
1 | public class Main { |
输出:
1 | s1 equals s2 |
为了避免
NullPointerException
,在比较前可以检查对象是否为
null
:
1 | public class Main { |
输出:
无输出,因为 s1
是 null
,不会执行
s1.equals("hello")
语句。
- 使用
if
、else if
和else
进行条件判断。- 确保
if
语句块中的多条语句用{}
括起来,以避免潜在错误。- 使用
else
和else if
来处理不同的条件分支。- 注意判断顺序,确保逻辑正确。
- 使用适当的浮点数比较方法来避免精度问题。
- 对于引用类型的比较,使用
.equals()
方法来比较内容,而不是==
。
在 Java 中,
switch
语句是一种更简洁的条件判断方式,特别适用于根据一个变量的值选择执行不同的代码块。与if
语句相比,switch
语句对于多个分支的条件判断更加清晰,尤其当这些条件基于同一变量时。
基本语法
switch
语句的基本语法如下:
1 | switch (表达式) { |
- 表达式:
switch
语句根据此表达式的结果决定跳转到哪个case
。 - case 值:每个
case
定义了一个可能的值与其对应的执行代码块。 - break:结束当前的
case
执行,防止执行到下一个case
。如果省略break
,程序会继续执行下一个case
,即“穿透”现象。 - default:可选的,提供一个默认的代码块,当没有任何
case
匹配时执行。
示例
简单 switch
示例:
1 | public class Main { |
switch
语句的穿透现象:
1 | public class Main { |
当 option = 2
时,输出会是:
1 | Selected 2 |
这是因为没有 break
语句,导致从 case 2
开始,所有后续的代码块都被执行。
合并 case
:
1 | public class Main { |
字符串匹配
switch
语句也可以用来匹配字符串:
1 | public class Main { |
Java 12 及以后版本的新特性
从 Java 12 开始,switch
语句引入了新的语法形式,提供了更简洁的表达方式,称为“增强的
switch
表达式”或“switch
表达式”:
简化的 switch
语法:
1 | public class Main { |
switch
表达式赋值:
1 | public class Main { |
使用 yield
返回复杂值:
1 | public class Main { |
一些例题:
1. 计算器
这个程序实现一个简单的计算器,根据用户输入的操作符执行加、减、乘、除操作。
1 | import java.util.Scanner; |
2. 星期几
这个程序根据用户输入的数字,输出对应的星期几。
1 | import java.util.Scanner; |
3. 学生成绩等级
这个程序根据学生的分数,输出成绩等级(A, B, C, D, F)。
1 | import java.util.Scanner; |
4. 货币兑换
这个程序将用户输入的金额根据选择的货币进行兑换。
1 | import java.util.Scanner; |
5. 任务调度
这个程序根据用户选择的任务编号,执行对应的任务。
1 | import java.util.Scanner; |
循环语句在编程中是非常重要的,它们允许我们重复执行一段代码,直到满足某个条件为止。Java提供了几种循环结构,包括
while
、do-while
和 for
循环。
while
循环
while
循环的基本结构如下:
1 | while (条件表达式) { |
在 while
循环中,程序会在每次循环开始前判断条件表达式是否为
true
。如果条件为
true
,则执行循环体内的代码,然后再判断条件。如果条件为
false
,则退出循环,继续执行循环后的代码。
示例 1: 计算从1到100的和
1 | public class Main { |
示例 2: 循环条件判断错误的情况
1 | public class Main { |
在这个示例中,n
从0开始,每次循环时 n
都会增加1,然后累加到 sum
中。最终 sum
的值是5050,这里并没有直接的错误,但如果 n
从0开始并且判断条件是 n <= 100
,最后 n
会变成101,而 sum
将累加0到101的值,所以需要注意循环的边界条件。
示例 3: 死循环和溢出
1 | public class Main { |
在这个示例中,n
从1开始,每次循环时自增。n
是 int
类型,所以当它增加到
Integer.MAX_VALUE
(2147483647)时,再增加1就会变成负数,导致循环停止。Integer.MAX_VALUE
是 Java int
类型的最大值,超出后会发生溢出。
do-while
循环
do-while
循环和 while
循环类似,但它至少会执行一次循环体,因为条件是在循环体执行后检查的。
1 | public class Main { |
for
循环
for
循环适用于已知循环次数的情况。其基本结构是:
1 | for (初始化; 条件表达式; 更新) { |
for
循环的例子:
1 | public class Main { |
while
循环: 循环体在每次循环前判断条件,条件不满足时退出循环。do-while
循环: 循环体在每次循环后判断条件,确保至少执行一次。for
循环: 适合固定次数的循环,初始化、条件判断、更新语句在同一行中指定。
以下是关于for
循环一些补充:
1. 灵活使用 for
循环
for
循环在 Java
中提供了很大的灵活性。除了标准的循环形式外,你可以根据需要省略部分或全部的语句。以下是几种特殊的
for
循环写法:
不设置结束条件
1 | for (int i = 0; ; i++) { |
这种形式的 for
循环会一直执行,直到遇到
break
语句或其他终止条件。
不设置结束条件和更新语句
1 | int i = 0; |
这种形式省略了循环变量的初始化和更新语句,初始化在循环外部完成,更新在循环体内处理。
什么都不设置
1 | int i = 0; |
这种形式完全省略了所有 for
循环的标准部分,只提供终止条件和循环体。
2. for-each
循环
for-each
循环提供了一种更简洁的方式来遍历数组或集合中的每个元素。这种循环形式的优点是代码更简洁,更易于理解。
遍历数组
1 | int[] ns = {1, 4, 9, 16, 25}; |
在 for-each
循环中,n
直接代表数组中的每个元素,而不需要手动处理索引。
遍历 List
1 | import java.util.ArrayList; |
for-each
循环也可以用于 List
之类的集合,这使得遍历更加直观和简洁。
3. 其他可迭代的数据类型
除了数组和 List
,for-each
循环还可以遍历
Set
、Map
等实现了 Iterable
接口的集合类型。
遍历 Set
1 | import java.util.HashSet; |
遍历 Map
的键值对
1 | import java.util.HashMap; |
练习题
1. 计算阶乘
编写一个程序,计算给定整数 $ n $ 的阶乘(即 $ n! $)。例如,对于 $ n = 5 $,计算 $ 5! = 5 = 120 $。
1 | public class Main { |
2. 打印九九乘法表
编写一个程序,使用 for
循环打印九九乘法表。
1 | public class Main { |
3. 查找质数
编写一个程序,查找并打印 1 到 100 之间的所有质数。质数是只能被 1 和它自身整除的数。
1 | public class Main { |
4. 打印斐波那契数列
编写一个程序,打印前 10 个斐波那契数。斐波那契数列的定义是:前两个数是 0 和 1,从第三个数开始,每个数都是前两个数之和。
1 | public class Main { |
5. 计算数组元素的平均值
编写一个程序,计算并打印一个整数数组的平均值。
1 | public class Main { |
6. 反转数组
编写一个程序,反转一个整数数组的元素并打印出来。
1 | public class Main { |
break和continue
break
语句
- 功能:
break
用于立即终止当前循环,无论是for
还是while
循环。 - 使用场景: 常配合
if
语句使用,以在满足某个条件时跳出循环。 - 影响范围: 只跳出当前所在的循环层级。例如,在嵌套循环中,只跳出内层循环,外层循环继续执行。
示例:
1 | for (int i = 1; ; i++) { |
continue
语句
- 功能:
continue
用于结束当前循环的当前迭代,直接跳到下一次循环迭代的开始。 - 使用场景: 常配合
if
语句使用,以在满足某个条件时跳过本次循环的剩余部分,直接进行下一次循环。 - 影响范围: 仅影响当前循环层级的迭代,继续执行当前循环的下一次迭代。
示例:
1 | for (int i = 1; i <= 10; i++) { |
break
: 用于退出整个循环。continue
: 用于跳过当前循环的剩余部分,直接进行下一次迭代。reak
: 用于退出整个循环。continue
: 用于跳过当前循环的剩余部分,直接进行下一次迭代。
数组遍历和打印
1. 使用标准 for
循环遍历数组
标准的 for
循环可以用来遍历数组,通过索引来访问每个元素:
1 | public class Main { |
解释: - 初始化:
int i = 0
,索引从0开始。 - 条件:
i < ns.length
,确保索引不超出数组范围。 -
更新: i++
,每次循环后索引增加1。
2. 使用 for-each
循环遍历数组
for-each
循环提供了更简洁的语法来遍历数组,无需显式地使用索引:
1 | public class Main { |
解释: -
for (int n : ns)
,n
直接表示数组
ns
中的每一个元素。 -
适用于只需访问元素而不需要索引的情况。
注意: for-each
循环不能获取当前元素的索引,如果需要索引信息,仍需使用标准
for
循环。
3. 打印数组内容
直接打印数组变量会得到数组在 JVM 中的引用地址,如下:
1 | int[] ns = { 1, 1, 2, 3, 5, 8 }; |
为了打印数组的实际内容,可以使用以下两种方法:
使用 for-each
循环:
1 | int[] ns = { 1, 1, 2, 3, 5, 8 }; |
使用 Arrays.toString()
方法:
Arrays.toString()
方法可以快速打印数组内容,生成数组元素的字符串表示:
1 | import java.util.Arrays; |
解释: - Arrays.toString(ns)
返回数组
ns
的字符串表示,其中包含数组的所有元素,格式为
[元素1, 元素2, ..., 元素n]
。
Java 排序算法
1. 冒泡排序 (Bubble Sort)
基本概念:
- 比较相邻的元素,如果顺序错误就交换它们。
- 每一趟遍历后,将当前最大值放到数组末尾。
时间复杂度: 最坏和平均情况 \(O(n^2)\),最好情况 \(O(n)\)(当数组已排序时)。
实现代码:
1 | public class BubbleSort { |
2. 选择排序 (Selection Sort)
基本概念: - 每一趟找到剩余部分的最小元素,放到当前未排序部分的开始位置。 - 逐步减少待排序部分的长度。
时间复杂度: \(O(n^2)\)(无论最好、最坏还是平均情况)。
实现代码:
1 | public class SelectionSort { |
3. 插入排序 (Insertion Sort)
基本概念: - 将每个元素插入到已排序部分的正确位置。 - 类似于手动排序扑克牌。
时间复杂度: 最坏和平均情况 \(O(n^2)\),最好情况 \(O(n)\)(当数组已排序时)。
实现代码:
1 | public class InsertionSort { |
4. 快速排序 (Quick Sort)
基本概念: - 选择一个基准元素,将数组分成两个子数组,一部分比基准小,一部分比基准大。 - 递归排序子数组。
时间复杂度: 最坏情况 \(O(n^2)\),平均情况 \(O(n \log n)\)。
实现代码:
1 | public class QuickSort { |
5. 归并排序 (Merge Sort)
基本概念: - 将数组分成两个子数组,递归排序两个子数组,然后将它们合并成一个有序数组。
时间复杂度: \(O(n \log n)\)(无论最好、最坏还是平均情况)。
实现代码:
1 | public class MergeSort { |
6. 堆排序 (Heap Sort)
基本概念: - 将数组视为一个最大堆,逐步将最大元素(根节点)移到数组末尾,调整堆。
时间复杂度: \(O(n \log n)\)(无论最好、最坏还是平均情况)。
实现代码:
1 | public class HeapSort { |
- 冒泡排序: 简单但效率低,适合小规模数据。
- 选择排序: 简单实现,性能和冒泡排序相似。
- 插入排序: 适用于小规模数据或几乎已排序的数据。
- 快速排序: 高效且广泛使用,但最坏情况下性能差。
- 归并排序: 稳定且高效,适合大规模数据。
- 堆排序: 高效且不需要额外的空间,适合大规模数据。
Java 多维数组
1. 二维数组
基本概念: - 二维数组是数组的数组。可以将其视为一个矩阵,其中每个元素都是一维数组。 - 二维数组在内存中其实是一个包含多个一维数组的数组。
定义和初始化:
1 | int[][] ns = { |
访问方式: -
访问二维数组中的元素需要使用两个索引,array[row][col]
。 -
例如,ns[1][2]
访问的是第二行第三列的元素
7
。
不规则二维数组: - 每一行的长度可以不同,形成不规则的矩阵。
1 | int[][] ns = { |
打印二维数组: - 可以使用两层嵌套的 for
循环遍历:
1
2
3
4
5
6for (int[] row : ns) {
for (int value : row) {
System.out.print(value + " ");
}
System.out.println();
}
使用
Arrays.deepToString()
方法:1
2
3
4
5
6
7
8
9
10
11
12import 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));
}
}
2. 三维数组
基本概念: - 三维数组是二维数组的数组。可以将其视为多个二维矩阵的集合。 - 在内存中,三维数组表现为一个包含多个二维数组的数组。
定义和初始化:
1 | int[][][] ns = { |
访问方式: -
访问三维数组中的元素需要使用三个索引,array[x][y][z]
。 -
例如,ns[2][0][1]
访问的是第三组(索引为2)、第一行(索引为0)、第二列(索引为1)的元素
15
。
打印三维数组: - 可以使用三层嵌套的 for
循环遍历:
1
2
3
4
5
6
7
8
9for (int[][] matrix : ns) {
for (int[] row : matrix) {
for (int value : row) {
System.out.print(value + " ");
}
System.out.println();
}
System.out.println();
}
- 二维数组:
用于表示矩阵或表格数据,每个元素是一个一维数组。访问方式为
array[row][col]
。 - 三维数组: 用于表示多个二维矩阵的集合。访问方式为
array[x][y][z]
。 - 打印数组: 对于多维数组,可以使用嵌套的
for
循环,或者利用Arrays.deepToString()
方法简化打印过程。
Java 程序的命令行参数
在 Java 程序中,main
方法是程序的入口点,它的标准定义是:
1 | public static void main(String[] args) |
其中 args
是一个 String
类型的数组,代表命令行传递给程序的参数。
1. 命令行参数的接收
当你在命令行中运行一个 Java 程序时,可以在 java
命令后面添加参数。这些参数会被传递给 main
方法的
args
数组。
示例:
1 | public class Main { |
如果你编译并运行这个程序,并传递一些命令行参数,例如:
1 | $ javac Main.java |
输出:
1 | Hello |
2. 处理特定参数
可以根据不同的命令行参数执行不同的代码。例如,如果你希望根据
-version
参数打印程序版本号,可以这样做:
1 | public class Main { |
解释:
args
是一个String
数组,其中包含传递给程序的所有参数。- 使用
for
循环遍历每个参数。 - 使用
if
语句检查是否存在-version
参数。 - 如果发现
-version
,程序打印版本号并通过break
语句退出循环。
3. 编译和运行程序
要使用命令行参数执行 Java 程序,必须在命令行中进行编译和运行:
编译:
这会生成一个1
$ javac Main.java
Main.class
文件。运行:
这会传递1
$ java Main -version
-version
作为命令行参数给程序。
4. 命令行参数的应用场景
- 配置: 可以通过命令行参数传递配置信息,例如数据库连接字符串或文件路径。
- 功能切换: 根据不同的参数决定程序的行为,如开启调试模式或打印详细日志。
- 版本控制: 实现
-version
参数来显示程序版本信息。
5. 命令行参数的特点
- 字符串类型: 所有命令行参数都是
String
类型,即使你输入数字,也会被当作字符串处理。 - 顺序: 参数的顺序在
args
数组中是保留的,你可以根据需要处理这些参数。 - 没有强类型检查: 需要在代码中进行适当的解析和验证,确保参数的正确性。