Day8 读书笔记&心得体会

一、心得体会

今天完成了什么

  • 看了20页的镐头书
  • 学了bag的10个controller

收获什么?

  • 新增、编辑都放在show视图里
  • 活动平台
  • 订单活动
  • 地址、地址快照、品牌

二、读书笔记
回顾昨天学到内容

  • 肯定匹配 =~
    举个栗子:
name = "ni shi da sha gua"
name =~ /a/   -> 8

匹配操作符返回匹配发生的字符位置,它们也有副作用,会设置一些Ruby变量???

$`得到匹配之前的那部分字符串,$'得到匹配之后的那部分字符串。

所以,可以这么写show_regexp方法,以说明具体的模式在何处发生匹配。

举个栗子:

def show_regexp(a, re)
  if a =~ re
    "#{$` } <<#{$&}>>#{$' }"
  else
    "no match"
  end
end
show_regexp('very interesting', /t/) -> very in <<t>>eresting

这个匹配也设置了线程局部变量(thread-local variables)???,$~与$1直到$9。

$~变量是MatchData对象,它持有你想知道的有关匹配的所有信息。???

$1等持有匹配各个部分的值,我们在后面会谈到这些。对那些看到类似Perl变量名就心怀畏惧的人来讲,请稍安勿躁得啦!

关于模式

每个正则表达式包含一种模式,用来对字符串进行正则表达式的匹配。

在模式内,除了.,|,(,),[,],{,},+,\,^,$,*和?字符之外,所有字符串匹配它们本身。

注意:在这些特殊字符之前放置一个反斜线便可以匹配它们的字面量。举个栗子:

show_regexp("yes | no", /\|/) -> yes <<|>> no

如果不加反斜线的话:

show_regexp("yes | no", /|/) -> <<>>yes | no

返回的竟然是:

<<>>yes | no

为什么会这样呢?
这是因为|是特别字符,未转义的竖线要么匹配在它之前的正则表达式,要么匹配在它之后的正则表达式。
这里的竖线(|)前后都是空,所以匹配的也是空,匹配的顺序是从左到右,所以就是上面那个样子。

但是,有些同学会疑惑,那到底是先匹配模式中竖线(|)的左边还是竖线的右边呢?让我们来做个对照试验。
举个栗子:

show_regexp(" red ball blue sky", /red|/) -> <<red>> ball blue sky
show_regexp("red ball blue sky", /|red/) -> <<>>red ball blue sky

red放在左边的时候,能匹配出字符串中的red
而red放在右边的时候,则不能匹配出字符串的red,反而匹配的是空。
这说明,前者在匹配的时候,是从模式中竖线的左边和字符串中的最左边的空开始匹配。

show_regexp("red ball blue sky", /red|blue/)  -> <<red>> ball blue sky
show_regexp("red ball blue sky", /blue|red/)  -> <<red>> ball blue sky

show_regexp("blue ball red sky", /red|blue/)  -> <<blue>> ball red sky
show_regexp("blue ball red sky", /blue|red/)  -> <<blue>> ball red sky

从上面的例子中,我们可以发现,无论匹配模式里的red和blue放在哪边,结果都是一样的,所以。

但是,如果把匹配字符串里的blue和red交换位置,我们会发现,匹配出来的不是red,竟然是blue。

所以,无论是先匹配模式中竖线的左边还是右边,都不会影响结果。

5.4.2基于模式的替换(Pattern-Based Substitution)

在字符串中能够发现模式有时就已经足够好了,如果你的朋友给出难题,让你找出顺序包含字母a,b,c,d和e的词,你可能会使用模式/a.b.cd.e/去查找词表并找出abjectedness,absconded,ambuscade和carbacidometer这些词等等,这种事情肯定有价值的。

当然,有时候待根据模式匹配去改变一些东西,让我们回到歌曲列表文件中,不管是谁,都会以小写方式输入歌曲演唱者的名字,以混合大小写方式在点唱机的屏幕上显示它们会更好看,怎么样才能把每个词的首字符改成大写呢?

String#sub和String#gsub方法超找出能够匹配第一个参数的那部分字符串,同时用第二个参数替换这它们。String#sub执行一次替换,而String#gsub在匹配每次出现都进行替换。

两个方法都返回了对含有这些替换字符串的新的拷贝。Mutator版本的String#sub!和String#gsub!会直接修改原先的字符串。

a = "the quick brown fox"
a.sub(/[aeiou]/, '*')
a.gsub(/[aeiou]/, '*')
a.sub(/\s\S+/, '')
a.gsub(/\s\S+/, '')

