故事起因

一个Java类中可以存在多种形式的变量,可以是最一般的成员变量、或静态变量、或临时变量。如下图:

public class VariableDemo {static int staticValue = 0; // 静态变量int fieldValue;             // 全局变量public int getMultiValue() {int localValue = 10;    // 局部变量return localValue * 2;}
}

图中的staticValuefieldValue和方法内部的localValue分别为静态变量、成员变量和局部变量。

但是在我阅读Android或者是JDK源码时,经常看到以下形式的代码:

public class Hashtable {private transient HashtableEntry<?,?>[] table;private void addEntry(int hash, K key, V value, int index) {HashtableEntry<?,?> tab[] = table;...// Creates the new entry.HashtableEntry<K,V> e = (HashtableEntry<K,V>) tab[index];tab[index] = new HashtableEntry<>(hash, key, value, e);count++;}
}

上图中,在HashTable源码的 addEntry 方法内部创建了一个临时的局部变量tab,用来替代成员变量table,并且后续的操作都由tab对象来执行。

那么这么做的意义是什么呢?

实践检验

这3种变量的存取效率是有区别的,合理使用这3种变量能够帮助我们实现更高性能的代码。可以用一个简单的例子来展示这3种变量各自的存取效率,如下代码:

package com.subject.variable;
/*** 使用临时变量提高Java代码性能*/
public class VariableDemo {private long instVar;// 成员变量private static long staticVar; // 静态变量// 存取类方法中的临时变量void tempAccess(long val) {int j = 0;// 临时变量long startTime = System.currentTimeMillis();for (long i = 0; i < val; i++) {j += 1;}long endTime = System.currentTimeMillis();System.out.println("temp var: " + (endTime - startTime) + " milli seconds");}// 存取类的成员变量void instanceAccess(long val) {long startTime = System.currentTimeMillis();for (long i = 0; i < val; i++) {instVar += 1;}long endTime = System.currentTimeMillis();System.out.println("instance var: " + (endTime - startTime) + " milli seconds");}// 存取类的 static 变量void staticAccess(long val) {long startTime = System.currentTimeMillis();for (long i = 0; i < val; i++) {staticVar += 1;}long endTime = System.currentTimeMillis();System.out.println("static var: " + (endTime - startTime) + " milli seconds");}public static void main(String[] args) {VariableDemo test = new VariableDemo();long count = 10 * 1000 * 1000 * 1000L;test.tempAccess(count);test.instanceAccess(count);test.staticAccess(count);}
}

解释说明

这段代码中的每个方法都执行相同的循环,并反复相同的次数。唯一的不同是每个循环使一个不同类型的变量递增:

  • tempAccess 使一个方法的局部变量递增

  • instanceAccess 使类的一个成员变量递增

  • staticAccess 使类的一个静态变量递增

计算结果如下:

temp var: 2987 milli seconds
instance var: 4039 milli seconds
static var: 3996 milli seconds

从结果中可以发现,instanceAccess 和 staticAccess 的执行时间基本相同。但是,tempAccess 要快两到三倍。

原理分析

存取堆栈变量如此快是因为JVM 存取局部变量的存取效率比它成员变量静态变量更高。

JVM 是一种基于堆栈的虚拟机,每一个方法对应一个栈帧,每个方法在线程中执行时,都会通过栈帧的形式进行运算。如下图:

所有局部变量都存储在一个栈帧中的局部变量表中,通过局部变量表与操作数栈的协同合作,局部变量可以被高效地存取。

相比之下,成员变量静态变量的存取成本就相对较高。因为 JVM 必须使用代价更高的操作码,并从常数存储池中存取它们。(常数存储池保存一个类型所使用的所有类型、字段和方法的符号引用)。通常,在第一次从常数存储池中访问成员变量静态变量以后,JVM 将动态更改字节码以使用效率更高的操作码。尽管有这种优化,堆栈变量的存取仍然更快。

注意:这也是为什么JVM会自动优化成员变量转化为局部变量,详细内容可以参考链接:不要让你的 Java 对象"逃逸"了!

代码优化

具体优化措施可以参考文章开头介绍的Android或Java源码中的做法,使用局部变量替换成员变量或者静态变量,以便通过基于栈操作使代码运行更加高效。

package com.subject.variable;/*** 使用临时变量提高Java代码性能*/
public class VariableDemo2 {private long instVar;// 成员变量private static long staticVar; // 静态变量// 存取类方法中的临时变量void tempAccess(long val) {int j = 0;// 临时变量long startTime = System.currentTimeMillis();for (long i = 0; i < val; i++) {j += 1;}long endTime = System.currentTimeMillis();System.out.println("temp var: " + (endTime - startTime) + " milli seconds");}// 存取类的成员变量void instanceAccess(long val) {long startTime = System.currentTimeMillis();long tmp=instVar;for (long i = 0; i < val; i++) {tmp += 1;}instVar=tmp;long endTime = System.currentTimeMillis();System.out.println("instance var: " + (endTime - startTime) + " milli seconds");}// 存取类的 static 变量void staticAccess(long val) {long startTime = System.currentTimeMillis();long tmp=staticVar;for (long i = 0; i < val; i++) {tmp += 1;}staticVar=tmp;long endTime = System.currentTimeMillis();System.out.println("static var: " + (endTime - startTime) + " milli seconds");}public static void main(String[] args) {VariableDemo2 test = new VariableDemo2();long count=10*1000*1000*1000L;test.tempAccess(count);test.instanceAccess(count);test.staticAccess(count);}
}

计算结果:

temp var: 2973 milli seconds
instance var: 3074 milli seconds
static var: 3018 milli seconds

如果你喜欢本文

长按二维码关注

Java中合理使用局部变量替代成员变量、静态变量相关推荐

