引言
本篇博文为 Java 基础阶段的常见概念及相关细节梳理,意在重学 Java 查漏补缺。
博文随时会进行更新,补充新的内容并修正错漏,该系列博文旨在帮助自己巩固扎实 Java 基础。
毕竟万丈高楼,基础为重,借此督促自己时常温习回顾。
计算机基础
存储单位
基本换算:1MB = 1024KB、1KB = 1024B、1B = 1byte = 8bit
bit(比特、位):计算机中的最小存储单位
byte(字节):计算机中基本存储单元
Java 基本常识
.java 源文件编程规则
一个 .java 文件中可以有多个 class 但 public 修饰的 class 只能有一个
JRE、JDK、JVM 之间的关系
- JRE = JVM + Java 核心类库
- JDK = JRE + Java 开发工具(javac、java、javadoc、…)
编译与运行
- javac 编译 .java 源代码文件为 .class 字节码文件
- java 运行 .class 字节码文件
- javac A.java (生成 A.class)
- java A (运行 A.class,不需要加 .class 后缀)
Java 基础
注释
单行注释:
// 单行注释 |
多行注释:
/* |
文档注释:
/** |
Java 关键字与保留字
命名时要避免使用关键字与保留字
关键字: 被 Java 赋予特殊作用的单词。关键字中所有字母均小写
- 用于定义数据类型的关键字:class、interface、enum、byte、short、int、long、float、double、char、boolean、void
- 用于定义流程控制的关键字:if、else、switch、case、default、while、do、for、break、continue、return
- 用于定义访问权限修饰符的关键字:private、(缺省)、protected、public
- 用于定义类、方法(函数)、变量修饰符的关键字:abstract、final、static、synchronized
- 用于定义类与类之间关系的关键字:extends、implements
- 用于定义建立实例及引用实例,判断实例的关键字:new、this、super、instanceof
- 用于异常处理的关键字:try、catch、finally、throw、throws
- 用于包的关键字:package、import
- 其他修饰关键字:native、strictfp、transient、volatile、assert
- *用于定义数据类型值的字面值:true、false、null
保留字:Java 现版本尚未使用,但以后版本可能会成为关键字使用:goto、const
标识符
Java 对各种变量、方法和类等要素命名时使用的字符序列称为标识符(凡是可以自己起名字的地方都叫做标识符)
定义合法标识符的规则:
- 由数字、字母、下划线( _ )、或 $ 组成
- 不可以用数字开头
- 不可以使用关键字和保留字,但可以包含关键字和保留字
- 严格区分大小写,长度无限制
- 不能包含空格
- 见名知义
标识符命名规范:
- 包名:多单词组成时所有字母均小写(aabbcc)
- 类名、接口名:多单词组成时,所有单词首字母大写(AaBbCc)
- 变量名、方法名:多单词组成时,第一个单词首字母小写,其余单词首字母大写(aaBbCc)
- 常量名:所有字母均大写,多单词组成时每个单词使用下划线( _ )连接(AA_BB_CC)
变量
- 变量用于表示特定类型的数据
- 变量声明告知编译器根据数据类型为变量分配合适的内存空间
- 一个变量在使用前必须被声明和初始化
- 方法中声明的变量在使用之前必须被赋值
注意
- Java 中每个变量必须先声明后使用
- 使用变量名来访问这块区域的数据
- 变量的作用域:其定义所在的一对 {} 内
- 变量只有在其作用域内才有效
- 同一个作用域内,不能定义重名的变量(变量不可以重复声明)
分类(按数据类型)
Java 对于每一种数据都定义了明确的具体数据类型(强类型语言),在内存中分配了不同大小的内存空间
- 基本数据类型:
- 数值型(整数类型(byte、short、int、long)、浮点类型(float、double))
- 字符型(char)
- 布尔型(boolean)
- 引用数据类型:
- 类(class)
- 接口(interface)
- 数组array([])
分类(按变量在类中声明的位置)
- 成员变量:实例变量(不以 static 修饰)、类变量(以 static 修饰)
- 局部变量:形参(方法、构造器中定义的变量)、方法局部变量(在方法内定义)、代码块局部变量(在代码块内定义)
基本数据类型
整型
Java 各整数类型有固定的表数范围和字段长度,不受具体的操作系统(OS)的影响,以保证 Java 程序的可移植性
Java 的整型常量默认为 int 型,声明 long 型常量后必须加 l 或 L 后缀(建议使用 L 便于辨识)
Java 程序中变量通常声明为 int 型,除非不足以表示较大的数,才使用 long
类型 | 占用存储空间 | 表数范围 |
---|---|---|
byte | 1字节=8比特 | -128 ~ 127 (-2^8 ~ 2^8 - 1) |
short | 2字节 | -2^(2*8-1) ~ 2^(2*8-1) - 1 |
int | 4字节 | -2^(4*8-1) ~ 2^(4*8-1) - 1 |
long | 8字节 | -2^(8*8-1) ~ 2^(8*8-1) - 1 |
整型数据类型表数范围为:-2^(占用字节数 * 8 - 1) ~ 2^(占用字节数 * 8 - 1) - 1
数值直接量:为了提高可读性,Java允许在数值直接量的两个数字间使用下划线
long ssn = 123_456_789; |
浮点类型
同整型,Java 各浮点类型也有固定的表数范围与字段长度,不受具体操作系统影响
Java 的浮点型常量默认为 double 型,声明 float 型常量必须加后缀 f 或 F
- float(单精度):尾数可以精确到
7
位有效数字(很多情况下精度很难满足需求) - double(双精度):精度是 float 的两倍(通常采用此类型)
浮点型常量两种表示形式:
- 十进制数形式:
3.14
、6.28F
、.1256
(必须有小数点) - 科学计数法形式:
3.14E2
、6.28e2
、1256E-4
类型 | 占用存储空间 | 表数范围 |
---|---|---|
float | 4字节 | -3.403E38 ~ 3.403E38 |
double | 8字节 | -1.798E308 ~ 1.798E308 |
字符类型(char)
char 型数据用来表示通常意义上的字符(2
字节)
Java 中的所有字符都使用 Unicode 编码,因此一个字符可以存储一个字母、一个汉字或其他书面语的一个字符
字符型变量的三种表现形式:
- 字符常量是通过单引号(‘’)括起来的单个字符 (char a = ‘a’)
- 直接使用 Unicode 值来表示字符型常量 (‘\uXXXX’,XXXX 代表一个十六进制整数)、(‘\u000a’ 表示 ‘\n’)
- Java 中允许使用转义字符 ‘\‘ 来将其后的字符转变为特殊字符型常量
转义字符 | 含义 |
---|---|
‘\b’ | (退格符) |
‘\n’ | (换行符) |
‘\r’ | (回车符) |
‘\t’ | (制表符) |
‘\‘’ | (单引号) |
‘\“‘ | (双引号) |
‘\\‘ | (反斜杠) |
char 类型是可以进行运算的,因为它都对应有 Unicode 码
ASCII 码
在计算机内部,所有数据都使用二进制表示。每一个二进制位(bit)有 0 和 1 两种状态,因此 8 个二进制位就可以组合出 256 种状态,这被称为一个字节(byte)。一个字节一共可以用来表示 256 种不同的状态,每一个状态对应一个符号,就是 256 个符号,从 00000000 到 11111111
ASCII 码:由美国制定的一套字符编码,对英语字符与二进制位之间的关系做了统一规定。ASCII 码一共规定了 128 个字符的编码
空格(‘SPACE’ 是 32(00100000),’A’ 是 65(01000001),’a’ 是 97)
这 128 个符号(包括 32 个不能打印出来的控制符号),只占用了一个字节的后面 7 位,最前面的 1 位统一规定为 0
布尔型(boolean)
只包含 true 和 false
基本数据类型转换
自动类型转换:容量小的类型自动转换为容量大的数据类型。
强制类型转换:将容量大的数据类型强制转换为容量小的数据类型,可能导致精度降低或溢出
- 通常字符串不能直接转换为基本类型,但通过基本类型对应的包装类则可以实现把字符串转换成基本类型
String a = "65";
int i = Integer.parseInt(a);- 强制类型转换格式:强转类型 变量 = (强转类型)原变量;
long longType = 100L;
int intType = (int)longType;有多种类型的数据混合运算时,系统首先自动将所有数据转换成容量最大的那种数据类型,然后再进行计算
byte、short、char 之间不会相互转换,它们三者在计算时首先转换为 int 类型(因为三者之间转换很容易超出表数范围)
boolean 类型不能与其他数据类型运算,不可以转换为其他数据类型
当把任何基本数据类型的值和字符串(String)进行连接运算时( + ),基本数据类型的值将自动转化为字符串(String)类型
进制
所有数字在计算机底层都以二进制形式存在
对于整数,有四种表示方式:
- 二进制(binary):0、1,满 2 进 1,以 0b 或 0B 开头表示
- 十进制(decimal):0-9,满 10 进 1
- 八进制(octal):0-7,满 8 进 1,以数字 0 开头表示
- 十六进制(hex):0-9及A-F,满 16 进 1,以 0x 或 0X 开头表示(此处 A-F 不区分大小写)
进制转换:
- 十进制转其他进制:十进制数除以其他进制取每次余数的倒序表示
- 其他进制转十进制:右侧第一个数为起始位 0 位,各位上的数字与进制的位次方的乘积求和
- 其他进制之间进行转换:先转换为十进制再转换为目标进制
二进制:
Java 整数常量默认是 int 类型,当用二进制定义整数时,其第 32 位是符号位。当是 long 类型时,二进制默认占 64 位,其第 64 位是符号位
二进制的整数有三种形式:
- 原码:直接将一个数值换成二进制数。最高位是符号位
- 负数的反码:对原码按位取反,最高位(符号位)确定为 1
- 负数的补码:其反码 +1
计算机以二进制补码的形式保存所有的整数
- 正数的原码、反码、补码都相同
- 负数的补码是其反码 +1
运算符
算数运算符:
- +、-、*、/、%、++、–
- 加( + )减( - )可作运算、可表正负,表示正负时优先级最高
- 自增( ++ )自减( – )运算符在前,先运算后取值、自增自减运算符在后,先取值后运算
- % 取模(求余数)、Java 中只有当被除数为负数时结果才为负
赋值运算符:
- =、+=、-=、*=、/=、%=
- 当( = )两侧数据类型不一致时,可以使用自动类型转换或使用强制类型转换原则进行处理
- 支持连续赋值
比较运算符(关系运算符):
- ==、!=、<、>、<=、>=、instanceof
- instanceof 检查是否是类的对象
- 比较运算符的结果都是 boolean 型,要么是 true 要么是 false
逻辑运算符:
&:逻辑与、&&:短路与、|:逻辑或、||:短路或、!:逻辑非、^:逻辑异或
区分 & 与 &&
/* |
- 区分 | 与 ||
/* |
位运算符:
- <<:左移(3<<2=12:3*2*2=12)、>>:右移(3>>1=1:3/2=1)、>>>:无符号右移、&:与运算、|:或运算、^:异或运算、~:取反运算
- <<:在一定范围内,每向左移
1
位,相当于* 2
- >>:在一定范围内,每向右移
1
位,相当于/ 2
位运算符的细节:
- <<:被移除的高位丢弃,空缺位补
0
- >>:被移位的二进制最高位为
0
,右移后,空缺位补0
;最高位为1
,右移后,空缺位补1
- >>>:被移位二进制最高位无论是
0
或1
,空缺位都用0
补 - &:二进制位进行
&
运算,只有1 & 1
时结果为1
,否则是0
- |:二进制位进行
|
运算,只有0 | 0
时结果是0
,否则是1
- ^:相同的二进制位进行
^
运算,结果是0
(1^1=0
、0^0=0
)、不相同的二进制位^
运算结果是1
(1^0=1
、0^1=1
) - ~:正数取反,各二进制码按补码各位取反、负数取反,各二进制位按补码各位取反
位运算是直接对整数的二进制进行的运算
三元运算符:
格式:(条件表达式) ?
表达式1 :
表达式2
- 条件表达式结果为 true,运算后结果是:表达式1
- 条件变大时结果为 false,运算后结果是:表达式2
- 表达式1 和表达式2 是同种类型(整型类型自动类型提升)
三元运算符与 if-else 的联系与区别:
- 三元运算符可以简化 if-else 语句
- 三元运算符要求必须返回一个结果
- if 后的代码块可以有多个语句
- 三元运算符可以嵌套使用
- 凡是可以使用三元运算符的地方都可以改写为 if-else 语句,反之不可以
运算符的优先级(运算顺序):只有单目运算符、三元运算符、赋值运算符是从右向左运算的
流程控制
顺序结构
程序从上到下逐行执行,中间没有任何判断和跳转
分支结构
根据条件,选择性地执行某段代码
if…else 分支语句、switch-case 分支语句
if…else 分支语句:
第一种:
if (条件表达式) { |
第二种(二选一):
if (条件表达式) { |
第三种(多选一):
if (条件表达式) { |
switch-case 分支语句:
switch(表达式) { |
- 根据 switch 表达式的值,依次匹配各个 case 中的常量。一旦匹配成功,则进入相应 case 结构中,调用其执行语句
- 当调用完执行语句后,则仍然继续向下执行其他 case 结构中的执行语句,直到遇到 break 关键字或此 switch-case 结构末尾结束为止
- break 可以使用在 switch-case 结构中,表示一旦执行到此关键字,就跳出 switch-case 结构
- switch 结构中的表达式,只能是:byte、short、char、int、枚举类型(JDK5.0新增)、String(JDK7.0新增) 类型之一
- case 子句中的值只能是常量,不能是变量名或不确定的表达式值,同一个 switch 语句,所有 case 子句中的值不相同
- break 关键字用来在执行完一个 case 分支后使程序跳出 switch 语句块,如果没有 break,程序会顺序执行到 switch 结尾
- default 子句是可选的。同时位置也是灵活的,当没有匹配的 case 时,执行 default 子句
if-else 与 switch-case 的关系:
- 凡是可以使用 switch-case 的结构,都可以转换为 if-else。反之不成立
- 当分支结构既可以使用 switch-case 又可以使用 if-else (同时,switch 中表达式的取值情况不太多),优先选择 switch-case,因其执行效率稍高
循环结构
根据循环条件,重复性地执行某段代码
while 循环语句、do…while 循环语句、for 循环语句
JDK1.5 后提供了 foreach 循环,方便遍历集合、数组元素
循环语句的四个组成部分:初始化部分(初始化条件)、循环条件部分(循环条件,boolean 类型)、循环体部分(循环体)、迭代部分(迭代条件)
for 循环的结构:
for (初始化条件; 循环条件; 迭代条件) { |
while `循环的结构:
初始化条件 |
注意:while 循环没有或错误的迭代条件可能导致死循环
do-while 循环的结构:
初始化条件 |
while 循环与 do-while 循环的区别在于执行过程,do-while 循环至少会执行一次循环体
最简单的 “无限” 循环格式:while(true)
、for(;;)
- 无限循环存在的目的:当程序并不知道需要循环多少次时,需要根据循环体内部某些条件来控制循环的结束
循环结构可以嵌套使用
break 和 continue 关键字的使用:
- break
- 使用范围:switch-case、循环结构
- 作用(不同点):结束当前
- continue
- 使用范围:循环结构
- 作用(不同点):结束当次
- 相同点:关键字后不能声明执行语句
带标识的 break 和 continue:结束指定标识的一层循环结构
// 求 100000 以内的质数 |
数组(Array)
多个相同类型数据按一定顺序排列的集合,并使用一个变量命名,通过编号的方式对这些数据进行统一管理
数组的分类
- 按照维度:一维数组、二维数组、…
- 按照元素的数据类型:基本数据类型元素的数组、引用数据类型元素的数组(即对象数组)
数组的长度:数组中元素的个数
数组的特点:
- 数组是有序排列的
- 数组本身是引用数据类型,而数组中的元素可以是任何数据类型,包括基本数据类型和引用数据类型
- 创建数组对象会在内存中开辟一整块连续的空间,而数组名中引用的是这块连续空间的首地址
- 数组的长度一旦确定就不能修改
- 可以通过下标(或索引)的方式调用指定位置的元素
一维数组的使用
一维数组的声明和初始化:
- 声明:int[] array;
- 静态初始化:数组的初始化和数组元素的赋值操作同时进行
array = new int[]{1, 2, 3}; |
- 动态初始化:数组的初始化和数组元素的赋值操作分开进行
- 数组一旦初始化完成,其长度就确定了
- 类型推断:int[] array = {1, 2, 3, 4};
String[] names = new String[5]; |
调用数组的指定位置的元素:
- 使用索引的方式调用
- 数组的索引从 0 开始,到数组长度 - 1 结束
names[0] = "a"; |
获取数组的长度:
array.length; |
遍历数组:
for (int i = 0; i < names.length; ++i) { |
数组元素的默认初始化值:
- 数组元素是整型:
0
- 数组元素是浮点型:
0.0
- 数组元素是
char
型:0
或'\u0000'
而非'0'
- 数组元素是
boolean
型:false
- 数组元素是引用数据类型:
null
数组的内存解析:
内存的简化结构:
- 栈(stack):局部变量
- 堆(heap):new 出来的结构:对象、数组
- 方法区:常量池、静态域
数组名(变量)在栈空间中,new 出的数组对象(连续空间,以首地址值表示)在堆空间中
默认值替换为初始化值
将首地址值赋给数组名(变量),通过数组名(变量、首地址值)就可以访问堆空间中的数组对象
多维数组
可以把一维数组当成是表格的某一行,那么二维数组就相当于是一整个有多行多列的完整表格
对于二维数组,可以看作是某个一维数组的元素又是一个一维数组,这个元素是一维数组的数组就是二维数组
本质上从数组的底层运行机制来看其实没有多维数组
二维数组的声明和初始化:
- 静态初始化
int[][] array = new int[][]{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}; |
- 动态初始化
String[][] array2 = new String[3][2]; |
- 动态初始化
String[][] array3 = new String[3][]; |
- 如下也正确
int[] array4[] = new int[][]{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}; |
二维数组的使用:
规定:二维数组分为外层数组的元素,内层数组的元素
int[][] array = new int[4][3]; |
- 外层元素:
array[0]
、array[1]
- 内层元素:
array[0][0]
、array[1][1]
二维数组的默认初始化值:
针对初始化方式一:
int[][] array = new int[4][3];
- 外层元素的初始化值为:(带数据类型的)地址值
- 内层元素的初始化值为:与一维数组初始化情况一致
针对初始化方式二:
int[][] array = new int[4][];
- 外层元素的初始化值为:
null
- 内层元素的初始化值为:不能调用,否则报错
- 外层元素的初始化值为: