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的全部功能。

学习路线图

  1. 学习Java SE:掌握Java语言、核心技术和标准库。
  2. 进阶Java EE:学习Web开发、数据库开发和分布式架构,如Spring框架。
  3. 大数据开发:学习Hadoop、Spark等基于Java的技术。
  4. 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. 代码示例

    1
    2
    3
    4
    5
    public class Hello {
    public static void main(String[] args) {
    System.out.println("Hello, world!");
    }
    }
  2. 解释

    • public class Hello:定义一个公开的类,类名是Hello
    • public static void main(String[] args):这是程序的入口方法,main方法是Java程序的起点。
    • System.out.println("Hello, world!");:输出“Hello, world!”到屏幕。
  3. 文件名

    • 文件名必须与类名相同,且区分大小写:Hello.java
  4. 格式

    • Java源码的缩进并非强制,但通常使用4个空格或一个tab进行缩进,以提高代码的可读性。

如何运行Java程序

  1. 编译源码

    • 使用javac命令将Java源码(.java文件)编译成字节码(.class文件)。
    • 命令:
      1
      javac Hello.java
    • 编译后目录下会生成Hello.class文件。
  2. 运行字节码

    • 使用java命令运行编译后的字节码文件。
    • 命令:
      1
      java Hello
    • 输出:
      1
      Hello, world!
  3. 单文件运行(Java 11+)

    • 直接运行.java文件,不需要预先编译:
      1
      java Hello.java
    • 但在实际项目中,通常会涉及多个文件和依赖库,这种单文件运行不适用。
  • javac:将.java编译为.class
  • java:运行.class文件。
  • Java 11起支持直接运行单个.java文件,但在复杂项目中常需编译和管理多个文件及库。

Java基础

Java程序的基本结构包括以下几个部分:

  1. 类定义
    1
    2
    3
    public class Hello {
    // 类的内容
    }
    • 类名:应以字母开头,后接字母、数字或下划线。习惯上首字母大写,如Hello
  2. 方法定义
    1
    2
    3
    public static void main(String[] args) {
    // 方法的代码
    }
    • 方法名:应以小写字母开头,符合驼峰命名法,如main
    • 返回值void表示方法没有返回值。
    • static:表示静态方法,Java程序入口方法必须是静态的。
  3. 语句
    • 每行语句以分号(;)结束。
      1
      System.out.println("Hello, world!");
  4. 注释
    • 单行注释:以//开头。
      1
      // 这是单行注释
    • 多行注释:以/*开头,以*/结束。
      1
      2
      3
      /*
      这是多行注释
      */
    • 文档注释:以/**开头,以*/结束,通常用于生成文档。
      1
      2
      3
      4
      /**
      * 这是文档注释
      * @author liaoxuefeng
      */
  5. 格式化
    • Java代码格式化遵循社区约定,使用IDE(如Eclipse)的格式化功能(快捷键Ctrl+Shift+F)帮助保持代码整洁。

变量

在Java中,变量用于存储数据。变量分为两种类型:基本类型和引用类型。以下是基本类型变量的简单介绍:

  1. 定义变量

    1
    int x = 1; // 定义一个整型变量x,并赋初值1
  2. 使用变量

    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的值
    }
    }

  3. 重新赋值

    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
    }
    }

  4. 变量之间的赋值

    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
2
int 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
  • 后缀ffloat类型的数值需要在末尾加fF,否则默认为double

示例

1
2
float f = 3.14f;
double d = 1.79e308;

3. 布尔类型

布尔类型boolean只有两个值:truefalse。它通常用于逻辑判断或条件控制。

  • 存储:理论上,布尔类型仅需1个bit,但在JVM中通常以4个字节来表示。

示例

1
2
boolean isGreater = 5 > 3; // true
boolean isAdult = age >= 18; // false

4. 字符类型

char用于表示单个字符,支持Unicode编码,可以表示全世界的文字。

数据类型 大小 (字节) 取值范围
char 2 0 ~ 65535
  • Unicodechar类型可以表示的字符包括ASCII和Unicode标准中的所有字符。

示例

1
2
char 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
2
int i = (100 + 200) * (99 - 88); // 3300
int n = 7 * (5 + (i - 9)); // 23072
  • 除法与求余:除法会丢弃小数部分,只保留整数部分。求余运算返回除法的余数。例如:
1
2
int x = 12345 / 67; // 184
int y = 12345 % 67; // 余数17
  • 运算溢出:整数在运算时,如果结果超出了数据类型的范围(例如 int),会发生溢出。溢出不会抛出错误,而是返回一个错误的值:
1
2
3
int x = 2147483640;
int y = 15;
int sum = x + y; // -2147483641 (因为发生了溢出)

要避免溢出,可以使用更大范围的 long 类型:

1
2
3
long x = 2147483640L;
long y = 15L;
long sum = x + y; // 2147483655

自增/自减运算

  • 自增 (++) 和自减 (--):可以对变量进行加1或减1操作。++n 是先加1再使用变量,而 n++ 是先使用变量再加1。建议避免将自增运算混入复杂的表达式中,以防引起混淆。
1
2
3
4
int n = 3300;
n++; // 3301
n--; // 3300
int y = 100 + (++n); // 3401

移位运算

移位运算基于整数的二进制表示形式。 - 左移 (<<):将所有位左移,低位补0,实际上是乘以2的幂。 - 右移 (>>):将所有位右移,高位补符号位,实际上是除以2的幂。 - 无符号右移 (>>>):与 >> 类似,但不保留符号位,高位补0。

示例:

1
2
3
4
int n = 7;       // 00000000 00000000 00000000 00000111
int a = n << 1; // 00000000 00000000 00000000 00001110 = 14
int b = n >> 1; // 00000000 00000000 00000000 00000011 = 3
int c = n >>> 1; // 00000000 00000000 00000000 00000011 = 3

对于负数,右移运算保持符号位:

1
2
3
int n = -536870912;
int a = n >> 1; // 11110000 00000000 00000000 00000000 = -268435456
int b = n >>> 1; // 01110000 00000000 00000000 00000000 = 1879048192

位运算

  • 与 (&):按位与运算,只有两个位都为1时结果才为1。
  • 或 (|):按位或运算,只要任一位为1,结果就是1。
  • 非 (~):按位取反运算,0变1,1变0。
  • 异或 (^):按位异或运算,两个位不同结果为1,相同结果为0。

示例:

1
2
3
int i = 167776589; // 00001010 00000000 00010001 01001101
int n = 167776512; // 00001010 00000000 00010001 00000000
System.out.println(i & n); // 167776512

运算优先级

运算符的优先级从高到低如下: 1. () (括号) 2. ! ~ ++ -- (逻辑非,按位取反,自增,自减) 3. * / % (乘,除,取余) 4. + - (加,减) 5. << >> >>> (左移,右移,无符号右移) 6. & (按位与) 7. | (按位或) 8. += -= *= /= (赋值运算符)

如果不确定优先级,使用括号 () 来确保运算顺序正确。

类型自动提升与强制转型

在表达式中,较小范围的类型(如 short)会自动提升为较大范围的类型(如 int)。强制类型转换可以将一个大范围的类型转换为小范围类型,但需注意可能发生的数据丢失:

