ZetCode

Java 类

最后修改于 2024 年 1 月 27 日

在本文中,我们将展示如何在 Java 中使用类。

Java 常规类

class 关键字用于定义类,类是创建对象的模板。这些对象被称为类的实例。 使用 new 关键字创建一个新类。

在类中,我们定义成员字段和成员函数。类中定义的函数称为方法。成员字段和函数通过点运算符访问。

com/zetcode/RegularClassEx.java
package com.zetcode;

import java.util.Objects;

class User {

    private String name;
    private String occupation;

    public User(String name, String occupation) {
        this.name = name;
        this.occupation = occupation;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getOccupation() {
        return occupation;
    }

    public void setOccupation(String occupation) {
        this.occupation = occupation;
    }

    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder("User{");
        sb.append("name='").append(name).append('\'');
        sb.append(", occupation='").append(occupation).append('\'');
        sb.append('}');
        return sb.toString();
    }
}

public class RegularClassEx {

    public static void main(String[] args) {

        var u = new User("John Doe", "gardener");
        System.out.println(u);

        System.out.println(u.getName());
        System.out.println(u.getOccupation());

    }
}

在该程序中,我们定义了 User 类。 该类有两个字段:nameoccupation。 在该类中,我们还定义了用于获取和设置字段的 getter 和 setter 方法,以及用于表示类字符串形式的 toString 方法。

class User {
...
}

class 关键字用于定义类。在花括号对内部,我们定义类的正文。

private String name;
private String occupation;

定义了两个 String 字段。

public User(String name, String occupation) {
    this.name = name;
    this.occupation = occupation;
}

这是类的构造函数;它是一种特殊的方法,与类具有相同的名称。创建类的实例时会调用它。在我们的例子中,我们在构造函数中初始化我们的字段。

public String getName() {
    return name;
}

public void setName(String name) {
    this.name = name;
}

public String getOccupation() {
    return occupation;
}

public void setOccupation(String occupation) {
    this.occupation = occupation;
}

对于访问私有字段,我们定义了两个 getter 和 setter 方法。

@Override
public String toString() {
    final StringBuilder sb = new StringBuilder("User{");
    sb.append("name='").append(name).append('\'');
    sb.append(", occupation='").append(occupation).append('\'');
    sb.append('}');
    return sb.toString();
}

要获取对象的字符串表示形式,我们定义了 toString 方法。 当我们将对象传递给 System.out.println 方法时,将调用该方法。

var u = new User("John Doe", "gardener");

使用 new 关键字创建一个新的 User 类实例。 此时,将调用该类的构造函数。 我们将两个字符串参数传递给构造函数。

System.out.println(u);

调用该类的 toString 方法。

System.out.println(u.getName());
System.out.println(u.getOccupation());

在 user 对象上,我们调用 getNamegetOccupation 方法。 使用点运算符调用这些方法。

$ java com.zetcode.RegularClassEx
User{name='John Doe', occupation='gardener'}
John Doe
gardener

Java 抽象类

抽象类是一个未完成的类。 它必须在其子类中实现。 使用 abstract 关键字创建抽象类。 我们可以创建抽象方法和成员字段。

抽象类的目的是为派生类提供一个通用的定义。

抽象类无法实例化。 如果一个类包含至少一个抽象方法,它也必须声明为抽象的。 抽象方法无法实现;它们仅声明方法的签名。

com/zetcode/AbstractClassEx.java
package com.zetcode;

abstract class Drawing {

    protected int x = 0;
    protected int y = 0;

    public abstract double area();

    public String getCoordinates() {

        return String.format("x: %d, y: %d", this.x, this.y);
    }
}

class Circle extends Drawing {

    private int r;

    public Circle(int x, int y, int r) {

        this.x = x;
        this.y = y;
        this.r = r;
    }

    @Override
    public double area() {

        return this.r * this.r * Math.PI;
    }

    @Override
    public String toString() {

        return String.format("Circle at x: %d, y: %d, radius: %d",
                this.x, this.y, this.r);
    }
}

public class AbstractClassEx {

    public static void main(String[] args) {

        Circle c = new Circle(12, 45, 22);

        System.out.println(c);
        System.out.format("Area of circle: %f%n", c.area());
        System.out.println(c.getCoordinates());
    }
}

我们有一个抽象的基类 Drawing。 该类定义了两个成员字段,定义了一个方法并声明了一个方法。 其中一个方法是抽象的,另一个方法是完全实现的。 Drawing 类是抽象的,因为我们无法绘制它。 我们可以绘制一个圆圈、一个点或一个正方形,但我们无法绘制一个“绘图”。 Drawing 类对于我们可以绘制的对象具有一些常见的功能。

abstract class Drawing {

我们使用 abstract 关键字来定义一个抽象类。

public abstract double area();

抽象方法也以 abstract 关键字开头。 Drawing 类是一个想法。 它是虚幻的,我们无法为其实现 area 方法。 这是我们使用抽象方法的情况。 该方法将在更具体的实体(如圆圈)中实现。

class Circle extends Drawing {

CircleDrawing 类的子类。 因此,它必须实现抽象的 area 方法。

@Override
public double area() {

    return this.r * this.r * Math.PI;
}

在这里,我们正在实现 area 方法。

$ java com.zetcode.AbstractClass
Circle at x: 12, y: 45, radius: 22
Area of circle: 1520.530844
x: 12, y: 45

我们创建一个 Circle 对象并打印其面积和坐标。

Java 嵌套类

可以在另一个类中定义一个类。 在 Java 术语中,这样的类称为嵌套类。 不是嵌套类的类称为顶级类。

Java 有四种类型的嵌套类

使用嵌套类可以提高代码的可读性并改善代码的组织。 内部类通常用作 GUI 中的回调。 例如,在 Java Swing 工具包中。

Java 静态嵌套类

静态嵌套类是一个嵌套类,可以在不使用封闭类实例的情况下创建。 它可以访问封闭类的静态变量和方法。

com/zetcode/SNCTest.java
package com.zetcode;

public class SNCTest {

