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