# 语法
# 2.1 基本概念
# 2.1.1 注释
注释用来解释程序内容,对程序本身而言不具有逻辑意义,也不会被编译成机器码。
适当的添加注释可以提高程序的可读性,还可以辅助排错。
Java 的注释分为三种类型:
- 单行注释
// 被注释文字
- 多行注释
/*
被注释文字
被注释文字
...
*/
2
3
4
5
- 文档注释
/**
被注释文字
...
*/
2
3
4
# 2.1.2 关键字
关键字也被称为保留字。在 Java 中,关键字是包含特殊意义的保留单词,共计50个。
Java Language Keywords
Here is a list of keywords in the Java programming language. You cannot use any of the following as identifiers in your programs. The keywordsconst
andgoto
are reserved, even though they are not currently used.true
,false
, andnull
might seem like keywords, but they are actually literals; you cannot use them as identifiers in your programs.
col 1 col 2 col 3 col 4 col 5 abstract
continue
for
new
switch
assert
***default
goto
*package
synchronized
boolean
do
if
private
this
break
double
implements
protected
throw
byte
else
import
public
throws
case
enum
****instanceof
return
transient
catch
extends
int
short
try
char
final
interface
static
void
class
finally
long
strictfp
**volatile
const
*float
native
super
while
* not used
** added in 1.2
*** added in 1.4
**** added in 5.0
原文 (opens new window)
# 2.1.3 标识符
在 Java 程序中自定义的名称,可以是任意长度,由字母、数字、下划线(_)和美元符号($)构成。
建议
- 我们建议对标识符的选取做到“见名知义”
- 用标识符给类、方法和变量起名时还需要遵循一定的规范,让程序更易读
注意
- 应尽量减少使用
$
作为标识符的内容 - Java 标识符对大小写敏感
禁止
- 标识符第一个字符不能是数字
- 标识符不能是关键字
- 标识符不能是布尔值
- 标识符不能是
null
# 2.2 常量与变量
# 2.2.1 常量
或称“字面常量”,包含以下几种类型:
整数常量
所有整数
可以用二进制、八进制、十进制、十六进制表示。具体见进制。浮点数常量
所有小数
Java 中具体表现形式见浮点型。字符常量
包括普通字符和转义字符两种
将一个字母、数字或符号使用''
包围,构成普通字符常量
为避免冲突或特殊含义,规定了转义字符。转义字符由字母或符号和和反斜杠(\)组成,如:
字符 | 含义 |
---|---|
\\ | \ |
\' | ' |
\" | " |
\n | 换行 |
\r | 回车 |
\t | 制表符 |
字符串常量
将一个或多个字符使用""
包围,构成字符串常量布尔常量
表示逻辑值,包括true
和false
空常量
表示空,值为null
# 2.2.2 变量
在 Java 中,变量用来记录数据,是一个有类型的存储单元
变量具有以下特点:
- 变量的使用范围不能超过其所在的
{...}
- 变量必须定义和给值后才能使用
变量可分为以下几类:
- 实例变量 (Instance Variables, Non-Static Fields): 在类中不使用修饰词
static
定义的变量 - 类变量 (Class Variables, Static Fields): 类中使用修饰词
static
定义的变量,或接口中的变量 - 局部变量 (Local Variables): 由局部变量声明语句生命的变量
- 参数 (Parameters): 方法、异常、创建类时需要的变量
以下代码中,高亮部分即变量的声明和赋值:
public class VariableDemo {
public static void main(String[] args) {
int a = 5;
System.out.println(a);
}
}
2
3
4
5
6
局部变量类型推断Java 10+
从 Java 10 开始,允许使用关键字 var
来代替局部变量声明中的类型,JVM 会从变量初始化器中自动填充合适的类型。
实例:
// 显式声明
var userChannels = new HashMap<User, List<String>>();
// 从方法返回
var channels = lookupUserChannels("Tom");
channels.forEach(System.out::println);
2
3
4
5
6
代码:
Path path = Paths.get("src/web.log");
try (Stream<String> lines = Files.lines(path)){
long warningCount
= lines
.filter(line -> line.contains("WARNING"))
.count();
System.out.println("Found " + warningCount + " warnings in the
log file");
} catch (IOException e) {
e.printStackTrace();
}
2
3
4
5
6
7
8
9
10
11
可以被改写为:
var path = Paths.get("src/web.log");
try (var lines = Files.lines(path)){
var warningCount
= lines
.filter(line -> line.contains("WARNING"))
.count();
System.out.println("Found " + warningCount + " warnings in the
log file");
} catch (IOException e) {
e.printStackTrace();
}
2
3
4
5
6
7
8
9
10
11
但是值得注意的是,这个特性不能很好的在多态中使用。 对于父类 Vehicle 和它的子类 Car 和 Bike,有:
var v = new Car(); // 会被自动推断为 Car
// v = new Bike(); 当本意是 Vehicle v = new Bike() 的向上造型会失效
2
3
不能使用局部变量类型推断的地方
- 不能用于类的属性与方法签名
//public long process(var list) { }
- 不能用于没有具体初始化的变量
//var x
- 不能给 var 变量赋
null
//var x = null
- 不能用于 Lambda 表达式
//var x = () -> {}
但var
关键字可以使用在 Lambda 表达式的参数中Java 11+
在匿名内部类中使用局部变量类型推断
通常情况下,匿名内部类中定义的属性不能在类外进行访问,但如果将匿名内部类的接受变量改为 var
则可以打破这个限制。
这个点可以用来解决当一个方法想返回一些值作为中间结果的情形。
在不使用这个特性时,必须创建和维护一个新的类来实现这样的功能,例如 Collectors.averagingDouble()
源码中的小 double 数组。
实例:
Product 类,包含产品的名称、库存、价值信息。要求计算一个 list 中所有产品的总价值(库存 * 价值)。最终输出每个产品的名称与总价值。
// product list
var products = List.of(
new Product(10, 3, "Apple"),
new Product(5, 2, "Banana"),
new Product(17, 5, "Pear"));
// 使用 stream 来映射名称与总价值,用 var 变量接收
var productInfos = products
.stream()
.map(product -> new Object() {
String name = product.getName();
int total = product.getStock() * product.getValue();
})
.collect(toList());
// 遍历输出
productInfos.forEach(prod ->
System.out.println("name = " + prod.name + ", total = " +
prod.total));
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
输出结果:
name = Apple, total = 30
name = Banana, total = 10
name = Pear, total = 85
2
3
# 2.2.3 进制
十进制:0 ~ 9,满十进一,数字在代码中默认十进制
二进制Java SE 7.0 +:0 ~ 1,满二进一,在程序中标识一个二进制数,以
0b
或0B
开头。如:0b0011
=0B0011
八进制:0 ~ 7,满八进一,以
0
开头。如:06
、012
十六进制:0 ~ 9,A ~ F,满16进一,以
0x
或0X
作为开头。如:0x23
=0X23
进制转换
计算机基础内容:
十进制和二进制转化
十变二
除二取余法,连除倒取余
如:25的二进制为110012|25...1 2|12...0 2|6...0 2|3...1 2|1...1
1
2
3
4
5二变十
2的幂次方累加
如:1101001的十进制是1051+8+32+64=105
1十变二的2的幂次累减法
如:781的二进制为1100001101[512]-[256]-128-64-32-16-[8]-[4]-2-[1]
1二进制和八进制转化
二变八
低位次开始,每三位一组产生八进制数字,最高位不足补0
如:1100001101的八进制为01415001|100|001|101 -> 1 4 1 5
1
2八变二
1位化3位
如:03627的二进制为011 110 010 111二进制和十六进制转化
二变十六
低位次开始,每四位一组产生八进制数字,最高位不足补0
如:0111 1001 0111的十六进制为0x797
如:0001 1101 1110的十六进制为0x1DE十六变二
1位化4位
# 2.3 数据类型
# 2.3.1 基本数据类型
- 数值型
整数型 (整型)
byte
字节型
长度:1个字节
取值范围:-27 ~ 27-1 (-128 ~ 127)
默认值:0
short
短整型
长度:2个字节
取值范围:-215 ~ 215-1 (-32768 ~ 32767)
默认值:0
int
整型
长度:4个字节
取值范围:-231 ~ 231-1 (-2147483648 ~ 2147483647)使用`_`分割大数字Java SE 7.0 +
例如:
int a = 100_500;
默认值:
0
long
长整型
长度:8个字节
取值范围:-263 ~ 263-1 (≈ -1018 ~ 1018)
以l
或L
结尾,推荐使用L
默认值:0L
浮点型
float
单精度浮点型
长度:4个字节
取值范围:≈ -1.445 ~ 3.4×1038
以f
或F
结尾
默认值:0.0f
double
双精度浮点型
长度:8个字节
取值范围:≈ -4.9-324 ~ 10308
以d
或D
结尾,默认不写
默认值:0.0d
TIP
科学计数法
3e10
→ 3×1010
0x3P5
→ 3×25 (十六进制)
- 字符型
char
字符型
长度:2个字节
取值范围:0 ~ 216-1 (0 ~ 65535, '\u0000' ~ '\uffff')
默认值:'\u0000'
- 布尔型
boolean
布尔型
长度:1位/1字节/4字节
取值范围:true
和false
默认值:false
# 2.3.2 引用数据类型
TIP
String
是一个引用数据类型,所以它并不包含在关键字中
声明赋值String String = "String";
是可以被理解的
# 2.3.3 数据类型转换
# 2.3.3.1 隐式转换/自动类型转换
规律一:小类型可以自动转化为大类型
int i = 10;
float f = i;
2
规律二:整数可以自动的转化为小数,但可能出现精度损失的问题
long l = 10L;
float f = i;
2
规律三:char
可以自动转化为int
char c = 'c';
int i = c;
2
TIP
- 代码编译期,JVM 会检查数据类型和声明类型是否一致或兼容
- 如果在赋值的时候数据是一个变量,不会去检查这个变量的值而是检查变量的类型和声明的数据类型是否兼容。
下列情形是不兼容情形 JVM 给出的反馈
Input
char c = 97;
short s = c;
2
Output
Application.java:4: 错误: 不兼容的类型: 从char转换到short可能会有损失
short s = c;
^
1 个错误
2
3
4
Input
short s = 97;
char c = s;
2
Output
Application.java:4: 错误: 不兼容的类型: 从short转换到char可能会有损失
char c = s;
^
1 个错误
2
3
4
# 2.3.3.2 显示转换/强制类型转换
在大类型赋值给小类型/小数类型转为整数类型使用
注意
小数类型强制转为整数类型时会抛弃小数位
示例:
Input
int i = 97;
short s = (short) i;
2
Input
double d = 1.1;
int i = (int) d;
System.out.println(i);
2
3
4
Output
1
精度丢失
Input
int i = 200;
byte b = (byte) i;
System.out.println(b);
2
3
4
Output
-56
# 2.3.3.3 引用类型的强制类型转换
在多态情况下,无法访问子类特有的成员(方法列表已被父类确定),需要进行强制类型转换来实现。
Input
class Supper() {
...
public void mSup_1() {
// ...
}
public void mSup_2() {
// ...
}
}
class Sub() extends Supper {
public void mSub() {
// ...
}
}
public static void main(String[] args) {
Supper supper = new Sub(); // 父类引用指向子类对象,向上造型
supper.mSub(); // 报错,无法访问
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
将对象强制转换为Sub类型后,即可访问该方法。
Input
Sub sub = (Sub) supper;
sub.mSub();
2
特别的
- 在进行强制类型转换时,编译器会检查两个类之间是否存在继承关系
若存在继承关系,则编译时会通过,但运行时不一定
若不存在继承关系,编译时会报错
# 2.4 运算符
# 2.4.1 算术运算符
包括+
、-
、*
、/
、%
、++
、--
.
注意
byte
类型和short
类型运算时会自动提升为int
类型
Input
byte b1 = 3;
byte b2 = 5;
byte sum = b1 + b2;
System.out.println(sum);
2
3
4
5
Output
Application.java:5: 错误: 不兼容的类型: 从int转换到byte可能会有损失
byte sum = b1 + b2;
^
1 个错误
2
3
4
- 同一类型参与运算,结果的类型与参与运算的类型一致
- 若运算中有大类型参与运算,结果为大类型
- 绝大部分小数转换为二进制是无限小数,所以double类型无法精确存储小数,也不能进行精确运算
Input
double d1 = 3;
double d2 = 2.98;
System.out.println(d1 - d2);
2
3
4
Output
0.020000000000000018
- 整数/0
ArithmeticException
;任意非0数字/0.0 输出Infinite
;0/0.0输出NaN
Input
int i = 1;
System.out.println(i / 0);
2
Output
Exception in thread "main" java.lang.ArithmeticException: / by zero
at Application.main(Application.java:4)
2
Input
int i = 1;
System.out.println(i / 0.0);
2
Output
Infinity
Input
int i = 1;
System.out.println(i / -0.0);
2
Output
-Infinity
Input
int i = 0;
System.out.println(i / -0.0);
2
Output
NaN
- 取模运算,先按正数取余,再看左运算位符号判定结果符号
Input
System.out.println(3.2 % 2);
Output
1.2
Input
System.out.println(5.4 % 1.7);
Output
0.3000000000000005
- 小数可以当做取模运算的操作数
++
/--
运算在变量名之前,表示先自增,自增完成之后才会参与运算。在之后则先拿原值参与运算,之后再自增
Input
int i = 5;
int j = i++;
2
取出i
中的5 → i
自增 → 将5赋值给j
Input
int i = 5;
int j = i++ * 2;
System.out.println("i = " + i);
System.out.println("j = " + j);
2
3
4
5
Output
i = 6
j = 10
2
取出i
中的5 → 5和2相乘得10 → i
自增 → 将10赋值给j
Input
int i = 5;
int j = ++i * 2;
System.out.println("i = " + i);
System.out.println("j = " + j);
2
3
4
5
Output
i = 6
j = 12
2
取出i
中的5 → i
自增(6) → 将6和2相乘得12 → 将12赋值给j
Input
int i = 3;
int j = i++ + ++i;
System.out.println("i = " + i);
System.out.println("j = " + j);
2
3
4
5
Output
i = 5
j = 8
2
取出i
中的3 → i
自增(4) → i
自增(5) → 3+5得8 → 将8赋值给j
Input
int i = 3;
int j = ++i + i++;
System.out.println("i = " + i);
System.out.println("j = " + j);
2
3
4
5
Output
i = 5
j = 8
2
取出i
中的3 → i
自增(4) → i
自增(5) → 将4和自增前的4相加得8 → 将8赋值给j
byte
和short
类型可以参与++
/--
运算,运算结束后保持原类型(底层进行了强制转换)
# 2.4.2 赋值运算符
包括=
、+=
、-=
、*=
、/=
、%=
、|=
、&=
、^=
、<<=
、>>=
和>>>=
.
注意
- 除
=
以外,其余赋值运算符要求变量必须有值 byte
/short
类型可以参与赋值运算
Bonus
判断以下魔法程序的输出值
- Input
int i = 5; System.out.println(i += i -= i *= 5);
1
2 - Input
int i = 3; System.out.println(i += i *= i -= i++);
1
2 - Input
int i = 5; System.out.println(i += i -= i *= i++);
1
2 - Input
int i = 5; System.out.println(i += i -= i *= i++ + ++i);
1
2
Output
-15
15 + (5 - (5 * 5)) = -15
Output
3
1int i = 3;
i += i *= i -= i++;
3 + (3 * (3 - 3)) = 3
Output
-15
15 + (5 - (5 * 5)) = -15
Output
-50
15 + (5 - (5 * (5 + 7))) = -50
# 2.4.3 关系运算符
或称“比较运算符”
包括>
、<
、>=
、<=
、==
、!=
# 2.4.4 逻辑运算符
或称“条件运算符”
包括&&
、||
、!
、&
、|
、^
和?:
特别的
- 运算符
?:
是一个三目运算符
语句result = someCondition ? value1 : value2;
表示:
“如果某条件
为真,则将值1
赋给结果
,否则将值2
赋给结果
”The Conditional Operators
...
Another conditional operator is?:
, which can be thought of as shorthand for anif-then-else
statement (discussed in the Control Flow Statements section of this lesson). This operator is also known as the ternary operator because it uses three operands. In the following example, this operator should be read as: "IfsomeCondition
istrue
, assign the value ofvalue1
to result. Otherwise, assign the value ofvalue2
to result."
...
原文 (opens new window) - 运算符
&&
和||
具有短路效果
# 2.4.5 位运算符
包括&
、|
、^
、~
、<<
、>>
和>>>
关于 & | ^
我们可以注意到,在逻辑运算符和位运算符中都出现了&
、|
、^
.
在官方文档 (opens new window)的表述中,&
、|
、^
三个运算符属于位运算符,而不是逻辑运算符,但在实践中我们发现两种具体情形下 JVM 都可以正确理解当前上下文这三个运算符的含义。故本文将三个运算符分别写进了两个分类。
Input
byte b1 = 0b0010101;
byte b2 = 0b1010101;
System.out.println(Integer.toBinaryString(b1 & b2));
boolean bb1 = true;
boolean bb2 = false;
System.out.println(bb1 & bb2);
2
3
4
5
6
7
Output
10101
false
2
注意
位运算针对的是整数,运算的是数据的补码
# 2.4.6 类型比较运算符
运算符instanceof
用来判断对象是否为类的实例
Input
System.out.println("abc" instanceof String);
Output
true
# 2.4.7 优先级
自上而下优先级逐渐递减。
特别的
运算符()
、[]
和.
虽不在表中,但享有最高的优先级。
# 2.5 表达式、语句和代码块
# 2.5.1 表达式
一个表达式由变量,运算符和方法调用组成,其具体组合方式由语法决定。
表达式的结果是一个值。
如代码int result = 1 + 2;
中,result = 1 + 2
就是一个表达式。
# 2.5.2 语句
语句是程序执行的最小单元。
在 Java 中一个语句由;
结束,下面这些表达式加上;
就可以构成一个语句:
- 声明表达式
- 使用
++
和--
- 方法调用
- 创建对象表达式
# 2.5.3 代码块
当把0到多个语句用括号包围时,这个结构就是一个代码块。
只要可以出现单独语句的地方都可以出现代码块。
详见代码块。
# 2.6 流程控制
# 2.6.1 顺序结构
结构化程序设计由迪杰斯特拉 (E.W.Dijikstra) 在 1965 年提出,其要求采用“自顶向下,逐步求精”的设计方法。
简单来讲,就是从上到下,从左到右,依次执行。
# 2.6.2 分支结构
if-then
语句
当条件
为真时,则执行{...}
中的语句。
格式:if (condition) { // some statements }
1
2
3特别的
当
then
的部分只有一条语句时,包裹其的大括号可以省略。if (condition) // a statement
1
2if-then-else
语句
当条件
为真时,执行{...}
中的内容,否则执行else {...}
中的内容。
格式:if (condition) { // some statements } else { // other statements }
1
2
3
4
5特别的
if-then-else
语句可以嵌套使用。
下面是一个简单的例子来实现“大于90分为A,大于80分为B……的成绩判断”程序。class IfElseDemo { public static void main(String[] args) { int testscore = 76; char grade; if (testscore >= 90) { grade = 'A'; } else if (testscore >= 80) { grade = 'B'; } else if (testscore >= 70) { grade = 'C'; } else if (testscore >= 60) { grade = 'D'; } else { grade = 'F'; } System.out.println("Grade = " + grade); } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20switch
语句
switch
语句可以拥有多个可执行的路径 (if
系列只有一个)。switch
语句可以对byte
,short
,char
和int
类型的数据进行判定。对
String
类进行判定Java SE 7.0 +。switch
还可以判定Enum
枚举类型和一些特定的包装类,如Character
,Byte
,Short
和Integer
.
下面是一个通过输入int
类型月份输入,由计算机输出对应单词的程序:
Input
public class SwitchDemo {
public static void main(String[] args) {
int month = 8;
String monthString;
switch (month) {
case 1: monthString = "January";
break;
case 2: monthString = "February";
break;
case 3: monthString = "March";
break;
case 4: monthString = "April";
break;
case 5: monthString = "May";
break;
case 6: monthString = "June";
break;
case 7: monthString = "July";
break;
case 8: monthString = "August";
break;
case 9: monthString = "September";
break;
case 10: monthString = "October";
break;
case 11: monthString = "November";
break;
case 12: monthString = "December";
break;
default: monthString = "Invalid month";
break;
}
System.out.println(monthString);
}
}
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
Output
August
注意
break;
语句用来跳出当前判定或循环。
所以我们可以知道当执行到某不含break;
的case
块时,判定仍将继续下去,直到所有case
均不满足或遇到break;
才退出操作。
Input
public class SwitchDemoFallThrough {
public static void main(String[] args) {
java.util.ArrayList<String> futureMonths =
new java.util.ArrayList<String>();
int month = 8;
switch (month) {
case 1: futureMonths.add("January");
case 2: futureMonths.add("February");
case 3: futureMonths.add("March");
case 4: futureMonths.add("April");
case 5: futureMonths.add("May");
case 6: futureMonths.add("June");
case 7: futureMonths.add("July");
case 8: futureMonths.add("August");
case 9: futureMonths.add("September");
case 10: futureMonths.add("October");
case 11: futureMonths.add("November");
case 12: futureMonths.add("December");
break;
default: break;
}
if (futureMonths.isEmpty()) {
System.out.println("Invalid month number");
} else {
for (String monthName : futureMonths) {
System.out.println(monthName);
}
}
}
}
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
Output
August
September
October
November
December
2
3
4
5
- 判定过程按
case
顺序依次判定,但若每个case
块内都含break;
语句则case
顺序不影响结果。
# 2.6.3 循环结构
while
语句
当条件
为真时,则持续执行{...}
中的语句。
格式:while (condition) { // some statements }
1
2
3do-while
语句
先执行一次循环体{...}
, 再进行条件
的判断。若条件
为真则持续执行循环体。
格式:do { // some statements } while (condition);
1
2
3for
语句
是一种重复迭代的循环语句,当不满足终止条件
时持续执行循环体。
格式:for (initialization; termination; increment) { // statements }
1
2
3注意
for
语句要求填写三部分:循环变量、终止条件和状态更新。三个部分可以为空,但其中的
;
需要留下。
增强的 for 循环
增强的 for 循环是一个语法糖,它使得
for
循环在某些场景更易读。
增强的 for 循环通常用在对集合类和数组遍历上,官方推荐在可以使用这种循环的地方都使用这种形式。
格式for (int item : someCollection) { // some statements }
1
2
3注意
在增强的 for 循环中,被循环的集合类或数组是一个新的“副本”。
所以在增强的 for 循环中对被循环变量进行更改操作不会生效。
特别的
while
和do-while
循环适用于循环次数不固定,变化不规律的场景。for
循环使用于变化规律的场景。
# 2.6.4 控制转移
break
语句
终止循环/switch.continue
语句
终止当前循环,执行下一次循环。
特别的
break
和continue
拥有两种形式:使用label
和不使用label
。
在多重循环下,一个label
标签可以更好地让程序跳转到该跳转的地方。
下面是两个实例,使用label
的地方用高亮标识出:
Input
class BreakWithLabelDemo {
public static void main(String[] args) {
int[][] arrayOfInts = {
{ 32, 87, 3, 589 },
{ 12, 1076, 2000, 8 },
{ 622, 127, 77, 955 }
};
int searchfor = 12;
int i;
int j = 0;
boolean foundIt = false;
search:
for (i = 0; i < arrayOfInts.length; i++) {
for (j = 0; j < arrayOfInts[i].length;
j++) {
if (arrayOfInts[i][j] == searchfor) {
foundIt = true;
break search;
}
}
}
if (foundIt) {
System.out.println("Found " + searchfor + " at " + i + ", " + j);
} else {
System.out.println(searchfor + " not in the array");
}
}
}
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
Output
Found 12 at 1, 0
Input
class ContinueWithLabelDemo {
public static void main(String[] args) {
String searchMe = "Look for a substring in me";
String substring = "sub";
boolean foundIt = false;
int max = searchMe.length() -
substring.length();
test:
for (int i = 0; i <= max; i++) {
int n = substring.length();
int j = i;
int k = 0;
while (n-- != 0) {
if (searchMe.charAt(j++) != substring.charAt(k++)) {
continue test;
}
}
foundIt = true;
break test;
}
System.out.println(foundIt ? "Found it" : "Didn't find it");
}
}
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
Output
Found it
return
语句
终止当前方法,方法的执行结果将会返回给调用该方法的地方。
# 2.7 数组
数组是用来存放一组相同数据类型的容器。
数组中每一项被称为元素。
每个数组自动的隐含了一个标识其长度的属性
length
,可以使用arrayName.length
表达式来获取。数组的下标从 0 开始,到其长度减 1 结束。
在内存中,数据部分存放在堆内存,并在栈内存存放一个指向该数据的引用。
# 2.7.1 声明
数组的声明包含两部分:数组的类型和数组的名称。
格式
1.
type[] arrayName;
type arrayName[];
# 2.7.2 创建
一个数组在声明时不会真正在堆内存分配空间,而只在栈内存生成了一个指向null
的地址引用。
我们使用new
关键字来创建数组。
格式
arrayName = new type[num];
下面是一个创建包含 10 个int
元素的数组的例子:
anArray = new int[10];
# 2.7.3 初始化
数组在创建完成后,程序会自动给该数组赋该数组类型的默认值,默认值详见数据类型。
我们可以手动对每个元素进行赋值:
anArray[0] = 100; // initialize first element
anArray[1] = 200; // initialize second element
anArray[2] = 300; // and so forth
2
3
合并声明、创建和初始化
以下两种缩写形式可以将数组的声明、创建和初始化合并在一条语句中。
1.
type[] arrayName = new type[]{e1, e2, ...};
type[] arrayName = {e1, e2, ...};
注意
数组一经创建,其长度即固定,不可改变。
# 2.7.4 多维数组
简单讲,多维数组是“数组的数组”。n 维数组的每一个元素都是 n-1 维数组。
下面是一个多维数组的例子:
Input
class MultiDimArrayDemo {
public static void main(String[] args) {
String[][] names = {
{"Mr. ", "Mrs. ", "Ms. "},
{"Smith", "Jones"}
};
// Mr. Smith
System.out.println(names[0][0] + names[1][0]);
// Ms. Jones
System.out.println(names[0][2] + names[1][1]);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
Output
Mr. Smith
Ms. Jones
2
# 2.7.5 使用
访问元素
使用arrayName[index]
表达式来访问某元素。数组复制
数组的名称仅仅标识了对数组数据的引用,所以下面的代码中,两个数组指向的是堆内存中同一个数组数据。int[] arr1 = {1, 2, 3}; int[] arr2 = arr1;
1
2Java 在
System
类中提供了arraycopy()
方法用来复制一个数组,其本质是在堆内存中生成一个新的内容相同的数组数据。
其具体参数是:System.arraycopy(src, srcPos, dest, destPos, length)
1可以将数组
src
中,从srcPos
开始的length
个元素复制到dest
数组的destPos
位置。