您的位置  > 互联网

和Java1.5的使用者是怎样的一种体验?

到目前为止,我们已经使用函数和模块来操作数据。 这称为面向过程的编程。 然而,还有另一种组织程序的方法:将数据和函数组合起来,并将它们放入称为对象的东西中。 这称为面向对象的编程范式。 在大多数情况下,您可以使用过程编程,但是当编写大型程序或遇到更适合这种方法的东西时,您可以使用基于对象的编程技术。

类和对象是面向对象编程的两个主要概念。 类创建一个新类型,对象是该类的实例。 一种观点是,可以将 int 类型的变量转换为存储整数的变量,该变量是 int 类的实例。

给静态语言程序员的提示

请注意,整数被视为 int 类的对象。 这与 C++ 和 Java(1.5 之前的版本)不同。 在这些语言中,整数被视为基本数据类型。

C# 和 Java 1.5 的用户会发现这个概念与装箱和拆箱非常相似。

对象可以使用原始变量(属于该对象)来存储数据。 属于对象或类的变量称为域。 对象可以通过使用属于类的函数来实现某些功能。 这些函数称为类的方法。 这个术语非常重要,因为它帮助我们区分独立函数和变量以及属于对象或类的变量和函数。 一般来说,字段和方法可以被视为类的属性。

有两种类型的字段 - 它们可以属于类的每个实例(即对象),也可以属于类本身。 它们分别称为实例变量和类变量。

使用class关键字创建一个类。 此类的字段和方法在代码块中列出。

关于自我

类方法和普通函数只有一个区别——它们必须在入口参数列表的开头有一个额外的形参,但是当你调用这个方法时,你不会给这个参数赋任何值,你会提供给它。 这个特殊参数指向对象本身,其名称约定为self。

虽然您可以给这个参数起任何名称,但强烈建议使用 self - 任何其他名称肯定会引起不满。 使用标准名称有很多优点 - 如果您使用 self,任何程序的渲染器都会自动识别它,甚至某些特定的 IDE () 可以为您提供额外的帮助。

给 C++/Java/C# 用户的提示

self in 相当于C++中的this指针以及Java和C#中的this引用。

您一定想知道如何为 self 赋值,以及为什么不必赋值。 举个例子就可以清楚地说明这些问题。 假设您有一个名为 的类以及该类的一个名为 的对象。 当你需要像这样调用这个对象的方法时:.(arg1, arg2),这条语句会自动转换成.(, arg1, arg2)的形式——这就是self的特殊地方。

这也意味着,如果你有一个没有声明任何形参的方法,它仍然有一个入口参数——self。

种类

最简单的类可能类似于以下代码(另存为文件 .py )。

输出:

这是如何运作的

我们使用类语句和类名创建一个类。 接下来是形成类主体的语句块。 在此示例中,我们使用 pass 语句声明一个空语句块。

