基础概念
- 类是创建对象的模板或蓝图 。对象是现实世界中的实体 或类的实例。
- 声明类使用 Java 关键字
*class,Java 中的所有关键字都是小写字母。 - class 关键字后面跟着类名,根据 Java 的约定,首字母必须大写。
*new* 关键字会在运行时动态地在 RAM(随机存取存储器)中创建对象,并返回所创建对象的引用(地址)。该引用存储在obj中,将来将使用该引用来访问对象。
一、Class 类的 5 个特性
- 类是相似对象的集合模板
类定义了一批事物共同的属性(字段)和行为(方法),比如User类,规定了所有用户都要有id、username属性、login()方法,是对同类事物的抽象归纳。
- 类是概念性的模板
就像房屋设计图纸,图纸只是设计方案,不是真实能住的房子;它只规定房子的结构,本身不能直接使用。
- 类不会分配堆内存
编译阶段加载类信息会占用少量方法区内存,但不会在堆上分配实例内存;只有用new创建对象时,才会给对象分配堆内存。
- 类可以不依赖任何对象独立存在
你可以只写好User.java编译出User.class,全程不创建任何User对象,这个类依然能被 JVM 识别、加载。
- 类的字段没有绑定具体数值
User类里只会声明private String username;,不会写死username = "张三";它只定义 "有这个属性",不设置具体内容。
二、Object 对象的 5 个特性
- 对象是类的实例
按照User这个图纸盖出来的某一套真实房子 ,就是对象;代码里User zhangsan = new User();,zhangsan就是User类的实例对象。
- 对象是真实存在的实体
它是能实际调用方法、存储数据的实体,就像建好的房子可以入住、使用。
- 每个对象都有独立的内存空间
User u1 = new User(); User u2 = new User();,u1和u2在堆上是两块独立内存,修改u1的属性不会影响u2。
- 对象不能脱离类存在
Java 里所有对象都必须由对应的类(或父类)来定义结构,没有无模板凭空出现的对象;创建对象时 JVM 必须先加载它所属的类。
- 每个对象的字段都有专属具体数值
zhangsan.username = "张三"; lisi.username = "李四";,两个对象的同一个字段可以存完全不同的数据,互相隔离。
三、类和对象对比总结
- 关系:先有类,才能造对象,类是图纸,对象是成品;
- 内存:类加载放方法区,对象创建在堆区;
- 数据:类只定义字段名,对象才存字段的实际内容;
- 独立性:类可以零实例存在,对象必须依附类存在。
四、方法区存类、堆存对象
- 方法区(Method Area)
专门存所有 Class 模板本身 :代码里写的类名、属性定义、方法代码、常量、注解全部存在这里。程序启动后 JVM 会把.class字节码解析好,放进方法区,全程只加载 1 次,所有对象共用这一份模板。
- 堆(Heap)
专门存new出来的实例对象(真实房子) ,每一次new都会在堆里开辟一块独立空间,存这个对象专属的字段值。
写一段极简 Java 代码,逐行看内存分配
typescript
// 1. 定义User类(图纸)
public class User {
// 模板里只声明有name、age,没有具体值
String name;
Integer age;
// 模板里的方法代码,只存一份
public void sayHi() {
System.out.println("你好");
}
}
// 主程序
public class Test {
public static void main(String[] args) {
// 2. 第一次new,堆里造第一个房子
User zhangsan = new User();
zhangsan.name = "张三";
zhangsan.age = 20;
// 3. 第二次new,堆里再造一间独立房子
User lisi = new User();
lisi.name = "李四";
lisi.age = 22;
}
}
1)程序启动时:方法区发生什么?
JVM 读取User.class字节码,把下面这些模板信息一次性存入方法区,永久只存一份:
- 类名:User
- 字段定义:String name、Integer age(只是声明有这两个属性,没有张三 / 李四的值)
- 方法完整代码:sayHi () 的执行逻辑
- 构造函数代码
类比前端:方法区 ≈ 你项目里
class User {}这段 TS 源码定义,整个项目全局只有这一份类型模板,不会复制多份。
2)执行 new User () 时:堆区发生什么?
每执行一次new User(),JVM 在堆内存新开一块独立空间:
zhangsan = new User()堆中一块内存:name = "张三",age = 20lisi = new User()堆中另一块完全隔离的内存:name = "李四",age = 22
两块堆内存互不干扰,修改zhangsan.age,不会影响lisi。
五、方法区和堆区核心区分
1. 存储内容完全不同
- 方法区:存模板定义(有什么属性、有什么方法,没有具体数据)
- 堆区:存实例数据(张三、李四这种真实业务值,每个对象独立一份)
2. 创建时机不一样
- 方法区:程序启动,第一次用到这个类时自动加载,只加载 1 次
- 堆区:只有写
new的时候才分配内存,写几次 new 就开辟几块内存
3. 数量关系
- 方法区:一个类永远只占 1 份空间
- 堆区:一个类可以对应 N 个堆对象,N 由你
new的次数决定
疑问:如果我只写 class,不 new 任何对象,内存怎么分配?
方法区依然会存入这个类的模板;但堆里是空的,没有分配任何内存。对应业务:你定义了实体类,但全程没有查询数据库、没有创建实例,堆不会占用额外空间。