Java开发教程5-封装与静态成员

xianaldai 2026-08-12 01:55 1

本期是Java开发教程系列的第五期,本期主要向各位介绍封装static 静态成员——把数据保护起来,并认识"属于类而不属于对象"的成员.若你已经熟悉 getter/setter 和 static,可以跳过本期内容.


每期叠甲:


时间仓促,水平有限,文中难免存在缺点和错误,恳请广大开发者和从业者指正.




一、封装:private + getter/setter


第 4 期学了 private——只有本类内部能访问.但光把字段设为 private 还不够,外面完全读不到也不行.封装的典型写法是:字段私有,再用公开的方法来读写



public class Student {

private String name; // 字段私有

private double score;

public Student(String name, double score) {

this.name = name;

this.score = score;

}

// getter:读值

public String getName() {

return name;

}

public double getScore() {

return score;

}

// setter:改值,可以在里面做校验

public void setScore(double score) {

if (score < 0) {

System.out.println("成绩不能为负数,拒绝修改");

return; // 直接结束方法,不修改

}

this.score = score;

}

}


现在外面不能直接写 s.score = -80 了(编译报错),只能通过 setScore() 改——而 setScore() 里拦住了负数.数据就安全了.


命名约定:字段叫 score,getter 叫 getScore(),setter 叫 setScore();boolean 字段的 getter 叫 isXxx().这个约定 IDE 可以一键生成.


为什么说封装是好事:




  1. 能校验:setter 里可以拦非法值,像上面的负数成绩




  2. 能改内部实现:字段将来换存储方式,只要 getter/setter 签名不变,外面代码不用动




  3. 缩小影响面:别人只依赖你公开的方法,内部怎么改都不怕






二、static 静态成员


static 修饰的成员属于而不是某个对象.看例子:



public class Student {

public static String school = "第一中学"; // 静态字段

public String name; // 实例字段

public static void welcome() { // 静态方法

System.out.println("欢迎来到 " + school);

}

}




  • 静态字段:所有对象共享同一份.改 Student.school = "第二中学",所有学生的 school 一起变




  • 实例字段:每个对象各有一份.s1.names2.name 互不影响




  • 静态方法:用类名直接调,不用创建对象:





Student.welcome(); // 直接调,不需要 new



  • 静态方法里不能直接用非静态的字段和方法(因为没有"某个对象"可指);反过来,非静态方法可以用静态成员


什么时候用 static:




  • 工具方法:像 Math.abs(),做一件和任何对象都无关的事




  • 常量:public static final 一起用,表示一个不变的全局值




  • 共享状态:像上面的 school,所有对象要共用





现在你回头看 System.out.println(...):System 是一个类,out 是它一个 public static 的字段——因为静态,不用创建对象,直接用 System.out 就能拿到.



static 与内存:静态成员不参与垃圾回收


讲完用法,还得说一个和内存有关的注意事项——static 字段不会被垃圾回收.


回忆一下:实例字段是跟着对象走的,对象没人用了,整个对象(连同它的实例字段)会被垃圾回收(垃圾回收是 JVM 自动做的事:没人再用的对象,占的内存会被收回去,这个机制后面我们会专门学).但静态字段属于,而类从加载进 JVM 的那一刻起就一直活着,直到程序结束.所以:




  • 静态字段的引用一直存在 → 垃圾回收器认为它引用的对象"还在被使用" → 永远不回收




  • 实例字段的对象没人用了 → 引用断开 → 正常回收




这不一定是坏事(常量本来就该常驻),但有一个经典坑:把会无限增长的东西放进 static 集合:



public class GlobalData {

// 全程序共享的日志列表,每来一条就 add 一条,只增不减

public static List<String> logs = new ArrayList<>();

}


logs 是 static,垃圾回收器永远认为它"还在用"——里面的每一条日志都回收不掉.运行一天两天看不出来,运行几个月,这个 List 越来越大,内存被悄悄吃掉,程序越来越慢.这就是最常见的内存泄漏之一:对象没用了却回收不掉.


三条使用建议:




  1. static 放常量(public static final 的数值、字符串)没问题:值小、数量固定,常驻内存也合理




  2. static 集合要当心:要么限制大小(满了清旧的),要么别用 static 装会增长的数据




  3. static 大对象(大数组、大缓存)动手前先问一句:它真的需要活到程序结束吗?不需要,就别用 static






三、小项目:受保护的成绩


把封装和 static 串起来,做一个带校验的成绩管理.


文件一 Student.java:



public class Student {

private String name;

private double score;

// 静态字段:所有学生共享

public static String school = "第一中学";

public Student(String name, double score) {

this.name = name;

this.score = score;

}

public String getName() {

return name;

}

public double getScore() {

return score;

}

// setter:带校验,成绩不能为负

public void setScore(double score) {

if (score < 0) {

System.out.println(name + " 的成绩不能为负数,拒绝修改");

return;

}

this.score = score;

}

}


文件二 Main.java:



public class Main {

public static void main(String\[\] args) {

Student s1 = new Student("小明", 92);

Student s2 = new Student("小红", 58);

// 直接改私有字段会编译报错: s1.score = -10;

s1.setScore(-10); // 走 setter,被校验拦下

s1.setScore(95); // 合法,修改成功

System.out.println(s1.getName() + ":" + s1.getScore());

System.out.println(s2.getName() + ":" + s2.getScore());

// 静态字段通过类名访问,所有对象共享

System.out.println("学校:" + Student.school);

Student.school = "第二中学"; // 改一处,全部对象一起变

System.out.println(s1.getName() + " 的学校:" + Student.school);

System.out.println(s2.getName() + " 的学校:" + Student.school);

}

}


运行结果:



小明 的成绩不能为负数,拒绝修改

小明:95.0

小红:58.0

学校:第一中学

小明 的学校:第二中学

小红 的学校:第二中学


重点看两处:负数成绩被 setter 拦下;Student.school 改一次,两个学生对象读到的学校一起变——静态字段就是"所有对象共享同一份".




朝花夕拾:System.out 背后的 static


从第 1 期到现在,我们写了无数遍 System.out.println(...),它到底怎么工作的?学完 static,终于能拆开:




  • Systemjava.lang 包里的一个类




  • out 是它内部的一个 public static 字段,类型是 PrintStream(一个负责输出的类)




  • 因为是 static,不需要 new System(),直接用 System.out 就能拿到那个输出对象




  • .println(...) 则是这个输出对象的方法




一行天天写的代码,背后是"静态字段 + 对象方法"的组合——这正是本期学的概念.另外,第 3 期说过 java.lang 自动导入,System 就是 java.lang 里的类,所以从第 1 期起我们写它从来没写过 import.




总结


本期完成了"把数据管起来"的关键一步:




  • 封装:private 字段 + getter/setter,setter 里做校验;命名约定 getXxx/setXxx/isXxx\




  • static:属于类不属于对象;静态方法用类名直接调,静态字段所有对象共享;静态方法里不能直接用非静态成员;静态字段不参与垃圾回收,慎放会增长的数据




  • 回头看 System.out,静态的活教材




接下来我们学习继承与多态——让类之间可以"父子相传",开始真正发挥面向对象的威力.

最新回复 (2)
  • ryuki 08-12 01:57
    1

    佬不睡觉的么,大半夜更教程,超人来的

  • xianaldai 楼主 08-12 02:09
    2

    这何尝不是熬夜流coding爱好者

* 帖子来源Linux.do
返回