抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

摘要:本文学习了Java编程的基础知识。

环境

Windows 10 企业版 LTSC 21H2
Java 1.8

1 注释

支持多种注释方式:

  • 行注释:可以注释单行内容,使用//注释从当前位置到行结尾。
  • 块注释:可以注释多行内容,使用/**/注释两个符号之间的内容。
  • 文档注释:可以生成文档,使用/**标记文档开始,使用*/标记文档结束,每行开头使用*标记文档内容。

2 命名

2.1 标识符

为各种变量、方法、类和包等起的名字,统统称之为标识符。

命名规则:

  • 应以字母、下划线、美元符开头,不能以数字开头,不能使用空格或非法的字符,如:#,%,&等。
  • 后跟字母、下划线、美元符、数字。
  • 大小写敏感,长度无限制。
  • 不能使用关键字。

命名约定:

  • 类和接口的首字母大写,比如TestClass。
  • 方法的首字母小写,其余首字母大写,尽量少用下划线,比如setTime。这种命名方法叫做驼峰式命名。
  • 基本数据类型常量的全部字母都大写,字与字之间用下划线分隔,比如SIZE_NAME。对象常量可大小混写,比如Person。
  • 变量可大小写混写,首字母小写,字间分隔符的首字母大写。不用下划线,少用美元符号。

2.2 关键字

关键字对编译器有特殊意义,它们用来表示一种数据类型,或则表示程序的结构等,关键字不能用作变量名、方法名、类名、包名和参数名。

常见关键字:

分类 关键字 说明
保留字 const 其他计算机语言中的关键字,用于修改字段或局部变量的声明,指定字段或局部变量的值是常数,不能被修改。
goto 其他计算机语言中的关键字,指定跳转到标签,找到标签后,程序将处理从下一行开始的命令。
包相关 import 引入包的关键字。
package 定义包的关键字。
访问控制 public 公有的,可跨包使用。
protected 受保护的,只能在当前包内使用。
private 私有的,当前类可用。
异常处理 try 执行有可能抛出异常的代码块。
catch 捕获异常。
throw 抛出一个异常对象。
finally 有没有异常都执行的代码块。
throws 声明一个异常,可能被抛出。
实例相关 new 新建,用于创建类的实例。
this 本类,用于引用当前实例。
super 父类,用于引用当前实例所属类的父类。也成为超类
instanceof 实例,用于确定对象所属的类。
特殊值 true boolean类型的值,表示真。
false boolean类型的值,表示假。
null 空值。
数据类型 byte 字节型的数据。
char 字符型的数据。
boolean 布尔型的数据。
short 短整型的数据。
int 整型的数据。
float 浮点型的数据。
long 长整型的数据。
double 双精度的数据。
void 表示null类型或者无返回值。
流程控制 if 如果,指示有条件地执行代码块。条件的计算结果必须是布尔值。
else 否则,与if关键字结合使用。else子句是可选的,如果if条件为false,则执行该子句。
for 循环,用于指定一个在每次迭代结束前检查其条件的循环。
while 循环,用于指定一个只要条件为真就会重复的循环。
do 运行,用于指定一个在每次迭代结束时检查其条件的循环。
switch 观察,基于某个表达式,选择执行多个代码块中的某一个。
case 返回观察里的结果,用来标记switch语句中的每个分支。
default 默认,用来标记switch语句中的默认分支。
break 跳出,中断,关键字用于提前退出循环。
continue 继续,用来跳转到循环的下一个迭代。
return 返回,会导致方法返回到调用它的方法,从而传递与返回类型匹配的值。
类和接口 class 类,用来定义类。
interface 接口,用来定义接口。
extends 继承,声明类和接口继承的父类和父接口。
implements 实现,声明类实现的接口。
修饰 abstract 声明抽象,修饰类和方法。
final 终极,不可改变的,修饰类和方法。
static 静态,意味着应用它的实体在声明该实体的类的任何特定实例外部可用。
synchronized 线程,同步。
native 本地,表示该方法是用Java以外的语言实现的。
strictfp 严格,精准,声明一个类、接口或者方法,所声明的范围内会完全依照浮点规范执行。
transient 短暂,应用于类的成员变量,表示成员变量不应被序列化。
volatile 易失,表示可以被多个线程异步修改的成员变量。

