Java 零基础教程

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(); // 输出: 品种: 金毛寻回犬, 名字: 巴迪, 年龄: 3
  • myDog.breed = "金毛寻回犬";: 将 myDog 对象的 breed 属性设置为 "金毛寻回犬"。
  • myDog.bark();: 调用 myDog 对象的 bark() 方法,该方法会在控制台打印出 "汪!"。

4. 深入理解属性与方法

让我们通过更多示例来深入探讨属性和方法。

4.1 属性与数据封装

理想情况下,属性应该是被封装的,这意味着应该限制从类外部直接访问它们。这可以通过使用访问修饰符来实现,如 private(私有)、protected(受保护)和 public(公开)。目前,我们先关注 publicprivate

  • 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());
    }
}

在这个例子中:

  • modelpublic,因此可以直接在 Main 类中访问。
  • speedprivate,因此只能通过 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() 是一个设置矩形 widthheight 的方法。它接收两个整数类型的参数。

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) 是一个接收两个参数(nameage)的构造方法。
  • this.name = name;this.age = age; 使用传递给构造方法的参数值来初始化对象的 nameage 属性。

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 关键字是对当前对象的引用。它通常用于以下场景:

  1. 当方法或构造方法中的参数名称与类属性名称相同时,用来访问对象的属性(以解决命名冲突)。
  2. 在同一个类中,从一个构造方法调用另一个构造方法(这在更高级的 OOP 主题中会详细介绍)。
  3. 从方法中返回当前对象(在基础场景中较少见)。
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