Java 类和对象
面向对象编程(OOP)是现代软件开发的基础,而理解“类(Classes)”和“对象(Objects)”是掌握 Java 中 OOP 原则的基础。
本章将介绍类和对象的核心概念,解释它们如何构成面向对象程序的构建块。我们将探索如何定义类、如何从这些类创建对象,以及理解它们之间的关系。这些知识将为后续章节中更高级的 OOP 概念奠定基础。
1. 定义类
类是创建对象的蓝图或模板。它定义了该类对象将具备的特征(属性)和行为(方法)。你可以把类想象成一个饼干模具,而对象就是一块块饼干。模具定义了形状,而每一块饼干都是这个形状的一个实例(Instance)。
1.1 类的语法
在 Java 中,定义一个类需要使用 class 关键字,紧跟类名,然后是一对包含类主体的花括号 {}。
class Dog {
// 属性(字段)
String breed; // 品种
String name; // 名字
int age; // 年龄
// 方法(行为)
void bark() {
System.out.println("汪!");
}
void displayDetails() {
System.out.println("品种: " + breed + ", 名字: " + name + ", 年龄: " + age);
}
}class Dog: 声明了一个名为Dog的类。在 Java 中,类名通常以大写字母开头。String breed;,String name;,int age;: 这些是Dog类的属性(或字段)。它们定义了每个Dog对象将持有的数据。void bark() { ... },void displayDetails() { ... }: 这些是Dog类的方法。它们定义了Dog对象可以执行的动作。
2. 属性和方法
2.1 属性(字段)
属性(也称为字段或实例变量)代表与对象关联的数据。它们定义了对象的状态。一个类的每个对象都有自己独立的一份属性副本。
示例:在 Dog 类中,breed(品种)、name(名字)和 age(年龄)就是属性。每一个 Dog 对象都会有属于自己的品种、名字和年龄。
2.2 方法(行为)
方法定义了对象可以执行的操作或行为。它们可以操作对象的数据(属性),也可以与其他对象进行交互。
示例:在 Dog 类中,bark()(吠叫)和 displayDetails()(显示详情)就是方法。一个 Dog 对象可以执行吠叫动作,也可以展示关于它自己的详细信息。
3. 创建对象
对象是类的一个实例。它是存在于内存中的具体实体,拥有自己的状态(数据)和行为(方法)。创建对象的过程也称为“实例化(Instantiation)”。
3.1 对象创建语法
在 Java 中,使用 new 关键字来创建对象,后跟类名和一对圆括号 ()。这会调用该类的构造方法(稍后会详细介绍构造方法)。
Dog myDog = new Dog();Dog myDog: 声明了一个名为myDog的变量,其类型为Dog。这个变量将持有一个对Dog对象的引用。new Dog(): 在内存中创建一个新的Dog对象。new关键字负责为对象分配内存,而Dog()则调用了Dog类的构造方法。=: 将新创建的Dog对象的引用赋值给myDog变量。
3.2 访问属性和方法
对象创建完成后,你可以使用点运算符(.)来访问它的属性和方法。
myDog.breed = "金毛寻回犬";
myDog.name = "巴迪";
myDog.age = 3;
myDog.bark(); // 输出: 汪!
myDog.displayDetails(); // 输出: 品种: 金毛寻回犬, 名字: 巴迪, 年龄: 3myDog.breed = "金毛寻回犬";: 将myDog对象的breed属性设置为 "金毛寻回犬"。myDog.bark();: 调用myDog对象的bark()方法,该方法会在控制台打印出 "汪!"。
4. 深入理解属性与方法
让我们通过更多示例来深入探讨属性和方法。
4.1 属性与数据封装
理想情况下,属性应该是被封装的,这意味着应该限制从类外部直接访问它们。这可以通过使用访问修饰符来实现,如 private(私有)、protected(受保护)和 public(公开)。目前,我们先关注 public 和 private。
public: 声明为public的属性可以从任何地方访问。private: 声明为private的属性只能在同一个类的内部访问。
class Car {
public String model; // 公开属性
private int speed; // 私有属性
// 公开方法:设置速度
public void setSpeed(int speed) {
if (speed >= 0 && speed <= 200) { // 示例验证逻辑
this.speed = speed;
} else {
System.out.println("无效的速度值。");
}
}
// 公开方法:获取速度
public int getSpeed() {
return speed;
}
}
public class Main {
public static void main(String[] args) {
Car myCar = new Car();
myCar.model = "特斯拉 Model 3"; // 允许:访问 public 属性
// myCar.speed = 100; // 错误:如果 speed 是 public 则可以,但它是 private,所以我们不能直接访问
myCar.setSpeed(100); // 允许:通过 public 方法访问 speed
System.out.println("汽车速度: " + myCar.getSpeed());
}
}在这个例子中:
model是public,因此可以直接在Main类中访问。speed是private,因此只能通过setSpeed()和getSpeed()方法来访问。这就是封装的一个实例,它保护了数据,并允许你控制数据被访问和修改的方式。setSpeed()方法还演示了数据验证,确保速度在一个合理的范围内。
4.2 方法与行为实现
方法包含了定义对象行为的逻辑。它们可以接收参数(输入)并返回数值(输出)。
class Rectangle {
int width;
int height;
// 计算面积的方法,返回一个整数
int calculateArea() {
return width * height;
}
// 设置尺寸的方法,接收两个参数
void setDimensions(int width, int height) {
this.width = width;
this.height = height;
}
}
public class Main {
public static void main(String[] args) {
Rectangle myRectangle = new Rectangle();
myRectangle.setDimensions(5, 10);
int area = myRectangle.calculateArea();
System.out.println("矩形面积: " + area); // 输出: 矩形面积: 50
}
}在这个例子中:
calculateArea()是一个计算并返回矩形面积的方法。setDimensions()是一个设置矩形width和height的方法。它接收两个整数类型的参数。
5. 构造方法
构造方法(Constructor)是一种特殊的方法,当类的对象被创建时会自动调用。它通常用于初始化对象的属性。构造方法的名称必须与类名完全相同,并且没有返回类型(连 void 都不写)。
5.1 默认构造方法
如果你没有在类中定义任何构造方法,Java 会自动提供一个默认构造方法。默认构造方法不接收任何参数,并且会将所有属性初始化为它们的默认值(例如:int 类型为 0,String 类型为 null)。
5.2 参数化构造方法
你也可以自定义带有参数的构造方法,以便在创建对象时使用特定的值来初始化对象的属性。
class Person {
String name;
int age;
// 构造方法
public Person(String name, int age) {
this.name = name;
this.age = age;
}
void displayDetails() {
System.out.println("姓名: " + name + ", 年龄: " + age);
}
}
public class Main {
public static void main(String[] args) {
Person john = new Person("张三", 30); // 使用参数化构造方法
john.displayDetails(); // 输出: 姓名: 张三, 年龄: 30
}
}在这个例子中:
Person(String name, int age)是一个接收两个参数(name和age)的构造方法。this.name = name;和this.age = age;使用传递给构造方法的参数值来初始化对象的name和age属性。
5.3 构造方法重载
一个类可以有多个参数列表不同的构造方法。这被称为构造方法重载(Constructor Overloading)。编译器会根据创建对象时传入的参数来决定调用哪一个构造方法。
class Book {
String title;
String author;
String isbn;
// 构造方法 1: 仅包含书名和作者
public Book(String title, String author) {
this.title = title;
this.author = author;
this.isbn = "未知"; // 赋予默认值
}
// 构造方法 2: 包含书名、作者和 ISBN
public Book(String title, String author, String isbn) {
this.title = title;
this.author = author;
this.isbn = isbn;
}
void displayDetails() {
System.out.println("书名: " + title + ", 作者: " + author + ", ISBN: " + isbn);
}
}
public class Main {
public static void main(String[] args) {
Book book1 = new Book("指环王", "J.R.R. 托尔金");
Book book2 = new Book("傲慢与偏见", "简·奥斯汀", "978-0141439518");
book1.displayDetails(); // 输出: 书名: 指环王, 作者: J.R.R. 托尔金, ISBN: 未知
book2.displayDetails(); // 输出: 书名: 傲慢与偏见, 作者: 简·奥斯汀, ISBN: 978-0141439518
}
}在这个例子中:
Book类有两个构造方法:一个接收书名和作者,另一个接收书名、作者和 ISBN 号。Main类创建了两个Book对象,分别使用了不同的构造方法。
6. this 关键字
this 关键字是对当前对象的引用。它通常用于以下场景:
- 当方法或构造方法中的参数名称与类属性名称相同时,用来访问对象的属性(以解决命名冲突)。
- 在同一个类中,从一个构造方法调用另一个构造方法(这在更高级的 OOP 主题中会详细介绍)。
- 从方法中返回当前对象(在基础场景中较少见)。
class Employee {
String name;
int id;
public Employee(String name, int id) {
this.name = name; // 'this.name' 指向类的属性,'name' 指向方法参数
this.id = id; // 'this.id' 指向类的属性,'id' 指向方法参数
}
void displayDetails() {
System.out.println("姓名: " + this.name + ", ID: " + this.id); // 这里使用 'this' 是可选的,但属于好习惯
}
}
public class Main {
public static void main(String[] args) {
Employee employee1 = new Employee("艾丽斯·史密斯", 12345);
employee1.displayDetails(); // 输出: 姓名: 艾丽斯·史密斯, ID: 12345
}
}在这个例子中:
this.name = name;严格区分了Employee类的name属性和构造方法的name参数。如果没有this关键字,编译器就无法确定你到底指的是哪一个name。