CS109_计算机程序设计基础笔记
ChenGou
1 引言
1.1 写在前面
关于笔记内容 :
Java本身课程内容相对固定, 不会出现 C语言因为只有一名老师任教所以教学内容变动较 大的情况, 值得注意的是, C语言设计内容相对较底层, 例如指针和链表, 学习 Java 的时候可以有意识地拓展知识面. 本学习笔记中的内容覆盖面相对较广, 注意筛选考试重点内容.
选修本门课程应该做什么?
强调课上提到的细节 了解流行的技术框架和高级生产型工具, 使用 gradle 和 jpackage 合理应用 AI (灵感介绍, 项目评审, 前端组件) 体会自己学习互联网技术的感受, 认清自己的课程定位和目标 加入 JavaA 互助课堂群 (QQ code : 811125093
笔记开源说明 :
部分基于 lhh 学长 24Spring计算机程序设计基础总评A 笔记 删除了浅显的示例代码、重复的语言说明, 添加了大量私人内容 因为笔记习惯较差的原因, 最后应付考试和新学习的时候内容整理不良.
联系作者 :
Github 个人网站 SUSTech Email : 12412903@mail.sustech.edu.cn QQ code : 1339495928
一些建议 :
尽早尽快地勇敢地踏上自学之路, 基本概念不需要做笔记, 学明白了再回顾理解, 希望你能够把这份笔记作为一份预习的索引或者是学习的材料, 正如我尽力提高他的质量, 而不是仅仅作为一份旧时代的笔记使用. 期末考试成绩和日常学习关系不大, 满绩与否仅仅关联你应付考试的能力. 这在大学里面太重要了, 也太令人悲伤了.
License :
CS109_计算机程序设计基础笔记 © 2025 by 陈勾 is licensed under CC BY-NC-SA 4.0. To view a copy of this license, visit.
最后更新时间 : 2025年6月17日20:40:47
2 考试八股内容
2.1.1 基本概念理解
pseudocode program : set of instruction algorithm entry point : where the program start to run, main{}即程序入口 one instruction is one statement(逻辑上的原子性而非物理行数)(声明时允许连写, 但是赋值必须单体) declare a variable : 变量储存数据 reference datatype : 数据是对象在内存中的地址, 即reference而非具体的数据值 literal 就是具体数据值 initialize a variable identifier : 唯一标识符 String : sequence of characters literal value : 程序中直接写出的不可变的数据值, 其他类型包括输入台读取和系统文件读取 variable : 储存数据的地方 symbolic reference : 关联 variable 和数据
Instantiation : 实例化(创建类变量): new : call a constructor argument 是实际参数,即传给方法的值,值传递或者引用传递 parameter 是形式参数,即函数中声明的参数名,在方法中使用
变量遮蔽:局部变量会覆盖类的字段。
2.1.2 汇总介绍
引用赋值Reference Assignment指的是对assign a variable with reference type 如果要避免使用相同的地址/reference, 需要值传递, 深拷贝, clone, 或者构造器new重新创建等等.
在Java中,所有对象都是引用类型,而不是基本数据类型。注意应当避免为不同变量赋相同引用值, 指向同一个对象.
方法1:使用 new String() 构造函数 方法2:使用 String.valueOf() 方法3:使用 String 的 substring() 方法4:使用 String 的 replace()
2.1.3 易错点
concrete class 不能包含 abstract method Arithmetic Exception才是除零错误. Scanner对象获取输入 printf会自动将float四舍五入到double, 所以精度还可以接受 byte, short, char 是同类型, 强制类型转换, 表达式中都是int switch可以是string, 不可以是浮点数(过高精度) ArrayList自动扩容, 使用.get(index), Arrays.deepToString()用于打印多维数组
An individual array element of type int, when passed to a method, is pass-by-value.
- 应该使用
.equals()
方法比较字符串内容。 - == 比较的是引用地址而不是字符串内容
Java 的字符串常量池是一个特殊的内存区域,用于存储字符串字面量。当使用字面量创建字符串时,Java 会在常量池中查找是否存在相同的字符串。如果存在,直接返回常量池中字符串的引用, 通过new创建的则是独立的
ArrayList每次将容量增加到原来的1.5倍
FINAL类不能被继承但是方法不全都是final, 本身不能有子类, 所以final无法被覆盖. printf四舍五入到double
静态方法需要通过类名调用,或者在静态上下文中直接调用。 不能仅通过方法名调用(invoke by using the name of method alone)。避免上下文和作用域歧义
override 允许协变返回类型(covariant return types),但参数列表必须相同。 子类中override重写的方法权限可以更宽松 私有成员不会被继承 final类中方法默认final, 不需要显示声明, 可以继承不能被重写 可以通过 double除法转换为 int
printf自动四舍五入 for循环中应该更新 i, 不只是算术表达式
- 静态方法中不能使用
this()
,因为没有实例上下文 - 不能被实例化意味着不能调用constructor, 但是可以作为父类? 多态的经典用法, 父类引用指向子类对象。
- 勇敢指出输入数组越界问题.args[]
3 开始学习之前
3.1 计算机结构
VonNeumann architecture: 三大原则 即采用二进制逻辑、程序存储执行以及电子计算机系统由五个部分组成(运算器、控制器、存储器、输入设备、输出设备,其中运算器与控制器又共同组成为中央处理器CPU) 这种结构要求数据必须先从输入设备写入存储器,CPU再从存储器读取数据进行运算,CPU不能直接从输入设备读取数据,同样CPU的运算结果也不能直接写入到输出设备,而是要先写入存储器,再从存储器中将结果刷新到输出设备
3.2 Java套件
JRE(Java Runtime Environment,Java 运行时环境)
- JVM(Java 虚拟机,负责执行字节码)
- 核心类库(如
java.lang
,java.util
等) - 其他运行时支持文件 JDK(Java Development Kit,Java 开发工具包)
- JRE(所以 JDK 自带运行环境)
- 编译器(
javac
,将.java
编译成.class
) - 调试工具(如
jdb
) - 开发工具(如
javadoc
、jconsole
) - 标准类库源代码(如
src.zip
)
属于JRE的Java启动工具负责与命令行交互, 检查命令: java -version javac -version javac main.java java main
3.2.1 常识
IDE : Integrated Development Environment, 编辑编译调试和图形用户界面工具
原始数据类型不指向变量,所以这些变量不能用于调用方法. Array element 的处理方式是数据副本吗? 变量必然有其值, 默认值为0 或者为null
3.2.2 IDE缩写与快捷键
psvm ->
public static void main(String[] args) { }
sout ->
System.out.println();
souf ->
System.out.printf("");
fori ->
for (int i = 0; i < ; i++) { }
Ctrl + Alt + L:调整格式
Ctrl + D:复制该行
Ctrl + Shift + F10:编译并运行
Ctrl + Shift + Enter:补全代码括号结构
Alt + Insert:类中生成
Alt + Enter:导入头文件/生成函数(即补全缺失的)
Ctrl + Shift + <- backspace:跳回前一个调用的地方
Ctrl + W:扩展选区
3.2.3 编程规范
3.2.3.1 命名习俗
E - Element 元素(used extensively by the Java Collections Framework)
K - Key 键
N - Number 数值
T - Type 类型
V - Value 值
S,U,V etc. - 2nd, 3rd, 4th types 第二种,第三种,第四种类型
避免MagicNumber问题
3.2.3.2 Final语句
- 用于定义常量,或者说值不再改变的量。
- final变量一定要在对象构造完成之前被初始化。
- 声明时初始化
- 在构造方法中初始化。
- 具有某种意义的数据:理解意义,维护修改
- 循环条件N=20898, final int WIDTH = 20898;
- 具有重要意义的常数:PI
- 特殊算法中的功能用指标
- 表示格式的识别符
3.2.3.3 作用域与声明:缩进、括号
Java非缩进语言的特性,依赖{};和()表示条件 这实际上是表示作用域,提升可读性的方式。
声明的简化写法
var a = new StringBuilder
变量类型不能通过 Python 方式重复声明改变,因为 Java 是静态类型语言,变量类型在编译阶段已经确定,仅用于指示 JVM 处理。
改变变量类型的方法:
- 使用原始父类行Object() (来自java.lang)
- 使用泛型例如ArrayList或者自定义泛类
- 方法重载:需要根据数据类型执行不同操作时
- 代码块作用域局限,所以可以重复命名
变量的内存回收机制
- 局部变量:当局部变量的作用域结束时(执行结束),其内存空间会被自动释放。
- 成员变量:成员变量的内存空间与对象的生命周期绑定。
- JVM垃圾回收主要清理堆内存中的无印用对象。
不能重复声明同名变量在于物理内存地址和变量名称的作用域内唯一确定关系
3.2.3.4 命名规则 LexicalRules
命名对象 | 规则 |
---|---|
类名 | 大驼峰(UpperCamelCase) |
对象, 实例, 变量名 | 小驼峰(lowerCamelCase) |
方法名 | 小驼峰(lowerCamelCase) |
常量, 枚举类 | 全大写 + 下划线(SNAKE_CASE) |
包名 | 全小写,倒置多级用点分隔 |
接口 | 形容词 |
变量命名Syntax
- 首字符:字母(
A-Z/a-z
)、_
或$
(兼容旧版本工具, 其他特殊符号有用处如编译器指令) - 后续字符:可加数字(
0-9
) - ❌ 不能以数字开头(区分数字字面量 Literal value)
- 支持Unicode的非ASCII字符
- 经过编译后, 变量名被替换成符号引用, 不影响JVM使用字节码执行
依附于对象的技能,可以直接访问对象的成员变量,必须依附类存在
- 将业务逻辑封装成方法(提高内聚性)
- 将通用工具写成函数(提高复用性)
3.3 其他编程知识
3.3.1 运行时间的确定
使用 Java 自带的 os 库获取系统运行时间就好了,但是运行时间不是固定的,由系统的调度决定时间的长短,相关内容见线程部分
System.currentTimeMillis();
3.3.2 生成随机数
import java.util.Random;
Random r = new Random();
n = r.nextInt(10); // 随机生成0-9整型
n = r.nextInt(10)+1; // 随机生成1-10整型
n = r.nextDouble();//(0,1)高精度小数
//想要扩大范围,(int)(n*5)正确, (int)n = 0;
随机整数 a~b(包含a和b) | r.nextInt(b - a + 1) + a |
随机浮点数 a~b(左闭右开) | r.nextDouble() * (b - a) + a |
伪随机数算法的初始值决定结果, 称为随机种子seed | |
Java 会使用系统时间(毫秒级)作为初始种子 |
3.3.3 Junit
JUnit 提供了一个清晰的框架来帮助开发者编写和维护他们的单元测试,这在持续集成和测试驱动开发的过程中尤为重要。用于进行回归测试.
3.3.4 StdDraw画图
- Color类的使用方法
- StdDraw类:静态方法
4 快速入门
4.1 输入与输出
import java.util.Scanner;
Scanner input = new Scanner(System.in);
input.close(); // 关闭输入流,如果没有关闭,会随着程序关闭而关闭
a = input.next(); // 读入字符串
b = input.nextInt(); // 读入并转化为整型
c = input.nextDouble(); // 读入并转化成浮点型
d = input.next().charAt(0); // 读入字符串的第一个字符
数据类型与格式 | 占位符 |
---|---|
byte, short, int, long(decimal) | %d |
float, double | %f |
char | %c |
String | %s |
boolean | %b |
保留 n 位小数(四舍五入) | %.nf |
左对齐20字符 | %-20f |
4.1.1 初始数据类型 Primitive Data Types
- 注意如果long定义的时候,赋的常数值大于了int类型,则必须加L,字面量标记
- float要以f结尾,默认为double
- char 16-bit Unicode character, 取值范围:0 到 65535. 4 位 16 进制最大值注意使用单引号
- 各个进制需要进制识别符标记引导(0b,0,0x) 012 == 10
- 这种标记用于 JVM 识别,不同于ASCII码,JVM均作为数字处理,在内存地址中都是二进制 直接映射到 JVM 的操作指令和栈帧中的操作数栈, 是最接近硬件的数据类型。
具体来说,0.1 在二进制中是一个无限循环小数,无法精确存储。float 类型的 0.1f 已经是一个近似值,而将其转换为 double 类型时,这个近似值会被更精确地存储。但由于 double 的精度更高,存储的实际上是 float 类型值的二进制近似值,这可能导致输出时显示更多的小数位,从而看起来像是一个奇怪的小数。
在输出时指定小数位数 public class Main { public static void main(String[] args) { float f1 = 0.1f; double f2 = f1; System.out.printf("%.1f%n", f2); // 输出:0.1 } }
4.1.2 数据类型转换
- Type Cast:
- 注意:强制数据类型转换只会创造一个a的浮点型临时副本,不改变a本身的数据类型
- 注意:强制数据类型转换的优先级高于二元运算符"/",所以实际上是double除以一个int
- Type Promotion:
- 在上述实例中,b会被临时提升成double,使结果也为double,所以b本身的数据类型不会发生改变
4.1.3 运算符
运算符 | 含义 |
---|---|
+ | 加 |
- | 减 |
* | 乘 |
/ | 整除(与C++一样,取决于参与运算的数据类型) |
% | 求余 |
++ | 自增 |
– | 自减 |
==,!= | 等于、不等于 |
>,<,>=,<= | 大于、小于、大于等于、小于等于 |
&& | 逻辑与 |
|| | 逻辑或 |
! | 逻辑非 |
+=,-=,*=,%=,=/ | 复合赋值运算符 |
?: | 三目运算符 |
^ | 逻辑异或 |
Math.pow(a,b) | a底数,b幂次 |
~ | 按位取反 |
优先级 | 运算符类型 | 运算符示例 | 结合性 |
---|---|---|---|
1(最高) | 括号、成员访问 | () , [] , . (点号) | 左→右 |
2 | 一元运算符 | ++ , -- , ! , ~ , + , - | 右→左 |
3 | 乘除取模 | * , / , % | 左→右 |
4 | 加减 | + , - | 左→右 |
5 | 位移 | << , >> , >>> | 左→右 |
6 | 关系比较 | < , <= , > , >= , instanceof | 左→右 |
7 | 相等比较 | == 与 != | 左→右 |
8 | 位与 | & | 左→右 |
9 | 位异或 | ^ | 左→右 |
10 | 位或 | | | 左→右 |
11 | 逻辑与 | && | 左→右 |
12 | 逻辑或 | | | 左→右 |
13 | 三元条件 | ? : | 右→左 |
14(最低) | 赋值运算符 | = , += , -= , *= , /= 等 | 右→左 |
一元逻辑运算符右结合, 而且往往是最低优先级 |
区别数据类型转换和变量类型转换 只能低级向高级自动promote,高级向低级可能会有数据损失,只能强制转换。 允许丢失精度但是不允许溢出,long 可以转换为int,相当于瘦身删除不用部分,编译时不会报错但是会产生错误 操作整型数据时最好统一使用 int
逻辑运算优先:非,与,异或,或
短路现象:当从左执行时,逻辑结果已经确定,则右侧的不会执行了(短路了),右侧的赋值语句也不会执行
短路求值发生在 &&
(逻辑与)和 ||
(逻辑或),而不是单符号 &
或 |
前后缀实际上是运算顺序,a= i++; 先获取当前值,然后 i++;
4.1.4 位运算符
运算符 | 含义 |
---|---|
& | 按位与 |
| | 按位或 |
^ | 按位异或(相同0,不同1) |
~ | 按位非(一元) |
« | 按位左移,低位补0,原数2倍 |
» | 按位右移,正数高位补0,负数高位补0 |
»> | 去掉符号左移/右移,负数爆炸 |
原因在32位操作系统中前方有默认的0,所以左方补0后翻转会爆炸多 | |
补码:负数的补码是其绝对值的二进制表示取反后加1。 |
单符号运算符的使用场景
- 位运算(Bitwise Operations)
- 对整数类型的每个二进制位直接操作:
&
(按位与):两位均为1时结果为1|
(按位或):任一位为1时结果为1- 常用于权限控制、标志位处理等底层操作。
- 对整数类型的每个二进制位直接操作:
- 非短路逻辑运算(Non-Short-Circuit Evaluation)
- 即使左操作数已能确定结果,仍会计算右操作数:
&
:左右均需为true
才返回true
|
:左右任一个为true
即返回true
- 适用场景:
- 需要强制执行右操作数的副作用(如方法调用、变量修改)。 掩码, 组合标志位, 程序密集状态检查的效率 区别操作和运算
- 即使左操作数已能确定结果,仍会计算右操作数:
4.1.5 流程控制
循环结构
- for : for expression; (默认执行第一条语句, 根据 ; 判断循环体范围)
- 注:据说++i会相对i++优化一些,因为不用临时内存空间,而二者表型一样
- do-while
- for(int x : 数组名)
- continue
- break
- for : for expression; (默认执行第一条语句, 根据 ; 判断循环体范围)
条件语句
- if-else
- switch-case
- 注:先进行匹配,如果有匹配则转入对应的case,如果没有匹配,则转入default。一旦进入case/default,则会一直进行后续语句,直至遇到break。
- 执行匹配:找到第一个匹配的case后,开始执行该case下的代码
- 穿透现象:如果没有break,会继续执行后续所有case的代码(包括default) , 穿透设计特性
- 只能是整数类型、字符,绝对不能是实数。
- 浮点数据类型设计原因会有精度问题,不适合精确匹配
- switch设计初衷为离散确定有限量,底层原理跳转表(比较次数少)依赖整数连续与精确
- 效率:switch使用跳转表(对于密集值)或二分查找(对于稀疏值),效率通常比if-else链高, 方便各个分支共享代码
- 只能是整数类型、字符,绝对不能是实数。
- 可读性:适合多分支等值比较的场景(基础物理实验等精度测量)
\\新版本语法, 默认不穿透
String result = switch (day) {
case "M", "W", "F" -> "MWF";
case "T", "TH", "S" -> "TTS";
default -> {
if(day.isEmpty()) yield "Please insert a valid day.";
else yield "Looks like a Sunday.";
}
};
4.1.6 错误控制与管理
- assertion : assert boolean : {};//如果条件为假会抛出异常
- try-catch :
- try{}catch(E e){System.out.println(""+e.getMessage());}
- throw new E("");
- 类里面的常量:Double.POSITIVE_INFINITY / Double.NaN
- try-catch-finally, 最后常用于资源释放
4.2 数据类型
4.2.1 数组 Array
数组方法/属性 | 描述 |
---|---|
c.length | 返回数组的长度。 |
c.clone() | 返回数组的副本,新副本的地址与原数组不同(直接赋值则地址相同)。 |
c.equals(hlh) | 比较两个对象的地址是否相同。 |
c.notNull() / c.null() | 检查对象是否不为 null 或是否为 null (注:c.null() 不是标准方法,可能是误写,通常使用 c == null 或 c != null 来判断)。 |
数组用
.length
(属性,无括号)集合用
.size()
(方法,有括号)array 数组可以包含基本类型和对象类型或者引用类型, arraylist只有对象类型
二维数组:
- 每个元素都是一维数组的地址
- 注:二维数组定义时一定要定义行数,但不一定需要列数。
- 这种设计主要是为了提供灵活性和方便性,允许锯齿数组,同时保持数组的结构特性
- 相当于空置位置,但是需要后续定义
arr[0] = new int[3];
需要注意的方法
deeptoString() 和 toString()方法
for (int e : arr[1]) //for-each 增强型 for 循环
int[] s = new int[2]{1,2};//这种写法不伦不类显然是错误的
4.2.2 字符与字符串
- 字符
- 数据类型转换
- java中unicode是包含了Ascii码表的。所以把字符强制数据类型转换为int,会变成ASCII码值(十进制)。(cpp同理)
- 若要把Unicode变成字符,表示为
\u1000
十六进制数字
- 数据类型转换
- 字符串
- String是reference type,指向一段储存了字符串的内存
- String具有不可变性,任何的modification都会创建一个新的String对象。
- 实例化/创建
- 也可以用字面常量(string literals)创建
- 示例:
String s1 = new String("Hello world!"); String s2 = new String(); // 空字符串 String s3 = new String(s1); // 会建立一个新的 String s4 = new String(charArray); String s5 = new String(charArray,3/*出发点offset*/,2/*数几个count*/); String s1="Hello world!"; String s2="Hello world!"; // 这里与语句String s2=s1;等价 String s1 = "Java"; String s2 = "Java"; String s3 = new String("Java"); String s4 = new String("Java"); System.out.println(s1 == s2); // true System.out.println(s3 == s4); // false
4.2.2.1 操作
- 字符串拼接
- 用 + 可以拼接两个string或者string和char
缺点:效率很低,空间占用率较大
- 字符串相等比较
- 对于reference type,如果使用 == 来比较两个字符串,则当且仅当指向的内存块相同的时候,才能相同。
- 如果使用 s.equals(ss) 则是比较内容是否匹配
- 字符串大小比较
- 从index从小到大,挨个比较ASCII码值/unicode编码
- 由于空字符是
\0
,所以它比其他的字符都要小 - 空字符是系统标识用、不可打印不可见,空格ASCII 32,可打印 - 同时null是无内存地址,无空间分配
- 对于reference type,如果使用 == 来比较两个字符串,则当且仅当指向的内存块相同的时候,才能相同。
- 如果使用 s.equals(ss) 则是比较内容是否匹配
- 字符串大小比较
- 从index从小到大,挨个比较ASCII码值/unicode编码
- 由于空字符是
String方法名 | 描述 |
---|---|
length() | 返回字符串的长度。 |
charAt(int index) | 返回指定位置的字符。 |
substring(int beginIndex, int endIndex) | 返回字符串的一个子字符串。 |
equals(Object anObject) | 比较字符串的内容是否相等。 |
indexOf(String str) | 返回指定子字符串在此字符串中第一次出现处的索引。 |
toLowerCase() 和 toUpperCase() | 将字符串转换为全部小写或大写。 |
concat(String str) | 将指定字符串连接到此字符串的结尾。 |
replace(CharSequence target, CharSequence replacement) | 替换字符串中的某部分。 |
StringBuilder方法名 | 描述 |
---|---|
append(xxx) | 将参数添加到当前 StringBuilder 对象的末尾。xxx 可以是任何类型,如 String 、int 、char 等。 |
setLength(int len) | 将长度设置到 len 位。 |
length() | 返回当前容量(字符数)。 |
charAt(int index) | 返回指定位置的字符, 序列从 0 开始 |
delete(int start, int end) | 删除此序列的子字符串中的字符。 |
insert(int offset, xxx) | 将指定数据插入此序列中的指定位置。xxx 可以是任何类型。 |
reverse() | 将此字符序列用其反转形式取代。 |
toString() | 将当前 StringBuilder 对象转换为 String 对象。 |
4.2.2.2 String 和 StringBuilder 添加对比
String str = "";
str += (char)(97+i % 26)//创建新对象,同时边添加边扩容、时间内存复杂
sb.append((char)(97+i % 26))//长度达到限度之后翻倍扩容
4.3 核心类
4.3.1 数据类型介绍
特性 | Primitive Type(基本类型) | Reference Type(引用类型) | Object Type(对象类型) |
---|---|---|---|
本质 | 直接存储数据值 | 存储对象的内存地址(引用) | 引用类型指向的实际对象 |
存储位置 | 栈内存(Stack) | 栈内存存储引用,对象在堆内存(Heap) | 堆内存(Heap) |
默认值 | 有默认值(如 int 默认为 0 ) | 默认为 null | 无独立默认值(依赖引用类型) |
示例 | int , double , boolean | String , 数组 , 自定义类 | new String("Hi") , new int[5] |
内存开销 | 较小(固定大小,如 int 占 4 字节) | 较大(引用 + 对象开销) | 较大(包含对象头和字段) |
赋值行为 | 值拷贝(复制实际值) | 引用拷贝(共享同一对象) | 通过引用操作 |
是否可调用方法 | ❌ 无方法 | ✔️ 可调用对象方法 | ✔️ 可调用方法 |
是否为对象 | ❌ 不是对象 | ✔️ 指向对象 | ✔️ 是对象实例 |
JVM 处理方式 | 直接操作值 | 通过引用间接操作对象 | 由 JVM 在堆中管理 |
Reference Type | 变量类型(存引用) | String s; (s 是引用变量) | |
Object Type | 实际对象类型(堆内存) | new String("Hi") (对象本身) |
4.3.2 Wrapper Class 包装类
Wrapper方法名称 | 功能描述 | 适用范围 |
---|---|---|
Wrapper.valueOf() | 将基本类型或字符串转换为包装类对象。 | 基本类型或字符串 |
Wrapper.parseXXX() | 将字符串转换为基本数据类型。例如 Integer.parseInt("123") 。 | 字符串到基本数据类型 |
Wrapper.toString() | 返回表示对象值的字符串。 | 包装类对象 |
Wrapper.compareTo() | 比较两个包装类对象。 | 包装类对象 |
作用 |
- 方便基本数据类型使用类与对象方法
- 方便把基本数据类型转换为 String 类型
- 自动装箱, 自动拆箱, autoboxing and autodeboxing
示例:
Integer myInt = 5; // 自动装箱,将int转换为Integer
int myPrimitiveInt = myInt; // 自动拆箱,将Integer转换为int
Integer myInteger = Integer.valueOf("123"); // 将字符串转换为Integer对象
int myInt = Integer.parseInt("123"); // 将字符串转换为int基本类型
4.3.3 Array类
#重点 String和StringBuilder的方法熟悉。
Array方法名称 | 功能描述 | 适用范围 |
---|---|---|
Arrays.toString(array) | 以字符串形式打印数组内容,格式为 [元素1, 元素2, ...] 。 | 仅适用于一维数组 |
Arrays.sort(array) | 对数组进行排序。可对整型数组、字符数组排序;支持部分排序、逆序、字典序、ASCII序。 | 整型数组、字符数组等 |
Arrays.equals(array1, array2) | 比较两个数组的元素是否相等。 | 任意类型的一维数组 |
Arrays.binarySearch(array, value) | 二分查找特定元素,可以指定区间。未找到返回负值,找到返回索引。 | 已排序的数组 |
Arrays.copyOf(array, newLength) | 创建一个新数组,内容为原数组的副本,并可指定新长度。 | 任意类型的一维数组 |
Arrays.copyOfRange(array, from, to) | 截取数组的指定范围 [from, to) 。 | 任意类型的一维数组 |
Arrays.fill(array, value) | 填充数组的所有元素为指定值。 | 任意类型的一维数组 |
Arrays.deepToString(array) | 以字符串形式打印多维数组的内容。 | 二维及以上数组 |
4.3.4 ArrayList类
- 可以动态变化长度,可以用for-each语句遍历。
- 定义: ArrayList 是 List 接口的一个实现类,使用动态数组来存储元素。它提供了可变大小的数组,并且支持所有 List 接口的方法。
- 特点:
- 动态数组实现,随着元素的增加,数组会自动扩展。
- 允许快速随机访问元素(通过索引)。
- 插入和删除操作相对较慢(特别是在中间位置),因为需要移动元素。
- 创建:
ArrayList<Integer> intArrayList = new ArrayList<Integer>(); // 尖括号内填写类命,而非数据类型
ArrayList成员方法 | 含义 |
---|---|
al.add() | 添加元素 |
al.get(index) | 访问元素 |
al.size() | 返回长度 |
al.set(index, ) | 修改元素 |
al.remove(index) | 删除元素 |
al.addAll(al2) | 把al2全都加入到al中来 |
al.isEmpty() | 是否为空 |
al.clone() | 返回复印件 |
al.clear() | 清空 |
al.contains() | 判断是否含有 |
al.indexOf() | 返回匹配元素的索引值(第一次出现) |
al.lastIndexOf() | 返回匹配元素的索引值(最后一次出现) |
al.removeAll | 删除 |
al.subList(int start, int end) | 截取子列表 |
静态方法 | 含义 |
---|---|
al.sort(Comparator.naturalOrder()) | 正向排序 |
al.sort(Comparator.reverseOrder()) | 反向排序 |
al.toArray() | 转换为数组 |
al.toString() | 转换为字符串 |
List接口方法 | 含义 |
---|---|
add(E e) | 在列表末尾添加元素 |
add(int index, E element) | 在指定位置添加元素 |
remove(int index) | 移除指定位置的元素 |
get(int index) | 获取指定位置的元素 |
set(int index, E element) | 替换指定位置的元素 |
size() | 返回列表中的元素数量 |
isEmpty() | 判断列表是否为空 |
contains(Object o) | 判断列表是否包含某个元素 |
indexOf(Object o) | 返回元素在列表中第一次出现的位置 |
4.3.5 LinkedList 类
- 定义: LinkedList 也是 List 接口的一个实现类,它使用链表数据结构来存储元素。 LinkedList 还实现了 Deque 接口,因此可以作为队列或双端队列使用。
- 特点:
- 链表实现,插入和删除操作更快(特别是在中间位置),不需要移动元素。
- 随机访问速度较慢,因为需要从头部或尾部开始遍历。
- 适合频繁的插入和删除操作。
常见方法
LinkedList<E> list = new LinkedList<>(Collection<? extends E> collection);//使用任意集合初始化,或者创建空的然后填充,可以直接print,元素顺序是迭代顺序(HashSet不确定)Collection 使用hash function确定映射关系
方法名称 | 功能 |
---|---|
add(e) | 添加元素到链表末尾 |
addFirst(e) | 添加元素到链表头部 |
addLast(e) | 添加元素到链表尾部 |
remove(E e) | 删除并返回链表第一个E e元素, 成功返回true |
removeFirst() | 删除并返回链表第一个元素 |
removeLast() | 删除并返回链表最后一个元素 |
getFirst() | 返回链表第一个元素 |
getLast() | 返回链表最后一个元素 |
peek() | 返回链表第一个元素,不删除 |
peekFirst() | 返回链表第一个元素,不删除 |
peekLast() | 返回链表最后一个元素,不删除 |
offer(e) | 添加元素到链表末尾(队列入队) |
poll() | 删除并返回链表第一个元素(队列出队) |
push(e) | 添加元素到链表头部(栈入栈) |
pop() | 删除并返回链表第一个元素(栈入栈) |
4.3.6 总结
- List 接口:定义了一组有序的元素,可以包含重复的元素,提供了许多方法来操纵列表中的元素。
- ArrayList 类: List 接口的一个实现,使用动态数组存储元素,适合频繁访问元素的场景。
- LinkedList 类: List 接口的另一个实现,使用链表存储元素,适合频繁插入和删除元素的场景。
5 面向对象特性OOP Features
5.1 封装Encapsulation
在其他方法里要输出数据的时候,尽量用get函数,因为这样无论要更改什么,只需要改变get函数就可以实现所有函数中的输出都被更改。(主要是程序模块化)
5.2 多态Polymorphism
方法重写(Override) | 子类重新定义父类的方法,实现不同的功能 |
向上转型(Upcasting) | 把子类对象赋值给父类引用,调用方法时执行子类实现 |
接口实现 | 不同类实现相同接口,提供不同方法实现 |
抽象类与抽象方法 | 定义抽象方法,由子类提供具体实现 |
5.3 继承Inheritance
调用者视角一致性原则
- 子类方法不能抛出比父类方法更宽泛的异常。
- 访问权限不能比父类方法更严格。
5.4 抽象Abstract
6 面向对象基础OOP
7 语言结构
7.1 包 Packages
- 类名包含完整包名:
com.example.MyClass
- 包是类的组织方式,没有父子关系
- 目录结构必须与包层次一致
- 编译器查找类名顺序:
- 完整类名 → 直接匹配
- 简单类名:
- 当前 package
- import 的类
java.lang.*
- 默认导入
java.lang
和当前 package 中所有类
7.2 方法 Methods
7.2.1 方法定义与调用
- 属于类或对象,不能嵌套在其它方法中
- 调用方式:跨类调用支持面向对象设计
- Java参数传递本质是按值传递
- 引用传递 : 引用类型的地址拷贝,方法内操作影响外部对象
- 值传递 : 原始类型的值拷贝,不影响外部变量
- 对象传递 : Python等实现
7.2.2 方法重载
依据方法签名Signature
- 方法名 + 参数类型、数量、顺序
- 与返回值无关
7.2.3 面向对象编程
- iteration or recursion
7.2.4 类 Class 的属性
- Fields: class variable(static variable) and instance variable(instance 定义为 class object, 更精确), 以及常量和枚举字段.
- 显然instance非静态
- instance variable:每个实例都有fields的copy
- 谈论面向对象设计时可粗略称为instance variables
- 习题课:
- 成员方法:由类的实例(对象)调用,成员变量定义在方法外部,属于类的一部分,储存在堆,内存随对象
- 构造方法:特殊的成员函数,自动初始化创建的类实例,没有返回类型,必须与类同名(自动调用)编译器会自动提供无参数构造方法.
- 静态方法:属于类本身,常常通过类名调用
- 可以调用静态变量
- 根据实例的参数得值
- 静态方法:由类名调用
- 根据传递参数得值
- 在成员函数内,可以调用成员变量(访问权限, 限制继承与实例化, 定义嵌套类行为)
protected, private and static 不可用于顶级类, 只可用于内部类.
如果需要在静态方法中访问实例变量或实例方法,可以通过以下方式:
- 创建对象实例:
- 在静态方法中创建类的实例,然后通过实例访问实例变量或调用实例方法。
- 传递对象实例作为参数:
- 将对象实例作为参数传递给静态方法,然后通过参数访问实例变量或调用实例方法。
7.2.5 static : 静态方法和静态变量
import static导入类的静态字段和方法
static : 在没有创建对象的情况下调用,使用被加载的类访问
静态导入static import 允许直接访问类的静态成员或方法, 无需加上类名前缀
import static java.lang.Math.sqrt;
static variables 只在类加载时获取一次内存空间,如果写入Constructor 可以实现计数器,类共有值,节省内存空间,eg. static school = “Sustech”
静态方法属于这个类而不是类对象,使用时不需要创建对象——不可访问非静态量与方法
静态代码块,通常用于初始化变量或者加载配置文件到内存或者初始化集合,优先于 main() 方法执行
静态变量是指在JVM链接阶段进行的,在这个阶段赋默认值,然后在初始化阶段再对静态变量赋初始值(程序员手动设定的值),同时也会执行静态代码块。之所以静态变量属于类而不属于某个类的实例,是因为在链接阶段还没有开始创建类的对象实例,所以它和某个具体的对象无关,也就不会被捆绑在某个具体的对象上。
7.2.6 成员变量和局部变量
成员变量包含实例变量和静态变量,实例变量需要对所有都this初始化
- 成员变量随着对象的创建而存在,局部变量随着方法的调用而存在
- 成员变量有默认初始值,局部变量没有默认初始值
- 局部变量可以和成员变量名称相同,就近原则
7.2.7 类
- 实例化:
- 创建新类型必须使用 new 才能赋值到circle数组里,类似二维数组
- 创建新类型引用不能使用方法,只能使用new
类的属性:
- field 是类变量的通称,attribute 指参数即用构造函数传入的成员变量,property 指属性即可以 set, get 的成员变量
- 在类中定义的变量是全局变量,能被类中的方法使用(会有初始化, default or null)。
protected: protected修饰的元素不仅可以被同一个包内的类访问,还可以被不同包中的子类访问。这意味着如果一个类继承了使用protected修饰的另一个类,即使这两个类不在同一个包中,子类也可以访问父类中的protected成员。
成员变量(实例变量instance variables):储存在对象的内存空间里,只能被成员方法访问(因为只能依靠对象访问),无法被静态方法访问。在本类中可以直接用变量名访问,也可以用 this 访问 (recommended,因为声明是成员变量)。(会有初始化,0,false,null)
静态变量(static variables):储存在类的内存区域里,不需要实例化就能直接访问。在本类中可以直接用变量名访问,也可以用类名访问 (recommended,因为声明是静态变量)。(会有初始化,0,false,null)
注:如果在方法内的局部变量名字与全局变量冲突,可以用类命或者 this 区分。
使用类名访问静态方法静态字段
7.2.8 类的权限结构与类型
- 普通类, package-private class, default access class, friendly class, helper class, 避免类污染全局命名空间
静态嵌套类, 不依赖于外部类的对象实例, 可以访问外部类的所有静态成员 匿名类 Anonymous Class, 可以使用lambda替代
- 没有名字的局部内部类
- 在定义时直接创建其实例
- 通常用于实现接口或继承类并重写方法,尤其是简化事件监听器等代码 local inner class, member inner class
7.2.9 方法
- 静态方法(static):被类命调用,无需实例就能调用,所以主要根据传入的参数和静态变量返回值
- 成员方法:由实例调用,结合属性、静态变量和传入的参数得返回值
7.2.9.1 构造方法
- Constructor : 方法名和类名完全一致,没有返回值
- (当类没有构造方法时,会自带一个缺省构造方法;如果有了,则会被覆盖掉)
public Circle(){}
//这个时候为了避免使用空参数方法报错可以额外写一次,然后使用 Circle.set() 方法
- 没有返回类型(没有void,因为void是返回值为空)
- 如果要在外部调用,则可以直接public+类命
- 方法名与类名相同,可以重载,可以无参数。
- 类的必须初始程序
7.2.10 修饰符权限结构
修饰符 | 可访问性(访问范围) | 是否可被继承 | 是否可被覆盖(Override) | 是否可被隐藏(Hide) |
---|---|---|---|---|
private | 当前类内部 | 不可被继承 | 不可被覆盖 | 不适用 |
default | 同一包内 | 可被继承 | 可被覆盖 | 可被隐藏 |
protected | 同一包内及子类 | 可被继承 | 可被覆盖 | 可被隐藏 |
public | 所有地方 | 可被继承 | 可被覆盖 | 可被隐藏 |
private static | 当前类内部 | 不可被继承 | 不可被覆盖 | 不适用 |
static | 所有地方 | 可被继承 | 不可被覆盖(可被隐藏) | 可被隐藏 |
访问范围 | private | default (包权限) | protected | public |
---|---|---|---|---|
同一类 | ✔️ | ✔️ | ✔️ | ✔️ |
同一包中的其他类 | ❌ | ✔️ | ✔️ | ✔️ |
不同包中的子类 | ❌ | ❌ | ✔️ | ✔️ |
不同包中的无关类 | ❌ | ❌ | ❌ | ✔️ |
7.2.11 继承语句
- 访问权限
- 现在
a
是类型为A
的引用,但指向的是B
的实例 - 即:编译时类型是 A,运行时类型是 B
AB类有两个同名字段, 但这是独立的, 不是重写, 访问对象取决于使用的类型引用方式
字段(field) | ❌ 否 | 访问字段看引用类型 |
方法(method) | ✅ 是 | 调用方法看实际对象类型 |
- 父类和子类的类型兼容关系, 需要强制类型转换声明
- 重写/覆盖 Overiding:
- 在子类中重写一个signature相同的方法。(则对应的子类实例会用对应的方法)
- 事实上,只要参数不同的方法都可以认为是完全不同的方法,因为parameter也是signature的一部分。
- 子类和父类各行其是。
- 子类可以实例化父类的引用。
- Overload指的是重载, 即 Method signiture 不同的同名方法.
- 强制类型转换绕过变量遮蔽((A) this).kobe
- super.kobe访问的是最近存在kobe类的kobe
- 变量的访问由引用类型决定, 在编译时静态绑定
7.2.12 枚举类 Enumerations
- 与其它类的区别:在定义的时候就确定了它的对象(实例化)。
- 枚举类功能完整也可以有自己的属性、方法。
- 必须是包私有级别的构造方法, 默认是private
- 若要传入参数,则在声明枚举值的时候要传入参数。
- 外部调用:可以用 Direction.NORTH 这样调用,或者 Direction.values() ,也可以用 EnumSet.range(from_name,to_name) 来返回一定区间的数组(顺序和声明的顺序相同)
- 仍然无法通过
new
创建枚举实例(枚举类型本身禁止外部实例化) - 构造函数不可以手动调用, 枚举常量声明时内部自动调用
- 私有构造是单例实例封闭的基础
编译期 | 编译器直接拒绝 new Enum() 的语法,报错。 |
字节码 | JVM 验证阶段拒绝非法枚举实例化的字节码,抛出 VerifyError 。 |
枚举类方法 | 含义 |
---|---|
Direction.values() | 返回一个数组,枚举类所有object的值 |
Direction.ordinal() | 返回枚举常量的index |
Direction.valueOf() | 返回指定字符串值的枚举常量(方便遍历) |
7.2.13 其他类
记录类 Record, 不可变数据载体, 自动生成equals()/hasCode()
嵌套类 Nested Class
Class modifier怎么用 vs Class Types是什么
sealed
限制继承
7.2.14 多态 Polymorphism
指的是 OOP 的继承特性、机制
所以它的子类可以重写他的方法。与抽象类搭配使用,则要求关键方法,使用同一个接口,独立实现具体不同的方法
tips:
- 让父类引用变量指向子类对象, downcast 向下转型
method binding : 练习方法调用与执行
- dynamic binding : runtime. late binding or runtime polymorphism, based on actual type not reference type(declared type)
- static binding : compile time.
final
修饰符阻止被覆写或者继承. Method overloading. private, static methos. 可以写一个父类的getter and setter方法, 然后使用多态机制利用父类变量获得子类私有变量值 - 父类的实例想用子类的方法,则只需要在父类中添加子类的方法名:(实际上是子类重写了父类的方法)
允许你可以引用父类的对象来表示一个子类的实例。而这些代码在运行时可以根据具体的对象类型来表现出不同的行为。
基于子类知道如何做的情况
多态的实现主要有两种方式:
- 方法覆盖(Override):子类重写继承自父类的方法。
- 不属于类型: #考试重点 子类方法签名与父类方法签名完全一致,并且抛出的异常类型比父类更宽泛。如父类方法是
private
时,子类同名方法是新的方法而不是重写 - 接口实现(Implement):类通过实现接口来提供接口方法的具体实现。
多态的好处:
- 提高代码的可复用性:可以使用相同的接口或父类引用来调用不同子类的实现。
- 提高代码的可扩展性:新增子类对多态方法的不同实现时,不需要修改调用这些方法的代码。
- 接口化编程:允许开发者编写灵活的代码,对“做什么”和“怎么做”进行分离。
使用情况常常为同一范畴下的多态数组,用父类命名然后放入父类arraylist
- 所有 class 继承自 Object, 其中有几个基本方法
toString()
^e101c1equals()
hashCode()
- superclass references 可以调用父类中所有有访问权限的成员,不能调用基于子类声明类型的特有成员。
代码解耦合,多态借口\父类引用的独立实现,对象寻求方法
7.2.14.1 toString方法
- getClass().getName() + ‘@’ + Integer.toHexString(hashCode())哈希码默认为[[#^f0058c|内存地址]]
- 输出对象或者reference时会隐式调用, 基础协议类型
- 利用String.format()更改toString()的输出格式
7.2.15 抽象类
相比之下, interface只有public static finla variable, 但是abstract class 可以有static, final组合, 而且abstract方法是必须声明, 而且可以有protected方法
- 定义:
- 抽象类是不能被实例化的类(仅仅针对类特点, 对于全部为concrete方法, main方法的存在性等等不做要求, 可以结合多态理解),通常用作其他类的基类(父类)。抽象类可以包含抽象方法和具体方法。抽象方法是没有实现的方法,它们只有声明而没有具体的执行体。
- 特质:
- 抽象类无法实例化自身
- 只有抽象类才能定义抽象方法
- 抽象类也可以有构造函数, 提供默认的初始逻辑, 并确保子类正确初始化
- 但它的子类可以被实例化。构造函数可以用于初始化抽象类中的成员变量,这些变量可能被子类继承并使用。
- 提供一个适当的基类,该基类定义了子类必须实现的方法。
- 封装了一些可以由多个子类共享的代码。
- 示例:
abstract class Animal { abstract void sound(); // 抽象方法,没有方法体 void sleep() { // 具体方法,有方法体 System.out.println("This animal is sleeping."); } }
7.2.16 接口 Interface
接口就像是一个契约, 协议,类之间的行为规范, 它规定了某个类必须具备哪些功能(方法),但不关心具体的实现细节。任何实现这个接口的类都必须实现这些方法。
不能有constructor, 因为不可能实例化, 抽象类的高级组
接口是一个完全抽象的类,它包含了一组抽象方法(只有方法签名,没有方法体)和常量(静态常量)。一个类可以实现多个接口,从而解决 Java 中单继承的局限。
接口的定义与实现(不只是抽象方法default, static, private)
private
方法:- 用于在接口内部拆分代码逻辑(如多个
default
方法共享功能)。 - 只能是实例方法(非
static
)。解决内部代码复用避免default重复公共逻辑 - 核定是定一公开契约, 而非层级访问控制
- 定义接口
- 接口使用 interface 关键字来定义。接口中的方法默认是 public 和 abstract 的,即使不显式声明。接口中的属性默认是 public static final 的。
- 实现接口
- 类使用 implements 关键字来实现接口。实现接口的类必须提供接口中所有方法的具体实现, interface 本身无实现所以不可implements interface
- 专注于行为抽象, 可以extends 多个接口
- 用于在接口内部拆分代码逻辑(如多个
继承:子类化机制用于定义新子类与超类的区别,指出属性行为不同的地方,未指定则直接指向Object,对超类的修改自动反映到子类中。设计出高效的类层次结构需要大量的规划和修订,对类层次结构进行审视判断
接口:在类层次结构的不同分支间复制。是一组方法,由实现接口的类定义行为 继承描述排他的独立关系, 而接口描述相容的共有性质
#JavaA-short
对比项 | 接口 | 抽象类 |
---|---|---|
构造函数 | 无 | 有 |
方法实现 | 默认抽象,Java 8+ 支持默认和静态方法 | 可以部分实现 |
成员变量 | 全部为常量(public static final) | 可以是普通变量 |
继承关系 | 多实现 | 单继承 |
设计目的 | 行为规范、多态性 | 代码复用、抽象共性 |
访问权限 | 所有方法默认 public | 可定义 protected、public 等 |
常见使用方式 |
- Comparable
- Runnable / Callable
- List / Set / Map : 定义集合操作规范
Best Practice
- 接口命名建议 :
- 接口名通常为形容词(如
Runnable
,Serializable
),表示具备某种能力。
- 接口名通常为形容词(如
- 优先使用接口而非继承 :
- 当只需要行为规范而不需要共享状态时,优先使用接口。
- 合理使用默认方法 :
- 用于向后兼容已有实现,避免破坏已有代码。
- 避免接口爆炸 :
- 控制接口数量,避免过度拆分导致维护困难。
- 结合工厂模式使用接口 :
- 接口 + 工厂类组合可实现灵活的对象创建与行为注入。
实战建议
- 面向接口编程 : 针对接口而不是具体类
- 用于定义协议, 提升拓展性
7.2.17 排序器 Comparable interface
比较字符实际上是比较 Unicode 的数值
- (让特定类可以比较大小)
- 返回negative(-1)表示当前对象(this)排在前面;
- 给一个不可比较的类接入一个比较器的接口,就相当于给予这个类某种可以比较的规则;只需要切换排序器,就能使一个类有不同的排序方式
- 示例:
public class Circle extends Shape implements Comparable<Circle> @Override public int compareTo(Circle o) { if(this.radius < o.radius){ return 1; }else if(this.radius > o.radius){ return -1; } return 0; }
- NComparator n= new NComparator();
circleList.sort(n);
8 其他高级特性
8.1 泛型Generic
- 泛型 是一种编译期的类型机制, 用于提高代码的通用性和类型安全。
- 多个方法因数据类型不同而重复实现相同逻辑。避免冗余的重载方法。
- 参数化数据类型。
- generic parameter may accept class that don’t implement compareTo method, solution:
<T extends Comparable<T>>
- 编译器自动进行类型检查和推断, 所以有显示指定和类型推断
- 使用 Object 容易产生类型转换错误, 这是更高级的特性之一
- 泛型是为了在编译时提供更强的类型检查,避免运行时类型转换错误。 #考试重点
- 泛型方法:写法是一种习惯 [T指代什么?][[#命名习俗]]
public static <T> void printArray(T[] array){ //可以换字母 } public static <T extends Number> void printArray(T[] array){ //上限是Number,T要是Number及其子类, Bounded Type Parameter, 有界类型参数, 调用父类或者特定类型参数的方法, 不能直接实例化泛型数组(如 `new T[10]`), 不能使用基本数据类型, 不是 Object 对象 }
泛型类: 为不同数据类型实现相同逻辑实践 由编译器自动执行类型转换, 保证类型正确. compile阶段所有类型都是 Object 显然只适用于reference datatype, 才具有hierarchy关系
8.1.1 TypeErasure
泛型信息在编译阶段被移除并且被实际替换, 仅在设计和编译时自动创建存在 泛型不具备协变性(Covariance)
- 编译器自动插入必要的类型转换(隐式强制类型转换)以保证类型安全, 自动转换为默认上界 Object
- 相同泛型类的不同类型参数实例共享相同的字节码, 不能用于区分方法签名, 泛型类所有静态变量是共享的. 不同的泛型类实例都具有相同的类对象(即
.class
文件相同) - 必须是使用上界, 例如
List<Obejct> list1 = ArrayList<>();
List<Object> list2 = list1;
多态适用于接口和类继承关系
List<? extends Object> list2 = list1;
但这只是只读访问,不能添加元素。使用通配符允许协变行为.
8.1.2 实战建议
- 设计通用数据结构 :
- 使用泛型类实现栈、队列、链表等结构,提高复用性。
- 封装复杂逻辑 :
- 使用泛型方法处理集合操作(如排序、查找最大值等)。
- 结合注解和框架使用 :
- 泛型常用于构建可扩展的库或框架(如 Spring、Hibernate 等)。
- 谨慎处理泛型与多态的交互 :
- 理解泛型与继承的关系,避免混淆。
8.2 反射Reflection(机制)
- 反射 是一种运行时的机制 ,允许程序在运行期间动态地分析、访问和修改自身结构, 是框架设计的灵魂
- 允许程序在运行时动态获取类的信息(如方法、字段、构造器等),并操作对象, 常用于框架开发
- 泛型集合的类型信息可以通过
ParameterizedType
接口获取(如List<String>
的String
类型)。 朴实直白地介绍 [反射机制][https://www.cnblogs.com/chanshuyi/p/head_first_of_reflection.html]
8.2.1 Wildcard and Bounds
通配符 | 含义 | 示例 | 场景 |
---|---|---|---|
? | 未知类型无界 | List<?> list (可以接受任何类型的 List) | 只关心集合本身的操作 |
? extends T | 上界通配符(T 或其子类) | List<? extends Number> (Number 或其子类) | 生产者, 从集合读取数据 |
? super T | 下界通配符(T 或其父类) | List<? super Integer> (Integer 或其父类) | 消费者, 向集合写入数据 |
用于避免强制类型转换, 提供上下界限. |
8.3 异常Exception : avoiding crashing
8.3.1 基本使用
中断程序运行流程
- try - catch block
- finally 块 :无论是否发生异常,都会执行,常用于资源释放。
- throw/throws 模式 :
- 使用
throws
在方法声明中抛出异常,交由调用者处理。 - 使用
throw
主动抛出自定义异常。 - 支持异常链(chained exceptions),保留原始异常信息。 异常处理最佳实践
- 使用
- 优先捕获具体异常 ,避免使用
catch (Exception e)
。 - finally 块 确保资源释放(如关闭文件流、数据库连接等)。
- 异常信息输出 :
e.getMessage()
:获取异常描述。e.printStackTrace()
:打印堆栈跟踪,便于调试。
- 合理使用异常链 :保留原始异常上下文,提升调试效率。
- 堆栈跟踪(Stack Trace) :显示异常发生的完整调用路径。
- 异常分类选择 :根据业务需求决定使用 checked 还是 unchecked 异常。
- 避免空异常处理 :捕获异常后应进行适当处理,而非简单忽略。
- 日志记录 :结合日志框架(如 Log4j)记录异常信息,便于后续分析。 实战建议
- 防御性编程 :提前检查参数合法性,防止异常发生。
- 统一异常处理策略 :在大型项目中采用统一的异常处理框架。
- 异常性能考量 :避免在高频循环中频繁抛出和捕获异常。
- 测试异常行为 :编写单元测试验证异常处理逻辑是否正确。
8.3.2 层次体系hierachy
分类 | 继承自 | 是否必须处理 | 出现场合 | 示例 | 说明 |
---|---|---|---|---|---|
Checked Exception | Exception (但不是RuntimeException 的子类) | ✅ 必须处理(try/catch 或 throws) | 外部资源问题、可恢复的情况 | IOException ,SQLException | 编译时异常,API 设计中用于强制异常处理, recoverable conditions |
Unchecked Exception | RuntimeException 或Error | ❌ 不强制处理 | 程序逻辑错误、系统级错误 | NullPointerException ,ArrayIndexOutOfBoundsException ,OutOfMemoryError | 运行时异常,通常由程序员错误引起,不需要强制捕获或声明, programming errors |
错误类型 | 是否属于Throwable | 描述 | 常见示例 | 说明 |
---|---|---|---|---|
Syntax Error(语法错误) | ❌ 否 | 编写代码时违反 Java 语法规则,导致编译失败 | 少分号、括号不匹配、拼写错误 | 属于编译器报错,不属于运行时异常体系 |
Runtime Error(运行时错误) | ✅ 是 | 程序在运行过程中发生的错误 | 除以零、数组越界 | 包括RuntimeException 和部分Error 类型 |
Logical Error(逻辑错误) | ❌ 否 | 程序能运行但结果不符合预期 | 条件判断错误、算法错误 | 不会抛出异常,需要通过调试发现 |
Compiler Error(编译器错误) | ❌ 否 | 更广义的概念,包含所有编译阶段错误 | 源码格式错误、环境配置问题等 | Syntax Error 是其常见子集 |
8.3.3 自定义异常类
public class MyException extends Exception {
public MyException(String message) {
super(message);
}
}
8.3.4 断言Assertions
要求必须显式启用
- 用途 :开发阶段用于验证假设条件,确保程序正确性。
- 常常与测试用例 TestUnit 结合, 作为程序可靠性验证, 但是本身也可用在程序中用于检验程序状态, 至少比 System.println() 能检测的内容要多一点 assert expression : message
9 高级程序设计
9.1 底层理解
9.2 内存管理
9.2.1 内存
JVM给出的内存地址 : 将对象的内部地址 (可以理解为 JVM 管理的对象引用地址)转换成整数作为哈希码。通常是JVM内部使用的标识符或者是虚拟地址
概念 | 含义 | 特点 |
---|---|---|
物理内存(Physical Memory) | 计算机实际安装的内存条上的 RAM | - 是硬件资源- 容量有限- 直接由 CPU 控制和访问 |
虚拟内存(Virtual Memory) | 操作系统提供给程序使用的“假想内存”,由操作系统映射到物理内存 | - 每个进程拥有独立的虚拟地址空间- 提供更高的安全性和隔离性- 支持内存分页、换出等优化机制 |
系统内存(System Memory) | 一般泛指物理内存 + 虚拟内存的总称,有时也指操作系统管理的整体内存资源 | - 不是一个精确术语- 常用于描述整体内存使用情况(如任务管理器中) |
^f0058c |
9.2.2 JVM段式内存管理
JVM 使用堆内存(heap space)来分配对象的内存。使用stack 储存primitive values 栈空间(stack space)主要用于存储方法调用的局部变量和方法调用的上下文信息。 请看 从字节码的层面理解逻辑
内存是JVM中储存数据的地方, JVM分成多个段
- bss segment : Block Started by Symbol, 未初始化全局变量
- data/static segment : 已初始化全局变量
- code/text segment : 程序运行前确定
- stack segment : 调用函数分配内存,储存 reference
- heap segment : 临时创建的局部变量,被压入发起调用的进程栈,FIFO,寄存交换临时数据的内存区
静态变量 → 存储在 Metaspace 中(类加载时分配) MethodsArea/Metaspace, 类信息, 常量池, 静态变量(随类存在)
磁盘读入或者Java内存分配不同使得打印 array 地址不同,如果想要输出值必要 for loop println特殊性,传递char类型数组可以直接输出
强类型语言中,变量的类型是严格定义的, 减少类型错误 弱类型语言如JavaScript会自动处理, 自动转化, 适合快速开发和脚本编写 基本数据类型不需要复杂的对象结构, 优先放在stack中提升性能. 但是事实上只有在local variables时才储存在stack上面, 有什么额外信息? 如果是 Integer 对象储存在heap 但是stack储存引用信息, 然后stack frame删除后通过garbage collector检查无引用对象删除heap中的local variables.
局部变量(Local variable) | 基本类型 | Stack | 如方法内部定义的int i = 10; |
成员变量(Instance variable) | 基本类型 | Heap | 如类中的private int age; |
静态变量(Static variable) | 基本类型 | Metaspace / Method Area | 如static final int MAX = 100; |
包装类(Wrapper class) | 对象(如Integer ,Double ) | Heap | 如Integer i = new Integer(10); |
Heap Memory | 堆内存、内存堆、堆空间 |
Stack Memory | 栈内存、栈空间、栈区 |
9.2.3 内存泄漏
memory is kind of important system resources,是动态调配,需要回收,长期占用却不使用导致可用总量下降就是泄漏 JVM 对无引用对象内存自动执行 garbage collection 因为即时释放特性,stack size less than heap
9.2.4 空与null
reference类型的初始值设定为为空和null的区别
- 空对象(
int[0]
)- 是一个长度为 0 的有效数组对象。
- 可以调用数组的方法,不会抛出异常。
- 在逻辑上表示“没有元素”,但仍然是一个有效的数组, 主要是人为的逻辑理解
null
- 表示没有引用任何对象。创建栈中的空指针, 没有实际地址, 引用变量不指向实际对象
- 不能调用任何方法或访问任何属性,否则会抛出
NullPointerException
。 - 逻辑上表示不存在
9.2.4.1 堆与堆栈, heap and stack
“The heap space is used by Java runtime to allocate memory to Objects.” “Stack memory stores information for execution of methods in a thread.” 方法被调用后自动创建 Stack Frame, 后进先出, 生命周期短而且速度快 对象引用储存在stack, 对象本身储存在 heap Garbage Collector worked mainly on the heap
缓存系统资源不在JVM内存模型中 系统决定如何将程序的虚拟内存映射为物理内存
变量的内存对象分配机制
- 局部变量:局部变量存储在栈内存中,每个方法调用时都会创建一个新的栈帧,栈帧中包含该方法中所有局部变量的内存空间。当方法执行完毕后,栈帧被销毁,局部变量的内存空间也会被释放。
- 成员变量:成员变量存储在堆内存中,与对象的生命周期绑定。每个对象都有自己的成员变量副本,即使多个对象的成员变量名称相同,它们在内存中是独立的。 当一个程序调用一个方法时,被调用的方法必须知道如何返回给调用者,因此调用方法的返回地址会被压入方法调用栈(也称为程序执行栈)。 •如果发生一系列方法调用,后续的返回地址会以后进先出的顺序压入栈中。 •程序执行栈还包含每次方法调用所使用的局部变量的内存。 •存储在方法调用的激活记录(或栈帧)中。 •当进行方法调用时,该方法调用的激活记录会被压入方法调用栈。 •当方法返回给调用者时,该方法调用的激活记录会从栈中弹出,那些局部变量也不再为程序所知。 #待处理
9.2.5 JVM规范内存区
- 程序计数器(Program Counter Register)
- Java虚拟机栈(Java Virtual Machine Stacks)
- 本地方法栈(Native Method Stack)
- Java堆(Heap)
- 方法区(Method Area)
- 运行时常量池(Runtime Constant Pool)
- 直接内存(Direct Memory) (非JVM规范强制要求)
9.2.6 常量池介绍
常量池是 Java 虚拟机(JVM)内存中的一个特殊区域,通常位于方法区(Method Area)。堆内存中的对象是独立的, 常量池里的不同. 直接使用字面量创建 String 和 WrapperClass 会指向相同的地址, 基本缓存 -128-127 的所有值
利用静态工厂方法或者单例模式控制对象创建实现常量池 实现基础特性:
- 内容大量重复
- immutability
- 不需要不同的内存地址
9.3 编译原理
流程图 : .java 源代码 ↓ javac 编译 .class 字节码 bytecode ↓ JVM 加载 内存中的类结构 ↓ 解释 / JIT 编译 CPU 执行机器码 machine code
ClassLoader类加载器 | - 验证字节码合法性- 分配内存空间- 初始化静态变量等 |
---|---|
解释执行 | JVM 逐行解释字节码为机器码并执行(启动快,效率低) |
JIT 编译 | 对热点代码(频繁调用的方法)进行编译优化,直接生成机器码(提升性能) |
JVM这种虚拟机规范是Java语言实现机制的核心优势.
- 动态性:JVM可以根据需要动态加载和执行字节码,而编译器生成的字节码可以在不同的JVM实现上运行,体现了Java的“一次编写,到处运行”的特性。
- 编译阶段 : 字节码是平台无关的中间代码
- 解释执行阶段:避免machine code的平台依赖性
- JVM(Java 虚拟机)在运行时 解释执行 字节码,将其转换为机器码。
- 现代 JVM(如 HotSpot)还会使用 JIT(Just-In-Time)编译,将热点代码编译成机器码以提高性能。 结论:Java 结合了编译(生成字节码)和解释/JIT(执行字节码)两种方式,因此被称为 “编译+解释” 型语言。
9.3.1 图形用户界面 GUI
- Graphic User Interface & Command-Line Interface
- 常用GUI开发工具APIs:AWT,Swing,JavaFX
- 详情:CS109_Ch12_GUI.pdf
- 等价计数器(正序和逆序)
- 需要输入的函数记得传入Scanner对象 #java重点
- nextLine读入会读入换行符前面的,尽量在前面先用一个nextLine换行
- 块作用域 Block Scope:注意变量的定义领域
- String.format("%d",n):返回一段格式化的字符串
- 复印ArrayList,注意深拷贝和浅拷贝的区别
ArrayList<Integer> c2 = new ArrayList<Integer>(c1); ArrayList<Integer> c2 = (ArrayList<Integer>)c1.clone();
- 用arraycopy函数可以进行一维数组的快速赋值:
arraycopy(model,start1,panel,start2,length)
while四指针方法 #java重点 创建一个方向矩阵
先创建存在,然后才能引用 如果方法是递归调用(即方法调用自身),Java允许这种调用,因为编译器可以推断出方法的签名和返回类型。类的加载顺序是由类加载器(ClassLoader)决定的。当一个类被加载时,如果它依赖其他类,这些依赖的类也会被加载。Java方法的调用顺序是由编译器的前向引用规则决定的,而不是声明顺序。编译器的作用:Java编译器会解析整个类的代码,识别所有方法的声明,因此方法的调用不会受到声明顺序的影响。
return立刻退出当前方法
背包问题解法
Java使用实例变量和类变量在对象间传递信息,因此不需要全局变量
字符变量的初始值是"/0" #java重点
字母、_$三种开始变量,变量的命名习惯
变量声明包括储存信息类型,基本数据类型、类名或者接口名、数组
存在primitive type 对应的类
功能性, 准确性, 专家语言 逻辑展示的准确性
Java关键字null指的是null对象,可用于任何对象引用,是每个元素的初始引用对象。
对象数组是一组到对象的引用,移动数组中的值是在重新指定引用,而不是复制。
do{} while();循环体至少执行一次,很适合用超过标准的要求。
标号标记跳出位置
词法作用域,完成之后全部消失。
受限类型参数
Object 是上限,upper bound
为什么覆写的时候要@override?用来提醒 compiler final方法不可以被覆写, 动态绑定是多态机制实现的
- 将
myPet.speak()
标记为虚方法调用(非final/private/static方法)。 - JVM根据实际类型决定, 是 Java 面向接口编程
9.3.2 词汇表
immutable 不可变 yield 是 Java 中的一个关键字,用于流操作中,表示从流中返回一个值。
关系类型 | 定义 | 特点 | 示例代码 |
---|---|---|---|
is-a | 表示“是一种”的关系,通常通过继承(extends )实现。 | 子类继承父类的属性和方法,子类是父类的一种特例。 | java<br>class Animal {}<br>class Dog extends Animal {}<br> |
has-a | 表示“有一个”的关系,通常通过成员变量实现。 | 一个类包含另一个类的对象作为成员变量,表示一种组合关系。 | java<br>class Engine {}<br>class Car {<br> Engine engine;<br>}<br> |
uses-a | 表示一个类使用另一个类的对象,但不包含它。 | 通常通过方法参数或局部变量体现。 | java<br>class A {<br> void method(B b) {<br> // 使用类B的对象<br> }<br>}<br>class B {}<br> |
association | 表示两个类之间存在某种逻辑上的关系,但不一定有继承或组合。 | 通常通过方法调用或对象引用体现。 | java<br>class Student {<br> void register(Course course) {<br> // 学生注册课程<br> }<br>}<br>class Course {}<br> |
aggregation | 表示“整体-部分”关系,但部分可以独立于整体存在。 | 部分对象可以独立于整体对象存在。 | java<br>class Department {<br> List<Employee> employees; // 部门包含员工<br>}<br>class Employee {}<br> |
dependency | 表示一个类依赖于另一个类,但这种依赖通常是临时的。 | 通常通过方法参数或局部变量体现,依赖关系较弱。 | java<br>class A {<br> void method() {<br> B b = new B(); // 依赖类B<br> }<br>}<br>class B {}<br> |
9.4 项目开发
9.4.1 架构Architecture
架构是对软件系统整体结构和组织方式的高层次设计,描述了系统的模块划分、层与层之间的关系、数据流向、技术选型等, 关注系统如何组织、分层、协作, 给出了自洽合理的方法体系实现特定功能
9.4.1.1 MVC架构
- View : 界面显示与用户交互
- Controller : 连接视图和模型的逻辑与功能
- Model : 持有所有数据状态和程序逻辑,接受视图数据的请求并返回最终处理结果
9.4.1.2 UBD架构
- USL, User Show Layer
- BLL, Business Logic Layer
- DAL, Data Access Layer
9.4.2 框架Framework
框架是一个可重用的代码结构或平台,提供基础功能和规范,帮助开发者更高效地实现具体功能. 产品本身独立性比较强, 通常以包的形式体现, 例如JavaFX, Swing, Spring, React等等. 往往是一些便于程序开发设计的基础结构, 调用流程和使用规范, 需要开发者遵守的规则, 是开发程序的模板. 强调控制反转, 与Library不同, 框架调用你的代码, 而不是你调用框架
9.4.3 设计模式 DesignPattern
设计模式是解决某一类问题的经典思路和最佳实践, 只是设计思想.
- 单例模式
- 工厂模式
- 观察者模式
9.5 算法考察
- 动态规划, 斐波那契数列, 上台阶问题
- 卡丹算法, 查找连续子数列最大值 ( 定义并表示最大变化方向与累积量)