Java面向对象编程五大核心:POP vs OOP、类与对象、变量作用域、参数传递、重载与重写深度解析

121 阅读7分钟

一、面向过程 vs 面向对象

1、面向过程的程序设计(Process-Oriented Programming)简称POP

  • 核心:以函数为中心,关注解决问题的步骤(先做什么、后做什么)
  • 数据与操作分离:数据(变量)和操作数据的函数是独立的
  • 典型的语言:C语言
  • 是一种“执行者思维”,适合解决简单问题。扩展能力差、后期维护难度较大

计算矩形面积和周长面向过程写法:

public class ProceduralExample {
    // 数据与函数分离
    public static double calculateArea(double length, double width) {
        return length * width;
    }
    
    public static double calculatePerimeter(double length, double width) {
        return 2 * (length + width);
    }

    public static void main(String[] args) {
        double length = 5.0;
        double width = 3.0;
        // 逐步调用函数
        double area = calculateArea(length, width);
        double perimeter = calculatePerimeter(length, width);
        System.out.println("面积: " + area + ", 周长: " + perimeter);
    }
}

2、面向对象的程序设计(Object-Oriented Programming)简称OOP

  • 核心:以对象为中心,将问题分解为相互交互的实体(对象)
  • 封装:数据(属性)和操作数据的方法绑定在对象中
  • 典型的语言:Java、C#、C++、Python、Ruby和PHP等
  • 是一种“设计者思维”,适合解决复杂问题。代码扩展性强、可维护性高

计算矩形面积和周长面向对象写法:

// 封装数据和操作
class Rectangle {
    private double length;
    private double width;

    public Rectangle(double length, double width) {
        this.length = length;
        this.width = width;
    }

    // 方法绑定到对象
    public double calculateArea() {
        return length * width;
    }

    public double calculatePerimeter() {
        return 2 * (length + width);
    }
}

public class OOPExample {
    public static void main(String[] args) {
        // 创建对象并调用方法
        Rectangle rect = new Rectangle(5.0, 3.0);
        System.out.println("面积: " + rect.calculateArea() + ", 周长: " + rect.calculatePerimeter());
    }
}

二、类(Class)vs 对象(Object)

  在Java中,类(Class)对象(Object)面向对象编程(OOP)的核心概念。它们的关系类似于“蓝图”“根据蓝图建造的具体实物”