1
2
3
4
short s = 1234;
int i = 123456;
int x = s + i; // s自动转型为int
short y = (short) (s + i); // 强制转型,可能会丢失数据

超出范围的强制转型会导致不正确的结果:

1
2
int i1 = 1234567;
short s1 = (short) i1; // -10617,数据丢失

一些例题:

例题1:计算商品折扣价格

编写一个程序计算商品的折扣价格。已知商品的原价为int price = 1200,折扣为int discount = 15(表示15%)。要求计算出折扣后的价格并打印出来。

提示:
计算折扣后的价格可以使用公式:折扣价格 = 原价 * (1 - 折扣 / 100)。注意在计算中可能涉及整数除法的精度问题。

例题2:判断奇偶性

编写一个程序判断一个整数是否为偶数。输入一个整数int num,如果该整数为偶数,打印"Even",否则打印"Odd"

提示:
使用位运算来判断奇偶性,具体方法是检查整数的最低位是否为0。

例题3:处理溢出问题

编写一个程序,演示整数溢出问题。定义两个变量int a = 1000000int 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 aint 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
2
3
4
5
6
7
8
9
10
public class DiscountPrice {
public static void main(String[] args) {
int price = 1200; // 商品原价
int discount = 15; // 折扣百分比

int discountPrice = price * (100 - discount) / 100;

System.out.println("折扣后的价格为: " + discountPrice);
}
}

输出:
折扣后的价格为: 1020

例题2:判断奇偶性

1
2
3
4
5
6
7
8
9
10
11
public class EvenOdd {
public static void main(String[] args) {
int num = 29; // 输入的整数

if ((num & 1) == 0) {
System.out.println("Even");
} else {
System.out.println("Odd");
}
}
}

输出:
Odd

例题3:处理溢出问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class OverflowExample {
public static void main(String[] args) {
int a = 1000000;
int b = 2000;

// 可能发生溢出
int result = a * b;
System.out.println("发生溢出的结果: " + result);

// 使用long类型防止溢出
long safeResult = (long) a * b;
System.out.println("防止溢出的结果: " + safeResult);
}
}

输出:
发生溢出的结果: -727379968
防止溢出的结果: 2000000000

例题4:IP地址网段判断

1
2
3
4
5
6
7
8
9
10
11
12
13
public class IPAddressCheck {
public static void main(String[] args) {
int ip = 3232235777; // IP地址 192.168.1.1
int network = 3232235776; // 网络地址 192.168.1.0
int mask = 4294967040; // 子网掩码 255.255.255.0

if ((ip & mask) == (network & mask)) {
System.out.println("IP在网段内");
} else {
System.out.println("IP不在网段内");
}
}
}

输出:
IP在网段内

例题5:实现简单的计算器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import java.util.Scanner;

public class SimpleCalculator {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);

System.out.println("输入第一个整数: ");
int a = scanner.nextInt();
System.out.println("输入第二个整数: ");
int b = scanner.nextInt();
System.out.println("输入操作符 (+, -, *, /): ");
char operator = scanner.next().charAt(0);

int result = 0;

switch (operator) {
case '+':
result = a + b;
break;
case '-':
result = a - b;
break;
case '*':
result = a * b;
break;
case '/':
if (b != 0) {
result = a / b;
} else {
System.out.println("除数不能为0");
return;
}
break;
default:
System.out.println("无效的操作符");
return;
}

System.out.println("结果: " + result);
}
}

输入/输出示例:
输入第一个整数: 10
输入第二个整数: 2
输入操作符 (+, -, , /):
结果: 20

例题6:类型转换陷阱

1
2
3
4
5
6
7
8
9
public class TypeConversionTrap {
public static void main(String[] args) {
int i = 987654321;
short s = (short) i;

System.out.println("原值: " + i);
System.out.println("转换后的short值: " + s);
}
}

输出:
原值: 987654321
转换后的short值: 7225

例题7:左移与右移操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class ShiftOperations {
public static void main(String[] args) {
int n = 1;

System.out.println("n << 1: " + (n << 1));
System.out.println("n << 2: " + (n << 2));
System.out.println("n << 3: " + (n << 3));

int m = -4;

System.out.println("m >> 1: " + (m >> 1));
System.out.println("m >> 2: " + (m >> 2));
System.out.println("m >> 3: " + (m >> 3));
}
}

输出:
n << 1: 2
n << 2: 4
n << 3: 8
m >> 1: -2
m >> 2: -1
m >> 3: -1

例题8:计算二进制中1的个数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class CountBits {
public static void main(String[] args) {
int n = 29; // 29的二进制是11101

int count = 0;

while (n != 0) {
count += n & 1;
n >>= 1;
}

System.out.println("二进制中1的个数: " + count);
}
}

输出:
二进制中1的个数: 4

这些例题展示了Java整数运算的各种常见场景及其处理方式,理解这些问题有助于掌握Java编程中的关键概念。

浮点数运算的细节

在计算机中,浮点数的运算由于表示方法的原因会产生误差,这与整数运算不同。

1. 浮点数的表示和误差

浮点数在计算机中的表示方式导致其不能精确表示某些值,例如0.1。因为0.1在二进制中是一个无限循环小数,所以存储时只能近似表示。运行以下代码可以观察浮点数的误差:

1
2
3
4
5
6
7
8
public class Main {
public static void main(String[] args) {
double x = 1.0 / 10;
double y = 1 - 9.0 / 10;
System.out.println(x); // 输出接近于0.1
System.out.println(y); // 输出接近于0.1
}
}

由于浮点数无法精确表示,直接比较两个浮点数是否相等可能会导致错误。因此,比较两个浮点数时,通常使用以下方法:

1
2
3
4
5
6
double r = Math.abs(x - y);
if (r < 0.00001) {
System.out.println("x和y相等");
} else {
System.out.println("x和y不相等");
}

2. 类型提升

在混合运算中,若参与运算的两个数中有一个是浮点型,整数会自动提升为浮点型进行运算。例如:

1
2
3
4
5
6
7
public class Main {
public static void main(String[] args) {
int n = 5;
double d = 1.2 + 24.0 / n; // 6.0
System.out.println(d); // 输出6.0
}
}

但需要注意,在复杂的四则运算中,如果都是整数,编译器仍会按整数进行运算。例如:

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
2
3
4
5
6
7
8
9
10
public class Main {
public static void main(String[] args) {
double d1 = 0.0 / 0; // NaN
double d2 = 1.0 / 0; // Infinity
double d3 = -1.0 / 0; // -Infinity
System.out.println(d1);
System.out.println(d2);
System.out.println(d3);
}
}

4. 强制转型与四舍五入

浮点数可以强制转型为整数,小数部分会被直接丢弃。例如:

1
2
3
4
int n1 = (int) 12.3;  // 12
int n2 = (int) 12.7; // 12
int n3 = (int) -12.7; // -12
int n4 = (int) 1.2e20; // 2147483647 (超过int范围,返回最大值)

如果想实现四舍五入,可以在转型前加上0.5:

1
2
3
4
5
6
7
public class Main {
public static void main(String[] args) {
double d = 2.6;
int n = (int) (d + 0.5); // 3
System.out.println(n);
}
}