之后,我们使用类的名称和一对括号创建该类的对象(实例)。 (我们将在下一节中查看更多示例。我们通过简单地打印变量 p 来确认变量类型。它是模块中某个类的对象。

请注意,还显示了该对象的内存地址。 该地址在您的计算机上可能具有不同的值,因为只要找到可用内存空间,该对象就会存储在那里。

方法

我们已经讨论过,类和对象可以像普通函数一样拥有方法。 但这些方法有一个额外的 self 变量。 现在让我们看一个示例(另存为文件 .py)。

输出:

这是如何运作的

现在让我们仔细看看 self 是如何工作的。 注意,方法中没有获取任何参数,但是定义方法时仍然有一个 self 参数。

对于类来说,许多方法名称具有特别重要的意义。 现在,让我们检查一个重要的方法。

示例(另存为文件 .py):

输出:

这是如何运作的

在这里,我们定义方法。 除了通常的 self 变量之外,该方法还有一个参数名称。 在这里我们创建一个新字段,也称为名称。 请注意,这里有两个不同的变量,但都称为“名称”。 这没有问题,因为点分符号 self.name 表示名为“name”的字段是类的一部分,另一个名称是局部变量。 这里我们明确指出要使用哪个变量,因此不存在任何冲突。

创建新的类实例p时,我们通过调用类名来创建新实例,并在下面的括号中填写初始化参数:p = ('')。

我们不显式调用此方法,这就是此方法的特殊之处。

如方法所示,现在我们可以在方法中使用 self.name 字段。

类和对象中的变量

我们已经讨论了有关类和对象中的函数(又称方法)的部分,现在让我们学习有关数据的部分。 数据部分(即字段)没有什么特别的,只是绑定到类或对象名称空间的普通变量。 这意味着这些变量仅在与这些类​​和对象关联的上下文中有效。 这就是为什么它们被称为名称空间。

有两种类型的字段——类变量和对象变量。 这是通过它们属于类还是对象来区分的。

类变量是共享的——它们可以被该类的所有对象访问。 类变量只有一份副本,这意味着当一个对象更改类变量时,该更改将发生在该类的所有对象中。

对象变量属于每个对象(实例)本身。 在这种情况下,每个对象都有自己的域(在不同的对象中,这些变量不共享,它们不相关,只是具有相同的名称)。 一个例子会让这更容易理解(保存到 file.py):

输出:

这是如何运作的

这将是一个很长的解释,但是它将帮助我们非常自然地理解类变量和对象变量。 这里,属于 Robot 类,因此是一个类变量。 name 变量属于每个个体(使用 self 指向),因此是一个对象变量。

由此,我们可以推断应该使用 Robot 来访问类变量。 而不是自己。; 我们可以推断,在对象方法中,应该使用 self.name 来访问对象变量名。 请记住类变量和对象变量之间的这个简单区别。 另请记住,与类对象同名的对象变量将屏蔽该类变量。

除了Robot.之外,我们还可以通过self..来访问这个类对象,因为每个对象都通过self..指向自己的类。 属性。

它实际上是属于类的方法,而不是对象的方法,这意味着我们可以使用或定义它。 区别在于我们是否需要知道我们属于哪个类别。 既然我们要声明一个类变量,那就使用它吧。

我们使用装饰器将该方法标记为类方法。

装饰器可以被认为是调用包装函数(包装另一个函数的函数,以便它可以在调用内部函数之前和之后执行某些操作)的快捷方式,因此使用 @ 装饰器相当于调用:

请注意,该方法用于初始化 Robot 实例并为机器人指定名称。 在此方法中,每次获得一个新机器人时,我们都会将其加 1。此外,请注意 self.name 变量的值因对象而异,这体现了对象变量的自然本质。

请记住,您只能通过 self 指向同一对象的变量和方法。 这称为属性引用 ( )。

在这个程序中,我们还可以看到 () 在类和方法值中的使用。 在运行时,我们可以通过Robot访问该类的文档字符串。 以及通过 Robot 的方法的文档字符串...

在die方法中,我们简单地减少Robot。 1.

所有班级成员都是公开的。 有一个例外:如果您使用双下划线前缀(例如),则名称破坏规则 (name-) 将应用于该变量并将其设为私有。

因此,结论是,所有仅在类和对象内部的变量都应该以下划线开头,所有其他名称都是公共的,可以被其他类和对象访问。 请记住,这是约定而不是要求(使用双下划线时除外)。

给 C++/Java/C# 用户的提示

在 中,所有成员(包括数据成员)都是公共的,所有方法都是虚拟的。

继承

面向对象编程的主要优点之一是代码重用,而实现代码重用的主要方式之一就是通过继承。 继承可以被认为是类之间类型和子类型关系的实现。

假设您想编写一个程序来跟踪大学的老师和同学。 他们有一些共同的特征,如姓名、年龄、地址等。他们也有一些独特的特征,如教师的工资、课程、离职等,以及学生的成绩和学费。

您当然可以构建两个单独的类来驱动这两种类型的程序。 但是当需要添加一个公共属性时,就意味着需要同时添加在这两个独立的类中。 这很快就会变得非常笨拙。

更好的办法是构造一个共同的类,然后让老师和学生分别继承这个类。 也就是说,它们都是这个类型(类)的子类型,以后我们还可以为这些子类型添加独特的属性。

这种方法有很多好处。 如果我们想在类中添加或更改功能,这也会自动反映在子类型中。 例如,只需修改班级即可为学生和教师添加新的身份证字段。 然而,一种子类型的变化不会反映在其他子类型中。 另一个好处是您可以使用对象来指向任何教师或学生对象。 这在某些情况下很有用,例如计算学校的总人数。 这称为多态性:如果存在超类型,则在任何情况下都可以替换子类型。 也就是说,子类型的对象可以被视为超类型的实例。

另外,请注意,我们重用了父类中的代码。 没有必要在不同的类中重复这段代码,只要我们不使用单独的类来实现它即可。

让我们看一下这个例子(另存为.py):

输出:

这是如何运作的

要使用继承,我们在类名后面的括号中指定父类的类名。 (例如,类 () )。 然后我们可以看到,在方法中,通过self变量显式的调用了父类的方法,来初始化子类对象中属于父类的部分。 记住这一点非常重要——由于我们在子类中定义了方法,因此父类中的构造函数不会被自动调用,您必须显式调用它。

相反,如果我们在子类中没有定义方法,那么就会自动调用父类中的构造函数。

当我们想要将一个实例视为或作为一个实例并想要调用tell方法时,我们只需要简单地输入.tell或.tell即可。 我们在每个子类中定义另一个新的tell方法(父类的tell方法是其中的一部分)来定制子类的功能。 因为我们这样做了,所以当我们调用.tell时,会使用子类中的tell方法,而不是父类中的tell方法。 但是,如果我们在子类中没有定义tell方法,那么就会使用父类中的方法。 始终首先在实际子类中查找方法。 如果不存在,则在子类声明语句中按顺序在父类中查找(这里我们只有一个父类,但可以声明多个父类)。

请注意术语 - 如果继承元组中列出了多个类,则称为多重继承。

end参数用在父类的tell()方法中调用的打印函数中,这样打印完一句后,下一次打印将紧接在第​​一句之后,而不会换行。 这个技巧使得 print 函数不会在输出末尾打印 \n 符号(换行符)。

我们已经探索了类和对象以及相关术语的各个方面。 我们也体验到了面向对象编程的优点和缺点。 它是高度面向对象的,因此仔细理解这些内容从长远来看将对您有很大帮助。

跟我来! 在下一章中,我们将学习如何处理输入/输出以及如何操作文件。