public class TestVariable { static int s; int i; int j; { int i = 1; i++; j++; s++; } public void test(int j) { j++; i++; s++; } public static void main(String[] args) { TestVariable obj1 = new TestVariable(); TestVariable obj2 = new TestVariable(); obj1.test(10); obj1.test(20); obj2.test(30); System.out.println(obj1.i + "," + obj1.j + "," + obj1.s); System.out.println(obj2.i + "," + obj2.j + "," + obj2.s); }
①声明的位置
- 局部变量:方法体{}中,形参,代码块{}中
- 成员变量:类中方法外
- 类变量:有static修饰
- 实例变量:没有static修饰
②修饰符
- 局部变量:final
- 成员变量:public、protected、private、final、static、volatile、transient
③值存储的位置
- 局部变量:栈
- 实例变量:堆
- 类变量:方法区
④作用域
- 局部变量:从声明处开始,到所属的}结束
- 实例变量:在当前类中“this.”(有时this.可以缺省),在其他类中“对象名.”访问
- 类变量:在当前类中“类名.”(有时类名.可以省略),在其他类中“类名.”或“对象名.”访问
⑤生命周期
- 局部变量:每一个线程,每一次调用执行都是新的生命周期
- 实例变量:随着对象的创建而初始化,随着对象的被回收而消亡,每一个对象的实例变量是独立的
- 类变量:随着类的初始化而初始化,随着类的卸载而消亡,该类的所有对象的类变量是共享的
根据以上区别,图示展示当前变量在jvm内存中的位置
java中执行main方法整体流程图示如下
1、栈中的main方法区,内存中的局部变量ob1,obj2,指向堆中的 new TestVariable()对象,new TestVariable()在堆中存的是成员变量 int i,int j,而int s 因为是类变量所以存在了方法区中
2、new TestVariable() 无参构造的时候实际上执行<init>()方法,因此非静态代码块会执行,这个再执行都是在栈中处理
{
int i = 1;
i++;
j++;
s++;
}
所以这里的i就是局部变量,i++ 结果=1 ,j根据就近原则指向的是堆中的obj 的j 因此 obj1,obj2指向堆中的j++ 等于1 ,方法区中的类变量s++两次 这时候=2
3、obj1.test(10);实际执行代码如下
public void test(int j) { j++; i++; s++; }
这里的j是局部变量存在栈中,j++ 结果=11 ,i根据就近原则指向的是堆中的obj1的i,因此obj1指向堆中的i++等于1,方法区中的类变量s++1次 这时候=3
4、obj1.test(20);执行代码如下
public void test(int j) { j++; i++; s++; }
这里的j是局部变量存在栈中,j++ 结果=21 ,i根据就近原则指向的是堆中的obj1的i,因此obj1指向堆中的i++等于2,方法区中的类变量s++1次 这时候=4
5、obj2.test(30);
public void test(int j) { j++; i++; s++; }
这里的j是局部变量存在栈中,j++ 结果=31 ,i根据就近原则指向的是堆中的obj2的i,因此obj2指向堆中的i++等于1,方法区中的类变量s++1次 这时候=5
需要强调的是线程对主内存的操作(读取赋值)等必须是在自己的工作内存中,首先要将主内存的数据拷贝到自己的工作内存空间(共享变量副本),然后对变量进行操作,完成之后再将变量写到主内存中去。不能直接操作主内存的变量,各个线程中的工作内存中存储着存储着主内存的变量副本拷贝,因此不同的线程是无法访问对方工作内存,线程之间的通信(值传递)必须通过主内存来完成。
每次执行完方法当前栈中的内存就会释放
因此最后结果是 2,1,5 和 1,1,5
全文完,感谢您的耐心阅读~
欢迎大家关注我的公众号