曾经有人说,作为Java程序员如果没有卷过这本书,就算不上是真正的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)
采用双引号界定字符串
即使一个方法没有参数,也需要使用空括号
Java 中的注释也不会出现在可执行程序中
在 Java 中,有 3 种标记注释的方式。最常用的方式是使用 //,其注释内容从 // 开始到本行结尾
可以使用 /* 和 */ 注释界定符将一段比较长的注释括起来
第 3 种注释可以用来自动地生成文档。这种注释以 /** 开始,以 */ 结束
Java 是一种强类型语言
这就意味着必须为每一个变量声明一种类型
在 Java 中,一共有 8 种基本类型(primitiv type),其中有 4 种整型、2 种浮点型、1种字符类型 char 和 1 种用于表示真值的 boolean 类型
整型用于表示没有小数部分的数值,允许是负值
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 类都提供了处理无符号除法和求余数的方法
浮点类型用于表示有小数部分的数值
float 4 字节
double 8 字节
double 表示这种类型的数值是 float 类型的两倍(有人称之为双精度值)
float 类型的数值有一个后缀 F 或 f
没有后缀 F 的浮点值总是默认为 double 类型
也可以在浮点数值后面添加后缀 D 或 d
所有的浮点数值都遵循 IEEE 754 规范
下面是用于表示溢出和出错情况的三个特殊的浮点数值:
一个正整数除以 0 的结果为正无穷大。计算 0/0 或者负数的平方根结果为 NaN
常量 Double.POSITIVE_INFINITY、Double.NEGATIVE_INFINITY 和 Double.NaN 分别表示这三个特殊的值
浮点数不适用于无法接受舍入误差的金融计算,应该使用 BigDecimal 类
char 类型的字面量值要用单引号括起来
char 类型的值可以表示为十六进制值,其范围从 \u0000 到 \uFFFF
对于任意给定的代码值,在不同的编码方案下有可能对应不同的字母
采用大字符集的语言其编码长度有可能不同
在设计 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 代码单元。最好将字符串作为抽象数据类型处理。
boolean 类型有两个值:false 和 true,用来判定逻辑条件
整型值和布尔值之间不能进行相互转换
变量名必须是一个以字母开头并由字母或数字构成的序列
大小写敏感
不能使用 Java 保留字作为变量名
在 Java 9 中,单下划线 _ 不能作为变量名,将来的版本可能使用 _ 作为通配符
可以在一行中声明多个变量
逐一声明每一个变量可以提高程序的可读性
声明一个变量之后,必须用赋值语句对变量进行显式初始化,千万不要使用为初始化的变量值
要想对一个已经声明过的变量进行赋值,就需要将变量名放在等号左侧,再把一个适当取值的 Java 表达式放在等号的右侧
也可以将变量的声明和初始化放在同一行中
在 Java 中可以将声明放在代码中的任何地方
变量的声明尽可能地靠近变量第一次使用的地方,这是一种良好的程序编写风格
从 Java 10 开始,对于局部变量,如果可以从变量的初始化推断出它的类型,就不再需要声明类型。只需要使用关键字 var 而无须指定类型
var vacationDays = 12; // vacationDays is an int
var greeting = "Hello"; // greeting is a String
在 Java 中,并不区分变量的声明和定义
在 Java 中,利用关键字 final 指示常量
关键字 final 表示这个变量只能被赋值一次。一旦被赋值之后,就不能再更改了。习惯上,常量名使用全大写。
在 Java 中,经常希望某个常量可以在一个类的多个方法中使用,通常将这些常量称为类常量(class constant)。可以使用关键字 static final 设置一个类常量。
如果一个常量被声明为 public,那么其他类的方法也可以使用这个常量
const 是 Java 保留的关键字,但目前并没有使用。在 Java 中,必须使用 final 定义常量
变量的取值只在一个有限的集合内
枚举类型包括有限个命名的值
enum Size { SMALL, MEDIUM, LARGE, EXTRA_LARGE };
Size s = Size.SMALL;
Size 类型的变量只能存储这个类型声明中给定的某个枚举值,或者特殊值 null,null 表示这个变量没有设置任何值
算术运算符 +、-、*、/ 表示加、减、乘、除运算
当参与 / 运算当两个操作数都是整数时,表示整数除法;否则,表示浮点除法
整数的求余操作用 % 表示
整数被 0 除将会产生一个异常,而浮点数被 0 除将会得到无穷大或 NaN 结果
无论在哪个虚拟机上运行,同一运算应该得到同样当结果。对于浮点数的算术运算,实现这样的可移植性是相当困难的。double 类型使用 64 位存储一个数值,而有些h处理器则使用 80 位的浮点寄存器。这些寄存器增加了中间过程的计算精度。但是,这个结果可能与始终使用 64 位计算的结果不一样。(p38 最好读一下灰色部分)
使用 strictfp 关键字标记的方法必须使用严格的浮点计算来生成可再生的结果
public static strictfp void main(String[] args)
main 方法中的所有指令都将使用严格的浮点计算
如果将一个类标记位 strictfp,这个类中的所有方法都要使用严格的浮点计算
在 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 类提供了一些方法使整数有更好的运算安全性
当用一个二元运算符连接两个值时(例如 n + f,n 是整数,f 是浮点数),先要将两个操作数转换为同一种类型,然后再进行计算
损失信息的转换要通过强制类型转换(cast)来完成
强制类型转换的语法格式是在圆括号中给出想要转换的目标类型,后面紧跟待转换的变量名
不要在 boolean 类型与任何数值类型之间进行强制类型转换
在赋值中使用二元运算符,这是一种很方便的简写形式
x += 4;
前缀形式会先完成加 1;后缀形式会使用变量原来的值。
检测相等性,可以使用两个等号 ==
使用 != 检测不相等
< 小于 > 大于 = 大于等于
&& 表示逻辑“与”运算
|| 表示逻辑“或”运算
! 表示逻辑非运算
&& 和 || 运算符是按照“短路”方式来求值的:如果第一个操作数已经能够确定表达式的值,第二个操作数就不必计算了
Java 支持三元操作符 ? :
可以使用掩码技术得到整数中的各个位
位运算包括:
& and
| or
^ xor
~ not
& 和 | 运算符不采用“短路”方式来求值
>> 右移
>>> 运算符会用 0 填充高位,这与 >> 不同,它会用富豪为填充高位
不存在
同一个级别的运算符按照从左到右的次序进行计算(但右结合运算符除外)
从概念上讲,Java 字符串就是 Unicode 字符序列
Java 没有内置的字符串类型,而是在标准 Java 类库中提供了一个预定义类,很自然地叫做 String
String 类的 substring 方法可以从一个较大的字符串提取出一个子串
String greeting = "Hello";
String s = greeting.substring(0, 3);
在 substring 中从 0 开始计数,直到 3 为止,但不包含 3
Java 语言允许使用 + 号连接(拼接)两个字符串
当一个字符串与一个非字符串的值进行拼接时,后者会转换成字符串
任何一个 Java 对象都可以转换成字符串
把多个字符串放在一起,用一个界定符分隔,可以使用静态 join 方法
String all = String.join(" / ", "s", "m", "l", "xl");
在 Java 11 中,还提供了一个 repeat 方法
String repeated = "Java".repeat(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;
使用 equals 方法检测两个字符串是否相等
检测两个字符串是否相等,而不区分大小写,可以使用 equalsIgnoreCase 方法
一定不要使用 == 运算符检测两个字符串是否相等!这个运算符只能够确定两个字符串是否存放在同一个位置上。当然,如果字符串在同一个位置上,它们必然相等。但是,完全有可能将内容相同的多个字符串副本放置在不同的位置上
如果虚拟机始终将相同的字符串共享,就可以使用 == 运算符检测是否相等。但实际上只有字符串字面量是共享的,而 + 和 substring 等操作得到的字符串并不共享。因此,千万不要使用 == 运算符测试字符串的相等性,以免在程序中出现这种最糟糕的 bug,看起来这种 bug 就像随机产生的间歇性错误
空串 “” 是长度为 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 值傻姑娘调用方法,会出现错误
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);
Java 中的 String 类包含了 50 多个方法。令人惊讶的是它们绝大多数都很有用,可以想见使用的频率非常高
java.lang.String
在 API 注释中,有一些 CharSequence 类型的参数。这是一种接口类型,所有字符串都属于这个接口。
当看到一个 CharSequence 形参时,完全可以传入 String 类型的实参
可以在浏览器中访问 http://docs.oracle.com/javase/9/docs/api
有些时候,需要由较短的字符串构建字符串。如果采用字符串拼接的方式来达到这个目的,效率会比较低。每次拼接字符串时,都会构建一个新的 String 对象,既耗时,又浪费空间。使用 StringBuilder 类就可以避免这个问题的发生。
如果需要用许多小段的字符串构建一个字符串,那么应该按照下列步骤进行。
首先构建一个空的字符串构建器:
StringBuilder builder = new StringBuilder();
当每次需要添加一部分内容时,就调用 append 方法。
builder.append(ch);
builder.append(str);
在字符串构建完成时就调用 toString 方法,将可以得到一个 String 对象,其中包含了构建器中的字符序列。
String copletedString = builder.toString();
StringBuilder 类在 Java 5 中引入。这个类的前身是 StringBuffer,它的效率稍有些低,但允许采用多线程的方式添加或删除字符。如果所有字符串编辑操作都在单个线程中执行,则应该使用 StringBuilder。
要想通过控制台进行输入,首先需要构造一个与“标准输入流” 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()
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);
要想读取一个文件,需要构造一个 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
Java 中没有 goto 语句,但 break 语句可以带标签,可以利用它从内层循环跳出
块(block)
块(即复合语句)是指由若干条 Java 语句组成的语句,并用一对大括号括起来。块确定了变量的作用域。一个块可以嵌套在另一个块中。
不能在嵌套的块中声明同名的变量
在 Java 中,条件语句的形式为
if (condition) statement
条件必须用小括号括起来
块语句(block statement)
更一般的条件语句如下所示
if (condition) statement1 else statement2
else 部分总是可选的。else 子句与最邻近的 if 构成一组
当条件为 true 时,while 循环执行一条语句(也可以是一个块语句)。一般形式如下:
while (condition) statement
如果开始时循环条件的值就为 false,那么 while 循环一次也不执行
如果希望循环体至少执行一次,需要使用 do/while 循环将检测放在最后。它的语法如下:
do statement while (condition)
这种循环语句先执行语句(通常是一个语句块),然欧再检测循环条件。如果为 true,就重复执行语句,然后再次检测循环条件,以此类推。
for 循环语句是支持迭代的一种通用结构,由一个计数器或类似的变量控制地带次数,每次迭代后这个变量将会更新
for 语句的第 1 部分通常是对计数器初始化;第 2 部分给出每次新一轮循环执行前要检测的循环条件;第 3 部分指定如何更新计数器
尽管 Java 允许在 for 循环的各个部分放置任何表达式,但有一条不成文但规则:for 语句的 3 个部分应该对同一个计数器变量进行初始化、检测和更新。若不遵守这一规则,编写的循环常常晦涩难懂。
当在 for 语句的第 1 部分中声明一个变量之后,这个变量的作用域就扩展到这个 for 循环体的末尾
如果在 for 语句内部定义一个变量,这个变量就不能在循环体之外使用。因此,如果希望在 for 循环体之外使用循环计数器的最终值,就要确保这个变量在循环之外声明。
可以在不同的 for 循环中定义同名的变量
for 循环语句只不过是 while 循环的一种简化形式
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 标签可以是:
当在 switch 语句中使用枚举常量时,不必在每个标签中指明枚举名,可以由 switch 的表达式值推倒得出。
Size sz = ...;
switch (sz)
{
case SMALL: // no need to use Size.SMALL
...
break;
...
}
Java 的设计者将 goto 作为保留字
通常,使用 goto 语句被认为是一种拙劣的程序设计风格
偶尔使用 goto 跳出循环还是有益处的。Java 设计者同意这种看法,甚至在 Java 语言中增加了一条新的语句:带标签的 break,以此来支持这种程序设计风格。
Java 还提供了一种带标签的 break 语句,用于跳出多重嵌套的循环语句
在嵌套很深的循环语句中会发生一些不可预料的事情。此时可能更加希望完全跳出所有嵌套循环之外
标签必须放在希望跳出的最外层循环之前,并且必须紧跟一个冒号
执行带标签的 break 会跳转到带标签的语句块末尾
continue 语句将控制转移到最内层循环的首部
如果将 continue 语句用于 for 循环中,就可以跳到 for 循环的“更新”部分
还有一种带标签的 continue 语句,将跳到与标签匹配的循环的首部
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 没有提供运算符重载功能
数组存储相同类型的序列
数组是一种数据结构,用来存储同一类型值的集合。通过一个整型下表可以访问数组中的每一个值。
在声明数组变量时,需要指出数组类型和数组变量的名字。
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 的数组
一旦创建了数组,就可以在数组中填入元素
创建一个数字数组时,所有元素都初始化为 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
增强的 for 循环的语句格式为:
for (variable : collection) statement
它定义一个变量用于暂存集合中的每一个元素,并执行相应的语句。collection 这一集合表达式必须是一个数组或者一个实现了 Iterable 接口的类对象。
for each 循环语句显得更加简洁、更不易出错,因为你不必为下标的起始值和终值而操心
在很多情况下还是需要使用传统的 for 循环。例如,如果不希望遍历整个集合,或者在循环内部需要使用下标值。
调用 Arrays.toString(a),返回一个包含数组元素的字符串,这些元素包围在中括号内,并用逗号分隔。
System.out.println(Arrays.toString(a));
在 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 得到数组中的下一个元素
每一个 Java 应用程序都有一个带 String args[] 参数的 main 方法。这个参数表明 main 方法将接收一个字符串数组,也就是命令行上指定的参数。
C++ 注释:程序名并没有存储在 args 数组中
要想对数值型数组进行排序,可以使用 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
多维数组将使用多个下标访问数组元素,它适用于表示表格或更加复杂的排列形式
声明一个二维数组相当简单
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));
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];