当前位置: 首页 > news >正文

计算机学习笔记 Java基础与底层原理详解③ 流程控制、大数值运算、数组与多维数组

📚 7月31日课程笔记下:流程控制、大数值运算、数组与多维数组

第一部分:流程控制

1.1 块作用域

定义:一对花括号{}定义的代码块,其内部的变量仅在该块内有效。

核心规则

  • 块内不能声明与外部同名的变量,否则会引发编译冲突
  • 块内声明的变量,作用域仅限于该代码块,执行到块结束的}时变量即被回收,之后无法访问
  • 嵌套代码块中同样不能声明同名变量,这与条件语句(如if判断)的作用域规则一致

比喻理解:就像在一个大房间里已经放了一盏灯(变量a),你不能在里面的小房间再放一盏同名的灯,否则就冲突了。

注意:这里的报错是因为变量重复声明,而不是因为没赋值。


1.2 分支语句:if / else

基本结构:根据条件判断执行不同分支的代码。

执行规则

  • 条件判断是从上往下依次执行
  • 只要满足某个条件,就执行对应的代码块并立即结束判断
  • 只有前面所有条件都不满足时,才会执行最后的else部分

⚠️ 条件顺序的重要性

处理连续范围时,条件的排列顺序至关重要。就像筛子要从大孔到小孔排列才有效:

  • ❌ 错误:先判断age > 20,再判断age > 80→ 80岁的人会被错误归入"大于20"
  • ✅ 正确:先判断age > 80,再判断age > 30,最后判断age > 20

范围判断的正确写法

想判断年龄在20到30岁之间,不能只写if(age > 20),必须同时满足两个条件:

if (age > 20 && age < 30) { System.out.println("青年"); }

if-else vs switch-case 的适用场景

  • if-else:适合范围判断(大于、小于、区间)
  • switch-case:适合精确值匹配(等于某个具体值)

1.3 分支语句:switch / case

适用条件:变量取值明确、有限,比如 a 只能是 5、10 或 15 这几个固定值。

执行流程:程序从上到下逐个比较 case,匹配到对应值后执行代码块,遇到break就立即退出整个 switch 结构。

switch 括号内允许的类型

  • charbyteshortint
  • 枚举值(枚举就是取值固定的类型)
  • ❌ 不能放自定义的复杂对象

⚠️ case 击穿现象

如果某个 case 匹配后没有写 break,程序会继续执行后面所有 case 的代码,直到遇到 break 或 switch 结束。就像打穿所有关卡一样。

  • default语句的 break 可以省略,因为已经是最后一个了
  • 当变量值不匹配任何 case 时,程序会执行default部分(处理意外情况的保险机制)

代码演示

