微信搜索我吃你家米了关注公众号
该文章主要是用于记录在Ruby学习过程中遇到的一些疑难点
Ruby中的类变量和实例变量
两者在形式上的区别就是,类变量使用@@标识,实例变量使用@标识
使用中的区别参考以下代码
class Test
@@class_var = 10
@class_instance_var = "234234234"
#类方法: 两种变量都可以访问
def self.outclass_var
puts @@class_var
end
def self.outclass_instance_var
puts @class_instance_var
end
#类实例方法: 不能访问类实例变量
def outclass_var
puts @@class_var
end
def outclass_instance_var
puts @class_instance_var
end
end
m = Test.new
m.outclass_var
m.outclass_instance_var
Test.outclass_instance_var
输出如下:
10
234234234
从输出结果可以看出outclass_instance_var
方法被调用后并没有任何输出,原因是实例变量是无法被实例方法访问的,只能被类方法访问,只有出现在initialize方法中的实例变量才能被实例方法访问
且实例变量是无法被子类继承的,类变量可以被继承
class Dog
def initialize(breed)
@breed = breed
end
end
class Lab < Dog
def initialize(breed, name)
super(breed)
@name = name
end
def to_s
"(#@breed, #@name)"
end
end
puts Lab.new("Labrador", "Benzy").to_s
输出结果:
(Labrador, Benzy)
可以看到子类调用了父类的initialize
方法,父类的initialize
对@breed
这个实例变量进行了赋值,导致该实例变量扩展到了子类中,之所以会这样是因为虽然实例变量和继承机制无关,但是父类的方法是会被继承的,那么被继承的方法所创建出来的变量就会扩展到子类中
# p049instvarinherit.rb
class C
def initialize
@n = 100
end
def increase_n
@n *= 20
end
end
class D < C
def show_n
puts "n is #{@n}"
end
end
d = D.new
d.increase_n
d.show_n
该代码输出结果是2000
,这说明了子类对象也拥有了实例变量@n
如果在子类中定义了和父类相同的实例变量,那么子类中的实例变量的值会直接覆盖父类变量
加深对ruby的类变量的理解,看下面一段代码:
# variables and methods start lowercase
$glob = 5 # global variables start with $
class TestVar # class name constant, start uppercase
@@cla = 6 # class variables start with @@
CONST_VAL = 7 # constant style, all caps, underscore
def initialize(x) # constructor
@inst = x # instance variables start with @
@@cla += 1 # each object shares @@cla
end
def self.cla # class method, getter
@@cla
end
def self.cla=(y) # class method, setter, also TestVar.
@@cla = y
end
def inst # instance method, getter
@inst
end
def inst=(i) # instance method, setter
@inst = i
end
end
puts $glob
test = TestVar.new(3) # calls constructor
puts TestVar.cla # calls getter
puts test.inspect # gives object ID and instance vars
TestVar.cla = 4 # calls setter
test.inst=8 # calls setter
puts TestVar.cla
puts test.inst # calls getter
other = TestVar.new(17)
other = TestVar.new(18)
other = TestVar.new(19)
puts other.inspect
puts TestVar.cla
输出如下:
5
7
#<TestVar:0x000000000632a1f8 @inst=3>
4
8
#<TestVar:0x0000000006329f50 @inst=19>
7
可以看到,正如注释中所说,类变量是被每一个对象所共享的,我们每调用一次new
,@@cal
类变量就会在initialize
方法中+1
,我们调用了3次new
,@@cal
就由4变成了7
ruby的访问控制
父类私有方法可以被子类继承
在ruby中,父类的私有方法是可以被子类继承的,但是一般情况下,私有方法是作为类内部的一些辅助方法,对类内部的一些功能可能起着至关重要的作用,所以子类在定义方法的时候注意不要和父类的私有方法相同,否则可能会造成父类私有方法重写的可能,这样在子类使用到这些私有方法的时候就可能会出现问题
封装
在访问控制方面,ruby和其他高级语言有着一些细微的区别,其中一点就是ruby定义的类中的成员变量是无法被外部对象直接访问的,也就是说他们全部都是私有的,要想让外部访问到这些变量,我们只能通过public
修饰的getter
和setter
方法来进行访问,在ruby中,这两个方法被称为attribute reader
和attribute writer
# p048accessor.rb
# First without accessor methods
class Song
def initialize(name, artist)
@name = name
@artist = artist
end
def name
@name
end
def artist
@artist
end
end
song = Song.new("Brazil", "Ivete Sangalo")
puts song.name
puts song.artist
# Now, with accessor methods
class Song
def initialize(name, artist)
@name = name
@artist = artist
end
attr_reader :name, :artist # create reader only
# For creating reader and writer methods
# attr_accessor :name
# For creating writer methods
# attr_writer :name
end
song = Song.new("Brazil", "Ivete Sangalo")
puts song.name
puts song.artist
可以看到ruby有一个非常简洁的语法来直接定义attribute reader
attr_reader :name, :artist
这其实就是个语法糖,还有attr_writer
和attr_accessor
这样声明之后我们就可以直接使用外部对象去访问类中的私有变量了
ruby的freeze方法
该方法可以用来将对象变成immutable,也就是只读的,我们来观察下面两个例子
str = 'A simple string. '
str.freeze
begin
str << 'An attempt to modify.'
rescue => err
puts "#{err.class} #{err}"
end
输出结果如下:
FrozenError can't modify frozen String: "A simple string. "
可以看到,当我们试图往只读对象str上附加字符串时,出现报错,报错类型为FrozenError
再看另一段代码:
str = 'Original string - '
str.freeze
str += 'attachment'
puts str
输出结果如下:
Original string - attachment
可以看到,这段代码成功运行了,并且str
的值改变了
原因就在于<<
和+=
这两个操作符的区别,前者不会改变str
所指向的对象,但是后者却会改变str
所指向的对象,可以看如下代码:
str = 'Original string - '
puts "Before:: #{str.object_id}"
str.freeze
str += 'attachment'
puts "After:: #{str.object_id}"
puts str
输出结果如下:
Before:: 60
After:: 80
Original string - attachment
ruby中的常量
A_CONST = "Doshi"
A_CONST = "Joshi"
puts A_CONST
puts "\n--------------------\n\n"
B_CONST = "Doshi"
B_CONST[0] = "J"
puts B_CONST
输出结果:
C:/Users/19856/p062stuff.rb:2: warning: already initialized constant A_CONST
C:/Users/19856/p062stuff.rb:1: warning: previous definition of A_CONST was here
Joshi
--------------------
Joshi
可以看到,使用第一种方式直接修改常量的值,虽然修改成功了,但是爆出了warning
,然而使用第二种方式却可以完美地更改常量的值
主要是因为,第二种更改方式是通过更改对象的内部状态来达到修改的目的的,可以看到代码中使用的是数组的形式B_CONST[0] = "J"