问题static final DioRequest _instance DioRequest._init();如果只用final行不行为啥两个关键字连用才能保证单一实例且只会执行一个初始化解答这是一个非常棒的问题触及了 Dart 语言底层设计的核心。简单来说static 决定了“在那儿”final 决定了“不可变”两者结合才构成了“唯一且安全的单例”。我们可以拆开来看1. 为什么必须有 static空间唯一性如果去掉 static只写 final DioRequest _instance …•变成了“成员变量”它就属于每一个具体的对象而不是属于类。•逻辑死循环每次你创建一个 DioRequest它内部又会去初始化一个 _instance这个 _instance 又是一个新的 DioRequest…… 结果就是导致内存溢出Stack Overflow。•结论static 确保了无论你的 App 运行多久在内存中 _instance 这个变量名只占一个坑位它是类级别的全局共享。2. 为什么必须有 final时间唯一性/安全性如果去掉 final只写 static DioRequest _instance …• 可以被修改虽然它是唯一的但它是“可写的”。这意味着代码里任何地方都可以通过 DioRequest._instance DioRequest._init(); 强行覆盖掉它。• 单例失效一旦被覆盖旧的实例和它里面已经配置好的 _dio 就会被销毁或失去引用单例的“持久性”就没了。•结论final 保证了 “这个坑位一旦填上了谁也别想换掉它”3. 为什么“只会执行一次初始化”这是 Dart 语言的一个非常重要的特性静态变量是“懒加载Lazy Initialization”的。当你写staticfinalDioRequest_instanceDioRequest._init();Dart 引擎并不会在程序启动时立刻运行 _init()。它会一直等等到你第一次在代码中访问 DioRequest() 时1.引擎发现 _instance 还没初始化。2.执行 DioRequest._init()。3.把返回的结果塞进 _instance 这个坑位。4.因为有 final标记这个坑位为“已锁死”。5.以后所有的访问引擎都直接从这个坑位拿值不再运行 _init()。总结如果只用 final 行不行•在类内部不行。会导致上面说的死循环或变成普通成员变量。•在类外部文件顶层可以。其实代码最后一行写的就是这种方式finaldioRequestDioRequest();// 这也是一种单例因为它在全局作用域但是 即使你在外面定义了 final dioRequest如果类内部不写 static final _instance 加 factory 构造函数别人依然可以通过 DioRequest() 创建新的实例。最标准的做法你现在的写法 通过 static final _instance 配合私有构造函数从物理上锁死了外部创建第二个对象的可能性。这才是真正的单例模式。最开始的一版是这样写的importpackage:dio/dio.dart;importpackage:flutter_learn/app/stores/TokenManager.dart;import../constants/index.dart;classDioRequest{// 1.内部私有化的 Dio 实例这才是真正发送额请求的对象staticfinalDio_dioDio();// 构造函数DioRequest(){_dio.options..baseUrlGlobalConstants.BASE_URL..sendTimeoutDuration(milliseconds:GlobalConstants.TIME_OUT)..connectTimeoutDuration(milliseconds:GlobalConstants.TIME_OUT)..receiveTimeoutDuration(milliseconds:GlobalConstants.TIME_OUT);//拦截器_addInterceptors();}//....此处为了方便比较省略详细代码// 单例对象finaldioRequestDioRequest();会有问题在 Dart 中你目前的写法在文件顶层定义 final dioRequest DioRequest();确实可以让外部通过这个变量使用同一个实例起到“全局单例”的作用。但是从严格的单例模式和代码健壮性角度来看目前的写法存在两个问题并没有阻止外部创建新实例虽然你定义了 dioRequest但外部仍然可以调用 DioRequest() 来创建一个全新的对象。潜在的 Bug重复添加拦截器由于你的 _dio 是 static静态的staticfinalDio_dioDio();这意味着无论你创建多少个 DioRequest 对象它们都共享同一个 _dio。 而在你的构造函数里DioRequest(){// ... 配置 options_addInterceptors();// 每次创建实例都会执行}如果有人不小心多次调用了 DioRequest()_addInterceptors() 就会被多次执行导致同一个 _dio 实例上挂载了多个重复的拦截器。这会导致每次请求时拦截器里的代码比如打印日志、添加 Token运行多次。推荐的改进写法标准单例模式为了确保真正的单例并避免重复初始化建议这样修改importpackage:dio/dio.dart;importpackage:flutter_learn/app/stores/TokenManager.dart;import../constants/index.dart;classDioRequest{// 1.内部私有化的 Dio 实例这才是真正发送额请求的对象staticfinalDio_dioDio();// 2.静态私有变量保存 DioRequest 的唯一实例staticfinalDioRequest_instanceDioRequest._init();// 3.工厂构造函数当外部调用 DioResquest()时返回那一个唯一的 _instancefactoryDioRequest()_instance;// 构造函数// DioRequest() {// 4.私有化构造函数用于初始化配置DioRequest._init(){_dio.options..baseUrlGlobalConstants.BASE_URL..sendTimeoutDuration(milliseconds:GlobalConstants.TIME_OUT)..connectTimeoutDuration(milliseconds:GlobalConstants.TIME_OUT)..receiveTimeoutDuration(milliseconds:GlobalConstants.TIME_OUT);//拦截器_addInterceptors();}//....此处为了方便比较省略详细代码// 单例对象finaldioRequestDioRequest();1.使用._init()私有化构造函数。2.确保_addInterreceptors()只在整个App生命周期内执行一次。ps:附上最后完整类// 基于Dio的网络请求工具类importpackage:dio/dio.dart;importpackage:flutter_learn/app/stores/TokenManager.dart;import../constants/index.dart;classDioRequest{// 1.内部私有化的 Dio 实例这才是真正发送额请求的对象staticfinalDio_dioDio();// 2.静态私有变量保存 DioRequest 的唯一实例staticfinalDioRequest_instanceDioRequest._init();// 3.工厂构造函数当外部调用 DioResquest()时返回那一个唯一的 _instancefactoryDioRequest()_instance;// 构造函数// DioRequest() {// 4.私有化构造函数用于初始化配置DioRequest._init(){_dio.options..baseUrlGlobalConstants.BASE_URL..sendTimeoutDuration(milliseconds:GlobalConstants.TIME_OUT)..connectTimeoutDuration(milliseconds:GlobalConstants.TIME_OUT)..receiveTimeoutDuration(milliseconds:GlobalConstants.TIME_OUT);//拦截器_addInterceptors();}// 添加拦截器void_addInterceptors(){_dio.interceptors.add(InterceptorsWrapper(onRequest:(request,handler){// 注入token request headers Authorization Bearer $tokenif(tokenManager.getToken().isNotEmpty){request.headers[Authorization]Bearer${tokenManager.getToken()};}print( 打印网络请求URL ${request.uri.toString()});// 在发送请求之前做一些事情handler.next(request);// 继续发送请求},onResponse:(response,handler){// 在收到响应之前做一些事情if(response.statusCode!nullresponse.statusCode!200response.statusCode!300){handler.next(response);// 继续处理响应}else{handler.reject(DioException(requestOptions:response.requestOptions),);// 继续处理响应}},onError:(DioExceptione,handler){// 在发生错误之前做一些事情// handler.reject(e); // 继续处理错误handler.reject(DioException(requestOptions:e.requestOptions,message:e.response?.data[msg]?? ,),);},),);}// 写了两层Future包裹就会报错// FutureFuturedynamic get(String url,{MapString,dynamic? params}) async{Futuredynamicget(Stringurl,{MapString,dynamic?params}){return_handlerResponse(_dio.get(url,queryParameters:params));}Futuredynamicpost(Stringurl,{MapString,dynamic?data}){return_handlerResponse(_dio.post(url,data:data));}Futuredynamic_handlerResponse(FutureResponsedynamictask)async{try{Responsedynamicresponseawaittask;finaldataresponse.dataasMapString,dynamic;// 业务状态码 是 1 ,不是数字 1if(data[code]GlobalConstants.SUCCESS_CODE){print( 加载数据成功 ${data[result]});returndata[result];}// throw Exception(data[msg] ?? 加载数据异常);throwDioException(requestOptions:response.requestOptions,message:data[msg]??加载数据异常,);}catch(e){// throw Exception(catch 加载数据异常 $e);rethrow;// 不改变原来抛出的异常类型}}}// 单例对象finaldioRequestDioRequest();// dio请求工具发出请求 返回的数据 Responsedynamic.data// 把 Responsedynamic.data 转换为 MapString,dynamic// 把所有的接口的data解放出来// 拿到真正的数据 要判断业务状态码是不是等于1