MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

Java类的定义与实例化

2022-01-293.5k 阅读

Java 类的定义

类的概念

在 Java 中,类(Class)是一种抽象的数据类型,它是对现实世界中具有相同属性和行为的事物的一种抽象描述。类就像是一个模板或者蓝图,用于创建对象(Object)。例如,我们可以定义一个“汽车”类,这个类可以包含汽车的属性,如颜色、品牌、型号等,以及汽车的行为,如启动、加速、刹车等。通过这个“汽车”类,我们可以创建出具体的汽车对象,每个对象都具有类所定义的属性和行为,但具体的属性值可能不同。

类定义的基本语法

在 Java 中,定义一个类的基本语法如下:

[访问修饰符] class 类名 {
    // 成员变量(属性)
    [变量修饰符] 数据类型 变量名;
    // 成员方法(行为)
    [方法修饰符] 返回值类型 方法名([参数列表]) {
        // 方法体
    }
}
  • 访问修饰符:用于控制类、成员变量和成员方法的访问权限。常见的访问修饰符有 publicprivateprotected 和默认(不写修饰符)。public 表示公共的,任何其他类都可以访问;private 表示私有的,只能在本类内部访问;protected 表示受保护的,在本类、同包的类以及子类中可以访问;默认访问修饰符表示在同包内可以访问。
  • class:是定义类的关键字。
  • 类名:遵循标识符命名规则,通常采用大写字母开头的驼峰命名法,如 CarPerson 等。类名应该能够清晰地反映类所代表的事物。
  • 成员变量:也称为属性,用于描述类的特征。变量修饰符可以是 publicprivateprotectedstaticfinal 等。数据类型可以是 Java 中的基本数据类型(如 intdoubleboolean 等),也可以是引用数据类型(如 String、自定义类等)。
  • 成员方法:用于描述类的行为。方法修饰符与变量修饰符类似,返回值类型表示方法执行后返回的数据类型,如果方法不需要返回值,则使用 void。参数列表用于接收调用方法时传递进来的数据。

成员变量

  1. 成员变量的分类
    • 实例变量:定义在类中,但在方法之外,并且没有 static 修饰。每个对象都有自己独立的实例变量副本,它们的值可以在不同对象间有所不同。例如:
public class Dog {
    // 实例变量
    private String name;
    private int age;

