《Java 核心技术卷1 基础知识》第三章 Java 的基本程序设计结构 笔记

曾经有人说,作为Java程序员如果没有卷过这本书,就算不上是真正的Java程序员,那么我就也来卷卷它吧。下面是我的读书摘录笔记。

目录

3.1 一个简单的 Java 应用程序

3.2 注释

3.3 数据类型

3.3.1 整型

3.3.2 浮点类型

3.3.3 char 类型

3.3.4 Unicode 和 char 类型

3.3.5 boolean 类型

3.4 变量与常量

3.4.1 声明变量

3.4.2 变量初始化

3.4.3 常量

3.4.4 枚举类型

3.5 运算符

3.5.1 算术运算符

3.5.2 数学函数与常量

3.5.3 数值类型之间的转换

3.5.4 强制类型转换

3.5.5 结合赋值和运算符

3.5.6 自增与自减运算符

3.5.7 关系和 boolean 运算符

3.5.8 位运算符

3.5.9 括号与运算符级别

3.6 字符串

3.6.1 子串

3.6.2 拼接

3.6.3 不可变字符串

3.6.4 检测字符串是否相等

3.6.5 空串与 Null 串

3.6.6 码点与代码单元

3.6.7 String API

3.6.8 阅读联机 API 文档

3.6.9 构造字符串

3.7 输入与输出

3.7.1 读取输入

3.7.2 格式化输出

3.7.3 文件输入与输出

3.8 控制流程

3.8.1 块作用域

3.8.2 条件语句

3.8.3 循环

3.8.4 确定循环

3.8.5 多重选择:switch 语句

3.8.6 中断控制流程的语句

3.9 大数

3.10 数组

3.10.1 声明数组

3.10.2 访问数组元素

3.10.3 for each 循环

3.10.4 数组拷贝

3.10.5 命令行参数

3.10.6 数组排序

3.10.7 多维数组

3.10.8 不规则数组


3.1 一个简单的 Java 应用程序

FirstSample

public class FirstSample
{
    public static void main(String[] args)
    {
        System.out.println("We will ot use 'Hello, World!'");
    }
}

Java 区分大小写

关键字 public 称为访问修饰符(access modifier),用于控制程序的其他部分对这段代码的访问级别

关键字 class 表明 Java 程序中的全部内容都包含在类中

将类作为程序逻辑的一个容器,程序逻辑定义了应用程序的行为

类名必须以字母开头,后面可以跟字母和数字的任意组合

长度基本上没有限制

不能使用 Java 保留字作为类名

标准的命名规范为:类名是以大写字母开头的名词。如果名字由多个单词组成,每个单词的第一个字母都应该大写

骆驼命名法(camel case)

源代码的文件必须与公共类的名字相同,并用 .java 作为扩展名

在编译这段源码之后就会得到一个包含这个类字节码的文件

当使用 java ClassName 运行已经编译当程序时,Java 虚拟机总是从指定类中的 main 方法的代码开始执行,因此为了代码能够执行,在类的源文件中必须包含一个 main 方法

根据 Java 语言的规范,main 方法必须声明为 public

Java 规范:http://docs.oracle.com/javase/specs

Java 虚拟机规范:The Java® Virtual Machine Specification

Sun 公司在 Java 开源很久以前就把 bug 报告及其解决方案放在网站上让所有人监督检查,这是一种非常了不起的举动

用大括号划分程序的各个部分(通常称为块)

main 方法没有为操作系统返回“退出码”

每个句子必须用分号结束

回车不是语句的结束标志,因此,如果需要可以将一条语句写在多行上

点号(.)用于调用方法

object.method(parameters)

采用双引号界定字符串

即使一个方法没有参数,也需要使用空括号

3.2 注释

Java 中的注释也不会出现在可执行程序中

在 Java 中,有 3 种标记注释的方式。最常用的方式是使用 //,其注释内容从 // 开始到本行结尾

可以使用 /* 和 */ 注释界定符将一段比较长的注释括起来

第 3 种注释可以用来自动地生成文档。这种注释以 /** 开始,以 */ 结束

3.3 数据类型

Java 是一种强类型语言

这就意味着必须为每一个变量声明一种类型

