写在前面:
此文是笔者在学习Java系列课程的过程中,参考相关课件、视频讲解、课程代码,并结合一些文档、思维导图及个人理解,对所学内容做的阶段性梳理与总结。
二进制
tips:二进制数系统中,每个0或1就是一个位,叫做bit(比特)
问:什么是 8421编码 ?
答:8421码又称为BCD码,是十进制代码中最常用的一种,每一位二值代码的“1”都代表一个固定数值2^(n-1) [右→左] 。
将每位“1”所代表的二进制数加起来,就可以得到它所代表的十进制数字。因为代码中从左至右看每一位“1”分别代表数字“8”“4”“2”“1”,故得名8421码。
字节
tips:在计算机数据存储中,存储数据的最小单位 → 位(bit),基本单位 → 字节(Byte 简称B)
常用DOS命令
cmd
(命令提示符)回车 → 进入DOS的操作窗口命令 | 操作符号 |
---|---|
盘符切换 | 盘符名: |
进入文件夹 | cd 文件夹名 |
进入多级文件夹 | cd 文件夹1\文件夹2\文件夹3 |
退出文件夹 | cd … |
直接回根路径 | cd \ |
查看当前内容 | dir |
清屏 | cls |
退出 | exit |
概念:
tips:
Java语言 具有跨平台性:同一段程序可运行在不同操作系统上
但 Java的虚拟机-JVM:本身不具有跨平台功能(每个操作系统配有不同版本的虚拟机)
目的:
意义:
步骤:
Java程序开发三步骤:编写、编译、运行
HelloWorld.java
HelloWorld
,后缀 .java
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, World!"); // 输出 Hello, World!
}
}
javac Java源文件名.后缀名
,如:javac HelloWorld.javajavac -encoding UTF-8 HelloWorld.java
java HelloWorld
tips:
- 程序的文件名:需和类的名字一致,注意大小写,每个字母和符号必须与示例代码一模一样。eg:HelloWorld
- 工具:(1).java文件的编译工具:javac.exe [ 编译器 ] (2).class文件的运行工具:java.exe [ 解释器 ]
编译和运行是两个不同概念
- 编译:将编写的Java源文件翻译成JVM认识的class文件(此过程中,编译器会检查所写程序是否错误——若有错误则会有提示,否则编译成功)
- 运行:将class文件通过解释器交给JVM运行(此时JVM就会去执行我们编写的程序)
类型 | 含义 | 举例 |
---|---|---|
整数常量 | 所有的整数 | 4、200、0、-250 |
小数常量 | 所有的小数 | 0.0、 -0.1、2.55 |
字符常量 | 单引号引起来,只能写一个字符,必须有内容 | ‘A’、‘b’、‘9’、‘中’ |
字符串常量 | 双引号引起来,可写多个字符,也可不写 | “A” 、“Hello” 、“你好” 、"" |
布尔常量 | 只有两个取值 | true 、false |
空常量 | 只有一个取值,代表没有任何数据 | null |
public class Demo01Const {
public static void main(String[] args) {
// 字符串常量
System.out.println(""); // 字符串两个双引号中间的内容为空 √
// 字符常量 —— 两个单引号中间必须有且仅有一个字符
System.out.println('A');// √
//System.out.println(''); // 没有字符不行 ×
//System.out.println('AB'); // 有两个字符不行 ×
// 空常量 —— 空常量不能直接用来打印输出
//System.out.println(null); // ×
}
数据类型
数据类型 | 关键字 | 内存占用 | 取值范围 |
---|---|---|---|
字节型 | byte | 1个字节 | -128~127 |
短整型 | short | 2个字节 | -32768~32767 |
整型 | int(默认) | 4个字节 | -231次方~2的31次方-1 |
长整型 | long | 8个字节 | -2的63次方~2的63次方-1 |
单精度浮点数 | float | 4个字节 | 1.4013E-45~3.4028E+38 |
双精度浮点数 | double(默认) | 8个字节 | 4.9E-324~1.7977E+308 |
字符型 | char | 2个字节 | 0-65535 |
布尔类型 | boolean | 1个字节 | true、false |
变量
数据类型
、 变量名
、 数据值
1. 格式1_先创建后赋值
数据类型 变量名称; // 创建了一个变量
变量名称 = 数据值; // 赋值,将右边的数据值,赋值交给左边的变量
eg: int num1; num1 = 10;
2. 格式2_一步到位
数据类型 变量名称 = 数据值; // 在创建一个变量的同时,立刻放入指定的数据值
eg: int num2 = 10;
public class VariableNotice {
public static void main(String[] args) {
int num1 = 10; // 创建了一个新的变量,名叫num1
// int num1 = 20; // [1] 又创建了另一个新的变量,名字也叫num1,错误!
byte num2 = 30; // 注意:右侧数值的范围不能超过左侧数据类型的取值范围
System.out.println(num2); // 30
// byte num3 = 400; // [2] 右侧超出了byte数据范围,错误!
int num4; // 定义了一个变量,但是没有进行赋值
// System.out.println(num4); // [3]直接使用打印输出就是错误的!
// System.out.println(num5); // 在创建变量之前,不能使用这个变量
int num5 = 500;
System.out.println(num5); // 500
{
int num6 = 60;
System.out.println(num6); // 60
}
// int num6;
// System.out.println(num6); //[4] 已经超出了大括号的范围,超出了作用域,变量不能再使用了
//[5] 同时创建了三个全都是int类型的变量
int a, b, c;
// 各自分别赋值
a = 10;
b = 20;
c = 30;
System.out.println(a); // 10
System.out.println(b); // 20
System.out.println(c); // 30
//[5] 同时创建三个int变量,并且同时各自赋值
int x = 100, y = 200, z = 300;
System.out.println(x); // 100
System.out.println(y); // 200
System.out.println(z); // 300
}
}
tips:
- Java中的默认类型:整数类型是 int 、浮点类型是 double
- 变量的定义:long类型 — 数据后加L表示、float类型 — 数据后加F表示(字母后缀F和L不要丢掉)
当数据类型不一样时,将会发生数据类型转换
byte、short、char‐‐>int‐‐>long‐‐>float‐‐>double
)public static void main(String[] args) {
int i = 1;
byte b = 2;
// byte x = b + i; // 报错
//int类型和byte类型运算,结果是int类型
int j = b + i;
System.out.println(j); //3
/*
左边是long类型,右边是默认的int类型,左右不一样
一个等号代表赋值,将右侧的int常量,交给左侧的long变量进行存储
int --> long,符合了数据范围从小到大的要求(自动类型转换)
*/
long num1 = 100;
System.out.println(num1); // 100
}
范围小的类型 变量名 = (范围小的类型) 原本范围大的数据;
int i = 1. 5 ; // 错误(将 double类型数据 赋值到 int类型变量,会产生编译失败,所以无法赋值)
int i = (int)1.5;//编译成功(double类型数据 强制转成 int类型,直接去掉小数点)
精度损失
、数据溢出
public static void main(String[] args) {
//1. double --> int,强制类型转换
int num1 = (int) 3.99;
System.out.println(num1); // 3,这并不是四舍五入,所有的小数位都会被舍弃掉(精度损失)
//2. long强制转换成为int类型
int num2 = (int) 6000000000L;
System.out.println(num2); // 1705032704(发生数据溢出,即:数据丢失)
//3. 定义num3为short范围内最大值
short num3 = 32767;
// short + int --> int + int --> int --> short
// 运算后,4字节int强制转换为2字节short,砍掉2个字节后会出现不确定的结果(数据丢失)
num3 = (short)(num3 + 10);
}
tips:
- byte/short/char这三种类型:
(1)都可以发生数学运算,例如 加法“+”;(2)在运算的时候,都会被首先提升成为int类型,然后再计算- boolean类型:不能发生数据类型转换
public static void main(String[] args) {
char c1 = '1';
// char字符类型和int类型计算
// char类型的字符先查询编码表,得到49,再与1求和,结果为50
System.out.println(c1 + 1); // 50
char c2 = 'A'; // 其实底层保存的是65数字
char c3 = 'c';
// 左侧是int类型,右边是char类型,
// char --> int,范围从小到大(自动类型转换)
int num = c3;
System.out.println(num); // 99
char c4 = '中'; // 正确写法 (Unicode码)
System.out.println(c4 + 0); // 20013
}
字符 | 数值 |
---|---|
0 | 48 |
9 | 57 |
A | 65 |
Z | 90 |
a | 97 |
z | 122 |
思维导图
代码分析
++
、自减运算符 --
public static void main(String[] args) {
//1. 与打印操作混合的时候
int num1 = 20;
// 混合使用,先++(先加后用),变量立刻马上变成21,然后打印结果21
System.out.println(++num1); // 21
System.out.println(num1); // 21
int num2 = 30;
// 混合使用,后++(先用后加),首先使用变量本来的30,然后再让变量+1得到31
System.out.println(num2++); // 30
System.out.println(num2); // 31
System.out.println("=================");
//2. 和赋值操作混合
int num3 = 40;
int result1 = --num3; // 混合使用,前--,变量立刻马上-1变成39,然后将结果39交给result1变量
System.out.println(result1); // 39
System.out.println(num3); // 39
int num4 = 50;
int result2 = num4--;// 混合使用,后--,首先把本来的数字50交给result2,然后我自己再-1变成49
System.out.println(result2); // 50
System.out.println(num4); // 49
System.out.println("=================");
//3. 综合
int x = 10;
int y = 20;
int result3 = ++x + y--;// 11 + 20 = 31
System.out.println(result3); // 31
System.out.println(x); // 11
System.out.println(y); // 19
// 30++; // 错误写法!常量不可以使用++或者--
}
数据类型 变量名称 = 条件判断 ? 表达式A : 表达式B;
public static void main(String[] args) {
int a = 10;
int b = 20;
// 判断a > b是否成立,如果成立将a的值赋值给max;如果不成立将b的值赋值给max。二者选其一
int max = a > b ? a : b; // 最大值的变量
System.out.println("最大值:" + max); // 20
// int result = 3 > 4 ? 2.5 : 10; // 错误写法!( 2.5不符合int类型)
System.out.println(a > b ? a : b); // 正确写法!
// a > b ? a : b; // 错误写法!(三元运算符的结果必须被使用)
}
“隐含强制类型转换”
的内容/*
对于byte/short/char三种类型来说,如果右侧赋值的数值没有超过范围,
那么javac编译器将会自动隐含地为我们补上一个(byte)(short)(char)。
1. 如果没有超过左侧的范围,编译器补上强转。
2. 如果右侧超过了左侧范围,那么直接编译器报错。
*/
public static void main(String[] args) {
// int --> byte,不是自动类型转换
byte num1 = /*(byte)*/ 30; // 右侧int数字没有超过左侧的范围,是正确的!
System.out.println(num1); // 30
// byte num2 = 128; // 右侧超过了左侧的范围,错误!
// int --> char,没有超过范围
// 编译器将会自动补上一个隐含的(char)
char zifu = /*(char)*/ 65;
System.out.println(zifu); // A
}
“编译器的常量优化”
/*
在给变量进行赋值的时候,如果右侧的表达式当中全都是常量,没有任何变量,
那么编译器javac将会直接将若干个常量表达式计算得到结果。
short result = 5 + 8; // 等号右边全都是常量,没有任何变量参与运算
编译之后,得到的.class字节码文件当中相当于【直接就是】:
short result = 13;
右侧的常量结果数值,没有超过左侧范围,所以正确。
这称为“编译器的常量优化”。
但是注意:一旦表达式当中有变量参与,那么就不能进行这种优化了。
*/
public static void main(String[] args) {
short num1 = 10; // 正确写法,右侧没有超过左侧的范围
short a = 5;
short b = 8;
/* 右侧只有两个常量,没有任何变量
在编译的时候(编译器javac),已经确定了 5+8 的结果并没有超过short类型的取值范围,
可以赋值给变量 result ,因此result = 5 + 8 正确
*/
short result = 5 + 8;
System.out.println(result);//13
/* 右侧存在变量的情况
short + short --> int + int --> int
*/
short result1 = a + b; // 错误写法!左侧需要是int类型
short result2 = 5 + a + 8; // 编译报错!不兼容的类型:从int转换到short可能会有损失
}
流程控制:通过控制语句的执行顺序来实现所需功能
判断语句 1:if
if(关系表达式){
//如果关系表达式为 true,则执行语句体;否则不执行
语句体;
}
判断语句 2:if…else
if(关系表达式) {
//如果关系表达式为 true,则执行语句体1
语句体 1 ;
}else {
//如果关系表达式为 false,则执行语句体2
语句体 2 ;
}
判断语句 3:if…else if…else
if (判断条件1) {
//如果判断条件1的值为true,执行代码
执行语句1;
} else if (判断条件2) {
//如果判断条件2的值为true,执行代码
执行语句2;
}
...
} else if (判断条件n) {
执行语句n;
} else {
//如果以上判断条件都不为true,执行代码
执行语句n+1;
}
选择语句:switch
switch(表达式) {
case 常量值1:
语句体1;
break; //可选
case 常量值2:
语句体2;
break; //可选
...
default: //可选
语句体n+1;
break; // 最后一个break语句可以省略,但是强烈推荐不要省略
}
case的穿透性
在switch语句中,如果case的后面不写break,将出现穿透现象
即:匹配哪一个case就从哪一个位置向下执行,直到遇到了break或者整体结束为止
循环结构的基本组成部分,一般可以分成四部分:
循环语句 1:for
for(初始化语句; 布尔表达式; 步进语句){
循环体 //代码语句
}
循环语句 2:while
//格式 1:标准格式
while (条件判断) {
循环体
}
//格式 2:扩展格式
初始化语句;
while (条件判断) {
循环体;
步进语句;
}
循环语句 3:do…while
//格式 1:标准格式
do {
循环体
} while (条件判断);
//格式 2:扩展格式
初始化语句
do {
循环体
步进语句
} while (条件判断);
三种循环语句的区别
扩展 - 死循环
//死循环的标准格式:
while (true) {
循环体
}
//如何结束一个死循环:使用跳出语句
扩展 - 嵌套循环
// 格式:
for(初始化语句; 循环条件; 步进语句){
for(初始化语句; 循环条件; 步进语句){
执行语句
}
}
//总共的循环次数 = 外循环次数 * 内循环次数
break关键字
public static void main(String[] args) {
for (int i = 1; i <= 10; i++) {
// 如果希望从第4次开始,后续全都不要了,就要打断循环
if (i == 4) {
// 如果当前是第4次
break; // 那么就打断整个循环
}
System.out.println("Hello" + i);
}
}
continue关键字
public static void main(String[] args) {
for (int i = 1; i <= 10; i++) {
if (i == 4) {
// 如果当前是第4层
continue; // 那么跳过本次循环,马上开始下一次(第5层)
}
System.out.println(i + "层到了。");
}
}
快捷键 | 功能 |
---|---|
Alt+Enter | 导入包,自动修正代码 |
Ctrl+Y | 删除光标所在行 |
Ctrl+D | 复制光标所在行的内容,插入光标位置下面 |
Ctrl+Alt+L | 格式化代码 |
Ctrl+/ | 单行注释 |
Ctrl+Shift+/ | 选中代码注释,多行注释,再按取消注释 |
Alt+Insert | 自动生成代码,toString,get,set等方法 |
Alt+Shift+上下箭头 | 移动当前代码行 |
什么是方法?方法就是一段可以重复调用的代码
定义方法的格式:
修饰符 返回值类型 方法名 (参数列表){
代码...
return ;
}
调用方法的三种格式:
public class MethodDefine {
public static void main(String[] args) {
// 单独调用
sum(10, 20);
System.out.println("===========");
// 打印调用
System.out.println(sum(10, 20)); // 30
System.out.println("===========");
// 赋值调用
int number = sum(15, 25);
number += 100;
System.out.println("变量的值:" + number); // 140
}
public static int sum(int a, int b) {
System.out.println("方法执行啦!");
int result = a + b;
return result;
}
}
注意事项:
return 返回值;
”,不能没有return ;
”(可省略)无效代码
public static void method1() {
// return 10; // 错误的写法!方法没有返回值,return后面就不能写返回值。
return; // 没有返回值,只是结束方法的执行而已。
}
public static void method2() {
System.out.println("AAA");
System.out.println("BBB");
// return; // 最后一行的return可以省略不写。
}
public static int method3(int a,int b) {
return a + b;
// System.out.println("Hello");// 错误,return已经结束,这里不会执行,无效代码
}
tips:
- 对于有返回值的方法,可以使用单独调用、打印调用或者赋值调用。
- 但是对于无返回值的方法,只能使用单独调用,不能使用打印调用或者赋值调用。
public static void open(){
} // 正确重载
public static void open(int a){
} // 正确重载
static void open(int a,int b){
} // 代码错误:和第8行冲突 (与修饰符无关)
public static void open(double a,int b){
} // 正确重载
public static void open(int a,double b){
} // 代码错误:和第6行冲突
public void open(int i,double d){
} // 代码错误:和第5行冲突 (与修饰符无关)
public static void OPEN(){
} // 代码正确不会报错,但是并不是有效重载
public static void open(int i,int j){
} // 代码错误:和第3行冲突
//说明:方法重载与修饰符(public static)无关
public static void main(String[] args) {
// 省略格式的静态初始化
int[] arrayA = {
10, 20, 30 };
System.out.println(arrayA); // 获得的是【地址值】
/* 下方内容 【编译报错】,原因:
编译器会认为数组限定的元素个数[3]与实际存储个数{10,20,30}个数可能不一致,
存在一定的安全隐患。
*/
// int[] arrayA2 = new int[3] { 10, 20, 30 }; //错误写法!
// 静态初始化的标准格式,可以拆分成为两个步骤
int[] arrayB; //或 int arrayB[]; (效果相同,但不是首选方法)
arrayB = new int[] {
11, 21, 31 };
// 动态初始化也可以拆分成为两个步骤
int[] arrayC;
arrayC = new int[5];
// 静态初始化的省略格式,不能拆分成为两个步骤
// int[] arrayD;
// arrayD = { 10, 20, 30 }; //写法错误
}
使用动态初始化数组时,其中的元素将会自动拥有一个默认值。规则如下:
数据类型 | 默认值 |
---|---|
整数类型 | 0 |
浮点类型 | 0.0 |
字符类型 | ‘\u0000’ |
布尔类型 | false |
引用类型 | null |
tips:
- 数组有定长特性,长度一旦指定,不可更改。
- 使用建议:如果不确定数组当中的具体内容,用动态初始化;否则,已经确定了具体的内容,用静态初始化。
- 静态初始化其实也有默认值的过程,只不过系统自动马上将默认值替换成为了大括号当中的具体数值。
内存:是计算机中的重要原件,临时存储区域,作用是运行程序
Java虚拟机的内存划分(5部分)
区域名称 | 说明 |
---|---|
栈(Stack) | 存放的都是方法中的局部变量。方法的运行一定要在栈中执行 |
堆(Heap) | 凡是new出来的东西,都在堆当中 |
方法区(Method Area) | 存储.class相关信息,包括方法的信息 |
本地方法栈(Native Method Stack) | 与操作系统相关 |
寄存器(pc Register) | 与CPU相关 |
数组在内存中的存储
main方法
加载到栈内存
中,并为其开辟一个内存空间(main方法进栈)创建数组
,JVM在堆内存
中开辟空间,用来存储数组(凡是new出来的东西,都在堆中)默认值为0
内存地址
(十六进制的地址值)地址值
被JVM赋值
给变量array(new出的数组在堆中,变量在栈中)数组越界异常
ArrayIndexOutOfBoundsException
public static void main(String[] args) {
int[] array = {
15, 25, 35 };
// 错误写法:并不存在3号元素,所以发生异常
System.out.println(array[3]);
}
数组空指针异常
NullPointerException
public static void main(String[] args) {
int[] array = null;
// array = new int[3]; //补上new即可
System.out.println(array[0]);
}
数组遍历
public static void main(String[] args) {
int[] array = {
15, 25, 30, 40 };
// 首先使用原始方式
System.out.println(array[0]); // 15
System.out.println(array[1]); // 25
System.out.println(array[2]); // 30
System.out.println(array[3]); // 40
System.out.println("=================");
// 使用循环,次数其实就是数组的长度(array.length)
for (int i = 0; i < array.length; i++) {
System.out.println(array[i]);
}
}
数组最值
//数组获取最大值元素
public static void main(String[] args) {
int[] array = {
5, 15, 30, 20, 10000, 30, 35 };
// 定义变量,保存数组中0索引的元素
int max = array[0];
// 遍历数组,取出每个元素
for (int i = 1; i < array.length; i++) {
// 如果当前元素,比max更大,则max换成大值
if (array[i] > max) {
max = array[i];
}
}
// 输出最大值
System.out.println("最大值:" + max);
}
数组反转
public static void main(String[] args) {
int[] array = {
10, 20, 30, 40, 50 };
/*
初始化语句:int min = 0, max = array.length - 1
条件判断:min < max
步进表达式:min++, max--
循环体:用第三个变量倒手
*/
for (int min = 0, max = array.length - 1; min < max; min++, max--) {
int temp = array[min];
array[min] = array[max];
array[max] = temp;
}
// 打印遍历输出数组后来的样子
for (int i = 0; i < array.length; i++) {
System.out.println(array[i]);//50, 40, 30, 20, 10
}
}
数组作为方法的参数
public static void main(String[] args) {
int[] array = {
10, 20, 30, 40, 50 };
System.out.println(array); // 地址值
//调用方法,传递数组
printArray(array); // 传递进去的是array中保存的地址值
}
/*
三要素
返回值类型:只是进行打印而已,不需要进行计算,也没有结果,用void
方法名称:printArray
参数列表:必须给我数组,我才能打印其中的元素。int[] array
*/
public static void printArray(int[] array) {
System.out.println("printArray方法收到的参数是:");
System.out.println(array); // 地址值
for (int i = 0; i < array.length; i++) {
System.out.println(array[i]);
}
}
数组作为方法的返回值
public static void main(String[] args) {
int[] result = calculate(10, 20, 30);
System.out.println("main方法接收到的返回值数组是:");
System.out.println(result); // 地址值
System.out.println("总和:" + result[0]);
System.out.println("平均数:" + result[1]);
}
public static int[] calculate(int a, int b, int c) {
int sum = a + b + c; // 总和
int avg = sum / 3; // 平均数
// 两个结果都希望进行返回
// 需要一个数组,也就是一个塑料兜,数组可以保存多个结果
/*
int[] array = new int[2];
array[0] = sum; // 总和
array[1] = avg; // 平均数
*/
int[] array = {
sum, avg };
System.out.println("calculate方法内部数组是:");
System.out.println(array); // 地址值
return array;
}
tips:
- 任何数据类型都能作为方法的参数类型,或者返回值类型
- 方法的参数为 基本类型 时,传递的是 数据值;
方法的参数为 引用类型 时,传递的是 地址值