Java对象和类

Java是一种纯粹的面向对象编程语言。面向对象编程(OOP)是现代软件开发的核心范式,它支持以下基本概念:

多态

同一操作作用于不同对象,可以产生不同的执行结果

继承

子类继承父类的属性和方法,实现代码复用

封装

隐藏对象的内部实现细节,只暴露必要的接口

抽象

提取对象的共同特征,忽略实现细节

本节重点研究对象和类的概念,这是面向对象编程的基础。

  • 对象(Object):对象是类的一个实例,具有状态(属性)和行为(方法)。例如,一条狗是一个对象,它的状态有:颜色、名字、品种;行为有:摇尾巴、叫、吃等。
  • 类(Class):类是一个模板或蓝图,它描述一类对象的行为和状态。类定义了对象的属性和方法。
现实世界 vs 软件世界

在现实世界中,每个具体的实体都是对象;在软件世界中,类是创建对象的模板,对象是类的具体实现。

Java中的类

类是Java程序的基本构建块,用于创建对象的模板。类定义了对象的属性和方法。

类的组成部分

一个典型的Java类包含以下部分:

1. 类声明

使用class关键字声明类,指定访问修饰符和类名

// 类声明的基本语法
[访问修饰符] class 类名 {
    // 类体
}
2. 成员变量

定义在类内部、方法外部的变量,表示对象的状态

// 成员变量示例
private String name;
private int age;
protected double salary;
3. 方法

定义在类中的函数,表示对象的行为

// 方法示例
public void displayInfo() {
    System.out.println("Name: " + name);
}

public int calculateAge() {
    return age;
}

类的定义示例

下面是一个完整的Dog类的定义:

/**
 * Dog类 - 表示狗的类
 * 这是一个简单的类定义示例
 */
public class Dog {

    // 成员变量(属性) - 表示狗的状态
    private String breed;     // 品种
    private int age;          // 年龄
    private String color;     // 颜色
    private String name;      // 名字
    private boolean isHungry; // 是否饥饿

    // 构造方法 - 用于创建对象时初始化
    public Dog(String name, String breed, int age, String color) {
        this.name = name;
        this.breed = breed;
        this.age = age;
        this.color = color;
        this.isHungry = true; // 默认新创建的狗是饥饿的
    }

    // 方法 - 表示狗的行为

    /**
     * 狗叫的方法
     */
    public void barking() {
        System.out.println(name + "正在汪汪叫!");
    }

    /**
     * 表示狗饿了
     */
    public void hungry() {
        if (isHungry) {
            System.out.println(name + "饿了,需要喂食!");
        } else {
            System.out.println(name + "不饿。");
        }
    }

    /**
     * 狗睡觉的方法
     */
    public void sleeping() {
        System.out.println(name + "正在睡觉...");
        isHungry = true; // 睡醒后可能会饿
    }

    /**
     * 喂狗的方法
     */
    public void feed() {
        isHungry = false;
        System.out.println("已经喂了" + name);
    }

    /**
     * 获取狗的详细信息
     * @return 狗的详细信息字符串
     */
    public String getDogInfo() {
        return "名字: " + name +
               ", 品种: " + breed +
               ", 年龄: " + age + "岁" +
               ", 颜色: " + color;
    }

    /**
     * 年龄增长方法
     */
    public void growOlder() {
        age++;
        System.out.println(name + "又长大了一岁,现在" + age + "岁了");
    }
}

类中的变量类型

在类中,可以定义三种类型的变量:

变量类型 描述 生命周期 示例
局部变量 在方法、构造方法或代码块中定义的变量 从声明处开始,到所在代码块结束 int result = 0;
成员变量
(实例变量)
在类中定义、方法外部的变量,属于对象实例 对象创建时创建,对象销毁时销毁 private String name;
类变量
(静态变量)
使用static关键字声明的变量,属于类本身 类加载时创建,程序结束时销毁 public static int count = 0;

Java中的对象

对象是类的实例,具有具体的状态和行为。对象在内存中占用空间,可以存储数据并执行操作。

对象的特征

  • 状态(State):对象属性的值,表示对象的特征
  • 行为(Behavior):对象可以执行的操作,通过方法实现
  • 标识(Identity):每个对象都有唯一的标识,通常通过引用变量实现
  • 封装(Encapsulation):对象隐藏内部实现,只暴露必要接口
  • 消息传递:对象通过互相发送消息来协作
  • 生命周期:对象从创建到销毁的整个过程