在 Java 中,一共有 8 种基本类型(primitiv type),其中有 4 种整型、2 种浮点型、1种字符类型 char 和 1 种用于表示真值的 boolean 类型

3.3.1 整型

整型用于表示没有小数部分的数值,允许是负值

int 4 字节

short 2 字节

long 8 字节

byte 1 字节

在 Java 种,整型的范围与运行 Java 代码的机器无关

长整型数值有一个后缀 L 或 l

十六机制数值有一个前缀 0x 或 0X

八进制有一个前缀 0

从 Java 7 开始,加上前缀 0b 或 0B 就可以写二进制数

从 Java 7 开始,还可以为数字字面量加下划线,如用 1_000_000 表示 100 万

在 Java 中,所有的数值类型所占据的字节数与平台无关

Java 没有任何无符号形式的 int、long、short 或 byte 类型

Byte、Integer 和 Long 类都提供了处理无符号除法和求余数的方法

3.3.2 浮点类型

浮点类型用于表示有小数部分的数值

float 4 字节

double 8 字节

double 表示这种类型的数值是 float 类型的两倍(有人称之为双精度值)

float 类型的数值有一个后缀 F 或 f

没有后缀 F 的浮点值总是默认为 double 类型

也可以在浮点数值后面添加后缀 D 或 d

所有的浮点数值都遵循 IEEE 754 规范

下面是用于表示溢出和出错情况的三个特殊的浮点数值:

  • 正无穷大
  • 负无穷大
  • NaN(不是一个数字)

一个正整数除以 0 的结果为正无穷大。计算 0/0 或者负数的平方根结果为 NaN

常量 Double.POSITIVE_INFINITY、Double.NEGATIVE_INFINITY 和 Double.NaN 分别表示这三个特殊的值

浮点数不适用于无法接受舍入误差的金融计算,应该使用 BigDecimal 类

3.3.3 char 类型

char 类型的字面量值要用单引号括起来

char 类型的值可以表示为十六进制值,其范围从 \u0000 到 \uFFFF

3.3.4 Unicode 和 char 类型

对于任意给定的代码值,在不同的编码方案下有可能对应不同的字母

采用大字符集的语言其编码长度有可能不同

在设计 Java 时决定采用 16 位的 Unicode 字符集

现在,16 位的 char 类型已经不能满足描述所有 Unicode 字符的需要了

码点(code point)是指与一个编码表中的某个字符对应的代码值

在 Unicode 标准中,码点采用十六进制书写,并加上前缀 U+

Unicode 的码点可以分成 17 个代码平面(code plane)

第一个代码平面称为基本多语言平面(basic multilingual plane),包括码点从 U+0000 到 U+FFFF 的“经典”Unicode 代码

其余的 16 个平面的码点为从 U+10000 到 U+10FFFF,包括辅助字符(supplementary character)

UTF-16 编码采用不同长度的编码表示所有 Unicode 码点

在基本多语言平面中,每个字符用 16 位表示,通常称为代码单元(code unit);而辅助字符编码为一对连续的代码单元

在 Java 中,char 类型描述了 UTF-16 编码中的一个代码单元

强烈建议不要在程序中使用 char 类型,除非确实需要处理 UTF-16 代码单元。最好将字符串作为抽象数据类型处理。

3.3.5 boolean 类型

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

整型值和布尔值之间不能进行相互转换

3.4 变量与常量

3.4.1 声明变量

变量名必须是一个以字母开头并由字母或数字构成的序列

大小写敏感

不能使用 Java 保留字作为变量名

在 Java 9 中,单下划线 _ 不能作为变量名,将来的版本可能使用 _ 作为通配符

可以在一行中声明多个变量

逐一声明每一个变量可以提高程序的可读性

3.4.2 变量初始化

声明一个变量之后,必须用赋值语句对变量进行显式初始化,千万不要使用为初始化的变量值

要想对一个已经声明过的变量进行赋值,就需要将变量名放在等号左侧,再把一个适当取值的 Java 表达式放在等号的右侧

也可以将变量的声明和初始化放在同一行中

在 Java 中可以将声明放在代码中的任何地方

变量的声明尽可能地靠近变量第一次使用的地方,这是一种良好的程序编写风格

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

var vacationDays = 12; // vacationDays is an int

