一、面向过程 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、成员变量
- 定义:直接声明在
类内部、方法/代码块外部的变量 - 分类:
- 实例变量(非
static) - 类变量(
static,静态变量)
- 实例变量(非
- 示例:
public class Person { // 实例变量(属于对象) private String name; // 无static修饰 // 类变量(属于类) public static int count; // 有static修饰 }
特点说明:
| 特性 | 说明 |
|---|---|
| 作用域 | 整个类(类变量可跨类访问) |
| 生命周期 | 实例变量:随对象创建而存在,随对象回收而销毁 类变量:随类加载而存在,随程序结束而销毁 |
| 存储位置 | 堆内存(实例变量在对象内)、方法区(类变量) |
| 初始化 | 自动赋予默认值(如 int 默认为 0,引用类型默认为 null) |
| 访问方式 | 实例变量:对象.变量名类变量: 类名.变量名 或 对象.变量名(不推荐) |
| 修饰符 | 可用 public/private/protected/static/final 等 |
2、局部变量
- 定义:声明在
方法、构造方法、代码块或形参列表中的变量 - 分类:
- 方法局部变量
- 代码块局部变量
- 参数变量(方法形参)
- 示例:
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)
- 定义:在同一个类中,多个方法使用
相同的方法名但参数列表不同(参数类型、个数或顺序不同) - 核心特点:
- 相同类中:所有重载方法必须在同一个类(或
父子类中也可重载) - 方法名相同:必须使用
完全相同的名称 - 参数列表不同:
- 参数
类型不同(如 int vs String) - 参数
个数不同(如 method() vs method(int a)) - 参数
顺序不同(如 method(int a, String b) vs method(String b, int a))
- 参数
- 与以下无关:
- 返回类型(可相同或不同)
- 访问修饰符(可不同)
- 抛出的异常(可不同)
- 相同类中:所有重载方法必须在同一个类(或
示例:
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)
- 定义:子类重新定义
从父类继承的方法,提供特定实现 - 目的:实现运行时多态,允许子类定制父类行为
- 核心特点:
- 继承关系:必须存在于父类和子类之间
- 方法签名相同:
方法名、参数列表、返回类型必须完全相同(Java 5+支持返回类型为父类方法返回类型的子类)
- 访问权限:子类方法访问修饰符不能比父类更严格
- 父类
public→ 子类必须public - 父类
protected→ 子类可protected或public
- 父类
- 异常限制:子类方法抛出的检查异常不能比父类更宽泛
- 不能重写:
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!"