两个函数的第二个参数可以是String或block。如果使用block,匹配的字符串会被传递被block,同时block的结果值会被替换到原先的字符串中。

a = "the quick brown fox"
a.sub(/^./){|match| match.upcase}
a.gsub(/[aeiou]/) {|vowel| vowel.upcase}

所以,对如何转换歌曲演唱者名字的这个问题,我们给出了大难。匹配词的首字符的模式是\b\w——它寻找后面跟着词字符(word character)的词边界。把这个模式和gsub结合起来,可以改变歌曲演唱者的名字了。

def mixed_case(name)
  name.gsub(/\b\w/) {|first| first.upcase}
end
mixed_case("fats waller")
mixed_case("louis armstrong")
mixed_case("strength in numbers")

替换中的反斜线序列

早些时候我们注意到序列\1和\2等在模式中可用,它们代表至今为止第n个已匹配的组,相同的序列也可以作为sub和gsub的第二个参数。

"fred:smith".sub(/(\w+):(\w+)/, '\2, \1')
"nercpyitno".gsub(/(.)(.)/, '\2\1')

其他的反斜线序列在替换字符串中起的作用:&(最后的匹配),+(最后匹配的组),'(匹配之前的字符串),'(匹配之后的字符串)和\(字面量反斜线)。

如果想在替换中包含字面量反斜线,我们会变得很困惑,显然可以写成:

str.gsub(/\\/, '\\\\')

很清楚,代码正试图用两个反斜线替换str中的每个反斜线,程序员在替换文本中用了双倍的反斜线,因为知道它们在语法分析阶段中会被转化成\。但是当替换发生时,正则表达式引擎对字符串又执行了一遍,并把\转换成\,所以最终结果是使用另外一个反斜线替换每个反斜线。需要把它写成gsub(/\/, '\\\\')!
str = 'a\b\c'
str.gsub(/\/, '\\\\')
但是,如果利用&被替换为已匹配的字符串这个事实,也可以写成:
str = 'a\b\c'
str.gsub(/\/, '&&')

如果使用gsub的block形式,用来替换的字符串会被和仅被分析一次(在语法分析阶段),这正是所希望看到的结果。

str = 'a\b\c'
str.gsub(/\/) { '\\' }
最后,下面的例子极好的表达了结合使用正则表达式和block,请参见由Wakou A编写的CGI库模块的代码片段,这段代码接受包含HTML转义序列的字符串并把它转换成普通的ASCII。

为了支持日本用户,它在正则表达式上使用了n修饰符关闭了宽字符处理,它也说明了Ruby的case表达式。

def unescapeHTML(string) 
  str = string.dup
  str.gsub!(/&(.*?); /n) {
    match = $1.dup
    case match
    when /\Aamp\z/ni  then '&'
    when /\Aquot\z/ni then  '*'
    when /\Agt\z/ni     then '>'
    when /\Alt\z/ni       then '<'
    when /\A#(\d+)\z\/n then Integer($1).chr
    when /\A#x([0-9a-f]+)\z/ni  then $1.hex.chr
    end
  }
  str
end

puts unescapeHTML("1<2 & 4>3")
puts unescapeHTML(""A" = A = A")

输出结果:
1<2 && 4>3
"A" = A = A

dup:复制对象没有分配ID,并被视为新记录。请注意,这是一个“浅”副本,因为它仅复制对象的属性,而不是其关联。 “深”副本的范围是特定于应用程序的,因此由应用程序根据需要进行实施。 dup方法不保留时间戳(已创建|更新)_(at | on)。

5.4.3面向对象的正则表达式(Object-Oritended Regular Expressions)

必须承认,尽管所有这些古怪的变量用起来十分方便,但是它们不是面向对象的,同时它们很神秘,难道我们没有说Ruby的所有物体都是对象吗?哪里出错了?

真的没有错,在Matz设计Ruby时,他设计了一个完全面向对象的正则表达处理系统,然后在它之上包装(wrap)所有这些$变量,使它们看起来对Perl程序员很熟悉。对象和类还在这里,只不过就在表面下,让我们花点时间把它们挖掘出来吧。

实际上已经遇到一个类:正则表达式字面量创建了Regexp类的实例。

re = /cat/
re.class -> Regexp

Regexp#match方法对字符串匹配正则表达式。如果失败了,它返回nil。如果成功,则返回MatchData类的一个实例。MatchData对象让你访问关于这次匹配的所有可用信息,所有这些好东西通过$变量得到的,它们绑定在一个小巧方便的对象。

re = /(\d+):(\d+)/ # match a time hh:mm
md = re.match("Time: 12:34am")
md.class 
md[0] # == $&
md[1] # == $1
md[2] # == $2
md.pre_match # == $`
md.post_match # == $'

匹配数据存储在它自己的对象里,这样可以同时保留两个或多个模式匹配的结果,这些事情使用那些$变量是做不出来的。在下面的例子中,对两个字符串匹配相同的Regexp对象,每次匹配返回唯一的MatchData对象,通过检验两个子模式字段来验证它。

re = /(\d+):(\d+)/
md1 = re.match("Time: 12:34am")
md2 = re.match("Time: 10:30pm")
md1[1, 2]

第6章

关于方法的更多细节(more about Methods)

6.1 定义一个方法

  • 以小写字母开头

如果你使用大写字符,并不会立即得到一个错误,但是当Ruby看到你调用这个方法时,它首先猜测这是一个常量,而不是一个方法调用,且结果是Ruby可能错误地解析这个调用。

  • 表示查询的方法名通常以?结尾,例如instance_of?
  • “危险的”或者会修改接收对象(receiver)的方法,可以用!结尾,例如String提供了chop和chop!方法,第一个返回一个修改后的字符串;第二个则就地修改对象。

举个栗子:

我们已经新方法指定了一个名字,可能还需要声明某些参数(parameter,形参),它们就是括号中列出的局部变量。(方法参数两边的括号是可选的;我们的惯例是,当方法有参数时则使用括号,否则忽略它们。)

def my_new_method(arg1, arg2, arg3)
  #Code for the method would go there
end

Ruby可以让你指定方法参数(argument,实参)的默认值——如果调用者在传入参数时没有明确指定所使用的的值。为此你可以使用赋值操作符。

def cool_dude(arg1="Miles", arg2="Coltrane", arg3="Roach")
    "#{arg1}, #{arg2}, #{arg3}."
end

方法体内是普通的Ruby表达式,你不能在方法内定义非单件(nonsingleto)类或模块。如果你在一个方法内定义另一个方法,内部的方法只有在外部执行时才得到定义,方法的返回值是执行的最后一个表达式的值,或者return表达式显示返回的值。

6.1.1 可变长度的参数列表(Variable-Length Argument Lists)

但是如果你希望传入可变个数的参数(argument)、或者想用一个形参(parameter)接收多个参数,在“普通”的参数名前放置一个星号(*)即可。

def varargs(arg1, *rest)
  "Got #{arg1} and #{rest.join(', ')}"
end
varargs("one")
varargs("one", "two")

在这个栗子中,和往常一样第一个参数赋值给方法的第一个形参,不过,第二个形参的前缀为星号,因此所有剩余的参数被装入到一个新的Array中,然后赋值给第二个形参。

6.1.2 方法和Block(methods and Blocks)

之前讲过“Block和迭代器“,调用一个方法是,可以用一个block与之相关联,通常,您可以使用yield从方法内部调用这个block。

def take_block(p1)
  if block_given?
    yield(p1)
  else
    p1
  end
end

take_block("no block")
take_block("no block") {|s| s.sub(/no /, '')}

不过如果方法定义的最后一个参数前缀为&,那么所关联的block会被转换为一个Proc对象,然后赋值给这个参数。

class TaxCalculator
  def initialize(name, &block)
    @name, @block = name, block
  end
  def get_tax(amount)
    "#@name on #{amount} = #{ @block.call(amount) }"
  end
end
tc = TaxCalculator.new("Sales tax") { |amt| amt * 0.075 } 
tc.get_tax(100)

调用方法(Calling a Method)

你可以通过指定接受者、方法的名称、可选的参数及block,来调用一个方法。

connection.download_MP3("jitterbug") {|p| show_progress(p)}

在这个栗子中,connection对象是接收者,download_MP3是方法的名称,”jitterbug“是参数,花括号中的内容是相关联的block。

对类方法或者模块来说,接收者是类或模块的名字。

File.size("testfile")
Math.sin(Math::PI/4)

如果你省略了接收者,其默认为self,也就是当前的对象。

self.class
self.frozen

Ruby正是用这种默认的机制实现私有方法调用的,我们无法调用某个接受对象的私有方法,它们只是在当前对象(self)中是可用的。