var greeting = "Hello"; // greeting is a String

在 Java 中,并不区分变量的声明和定义

3.4.3 常量

在 Java 中,利用关键字 final 指示常量

关键字 final 表示这个变量只能被赋值一次。一旦被赋值之后,就不能再更改了。习惯上,常量名使用全大写。

在 Java 中,经常希望某个常量可以在一个类的多个方法中使用,通常将这些常量称为类常量(class constant)。可以使用关键字 static final 设置一个类常量。

如果一个常量被声明为 public,那么其他类的方法也可以使用这个常量

const 是 Java 保留的关键字,但目前并没有使用。在 Java 中,必须使用 final 定义常量

3.4.4 枚举类型

变量的取值只在一个有限的集合内

枚举类型包括有限个命名的值

enum Size { SMALL, MEDIUM, LARGE, EXTRA_LARGE };

Size s = Size.SMALL;

Size 类型的变量只能存储这个类型声明中给定的某个枚举值,或者特殊值 null,null 表示这个变量没有设置任何值

3.5 运算符

3.5.1 算术运算符

算术运算符 +、-、*、/ 表示加、减、乘、除运算

当参与 / 运算当两个操作数都是整数时,表示整数除法;否则,表示浮点除法

整数的求余操作用 % 表示

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

无论在哪个虚拟机上运行,同一运算应该得到同样当结果。对于浮点数的算术运算,实现这样的可移植性是相当困难的。double 类型使用 64 位存储一个数值,而有些h处理器则使用 80 位的浮点寄存器。这些寄存器增加了中间过程的计算精度。但是,这个结果可能与始终使用 64 位计算的结果不一样。(p38 最好读一下灰色部分)

使用 strictfp 关键字标记的方法必须使用严格的浮点计算来生成可再生的结果

public static strictfp void main(String[] args)

main 方法中的所有指令都将使用严格的浮点计算

如果将一个类标记位 strictfp,这个类中的所有方法都要使用严格的浮点计算

3.5.2 数学函数与常量

在 Math 类中,包含了各种各样的数学函数

平方根 double y = Math.sqrt(x);

幂运算 double y = Math.pow(x, a);

Math.floorMod 求余数的

常用的三角函数

Math.sin

Math.cos

Math.tan

Math.atan

Math.atan2

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

Math.exp

Math.log

Math.log10

Java 提供了两个常量

Math.PI

Math.E

import static java.lang.Math.*;

在 Math 类中,为了达到最佳的性能,所有的方法都使用计算机浮点单元中的例程。如果得到一个完全可以预测的结果比运算速度更重要的话,那么就应该使用 strictMath 类。

Math 类提供了一些方法使整数有更好的运算安全性

3.5.3 数值类型之间的转换

当用一个二元运算符连接两个值时(例如 n + f,n 是整数,f 是浮点数),先要将两个操作数转换为同一种类型,然后再进行计算

  • 如果两个操作数中有一个是 double 类型,另一个操作数就会转换为 double 类型
  • 否则,如果两个操作数中有一个是 float 类型,另一个操作数就会转换为 float 类型
  • 否则,如果两个操作数中有一个是 long 类型,另一个操作数就会转换为 long 类型
  • 否则,两个操作数都将被转换为 int 类型

3.5.4 强制类型转换

损失信息的转换要通过强制类型转换(cast)来完成

强制类型转换的语法格式是在圆括号中给出想要转换的目标类型,后面紧跟待转换的变量名

不要在 boolean 类型与任何数值类型之间进行强制类型转换

3.5.5 结合赋值和运算符

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

x += 4;

3.5.6 自增与自减运算符

前缀形式会先完成加 1;后缀形式会使用变量原来的值。

3.5.7 关系和 boolean 运算符

检测相等性,可以使用两个等号 ==

使用 != 检测不相等

< 小于 > 大于 = 大于等于

&& 表示逻辑“与”运算

|| 表示逻辑“或”运算

! 表示逻辑非运算

&& 和 || 运算符是按照“短路”方式来求值的:如果第一个操作数已经能够确定表达式的值,第二个操作数就不必计算了

Java 支持三元操作符 ? :

3.5.8 位运算符

可以使用掩码技术得到整数中的各个位

