对于很多Java开发者习惯了 Java 的面向对象编程范式,在刚接触 Python 时是不是总觉得很别扭?比如没有public/private关键字、构造方法不能重载、甚至连this都变成了显式的self?今天我们就以一个简单的Squre类为例,一步步了解Python OOP和Java的核心差异。
首先,我们定义一个Java版本的Square类,它继承自抽象的Shape类,包含位置、边长属性和周长面积计算方法:
abstract class Shape { abstractintarea(); abstractintperimeter();}public class Square extends Shape { private int x; private int y; private int side; // 构造方法1:直接传入坐标和边长 publicSquare(int x, int y, int side) { this.x = x; this.y = y; this.side = side; } // 构造方法2:通过两个对角点计算边长 publicSquare(int x1, int y1,int x2,int y2) { if(Math.abs(x2 - x1) != Math.abs(y2 - y1) ) { throw new IllegalArgumentException("Square must have equal sides"); } x = x1; y = y1; side = (x2 - x1); } publicintarea() { return side * side; } publicintperimeter() { return 4 * side; } public String toString() { return String.format("Square[x=%d,y=%d,side=%d", x, y, side); }}
接下来我们把它转成Python版,看看两者的差异,Java里可以重载多个构造方法,但Python没有方法重载,只能有一个__init__(),空代码块也不允许
# 第一步:继承Shape类(先定义空类)class Shape: pass# Python的构造方法只有__init__,且第一个参数必须是selfclass Square(Shape): # 核心构造方法:显式声明self,初始化属性 def __init__(self, x, y, side): self.x = x # self等价于Java的this,但必须显式写 self.y = y self.side = side# 创建实例:无需new关键字s1 = Square(1, 3, 10)s2 = Square(x=1, y=3, side=10) # 还支持关键字参数,Java没有这个特性
那么Java里的第二个构造方法(4个参数)怎么实现?Python用@classmethod装饰器来做工厂方法:
@classmethoddef make_square_from_points(cls, x1, y1, x2, y2): if abs(x2 - x1) != abs(y2 - y1): raise ValueError("Square must have equal sides") return cls(x1, y1, abs(x2 - x1)) # cls等价于类本身,对应Java的Square
Java通过public/protected/private严格控制访问权限,但python没有强制封装,约定俗成的以_开头的属性/方法视为私有,但它并不是真正的禁止访问,双下划线__开头会触发名词改写
class Square(Shape): def __init__(self, x, y, side): self._x = x # 约定为私有属性 self._y = y self.__side = side # 名称改写,外部无法直接通过__side访问# 外部仍能访问(只是不推荐)sq = Square(1,2,3)print(sq._x) # 能输出1# print(sq.__side) # 报错print(sq._Square__side) # 仍能通过改写后的名称访问
Java用toString()返回对象字符串,python则分两个方法,__str__ 面向用户的友好字符串,__repr__面向开发者的字符串
class Square(Shape): # ... 其他代码 ... def __str__(self): return f"Square [x={self._x}, y={self._y}, side={self.__side}]" def __repr__(self): return f"Square({self._x}, {self._y}, {self.__side})"sq = Square(5,5,10)print(sq) # 输出__str__的结果:Square [x=5, y=5, side=10]print(repr(sq)) # 输出__repr__的结果:Square(5,5,10)
需要注意的是Python中字符串拼接不会自动调用__str__,必须显示转换:
# print(sq + ".") # 报错:不能拼接对象和字符串print(str(sq) + ".") # 正确:Square [x=5, y=5, side=10].
对于异常方法,Java用throw抛异常,python用raise,还能自定义异常类:
# 自定义异常(继承ValueError)class MalformedSquareException(ValueError): def __init__(self, x1, y1, x2, y2): self.x1 = x1 self.y1 = y1 self.x2 = x2 self.y2 = y2 def __str__(self): return f"点({self.x1},{self.y1})和({self.x2},{self.y2})无法构成正方形"# 使用自定义异常@classmethoddef make_square_from_points(cls, x1, y1, x2, y2): if abs(x2 - x1) != abs(y2 - y1): raise MalformedSquareException(x1, y1, x2, y2) return cls(x1, y1, abs(x2 - x1))# 捕获异常try: sq = Square.make_square_from_points(4,5,7,11)except MalformedSquareException as e: print(e) # 输出:点(4,5)和(7,11)无法构成正方形
对于抽象,Java用abstract关键字定义,python需要导入abc模块实现:
import abc# 定义抽象类(两种方式二选一)class Shape(metaclass=abc.ABCMeta): # 抽象方法:必须实现,且需要有方法体(用...占位) @abc.abstractmethod def area(self): ... @abc.abstractmethod def perimeter(self): ... # Python还支持静态抽象方法(Java没有这个概念) @staticmethod @abc.abstractmethod def nb_sides(): ...# 实现抽象类class Square(Shape): # ... 其他代码 ... def area(self): return self.__side * self.__side def perimeter(self): return 4 * self.__side @staticmethod def nb_sides(): return 4
另一个点,Java仅支持字符串+重载,而Python可以重载几乎所有运算符(__mul__、__lt__、__gt__等):
class Square(Shape): # ... 其他代码 ... # 重载*:sq * 3 表示边长放大√3倍 def __mul__(self, n): return Square(self._x, self._y, self.__side * math.sqrt(n)) # 重载<:比较边长大小 def __lt__(self, other): return self.__side < other.__side # 重载>:比较边长大小 def __gt__(self, other): return self.__side > other.__side# 使用重载的运算符s1 = Square(0,0,10)s2 = s1 * 3 # 调用__mul__print(s1 < s2) # True,调用__lt__
最后,Python 的 OOP 看似 “松散”,没有 Java 那么多严格的语法约束,但其实是用 “约定优于配置” 的思路,兼顾了灵活性和可读性。对于 Java 开发者来说,不用纠结 “为什么 Python 没有 private”,而是要理解 Python 的设计模式。