JAVA 面向对象编程

67 阅读16分钟

类和对象

1. 类的定义

  • 类 是对象的模板,用于定义对象的属性和行为
  • 类的组成
    • 属性:表示对象的特征(名字/年龄/性别)
    • 方法:表示对象的行为(吃饭/睡觉/跑步)
// 语法
class 类名 {
    // 属性(成员变量)
    数据类型 属性名;
    
    // 方法(成员方法)
    返回类型 方法名(参数列表) {
        // 方法体
    }
}

class Student {
    // 属性(特征)
    String name; // 学生的名字
    int age;     // 学生的年龄
    
    // 方法(行为)
    void study() {
        System.out.println(name + " 正在学习。");
    }
    
    void introduce() {
        System.out.println("大家好,我是 " + name + ",今年 " + age + " 岁。");
    }
}

2. 对象的定义

  • 对象是类的具体实例。类是抽象的,而对象是具体的
// 语法
类名 对象名 = new 类名();

// 示例
public class Main {
    public static void main(String[] args) {
        // 创建 Student 类的对象
        Student student = new Student();
        
        // 设置对象的属性
        student.name = "小明";
        student.age = 18;
        
        // 调用对象的方法
        student.study();
        student.introduce();
    }
}

// 输出结果
小明 正在学习。
大家好,我是 小明,今年 18 岁。

3. 类和对象的核心思想

  • 类:抽象定义了一类事物的属性和行为。
    • 如“学生”是类,描述了所有学生的共性(名字、年龄等属性,学习等行为)。
  • 对象:类的一个具体实例。
    • 如“小明”是一个学生对象,“小红”是另一个学生对象。

4. 构造方法

构造方法用于初始化对象的属性。

  • 构造方法的特点
    • 构造方法的名称必须和类名相同。
    • 构造方法没有返回值。
    • 创建对象时,构造方法会被自动调用。
// 示例
class Student {
    String name;
    int age;
    
    // 构造方法
    Student(String name, int age) {
        this.name = name; // 使用 this 关键字引用当前对象
        this.age = age;
    }
    
    void introduce() {
        System.out.println("大家好,我是 " + name + ",今年 " + age + " 岁。");
    }
}

public class Main {
    public static void main(String[] args) {
        // 创建对象时传入参数
        Student student = new Student("小明", 18);
        student.introduce();
    }
}

大家好,我是 小明,今年 18 岁。

5. 类与对象的高级特性(封装继承多态)

  1. 封装
    • 是将对象的属性和行为(方法)封装在一起,通过访问控制(如 private)限制外部对属性的直接访问,提供公共方法(如 get 和 set 方法)与外界交互。
    • 优点:
      • 提高安全性:保护对象内部数据,不允许外界随意修改。
      • 提高代码维护性:通过方法控制数据的合法性。
      • 隐藏实现细节:只暴露对外接口,简化使用。
// 封装
class Person {
    private String name;  // 私有属性,只能通过方法访问
    private int age;

    // 构造方法初始化对象
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // 公共方法提供访问接口
    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 > 0) { // 数据校验
            this.age = age;
        } else {
            System.out.println("年龄不能为负数!");
        }
    }
}

public class Main {
    public static void main(String[] args) {
        Person person = new Person("小明", 25);
        System.out.println("姓名:" + person.getName());
        System.out.println("年龄:" + person.getAge());

        person.setAge(-5); // 不合法的年龄修改
        person.setAge(30); // 合法的修改
        System.out.println("修改后的年龄:" + person.getAge());
    }
}

姓名:小明
年龄:25
年龄不能为负数!
修改后的年龄:30

  1. 继承
    • 一个类可以继承另一个类的属性和方法
    • 优点:
      • 代码复用:子类直接使用父类的功能,不需要重复定义。
      • 易于扩展:子类可以在父类基础上添加新功能。
      • 提高维护性:公共功能集中在父类,减少冗余代码
// 父类
class Animal {
    String name;

    void eat() {
        System.out.println(name + " 正在吃东西。");
    }
}

