跳到主要内容

第6章:面向对象编程

作为 Java 开发者,你已经熟悉了面向对象编程的概念。Python 也支持面向对象编程,并且语法更加简洁。本章将详细对比两种语言的面向对象编程特性。

6.1 类的定义

Java 类定义

public 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) {
this.age = age;
}

public void introduce() {
System.out.println("My name is " + name + " and I am " + age + " years old.");
}
}

Python 类定义

class Person:
"""人员类"""

# 构造方法
def __init__(self, name, age):
self.name = name
self.age = age

# 成员方法
def introduce(self):
"""自我介绍"""
print(f"My name is {self.name} and I am {self.age} years old.")

对比

特性JavaPython
类定义public class ClassNameclass ClassName:
构造方法与类名相同的方法__init__ 方法
成员变量必须声明类型动态添加,无需声明
访问修饰符public, private, protected无(通过命名约定)
this 关键字thisself
方法定义必须声明返回类型无需声明返回类型
代码块大括号包围缩进

Python 类示例

# 基本类定义
class Person:
"""人员类"""

# 类变量
species = "Homo sapiens"

# 构造方法
def __init__(self, name, age):
# 实例变量
self.name = name
self.age = age

# 实例方法
def introduce(self):
"""自我介绍"""
print(f"My name is {self.name} and I am {self.age} years old.")

# 类方法
@classmethod
def get_species(cls):
"""获取物种"""
return cls.species

# 静态方法
@staticmethod
def is_adult(age):
"""判断是否成年"""
return age >= 18

# 创建对象
person = Person("Alice", 25)

# 调用实例方法
person.introduce() # My name is Alice and I am 25 years old.

# 访问实例变量
print(person.name) # Alice
print(person.age) # 25

# 访问类变量
print(Person.species) # Homo sapiens
print(person.species) # Homo sapiens

# 调用类方法
print(Person.get_species()) # Homo sapiens

# 调用静态方法
print(Person.is_adult(18)) # True
print(Person.is_adult(17)) # False

6.2 继承

Java 继承

public class Student extends Person {
private String major;

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

public String getMajor() {
return major;
}

public void setMajor(String major) {
this.major = major;
}

@Override
public void introduce() {
super.introduce();
System.out.println("I am majoring in " + major + ".");
}
}

Python 继承

class Student(Person):
"""学生类"""

def __init__(self, name, age, major):
super().__init__(name, age)
self.major = major

def introduce(self):
"""自我介绍"""
super().introduce()
print(f"I am majoring in {self.major}.")

对比

特性JavaPython
继承语法extends 关键字括号中指定父类
调用父类构造方法super()super().__init__()
方法重写@Override 注解直接重写方法
多继承不支持(接口除外)支持
访问父类方法super().method()super().method()

Python 继承示例

# 父类
class Person:
def __init__(self, name, age):
self.name = name
self.age = age

def introduce(self):
print(f"My name is {self.name} and I am {self.age} years old.")

# 子类
class Student(Person):
def __init__(self, name, age, major):
super().__init__(name, age)
self.major = major

def introduce(self):
super().introduce()
print(f"I am majoring in {self.major}.")

# 另一个子类
class Employee(Person):
def __init__(self, name, age, company):
super().__init__(name, age)
self.company = company

def introduce(self):
super().introduce()
print(f"I work at {self.company}.")

# 创建对象
student = Student("Alice", 20, "Computer Science")
employee = Employee("Bob", 30, "Google")

# 调用方法
student.introduce()
# My name is Alice and I am 20 years old.
# I am majoring in Computer Science.

employee.introduce()
# My name is Bob and I am 30 years old.
# I work at Google.

# 检查类型
print(isinstance(student, Student)) # True
print(isinstance(student, Person)) # True
print(isinstance(employee, Person)) # True
print(isinstance(student, Employee)) # False

6.3 多继承

Java 多继承

  • Java 不支持类的多继承
  • 只能通过接口实现多继承

Python 多继承

  • Python 支持类的多继承
  • 使用 C3 线性化算法解决菱形继承问题

Python 多继承示例

# 基类 1
class Animal:
def speak(self):
print("Animal speaks")

# 基类 2
class Pet:
def be_friendly(self):
print("Pet is friendly")

# 多继承子类
class Dog(Animal, Pet):
def bark(self):
print("Dog barks")

