Java基本程序设计结构

1. 数据类型

1.1 整型

一个字节(byte)是计算机存储和处理数据的最小单位。在计算机科学中,一个字节由8个二进制位(bit)组成。

类型存储需求取值范围
byte(字节)1字节-128~127
short(短整数)2字节-32768~32767
Int(整数)4字节-2147483648~2147483647(略高于20亿)
Long(长整数)8字节9223372036854775808~9223372036854775807

1.2 浮点类型

3.4E38表示的是科学计数法中的浮点数,它表示的是一个非常大的数值。

具体来说,3.4E38可以理解为3.4乘以10的38次方,即3.4乘以1后面跟着38个零。

类型存储需求取值范围
float(单精度)4字节-3.4E38 ~ 3.4E38(6~7位有效数字)
double(双精度)8字节-1.7E308 ~ 1.7E308(15位有效数字)

注意

  • 浮点数值不适用与无法接受舍入误差的金融计算,原因是浮点数采用二进制表示导致舍入误差。

1.3 char类型

在Java中,char是一种基本数据类型,可以使用单引号 ' ' 来表示char类型的字符常量,用于表示Unicode字符。char类型是16位的,范围是从\u0000(即十进制的0)到\uffff(即十进制的65535)。它可以用来表示所有的Unicode字符,包括字母、数字、标点符号、特殊字符等。

Unicode转义序列是一种表示Unicode字符的特殊语法。它使用\u后跟四个十六进制数字来表示一个Unicode字符的编码。

Unicode转义序列的语法如下: \uXXXX

其中,XXXX是四个十六进制数字,表示Unicode字符的编码值。每个十六进制数字可以是0-9、A-F或a-f之间的一个字符。

以下是一些示例:

  • \u0041 表示字符 'A'
  • \u00A9 表示版权符号 '©'
  • \u20AC 表示欧元符号 '€'
  • \u4E2D 表示汉字 '中'

Unicode转义序列可以方便地表示各种Unicode字符,无论是ASCII字符还是非ASCII字符。它在处理特殊字符和跨平台文本表示时非常有用。

unicode编码机制 Unicode是一种字符编码标准,用于表示世界上几乎所有的字符和符号。它为每个字符分配了一个唯一的数字编码,称为Unicode码点。

Unicode编码采用了固定长度的编码方式,其中最常用的是UTF-8、UTF-16和UTF-32。

  1. UTF-8(8-bit Unicode Transformation Format)是一种可变长度的编码方式,使用8位(1字节)来表示ASCII字符,而使用2至4字节来表示其他Unicode字符。它是目前互联网上最常用的字符编码方式,因为它具有兼容ASCII字符的特性,且节省空间。
  2. UTF-16(16-bit Unicode Transformation Format)是一种固定长度的编码方式,使用16位(2字节)来表示大部分常用字符,但对于辅助字符(Supplementary Characters)需要使用4字节来表示。UTF-16可以以大端序(Big-Endian)或小端序(Little-Endian)方式存储。
  3. UTF-32(32-bit Unicode Transformation Format)是一种固定长度的编码方式,使用32位(4字节)来表示所有Unicode字符。每个字符都使用相同长度的编码,不需要对辅助字符进行特殊处理。UTF-32在存储和处理上可能会占用更多的空间。

特殊字符的转义序列:

转义序列名称Unicode值
\b退格\u0008
\t制表\u0009
\n换行\u000a
\r回车\u000d
\f换页\u000c
\"双引号\u0022
\'单引号\u0027
\\反斜线\u005c
\s空格。在文本块中用来保留末尾空白符\u0020
\newline只在文本块中使用:连接这一行和下一行

1.4 boolean类型

boolean(布尔)类型有两个值:false和true,用来判定逻辑条件。

2. 变量与常量

2.1 声明变量

Java中每个变量都有一个类型,声明变量时,先指定变量类型,然后变量名,变量也叫标识符

String name;
double salary;
int age;

关于变量名的命名规则:

  • 由字母、数字、货币符号、标点连接符组成,第一个字符不能是数字
  • "+" 、"@" 之类的符号不能出现在变量名中,空格也不行,字母区分大小写,长度无限制
  • 标点连接符包括 下划线、波浪线,实际使用下划线_,在java9中 单下划线是保留字

2.2 初始化变量

声明变量后,必须用赋值语句显式的初始化变量,需要将变量名放在等号(=)左侧,再把一个适当的值放在等号右侧

int salary = 40000; 

java 10开始,对于局部变量,如果可以从变量的初始值推断类型,就不再需要声明类型,只需使用关键字var,无须指定类型

var salary = 40000;
var name = "Bob";   //这个特性可以让对象声明更为简洁

2.3 常量

java中,使用final关键字指示常量,声明常量表示这个变量只能被赋值一次,不能更改,习惯上常量名使用全大写

final String URL = "https://www.xzydot.com";

在java中,经常需要创建一个常量便于在一个类中的多个方法中使用,可声明类常量。使用关键字static final设置类常量

public class Constants{
    public static final URL = "https://www.xzydot.com";
    
    public static void main(String[] args){
        System.out.pringtln(URL)
    }
}

类常量定义于方法外,同一个类的其它方法都可以使用这个常量,如果一个常量被声明为public,其它类的方法也可以使用 Constants.URL

2.4 枚举

有时候需要一个变量只包含有限的一组值 ,举个例子,假设我们要表示一个学生的班级,班级编号只能是1、2、3或4中的一个。在这种情况下,我们可以将班级编号定义为一个只包含有限值的变量,其取值范围被限定为1、2、3和4。任何其他值都是无效的。

这种限制有助于确保变量的取值始终是可控的,并且不会出现意外或非法的取值

它还提供了更好的代码可读性和可维护性,因为开发人员可以清晰地知道变量可以取的值是哪些。