// 子类
class Dog extends Animal {
    void bark() {
        System.out.println(name + " 在汪汪叫。");
    }
}

public class Main {
    public static void main(String[] args) {
        Dog dog = new Dog();
        dog.name = "小艾克斯"; // 继承父类的属性
        dog.eat();        // 继承父类的方法
        dog.bark();       // 调用子类的方法
    }
}

小艾克斯 正在吃东西。
小艾克斯 在汪汪叫。

  1. 多态
    • 多态指同一个方法在不同对象上的表现行为不同。分为:
      • 编译时多态(方法重载):在同一类中定义多个同名方法,但参数不同。
      • 运行时多态(方法重写):通过父类引用调用子类对象的方法。
    • 优点:
      • 灵活性:父类定义统一的接口,子类根据需要实现具体行为。
      • 扩展性:添加新子类时无需修改现有代码。
  • 编译时多态:方法重载
    • 根据同名方法根据参数的类型/数量或顺序的不同,执行不同的逻辑
class Calculator {
    int add(int a, int b) {
        return a + b;
    }

    // 重载方法
    double add(double a, double b) {
        return a + b;
    }

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

public class Main {
    public static void main(String[] args) {
        Calculator calculator = new Calculator();
        System.out.println("两整数相加:" + calculator.add(3, 5));
        System.out.println("两小数相加:" + calculator.add(3.5, 5.2));
        System.out.println("三整数相加:" + calculator.add(3, 5, 7));
    }
}

两整数相加:8
两小数相加:8.7
三整数相加:15

  • 运行时多态:方法重写
    • 方法重写是子类重写父类的方法,通过父类引用调用子类实现
class Animal {
    void sound() {
        System.out.println("动物发出声音。");
    }
}

class Dog extends Animal {
    @Override
    void sound() {
        System.out.println("艾克斯汪汪叫。");
    }
}

class Cat extends Animal {
    @Override
    void sound() {
        System.out.println("猫喵喵叫。");
    }
}

public class Main {
    public static void main(String[] args) {
        Animal animal1 = new Dog(); // 父类引用指向子类对象
        animal1.sound(); // 调用子类的重写方法

        Animal animal2 = new Cat();
        animal2.sound();
    }
}

艾克斯汪汪叫。
猫喵喵叫。

  • 封装/继承/多态 的关系
    • 封装:保护属性,隐藏实现细节。
    • 继承:复用代码,将共性提取到父类。
    • 多态:让同一接口在不同对象上表现出不同行为。
class Animal {
    private String name; // 封装

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

    public String getName() {
        return name;
    }

    public void sound() {
        System.out.println(name + " 发出声音。");
    }
}

class Dog extends Animal {
    public Dog(String name) {
        super(name); // 调用父类构造方法
    }

    @Override
    public void sound() {
        System.out.println(getName() + " 汪汪叫。");
    }
}

class Cat extends Animal {
    public Cat(String name) {
        super(name);
    }

    @Override
    public void sound() {
        System.out.println(getName() + " 喵喵叫。");
    }
}

public class Main {
    public static void main(String[] args) {
        Animal dog = new Dog("小艾克斯"); // 多态
        dog.sound();

        Animal cat = new Cat("小猫");
        cat.sound();
    }
}

小艾克斯 汪汪叫。
小猫 喵喵叫。

6. 总结

  • 类是抽象的,是一类事物的模板。
  • 对象是类的实例,是一个具体存在的实体。
  • 类与对象的核心特性包括:
    • 封装:保护属性的安全性。
    • 继承:实现代码复用。
    • 多态:提高代码灵活性。

方法和封装

1. 方法

方法是封装在类中的功能块,表示对象可以执行的行为或操作

  • 方法的定义
    • Java 方法通常由以下部分组成:
    • 访问修饰符:控制方法的访问权限(如 public、private)。
    • 返回值类型:方法返回的数据类型(如 int、void)。
    • 方法名:标识方法的名称,应该遵循驼峰命名规则。
    • 参数列表:方法需要的输入参数,参数类型和名称需声明。
    • 方法体:方法执行的代码块,包含具体的功能实现。
// 语法
访问修饰符 返回值类型 方法名(参数列表) {
    // 方法体
    return 返回值; // 如果返回值类型是 void,可以省略 return
}

// 示例
public class Main {
    // 定义一个方法
    public static int add(int a, int b) {
        return a + b; // 返回两个整数的和
    }