一些例题:

例题 1: 浮点数精度问题

计算以下表达式并判断结果是否为预期值:

1
2
3
4
5
6
7
public 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
8
public 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
12
public 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
8
public 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
10
public 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 中只有两个值:truefalse。布尔类型用于表示逻辑值和结果。

布尔运算

布尔运算主要包括比较运算符、与运算符、或运算符、非运算符等。

  1. 比较运算符 比较运算符用于比较两个值,结果是布尔值:

    • > 大于
    • >= 大于或等于
    • < 小于
    • <= 小于或等于
    • == 等于
    • != 不等于

    示例:

    1
    2
    3
    4
    5
    6
    boolean 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

  2. 逻辑运算符

    • 与运算符 (&&): 如果两个操作数都为 true,结果为 true,否则为 false
    • 或运算符 (||): 如果至少一个操作数为 true,结果为 true,否则为 false
    • 非运算符 (!): 取反操作符,将 true 变为 false,将 false 变为 true

    示例:

    1
    2
    3
    boolean result1 = true && false; // false
    boolean result2 = true || false; // true
    boolean result3 = !true; // false

  3. 短路运算 短路运算是一种优化机制,当可以确定整个表达式的结果时,后续部分的计算会被跳过:

    • 对于 &&(与运算),如果第一个操作数为 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
    }
    }

    如果 btrue,则 (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
    }
    }
  4. 三元运算符 三元运算符用于基于布尔表达式的结果选择两个值中的一个。语法为 b ? x : y,其中 b 为布尔表达式,xy 为待返回的值。

    • 如果 btrue,返回 x
    • 如果 bfalse,返回 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
2
3
4
5
6
7
public class Main {
public static void main(String[] args) {
int number = 15;
boolean isInRange = number >= 10 && number <= 20;
System.out.println("Is in range: " + isInRange); // true
}
}

例题 2: 判断一个数是否为正数或零,并且不是 100。

1
2
3
4
5
6
7
public class Main {
public static void main(String[] args) {
int number = 50;
boolean result = (number >= 0 && number != 100);
System.out.println("Result: " + result); // true
}
}

例题 3: 使用三元运算符计算一个数的绝对值。

1
2
3
4
5
6
7
public class Main {
public static void main(String[] args) {
int number = -75;
int absoluteValue = number >= 0 ? number : -number;
System.out.println("Absolute value: " + absoluteValue); // 75
}
}

Java中的字符和字符串

在Java中,字符(char)和字符串(String)是两种不同的类型,分别用于处理单个字符和字符序列。

字符类型 char

  • 定义: char 是基本数据类型,用于表示单个 Unicode 字符。
  • 大小: 一个 char 占用 2 个字节。
  • Unicode: Java 使用 Unicode 编码来表示字符,无论是英文字符还是中文字符。

示例:

1
2
3
4
5
6
char c1 = 'A'; // 英文字符 A
char c2 = '中'; // 中文字符 中

// 输出字符的 Unicode 编码
int n1 = 'A'; // 65
int n2 = '中'; // 20013

使用转义字符: - \u 加上四位十六进制数表示 Unicode 字符。

示例:

1
2
char c3 = '\u0041'; // 'A'
char c4 = '\u4e2d'; // '中'

字符串类型 String

  • 定义: String 是引用类型,用双引号 " 包含字符序列。
  • 大小: 一个 String 对象可以存储任意长度的字符序列。

示例:

1
2
3
4
String s = ""; // 空字符串
String s1 = "A"; // 包含一个字符
String s2 = "ABC"; // 包含三个字符
String s3 = "中文 ABC"; // 包含六个字符

转义字符: - 用于在字符串中插入特殊字符: - \" 表示双引号字符 " - \' 表示单引号字符 ' - \\ 表示反斜杠字符 \ - \n 表示换行符 - \r 表示回车符 - \t 表示制表符 - \u#### 表示 Unicode 编码字符(四位十六进制)

示例:

1
2
3
String 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
8
String 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
15
String 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
7
String s = """
SELECT * FROM
users
WHERE id > 100
ORDER BY name DESC
""";
System.out.println(s);

字符串的不可变性

  • 不可变性: Java 中的字符串是不可变的。这意味着一旦创建,字符串内容不能被改变。修改字符串实际上是创建了一个新的字符串对象。

示例:

1
2
3
4
String s = "hello";
String t = s;
s = "world";
System.out.println(t); // 输出 "hello",因为 t 仍然指向原来的字符串

解释: - 初始时,st 都指向字符串 "hello"。 - 当 s 被赋值为 "world" 时,t 仍然指向原来的字符串 "hello"。 - 字符串 "hello" 并没有被修改,而是创建了一个新的字符串对象 "world"

空值 null

  • 定义: null 表示一个变量不指向任何对象。与空字符串 "" 不同,空字符串是一个有效的字符串对象。

示例:

1
2
3
String s1 = null; // s1 是 null
String s2 = s1; // s2 也是 null
String s3 = ""; // s3 指向一个空字符串对象,不是 null

区分: - null 是一个特殊的值,表示“没有对象”。 - 空字符串 "" 是一个有效的 String 对象,长度为0。

数组基础

  1. 定义和初始化数组

    在Java中,数组用于存储相同类型的数据集合。你可以用以下方式定义和初始化一个数组:

    1
    2
    3
    4
    int[] ns = new int[5]; // 创建一个包含5个元素的整型数组
    ns[0] = 68; // 设置第一个元素为68
    ns[1] = 79; // 设置第二个元素为79
    // 依此类推...

    你也可以在定义数组时直接初始化其元素:

    1
    int[] ns = {68, 79, 91, 85, 62}; // 创建一个包含5个初始化元素的整型数组
  2. 数组的特点

    • 大小不可变:一旦创建,数组的大小不能更改。例如:

      1
      2
      int[] 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
2
3
4
5
6
7
8
public class Main {
public static void main(String[] args) {
String[] names = {"ABC", "XYZ", "zoo"};
String s = names[1]; // s指向"XYZ"
names[1] = "cat"; // names[1]现在指向"cat"
System.out.println(s); // 输出 "XYZ"
}
}

在这个代码片段中:

  • String s = names[1]; 这行代码将 names 数组中索引为1的元素(即"XYZ")的引用赋给变量 s
  • 当你执行 names[1] = "cat"; 时,你实际上是将 names 数组中索引为1的元素的引用从"XYZ"更改为"cat"。
  • s 仍然指向原来的"XYZ"字符串,因为 snames[1] 分别保存了不同的引用。

这表明对数组中元素的修改不会影响已保存的其他引用的值。s 指向的字符串依然是"XYZ",即使 names[1] 现在指向了"cat"。

要点:

  • 数组:数组的大小一旦确定就不能更改,数组中的元素可以通过索引访问,数组的元素初始化为默认值。
  • 字符串数组:字符串是引用类型,对数组元素的修改实际上是改变了数组中引用的对象。数组的元素在被修改时,原有的字符串对象不会改变,只是引用发生了变化。

在Java中,输入和输出(I/O)是程序与外界交互的重要方式。下面是对你提供的代码以及相关概念的详细解释。

输出