创建对象的步骤

在Java中创建对象需要以下三个步骤:

1. 声明对象

声明一个引用变量,指定对象类型和名称

// 声明对象
Dog myDog;
2. 实例化对象

使用new关键字创建对象实例

// 实例化对象
myDog = new Dog();
3. 初始化对象

调用构造方法初始化对象状态

// 通常声明、实例化、初始化合并
Dog myDog = new Dog("Buddy", "Golden Retriever", 3, "金色");

对象创建示例

使用之前定义的Dog类创建对象:

public class DogTest {
    public static void main(String[] args) {
        // 创建第一个Dog对象
        Dog dog1 = new Dog("Buddy", "Golden Retriever", 3, "金色");

        // 创建第二个Dog对象
        Dog dog2 = new Dog("Max", "German Shepherd", 5, "黑色");

        // 调用对象的方法
        System.out.println(dog1.getDogInfo());
        dog1.barking();
        dog1.hungry();
        dog1.feed();
        dog1.hungry();

        System.out.println("\n-------------------\n");

        System.out.println(dog2.getDogInfo());
        dog2.sleeping();
        dog2.growOlder();

        // 每个对象都有自己的状态
        System.out.println("\n两只狗是不同的对象:");
        System.out.println("dog1的名字: " + dog1.getDogInfo().split(",")[0]);
        System.out.println("dog2的名字: " + dog2.getDogInfo().split(",")[0]);
    }
}

构造方法

构造方法是一种特殊的方法,用于创建和初始化对象。构造方法名称必须与类名相同,且没有返回类型。

构造方法的特性

  • 构造方法名必须与类名完全相同
  • 构造方法没有返回类型,连void都没有
  • 构造方法可以重载(多个构造方法,参数不同)
  • 如果没有显式定义构造方法,编译器会提供一个默认的无参构造方法
  • 构造方法不能被staticfinalabstract修饰
  • 构造方法不能被子类继承,但子类可以调用父类的构造方法

构造方法类型

默认构造方法

没有参数的构造方法,如果没有定义任何构造方法,Java会自动生成

public class Student {
    // 如果没有定义构造方法,Java提供:
    // public Student() { }
}
有参构造方法

带参数的构造方法,用于初始化对象时传入初始值

public class Student {
    private String name;
    private int age;

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

构造方法重载示例

一个类可以有多个构造方法,这称为构造方法重载:

public class Puppy {
    private String name;
    private int age;
    private String breed;

    // 默认构造方法
    public Puppy() {
        this.name = "Unknown";
        this.age = 0;
        this.breed = "Mixed";
        System.out.println("创建了一个匿名小狗");
    }

    // 只有一个参数的构造方法
    public Puppy(String name) {
        this.name = name;
        this.age = 0;
        this.breed = "Mixed";
        System.out.println("创建了小狗: " + name);
    }

    // 有两个参数的构造方法
    public Puppy(String name, int age) {
        this.name = name;
        this.age = age;
        this.breed = "Mixed";
        System.out.println("创建了小狗: " + name + ", 年龄: " + age);
    }

    // 有三个参数的构造方法
    public Puppy(String name, int age, String breed) {
        this.name = name;
        this.age = age;
        this.breed = breed;
        System.out.println("创建了小狗: " + name + ", 品种: " + breed + ", 年龄: " + age);
    }

    // 获取小狗信息
    public String getInfo() {
        return "名字: " + name + ", 品种: " + breed + ", 年龄: " + age + "岁";
    }

    public static void main(String[] args) {
        // 使用不同的构造方法创建对象
        Puppy puppy1 = new Puppy();                    // 调用无参构造方法
        Puppy puppy2 = new Puppy("Tommy");            // 调用单参构造方法
        Puppy puppy3 = new Puppy("Max", 2);           // 调用双参构造方法
        Puppy puppy4 = new Puppy("Buddy", 3, "Golden Retriever"); // 调用三参构造方法

        System.out.println("\n小狗信息:");
        System.out.println(puppy1.getInfo());
        System.out.println(puppy2.getInfo());
        System.out.println(puppy3.getInfo());
        System.out.println(puppy4.getInfo());
    }
}

访问实例变量和方法

创建对象后,可以通过对象引用访问其成员变量和方法。

访问语法

访问成员变量
// 语法:对象引用.变量名
objectReference.variableName;

// 示例
Dog myDog = new Dog("Buddy", "Golden Retriever", 3, "金色");
String dogName = myDog.name; // 直接访问(如果变量是public)
int dogAge = myDog.getAge(); // 通过getter方法访问(推荐)
调用成员方法
// 语法:对象引用.方法名(参数)
objectReference.methodName(parameters);

// 示例
Dog myDog = new Dog("Buddy", "Golden Retriever", 3, "金色");
myDog.barking();        // 调用无参方法
myDog.setAge(4);        // 调用带参方法
String info = myDog.getDogInfo(); // 调用返回信息的方法

综合示例

public class Puppy {
    // 成员变量
    private String name;
    private int puppyAge;