    public static void main(String[] args) {
        int result = add(5, 3); // 调用方法
        System.out.println("5 + 3 = " + result);
    }
}

2. 封装

封装是将数据(属性)和行为(方法)包装在一起,并对外隐藏实现细节,只暴露必要的接口。 它是面向对象的三大特性之一,主要用于保护对象的内部状态,防止外部不安全的访问和修改。

  • 封装的核心原则
    • 使用访问修饰符控制访问权限:
      • public:对所有类可见。
      • private:仅对当前类可见。
      • protected:对同包和子类可见。
      • 默认(包级私有):对同包内可见。
    • 通过方法访问属性:
      • 私有属性通过公共的 getter 和 setter 方法提供访问和修改。
    • 隐藏实现细节:
      • 类的具体实现对外部不可见,只暴露接口。
  • 为什么需要封装?
    • 提高代码安全性:防止外部直接修改对象的内部数据。
    • 易维护性:可以随时修改实现细节而不影响外部代码。
    • 增强可读性:通过 getter 和 setter 方法提供明确的数据访问方式。

3. 封装的实现步骤

  1. 将属性声明为 private,禁止外部直接访问。
  2. 提供 public 的 getter 和 setter 方法,用以访问和修改属性
public class Person {
    // 私有属性
    private String name;
    private int age;

    // 构造方法
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // Getter 方法
    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    // Setter 方法
    public void setName(String name) {
        this.name = name;
    }

    public void setAge(int age) {
        if (age > 0) { // 确保年龄为正数
            this.age = age;
        }
    }

    // 自定义方法
    public void showInfo() {
        System.out.println("姓名:" + name + ",年龄:" + age);
    }
}

// 测试封装
public class Main {
    public static void main(String[] args) {
        // 创建对象
        Person person = new Person("张三", 25);

        // 通过 Getter 获取属性值
        System.out.println("姓名:" + person.getName());
        System.out.println("年龄:" + person.getAge());

        // 使用 Setter 修改属性值
        person.setName("李四");
        person.setAge(30);

        // 调用方法查看修改后的信息
        person.showInfo(); // 输出:姓名:李四,年龄:30
    }
}

4. 方法和封装的结合

封装和方法密切相关。封装是将数据和操作隐藏起来,通过方法提供访问接口,而方法是实现封装的主要工具

public class BankAccount {
    private double balance; // 余额(封装为私有)

    // 构造方法初始化账户余额
    public BankAccount(double initialBalance) {
        if (initialBalance > 0) {
            this.balance = initialBalance;
        }
    }

    // 存款方法
    public void deposit(double amount) {
        if (amount > 0) {
            balance += amount;
            System.out.println("存款成功,当前余额:" + balance);
        } else {
            System.out.println("存款金额无效");
        }
    }

    // 取款方法
    public void withdraw(double amount) {
        if (amount > 0 && amount <= balance) {
            balance -= amount;
            System.out.println("取款成功,当前余额:" + balance);
        } else {
            System.out.println("取款金额无效或余额不足");
        }
    }

    // 查询余额方法
    public double getBalance() {
        return balance;
    }
}

// 测试 BankAccount 类
public class Main {
    public static void main(String[] args) {
        BankAccount account = new BankAccount(1000); // 初始化账户
        account.deposit(500); // 存款
        account.withdraw(300); // 取款
        System.out.println("最终余额:" + account.getBalance()); // 查询余额
    }
}

5. 总结

  1. 方法是定义在类中、描述对象行为的功能块。
  2. 封装是面向对象编程的核心,用于隐藏内部实现细节,确保数据安全。
  3. 方法和封装相辅相成,封装数据后,需要通过方法实现数据的访问与操作。

static 关键字和继承

1. static 关键字

static 是 Java 中的一个关键字,用来修饰 变量、方法、代码块 和 嵌套类。被 static 修饰的成员属于类,而不是某个具体的对象。它在内存中的存储位置是方法区中的静态区

1.1 修饰变量