在 Java 中,你可以通过使用 enum 关键字来定义一个枚举类型。以下是一个示例:

enum DayOfWeek {
    MONDAY,
    TUESDAY,
    WEDNESDAY,
    THURSDAY,
    FRIDAY,
    SATURDAY,
    SUNDAY
}

要使用枚举变量,你可以声明一个变量并将其赋值为枚举常量之一。例如:

DayOfWeek today = DayOfWeek.MONDAY;

2.5 运算符

运算符用于连接值,,java提供了一组丰富的算术和逻辑运算符以及数学函数。

2.6 算术运算符

在java中,使用通常的算术运算符 =、-、*、/ 分别表示加、减、乘、除

运算的两个操作数都是整数时,/表示整数除法 例如 15/2 等于 7;否则表示浮点除法 例如15.0/2 等于7.5

整数的求余操作(有时称为取模(modules))用%表示。15%2 等于1

需要注意,整数被0除将产生异常,而浮点数被0除将得到一个无穷大或NaN结果

2.7 数学函数与常量

Math类中包含你可能用到的各种数学函数。

要想计算一个数的平方根,可以使用sqrt方法

double x = 4
double y = Math.sqrt(x);
// y = 2.0

java没有完成幂运算的运算符,必须使用Math类的pow方法;

Math类提供了一些常用的三角函数:

  • Math.sin
  • Math.cos
  • Math.tan
  • Math.atan
  • Math.atan2

指数函数以及它的反函数——自然对数和以10为底的对数:

  • Math.exp
  • Math.log
  • Math.log10

最后,还提供两个常量表示π 和e常量最接近的近似值

  • Math.PI
  • Math.E

注意,如果一个计算溢出,数学运算符只是悄悄返回错误的结果而不做任何提醒。Math类提供了一些方法使整数运算更安全,Math.multyplyExact(1000000000,3), 就会产一个异常,可以捕获异常或者让程序终止,而不是给出错误结果继续运行。

2.8 数值类型之间的转换

在 Java 中,可以使用类型转换操作符来进行数值类型之间的转换。以下是常见的数值类型之间的转换方式:

  1. 自动类型转换(隐式类型转换):
    • 小类型向大类型的转换是自动进行的,不需要显式的类型转换。例如,将一个整数值赋给一个长整型变量,或将一个浮点数值赋给一个双精度浮点型变量。
  2. 强制类型转换(显式类型转换):
    • 大类型向小类型的转换需要使用强制类型转换操作符(类型)。这种转换可能会导致数据丢失或精度损失。需要注意的是,当进行大类型向小类型的转换时,可能会发生溢出或截断。
    • 例如,将一个长整型值转换为整型或将一个双精度浮点型值转换为单精度浮点型。

下面是一些示例代码来说明数值类型之间的转换:

int a = 10;
long b = a; // 自动类型转换,int 转为 long

double x = 3.14;
float y = (float) x; // 强制类型转换,double 转为 float

long c = 1000;
int d = (int) c; // 强制类型转换,long 转为 int,可能会发生溢出或截断

需要注意的是,在进行类型转换时要注意数据的范围和精度,以避免数据丢失或不准确的结果。

2.9 赋值

可以在赋值中使用二元运算符,有一种很方便的简写形式

x += 4;
//等价于 x = x + 4

2.10 自增与自减运算符

在 Java 中,自增运算符(++)和自减运算符(--)用于对变量进行增加或减少操作。

自增运算符(++)有两种形式:

  1. 前缀形式:++x
    • 先对变量 x 的值进行加一操作,然后返回增加后的值。
    • 例如,++x 将变量 x 的值增加 1,然后返回增加后的值。
  2. 后缀形式:x++
    • 先返回变量 x 的值,然后再对变量 x 的值进行加一操作。
    • 例如,x++ 先返回变量 x 的值,然后将变量 x 的值增加 1。

自减运算符(--)与自增运算符类似,也有前缀形式和后缀形式。使用方式与自增运算符类似,只是操作是减一而不是加一。

以下是一些示例代码来说明自增和自减运算符的使用:

int x = 5;

System.out.println(++x); // 输出:6,前缀形式,先增加再返回
System.out.println(x++); // 输出:6,后缀形式,先返回再增加
System.out.println(x);   // 输出:7,后缀形式增加后的值

int y = 10;

System.out.println(--y); // 输出:9,前缀形式,先减少再返回
System.out.println(y--); // 输出:9,后缀形式,先返回再减少
System.out.println(y);   // 输出:8,后缀形式减少后的值

需要注意的是,自增和自减运算符可以应用于整数类型和浮点数类型的变量。在表达式中的使用方式也可能会影响运算结果。

2.11 关系运算符和boolean运算符

在 Java 中,关系运算符用于比较两个值之间的关系,并返回一个布尔值(true或false)。布尔运算符用于对布尔值进行逻辑运算。

以下是常见的关系运算符和布尔运算符:

关系运算符:

  • 相等:==(等于),!=(不等于)
  • 比较:>(大于),<(小于),>=(大于等于),<=(小于等于)

布尔运算符:

  • 逻辑与:&&(与)
  • 逻辑或:||(或)
  • 逻辑非:!(非)

关系运算符和布尔运算符可以用于基本数据类型(如整数、浮点数、字符)之间的比较,以及用于对象之间的比较(通过调用对象的equals方法进行比较)。

以下是一些示例代码来说明关系运算符和布尔运算符的使用:

int a = 5;
int b = 10;

boolean isEqual = (a == b); // 判断 a 是否等于 b
boolean isGreater = (a > b); // 判断 a 是否大于 b
boolean isLessOrEqual = (a <= b); // 判断 a 是否小于等于 b