System.out.println()System.out.print() 用于在控制台输出信息:

  • System.out.println():输出信息并自动换行。
  • System.out.print():输出信息但不换行。

示例代码:

1
2
3
4
5
6
7
8
9
public class Main {
public static void main(String[] args) {
System.out.print("A,");
System.out.print("B,");
System.out.print("C.");
System.out.println(); // 换行
System.out.println("END");
}
}

执行效果:

1
2
A,B,C
END

格式化输出

格式化输出使用 System.out.printf() 方法,它允许你根据指定的格式输出信息。格式化输出通常用在需要以特定格式呈现数据时,例如对浮点数的精确度控制或者将整数转换为不同的进制。

格式化字符串使用占位符(如 %d, %f 等)来指定输出的格式。常用占位符及其说明:

  • %d:格式化整数
  • %x:格式化为十六进制整数
  • %f:格式化浮点数
  • %e:格式化为科学计数法表示的浮点数
  • %s:格式化字符串

示例代码:

1
2
3
4
5
6
7
8
9
10
public class Main {
public static void main(String[] args) {
double d = 3.1415926;
System.out.printf("%.2f\n", d); // 显示两位小数 3.14
System.out.printf("%.4f\n", d); // 显示四位小数 3.1416

int n = 12345000;
System.out.printf("n=%d, hex=%08x\n", n, n); // 输出整数及其十六进制形式,十六进制数补足到8位
}
}

执行效果:

1
2
3
3.14
3.1416
n=12345000, hex=00bc614e

输入

Java中用于从控制台读取输入的主要工具是 Scanner 类。Scanner 类提供了便捷的方法来读取各种类型的输入数据,例如字符串和整数。

使用 Scanner 类的步骤:

  1. 导入 Scanner:通过 import 语句导入 java.util.Scanner
  2. 创建 Scanner 对象:将 System.in 传递给 Scanner 构造函数,表示从标准输入流中读取数据。
  3. 读取数据:使用 Scanner 对象的方法(如 nextLine()nextInt())读取用户输入的数据。

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
import java.util.Scanner;

public class Main {
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); // 格式化输出
}
}

执行步骤:

  1. 编译:使用 javac 命令编译代码:

    1
    $ javac Main.java

  2. 执行:使用 java 命令运行编译后的程序:

    1
    $ java Main

    然后根据提示输入数据,例如:

    1
    2
    3
    Input 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中,条件控制语句(如 ifelse ifelse)用于根据条件的真假来控制程序的执行路径。以下是对if语句及相关条件判断的详细解释,包括常见的陷阱和最佳实践。

基本 if 语句

if 语句的基本结构如下:

1
2
3
if (条件) {
// 条件满足时执行
}

当条件为 true 时,if 语句块中的代码将被执行。如果条件为 false,则跳过该代码块。

示例代码:

1
2
3
4
5
6
7
8
9
public class Main {
public static void main(String[] args) {
int n = 70;
if (n >= 60) {
System.out.println("及格了");
}
System.out.println("END");
}
}

输出:

1
2
及格了
END

多语句的 if 语句块

if 语句块中有多条语句时,必须使用花括号 {} 将其括起来,以确保所有语句都属于 if 语句块的一部分。

示例代码:

1
2
3
4
5
6
7
8
9
10
public class Main {
public static void main(String[] args) {
int n = 70;
if (n >= 60) {
System.out.println("及格了");
System.out.println("恭喜你");
}
System.out.println("END");
}
}

输出:

1
2
3
及格了
恭喜你
END

省略花括号的情况

if 语句块只有一条语句时,可以省略花括号,但这种做法不推荐,因为它可能导致代码难以维护和出错。

示例代码:

1
2
3
4
5
6
7
8
public class Main {
public static void main(String[] args) {
int n = 70;
if (n >= 60)
System.out.println("及格了");
System.out.println("END");
}
}

输出:

1
2
及格了
END

使用 elseelse if

elseelse if 语句用于处理 if 条件为 false 时的情况。

示例代码:

1
2
3
4
5
6
7
8
9
10
11
public class Main {
public static void main(String[] args) {
int n = 70;
if (n >= 60) {
System.out.println("及格了");
} else {
System.out.println("挂科了");
}
System.out.println("END");
}
}

输出:

1
2
及格了
END

可以使用多个 else if 来处理多个条件:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Main {
public static void main(String[] args) {
int n = 70;
if (n >= 90) {
System.out.println("优秀");
} else if (n >= 60) {
System.out.println("及格了");
} else {
System.out.println("挂科了");
}
System.out.println("END");
}
}

输出:

1
2
及格了
END

判断顺序

在使用多个 if ... else if ... 语句时,确保条件的顺序是合理的。上面的示例展示了如何从高到低逐步判断条件。错误的顺序会导致逻辑错误:

1
2
3
4
5
6
7
8
9
10
11
12
public class Main {
public static void main(String[] args) {
int n = 100;
if (n >= 60) {
System.out.println("及格了");
} else if (n >= 90) {
System.out.println("优秀");
} else {
System.out.println("挂科了");
}
}
}

输出:

1
及格了

边界条件和浮点数比较

当使用边界条件时,要注意 >>= 的区别:

1
2
3
4
5
6
7
8
9
10
11
12
public class Main {
public static void main(String[] args) {
int n = 90;
if (n > 90) {
System.out.println("优秀");
} else if (n >= 60) {
System.out.println("及格了");
} else {
System.out.println("挂科了");
}
}
}

输出:

1
及格了

浮点数比较

浮点数比较时,直接使用 == 可能不可靠,因为浮点数计算可能产生微小的误差。应使用一个误差范围来比较浮点数:

1
2
3
4
5
6
7
8
9
10
public class Main {
public static void main(String[] args) {
double x = 1 - 9.0 / 10;
if (Math.abs(x - 0.1) < 0.00001) {
System.out.println("x is 0.1");
} else {
System.out.println("x is NOT 0.1");
}
}
}

输出:

1
x is 0.1

引用类型的比较

对于引用类型(如 String),== 比较的是引用是否相同(即是否指向同一个对象)。要比较内容是否相等,应使用 .equals() 方法:

1
2
3
4
5
6
7
8
9
10
11
public class Main {
public static void main(String[] args) {
String s1 = "hello";
String s2 = "HELLO".toLowerCase();
if (s1.equals(s2)) {
System.out.println("s1 equals s2");
} else {
System.out.println("s1 not equals s2");
}
}
}

输出:

1
s1 equals s2

为了避免 NullPointerException,在比较前可以检查对象是否为 null

1
2
3
4
5
6
7
8
public class Main {
public static void main(String[] args) {
String s1 = null;
if (s1 != null && s1.equals("hello")) {
System.out.println("hello");
}
}
}

输出:

无输出,因为 s1null,不会执行 s1.equals("hello") 语句。

  • 使用 ifelse ifelse 进行条件判断。
  • 确保 if 语句块中的多条语句用 {} 括起来,以避免潜在错误。
  • 使用 elseelse if 来处理不同的条件分支。
  • 注意判断顺序,确保逻辑正确。
  • 使用适当的浮点数比较方法来避免精度问题。
  • 对于引用类型的比较,使用 .equals() 方法来比较内容,而不是 ==

