Ruby学习笔记之面向对象

面向对象


创建对象

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"


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值