    /**
     * 构造方法
     * @param name 小狗的名字
     */
    public Puppy(String name) {
        this.name = name;
        System.out.println("创建了小狗: " + name);
    }

    /**
     * 设置年龄
     * @param age 年龄
     */
    public void setAge(int age) {
        if (age >= 0 && age <= 20) {
            puppyAge = age;
            System.out.println(name + "的年龄设置为: " + age);
        } else {
            System.out.println("无效的年龄: " + age);
        }
    }

    /**
     * 获取年龄
     * @return 年龄
     */
    public int getAge() {
        System.out.println(name + "的年龄是: " + puppyAge);
        return puppyAge;
    }

    /**
     * 获取小狗信息
     * @return 信息字符串
     */
    public String getPuppyInfo() {
        return "名字: " + name + ", 年龄: " + puppyAge + "岁";
    }

    public static void main(String[] args) {
        // 创建对象
        Puppy myPuppy = new Puppy("Tommy");

        // 通过setter方法设置年龄
        myPuppy.setAge(2);

        // 通过getter方法获取年龄
        int age = myPuppy.getAge();

        // 获取完整信息
        String info = myPuppy.getPuppyInfo();
        System.out.println("小狗信息: " + info);

        // 再次修改年龄
        myPuppy.setAge(3);

        // 显示更新后的信息
        System.out.println("更新后: " + myPuppy.getPuppyInfo());
    }
}

源文件声明规则

Java源文件需要遵循特定的规则和约定,以确保编译器能够正确编译和组织代码。

源文件基本规则
  1. 一个源文件只能有一个public类:文件名必须与public类的名称完全一致(包括大小写)
  2. 一个源文件可以有多个非public类:但只能有一个public类
  3. 包声明:如果类属于某个包,则package语句必须是源文件的第一行(除了注释)
  4. 导入语句import语句必须位于package语句之后,类声明之前
  5. 类声明:然后是类的声明

源文件结构示例

// 1. 包声明(可选)
package com.example.animals;

// 2. 导入语句(可选)
import java.util.Date;
import java.util.List;

// 3. 类注释(可选)
/**
 * Dog类
 * 这是一个示例类,展示源文件结构
 * @author YourName
 * @version 1.0
 */

// 4. 公共类(只能有一个)
public class Dog {
    // 类体...

    // 主方法(可选)
    public static void main(String[] args) {
        System.out.println("Hello Dog!");
    }
}

// 5. 非公共类(可以有多个)
class AnimalHelper {
    // 辅助类代码...
}

// 另一个非公共类
class DogFood {
    // 狗粮类代码...
}

常见错误示例

错误示例
// 错误:一个文件中有两个public类
public class Student {
    // ...
}

public class Teacher {  // 编译错误!
    // ...
}
错误示例
// 错误:文件名与public类名不匹配
// 文件名:Person.java

public class Employee {  // 编译错误!
    // 文件名应该是Employee.java
}

Java包和导入语句

包(Package)是Java中用于组织类的一种机制,类似于文件系统的目录结构。

包的主要作用

  • 避免命名冲突:不同包中可以有相同名称的类
  • 组织和管理类:将相关的类组织在一起
  • 访问控制:包可以控制类的访问权限
  • 提供命名空间:每个包都是一个独立的命名空间

包的命名约定

