Java面向对象(上)

一、面向对象思想

面向对象思想(Object-Oriented Programming,OOP)是一种以 "对象" 为核心的编程范式,它将现实世界中的事物抽象为程序中的对象,通过对象之间的交互来实现程序功能,旨在解决传统面向过程编程中代码复用性低、维护性差、扩展性弱等问题。

简单来说,面向对象的核心是把 "数据" 和 "操作数据的行为" 打包在一起,而不是像面向过程那样把程序拆成一系列线性的步骤和函数。

1. 封装(Encapsulation):"数据隐藏" 与 "接口暴露"

将对象的属性(数据)和方法(行为)捆绑在一起,同时隐藏对象内部的实现细节,只对外提供统一的访问接口。

目的

保护数据安全:避免外部直接修改对象的属性,防止数据被非法篡改;

降低耦合度:外部只需关注对象的功能,无需关心内部如何实现。

举例 :一个 "学生" 对象的成绩属性(score)可以被私有化,外部不能直接修改,只能通过setScore()方法修改(可在方法中增加分数合法性校验),通过getScore()方法获取。

2. 继承(Inheritance):"代码复用" 与 "层次划分"

子类(派生类)可以继承父类(基类 / 超类)的属性和方法,同时可以根据需要扩展自己的属性和方法。

目的

实现代码复用:避免重复编写相同的代码;

构建类的层次结构:体现事物的 "一般与特殊" 关系。

举例:定义 "动物" 作为父类(属性:名字、年龄;方法:吃、睡),"猫" 和 "狗" 作为子类,继承父类的属性和方法,同时新增自己的方法(猫:抓老鼠;狗:看门)。

3. 多态(Polymorphism):"一个接口,多种实现"

同一行为(方法)在不同对象上有不同的表现形式,即父类的引用可以指向子类的对象,调用方法时会根据对象的实际类型执行对应的实现。

目的

提高代码的灵活性和扩展性;

降低代码的冗余,符合 "开闭原则"(对扩展开放,对修改关闭)。

举例 :父类 "动物" 有方法makeSound(),子类 "猫" 实现为 "喵喵叫",子类 "狗" 实现为 "汪汪叫"。当用Animal animal = new Cat()调用animal.makeSound()时,会执行猫的叫声;换成new Dog()则执行狗的叫声。

二类与对象:面向对象的核心载体

类(Class)和对象(Object)是面向对象思想的基本构成单元 ,二者是 "抽象模板" 与 "具体实例" 的关系,简单来说:类是对一类事物的抽象定义,对象是这类事物的具体存在

1.类与对象的概念

类是对现实世界中同一类事物的共同属性和行为的抽象概括,它定义了这类事物有什么特征(属性)、能做什么动作(方法),但本身并不是具体的事物,只是一个 "蓝图" 或 "模板"。

对象是类的实例化结果 ,是根据类的模板创建出来的具体个体,拥有类中定义的所有属性和方法,是实实在在存在的 "实体"。

2.类的定义

在java中类的定义格式如下:

java 复制代码
class 类名{
    成员变量;
    成员方法;
}

注意:在Java中,定义在类中的变量成为成员变量,定义在方法中的变量称为局部变量。如下示例:

java 复制代码
class Student {
    int age = 30;
    void read(){
        int age = 50;
        System.out.println("大家好,我" + age + "岁了,我在看书!");
    }
}

上述代码中,再Student类的read()方法中有一条打印语句,访问了变量age,此时访问的是局部变量age,也就是说当有一个程序调用read()方法时,输出的age值为50,而不是30。

3.对象的创建与使用

在java程序中可以使用new关键字创建对象,格式如下:

java 复制代码
 类名 对象名 = new 类名();

使用类创建对象具体实例:

Phone.java

java 复制代码
// 定义类:手机类(作为创建对象的模板)
class Phone {
    // 成员变量(属性):描述手机的特征
    String brand; // 品牌
    int price;    // 价格

    // 成员方法(行为):描述手机的功能
    public void call() {
        System.out.println(brand + "手机正在打电话");
    }

    public void sendMessage() {
        System.out.println(brand + "手机正在发短信");
    }
}

Test01.java

