C++基础数据类型与变量管理:内存安全与高效代码的基石

C++基础数据类型与变量管理:内存安全与高效代码的基石

一、学习目标与重点

本章是C++编程的起点,将帮助你建立对基础数据类型和变量管理的核心认知。通过学习,你将能够:

  1. 掌握C++的基础数据类型系统,包括内置类型的分类、取值范围和内存占用
  2. 理解变量的声明与定义规则,建立变量生命周期管理的基本概念
  3. 学会类型转换的正确方式,避免隐式类型转换带来的bug
  4. 理解引用与指针的本质区别,并掌握其安全使用方法
  5. 培养内存安全意识,建立良好的编程习惯,为后续学习打下坚实基础

二、C++基础数据类型系统

2.1 内置类型的分类与特点

C++语言提供了丰富的内置数据类型,按照使用场景可以分为以下几类:

  • 整数类型:用于表示整数值
  • 浮点类型:用于表示实数(包含小数部分)
  • 字符类型:用于表示单个字符或小整数
  • 布尔类型:用于表示逻辑值(true或false)
  • 空类型:用于表示无类型或空指针

2.2 常用内置类型详解

让我们通过代码示例深入了解C++的常用内置类型:

cpp 复制代码
#include <iostream>
#include <iomanip>  // 用于设置输出格式

int main() {
    // 整数类型示例
    int int_value = 42;
    long long long_value = 1000000000000LL; // 末尾LL表示long long类型
    unsigned int uint_value = 100; // 无符号整数

    // 浮点类型示例
    float float_value = 3.14f; // 末尾f表示float类型
    double double_value = 3.141592653589793; // 默认浮点类型
    long double ld_value = 3.14159265358979323846L; // 末尾L表示long double类型

    // 字符类型示例
    char char_value = 'A';
    wchar_t wchar_value = L'中'; // 宽字符类型,支持Unicode字符
    char16_t char16_value = u'中'; // 16位Unicode字符
    char32_t char32_value = U'中'; // 32位Unicode字符

    // 布尔类型示例
    bool bool_value = true; // 或者false

    // 输出各类型的信息
    std::cout << "=== C++基础数据类型示例 ===" << std::endl;

    std::cout << "\n--- 整数类型 ---" << std::endl;
    std::cout << "int: " << int_value << std::endl;
    std::cout << "long long: " << long_value << std::endl;
    std::cout << "unsigned int: " << uint_value << std::endl;

    std::cout << "\n--- 浮点类型 ---" << std::endl;
    std::cout << std::fixed << std::setprecision(15); // 设置浮点数输出精度
    std::cout << "float: " << float_value << std::endl;
    std::cout << "double: " << double_value << std::endl;
    std::cout << "long double: " << ld_value << std::endl;

    std::cout << "\n--- 字符类型 ---" << std::endl;
    std::cout << "char: " << char_value << std::endl;
    std::wcout << L"wchar_t: " << wchar_value << std::endl; // 宽字符输出
    std::cout << "char16_t: " << static_cast<int>(char16_value) << std::endl;
    std::cout << "char32_t: " << static_cast<int>(char32_value) << std::endl;

    std::cout << "\n--- 布尔类型 ---" << std::endl;
    std::cout << "bool: " << bool_value << std::endl;

    // 输出各类型的字节大小
    std::cout << "\n--- 各类型字节大小 ---" << std::endl;
    std::cout << "int: " << sizeof(int) << "字节" << std::endl;
    std::cout << "long long: " << sizeof(long long) << "字节" << std::endl;
    std::cout << "unsigned int: " << sizeof(unsigned int) << "字节" << std::endl;
    std::cout << "float: " << sizeof(float) << "字节" << std::endl;
    std::cout << "double: " << sizeof(double) << "字节" << std::endl;
    std::cout << "long double: " << sizeof(long double) << "字节" << std::endl;
    std::cout << "char: " << sizeof(char) << "字节" << std::endl;
    std::cout << "wchar_t: " << sizeof(wchar_t) << "字节" << std::endl;
    std::cout << "char16_t: " << sizeof(char16_t) << "字节" << std::endl;
    std::cout << "char32_t: " << sizeof(char32_t) << "字节" << std::endl;
    std::cout << "bool: " << sizeof(bool) << "字节" << std::endl;

    return 0;
}

2.3 类型范围与内存占用分析