位运算包括:

& and

| or

^ xor

~ not

& 和 | 运算符不采用“短路”方式来求值

>> 右移

>>> 运算符会用 0 填充高位,这与 >> 不同,它会用富豪为填充高位

不存在

3.5.9 括号与运算符级别

同一个级别的运算符按照从左到右的次序进行计算(但右结合运算符除外)

3.6 字符串

从概念上讲,Java 字符串就是 Unicode 字符序列

Java 没有内置的字符串类型,而是在标准 Java 类库中提供了一个预定义类,很自然地叫做 String

3.6.1 子串

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

String greeting = "Hello";

String s = greeting.substring(0, 3);

在 substring 中从 0 开始计数,直到 3 为止,但不包含 3

3.6.2 拼接

Java 语言允许使用 + 号连接(拼接)两个字符串

当一个字符串与一个非字符串的值进行拼接时,后者会转换成字符串

任何一个 Java 对象都可以转换成字符串

把多个字符串放在一起,用一个界定符分隔,可以使用静态 join 方法

String all = String.join(" / ", "s", "m", "l", "xl");

在 Java 11 中,还提供了一个 repeat 方法

String repeated = "Java".repeat(3);

3.6.3 不可变字符串

String 类没有提供修改字符串中某个字符的方法

如何修改这个字符串呢?可以提取想要保留的子串,再与希望替换的字符拼接

由于不能修改 Java 字符串中的单个字符,所以在 Java 文档中将 String 类对象称为不可变的

不过,可以修改字符串变量,让它引用另外一个字符串

好像修改一个代码单元要比从头创建一个新字符串更加简洁

通过拼接来创建一个新字符串的效率确实不高

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

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

Java 的设计者认为共享带来的高效率远远胜过于提取子串、拼接字符串所带来的低效率。可以看看你自己的程序,我们发现:大多数情况下都不会修改字符串,而只是需要对字符串进行比较

Java 字符串大致类似于 char* 指针:

char* greeting = "Hello";

当把 greeting 替换为另一个字符串的时候,Java 代码大致进行下列操作:

char* temp = malloc(6);

strncpy(temp, greeting, 3);

strncpy(temp + 3, "p!", 3);

greeting = temp;

3.6.4 检测字符串是否相等

使用 equals 方法检测两个字符串是否相等

检测两个字符串是否相等,而不区分大小写,可以使用 equalsIgnoreCase 方法

一定不要使用 == 运算符检测两个字符串是否相等!这个运算符只能够确定两个字符串是否存放在同一个位置上。当然,如果字符串在同一个位置上,它们必然相等。但是,完全有可能将内容相同的多个字符串副本放置在不同的位置上

如果虚拟机始终将相同的字符串共享,就可以使用 == 运算符检测是否相等。但实际上只有字符串字面量是共享的,而 + 和 substring 等操作得到的字符串并不共享。因此,千万不要使用 == 运算符测试字符串的相等性,以免在程序中出现这种最糟糕的 bug,看起来这种 bug 就像随机产生的间歇性错误

3.6.5 空串与 Null 串

空串 “” 是长度为 0 的字符串,检查一个字符串是否为空

if (str.length() == 0)

if (str.equals(""))

空串是一个 Java 对象,有自己的串长度(0)和内容(空)

String 变量还可以存放一个特殊的值,名为 null,表示目前没有任何对象与该变量关联

要检查一个字符串是否为 null,要使用以下条件:

if (str == null)

有时要检查一个字符串既不是 null 也不是空串

if (str != null && str.length() != 0)

首先要检查 str 不为 null

如果在一个 null 值傻姑娘调用方法,会出现错误

3.6.6 码点与代码单元

Java 字符串是由 char 值序列组成

char 数据类型是一个采用 UTF-16 编码表示 Unicode 码点的代码单元

最常用的 Unicode 字符使用一个代码单元就可以表示,而辅助字符需要一对代码单元表示

length 方法将返回采用 UTF-16 编码表示给定字符串所需要的代码单元数量

String greeting = "Hello";

int n = greeting.length(); // is 5

得到实际的数量,即码点数量

int cpCount = greeting.codePointCount(0, greeting.length()); // is 5

