hi 大家好。先预告一下,下文中仅涉及java语法的讨论,和Android源码关系不大,请不要有阅读压力。我发现在Android的源码中很多地方对final关键字的用法很是“别出心裁”,之所以这么说是因为我从没看过是这么使用final关键字的,一个典型的例子是View类中onScrollChanged方法(不妨将其成为方案一): protected void onScrollChanged(int l, int t, int oldl, int oldt) {
mBackgroundSizeChanged = true; final AttachInfo ai = mAttachInfo;
if (ai != null) {
ai.mViewScrollChanged = true;
}
}看到了吗?此处mAttachInfo是View类的一个成员变量,而在这个方法中Android的程序员并没有直接操作mAttachInfo变量,而是先赋值给一个标明为final的局部变量ai,然后再操作这个ai。这个写法我很是想不通,这不是多此一举吗?但是仔细想想又觉得没这么简单,身经百战的Android开发小组这么写应该不会是空穴来风,难道这种写法真的有其他的目的?想了很久也猜了很久,有个念头突然蹦了出来,难道这种写法是因为多线程编程的需要?考虑下面这种写法(不妨将其成为方案二): protected void onScrollChanged(int l, int t, int oldl, int oldt) {
mBackgroundSizeChanged = true; if (mAttachInfo != null) { // #1
mAttachInfo.mViewScrollChanged = true; // #2
}
}在上面这种写法中,取消了final的局部变量ai而直接操作mAttachInfo。考虑这样一种场景,假设线程A执行完#1将要执行#2时,突然有另外一个线程B在其他地方对mAttachInfo做了修改,将其指向了另外一个对象,那么线程A执行到#2时,操作的将是这个新的对象而不是原对象,而在方案一中,则可以避免这种现象。以上只是一种猜测,我认为方案一中的写法是一种简单有效的同步方式,大家认为呢?
mBackgroundSizeChanged = true; final AttachInfo ai = mAttachInfo;
if (ai != null) {
ai.mViewScrollChanged = true;
}
}看到了吗?此处mAttachInfo是View类的一个成员变量,而在这个方法中Android的程序员并没有直接操作mAttachInfo变量,而是先赋值给一个标明为final的局部变量ai,然后再操作这个ai。这个写法我很是想不通,这不是多此一举吗?但是仔细想想又觉得没这么简单,身经百战的Android开发小组这么写应该不会是空穴来风,难道这种写法真的有其他的目的?想了很久也猜了很久,有个念头突然蹦了出来,难道这种写法是因为多线程编程的需要?考虑下面这种写法(不妨将其成为方案二): protected void onScrollChanged(int l, int t, int oldl, int oldt) {
mBackgroundSizeChanged = true; if (mAttachInfo != null) { // #1
mAttachInfo.mViewScrollChanged = true; // #2
}
}在上面这种写法中,取消了final的局部变量ai而直接操作mAttachInfo。考虑这样一种场景,假设线程A执行完#1将要执行#2时,突然有另外一个线程B在其他地方对mAttachInfo做了修改,将其指向了另外一个对象,那么线程A执行到#2时,操作的将是这个新的对象而不是原对象,而在方案一中,则可以避免这种现象。以上只是一种猜测,我认为方案一中的写法是一种简单有效的同步方式,大家认为呢?
final Connection currentConnection = mConnection; // #1
if (currentConnection != null) {
currentConnection.close; // 省略 try-catch 代码块
}
}当执行完#1时,无论 mConnection 是否已经指向一个新的连接(比如其他线程在其他地方又重新创建了一个),我都能保证在执行#1时 mConnection 指向的连接能够正确关闭(此处的赋值操作是一个原子操作)。
1、为什么在方法里要先将全局变量赋给一个局部变量?
2、为什么要给这个局部变量添加final关键字?我有个同事说,使用final关键字可能可以提高效率,如果被他言中,首先解释了为什么第2个问题为什么使用final关键字,其实解释了第1个问题为什么要将全局变量赋给局部变量(因为只有先赋给局部变量,这个局部变量才有机会声明成final)。可是,将一个局部变量声明成final真的可以提高性能么?
第一步:将全局变量赋给一个局部变量:
只是为了取得 在程序运行过程中,该全局变量的当前时刻的值!
但这样是不够的!因为设置 变量local=global,在JAVA里只是将局部的一个引用指向这个全局变量,当全局变量发生改变时,引用它的这个局部变量值会随之改变,倘若局部变量在这个局部代码块里正在运行,又中途被改变了,这会发生多么严重的错误!第二部:在局部变量前添加final:
这样的话,在该局部代码块中,就不能以任何方式去更改这个引用变量的值了,这样,下面代码的运行安全就有了保障!但在其他代码块里,仍然可以有其他语句去更改那个全局变量的值!总结:
这段小插曲的作用,其实就是对全局变量取一次样,就像在特定时刻取一次样品,来做实验一样,来做一些事情。取得的样品当然不希望中途变质,要让它final,才能正常使用。而全局变量就任由他继续变化去吧
另外,如果在这段代码里创建内部类,并在内部类里使用这个局部变量的话,确实会报错。
Cannot refer to a non-final variable inside an inner class defined in a different method
另外,楼主发现的解决线程同步的问题,也是正解。
比如 final 集合 a=new 集合();
集合 增删该插 都能操作
但是 就是不能改变内存地址 也就是不能 a=new 集合(); 了
就楼主的这个代码段来看,写不写final没有任何本质的改变,不会因为写了final而影响任何玩意方法参数,及局部变量使用 final 修饰,最重要的作用就是可以给下面定义的内部类使用
个别时候是为了强制约束变量不可更改
@Override
public View getView(int position, View convertView, ViewGroup parent) {
final int aaa = position;
System.out.println("---------" + aaa);
ViewHolder holder = null;
if(convertView == null){
holder = new ViewHolder();
convertView = mInflater.inflate(R.layout.items, null);
holder.tv1 = (TextView)convertView.findViewById(R.id.textView1);
holder.tv2 = (TextView)convertView.findViewById(R.id.textView2);
holder.btn1 = (Button)convertView.findViewById(R.id.button1);
holder.btn1 = (Button)convertView.findViewById(R.id.button1);
convertView.setTag(holder);
}else {
holder = (ViewHolder)convertView.getTag();
}
holder.tv1.setText((String)list.get(position).getName());
holder.tv2.setText(list.get(position).getNum()+"");
holder.btn1.setText(list.get(position).getNum()+"a");
holder.btn1.setOnClickListener(
new OnClickListener() {
@Override
public void onClick(View v) {
//按钮点击事件
System.out.println("this is aaa:"+aaa);
}
});看看这个例子中如果不用final的话就会报错
因为如果有别的线程把mAttachInfo改变了。那后面的ai改变就变的毫无意义。
那这样说来final就是锁定内存地址,在这段时间其他线程读写内存的访问都要排队等候。
这样其实也是一个同步锁的作用。
效果上来看与synchronized(mAttachInfo)是一样的。但是由于synchronized还是有点性能消耗,所以这里加上final真的是很nice。
b》关于final局部变量是习惯吧。。比如经常如果在一个函数内部的局部内部类的参数有局部变量的话,该参数经常会加final,因为局部类对象的生命周期和局部变量不一致,会出现函数退出栈后,内部变量死了,局部内部类对象健在,指向一个不存在的“尸体”。
同时呢,也可能出于逻辑意义上提醒coder不改变值的意思。因为把外地对象的变量传递给局部变量以后,局部变量的改变不会回馈给外部对象,所以对其值的修改没有意义。
以上,个人理解~