各语言特有 · 37
类与对象
把数据(字段)和行为(方法)放进同一个模板,再用它创建多个对象。
本单元适用于 Python · JavaScript · TypeScript · Java · C# · C++。
C 没有类,用结构体(struct)加函数完成同样的事(见结构体一课)。
Craykon 没有自定义类,而是把这类数据放进 字典(),如 学生["姓名"]。
类是设计图,对象是按设计图造出来的实物。
下面的示例用带有姓名和分数的 Student 类创建几个学生,按分数从高到低排序,并连同等级一起输出。
class Student: def __init__(self, name, score): self.name = name self.score = score def grade(self): if self.score >= 90: return "A" if self.score >= 80: return "B" return "C" def describe(self): return f"{self.name} {self.score} {self.grade()}" n = int(input())students = []for _ in range(n): name, score = input().split() students.append(Student(name, int(score))) students.sort(key=lambda s: s.score, reverse=True)for s in students: print(s.describe())class Student { constructor(name, score) { this.name = name; this.score = score; } grade() { if (this.score >= 90) return "A"; if (this.score >= 80) return "B"; return "C"; } describe() { return `${this.name} ${this.score} ${this.grade()}`; }} const lines = require("fs").readFileSync(0, "utf8").trim().split("\n");const n = Number(lines[0]);const students = [];for (let i = 1; i <= n; i++) { const [name, score] = lines[i].trim().split(" "); students.push(new Student(name, Number(score)));} students.sort((a, b) => b.score - a.score);for (const s of students) console.log(s.describe());import { readFileSync } from "node:fs"; class Student { readonly name: string; score: number; constructor(name: string, score: number) { this.name = name; this.score = score; } grade(): string { if (this.score >= 90) return "A"; if (this.score >= 80) return "B"; return "C"; } describe(): string { return `${this.name} ${this.score} ${this.grade()}`; }} const lines: string[] = readFileSync(0, "utf8").trim().split("\n");const n: number = Number(lines[0]);const students: Student[] = [];for (let i = 1; i <= n; i++) { const [name, score] = lines[i].trim().split(" "); students.push(new Student(name, Number(score)));} students.sort((a, b) => b.score - a.score);for (const s of students) console.log(s.describe());import java.util.*; class Student { private final String name; private final int score; Student(String name, int score) { this.name = name; this.score = score; } int getScore() { return score; } String grade() { if (score >= 90) return "A"; if (score >= 80) return "B"; return "C"; } String describe() { return name + " " + score + " " + grade(); }} public class Main { public static void main(String[] args) { Scanner sc = new Scanner(System.in); int n = sc.nextInt(); List<Student> students = new ArrayList<>(); for (int i = 0; i < n; i++) { students.add(new Student(sc.next(), sc.nextInt())); } students.sort(Comparator.comparingInt(Student::getScore).reversed()); for (Student s : students) System.out.println(s.describe()); }}using System;using System.Collections.Generic; class Student{ public string Name { get; } public int Score { get; } public Student(string name, int score) { Name = name; Score = score; } public string Grade() { if (Score >= 90) return "A"; if (Score >= 80) return "B"; return "C"; } public string Describe() => $"{Name} {Score} {Grade()}";} class Program{ static void Main() { int n = int.Parse(Console.ReadLine()); var students = new List<Student>(); for (int i = 0; i < n; i++) { string[] parts = Console.ReadLine().Split(' '); students.Add(new Student(parts[0], int.Parse(parts[1]))); } students.Sort((a, b) => b.Score.CompareTo(a.Score)); foreach (var s in students) Console.WriteLine(s.Describe()); }}#include <algorithm>#include <iostream>#include <string>#include <utility>#include <vector> class Student {public: Student(std::string name, int score) : name_(std::move(name)), score_(score) {} int score() const { return score_; } std::string grade() const { if (score_ >= 90) return "A"; if (score_ >= 80) return "B"; return "C"; } std::string describe() const { return name_ + " " + std::to_string(score_) + " " + grade(); } private: std::string name_; int score_;}; int main() { int n; std::cin >> n; std::vector<Student> students; for (int i = 0; i < n; i++) { std::string name; int score; std::cin >> name >> score; students.emplace_back(name, score); } std::sort(students.begin(), students.end(), [](const Student& a, const Student& b) { return a.score() > b.score(); }); for (const Student& s : students) std::cout << s.describe() << '\n';}3 Mina 82 Joon 95 Sora 78
Joon 95 A Mina 82 B Sora 78 C
要点
- 类里写明对象拥有的数据(字段)和对象能做的事(方法)。
- 构造函数是在创建对象时填入初始值的特殊函数。
- 在方法里,对象自己是
this(Python 是self)。
参数同名时,用this.name = name区分。 - 用访问修饰符(
public、private)决定外部能用哪些成员。
字段通常藏起来,通过方法或属性读取。 - 对象列表按某个字段排序。
把“比较什么”交给排序函数即可。
各语言的写法
| Python | class Student: 里写构造函数 def __init__(self, name, score):。字段用 self.name = name 创建,所有方法的第一个参数都是 self。创建对象不用 new:Student("Mina", 82)。排序: students.sort(key=lambda s: s.score, reverse=True) |
|---|---|
| JavaScript | class Student { constructor(name, score) { this.name = name; } }。用 new Student("Mina", 82) 创建对象,漏掉 new 会报 TypeError: Class constructor Student cannot be invoked without 'new'。排序: students.sort((a, b) => b.score - a.score) |
| TypeScript | 和 JavaScript 同样的写法,再用 score: number; 先声明字段。private、readonly 只在编译时检查,运行时就消失了。真要隐藏,用 JavaScript 的私有字段 #score |
| Java | 构造函数和类同名,不写返回类型。 字段用 private 隐藏,通过 getScore() 这样的 getter 读取。不写修饰符时只在同一个包内可见。 排序: students.sort(Comparator.comparingInt(Student::getScore).reversed()) |
| C# | 用属性 public int Score { get; },外部可以像字段一样读 s.Score。不写修饰符的成员是 private。排序: students.Sort((a, b) => b.Score.CompareTo(a.Score)) |
| C++ | class 默认 private,struct 默认 public,区别仅此而已,所以示例自己写了 public: 和 private:。构造函数用初始化列表 : name_(name), score_(score) 填字段,方法后面的 const 表示“不修改对象”。对象是值,不用 new:Student s("Mina", 82);。排序: std::sort 加 lambda [](const Student& a, const Student& b) { return a.score() > b.score(); } |
常见错误
方法漏写了 selfPython
class Student:
def grade():
return "A"
Student().grade()→ TypeError: Student.grade() takes 0 positional arguments but 1 was given。在方法里不加 self. 直接写 score 会报 NameError
第一个参数写 self,字段总是用 self.score 读取。
默认值用了列表 []Python
def __init__(self, name, tags=[]):
self.tags = tags→ 这个列表只在 def 执行时创建一次。给一个学生加的标签,其他学生也能看到
用 tags=None 接收,在构造函数里新建列表。
用 == 比较对象的内容Java · C#
new Student("Mina", 82) == new Student("Mina", 82)→ false —— == 看的是是不是同一个对象。在重新定义 equals 之前,它和 == 完全一样
内容用 equals(C# 是 Equals)比较,并自己重新定义这个方法,或者使用 record。
各语言的差异
执行
C++ 的对象是值,
执行
b = a 后再修改 b,在 Python、JavaScript、TypeScript、Java、C# 中 a 也会变(两个名字指向同一个对象)。C++ 的对象是值,
Student b = a; 和 for (Student s : students) 都会复制,想操作原对象就用 Student&(只读时用 const Student&)。