调用 s.charAt(n) 将返回位置 n 的代码单元, n 介于 0 ~ s.length() - 1 之间

char first = greeting.charAt(0); // first is 'H'

char last = greeting.charAt(4); // last is 'o'

遍历一个字符串,并且依次查看每一个码点,更容易的办法是使用 codePoints 方法,它会生成一个 int 值的“流”,每个 int 值对应一个码点

int[] codePoints = str.codePoints().toArray();

要把一个码点数组转换为一个字符串,可以使用构造器

String str = new String(codePoints, 0, codePoints.length);

3.6.7 String API

Java 中的 String 类包含了 50 多个方法。令人惊讶的是它们绝大多数都很有用,可以想见使用的频率非常高

java.lang.String

在 API 注释中,有一些 CharSequence 类型的参数。这是一种接口类型,所有字符串都属于这个接口。

当看到一个 CharSequence 形参时,完全可以传入 String 类型的实参

3.6.8 阅读联机 API 文档

可以在浏览器中访问 http://docs.oracle.com/javase/9/docs/api

3.6.9 构造字符串

有些时候,需要由较短的字符串构建字符串。如果采用字符串拼接的方式来达到这个目的,效率会比较低。每次拼接字符串时,都会构建一个新的 String 对象,既耗时,又浪费空间。使用 StringBuilder 类就可以避免这个问题的发生。

如果需要用许多小段的字符串构建一个字符串,那么应该按照下列步骤进行。

首先构建一个空的字符串构建器:

StringBuilder builder = new StringBuilder();

当每次需要添加一部分内容时,就调用 append 方法。

builder.append(ch);

builder.append(str);

在字符串构建完成时就调用 toString 方法,将可以得到一个 String 对象,其中包含了构建器中的字符序列。

String copletedString = builder.toString();

StringBuilder 类在 Java 5 中引入。这个类的前身是 StringBuffer,它的效率稍有些低,但允许采用多线程的方式添加或删除字符。如果所有字符串编辑操作都在单个线程中执行,则应该使用 StringBuilder。

3.7 输入与输出

3.7.1 读取输入

要想通过控制台进行输入,首先需要构造一个与“标准输入流” System.in 关联的 Scanner 对象

Scanner in = new Scanner(System.in);

使用 Scanner 类的各种方法读取输入

nextLine 方法将读取一行输入

String name = in.nextLine();

使用 nextLine 是因为在输入航中有可能包含空格。要想读取一个单词,可以调用

String firstName = in.next();

要想读取一个整数,就调用 nextInt 方法

int age = in.nextInt();

要想读取一个浮点数,就调用 nextDouble 方法

Scanner 类定义在 java.util 包中。当使用的类不是定义在基本 java.lang 包中时,一定要使用 import 指令倒入相应的包

程序清单 3-2

package chapter3.InputTest;

import java.io.Console;
import java.util.Scanner;

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

        System.out.println("What is your name?");
        String name = in.nextLine();

        System.out.println("How old are you?");
        int age = in.nextInt();

        System.out.println("Hell, " + name + ". Next year, you'll be " + (age + 1));
    }
}

因为输入是可见的,所以 Scanner 类不适用于从控制台读取密码。Java 6 特别引入了 Console 类来实现这个目的。要想读取一个密码,可以使用下列代码:

Console cons = System.console();

String username = cons.readLine("User name:");

char[] passwd = cons.readPassword("Password:");

采用 Console 对象处理输入不如采用 Scanner 方便。必须每次读取一行输入,而没有能够读取单个单词或数值的方法

java.util.Scanner

boolean hasNext()

boolean hasNextInt()

boolean hasNextDouble()

3.7.2 格式化输出

Java 5 沿用了 C 语言函数库中的 printf 方法

System.out.printf("%8.2f", x);

会以一个字段宽度打印 x:这包括 8 个字符,另外精度为小数点后 2 个字符

可以为 printf 提供多个参数

System.out.printf("Hello, %s. Next year, you'll be %d", name, age)

每一个以 % 字符开始的格式说明符都用相应都参数替换。格式说明符尾部都转换符指示要格式化的数值的类型:f 表示浮点数,s 表示字符串,d 表示十进制整数。

可以使用静态的 String.format 方法创建一个格式化的字符串

