编程基础与函数用法总结

起因

编程基础的知识过于细碎,没有章法。提炼总结本文,用于总结统筹。

一、编程基础

编程就是编写程序,所谓程序,就是告诉计算机要操作的数据和执行的指令序列,即对什么数据做什么操作。见于 编程-什么是编程

变量与数据类型

变量

变量 = 变量名(标识符) + 变量值 + 数据类型

变量是计算机内存中一个具有名称的存储位置,用于临时存储程序在运行过程中需要使用和操作的数据。

设计价值:

  • 抽象内存操作:用名称替代物理地址(类似于域名替代IP)
  • 动态数据管理:存储中间结果、用户输入等运行时数据
  • 提升代码复用性:逻辑与数据解耦,修改值无需改逻辑
  • 类型安全屏障:编译期拦截非法操作(如文本做加法)
// 声明一个int型变量i
// 内存中分配4字节空间,i指向该地址
// i位置不变,值可变(这是"变"量的本质)
int i;
i = 5;  // 将指向的内存空间值改为5

变量 vs 常量

  • 变量:值可以改变
  • 常量:定义后不可改变(final 关键字修饰)

数据类型

什么是数据类型? 数据类型是编程语言中对不同种类数据进行分类的方式,定义了数据的性质、大小、取值范围以及可执行的操作。

为什么引入数据类型?

  • 明确数据含义:让计算机知道二进制数据表示数字、文本还是其他
  • 分配合适的内存空间:不同类型占据不同内存(int 4字节 vs long 8字节)
  • 确保操作有效性:限制特定类型的操作(避免对文本进行数学加法)
  • 提高可读性和可维护性:明确类型帮助理解数据用途和行为

又可以进一步划分为基本数据类型对象类型

基本数据类型
  • 直接存储值本身
  • 存储在栈内存中
  • 特点:占用空间小,存取速度快,值传递时复制数据副本
  • 赋值即复制值;== 比较内容;生命周期随作用域结束
