携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第9天,点击查看活动详情
一、对象的复制与冻结
在编码过程中有时可能需要一个已有对象的备份,因为已有对象中包含了许多的状态数据,重新创建可能无法满足条件。
变量只是对对象的应用,因此通过 = 赋值符号是无法对对象进行备份的,因为两个变量指向的还是同一个对象
class Female
attr_accessor :name
end
f = Female.new
f.name = "Penny"
f2 = f
f2.name = "Abby"
puts f.name # Abby
puts f2.name # Abby
但是对于基本数据类型,如字符串,数字在复制时会进行自动赋值操作,不会出现两个变量指向同一个数据。
一般对象的复制,可以使用 Object 类的 clone 方法和 dup 方法。这两个方法功能类似,都可以赋值对象的状态数据,唯一不同的是 clone 方法还能保留与对象相关的方法等内容。
class Female
attr_accessor :name
end
f1 = Female.new
f1.name = "Penny"
def f1.print_name
puts "print_name 方法调用了"
end
f2_clone = f1.clone
f3_dup = f1.dup
puts f1.name
puts f2_clone.name
puts f3_dup.name
# 调用对象的方法
f1.print_name
f2_clone.print_name
f3_dup.print_name
执行上述代码,输出结果如下:
Penny
Penny
Penny
print_name 方法调用了
print_name 方法调用了
Traceback (most recent call last):
/ex31.rb:22:in `<main>': undefined method `print_name' for #<Female:0x00007f7abe8367c0 @name="Penny"> (NoMethodError)
使用 dup 方法复制的对象在调用 f1 对象新添加的 print_name 方法时报错了,说明 dup 没有复制到 f1 对象新定义的方法。
在应用中如果不希望自己定义的类被修改,就需要进行冻结操作,冻结对象使用的是 freeze 方法,冻结后在修改该对象会出现 FrozenError 的报错
class Female
attr_accessor :name
end
f1 = Female.new
f1.name = "Penny"
f1.freeze
def f1.print_name
puts "f1 对象冻结后再新定义方法"
end
执行上述代码,输出结果如下:
/ex32.rb:10:in `<main>': can't modify frozen object (FrozenError)
Ruby 中一切皆对象,所以也适用于普通的数字和字符串对象,但是要注意如果操作返回了一个新的对象是不适用的。
二、对象序列化
上面提到的 clone 和 dup 都是浅复制,如果对象中还引用了别的对象,只会复制引用,不会复制引用的对象,如果想要实现深度复制,就需要使用到对象的序列化。
在面向对象中,有时候需要将整个对象及其状态报错起来,进行持久化存储,在需要时又可以还原成对象的状态,这个过程就是对象的序列化和反序列化过程。
Ruby 对对象的序列化提供了基本支持,内置的 Marshal 模块可以非常方便的记性对象的序列化和反序列化操作。
class Female
attr_accessor :name
end
f1 = Female.new
f1.name = "Penny"
puts "=====序列化====="
# 序列化
dump_val = Marshal.dump(f1)
puts dump_val.class
puts "=====反序列化====="
# 反序列化
load_obj = Marshal.load(dump_val)
puts load_obj.class
执行上述代码,输出结果如下:
=====序列化=====
String
=====反序列化=====
Female
对于嵌套比较深的对象可以使用 Marshal 模块来记性序列化操作,可以对对象进行深度复制
对象的序列化和反序列化不仅限于 Marshal 模块,Marshal 模块式 Ruby 的内置模块,处理速度块,其他更常用的还有 YAML 方式进行序列化和反序列化。