3 变量

3.1 定义

一块内存中的数据存储空间,因为里面的数据可以更改,所以称为变量。

分类:

  • 局部变量:方法或语句块内部定义的变量,作用域是当前方法或当前语句块,需要在初始化时赋值,存在栈内存中。
  • 成员变量:方法外部或类的内部定义的变量,作用域是整个类,有默认值,存在堆内存中。

3.2 常量

常量是特殊的变量,常量存储的数据不可以更改。

4 数据类型

4.1 说明

Java语言是一种强类型语言。通俗点说,在Java中存储的数据都是有类型的,而且必须在编译时就确定其类型。

4.2 分类

分类:

  • 基本类型变量:保存原始值,它代表的值就是数值本身。通过值传递进行赋值。
  • 引用类型变量:保存引用值,指向内存空间的地址,代表了某个对象的引用,对象本身存放在这个引用值所表示的地址的位置。通过引用传递进行赋值。

图示:
20250424113358-分类

基本数据类型:

分类 类型 内容 默认值 大小 范围
布尔型 boolean true false false 1bit true false
字符型 char Unicode字符集 \u0000 16bits \u0000 ~ \uFFFF
整数类型 byte 带符号整数 0 8bits -2^7 ~ 2^7-1
short 带符号整数 0 16bits -2^15 ~ 2^15-1
int 带符号整数 0 32bits -2^31 ~ 2^31-1
long 带符号整数 0L 64bits -2^63 ~ 2^63-1
浮点类型 float IEEE 754标准的浮点数 0.0f 32bits -2^63 ~ 2^63-1
double IEEE 754标准的浮点数 0.0 64bits -2^63 ~ 2^63-1

4.2.1 布尔型

boolean类型表示1位的信息,只有true和false两个取值,这种类型只作为一种标志来记录true和false情况。

默认值是false。

4.2.2 字符型

char类型是一个单一的16位Unicode字符,单引号括起来的单个字符表达,通常用16进制表示。

最小值是\u0000(即为0),最大值是\uffff(即为65535),可以当整数来用,它的每一个字符都对应一个数字。

字符常量指用单引号括起来的单个字符,比如'a''A'。请特别注意,字符的定界符是单引号,而非双引号。

除了以上所述形式的字符常量值之外,还有一种特殊形式的字符常量值,这通常用于表示难以用一般字符来表示的字符,这种特殊形式的字符是以一个\开头的字符序列,称为转义字符。

常用转义字符:

转义字符 意义 ASCII码值(十进制)
\a 响铃(BEL) 007
\b 退格(BS),将当前位置移到前一列 008
\f 换页(FF),将当前位置移到下一页 012
\n 换行(LF),将当前位置移到下一行 010
\r 回车(CR),将当前位置移到本行开头 013
\t 水平制表(HT),跳到下一个TAB位置 009
\v 垂直制表(VT) 011
\\ 代表\反斜线字符 092
\' 代表'单引号字符 039
\" 代表"双引号字符 034
\0 空字符(NULL) 000

4.2.3 整数类型

4.2.3.1 byte

byte类型是8位的有符号的以二进制补码表示的整数,占1字节。

最小值是-128(-2^7),最大值是127(2^7-1),默认值是0。

4.2.3.2 short

short类型是16位的有符号的以二进制补码表示的整数,占2字节。

最小值是-32768(-2^15),最大值是32767(2^15-1),默认值是0。

4.2.3.3 int

int类型是32位的有符号的以二进制补码表示的整数,占4字节。

最小值是-2^31,最大值是2^31-1,默认值是0。

4.2.3.4 long

long类型是64位的有符号的以二进制补码表示的整数,占8字节。

