首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >《Java 程序设计》第 4 章 - 类和对象

《Java 程序设计》第 4 章 - 类和对象

作者头像
啊阿狸不会拉杆
发布2026-01-21 12:45:39
发布2026-01-21 12:45:39
160
举报
        大家好!今天我们来深入学习《Java 程序设计》的核心章节 —— 第 4 章类和对象。面向对象编程(OOP)是 Java 的灵魂,掌握类和对象的概念与使用是学好 Java 的基础。本文将从理论到实践,带大家全面理解类和对象的方方面面,每个知识点都配有可直接运行的代码示例,方便大家动手实践。

本章知识思维导图

4.1 面向对象概述

        在学习类和对象之前,我们先来了解什么是面向对象编程(OOP),它为什么会产生,以及它的核心思想是什么。

4.1.1 OOP 的产生

        早期的编程采用面向过程(Procedure-Oriented Programming) 思想,关注 "怎么做",将问题分解为步骤,用函数实现每个步骤。但随着软件规模扩大,面向过程的代码维护性和扩展性越来越差。

        20 世纪 80 年代,面向对象编程(Object-Oriented Programming) 思想应运而生,它关注 "用什么做",将问题分解为对象,通过对象之间的交互解决问题。Java 是典型的面向对象编程语言。

4.1.2 面向对象的基本概念
  • 对象(Object):现实世界中的实体,具有属性(数据)和行为(操作)。例如:一个学生是对象,属性有姓名、年龄;行为有学习、考试。
  • 类(Class):对象的抽象模板,定义了该类对象共同的属性和行为。例如:"学生类" 是对所有学生的抽象,定义了学生都有姓名和年龄,都能学习和考试。
  • 封装(Encapsulation):将对象的属性和行为封装在一起,隐藏内部实现细节,仅通过公共接口交互。
  • 继承(Inheritance):子类可以继承父类的属性和行为,实现代码复用。
  • 多态(Polymorphism):同一行为在不同对象上有不同实现,提高代码灵活性。
4.1.3 面向对象基本特征

面向对象有三大核心特征:

  1. 封装: 隐藏对象的内部细节,只暴露必要的接口。例如:电视机的内部电路被封装,用户只需通过遥控器(接口)操作。
  2. 继承: 子类继承父类的特征和行为,可新增或重写方法。例如:"学生" 和 "老师" 都继承自 "人" 类,都有姓名、年龄,又有各自特有的行为。
  3. 多态: 同一方法在不同对象上有不同实现。例如:"动物" 类有 "发声" 方法,"猫" 实现为 "喵喵叫","狗" 实现为 "汪汪叫"。
4.1.4 OOP 的优势
  • 代码复用:通过继承实现代码复用,减少重复开发。
  • 可维护性:封装使修改内部实现不影响外部使用,便于维护。
  • 可扩展性:新增功能只需新增类或方法,不影响原有代码。
  • 灵活性:多态使程序能处理不同类型的对象,提高灵活性。
  • 贴近现实:以对象为中心的思维更符合人类对现实世界的认知。

4.2 为对象定义类

        类是对象的模板,对象是类的实例。本节将学习如何定义类、创建对象,以及类和对象在内存中的存储方式。

4.2.1 类的定义

在 Java 中,类通过class关键字定义,包含属性(成员变量)和方法(成员函数)。

语法结构

代码语言:javascript
复制
[访问修饰符] class 类名 {
    // 属性定义
    数据类型 属性名1;
    数据类型 属性名2;
    
    // 方法定义
    [访问修饰符] 返回值类型 方法名(参数列表) {
        // 方法体
    }
}

示例:定义一个学生类

代码语言:javascript
复制
/**
 * 学生类:描述学生的共同属性和行为
 */
public class Student {
    // 属性(成员变量)
    String name;  // 姓名
    int age;      // 年龄
    String major; // 专业
    
    // 方法(成员函数)
    /**
     * 学习方法
     */
    public void study() {
        System.out.println(name + "正在学习Java编程!");
    }
    
    /**
     * 介绍自己
     */
    public void introduce() {
        System.out.println("大家好!我叫" + name + ",今年" + age + "岁,专业是" + major);
    }
}
4.2.2 对象的使用

        定义类后,需要创建对象才能使用。通过new关键字创建对象,使用.操作符访问对象的属性和方法。

步骤

  1. 声明对象引用变量
  2. 创建对象(分配内存)
  3. 调用构造方法初始化对象
  4. 使用对象(访问属性 / 调用方法)

示例:使用 Student 类创建对象

代码语言:javascript
复制
/**
 * 测试学生类的使用
 */