在 Java 中,switch 语句是一种更简洁的条件判断方式,特别适用于根据一个变量的值选择执行不同的代码块。与 if 语句相比,switch 语句对于多个分支的条件判断更加清晰,尤其当这些条件基于同一变量时。

基本语法

switch 语句的基本语法如下:

1
2
3
4
5
6
7
8
9
10
11
switch (表达式) {
case1:
// 语句块1
break;
case2:
// 语句块2
break;
...
default:
// 默认语句块
}
  • 表达式switch 语句根据此表达式的结果决定跳转到哪个 case
  • case 值:每个 case 定义了一个可能的值与其对应的执行代码块。
  • break:结束当前的 case 执行,防止执行到下一个 case。如果省略 break,程序会继续执行下一个 case,即“穿透”现象。
  • default:可选的,提供一个默认的代码块,当没有任何 case 匹配时执行。

示例

简单 switch 示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Main {
public static void main(String[] args) {
int option = 1;
switch (option) {
case 1:
System.out.println("Selected 1");
break;
case 2:
System.out.println("Selected 2");
break;
case 3:
System.out.println("Selected 3");
break;
default:
System.out.println("Selected other");
break;
}
}
}

switch 语句的穿透现象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Main {
public static void main(String[] args) {
int option = 2;
switch (option) {
case 1:
System.out.println("Selected 1");
case 2:
System.out.println("Selected 2");
case 3:
System.out.println("Selected 3");
default:
System.out.println("Selected other");
}
}
}

option = 2 时,输出会是:

1
2
3
Selected 2
Selected 3
Selected other

这是因为没有 break 语句,导致从 case 2 开始,所有后续的代码块都被执行。

合并 case

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Main {
public static void main(String[] args) {
int option = 2;
switch (option) {
case 1:
System.out.println("Selected 1");
break;
case 2:
case 3:
System.out.println("Selected 2, 3");
break;
default:
System.out.println("Selected other");
break;
}
}
}

字符串匹配

switch 语句也可以用来匹配字符串:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Main {
public static void main(String[] args) {
String fruit = "apple";
switch (fruit) {
case "apple":
System.out.println("Selected apple");
break;
case "pear":
System.out.println("Selected pear");
break;
case "mango":
System.out.println("Selected mango");
break;
default:
System.out.println("No fruit selected");
break;
}
}
}

Java 12 及以后版本的新特性

从 Java 12 开始,switch 语句引入了新的语法形式,提供了更简洁的表达方式,称为“增强的 switch 表达式”或“switch 表达式”:

简化的 switch 语法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Main {
public static void main(String[] args) {
String fruit = "apple";
switch (fruit) {
case "apple" -> System.out.println("Selected apple");
case "pear" -> System.out.println("Selected pear");
case "mango" -> {
System.out.println("Selected mango");
System.out.println("Good choice!");
}
default -> System.out.println("No fruit selected");
}
}
}

switch 表达式赋值:

1
2
3
4
5
6
7
8
9
10
11
public class Main {
public static void main(String[] args) {
String fruit = "apple";
int opt = switch (fruit) {
case "apple" -> 1;
case "pear", "mango" -> 2;
default -> 0;
}; // 注意赋值语句要以;结束
System.out.println("opt = " + opt);
}
}

使用 yield 返回复杂值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Main {
public static void main(String[] args) {
String fruit = "orange";
int opt = switch (fruit) {
case "apple" -> 1;
case "pear", "mango" -> 2;
default -> {
int code = fruit.hashCode();
yield code; // 使用yield返回值
}
};
System.out.println("opt = " + opt);
}
}

一些例题:

1. 计算器

这个程序实现一个简单的计算器,根据用户输入的操作符执行加、减、乘、除操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import java.util.Scanner;

public class Calculator {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);

System.out.println("Enter first number:");
double num1 = scanner.nextDouble();

System.out.println("Enter an operator (+, -, *, /):");
char operator = scanner.next().charAt(0);

System.out.println("Enter second number:");
double num2 = scanner.nextDouble();

double result;

switch (operator) {
case '+':
result = num1 + num2;
System.out.println("Result: " + result);
break;
case '-':
result = num1 - num2;
System.out.println("Result: " + result);
break;
case '*':
result = num1 * num2;
System.out.println("Result: " + result);
break;
case '/':
if (num2 != 0) {
result = num1 / num2;
System.out.println("Result: " + result);
} else {
System.out.println("Error: Division by zero");
}
break;
default:
System.out.println("Error: Invalid operator");
break;
}

scanner.close();
}
}

2. 星期几

这个程序根据用户输入的数字,输出对应的星期几。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
import java.util.Scanner;

public class Weekday {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);

System.out.println("Enter a number (1-7):");
int day = scanner.nextInt();

String dayName;

switch (day) {
case 1:
dayName = "Monday";
break;
case 2:
dayName = "Tuesday";
break;
case 3:
dayName = "Wednesday";
break;
case 4:
dayName = "Thursday";
break;
case 5:
dayName = "Friday";
break;
case 6:
dayName = "Saturday";
break;
case 7:
dayName = "Sunday";
break;
default:
dayName = "Invalid day";
break;
}

System.out.println("The day is: " + dayName);

scanner.close();
}
}

3. 学生成绩等级

这个程序根据学生的分数,输出成绩等级(A, B, C, D, F)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import java.util.Scanner;

public class GradeEvaluator {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);

System.out.println("Enter your score (0-100):");
int score = scanner.nextInt();

char grade;

switch (score / 10) {
case 10:
case 9:
grade = 'A';
break;
case 8:
grade = 'B';
break;
case 7:
grade = 'C';
break;
case 6:
grade = 'D';
break;
default:
grade = 'F';
break;
}

System.out.println("Your grade is: " + grade);

scanner.close();
}
}

4. 货币兑换

这个程序将用户输入的金额根据选择的货币进行兑换。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import java.util.Scanner;

public class CurrencyConverter {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);

System.out.println("Enter amount in USD:");
double amount = scanner.nextDouble();

System.out.println("Select target currency (1 for EUR, 2 for JPY, 3 for GBP):");
int choice = scanner.nextInt();

double convertedAmount;

switch (choice) {
case 1:
convertedAmount = amount * 0.93; // Example conversion rate
System.out.println("Amount in EUR: " + convertedAmount);
break;
case 2:
convertedAmount = amount * 138.56; // Example conversion rate
System.out.println("Amount in JPY: " + convertedAmount);
break;
case 3:
convertedAmount = amount * 0.77; // Example conversion rate
System.out.println("Amount in GBP: " + convertedAmount);
break;
default:
System.out.println("Invalid choice");
break;
}

scanner.close();
}
}

5. 任务调度

这个程序根据用户选择的任务编号,执行对应的任务。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import java.util.Scanner;

public class TaskScheduler {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);

System.out.println("Select a task (1 for Task A, 2 for Task B, 3 for Task C):");
int task = scanner.nextInt();

switch (task) {
case 1:
System.out.println("Executing Task A");
// Add code for Task A
break;
case 2:
System.out.println("Executing Task B");
// Add code for Task B
break;
case 3:
System.out.println("Executing Task C");
// Add code for Task C
break;
default:
System.out.println("Invalid task selection");
break;
}