  • 静态变量(类变量) 是类的所有对象共享的。无论创建多少个对象,静态变量只有一份,所有对象对它的修改都会影响其他对象
public class Counter {
    public static int count = 0; // 静态变量

    public Counter() {
        count++;
    }
}

public class Main {
    public static void main(String[] args) {
        Counter c1 = new Counter();
        Counter c2 = new Counter();
        Counter c3 = new Counter();

        System.out.println("创建的对象数量:" + Counter.count); // 输出:3
    }
}

1.2 修饰方法

  • 静态方法(类方法) 不依赖于实例,可以通过类名直接调用。
  • 静态方法中 不能使用非静态成员,因为非静态成员属于对象,而静态方法不依赖对象。
public class MathUtil {
    public static int add(int a, int b) {
        return a + b;
    }
}

public class Main {
    public static void main(String[] args) {
        int result = MathUtil.add(5, 3); // 通过类名调用静态方法
        System.out.println("5 + 3 = " + result);
    }
}

1.3 静态代码块

  • 静态代码块 用于初始化类,在类加载时执行一次。
  • 通常用于加载静态资源或配置文件。
public class Database {
    static {
        System.out.println("静态代码块执行:数据库连接已初始化");
    }

    public static void connect() {
        System.out.println("数据库连接成功");
    }
}

public class Main {
    public static void main(String[] args) {
        Database.connect();
    }
}

1.4 修饰嵌套类

  • 静态嵌套类 是可以独立于外部类实例而存在的类。
  • 静态嵌套类不能直接访问外部类的非静态成员。
public class Outer {
    static class Nested {
        public void display() {
            System.out.println("这是一个静态嵌套类");
        }
    }
}

public class Main {
    public static void main(String[] args) {
        Outer.Nested nested = new Outer.Nested(); // 直接创建静态嵌套类对象
        nested.display();
    }
}

2. 继承

继承是面向对象的三大特性之一,用于表示 “is-a” 的关系。子类继承父类后,可以直接复用父类的字段和方法,也可以重写父类的方法。

2.1 继承的语法

// 父类
public class Animal {
    public void eat() {
        System.out.println("动物吃东西");
    }
}

// 子类
public class Dog extends Animal {
    public void bark() {
        System.out.println("艾克斯汪汪叫");
    }
}

public class Main {
    public static void main(String[] args) {
        Dog dog = new Dog();
        dog.eat(); // 调用父类的方法
        dog.bark(); // 调用子类的方法
    }
}

2.2 继承的特点

  1. 单继承:Java 不支持多继承,一个类只能继承一个父类。
    • 为了避免多继承导致的 菱形继承问题,Java 使用接口来提供多继承的功能。
  2. 支持多层继承:一个类可以有多个层级的继承链。
  3. super 关键字:
    • 调用父类的构造方法或成员。
    • 示例:super.成员 或 super(参数)。
public class Animal {
    public Animal(String name) {
        System.out.println("动物的名字是:" + name);
    }
}

public class Dog extends Animal {
    public Dog(String name) {
        super(name); // 调用父类的构造方法
    }
}

public class Main {
    public static void main(String[] args) {
        Dog dog = new Dog("小黑"); // 输出:动物的名字是:小黑
    }
}

2.3 方法重写

  • 子类可以重写父类的方法,提供自己的实现逻辑。
    • 必须保证方法签名一致:方法名、参数列表和返回类型必须与父类方法相同。
    • 不能降低访问权限:比如,父类的方法是 public,子类不能重写为 protected 或 private。
public class Animal {
    public void eat() {
        System.out.println("动物吃东西");
    }
}

public class Dog extends Animal {
    @Override
    public void eat() { // 重写父类方法
        System.out.println("艾克斯吃骨头");
    }
}

public class Main {
    public static void main(String[] args) {
        Dog dog = new Dog();
        dog.eat(); // 输出:艾克斯吃骨头
    }
}

2.4 使用 final 限制继承