String message = String.format("Hello, %s. Next year, you'll be %d", name, age);

3.7.3 文件输入与输出

要想读取一个文件,需要构造一个 Scanner 对象

Scanner in = new Scanner(Path.of("myfile.txt"), StandardCharsets.UTF_8);

读取一个文件时,要知道它的字符编码

要想写入文件,就需要构造一个 PrintWriter 对象。在构造器中,需要提供文件名和字符编码

PrintWriter out = new PrintWriter("myfile.txt", StandardCharset.UTF_8);

如果文件不存在,创建该文件。可以像输出到 System.out 一样使用 print、println 以及 printf 命令

当指定一个相对文件时,文件位于相对于 Java 虚拟机启动目录的位置

java MyProg

启动目录就是命令解释器的当前目录

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

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

如果觉得定位文件太麻烦,可以考虑使用绝对路径名

如果用一个不存在的文件构造一个 Scanner,或者用一个无法创建的文件名构造一个 PrintWriter,就会产生异常

java.nio.file.Path

static Path of(String pathname) 根据给定的路径名构造一个 Path

3.8 控制流程

Java 中没有 goto 语句,但 break 语句可以带标签,可以利用它从内层循环跳出

3.8.1 块作用域

块(block)

块(即复合语句)是指由若干条 Java 语句组成的语句,并用一对大括号括起来。块确定了变量的作用域。一个块可以嵌套在另一个块中。

不能在嵌套的块中声明同名的变量

3.8.2 条件语句

在 Java 中,条件语句的形式为

if (condition) statement

条件必须用小括号括起来

块语句(block statement)

更一般的条件语句如下所示

if (condition) statement1 else statement2

else 部分总是可选的。else 子句与最邻近的 if 构成一组

3.8.3 循环

当条件为 true 时,while 循环执行一条语句(也可以是一个块语句)。一般形式如下:

while (condition) statement

如果开始时循环条件的值就为 false,那么 while 循环一次也不执行

如果希望循环体至少执行一次,需要使用 do/while 循环将检测放在最后。它的语法如下:

do statement while (condition)

这种循环语句先执行语句(通常是一个语句块),然欧再检测循环条件。如果为 true,就重复执行语句,然后再次检测循环条件,以此类推。

3.8.4 确定循环

for 循环语句是支持迭代的一种通用结构,由一个计数器或类似的变量控制地带次数,每次迭代后这个变量将会更新

for 语句的第 1 部分通常是对计数器初始化;第 2 部分给出每次新一轮循环执行前要检测的循环条件;第 3 部分指定如何更新计数器

尽管 Java 允许在 for 循环的各个部分放置任何表达式,但有一条不成文但规则:for 语句的 3 个部分应该对同一个计数器变量进行初始化、检测和更新。若不遵守这一规则,编写的循环常常晦涩难懂。

当在 for 语句的第 1 部分中声明一个变量之后,这个变量的作用域就扩展到这个 for 循环体的末尾

如果在 for 语句内部定义一个变量,这个变量就不能在循环体之外使用。因此,如果希望在 for 循环体之外使用循环计数器的最终值,就要确保这个变量在循环之外声明。

可以在不同的 for 循环中定义同名的变量

for 循环语句只不过是 while 循环的一种简化形式

3.8.5 多重选择:switch 语句

Java 有一个与 C/C++ 完全一样的 switch 语句

switch 语句将从与选项值相匹配的 case 标签开始执行,直到遇到 break 语句,或者执行到 switch 语句的结束处为止。如果没有相匹配的 case 标签,而有 default 子句,就执行这个子句。

有可能触发多个 case 分支。如果在 case 分支语句的末尾没有 break 语句,那么就会接着执行下一个 case 分支语句。

编译代码时可以考虑加上 +Xlint:fallthrough 选项,如下:

javac -Xlint:fallthrough Test.java

这样一来,如果某个分支最后缺少一个 break 语句,编译器就会给出一个警告消息

如果你确实正是想使用这种“直通式”(fallthrough)行为,可以为其外围方法加一个注解 @SuppressWarnings("fallthrough")。这样就不会对这个方法生成警告了。

注解是为编译器或处理 Java 源文件或类文件的工具提供信息的一种机制

