前置准备

JRE 与 JDK

  • JRE(Java Runtime Environment,Java 运行环境)包括 JVM 和 Java 核心类库,安装后可运行 Java 程序,但无法开发;
  • JDK(Java Development Kit, Java 开发工具包)包括 JRE 和 Java 开发工具,可开发和运行 Java 程序,Oracle JDK 1.8.0_202 后版本在生产环境中需付费.

安装

  1. 登录 Oracle 官网下载 JDK 1.8.0_202。
  2. (推荐)默认路径进行安装。
  3. 使用 cmd 或其他控制台程序输入 java 验证。

Windows 环境变量配置

我们强烈不推荐在系统环境变量中设置 classpath ,那样会污染整个系统环境。在启动JVM时设置 classpath 才是推荐的做法[1]

Windows 环境变量变量名不区分大小写

  1. win + R 输入 sysdm.cpl 打开系统高级设置 -> 环境变量;
  2. 系统变量 -> 新建变量名 JAVA_HOME,复制粘贴 JDK 安装的绝对路径,如 C:\Program Files\Java\jdk1.8.0_202
  3. 系统变量 -> Path -> 编辑 -> 新建,环境变量为 %JAVA_HOME%\bin
  4. 从 JDK 1.5 开始 CLASSPATH 默认是当前路径,不用再配置。

    • 如若需要,则同设置 JAVA_HOME ,新建变量名CLASSPATH,值为 .;%JAVA_HOME%\lib\dt.jar;%JAVA_HOME%\lib\tools.jar
  5. 使用 cmd 或其他命令行程序,输入 javac ,java -version进行验证。

JAVA_HOME 与 PATH 简要介绍

当我们在命令行界面(如 cmd )中输入不同指令时,会得到系统的相应回馈。实际上,这些指令就是对应软件 bin 目录下的可执行文件(如 Windows 下的 .exe)。

  1. PS C:\Users\hu> java -version
  2. java : 无法将“java”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。请检查名称的拼写,如果包括路径,请确保路径正确,然后再试一次。

由上述回馈可知,命令行无法找到输入的指令 java --version 。原因是不存在于环境变量 PATH 中,只需在 PATH 中添加对应软件的 bin 目录即可。

更推荐做法是引入另一个系统变量 JAVA_HOME。在 JAVA_HOME 中存放软件的绝对路径,而在 PATH 中存放相对路径 %JAVA_HOME%\jdk1.8.0_202\bin。这样的好处有:

  • 其他变量需要时,方便引用;
  • 归一原则,绝对路径发生改变时,仅修改 Java_HOME 即可;
  • 第三方软件按约定会引用到 JAVA_HOME

配置正确后得到

  1. PS C:\Users\hu> java -version
  2. java version "1.8.0_202"
  3. Java(TM) SE Runtime Environment (build 1.8.0_202-b08)
  4. Java HotSpot(TM) 64-Bit Server VM (build 25.202-b08, mixed mode)

入门

第一个 Java 程序

  1. // 记事本编写,保存为 HelloWorld.java
  2. public class HelloWorld{
  3. public static void main(String[] args){
  4. System.out.println("HelloWorld");
  5. }
  6. }

命令行切换到文件所在目录输入命令

  1. C:\Users\admin>javac HelloWorld.java
  2. 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;
  • 代码编写使用行尾风格(推荐)或次行风格;
  • 详见阿里巴巴代码规范。

命名规则

  1. 由数字、英文字母、下划线或 $ 组成;
  2. 不能独立使用关键字和保留字,但能包含;
  3. 严格区分大小写,长度无限制,不能有空格。

命名规范

  • 包名:多单词组成时所有字母小写,推荐域名反转,如 com.google.test
  • 类名、接口名:多单词组成时,大驼峰(首字母大写);
  • 变量名、方法名:多单词组成时,小驼峰(首字母小写);
  • 常量名:全大写,多单词组成时,下划线做分隔。

Java API 文档

API(application programming interface,应用程序编程接口),是 Java 提供的基本编程接口,提供了大量的基础类与方法。

官方提供相应的 API 文档为开发者介绍如何使用这些类和方法。

