面向对象
创建对象
a_book = BookInStock.new
another_book = BookInStock.new
类声明
class BookInStock
def initialize(isbn, price)
@isbn = isbn
@price = Float(price)
end
def to_s
"ISBN: #{@isbn}, price: #{@price}"
end
注意:
1.出现在init方法中的属性加入到类中
2.to_s是toString方法
3.类名必须以大写开头,类名是常量
构造器
Ruby的构造器同Java不同的是:
1.Ruby的对象属性都由@开头。
2.Ruby只有一个构造方法。
3.在各个方法中都可以吧变量加入类中,这种灵活性在静态语言中并未出现。因此Ruby中必须有完全的了解。
3.常量只能在类体中出现,在其他方法中出现都是错误的。
getter和setter
Ruby中的所有属性都是私有的,因此要为他加上访问方法,getter方法就是属性名,setter方法是属性名加等于号。
例如:
def price=(new_price)
@price = new_price
end
def price
@price
end
Ruby还提供了可以生成简单的setter和getter的方法
attr_reader :isbn, :price //生成getter方法
attr_accessor :price //生成getter和setter方法
虚拟属性就是提供了一对getter和setter方法
属性访问
1.在其他语言中在类中可以访问同一类对象的私有属性,但是Ruby中不可以,只能通过getter和setter来访问(可能是缺乏类型检查的原因)
def +(other) # Define + to do vector addition
Point.new(@x + other.x, @y + other.y)
end
2.类属性在类内部和类外部的表现是不同的。
java和scala中的构造顺序问题
问题:java在子类构造过程中调用父类的构造器可能会子类覆盖的方法,但是这时子类仅仅产生并未初始化,也就是说父类得到的结果是没有定义的,因此构造结果也无法定义。在C++中父类构造过程中调用的是父类的方法。
解决方法:父类的使用未定义字段的字段设为lazy
但是在Ruby中,没有抽象的东西,因此没有这个问题
类体中执行代码
在Ruby中,类也是一个对象。因此在类产生时可以执行在类体中的代码,这时self指向的这个类本身,因此对于可执行的代码有要求。这些方法应该是Class及其父类的实例方法,以及这个类本身和其父类的类方法。
类和实例变量
常量
1.类中常量要全大写,且在构造器外初始化,
2.在应用是用双冒号
3.::ARGV # The global constant ARGV
4.但是Ruby并不阻止程序改变常量,只是会有一个警告
class Point
def initialize(x,y) # Initialize method
@x,@y = x, y
end
ORIGIN = Point.new(0,0)
UNIT_X = Point.new(1,0)
UNIT_Y = Point.new(0,1)
# Rest of class definition goes here
end
访问控制
Ruby中的对象访问都是通过方法实现的,因为属性都是私有的。而方法默认public,还有protected和private
可以直接在方法前面设定,也可以在类后面列出来
public :method1, :method4
protected :method2
private :method3
类方法
类方法就是静态方法,在java中对象可以访问类方法,但是在Ruby中只有类可以访问,对象无法访问。
参见: 类方法(静态方法)
self
类定义
在类定义中(但在实例方法外部)以及类方法中self指的是这个类。与此相反的是,在实例方法中,
类属性
类属性是同类同时存在的变量,和Java相同的是:类属性在类的各个实例之间共享,类属性可以用在类方法,实例方法中。
不同之处是:
1.类属性必须在类体中声明,同时可以在init方法中进行初始化
2.类属性没有getter和setter,只能在类体中访问,无法直接访问。
3,类属性的另种定义
class << String
attr_accessor :b
end
注意:这儿并没有设定任何的属性,仅仅通过attr_方法来得到一对属性
类实例变量
类属性和类实例属性
1,类属性在在定义的类,继承类和他门的对象中是共享的。
2,类实例属性仅仅在本类及其对象中可用
3,由于他们的不同,Ruby中建议使用类的实例属性,用来仅仅在定义的类和其对象来共享。和子类没有关系。
4,他们的setter和getter的设置方法都是一样的
class Foo
@@i = 1
def self.i
@@i
end
def self.i=(value)
@@i = value
end
end
5,类实例属性必须在类体中的设定,不能再类的实例方法中设定
class MyClass
@my_var = 1
def self.read; @my_var; end
def write; @my_var = 2; end
def read; @my_var; end
end
obj = MyClass.new
obj.write
obj.read # => 2
MyClass.read # => 1
6,类属性可以在类的实例方法中访问,而类实例属性则只能在类方法中访问。从这个意义上,类属性和Java中static属性相同。
类和实例方法
对象深拷贝
dup方法,会把冻结的对象解冻
copy方法,不会把冻结的对象解冻
注意copy和dup方法在拷贝对象的属性时,仅仅只会拷贝他们的引用。因此不符合深拷贝的要求,要想改变这个默认行为,要覆写init_copy方法
def initialize_copy(orig) # If someone copies this Point object
@coords = @coords.dup # Make a copy of the coordinates array, too
end
重载操作符
def coerce(other)
[self, other]
end
这样就可以允许,操作符的任意顺序
对象冻结
freeze方法,对象一旦冻结就无法更改其内容
对象不安全标记
一个对象通过调用taint来标记为不安全的对象,一个由不安全的对象产生的对象都是不安全的,包括拷贝。调用untaint方法可以吧一个对象变成安全的。
注意:用户输入,包括命令行,环境变量以及标准输入都会默认标记为不安全的。
模块
1.模块不可以被继承
2.模块被用作名字空间和混入。
3.模块名和类名相同,首字母必须大写
混入
在要混入的模块中,难免会使用他未定义的方法或者变量,那么这时候模块就变成了抽象的。必须在他混入的类中声明这些。
导入
Ruby中的模块名名和类名都是大写开头,文件名和文件夹名则是小写开头,在导入的时候必须导入文件名,有的模块仅仅有一个文件构成,而有的模块有多个文件构成,他的存在仅仅是一个文件夹,这时候不能直接导入模块,而要导入相应的文件名。注意导入时会忽略大小写,但是一般都是按文件名导入(都是小写)。
执行
用require加载一个文件时会执行其中的代码。
自动加载
自动加载在第一次引用某个模块中的内容时,用require加载模块,相当于懒加载。
autoload :TCPSocket, "socket"
可以定义的量
类方法,常量,类方法,实例方法
模块和类的相似性
类是一个Class类的实例,而模块是一个Module类的实例,因此大部分可以用于类的规则也同时适用于模块。
继承
语法
class Parent
end
class Child < Parent
end
继承结构
nil->BasicObject->Object
其中BasicObject用在元编程中,Object则是每个类继承的根类
抽象类
Ruby中并没有抽象类,所谓的抽象类仅仅是缺少方法的类
class AbstractGreeter
def greet
puts "#{greeting} #{who}"
end
end
# A concrete subclass
class WorldGreeter < AbstractGreeter
def greeting; "Hello"; end
def who; "World"; end
end
这儿的父类调用了不存在的方法,只能由子类来实现
继承变量
实例变量
在Ruby中,类的实例变量都是私有的。但是在一定程度上,子类还是继承了私有变量。
类变量
子类会完全继承父类的类变量
常量
Ruby中的常量会完全继承,并且覆写它时不会对父类使用常量的方法产生影响,因为父类使用的是父类的常量。
继承方法
类方法
类方法会被继承,并且也可以被继承。但是在调用类方法时建议在定义的地方调用
实例方法
在Ruby中,即使是私有的方法都可以覆盖。对此,唯一的方法就是仅仅继承熟悉的类。因此这样的覆写不安全,因为私有方法是类实现的细节。事实上,私有的方法也可以在子类中使用。
需要注意的地方
1.子类在父类没有无参构造器时必须显式调用父类构造器
2.调用父类用super
3.Ruby使用单继承
3.在其他语言中:在继承有参构造器时,子类必须显式调用父类的构造器,但是Ruby不需要(事实上,Ruby中的构造器是一个普通方法,可以继承)。如果要调用父类构造器,那么可以用super。
4.调用父类用super
super
Ruby中的一个方法可以简单的用super调用其父类方法,如果不改变任何传递给父类的参数,可以什么都不写。
例如:
def method_missing(id,*args,&block)
return as($1.to_sym,*args,&block) if id.to_s =~ /^to_(.*)/
return rows_with($1.to_sym => args[0]) if id.to_s =~ /^rows_with_(.*)/
super
end
异常
抛出异常
raise
raise "bad mp3 encoding"
raise InterfaceException, "Keyboard failure", caller
接受异常
begin
while line = web_page.gets
output.puts line
end
output.close
rescue Exception
STDERR.puts "Failed to download #{page}: #{$!}"
output.close
File.delete(file_name)
raise
end
1.这儿的标准错误输出是STDERR
2.raise可以重新抛出异常
begin
eval string
rescue SyntaxError, NameError => boom
print "String doesn't compile: " + boom
rescue StandardError => bang
print "Error running script: " + bang
end
1.这儿可以有多个异常的接受
2.每个异常可以有一个变量,用来记录异常
ensure
f = File.open("testfile")
begin
# .. process
rescue
# .. handle error
else
puts "Congratulationsno
errors!"
ensure
f.close
end
ensure用来保证资源一定可以释放
retry
retry用来在错误恢复的时候重新执行整个try块
begin
...
end
rescue ProtocolError
if @esmtp then
@esmtp = false
retry
else
raise
end
end
自定义异常
class RetryException < RuntimeError
attr :ok_to_retry
def initialize(ok_to_retry)
@ok_to_retry = ok_to_retry
end
end
可以看到只要继承StandardError或者他的子类即可
异常层次
处理整个方法的异常
def ==(o) # Is self == o?
@x == o.x && @y == o.y # Assume o has proper x and y methods
rescue # If that assumption fails
false # Then self != o
end
错误跟踪
特殊变量
这些特殊变量都位于Kernel中。
__method__ 出错的方法
__callee__ 出错的调用方法
__FILE__ 出错文件名
__LINE__ 出错行
异常栈
一旦异常发生就可以用Exception.backtrace来查看异常栈,不过也可以不触发异常,用kernel.caller来查看异常栈
Struct
Struct根据提供的属性名来动态创建类,并且提供了一些方法
一。创建类:
Point = Struct.new(:x, :y)
二。对象方法:
p = Point.new(1,2) # => #<struct Point x=1, y=2>
getter和setter
p.x # => 1
p.y # => 2
p.x = 3 # => 3
p.x # => 3
p[:x] = 4 # => 4: same as p.x =
p[:x] # => 4: same as p.x
p[1] # => 2: same as p.y
迭代方法
p.each {|c| print c} # prints "42"
p.each_pair {|n,c| print n,c } # prints "x4y2"
相等
q == p
toString
q.to_s
三。动态更改类
添加比较
Point = Struct.new(:x, :y) # Create new class, assign to Point
class Point # Open Point class for new methods
include Comparable # Include a module for the class
def <=>(other) # Define the <=> operator
return nil unless other.instance_of? Point
self.x**2 + self.y**2 <=> other.x**2 + other.y**2
end
end
变成不变的
Point = Struct.new(:x, :y) # Define mutable class
class Point # Open the class
undef x=,y=,[]= # Undefine mutator methods
end
全局变量和方法
因为Ruby中大部分类中的变量,方法和常量都可以被继承和重写。同时又因为Object和Kernel处于继承层次结构的顶端,因此只要把方法和变量放入他们两个中,就成了全局的。
类的双面
在Ruby中,一个类是一个Class类的实例,也就是说:一个类就是一个对象,事实上累还是一个常量。既然类也是对象,那么类也可以用Class及其父类中的实例方法。而这些实例方法的定义都是对象通用的方法,这也是为什么类和对象有时会有相等的操作。同时这也导致了一些方法的差异。比如类上作用的是类方法,而对象上作用的是实例方法。
调用查找
实例方法查找算法
1.首先从对象的单例方法中开始查找,如果没有进入下一步
2.从对象的类中查找实例方法,如果找不到。倒序在类中包含的模块中查找。
3.进入类的父类开始查找,并重复2中的步骤。
4.一直查找到Ruby的顶层类,如果没,那么就使用同样的步骤查找method_missing方法。在Kernel中提供了一个method_missing方法提供默认的行为。
注意:
1.注意查找的顺序是先类在模块
2.由于Ruby的继承层次,BasicObject,Object和Kernel模块参与每次的查找。
3.这样的顺序其实是沿着Ruby的类继承链查找的。
继承链
继承链是指实例方法的查找顺序,这个可以通过Module.ancestors来获得。
类方法
变量查找算法
Eigenclass
Ruby的单例方法存在于每一个对象或者类的Eigenclass中,也叫做单实例类,因为这个类是每一个对象独有的类。在方法查找的轨迹中这是第一个要检测的地方。
访问
class << an_object
# your code here
end
得到单实例类
obj = Object.new
eigenclass = class << obj
self
end
eigenclass.class
得到Eigenclass的帮助方法
class Object
def eigenclass
class << self; self; end
end
end
继承
对象Eigenclass
一个对象的Eigenclass就是这个对象的类的子类。这也意味着Ruby中每个对象的类其实是Eigenclass。
类Eigenclass
一个类的Eigenclass是这个类的父类的Eigenclass,到最顶层BasicObject的Eigenclass是Class,这样有两个要注意:
1,如果把Class看做Ruby中一个类的类的话,那么Class的Eigenclass和独享的Eigenclass就是类似的。
2,Eigenclass相互继承,这样类方法才可以继承通过子类来调用。
Extension
Class Extension
可以看到这个方法使得一个模块的实例方法变成了类的单例方法
module MyModule
def my_method; 'hello' ; end
end
class MyClass
class << self
include MyModule
end
end
MyClass.my_method
注意这个方法中,访问类的Eigenclass不是最简化的方式而是用这种间接的方式
Object Extension
module MyModule
def my_method; 'hello' ; end
end
obj = Object.new
class << obj
include MyModule
end
obj.my_method # => "hello"
obj.singleton_methods # => [:my_method]
模块的实例方法变成了对象的单例方法
extend
Object.extend是一种简化的方法,它综合了上俩种Extension的功能。
module MyModule
def my_method; 'hello' ; end
end
obj = Object.new
obj.extend MyModule
obj.my_method # => "hello"
class MyClass
extend MyModule
end
MyClass.my_method # => "hello"