public class StudentTest {
    public static void main(String[] args) {
        // 1. 创建对象(声明引用 + 实例化对象)
        Student student1 = new Student();

        // 2. 访问对象属性并赋值
        student1.name = "啊阿狸不会拉杆";
        student1.age = 20;
        student1.major = "人工智能";

        // 3. 调用对象方法
        student1.introduce();
        student1.study();

        // 创建第二个对象
        Student student2 = new Student();
        student2.name = "北海";
        student2.age = 21;
        student2.major = "电子信息";
        student2.introduce();
        student2.study();
    }
}

运行结果

4.2.3 理解栈与堆

Java 中,对象和引用的存储位置不同,理解这一点对掌握 Java 内存模型至关重要。

  • 栈(Stack):存储基本类型变量和对象引用(地址),速度快,容量小。
  • 堆(Heap):存储对象实例(属性值),速度较慢,容量大,由 JVM 自动管理内存。

内存分配流程图

代码演示内存分配

代码语言:javascript
复制
public class MemoryDemo {
    public static void main(String[] args) {
        // 基本类型变量存储在栈中
        int a = 10;
        double b = 3.14;
        
        // 对象引用存储在栈中,对象实例存储在堆中
        Student s1 = new Student();
        s1.name = "王五";
        s1.age = 22;
        
        // s2引用指向s1引用的对象(堆中同一个对象)
        Student s2 = s1;
        s2.major = "人工智能";  // 修改s2的属性,s1也会变化
        
        System.out.println("s1的专业:" + s1.major);  // 输出"人工智能"
        System.out.println("s2的专业:" + s2.major);  // 输出"人工智能"
    }
}

运行结果

注意:当多个引用指向同一个对象时,修改任意引用的属性都会影响该对象本身。

4.2.4 用 UML 图表示类

UML(统一建模语言)类图是描述类结构的标准化图形。用 PlantUML 表示类图:

代码语言:javascript
复制
@startuml
class Student {
  - String name
  - int age
  - String major
  + void study()
  + void introduce()
}

class StudentTest {
  + void main(String[] args)
}

StudentTest --> Student : 使用
@enduml

UML 类图说明

  • 类名在顶部
  • 属性在中间:[可见性] 属性名: 类型-表示私有,+表示公有)
  • 方法在底部:[可见性] 方法名(参数): 返回类型
  • 箭头表示类之间的关系

4.3 方法设计

        方法是类的行为,用于实现特定功能。本节学习方法的设计原则、调用方式、重载、构造方法等核心知识。

4.3.1 如何设计方法

        设计方法应遵循 "单一职责原则":一个方法只做一件事,保持方法简洁易懂。

方法定义语法

代码语言:javascript
复制
[访问修饰符] [static] 返回值类型 方法名([参数类型 参数名1, 参数类型 参数名2...]) {
    // 方法体
    [return 返回值;]  // 如果返回值类型不是void,必须有return语句
}

示例:设计一个计算器类的方法

代码语言:javascript
复制
/**
 * 简单计算器类
 */
public class Calculator {
    /**
     * 加法计算
     * @param a 第一个加数
     * @param b 第二个加数
     * @return 两个数的和
     */
    public int add(int a, int b) {
        return a + b;  // 返回计算结果
    }
    
    /**
     * 减法计算
     * @param a 被减数
     * @param b 减数
     * @return 两个数的差
     */
    public int subtract(int a, int b) {
        return a - b;
    }
    
    /**
     * 打印计算结果
     * @param result 要打印的结果
     */
    public void printResult(int result) {  // void表示无返回值
        System.out.println("计算结果:" + result);
    }
}
4.3.2 方法的调用

方法调用分为实例方法调用静态方法调用(4.4 节详细讲解)。实例方法需要通过对象调用。

调用流程

示例:调用计算器类的方法

代码语言:javascript
复制
public class CalculatorTest {
    public static void main(String[] args) {
        // 创建计算器对象
        Calculator calculator = new Calculator();
        
        // 调用加法方法
        int sum = calculator.add(10, 20);
        calculator.printResult(sum);  // 输出:计算结果:30
        
        // 调用减法方法
        int difference = calculator.subtract(50, 15);
        calculator.printResult(difference);  // 输出:计算结果:35
    }
}
4.3.3 方法重载

        方法重载(Overload):在同一个类中,允许存在多个同名方法,只要它们的参数列表不同(参数个数、类型或顺序不同)。

作用:提高代码可读性,用相同的方法名实现相似功能。

示例:方法重载实践

代码语言:javascript
复制
/**
 * 方法重载示例
 */
public class OverloadDemo {
    // 1. 两个int相加
    public int add(int a, int b) {
        System.out.println("调用int+int的add方法");
        return a + b;
    }
    
    // 2. 三个int相加(参数个数不同)
    public int add(int a, int b, int c) {
        System.out.println("调用int+int+int的add方法");
        return a + b + c;
    }
    
    // 3. 两个double相加(参数类型不同)
    public double add(double a, double b) {
        System.out.println("调用double+double的add方法");
        return a + b;
    }
    
    // 4. int和double相加(参数顺序不同)
    public double add(int a, double b) {
        System.out.println("调用int+double的add方法");
        return a + b;
    }
    