引用数据类型(对象类型
  • 存储内存地址(引用),实际数据在堆内存中
  • 包括:对象、数组、String(Java中)
  • 特点:占用空间大,引用传递时共享同一内存地址
  • == 比较地址,内容比较需用equals();对象由GC管理生命周期
    int[] a = new int[]{1,2};
    int[] b = a;  // 复制引用,非复制数组
    b[0] = 99;    // a[0] 同步变为99(共享同一对象)

赋值

赋值操作的本质

  • 基本类型:将值复制到变量指向的内存空间
  • 引用类型:将内存地址赋值给变量,多个变量可指向同一对象

基本运算

流程控制

条件执行

  • if-else链:适用于区间判断、复杂条件组合
  • switch优化:
    • JDK12+:支持yield返回值
    • JDK14+:表达式形式(无break穿透风险)
    • 字符串/枚举支持(编译期优化为哈希跳转)
  • 三元运算符:条件 ? 真值 : 假值(简洁赋值场景)

循环语句

循环类型适用场景关键技巧
for已知迭代次数嵌套循环注意变量作用域
while条件驱动循环避免死循环(确保条件可变)
do-while至少执行1次先执行后判断
增强for遍历集合/数组无法获取索引

控制流增强:

  • break label:跳出多层嵌套
  • continue:跳过本次剩余代码

二、函数的用法

函数基础

函数是组织好的、可重复使用的代码块,用于实现单一或相关联的功能。它是从「线性执行」到「模块化编程」的关键跃迁。见于函数

  • 本质:逻辑封装单元 + 接口契约(输入→处理→输出)
  • 核心概念:
    • 定义(Definition):声明函数签名(名称、参数、返回值)和函数体
    • 声明(Declaration):提前告知编译器函数接口(头文件/接口概念)
    • 调用(Invocation):通过函数名触发执行,程序计数器跳转到函数入口
  • 四要素:
    • 返回值:return 语句
    • 名称:符合规范
    • 参数列表:形参列表
    • 函数体
  • 设计原则:
    • 单一职责:一个函数只做一件事
    • 副作用最小化:避免隐式修改外部状态
    • 明确返回:void表示无返回值,非“返回空”

参数系统

参数是函数与外部交互的接口,是函数设计中最重要的部分。

参数类型匹配

  • 形参(Parameter):函数定义中的占位符,局部变量
  • 实参(Argument):调用时传入的实际值或引用
  • 匹配规则:数量匹配、顺序匹配、类型兼容(隐式转换或显式重载解析)
    • 隐式转换:小类型→大类型(intlong
    • 显式转换:大类型→小类型需强制转换(可能精度丢失)
    • 自动装箱/拆箱:intInteger(注意NPE风险)
// 定义:形参 a, b 是占位符
int add(int a, int b) {
    return a + b;
}
 
// 调用:实参 3, 5 是实际值
int result = add(3, 5);

传递方式(以Java为例):值传递 vs 引用传递

值传递(Pass by Value)
  • 机制:实参值的副本传递给形参
  • 适用:基本数据类型(int, double, boolean 等)
  • 效果:函数内修改形参不影响实参
  • 内存行为:栈中新建副本,函数结束副本销毁
void modify(int x) {
    x = 100;  // 只修改副本
}
int a = 10;
modify(a);    // a 仍然是 10
引用传递(Pass by Reference)
  • 机制:实参的内存地址传递给形参
  • 适用:引用数据类型(对象、数组)
  • 效果:函数内通过引用修改对象会影响实参指向的对象,重赋引用(即内部形参修改引用)→不影响实参指向的对象
  • 内存行为:栈中复制引用地址(指向同一堆内存),堆中数据共享
void demo(int x, List<String> list) {
    x = 100;          // 外部int不变
    list.add("new");  // 外部List新增元素
    list = new ArrayList<>(); // 外部引用不变(仅形参指向新对象)
}

重要辨析:Java/Python 等语言是”值传递引用类型”(传递引用的副本),而非纯粹的引用传递。这解释了为什么交换对象引用本身不会生效。

可变参数

  • 语法:类型... name(必须是参数列表末尾)
  • 本质:编译为数组,可传0~N个参数
  • 重载优先级:固定参数 > 可变参数(避免歧义)

函数特性

函数重载(Overload)

  • 定义:同一类中同名函数,参数列表不同(数量、类型、顺序)+ 与返回值无关
  • 解析机制:编译时静态绑定(Static Binding),根据实参类型选择最匹配版本
  • 价值:提供友好API(如print(int), print(String)

递归函数

  • 定义:函数直接或间接调用自身
  • 要素:基准条件(Base Case)+ 递归步骤(Recursive Step)
  • 经典案例:阶乘、斐波那契、树形遍历、分治算法
  • ⚠️ 警惕:
    • 栈溢出(StackOverflowError
    • 重复计算(可用记忆化优化)
    • 尾递归:部分语言优化(Java不支持,需手动转循环)

底层原理

理解函数如何实现,才能写出高性能、线程安全的代码。

内存模型(函数视角)

程序内存分区

  1. 代码区:存储函数体指令(只读)
  2. 栈区(Stack):存储局部变量、函数参数、返回地址
    • 函数调用期,每次调用生成新栈帧
    • 自动分配/释放,LIFO(后进先出)
    • 空间有限(通常1-8MB),递归过深导致溢出
  3. 堆区(Heap):存储对象实例、引用类型数据
    • 手动/垃圾回收管理,空间大但访问慢于栈
    • 函数间共享数据的媒介
  • 方法区:存储字节码、静态变量
    • JVM运行期,函数代码本身存储位置

变量生命周期

  • 局部变量:函数调用时栈分配,返回时销毁
  • 实例变量:对象在堆中创建时产生,垃圾回收时销

调用栈(Call Stack)

  • 工作流程:
    1. 调用函数 → 创建栈帧(含局部变量表、操作数栈等)→ 压栈
    2. 执行中调用新函数 → 重复压栈
    3. 函数返回 → 弹出栈帧 → 恢复上一帧状态
  • 调试价值:
    • 异常堆栈跟踪 = 调用栈快照(定位错误源头)
    • 递归深度可视化(IDE调试器可查看栈帧)
  • 溢出根源:无限递归/超深递归 → 栈空间耗尽 → StackOverflowError
  • 尾递归优化(Tail Call Optimization) 若递归调用是函数最后操作,编译器可复用当前栈帧而非新建,将递归转为循环(但并非所有语言支持,如 Java 不支持,Scala/C++ 部分支持)。
return func(a);      // ✅ 是!直接返回递归结果
return func(a) + 1;  // ❌ 否!递归后还有加法
int x = func(a);     // ❌ 否!赋值后还有操作
return 1 + func(a);  // ❌ 否!运算在递归后
public static int sumTail(int n, int acc) {
    if (n == 0) return acc;
    return sumTail(n - 1, acc + n); // 递归是最后一行!无后续操作
}
// 调用 sumTail(10000, 0) → 依然 StackOverflowError!
 
// 实际优化(Java不优化)
int sumTail(int n, int acc) {
    while (true) {
        if (n == 0) return acc;
        acc = acc + n;
        n = n - 1;
    }
}

关联网络

演化日志

  • v0.1 (2026-01-30):补充关联网络、演化日志,初始版本

复习回顾

📈 轮次: 1 🕒 lastReview: 2026-01-30 14:22:40 📅 nextReview: 2026-02-06 00:00:00