# 创建对象
dog = Dog()

# 调用父类方法
dog.speak() # Animal speaks
dog.be_friendly() # Pet is friendly

# 调用子类方法
dog.bark() # Dog barks

# 方法解析顺序
print(Dog.__mro__) # 显示方法解析顺序

6.4 封装

Java 封装

  • 使用访问修饰符(public, private, protected)
  • 提供 getter 和 setter 方法
  • 封装成员变量

Python 封装

  • 没有访问修饰符
  • 使用命名约定:
    • _variable:单下划线,表示保护成员
    • __variable:双下划线,表示私有成员(会被名称修饰)
  • 可以使用 property 装饰器创建属性

对比示例

特性JavaPython
私有成员private 关键字__variable(名称修饰)
保护成员protected 关键字_variable(命名约定)
公共成员public 关键字无下划线
Getter/Setter显式方法@property 装饰器

Python 封装示例

class Person:
def __init__(self, name, age):
self.name = name # 公共属性
self._age = age # 保护属性
self.__salary = 50000 # 私有属性

# Getter 方法
@property
def age(self):
return self._age

# Setter 方法
@age.setter
def age(self, value):
if value >= 0:
self._age = value
else:
raise ValueError("Age cannot be negative")

# 访问私有属性的方法
def get_salary(self):
return self.__salary

def set_salary(self, value):
if value >= 0:
self.__salary = value
else:
raise ValueError("Salary cannot be negative")

# 创建对象
person = Person("Alice", 25)

# 访问公共属性
print(person.name) # Alice

# 访问保护属性(不推荐)
print(person._age) # 25

# 访问私有属性(会报错)
# print(person.__salary) # AttributeError

# 通过方法访问私有属性
print(person.get_salary()) # 50000

# 使用 property
person.age = 30
print(person.age) # 30

# 尝试设置负值(会报错)
# person.age = -5 # ValueError

6.5 多态

Java 多态

  • 编译时多态:方法重载
  • 运行时多态:方法重写
  • 基于继承和接口

Python 多态

  • 动态类型,天然支持多态
  • 鸭子类型:"如果它走路像鸭子,叫起来像鸭子,那么它就是鸭子"
  • 方法重写
  • 不需要显式的接口定义

对比示例

特性JavaPython
方法重载支持(相同方法名,不同参数)不支持(后定义的方法会覆盖前一个)
方法重写支持支持
接口显式定义隐式(鸭子类型)
多态实现基于类型基于行为

Python 多态示例

# 多态示例
class Animal:
def speak(self):
pass

class Dog(Animal):
def speak(self):
return "Woof!"

class Cat(Animal):
def speak(self):
return "Meow!"

class Duck(Animal):
def speak(self):
return "Quack!"

# 多态函数
def make_animal_speak(animal):
print(animal.speak())

# 创建对象
dog = Dog()
cat = Cat()
duck = Duck()

# 调用函数(多态)
make_animal_speak(dog) # Woof!
make_animal_speak(cat) # Meow!
make_animal_speak(duck) # Quack!

# 鸭子类型示例
class Car:
def speak(self):
return "Vroom!"

# 即使不是 Animal 的子类,只要有 speak 方法就可以使用
car = Car()
make_animal_speak(car) # Vroom!

6.6 抽象类和接口

Java 抽象类和接口

  • 抽象类:使用 abstract 关键字,不能实例化,可以有抽象方法和具体方法
  • 接口:使用 interface 关键字,只能有抽象方法(Java 8+ 可以有默认方法)

Python 抽象类和接口

  • 抽象类:使用 abc 模块,不能实例化,可以有抽象方法和具体方法
  • 接口:没有专门的接口关键字,使用抽象类或鸭子类型

Python 抽象类示例

from abc import ABC, abstractmethod

class Shape(ABC):
"""形状抽象类"""

@abstractmethod
def area(self):
"""计算面积"""
pass

@abstractmethod
def perimeter(self):
"""计算周长"""
pass

def description(self):
"""描述方法"""
return f"This is a {self.__class__.__name__}"

class Circle(Shape):
"""圆形类"""

def __init__(self, radius):
self.radius = radius

def area(self):
return 3.14159 * self.radius ** 2

def perimeter(self):
return 2 * 3.14159 * self.radius

class Rectangle(Shape):
"""矩形类"""

def __init__(self, width, height):
self.width = width
self.height = height