最小值是-2^63,最大值是2^63-1,默认值是0L。

4.2.4 浮点类型

4.2.4.1 float

float类型是32位的单精度的符合IEEE 754标准的浮点数,占4字节。

默认值是0.0F。

4.2.4.2 double

double类型是64位的双精度的符合IEEE 754标准的浮点数,占8字节。

默认值是0.0D,浮点数的默认类型为double类型。

4.3 类型转换

字符型、整型、浮点型的数据在混合运算中相互转换,遵循以下规则:

  • boolean类型不可以转换为其他的数据类型。
  • 容量小的类型自动转换为容量大的类型,顺序为:
    • byte/short/char int long float double
    • byte/short/char之间不会相互转换,他们在计算时会首先转为int类型。
  • 容量大的数据类型转为容量小的数据类型时,要加上强制转换符,但可能造成精度降低或溢出。
  • 有多种类型的数据类型混合运算时,首先自动将所有的数据转换成容量最大的数据类型,然后在进行计算。

5 运算符

5.1 说明

运算符用于执行程序代码运算,会针对一个以上操作数项目来进行运算。

支持如下运算符:

  • 算术运算符:++,–,+,-,*,/,%。
  • 赋值运算符:=,+=,-=,*=,/=,%=。
  • 关系运算符:>,<,>=,<=,==,!=。
  • 逻辑运算符:&,|,!,^,&&,||。
  • 条件运算符:?:。
  • 位运算符:&,|,~,^,>>,<<,>>>。

优先级如下图所示:

优先级 运算符 名称 结合性
1 . 从左到右
() 圆括号 从左到右
[] 方括号 从左到右
2 + 取正 从右到左
- 取负 从右到左
++ 自增 从右到左
-- 自减 从右到左
~ 按位非 从右到左
! 逻辑非 从右到左
3 * 相乘 从左到右
/ 相除 从左到右
% 取余 从左到右
4 + 相加 从左到右
- 相减 从左到右
5 << 带符号左移 从左到右
>> 带符号右移 从左到右
>>> 无符号右移 从左到右
6 < 小于 从左到右
<= 小于等于 从左到右
> 大于 从左到右
>= 大于等于 从左到右
instanceof 是否属于类 从左到右
7 == 等于 从左到右
!= 不等于 从左到右
8 & 逻辑与(按位与) 从左到右
9 | 逻辑或(按位或) 从左到右
10 ^ 逻辑异或(按位异或) 从左到右
11 && 短路与 从左到右
12 || 短路或 从左到右
13 ?: 条件运算符 从右到左
14 = 将右侧的值赋给左侧的变量 从右到左
+= 左侧的变量加右侧的值取和,将和赋给左侧的变量 从右到左
-= 左侧的变量减右侧的值取差,将差赋给左侧的变量 从右到左
*= 左侧的变量乘右侧的值取积,将积赋给左侧的变量 从右到左
/= 左侧的变量除右侧的值取商,将商赋给左侧的变量 从右到左
%= 左侧的变量除右侧的值取余,将余赋给左侧的变量 从右到左

5.2 分类

5.2.1 算术运算符

算术运算符主要用于进行基本的算术运算,如加法、减法、乘法、除法等。

分类:

  • 单目运算符主要有+(取正),-(取负),++(自增),–(自减)。
  • 双目运算符主要有+(相加),-(相减),*(相乘),/(相除),%(取余)。

5.2.2 自增和自减

只能用来操作变量,不能用来操作数值或者常量。

自增(++)或自减(–)在变量后面,会先赋值,然后再计算:

java
1
2
3
4
5
6
public static void main(String [] args) {
int a = 0;
int b = a++;
System.out.println("a = " + a);// a = 1
System.out.println("b = " + b);// b = 0
}

自增(++)或自减(–)在变量前面,会先计算,然后再赋值:

java
1
2
3
4
5
6
public static void main(String [] args) {
int a = 0;
int b = ++a;
System.out.println("a = " + a);// a = 1
System.out.println("b = " + b);// b = 1
}