  • 使用小写字母
  • 使用公司域名的倒序(例如:com.example.project
  • 避免使用Java关键字
  • 包名应该具有描述性

导入语句的使用

导入单个类
import java.util.ArrayList;
import java.util.Date;
导入整个包
import java.util.*; // 导入util包中的所有类
静态导入
// 导入静态成员
import static java.lang.Math.PI;
import static java.lang.Math.sqrt;
使用完全限定名
// 不使用import,直接使用完全限定名
java.util.ArrayList list = new java.util.ArrayList();

综合示例:员工管理系统

下面是一个完整的企业级示例,展示如何使用类和对象创建简单的员工管理系统。

Employee.java - 员工类

// 包声明
package com.company.model;

/**
 * Employee类
 * 表示公司员工
 */
public class Employee {

    // 成员变量(属性)
    private String name;
    private int age;
    private String designation;
    private double salary;
    private String department;
    private int employeeId;

    // 类变量(静态变量)- 用于生成员工ID
    private static int nextId = 1000;

    /**
     * 构造方法1:提供基本信息
     * @param name 员工姓名
     * @param age 员工年龄
     * @param designation 职位
     */
    public Employee(String name, int age, String designation) {
        this.name = name;
        this.age = age;
        this.designation = designation;
        this.salary = 0.0; // 默认工资
        this.department = "未分配"; // 默认部门
        this.employeeId = nextId++; // 自动生成ID
    }

    /**
     * 构造方法2:提供完整信息
     * @param name 姓名
     * @param age 年龄
     * @param designation 职位
     * @param salary 工资
     * @param department 部门
     */
    public Employee(String name, int age, String designation,
                   double salary, String department) {
        this(name, age, designation); // 调用另一个构造方法
        this.salary = salary;
        this.department = department;
    }

    // Getter和Setter方法

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        if (age >= 18 && age <= 65) {
            this.age = age;
        } else {
            System.out.println("年龄必须在18-65之间");
        }
    }

    public String getDesignation() {
        return designation;
    }

    public void setDesignation(String designation) {
        this.designation = designation;
    }

    public double getSalary() {
        return salary;
    }

    public void setSalary(double salary) {
        if (salary >= 0) {
            this.salary = salary;
        } else {
            System.out.println("工资不能为负数");
        }
    }

    public String getDepartment() {
        return department;
    }

    public void setDepartment(String department) {
        this.department = department;
    }

    public int getEmployeeId() {
        return employeeId;
    }

    // 业务方法

    /**
     * 给员工加薪
     * @param percentage 加薪百分比
     */
    public void giveRaise(double percentage) {
        if (percentage > 0) {
            double increase = salary * percentage / 100;
            salary += increase;
            System.out.println(name + "获得加薪: $" + increase +
                             ",新工资: $" + salary);
        }
    }

    /**
     * 员工庆祝生日,年龄+1
     */
    public void celebrateBirthday() {
        age++;
        System.out.println("祝" + name + "生日快乐!现在" + age + "岁了");
    }

    /**
     * 获取员工信息
     * @return 员工信息字符串
     */
    public String getEmployeeInfo() {
        return String.format(
            "ID: %d, 姓名: %s, 年龄: %d, 职位: %s, 部门: %s, 工资: $%.2f",
            employeeId, name, age, designation, department, salary
        );
    }

    /**
     * 打印员工信息
     */
    public void printEmployee() {
        System.out.println("员工信息:");
        System.out.println("  ID: " + employeeId);
        System.out.println("  姓名: " + name);
        System.out.println("  年龄: " + age);
        System.out.println("  职位: " + designation);
        System.out.println("  部门: " + department);
        System.out.println("  工资: $" + salary);
    }

    // 类方法(静态方法)

    /**
     * 获取下一个可用的员工ID
     * @return 下一个员工ID
     */
    public static int getNextAvailableId() {
        return nextId;
    }
}

EmployeeTest.java - 测试类

// 包声明
package com.company.test;

// 导入需要的类
import com.company.model.Employee;

/**
 * EmployeeTest类
 * 测试Employee类的功能
 */
public class EmployeeTest {

