{
"name": "Dart 开发实战",
"lvNames": ["基础语法", "控制流与函数", "集合", "面向对象", "空安全与异步", "Dart 高级特性"],
"questions": [
{
"lv": 1,
"q": "开发注册页时,用户输入的年龄是字符串,需要转成 int 保存。已知 String ageText = \"18\";,正确的转换写法是?",
"opts": ["int(ageText)", "toInt(ageText)", "int.parse(ageText)", "ageText.parseInt()"],
"ans": 2,
"exp": "int.parse() 把字符串解析成整数,解析失败会抛 FormatException,更稳妥的是 int.tryParse()(失败返回 null)。注意 Dart 没有 \"18\".parseInt() 这种写法,int(...) 也不是转换函数。"
},
{
"lv": 1,
"q": "项目里要定义一个\"永远不允许修改\"的 API 地址常量,以下哪种声明最合适?",
"opts": ["final String api = \"https://api.example.com\";", "const String api = \"https://api.example.com\";", "String api = \"https://api.example.com\";", "var api = \"https://api.example.com\";"],
"ans": 1,
"exp": "const 是编译期常量:值在编译时就确定,且不可变,编译器可直接内联替换,还能配合 const 构造函数做实例复用。final 是运行期赋值一次后不可变。给字面量常量用 const 是更优选择。"
},
{
"lv": 1,
"q": "你在重构代码时写下:var x = 10; x = \"hello\"; 会发生什么?",
"opts": ["正常运行,x 变成字符串", "编译报错:x 已被推断为 int 类型", "运行时报错", "x 自动变为 null"],
"ans": 1,
"exp": "var 声明时由初始值自动推断类型,推断后类型就固定了:x 是 int,再赋 String 在编译期直接报错。想存任意类型,应显式声明 dynamic 或 Object?。"
},
{
"lv": 1,
"q": "给变量起名时,以下哪个不是合法的 Dart 标识符?",
"opts": ["_score", "2x", "中文名", "name_2"],
"ans": 1,
"exp": "Dart 标识符不能以数字开头。_score 合法(下划线开头表示库内私有,是 Dart 的约定俗成);Dart 支持 Unicode 标识符,中文变量名同样合法。"
},
{
"lv": 1,
"q": "结算功能计算人均消费:int total = 10; int people = 4; double avg = total / people; 打印 avg 得到?",
"opts": ["2", "2.5", "2.0", "报错"],
"ans": 1,
"exp": "Dart 中 int 除以 int 的结果一定是 double:10 / 4 = 2.5。想得到整除结果用 ~/:10 ~/ 4 == 2,取余用 %。"
},
{
"lv": 2,
"q": "写优惠逻辑:订单数 > 10 打 9 折,> 50 打 8 折。对 orders = 60,以下代码会得到什么折扣?
double d; if (orders > 10) { d = 0.9; } else if (orders > 50) { d = 0.8; }",
"opts": ["0.8", "0.9", "1.0", "0.7"],
"ans": 1,
"exp": "if-else 链从上到下匹配,命中即止:60 > 10 先成立就直接取 0.9,后面的 else if 不再执行。正确写法应把范围大的条件放前面(先判断 > 50)。判断顺序是 if-else 链最经典的坑。"
},
{
"lv": 2,
"q": "接口返回 Map<String, dynamic> data,想遍历所有 key 打印出来,以下哪种写法正确?",
"opts": ["for (var k in data) print(k);", "for (var k in data.keys) print(k);", "data.keys.forEach(print);", "B 和 C 都可以"],
"ans": 3,
"exp": "Map 本身不能直接 for-in 遍历,要先取 keys / values / entries。B 是标准写法;C 中 forEach(print) 利用了函数引用,同样正确。两种都行,按团队规范选择。"
},
{
"lv": 2,
"q": "写一个返回用户名的函数:String getUserName() { },忘了写 return,会怎样?",
"opts": ["返回 null", "编译报错", "运行时崩溃", "自动返回空字符串"],
"ans": 1,
"exp": "空安全下,声明了非空返回类型 String 的函数必须有 return,漏写直接编译报错,把隐患消灭在编译期。如果确实允许无返回值,应把返回类型声明为 String?。"
},
{
"lv": 2,
"q": "问候函数:void greet(String name, {String greeting = \"你好\"}),调用 greet(\"小明\", \"早上好\") 会发生什么?",
"opts": ["正常执行,greeting 为 \"早上好\"", "编译报错:命名参数不能按位置传参", "greeting 变为 null", "编译警告但能运行"],
"ans": 1,
"exp": "{} 包裹的是命名参数,必须写成 greet(\"小明\", greeting: \"早上好\");[] 才是可选位置参数。命名参数让调用意图清晰,是团队协作中常用的做法。"
},
{
"lv": 2,
"q": "写\"重试发送验证码\"逻辑:要求至少发送一次,失败则继续重试。哪种循环语义最贴切?",
"opts": ["while", "do-while", "for", "repeat"],
"ans": 1,
"exp": "do-while 先执行循环体再判断条件,保证至少执行一次,正是\"先发一次、不行再重试\"的语义。while 先判断后执行,可能一次都不执行。"
},
{
"lv": 3,
"q": "实现\"删除待办\"功能,list.remove(3) 和 list.removeAt(3) 的区别是?",
"opts": ["remove 按值删除,removeAt 按下标删除", "两者完全相同", "remove 删下标,removeAt 按值删除", "remove 会删除所有等于 3 的元素"],
"ans": 0,
"exp": "remove(value) 删除第一个值相等的元素并返回是否成功;removeAt(index) 按下标删除并返回被删的元素,越界抛 RangeError。写代码前想清楚你要删的是\"值\"还是\"位置\"。"
},
{
"lv": 3,
"q": "解析接口数据:用 data[\"name\"] 取一个不存在的 key 时,返回什么?",
"opts": ["抛异常", "null", "空字符串", "抛出 NoSuchMethodError"],
"ans": 1,
"exp": "Map 用 [] 取值时,key 不存在返回 null 而不是抛异常。所以 data[\"name\"] 可能为 null,取值后判空再使用是常规操作(配合 ?? 兜底)。"
},
{
"lv": 3,
"q": "配置列表按条件插入元素:var l = [1, 2, if (isVip) 3]; 当 isVip 为 false 时 l 是?",
"opts": ["[1, 2]", "[1, 2, 3]", "[1, 2, false]", "编译错误"],
"ans": 0,
"exp": "这是 collection-if 语法:条件不成立时该元素直接不生成。Flutter 里常用于条件渲染组件列表,配合 ... 展开和 collection-for 一起使用。"
},
{
"lv": 3,
"q": "两个用户各自关注了一批标签,要找出共同关注的标签,用哪种写法?",
"opts": ["a & b", "a.intersect(b)", "a.toSet().intersection(b.toSet())", "a.union(b)"],
"ans": 2,
"exp": "List 没有交集方法,要先转 Set。intersection() 返回两个集合的共同元素;union() 是并集。注意方法名是 intersection(),不是其它语言的 intersect()。"
},
{
"lv": 3,
"q": "计算购物车总价:List<double> prices = []; 空列表调用 prices.reduce((a, b) => a + b) 会?",
"opts": ["返回 0", "抛出 StateError 异常", "返回 null", "返回空列表"],
"ans": 1,
"exp": "reduce 要求列表至少有一个元素,空列表直接抛 \"Bad state: No element\"。更稳的写法是 fold(0, (a, b) => a + b):带初始值,空列表返回 0。"
},
{
"lv": 4,
"q": "写日期工具类,希望 DateUtil.parse(\"2024-01-01\") 在字符串不合法时返回 null 而不是抛异常,用哪种构造函数?",
"opts": ["factory 构造函数", "普通构造函数", "命名构造函数", "const 构造函数"],
"ans": 0,
"exp": "factory 构造函数可以不返回新实例——可以返回缓存实例、子类实例,甚至返回 null。它把\"按需创建/校验\"的逻辑封装在构造入口,调用方写法统一且安全。"
},
{
"lv": 4,
"q": "日志打印、权限检查这类能力要被多个互不相关的类复用,又不想强制继承关系,Dart 的推荐做法是?",
"opts": ["extends 继承", "implements 实现", "with 混入 Mixin", "abstract 抽象类"],
"ans": 2,
"exp": "Mixin(with)把可复用逻辑抽出来按需混入,避免\"为了复用而强行继承\"的长继承链。Flutter 源码大量使用,如 class _X with RouteAware。"
},
{
"lv": 4,
"q": "类内部要声明一个\"仅供本文件使用\"的私有字段,正确写法是?",
"opts": ["private String _token;", "String _token;", "-String token;", "#token"],
"ans": 1,
"exp": "Dart 没有 private 关键字,以下划线开头表示库内私有:_token。注意这是\"库\"级别私有(同文件可见),不是类级别。"
},
{
"lv": 4,
"q": "用户类的 name 字段希望赋值时自动去掉首尾空格,最佳做法是?",
"opts": ["声明 getter/setter,在 setter 里 trim", "直接写公共字段,调用方自己 trim", "用 final 禁止修改", "用 static 修饰"],
"ans": 0,
"exp": "setter 在赋值入口统一处理,所有调用方(包括构造函数)都自动享受规范化,不用每个地方都记得 trim。写法:String get name 与 set name(String v)。"
},
{
"lv": 4,
"q": "class Dog implements Animal 与 extends 继承的关键区别是?",
"opts": ["implements 要求实现 Animal 的全部成员(含字段),extends 可复用已有实现", "两者完全一样", "implements 只能用于抽象类", "extends 要求实现全部成员"],
"ans": 0,
"exp": "implements 把 Animal 当接口:所有成员都要自己重写,即使父类已有实现;extends 继承实现,只覆写需要改的部分。implements 用于\"规定能力\",extends 用于\"复用实现\"。"
},
{
"lv": 5,
"q": "用户对象可能为 null:User? u = await fetchUser();,以下哪种使用方式更安全?",
"opts": ["if (u != null) { print(u.name); } 判空后直接用 u", "if (u != null) { print(u!.name); } 必须加 !", "print(u?.name ?? \"\");", "A 和 C 都可以"],
"ans": 3,
"exp": "A:局部变量判空后编译器会自动提升为非空类型,直接使用即可;C:?. 安全调用 + ?? 兜底,一行搞定。只有被闭包捕获的变量才需要 !。"
},
{
"lv": 5,
"q": "详情页要同时请求\"用户信息\"和\"订单列表\"两个接口,都拿到再渲染,用哪个方法?",
"opts": ["Future.wait([fetchUser(), fetchOrders()])", "串行写两个 await", "Future.any", "Future.delayed 等待"],
"ans": 0,
"exp": "Future.wait 并发启动多个 Future,全部完成后统一返回结果列表,总耗时约等于最慢的那个;串行 await 则是两个请求耗时相加。Future.any 只等第一个完成的。"
},
{
"lv": 5,
"q": "某个字段要等登录成功后才能赋值,但又想声明成非空类型,正确声明方式是?",
"opts": ["late String token;", "String? token = \"\";", "dynamic token;", "final token = null;"],
"ans": 0,
"exp": "late 表示\"推迟初始化,首次访问时才赋值\",适合依赖异步结果的字段或计算代价高的属性。注意:late 变量在赋值前被访问会抛 LateInitializationError。"
},
{
"lv": 5,
"q": "把解析结果转成目标类型:var age = data[\"age\"] as int;,当 age 实际是 String 时会发生?",
"opts": ["返回 null", "抛出 TypeError(类型转换失败)", "静默忽略", "自动转成 int"],
"ans": 1,
"exp": "as 是强制类型转换,类型不符立即抛异常。安全做法:先用 is 判断类型,或先统一规范数据(如 num 一律 .toInt())。"
},
{
"lv": 5,
"q": "对可能为 null 的 String? s 使用 s!,若 s 确实为 null 会?",
"opts": ["返回 null", "抛出 TypeError(空检查失败)", "静默继续", "编译错误"],
"ans": 1,
"exp": "! 是强制解包运算符,向编译器承诺\"这里不可能为 null\",承诺落空时运行时抛 TypeError。能用判空提升或 ?? 的地方尽量别用 !,减少运行时崩溃点。"
},
{
"lv": 6,
"q": "Flutter 中常见 ButtonStyle()..backgroundColor = ... ..shape = ... 写法,.. 级联操作符的核心行为是?",
"opts": ["对同一对象连续操作且返回对象本身", "链式调用并返回最后一次结果", "只能用于 List", "与 ... 展开等价"],
"ans": 0,
"exp": "级联操作符 .. 对同一对象连续执行多个操作,不改变返回值(始终是原对象),适合集中配置对象。. 返回的是方法调用的返回值,两者行为不同。"
},
{
"lv": 6,
"q": "Dart 3 中,函数想直接返回\"用户名 + 年龄\"这组小数据,又不想专门建一个类,推荐用?",
"opts": ["Map<String, dynamic>", "Record 记录类型:(String name, int age)", "List<dynamic>", "用全局变量"],
"ans": 1,
"exp": "Record 是 Dart 3 的轻量聚合类型:var r = (\"张三\", 18); 或命名字段 (name: \"张三\", age: 18),用 r.$1 / r.name 取值,还能配合模式匹配解构,比 Map 更类型安全。"
},
{
"lv": 6,
"q": "Dart 3 的 switch 可以直接当表达式赋值:var s = switch (status) { \"ok\" => \"成功\", _ => \"未知\" }; 这种写法?",
"opts": ["合法:switch 表达式返回一个值", "非法:switch 不能作表达式", "必须每个分支加 break", "只能匹配字符串"],
"ans": 0,
"exp": "switch 表达式用 => 代替冒号,整体作为值赋给变量,配合模式匹配可以解构、判断类型,比旧式 switch 简洁,也天然避免了漏 break 的坑。"
},
{
"lv": 6,
"q": "想给系统类 String 加一个 isEmail() 方法,但 String 不能改源码,用?",
"opts": ["extension 扩展:extension EmailCheck on String { bool isEmail() {...} }", "继承 String", "用 mixin 混入", "写静态工具类"],
"ans": 0,
"exp": "extension 可以不修改原类给已有类型添加方法,之后 \"a@b.com\".isEmail() 直接调用。注意扩展方法是静态分发(编译期决定),不参与运行时多态。"
},
{
"lv": 6,
"q": "泛型容器 class Box<T extends num> {} 中 extends num 的含义是?",
"opts": ["T 只能是 int、double 等数值类型", "T 可以是任意类型", "T 必须可空", "T 必须是集合类型"],
"ans": 0,
"exp": "泛型约束限定 T 必须是 num 及其子类(int/double),这样 Box 内部就能安全调用数值运算。不加约束时 T 等价于 Object?。"
}
]
}