5.2.3 赋值运算符

赋值运算符是指为变量或常量指定数值的符号,如可以使用=将右边的表达式结果赋给左边的操作数。

说明:

  • a += b等价于a = a + b
  • a -= b等价于a = a - b
  • a *= b等价于a = a * b
  • a /= b等价于a = a / b
  • a %= b等价于a = a % b

5.2.4 关系运算符

比较运算符用于判断两个数据的大小,如大于、等于、不等于,比较的结果是一个布尔值。

注意不要对浮点数进行相等性运算,比如1.0 - 0.3 == 0.7

注意:

  • 使用> < >= <=只支持左右两边操作数是数值类型。
  • 使用== !=两边的操作数既可以是数值类型,也可以是引用类型。

比较equals和==的区别:

变量类型 存储位置 == equals
值类型 比较值是否相等 无(值类型通常使用==比较)
引用类型 栈中存储地址,对象存储在堆中 比较地址是否相同(栈中内容是否相同) 判断对象的内容是否相同

说明:

  • ==比较的是地址,equals比较的是内容。当equals为true时,==不一定为true。

5.2.5 逻辑运算符

逻辑运算符主要用于进行逻辑运算:

  • 逻辑与(&):只有当两边都为真,结果才为真,如果有一边为假,结果为假。
  • 逻辑或(|):如果有一边为真,结果为真,只有当两边都为假,结果才为假。
  • 逻辑非(!):当右边为假,结果为真,当右边为真,结果为假。
  • 逻辑异或(^):当两边真假不一致,结果才为真,两边同为真或两边同为假,结果均为假。
  • 短路与(&&):当左边为假,则不再判断右边,直接得出结果为假。
  • 短路或(||):当左边为真,则不再判断右边,直接得出结果为真。

5.2.6 条件运算符

条件运算符(?:)也称为三元运算符。

语法:

java
1
布尔表达式 ? 表达式1 : 表达式2;

运算过程:

  • 如果布尔表达式的值为真,则返回表达式1的值。
  • 如果布尔表达式的值为假,则返回表达式2的值。

5.2.7 位运算符

主要有按位与(&),按位或(|),按位非(~),按位异或(^),带符号左移(<<),带符号右移(>>),无符号右移(>>>)。

位运算是以二进制位为单位进行的运算,其操作数和运算结果都是整型值。

位运算与逻辑运算的相应操作的真值表完全相同,其差别只是位运算操作的操作数和运算结果都是二进制整数,而逻辑运算相应操作的操作数和运算结果都是逻辑值布尔型。

移位运算符:

  • 带符号左移(<<)是将一个二进制数按指定移动的位数向左移位,移掉的被丢弃,右边移进的部分正数补0负数补1。将一个数带符号左移会使该值乘以2的幂。
  • 带符号右移(>>)是将一个二进制数按指定移动的位数向右移位,移掉的被丢弃,左边移进的部分正数补0负数补1。将一个数带符号右移会使该值除以2的幂。
  • 无符号右移(>>>)是将一个二进制数按指定移动的位数向右移位,左边移进的部分始终补0,所以永远不会产生负号。将一个数无符号右移会使该值除以2的幂,如果是负数,可能会得到一个很大的正数。

6 进制

6.1 介绍

二进制的前缀是0B或0b,范围是0-1

八进制的前缀是0,范围是0-7

十进制不需要前缀,范围是0-9

十六进制的前缀是0X或0x,范围是0-9 A-F或者0-9 a-f

6.2 转换

6.2.1 其他进制转换为十进制

6.2.1.1 二进制转换为十进制

从右往左开始计算,底数为2,指数从0开始依次递增,最后结果累加。

二进制数字0111转为十进制的计算逻辑:

java
1
1 * 2 ^ 0 + 1 * 2 ^ 1 + 1 * 2 ^ 2 + 0 * 2 ^ 3 = 7
6.2.1.2 八进制转换为十进制