int a = 10; switch (a) { case 5: System.out.println("五"); break; case 10: System.out.println("十"); // 匹配到这里 break; // 没有break就会继续往下执行! case 15: System.out.println("十五"); break; default: System.out.println("其他"); }

1.4 循环语句

① while 循环

特点:先判断条件,再执行循环体。如果初始条件就不满足,循环体一次都不会执行

int a = 10; while (a > 4) { System.out.println(a); a--; } // 输出: 10 9 8 7 6 5(a=4时不满足条件,直接退出)

② do-while 循环

特点:先执行一次循环体,再判断条件。即使初始条件不满足,也至少执行一次。就像"先上车后补票"。

int a = 4; do { System.out.println(a); // 先执行:打印4 a--; // a变成3 } while (a > 4); // 再判断:3>4为false,退出 // 输出: 4(虽然条件一开始就不满足,但还是执行了一次)

while vs do-while 对比

对比项whiledo-while
判断时机先判断后执行先执行后判断
最少执行次数0次1次
适用场景不确定是否需要执行至少要执行一次

③ for 循环

特点:用于已知循环次数的场景,包含初始化、循环条件和迭代三个部分。

结构说明

  • 初始化(如i=0)可以写在循环外部,但通常放在第一个分号前
  • 循环条件写在第二个位置
  • 迭代语句(如i++)既可以放在第三个位置,也可以移到循环体末尾,效果一样
  • 两个分号必不可少,但三个部分都可以省略(不写循环条件时要注意避免死循环)
// 标准写法 for (int i = 0; i < 10; i++) { System.out.println(i); } // 省略写法(分号不能少) int i = 0; for (; i < 10; ) { System.out.println(i); i++; }

④ for-each 循环

特点:用于遍历数组或集合,简化了索引操作。前面的变量会自动依次代表数组中的每个元素,从第一个到最后一个。

适用场景

  • 不确定数组长度时(如某些方法返回的数组),用 for-each 更安全,不用担心越界
  • 已知长度时用普通 for 循环更合适

⚠️ 重要限制:for-each 中的变量不能直接修改原数组的值。就像用放大镜观察标本,你能看到每个细节(打印数据),但无法用放大镜直接改变标本本身。

int[] arr = {1, 2, 3, 4, 5}; // for-each遍历(只读) for (int x : arr) { System.out.println(x); // x依次代表1,2,3,4,5 } // ❌ 这样不能修改原数组 for (int x : arr) { x = x * 2; // 只修改了临时变量x,arr不变! }

1.5 循环控制:break 与 continue

break

作用:直接终止整个循环,跳出循环体,执行循环后面的代码。

for (int i = 0; i < 10; i++) { if (i == 4) { break; // i=4时直接终止,5,6,7,8,9都不会执行 } System.out.println(i); } // 输出: 0 1 2 3
continue

作用:只跳过当前这一次循环的剩余代码,直接进入下一次循环。循环本身还会继续。

for (int i = 0; i < 10; i++) { if (i == 4) { continue; // i=4时跳过打印,但循环继续 } System.out.println(i); } // 输出: 0 1 2 3 5 6 7 8 9(跳过了4)
对比总结
对比项breakcontinue
效果彻底退出循环跳过本轮,继续下一轮
后续迭代不再执行仍然执行
比喻直接离开教室这节课不听,下节课还来

1.6 带标签的 break 与 continue(了解即可)

可以给外层循环设置一个标签,然后在 break 或 continue 后面加上标签名,控制具体跳出哪个循环。

outer: for (int i = 0; i < 5; i++) { for (int j = 0; j < 5; j++) { if (j == 3) { break outer; // 直接结束整个外层循环 // continue outer; // 跳过外层循环的本次迭代,进入i的下一轮 } System.out.println(i + "," + j); } }

注意

  • break outer:直接结束整个外层循环(不是内层)
  • continue outer:跳过外层循环的本次迭代,直接开始下一次外层循环
  • 实际开发中很少使用这种写法,了解即可

1.7 嵌套循环的执行过程

外层循环每执行一次,内层循环会完整执行一轮

for (int i = 0; i < 3; i++) { for (int j = 0; j < 4; j++) { System.out.println("i=" + i + ", j=" + j); } } // i=0时,j从0到3全部执行 // i=1时,j从0到3全部执行 // i=2时,j从0到3全部执行

注意:break 只会影响当前所在的循环层,不会跨层。


第二部分:大数值运算

2.1 基本类型的局限

  • 基础数据类型(int、float、double)存在数值范围精度问题
  • 64位系统最多只能用64个比特位来表示数据,超出范围运算结果就会出错
  • float 和 double 并不能精确表示所有小数,计算结果可能出现偏差
  • 即使是64位的 double 类型,也有它的表示上限

典型问题:货币运算等需要精确计算的场景,不能用 float 或 double。


2.2 BigInteger 与 BigDecimal

用途
BigInteger大整数运算(超出long范围的整数)
BigDecimal高精度小数运算(如货币计算)

2.3 创建方式

两种方式

  1. valueOf() 方法 / 直接赋值:数值必须在 long 类型的范围内,否则报错
  2. new + 字符串参数:可以传入超出 long 范围的字符串,表示超大数值
// BigInteger BigInteger a = BigInteger.valueOf(100); // 方式1:范围内 BigInteger b = new BigInteger("99999999999999999999"); // 方式2:超范围用字符串 // BigDecimal(推荐用字符串创建,避免精度问题) BigDecimal c = new BigDecimal("3.14159265358979323846"); BigDecimal d = BigDecimal.valueOf(3.14);

2.4 运算方式

核心规则:不使用传统的+-*/运算符,而是调用相应的方法进行运算。

就像不能直接用 + 号把两个箱子相加,而要打开箱子取出里面的东西才能计算。

BigInteger a = new BigInteger("100"); BigInteger b = new BigInteger("200"); BigInteger sum = a.add(b); // 加法 BigInteger diff = a.subtract(b); // 减法 BigInteger product = a.multiply(b); // 乘法 BigInteger quotient = a.divide(b); // 除法 System.out.println(sum); // 300

注意事项

  • 方法签名很关键:前面是返回类型,括号里是参数类型
  • 接收结果时要匹配返回类型(如 add() 返回 BigInteger 就要用 BigInteger 接收)
  • 参数传递必须严格按方法要求的类型来
  • BigDecimal 同理,所有运算都通过方法调用实现

第三部分:数组

3.1 基本概念

  • 数组是一种数据结构,用于存储同一种类型的数据
  • 元素在内存中连续存放
  • 访问元素通过从0 开始的下标
  • 声明数组时必须确定长度(这是原生数组的特点)

3.2 数组初始化

多种写法

// 方式1:声明时直接赋值 int[] a = {1, 2, 3, 4, 5}; // 方式2:new关键字创建(指定长度,元素有默认值) int[] b = new int[100]; // 100个元素,默认值都是0 // 方式3:new + 赋值 int[] c = new int[]{5, 7, 4, 10}; // 匿名数组(不赋值给变量,只能临时使用,用完就扔) new int[]{1, 2, 3}; // 必须用new,直接用括号不行

各类型的默认初始值

类型默认值
int0
double / float0.0
booleanfalse
String / 引用类型null

注意int[] a只是定义了一个数组变量,只有用new int[100]初始化后,才真正创建了存储空间。要区分声明实际创建数组的区别。

补充:Java 提供了更灵活的ArrayList,底层虽然是数组,但能自动扩容、动态调整大小。


3.3 数组拷贝

浅拷贝(等号赋值)

本质:传递的是地址值。两个变量指向同一块内存空间,修改其中一个会影响另一个。

int[] small = {1, 2, 3}; int[] luckyNumbers = small; // 浅拷贝:只复制了引用地址 luckyNumbers[0] = 99; System.out.println(small[0]); // 99(原数组也变了!)

比喻:两个人拿着同一把钥匙开同一个保险箱,一个人往里面放了东西,另一个人打开时自然能看到变化。


深拷贝(创建新数组)

本质:创建一个全新的数组,将源数组的每个元素值复制到新数组中。两个变量指向独立的空间,互不影响。

int[] arr = {5, 7, 4, 10}; int[] brr = new int[arr.length]; // 新开辟内存空间 // 手动逐个复制 for (int i = 0; i < arr.length; i++) { brr[i] = arr[i]; } brr[0] = 99; System.out.println(arr[0]); // 5(原数组不受影响)

比喻:就像复印文件,原文件和复印件是完全独立的。


Arrays.copyOf() 方法(实现深拷贝)
int[] arr = {5, 7, 4, 10}; int[] copy = Arrays.copyOf(arr, arr.length); // 参数:源数组, 拷贝长度 copy[0] = 99; System.out.println(arr[0]); // 5(互不影响,是深拷贝)

底层原理

  • 传入参数20,就会创建一个长度为20的新数组,然后依次复制原数组的值
  • 如果原数组长度不够,多出来的位置会自动补零
  • 本质上和手动遍历复制是一样的

3.4 数组遍历

int[] arr = {1, 2, 3, 4, 5}; // 方式1:普通for循环(已知长度) for (int i = 0; i < arr.length; i++) { System.out.println(arr[i]); } // 方式2:for-each(不确定长度时更安全) for (int x : arr) { System.out.println(x); }

3.5 命令行参数(args)

  • main方法里的args参数就是一个字符串数组,用来接收外部传入的参数
  • 在 IDE 里直接运行时,args 是空的
  • 通过命令行执行 jar 包时,可以在命令后面添加参数传入
  • 不知道 args 有多长时,用 for-each 遍历最方便
// 遍历命令行参数 for (String a : args) { System.out.println(a); }

3.6 Arrays 类常用方法

方法功能说明
Arrays.sort(arr)排序底层采用快速排序算法,默认从小到大
Arrays.toString(arr)转为字符串方便打印数组内容
Arrays.fill(arr, val)填充把所有元素批量修改为指定值
Arrays.equals(arr1, arr2)比较比较两个数组内容是否相同
Arrays.copyOf(arr, len)深拷贝创建新数组并复制元素
Arrays.binarySearch(arr, key)二分查找需传入起始和结束下标

代码演示

int[] arr = {4, 1, 3, 2}; // 排序(从小到大) Arrays.sort(arr); System.out.println(Arrays.toString(arr)); // [1, 2, 3, 4] // 填充 Arrays.fill(arr, 1); System.out.println(Arrays.toString(arr)); // [1, 1, 1, 1] // 比较(比较内容,不是地址) int[] a = {1, 2, 3}; int[] b = {1, 2, 3}; System.out.println(a == b); // false(地址不同) System.out.println(Arrays.equals(a, b)); // true(内容相同)

⚠️ 重点

  • 直接用==比较两个数组判断的是内存地址是否相同,往往不是我们想要的结果
  • 正确做法是调用Arrays.equals(),它重写了 equals 方法,比较的是实际内容
  • 因为是 static 方法,可以直接通过类名Arrays调用,不需要创建对象
  • 只要两个数组的下标和对应值完全相同,就判定为相等

从大到小排序:Java 没有直接的从大到小 sort 方法,可以用循环倒序遍历来实现。实际编程中从小到大排序更常用。


第四部分:多维数组

4.1 基本概念

  • 多维数组本质上就是一维数组的嵌套
  • 一维数组里存放数组 → 二维数组
  • 二维数组里再嵌套数组 → 三维数组,以此类推
  • 就像俄罗斯套娃:外层是容器,内层才是实际内容

4.2 内存结构

  • 二维数组在内存中并非完全连续存储
  • 只有每个子数组内部是连续存放的
  • 外壳数组存储的是各个子数组的地址引用
  • 子数组里直接存放具体数值

数组长度:由最外层元素数量决定。比如外壳数组包含4个子数组,长度就是4。


4.3 访问方式

通过多层下标访问元素,像剥洋葱一样层层深入:

int[][] arr = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}; // arr[i][j]:i是外层数组的索引,j是内层数组的索引 System.out.println(arr[1][2]); // 6(第2个子数组的第3个元素)

4.4 遍历(双重 for-each)

for-each 循环只能处理一维数组,遍历二维数组需要嵌套循环

int[][] arr = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}; // 外层循环:i依次代表每个子数组(i本身又是一个数组) for (int[] i : arr) { // 内层循环:处理子数组里的每个元素 for (int j : i) { System.out.print(j + " "); } System.out.println(); } // 输出: // 1 2 3 // 4 5 6 // 7 8 9

三维数组同理,无非是多套一层循环。


4.5 不规则数组

定义:内层数组长度不一致的二维数组。

原理:存储的是指向不同长度数组的地址,所以每行长度可以不同。

// 三角形结构:第1行长度1,第2行长度2,第3行长度3 int[][] triangle = { {1}, {2, 3}, {4, 5, 6} }; // 遍历(同样用双重for-each,自动适配不同长度) for (int[] row : triangle) { for (int val : row) { System.out.print(val + " "); } System.out.println(); } // 输出: // 1 // 2 3 // 4 5 6

应用场景:矩阵运算、图形打印等需要动态调整每行元素数量的场景。


4.6 deepToString 方法

用于将多维数组转为字符串表示,内部逻辑就是逐层遍历数组元素进行字符串拼接:

int[][] arr = {{1, 2}, {3, 4}}; System.out.println(Arrays.deepToString(arr)); // [[1, 2], [3, 4]]

http://www.jsqmd.com/news/1304579/

相关文章:

  • 化工园区做AR安全巡检推荐哪家供应商
  • GPT-5.5技术前瞻:从架构创新到开发者应对策略
  • 2026年长沙自流平厂家推荐榜:环氧/防静电/导静电地坪源头施工品牌精选 - 优企名品
  • 2026短剧IP孵化哪家机构实力更强?深度评测 - 品牌排行榜
  • 从语言模型到行动智能:Mythos如何让AI从“会说”到“会做”
  • 2026年江苏常州升学规划综合评价榜单:强基计划/高考志愿/港澳本科/出国留学申请,高一选科与中外合办院校申请全案解析 - 优企名品
  • 西门子SCL批量处理模拟量信号:从数据分离到工程实践
  • 终极静音方案:TPFanControl2让你的ThinkPad风扇不再吵闹
  • 抖音批量下载完整指南:开源工具如何帮你轻松管理海量视频资源
  • CLI 工具的配置文件管理方案:多层级配置合并的工程实践
  • 2026工业吸尘器品牌哪家强?快来看看这份榜单 - 品牌排行榜
  • OpenAI收购Codex:AI编程助手迈向“永不下线”时代的技术解析与应对策略
  • 2026年动物实验找哪家做比较好?看这篇就够了! - 品牌排行榜
  • JDK23 安装包(附安装教程)
  • 06-数据库学习笔记(存储模型与数据压缩)
  • 2026年工业网线厂家推荐榜单:高柔耐弯折拖链网线,屏蔽千兆超六类及相机机器人自动化设备网线定制优选 - 优企名品
  • 工业级PID控制器C语言实现:从离散化到抗饱和与参数整定
  • 2026年,我需要做小鼠动物实验,找哪家? - 品牌排行榜
  • 2026年 防腐涂料源头厂家推荐榜:环氧树脂/玻璃鳞片/耐高温/聚氨酯/防静电 工业重防腐定制实力派 - 优企名品
  • AI考勤失真、会议低效、知识沉没——混合办公管理三大黑洞,90%企业尚未察觉,今天必须修复
  • 2026年推荐电梯节能设备厂家 - 品牌排行榜
  • 2026年8月东莞展示盒纸类包装/彩色标签纸类包装厂家怎么选_东莞市时岱包装制品有限公司 - 行业平台推荐
  • FPGA设计核心:Xilinx缓冲器原理、应用与调试指南
  • 重塑物理世界的数字镜像:视频三维重构迈向全域透明化管理研发课题方案
  • 从零到一:原神脚本的三种玩家成长路径指南
  • 2026年8月冲天炉炉衬可塑料/扬州电炉炉衬捣打料行业公司推荐_扬州吉能耐火材料有限公司 - 行业平台推荐
  • 宁波验厂服务内容及办理准备事项 - 品牌排行榜
  • 推挽与开漏输出电路原理详解:从MOSFET结构到I2C总线应用
  • Claude Opus 4.8深度评测:顶尖AI的推理能力与成本控制实战
  • 技术团队协作:从事故复盘到高效沟通的工程化实践