CodeRoadMap
首页路线题库资源库文章社区
CodeRoadMap

程序员的学习成长路线图。内容免费开放,登录后可同步路线图与课时进度。

由 程序员柏风 独立维护 · 关于作者

学习

  • 路线图
  • 课程
  • 面试题
  • 文章
  • 我

更多

  • 资源
  • 社区
  • 关于作者
  • 内容说明

© 2026 CodeRoadMap

津ICP备2026012044号-1·津公网安备12011002026155号·coderoadmap@126.com
Ruby 语言入门/课时/数据类型
RawBlame
课时目录12 节

Ruby 语言入门

  1. 01Ruby 环境
  2. 02数据类型
  3. 03语法基础
  4. 04控制流
  5. 05方法与 Blocks
  6. 06OOP 与 Mixin
  7. 07元编程
  8. 08IO 与 Enumerable
  9. 09并发
  10. 10Gems 与框架
  11. 11测试与质量
  12. 12综合练习

数据类型

Symbol/Array/Hash。

30 分钟

数据类型

这一课你将能做到

  1. 正确使用 Integer、Float、String、Symbol、Array、Hash、Range、nil 八种核心类型
  2. 说清 Symbol 与 String 的本质区别,知道什么时候该用哪个
  3. 用 Array/Hash 的常用方法完成增删改查,并理解 Ruby 的「一切皆对象」

为什么要学

Ruby 代码 80% 的操作是在字符串、数组、哈希之间倒腾数据。Rails 里从数据库取一行是 Hash 风格的访问,路由参数是 Hash,配置是 Hash——Hash 和 Symbol 不熟,Rails 源码一行都读不下去。

人话:类型是盛放数据的容器。写业务代码就是不停地把数据从一种容器倒进另一种容器,再筛一遍。容器认不全,等于进了厨房不认识锅碗瓢盆。

先建立图像

Ruby 常见类型一览
┌──────────┬───────────────────┬────────────────────┐
│ 类型      │ 字面量示例          │ 一句话定位           │
├──────────┼───────────────────┼────────────────────┤
│ Integer  │ 42, -7            │ 任意精度整数          │
│ Float    │ 3.14              │ 双精度浮点           │
│ String   │ "hello"           │ 可变字符序列          │
│ Symbol   │ :name             │ 不可变「名字标签」     │
│ Boolean  │ true / false      │ 仅两个实例           │
│ NilClass │ nil               │ 「什么都没有」的对象   │
│ Array    │ [1, "a", :x]      │ 有序、可混合类型      │
│ Hash     │ {name: "Tom"}     │ 键值对(保持插入序)   │
│ Range    │ (1..5), ("a".."z")│ 区间                │
└──────────┴───────────────────┴────────────────────┘

关键心智模型:Ruby 中一切值都是对象,包括数字和 nil。1.class #=> Integer,nil.class #=> NilClass。没有 Java 那种基本类型/包装类的分裂。

跟着做

步骤 1:数字与布尔

新建 types.rb:

# 数字:Integer 没有溢出问题,自动变大数
big = 2 ** 100
puts big                    # 一个 31 位数
puts 7 / 2                  # => 3   整数除法
puts 7.0 / 2                # => 3.5 有一个是浮点就是浮点结果
puts 7.divmod(2).inspect    # => [3, 1] 商和余数

# 只有 false 和 nil 是「假」,其余全为真——包括 0 和空字符串!
puts 0 ? "0 是真" : "0 是假"      # => 0 是真(Java/JS 程序员最易踩)
puts "" ? "空串是真" : "空串是假"   # => 空串是真

步骤 2:字符串与插值

name = "小明"
# 双引号支持插值 #{},单引号完全不解析
puts "你好,#{name},明年 #{18 + 1} 岁"   # => 你好,小明,明年 19 岁
puts '你好,#{name}'                       # => 你好,#{name}(原样输出)

s = "ruby"
puts s.upcase       # => "RUBY"   不改原串
puts s              # => "ruby"
s.upcase!           # 带 ! 的方法原地修改
puts s              # => "RUBY"

# 常用:分割、去空白、包含判断
"a,b,c".split(",")          # => ["a", "b", "c"]
"  hi  ".strip              # => "hi"
"hello world".include?("wor")  # => true

步骤 3:Symbol——Ruby 特有

# Symbol 是不可变的「名字」,写一次内存里只存一份
:name.object_id == :name.object_id   # => true,同一个对象
"a".object_id == "a".object_id       # => false,两个字符串对象

# 典型用途:Hash 的键、方法名、状态标签
status = :pending
user = { name: "Tom", role: :admin }   # name: 是 :name 的语法糖
puts user[:name]                       # => Tom
puts status == "pending"               # => false!Symbol 不等于 String

步骤 4:Array 增删改查

arr = [3, 1, 2]
arr << 4              # 尾部追加 => [3,1,2,4]
arr.push(5)           # 同上
arr.unshift(0)        # 头部插入 => [0,3,1,2,4,5]
arr.pop               # 删除尾部 => 5
puts arr.first.inspect   # => 0
puts arr.last.inspect    # => 4
puts arr[1..2].inspect   # => [3, 1]  区间切片
puts arr.sort.inspect    # => [0,1,2,3,4]  不改原数组
puts arr.map { |n| n * 10 }.inspect   # => [0,30,10,20,40]
puts arr.select { |n| n.even? }.inspect # => [0,2,4]
puts arr.sum              # => 10

