今天的练习
周一,一天三轮九问:上午 Java 的 String 家族三连,下午前端 computed/watch、组件通信、箭头函数,收工前数据库 JOIN、主键和唯一索引、HTTP 状态码。整体状态不错,九问只栽了一个硬错误——我把 MySQL 唯一索引的外键资格直接取消了,这个坑值得单独记一笔。
题目一:String 的不可变性、== 与 equals、StringBuilder 和 StringBuffer
String 的"不可变性"是什么意思?为什么这么设计?== 和 equals() 有什么区别?StringBuilder 和 StringBuffer 有什么区别?
我的第一版回答:String 是引用类型,存堆内存的地址,跟 const 的不可变性一样,不同的是 String 保持的地址可以变而 const 不能变。不可变是因为安全性,天然线程安全。== 比较地址的值,equals 会比较两个地址对应的值是否相等。StringBuilder 和 StringBuffer 的区别是 StringBuffer 有同步锁……StringBuffer 没有同步机制,属于裸奔,但是性能好。
复盘
- 名字说反了:裸奔没锁的是 StringBuilder,有同步锁的是 StringBuffer。内容理解全对,就是最后两个名字对调,面试里这种口误最冤。
- 类比只说了一半:const(JS)锁的是地址,指向的对象内容随便改;String 反过来,锁的是内容,引用(地址)可以换成别的对象。俩是镜像关系,不是"一样"。
- 漏掉的:equals 比内容不是天生的——Object 里 equals 默认就是 ==,是 String 类重写了它才变成比内容。另外不可变还带来常量池共享和 hashCode 缓存,这是 String 当 HashMap key 稳的根因。
标准答案
String 内部的字符数组被 final 锁死,任何"修改"都会产出新对象,原对象不动。好处:常量池安全共享、hashCode 可缓存、天然线程安全、作为 HashMap key 不会被篡改。== 比地址,equals(String 重写后)比内容。StringBuilder 单线程用,无锁快;StringBuffer 方法带 synchronized,多线程共享时才用。
一句话记住
String 锁内容,const 锁地址;裸奔的是 Builder,带锁的是 Buffer。
题目二:computed 和 watch 的区别
computed 和 watch 有什么区别?什么场景用哪个?
我的第一版回答:computed 是计算属性,依赖不变不重新计算,模板 {{}} 直接用,依赖变化就会立即重新计算。watch 是监听,数据变化执行回调,没有返回值,每次变化都触发。一般尽量用 computed,性能好没副作用。
复盘
- 细节翻车:computed 不是"立即重算",是惰性的——依赖变化只打个脏标记,下次被读取时才真正重算,没人读它一次都不算。这正是它省性能的另一半原因。
- 场景举偏了:"点按钮改状态"直接在事件函数里改就行,轮不到 watch。watch 的主场是副作用:值变了要发请求、存 localStorage、防抖后调接口这类"变化之后做点什么"的操作。
标准答案
computed:基于依赖缓存、必须有返回值、惰性求值,适合派生数据(拼接、过滤、排序、列表统计)。watch:监听变化执行回调、无返回值,适合副作用(异步请求、本地存储、DOM 操作)。派生数据用 computed,要"做事"用 watch。
一句话记住
computed 算个值,watch 干件事。
题目三:父子组件通信
父组件给子组件传数据用什么?子组件通知父组件又用什么?说说流程。
我的第一版回答:父传子用 props,子组件 defineProps 声明接收;子传父用 emit,defineEmits 声明事件,内部 emit('事件名', 数据),父组件用 @ 监听。
复盘
- 这题全对,没坑。流程完整。
- 加分延伸:面试官常追问跨层级——爷爷传孙子用 provide/inject,全局共享状态上 pinia。另外 v-model 就是 props + emit 的语法糖,说出来很加分。
一句话记住
父到子 props,子到父 emit,跨层 provide/inject,全局 pinia。
题目四:箭头函数和普通函数的区别
箭头函数和普通函数有什么区别?至少说出两点。
我的第一版回答:最大区别是 this——箭头函数认外层作用域的 this,普通函数谁调用 this 就是谁。另外箭头函数没有 new,也没有 arguments。
复盘
- 全对。要求两点答了三点。
- 加分延伸:正因为认外层 this,箭头函数不适合当对象方法——
sayName: () => this.name里的 this 不指向对象本身,拿到 undefined。事件处理、原型方法都要用普通函数。
一句话记住
箭头函数没有自己的 this,借外层的用到底。
题目五:内连接和左外连接
INNER JOIN 和 LEFT JOIN 有什么区别?学生表和成绩表做 LEFT JOIN 和 INNER JOIN 结果有什么不同?
我的第一版回答:内连接只保留两表都有的数据;左外连接保留左表全部数据和右表符合条件的行。学生表 LEFT JOIN 成绩表会保留所有学生,有成绩的带上成绩;内连接只留有成绩的学生。
复盘
- 全对。
- 加分延伸:LEFT JOIN 没匹配上的行右表字段全 NULL——找"没成绩的学生"用 WHERE 成绩 IS NULL;统计每个学生成绩条数要 COUNT(成绩.id) 而不是 COUNT(*),不然没成绩的学生会被数成 1 而不是 0。
一句话记住
LEFT JOIN 保左表,没配上的全是 NULL,数条数认准右表主键。
题目六:主键和唯一索引——今天最大的坑
主键(PRIMARY KEY)和唯一索引(UNIQUE)有什么区别?一张表能建几个?
我的第一版回答:主键不能为空、一张表一个、自动建索引、可以作为外键。唯一索引可以为空但空只能有一个,不然算重复;可以有多个、自动建索引、不能作为外键。主键是一行数据的身份证,唯一索引是某列的值不能重复。
复盘
- 硬错误:唯一索引"不能作为外键"——错。主键和唯一键都可以被外键引用。手机号列建唯一索引、订单表引用它,是很常见的设计。我把唯一键的外键资格直接取消了。
- 跨库背错了口诀:"空只能有一个"是 SQL Server 的规矩。MySQL 的唯一索引列可以有多行 NULL,NULL 之间不视为重复。规则要以主力数据库的文档为准,不能混着背。
- 措辞不严谨:不是"主键可以作为外键",是主键能被别的表的外键引用,方向别说反。
标准答案
主键:非空 + 唯一,一张表一个,自带索引,天然可以被外键引用,是一行数据的身份证。唯一索引:保证列值不重复,一张表可以多个;MySQL 里允许多个 NULL;同样自带索引、同样可以被外键引用。区别主要在数量(1 对多)和是否允许 NULL(主键不允许,唯一索引在 MySQL 里允许多个)。
一句话记住
MySQL 里 NULL 不参与唯一性;唯一键和主键一样,都能被外键看上。
题目七:HTTP 状态码,301 和 302 的区别
200、301、302、404、500 分别是什么意思?301 和 302 有什么区别?
我的第一版回答:200 成功;301 永久重定向,浏览器和搜索引擎会更新收录新地址;302 临时重定向,下次还访问原地址;404 资源不存在;500 服务器内部错误。301 会缓存、SEO 权重转移到新地址,适合换域名和 HTTP 升 HTTPS;302 不缓存不转移权重,适合未登录跳登录页。
复盘
- 全对,还是超纲全对。缓存差异、SEO 权重转移、典型场景全齐。
一句话记住
301 永久搬家带走权重,302 临时借道原址不动。
数据库的口诀不能跨数据库背——SQL Server 的 NULL 规则安在 MySQL 头上,就是一个实打实的错误答案。
今天九问只栽一个硬错误,感觉比上周踏实多了。唯一索引外键那个坑提醒我:背八股要落到自己天天用的数据库上验证一遍,别把别的库的规矩当成通用的。明天 Java 开始上 B+ 树,接着练。