打开类
可以重新打开已经存在的类并进行动态修改,即便是标准类库也不例外
class String
def my_method
puts "This is my_method()"
end
end
"str".my_method #=> This is my_method()
这种方式是不是很方便? 当然凡事都有利弊,带来方便的同时也给我们带来了一些问题:可能会对于类中已有的方法进行覆盖,为了修复这样的问题所使用的patch称为猴子补丁.
"A".downcase #=> "a"
class String
def downcase
puts "This is downcase() method"
end
end
"A".downcase #=> This is downcase() method
所以,虽然打开类技术非常方便,但是由于若是乱用可能会导致很大的问题.
类与对象
1. 对象中有什么
class MyClass
def my_method
@v = 1
end
end
obj = MyClass.new
实例变量
与一般静态语言不同,Ruby中的对象的类和它的实例变量没有关系,当给实例变量赋值时,它们才会产生.
obj.instance_variables #=>[]
obj.my_method
obj.instance_variables #=>[:@v]
方法
除了实例变量,对象还有方法,虽然可以利用methods方法查看到对象所拥有的方法,但实际上对象并没有存放这些方法,而是存放一个对自身类的引用.而这些方法存放在类中,称之为实例方法,与类方法截然不同.实例方法必须是类的实例才能调用,而类方法必须是类本身才能调用.
String.instance_methods == "abc".methods #=>true
String.methods == "abc".methods #=>false
总结: 一个对象的实例变量存在与对象本身,而一个对象的方法存在与对象自身的类.这也是为什么同一个类的对象共享者同样的方法,但不共享实例变量的原因.
2. 类
- 类自身也是对象
"Hello".class #=> String
String.class #=> Class
一个对象的方法也就是其类的实例方法,也就是说一个类的方法就是Class的实例方法
Class.instance_methods(false) #=>[:superclass,:allocate,:new]
new()方法用于创建对象
allocate()方法是new()的支撑方法
superclass()返回一个类的超类
所有类最终都继承与Object,Object本身又继承与BasicObject, BasicObject是Ruby对象体系中的根结点.
String.superclass # => Object
Object.superclass # => BasicObject
BasicObject.superclass # => nil
class.superclass # => Module
Module.superclass # => Object
所以,一个类不过是一个增加了三个方法的Module.类和模块基本上是一样的,绝大多数适用于类的情况也同样适用于模块,反之亦然.
class MyClass ; end
obj1 = MyClass.new
obj2 = MyClass.new
上述代码的模型图如下:
load 与require
使用load(‘test.rb’)可引用外部代码, 但对于test.rb文件所定义的变量和类会落在当前作用域之外,而常量则会落在当前作用域之类,这样便会污染当前程序的命名空间,不过可使用load(‘test.rb’, true)强制其常量仅在自身范围内有效.
require() 方法与load()相似,但load可以执行代码,而require则是导入类库
总结:
类就是对象,只不过类名是常量[任何以大写字母开头的引用,包括类名和模块名].
方法调用
当调用一个方法时, Ruby会做两件事
1. 找到这个方法, 需要祖先链
2. 执行这个方法, 需要self
1.方法查找
接收者 : 即为调用方法所在的对象, 如
obj.method()
中,obj
即为接收者祖先链 : 从当前类一直向上寻找超类直到找到
BasicObject
类所经过的路径,即为祖先链(亦包括模块).
方法查找: 首先在接收者的类中查找,然后一层层在祖先链中查找,直到找到该方法为止.
class MyClass
def my_method; 'my_method()'; end
end
class MySubClass < MyClass
end
obj = MySubClass.new
obj.my_method()
MySubClass.ancestors # =>[MySubClass, MyClass, Object, Kernel, BasicObject]
当在一个类(或模块)中包含一个模块时,Ruby创建一个封装该模块的匿名类,并将其插入到祖先链中.
module M
def my_method
'M#my_method()'
end
end
class C
include M
end
class D < C; end
D.new.my_method() # => "M#my_method()"
D.ancestors # => [D, C, M, Object, Kernel, BasicObject]
Kernel模块
如果给Kernel模块增加一个方法,这个内核方法就对所有对象可用.
2.方法执行
- 探索self
每一行代码都会在一个当前对象中执行,可使用
self
关键字访问当前对象.当调用一个方法时,接收者就成为self, 这时所有的实例变量都是self 的实例变量,搜偶没有明确指明接收者的方法都在self上调用.
- 类定义与self
self通常由最后一个接受到方法的对象充当,但在类或模块定义中[任意方法定义之外], self由这个类或模块充当.
class MyClass
def self.my_method # =>类方法,self代表MyClass
puts "self is #{self}"
end
def my_method # =>实例方法, self为接收者
puts "self is #{self}"
end
end
MyClass.my_method # =>self is MyClass
obj = MyClass.new
obj.my_method # =>self is obj
- 私有private
不能明确指定一个接收者来调用一个私有方法, 但可通过send方法破坏私有性
小结
- 对象由一组实例变量和一个类的引用组成
- 对象的方法存在与自身的类中,称之为实例方法
- 类本身是Class类的实例,类名即为常量
- Class类是Module的子类,一个模块是有一组方法组成的包.
- 常量像文件系统一样,按照树形结构组织, 其模块和类的名字扮演目录的角色, 其它普通常量则扮演文件的角色.
- 每一类都有一个祖先链, 从自己所属类开始直到BasicObject结束
- 当调用一个方法时,Ruby会先向右来找到所属类,然后沿着祖先链向上查找
- 每当类包含一个模块时, 该模块会被插入到祖先链中, 位于该类的正上方
- 当调用一个方法时, 接收者会扮演self角色
- 当定义一个模块或类时, 该模块或类扮演self角色
- 实例变量永远被认定为self的实例变量
- 任何没有明确指定接收者的方法调用,都是调用self的方法