boolean logicalAnd = (a > 0 && b < 20); // 判断 a 大于 0 并且 b 小于 20
boolean logicalOr = (a > 0 || b < 20); // 判断 a 大于 0 或者 b 小于 20
boolean logicalNot = !(a == b); // 判断 a 不等于 b

System.out.println(isEqual); // 输出:false
System.out.println(isGreater); // 输出:false
System.out.println(isLessOrEqual); // 输出:true
System.out.println(logicalAnd); // 输出:true
System.out.println(logicalOr); // 输出:true
System.out.println(logicalNot); // 输出:true

需要注意的是,布尔运算符具有短路特性。在使用逻辑与(&&)和逻辑或(||)时,如果第一个条件已经能够确定整个表达式的结果,那么第二个条件将不会被计算。

2.12 条件运算符

Java 中,条件运算符(也称为三元运算符)用于根据条件的真假来选择不同的值。条件运算符的语法如下:

条件 ? 表达式1 : 表达式2

其中,条件是一个布尔表达式,如果条件为真,则返回表达式1的值;如果条件为假,则返回表达式2的值。

以下是一个示例代码来说明条件运算符的使用:

int x = 5;
int y = 10;

int max = (x > y) ? x : y; // 如果 x 大于 y,则返回 x 的值,否则返回 y 的值

System.out.println(max); // 输出:10,因为 y 的值大于 x 的值

在上面的示例中,如果 x 大于 y,则将 x 的值赋给变量 max;否则将 y 的值赋给变量 max。最后,将 max 的值打印出来。

条件运算符在简单的条件判断和赋值操作中非常有用,可以简化代码并提高可读性。

需要注意的是,条件运算符可以嵌套使用,以实现更复杂的条件判断。

2.13 switch表达式-Java 12

Java 12 引入了一种新的语法特性,即 switch 表达式。在之前的 Java 版本中,switch 语句只能用于执行多个分支的条件判断,而 switch 表达式使得 switch 更加灵活和简洁。

以下是 switch 表达式的基本语法:

int result = switch (expression) {
    case value1 -> expression1;
    case value2 -> expression2;
    case value3, value4 -> expression3;
    default -> expression4;
};

在上面的语法中,expression 是一个表达式,value1、value2、value3 等是具体的值,expression1、expression2、expression3 等是表达式,可以是一个值、一个方法调用、一个表达式等。default 分支是可选的,用于处理除了指定的值之外的情况。

下面是一个示例代码来说明 switch 表达式的使用:

int day = 1;
String dayOfWeek = switch (day) {
    case 1 -> "Monday";
    case 2 -> "Tuesday";
    case 3, 4, 5 -> "Wednesday";
    default -> "Other";
};

System.out.println(dayOfWeek); // 输出:Monday

在上面的示例中,根据变量 day 的值,使用 switch 表达式选择不同的星期几的字符串值赋给变量 dayOfWeek。

switch 表达式还支持多种新的语法特性,如使用箭头表达式进行值的转换、使用 yield 关键字返回值等。这些特性可以使 switch 表达式更加强大和灵活。

需要注意的是,switch 表达式在 Java 12 中是作为一项实验性特性引入的,需要在编译时使用 --enable-preview 选项来启用它。在更高版本的 Java 中,switch 表达式已成为正式的语法特性。

在 Java 中,可以使用枚举常量作为 switch 表达式的条件。枚举常量在 switch 表达式中可以直接使用,无需使用枚举类型的名称。

以下是使用枚举常量的 switch 表达式的示例代码:

enum Day {
    MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
}

Day day = Day.MONDAY;

String dayOfWeek = switch (day) {
    case MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY -> "Weekday";
    case SATURDAY, SUNDAY -> "Weekend";
};

System.out.println(dayOfWeek); // 输出:Weekday

在上面的示例中,使用枚举类型 Day 和其常量作为 switch 表达式的条件。根据不同的枚举常量,执行相应的表达式。

需要注意的是,枚举常量在 switch 表达式中不需要使用枚举类型的名称作为前缀,直接使用常量即可。

2.14 位运算符

在 Java 中,位运算符用于对整数类型的数据执行位级操作。位运算符操作的是二进制表示的数值的各个位,对每个位进行逻辑运算或移位操作。

下面是 Java 中常用的位运算符:

  1. 位与(&):对两个操作数的每个位执行与操作,只有两个位都为 1 时结果为 1,否则为 0。
  2. 位或(|):对两个操作数的每个位执行或操作,只要两个位中有一个为 1 时结果为 1,否则为 0。
  3. 位异或(^):对两个操作数的每个位执行异或操作,如果两个位不相同则结果为 1,否则为 0。
  4. 取反(~):对操作数的每个位执行取反操作,将 0 变为 1,将 1 变为 0。
  5. 左移(<<):将操作数的所有位向左移动指定的位数,右侧用 0 填充。
  6. 右移(>>):将操作数的所有位向右移动指定的位数,左侧用符号位(正数为 0,负数为 1)填充。
  7. 无符号右移(>>>):将操作数的所有位向右移动指定的位数,左侧用 0 填充。

下面是一个示例代码来说明位运算符的使用:

int a = 5; // 二进制表示为 00000101
int b = 3; // 二进制表示为 00000011

   00001100
 & 00001010
------------
   00001000

int result1 = a & b; // 位与运算:00000001,结果为 1
int result2 = a | b; // 位或运算:00000111,结果为 7
int result3 = a ^ b; // 位异或运算:00000110,结果为 6
int result4 = ~a;    // 取反运算:11111010,结果为 -6
int result5 = a << 2; // 左移运算:00010100,结果为 20
int result6 = a >> 1; // 右移运算:00000010,结果为 2
int result7 = a >>> 1; // 无符号右移运算:00000010,结果为 2

3. 字符串

在Java中,字符串是一个表示文本数据的对象。在Java中,字符串类被称为java.lang.String,它提供了许多用于操作字符串的方法。

