Dart 变量与数据类型
变量是程序中存储数据的基本单元,数据类型决定了变量能存储什么值。
本章介绍 Dart 中的变量声明方式、基本数据类型以及重要的空安全机制。
变量的声明方式:var、final、const
Dart 提供了三种声明变量的关键字,分别适用于不同的场景。
理解它们的区别是学好 Dart 的第一步。
var——类型推断变量
使用 var 声明的变量,类型由初始值自动推断,且类型一旦确定就不能更改。
实例
// 类型推断:name 的类型被推断为 String
var name = 'RUNOOB';
// 类型推断:age 的类型被推断为 int
var age = 10;
print('$name 已经 $age 年了');
// 以下写法会报错——类型确定后不能再赋其他类型的值
// name = 123; // 错误:不能将 int 赋值给 String
}
RUNOOB 已经 10 年了
var 不是"无类型"或"动态类型",它的类型在赋值那一刻就固定了。这和 JavaScript 的 var 完全不同。
final——运行时常量
使用 final 声明的变量只能赋值一次,但赋值时机可以延迟到运行时。
实例
// final 变量:一旦赋值就不能再修改
final currentTime = DateTime.now();
print('当前时间: $currentTime');
// 延迟赋值也是允许的
final String greeting;
greeting = 'Hello, RUNOOB!'; // 第一次赋值 OK
// greeting = 'Hello again'; // 错误:final 变量不能再次赋值
print(greeting);
}
当前时间: 2026-06-12 10:30:00.000 Hello, RUNOOB!
const——编译时常量
const 比 final 更严格:它的值必须在编译时就能确定。
实例
// const 必须在编译时就能确定值
const siteName = 'RUNOOB';
const maxUsers = 100;
print('站点: $siteName, 最大用户数: $maxUsers');
// 以下写法会报错——DateTime.now() 是运行时才能确定的值
// const currentTime = DateTime.now(); // 错误!
}
站点: RUNOOB, 最大用户数: 100
三者对比总结
| 关键字 | 可修改 | 赋值时机 | 典型场景 |
|---|---|---|---|
| var | 可以 | 任意 | 值会变化的普通变量 |
| final | 不可修改 | 运行时确定 | 从 API 获取的配置、当前时间 |
| const | 不可修改 | 编译时确定 | 数学常量、固定配置、颜色值 |
一个实用建议:默认使用 final。当你发现需要修改它时,再改为 var。这个习惯能让代码更安全,减少意外修改导致的 bug。
数字类型:int 与 double
Dart 的数字类型分为两种:int(整数)和 double(浮点数)。
它们的共同父类是 num。
实例
// int:整数,范围是 -2^63 到 2^63-1
int viewCount = 10000;
int negativeNumber = -42;
// double:双精度浮点数
double price = 9.99;
double pi = 3.1415926;
// num 可以同时接收 int 和 double
num someNumber = 10;
someNumber = 10.5; // 合法
print('浏览次数: $viewCount');
print('价格: ¥$price');
print('圆周率: $pi');
}
浏览次数: 10000 价格: ¥9.99 圆周率: 3.1415926
数字常用方法
实例
void main() {
int a = -10;
double b = 3.7;
// 绝对值
print('a 的绝对值: ${a.abs()}'); // 10
// 四舍五入取整
print('b 四舍五入: ${b.round()}'); // 4
// 向下取整
print('b 向下取整: ${b.floor()}'); // 3
// 向上取整
print('b 向上取整: ${b.ceil()}'); // 4
// 字符串转数字
int parsed = int.parse('42');
double parsedDouble = double.parse('3.14');
print('解析结果: $parsed, $parsedDouble');
// 生成随机数(需要 import 'dart:math')
var random = Random().nextInt(100);
print('随机数(0-99): $random');
}
a 的绝对值: 10 b 四舍五入: 4 b 向下取整: 3 b 向上取整: 4 解析结果: 42, 3.14 随机数(0-99): 67
字符串:String 与插值语法
String 用于表示文本,Dart 的字符串支持单引号和双引号两种写法。
字符串插值(String Interpolation)是 Dart 最方便的特性之一。
实例
// 字符串可以用单引号或双引号
String singleQuoted = 'Hello';
String doubleQuoted = "World";
// 多行字符串:使用三个引号
String multiLine = '''
这是第一行
这是第二行
这是第三行 - RUNOOB
''';
// 字符串插值:使用 $变量名 或 ${表达式}
String site = 'RUNOOB';
int years = 10;
// $变量名:直接引用变量
print('欢迎来到 $site');
// ${表达式}:嵌入复杂表达式
print('$site 已经陪伴我们 ${years + 1} 年了');
// 转义字符
print('换行符: 第一行\n第二行');
print('制表符: 列1\t列2');
}
欢迎来到 RUNOOB RUNOOB 已经陪伴我们 11 年了 换行符: 第一行 第二行 制表符: 列1 列2
字符串常用方法
实例
String text = ' Hello, RUNOOB! ';
// 去除首尾空格
print('trim: [${text.trim()}]');
// 转大写 / 转小写
print('大写: ${text.toUpperCase()}');
print('小写: ${text.toLowerCase()}');
// 是否包含某个子串
print('包含 RUNOOB? ${text.contains('RUNOOB')}');
// 是否以某字符串开头/结尾
print('以空格开头? ${text.startsWith(' ')}');
print('以!结尾? ${text.trim().endsWith('!')}');
// 字符串替换
print('替换: ${text.replaceAll('RUNOOB', 'DART')}');
// 字符串分割
String csv = 'Dart,Flutter,Google';
List<String> items = csv.split(',');
print('分割结果: $items');
// 字符串拼接
print('拼接: ' + 'Hello' + ', ' + 'World');
}
trim: [Hello, RUNOOB!] 大写: HELLO, RUNOOB! 小写: hello, runoob! 包含 RUNOOB? true 以空格开头? true 以!结尾? true 替换: Hello, DART! 分割结果: [Dart, Flutter, Google] 拼接: Hello, World
布尔值 bool
bool 类型只有两个值:true 和 false。
它主要用于条件判断,配合 if 语句使用。
实例
bool isLoggedIn = true;
bool hasPermission = false;
// 布尔表达式
bool canAccess = isLoggedIn && hasPermission; // 与:两者都为 true
bool isGuest = !isLoggedIn; // 非:取反
print('可以访问: $canAccess');
print('是访客: $isGuest');
// 比较运算产生 bool 值
int age = 20;
bool isAdult = age >= 18;
print('是否成年: $isAdult');
// 直接用于条件判断
if (isAdult) {
print('欢迎访问 RUNOOB');
}
}
可以访问: false 是访客: false 是否成年: true 欢迎访问 RUNOOB
Dart 是强类型语言,条件判断中只接受 bool 类型。这和 JavaScript 不同——在 Dart 中,if(0) 或 if('') 这样的写法会直接报错。
空安全(Null Safety)
空安全是 Dart 2.12 引入的最重要特性。
它的核心思想是:默认情况下,所有变量都不能为 null,除非你明确声明。
可空类型与非空类型
在类型后面加 ? 表示该变量可以为 null。
实例
// 非空类型:不能赋值为 null
String name = 'RUNOOB';
// name = null; // 错误!name 不能为 null
// 可空类型:允许为 null
String? nickname;
nickname = null; // OK
nickname = 'R'; // 也 OK
print('昵称: $nickname');
// int? 也可空
int? score;
print('分数: $score'); // 输出 null
}
昵称: R 分数: null
空值处理操作符
| 操作符 | 说明 | 示例 |
|---|---|---|
| ?? | 如果左侧为 null,使用右侧值 | name ?? '默认名' |
| ??= | 如果变量为 null,则赋值为右侧 | name ??= '默认名' |
| ?. | 安全访问:对象为 null 时跳过调用 | user?.name |
| ! | 断言非 null(谨慎使用) | name! |
实例
String? username;
// ?? 操作符:如果 username 为 null,使用默认值
String displayName = username ?? 'RUNOOB 访客';
print(displayName); // RUNOOB 访客
// ??= 操作符:如果为 null 则赋值
username ??= '新用户';
print(username); // 新用户
// ?. 安全访问:只在非 null 时调用
String? maybeNull;
print(maybeNull?.length); // null(不会报错)
maybeNull = 'Hello';
print(maybeNull?.length); // 5
// ! 断言:我确定它不是 null(为 null 时会抛异常)
String definitelyNotNull = 'RUNOOB';
print(definitelyNotNull!.length); // 6
}
RUNOOB 访客 新用户 null 5 6
尽量避免使用 ! 断言操作符。如果你频繁使用 !,说明你的类型设计可能有问题。优先使用 ??、??= 和 ?. 来安全地处理空值。
类型转换
在 Dart 中,不同类型的值可以相互转换。
实例
// 数字 → 字符串
int count = 42;
String countStr = count.toString();
print('数字转字符串: $countStr (类型: ${countStr.runtimeType})');
// 字符串 → 数字
String priceStr = '19.99';
double price = double.parse(priceStr);
print('字符串转数字: ¥$price');
// int ↔ double
int integer = 10;
double decimal = integer.toDouble();
print('int 转 double: $decimal');
double pi = 3.14;
int piInt = pi.toInt(); // 截断小数部分
print('double 转 int (截断): $piInt');
// 任何值 → 字符串
bool flag = true;
print('bool 转字符串: ${flag.toString()}');
// 字符串 → bool(需要手动判断)
String boolStr = 'true';
bool parsed = boolStr == 'true';
print('字符串转 bool: $parsed');
}
数字转字符串: 42 (类型: String) 字符串转数字: ¥19.99 int 转 double: 10.0 double 转 int (截断): 3 bool 转字符串: true 字符串转 bool: true
