Java类与对象_10
在 Java 中,类(Class)和对象(Object)是面向对象编程(OOP)的核心概念。类是对象的模板,而对象是类的实例。
类
类是对象的蓝图或模板,它定义了对象的属性(字段)和行为(方法)
class 类名 {
// 字段(属性)
数据类型 字段名;
// 构造方法
类名(参数列表) {
// 初始化代码
}
// 方法(行为)
返回类型 方法名(参数列表) {
// 方法体
}
}
示例:
class Dog {
// 字段
String name;
int age;
// 构造方法
Dog(String name, int age) {
this.name = name;
this.age = age;
}
// 方法
void bark() {
System.out.println(name + " is barking!");
}
}
字段
字段(Field):类的属性,用于描述对象的状态
String name;
int age;
属性的定义:访问修饰符 属性类型 属性名
| 访问修饰符 | 作用范围 |
|---|---|
| Public(公共) | 任何地方都可以访问 修饰的成员可以被类内部、外部、子类以及其他包中的代码访问 |
| Protected(受保护) | 类内部、子类以及同一个包中的代码可以访问 修饰的成员可以在类内部、子类以及同一个包中的其他类中访问 |
| Default(默认,包私有) | 同一个包中的代码可以访问 如果没有显式指定访问修饰符,则默认为包私有 修饰的成员只能在同一个包中的类中访问,包外的代码无法访问 |
| Private(私有) | 仅在类内部可以访问 修饰的成员只能在定义它的类中访问,外部代码(包括子类)无法直接访问 |
| 访问修饰符 | 类内部 | 同一个包 | 子类 | 其他包 |
|---|---|---|---|---|
public |
✔️ | ✔️ | ✔️ | ✔️ |
protected |
✔️ | ✔️ | ✔️ | ❌ |
default |
✔️ | ✔️ | ❌ | ❌ |
private |
✔️ | ❌ | ❌ | ❌ |
方法
方法(Method):用于定义对象的功能
// 方法
void bark() {
System.out.println(name + " is barking!");
}
方法的定义(方法签名):访问修饰符、返回类型、方法名、参数列表
访问修饰符 返回类型 方法名(参数列表) {
// 方法体
return 返回值; // 如果返回类型不是 void
}
访问修饰符: public、protected 、default、private
方法参数列表中的可变参数
可变参数(Varargs)是Java 5引入的一个特性,它允许方法接受不定数量的参数。
返回值类型 方法名(类型... 变量名) {
// 方法体
}
-
类型... 变量名:表示可变参数。可变参数本质上是一个数组
-
可变参数必须是方法的最后一个参数
-
可变参数可以接受0个参数
-
一个参数列表中只能有一个可变参数
-
错误示例:
public void printValues(int... values) { } public void printValues(int a, int... values) { }// 编译错误 public void printValues(int... values) { } public void printValues(int[] values) { } // 编译错误,方法签名冲突 public void printValues(int... intValues,String... stringValues) { }// 编译错误
方法的分类
-
实例方法(Instance Method)
- 属于类的实例(对象),必须通过对象调用
- 可以访问实例变量和其他实例方法
public class MyClass { public void printMessage() { System.out.println("This is an instance method."); } } // 调用 MyClass obj = new MyClass(); obj.printMessage(); -
静态方法(Static Method)
- 属于类本身,而不是类的实例,可以通过类名直接调用
- 不能直接访问实例变量或实例方法(需要通过对象访问)
public class MyClass { public static void printMessage() { System.out.println("This is a static method."); } } // 调用 MyClass.printMessage(); -
构造方法(Constructor)
- 用于创建对象时初始化对象的状态
- 方法名必须与类名相同,且没有返回类型
- 如果没显示声明构造器则有一个默认的空参构造器
- 如果显示声明构造器后则不会生成默认的空参构造器
public class MyClass { private int value; // 构造方法 public MyClass(int value) { this.value = value; } } // 调用 MyClass obj = new MyClass(10); -
抽象方法(Abstract Method)
- 只有方法声明,没有方法体,必须在抽象类或接口中定义
- 子类或实现类必须重写抽象方法
public abstract class MyClass { public abstract void printMessage(); } -
重载方法(Overloaded Method)
- 在同一个类中,方法名相同但参数列表不同(参数类型、数量或顺序)
- 返回值类型无要求并且不构成重载方法的条件
- 抛出异常类型无要求并且不构成重载方法的条件
-
重写方法(Overridden Method)
- 子类重写父类的方法,方法签名必须相同
- 子类重写的方法的返回值类型必须与父类被重写的方法的返回值类型相同
- 从Java 5开始,允许子类重写的方法返回值类型是父类返回值类型的子类。这被称为协变返回类型(Covariant Return Type)
- 访问修饰符不能更严格:子类方法的访问修饰符不能比父类方法更严格(例如,父类方法是protected,子类方法可以是public,但不能是private)
- 异常列表不能更宽:子类方法抛出的异常不能比父类方法抛出的异常更宽(即子类方法可以抛出更具体的异常或不抛出异常)
- 如果父类或者接口的方法中没有抛出异常,那么子类的重写方法中不能抛出编译时异常,可以抛出运行时异常
- 子类方法不能重写父类的private方法(因为private方法对子类不可见)。
- 子类方法不能重写父类的final方法(final方法禁止重写)。
- 子类方法不能重写父类的static方法(static方法属于类级别,不是实例级别)。
方法传参
- 值传递(Pass by Value)
- Java 中的方法参数是按值传递的,即方法接收的是参数的副本,而不是原始值。
- 对于基本数据类型(如
int、double),方法内修改参数不会影响原始值。 - 对于引用数据类型(如对象、数组),方法内修改对象的属性会影响原始对象。
- 需要特别注意的是:包装类(如 Integer、Double、Boolean 等)是不可变类(包装类本身的设计)。这意味着一旦创建了一个包装类对象,其内部的值就不能被修改,任何对实例的修改操作都会返回一个新的实例,而不是修改原始实例
| 方法中的一些知识点 |
|---|
| 可变参数允许方法接收任意数量的参数,使用 ... 表示 可变参数必须是方法的最后一个参数 在方法内部,可变参数被当作数组处理 |
| void 方法可以没有 return 语句,或者使用 return; 提前结束方法 |
| 同类中的方法直接调用即可,跨类调用方法需要使用对象或者类进行调用 |
对象
对象是类的实例,通过类可以创建多个对象。每个对象都有自己的状态(字段值)和行为(方法)
对象在堆中存储
-
创建对象
使用 new 关键字调用构造方法创建对象
类名 对象名 = new 类名(参数列表); Dog myDog = new Dog("Buddy", 3); -
访问对象的字段和方法
使用点号(.)访问对象的字段和方法
// 访问字段 System.out.println(myDog.name); // 输出 "Buddy" System.out.println(myDog.age); // 输出 3 // 调用方法 myDog.bark(); // 输出 "Buddy is barking!" -
this关键字
this代表当前对象的引用。this 关键字主要用于区分实例变量和局部变量、调用当前类的其他构造函数,以及作为参数传递当前对象
-
引用当前对象的实例变量,当方法的参数名或局部变量名与实例变量名相同时,使用 this 可以明确指定访问的是实例变量
-
调用当前类的其他构造函数,在一个构造函数中,可以使用 this() 调用当前类的其他构造函数。这种方式称为 构造函数重载
class Person { private String name; private int age; // 无参构造函数 public Person() { // 调用另一个构造函数 this("Unknown", 0); } // 带参构造函数 public Person(String name, int age) { this.name = name; this.age = age; } } //注意:this() 必须放在构造函数的第一行 -
作为参数传递当前对象
this 可以作为参数传递给其他方法或构造函数,表示当前对象
public void printDetails(Printer printer) { // 将当前对象传递给 Printer printer.print(this); } return this;//表示返回当前对象
-
-
对象创建流程
-
类加载
当JVM遇到new关键字时,首先会检查该类是否已经加载。如果未加载,JVM会通过类加载器加载该类。类加载过程包括
-
加载:查找并加载类的字节码文件(
.class文件)。 -
验证:确保字节码文件的正确性和安全性。
-
准备:为类的静态变量分配内存并设置默认值(如
0、null等)。 -
解析:将符号引用转换为直接引用。
符号引用通常以字符串的形式存在,例如类名、方法名、字段名等。符号引用不依赖于具体的内存地址
直接引用是指向内存中某个具体对象的引用
-
初始化:执行类的静态初始化块和静态变量的赋值操作。
-
-
内存分配
类加载完成后,JVM会为对象分配内存。内存分配的方式取决于JVM的实现,常见的方式有
- 指针碰撞:如果内存是规整的,JVM只需移动指针即可分配内存。
- 空闲列表:如果内存不规整,JVM需要维护一个空闲列表来查找合适的内存块。
-
初始化默认值
在内存分配完成后,JVM会将对象的内存空间初始化为默认值
-
数值类型(如int、float等)初始化为0。
-
布尔类型初始化为false。
-
引用类型初始化为null。
-
-
设置对象头
JVM会在对象的内存空间中设置对象头,对象头包含以下信息:
- Mark Word:存储对象的哈希码、GC分代年龄、锁状态等信息。
- Klass Pointer:指向类的元数据(即Class对象),用于确定对象的类型。
-
执行实例初始化块和构造函数
在对象头设置完成后,JVM会执行对象的初始化过程,包括
- 实例初始化块:如果类中有实例初始化块({}),JVM会执行这些块中的代码。
- 构造函数:JVM会调用类的构造函数来初始化对象。构造函数的执行顺序如下:
- 调用父类的构造函数(如果没有显式调用,JVM会自动调用父类的无参构造函数)。
- 执行实例变量初始化(即直接赋值操作)。
- 执行构造函数中的代码。
- 先执行实例初始化块后调用构造函数
-
返回对象引用
-

浙公网安备 33010602011771号