记一次final对局部变量的错误使用

115 阅读2分钟

问题的产生

我司app采用H5混合开发,其中H5页面又采用动静分离,即静态资源,如js、css等,是一次下载缓存到本地,避免重复下载。随着业务的丰富,需要下载的静态静态资源包逐渐增多。在一次换机后,需要一次性下载所有的资源包,我发现一个概现的问题,下载的资源包不能全部解压。
下面是下载过程的核心代码:

private void processResourceDownload(String fileId) {
    ITDownloader downloader = getTDownloader();
    // 将fileId添加到downloader,生成downloadId
    long downloadId = downloader.downloadUrl(fileId);
    final DownloadZipBean downloadZipBean = new DownloadZipBean(downloadId, fileId);
    idownloadCallBack = new IdownloadCallBack() {
	public void onSuccess(long downloadId) {
            // downloadId用来比较完成的是哪一个下载任务
            if (downloadZipBean.getDownloadId() == downloadId) {
                // 解压缩(fileId)
            }
	}
    };
    downloader.addDownloadCallback(idownloadCallBack);
}

这段代码中有一个匿名内部类IdownloadCallback,他要访问外部的DownloadZipBean(比较downloadId),IDE会提醒dever,需要给DownloadZipBean加上final关键字。为什么呢?

分析

在很多地方都能看到,被final修饰的局部变量,相当于是全局变量。 因为局部变量是存储在栈,随着方法执行结束,这个局部变量也会被销毁。但是匿名内部类的对象不一定销毁了的,他还是有访问局部变量的情况的。给局部变量添加final关键字,相当于它是全局变量。更深层原因是局部变量存储与栈,全局变量存储与堆,被final修饰的局部变量也存储于堆。
上面的问题产生的原因是,瞬间添加多个下载任务时,全局变量(final修饰的那个)只有一个,且被改变过。所以下载完成的任务,比较donwloadId时不会通过,而无法完成加压缩任务。

修改

这个问题产生的原因,简单说就是,在下载方法内,一个下载任务对应一个DownloadZipBean。但方法多次调用时,多个下载任务还是对应一个DownloadZipBean。所以解决的思路是让多个下载任务对应多个DownloadZipBean即可。具体有两种方法。 1、用一个List,或者Set来记录DownloadZipBean; 2、把DownloadZipBean传递给那个Callback,比较时使用DownloadZipBean内部的DownloadZipBean。

总结

被final修饰的局部变量,相当于全局变量。这句话在很多地方看到。我对这句话记得很牢固,但也仅停留在记住的基础上。对这句话的使用,也仅仅是在匿名内部类访问局部变量时,IDE提示错误,我根据提示添加一下final而已,并没有更深层次的理解。今天通过这个问题的发现以及分析过程,我对final的理解和使用更进了一步。工作中大牛领导经常说要知其然知其所以然,不然可能平常用的好好的功能,突然某一天跳出来给你一刀背刺。