    public static void main(String[] args) {
        OverloadDemo demo = new OverloadDemo();
        System.out.println("结果1:" + demo.add(10, 20));
        System.out.println("结果2:" + demo.add(10, 20, 30));
        System.out.println("结果3:" + demo.add(3.14, 2.5));
        System.out.println("结果4:" + demo.add(10, 3.14));
    }
}

运行结果

注意:方法重载与返回值类型无关,仅由参数列表决定。

4.3.4 构造方法

构造方法(Constructor):用于初始化对象的特殊方法,在创建对象时自动调用。

特点

  • 方法名与类名完全相同
  • 没有返回值类型(连 void 都没有)
  • 不能被staticfinalabstract修饰
  • 如果不定义构造方法,编译器会自动生成无参构造方法

分类

  • 无参构造方法
  • 有参构造方法

示例:构造方法的使用

代码语言:javascript
复制
/**
 * 构造方法示例
 */
public class ConstructorDemo {
    String name;
    int age;
    
    // 1. 无参构造方法
    public ConstructorDemo() {
        System.out.println("调用无参构造方法");
        name = "默认姓名";
        age = 0;
    }
    
    // 2. 有参构造方法(方法重载)
    public ConstructorDemo(String n, int a) {
        System.out.println("调用有参构造方法");
        name = n;
        age = a;
    }
    
    public void introduce() {
        System.out.println("姓名:" + name + ",年龄:" + age);
    }
    
    public static void main(String[] args) {
        // 使用无参构造方法创建对象
        ConstructorDemo obj1 = new ConstructorDemo();
        obj1.introduce();  // 输出:姓名:默认姓名,年龄:0
        
        // 使用有参构造方法创建对象
        ConstructorDemo obj2 = new ConstructorDemo("张三", 20);
        obj2.introduce();  // 输出:姓名:张三,年龄:20
    }
}
4.3.5 this 关键字的使用