scanner.close();
}
}

循环语句在编程中是非常重要的,它们允许我们重复执行一段代码,直到满足某个条件为止。Java提供了几种循环结构,包括 whiledo-whilefor 循环。

while 循环

while 循环的基本结构如下:

1
2
3
4
while (条件表达式) {
// 循环体
// 执行的代码
}

while 循环中,程序会在每次循环开始前判断条件表达式是否为 true。如果条件为 true,则执行循环体内的代码,然后再判断条件。如果条件为 false,则退出循环,继续执行循环后的代码。

示例 1: 计算从1到100的和

1
2
3
4
5
6
7
8
9
10
11
public class Main {
public static void main(String[] args) {
int sum = 0; // 初始化累加和
int n = 1; // 从1开始累加
while (n <= 100) { // 当n小于等于100时继续循环
sum = sum + n; // 将n累加到sum中
n++; // n自增1
}
System.out.println("Sum: " + sum); // 输出结果
}
}

示例 2: 循环条件判断错误的情况

1
2
3
4
5
6
7
8
9
10
11
public class Main {
public static void main(String[] args) {
int sum = 0;
int n = 0;
while (n <= 100) {
n++;
sum = sum + n;
}
System.out.println("Sum: " + sum); // 输出结果
}
}

在这个示例中,n 从0开始,每次循环时 n 都会增加1,然后累加到 sum 中。最终 sum 的值是5050,这里并没有直接的错误,但如果 n 从0开始并且判断条件是 n <= 100,最后 n 会变成101,而 sum 将累加0到101的值,所以需要注意循环的边界条件。

示例 3: 死循环和溢出

1
2
3
4
5
6
7
8
9
10
11
12
public class Main {
public static void main(String[] args) {
int sum = 0;
int n = 1;
while (n > 0) {
sum = sum + n;
n++;
}
System.out.println("n: " + n); // 输出结果
System.out.println("Sum: " + sum);
}
}

在这个示例中,n 从1开始,每次循环时自增。nint 类型,所以当它增加到 Integer.MAX_VALUE(2147483647)时,再增加1就会变成负数,导致循环停止。Integer.MAX_VALUE 是 Java int 类型的最大值,超出后会发生溢出。

do-while 循环

do-while 循环和 while 循环类似,但它至少会执行一次循环体,因为条件是在循环体执行后检查的。

1
2
3
4
5
6
7
8
9
10
11
public class Main {
public static void main(String[] args) {
int sum = 0;
int n = 1;
do {
sum = sum + n;
n++;
} while (n <= 100);
System.out.println("Sum: " + sum);
}
}

for 循环

for 循环适用于已知循环次数的情况。其基本结构是:

1
2
3
for (初始化; 条件表达式; 更新) {
// 循环体
}

for 循环的例子:

1
2
3
4
5
6
7
8
9
public class Main {
public static void main(String[] args) {
int sum = 0;
for (int n = 1; n <= 100; n++) {
sum = sum + n;
}
System.out.println("Sum: " + sum);
}
}
  • while 循环: 循环体在每次循环前判断条件,条件不满足时退出循环。
  • do-while 循环: 循环体在每次循环后判断条件,确保至少执行一次。
  • for 循环: 适合固定次数的循环,初始化、条件判断、更新语句在同一行中指定。

以下是关于for循环一些补充:

1. 灵活使用 for 循环

for 循环在 Java 中提供了很大的灵活性。除了标准的循环形式外,你可以根据需要省略部分或全部的语句。以下是几种特殊的 for 循环写法:

不设置结束条件

1
2
3
4
for (int i = 0; ; i++) {
if (i > 10) break; // 必须提供终止条件,否则会进入无限循环
System.out.println(i);
}

这种形式的 for 循环会一直执行,直到遇到 break 语句或其他终止条件。

不设置结束条件和更新语句

1
2
3
4
5
int i = 0;
for (; i < 10;) {
System.out.println(i);
i++;
}

这种形式省略了循环变量的初始化和更新语句,初始化在循环外部完成,更新在循环体内处理。

什么都不设置

1
2
3
4
5
6
int i = 0;
for (;;) {
if (i > 10) break; // 必须提供终止条件,否则会进入无限循环
System.out.println(i);
i++;
}

这种形式完全省略了所有 for 循环的标准部分,只提供终止条件和循环体。

2. for-each 循环

for-each 循环提供了一种更简洁的方式来遍历数组或集合中的每个元素。这种循环形式的优点是代码更简洁,更易于理解。

遍历数组

1
2
3
4
int[] ns = {1, 4, 9, 16, 25};
for (int n : ns) {
System.out.println(n);
}

for-each 循环中,n 直接代表数组中的每个元素,而不需要手动处理索引。

遍历 List

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import java.util.ArrayList;
import java.util.List;

public class Main {
public static void main(String[] args) {
List<String> fruits = new ArrayList<>();
fruits.add("Apple");
fruits.add("Banana");
fruits.add("Cherry");

for (String fruit : fruits) {
System.out.println(fruit);
}
}
}

for-each 循环也可以用于 List 之类的集合,这使得遍历更加直观和简洁。

3. 其他可迭代的数据类型

除了数组和 Listfor-each 循环还可以遍历 SetMap 等实现了 Iterable 接口的集合类型。

遍历 Set

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import java.util.HashSet;
import java.util.Set;

public class Main {
public static void main(String[] args) {
Set<String> colors = new HashSet<>();
colors.add("Red");
colors.add("Green");
colors.add("Blue");

for (String color : colors) {
System.out.println(color);
}
}
}

遍历 Map 的键值对

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import java.util.HashMap;
import java.util.Map;

public class Main {
public static void main(String[] args) {
Map<String, Integer> ages = new HashMap<>();
ages.put("Alice", 30);
ages.put("Bob", 25);
ages.put("Charlie", 35);

for (Map.Entry<String, Integer> entry : ages.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
}
}

练习题

1. 计算阶乘

编写一个程序,计算给定整数 $ n $ 的阶乘(即 $ n! $)。例如,对于 $ n = 5 $,计算 $ 5! = 5 = 120 $。

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Main {
public static void main(String[] args) {
int n = 5;
int factorial = 1;

// 使用for循环计算阶乘
for (int i = 1; i <= n; i++) {
factorial *= i;
}

System.out.println("Factorial of " + n + " is " + factorial);
}
}

2. 打印九九乘法表

编写一个程序,使用 for 循环打印九九乘法表。

1
2
3
4
5
6
7
8
9
10
public class Main {
public static void main(String[] args) {
for (int i = 1; i <= 9; i++) {
for (int j = 1; j <= i; j++) {
System.out.print(j + " * " + i + " = " + (i * j) + "\t");
}
System.out.println();
}
}
}

3. 查找质数

编写一个程序,查找并打印 1 到 100 之间的所有质数。质数是只能被 1 和它自身整除的数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Main {
public static void main(String[] args) {
for (int num = 2; num <= 100; num++) {
boolean isPrime = true;
for (int i = 2; i <= Math.sqrt(num); i++) {
if (num % i == 0) {
isPrime = false;
break;
}
}
if (isPrime) {
System.out.println(num);
}
}
}
}

4. 打印斐波那契数列

编写一个程序,打印前 10 个斐波那契数。斐波那契数列的定义是:前两个数是 0 和 1,从第三个数开始,每个数都是前两个数之和。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Main {
public static void main(String[] args) {
int n = 10;
int a = 0, b = 1;

System.out.print(a + " " + b + " ");

for (int i = 2; i < n; i++) {
int c = a + b;
System.out.print(c + " ");
a = b;
b = c;
}
}
}

5. 计算数组元素的平均值

编写一个程序,计算并打印一个整数数组的平均值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Main {
public static void main(String[] args) {
int[] numbers = { 10, 20, 30, 40, 50 };
int sum = 0;

// 使用for-each循环计算数组元素的总和
for (int number : numbers) {
sum += number;
}

double average = (double) sum / numbers.length;
System.out.println("Average: " + average);
}
}

6. 反转数组

编写一个程序,反转一个整数数组的元素并打印出来。

1
2
3
4
5
6
7
8
9
10
11
public class Main {
public static void main(String[] args) {
int[] array = { 1, 2, 3, 4, 5 };
int n = array.length;

System.out.print("Reversed array: ");
for (int i = n - 1; i >= 0; i--) {
System.out.print(array[i] + " ");
}
}
}

break和continue

break 语句