def area(self):
return self.width * self.height

def perimeter(self):
return 2 * (self.width + self.height)

# 创建对象
circle = Circle(5)
rectangle = Rectangle(4, 6)

# 调用方法
print(circle.area()) # 78.53975
print(circle.perimeter()) # 31.4159
print(circle.description()) # This is a Circle

print(rectangle.area()) # 24
print(rectangle.perimeter()) # 20
print(rectangle.description()) # This is a Rectangle

# 尝试实例化抽象类(会报错)
# shape = Shape() # TypeError

6.7 特殊方法

Python 有许多特殊方法(魔术方法),用于自定义类的行为。

常用特殊方法

方法描述示例
__init__构造方法def __init__(self, name):
__str__字符串表示def __str__(self):
__repr__正式字符串表示def __repr__(self):
__eq__等于比较def __eq__(self, other):
__lt__小于比较def __lt__(self, other):
__add__加法运算def __add__(self, other):
__len__长度def __len__(self):
__getitem__索引访问def __getitem__(self, index):
__setitem__索引赋值def __setitem__(self, index, value):
__call__使对象可调用def __call__(self, *args, **kwargs):

特殊方法示例

class Vector:
"""向量类"""

def __init__(self, x, y):
self.x = x
self.y = y

def __str__(self):
"""字符串表示"""
return f"Vector({self.x}, {self.y})"

def __repr__(self):
"""正式字符串表示"""
return f"Vector({self.x}, {self.y})"

def __add__(self, other):
"""向量加法"""
return Vector(self.x + other.x, self.y + other.y)

def __sub__(self, other):
"""向量减法"""
return Vector(self.x - other.x, self.y - other.y)

def __mul__(self, scalar):
"""向量乘法"""
return Vector(self.x * scalar, self.y * scalar)

def __eq__(self, other):
"""相等比较"""
return self.x == other.x and self.y == other.y

def __len__(self):
"""向量长度"""
return int((self.x ** 2 + self.y ** 2) ** 0.5)

# 创建对象
v1 = Vector(3, 4)
v2 = Vector(1, 2)

# 调用特殊方法
print(v1) # Vector(3, 4)
print(v1 + v2) # Vector(4, 6)
print(v1 - v2) # Vector(2, 2)
print(v1 * 2) # Vector(6, 8)
print(v1 == v2) # False
print(len(v1)) # 5

6.8 类的属性和方法

Java 类成员

  • 静态变量(类变量)
  • 实例变量
  • 静态方法
  • 实例方法
  • 构造方法
  • 抽象方法
  • 最终方法(final)

Python 类成员

  • 类变量
  • 实例变量
  • 类方法(@classmethod)
  • 静态方法(@staticmethod)
  • 实例方法
  • 特殊方法
  • 属性(@property)

对比

成员类型JavaPython
类变量static 关键字类内部定义的变量
静态方法static 关键字@staticmethod 装饰器
类方法无直接对应@classmethod 装饰器
实例方法普通方法普通方法
属性显式 getter/setter@property 装饰器

6.9 练习

  1. 类定义练习:定义一个 Car 类,包含品牌、型号、年份等属性,以及启动、停止等方法
  2. 继承练习:定义一个 ElectricCar 类,继承自 Car 类,添加电池容量等属性
  3. 多态练习:定义一个 Shape 抽象类,派生出 CircleRectangleTriangle 子类,实现面积计算
  4. 封装练习:定义一个 BankAccount 类,使用 @property 装饰器保护余额属性
  5. 特殊方法练习:定义一个 Point 类,实现 __add____sub____eq__ 等特殊方法
  6. 多继承练习:定义一个 Teacher 类,继承自 PersonEmployee

6.10 小结

  • Python 的类定义比 Java 更简洁,不需要访问修饰符
  • Python 使用 self 而不是 this 关键字
  • Python 支持多继承,而 Java 不支持
  • Python 使用命名约定和 @property 装饰器实现封装
  • Python 基于鸭子类型实现多态,更加灵活
  • Python 有丰富的特殊方法,可以自定义类的行为
  • Python 的抽象类使用 abc 模块实现
  • Python 的静态方法和类方法使用装饰器标记

通过本章的学习,你已经了解了 Java 和 Python 在面向对象编程上的主要区别。接下来,我们将学习 Python 的异常处理。