    public static void main(String[] args) {

        System.out.println("=== 员工管理系统测试 ===\n");

        // 创建员工对象
        Employee emp1 = new Employee("张三", 28, "高级软件工程师", 85000, "技术部");
        Employee emp2 = new Employee("李四", 25, "软件工程师", 65000, "技术部");
        Employee emp3 = new Employee("王五", 30, "产品经理", 90000, "产品部");
        Employee emp4 = new Employee("赵六", 22, "实习生", 30000, "技术部");

        // 显示员工信息
        System.out.println("1. 初始员工信息:");
        emp1.printEmployee();
        emp2.printEmployee();
        emp3.printEmployee();
        emp4.printEmployee();

        // 修改员工信息
        System.out.println("\n2. 修改员工信息:");
        emp1.setSalary(90000); // 给张三加薪
        emp2.setDepartment("研发部"); // 调整李四的部门
        emp3.giveRaise(10); // 给王五加薪10%
        emp4.celebrateBirthday(); // 赵六过生日

        // 显示更新后的信息
        System.out.println("\n3. 更新后的员工信息:");
        System.out.println(emp1.getEmployeeInfo());
        System.out.println(emp2.getEmployeeInfo());
        System.out.println(emp3.getEmployeeInfo());
        System.out.println(emp4.getEmployeeInfo());

        // 创建更多员工
        System.out.println("\n4. 创建更多员工:");
        Employee emp5 = new Employee("孙七", 35, "技术总监");
        emp5.setSalary(120000);
        emp5.setDepartment("技术部");

        Employee emp6 = new Employee("周八", 27, "UI设计师");
        emp6.setSalary(55000);
        emp6.setDepartment("设计部");

        System.out.println(emp5.getEmployeeInfo());
        System.out.println(emp6.getEmployeeInfo());

        // 显示下一个可用的员工ID
        System.out.println("\n5. 系统信息:");
        System.out.println("下一个可用的员工ID: " + Employee.getNextAvailableId());
        System.out.println("已创建员工总数: " + (Employee.getNextAvailableId() - 1000));

        System.out.println("\n=== 测试完成 ===");
    }
}

运行结果

=== 员工管理系统测试 ===

1. 初始员工信息:
员工信息:
  ID: 1000
  姓名: 张三
  年龄: 28
  职位: 高级软件工程师
  部门: 技术部
  工资: $85000.00
员工信息:
  ID: 1001
  姓名: 李四
  年龄: 25
  职位: 软件工程师
  部门: 技术部
  工资: $65000.00
员工信息:
  ID: 1002
  姓名: 王五
  年龄: 30
  职位: 产品经理
  部门: 产品部
  工资: $90000.00
员工信息:
  ID: 1003
  姓名: 赵六
  年龄: 22
  职位: 实习生
  部门: 技术部
  工资: $30000.00

2. 修改员工信息:
王五获得加薪: $9000.0,新工资: $99000.0
祝赵六生日快乐!现在23岁了

3. 更新后的员工信息:
ID: 1000, 姓名: 张三, 年龄: 28, 职位: 高级软件工程师, 部门: 技术部, 工资: $90000.00
ID: 1001, 姓名: 李四, 年龄: 25, 职位: 软件工程师, 部门: 研发部, 工资: $65000.00
ID: 1002, 姓名: 王五, 年龄: 30, 职位: 产品经理, 部门: 产品部, 工资: $99000.00
ID: 1003, 姓名: 赵六, 年龄: 23, 职位: 实习生, 部门: 技术部, 工资: $30000.00

4. 创建更多员工:
ID: 1004, 姓名: 孙七, 年龄: 35, 职位: 技术总监, 部门: 技术部, 工资: $120000.00
ID: 1005, 姓名: 周八, 年龄: 27, 职位: UI设计师, 部门: 设计部, 工资: $55000.00

5. 系统信息:
下一个可用的员工ID: 1006
已创建员工总数: 6

=== 测试完成 ===

面向对象编程的最佳实践

类的设计原则
  • 单一职责原则:一个类应该只有一个引起变化的原因
  • 封装性:将数据和行为封装在类中,隐藏实现细节
  • 高内聚:类的方法应该密切相关
  • 低耦合:类之间的依赖应该尽可能少
  • 使用访问修饰符:合理使用public、private、protected
命名约定
  • 类名:使用名词,首字母大写,如CarStudent
  • 方法名:使用动词,首字母小写,如calculateTotal()
  • 变量名:有意义的名字,首字母小写,如studentName
  • 常量名:全大写,下划线分隔,如MAX_SIZE
  • 包名:全小写,使用域名倒序,如com.example.project
常见错误和注意事项
  • 忘记使用new关键字创建对象
  • 混淆类变量和实例变量的使用
  • 在构造方法中调用可被重写的方法
  • 没有正确处理对象的状态变化
  • 过度使用public访问修饰符,破坏封装性