  • final 类:不能被继承。
  • final 方法:不能被子类重写。
  • final 属性:不能被修改
public final class Constants {
    public final double PI = 3.14;
}

// 以下会报错:
public class SubClass extends Constants {}

3. 结合 static 与继承

  • 静态成员不能被重写:静态方法属于类本身,而不是实例,因此子类中的静态方法只是隐藏了父类的静态方法。
  • 静态代码块与继承:子类继承父类时,父类的静态代码块会优先执行。
public class Parent {
    public static void display() {
        System.out.println("父类的静态方法");
    }
}

public class Child extends Parent {
    public static void display() {
        System.out.println("子类的静态方法");
    }
}

public class Main {
    public static void main(String[] args) {
        Parent.display(); // 输出:父类的静态方法
        Child.display();  // 输出:子类的静态方法
    }
}

4. 总结

  • static 关键字:
    • 用于修饰变量、方法、代码块、嵌套类。
    • 静态成员属于类,不依赖于对象。
  • 继承:
    • 是一种复用代码的方式,子类可扩展父类功能。
    • 继承遵循单继承原则,但支持多层继承。
  • 两者结合时,静态成员不参与继承,仅表现为隐藏。

特殊类

Java 中的特殊类包括 抽象类、接口、内部类、匿名类、枚举类 等,它们提供了更灵活的设计和实现能力,适用于特定的场景。以下是对这些特殊类的详细说明和示例。

1. 抽象类

  • 特点:
    • 不能实例化:抽象类不能直接创建对象,只能被继承。
    • 抽象方法:一个类中如果有一个方法声明为 abstract,那么这个类必须是抽象类。
    • 可以有普通方法和字段:抽象类不仅可以有抽象方法,还可以有具体实现的方法和成员变量。
  • 应用场景:
    • 需要定义某种通用的行为,而具体实现由子类完成。
    • 可以部分实现功能,部分留给子类实现。
abstract class Animal {
    public abstract void sound(); // 抽象方法,没有实现

    public void eat() { // 普通方法,有具体实现
        System.out.println("动物正在吃东西");
    }
}

class Dog extends Animal {
    @Override
    public void sound() {
        System.out.println("艾克斯叫:汪汪");
    }
}

class Cat extends Animal {
    @Override
    public void sound() {
        System.out.println("猫叫:喵喵");
    }
}

public class Main {
    public static void main(String[] args) {
        Animal dog = new Dog();
        dog.sound();
        dog.eat();

        Animal cat = new Cat();
        cat.sound();
    }
}

艾克斯叫:汪汪
动物正在吃东西
猫叫:喵喵

2. 接口

  • 特点:
    • 接口是比抽象类更严格的抽象机制。
    • 不能有实例字段:所有字段必须是 public static final(常量)。
    • 方法默认是抽象的:所有方法默认是 public abstract。
    • Java 8 开始,接口可以有默认方法和静态方法。
  • 应用场景:
    • 需要定义多个类的行为,但这些类可能没有继承关系。
    • 实现多重继承(一个类可以实现多个接口)。
interface Flyable {
    void fly(); // 抽象方法
}

interface Swimable {
    void swim();
}

class Duck implements Flyable, Swimable {
    @Override
    public void fly() {
        System.out.println("鸭子可以飞");
    }

    @Override
    public void swim() {
        System.out.println("鸭子也会游泳");
    }
}

public class Main {
    public static void main(String[] args) {
        Duck duck = new Duck();
        duck.fly();
        duck.swim();
    }
}

鸭子可以飞
鸭子也会游泳

接口的默认方法和静态方法
interface Printable {
    default void print() {
        System.out.println("默认的打印方法");
    }

    static void staticMethod() {
        System.out.println("接口的静态方法");
    }
}

public class Main {
    public static void main(String[] args) {
        Printable printable = new Printable() {}; // 匿名类
        printable.print();
        Printable.staticMethod();
    }
}

默认的打印方法
接口的静态方法

3. 内部类