java 复制代码
public class Test01 {
    public static void main(String[] args) {
        // 2. 创建对象:通过new关键字+构造方法实例化
        // 方式1:无参构造创建对象(Java默认提供)
        Phone phone1 = new Phone();

        // 3. 使用对象:为属性赋值
        phone1.brand = "苹果";
        phone1.price = 5999;

        // 4. 使用对象:调用方法
        System.out.println("手机品牌:" + phone1.brand + ",价格:" + phone1.price + "元");
        phone1.call();
        phone1.sendMessage();
    }
}

上述代码在main()方法中实例化了一个Phone对象,名称为Phone1。在java中,使用new关键字创建的对象在堆内分配空间。如下图:

栈内存 :存储对象的引用变量(即对象的地址)。

堆内存 :存储对象的实际数据(属性值)。

创建对象后,使用"."访问对象的属性与方法,我们可以改写一下Test01.java的内容:

java 复制代码
public class Test01 {
    public static void main(String[] args) {
        // 2. 创建对象:通过new关键字+构造方法实例化
        // 方式1:无参构造创建对象(Java默认提供)
        Phone phone1 = new Phone();

        // 3. 使用对象:为属性赋值
        phone1.brand = "苹果";
        phone1.price = 5999;

        // 4. 使用对象:调用方法
        System.out.println("手机品牌:" + phone1.brand + ",价格:" + phone1.price + "元");
        phone1.call();
        phone1.sendMessage();

        phone1.brand="华为";
        phone1.price=6999;
        System.out.println("手机品牌:" + phone1.brand + ",价格:" + phone1.price + "元");
    }
}

4.对象的引用传递

在 Java 中,一切变量的传递都是值传递 ,但对于对象类型 ,变量存储的是对象在堆内存中的地址(引用),因此传递的是 "引用的副本"------ 这就是常说的 "引用传递"(本质是值传递的一种特殊形式)。

引用传递的本质 :方法传参时,将栈中 "引用变量的地址值" 复制一份传给方法的形参,此时实参和形参指向堆中同一个对象

定义一个简单的类:Student.java

java 复制代码
// 学生类(包含姓名属性)
class Student {
    String name; // 对象的属性(存储在堆内存)

    public Student(String name) {
        this.name = name;
    }
}

测试引用传递:Test02.java

java 复制代码
public class Test02 {

    public static void main(String[] args) {
        // 创建对象,引用变量s指向堆中的Student对象
        Student stu1 = new Student("张三");
        Student stu2 = null;
        stu2 = stu1; // 引用变量stu2指向stu1指向的堆中的Student对象
        stu2.name = "李四";
        System.out.println(stu1.name);
    }
}

方法内通过修改stu2的name属性,本质是修改堆中的数据,因此对象stu1的name属性也会被改变。

5.访问控制

Java 的访问控制(访问修饰符) 用于限定类、属性、方法、构造方法的可见性范围,核心目的是实现封装 、控制代码的访问权限,降低程序耦合度。Java 提供了 4 种访问修饰符,按访问范围从宽到窄依次为:publicprotecteddefault(缺省,无关键字)、private

4 种访问修饰符的核心特性

修饰符 同一类内 同一包内的类 不同包的子类 不同包的非子类 访问范围总结
private 最严格,仅自身可见
default 包级访问,仅同包可见
protected 子类可访问(跨包也可)+ 同包可见
public 最宽松,所有类可见

三、封装性

1.为什么要封装

封装是面向对象的核心特性之一,简单来说,封装的本质是 "隐藏内部细节,暴露统一接口" 。在 Java 中使用封装,核心目的是为了保证数据安全、降低代码耦合、提高程序的可维护性和可扩展性,解决直接访问对象属性 / 方法带来的各种问题。

比如,一个Order类有一个calculatePrice()方法用于计算订单总价,内部实现可能包含折扣、税费等复杂逻辑:

java 复制代码
class Order {
    private double price;
    private double discount;

    // 封装的计算方法(外部只需调用,无需关心内部逻辑)
    public double calculatePrice() {
        // 内部逻辑:原价 - 折扣 + 税费
        return price * (1 - discount) + price * 0.09;
    }

    // set方法用于赋值
    public void setPrice(double price) {
        this.price = price;
    }

    public void setDiscount(double discount) {
        this.discount = discount;
    }
}

2.如何进行封装

在 Java 中,封装的核心实现步骤是:

(1)将类的属性(成员变量)用 private 修饰,使其仅在类内部可见;

(2)提供公共的 getter/setter 方法,用于外部访问和修改属性(可在方法中添加数据校验);