从上述示例中,我们可以看到:

  • int类型 通常占4字节,取值范围约为-231到231-1
  • long long类型 通常占8字节,取值范围约为-263到263-1
  • float类型通常占4字节,精度约为6位小数
  • double类型通常占8字节,精度约为15位小数
  • char类型通常占1字节,可表示ASCII字符
  • wchar_t类型通常占2字节(Windows)或4字节(Unix/Linux),支持Unicode字符

2.4 自定义类型简介

除了内置类型,C++还支持自定义类型,包括:

  • 结构体(struct):允许将不同类型的数据组合在一起
  • 类(class):面向对象编程的核心,封装数据和方法
  • 枚举(enum):定义一组命名的整数常量
  • 联合(union):允许不同类型的数据共享同一块内存空间

我们来看一个自定义类型的简单示例:

cpp 复制代码
#include <iostream>
#include <string>

// 定义结构体类型
struct Person {
    std::string name;
    int age;
    double height;
};

// 定义枚举类型
enum Weekday {
    Monday,    // 默认值为0
    Tuesday,   // 默认值为1
    Wednesday, // 默认值为2
    Thursday,  // 默认值为3
    Friday,    // 默认值为4
    Saturday,  // 默认值为5
    Sunday     // 默认值为6
};

// 定义类类型
class Circle {
private:
    double radius;  // 私有成员变量
    
public:
    // 构造函数
    Circle(double r) : radius(r) {}
    
    // 计算面积的成员函数
    double getArea() const {
        return 3.14159 * radius * radius;
    }
    
    // 计算周长的成员函数
    double getCircumference() const {
        return 2 * 3.14159 * radius;
    }
};

int main() {
    // 使用结构体类型
    Person person1;
    person1.name = "张三";
    person1.age = 30;
    person1.height = 1.75;
    
    std::cout << "=== 结构体类型示例 ===" << std::endl;
    std::cout << "姓名: " << person1.name << std::endl;
    std::cout << "年龄: " << person1.age << "岁" << std::endl;
    std::cout << "身高: " << person1.height << "米" << std::endl;
    
    // 使用枚举类型
    Weekday today = Monday;
    std::cout << "\n=== 枚举类型示例 ===" << std::endl;
    std::cout << "今天是周" << today + 1 << std::endl;
    
    // 使用类类型
    Circle circle(5.0);
    std::cout << "\n=== 类类型示例 ===" << std::endl;
    std::cout << "圆的半径: " << 5.0 << std::endl;
    std::cout << "圆的面积: " << circle.getArea() << std::endl;
    std::cout << "圆的周长: " << circle.getCircumference() << std::endl;
    
    return 0;
}

三、变量的声明、定义与生命周期

3.1 变量的声明与定义

在C++中,变量的声明定义是两个不同的概念:

  • 声明:向编译器介绍变量的名称和类型,但不分配内存空间
  • 定义:不仅介绍变量的名称和类型,还为其分配内存空间
cpp 复制代码
#include <iostream>

// 声明一个全局变量(声明但不定义)
extern int global_var;

int main() {
    // 定义一个局部变量
    int local_var = 10;
    
    // 声明一个变量,但未定义(会导致编译错误)
    // int undefined_var;
    // std::cout << undefined_var << std::endl;
    
    std::cout << "局部变量: " << local_var << std::endl;
    return 0;
}

// 定义全局变量
int global_var = 20;

3.2 变量的存储类型与作用域

变量的存储类型决定了它的存储位置和生命周期:

cpp 复制代码
#include <iostream>

// 全局变量(静态存储区)
int global_var = 10;

void function() {
    // 静态局部变量(静态存储区)
    static int static_var = 0;
    static_var++;
    
    // 局部变量(栈区)
    int local_var = 0;
    local_var++;
    
    std::cout << "静态局部变量: " << static_var << std::endl;
    std::cout << "局部变量: " << local_var << std::endl;
}

int main() {
    std::cout << "第一次调用function():" << std::endl;
    function();
    
    std::cout << "\n第二次调用function():" << std::endl;
    function();
    
    std::cout << "\n全局变量: " << global_var << std::endl;
    
    return 0;
}

运行上述代码,你会发现静态局部变量static_var的数值会在函数调用之间保留,而局部变量local_var的数值每次都会重置为0。

3.3 变量的初始化

变量的初始化是一个重要的编程习惯,可以避免未初始化变量导致的未定义行为:

cpp 复制代码
#include <iostream>
#include <string>

// 初始化结构体
struct Point {
    int x;
    int y;
    
    // 构造函数(C++11及以后)
    Point(int x = 0, int y = 0) : x(x), y(y) {}
};

int main() {
    // 初始化基础类型变量
    int a = 0;              // 拷贝初始化
    int b(10);             // 直接初始化
    int c{20};             // 列表初始化(C++11及以后)
    
    // 初始化数组
    int arr1[3] = {1, 2, 3};       // 数组初始化
    int arr2[] = {1, 2, 3, 4};     // 自动推断大小
    
    // 初始化字符串
    std::string str1 = "Hello";    // 拷贝初始化
    std::string str2("World");     // 直接初始化
    std::string str3{"C++"};       // 列表初始化
    
    // 初始化结构体
    Point p1;              // 使用默认构造函数
    Point p2(10, 20);      // 直接初始化
    Point p3{100, 200};    // 列表初始化
    
    std::cout << "=== 变量初始化示例 ===" << std::endl;
    
    std::cout << "\n基础类型: " << a << ", " << b << ", " << c << std::endl;
    
    std::cout << "\n数组arr1: ";
    for (int i : arr1) std::cout << i << " ";
    std::cout << std::endl;
    
    std::cout << "数组arr2: ";
    for (int i : arr2) ::cout << i << " ";
    std::cout << std::endl;
    
    std::cout << "\n字符串: " << str1 << ", " << str2 << ", " << str3 << std::endl;
    
    std::cout << "\n结构体: (" << p1.x << ", " << p1.y << "), ";
    std::cout << "(" << p2.x << ", " << p2.y << "), ";
    std::cout << "(" << p3.x << ", " << p3.y << ")" << std::endl;
    
    return 0;
}

四、类型转换的正确方式

4.1 隐式类型转换与显式类型转换

C++中的类型转换分为隐式类型转换显式类型转换。隐式类型转换是编译器自动进行的,而显式类型转换需要我们显式地告诉编译器。

cpp 复制代码
#include <iostream>
#include <iomanip>

int main() {
    // 隐式类型转换示例
    int int_value = 10;
    double double_value = int_value;  // int -> double,隐式转换
    
    char char_value = 'A';
    int ascii_value = char_value;    // char -> int,隐式转换
    
    bool bool_value = true;
    int bool_int = bool_value;       // bool -> int,隐式转换
    
    std::cout << "=== 隐式类型转换示例 ===" << std::endl;
    std::cout << "int转double: " << double_value << std::endl;
    std::cout << "char转int: " << ascii_value << std::endl;
    std::cout << "bool转int: " << bool_int << std::endl;
    
    // 显式类型转换示例
    double d = 3.14159;
    int i = (int)d;                  // C风格强制转换
    int j = static_cast<int>(d);     // C++风格强制转换
    
    std::cout << "\n=== 显式类型转换示例 ===" << std::endl;
    std::cout << "C风格强制转换: " << i << std::endl;
    std::cout << "C++风格强制转换: " << j << std::endl;
    
    // 类型转换警告示例
    int large_int = 1000;
    char overflow_char = large_int;  // 可能导致溢出的隐式转换
    
    std::cout << "\n=== 类型转换警告示例 ===" << std::endl;
    std::cout << "大整数转char: " << static_cast<int>(overflow_char) << std::endl;
    
    return 0;
}

4.2 C++中的四种类型转换运算符

C++提供了四种类型转换运算符,它们比C风格的强制转换更加安全和明确:

cpp 复制代码
#include <iostream>
#include <vector>

// 基类
class Base {
public:
    virtual void print() {
        std::cout << "Base class" << std::endl;
    }
};

// 派生类
class Derived : public Base {
public:
    void print() override {
        std::cout << "Derived class" << std::endl;
    }
    
    void specificFunction() {
        std::cout << "Derived specific function" << std::endl;
    }
};

