一、前言
记录时间 [2024-05-10]
系列文章简摘:
Java 笔记 01:Java 概述,MarkDown 常用语法整理
Java 笔记 11:Java 方法相关内容,方法的设计原则,以及方法的定义和调用
面向对象 01:Java 面向对象相关内容整体概述
面向对象 02:区分面向过程与面向对象,类和对象的关系
更多 Java 相关文章,请参考专栏哦。
本文内容主要关于类与对象的创建、初始化和使用,以及如何通过 new 关键字调用构造方法。此外,文章还对无参 / 有参这两种构造器进行描述,并通过内存分析创建对象的过程。
二、类与对象的创建
以类的形式组织代码,以对象的形式封装数据。
1. 关键字 new
在 Java 中,关键字 new
是一个非常重要的运算符。
使用 new 关键字创建对象时:
- 为该对象分配进行内存分配;
- 给对象进行默认的初始化;
- 在类中使用构造器进行调用。
2. 关键字 this
在 Java 中,关键字 this
是一个非常重要的引用,它在类的方法或构造器中使用,通常用于引用当前对象或当前实例的引用。
具体如下:
引用当前对象
在类的内部,this
关键字用来指代当前正在操作的这个类的对象实例。
当方法内部的局部变量与类的成员变量同名时,this
可以帮助明确指代成员变量,避免混淆。
例如:
public class Person {
String name;
public void setName(String name) {
// 这里的 this.name 指的是 Person 类的成员变量 name
this.name = name;
}
}
调用当前类的构造器
在一个构造器中,可以使用 this
关键字调用本类中的其他构造器。这种机制简化了代码,避免了重复初始化代码。
但是,必须作为构造器的第一条语句,并且只能在非静态构造器中使用。
例如:
public class Person {
String name;
int age;
public Person() {
this("未知", 0); // 调用带有两个参数的构造器
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
作为方法的参数传递
可以使用 this
关键字将当前对象作为一个参数传递给其他方法。
例如:
先编写一个 Person
类,在 introduce()
方法中调用 display()
方法,并将当前类的对象作为参数传递。
public class Person {
// Person 类中的 name 属性的对象是 zhangsan
String name = "zhangsan";
void display(Person friend) {
System.out.println("朋友的名字是: " + friend.name);
}
void introduce() {
display(this); // 将当前对象作为参数传递
}
}
接着,在 main ()
方法中,实例化一个 Person
类,并调用该类中的 introduce()
方法,得到输出结果。
public static void main(String[] args) {
// 实例化一个 Person 类
Person person = new Person();
// 调用该类中的方法
person.introduce();
}
// 得到输出结果:朋友的名字是: zhangsan
返回当前对象的引用
在某些情况下,可以让方法返回 this
,以便实现链式调用或者构建流式接口。
例如:
public class StringBuilderExample {
StringBuilder sb = new StringBuilder();
public StringBuilderExample append(String str) {
sb.append(str);
return this; // 返回当前对象,允许链式调用
}
}
3. 创建和初始化对象
在 Java 中,创建和初始化对象是一个基本且核心的过程,以下是创建和初始化对象的基本步骤。
准备学生类
在类中,只能写属性和方法。
先准备学生类 Student
用于创建和初始化对象的演示。该类中,包含 name 和 age 两个属性,以及一个 study 方法。
// 学生类
public class Student {
// 属性:字段
String name; // null
int age; // 0
// 方法
public void study() {
System.out.println(this.name + "在学习");
}
}
创建和初始化
在 Application
类中,创建 main
方法。注意,一个项目应该只有一个 main
方法。
使用 new 关键字,以及默认构造器创建 Student
对象。
在这个示例中,为 Student
类创建了 xiaoming 和 zhangsan 两个对象,并分别给它们赋值。
public static void main(String[] args) {
// 类:抽象的,实例化
// 类实例化后返回一个自己的对象
// student 对象就是一个 Student 类的具体实例
Student xiaoming = new Student();
Student zhangsan = new Student();
// 给 xiaoming 对象赋值
xiaoming.name = "小明";
xiaoming.age = 3;
System.out.println(xiaoming.name);
System.out.println(xiaoming.age);
// 给 zhangsan 对象赋值
zhangsan.name = "张三";
zhangsan.age = 3;
System.out.println(zhangsan.name);
System.out.println(zhangsan.age);
}
使用
- 使用对象的属性:
student.name
- 使用对象的方法:
student.study()
三、构造器
1. 构造器概述
在 Java 中,类的构造器,也称为构造方法,是一种特殊类型的方法。
构造器的主要职责是初始化新创建的对象,是在进行创建对象的时候必须要调用的。通过使用构造器,开发者能够灵活地控制对象实例化的细节。
构造器具有以下特点和规则:
- 名称匹配:构造器的名称必须与类名完全相同,这是区分构造器与其他方法的基本方式。
- 无返回类型:构造器没有返回类型声明,即使它实际上隐式地返回了新创建的实例的引用,也不需要像普通方法那样声明一个返回类型。
- 自动调用:每当使用
new
关键字创建类的实例时,相应的构造器会自动被调用。 - 初始化:构造器的主要任务是对新创建对象的成员变量进行初始化,可以设置默认值或者基于传入参数进行特定的初始化。
- 重载:构造器支持重载,意味着在同一个类中可以有多个构造器,只要它们的参数列表不同即可。这样可以根据不同的场景提供多种初始化方式。
- 默认构造器:如果一个类没有定义任何构造器,Java 编译器会自动为该类生成一个默认(无参数)构造器。
注意:一旦为类定义了一个或多个构造器,编译器就不会再自动生成默认构造器。
2. 无参 / 有参构造器
下面是一个简单的 Java 类及其无参 / 有参构造器的示例:
public class Student {
String name;
int age;
// 默认构造器
public Student() {
this.name = "匿名";
this.age = 0;
}
// 带参数的构造器
public Student(String name, int age) {
this.name = name;
this.age = age;
}
// 其他方法...
}
在这个例子中,Student
类有两个构造器:
- 一个是无参数的默认构造器,用于创建一个名字默认为"匿名"、年龄默认为 0 的学生对象;
- 另一个构造器接受两个参数,允许在创建对象时指定学生的姓名和年龄。
3. 构造器的作用
使用 new
关键字,本质上是在调用构造器,构造器一般用于初始化值。可分为无参构造和有参构造。
无参构造
一个类即使什么都不写,它也会有一个默认构造方法,即无参数构造器。
例如,Person
类中的构造默认构造器 Person()
。当 Person
类中没有写构造方法时,Person()
默认存在。
public Person() {
}
当然,我们可以显式地定义构造器,使其具备实例化初始值的功能。
如图所示,在默认构造器中为 name 属性赋初值。
public Person() {
this.name = "yuanyuan";
}
当使用 new 关键字实例化 person
对象时,它会调用默认构造器,并为 name 属性赋初值 yuanyuan
,所以 person.name
的值为 yuanyuan
。
有参构造
一旦定义了有参构造,Java 就不会默认生成无参构造器。因此,此时无参构造需要显式定义。
生成一个带参构造器:(快捷键 alt+insert
,或者右键 Generate
)
// 使用 alt+insert 快速生成构造器
public Person(String name) {
this.name = name;
}
使用 new
关键字实例化对象的同时,传递值:
Person person1 = new Person("zhangsan");
它会调用带参构造器,并为 name 属性赋初值 zhangsan
,所以 person1.name
的值为 zhangsan
。
四、内存分析
1. 以 Pet 类为例
准备一个 Pet
类:
public class Pet {
String name;
int age;
public void shout() {
System.out.println("叫了一声");
}
}
2. 内存分析
在 Java 中,创建对象的过程涉及内存管理的几个关键区域,主要包括栈内存、堆内存和方法区。
第一步
在代码中声明一个对象变量时,比如 Pet pet;
,变量 pet
会被分配在栈内存中。此时,变量 pet
仅是一个引用(可以理解为一个地址),并没有指向实际的对象。
第二步
使用 new
关键字创建对象,如 pet = new Pet();
时,Java 在堆内存中分配一块足够大的内存空间来存放 Pet
对象的所有实例变量。
这些实例变量包括基本类型,以及引用类型。
创建后,这些实例变量被默认初始化,基本类型的默认值(如 int=0, boolean=false
等),引用类型为 null
值。
同时,相应的构造器会被执行,用于初始化这些实例变量(如果有的话)。构造器可以设置初始值,执行一些初始化逻辑等。
第三步
完成对象创建和初始化后,堆内存中对象的地址会被复制给栈内存中的变量 pet
。
此时,pet
变量就指向了堆内存中的实际对象。
第四步
可以使用对象中的属性和方法。比如,分别给 Pet
类的 dog
对象和 cat
对象的属性 name
赋值。
Pet dog = new Pet();
Pet cat = new Pet();
赋值方法:
dog.name="旺财";
cat.name="喵喵";
接着分别调用 shout()
方法:
dog.shout();
cat.shout();
其他
类的元数据(如类名、父类、实现的接口、静态变量、静态方法等)存储在方法区或元数据区。
当类第一次被加载时,其相关信息就会被放入此区域。构造器的信息也位于此区域。
通过这个过程,Java 确保了对象的创建是安全且高效的,同时也支持了垃圾回收机制,自动管理不再使用的对象所占用的内存空间。
此处附上一张创建对象内存分析的配图,来源于《狂神说 Java》配图。
主要是为了说明:为什么不同的对象调用同一个类的同一个属性,比如 name
,dog.name
和 cat.name
是不一样的。
五、总结
本文内容主要关于类与对象的创建、初始化和使用,以及如何通过 new 关键字调用构造方法。此外,文章还对无参 / 有参这两种构造器进行描述,并通过内存分析创建对象的过程。
一些参考资料
狂神说 Java 零基础:https://www.bilibili.com/video/BV12J41137hu/
TIOBE 编程语言走势: https://www.tiobe.com/tiobe-index/
Typora 官网:https://www.typoraio.cn/
Oracle 官网:https://www.oracle.com/
Notepad++ 下载地址:https://notepad-plus.en.softonic.com/
IDEA 官网:https://www.jetbrains.com.cn/idea/
Java 开发手册:https://developer.aliyun.com/ebook/394
Java 8 帮助文档:https://docs.oracle.com/javase/8/docs/api/