(3)按需封装方法(如内部工具方法用 private,对外功能用 public)。

java 复制代码
// 封装的学生类
public class Student {
    // 1. 私有属性:用private修饰,外部无法直接访问
    private String name; // 姓名
    private int age;     // 年龄

    // 2. 公共的getter方法:获取属性值
    public String getName() {
        return name;
    }

    // 3. 公共的setter方法:修改属性值(可添加数据校验)
    public void setName(String name) {
        // 简单校验:姓名不为空
        if (name != null && !name.trim().isEmpty()) {
            this.name = name;
        } else {
            System.out.println("姓名不能为空!");
        }
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        // 简单校验:年龄在合理范围
        if (age >= 0 && age <= 150) {
            this.age = age;
        } else {
            System.out.println("年龄不合法!");
        }
    }

    // 4. 对外提供的方法:展示学生信息(封装行为)
    public void showInfo() {
        System.out.println("姓名:" + name + ",年龄:" + age);
    }
}

// 测试类
public class TestEncapsulation {
    public static void main(String[] args) {
        // 创建学生对象
        Student stu = new Student();

        // 无法直接访问私有属性(编译报错)
        // stu.name = "张三";
        // stu.age = -18;

        // 通过setter方法赋值(触发数据校验)
        stu.setName(""); // 姓名为空,提示错误
        stu.setName("张三");
        stu.setAge(-18); // 年龄非法,提示错误
        stu.setAge(20);

        // 通过getter方法获取属性值
        System.out.println("获取的姓名:" + stu.getName());
        System.out.println("获取的年龄:" + stu.getAge());

        // 调用封装的方法
        stu.showInfo();
    }
}

四、构造方法

构造方法(Constructor)是 Java 中用于创建并初始化对象的特殊方法,它是对象实例化过程中自动执行的核心方法,保证对象被创建时就具备合理的初始状态。

1.定义构造方法

构造方法的特征:

  1. 名称与类名完全一致 :比如类名是Student,构造方法名也必须是Student,大小写敏感。
  2. 无返回值 :区别于普通方法,不需要写return类型(连void都不能写)。
  3. 自动调用 :通过new关键字创建对象时,JVM 会自动调用对应的构造方法,无需手动调用。
  4. 可重载 :一个类可以定义多个构造方法,只要参数列表(个数、类型、顺序)不同即可。
无参构造方法
  • 如果类中没有显式定义任何构造方法,Java 会自动提供一个默认的无参构造方法
  • 若定义了有参构造,默认无参构造会失效,需手动声明。
java 复制代码
class Cat {
    String name;
    int age;

    // 手动声明无参构造
    public Cat() {
        name = "小白"; // 初始化默认值
        age = 1;
    }
}

public class Test {
    public static void main(String[] args) {
        Cat cat = new Cat(); // 创建对象时自动调用无参构造
        System.out.println(cat.name); // 输出:小白
    }
}
有参构造方法

创建对象时直接为属性赋值,避免后续手动调用setter方法,更高效。

java 复制代码
class Cat {
    String name;
    int age;

    // 有参构造
    public Cat(String name, int age) {
        this.name = name; // this区分成员变量和局部变量
        this.age = age;
    }
}

public class Test {
    public static void main(String[] args) {
        Cat cat = new Cat("小黑", 2); // 调用有参构造赋值
        System.out.println(cat.name + "," + cat.age); // 输出:小黑,2
    }
}

2.构造方法的重载

构造方法的重载(Constructor Overloading)是 Java 中多态 的一种体现,指的是一个类中定义多个构造方法,这些方法名称与类名一致,但参数列表(参数个数、类型、顺序)不同,从而满足不同的对象初始化需求。

核心规则

  1. 方法名必须相同:所有构造方法的名称都和类名一致。
  2. 参数列表必须不同
    • 参数个数不同(比如一个无参,一个单参);
    • 参数类型不同(比如一个参数是int,一个是String);
    • 参数顺序不同(比如(String name, int age)(int age, String name))。
  3. 返回值无要求(因为构造方法本身无返回值):无需考虑返回值的差异。
java 复制代码
class Cat {
    String name;
    int age;

    // 无参构造
    public Cat() {
        this.name = "未知";
        this.age = 0;
    }

    // 单参构造(仅姓名)
    public Cat(String name) {
        this.name = name;
        this.age = 0;
    }