  • 功能: break 用于立即终止当前循环,无论是 for 还是 while 循环。
  • 使用场景: 常配合 if 语句使用,以在满足某个条件时跳出循环。
  • 影响范围: 只跳出当前所在的循环层级。例如,在嵌套循环中,只跳出内层循环,外层循环继续执行。

示例:

1
2
3
4
5
6
for (int i = 1; ; i++) {
if (i > 5) {
break; // 当 i > 5 时,退出循环
}
System.out.println(i);
}

continue 语句

  • 功能: continue 用于结束当前循环的当前迭代,直接跳到下一次循环迭代的开始。
  • 使用场景: 常配合 if 语句使用,以在满足某个条件时跳过本次循环的剩余部分,直接进行下一次循环。
  • 影响范围: 仅影响当前循环层级的迭代,继续执行当前循环的下一次迭代。

示例:

1
2
3
4
5
6
for (int i = 1; i <= 10; i++) {
if (i % 2 == 0) {
continue; // 当 i 为偶数时,跳过本次循环,直接进入下一次循环
}
System.out.println(i); // 只打印奇数
}
  • break: 用于退出整个循环。
  • continue: 用于跳过当前循环的剩余部分,直接进行下一次迭代。
  • reak: 用于退出整个循环。
  • continue: 用于跳过当前循环的剩余部分,直接进行下一次迭代。

数组遍历和打印

1. 使用标准 for 循环遍历数组

标准的 for 循环可以用来遍历数组,通过索引来访问每个元素:

1
2
3
4
5
6
7
8
9
public class Main {
public static void main(String[] args) {
int[] ns = { 1, 4, 9, 16, 25 };
for (int i = 0; i < ns.length; i++) {
int n = ns[i]; // 通过索引访问数组元素
System.out.println(n);
}
}
}

解释: - 初始化: int i = 0,索引从0开始。 - 条件: i < ns.length,确保索引不超出数组范围。 - 更新: i++,每次循环后索引增加1。

2. 使用 for-each 循环遍历数组

for-each 循环提供了更简洁的语法来遍历数组,无需显式地使用索引:

1
2
3
4
5
6
7
8
public class Main {
public static void main(String[] args) {
int[] ns = { 1, 4, 9, 16, 25 };
for (int n : ns) {
System.out.println(n); // 直接获取数组元素的值
}
}
}

解释: - for (int n : ns)n 直接表示数组 ns 中的每一个元素。 - 适用于只需访问元素而不需要索引的情况。

注意: for-each 循环不能获取当前元素的索引,如果需要索引信息,仍需使用标准 for 循环。

3. 打印数组内容

直接打印数组变量会得到数组在 JVM 中的引用地址,如下:

1
2
int[] ns = { 1, 1, 2, 3, 5, 8 };
System.out.println(ns); // 输出类似 [I@7852e922

为了打印数组的实际内容,可以使用以下两种方法:

使用 for-each 循环:

1
2
3
4
int[] ns = { 1, 1, 2, 3, 5, 8 };
for (int n : ns) {
System.out.print(n + ", "); // 打印元素并添加分隔符
}

使用 Arrays.toString() 方法:

Arrays.toString() 方法可以快速打印数组内容,生成数组元素的字符串表示:

1
2
3
4
5
6
7
8
import java.util.Arrays;

public class Main {
public static void main(String[] args) {
int[] ns = { 1, 1, 2, 3, 5, 8 };
System.out.println(Arrays.toString(ns)); // [1, 1, 2, 3, 5, 8]
}
}

解释: - Arrays.toString(ns) 返回数组 ns 的字符串表示,其中包含数组的所有元素,格式为 [元素1, 元素2, ..., 元素n]

Java 排序算法

1. 冒泡排序 (Bubble Sort)

基本概念:

  • 比较相邻的元素,如果顺序错误就交换它们。
  • 每一趟遍历后,将当前最大值放到数组末尾。

时间复杂度: 最坏和平均情况 \(O(n^2)\),最好情况 \(O(n)\)(当数组已排序时)。

实现代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class BubbleSort {
public static void bubbleSort(int[] arr) {
int n = arr.length;
for (int i = 0; i < n - 1; i++) {
for (int j = 0; j < n - 1 - i; j++) {
if (arr[j] > arr[j + 1]) {
// 交换 arr[j] 和 arr[j + 1]
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
}

2. 选择排序 (Selection Sort)

基本概念: - 每一趟找到剩余部分的最小元素,放到当前未排序部分的开始位置。 - 逐步减少待排序部分的长度。

时间复杂度: \(O(n^2)\)(无论最好、最坏还是平均情况)。

实现代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class SelectionSort {
public static void selectionSort(int[] arr) {
int n = arr.length;
for (int i = 0; i < n - 1; i++) {
int minIndex = i;
for (int j = i + 1; j < n; j++) {
if (arr[j] < arr[minIndex]) {
minIndex = j;
}
}
// 交换 arr[i] 和 arr[minIndex]
int temp = arr[i];
arr[i] = arr[minIndex];
arr[minIndex] = temp;
}
}
}

3. 插入排序 (Insertion Sort)

基本概念: - 将每个元素插入到已排序部分的正确位置。 - 类似于手动排序扑克牌。

时间复杂度: 最坏和平均情况 \(O(n^2)\),最好情况 \(O(n)\)(当数组已排序时)。

实现代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class InsertionSort {
public static void insertionSort(int[] arr) {
int n = arr.length;
for (int i = 1; i < n; i++) {
int key = arr[i];
int j = i - 1;
// 将大于 key 的元素移到后面
while (j >= 0 && arr[j] > key) {
arr[j + 1] = arr[j];
j--;
}
arr[j + 1] = key;
}
}
}

4. 快速排序 (Quick Sort)

基本概念: - 选择一个基准元素,将数组分成两个子数组,一部分比基准小,一部分比基准大。 - 递归排序子数组。

时间复杂度: 最坏情况 \(O(n^2)\),平均情况 \(O(n \log n)\)

实现代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class QuickSort {
public static void quickSort(int[] arr, int low, int high) {
if (low < high) {
int pi = partition(arr, low, high);
quickSort(arr, low, pi - 1); // 排序基准左边的子数组
quickSort(arr, pi + 1, high); // 排序基准右边的子数组
}
}

private static int partition(int[] arr, int low, int high) {
int pivot = arr[high];
int i = (low - 1); // 最小元素的索引
for (int j = low; j < high; j++) {
if (arr[j] <= pivot) {
i++;
// 交换 arr[i] 和 arr[j]
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
// 交换 arr[i + 1] 和 arr[high] (或 pivot)
int temp = arr[i + 1];
arr[i + 1] = arr[high];
arr[high] = temp;
return i + 1;
}
}

5. 归并排序 (Merge Sort)

基本概念: - 将数组分成两个子数组,递归排序两个子数组,然后将它们合并成一个有序数组。

时间复杂度: \(O(n \log n)\)(无论最好、最坏还是平均情况)。

实现代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public class MergeSort {
public static void mergeSort(int[] arr, int l, int r) {
if (l < r) {
int m = l + (r - l) / 2;
mergeSort(arr, l, m); // 排序左半部分
mergeSort(arr, m + 1, r); // 排序右半部分
merge(arr, l, m, r); // 合并两部分
}
}

private static void merge(int[] arr, int l, int m, int r) {
int n1 = m - l + 1;
int n2 = r - m;

int[] L = new int[n1];
int[] R = new int[n2];

System.arraycopy(arr, l, L, 0, n1);
System.arraycopy(arr, m + 1, R, 0, n2);

int i = 0, j = 0;
int k = l;
while (i < n1 && j < n2) {
if (L[i] <= R[j]) {
arr[k++] = L[i++];
} else {
arr[k++] = R[j++];
}
}
while (i < n1) {
arr[k++] = L[i++];
}
while (j < n2) {
arr[k++] = R[j++];
}
}
}

6. 堆排序 (Heap Sort)

基本概念: - 将数组视为一个最大堆,逐步将最大元素(根节点)移到数组末尾,调整堆。

时间复杂度: \(O(n \log n)\)(无论最好、最坏还是平均情况)。

实现代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public class HeapSort {
public static void heapSort(int[] arr) {
int n = arr.length;
// 建立最大堆
for (int i = n / 2 - 1; i >= 0; i--) {
heapify(arr, n, i);
}
// 提取元素
for (int i = n - 1; i > 0; i--) {
// 将当前根节点移动到数组末尾
int temp = arr[0];
arr[0] = arr[i];
arr[i] = temp;
// 调整堆
heapify(arr, i, 0);
}
}

private static void heapify(int[] arr, int n, int i) {
int largest = i;
int l = 2 * i + 1;
int r = 2 * i + 2;
if (l < n && arr[l] > arr[largest]) {
largest = l;
}
if (r < n && arr[r] > arr[largest]) {
largest = r;
}
if (largest != i) {
int swap = arr[i];
arr[i] = arr[largest];
arr[largest] = swap;
heapify(arr, n, largest);
}
}
}
  • 冒泡排序: 简单但效率低,适合小规模数据。
  • 选择排序: 简单实现,性能和冒泡排序相似。
  • 插入排序: 适用于小规模数据或几乎已排序的数据。
  • 快速排序: 高效且广泛使用,但最坏情况下性能差。
  • 归并排序: 稳定且高效,适合大规模数据。
  • 堆排序: 高效且不需要额外的空间,适合大规模数据。

Java 多维数组

1. 二维数组

基本概念: - 二维数组是数组的数组。可以将其视为一个矩阵,其中每个元素都是一维数组。 - 二维数组在内存中其实是一个包含多个一维数组的数组。

定义和初始化:

1
2
3
4
5
int[][] ns = {
{ 1, 2, 3, 4 },
{ 5, 6, 7, 8 },
{ 9, 10, 11, 12 }
};

访问方式: - 访问二维数组中的元素需要使用两个索引,array[row][col]。 - 例如,ns[1][2] 访问的是第二行第三列的元素 7

不规则二维数组: - 每一行的长度可以不同,形成不规则的矩阵。

1
2
3
4
5
int[][] ns = {
{ 1, 2, 3, 4 },
{ 5, 6 },
{ 7, 8, 9 }
};

打印二维数组: - 可以使用两层嵌套的 for 循环遍历:

1
2
3
4
5
6
for (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
    12
    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));
    }
    }

2. 三维数组

基本概念: - 三维数组是二维数组的数组。可以将其视为多个二维矩阵的集合。 - 在内存中,三维数组表现为一个包含多个二维数组的数组。

定义和初始化:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int[][][] ns = {
{
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
},
{
{10, 11},
{12, 13}
},
{
{14, 15, 16},
{17, 18}
}
};

访问方式: - 访问三维数组中的元素需要使用三个索引,array[x][y][z]。 - 例如,ns[2][0][1] 访问的是第三组(索引为2)、第一行(索引为0)、第二列(索引为1)的元素 15

打印三维数组: - 可以使用三层嵌套的 for 循环遍历:

1
2
3
4
5
6
7
8
9
for (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
2
3
4
5
6
7
public class Main {
public static void main(String[] args) {
for (String arg : args) {
System.out.println(arg);
}
}
}

如果你编译并运行这个程序,并传递一些命令行参数,例如:

1
2
$ javac Main.java
$ java Main Hello World 123

输出:

1
2
3
Hello
World
123

2. 处理特定参数

可以根据不同的命令行参数执行不同的代码。例如,如果你希望根据 -version 参数打印程序版本号,可以这样做:

1
2
3
4
5
6
7
8
9
10
public class Main {
public static void main(String[] args) {
for (String arg : args) {
if ("-version".equals(arg)) {
System.out.println("v 1.0");
break;
}
}
}
}

解释:

  • args 是一个 String 数组,其中包含传递给程序的所有参数。
  • 使用 for 循环遍历每个参数。
  • 使用 if 语句检查是否存在 -version 参数。
  • 如果发现 -version,程序打印版本号并通过 break 语句退出循环。

3. 编译和运行程序

要使用命令行参数执行 Java 程序,必须在命令行中进行编译和运行:

  1. 编译:

    1
    $ javac Main.java
    这会生成一个 Main.class 文件。

  2. 运行:

    1
    $ java Main -version
    这会传递 -version 作为命令行参数给程序。

4. 命令行参数的应用场景

  • 配置: 可以通过命令行参数传递配置信息,例如数据库连接字符串或文件路径。
  • 功能切换: 根据不同的参数决定程序的行为,如开启调试模式或打印详细日志。
  • 版本控制: 实现 -version 参数来显示程序版本信息。

5. 命令行参数的特点

  • 字符串类型: 所有命令行参数都是 String 类型,即使你输入数字,也会被当作字符串处理。
  • 顺序: 参数的顺序在 args 数组中是保留的,你可以根据需要处理这些参数。
  • 没有强类型检查: 需要在代码中进行适当的解析和验证,确保参数的正确性。