而在前面的栗子中,我们调用self.class时必须要指定一个接收对象,这是因为class在Ruby中是一个关键字(它引入类的定义),因此单独使用它会产生语法错误。

可选的参数跟随在方法名之后,如果没有二义性,在调用方法时,你可以省略参数列表两侧的括号,不过,除非最简单的情况,我们不推荐这样做——某些微妙的问题可能会让你犯错误,我们的规则很简单:只要你有任何疑虑,就使用括号。

a = obj.hash
a = obj.hash()
obj.some_method

6.2.1 方法返回值(Method Return Values)
每个被调用的方法都会返回一个值(尽管没有规定说你必须要使用这个值)。方法的值,是在方法执行中最后一个语句执行的结果,Ruby有一个return语句,可以从当前的执行的方法中退出,return的返回值是其参数的值,如果不需要return就省略之,这是Ruby的一个惯用技法。

def meth_one
  "one"
end
meth_one
def meth_three
  100.times do |num|
    square = num*num
    return num, square if square >1000
  end
end

最后一种情况演示了,如果你给定return多个参数,方法会将它们以数组的形式返回。你可以使用并行赋值来收集返回值。

num, square = meth_three

在方法调用中的数组展开

我们最早看到,在方法定义中,如果你在一个正规参数前加一个星号,那么传入这个方法调用的多个参数将会被装入一个数组中,当然,也有某些操作是相反的方式。

当你调用一个方法时,你可以分解一个数组,这样每个成员都视为单独的参数。在数组参数(必须在所有普通参数的后面)前加一个星号完成这一点。

def five(a, b, c, d, e)
  "I was passed #{a} #{b} #{c} #{d} #{e}"
end

five(1, 2, 3, 4, 5)

让block更加动态

我们已经看到如何为方法调用关联一个block。

list_bones("aardvark") do |bone|

...

end

一般来说,这已经足够好了——你可以将一个固定的block关联到方法,就如同在if或while语句之后编写的一大块代码。

但是,某些时候,你希望更加灵活一些,例如,我们希望教授一些算术技法,学生可能想要一个n次相加或相乘的表,如果学生需要一个2倍次的表,我们就输出2,4,6,8等。

print "(t)imes or (p)lus:"
times = gets
print "number: "
number = Integer(gets)
if times =~ /^t/
  puts ((1..10).collect {|n| n*number}.join(",  "))
else
  puts ((1..10).collect {|n| n+number}.join(",  "))
end

这可以工作,不过在每个if语句之后重复实质上等价的代码,颇为丑陋,如果我们可以将完成计算的block抽取出来,代码就漂亮富多了。

print "(t)imes or (p)lus:"
times = gets
print "number: "
number = Integer(gets)
if times =~ /^t/
  puts (lambda{|n| n*number})
else
  puts (lambda {|n| n+number})
end

如果方法的最后一个参数前有&符号,Ruby将认为它是一个Proc对象,它会将其从参数列表中删除,并将Proc对象转换为一个block,然后关联到该方法。

收集散列参数

某些语言支持关键字参数——也就是你可以任意顺序传入参数的名称与值,而非按指定顺序传入参数。

因为我们在本书上一版中提到Ruby1.8将会支持这一特性,同时,人们可以使用散列表来取得相同的效果,例如,可以考虑为我们的SongList加入更强大的按名搜索功能。

class SongList
  def create_search(name, params)
    # ...
  end
end

list.create_search("short jazz songs", {
  'genre' => "jazz",
  'duration_less_than' => 270
})

第一个参数是搜索的名字,第二个是一个散列式,其中包括搜索的参数。使用散列表意味着我们可以模拟关键字:查询类型为”爵士乐“, 且时长小于4分半的歌曲。不过这种方式有点笨重,并且一组大括号容易误写为一个和方法关联的block。

因此,Ruby提供了一种快捷方式,只要在参数列表中,散列数组在正常的参数之后,并位于任何数组或block参数之前,你就可以使用键值对,所有的这些对会被集合到一个散列数组中,并作为方法的一个参数传入。无须使用大括号。

list.create_search('short jazz songs',
  'genre'=>"jazz",
  'duration'=>270
)

最后,Ruby的惯用技法是,你可以使用符号(Symbol)而非字符串作为参数,符号清楚地表达了你所引用的是某个事物的名字。

list.create_search('short jazz songs',
  :genre => :jazz,
  :duration = 270
)

一个潜心编写的Ruby程序通常包括许多方法,每个都很短小,因此熟悉定义和使用Ruby方法的各种选择是非常值得的。