3.1 基本使用

字符串截取:String类的subString方法可以从一个较大的字符串提取出一个子串

String name = "hello";
String s = name.substring(0,3); //输出hel

字符串创建:可以使用字符串字面值直接创建字符串对象,例如:

javaCopy codeString str1 = "Hello, World!"; // 使用字符串字面值创建字符串对象
String str2 = new String("Hello, World!"); // 使用构造函数创建字符串对象

字符串拼接:使用加号运算符(+)可以将多个字符串拼接在一起,例如:

javaCopy codeString name = "Alice";
String greeting = "Hello, " + name + "!"; // 拼接字符串

字符串长度:可以使用length()方法获取字符串的长度,例如:

javaCopy codeString str = "Hello";
int length = str.length(); // 获取字符串长度

字符串索引:可以使用索引访问字符串中的单个字符,索引从0开始,例如:

javaCopy codeString str = "Hello";
char firstChar = str.charAt(0); // 获取第一个字符 'H'

字符串比较:可以使用equals()方法或compareTo()方法进行字符串的比较,例如:

javaCopy codeString str1 = "Hello";
String str2 = "World";
boolean isEqual = str1.equals(str2); // 判断字符串是否相等
int compareResult = str1.compareTo(str2); // 比较字符串的大小

字符串切割:可以使用split()方法将字符串按照指定的分隔符切割成字符串数组,例如:

javaCopy codeString str = "apple,banana,orange";
String[] fruits = str.split(","); // 按逗号切割字符串

字符串替换:可以使用replace()方法将字符串中的指定字符或子字符串替换为新的字符或字符串,例如:

javaCopy codeString str = "Hello, World!";
String newStr = str.replace("Hello", "Hi"); // 将"Hello"替换为"Hi"

3.2 字符串不可变

Java中的字符串是不可变的,也称为"immutable"。这意味着一旦创建了一个字符串对象,它的值就无法被修改。

当你对字符串执行一些操作时(如拼接、替换、转换大小写等),实际上是创建了一个新的字符串对象,而原始字符串对象保持不变。这是因为Java中的字符串是通过java.lang.String类表示的,该类被设计为不可变的。

不可变字符串优点:编译器可以让字符串共享

可以想象各个字符串放在公共存储池中,字符串字面量指向存储池中相应的位置。如果复制一个字符串变量,原始字符串和复制的字符串共享公共存储池中的相同字符

3.3 检测字符串是否相等

在Java中,比较字符串时应该使用.equals()方法而不是==运算符。这是因为在Java中,==运算符用于比较对象的引用是否相同,而不是比较对象的内容。

在字符串的情况下,==运算符只会在两个字符串引用指向同一个对象时返回true,即它们是同一个对象。而使用.equals()方法可以比较两个字符串对象的内容是否相等。

例如,考虑以下示例:

String str1 = "Hello";
String str2 = "Hello";
String str3 = new String("Hello");

System.out.println(str1 == str2); // true,引用相同
System.out.println(str1 == str3); // false,引用不同
System.out.println(str1.equals(str2)); // true,内容相等
System.out.println(str1.equals(str3)); // true,内容相等

3.4 空串与null串

在Java中,空串(empty string)和null串是不同的概念。

空串是指一个长度为0的字符串,即不包含任何字符的字符串。可以通过""String str = new String()来表示空串。例如:

String emptyString = ""; // 空串

而null串表示一个空引用,即变量没有引用任何对象。可以将null赋值给字符串变量,表示该变量不引用任何字符串对象。例如:

String nullString = null; // null串

在对空串和null串进行操作时需要注意以下几点:

  1. 比较:对于空串,可以使用.equals()方法进行比较,例如 "".equals(emptyString);而对于null串,需要先进行空引用检查,再使用.equals()方法,例如 nullString != null && nullString.equals("some string")
  2. 方法调用:对于空串,可以直接调用字符串的方法,例如 emptyString.length();而对于null串,调用任何方法都会抛出NullPointerException异常,需要进行空引用检查。
  3. 初始化:对于字符串变量,如果需要将其初始化为空串,可以使用"",例如 String str = "";;如果需要将其初始化为null串,可以直接赋值为null,例如 String str = null;

3.5 码点与代码单元

在Java中,字符串是由Unicode字符序列组成的。Unicode字符集定义了每个字符对应的唯一码点(code point),而Java中的字符串则是由一个或多个代码单元(code unit)组成的。

代码单元是指在Java中用于表示字符的基本单位,它采用UTF-16编码方案,每个代码单元占用16位(2个字节)的存储空间。大多数常用字符(如英文字母、数字、标点符号等)的码点可以由一个代码单元表示,但一些特殊字符(如表情符号、某些非拉丁字符等)的码点需要由多个代码单元表示。

通过Java的String类提供的方法,我们可以在字符串和代码单元之间进行转换。例如:

  • length()方法返回字符串的代码单元数量(长度)。
  • charAt(int index)方法返回指定索引处的代码单元。
  • codePointAt(int index)方法返回指定索引处的码点。
  • codePointCount(int beginIndex, int endIndex)方法返回指定范围内的码点数量。

需要注意的是,由于某些字符的码点需要多个代码单元表示,因此在处理字符串时应该注意处理码点而不是代码单元。如果需要对字符串进行遍历、截取、比较等操作,应考虑使用针对码点的方法,而不是仅仅依赖于代码单元。

以下是一个示例,展示了如何在Java中操作码点和代码单元:

String str = "Hello, 世界!";
int codeUnitCount = str.length(); // 代码单元数量
int codePointCount = str.codePointCount(0, str.length()); // 码点数量

System.out.println("代码单元数量:" + codeUnitCount); // 输出:13
System.out.println("码点数量:" + codePointCount); // 输出:9