从右往左开始计算,底数为8,指数从0开始依次递增,最后结果累加。

八进制数字017转为十进制的计算逻辑:

java
1
7 * 8 ^ 0 + 1 * 8 ^ 1 = 15
6.2.1.3 十六进制转换为十进制

从右往左开始计算,底数为16,指数从0开始依次递增,最后结果累加。

十六进制数字0x2c转为十进制的计算逻辑:

java
1
12 * 16 ^ 0 + 2 * 16 ^ 1 = 44

6.2.2 十进制转换为其他进制

6.2.2.1 十进制转换为二进制

一直除以2,求出每次的余数,直到商为0为止,最后余数倒过来就是二进制数。

十进制数字10转二进制数字为1010

6.2.2.2 十进制转换为八进制

一直除以8,求出每次的余数,直到商为0为止,最后余数倒过来就是八进制数。

十进制数字10转八进制数字为012

6.2.2.3 十进制转换为十六进制

一直除以16,求出每次的余数,直到商为0为止,最后余数倒过来就是十六进制数。

十进制数字10转十六进制数字为A

6.2.3 其他进制转换为二进制

6.2.3.1 八进制转换为二进制

从最低位开始算,每一位转换一个三位的二进制数,最后拼成一个最终的二进制数。

八进制数字0136转二进制数字为000 001 011 110

6.2.3.2 十六进制转换为二进制

从最低位开始算,每一位转换成一个四位的二进制数,最后拼成一个最终的二进制数。

十六进制数字5E转二进制数字为0101 1110

6.2.4 二进制转换为其他进制

6.2.4.1 二进制转换为八进制

从最低位开始算,每三位转换成一个十进制数,最后拼成一个八进制数。

二进制数字000 001 011 110转八进制数字为0136

6.2.4.2 二进制转换为十六进制

从最低位开始算,每四位转换成一个十进制数,最后拼成一个十六进制数。

二进制数字0101 1110转十六进制数字为5E

6.3 码制

相关概念:

  • 原码:人最容易看得懂和理解的一种形式,进制转换都是原码。
  • 反码:计算机为了让符号位参与运算,所以有了反码。
  • 补码:计算机为了解决0的符号问题,所以有了补码。
  • 移码:不管正负数,只要将其补码的符号位取反即可得到移码。

特点:

  • 正数:正数的原码、反码、补码都一样。
  • 负数:负数的反码符号位不变,其他位取反。负数对的补码符号位不变,在反码基础上加1。

7 流程控制

流程控制指的是在程序运行的过程中控制程序运行走向的方式。主要分为以下三种:

  • 顺序结构:从上到下依次执行每条语句操作。
  • 分支结构:根据条件判断执行哪些语句操作。
  • 循环结构:根据循环初始条件和终结要求,执行循环体内的操作。

7.1 分支结构

分支结构有if分支结构和switch分支结构:

  • if分支结构使用布尔表达式或者布尔值进行判断来进行分支的控制。
  • switch分支结构使用整数、String类型、枚举类型进行判断来进行分支的控制。

分支的各种情况要满足不重复,不遗漏的原则。相互独立,完全穷尽(MECE,Mutually Exclusive Collectively Exhaustive)。

7.1.1 if分支结构

7.1.1.1 if语句

语法:

java
1
2
3
if (条件) {
语句;
}

说明:

  • 如果条件成立,执行大括号中的语句。
  • 如果条件不成立,跳过大括号,执行后面的语句。

判断一个数字是否是正数:

java
1
2
3
4
int i = 1;
if (i > 0) {
System.out.println(i + "是正数!");
}
7.1.1.2 if … else语句

语法:

java
1
2
3
4
5
if (条件) {
语句;
} else {
语句;
}

说明:

  • 如果条件成立,执行大括号中的语句。
  • 如果条件不成立,执行else后面大括号中的语句。

判断成绩是否及格:

