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