变量

可以看作是由内存提供的一个存放数据的容器,一般由数据类型、名称、值构成,根据变量名可以访问到变量(值)。

声明与使用

  1. // 声明并赋值(推荐)
  2. int a = 1;
  3. // 或先声明,再赋值
  4. int b;
  5. b = 1;
  6. // 再使用
  7. System.out.println(a);
  8. 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 需要在数值后加 lL

浮点型使用细节

  • 浮点数在机器中存放形式,浮点数=符号位+指数位+尾数位。尾数部分可能丢失,造成精度损失(小数都是近似值)
  • 默认为 double ,若使用 float 需要在数值后加 fF
  • 科学计数法 3.14*10² <=> 3.14e23.14e-2 负二次方,e 大小写等价。
  • 判断浮点数和浮点数除法的结果是否相等:二者之差的绝对值是否在要求精度范围内。

字符型使用细节

  • 两个单引号括起来一个字符,可以使用转义字符 \ 转为特殊字符常量(如换行符\n
  • 本质是一个整数,可以进行运算。
  • 输出是与 Unicode 码对应的字符。因此可以按照 Unicode 编码赋值输出对应字符。

布尔型使用细节

  • 占一个字节,只允许 truefalse
  • 适用于逻辑运算。

数据类型转换

自动类型转换 按照精度范围,小范围自动转为大范围。

  • 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种常见方法

String常见方法1

String常见方法2

运算符

图源菜鸟教程

用于数据的运算、赋值和比较等,一般包括:

  • 算术运算符
  • 赋值运算符
  • 关系运算符
  • 逻辑运算符
  • 位运算符
  • 三元(目)运算符

算数运算符

1 基础部分😁 - 图3

赋值运算符

1 基础部分😁 - 图4

关系运算符

运算结果为 boolean
1 基础部分😁 - 图5

逻辑运算符

运算结果为 boolean
1 基础部分😁 - 图6

位运算符

1 基础部分😁 - 图7

三元运算符

表达式 ? 布尔值 : 布尔值

instanceof 运算符

检查该对象是否是一个特定类型

  1. A instanceof B

运算符优先级

1 基础部分😁 - 图8

流程控制

三大流程控制语句

  • 顺序控制
  • 分支控制
  • 循环控制

分支控制

分支结构可以嵌套
单分支 if
双分支 if else
多分支 if ... else if ... else
switch 分支结构

  1. switch(表达式){
  2. case 常量1
  3. 语句块1;
  4. break;
  5. ...
  6. default: // 可选
  7. 语句块;
  8. break
  9. }
  • 不同常量执行的语句块相同,则写为
  1. case 常量1
  2. case 常量2:
  3. 语句块;
  4. break;
  • 表达式可以是 byte short int char enum。从 Java SE 7 开始支持 Stringcase 标签必须为字符串常量或字面量。

循环控制

for 循环 for(循环变量初始化;循环条件;循环变量迭代){...}

  • 循环变量可多个一起初始化,使用 , 隔开,循环条件也一样。
  • 多重 for 循环,当最内层 for 循环执行完毕,才执行外层循环。

while 循环 while(条件){...}

  • 条件为 true 将继续执行循环体。

do…while() do{...}while(条件)

  • 至少执行一次 do 中的循环体,然后再判断条件,为 true 则继续执行循环体。

break、continue 和 return

return 使用在方法,表示跳出所在的方法。
breakcontinue 都是终止语句块的继续执行,在此之前的语句都会执行,一般用于跳出语句块(如循环),不同之处在于:

  • break 完全不会再执行该语句块。
  • continue 结束本次循环,执行下一次循环。
  1. for (int i= 1; i<10;i++){
  2. System.out.println("a"); // 必被执行
  3. if (i == 1) break; // 一旦触发,整个循环结束
  4. System.out.println(i);
  5. }
  6. for (int j= 1; j<10;j++){
  7. System.out.println("a"); // 必被执行
  8. if (j == 1) continue; // 一旦触发,j = 1 的循环结束,执行 j = 2 的
  9. System.out.println(i);
  10. }

数组

数组基础

可以存放相同数据类型元素的集合,是一种引用类型的数据类型。

创建数组后未赋值则为默认值,整型为 0,浮点型 0.0,字符型 \u0000,布尔型 false,引用类型 null

定义数组

  1. // 方式一 动态初始化
  2. elemType[] name = new elemType[num];// num 表示数组大小(元素个数)
  3. // 方式二 动态初始化
  4. elemType[] name;
  5. name = new elemType[num]
  6. // 方式三 静态初始化
  7. elemType[] name = {a,b,...}; // 元素个数即为数组大小

访问数组

  • arr[index] 索引从 0 开始,超出将会发生越界异常;
  • 遍历元素使用 for/for each

数组赋值机制

将一个数组赋值另一个数组,默认情况下是引用传递,赋的值是地址。

  1. int[] a = {1,2,3};
  2. int[] b = a;
  3. b[0] = 99; // 此时,a 数组也变成 99,2,3

数组拷贝

解决上述问题,利用 for/for each 逐个赋值。

  1. int count = 0;
  2. for(int temp : a){
  3. b[count++] = temp;
  4. }

1 基础部分😁 - 图9

数组反转

将元素内容反转,两种思路:

  1. 首尾元素两两交换,进行 length/2 向下取整次。(临时变量)
  2. 或是逆序赋值。(临时数组)

数组扩容

动态给数组添加元素,思路:数组容量已满,新建长度加 1 的数组,原始数组遍历赋值给新数组,并将新添加元素加入新数组末尾。

初见排序和查找

外部排序 数据量过大,无法全部加载到内存中,需要借助外部存储进行排序。包括(合并排序法和直接合并排序法)
内部排序 指将需要处理的所有数据都加载到内部存储器中进行排序。包括(交换式排序法、选择式排序法和插入式排序法)

冒泡排序

从前往后依次两两比较,后者大于前者则交换位置。一共进行 n-1 轮比较,每轮比较 n - i

1 基础部分😁 - 图10

二分查找

前提 元素有序且使用顺序存储结构
序列为基数,首尾索引相加/2 (无论从 0 还是 1 开始计数)。
序列为偶数,首尾索引相加/2 再向下取整 (无论从 0 还是 1 开始计数)。

二维数组

可以理解为数组元素是数组,且行列个数不强制相等。
形式为 elemType[][] name;可以理解为行列,访问都是指定位置如 elemType[1][1]
除此之外还有 elemType name[][];elemType []name[];

使用方式1——动态初始化

  1. // elemType[][] arrName = new elemType[大小][大小];
  2. int[][] arr1 = new int[2][3];

同一位数组,索引是从 0 开始,声明时的仅为大小(个数)

使用方式2——动态初始化

  1. //先声明,再定义(开辟空间)
  2. int[][] arr2;
  3. arr2 = new int[2][3];

使用方式3——动态初始化-列数不确定

1 基础部分😁 - 图11

  1. //创建二维数组,一个有 3 个一维数组
  2. //虽然看起来有new,但每个一维数组还没有开辟空间
  3. //打印结果为 null 而非一个地址
  4. int[][] arr3 = new int[3][];
  5. // 赋值
  6. for (int i = 0; i < arr2.length; i++) {
  7. // 给每行的一维数组开空间,开多少取决上图
  8. arr2[i] = new int[i+1];
  9. // 指定位置赋值
  10. for (int j = 0; j < arr2[i].length; j++) {
  11. arr2[i][j]=i+1;
  12. }
  13. }
  14. // 打印
  15. for (int[] arr:arr2) {
  16. for (int i = 0; i < arr.length; i++) {
  17. System.out.print(arr[i] + "\t");
  18. }
  19. System.out.println();
  20. }

使用方式4——静态初始化

  1. elemType[][] arr4 = {
  2. {elem1,...},
  3. ...,
  4. {elem1,...}
  5. };

上述只是为了通过“表格行列”这一概念,更好理解二维数组,进行了格式化。


  1. classpath和jar - 廖雪峰的官方网站 (liaoxuefeng.com) ↩︎