    // 双参构造(姓名+年龄)
    public Cat(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

3.案例:银行取款机

对于银行存取款的流程,人们非常熟悉,用户可在银行对自己的资金账户进行存款、取款、查询余额等操作,极大地方便了人们对资金的管理。本案例要求使用所学的知识编写一个程序,实现银行存取款功能。案例要求具体如下:

(1)创建账户,初始存款为 0 元。

(2)向账户存入 100 元。

(3)从账户取出 80 元。

整体采用 "循环菜单展示 + 分支指令处理" 的核心思路实现存款、取款、查询余额和退出的基础功能。首先初始化账户余额为 0.0,创建 Scanner 对象用于接收用户输入,并定义布尔变量isRunning控制程序的持续运行;接着通过while循环持续展示 ATM 功能菜单(存款、取款、显示余额、退出),每次循环中获取用户输入的功能选择项;然后利用switch分支语句对用户的不同选择进行针对性处理:选择 1 时接收存款金额,校验金额大于 0 后更新余额并提示存款成功,否则提示输入错误;选择 2 时接收取款金额,先校验金额大于 0,再判断余额是否充足,满足则扣除金额并提示取款成功,否则分别提示金额错误或余额不足;选择 3 时直接打印当前账户余额;选择 4 时将isRunning设为 false 以终止循环,实现程序退出;若用户输入非 1-4 的数字,通过default分支提示无效选择。

java 复制代码
import java.util.Scanner;

public class ATM {
    public static void main(String[] args) {
        // 初始化余额为0
        double balance = 0.0;// 余额
        Scanner scanner = new Scanner(System.in);
        boolean isRunning = true;

        while (isRunning) {
            // 显示菜单
            System.out.println("=========ATM========");
            System.out.println("1、存款");
            System.out.println("2、取款");
            System.out.println("3、显示余额");
            System.out.println("4、退出");
            System.out.print("请选择(1-4):");

            // 获取用户选择
            int choice = scanner.nextInt();

            switch (choice) {
                case 1: // 存款
                    System.out.print("请输入存款金额:");
                    double deposit = scanner.nextDouble();
                    if (deposit > 0) {
                        balance += deposit;
                        System.out.println("存款成功!当前余额:" + balance + "元");
                    } else {
                        System.out.println("存款金额输入错误,请重新操作!");
                    }
                    break;

                case 2: // 取款
                    System.out.print("请输入取款金额:");
                    double withdraw = scanner.nextDouble();
                    if (withdraw > 0) {
                        if (withdraw <= balance) {
                            balance -= withdraw;
                            System.out.println("取款成功!当前余额:" + balance + "元");
                        } else {
                            System.out.println("余额不足,无法取款!");
                        }
                    } else {
                        System.out.println("取款金额必须大于0,请重新操作!");
                    }
                    break;

                case 3: // 显示余额
                    System.out.println("当前账户余额:" + balance + "元");
                    break;

                case 4: // 退出
                    isRunning = false;
                    System.out.println("感谢使用,再见!");
                    break;

                default:
                    System.out.println("无效选择,请输入1-4之间的数字!");
            }
            System.out.println(); // 输出空行,分隔每次操作
        }
        scanner.close();
    }
}

五、this关键字

1.使用this关键字调用本类中的属性

this 调用本类属性的核心场景是局部变量(如构造方法参数、方法参数)与类的成员变量重名 时,通过 this.属性名 明确指向成员变量,避免命名冲突。

java 复制代码
// 定义一个学生类
class Student {
    // 类的成员变量(属性)
    String name;
    int age;

    // 构造方法:参数与成员变量重名
    public Student(String name, int age) {
        // this.name 指向本类的name属性,name指向构造方法的参数
        this.name = name;
        // this.age 指向本类的age属性,age指向构造方法的参数
        this.age = age;
    }

    // 普通方法:参数与成员变量重名
    public void setInfo(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // 展示属性值
    public void show() {
        // 此处可省略this(无重名时),但加上也可明确指向成员变量
        System.out.println("姓名:" + this.name + ",年龄:" + this.age);
    }
}
java 复制代码
public class ThisTest {
    public static void main(String[] args) {
        // 创建对象时调用构造方法,通过this为属性赋值
        Student stu1 = new Student("张三", 20);
        stu1.show(); // 输出:姓名:张三,年龄:20

        // 调用普通方法,通过this修改属性值
        stu1.setInfo("李四", 22);
        stu1.show(); // 输出:姓名:李四,年龄:22
    }
}

2.使用this关键字调用成员方法

利用 this 返回当前对象,可实现成员方法的链式调用。

java 复制代码
class Person {
    String name;
    int age;

    // 设置姓名,返回当前对象
    public Person setName(String name) {
        this.name = name;
        return this; // 返回当前对象
    }

    // 设置年龄,返回当前对象
    public Person setAge(int age) {
        this.age = age;
        return this; // 返回当前对象
    }

    // 展示信息,调用本类成员方法
    public void show() {
        this.printInfo(); // 调用本类的printInfo()方法
    }

    // 私有成员方法(也可通过this调用)
    private void printInfo() {
        System.out.println("姓名:" + name + ",年龄:" + age);
    }
}

// 测试链式调用
public class ThisChainTest {
    public static void main(String[] args) {
        // 链式调用:通过this返回对象,连续调用方法
        new Person().setName("李四").setAge(25).show();
    }
}

3.使用this关键字调用本类中的构造方法

在 Java 中,可通过this(参数)在一个构造方法中调用本类的另一个构造方法,必须将其作为构造方法的第一行代码,这样能减少初始化代码的冗余,统一初始化逻辑。

java 复制代码
// 定义一个学生类
class Student {
    String name;
    int age;
    String id;

    // 1. 无参构造方法:调用三参构造方法,传入默认值
    public Student() {
        this("未知姓名", 0, "000000"); // 调用本类的三参构造,必须是第一行
        System.out.println("无参构造方法执行");
    }

    // 2. 双参构造方法:调用三参构造方法,传入姓名、年龄和默认学号
    public Student(String name, int age) {
        this(name, age, "000000"); // 调用本类的三参构造,必须是第一行
        System.out.println("双参构造方法执行");
    }

    // 3. 三参构造方法:核心初始化逻辑
    public Student(String name, int age, String id) {
        this.name = name; // this调用属性
        this.age = age;
        this.id = id;
        System.out.println("三参构造方法执行");
    }
}

// 测试类
public class ThisConstructorTest {
    public static void main(String[] args) {
        // 调用无参构造
        System.out.println("=====创建第一个学生对象=====");
        Student stu1 = new Student();
        System.out.println("姓名:" + stu1.name + ",年龄:" + stu1.age + ",学号:" + stu1.id);

        // 调用双参构造
        System.out.println("\n=====创建第二个学生对象=====");
        Student stu2 = new Student("张三", 20);
        System.out.println("姓名:" + stu2.name + ",年龄:" + stu2.age + ",学号:" + stu2.id);
    }
}

代码中定义了无参、双参、三参 三个构造方法,它们名称与类名一致,仅参数列表不同,这正是构造方法重载的体现。通过重载,实现了不同初始化场景的支持:

  • 无参构造:给属性赋默认值,适合创建 "默认状态" 的对象;
  • 双参构造:仅传入姓名和年龄,学号用默认值;
  • 三参构造:传入全部属性值,支持自定义初始化。

参和双参构造通过this(...)调用三参构造,避免了在多个构造方法中重复写this.name = name;这类赋值代码。

六、代码块

Java 中的代码块 是指用{}包裹的一段代码,根据定义位置和关键字修饰的不同,可分为普通代码块、构造代码块、静态代码块、同步代码块四类,其中前三者是日常开发中最常用的,核心作用是实现代码的复用和对象 / 类的初始化。

1.普通代码块

定义在方法内部的代码块,主要作用是限定变量的作用域,避免变量名冲突(实际开发中使用较少,更多是语法层面的存在)。

java 复制代码
public class BlockTest {
    public static void main(String[] args) {
        // 普通代码块
        {
            int a = 10;
            System.out.println("普通代码块:a = " + a);
        }
        // 此处无法访问a,因为a的作用域仅限于上面的代码块
        // System.out.println(a); // 编译报错
    }
}

2.构造块

定义在类中、方法外的代码块,每次创建对象时都会执行,且执行顺序在构造方法之前,常用于提取所有构造方法的公共初始化逻辑,实现代码复用。

java 复制代码
class Person {
    String name;

    // 构造代码块
    {
        System.out.println("构造代码块执行:初始化公共逻辑");
        name = "默认姓名"; // 所有对象创建时都会执行的初始化
    }

    // 无参构造
    public Person() {
        System.out.println("无参构造方法执行");
    }

    // 有参构造
    public Person(String name) {
        this.name = name;
        System.out.println("有参构造方法执行");
    }
}

public class BlockTest {
    public static void main(String[] args) {
        System.out.println("=====创建第一个对象=====");
        Person p1 = new Person();
        System.out.println("=====创建第二个对象=====");
        Person p2 = new Person("张三");
    }
}

七、static关键字

static(静态)是 Java 中的一个重要关键字,用于修饰成员变量成员方法代码块内部类 ,其核心特征是:static修饰的成员属于类本身,而非类的某个对象,可直接通过类名访问,无需创建对象。

简单来说,static的作用是将成员 "共享" 给该类的所有对象使用,不再与单个对象绑定。

1.静态属性

static修饰的成员变量,存储在方法区的静态域中,属于类的全局变量。

特点:所有对象共享同一个静态变量,修改一个对象的静态变量,会影响所有对象;类加载时初始化,生命周期与类一致。

java 复制代码
class Student {
    // 静态变量:所有学生共享同一个学校名称
    static String school = "北京大学";
    // 实例变量:每个学生有自己的姓名
    String name;

    public Student(String name) {
        this.name = name;
    }
}

public class StaticTest {
    public static void main(String[] args) {
        Student s1 = new Student("张三");
        Student s2 = new Student("李四");

        // 直接通过类名访问静态变量(推荐)
        System.out.println(Student.school);
        // 通过对象访问(不推荐,易混淆)
        System.out.println(s1.school);

        // 修改静态变量,所有对象都受影响
        Student.school = "清华大学";
        System.out.println(s2.school);
    }
}

2.静态方法(类方法)

static修饰的成员方法,属于类本身。

特点 :可直接通过类名调用,无需创建对象;静态方法中不能直接访问非静态成员(实例变量 / 实例方法),因为静态方法属于类,执行时可能还没有对象。

java 复制代码
class MathUtil {
    // 静态方法:计算两数之和
    public static int add(int a, int b) {
        return a + b;
    }

    // 实例方法:静态方法中无法直接调用
    public void show() {
        System.out.println("实例方法");
    }
}

public class StaticTest {
    public static void main(String[] args) {
        // 直接通过类名调用静态方法
        int sum = MathUtil.add(10, 20);
        System.out.println(sum); // 输出:30

        // 静态方法中调用实例方法(需先创建对象)
        MathUtil util = new MathUtil();
        util.show();
    }
}

3.静态代码块

static修饰的代码块(static {}),位于类中、方法外。

  • 特点类加载时执行,且仅执行一次;常用于初始化静态变量、加载配置文件、连接数据库等全局初始化操作。
java 复制代码
class Config {
    static String configPath;

    // 静态代码块:初始化静态变量
    static {
        configPath = "/config/app.properties";
        System.out.println("静态代码块执行,初始化配置路径");
    }
}

public class StaticTest {
    public static void main(String[] args) {
        // 类加载时静态代码块已执行
        System.out.println(Config.configPath);
    }
}

静态代码块static {})是static修饰的代码块,其核心特征是:Config类被 JVM 加载时自动执行,且整个程序运行期仅执行一次 。这里的逻辑是给静态变量configPath赋值为配置文件的路径,并打印初始化提示。

相关推荐
一点晖光2 小时前
maven推送项目到harhor私有仓库
java·maven
代码or搬砖2 小时前
MySQL窗口函数 OVER()讲解
java·mysql
静心观复3 小时前
Java 中,`1 << 1`
java·开发语言
爱睡觉的王宇昊3 小时前
单体架构详细解析:从概念到实践--购物网站搭建
java·spring boot·架构·团队开发·个人开发·敏捷流程
不思念一个荒废的名字3 小时前
【黑马JavaWeb+AI知识梳理】Web后端开发04-登录认证
java·后端
java1234_小锋3 小时前
Redis到底支不支持事务啊?
java·数据库·redis
爱笑的眼睛113 小时前
超越`cross_val_score`:深入剖析Scikit-learn交叉验证API的设计哲学与高阶实践
java·人工智能·python·ai
L0CK4 小时前
三种依赖注入详解
java
shoubepatien4 小时前
JAVA -- 07
java·后端·intellij-idea