步骤 5:Hash 与 Range

# Hash:保持插入顺序
prices = { apple: 3, banana: 2 }
prices[:orange] = 5            # 增
prices[:apple] = 4             # 改
prices.delete(:banana)         # 删
puts prices[:apple]            # => 4
puts prices[:missing].inspect  # => nil(不存在不报错)
puts prices.fetch(:missing, 0) # => 0(fetch 可给默认值)

prices.each do |fruit, price|
  puts "#{fruit}: #{price} 元"
end
puts prices.keys.inspect       # => [:apple, :orange]

# Range:连续区间
(1..5).to_a          # => [1,2,3,4,5]  双点含尾
(1...5).to_a         # => [1,2,3,4]    三点不含尾
("a".."e").to_a      # => ["a","b","c","d","e"]
(1..10).step(2).to_a # => [1,3,5,7,9]

运行 ruby types.rb 确认每一步输出。

讲清楚要点

  1. 一切皆对象:nil 是 NilClass 的唯一实例,有自己的方法(nil.to_s、nil.nil?)。这统一了模型,也是 Ruby 优雅的根基。
  2. Symbol vs String:Symbol 不可变、同一名字全局唯一、比较是 O(1) 指针比较;String 可变、每次字面量都新建对象。规则:当「名字/标签」用 Symbol,当「内容数据」用 String。
  3. 可变性与 ! 约定:方法名带 ! 表示原地修改接收者(危险版),不带则返回新对象。sort vs sort!、upcase vs upcase!。注意 ! 只是命名约定,不是语言机制。
  4. Hash 键不自动转换:{name: "x"}["name"] 取不到值——:name(Symbol)和 "name"(String)是两个键。Rails 的 HashWithIndifferentAccess 才做这种宽容处理。
  5. 真值表极简:全 Ruby 只有 false 和 nil 为假。0、""、[] 都是真。与 JavaScript 完全不同,务必刻进脑子。

你可能会懵的地方

  • 为什么 Ruby 要发明 Symbol 这种怪东西? 为了表达「这是一个标识符,不是一段文本」。Hash 键、方法名这类场景需要快速比较和不可变性,Symbol 正好是。它还省了字符串驻留的开销。

  • ** frozen_string_literal 是什么?** Ruby 3 中文件首行加 # frozen_string_literal: true,该文件所有字符串字面量自动冻结不可变,提升性能。新文件建议都加。

  • Array 能装不同类型吗? 能。[1, "a", :sym, [2, 3]] 完全合法。但实务上保持同类型,遍历处理时才不出幺蛾子。

  • arr[10] 越界会怎样? 返回 nil,不抛异常。这是 Ruby 的宽容风格,但也意味着拼错下标不会报错——调试时注意。

  • Range 的 .. 和 ... 哪个常用? ..(含尾)更常用。... 主要用于数组切片 arr[0...3](取前 3 个)和 Rails 时间区间。

错法对照

错法问题正解
if count == 0 当假判断后写 elseRuby 中 0 为真,逻辑反了显式 count.zero? 或牢记 0 是真
用字符串当 Hash 键又混用 symbol取值莫名 nil全项目统一用 symbol 键
s.upcase 后直接打印 s以为已修改,其实返回新串要改原串用 upcase! 或重新赋值
prices[:miss] 报错预期异常Hash 缺键返回 nil 静默通过需要严格时用 fetch(:key)
"5" + 3TypeError,Ruby 不做隐式转换"5".to_i + 3 显式转换

卡点急救

  1. Hash 取不到值但 puts 明明有:99% 是 Symbol/String 键混用。puts hash.keys 看真实键类型。
  2. 数字计算结果全是整数丢了小数:两个 Integer 相除。把任一操作数加 .0 或用 7.fdiv(2)。
  3. 字符串里中文乱码:文件保存为 UTF-8,且首行加 # encoding: UTF-8(Ruby 3 默认 UTF-8,一般不用加,Windows 旧编辑器除外)。

面试常问

  • Symbol 和 String 的区别? 可变性(Symbol 不可变)、内存(同名 Symbol 单例,同内容 String 多份)、用途(Symbol 做标识,String 装数据)、比较成本(Symbol O(1) 比较对象ID)。补一句 Ruby 2.2+ Symbol 会被 GC,不再有符号泄露 DoS 问题,更加分。

  • Ruby 的 nil 和其他语言的 null 有什么不同? nil 是 NilClass 的对象实例,有方法(nil.to_a => []、nil.nil? => true),可以参与链式调用而不立刻 NPE(配合 &.)。null 只是空指针关键字。

  • ==、eql?、equal? 区别? == 值相等;eql? 更严格(区分 1 和 1.0),Hash 键比较用它;equal? 是对象身份(object_id)相等。

自测三问

  1. 0、""、[]、nil、false 中哪些在 if 里是「假」?
  2. 什么时候 Hash 键应该用 Symbol,什么时候用 String?
  3. map 和 map! 的差别?! 后缀是语法还是约定?

小结一句话

记住「一切皆对象、只有 false 和 nil 为假、Symbol 是名字 String 是内容」,Ruby 类型系统就掌握了。

延伸

  • Ruby 官方核心类文档
  • 下一课:语法基础——变量、常量、运算符与 Ruby 独特的命名约定。
加载本节练习…
← 上一课Ruby 环境下一课 →语法基础