1、类(Class)

  • 定义:类是描述一类对象的模板蓝图。它定义了对象的属性(字段)和行为(方法)
  • 组成:
    • 字段(Fields):对象的属性(如 String name;
    • 方法(Methods):对象的行为(如 void eat() {...}
    • 构造器(Constructor):创建对象时初始化状态的特殊方法
  • 示例:
    // 定义一个"Dog"类
    public class Dog {
        // 字段(属性)
        String breed;
        int age;
        String color;
        
        // 构造器(初始化对象)
        public Dog(String breed, int age, String color) {
            this.breed = breed;
            this.age = age;
            this.color = color;
        }
        
        // 方法(行为)
        void bark() {
            System.out.println("汪汪!");
        }
        
        void sleep() {
            System.out.println("睡觉中...");
        }
    }
    

2、对象(Object)

  • 定义:对象是类的实例(Instance),是根据类创建的具体实体
  • 特点:
    • 每个对象拥有独立的状态(字段的值)和行为(方法的实现)
    • 通过 new 关键字创建对象
  • 示例:
    public class Main {
        public static void main(String[] args) {
            // 创建Dog类的对象
            Dog myDog = new Dog("拉布拉多", 3, "黄色");
            
            // 访问对象的字段
            System.out.println("品种: " + myDog.breed); // 输出: 拉布拉多
            
            // 调用对象的方法
            myDog.bark(); // 输出: 汪汪!
        }
    }
    

三、成员变量 vs 局部变量

1、成员变量

  • 定义:直接声明在类内部、方法/代码块外部的变量
  • 分类:
    1. 实例变量(非 static
    2. 类变量(static,静态变量)
  • 示例:
    public class Person {
        // 实例变量(属于对象)
        private String name;   // 无static修饰
        
        // 类变量(属于类)
        public static int count; // 有static修饰
    }
    

特点说明:

特性说明
作用域整个类(类变量可跨类访问)
生命周期实例变量:随对象创建而存在,随对象回收而销毁
类变量:随类加载而存在,随程序结束而销毁
存储位置堆内存(实例变量在对象内)、方法区(类变量)
初始化自动赋予默认值(如 int 默认为 0,引用类型默认为 null
访问方式实例变量:对象.变量名
类变量:类名.变量名对象.变量名(不推荐)
修饰符可用 public/private/protected/static/final

2、局部变量

  • 定义:声明在方法、构造方法、代码块或形参列表中的变量
  • 分类:
    1. 方法局部变量
    2. 代码块局部变量
    3. 参数变量(方法形参)
  • 示例:
    public void printInfo(int age) { // age是参数(局部变量)
        String message = "Age: ";   // 方法局部变量
        {
            int temp = 10;          // 代码块局部变量(仅在此块内有效)
        }
        System.out.println(message + age);
    }
    

特点说明:

特性说明
作用域从声明处开始,到所属代码块结束(如方法/循环/条件体内部)
生命周期随方法/代码块的执行而创建,执行结束后销毁
存储位置栈内存
初始化必须手动初始化(否则编译报错)
访问方式直接通过变量名(仅在作用域内有效)
修饰符不可用static/访问修饰符(如 public),但可用 final

四、值传递 vs 引用传递

  在 Java 中,所有方法的参数传递都是值传递。这意味着方法内部获得的是参数的副本,而非原始变量本身。理解这一点对于基本数据类型引用类型(对象)的行为至关重要。

1、基本数据类型(值传递)

  • 传递的是值的副本,方法内修改参数不影响原始变量
  • 示例:
    void modify(int x) {
        x = 20;  // 修改的是副本
    }
    
    public static void main(String[] args) {
        int a = 10;
        modify(a);
        System.out.println(a);  // 输出 10(原始值未变)
    }
    

2、引用类型(传递引用地址的值)

  • 传递的是对象地址的副本,方法内通过该地址修改对象属性会影响原始对象,但重新赋值引用不影响原始引用
  • 关键点:
    • 修改对象属性:影响原始对象(因副本和原始引用指向同一对象
    • 重定向引用:不影响原始引用(修改的是副本地址

示例1:修改对象属性(原始对象被改变

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

void changeName(Person p) {
    p.name = "Bob";  // 修改副本指向的对象属性
}

public static void main(String[] args) {
    Person person = new Person("Alice");
    changeName(person);
    System.out.println(person.name);  // 输出 "Bob"(原始对象被修改)
}

示例2:重定向引用(原始引用不变

void createNewPerson(Person p) {
    p = new Person("Bob");  // 副本指向新对象,原始引用不变
}

public static void main(String[] args) {
    Person person = new Person("Alice");
    createNewPerson(person);
    System.out.println(person.name);  // 输出 "Alice"(原始引用未变)
}

注意⚠️:Java 只有值传递!对于引用类型,传递的是引用的值(即对象地址的副本),而非引用本身(即不是引用传递)

五、方法重载 vs 方法重写

1、方法重载(Overloading)

  • 定义:在同一个类中,多个方法使用相同的方法名参数列表不同(参数类型、个数或顺序不同)
  • 核心特点:
    1. 相同类中:所有重载方法必须在同一个类(或父子类中也可重载)
    2. 方法名相同:必须使用完全相同的名称
    3. 参数列表不同:
      • 参数类型不同(如 int vs String)
      • 参数个数不同(如 method() vs method(int a))
      • 参数顺序不同(如 method(int a, String b) vs method(String b, int a))
    4. 与以下无关:
      • 返回类型(可相同或不同)
      • 访问修饰符(可不同)
      • 抛出的异常(可不同)

示例:

class Calculator {
    // 重载add方法
    public int add(int a, int b) {
        return a + b;
    }
    
    public double add(int a, double b) { // 参数类型不同
        return a + b;
    }
    
    public int add(int a, int b, int c) { // 参数个数不同
        return a + b + c;
    }
    
    public double add(double a, int b) { // 参数顺序不同
        return a + b;
    }

    // public double add(int a, int b) { // 只有返回值类型不同,编译失败
    //     return a + b;
    // }
}

调用原理:编译器在编译时根据参数类型数量决定调用哪个方法(静态绑定)

Calculator calc = new Calculator();
calc.add(2, 3);       // 调用 int add(int, int)
calc.add(2.5, 3.7);   // 调用 double add(double, double)
calc.add("He", "llo"); // 调用 String add(String, String)

2、方法重写(Overriding)

  • 定义:子类重新定义从父类继承的方法,提供特定实现
  • 目的:实现运行时多态,允许子类定制父类行为
  • 核心特点:
    1. 继承关系:必须存在于父类和子类之间
    2. 方法签名相同:
      • 方法名、参数列表、返回类型必须完全相同(Java 5+支持返回类型为父类方法返回类型的子类
    3. 访问权限:子类方法访问修饰符不能比父类更严格
      • 父类 public → 子类必须 public
      • 父类 protected → 子类可 protectedpublic
    4. 异常限制:子类方法抛出的检查异常不能比父类更宽泛
    5. 不能重写:
      • private 方法(不可继承)
      • final 方法
      • static 方法(静态方法属于类,不属于对象)

示例:

class Animal {
    public void makeSound() {
        System.out.println("Animal makes sound");
    }
}

class Dog extends Animal {
    @Override // 注解确保正确重写
    public void makeSound() { // 重写父类方法
        System.out.println("Dog barks: Woof!");
    }
}

调用原理:JVM在运行时根据对象实际类型决定调用哪个方法(动态绑定)

Animal myDog = new Dog(); // 向上转型
myDog.makeSound(); // 输出 "Dog barks: Woof!"