Android中onCreate()前后初始化变量的区别及private声明问题
两种列表初始化方式的差异
先明确两种写法对应的执行逻辑,我们以Activity场景为例,两种写法的代码分别是:
// 写法1:成员位置只声明,onCreate里初始化 List<String> stringList; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); stringList = new ArrayList<>(); } // 写法2:成员位置声明时直接初始化 List<String> stringList = new ArrayList<>(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); }
两者的核心差异集中在3点:
- 初始化执行时机完全不同
声明时直接初始化的代码,会在Activity实例的构造函数执行阶段运行,时机远早于onCreate()回调。这个阶段Activity还没完成系统侧的attach流程,getIntent()拿不到启动参数、getBaseContext()返回null、依附的Window和View树也没创建,如果你的列表初始化需要依赖这些对象,直接写在声明位置会立刻触发空指针。而onCreate()执行时,Activity已经完成基础环境绑定,是官方推荐的业务初始化时机。 - 异常风险不同
如果你用写法1,万一某个逻辑在onCreate()执行前就调用了stringList(比如自定义的构造函数逻辑、父类构造函数里调用的重写方法),会直接抛出空指针异常。写法2因为在构造阶段就完成了赋值,不会出现这种“声明了但没赋值”的空指针,但前提是初始化逻辑不依赖任何生命周期里才能拿到的对象。 - 重建场景的表现一致
不管是哪种写法,在屏幕旋转、系统内存不足触发Activity重建时,都会跟着新的实例重新执行初始化,不会保留之前的列表数据。除非你给变量加了static修饰符,那写法2的初始化只会在类第一次加载时执行一次,后续重建不会重新赋值,很容易产生脏数据。
如果你的列表只是个不依赖任何组件环境的纯内存容器,两种写法功能上没有本质区别,选哪种都可以;如果初始化需要依赖Intent、Context、控件实例,必须放在onCreate()里做。
为什么成员位置声明的列表建议加private修饰
这不是强制语法要求,是面向对象封装的通用实践,核心原因有3个:
- 避免跨类误操作
Java里如果不写访问修饰符,默认是包级访问权限,同一个包下的其他类可以随意修改这个列表的指向、增删列表内容,甚至直接把变量置null,很容易触发你自己类内部的逻辑崩溃。加了private之后,只有当前类内部能访问、修改这个变量,从语法层面堵死了跨类误改的可能。 - 减少内存泄漏风险
Android里很多内存泄漏问题,都是因为非私有成员变量被内部类、外部类持有了引用,导致Activity销毁后无法被GC回收。把成员变量设为private,你可以完全控制变量的访问范围,避免引用被意外传到类外部。 - 降低维护成本
后续如果要给列表加线程安全校验、空判断、数据埋点逻辑,private变量可以直接在类内部的访问入口加逻辑,不需要改所有外部调用的代码。如果是public/包级访问的变量,后续改逻辑要排查所有调用点,维护成本高很多。
注意:访问修饰符只对类的成员变量生效,如果你是在
onCreate()方法内部声明的局部变量,不存在private/public的概念,只能在当前方法内访问。
内容的提问来源于stack exchange,提问作者Kelven Lim
相关产品推荐
相关产品推荐