第七章 表达式(Expression)

Ruby和其他语言的一个不同之处就是任何东西都可能返回一个值:几乎所有东西都是表达式。

明显的一个好处就是实现链式语句。

a = b = c = 0
[3, 1, 7, 0].sort.reverse

不太明显的好处是,C和Java中的普通语句在Ruby中也是表达式,例如,if和case语句都返回最后执行的表达式的值。

song_type = if song.mp3_type == MP3::Jazz
  if song.written < Date.new(1935, 1, 1)
    Song::TradJazz
  else
    Song::Jazz
  end
  else  
    Song::other
  end

rating = case votes_cast
  when 0...10 then Rating::SkipThisOne
  when 10...50 then Rating::CouldDoBetter
else Rating::Rave
end

运算符表达式(Operator Expression)

Ruby提供了基本的运算符集(如+、-、*、/等等),也提供了几个独特的运算符。

实际上,Ruby中的许多运算符是由方法调用来实现的,例如,当你执行 ab+c时,实际上你是请求a对象执行方法,传入的参数是b。然后请求返回的结果对象执行+方法,传入的参数是c。这等价于:

(a.*(b).+c)

因为任何东西都是对象,而且你可以重新定义实例方法,所以你可以重新定义任何不满足你需求的基本算术方法。

class Fixnum
  alias old_plus + # we can reference the original '+' as 'old_plus'
  # Redefine addition of Fixnums. This
  # is a BAD IDEA
  def +(other)
    old_plus(other).succ
  end
end

更有用的是,你写的类可以像内建那样参与到运算符表达式中,比如,你可能香葱歌曲中间剪辑一段,这可以用索引操作来实现。

class Song
  def [](from_time, to_time)
    result = Song.new(self.title + " [extract]"),
    self.artist
    to_time - from_time
    result.set_start_time(from_time)
    result
  end
end

这段代码扩展了类Song:提供了[]方法,该方法有两个参数(开始时间和结束时间)。它返回对应给定时间间隔的一个新的song对象。我们可以通过下面的代码来试听一首歌:

  song[0, 15].play

7.2 表达式之杂项
除了支持明显的运算符表达式和方法调用,以及不太明显的语句表达(例如if和case)外,Ruby的表达式还支持更多的东西。

7.2.1命令展开(Command Expansion)

如果你用反索引号(`), 或者以%x为前缀的分界形式,括起一个字符串,默认情况下它会被当做底层操作系统的命令来执行。表达式的返回值就是该命令的标准输出,由于没有去处新行符,所以你获得的返回值结尾可能会有回车符或者换行符。

`date`
`ls`.split[34]
%x{echo "Hello there"}

你也可以在命令字符串中使用表达式展开和所有普通的转义序列。

for i in 0..3
  status = 'dbmanager status id=#{1}'
  # ...
end

命令的退出状态(exit status)保存在全局变量$?中。

重定义反引号

在前面的描述中,我们说反引号括起来的字符串”默认“被当做命令执行。实际上,字符串被传递给了名为Kernel.方法(单引号)。如果你愿意,可以重载它。

alias old_backquote
  def `(cmd)
    result = old_backquote(cmd)
    if $? !=0
      fail "Command #{cmd} failed: #$?"
    end
    result
  end
print `data`
print `data`
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,302评论 5 470
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,232评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,337评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,977评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,920评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,194评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,638评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,319评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,455评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,379评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,426评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,106评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,696评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,786评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,996评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,467评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,043评论 2 341

推荐阅读更多精彩内容

  • 一、读书笔记回顾昨天的收获:什么是block、proc? block和proc是两种不同的东西, block有形无...
    柳辉阅读 370评论 0 0
  • 一、心得体会1、今天完成了什么? 看了Ruby核心、Duck typing、类与对象、RUby安全 看10个con...
    柳辉阅读 200评论 0 0
  • 一、心得体会1、今天完成了什么? 看了6个小时的镐头书18章、21章、22章,我想快速把书看一遍,然后看看哪些是需...
    柳辉阅读 388评论 0 0
  • 一、读书笔记3.4 剩余部分当对象需要访问同类的其他对象的内部状态时,使用保护访问(protected acces...
    柳辉阅读 361评论 0 0
  • 酒店 都说巴厘岛是一流的酒店,二流的沙滩。此行的酒店我们的领队牛小晟是花了大功夫做的功课,我们一起住了四间酒店 档...
    浮云小姐是造梦者阅读 168评论 0 0