8 面向对象编程
8.1 类变量和类方法
8.1.1 类变量-提出问题
在面向对象编程中,我们会遇到这样的场景:多个对象需要共享同一份数据 。比如定义学生类时,想统计所有学生一共缴纳的学费,若用普通的实例变量,每个对象的该变量都是独立的,无法直接实现“所有对象共享数据”的需求,这就引出了类变量(静态变量)来解决这类问题。
8.1.2 传统的方法来解决
若不使用类变量,要实现类似“统计所有学生总学费”的功能,可能需要在类外部定义一个全局变量,然后在操作学生对象(如创建学生、缴纳学费时)去修改这个全局变量 。但这种方式会让代码的耦合性变高,全局变量容易被其他无关代码意外修改,而且从面向对象的封装性角度看,破坏了类的封装,不利于代码的维护和扩展。
8.1.3 类变量快速入门
定义与示例
类变量是用 static
修饰的变量,属于类本身,而非单个对象。以下通过学生类示例展示:
class Student {
// 实例变量,每个学生对象独有的姓名
String name;
// 类变量,所有 Student 对象共享的总学费
static double totalFee;
public Student(String name) {
this.name = name;
}
}
public class Test {
public static void main(String[] args) {
// 直接通过类名访问类变量,设置所有学生的总学费
Student.totalFee = 10000;
Student s1 = new Student("张三");
Student s2 = new Student("李四");
// 输出总学费,验证共享性
System.out.println(Student.totalFee);
}
}
在上述代码中,totalFee
是类变量,所有 Student
对象都共享它的值,通过 Student.totalFee
就能直接操作这份共享数据 。
8.1.4 类变量内存布局
存储位置与流程
当类加载时,类的相关信息(包括类变量)会存储在方法区的静态域中 。实例变量则存储在堆内存中,每个对象的实例变量是独立的。以 Student
类为例,totalFee
(类变量)在方法区,而每个 Student
对象(如 s1
、s2
)的 name
(实例变量)在堆中各自有一份。当通过 Student.totalFee
操作时,直接修改的是方法区里类变量的值,所有对象访问的都是这同一个位置的数据,从而实现共享。
8.1.5 什么是类变量
类变量,也叫静态变量,是被 static
关键字修饰的变量 。它属于整个类,而不是类的某个实例(对象)。对比实例变量(普通属性),实例变量是每个对象单独拥有的属性,各个对象的实例变量相互独立;类变量则是所有对象共享的,一处修改,处处可见。
8.1.6 如何定义类变量
语法格式
类变量的定义语法为:[访问修饰符] static 数据类型 变量名;
,推荐把 static
写在访问修饰符之后,例如 public static double totalFee;
,这样符合编码规范,也更清晰体现其是类变量的特性。访问修饰符(如 public
、private
等)用于控制类变量的访问权限 。
8.1.7 如何访问类变量
访问方式
- 推荐方式:
类名.类变量名
,比如Student.totalFee
,这种方式清晰表明是在访问类共享的变量,语义明确 。 - 不推荐方式:
对象名.类变量名
,像s1.totalFee
,虽然语法上可行,但容易让人误解该变量是对象独有的,混淆类变量和实例变量的区别 。 - 前提条件:要满足访问修饰符的权限,若类变量用
private
修饰,外部类一般无法直接通过上述方式访问,需要借助类的 getter 等方法间接访问 。
8.1.8 类变量使用注意事项和细节讨论
- 类加载时初始化:类变量在类加载阶段就完成初始化,不用创建对象就能使用。比如还没创建
Student
对象时,就可以通过Student.totalFee
获取或设置它的值 。 - 与实例变量访问区别:实例变量不能通过
类名.变量名
方式访问,因为实例变量属于对象,必须先创建对象才能访问,而类变量属于类,不依赖对象存在 。 - 生命周期:类变量随着类的加载而创建,随着类的卸载而销毁,其生命周期贯穿类存在的整个过程,比对象的生命周期更长 。
8.1.12 类方法经典的使用场景
典型场景与示例
- 工具类:像
Math
类的数学计算方法(如Math.abs()
)、Arrays
类的数组操作方法(如Arrays.sort()
),这些方法不依赖特定对象,属于通用工具逻辑,设计成类方法(静态方法),无需创建对象就能调用,方便快捷 。 - 通用逻辑实现:比如实现打印一维数组的功能,写成类方法后,任何地方要打印数组,直接用
工具类名.打印方法名
即可,不用每次创建工具类对象,提升开发效率,示例如下:
class ArrayUtils {
public static void printArray(int[] arr) {
for (int num : arr) {
System.out.print(num + " ");
}
System.out.println();
}
}
public class Test {
public static void main(String[] args) {
int[] arr = {1, 2, 3};
// 直接通过类名调用类方法
ArrayUtils.printArray(arr);
}
}
8.1.13 类方法使用注意事项和细节讨论
- 加载与
this
参数:类方法和普通方法都在类加载时加载到方法区存储结构信息,但类方法中没有this
参数(因为不属于具体对象),普通方法隐含this
参数(代表当前调用的对象) 。 - 调用方式:类方法可以通过
类名.类方法名
或对象名.类方法名
调用(推荐前者);普通方法必须通过对象名.方法名
调用,因为依赖对象的状态 。 - 关键字限制:类方法中不允许使用
this
和super
关键字,因为类方法不关联具体对象,不存在this
指向的当前对象,也用不到super
相关的继承上下文;而普通方法(成员方法)可以使用这些关键字 。 - 成员访问限制:类方法(静态方法)中只能访问静态变量或静态方法,因为非静态成员属于对象,类方法执行时可能没有对象存在;普通成员方法既可以访问非静态成员,也能访问静态成员(只要符合访问权限) 。