类是对象的模板,对象是内存中的实体。本篇讲透”定义类、创建对象、访问成员”这条主线,用内存布局说清栈、堆、方法区的分工,再落到封装、private 与 this 这三个最易含糊的概念上。

本篇目标

  • 说清类与对象的关系,能独立写出标准类
  • 描述 new Student() 执行后的内存布局
  • 分清成员变量与局部变量的区别
  • 理解封装动机,会用 private + getter/setter,明白 this 指向谁

从面向过程到面向对象

面向过程关注”第一步做什么、第二步做什么”,自己动走完全部流程;面向对象先问”这件事该由谁来做”,把数据和行为装进对象,再指挥对象协作。洗衣服:面向过程是”接水、加洗衣粉、搓洗、晾晒”一步步来;面向对象是找到”洗衣机”对象,调用它的洗涤方法。角色从执行者变成指挥者,复杂的事被拆给不同对象去办——面向对象开发就是不断创建对象、使用对象、指挥对象做事,这片土壤上长出封装、继承、多态三大特征。

类与对象:模板与实例

现实事物用”属性 + 行为”描述:属性是名词,行为是动词。Java 用 class 描述事物,成员变量对应属性,成员方法对应行为。类是一组相关属性和行为的集合,是抽象的模板;对象是模板造出的具体个体。”学生”是类,”坐在第三排的张三”才是对象。

class Student {
String name; // 成员变量:类中、方法外
int age;

public void study() { // 成员方法:去掉 static
System.out.println(name + "正在学习");
}
}

使用就三步:类名 对象名 = new 类名();,然后 对象名.变量名、对象名.方法名()。初学阶段可以在一个 .java 文件里写两个类——一个被描述的类、一个含 main 的测试类,文件名与测试类保持一致即可。常见错误:习惯性给成员方法加 static——加了它就属于类而不属于对象,访问不了成员变量,原因下篇揭晓。

对象创建时的内存分配

Student s = new Student(); 背后,JVM 三块内存各司其职:

内存区域 存什么 本例内容
方法区 类的字节码信息 Student.class 的字段与方法描述
堆 new 出的对象 实例,name=null、age=0
栈 方法的局部变量 引用 s,保存对象地址

顺序:Student.class 进方法区;main 进栈并声明引用 s;new 在堆中开辟对象、字段拿默认值;构造方法进栈赋值后弹栈;地址交给 s。口诀:”栈中存引用,堆中存对象”。再写 Student s2 = s;,堆里不会多对象,只是栈中多一个指向同一地址的引用——“三个引用两个对象”的内存图说的就是它:通过 s2 改字段,用 s 看同样变。

成员变量与局部变量

维度 成员变量 局部变量
定义位置 类中、方法外 方法内或形参上
内存位置 堆(随对象) 栈(随方法)
生命周期 随对象创建而生、消失而亡 随方法调用而生、弹栈而亡
初始值 有默认值(0、null、false) 无默认值,必须先赋值

同名时方法内就近取值,想访问成员变量必须加 this。引用类型(数组、类、接口、枚举)作形参时传的是对象地址。

匿名对象

new Student().study(); 这种没有引用接住的对象就是匿名对象。两个适用场景:方法只调一次,省一行声明;作为实参传递,如 print(new Student());——看到形参是类类型,就要明白它需要的是该类的对象。注意:匿名对象用完即成垃圾,等垃圾回收器处理,要操作两次以上必须给它名字。

封装与 private

直接把 age 暴露出去,调用方写 s.age = -20; 编译器不拦——语法合法,逻辑荒谬。封装就是隐藏对象的属性和实现细节,仅对外提供受控的公共访问方式:字段用 private 私有化,再提供 getXxx/setXxx,在 setter 里加校验。private 是权限修饰符,被它修饰的成员只能在本类中访问。好处:隐藏细节、提高复用、保证安全。注意 private 只是封装的一种体现,方法、类本身也是封装,完整的权限图景要等学到”包”。时代化一句:今天 getter/setter 常由 Lombok 注解或 record 关键字自动生成,但”字段私有、受控访问”的封装思想从没变过。

this 的作用

this 代表当前对象的引用——谁调用这个方法,this 就是谁。最常见用途是解决 setter 重名:

public void setAge(int age) {
this.age = age; // 左边成员变量,右边局部变量
}

没有 this,age = age 按就近原则只是把参数赋给自己,成员变量纹丝不动。getter 里 return this.age; 的 this 可省略,系统默认补上。此外 this 还能以 this(...) 的形式在构造方法里调用本类其他构造,留待构造方法一篇展开。

经典练习:标准手机类

字段私有化、配齐 getter/setter、用 this 区分重名,就是标准 JavaBean:

class Phone {
private String brand;
private int price;

public void setBrand(String brand) { this.brand = brand; }
public String getBrand() { return brand; }
public void setPrice(int price) {
if (price < 0) { System.out.println("价格非法"); return; }
this.price = price;
}
public int getPrice() { return price; }

public void call() { System.out.println("打电话"); }
public void sendMessage() { System.out.println("发短信"); }
public void playGame() { System.out.println("玩游戏"); }
}

测试类里 Phone p = new Phone(); p.setBrand("三星");,体会”创建对象、指挥对象做事”。

小结

  • 类是模板,对象是实例:属性写成员变量,行为写成员方法。
  • 内存分工:方法区存类信息,堆存对象,栈存引用与局部变量;引用赋值只复制地址。
  • 成员变量与局部变量差在位置、内存、生命周期、初始值;同名就近原则。
  • 封装的落点是 private 字段 + 公共 get/set;this 是当前对象引用,用来区分重名变量。