java
1
2
3
4
5
6
int i = 58;
if (i >= 60) {
System.out.println("成绩及格了!");
} else {
System.out.println("成绩不及格!");
}
7.1.1.3 if … else … if语句

语法:

java
1
2
3
4
5
6
7
if (条件) {
语句;
} else if (条件) {
语句;
} else {
语句;
}

说明:

  • 如果第一个条件成立,执行第一个条件后面大括号里的语句。
  • 如果第一个条件不成立,判断下一个条件,依次类推。
  • 如果所有条件都不成立,执行else中的语句。

判断成绩级别:

java
1
2
3
4
5
6
7
8
9
10
11
12
int i = 78;
if (i == 100) {
System.out.println("满分!");
} else if (i >= 90) {
System.out.println("优秀!");
} else if (i >= 80) {
System.out.println("良好!");
} else if (i >= 60) {
System.out.println("及格!");
} else {
System.out.println("不及格!");
}

7.1.2 switch分支结构

语法:

java
1
2
3
4
5
6
7
8
9
10
11
switch (表达式) {
case 常量:
语句;
break;
case 常量:
语句;
break;
default:
语句;
break;
}

说明:

  • 拿着变量或表达式得到的值,去和各个case后面常量的值做判断,看是否匹配,
  • 如果有匹配,则执行当前常量后面的语句,遇见break则跳出匹配,否则继续匹配。
  • 如果没有匹配,则执行default后面的语句。

判断性别:

java
1
2
3
4
5
6
7
8
9
10
11
12
int sex = 1;
switch (sex) {
case 1:
System.out.println("性别男!");
break;
case 2:
System.out.println("性别女!");
break;
default:
System.out.println("无性别!");
break;
}

注意:

  • switch只能做等值判断,支持基本类型的byte、short、int、char,在JDK1.7以后还支持引用类型的String和Enum。
  • case后的常量值不可以重复。
  • default可以省略,而且default位置也可以提到前面,只是不建议。
  • break可以省略。

7.1.3 对比

相同点:

  • 都可以实现分支结构。

不同点:

  • 能用switch解决的题目肯定能用if解决,switch结构的效率比if结构的效率高。
  • 能用if解决的题目不一定可以用switch解决,switch只能处理等值的条件判断,且条件是整型变量或字符变量的等值判断。

7.2 循环结构

循环语句也称作迭代语句,循环语句可以在满足条件的情况下反复执行某一段代码。

7.2.1 while循环结构

语法:

java
1
2
3
while (条件) {
语句;
}

说明:

  • 先判断条件是否满足,如果不满足则跳出循环,如果满足则执行语句。
  • 然后再次判断条件是否满足,直到条件不满足或跳出循环。

循环打印从0到10的数字:

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

7.2.2 do … while循环结构

语法:

java
1
2
3
do {
语句;
} while (条件);

说明:

  • 先执行一次语句,然后判断条件是否满足,如果不满足则跳出循环,如果满足则执行循环语句。
  • 继续判断条件是否满足,直到条件不满足或跳出循环。

循环打印从0到10的数字:

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

7.2.3 for循环结构

语法:

java
1
2
3
for (初始化表达式; 条件; 变量更新) {
语句;
}

说明:

  • 先执行一次初始化表达式,然后判断条件,如果不满足则跳出循环,如果满足则执行语句,然后执行变量更新。
  • 判断条件是否满足,不满足则跳出,满足则执行语句和变量更新,直到条件不满足或跳出循环。

循环打印从0到10的数字:

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

注意事项:

  • 两个分号必不可少。
  • 三个表达式可以不写,但有可能造成死循环。
  • 表达式1可以有多个变量初始化,要用逗号隔开。
  • 表达式3可以有多个变量更新,要用逗号隔开。

7.2.4 对比

相同点:

  • 都具备循环的四要素:循环变量初始化,循环条件,循环操作(循环体),循环变量更新(迭代)。

不同点:

  • 语法不同。
  • 执行顺序不同,while循环和for循环都是先判断后执行,do-while是先执行后判断。
  • 执行效率不同,do-while的执行效率最高。

