做了这么多年的Java,一般也不想了解一些细枝末节的东西比如如题的这三个东西。面试的时候常常会问到,我才留了个心。最近在细看flutter的东西,看到这里边一个变量,居然用了final来修饰,果断问了下豆包,有了新的认知。
Future<void> _login() async {
// 这里居然用了final来修饰一个变量,final不是不能修改的吗?
final Map<String, dynamic> data = {
"account": _phoneController.text,
"password": _codeController.text,
};
try {
final response = await loginAPI(data);
print(response);
_userController.updateUserInfo(response);
ToastUtils.showToast(context, "登录成功");
Navigator.pop(context);
} catch (e) {
ToastUtils.showToast(context, (e as DioException).message ?? "登录异常");
}
}
问:这里可以用final修饰吗?
答:是的,这里可以使用 final 修饰。
分析
- ✅ 可以使用
final:在这个场景下使用final是合适的 - ✅ 推荐使用
final:因为这个 [data](file:///C:/GitHub/flutter_learn/lib/app/pages/login/index.dart#L207-L210) 变量在创建后不需要重新赋值 - ✅ 性能优化:
final修饰符可以防止意外的重新赋值,提高代码安全性
详细说明
// 当前代码 - 已经使用了 final(正确)
final Map<String, dynamic> data = {
"account": _phoneController.text,
"password": _codeController.text,
};
- [data](file://C:\GitHub\flutter_learn\build\app\intermediates\flutter\debug\flutter_assets\vm_snapshot_data) 变量在初始化后只用于传递给 [loginAPI](file:///C:/GitHub/flutter_learn/lib/app/api/user_api.dart#L4-L8) 函数
- 没有对该变量进行重新赋值的需求
- 使用
final符合不可变性原则,使代码更加安全和可预测
因此,当前代码中使用 final 的做法是正确的。
问:但是这个对象中的值会随着用户输入的改变而变化,为什么还能用final修饰?
答:
关于 final 修饰 Map 的说明
1. final 与不可变性的理解
final修饰变量引用:防止变量指向其他对象- 不修饰对象内容:Map 内部的值仍可以修改
- 当前场景适用:[data](file:///C:/GitHub/flutter_learn/lib/app/pages/login/index.dart#L207-L210) 只用于 API 调用,无需后续修改
2. Dart 中的可变 vs 不可变
// final 修饰引用,但 Map 内容可变
final Map<String, dynamic> data = {"account": "123"};
data["account"] = "456"; // ✅ 允许修改内容
data = {}; // ❌ 不允许重新赋值
// 如需完全不可变,使用:
final Map<String, dynamic> immutableData = Map.unmodifiable({
"account": _phoneController.text,
"password": _codeController.text,
});
3. 当前代码的合理性
- [data](file:///C:/GitHub/flutter_learn/lib/app/pages/login/index.dart#L207-L210) 作用域:仅在
_login()方法内使用一次 - 数据流向:从控制器获取 → 组装对象 → 传递给 API → 完成使命
- 使用
final正确:防止意外的变量重新赋值,保证引用不变
4. 总结
final 修饰的是变量引用而非对象内容,因此即使用户输入变化,[data](file:///C:/GitHub/flutter_learn/lib/app/pages/login/index.dart#L207-L210) 在创建时捕获的值也不会影响 final 的使用合理性。
问:Java中的final也是类似的意思吗?
答:
Java 中的 final 与 Dart 中的 final 对比
1. 相似性
- 引用不变性:都用于防止变量引用被重新赋值
- 对象内容可变:修饰的容器对象内部元素仍可修改
- 编译时检查:都会在编译阶段检查是否违反
final规则
2. Dart 中的 final
final Map<String, String> data = {"key": "value"};
data["key"] = "new value"; // ✅ 允许
data = {}; // ❌ 不允许
3. Java 中的 final
final Map<String, String> data = new HashMap<>();
data.put("key", "value"); // ✅ 允许
data = new HashMap<>(); // ❌ 不允许
4. 关键差异
- 初始化时机:Java 支持在构造器中初始化
final字段 - 语法细节:Java 中
final可用于类、方法、变量多个层面 - 性能优化:Java 的
final在 JVM 优化中作用更显著
5. 总结
- 两种语言的
final概念基本一致 - 都是引用级别的不可变性
- 容器内容的可变性在两种语言中都存在
问:final和static的异同?
final 和 static 的异同
相同点
- 都用于修饰符:都是编程语言中的关键字
- 限制性:都对被修饰的元素施加某种限制
不同点
final
- 作用:修饰变量,表示不可变性
- 生命周期:运行时检查,变量只能被赋值一次
- 实例相关:通常用于实例变量或局部变量
- Dart 示例:
final String name = "user";
// name = "new"; // ❌ 编译错误
static
- 作用:修饰成员,表示类级别而非实例级别
- 内存分配:属于类本身,不依赖于实例
- 访问方式:通过类名直接访问,无需实例化
- Dart 示例:
class MyClass {
static String className = "MyClass";
static void printInfo() {
print("This is $className");
}
}
// 访问:MyClass.className 或 MyClass.printInfo()
结合使用
static final:最常见组合,用于定义常量- Dart 示例:
static final String API_URL = "https://api.example.com";
总结
final关注可变性,static关注作用域final限制赋值次数,static限制访问方式- 两者解决不同问题,可同时使用
问:static的访问方式有什么区别,应该是可变的吧?
static 访问方式的区别
访问方式对比
1. 访问途径
- 类名访问:
ClassName.staticMember - 实例访问:
instance.staticMember(不推荐,但仍可行)
2. Dart 中的访问示例
class MyClass {
static String className = "MyClass";
static void printInfo() {
print("Static method called");
}
}
// 推荐方式
MyClass.className;
MyClass.printInfo();
// 技术上可行但不推荐
MyClass instance = MyClass();
instance.className; // 不推荐
可变性说明
1. static 本身不保证不可变
- 可变:
static var或未使用final的static变量 - 不可变:
static final或static const
2. 示例对比
class Config {
// 可变的 static 变量
static String apiUrl = "https://api.example.com";
// 不可变的 static 常量
static final String APP_NAME = "MyApp";
static const int VERSION = 1;
}
// 可变 static 变量可以被修改
Config.apiUrl = "https://new-api.example.com"; // ✅ 允许
// 不可变 static 常量不能被修改
// Config.APP_NAME = "NewName"; // ❌ 编译错误
3. 实际情况
static修饰的成员可以是可变的- 是否可变取决于是否使用
final或const修饰 static只表示作用域(类级别),不表示可变性
总结
static成员通过类名直接访问static本身不保证不可变性- 可变性由
final/const关键字控制
问:
static final String APP_NAME = “MyApp”;
static const int VERSION = 1;
这两个final和const有什么区别
答:
final 和 const 的区别
编译时 vs 运行时
const:编译时常量,在编译时确定值final:运行时常量,在运行时首次赋值后不可变
内存分配差异
const:相同值的const对象共享内存,保证对象唯一性final:每次创建新的对象实例,不保证唯一性
使用场景对比
static const
static const int VERSION = 1; // 编译时已知的常量
- 适用于编译时确定的常量值
- 如数字、字符串字面量、布尔值等
static final
static final String APP_NAME = "MyApp"; // 运行时确定的常量
- 适用于运行时计算的常量值
- 如通过函数计算得到的值
初始化时机
const:必须在声明时确定值final:可以在运行时任何时间点赋值(但只能赋值一次)
性能差异
const:编译时优化,内存占用更优final:运行时初始化,灵活性更高
实际应用
在你的示例中:
static const int VERSION = 1- 版本号是编译时常量static final String APP_NAME = "MyApp"- 应用名通常也是编译时常量,可考虑改为const



5万+

被折叠的 条评论
为什么被折叠?



