# Ruby 中的对象

> 原文： [https://zetcode.com/lang/rubytutorial/objects/](https://zetcode.com/lang/rubytutorial/objects/)

在 Ruby 教程的这一部分中，我们将简要介绍 Ruby 语言中对象的概念。 我们将在 OOP 一章中了解有关对象的更多信息。 我写了有关对象的本章，因为许多 Ruby 功能可能会使新手感到困惑，特别是如果他们已经知道任何其他编程语言的话。

Ruby 是一种面向对象的编程语言。 这意味着在 Ruby 程序中，我们使用对象。 从语言程序员的角度来看，Ruby 程序是令牌流。 这些标记是 Ruby 关键字，运算符，各种分隔符或字面值。 从语义的角度来看，Ruby 程序由对象组成。 这些对象是在 Ruby 脚本的生存期内创建和修改的。

有两种对象：内置对象和自定义对象。 内置对象是所有程序员都可以使用的预定义对象。 它们以 Ruby 语言的核心或各种库提供。 定制对象是由应用员为其应用域创建的。

必须先创建所有对象，然后才能使用它们。 我们经常使用术语对象实例化。 它是对象创建的同义词。 对象由数据和方法组成。 数据是对象的静态部分。 方法构成对象的动态部分。 对象被修改并通过方法彼此通信。

```ruby
#!/usr/bin/ruby

puts "Ruby language"

```

我们有一个简单的 Ruby 脚本。 如果我们熟悉某些程序语言（例如 Pascal 或 C），我们可能会看到一个名为`puts`的关键字或函数及其参数`"Ruby language"`（它是一个字符串）。

Ruby 是一种纯粹的面向对象的语言，情况有所不同。 `"Ruby language"`确实是一个字符串，它是一种常见的数据类型。 但这也是一个对象。 与所有对象一样，我们可以调用其方法。 这与其他语言有点不同。 `puts`是一种方法。 方法是在对象中定义的函数。 方法本身并不存在。 实际上，`puts`方法是`Kernel`模块的一部分。

```ruby
#!/usr/bin/ruby

Kernel.puts "Ruby language"
Kernel.puts "Ruby language".size

```

在上面的脚本中，我们有两个代码行。

```ruby
Kernel.puts "Ruby language"

```

在第一个示例中，我们调用的`puts`方法没有`Kernel`部分，可以将其省略。 这样可以节省时间和一些打字。 实际上，这是`Kernel.puts`正式电话的简写。 在 C# 中，我们有`Console.writeln`在 Java 中`System.println`。 想法是一样的。 方法必须与某个对象关联，或者，如果是类方法，则必须与一个类关联。

```ruby
Kernel.puts "Ruby language".size

```

在此代码行中，我们将`"Ruby language"`字符串的大小打印到控制台。 这可能会使使用其他语言编写代码的程序员感到困惑。 在其他语言中，字符串是无法修改的原始数据类型，并且缺少其自己的方法。 在 Ruby 中，字符串是完整对象，并且具有自己的方法。 `size`方法就是其中一种。 它以字符为单位返回字符串的大小。

```ruby
$ ./simple2.rb
Ruby language
13

```

代码示例的输出。

在下面的示例中，我们将看一个整数。 与字符串类似，整数值也是 Ruby 对象。

```ruby
#!/usr/bin/ruby

puts 6.object_id

puts 6.even?
puts 6.zero?

puts 6.class

```

在示例中，我们有一个整数 6。我们在数字上调用了一些方法。

```ruby
puts 6.object_id

```

6 是一个对象。 `object_id`是一种方法。 该方法返回与该对象关联的 ID。 每个对象都有一个 ID。 如果我们在对象上调用方法，则必须始终在两者之间放置点字符。

```ruby
puts 6.even?
puts 6.zero?

```

在这里，我们在 6 个对象上调用两个方法。 如果数字为偶数，则`even?`返回`true`。 如果数字等于零，则`zero?`方法返回`true`。 请注意，这两种方法都以问号结尾。 这是 Ruby 约定。 返回布尔值的方法以问号结尾。

```ruby
puts 6.class

```

`class`方法告诉我们正在处理哪种对象。 在我们的例子中 6 是`Fixnum`

```ruby
$ ./objectnumber.rb
13
true
false
Fixnum

```

代码示例输出。

## Ruby 对象创建

我们已经提到必须先创建 Ruby 对象，然后才能使用它们。 可以隐式或显式创建对象。 隐式对象创建是通过字面值符号创建对象。 显式对象的创建通过使用`new`关键字进行。 始终使用`new`关键字创建自定义对象。 必须从特定的类创建自定义对象。 类是对象的模板。 一个类可以用来创建许多对象。

```ruby
#!/usr/bin/ruby

class Being
end

puts 67
puts "ZetCode"

s = String.new "ZetCode"
puts s

# n1 = Fixnum.new 67
# puts n1

b = Being.new
puts b

```

该代码示例演示了如何在 Ruby 中创建对象。

```ruby
class Being
end

```

这是名为`Being`的自定义对象的模板。 使用`class`关键字创建模板。 自定义对象的模板通常放在源文件的顶部或单独的 Ruby 文件中。

```ruby
puts 67
puts "ZetCode"

```

在这两行中，我们使用两个对象。 `Fixnum`类型的 67 对象和`String`类型的`"ZetCode"`字符串。 67 和`"ZetCode"`是我们所谓的字面值。 字面值是类型的特定值的字面值表示。 这两个对象是由 Ruby 解释器在后台创建的。 Ruby 中的某些对象是通过在源代码中指定其字面值来创建的。

```ruby
s = String.new "ZetCode"
puts s

```

这是创建`String`对象的正式方法。 它等于之前使用字符串字面值的隐式创建。

```ruby
# n1 = Fixnum.new 67
# puts n1

```

并非所有内置对象都可以使用`new`方法创建。 此代码无法编译。 到目前为止，`Fixnum`数字只能用字面值表示法创建。

```ruby
b = Being.new
puts b

```

在这里，我们创建了自定义对象的实例。 `puts`方法为我们简要介绍了该对象。

```ruby
$ ./ocreation.rb
67
ZetCode
ZetCode
#<Being:0x9944d9c>

```

示例的输出。

我们将继续进行一些正式的对象创建。

```ruby
#!/usr/bin/ruby

s1 = String.new "Ruby"
puts s1.size
puts s1.downcase

a1 = Array.new 
a1.push 1, 2, 3
puts a1.include? 3
puts a1.empty?

r1 = Range.new 1, 6
puts r1.class
puts r1.include? 4

```

在示例中，我们创建了三个内置对象并调用了其中的一些方法。

```ruby
s1 = String.new "Ruby"
puts s1.size
puts s1.downcase

```

创建一个`String`对象。 我们调用对象的两种方法。 `size`方法返回字符串的大小。 `downcase`方法将字符串的字符小写。

```ruby
a1 = Array.new 
a1.push 1, 2, 3
puts a1.include? 3
puts a1.empty?

```

在这里，我们创建一个`Array`对象，并向其添加三个数字。 稍后我们调用两个数组方法。 `include?`方法检查特定值（在我们的例子中为 3）是否是数组的一部分。 `empty?`方法返回一个布尔值，指示该数组是否为空。

```ruby
r1 = Range.new 1, 6
puts r1.class
puts r1.include? 4

```

创建`Range`类的实例。 它包含从 1 到 6 的数字。`class`方法返回对象的名称。 `include?`方法检查数字 4 是否在范围内。 就我们而言。

```ruby
$ ./formal.rb
4
ruby
true
false
Range
true

```

运行示例将给出此输出。

## Ruby 对象字面值

正如我们已经提到的，可以使用对象字面值来创建一些内置对象。 以下示例显示了几个对象字面值。

```ruby
#!/usr/bin/ruby

4.times { puts "Ruby" }

puts "Ruby".size
puts "Ruby".downcase

puts [1, 2, 3].include? 3
puts [1, 2, 3].empty?

puts :name.class
puts :name.frozen?

puts (1..6).class
puts (1..6).include? 4

```

在上面的示例中，我们使用字面值符号创建一个`Fixnum`，字符串，数组，符号和范围。

```ruby
4.times { puts "Ruby" }

```

我们可以立即在整数字面值上调用方法。 该行向终端打印四次`"Ruby"`字符串。

```ruby
puts "Ruby".size
puts "Ruby".downcase

```

我们在用字符串字面值创建的`String`对象上调用两个方法。

```ruby
puts [1, 2, 3].include? 3
puts [1, 2, 3].empty?

```

在这里，我们使用数组字面值符号创建两个`Array`对象。 我们使用`include?`方法检查特定的数字是否是数组的一部分。 `empty?`方法检查数组对象是否为空。

```ruby
puts :name.class
puts :name.frozen?

```

调用 Symbol 对象的两种方法。 该符号是用一个以冒号开头的符号字面值创建的。

```ruby
puts (1..6).class
puts (1..6).include? 4

```

使用范围字面值创建两个`Range`对象。 我们在那些对象上调用两个方法。 `class`方法返回类的名称，`include?`方法检查给定数字是否在范围内。

```ruby
$ ./literals.rb
Ruby
Ruby
Ruby
Ruby
4
ruby
true
false
Symbol
false
Range
true

```

示例输出。

## Ruby 对象层次结构

在许多面向对象的语言中，对象形成层次结构。 Ruby 也具有对象层次结构。 它是一个树状的层次结构，其中有父对象和子对象。 对象从其父对象继承数据和行为。 在层次结构的顶部，有根对象。 它称为`Object`。 Ruby 中的每个对象都有至少一个父对象。 换句话说，每个对象都继承自基本的`Object`对象。

根据 Ruby 的官方文档，`Object`是 Ruby 类层次结构的根。 除非明确重写，否则它的方法可用于所有类。

```ruby
#!/usr/bin/ruby

puts 4.is_a? Object
puts "Ruby".is_a? Object
puts [2, 3].is_a? Object
puts :name.is_a? Object
puts (1..2).is_a? Object

```

在上面的代码示例中，我们演示了所有对象均从根`Object`继承

```ruby
puts 4.is_a? Object

```

我们使用`is_a?`方法检查数字是否为特定类型：换句话说，数字是否继承自给定的对象类型。

```ruby
$ ./mother.rb 
true
true
true
true
true

```

所有方法均返回`true`，这意味着所有对象均从母对象继承。

即使对于非常基本的 Ruby 对象，继承层次结构也可能非常复杂。

```ruby
#!/usr/bin/ruby

puts 6.class

puts 6.is_a? BasicObject
puts 6.is_a? Object
puts 6.is_a? Numeric
puts 6.is_a? Integer
puts 6.is_a? Fixnum

puts 6.is_a? Bignum
puts 6.is_a? String

```

在此示例中，我们对较小数值的继承层次结构有所了解。

```ruby
puts 6.class

```

我们找出数字值为 6 的对象是什么。该行将`Fixnum`打印到控制台。

```ruby
puts 6.is_a? BasicObject
puts 6.is_a? Object
puts 6.is_a? Numeric
puts 6.is_a? Integer
puts 6.is_a? Fixnum

```

以上所有行均返回`true`。 数字 6 是`Fixnum`。 从 Ruby 文档中，我们发现其他四个对象是`Fixnum`对象的父对象。

```ruby
puts 6.is_a? Bignum
puts 6.is_a? String

```

以上两个对象不是 6 值的父对象。

```ruby
$ ./inheritance.rb
Fixnum
true
true
true
true
true
false
false

```

示例的输出。

我们将以一个示例结束本节，该示例演示自定义用户对象的继承。

```ruby
#!/usr/bin/ruby

class Being

    def to_s
        "This is Being"
    end

    def get_id
        9
    end
end

class Living < Being

    def to_s
        "This is Living"
    end
end

l = Living.new

puts l
puts l.get_id
puts l.is_a? Being
puts l.is_a? Object
puts l.is_a? BasicObject

```

在示例中，我们创建两个对象`Being`和`Living`。 `Living`对象继承自`Being`。 第一个是父对象，第二个是子对象。

```ruby
class Being

    def to_s
        "This is Being"
    end

    def get_id
        9
    end
end

```

这是自定义 Ruby 对象的定义。 定义位于`class`和`end`关键字之间。 在定义内部，我们创建两个方法。 当`puts`方法将对象作为参数时，它将调用其`to_s`方法。 它通常给出对象的字符串表示/描述。

```ruby
class Living < Being

    def to_s
        "This is Living"
    end
end

```

我们创建`Living`对象的定义。 该对象继承自`Being`对象。 `<`运算符用于创建继承关系。 `to_s`方法被覆盖。

```ruby
l = Living.new

```

从上面的`Living`对象模板，我们创建`Living`对象的实例。 使用`new`关键字创建自定义对象的实例。

```ruby
puts l

```

`puts`方法调用`Living`对象的`to_s`方法。 如果`Living`类中未定义`to_s`方法，则将调用`Being`类中的`to_s`方法。

```ruby
puts l.get_id

```

`Living`对象未定义`get_id`方法。 在这种情况下，将检查父类是否存在这种方法。 在我们的情况下，`Being`方法具有这种方法，因此被称为。

```ruby
puts l.is_a? Being

```

该行返回`true`。 `Living`是`Being`的一种； 例如它继承自`Being`类。

```ruby
puts l.is_a? Object
puts l.is_a? BasicObject

```

对于我们的`Living`自定义对象，我们没有明确指定与`Object`或`BasicObject`对象的任何关系。 但是这两行返回`true`。 这是因为 Ruby 中的每个对象自动都是这两个对象的后代。 这是由 Ruby 解释器在后台完成的。

```ruby
$ ./custominher.rb
This is Living
9
true
true
true

```

Output of the example.

## Ruby 顶级

Ruby 有一个特定的对象，称为 Ruby 顶级。 这是在任何其他上下文（例如类或模块定义）之外定义的默认执行环境。 顶级名称为`main`。 它是`Object`类型的实例。 有一个与`main`关联的局部空间，所有局部变量都驻留在该空间中。

```ruby
#!/usr/bin/ruby

n1 = 3
n2 = 5

puts local_variables

Kernel.puts self
puts self.class

```

这是描述 Ruby 顶层的第一个示例。

```ruby
n1 = 3
n2 = 5

```

在这里，我们定义了两个数值变量。 这些变量是本地变量。

```ruby
puts local_variables

```

在这里，我们列出了所有局部变量。 `local_variables`是`Kernel`模块的一种方法，已混入每个`Object`（包括顶层对象）中。

```ruby
Kernel.puts self

```

`self`是 Ruby 伪变量。 它返回当前的对象接收者。 该行将`"main"`打印到控制台。 它是 Ruby 顶层的名称。 `Kernel.puts`代码的`Kernel`部分可以省略。 通过完全指定名称，我们显示`puts`方法属于`Kernel`模块。

```ruby
puts self.class

```

该行显示顶层的`class`。 我们得到顶层的对象类型。 它是`Object`，它是 Ruby 类层次结构的根。

```ruby
$ ./toplevel.rb
n1
n2
main
Object

```

这是示例的输出。 `n1`和`n2`是与顶层关联的局部变量。 主要是为 Ruby 顶级执行环境指定的名称。 最后，`Object`是顶层的类型。

我们将有另一个与 Ruby 顶层相关的示例。

```ruby
#!/usr/bin/ruby

@name = "Jane"
@age = 17

def info
   "#{@name} is #{@age} years old" 
end

puts self.instance_variables
puts self.private_methods.include? :info

puts info

```

我们显示了属于顶级环境的实例变量和方法。

```ruby
@name = "Jane"
@age = 17

```

我们定义了两个实例变量。 实例变量以 Ruby 中的`@`字符开头。 实例变量属于特定的对象实例。 在这种情况下，它们属于 Ruby 顶级。

```ruby
def info
   "#{@name} is #{@age} years old" 
end

```

这是一个方法定义。 每个方法必须属于某个对象。 此方法属于顶级对象。 所有顶级方法都是私有的。 私有方法的访问受到限制。

```ruby
puts self.instance_variables

```

`instance_variables`方法打印出`self`的所有实例变量，在此上下文中指向 Ruby 顶层。

```ruby
puts self.private_methods.include? :info

```

所有顶级方法都是自动私有的。 `private_methods`返回对象的所有私有方法。 由于方法很多，我们调用`include?`方法来检查`info`方法是否在其中。 请注意，我们通过符号名称来引用`info`方法。

```ruby
$ ./toplevel2.rb
@name
@age
true
Jane is 17 years old

```

示例输出。

本章介绍了 Ruby 语言中对象的一些基础知识。