1. 对象到底是什么
1.1 对象不是玄学:从生活类比说起
接触编程久了你会发现,不管是 Python、Java、JavaScript、C++ 还是 PHP,绕不开的一个词就是“对象”。尤其是刚入行那阵子,看到“面向对象编程”这几个字就头大,总觉得这是什么高深莫测的东西。
其实对象这东西没那么玄。你打开外卖软件点一份黄焖鸡,这份黄焖鸡就是“对象”;你发一条微博,这条微博数据也是“对象”;你手机通讯录里的每一个联系人,还是“对象”。对象就是“把一堆相关数据和操作打包在一起的一个东西”,仅此而已。
再说得直白一点:对象就是一个“数据 + 行为”的组合体。数据叫做属性,行为叫做方法。比如一个 Student 对象,name、age、score 是属性,study()、exam() 是方法。你把一个学生的所有信息和动作封装在一起,就得到了一个学生对象。
类和对象的关系也很容易理解。类是“图纸”,对象是“按照图纸造出来的实物”。你先定义 class Student,然后根据这个类 new 出 s1、s2、s3 三个对象,这三个对象各自有独立的数据,但共享同一套行为逻辑。图纸只有一张,实物可以造无数个。
1.2 不同语言里的对象长什么样
对象在不同语言里的表现形态其实差异不小,搞清楚这些差异,能省掉你日后跨语言开发时踩坑的时间。
在 Python 里,万物皆对象。整数是对象,字符串是对象,函数也是对象。类本身也是对象,因为 Python 的类在运行时可以被动态创建和修改。这就牵扯出“类对象”和“实例对象”的区别:类对象是模板本身,实例对象是根据模板创建出来的具体个体。
python复制class Dog:
def __init__(self, name):
self.name = name
def bark(self):
print(f"{self.name} is barking")
# d 是 Dog 类的实例对象
d = Dog("旺财")
d.bark()
在 Java 和 C# 里,对象的创建更严格,必须通过类来实例化,而且对象在堆内存中管理,存在垃圾回收机制。Java 里你还会经常碰到“把对象当方法参数”的场景,这里有个经典的坑:Java 方法传参时,基本类型是值传递,对象类型传递的是引用的副本。也就是说,你在方法内部修改对象属性,会影响原对象;但如果你给参数重新赋一个新对象,原对象不受影响。
在 JavaScript 里,对象更像是一组键值对的集合,而且它的继承机制不是基于类,而是基于原型链。ES6 引入 class 语法糖之后写起来像面向对象了,但底层仍然是原型链的玩法。再加上 this 的动态绑定,JavaScript 的对象机制可以说是最容易让人翻车的地方之一,后面我会专门说这个。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 对象的创建与基本操作
2.1 创建对象的几种姿势
不同场景下,创建对象的方式选择,直接关系到代码的可读性和维护成本。我按语言分别说几个常见姿势。
先说 JavaScript。最基础的方式是字面量:
javascript复制const user = {
name: "张三",
age: 25,
login() {
console.log(`${this.name} 登录了`);
}
};
这种方式简单直接,适合创建单个独立对象。但如果要创建多个结构相同的对象,就得用构造函数或者 ES6 的 class:
javascript复制class User {
constructor(name, age) {
this.name = name;
this.age = age;
}
}
const u1 = new User("李四", 30);
再说 Python。最常见的做法是直接实例化类,配合 __init__ 方法初始化属性。还有一种方式是用 dataclass 装饰器,适合那些主要用来存数据的类:
python复制from dataclasses import dataclass
@dataclass
class Product:
name: str
price: float
stock: int
p = Product("机械键盘", 399.0, 50)
Java 那边,除了最基本的 new 之外,Builder 模式在对象字段多的时候非常实用。用 Builder 可以避免构造器参数太多导致的可读性问题,也能省掉写一堆重载构造器的麻烦。
注意:对象创建最忌讳的是“一个类里塞了几十个字段”。如果一个类的职责太多,考虑拆分成多个小对象,比硬塞在一起要好维护得多。
2.2 判断对象为空的正确方式
“判断对象为空”是热搜词里非常高频的一个操作,而且各个语言都有自己的坑。
JavaScript 里,判断一个对象是否为空最常用的方式是 Object.keys(obj).length === 0。但要注意,{} 是空对象,null 和 undefined 不是对象。所以严谨的判断应该是:
javascript复制function isEmptyObject(obj) {
if (!obj || typeof obj !== "object") return false;
return Object.keys(obj).length === 0;
}
另一个陷阱是 JSON.stringify(obj) === "{}"。这个方法在对象有 undefined、函数、Symbol 属性时会失效,因为这些值会被直接忽略掉。
Python 里判断对象为空要看上下文。如果对象是 None,直接用 is None。如果是一个容器对象(列表、字典、集合),直接用 if not obj 就行,因为空容器会被隐式转换为 False。如果你自定义了一个类,想让它支持这种判断,可以实现 __bool__ 方法:
python复制class Order:
def __init__(self):
self.items = []
def __bool__(self):
return len(self.items) > 0
order = Order()
if not order:
print("订单是空的")
Java 里判断对象为空通常就是 obj == null,但为了应对字符串、集合这些常见的空状态,很多人会引入 Objects.isNull() 或者 Apache Commons 的 StringUtils.isEmpty()、CollectionUtils.isEmpty()。JDK 8 以后还有 Optional,可以避免显式的空指针判断:
java复制Optional<String> name = Optional.ofNullable(user.getName());
String result = name.orElse("默认值");
C# 那边的写法类似,用 is null 或 == null 都可以,但建议用前者,因为