// 遍历字符串的码点
for (int i = 0; i < str.length(); ) {
    int codePoint = str.codePointAt(i);
    System.out.println("码点:" + codePoint);
    
    // 根据码点获取字符
    String character = new String(Character.toChars(codePoint));
    System.out.println("字符:" + character);
    
    // 更新索引,跳过码点占用的代码单元
    i += Character.charCount(codePoint);
}

在上述示例中,我们首先获取了字符串的代码单元数量和码点数量。然后使用循环遍历字符串的码点,逐个输出码点和对应的字符。通过Character.toChars()方法将码点转换为字符。

需要注意的是,如果在字符串操作中需要处理码点,建议使用java.text包中的java.text.Normalizer类和相关方法,以处理特定的字符规范化和规范化等操作。

3.6 构建字符串

StringBuilderStringBuffer类适用于需要频繁拼接字符串的情况,因为它们具有可变性能,可以高效地进行字符串操作。StringJoiner类适用于将多个字符串拼接成一个字符串的场景。使用数组转换为字符串则适用于将数组中的元素拼接成一个字符串的情况。

需要注意的是,由于Java中的字符串是不可变的,每次对字符串进行修改都会创建一个新的字符串对象。因此,在需要频繁修改字符串内容时,建议使用可变的字符串类(如StringBuilderStringBuffer)以提高性能。

StringBuilder sb = new StringBuilder();
sb.append("Hello");
sb.append(", ");
sb.append("World!");
String str = sb.toString();

3.7 文本块

Java 文本块是 Java 13 中引入的新特性,用于简化多行字符串的书写。它可以更方便地编写包含换行符和缩进的文本,而无需使用转义字符或连接符号。以下是一个示例:

String message = """
    Hello,
    Welcome to Java!
""";