int main() {
    // 1. static_cast:用于编译时的类型转换
    double d = 3.14;
    int i = static_cast<int>(d);
    std::cout << "static_cast: " << i << std::endl;
    
    // 2. dynamic_cast:用于运行时的类型转换(主要用于类层次结构)
    Base* base_ptr = new Derived();
    Derived* derived_ptr = dynamic_cast<Derived*>(base_ptr);
    
    if (derived_ptr) {
        std::cout << "dynamic_cast成功: ";
        derived_ptr->specificFunction();
    } else {
        std::cout << "dynamic_cast失败" << std::endl;
    }
    
    // 3. const_cast:用于修改类型的const属性
    const int* const_ptr = new int(10);
    int* mutable_ptr = const_cast<int*>(const_ptr);
    *mutable_ptr = 20;
    std::cout << "const_cast: " << *mutable_ptr << std::endl;
    
    // 4. reinterpret_cast:用于底层类型转换(最危险)
    int x = 10;
    void* void_ptr = &x;
    int* int_ptr = reinterpret_cast<int*>(void_ptr);
    std::cout << "reinterpret_cast: " << *int_ptr << std::endl;
    
    // 清理内存
    delete base_ptr;
    delete const_ptr;
    
    return 0;
}

4.3 避免隐式类型转换的最佳实践

隐式类型转换可能导致意外的行为和难以调试的bug。以下是一些最佳实践:

  1. 尽量避免使用可能导致隐式转换的操作
  2. 对于需要转换的地方,使用显式类型转换
  3. 对于自定义类型,尽量不要定义隐式转换构造函数
  4. 使用C++风格的类型转换运算符而不是C风格的强制转换
  5. 对于关键代码,可以使用static_assert来检查类型的兼容性

五、引用与指针的本质区别

5.1 引用的基本用法

引用是C++中一个重要的概念,它提供了一个变量的别名:

cpp 复制代码
#include <iostream>
#include <string>

void swap(int& a, int& b) {
    int temp = a;
    a = b;
    b = temp;
}

int main() {
    int x = 10;
    int y = 20;
    
    std::cout << "交换前: x = " << x << ", y = " << y << std::endl;
    swap(x, y);
    std::cout << "交换后: x = " << x << ", y = " << y << std::endl;
    
    // 引用必须初始化
    // int& ref;  // 编译错误
    
    // 引用一旦绑定到一个变量,就不能再绑定到其他变量
    int z = 30;
    int& ref = x;
    ref = z;  // 这不是改变引用的绑定,而是将z的值赋给x
    std::cout << "ref = z后: x = " << x << ", z = " << z << std::endl;
    
    return 0;
}

5.2 指针的基本用法

指针是C++中另一个重要的概念,它存储了一个变量的内存地址:

cpp 复制代码
#include <iostream>

void swap(int* a, int* b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}

int main() {
    int x = 10;
    int y = 20;
    
    std::cout << "交换前: x = " << x << ", y = " << y << std::endl;
    swap(&x, &y);
    std::cout << "交换后: x = " << x << ", y = " << y << std::endl;
    
    // 指针可以为空
    int* null_ptr = nullptr;
    
    // 指针可以重新指向其他变量
    int z = 30;
    int* ptr = &x;
    ptr = &z;  // 指针重新指向z
    std::cout << "ptr指向z后: " << *ptr << std::endl;
    
    return 0;
}

5.3 引用与指针的区别

引用和指针虽然都可以用来间接访问变量,但它们有几个关键区别:

特性 引用 指针
定义方式 int& ref = x; int* ptr = &x;
空值 引用不能为空,必须初始化 指针可以为空(nullptr
重新绑定 引用一旦绑定到变量,就不能再绑定到其他变量 指针可以重新指向其他变量
内存开销 引用本身不占用内存空间(它是变量的别名) 指针通常占8字节(在64位系统上)
使用方式 直接使用引用名,就像使用变量本身一样 需要使用解引用运算符*来访问所指向的变量
生命周期 引用的生命周期取决于它所绑定的变量 指针的生命周期独立于它所指向的变量

5.4 智能指针的使用

为了避免手动管理内存导致的内存泄漏和悬挂指针问题,C++11引入了智能指针:

cpp 复制代码
#include <iostream>
#include <memory>

void printValue(std::shared_ptr<int> ptr) {
    std::cout << "Value: " << *ptr << std::endl;
    std::cout << "Use count: " << ptr.use_count() << std::endl;
}

int main() {
    // shared_ptr:多个指针共享同一个对象
    std::shared_ptr<int> ptr1 = std::make_shared<int>(10);
    std::cout << "ptr1 use count: " << ptr1.use_count() << std::endl;
    
    std::shared_ptr<int> ptr2 = ptr1;
    std::cout << "ptr2 use count: " << ptr2.use_count() << std::endl;
    
    printValue(ptr1);
    std::cout << "After printValue use count: " << ptr1.use_count() << std::endl;
    
    // unique_ptr:独占对象的所有权
    std::unique_ptr<int> unique_ptr = std::make_unique<int>(20);
    std::cout << "\nunique_ptr value: " << *unique_ptr << std::endl;
    
    // 不能拷贝unique_ptr,但可以移动
    // std::unique_ptr<int> unique_ptr2 = unique_ptr;  // 编译错误
    std::unique_ptr<int> unique_ptr2 = std::move(unique_ptr);
    std::cout << "unique_ptr2 value: " << *unique_ptr2 << std::endl;
    
    // weak_ptr:不增加对象的引用计数
    std::weak_ptr<int> weak_ptr = ptr1;
    std::cout << "\nweak_ptr use count: " << ptr1.use_count() << std::endl;
    
    if (auto lock_ptr = weak_ptr.lock()) {
        std::cout << "weak_ptr value: " << *lock_ptr << std::endl;
    } else {
        std::cout << "weak_ptr指向的对象已被释放" << std::endl;
    }
    
    return 0;
}

六、内存安全与最佳实践

6.1 常见的内存安全问题

在C++编程中,常见的内存安全问题包括:

  • 内存泄漏:动态分配的内存未被释放
  • 悬挂指针:指针指向已被释放的内存
  • 缓冲区溢出:向数组写入超出其边界的数据
  • 重复释放:对已被释放的内存再次调用delete

6.2 内存管理的最佳实践

为了避免内存安全问题,我们可以采取以下最佳实践:

  1. 使用智能指针 :优先使用std::shared_ptrstd::unique_ptr来管理动态内存
  2. 避免裸指针:尽量避免使用裸指针来管理动态内存
  3. 使用容器类 :使用标准库提供的容器类(如std::vectorstd::string)代替手动管理数组
  4. 初始化变量:确保所有变量都被正确初始化
  5. 避免缓冲区溢出:使用边界检查或标准库提供的安全函数
  6. 使用RAII(资源获取即初始化):将资源的获取和初始化绑定在一起,确保资源被正确释放

6.3 使用Valgrind进行内存泄漏检测

Valgrind是一个强大的内存调试工具,可以帮助我们检测内存泄漏和其他内存安全问题:

bash 复制代码
# 编译程序时启用调试信息
g++ -g -o program program.cpp

# 使用Valgrind检测内存泄漏
valgrind --leak-check=yes ./program

七、综合案例:实现一个简单的学生管理系统

让我们通过一个综合案例来应用本章所学的知识:

cpp 复制代码
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>

// 定义学生类
class Student {
private:
    std::string name;
    int age;
    double score;
    
public:
    // 构造函数
    Student(const std::string& name, int age, double score)
        : name(name), age(age), score(score) {}
    
    // 获取学生信息的方法
    std::string getName() const { return name; }
    int getAge() const { return age; }
    double getScore() const { return score; }
    
    // 设置学生信息的方法
    void setName(const std::string& newName) { name = newName; }
    void setAge(int newAge) { age = newAge; }
    void setScore(double newScore) { score = newScore; }
    
    // 打印学生信息的方法
    void printInfo() const {
        std::cout << "姓名: " << name << ", 年龄: " << age << ", 成绩: " << score << std::endl;
    }
};

// 定义学生管理系统类
class StudentManagementSystem {
private:
    std::vector<Student> students;
    
public:
    // 添加学生
    void addStudent(const Student& student) {
        students.push_back(student);
    }
    
    // 删除学生
    void removeStudent(const std::string& name) {
        auto it = std::remove_if(students.begin(), students.end(),
            [name](const Student& s) { return s.getName() == name; });
        students.erase(it, students.end());
    }
    
    // 查找学生
    Student* findStudent(const std::string& name) {
        for (auto& student : students) {
            if (student.getName() == name) {
                return &student;
            }
        }
        return nullptr;
    }
    
    // 打印所有学生信息
    void printAllStudents() const {
        std::cout << "=== 学生信息列表 ===" << std::endl;
        for (const auto& student : students) {
            student.printInfo();
        }
        std::cout << std::endl;
    }
    
    // 按成绩排序学生
    void sortByScore() {
        std::sort(students.begin(), students.end(),
            [](const Student& a, const Student& b) { return a.getScore() > b.getScore(); });
    }
    
    // 计算平均成绩
    double calculateAverageScore() const {
        if (students.empty()) {
            return 0.0;
        }
        
        double totalScore = 0.0;
        for (const auto& student : students) {
            totalScore += student.getScore();
        }
        
        return totalScore / students.size();
    }
};

// 主函数
int main() {
    // 创建学生管理系统对象
    StudentManagementSystem sms;
    
    // 添加学生
    sms.addStudent(Student("张三", 20, 85.5));
    sms.addStudent(Student("李四", 21, 90.0));
    sms.addStudent(Student("王五", 19, 78.5));
    sms.addStudent(Student("赵六", 20, 92.5));
    
    // 打印所有学生信息
    sms.printAllStudents();
    
    // 查找学生
    std::string searchName = "李四";
    Student* foundStudent = sms.findStudent(searchName);
    if (foundStudent) {
        std::cout << "找到了学生 " << searchName << ": ";
        foundStudent->printInfo();
    } else {
        std::cout << "没有找到学生 " << searchName << std::endl;
    }
    std::cout << std::endl;
    
    // 按成绩排序
    sms.sortByScore();
    std::cout << "按成绩排序后的学生信息:" << std::endl;
    sms.printAllStudents();
    
    // 计算平均成绩
    double averageScore = sms.calculateAverageScore();
    std::cout << "平均成绩: " << averageScore << std::endl;
    
    // 删除学生
    std::string removeName = "王五";
    sms.removeStudent(removeName);
    std::cout << "删除学生 " << removeName << "后的学生信息:" << std::endl;
    sms.printAllStudents();
    
    return 0;
}

八、总结与练习

8.1 本章总结

本章介绍了C++基础数据类型与变量管理的核心知识,包括:

  1. C++基础数据类型系统的分类和特点
  2. 变量的声明、定义和生命周期管理
  3. 类型转换的正确方式和最佳实践
  4. 引用与指针的本质区别及其安全使用方法
  5. 内存安全管理的重要性和最佳实践
  6. 综合案例:实现一个简单的学生管理系统

8.2 练习题

  1. 写一个程序,计算并输出int、long、long long和unsigned int类型的最大值和最小值。
  2. 编写一个函数,将两个整数相加,并使用引用传递参数返回结果。
  3. 编写一个程序,使用智能指针管理动态分配的数组。
  4. 写一个程序,计算并输出两个浮点数的和、差、积和商。
  5. 实现一个简单的图书管理系统,包含添加、删除、查找和打印图书信息的功能。

8.3 进阶挑战

  1. 研究C++中的类型安全问题,并编写一个程序来演示这些问题。
  2. 学习如何使用Valgrind工具检测内存泄漏,并应用到实际项目中。
  3. 研究C++中的内存对齐规则,并编写一个程序来演示这些规则。
  4. 学习如何使用C++的模板技术来实现通用的类型转换函数。

通过本章的学习,你已经掌握了C++基础数据类型与变量管理的核心知识,这些知识将为你后续学习C++的高级特性奠定坚实的基础。

相关推荐
西红市杰出青年2 小时前
crawl4ai------AsyncPlaywrightCrawlerStrategy使用教程
开发语言·python·架构·正则表达式·pandas
进击的小菜鸡dd2 小时前
互联网大厂Java面试:微服务、电商场景下的全栈技术问答与解析
java·spring boot·缓存·微服务·消息队列·日志·电商
sunnyday04262 小时前
Spring Boot 应用启动成功后的事件监听与日志输出实践
java·spring boot·后端
Logan Lie2 小时前
Go语言接口(interface)深度详解
开发语言·数据库·golang
小欣加油2 小时前
leetcode 面试题17.16 按摩师
数据结构·c++·算法·leetcode·动态规划
予枫的编程笔记2 小时前
【JDK版本】JDK版本迁移避坑指南:从8→17/21实操全解析
java·人工智能·jdk
独断万古他化2 小时前
【MyBatis 深度解析】注解操作与 XML 配置:增删改查全流程实现
xml·java·spring·mybatis
西部风情2 小时前
稳定性质量系列-系统稳定性建设实践
java·开发语言
短剑重铸之日2 小时前
《7天学会Redis》Day 7 - Redisson 全览
java·数据库·redis·后端·缓存·redission