this关键字代表当前对象,用于:

  1. 区分成员变量和局部变量(当名称冲突时)
  2. 调用当前类的其他构造方法(this(参数),必须放在构造方法第一行)
  3. 返回当前对象(return this;

示例:this 关键字的应用

代码语言:javascript
复制
/**
 * this关键字示例
 */
public class ThisDemo {
    String name;
    int age;
    
    // 1. 使用this区分成员变量和局部变量
    public ThisDemo(String name, int age) {
        this.name = name;  // this.name指成员变量,name指参数
        this.age = age;
    }
    
    // 2. 使用this调用其他构造方法
    public ThisDemo() {
        this("默认姓名", 0);  // 调用有参构造方法,必须在第一行
    }
    
    // 3. 使用this返回当前对象
    public ThisDemo setName(String name) {
        this.name = name;
        return this;  // 返回当前对象,支持链式调用
    }
    
    public ThisDemo setAge(int age) {
        this.age = age;
        return this;
    }
    
    public void introduce() {
        System.out.println("姓名:" + this.name + ",年龄:" + this.age);
    }
    
    public static void main(String[] args) {
        ThisDemo obj1 = new ThisDemo("张三", 20);
        obj1.introduce();  // 输出:姓名:张三,年龄:20
        
        ThisDemo obj2 = new ThisDemo();
        // 链式调用:因为setName和setAge返回当前对象
        obj2.setName("李四").setAge(22);
        obj2.introduce();  // 输出:姓名:李四,年龄:22
    }
}
4.3.6 方法参数的传递

Java 中方法参数传递只有值传递

  • 基本类型:传递的是值的副本,修改参数不影响原变量
  • 引用类型:传递的是对象地址的副本,修改对象属性会影响原对象,但修改引用本身不影响原引用

示例:参数传递机制演示

代码语言:javascript
复制
/**
 * 参数传递机制演示
 */
public class ParameterPassing {
    // 基本类型参数传递
    public void modifyPrimitive(int num) {
        num = 100;  // 修改的是副本,不影响原变量
        System.out.println("方法内num:" + num);
    }
    
    // 引用类型参数传递
    public void modifyObject(Student student) {
        student.age = 30;  // 修改对象属性,会影响原对象
        // 修改引用本身,不影响原引用
        student = new Student();
        student.name = "新学生";
        student.age = 18;
        System.out.println("方法内对象:" + student.name + "," + student.age);
    }
    
    public static void main(String[] args) {
        ParameterPassing demo = new ParameterPassing();
        
        // 测试基本类型传递
        int num = 10;
        demo.modifyPrimitive(num);
        System.out.println("方法外num:" + num);  // 仍为10,未被修改
        
        // 测试引用类型传递
        Student s = new Student();
        s.name = "原学生";
        s.age = 20;
        demo.modifyObject(s);
        System.out.println("方法外对象:" + s.name + "," + s.age);  // 年龄变为30
    }
}

// 复用之前定义的Student类
class Student {
    String name;
    int age;
}

运行结果

4.4 静态变量和静态方法

        被static修饰的变量和方法称为静态变量(类变量)和静态方法(类方法),它们属于类本身,而非对象。

4.4.1 静态变量

静态变量:用static修饰,属于类,所有对象共享同一内存空间,在类加载时初始化。

作用:存储所有对象共享的数据,如计数器、常量等。

示例:静态变量的使用

代码语言:javascript
复制
/**
 * 静态变量示例
 */
public class StaticVariableDemo {
    // 实例变量:每个对象有独立的副本
    String name;
    // 静态变量:所有对象共享
    static int count;  // 计数器,统计创建的对象数量
    
    public StaticVariableDemo(String name) {
        this.name = name;
        count++;  // 每次创建对象,计数器加1
    }
    
    public static void main(String[] args) {
        // 静态变量可以通过类名直接访问
        System.out.println("初始count:" + StaticVariableDemo.count);
        
        StaticVariableDemo obj1 = new StaticVariableDemo("对象1");
        System.out.println("创建对象1后count:" + StaticVariableDemo.count);
        
        StaticVariableDemo obj2 = new StaticVariableDemo("对象2");
        System.out.println("创建对象2后count:" + StaticVariableDemo.count);
        
        // 也可以通过对象访问(不推荐)
        System.out.println("通过obj1访问count:" + obj1.count);
    }
}

运行结果

4.4.2 静态方法

静态方法:用static修饰,属于类,可通过类名直接调用,无需创建对象。

特点

  • 不能直接访问实例变量和实例方法
  • 可以访问静态变量和静态方法
  • 不能使用thissuper关键字

示例:静态方法的使用

代码语言:javascript
复制
/**
 * 静态方法示例
 */
public class StaticMethodDemo {
    static int a = 10;  // 静态变量
    int b = 20;  // 实例变量
    
    // 静态方法
    public static void staticMethod() {
        System.out.println("静态方法访问静态变量a:" + a);
        // 静态方法不能访问实例变量
        // System.out.println("静态方法访问实例变量b:" + b);  // 编译错误
        
        // 静态方法可以调用其他静态方法
        anotherStaticMethod();
        
        // 静态方法不能使用this
        // System.out.println(this.a);  // 编译错误
    }
    
    // 另一个静态方法
    public static void anotherStaticMethod() {
        System.out.println("这是另一个静态方法");
    }
    
    // 实例方法
    public void instanceMethod() {
        // 实例方法可以访问静态变量和静态方法
        System.out.println("实例方法访问静态变量a:" + a);
        staticMethod();
        // 实例方法可以访问实例变量
        System.out.println("实例方法访问实例变量b:" + b);
    }
    
    public static void main(String[] args) {
        // 静态方法通过类名直接调用
        StaticMethodDemo.staticMethod();
        
        // 实例方法需要通过对象调用
        StaticMethodDemo obj = new StaticMethodDemo();
        obj.instanceMethod();
    }
}
4.4.3 单例模式

单例模式:确保一个类只有一个实例,并提供全局访问点,属于创建型设计模式。

实现方式

  1. 饿汉式:类加载时创建实例(线程安全,可能浪费内存)
  2. 懒汉式:首次使用时创建实例(需处理线程安全问题)

示例 1:饿汉式单例

代码语言:javascript
复制
/**
 * 饿汉式单例模式
 */
public class SingletonHungry {
    // 私有静态实例,类加载时初始化
    private static SingletonHungry instance = new SingletonHungry();
    
    // 私有构造方法,防止外部创建实例
    private SingletonHungry() {}
    
    // 公有静态方法,提供全局访问点
    public static SingletonHungry getInstance() {
        return instance;
    }
    
    public void showMessage() {
        System.out.println("饿汉式单例模式:Hello Singleton!");
    }
}

示例 2:懒汉式单例(线程安全)

代码语言:javascript
复制
/**
 * 懒汉式单例模式(线程安全)
 */
public class SingletonLazy {
    // 私有静态实例,初始为null
    private static SingletonLazy instance;
    
    // 私有构造方法
    private SingletonLazy() {}
    
    // 公有静态方法,首次使用时创建实例
    // synchronized确保线程安全
    public static synchronized SingletonLazy getInstance() {
        if (instance == null) {
            instance = new SingletonLazy();
        }
        return instance;
    }
    
    public void showMessage() {
        System.out.println("懒汉式单例模式:Hello Singleton!");
    }
}

单例模式测试

代码语言:javascript
复制
/**
 * 单例模式测试类
 */
public class SingletonTest {
    public static void main(String[] args) {
        // 测试饿汉式单例
        SingletonHungry hungry1 = SingletonHungry.getInstance();
        SingletonHungry hungry2 = SingletonHungry.getInstance();
        System.out.println("饿汉式是否为同一实例:" + (hungry1 == hungry2));  // true
        hungry1.showMessage();
        
        // 测试懒汉式单例
        SingletonLazy lazy1 = SingletonLazy.getInstance();
        SingletonLazy lazy2 = SingletonLazy.getInstance();
        System.out.println("懒汉式是否为同一实例:" + (lazy1 == lazy2));  // true
        lazy1.showMessage();
    }
}
4.4.4 递归

        递归:方法调用自身的编程技巧,用于解决可分解为相似子问题的问题(如阶乘、斐波那契数列、树遍历等)。

递归三要素

  1. 递归终止条件(避免无限递归)
  2. 递归关系(原问题分解为子问题)
  3. 返回值(子问题的解如何组合成原问题的解)

示例 1:递归计算阶乘

代码语言:javascript
复制
/**
 * 递归计算阶乘
 */
public class RecursionFactorial {
    /**
     * 计算n的阶乘
     * n! = n × (n-1) × ... × 1
     * 递归关系:n! = n × (n-1)!
     * 终止条件:0! = 1
     */
    public static int factorial(int n) {
        // 递归终止条件
        if (n == 0) {
            return 1;
        }
        // 递归调用:n! = n × (n-1)!
        return n * factorial(n - 1);
    }
    
    public static void main(String[] args) {
        int num = 5;
        int result = factorial(num);
        System.out.println(num + "的阶乘是:" + result);  // 输出:5的阶乘是:120
    }
}

示例 2:递归计算斐波那契数列

代码语言:javascript
复制
/**
 * 递归计算斐波那契数列
 */
public class RecursionFibonacci {
    /**
     * 计算斐波那契数列第n项
     * 斐波那契数列:1, 1, 2, 3, 5, 8...
     * 递归关系:f(n) = f(n-1) + f(n-2)
     * 终止条件:f(1) = 1, f(2) = 1
     */
    public static int fibonacci(int n) {
        // 递归终止条件
        if (n == 1 || n == 2) {
            return 1;
        }
        // 递归调用
        return fibonacci(n - 1) + fibonacci(n - 2);
    }
    
    public static void main(String[] args) {
        int n = 10;
        System.out.println("斐波那契数列第" + n + "项是:" + fibonacci(n));  // 输出:55
    }
}

4.5 对象初始化和清除

        对象从创建到销毁有完整的生命周期,本节学习对象的初始化顺序、垃圾回收机制和变量作用域。

4.5.1 实例变量的初始化

实例变量(非静态成员变量)的初始化顺序:

  1. 默认初始化(数值型 0,布尔型 false,引用型 null)
  2. 显式初始化(定义时赋值)
  3. 实例代码块初始化({ }包裹的代码)
  4. 构造方法初始化

示例:实例变量初始化顺序

代码语言:javascript
复制
/**
 * 实例变量初始化顺序演示
 */
public class InstanceInitialization {
    // 1. 默认初始化(int默认0)
    int a;
    
    // 2. 显式初始化
    int b = 20;
    
    // 3. 实例代码块
    {
        c = 30;
        System.out.println("执行实例代码块,c=" + c);
    }
    
    int c;  // 注意:这里可以先使用后定义(但不推荐)
    
    // 4. 构造方法初始化
    public InstanceInitialization() {
        a = 10;
        System.out.println("执行构造方法,a=" + a);
    }
    
    public static void main(String[] args) {
        InstanceInitialization obj = new InstanceInitialization();
        System.out.println("最终a=" + obj.a + ", b=" + obj.b + ", c=" + obj.c);
    }
}

运行结果

代码语言:javascript
复制
执行实例代码块,c=30
执行构造方法,a=10
最终a=10, b=20, c=30
4.5.2 静态变量的初始化

静态变量的初始化顺序:

  1. 默认初始化
  2. 显式初始化(定义时赋值)
  3. 静态代码块初始化(static { }包裹的代码)

特点:静态初始化只在类加载时执行一次,优先于实例初始化。

示例:静态变量初始化顺序

代码语言:javascript
复制
/**
 * 静态变量初始化顺序演示
 */
public class StaticInitialization {
    // 1. 默认初始化(static int默认0)
    static int x;
    
    // 2. 显式初始化
    static int y = 200;
    
    // 3. 静态代码块
    static {
        x = 100;
        z = 300;  // 可以先使用后定义(不推荐)
        System.out.println("执行静态代码块,x=" + x + ", z=" + z);
    }
    
    static int z;
    
    // 实例相关
    int m = 10;
    {
        System.out.println("执行实例代码块,m=" + m);
    }
    
    public StaticInitialization() {
        System.out.println("执行构造方法");
    }
    
    public static void main(String[] args) {
        System.out.println("第一次创建对象:");
        StaticInitialization obj1 = new StaticInitialization();
        
        System.out.println("\n第二次创建对象:");
        StaticInitialization obj2 = new StaticInitialization();
        
        System.out.println("\n静态变量最终值:x=" + x + ", y=" + y + ", z=" + z);
    }
}

运行结果

代码语言:javascript
复制
执行静态代码块,x=100, z=300
第一次创建对象:
执行实例代码块,m=10
执行构造方法

第二次创建对象:
执行实例代码块,m=10
执行构造方法

静态变量最终值:x=100, y=200, z=300

注意:静态初始化只执行一次,无论创建多少对象都不会重复执行。

4.5.3 垃圾回收器

        垃圾回收器(Garbage Collector, GC):JVM 自动管理内存的机制,负责回收不再被引用的对象所占用的内存。

工作原理

  • 标记阶段:识别哪些对象不再被引用
  • 清除阶段:回收标记对象的内存
  • 压缩阶段:整理内存碎片(可选)

相关方法

  • System.gc():建议 JVM 执行垃圾回收(但不确定立即执行)
  • finalize():对象被回收前调用(不推荐依赖此方法)

示例:垃圾回收演示

代码语言:javascript
复制
/**
 * 垃圾回收演示
 */
public class GarbageCollectionDemo {
    // 重写finalize方法
    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        System.out.println("对象被垃圾回收器回收了!");
    }
    
    public static void main(String[] args) {
        // 创建对象
        GarbageCollectionDemo obj1 = new GarbageCollectionDemo();
        GarbageCollectionDemo obj2 = new GarbageCollectionDemo();
        
        // 让对象失去引用
        obj1 = null;
        obj2 = null;
        
        // 建议JVM执行垃圾回收
        System.gc();
        
        // 给GC一些时间执行
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

可能的运行结果

代码语言:javascript
复制
对象被垃圾回收器回收了!
对象被垃圾回收器回收了!

注意:垃圾回收的时机由 JVM 决定,System.gc()只是建议,不能保证立即执行。

4.5.4 变量作用域和生存期

变量作用域:变量可被访问的范围,分为:

  • 局部变量:方法内或代码块内定义,作用域限于当前方法或代码块
  • 成员变量:类中定义,作用域限于当前类(通过访问修饰符控制)

变量生存期:变量从创建到销毁的时间,取决于变量类型和作用域。

示例:变量作用域演示

代码语言:javascript
复制
/**
 * 变量作用域演示
 */
public class VariableScopeDemo {
    // 成员变量:作用域为整个类
    int memberVar = 100;  // 实例成员变量
    static int staticVar = 200;  // 静态成员变量
    
    public void method1() {
        // 局部变量:作用域限于method1方法
        int localVar1 = 10;
        System.out.println("method1访问成员变量:" + memberVar);
        System.out.println("method1访问局部变量:" + localVar1);
    }
    
    public void method2() {
        // 局部变量:与method1的localVar1同名但不冲突
        int localVar1 = 20;
        System.out.println("method2访问局部变量:" + localVar1);
        
        // 代码块内的局部变量
        if (true) {
            int blockVar = 30;  // 作用域限于当前if代码块
            System.out.println("代码块内访问blockVar:" + blockVar);
        }
        
        // 编译错误:超出blockVar的作用域
        // System.out.println("代码块外访问blockVar:" + blockVar);
    }
    
    public static void main(String[] args) {
        VariableScopeDemo obj = new VariableScopeDemo();
        obj.method1();
        obj.method2();
        
        // 静态方法可以访问静态成员变量
        System.out.println("main方法访问静态变量:" + staticVar);
        // 静态方法不能直接访问实例成员变量
        // System.out.println("main方法直接访问实例变量:" + memberVar);  // 编译错误
        // 通过对象访问实例成员变量
        System.out.println("main方法通过对象访问实例变量:" + obj.memberVar);
    }
}

4.6 包与类的导入

        包(Package)用于组织类,避免类名冲突,类似于文件夹的作用。

4.6.1 包

        包的定义:使用package关键字,放在 Java 源文件第一行,格式:

代码语言:javascript
复制
package 包名;  // 例如:package com.example.demo;

命名规范

  • 全小写字母
  • 通常使用反转的域名(如com.公司名.项目名.模块名
  • 包名对应目录结构(如com.example对应目录com/example

示例:定义包 创建文件com/example/package_demo/MyPackageClass.java

代码语言:javascript
复制
// 定义包(必须在第一行)
package com.example.package_demo;

/**
 * 包演示类
 */
public class MyPackageClass {
    public void showMessage() {
        System.out.println("这是com.example.package_demo包中的类");
    }
}
4.6.2 类的导入

当需要使用其他包的类时,需要导入:

  1. 显式导入特定类:import 包名.类名;
  2. 导入包中所有类:import 包名.*;(不推荐,可能引发冲突)
  3. 使用全限定类名:包名.类名(不导入直接使用)

示例:导入和使用其他包的类 创建文件com/example/package_demo/ImportDemo.java

代码语言:javascript
复制
package com.example.package_demo;

// 显式导入Java标准库的类
import java.util.Date;

// 导入当前包的类(同一包下可以省略,但显式导入更清晰)
import com.example.package_demo.MyPackageClass;

/**
 * 类的导入演示
 */
public class ImportDemo {
    public static void main(String[] args) {
        // 使用导入的类
        MyPackageClass myClass = new MyPackageClass();
        myClass.showMessage();
        
        // 使用导入的Date类
        Date date = new Date();
        System.out.println("当前时间:" + date);
        
        // 使用全限定类名(不导入直接使用)
        java.util.Scanner scanner = new java.util.Scanner(System.in);
        System.out.println("请输入一个数字:");
        int num = scanner.nextInt();
        System.out.println("你输入的数字是:" + num);
        scanner.close();
    }
}
4.6.3 Java 编译单元

Java 编译单元:一个.java源文件,有以下规则:

  1. 最多包含一个public类,类名必须与文件名相同
  2. 可以包含多个非public
  3. package语句最多一个,必须在第一行(可选)
  4. import语句在package之后,类定义之前(可选)

示例:Java 编译单元结构 文件名:CompilationUnitDemo.java(必须与 public 类名相同)

代码语言:javascript
复制
// 1. 包声明(可选)
package com.example.demo;

// 2. 导入语句(可选)
import java.util.ArrayList;
import java.util.List;

// 3. public类(最多一个,与文件名相同)
public class CompilationUnitDemo {
    public static void main(String[] args) {
        System.out.println("这是public类");
        NonPublicClass obj = new NonPublicClass();
        obj.show();
    }
}

// 4. 非public类(可以多个)
class NonPublicClass {
    public void show() {
        System.out.println("这是非public类");
    }
}

// 5. 另一个非public类
class AnotherClass {
    // 类内容
}

4.7 小结

本章我们学习了 Java 面向对象编程的核心概念 —— 类和对象,主要内容包括:

  1. 面向对象基础:OOP 的产生、基本概念(类、对象)、三大特征(封装、继承、多态)及优势。
  2. 类与对象:类的定义(属性和方法)、对象的创建与使用、栈与堆的内存模型、UML 类图表示。
  3. 方法设计:方法的定义与调用、方法重载、构造方法、this 关键字的使用、参数传递机制(值传递)。
  4. 静态成员:静态变量(类变量)、静态方法(类方法)、单例模式实现、递归算法。
  5. 对象生命周期:实例变量和静态变量的初始化顺序、垃圾回收机制、变量作用域和生存期。
  6. 包与导入:包的定义与作用、类的导入方式、Java 编译单元规则。

        面向对象编程是 Java 的核心思想,掌握类和对象的使用是学好 Java 的基础,后续的继承、多态等高级特性都基于这些基础知识。

编程练习

练习 1:学生信息管理系统(综合案例)

        设计一个学生信息管理系统,实现添加学生、显示所有学生信息、统计学生数量的功能,综合运用类和对象、静态变量、构造方法、方法重载等知识点。

完整代码

代码语言:javascript
复制
package com.example.studentmanagement;

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

/**
 * 学生类
 */
class Student {
    // 学生属性
    private String id;      // 学号
    private String name;    // 姓名
    private int age;        // 年龄
    private String major;   // 专业
    
    // 无参构造方法
    public Student() {}
    
    // 有参构造方法(方法重载)
    public Student(String id, String name, int age, String major) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.major = major;
    }
    
    // getter和setter方法
    public String getId() {
        return id;
    }
    
    public void setId(String id) {
        this.id = id;
    }
    
    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        this.name = name;
    }
    
    public int getAge() {
        return age;
    }
    
    public void setAge(int age) {
        this.age = age;
    }
    
    public String getMajor() {
        return major;
    }
    
    public void setMajor(String major) {
        this.major = major;
    }
    
    // 显示学生信息
    public void showInfo() {
        System.out.println("学号:" + id + ",姓名:" + name + 
                           ",年龄:" + age + ",专业:" + major);
    }
}

/**
 * 学生管理类
 */
class StudentManager {
    // 静态变量:统计学生总数
    private static int totalStudents = 0;
    
    // 存储学生对象的集合
    private List<Student> students = new ArrayList<>();
    
    /**
     * 添加学生(方法重载1:通过Student对象添加)
     */
    public void addStudent(Student student) {
        students.add(student);
        totalStudents++;  // 学生总数加1
    }
    
    /**
     * 添加学生(方法重载2:通过参数添加)
     */
    public void addStudent(String id, String name, int age, String major) {
        Student student = new Student(id, name, age, major);
        students.add(student);
        totalStudents++;  // 学生总数加1
    }
    
    /**
     * 显示所有学生信息
     */
    public void showAllStudents() {
        if (students.isEmpty()) {
            System.out.println("暂无学生信息!");
            return;
        }
        System.out.println("=== 所有学生信息 ===");
        for (Student student : students) {
            student.showInfo();
        }
    }
    
    /**
     * 获取学生总数(静态方法)
     */
    public static int getTotalStudents() {
        return totalStudents;
    }
}

/**
 * 主程序类
 */
public class StudentManagementSystem {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        StudentManager manager = new StudentManager();
        
        while (true) {
            System.out.println("\n=== 学生信息管理系统 ===");
            System.out.println("1. 添加学生");
            System.out.println("2. 显示所有学生");
            System.out.println("3. 显示学生总数");
            System.out.println("4. 退出");
            System.out.print("请选择操作:");
            int choice = scanner.nextInt();
            scanner.nextLine();  //  consume newline
            
            switch (choice) {
                case 1:
                    System.out.print("请输入学号:");
                    String id = scanner.nextLine();
                    System.out.print("请输入姓名:");
                    String name = scanner.nextLine();
                    System.out.print("请输入年龄:");
                    int age = scanner.nextInt();
                    scanner.nextLine();  // consume newline
                    System.out.print("请输入专业:");
                    String major = scanner.nextLine();
                    
                    // 调用添加学生方法
                    manager.addStudent(id, name, age, major);
                    System.out.println("学生添加成功!");
                    break;
                    
                case 2:
                    manager.showAllStudents();
                    break;
                    
                case 3:
                    System.out.println("当前学生总数:" + StudentManager.getTotalStudents());
                    break;
                    
                case 4:
                    System.out.println("谢谢使用,再见!");
                    scanner.close();
                    return;
                    
                default:
                    System.out.println("无效选择,请重新输入!");
            }
        }
    }
}
练习 2:单例模式工具类

设计一个单例模式的日志工具类,提供日志输出功能,确保整个应用只有一个日志实例。

完整代码

代码语言:javascript
复制
package com.example.singletonlog;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

/**
 * 单例模式日志工具类
 */
public class Logger {
    // 私有静态实例(懒汉式)
    private static Logger instance;
    
    // 私有构造方法,防止外部创建
    private Logger() {}
    
    // 公有静态方法,提供全局访问点(线程安全)
    public static synchronized Logger getInstance() {
        if (instance == null) {
            instance = new Logger();
        }
        return instance;
    }
    
    /**
     * 输出普通日志
     */
    public void info(String message) {
        String time = getCurrentTime();
        System.out.println("[" + time + "] [INFO] " + message);
    }
    
    /**
     * 输出错误日志
     */
    public void error(String message) {
        String time = getCurrentTime();
        System.err.println("[" + time + "] [ERROR] " + message);
    }
    
    /**
     * 获取当前时间字符串
     */
    private String getCurrentTime() {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        return LocalDateTime.now().format(formatter);
    }
    
    // 测试单例日志工具
    public static void main(String[] args) {
        // 获取日志实例
        Logger logger1 = Logger.getInstance();
        Logger logger2 = Logger.getInstance();
        
        // 验证是否为同一实例
        System.out.println("logger1和logger2是否为同一实例:" + (logger1 == logger2));
        
        // 使用日志工具
        logger1.info("应用程序启动");
        logger1.info("用户登录成功");
        logger2.error("数据库连接失败");
        logger2.info("应用程序退出");
    }
}

        以上就是本章的全部内容,希望通过理论讲解和实践案例,能帮助大家掌握类和对象的核心知识。动手练习是学好编程的关键,建议大家亲自运行代码,并尝试修改和扩展功能,加深理解。如有问题欢迎在评论区交流! 😊

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-07-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 本章知识思维导图
  • 4.1 面向对象概述
    • 4.1.1 OOP 的产生
    • 4.1.2 面向对象的基本概念
    • 4.1.3 面向对象基本特征
    • 4.1.4 OOP 的优势
  • 4.2 为对象定义类
    • 4.2.1 类的定义
    • 4.2.2 对象的使用
    • 4.2.3 理解栈与堆
    • 4.2.4 用 UML 图表示类
  • 4.3 方法设计
    • 4.3.1 如何设计方法
    • 4.3.2 方法的调用
    • 4.3.3 方法重载
    • 4.3.4 构造方法
    • 4.3.5 this 关键字的使用
    • 4.3.6 方法参数的传递
  • 4.4 静态变量和静态方法
    • 4.4.1 静态变量
    • 4.4.2 静态方法
    • 4.4.3 单例模式
    • 4.4.4 递归
  • 4.5 对象初始化和清除
    • 4.5.1 实例变量的初始化
    • 4.5.2 静态变量的初始化
    • 4.5.3 垃圾回收器
    • 4.5.4 变量作用域和生存期
  • 4.6 包与类的导入
    • 4.6.1 包
    • 4.6.2 类的导入
    • 4.6.3 Java 编译单元
  • 4.7 小结
  • 编程练习
    • 练习 1:学生信息管理系统(综合案例)
    • 练习 2:单例模式工具类
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档