在上面的示例中,使用三个双引号(""")包围文本块,文本块可以跨越多行,并保留原始的缩进格式。这意味着你可以在文本块中自由地添加换行符和空格,而不需要使用额外的转义字符或连接操作符。

文本块还支持一些特性,如插入表达式和转义序列。你可以在文本块中使用${expression}来插入表达式的值,并使用\来表示转义序列。

String name = "John";
int age = 25;

String message = """
    Hello, ${name}!
    You are ${age} years old.
""";

文本块的引入使得多行字符串的编写更加清晰和易读,特别适用于需要保留文本格式的场景,如生成 HTML、JSON 或 SQL 语句等。

4. 输入与输出

4.1 读取输入

在Java中,你可以使用Scanner类来读取用户的输入。Scanner类提供了一系列方法来读取不同类型的输入,如整数、浮点数、字符串等。下面是一个示例代码:

import java.util.Scanner;

public class InputExample {
    public static void main(String[] args) {
        // 创建 Scanner 对象
        Scanner scanner = new Scanner(System.in);

        // 读取整数
        System.out.print("请输入一个整数: ");
        int num = scanner.nextInt();
        System.out.println("你输入的整数是: " + num);

        // 读取浮点数
        System.out.print("请输入一个浮点数: ");
        double decimal = scanner.nextDouble();
        System.out.println("你输入的浮点数是: " + decimal);

        // 读取字符串
        System.out.print("请输入一个字符串: ");
        String str = scanner.next();
        System.out.println("你输入的字符串是: " + str);

        // 关闭 Scanner 对象
        scanner.close();
    }
}

在上面的示例中,我们先创建了一个Scanner对象,并将其与标准输入流(System.in)关联。然后使用nextInt()nextDouble()next()方法来读取用户的输入,并打印出来。最后,记得要调用close()方法关闭Scanner对象。

通过这种方式,你可以从控制台读取用户的输入,并根据需要进行处理和使用。

4.2 格式化输出

在Java中,你可以使用System.out.printf()方法或String.format()方法来进行格式化输出。这两种方法都使用了类似于C语言中的格式化字符串的语法。

下面是一个示例代码,展示了如何进行格式化输出:

public class FormatOutputExample {
    public static void main(String[] args) {
        String name = "John";
        int age = 25;
        double height = 1.75;

        // 使用 System.out.printf() 进行格式化输出
        System.out.printf("姓名: %s, 年龄: %d, 身高: %.2f\n", name, age, height);

        // 使用 String.format() 进行格式化输出
        String output = String.format("姓名: %s, 年龄: %d, 身高: %.2f", name, age, height);
        System.out.println(output);
    }
}

在上面的示例中,我们使用了%s%d%.2f等格式化占位符来表示不同类型的数据,并通过传递相应的变量值来填充占位符。

可以使用静态的String.format方法创建一个格式化字符串,而不打印输出、

String name = "Alice";
int age = 30;
double height = 1.65;

String formattedString = String.format("姓名:%s,年龄:%d,身高:%.2f", name, age, height);
System.out.println(formattedString);

下面是一些常见的格式化占位符及其对应的数据类型:

  • %s:字符串类型
  • %d:整数类型
  • %f:浮点数类型
  • %b:布尔类型
  • %c:字符类型
  • %t:日期/时间类型
  • %n:换行符

4.3 文件输入与输出

在Java中,可以使用java.util.Scanner类来进行文件的输入操作。以下是使用Scanner读取文件的示例代码:

import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;

public class FileInputExample {
    public static void main(String[] args) {
        try {
            // 创建文件对象
            File file = new File("path/to/file.txt");
            
            // 创建Scanner对象,将文件作为输入源
            Scanner scanner = new Scanner(file);
            
            // 逐行读取文件内容
            while (scanner.hasNextLine()) {
                String line = scanner.nextLine();
                // 处理每一行的数据
                System.out.println(line);
            }
            
            // 关闭Scanner对象
            scanner.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }
}

在上述代码中,我们通过创建一个File对象来表示要读取的文件,然后将该文件作为输入源创建一个Scanner对象。通过调用scanner.nextLine()方法逐行读取文件内容,并进行相应的处理。最后,记得关闭Scanner对象以释放资源。

除了逐行读取,Scanner还提供了许多其他的读取方法,例如nextInt()nextDouble()等,可以根据需要选择合适的方法来读取文件中的数据。

Scanner类主要用于从输入源(如键盘)读取数据。对于文件的输出,可以使用其他类,如java.io.PrintWriterjava.io.FileWriter。以下是使用PrintWriter进行文件输出的示例代码:

import java.io.File;
import java.io.FileNotFoundException;
import java.io.PrintWriter;

public class FileOutputExample {
    public static void main(String[] args) {
        try {
            // 创建文件对象
            File file = new File("path/to/output.txt");
            
            // 创建PrintWriter对象,将文件作为输出目标
            PrintWriter writer = new PrintWriter(file);
            
            // 写入数据到文件
            writer.println("Hello, World!");
            writer.println("This is a sample output.");
            
            // 关闭PrintWriter对象
            writer.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }
}

注意

当指定一个相对文件名时,文件将相对于启动java虚拟机的那个目录放置 如果从一个命令shell执行以下命令启动程序 java Myprog,启动目录就是shell的当前目录

如果使用集成开发环境,启动目录将由IDE控制,可以使用下面的调用找到这个目录的位置

String dir = System.getProperty("user.dir");

5. 控制流程

5.1 块作用域

在Java中,块作用域是指由一对花括号({})括起来的代码块。在块作用域中声明的变量只在该代码块内部有效,超出该代码块的范围后就无法访问。这种作用域规则有助于控制变量的可见性和生命周期。

5.2 条件语句

在Java中,条件语句用于根据特定条件的真假来决定是否执行一段代码。Java提供了多种条件语句的形式,包括if语句、if-else语句、if-else if-else语句和switch语句。

if语句:if语句用于根据一个条件的真假来执行特定的代码块。

if (condition) {
    // 当条件为真时执行的代码
}

if-else语句:if-else语句用于在条件为真时执行一段代码,条件为假时执行另一段代码。

if (condition) {
    // 当条件为真时执行的代码
} else {
    // 当条件为假时执行的代码
}

if-else if-else语句:if-else if-else语句用于根据多个条件的真假来执行相应的代码块。

if (condition1) {
    // 当条件1为真时执行的代码
} else if (condition2) {
    // 当条件2为真时执行的代码
} else {
    // 当所有条件都为假时执行的代码
}

5.3 循环

在Java中,循环语句用于重复执行一段代码,直到满足特定条件。Java提供了多种类型的循环语句,包括for循环、while循环和do-while循环。

for循环:for循环用于在一定的次数内重复执行一段代码。

for (初始化语句; 循环条件; 更新语句) {
    // 循环体,要重复执行的代码
}

while循环:while循环在循环开始前先判断条件是否满足,只有当条件为真时才执行循环体。

while (循环条件) {
    // 循环体,要重复执行的代码
}

do-while循环:do-while循环先执行一次循环体,然后判断条件是否满足,只有当条件为真时才继续执行循环。

do {
    // 循环体,要重复执行的代码
} while (循环条件);

这些循环语句可以根据需要灵活地控制循环次数和循环条件,实现不同的循环逻辑

5.4 确定性循环

在Java中,确定性循环是指在编写循环时,循环的迭代次数是已知的或可预测的,而不是由循环内部的条件动态确定的。确定性循环通常使用for循环来实现,因为for循环提供了明确的循环条件和迭代器。

以下是一个使用确定性循环的示例:

for (int i = 0; i < 5; i++) {
    System.out.println("Iteration: " + i);
}

在上述示例中,循环的迭代次数是已知的,即循环将执行5次。循环变量i从0开始,每次迭代增加1,直到达到循环条件i < 5为止。

确定性循环非常适合在需要指定精确迭代次数的情况下使用,例如遍历数组或执行固定次数的计算。

请注意,如果循环的迭代次数是不确定的或由动态条件确定的,则需要使用非确定性循环,例如while循环或do-while循环。

5.5 多重选择:switch语句

Java中,可以使用switch语句实现多重选择的逻辑。switch语句根据表达式的值,从一系列的选项中选择执行特定的代码块。

以下是switch语句的基本语法:

switch (expression) {
    case value1:
        // 执行代码块1
        break;
    case value2:
        // 执行代码块2
        break;
    case value3:
        // 执行代码块3
        break;
    // 可以有更多的case语句
    default:
        // 如果没有匹配的case,则执行默认代码块
}

switch语句中的expression是要进行匹配的值,可以是整数、字符、枚举或字符串类型。每个case语句后面跟着一个特定的值,如果expression的值与某个case语句的值匹配,那么对应的代码块将被执行。如果没有匹配的case语句,可以使用default关键字指定默认的代码块。

请注意,每个case语句后面必须使用break关键字来终止当前的代码块,否则程序将会继续执行后面的case语句(这被称为"case穿透")。

5.6 中断控制流程语句

在 Java 中,有三种常用的控制流程语句可以用于中断程序的执行或改变程序的执行流程:

  1. break 语句:用于立即中断循环或 switch 语句的执行,并跳出当前的代码块。在循环中使用 break 语句可以提前终止循环的执行。在 switch 语句中使用 break 语句可以跳出 switch 代码块,继续执行 switch 语句后面的代码。

    示例:

    for (int i = 0; i < 10; i++) {
        if (i == 5) {
            break; // 终止循环
        }
        System.out.println(i);
    }
    
  2. continue 语句:用于立即跳过当前迭代的剩余代码,进入下一次迭代。在循环中使用 continue 语句可以跳过当前迭代中的代码,直接进入下一次迭代。

    示例:

    for (int i = 0; i < 10; i++) {
        if (i == 5) {
            continue; // 跳过当前迭代
        }
        System.out.println(i);
    }
    
  3. return 语句:用于中断方法的执行,并返回方法的返回值(如果有)。在方法中使用 return 语句可以提前结束方法的执行,并将结果返回给调用者。

    示例:

    public int add(int a, int b) {
        return a + b; // 中断方法执行,返回结果
    }
    

这些控制流程语句可以根据需要灵活使用,以实现不同的控制逻辑和程序流程。请注意,在使用这些语句时要小心,确保程序的逻辑正确性和可读性。

6. 大数

在Java中,大数(Big Number)通常指的是超出了基本数据类型范围的数值,例如超过了int或long类型所能表示的范围。对于处理大数,Java提供了BigInteger和BigDecimal两个类。

BigInteger类:用于处理大整数。它支持任意精度的整数运算,可以处理超出long类型范围的整数。BigInteger类提供了各种方法来执行基本的整数操作,如加法、减法、乘法、除法、取模等。

示例:

import java.math.BigInteger;

public class BigIntegerExample {
    public static void main(String[] args) {
        BigInteger num1 = new BigInteger("12345678901234567890");
        BigInteger num2 = new BigInteger("98765432109876543210");

        BigInteger sum = num1.add(num2);
        BigInteger difference = num1.subtract(num2);
        BigInteger product = num1.multiply(num2);
        BigInteger quotient = num1.divide(num2);
        BigInteger remainder = num1.remainder(num2);

        System.out.println("Sum: " + sum);
        System.out.println("Difference: " + difference);
        System.out.println("Product: " + product);
        System.out.println("Quotient: " + quotient);
        System.out.println("Remainder: " + remainder);
    }
}

BigDecimal类:用于处理大十进制数,具有任意精度的小数运算。BigDecimal类提供了各种方法来执行小数操作,包括加法、减法、乘法、除法、取模等。

示例:

import java.math.BigDecimal;

public class BigDecimalExample {
    public static void main(String[] args) {
        BigDecimal num1 = new BigDecimal("1234.56789");
        BigDecimal num2 = new BigDecimal("9876.54321");

        BigDecimal sum = num1.add(num2);
        BigDecimal difference = num1.subtract(num2);
        BigDecimal product = num1.multiply(num2);
        BigDecimal quotient = num1.divide(num2, 10, BigDecimal.ROUND_HALF_UP);

        System.out.println("Sum: " + sum);
        System.out.println("Difference: " + difference);
        System.out.println("Product: " + product);
        System.out.println("Quotient: " + quotient);
    }
}

这些类提供了丰富的方法来执行各种数值操作,并且能够处理非常大的数值,适用于大数计算需求。

7. 数组

7.1 声明数组

在Java中声明数组有两种方式:

声明数组变量:只声明数组的引用变量,而不创建数组对象。语法如下:

<数据类型>[] <变量名>;

声明并创建数组对象:声明数组变量的同时创建数组对象,并指定数组的长度。语法如下:

<数据类型>[] <变量名> = new <数据类型>[数组长度];

在创建数组对象时,需要指定数组的长度,即数组可以容纳的元素个数。注意,数组的长度是固定的,一旦创建后就无法更改。

7.2 访问数组元素

创建数组对象后,可以通过索引访问和修改数组中的元素。数组的索引从0开始,到数组长度减1结束。

示例:

javaCopy codeint[] numbers = new int[3];
numbers[0] = 10; // 设置第一个元素为10
numbers[1] = 20; // 设置第二个元素为20
numbers[2] = 30; // 设置第三个元素为30

System.out.println(numbers[0]); // 输出第一个元素的值:10
System.out.println(numbers[1]); // 输出第二个元素的值:20
System.out.println(numbers[2]); // 输出第三个元素的值:30

7.3 for each循环

Java中的增强型for循环(也称为for-each循环)用于遍历数组或集合中的元素。它提供了一种简洁的方式来遍历并访问数组或集合中的每个元素,而无需使用传统的for循环和索引。

增强型for循环的语法如下:

for (元素类型 变量名 : 数组或集合) {
    // 执行操作
}

其中,元素类型是数组或集合中元素的数据类型,变量名是用于遍历元素的临时变量,数组或集合是要遍历的对象。

下面是一个使用增强型for循环遍历数组的示例:

int[] numbers = {1, 2, 3, 4, 5};

for (int number : numbers) {
    System.out.println(number);
}

上述代码中,使用增强型for循环遍历了整型数组numbers中的每个元素,并将每个元素打印输出。

同样,我们也可以使用增强型for循环遍历集合:

List<String> names = Arrays.asList("Alice", "Bob", "Charlie");

for (String name : names) {
    System.out.println(name);
}

7.4 数组拷贝

Arrays.copyOf()是Java中用于对数组进行拷贝的方法之一。它可以用来创建一个新的数组,并将原始数组的内容复制到新数组中。

Arrays.copyOf()方法有两种使用方式:

拷贝整个数组:

int[] sourceArray = {1, 2, 3, 4, 5};
int[] targetArray = Arrays.copyOf(sourceArray, sourceArray.length);

上述代码将整个sourceArray数组复制到targetArray数组中。

拷贝部分数组:

int[] sourceArray = {1, 2, 3, 4, 5};
int[] targetArray = Arrays.copyOf(sourceArray, 3);

上述代码将sourceArray数组的前三个元素复制到targetArray数组中。

需要注意的是,Arrays.copyOf()方法返回的是一个新的数组,与原始数组完全独立。修改原始数组或目标数组中的元素不会影响另一个数组。

7.5 命令行参数

在Java中,可以通过命令行传递参数给Java程序。这些命令行参数可以在程序运行时访问和使用。

Java程序可以通过main()方法接收命令行参数。main()方法是Java程序的入口方法,它接收一个字符串数组作为参数,通常被命名为args。这个参数数组包含了命令行传递给程序的所有参数。

以下是一个简单的示例,演示如何在Java程序中使用命令行参数:

public class CommandLineArgsExample {
    public static void main(String[] args) {
        // 输出命令行参数的个数
        System.out.println("命令行参数个数:" + args.length);

        // 遍历并输出所有命令行参数
        for (int i = 0; i < args.length; i++) {
            System.out.println("参数 " + (i + 1) + ": " + args[i]);
        }
    }
}

假设将上述代码保存为CommandLineArgsExample.java,然后通过命令行编译和运行程序。使用以下命令编译程序:

javac CommandLineArgsExample.java

编译成功后,可以通过以下命令运行程序并传递参数:

java CommandLineArgsExample arg1 arg2 arg3

程序将输出命令行参数的个数和每个参数的值。

7.6 数组排序

在Java中,可以使用Arrays类提供的方法来对数组进行排序。Arrays类提供了几种不同的排序方法,包括基本数据类型和对象类型的排序。

  1. 对基本数据类型数组进行排序:
int[] numbers = {5, 2, 8, 1, 9};
Arrays.sort(numbers);

上述代码将整数数组numbers按升序进行排序。

  1. 对对象类型数组进行排序:
String[] names = {"Alice", "Bob", "Charlie", "David"};
Arrays.sort(names);

上述代码将字符串数组names按字母顺序进行排序。

  1. 对自定义对象数组进行排序: 如果要对自定义对象数组进行排序,需要确保对象实现了Comparable接口,并实现了compareTo()方法来定义对象的排序规则。例如:
public class Person implements Comparable<Person> {
    private String name;
    private int age;

    // 省略构造方法和其他代码

    @Override
    public int compareTo(Person other) {
        // 根据姓名进行排序
        return this.name.compareTo(other.name);
    }
}

Person[] people = {new Person("Alice", 25), new Person("Bob", 30), new Person("Charlie", 20)};
Arrays.sort(people);

上述代码将people数组按姓名进行排序,使用对象的compareTo()方法定义排序规则。

请注意,数组的排序会直接修改原始数组的顺序。如果需要保留原始数组,可以在排序前先创建一个副本进行排序。

Arrays.sort()方法使用的是一种改进的快速排序算法(Dual-Pivot Quicksort)。快速排序是一种常用的排序算法,它具有较高的性能和效率。

7.7 多维数组

在Java中,多维数组是指数组中包含其他数组作为元素的数组。它实际上是一个由行和列组成的表格结构。

Java中可以创建任意维度的数组,例如二维数组、三维数组等。创建多维数组的语法如下:

// 声明并初始化一个二维数组
dataType[][] arrayName = new dataType[rowSize][columnSize];

// 声明并初始化一个三维数组
dataType[][][] arrayName = new dataType[dimension1Size][dimension2Size][dimension3Size];

// 以此类推,可以创建任意维度的数组

以下是一个示例,创建并访问一个二维数组:

// 创建一个二维数组
int[][] matrix = new int[3][4];

// 给二维数组赋值
matrix[0][0] = 1;
matrix[0][1] = 2;
matrix[0][2] = 3;
// ...

// 访问二维数组元素
int value = matrix[1][2];
System.out.println(value); // 输出:0

多维数组的访问方式与一维数组类似,使用索引来指定元素的位置。对于二维数组,第一个索引表示行,第二个索引表示列。对于三维数组,第一个索引表示第一维度,第二个索引表示第二维度,依此类推。

多维数组可以用于解决一些复杂的问题,例如矩阵运算、图像处理等。在使用多维数组时,需要注意索引的范围以及数组的维度匹配等问题,以避免数组越界或逻辑错误。

7.8 不规则数组

Java中,不规则数组(Ragged Array)是指每一行的长度可以不同的二维数组。与规则数组不同,不规则数组的各行长度可以不相等。

创建不规则数组的步骤如下:

  1. 声明一个二维数组变量,但不指定每行的长度。
  2. 为每一行分配独立的一维数组,每个一维数组的长度可以不同。

一个常见的例子是存储学生成绩的二维数组,其中每个学生可能有不同数量的科目成绩。

以下是一个示例:

// 声明一个不规则数组来存储学生成绩
double[][] studentScores = new double[3][];

// 为每个学生分配独立的一维数组来存储科目成绩
studentScores[0] = new double[4]; // 学生1有4门科目
studentScores[1] = new double[5]; // 学生2有5门科目
studentScores[2] = new double[3]; // 学生3有3门科目

// 给学生成绩赋值
studentScores[0][0] = 85.5;
studentScores[0][1] = 90.0;
studentScores[0][2] = 78.5;
studentScores[0][3] = 92.0;

studentScores[1][0] = 77.0;
studentScores[1][1] = 88.5;
studentScores[1][2] = 92.0;
studentScores[1][3] = 79.5;
studentScores[1][4] = 84.0;

studentScores[2][0] = 92.5;
studentScores[2][1] = 86.0;
studentScores[2][2] = 91.5;

// 遍历并输出学生成绩
for (int i = 0; i < studentScores.length; i++) {
    System.out.print("学生 " + (i + 1) + " 成绩:");
    for (int j = 0; j < studentScores[i].length; j++) {
        System.out.print(studentScores[i][j] + " ");
    }
    System.out.println();
}

在这个例子中,我们声明了一个不规则数组studentScores来存储学生成绩。每个学生的科目成绩存储在一个独立的一维数组中,因此每个学生可以有不同数量的科目。我们为每个学生分配了相应的一维数组,并为每个科目成绩赋值。最后,我们使用嵌套的循环遍历不规则数组,并输出学生成绩。