    public Dog(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

在上述代码中,nameageDog 类的实例变量,每创建一个 Dog 对象,都会有各自独立的 nameage 变量。 - 类变量(静态变量):使用 static 修饰的成员变量。类变量属于类本身,而不是属于某个具体的对象。无论创建多少个对象,类变量只有一份副本,所有对象共享该变量。例如:

public class Company {
    // 类变量
    private static String companyName = "ABC 公司";

    public Company() {
    }
}

在上述代码中,companyNameCompany 类的类变量,所有 Company 对象共享这个变量。

  1. 成员变量的作用域 成员变量的作用域是整个类,在类的任何方法中都可以访问。例如:
public class Circle {
    private double radius;

    public double calculateArea() {
        return Math.PI * radius * radius;
    }
}

Circle 类中,radius 是成员变量,calculateArea 方法可以直接访问它。

成员方法

  1. 成员方法的分类
    • 实例方法:没有 static 修饰的方法,实例方法必须通过对象来调用。实例方法可以访问实例变量和类变量,也可以调用其他实例方法和类方法。例如:
public class Person {
    private String name;

    public Person(String name) {
        this.name = name;
    }

    // 实例方法
    public void sayHello() {
        System.out.println("Hello, I'm " + name);
    }
}

在上述代码中,sayHello 是实例方法,需要通过 Person 对象来调用。 - 类方法(静态方法):使用 static 修饰的方法,类方法可以直接通过类名来调用,不需要创建对象。类方法只能访问类变量和其他类方法,不能直接访问实例变量和实例方法。例如:

public class MathUtils {
    // 类方法
    public static int add(int a, int b) {
        return a + b;
    }
}

在上述代码中,add 是类方法,可以通过 MathUtils.add(3, 5) 这样的方式直接调用。

  1. 方法的重载(Overloading) 在同一个类中,可以定义多个方法名相同但参数列表不同的方法,这就是方法的重载。方法的重载可以使程序更加灵活,方便用户根据不同的参数类型和数量来调用合适的方法。例如:
public class Calculator {
    public int add(int a, int b) {
        return a + b;
    }

    public double add(double a, double b) {
        return a + b;
    }

    public int add(int a, int b, int c) {
        return a + b + c;
    }
}

Calculator 类中,定义了三个 add 方法,它们的参数列表不同,构成了方法的重载。

  1. 方法的重写(Override) 当子类继承父类时,可以对父类中的非 final 方法进行重新实现,这就是方法的重写。重写的方法必须与被重写方法具有相同的方法名、参数列表和返回值类型(返回值类型可以是被重写方法返回值类型的子类)。同时,重写方法不能比被重写方法具有更严格的访问权限。例如:
class Animal {
    public void makeSound() {
        System.out.println("Animal makes a sound");
    }
}

class Dog extends Animal {
    @Override
    public void makeSound() {
        System.out.println("Dog barks");
    }
}

在上述代码中,Dog 类继承了 Animal 类,并重写了 makeSound 方法。

Java 类的实例化

实例化的概念

类的实例化是指根据类创建对象的过程。类只是一个抽象的模板,而对象是类的具体实例,具有类所定义的属性和行为。通过实例化,我们可以在程序中使用具体的对象来执行相应的操作。例如,根据“汽车”类创建出一辆具体的汽车对象,这辆汽车就具有“汽车”类所定义的颜色、品牌等属性,并且可以执行启动、加速等行为。

实例化的方式

  1. 使用 new 关键字 这是最常见的实例化对象的方式。通过 new 关键字调用类的构造方法来创建对象。构造方法是一个特殊的方法,它的方法名与类名相同,没有返回值类型(包括 void)。例如:
public class Cat {
    private String name;

    // 构造方法
    public Cat(String name) {
        this.name = name;
    }
}

public class Main {
    public static void main(String[] args) {
        // 使用 new 关键字实例化 Cat 对象
        Cat cat = new Cat("Tom");
    }
}

在上述代码中,通过 new Cat("Tom") 创建了一个 Cat 对象,并将其赋值给 cat 变量。在创建对象时,会调用 Cat 类的构造方法,将参数 "Tom" 传递给构造方法,从而初始化 name 属性。

  1. 使用 Class 类的 newInstance() 方法 这种方式可以在运行时根据类的名称来实例化对象。它适用于在编译时不知道具体要实例化哪个类的情况,例如在反射机制中经常使用。newInstance() 方法会调用类的无参构造方法。例如:
public class Bird {
    public Bird() {
        System.out.println("Bird is created");
    }
}

public class Main {
    public static void main(String[] args) throws Exception {
        // 使用 Class 类的 newInstance() 方法实例化 Bird 对象
        Class<?> birdClass = Class.forName("Bird");
        Bird bird = (Bird) birdClass.newInstance();
    }
}

在上述代码中,首先通过 Class.forName("Bird") 获取 Bird 类的 Class 对象,然后调用 newInstance() 方法创建 Bird 对象。需要注意的是,newInstance() 方法可能会抛出 InstantiationExceptionIllegalAccessException 异常,所以需要进行异常处理。

  1. 使用 Constructor 类的 newInstance() 方法 这种方式与 Class 类的 newInstance() 方法类似,但可以调用类的指定构造方法(包括带参数的构造方法)。例如:
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class Fish {
    private String name;

    public Fish(String name) {
        this.name = name;
    }
}

public class Main {
    public static void main(String[] args) {
        try {
            // 获取 Fish 类的 Class 对象
            Class<?> fishClass = Class.forName("Fish");
            // 获取带参数的构造方法
            Constructor<?> constructor = fishClass.getConstructor(String.class);
            // 使用 Constructor 的 newInstance() 方法实例化 Fish 对象
            Fish fish = (Fish) constructor.newInstance("Nemo");
        } catch (ClassNotFoundException | NoSuchMethodException |
                 InstantiationException | IllegalAccessException |
                 InvocationTargetException e) {
            e.printStackTrace();
        }
    }
}

在上述代码中,首先通过 Class.forName("Fish") 获取 Fish 类的 Class 对象,然后通过 getConstructor(String.class) 获取带 String 类型参数的构造方法,最后使用 constructor.newInstance("Nemo") 来实例化 Fish 对象。同样,这里也需要处理可能抛出的异常。

构造方法

  1. 构造方法的作用 构造方法用于在对象创建时对对象进行初始化。它可以为对象的成员变量赋初始值,确保对象在创建后处于一个合理的初始状态。例如,在创建一个 Person 对象时,可以通过构造方法为 nameage 等属性赋值。
  2. 构造方法的特点
    • 构造方法的方法名必须与类名完全相同。
    • 构造方法没有返回值类型,甚至不能是 void
    • 一个类可以有多个构造方法,它们构成构造方法的重载,通过不同的参数列表来满足不同的初始化需求。例如:
public class Book {
    private String title;
    private String author;

    // 无参构造方法
    public Book() {
        title = "Unknown Title";
        author = "Unknown Author";
    }

    // 带参数的构造方法
    public Book(String title, String author) {
        this.title = title;
        this.author = author;
    }
}

在上述 Book 类中,定义了一个无参构造方法和一个带两个参数的构造方法,分别用于不同的初始化场景。

  1. 默认构造方法 如果一个类中没有显式地定义构造方法,Java 编译器会自动为该类提供一个默认的无参构造方法。这个默认构造方法的方法体为空,它的作用是创建对象时对对象进行默认的初始化(例如将基本数据类型的成员变量初始化为默认值,将引用数据类型的成员变量初始化为 null)。例如:
public class Student {
    private int id;
    private String name;
}

在上述 Student 类中,虽然没有显式定义构造方法,但编译器会自动提供一个默认的无参构造方法 Student()。然而,如果类中已经显式定义了构造方法,编译器将不再提供默认构造方法。例如:

public class Employee {
    private String name;

    // 显式定义了带参数的构造方法
    public Employee(String name) {
        this.name = name;
    }
}

在上述 Employee 类中,由于显式定义了带参数的构造方法,所以如果需要使用无参构造方法,就必须自己显式定义。

对象的内存分配

当使用 new 关键字实例化一个对象时,Java 虚拟机(JVM)会在堆内存中为该对象分配内存空间。对象的成员变量存储在堆内存中,而对象的引用(即指向对象的变量)存储在栈内存中。例如:

public class Point {
    private int x;
    private int y;

    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }
}

public class Main {
    public static void main(String[] args) {
        Point point = new Point(3, 5);
    }
}

在上述代码中,当执行 Point point = new Point(3, 5) 时,JVM 会在堆内存中为 Point 对象分配空间,用于存储 xy 成员变量的值(分别为 3 和 5)。同时,在栈内存中创建一个 point 变量,它存储了指向堆内存中 Point 对象的地址。这样,通过 point 引用就可以访问堆内存中的 Point 对象及其成员变量。

对象的生命周期