  1. java中的局部变量、成员变量、类变量

    局部变量:在方法.构造函数或者语句块中定义的变量被称为局部变量. 特点:变量的声明和初始化都是在方法中,方法结束后,变量就会自动销毁. 例:下面代码块的s2 成员变量:成员变量是定义在类中,方法体之外 ...

  2. java中类变量局部量_java入门---变量类型类变量局部变量实例变量静态变量

    在Java语言中,所有的变量在使用前必须声明.声明变量的基本格式如下: typeidentifier[= value][, identifier[= value]...]; 格式说明:type为Jav ...

  3. java 定义变量时 赋值与不赋值_探究Java中基本类型和部分包装类在声明变量时不赋值的情况下java给他们的默认赋值...

    探究Java中基本类型和部分包装类在声明变量时不赋值的情况下java给他们的默认赋值 当基本数据类型作为普通变量(八大基本类型: byte,char,boolean,short,int,long,fl ...

  4. java根据父类找子类_在java中实现多态时,可以通过父类变量引用子类的对象。_学小易找答案...

    [单选题]对于字符串s="java",下面哪个选项返回字符在字符串中'v'的位置( ). [简答题]汽车核保的主要内容 [判断题]在java中产生子类对象时,直接调用子类的构造方法 ...

  5. java 基本类型 不赋值_探究Java中基本类型和部分包装类在声明变量时不赋值的情况下java给他们的默认赋值...

    探究Java中基本类型和部分包装类在声明变量时不赋值的情况下java给他们的默认赋值 当基本数据类型作为普通变量(八大基本类型: byte,char,boolean,short,int,long,fl ...

  6. java三大变量——静态变量、实例变量、局部变量

    文章目录 前言 一.java中变量分类 二.三者的区别与联系 1.静态变量.实例变量.局部变量区别 2.成员变量的两种细分类型的区别 总结 前言 变量是内存当中存储数据最基本的单元,将数据(字面量)放 ...

  7. Java学习笔记——局部变量和成员变量

    声明:本文首发于我的个人微信公众号[Java编程社区],查看更多文章与学习资源请移步我的公众号Java编程社区 成员变量:定义在类中的变量. 局部变量:定义在方法中的变量. 成员变量与局部变量的区别: ...

  8. Java 静态变量中包含变量,修改变量静态变量无法改变问题

    先描述下问题场景:接口地址中父类地址为可变地址,修改父类地址后发现接口地址未曾改变.: public class Init {private static String baseUrl = " ...

  9. java中数据类型的等级_Java 数据类型、变量

    Java 数据类型 在 Java 中,对于每一种数据都定义了明确的具体的数据类型,在内存中分配了不同大小的内存空间. 整数类型 (byte.short.int.long) 1.Java 各整数类型有固 ...

最新文章

  1. Android 游戏开发必备的基础知识
  2. python实现逻辑回归的流程_逻辑回归原理及其python实现
  3. hdu 6034 B - Balala Power! 贪心
  4. 【CyberSecurityLearning 68】python 编写exp
  5. POJ1155 TELE(树形DP)
  6. RabbitMQ在Ubuntu上的环境搭建
  7. MySQL主从同步相关-主从多久的延迟?
  8. python按位处理二进制文件_对如何在python shell中对16位二进制数执行按位运算感到困惑...
  9. Win7安装英文语言包(图文)
  10. 测试换发型用啥软件,什么软件可以测试自己适合的发型
  11. 2021年国内PT站点汇总(中英文名称对照表)很全呦!
  12. 如何设置快速启动栏 win7 快速启动栏 快速启动栏不见了
  13. hdu5651 xiaoxin juju needs help(组合数学)
  14. design pattern scard
  15. w7计算机摄像头怎么打开,如何打开摄像头,详细教您Win7摄像头怎么打开
  16. Centos7命令行连接wifi网络,手机usb共享网络
  17. 各种类型sizeof大小及C++有符号数与无符号数进行比较
  18. 新网站对SEO工作内容简介
  19. 自用笔记17——泰波那契数列
  20. 输出图案(四)----输出正多边形图案:(难度系数:半颗星)

热门文章

  1. Autovue直连P6 EPPM
  2. Centos7开机自启动手册
  3. 如何快速检索PDF文档中的关键词?
  4. phonex的使用,二级索引,预分区,调优
  5. 攻防世界--no-strings-attached
  6. 异常 exception
  7. 获取Mac地址getMacAddress
  8. 大连理工大学开发区校区新手指南——2.校园介绍篇
  9. 超级任天堂模拟器 bsnes 开发者自杀(文末附模拟器及ROM)
  10. 人工智能里你不知道的那些事!所有人都感兴趣的文章