case 标签可以是:

  • 类型为 char、byte、short 或 int 的常量表达式
  • 枚举常量
  • 从 Java 7 开始,case 标签还可以是字符串字面量

当在 switch 语句中使用枚举常量时,不必在每个标签中指明枚举名,可以由 switch 的表达式值推倒得出。

Size sz = ...;

switch (sz)

{

case SMALL: // no need to use Size.SMALL

...

break;

...

}

3.8.6 中断控制流程的语句

Java 的设计者将 goto 作为保留字

通常,使用 goto 语句被认为是一种拙劣的程序设计风格

偶尔使用 goto 跳出循环还是有益处的。Java 设计者同意这种看法,甚至在 Java 语言中增加了一条新的语句:带标签的 break,以此来支持这种程序设计风格。

Java 还提供了一种带标签的 break 语句,用于跳出多重嵌套的循环语句

在嵌套很深的循环语句中会发生一些不可预料的事情。此时可能更加希望完全跳出所有嵌套循环之外

标签必须放在希望跳出的最外层循环之前,并且必须紧跟一个冒号

执行带标签的 break 会跳转到带标签的语句块末尾

continue 语句将控制转移到最内层循环的首部

如果将 continue 语句用于 for 循环中,就可以跳到 for 循环的“更新”部分

还有一种带标签的 continue 语句,将跳到与标签匹配的循环的首部

3.9 大数

java.math 包中两个很有用的类:BigInteger 和 BigDecimal。这两个类可以处理包含任意长度数字序列的数值。BigInteger 类实现任意精度的整数运算,BigDecimal 实现任意精度的浮点数运算。

使用静态的 valueOf 方法可以将普通的数值转换为大数:

BigInteger a = BigInteger.valueOf(100);

对于更大的数,可以使用一个带字符串参数的构造器:

BigInteger reallyBig = new BigInteger("1234567890987654321");

另外还有一些常量:BigInteger.ZERO、BigInteger.ONE 和 BigInteger.TEN,Java 9 之后还增加了 BigInteger.TWO

不能使用人们熟悉的算术运算符处理大数,而需要使用大数类中的方法

BigInteger c = a.add(b); // c = a + b

BigInteger d = c.multiply(b.add(BigInteger.valueOf(2))); // d = c * (b + 2)

Java 没有提供运算符重载功能

3.10 数组

数组存储相同类型的序列

3.10.1 声明数组

数组是一种数据结构,用来存储同一类型值的集合。通过一个整型下表可以访问数组中的每一个值。

在声明数组变量时,需要指出数组类型和数组变量的名字。

int[] a;

这条语句只声明类变量 a,并没有将 a 初始化为一个真正的数组。应该使用 new 操作符创建数组。

int[] a = new int[100];

这条语句声明并初始化类一个可以存储 100 个整数的数组。

数组长度不要求是常量:new int[n] 会创建一个长度为 n 的数组

一旦创建了数组,就不能再改变它的长度

如果程序运行中需要经常扩展数组大的大小,就应该使用另一种数据结构 —— 数组列表(array list)

一种创建数组对象并同时提供初始化值的简写形式

int[] smallPrimes = {2, 3, 4, 5, 11, 13};

这个语法不需要使用 new,甚至不用指定长度

声明一个匿名数组:

new int[] {17, 19, 23, 29, 31, 37};

这会分配一个新数组并填入大括号中提供的值。它会统计初始化个数,并相应地设置数组大小。可以使用这种语法重新初始化一个数组而无须创建新变量。

int[] anonymous = {17, 19, 23, 29, 31, 37};

smallPrimes = anonymous;

在 Java 中,允许有长度为 0 的数组

3.10.2 访问数组元素

一旦创建了数组,就可以在数组中填入元素

创建一个数字数组时,所有元素都初始化为 0。boolean 数组的元素会初始化为 false。对象数组都元素则初始化为一个特殊值 null,表示这些元素未存放任何对象。

String[] names = new String[10];

会创建一个包含 10 个字符串的数组,所有字符串都为 null。如果希望这个数组包含空串,必须为元素指定空串:

for (int i = 0; i < 10; i ++) names[i] = "";