  1. 对象的创建 通过前面介绍的实例化方式,如使用 new 关键字、Class 类的 newInstance() 方法等,在堆内存中为对象分配空间,并调用构造方法对对象进行初始化,标志着对象的创建完成。
  2. 对象的使用 在对象创建后,可以通过对象的引用调用对象的成员方法,访问和修改对象的成员变量,从而实现对象的各种功能。例如:
public class Rectangle {
    private int width;
    private int height;

    public Rectangle(int width, int height) {
        this.width = width;
        this.height = height;
    }

    public int calculateArea() {
        return width * height;
    }
}

public class Main {
    public static void main(String[] args) {
        Rectangle rectangle = new Rectangle(5, 3);
        int area = rectangle.calculateArea();
        System.out.println("The area of the rectangle is: " + area);
    }
}

在上述代码中,创建 Rectangle 对象后,通过 rectangle.calculateArea() 调用对象的方法来计算矩形的面积。 3. 对象的销毁 当对象不再被任何引用所指向时,该对象就成为了垃圾对象。Java 具有自动垃圾回收机制(Garbage Collection,GC),垃圾回收器会在适当的时候回收这些垃圾对象所占用的内存空间。垃圾回收器会定期检查堆内存中的对象,标记那些不再被引用的对象,并在合适的时机释放它们所占用的内存。需要注意的是,程序员无法精确控制垃圾回收的时机,只能通过将对象的引用设置为 null 等方式,使对象符合垃圾回收的条件。例如:

public class MyObject {
    // 类的定义
}

public class Main {
    public static void main(String[] args) {
        MyObject obj = new MyObject();
        // 使用 obj
        obj = null; // 将 obj 引用设置为 null,使 MyObject 对象符合垃圾回收条件
    }
}

在上述代码中,将 obj 设置为 null 后,MyObject 对象就不再被任何引用指向,垃圾回收器可能会在某个时候回收该对象所占用的内存。

综上所述,Java 类的定义与实例化是 Java 编程的基础,深入理解它们对于编写高效、健壮的 Java 程序至关重要。通过合理地定义类的成员变量和方法,以及正确地进行类的实例化和对象的使用与管理,可以实现复杂的业务逻辑和功能。同时,了解对象的内存分配和生命周期,有助于优化程序的性能和资源利用。在实际开发中,应根据具体的需求和场景,灵活运用这些知识,创建出高质量的 Java 应用程序。