返回首页

封装,private,getter,setter,static,final

3645 字 18 分钟

day7#

前一天回顾#

观察:

Student[] students = new Student[2];
students[0] = new Student("张三", 85, javaClass);
Student firstStudent = students[0];

回答:

  1. 不计算之前已有的 javaClass,这段代码新创建了几个对象?分别是什么? 1个Student[]数组对象 1个Student对象 总计2个对象
  2. students[0]firstStudent 是否指向同一个 Student 对象?
  3. students[1] 当前是什么?
  4. 如果执行 firstStudent.score = 100,再读取 students[0].score,结果是多少?为什么? 构造方法中:this this.属性名字=属性名字 为什么不用属性名字=属性名字 例如: score=score;
public class Student {
int score;
public Student(int score) {
score = score;
}
}

这里存在两个同名内容:

int score; // 对象属性
public Student(int score) // 构造方法参数

在构造方法内部,参数 score 的作用域距离当前代码更近,因此它会遮住对象的同名属性。这叫“变量遮蔽”。

Java看到:

score = score;

不会根据左边或右边自动判断哪个是属性。两个 score 都优先表示构造方法参数。 假设调用:

Student student = new Student(85);

执行过程:

1. 创建Student对象
对象属性score使用默认值0
2. 调用构造方法
参数score接收到85
3. 执行 score = score
左边:参数score
右边:参数score
实际效果:参数85 = 参数85
4. 构造方法结束
对象属性score从未被修改,仍然是0
对象属性score使用默认值0

使用 this 后:

this.score = score;

含义变成:

左边 this.score:当前对象的属性
右边 score:构造方法参数

执行效果:

对象属性score = 参数85

所以对象的成绩变成85。

参数名不同时可以不用 this#

下面也能正确赋值:

public Student(int inputScore) {
score = inputScore;
}

因为:

  • score:没有同名参数遮蔽,所以表示对象属性。
  • inputScore:构造方法参数。

但Java开发中通常让参数名和属性名保持一致,所以使用:

this.score = score;

学习内容#

  • 封装
  • private
  • getter / setter
  • static
  • final 后端开发中用于保护对象状态、验证输入、管理共享数据和表达不可修改的规则。 必须掌握:
  • 属性通常使用 private
  • 通过构造方法、getter和setter访问或修改属性
  • 使用setter控制非法数据
  • 区分实例成员与 static 成员 问题:
  • 什么是封装?
  • 为什么属性通常不写成 public
  • static 属于类还是对象?
  • final 修饰变量有什么作用?

模块一:封装与 private#

封装的核心思想是:

隐藏对象内部数据
→ 对外提供受控制的操作方式

使用 private

public class Student {
private String name;
private int score;
private Classroom classroom;
}

private 表示这些属性只能在 Student 类的内部直接访问。 类内部仍然可以使用:

public boolean isPassed() {
return score >= 60;
}

或者:

public void printInfo() {
System.out.println(name);
}

StudentDemo 中不能再写:

firstStudent.score

否则会出现类似编译错误:

score has private access in Student

为什么后端需要封装 如果成绩可以随意修改:

student.score = 5000;

系统的数据就不可信。 封装后,我们可以规定:

外部想修改成绩
→ 必须通过指定方法
→ 方法检查0~100
→ 合法才修改

模块二:getter#

如果外部只需要读取成绩,可以由 Student 提供一个公开方法:

public int getScore() {
return score;
}

就是在存在private属性的类中创建一个返回private属性的方法,使得private属性返给调用者 含义是:

  • public:其他类可以调用。
  • int:返回一个整数成绩。
  • getScore:表示读取成绩。
  • 空括号:读取时不需要传入参数。
  • return score:把私有属性的值交给调用者。

使用:

int currentScore = student.getScore();

getter没有开放对属性的直接修改:

student.score = -500; // 不允许

但允许安全读取:

student.getScore(); // 允许

返回值不一定必须先保存到变量中,也可以直接参与后续表达式。 例如:

students[index].getScore() > topStudent.getScore()

Java会先执行两个方法调用:

students[index].getScore() → 返回92
topStudent.getScore() → 返回85

然后原表达式就相当于:

92 > 85

结果为 true,所以不需要额外变量。

使用返回值的几种方式#

  1. 保存到变量
int score = student.getScore();

适合后面要多次使用这个值。

  1. 直接输出
System.out.println(student.getScore());

返回值直接交给 println()

  1. 直接参与判断
if (student.getScore() >= 60) {
System.out.println("及格");
}
  1. 直接参与计算
double average =
(firstStudent.getScore() + secondStudent.getScore()) / 2.0;
  1. 作为另一个方法的参数
printScore(student.getScore());

6.什么情况下必须接收 后续还用的情况下 昨天的代码:

studentCount = addStudent(...);

之所以需要保存返回值,是因为新的 studentCount 之后还要继续用于:

  • 判断容量
  • 保存下一名学生
  • 遍历有效学生

模块二:setter#

private score 阻止了外部直接修改:

student.score = 95; // 无法编译

如果业务允许修改成绩,可以提供受控制的方法:

public void setScore(int score) {
if (score < 0 || score > 100) {
System.out.println("成绩必须在0~100之间");
return;
}
this.score = score;
}

这里的 setter 会先验证数据,合法才修改对象属性。

this.#

public void setScore(int score) {
this.score = score;
}

同时存在两个同名的 score。 右边的 score

public void setScore(int score)

括号里的 score 是方法参数,接收调用者传入的新成绩。

例如:

firstStudent.setScore(95);

进入方法后:

参数 score = 95

左边的 this.score

this.score

表示当前调用这个方法的对象所拥有的 score 属性。

调用:

firstStudent.setScore(95);

此时 this 表示 firstStudent 指向的学生对象,所以:

this.score = score;

相当于:

firstStudent对象的score属性 = 参数95

如果调用:

secondStudent.setScore(70);

这次 this 表示第二个学生对象,相当于:

secondStudent对象的score属性 = 参数70

完整执行过程

假设张三原成绩是85:

firstStudent.setScore(95);

执行:

1. 参数score接收95
2. this指向张三对象
3. this.score找到张三对象原来的成绩85
4. 把参数95赋给this.score
5. 张三对象的成绩变成95

为什么不能写 score = score

score = score;

因为方法参数会遮住同名属性,两边都表示参数,相当于:

参数95 = 参数95

对象属性不会改变。

如果不想使用 this,可以把参数改名:

public void setScore(int newScore) {
score = newScore;
}

isBlank()和isEmpty()#

getter ,setter是在类中增加的可以对private属性的方法 isBlank() 是什么

isBlank()String 的方法,用于判断字符串是不是“空白”。

String text = " ";
boolean result = text.isBlank();

结果是:

true

以下字符串调用 isBlank() 都返回 true

""
" "
" "
"\t"
"\n"

因为它们没有实际可见文字。

以下返回 false

"Java一班"
" 张三 "
"123"
对比 isEmpty()#
"".isEmpty() // true
" ".isEmpty() // false
"".isBlank() // true
" ".isBlank() // true
  • isEmpty():字符串长度是否为0。
  • isBlank():是否为空字符串或只包含空白字符。

班级名称输入几个空格也应该判为无效,因此使用 isBlank() 更合适。 className == null 是什么

null 表示这个变量目前没有指向任何字符串对象:

String className = null;

它与空字符串不同:

String first = null; // 没有String对象
String second = ""; // 有String对象,但内容长度为0

不能对 null 调用方法:

className.isBlank();

否则会发生:

NullPointerException
为什么使用 ||#
className == null || className.isBlank()

意思是:

班级名称没有对象
或者
班级名称是空白内容

|| 具有短路效果。

className == nulltrue 时,整个“或”表达式已经确定为 true,Java不会继续执行右边的:

className.isBlank()

因此不会发生空指针异常。

执行情况:

className== nullisBlank()最终结果
nulltrue不执行true
""falsetruetrue
" "falsetruetrue
"Java一班"falsefalsefalse
顺序不能随意交换:#
className.isBlank() || className == null

如果 classNamenull,程序会先调用 isBlank(),立即发生异常,根本执行不到后面的空值判断。

模块三:static#

static 表示成员属于类,并由所有对象共享。 普通属性属于每一个对象:

private String name;
private int score;

张三和李四分别拥有自己的 namescore

如果某个数据应该由整个类共享,就使用 static

private static int studentCount = 0;

它不属于某一个学生,而属于整个 Student 类。无论创建多少对象,都共享同一个计数器。

使用 static 统计对象数量#

static“属于类并由所有对象共享”;能否从外部访问还要看 private/publicStudent 中定义:

private static int studentCount = 0;

每调用一次构造方法,就说明创建了一个 Student 对象:

public Student(String name, int score, Classroom classroom) {
this.name = name;
this.classroom = classroom;
setScore(score);
studentCount++;
}

提供读取方法:

public static int getStudentCount() {
return studentCount;
}

调用时使用类名:

System.out.println(
"已创建学生对象数量:" + Student.getStudentCount()
);

推荐使用:

Student.getStudentCount()

而不是:

firstStudent.getStudentCount()

因为这个计数属于整个 Student 类,不属于张三个人。

static 与普通成员的区别#
name、score:
每个Student对象各有一份
studentCount:
整个Student类只有一份,所有对象共享

静态方法中不能直接使用:

this
name
score

因为静态方法属于类,调用它时可能根本没有具体对象,Java不知道应该读取张三还是李四的成绩。

普通变量 studentCount 属于每个对象,每个学生都有独立的一份;static studentCount 属于 Student 类,所有学生共享同一份,因此可以累计对象总数。

final:只能赋值一次#

  • final 修饰变量后只能赋值一次。
  • final 引用不能重新指向其他对象,但对象内部仍可能改变。

普通变量可以修改:

int maxScore = 100;
maxScore = 120;

使用 final 后,只能赋值一次:

final int maxScore = 100;
maxScore = 120; // 编译错误

final 常用于不会改变的规则,例如合法成绩范围。

static final:类常量#

Student 中定义:

private static final int MIN_SCORE = 0;
private static final int MAX_SCORE = 100;

含义:

  • private:只允许 Student 内部使用。
  • static:整个 Student 类共享一份。
  • final:初始化后不能修改。
  • 全大写:Java常量命名规范。

setter可以改成:

public void setScore(int score) {
if (score < MIN_SCORE || score > MAX_SCORE) {
System.out.println("成绩必须在0~100之间");
return;
}
this.score = score;
}

这样避免在代码中反复写数字 0100

final引用:#

final Classroom classroom = new Classroom("Java一班");

不能让变量指向另一个对象:

classroom = new Classroom("Java二班"); // 编译错误

但仍然可以修改原对象内部的状态:

classroom.setClassName("Java二班"); // 可以

final 限制的是引用变量不能重新指向其他对象,不代表对象内部永远不变。

final 补充#

1. final 修饰变量:不能再次赋值#
final int age = 18;
age = 20; // 编译报错

所以:

final int MAX_SIZE = 100;

这种变量通常用来表示常量。

但如果 final 修饰的是对象引用,不能变的是“引用指向”,不代表对象内容不能变:

final User user = new User();
user.setName("Tom"); // 可以
user = new User(); // 不可以

可以理解成:

final User user
└────→ User对象
name 可以修改
user 本身不能再指向另一个 User对象
2. final 修饰方法:子类不能重写#
class Parent {
public final void show() {
System.out.println("hello");
}
}
class Child extends Parent {
// 报错:不能重写 final 方法
public void show() {
}
}

也就是说: final 方法可以被继承,但不能被 @Override

3. final 修饰类:不能被继承#

例如 Java 中的 String

public final class String {
...
}

所以你不能:

class MyString extends String {
}

因为 Stringfinal 类。

4. 一个表就记住#
使用位置含义
final 变量不能再次赋值
final 方法不能被子类重写
final 类不能被继承

final 变量不能改,final 方法不能重写,final 类不能继承。

汇总#

static 成员属于类,不属于某个具体对象,所有实例共享同一份。final 变量只能赋值一次。static final 表示全类共享且不可重新赋值的常量,因此适合表示最低分和最高分等固定业务规则。 封装不只是“把属性设为私有”,而是:

把数据和操作数据的方法放在类中,隐藏内部实现,只允许外部通过受控制的方法访问。

score 不能设为 public,因为外部可以直接写:

student.score = -10;

这会绕过成绩范围校验,破坏对象的合法状态。设为 private 后:

  • getScore():提供受控的读取入口。
  • setScore():提供受控的修改入口,可以在修改前校验数据。

更完整的回答:

封装是隐藏对象内部的数据和实现细节,通过公开方法提供受控制的访问。score 设为 private,可以防止外部随意写入负数或超过100的成绩。getScore() 用来读取成绩,setScore() 用来修改成绩,并可以集中完成合法性校验。这样能够保护对象状态,也方便以后修改业务规则。

  • static:成员属于类,所有对象共享同一份数据。
  • private:决定只能在当前类内部直接访问。

因此,static 不等于“所有地方都能访问”。

  • final:赋值后不能再次修改。
  • 全大写加下划线:Java 常量命名规范,例如 MAX_SCORE

封装#

在 Java 中,**封装(Encapsulation)**是面向对象三大特性之一:

把对象的数据和操作数据的方法放在一个类里,并限制外部直接访问数据,只通过规定的方法进行操作。

简单理解就是:把内部细节“藏起来”,对外只提供必要的操作接口。

比如我们有一个 Person 类。如果不封装:

class Person {
public int age;
}

外部可以随便修改:

Person p = new Person();
p.age = 18;
p.age = -100; // 也能赋值,明显不合理
p.age = 9999; // 也可以

问题就在这里:age 完全暴露给外部,Person 自己控制不了别人给它什么值。

使用封装之后:

class Person {
private int age;
public void setAge(int age) {
if (age >= 0 && age <= 150) {
this.age = age;
}
}
public int getAge() {
return age;
}
}

现在外部不能直接:

Person p = new Person();
p.age = 18; // ❌ 报错,因为 age 是 private

只能通过我们提供的方法:

Person p = new Person();
p.setAge(18); // ✅
System.out.println(p.getAge());

这样 Person 类就可以自己控制:

外部
│ setAge(18)
┌────────────────────┐
│ Person │
│ │
│ private int age │ ← 外部不能直接访问
│ │
│ setAge() │ ← 对外提供入口
│ getAge() │ ← 对外提供入口
└────────────────────┘

为什么要封装?

核心不是单纯地“把变量改成 private”,而是控制别人怎么使用这个对象

封装,private,getter,setter,static,final
https://335264.xyz/posts/java第七天/
作者
刺儿菜
发布于
2026-09-09
许可协议
CC BY-NC-SA 4.0
Comments

评论