7.3 跳转结构

跳转语句用于实现循环执行过程中程序流程的跳转。

7.3.1 break语句

在switch条件语句和循环语句中都可以使用break语句。

当它出现在switch条件语句中时,作用是终止某个case并跳出switch结构。

当它出现在循环语句中,作用是跳出循环语句,执行后面的代码。

循环打印从0到10的数字,要求当要打印6时停止循环:

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

当条件判断i为6时,循环终止,不再打印6和之后的数字。

7.3.2 continue语句

只能用在循环语句中,它的作用是跳过本次循环,执行下一次循环。

循环打印从0到10的数字,要求当要打印6时跳过,继续打印剩下的:

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

当条件判断i为6时,循环跳过,不再打印6,继续打印之后的数字。

7.3.3 return语句

用于跳出循环结构。

8 数组

8.1 定义

数组可以理解成保存一组数的容器,而变量可以理解为保存一个数的容器。

数组是一种引用类型,用于保存一组相同类型的数据。

数组定义的类型可以为任意类型,可以定义int类型的数组,也可以定义String类型的数组,或其他任何类型的数组。

数组实际在内存的存储和基本类型不同,实际上是在堆中申请空间保存一组数,栈中只是有一个地址的引用。

8.2 使用

8.2.1 声明

语法:

java
1
2
类型[] 数组名;
类型 数组名[];

示例:

java
1
2
int[] sum;
int color[];

此时只是声明了一个数组变量,并没有在内存中开辟空间。

8.2.2 开辟空间

语法:

java
1
数组名 = new 类型[长度];

示例:

java
1
sum = new int[5];

数组开辟空间时,必须要设置开辟的空间长度。

这时会根据类型对数组里的数据设置默认值。

8.2.3 赋值

语法:

java
1
数组名[下标] = 值;

示例:

java
1
sum[0] = 10;

除了上面通过下标赋值,还可以在声明时开辟空间并赋值,或者在开辟空间时赋值。

语法:

java
1
2
数组名 = new 类型[]{值, 值};
类型[] 数组名 = {值, 值};

示例:

java
1
2
int[] sum = {1, 2};
sum = new int[]{1, 2, 3};

在对数组进行赋值时,注意下标不要超过数组的长度范围,否则会产生错误。

在开辟空间时赋值,不需要在中括号里面填写数组长度。

8.2.4 访问元素

语法:

java
1
数组名[下标]

示例:

java
1
System.out.println(sum[0]);

同样需要注意下标不要超过数组的长度范围。

8.2.5 注意

数组不赋值也有默认值。

数组中每个元素的赋值和访问都是通过数组名[下标]实现的,不能直接使用数组名。

通过数组名.length获取数组的长度,经常用在循环中,提高代码的维护性。

数组的下标从0数组名.length - 1,如果不是这个范围,会导致一个数组下标越界的异常(ArrayIndexOutOfBoundsException)。

数组没有开辟空间直接使用,会导致一个空指针异常(NullPointerException)。

8.3 进阶使用

8.3.1 复制

8.3.1.1 复制引用地址

引用类型的数组复制的是引用地址,二者共同引用一个地址,其中一个对数组做了更改,都会影响另一个。

示例:

java
1
2
3
4
5
6
int[] a1 = {1, 2, 3};
int[] a2;
a2 = a1;
System.out.println(a2[0]);// 1
a1[0] = 100;
System.out.println(a2[0]);// 100
8.3.1.2 复制数值内容

基本类型的数组复制的是数值内容,实际上是一个备份,其中一个对它更改,不会影响另一个。

示例:

java
1
2
3
4
5
6
7
8
int[] a1={1, 7, 8};
int[] a2 = new int[a1.length];
for (int i = 0; i < a1.length; i++) {
a2[i] = a1[i];
}
System.out.println(a2[0]);// 1
a2[1]=100;
System.out.println(a2[0]);// 1

8.8.2 排序

示例:

