前置准备
JRE 与 JDK
- JRE(Java Runtime Environment,Java 运行环境)包括 JVM 和 Java 核心类库,安装后可运行 Java 程序,但无法开发;
- JDK(Java Development Kit, Java 开发工具包)包括 JRE 和 Java 开发工具,可开发和运行 Java 程序,Oracle JDK 1.8.0_202 后版本在生产环境中需付费.
安装
- 登录 Oracle 官网下载 JDK 1.8.0_202。
- (推荐)默认路径进行安装。
- 使用
cmd
或其他控制台程序输入java
验证。
Windows 环境变量配置
我们强烈不推荐在系统环境变量中设置
classpath
,那样会污染整个系统环境。在启动JVM时设置classpath
才是推荐的做法[1]。
Windows 环境变量变量名不区分大小写
win + R
输入sysdm.cpl
打开系统高级设置 -> 环境变量;- 系统变量 -> 新建变量名
JAVA_HOME
,复制粘贴 JDK 安装的绝对路径,如C:\Program Files\Java\jdk1.8.0_202
; - 系统变量 -> Path -> 编辑 -> 新建,环境变量为
%JAVA_HOME%\bin
; 从 JDK 1.5 开始 CLASSPATH 默认是当前路径,不用再配置。
- 如若需要,则同设置
JAVA_HOME
,新建变量名CLASSPATH
,值为.;%JAVA_HOME%\lib\dt.jar;%JAVA_HOME%\lib\tools.jar
;
- 如若需要,则同设置
- 使用
cmd
或其他命令行程序,输入javac
,java -version
进行验证。
JAVA_HOME 与 PATH 简要介绍
当我们在命令行界面(如 cmd
)中输入不同指令时,会得到系统的相应回馈。实际上,这些指令就是对应软件 bin 目录下的可执行文件(如 Windows 下的 .exe
)。
PS C:\Users\hu> java -version
java : 无法将“java”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。请检查名称的拼写,如果包括路径,请确保路径正确,然后再试一次。
由上述回馈可知,命令行无法找到输入的指令 java --version
。原因是不存在于环境变量 PATH
中,只需在 PATH
中添加对应软件的 bin
目录即可。
更推荐做法是引入另一个系统变量 JAVA_HOME
。在 JAVA_HOME
中存放软件的绝对路径,而在 PATH
中存放相对路径 %JAVA_HOME%\jdk1.8.0_202\bin
。这样的好处有:
- 其他变量需要时,方便引用;
- 归一原则,绝对路径发生改变时,仅修改
Java_HOME
即可; - 第三方软件按约定会引用到
JAVA_HOME
。
配置正确后得到
PS C:\Users\hu> java -version
java version "1.8.0_202"
Java(TM) SE Runtime Environment (build 1.8.0_202-b08)
Java HotSpot(TM) 64-Bit Server VM (build 25.202-b08, mixed mode)
入门
第一个 Java 程序
// 记事本编写,保存为 HelloWorld.java
public class HelloWorld{
public static void main(String[] args){
System.out.println("HelloWorld");
}
}
命令行切换到文件所在目录输入命令
C:\Users\admin>javac HelloWorld.java
C:\Users\admin>java HelloWorld
补充说明
- 源代码文件以
.java
为扩展名。 public static void main(String[] args)
称之为 main 方法,其花括号中为方法体,main 方法是程序执行入口。- 代码严格区分大小写,每条语句以英文分号
;
结束,花括号后省略。 - 源代码中有且仅有一个
public class
且和源代码文件名保持一致。存在与之『平级』的class
,其个数、名字不限。使用javac 公共类名
会同时将其他类一起编译。
Java 程序执行过程
*.java
文件 —>(javac
)编译器编译—> *.class
字节码文件 —>(java
) JVM 执行 —> 结果
关键字和保留字
关键字:被 Java 语言赋予了特殊含义,用做专门用途的字符串(单词)。
Java 保留字:现有 Java 版本尚未使用,但以后版本可能会作为关键字使用。
常见转义字符
\t 制表符;\n 换行;\ 反斜杠;\’’ 双引号;\’ 单引号
注释
- 单行注释
//
- 多行注释
/* */
- 文档注释
/** */
Java 代码规范
- 类、方法的注释,以 javadoc(文档注释) 形式书写;
- 使用 tab 缩进(四个空格);
- 符号两边各加一个空格;
- 编码使用 UTF-8;
- 代码编写使用行尾风格(推荐)或次行风格;
- 详见阿里巴巴代码规范。
命名规则
- 由数字、英文字母、下划线或 $ 组成;
- 不能独立使用关键字和保留字,但能包含;
- 严格区分大小写,长度无限制,不能有空格。
命名规范
- 包名:多单词组成时所有字母小写,推荐域名反转,如
com.google.test
; - 类名、接口名:多单词组成时,大驼峰(首字母大写);
- 变量名、方法名:多单词组成时,小驼峰(首字母小写);
- 常量名:全大写,多单词组成时,下划线做分隔。
Java API 文档
API(application programming interface,应用程序编程接口),是 Java 提供的基本编程接口,提供了大量的基础类与方法。
官方提供相应的 API 文档为开发者介绍如何使用这些类和方法。
变量
可以看作是由内存提供的一个存放数据的容器,一般由数据类型、名称、值构成,根据变量名可以访问到变量(值)。
声明与使用
// 声明并赋值(推荐)
int a = 1;
// 或先声明,再赋值
int b;
b = 1;
// 再使用
System.out.println(a);
System.out.println(b);
变量声明可以同类型,但不能同名。
声明和定义是有区别的,但在 Java 中,并没有像 C/C++ 那样真正的分离声明和定义,因此不必过于纠结。
数据类型
每一种数据都定义了明确的数据类型,并在内存中分配了不同大小的内存空间。
Java 的数据类型有两大类:基本数据类型和引用数据类型,其范围和字段长度固定(因为 JVM),不受物理机系统的影响。
基本数据类型
数值型
- 整型:
byte
[1],short
[2],int
[4],long
[8] - 浮点型:
float
[4],double
[8]
字符型 char
[2]
布尔型 boolean
[1]
整型使用细节
- 默认为
int
若使用long
需要在数值后加l
或L
浮点型使用细节
- 浮点数在机器中存放形式,浮点数=符号位+指数位+尾数位。尾数部分可能丢失,造成精度损失(小数都是近似值)
- 默认为
double
,若使用float
需要在数值后加f
或F
- 科学计数法
3.14*10²
<=>3.14e2
;3.14e-2
负二次方,e 大小写等价。 - 判断浮点数和浮点数除法的结果是否相等:二者之差的绝对值是否在要求精度范围内。
字符型使用细节
- 两个单引号括起来一个字符,可以使用转义字符
\
转为特殊字符常量(如换行符\n
) - 本质是一个整数,可以进行运算。
- 输出是与
Unicode
码对应的字符。因此可以按照Unicode
编码赋值输出对应字符。
布尔型使用细节
- 占一个字节,只允许
true
或false
。 - 适用于逻辑运算。
数据类型转换
自动类型转换 按照精度范围,小范围自动转为大范围。
- char < int < long < float < double
- byte < short < int < long < float < double
细节
- 多类型数据混合运算,系统自动将所有数据转换成精度范围最大的,再计算。结果类型范围为最大的。
- 大精度 —> 小精度,报错。反之,自动转换。
byte
short
二者不会与char
自动转换。byte
short
char
可以进行数值计算,会先转为int
型。boolean
不参与转换
强制类型转换
大范围 —> 小范围使用
()
,可能造成精度降低或溢出。只针对最近的操作数有效,使用小括号提升优先级。
char
不能保存 int 变量,需强转。
引用数据类型
- 类(class)
- 接口(interface)
- 数组([])
基本数据类型和 String 类型转换
- 基 —>
String
使用+
拼接""
即可。 String
—> 基,使用基本类型包装类的parseXxx()
方法。
String 常见方法(下标起始 0)
图源 String类的20种常见方法Jacky_Cmd-CSDN博客string类的20种常见方法
运算符
图源菜鸟教程
用于数据的运算、赋值和比较等,一般包括:
- 算术运算符
- 赋值运算符
- 关系运算符
- 逻辑运算符
- 位运算符
- 三元(目)运算符
算数运算符
赋值运算符
关系运算符
运算结果为 boolean
逻辑运算符
运算结果为 boolean
位运算符
三元运算符
表达式 ? 布尔值 : 布尔值
instanceof 运算符
检查该对象是否是一个特定类型
A instanceof B
运算符优先级
流程控制
三大流程控制语句
- 顺序控制
- 分支控制
- 循环控制
分支控制
分支结构可以嵌套
单分支 if
双分支 if else
多分支 if ... else if ... else
switch 分支结构
switch(表达式){
case 常量1:
语句块1;
break;
...
default: // 可选
语句块;
break
}
- 不同常量执行的语句块相同,则写为
case 常量1:
case 常量2:
语句块;
break;
- 表达式可以是
byte
short
int
char
enum
。从 Java SE 7 开始支持String
,case
标签必须为字符串常量或字面量。
循环控制
for 循环 for(循环变量初始化;循环条件;循环变量迭代){...}
。
- 循环变量可多个一起初始化,使用
,
隔开,循环条件也一样。 - 多重 for 循环,当最内层 for 循环执行完毕,才执行外层循环。
while 循环 while(条件){...}
- 条件为
true
将继续执行循环体。
do…while() do{...}while(条件)
- 至少执行一次
do
中的循环体,然后再判断条件,为true
则继续执行循环体。
break、continue 和 return
return
使用在方法,表示跳出所在的方法。
break
和 continue
都是终止语句块的继续执行,在此之前的语句都会执行,一般用于跳出语句块(如循环),不同之处在于:
break
完全不会再执行该语句块。continue
结束本次循环,执行下一次循环。
for (int i= 1; i<10;i++){
System.out.println("a"); // 必被执行
if (i == 1) break; // 一旦触发,整个循环结束
System.out.println(i);
}
for (int j= 1; j<10;j++){
System.out.println("a"); // 必被执行
if (j == 1) continue; // 一旦触发,j = 1 的循环结束,执行 j = 2 的
System.out.println(i);
}
数组
数组基础
可以存放相同数据类型元素的集合,是一种引用类型的数据类型。
创建数组后未赋值则为默认值,整型为 0
,浮点型 0.0
,字符型 \u0000
,布尔型 false
,引用类型 null
。
定义数组
// 方式一 动态初始化
elemType[] name = new elemType[num];// num 表示数组大小(元素个数)
// 方式二 动态初始化
elemType[] name;
name = new elemType[num]
// 方式三 静态初始化
elemType[] name = {a,b,...}; // 元素个数即为数组大小
访问数组
arr[index]
索引从 0 开始,超出将会发生越界异常;- 遍历元素使用 for/for each
数组赋值机制
将一个数组赋值另一个数组,默认情况下是引用传递,赋的值是地址。
int[] a = {1,2,3};
int[] b = a;
b[0] = 99; // 此时,a 数组也变成 99,2,3
数组拷贝
解决上述问题,利用 for/for each 逐个赋值。
int count = 0;
for(int temp : a){
b[count++] = temp;
}
数组反转
将元素内容反转,两种思路:
- 首尾元素两两交换,进行
length/2
向下取整次。(临时变量) - 或是逆序赋值。(临时数组)
数组扩容
动态给数组添加元素,思路:数组容量已满,新建长度加 1 的数组,原始数组遍历赋值给新数组,并将新添加元素加入新数组末尾。
初见排序和查找
外部排序 数据量过大,无法全部加载到内存中,需要借助外部存储进行排序。包括(合并排序法和直接合并排序法)
内部排序 指将需要处理的所有数据都加载到内部存储器中进行排序。包括(交换式排序法、选择式排序法和插入式排序法)
冒泡排序
从前往后依次两两比较,后者大于前者则交换位置。一共进行 n-1
轮比较,每轮比较 n - i
次
二分查找
前提 元素有序且使用顺序存储结构
序列为基数,首尾索引相加/2 (无论从 0 还是 1 开始计数)。
序列为偶数,首尾索引相加/2 再向下取整 (无论从 0 还是 1 开始计数)。
二维数组
可以理解为数组元素是数组,且行列个数不强制相等。
形式为 elemType[][] name;
可以理解为行列,访问都是指定位置如 elemType[1][1]
除此之外还有 elemType name[][];
和 elemType []name[];
使用方式1——动态初始化
// elemType[][] arrName = new elemType[大小][大小];
int[][] arr1 = new int[2][3];
同一位数组,索引是从 0 开始,声明时的仅为大小(个数)
使用方式2——动态初始化
//先声明,再定义(开辟空间)
int[][] arr2;
arr2 = new int[2][3];
使用方式3——动态初始化-列数不确定
//创建二维数组,一个有 3 个一维数组
//虽然看起来有new,但每个一维数组还没有开辟空间
//打印结果为 null 而非一个地址
int[][] arr3 = new int[3][];
// 赋值
for (int i = 0; i < arr2.length; i++) {
// 给每行的一维数组开空间,开多少取决上图
arr2[i] = new int[i+1];
// 指定位置赋值
for (int j = 0; j < arr2[i].length; j++) {
arr2[i][j]=i+1;
}
}
// 打印
for (int[] arr:arr2) {
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + "\t");
}
System.out.println();
}
使用方式4——静态初始化
elemType[][] arr4 = {
{elem1,...},
...,
{elem1,...}
};
上述只是为了通过“表格行列”这一概念,更好理解二维数组,进行了格式化。