开篇:先解决上一章留下的"数据错位"问题
第四章结尾我们留了个痛点:姓名一个数组、成绩一个数组,靠下标对齐维系关系,一旦错位,程序不报错但结果全错。这一章用类与对象把它根治。先看完整代码------把"工人"封装成一个类,一份数据一个对象,物理上捆在一起:
java
public class Worker {
// 字段:一个工人"有什么"
String name;
int score;
String team;
// 构造方法:创建对象时一次性把数据填好
Worker(String name, int score, String team) {
this.name = name;
this.score = score;
this.team = team;
}
// 方法:一个工人"能做什么"
String getLevel() {
if (score >= 90) {
return "优秀";
} else if (score >= 75) {
return "良好";
} else if (score >= 60) {
return "及格";
} else {
return "不及格";
}
}
void printInfo() {
System.out.println(team + " | " + name + " | "
+ score + " 分 | " + getLevel());
}
}
再写入口类,创建几个对象用起来:
java
public class WorkerApp {
public static void main(String[] args) {
Worker w1 = new Worker("张伟", 85, "综采一队");
Worker w2 = new Worker("李强", 92, "综采一队");
Worker w3 = new Worker("王磊", 58, "掘进三队");
w1.printInfo();
w2.printInfo();
w3.printInfo();
// 统计有多少个工人对象被创建过
System.out.println("当前共登记 " + Worker.count + " 名工人");
}
}
输出:
java
综采一队 | 张伟 | 85 分 | 良好
综采一队 | 李强 | 92 分 | 优秀
掘进三队 | 王磊 | 58 分 | 不及格
(最后一行先运行会报错------Worker.count 我们还没写,这正是讲 static 时要补上的,稍等。)
现在姓名、成绩、班组被"焊"在同一个对象里,w1.name 永远对应 w1.score,想错位都没机会。下面把这个类逐层拆开。
第一部分:字段与方法------对象的状态与行为
一个类里装两种东西:
java
String name; // 字段(field):对象的数据,描述"是什么"
int score;
String getLevel() { ... } // 方法(method):对象的行为,描述"能干什么"
-
字段 写在类里、方法外,每个对象各有一份。w1 的 score 是 85,w2 的是 92,互不干扰。
-
方法 就是装在类里的函数,和 C/JS 函数的区别是:它天然属于某个对象,调用时要通过对象来点出来------
w1.printInfo()。
对照 TypeScript:Worker 类 ≈ 一个 interface + 实现。但 Java 里没有对象字面量,想造数据必须先定义类,这是 Java"重"的地方,也是它结构清晰的原因。
方法的声明格式
java
String getLevel() { ... }
// ↑ ↑
// 返回值类型 方法名
void printInfo() { ... } // void 表示不返回任何东西
两个要点:
-
声明了返回值类型(非 void),方法里每条路径都必须 return 对应类型的值,少一个 return 编译报错;
-
return除了交回结果,还有"立即结束方法"的作用,后面的代码不再执行。
方法参数与"值传递"
java
void updateScore(int newScore) {
score = newScore;
}
Java 的参数传递只有值传递 一种:传基本类型,传的是值的副本(方法里改了,外面不受影响);传引用类型,传的是地址的副本(方法里能通过这个地址改到对象内部)。日常开发记住一句就够:int、double 这些传进去随便改不影响外面;对象传进去,改它的字段是会影响外面的。这个考点软考偶尔出现,先有个印象。
第二部分:构造方法------对象出生时的"登记表"
java
Worker(String name, int score, String team) {
this.name = name;
this.score = score;
this.team = team;
}
new Worker("张伟", 85, "综采一队") 这行代码干了三件事:在内存里划出一块地方(新对象)→ 调用构造方法把三个值填进字段 → 把对象地址交给 w1。
构造方法的特征,一眼识别:
-
方法名和类名完全相同;
-
没有返回值类型 ------连
void都不写(写了 void 就变成普通方法了,这是个经典考点); -
只能通过
new触发,不能像普通方法那样w1.Worker(...)调用。
两个必知点:
- 不写构造方法时,编译器白送你一个无参的 (
new Worker()可用,字段全是默认值);一旦你写了任何构造方法,这个白送的就收回了 。上面的代码写了三参构造,此时new Worker()会编译报错。想让两种都能用,就自己再补一个无参的:
java
Worker() {
}
- 构造方法的典型职责就是初始化字段,顺便可以做校验,比如分数不合法时给个默认值:
java
Worker(String name, int score, String team) {
this.name = name;
this.score = (score >= 0 && score <= 100) ? score : 0;
this.team = team;
}
第三部分:this------"我自己"
构造方法里满屏的 this. 是什么意思?
java
this.name = name;
当参数名和字段名同名时(都叫 name),name = name 就成了自己赋给自己,字段根本拿不到值。this 指代当前这个对象 ,this.name 明确说"我要的是字段那个 name",右边的 name 则是参数。这句是 Java 里出镜率最高的代码模式之一,IDE 生成构造方法时自动就是这么写的。
this 的第二个常用场景是普通方法里访问自己的字段 。方法内直接使用 score,其实隐含了 this.score,一般省略;需要把自己交给别人时就必须显式写,比如 list.add(this)------"把我自己加进清单"。
第四部分:static------属于"类",不属于"某个对象"
现在补上开头欠的账:统计一共创建了多少个工人。这个数不属于任何一个工人------它是"工人这个类"的公共信息。用 static:
java
public class Worker {
static int count = 0; // 静态字段:全类共享一份
String name;
int score;
String team;
Worker(String name, int score, String team) {
this.name = name;
this.score = score;
this.team = team;
count++; // 每创建一个对象,计数加一
}
// ...
}
现在入口里那句 Worker.count 就能跑通了,输出 当前共登记 3 名工人。
static 的本质一句话:
不加 static 的成员,每个对象各有一份(实例成员);加了 static 的成员,整个类只有一份,所有对象共享(静态成员/类成员)。
对应的访问方式也不同:
-
实例成员:
对象.成员,如w1.name、w1.printInfo(); -
静态成员:
类名.成员,如Worker.count(用对象也能点出来,但规范写法是类名)。
main 方法为什么是 static 的?第一章埋的伏笔现在能回答了:程序启动时还没有任何对象,JVM 只能调用"属于类本身"的方法,所以入口必须 static。
还有一个硬限制要记住:静态方法里不能直接访问实例成员 。比如给 Worker 加一个静态方法,里面直接写 System.out.println(name) 会编译报错------静态方法是"类"在干活,此刻根本没有指定"哪个工人",哪来的 name?这也是为什么 main 方法里想干活,都得先 new 出对象来。
常踩的坑(排雷清单)
-
给构造方法写了 void :
public void Worker(...) { }编译不报错,但它变成了普通方法,new Worker(...)会报"找不到构造方法"。构造方法没有任何返回值类型。 -
写了带参构造,又想用无参 new:白送的无参构造已被收回,要么补上无参构造,要么老老实实传参。
-
静态方法里访问实例字段 :编译报错。记住静态上下文里没有"当前对象",
this也用不了。 -
用对象访问静态成员 :
w1.count能编译,但容易让人误以为 count 是 w1 私有的。统一用Worker.count的写法。 -
字段不初始化直接用 :字段有默认值(0 / null)所以不报错,但逻辑可能是错的------int 字段忘了赋值就是 0 分。局部变量则不同,不初始化直接编译报错。
-
把 static 当全局变量滥用:计数器这种"全类共享"的场景用 static 恰如其分;如果一个数据明显属于"某个对象",加 static 会让所有对象互相串数据,这是新手期很典型的设计错误。
本章小结
-
类 = 字段(状态)+ 方法(行为);对象 = new 出来的具体实例,每个对象的字段各有一份。
-
构造方法:名字同类、无返回值类型、由 new 触发;写了带参构造,白送的无参构造就没了。
-
this指当前对象,构造方法里this.name = name是标准模式。 -
static成员全类共享一份,用类名.成员访问;静态方法里不能碰实例成员------main 方法就是最典型的静态方法。
这一章的 Worker 类,字段全部敞开着(w1.score = -999 随便改没人拦),这显然不合理。下一章:方法重载、访问修饰符与封装 ------我们会给 Worker 加上 private 和 getter/setter,讲讲为什么 Java 的字段几乎都是 private 的,顺便把方法重载这个高频考点讲透。