    private static int x = 5;

    static class Nested {

        @Override
        public String toString() {
            return "This is a static nested class; x:" + x;
        }
    }

    public static void main(String[] args) {

        SNCTest.Nested sn = new SNCTest.Nested();
        System.out.println(sn);
    }
}

该示例展示了一个静态嵌套类。

private static int x = 5;

这是 SNCTest 类的私有静态变量。 静态嵌套类可以访问它。

static class Nested {

    @Override
    public String toString() {
        return "This is a static nested class; x:" + x;
    }
}

定义了一个静态嵌套类。 它有一种方法,用于打印一条消息并引用静态变量 x

SNCTest.Nested sn = new SNCTest.Nested();

点运算符用于引用嵌套类。

$ java com.zetcode.SNCTest
This is a static nested class; x:5

Java 内部类

普通类或顶级类的实例可以独立存在。 相比之下,内部类的实例无法在不绑定到顶级类的情况下进行实例化。 内部类也称为成员类。 它们属于封闭类的实例。 内部类可以访问封闭类的成员。

com/zetcode/InnerClassTest.java
package com.zetcode;

public class InnerClassTest {

    private int x = 5;

    class Inner {

        @Override
        public String toString() {
            return "This is Inner class; x:" + x;
        }
    }

    public static void main(String[] args) {

        InnerClassTest nc = new InnerClassTest();
        InnerClassTest.Inner inner = nc.new Inner();

        System.out.println(inner);
    }
}

嵌套类在 InnerClassTest 类中定义。 它可以访问成员变量 x

class Inner {

    @Override
    public String toString() {
        return "This is Inner class; x:" + x;
    }
}

Inner 类在 InnerClassTest 类的正文中定义。

InnerClassTest nc = new InnerClassTest();

首先,我们需要创建顶级类的实例。 内部类不能在没有封闭类实例的情况下存在。

InnerClassTest.Inner inner = nc.new Inner();

实例化顶级类后,我们可以创建内部类的实例。

$ java com.zetcode.InnerClassTest
This is Inner class; x:5

这是 com.zetcode.InnerClassTest 程序的输出。

Java 局部类

局部类是内部类的一种特殊情况。 局部类是在块中定义的类。(块是花括号之间的一组零个或多个语句。)局部类可以访问其封闭类的成员。

此外,如果局部变量声明为 final,则局部类可以访问这些变量。 造成这种情况的原因是技术性的。 局部类的实例的生存期可能比定义该类的方法的执行时间长得多。 为了解决这个问题,局部变量被复制到局部类中。 为了确保以后不更改它们,必须将它们声明为 final

局部类不能是 publicprivateprotectedstatic。 不允许将它们用于局部变量声明或局部类声明。 除了声明为 staticfinal 的常量外,局部类不能包含静态字段、方法或类。

com/zetcode/LocalClassEx.java
package com.zetcode;

public class LocalClassEx {

    public static void main(String[] args) {

        final int x = 5;

        class Local {

            @Override
            public String toString() {
                return "This is Local class; x:" + x;
            }
        }

        Local loc = new Local();
        System.out.println(loc);
    }
}

局部类在 main 方法的正文中定义。

@Override
public String toString() {
    return "This is Local class; x:" + x;
}

如果将局部变量声明为 final,则局部类可以访问这些变量。

Java 匿名类

匿名类是没有名称的局部类。 它们使我们能够同时声明和实例化一个类。 如果我们只想使用一次该类,则可以使用匿名类。 匿名类在一个表达式中定义和实例化。 匿名内部类也用于事件处理代码仅由一个组件使用,因此不需要命名引用的情况。

匿名类必须实现接口或从类继承。 但是不使用 implementsextends 关键字。 如果 new 关键字后面的名称是类的名称,则匿名类是命名类的子类。 如果 new 后面的名称指定了一个接口,则匿名类实现该接口并扩展 Object

由于匿名类没有名称,因此无法为匿名类定义构造函数。 在匿名类的正文中,我们无法定义任何语句;只能定义方法或成员。

com/zetcode/AnonymousClass.java
package com.zetcode;

public class AnonymousClass {

   interface Message {
        public void send();
    }

    public void createMessage() {

        Message msg = new Message() {

            @Override
            public void send() {
                System.out.println("This is a message");
            }
        };

        msg.send();
    }

    public static void main(String[] args) {

        AnonymousClass ac = new AnonymousClass();
        ac.createMessage();
    }
}

在此代码示例中,我们创建一个匿名类。

interface Message {
    public void send();
}

匿名类必须是子类或必须实现一个接口。 我们的匿名类将实现一个 Message 接口。 否则,编译器将无法识别该类型。

public void createMessage() {

    Message msg = new Message() {

        @Override
        public void send() {
            System.out.println("This is a message");
        }
    };

    msg.send();
}

匿名类是一个局部类,因此它在方法的正文中定义。 匿名类在表达式中定义;因此,封闭的右括号后跟一个分号。

来源

Java 类 - 教程

在本文中,我们讨论了 Java 类。

作者

我叫 Jan Bodnar,是一位充满激情的程序员,拥有丰富的编程经验。 自 2007 年以来,我一直在撰写编程文章。 迄今为止,我已经撰写了超过 1,400 篇文章和 8 本电子书。 我拥有超过十年的编程教学经验。

列出所有Java教程