成员变量指在类中定义的变量,即field,局部变量指在方法中定义的变量,Main也是方法。(Java并没有全局变量这一说法)
1. 类变量(也叫静态变量)
- 和类共存亡,只要类在,就可以访问类变量,类变量是对所有对象共享的
- 大多数静态变量声明为public,因为它们必须可用于类的使用者
- 当定义变量为public static final,那么变量的名称(常量)是大写
- 只能用在方法,构造方法,块外,不能用于静态块中
- 静态变量不能在静态块中定义,可以在类内部定义静态变量,然后在静态块中初始化操作
2. 实例变量
- 实例变量作为实例的一个成员,单独属于这个对象的,改变它,不影响其他对象
- 在类中声明,但在方法,构造函数,块外面
- 当空间分配给某个对象在堆中,插槽(slot)为每个实例变量创建值
- 访问修饰符可以修饰实例变量
- 实例变量对于所有的方法,构造函数,块在类种可见,一般是设置private,然后用方法对外面提供可访问的接口(setter/getter方法)
- 实例变量有默认值,无须显示初始化,数字为0,对象引用为null,字符串为null,布尔为false。值一般在声明或构造函数中分配。
- 实例可以调类变量,但不推荐这么写
3. 局部变量
- 一般在方法(不管是不是静态方法),构造函数,块内声明。一旦退出则变量将销毁
- 访问修饰符private,public等不能用于局部变量
- 局部变量在堆栈级别内部实现
- 局部变量没有默认值,必须显示的初始化
- 局部变量与成员变量同名,如果在方法里,则会用局部变量。如果用成员变量,用this访问实例变量,类名访问类变量。(this不能用于static方法,因为this就是表示当前的这个对象,而普通方法就要要看哪个对象去调用它)
public class Test {
public int a1 = 1;
public static int a2 = 2;
public static void main(String[] args) {
int a3 = 3; //初始化,且不能加static,public
int a2 = 4;
System.out.println(a3); //3,局部变量
System.out.println(Test.a2); //2,类变量
System.out.println(a2); //4,局部变量
Test t = new Test();
System.out.println(t.a1); //1,实例变量
t.a1 = 2;
System.out.println(t.a1); //2,修改后的实例变量
System.out.println(new Test().a1); // 1, 另一个实例的实例变量仍然不变
t.fun(); //之前创建过的实例
new Test().fun(); //新的实例调用
}
public void fun(){
int a1 = 6;
System.out.println(this.a1); //this不能用于static方法中,2,实例变量,因为已经修改了这个对象的实例变量了
System.out.println(a1); //6,局部变量
}
}
4.从内存的角度来看:
成员变量:
系统加载类或创建该类实例,自动为成员变量分配在内存空间中,自动为成员变量指定初始值。
初始化加载类时,静态变量存在于方法区,而此时栈内存空
创建一个对象后,堆内存就分配对象,并将实例变量放在对象里,此时栈里的值指向分配在堆上的对象。
局部变量:
局部变量定义后,必须显示初始化才能使用,这表示并不是系统为这个变量分配空间。它不属于任何类或实例,它保存在其方法的栈内存中。如果局部变量是一个基本类型的变量,则直接把值保存在该变量对应的内存中,如果是一个引用类型,则存放的是地址,去引用实际的对象或数组。
看一个例子:
public class Test{
static{
int x=5;
}
static int x,y;
public static void main(String args[]){
x--;
myMethod( );
System.out.println(x+y+ ++x);
}
public static void myMethod( ){
y=x++ + ++x;
}
}
/**
output:3
*/
静态块中x为局部变量,不影响静态变量的值;x、y为静态变量,初始为0;
x++ + ++x x先是-1,自加到0然后++x变成1,得到y=0
5.总结:
一般的,JVM常用的就是堆,栈,方法区三个,局部变量保存在栈中,静态变量保存在方法区中,局部变量出了方法就被栈回收了,而静态变量不会,所以局部变量不加static关键字。因为在编译时就知道静态变量位置(如果不加final静态变量还是可以变的),而运行时才知道方法中的局部变量。
局部变量每调用一次方法都会将其值在当前线程的当前栈中临时分配,也就是说,对于程序,同一时刻,只可能只有一块栈内存被当前方法使用,用完了就回收了。所以静态变量才放到方法区中,局部变量用完了就回收了。
所以、在多线程并发访问中,访问一个值用到一个局部变量的方法是不用考虑并发安全性的,因为局部变量不可能被其他线程访问到,栈是线程私有的,而方法区,堆是要考虑线程安全的。