  • 特点:
    • 内部类是定义在另一个类内部的类,分为以下几种类型:
      • 成员内部类:作为外部类的成员,与普通字段类似。
      • 静态内部类:类似于静态字段。
      • 局部内部类:定义在方法或代码块中的类。
      • 匿名内部类:没有名字的类,常用于简化代码。
  • 应用场景:
    • 当一个类需要紧密绑定另一个类时,使用内部类可以提高封装性。
// 成员内部类
class OuterClass {
    private String message = "外部类的消息";

    class InnerClass {
        public void display() {
            System.out.println("内部类访问:" + message);
        }
    }
}

public class Main {
    public static void main(String[] args) {
        OuterClass outer = new OuterClass();
        OuterClass.InnerClass inner = outer.new InnerClass();
        inner.display();
    }
}
内部类访问:外部类的消息


// 静态内部类
class OuterClass {
    private static String message = "静态成员";

    static class InnerStaticClass {
        public void displayMessage() {
            System.out.println("访问:" + message);
        }
    }
}

public class Main {
    public static void main(String[] args) {
        OuterClass.InnerStaticClass inner = new OuterClass.InnerStaticClass();
        inner.displayMessage();
    }
}
访问:静态成员


// 局部内部类
class OuterClass {
    public void showMessage() {
        final String localMessage = "局部变量";

        class LocalInnerClass {
            public void printMessage() {
                System.out.println("内部类访问:" + localMessage);
            }
        }

        LocalInnerClass localInner = new LocalInnerClass();
        localInner.printMessage();
    }
}

public class Main {
    public static void main(String[] args) {
        OuterClass outer = new OuterClass();
        outer.showMessage();
    }
}
内部类访问:局部变量


// 匿名内部类
interface Greeting {
    void sayHello();
}

public class Main {
    public static void main(String[] args) {
        Greeting greeting = new Greeting() {
            @Override
            public void sayHello() {
                System.out.println("Hello, World!");
            }
        };

        greeting.sayHello();
    }
}
Hello, World!



4. 匿名类

  • 特点:
    • 匿名类是没有名字的类。
    • 通常用于实现接口或继承抽象类的快速实现。
    • 语法简洁,但只适合用于一次性的逻辑。
  • 应用场景:
    • 简化代码,只需要实现一次逻辑时使用。
interface Greeting {
    void sayHello();
}

public class Main {
    public static void main(String[] args) {
        Greeting greeting = new Greeting() {
            @Override
            public void sayHello() {
                System.out.println("Hello, World!");
            }
        };
        greeting.sayHello();
    }
}

Hello, World!



5. 枚举类

  • 特点:
    • 枚举类是特殊的类,用于定义一组固定的常量。
    • 每个枚举值本质上是该枚举类的实例。
    • 可以定义构造方法、字段和方法。
  • 应用场景:
    • 定义一组不可变的常量,例如星期、季节、颜色等。
enum Day {
    MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY;

    public boolean isWeekend() {
        return this == SATURDAY || this == SUNDAY;
    }
}

public class Main {
    public static void main(String[] args) {
        Day today = Day.FRIDAY;
        System.out.println("今天是:" + today);
        System.out.println("今天是周末吗?" + today.isWeekend());
    }
}

今天是:FRIDAY
今天是周末吗?false

带构造方法的枚举
enum Color {
    RED("红色"), GREEN("绿色"), BLUE("蓝色");

    private String description;

    Color(String description) {
        this.description = description;
    }

    public String getDescription() {
        return description;
    }
}

public class Main {
    public static void main(String[] args) {
        for (Color color : Color.values()) {
            System.out.println(color + " - " + color.getDescription());
        }
    }
}

RED - 红色
GREEN - 绿色
BLUE - 蓝色

6. 总结

特殊类特点应用场景
抽象类不能实例化;可以包含抽象和具体方法定义通用行为,由子类实现细节
接口方法默认为 public abstract;支持多实现定义多个类的统一行为
内部类嵌套在外部类中;封装性强逻辑紧密相关的类
匿名类没有名字;用于快速实现接口或继承类一次性实现简单逻辑
枚举类定义固定的常量;不可变定义一组常量,例如状态或类型