面向对象:封装

面向对象:封装

Java是一门面向对象的语言。在面向对象的世界里,一切皆为对象。面向对象是解决问题的一种思想,主要依靠对象之间的交互完成一件事情。

比如说洗衣服这件事:

  • 传统洗衣模式:需要依次完成「拿盆子→放水→放衣服→放洗衣粉」等步骤,注重执行过程,少一个环节都无法完成;而且不同衣物、鞋子的清洗流程各不相同,用这种思路编写代码,后续维护会十分繁琐。
  • 现代洗衣模式:只需将衣服放进洗衣机、倒入洗衣粉、启动开关即可完成,注重对象交互(用户与洗衣机的交互),无需关心内部实现细节,这就是面向对象思想的核心体现。

一、类的定义

类是用来对一个实体(对象)进行描述的模板,主要描述该实体具有哪些属性(数据)拥有哪些功能(行为),描述完成后,计算机就能识别这个自定义的实体类型。

一个完整的类通常由「成员变量」(对应属性)和「成员方法」(对应功能)组成,定义类需遵循两个基本规则:

  1. 使用 class 关键字声明类;
  2. 类名采用「大驼峰命名法」(英文单词首字母全部大写,如 DogStudent)。

示例:定义一个类(Dog)

public class Dog {
    // 成员变量:描述狗的属性(名字、年龄)
    public String name;  // 名字
    public int age;      // 年龄
    
    // 成员方法:描述狗的行为(吃饭)
    public void eat() {
        System.out.println(name + "吃狗粮");
    }
}

从示例中可以看出:狗具有「名字」和「年龄」两个属性,同时具有「吃饭」这个行为,这就是一个简单的类定义。


二、类的实例化

定义了一个类,相当于在计算机中定义了一种新的自定义数据类型,它与Java内置的 intdouble 类型本质上是一致的,区别仅在于:

  • intdouble 是Java自带的内置基本类型;
  • 类是用户根据业务需求自定义的引用类型。

使用自定义类创建具体对象的过程,称为「类的实例化」,实例化的核心是使用 new 关键字。

示例1:实例化Dog类对象

public class Test {
    public static void main(String[] args) {
        // 1. 通过 new 关键字,实例化第一个 Dog 对象(变量名:dog1)
        Dog dog1 = new Dog(); 
        
        // 2. 实例化第二个 Dog 对象(变量名:dog2)
        Dog dog2 = new Dog();
    }
}

示例2:访问对象的成员(属性+方法)

实例化对象后,可通过 对象名.成员变量对象名.成员方法() 的格式,访问对象的属性和行为。

public class Test {
    public static void main(String[] args) {
        // 实例化 Dog 对象
        Dog dog1 = new Dog();
        
        // 给成员变量赋值(访问成员变量)
        dog1.name = "旺财";
        dog1.age = 3;
        
        // 调用成员方法(访问成员方法)
        dog1.eat();
    }
}

运行结果

旺财吃狗粮

三、this关键字

this 关键字是Java中的内置引用,仅能在类的「成员方法」或「构造方法」中使用,核心作用是指向当前调用成员的对象

常用用法有三种:

  1. this.成员变量:区分成员变量和局部变量(尤其是变量名相同时);
  2. this.成员方法():调用当前类中的其他成员方法;
  3. this(...):调用当前类中的其他构造方法(仅能在构造方法中使用,且必须放在第一行)。

示例:使用this区分成员变量和局部变量

public class Date {
    // 成员变量:描述日期的年、月、日
    public int year;
    public int month;
    public int day;

    // 成员方法:给日期赋值
    public void setDay(int year, int month, int day){
        // this.year:指代类中定义的成员变量(对象的属性)
        // 等号右边的 year:指代方法的局部变量(方法参数)
        this.year = year;
        this.month = month;
        this.day = day;
    }
}

this关键字的核心特性

  1. this 的类型:与对应类的类型一致,哪个对象调用成员,this 就指向哪个对象;
  2. this 只能在「成员方法」或「构造方法」中使用,不能在静态方法中使用;
  3. this 不能被赋值,它的指向是由JVM自动决定的,无需手动修改。

四、对象的初始化

对象的初始化是指为对象的成员变量赋值的过程,Java提供了三种常见的初始化方式,优先级从低到高依次为:「默认初始化」<「就地初始化」<「构造方法初始化」。

1. 默认初始化

Java规定:局部变量必须手动初始化后才能使用,但类的成员变量无需手动赋值,当对象被创建时,JVM会自动为其赋予一个「默认值」。

示例:成员变量的默认初始化

public class Dog {
    public String name;  // 引用类型:默认值为 null
    public int age;      // 基本类型int:默认值为 0
}

各类数据类型的默认值汇总

数据类型分类具体类型默认值
基本数据类型 - 整数型byte、short、int、long0(对应类型的零值)
基本数据类型 - 浮点型float、double0.0(float为 0.0f,double为 0.0)
基本数据类型 - 字符型char'\u0000'(空字符,对应ASCII码 0)
基本数据类型 - 布尔型booleanfalse
引用数据类型所有类、接口、数组等null(表示没有指向任何对象)

2. 就地初始化

在定义成员变量时,直接为其赋值,这种方式称为「就地初始化」,赋值结果会被保留,且会覆盖默认初始化的值。

示例:成员变量的就地初始化

public class Dog {
    // 就地初始化:覆盖默认值 0,直接赋值为 1
    public int age = 1;
    // 就地初始化:覆盖默认值 null,直接赋值为 "旺财"
    public String name = "旺财";
}

3. 构造方法初始化

构造方法是一种特殊的成员方法,专门用于对象的初始化,它在对象被创建(new 关键字)时,由JVM自动调用,且在整个对象的生命周期内仅调用一次

构造方法的三大特性

  1. 方法名必须与类名完全一致(包括大小写);
  2. 没有返回值类型(连 void 都不能写);
  3. 不能被 staticfinalabstract 等关键字修饰(后续知识点会详细讲解)。

示例1:基本构造方法的使用

public class Date {
    // 成员变量
    public int year;
    public int month;
    public int day;

    // 构造方法:与类名 Date 一致,无返回值类型
    public Date(int year, int month, int day){
        this.year = year;
        this.month = month;
        this.day = day;
        System.out.println("构造方法被调用了");
    }
    
    public static void main(String[] args) {
        // 创建对象时,自动调用对应参数的构造方法
        Date d = new Date(1999, 6, 9);
    }
}

运行结果

构造方法被调用了

构造方法的两个核心要点

要点1:构造方法支持重载

构造方法可以根据业务需求,定义多个参数列表不同的构造方法,这称为「构造方法重载」(满足方法重载的规则:方法名相同,参数列表的个数、类型、顺序不同)。

另外,如果用户没有显式定义任何构造方法,编译器会自动生成一个无参构造方法;一旦用户定义了构造方法,编译器将不再自动生成无参构造方法。

public class Date {
    // 成员变量
    public int year;
    public int month;
    public int day;
    
    // 无参构造方法
    public Date() {
        
    }
    
    // 有参构造方法(3个参数)
    public Date(int year, int month, int day){
        this.year = year;
        this.month = month;
        this.day = day;
    }
}

要点2:使用 this(...) 调用其他构造方法

在构造方法中,可以通过 this(参数列表) 的格式,调用当前类中的其他构造方法,从而简化代码。

注意事项:

  1. this(...) 必须放在构造方法的第一行
  2. 不能形成「循环调用」(如A构造方法调用B,B又调用A)。
public class Date {
    // 成员变量
    public int year;
    public int month;
    public int day;
    
    // 无参构造方法:调用有参构造方法,赋予默认值 1900-01-01
    public Date() {
        this(1900, 1, 1); // 必须放在第一行
    } 
    
    // 有参构造方法:负责实际的赋值逻辑
    public Date(int year, int month, int day) {
        this.year = year;
        this.month = month;
        this.day = day;
    }
}

五、对象的打印

如果直接使用 System.out.println(对象名) 打印对象引用,默认输出结果为「类的全路径名@对象的哈希码值」,这种结果对我们排查问题没有实际意义。

示例1:默认打印对象

public class Date {
    // 成员变量
    public int year;
    public int month;
    public int day;

    // 无参构造方法
    public Date() {
        
    }

    public static void main(String[] args) {
        Date date = new Date();
        System.out.println(date);
    }
}

运行结果

Date@4eec7777

示例2:重写 toString() 方法,自定义对象打印格式

如果想要默认打印对象的属性信息,需要在类中重写 toString() 方法(该方法来自 Object 类,所有Java类都默认继承自 Object)。

重写后,System.out.println(对象名) 会自动调用该对象的 toString() 方法,输出自定义的结果。

public class Date {
    // 成员变量
    public int year;
    public int month;
    public int day;

    // 有参构造方法
    public Date(int year, int month, int day) {
        this.year = year;
        this.month = month;
        this.day = day;
    }

    // 重写 toString() 方法,自定义打印格式
    @Override
    public String toString() {
        return "Date{" +
                "year=" + year +
                ", month=" + month +
                ", day=" + day +
                '}';
    }

    public static void main(String[] args) {
        Date date = new Date(1900, 1, 1);
        System.out.println(date);
    }
}

运行结果

Date{year=1900, month=1, day=1}

六、包(Package)

包的本质就是文件夹,它的核心作用是:

  1. 解决「类名冲突」问题(不同包中可以存在同名类,如 com.test.Dogcom.demo.Dog);
  2. 对类进行分类管理,提高代码的可读性和可维护性;
  3. 配合访问限定符,控制类的访问权限。

1. 导入包中的类

使用其他包中的类时,需要通过 import 关键字导入,常见两种导入方式:

  • 导入具体类:import 包名.类名;(推荐使用,避免类名冲突);
  • 导入包中所有类:import 包名.*;(不推荐,可能出现同名类冲突)。

示例:导入 java.util 包中的 Scanner

// 导入具体类:java.util 包下的 Scanner 类
import java.util.Scanner;

public class Test {
    public static void main(String[] args) {
        // 使用导入的 Scanner 类创建对象
        Scanner scanner = new Scanner(System.in);
    }
}

2. 自定义包

自定义包的核心步骤:

  1. 在项目的 src 目录下创建文件夹(遵循包名命名规则:全小写英文,多级包用 . 分隔,如 com.demo);
  2. 在该文件夹下的Java类中,使用 package 包名; 语句声明包(该语句必须放在类文件的第一行,且前面不能有任何其他代码)。

示例:自定义包 demo

// 声明当前类属于 demo 包(必须放在第一行)
package demo;

public class Dog {
    public String name;
    public int age;
}

七、封装

封装是面向对象的三大特性之一(封装、继承、多态),核心思想是「套壳屏蔽细节」,即:

  1. 将类的成员变量私有化(使用 private 修饰),禁止外部类直接访问;
  2. 提供公共的(public 修饰)getXxx()setXxx() 方法,用于间接访问和修改私有化的成员变量;
  3. setXxx() 方法中可以添加数据校验逻辑,保证数据的合法性。

示例:实现学生类(Student)的封装

public class Student {
    // 成员变量私有化:外部类无法直接访问
    private String name;
    private int age;

    // 公共的 get 方法:获取 name 的值
    public String getName() {
        return name;
    }

    // 公共的 set 方法:设置 name 的值
    public void setName(String name) {
        this.name = name;
    }

    // 公共的 get 方法:获取 age 的值
    public int getAge() {
        return age;
    }

    // 公共的 set 方法:设置 age 的值(添加数据校验)
    public void setAge(int age) {
        if (age >= 0 && age <= 150) { // 保证年龄的合法性
            this.age = age;
        } else {
            System.out.println("年龄输入不合法!");
        }
    }

    // 测试类
    public static class Test {
        public static void main(String[] args) {
            Student student = new Student();
            
            // 无法直接访问 private 成员变量,只能通过 get/set 方法操作
            student.setName("张三");
            student.setAge(20);
            
            System.out.println("姓名:" + student.getName());
            System.out.println("年龄:" + student.getAge());
        }
    }
}

运行结果

姓名:张三
年龄:20

Java中的访问限定符

封装的实现依赖于Java的访问限定符,Java提供了4种访问限定符,用于控制类、成员变量、成员方法的访问范围,访问权限从窄到宽依次为:private < default < protected < public

访问范围private(私有)default(默认,无关键字)protected(受保护)public(公共)
同一包中的同一类
同一包中的不同类
不同包中的子类
不同包中的非子类

八、static关键字

static 关键字是Java中的常用关键字,可用于修饰「成员变量」、「成员方法」、「代码块」等,被 static 修饰的成员,属于类本身,而不属于某个具体的对象,生命周期伴随类的加载和销毁。

1. 静态成员变量(类变量)

static 修饰的成员变量,称为静态成员变量,也叫类变量。

核心特性

  1. 属于类,所有对象共享同一个静态成员变量(一个对象修改后,其他对象访问的是修改后的值);
  2. 生命周期伴随类的一生(类加载时创建,类卸载时销毁);
  3. 访问方式:推荐使用 类名.静态成员变量,也可以使用 对象名.静态成员变量(不推荐,容易混淆);
  4. 不能与 thissuper 关键字一起使用。

示例:使用静态成员变量

public class ClassInfo {
    // 静态成员变量:属于类,所有对象共享
    public static String className = "Financial Management 242";

    public static void main(String[] args) {
        // 推荐:通过类名访问静态成员变量
        System.out.println(ClassInfo.className);
        
        // 修改静态成员变量的值
        ClassInfo.className = "Financial Management 243";
        System.out.println(ClassInfo.className);
    }
}

运行结果

Financial Management 242
Financial Management 243

2. 静态成员方法(类方法)

static 修饰的成员方法,称为静态成员方法,也叫类方法。

核心特性

  1. 属于类,不属于某个具体的对象;
  2. 访问方式:推荐使用 类名.静态成员方法(),也可以使用 对象名.静态成员方法()(不推荐);
  3. 静态方法中,不能访问任何非静态成员变量和非静态成员方法(非静态成员属于对象,静态方法执行时,对象可能尚未创建);
  4. 不能使用 thissuper 关键字。

示例:使用静态成员方法

public class ClassInfo {
    // 静态成员变量
    public static String className = "Financial Management 242";
    
    // 静态成员方法:返回班级名称
    public static String getClassName() {
        // 静态方法中可以访问静态成员变量
        return className;
    }

    public static void main(String[] args) {
        // 推荐:通过类名访问静态成员方法
        System.out.println(ClassInfo.getClassName());
    }
}

运行结果

Financial Management 242

3. 静态成员变量的初始化

静态成员变量的初始化方式有两种:「就地初始化」和「静态代码块初始化」。

方式1:就地初始化

定义静态成员变量时,直接为其赋值,与普通成员变量的就地初始化一致。

public class ClassInfo {
    // 静态成员变量:就地初始化
    public static String className = "Financial Management 242";
}

方式2:静态代码块初始化

通过静态代码块为静态成员变量赋值,适合复杂的初始化逻辑(如循环赋值、读取配置文件等)。


九、代码块

使用 {} 包裹的代码片段,称为代码块,Java中常见的代码块有4种:「普通代码块」、「实例代码块」、「静态代码块」、「同步代码块」(同步代码块后续多线程知识点讲解)。

1. 普通代码块

定义在方法内部的代码块,称为普通代码块,核心作用是「限定变量的生命周期」,避免变量名冲突。

示例:普通代码块

public class Test {
    public static void main(String[] args) {
        // 普通代码块:定义在 main 方法内部
        {
            int a = 10;
            System.out.println("a = " + a);
        }
        
        // 此处无法访问变量 a(a 的生命周期仅限于上面的代码块)
        // System.out.println(a); // 编译报错
    }
}

2. 静态代码块

static 修饰的代码块,称为静态代码块,定义在类内部、方法外部

核心特性

  1. 静态代码块在类加载时执行,且仅执行一次
  2. 一个类中可以有多个静态代码块,按照定义顺序从上到下依次执行;
  3. 主要用于初始化「静态成员变量」,不能访问非静态成员;
  4. 执行优先级高于实例代码块和构造方法。

示例:静态代码块初始化静态成员变量

public class Animal {
    // 静态成员变量
    public static String name;

    // 静态代码块:初始化静态成员变量
    static {
        name = "animal";
        System.out.println("静态代码块被执行");
    }

    public static void main(String[] args) {
        System.out.println("动物名称:" + Animal.name);
    }
}

运行结果

静态代码块被执行
动物名称:animal

3. 实例代码块

没有 static 修饰的代码块,称为实例代码块,也叫构造代码块,定义在类内部、方法外部

核心特性

  1. 实例代码块在每次创建对象时执行,且执行在构造方法之前
  2. 一个类中可以有多个实例代码块,按照定义顺序从上到下依次执行;
  3. 主要用于初始化「非静态成员变量」,可以访问静态成员和非静态成员;
  4. 每个对象创建时,都会执行一次实例代码块。

示例:实例代码块初始化非静态成员变量

public class Animal {
    // 非静态成员变量
    public String name;

    // 实例代码块:初始化非静态成员变量
    {
        this.name = "animal";
        System.out.println("实例代码块被执行");
    }

    // 构造方法
    public Animal() {
        System.out.println("构造方法被执行");
    }

    public static void main(String[] args) {
        // 创建第一个 Animal 对象
        Animal animal1 = new Animal();
        // 创建第二个 Animal 对象
        Animal animal2 = new Animal();
    }
}

运行结果

实例代码块被执行
构造方法被执行
实例代码块被执行
构造方法被执行
面向对象:继承 2025-07-05