计算机学习笔记 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 括号内允许的类型char、byte、short、int枚举值枚举就是取值固定的类型❌ 不能放自定义的复杂对象⚠️ 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 5a4时不满足条件直接退出② do-while 循环特点先执行一次循环体再判断条件。即使初始条件不满足也至少执行一次。就像先上车后补票。int a 4; do { System.out.println(a); // 先执行打印4 a--; // a变成3 } while (a 4); // 再判断34为false退出 // 输出: 4虽然条件一开始就不满足但还是执行了一次while vs do-while 对比对比项whiledo-while判断时机先判断后执行先执行后判断最少执行次数0次1次适用场景不确定是否需要执行至少要执行一次③ for 循环特点用于已知循环次数的场景包含初始化、循环条件和迭代三个部分。结构说明初始化如i0可以写在循环外部但通常放在第一个分号前循环条件写在第二个位置迭代语句如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; // 只修改了临时变量xarr不变 }1.5 循环控制break 与 continuebreak作用直接终止整个循环跳出循环体执行循环后面的代码。for (int i 0; i 10; i) { if (i 4) { break; // i4时直接终止5,6,7,8,9都不会执行 } System.out.println(i); } // 输出: 0 1 2 3continue作用只跳过当前这一次循环的剩余代码直接进入下一次循环。循环本身还会继续。for (int i 0; i 10; i) { if (i 4) { continue; // i4时跳过打印但循环继续 } 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); } } // i0时j从0到3全部执行 // i1时j从0到3全部执行 // i2时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 创建方式两种方式valueOf() 方法 / 直接赋值数值必须在 long 类型的范围内否则报错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}; // 方式2new关键字创建指定长度元素有默认值 int[] b new int[100]; // 100个元素默认值都是0 // 方式3new 赋值 int[] c new int[]{5, 7, 4, 10}; // 匿名数组不赋值给变量只能临时使用用完就扔 new int[]{1, 2, 3}; // 必须用new直接用括号不行各类型的默认初始值类型默认值int0double / float0.0booleanfalseString / 引用类型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]); } // 方式2for-each不确定长度时更安全 for (int x : arr) { System.out.println(x); }3.5 命令行参数argsmain方法里的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-eachfor-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]]

相关新闻