如果创建了一个 100 个元素的数组,并且试图访问元素 a[101],就会引发“array index out of bounds”异常

要想获得数组中的元素个数,可以使用 array.length

3.10.3 for each 循环

增强的 for 循环的语句格式为:

for (variable : collection) statement

它定义一个变量用于暂存集合中的每一个元素,并执行相应的语句。collection 这一集合表达式必须是一个数组或者一个实现了 Iterable 接口的类对象。

for each 循环语句显得更加简洁、更不易出错,因为你不必为下标的起始值和终值而操心

在很多情况下还是需要使用传统的 for 循环。例如,如果不希望遍历整个集合,或者在循环内部需要使用下标值。

调用 Arrays.toString(a),返回一个包含数组元素的字符串,这些元素包围在中括号内,并用逗号分隔。

System.out.println(Arrays.toString(a));

3.10.4 数组拷贝

在 Java 中,允许将一个数组变量拷贝到另一个数组变量。这时,两个变量将引用同一个数组

int[] luckyNumbers = smallPrimes;

luckyNumber2[5] = 12; // now smallPrimes[5] is also 12

如果希望将一个数组到所有值拷贝到一个新的数组中去,就要使用 Arrays 类的 copyOf 方法:

int[] coiedLucyNumbers = Arrays.copyOf(luckyNumbers, luckyNumbers.length);

第 2 个参数是新数组的长度。这个方法通常用来增加数组的大小:

luckyNumbers = Arrays.copyOf(luckyNumbers, 2 * luckyNumbers.length);

如果长度小于原始数组的长度,则只拷贝前面的值

C++ 注释:基本上与在堆(heap)上分配的数组指针一样。也就是说,

int[] a = new int[100];

不用于

int a[100];

而等同于

int *a = new int[100];

Java 中的 [] 运算符被预定义为会完成越界检查,而没有指针运算,即不能通过 a 加 1 得到数组中的下一个元素

3.10.5 命令行参数

每一个 Java 应用程序都有一个带 String args[] 参数的 main 方法。这个参数表明 main 方法将接收一个字符串数组,也就是命令行上指定的参数。

C++ 注释:程序名并没有存储在 args 数组中

3.10.6 数组排序

要想对数值型数组进行排序,可以使用 Arrays 类中的 sort 方法

int[] a = new int[100];

...

Arrays.sort(a);

这个方法使用了优化的快速排序算法。快速排序算法对于大多数数据集合来说都是效率比较高的。

Math.random 方法将返回一个 0 到 1 之前(包含 0、不包含 1)的随机浮点数。用 n 乘以这个浮点数,就可以得到从 0 到 n - 1 之间的一个随机数

sort

copyOf

copyOfRange

binarySearch

fill

equals

3.10.7 多维数组

多维数组将使用多个下标访问数组元素,它适用于表示表格或更加复杂的排列形式

声明一个二维数组相当简单

double[][] balance;

对数组进行初始化之前是不能使用的。在这里可以如下初始化:

balance = new double[NYEAR][NRATE];

如果直到数组元素,就可以不调用 new ,而直接使用简写形式对多维数组进行初始化。

int[][] magicSquare =

{

{1, 2, 3, 4},

{2, 3, 4, 5},

{3, 4, 5, 6}

};

一旦数组初始化,就可以利用两个中括号访问各个元素

for each 循环语句不能自动处理二维数组对每一个元素。它会循环处理行,而这些行本身就是一维数组。要想访问二维数组 a 的所有元素,需要使用两个嵌套的循环,循环如下:

for (double[] row: a)

for (double value : row)

do someting with value

要想快速地打印一个二维数组的数据元素列表,可以调用

System.out.println(Arrays.deepToString(a));

3.10.8 不规则数组

Java 实际上没有多维数组,只有一维数组。多维数组被解释为“数组的数组”

由于可以单独地访问数组的某一行,所以可以让两行交换

double[] temp = balances[i];

balance[i] = balances[i + 1];

balance[i + 1] = temp;

还可以方便地构造一个“不规则”数组,即数组的每一行有不同的长度

int[][] odds = new int[NMAX][];

for (int n = 0; n

odds[n] = new int[n + 1];

猜你喜欢

转载自blog.csdn.net/EasySecurity/article/details/128178322