java
1
2
3
4
5
6
7
8
9
10
int[] nums = {1, 9, 3, 5, 2, 8, 4};
for(int i = 0; i < nums.length - 1; i++) {// 控制轮数
for(int j = 0; j < nums.length - 1 - i; j++) {
if (nums[j] < nums[j + 1]) {// 交换
int temp = nums[j];
nums[j] = nums[j + 1];
nums[j + 1] = temp;
}
}
}

8.8.3 倒置

示例:

java
1
2
3
4
5
6
int[] nums = {1, 9, 3, 5, 2, 8, 4};
for(int i = 0; i < nums.length / 2; i++) {
int temp = nums[i];
nums[i] = nums[nums.length - 1 - i];
nums[nums.length - 1 - i] = temp;
}

8.4 工具类

排序:

java
1
Arrays.sort(nums)

填充:

java
1
Arrays.fill(nums, 1)

比较:

java
1
Arrays.equals(a1, a2)

转字符串:

java
1
Arrays.toString(nums)

8.5 二维数组

二维数组可以看做是一个数组,里面存放的元素是一维数组。

声明:

java
1
2
3
int[][] nums;
int nums[][];
int[] nums[];

开辟空间:

java
1
2
nums = new int[3][];// 行固定,列不固定
nums = new int[3][2];// 行固定,列固定

数组赋值:

  • 动态初始化,使用时赋值,需要指明行和列:
    java
    1
    nums[0][1] = 1;
  • 静态初始化,声明时赋值,不必也不能指明行和列,否则会提示错误:
    java
    1
    2
    int[][] nums = new int[][]{{1}, {9, 3, 5, 2}, {7, 6}};
    int[][] nums = {{1}, {9, 3, 5, 2}, {7, 6}};

说明:

  • 如果在动态初始化时明确了二维数组的行数和列数,那么一维数组的长度就是固定的列数。
  • 如果在动态初始化时仅仅明确了行数,或者通过静态初始化赋值,那么一维数组可以有不同的长度。

8.6 排序算法

8.6.1 冒泡排序

思路:

  • 属于交换排序。
  • 从第一个元素开始,比较两个相邻的元素,如果两个元素位置不正确,则进行交换,否则继续比较下面相邻的两个元素。

示例:

java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public void test() {
int[] arr = new int[]{1, 7, 3, 8, 5};
int len = arr.length;
for (int i = 0; i < len - 1; i++) {
for (int j = 0; j < len - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}

8.6.2 快速排序

思路:

  • 属于交换排序。
  • 任取序列的某个元素作为界点,将序列分为两个子序列,左子序列的元素小于界点,右子序列的元素大于界点,然后递归每个子序列,直到每个子序列只有一个元素。

示例:

java
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
public void test() {
int[] arr = new int[]{8, 3, 11, 4, 9, 5, 1};
sort(0, arr.length - 1, arr);
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
public void sort(int left, int right, int[] arr){
int l = left;
int r = right;
int pivot = arr[(left + right)/2];// 找中间值
int temp = 0;
while(l < r){
while(arr[l] < pivot) l++;
while(arr[r] > pivot) r--;
if(l >= r) break;
temp = arr[l];
arr[l] = arr[r];
arr[r] = temp;
if(arr[l] == pivot) --r;
if(arr[r] == pivot) ++l;
}
if(l == r){
l++;
r--;
}
if(left < r) sort(left, r, arr);
if(right > l) sort(l, right, arr);
}

8.6.3 直接选择排序

思路:

  • 属于选择排序。
  • 每次都选取最小值放在起始位上,一共通过n-1次。

示例:

java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public void test() {
int[] arr = new int[]{3, 2, 6, 10, 1, 9, 8};
for (int i = 0; i < arr.length - 1; i++) {
int index = i;
for (int j = i + 1; j < arr.length; j++) {
if (arr[index] > arr[j]) {
index = j;
}
}
int temp = arr[i];
arr[i] = arr[index];
arr[index] = temp;
}
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}

评论