java循环 修改成员变量的值,java中成员变量与局部变量存在时间的有关问题

java中成员变量与局部变量存在时间的问题

先看一下代码

class Test{

int num;

public void function(){

System.out.println("执行方法");

for(int i=0; i<3; i++)

System.out.println(i);

}

public static void main(String[] args){

Test t1;

Test t2 = new Test();

t2.function();

System.out.println("结束");

}

}

我的问题,在网上查找成员变量与局部变量的区别,都写了存在时间不同,但是都没有细讲,所以我想问一下

成员变量num什么时候在内存中被存储,是主函数运行的时候内存中就有num了,还是Test t1;的时候有,又或者是在Test t2 = new Test();实例化之后才有的num?又是什么时候释放空间被回收的呢,是在t2.function();这条语句结束之后,还是在整个java程序结束之后?

局部变量i又是什么时候被存储的呢,是在function方法被调用的时候存储的还是在function方法被调用执行到for语句的时候才有的?释放时间呢,for语句结束就释放掉了?还是t2调用方法结束之后?

------解决思路----------------------

1.num这个变量一直都没有用过,相当于不存在一样,如果放在main方法中,虽然没有使用,但会存储一段时间,知道main方法结束.简单来说,你所谓定义的num变量.在main方法中根本不能使用,除非你定义了静态变量,加上static,这个时候,num变量会一直存在,程序启动的时候就会存储,至于释放,需要等java的垃圾回收机制来决定.另一种方法就是在main方法中定义num,也是全局变量

2.在for循环的时候,i会被存储,执行完循环后,i就释放了

public class Test {

// 如果在main方法外定义全局变量,需要加上static

public void function(int i) {

System.out.println("执行方法");

while (i > 5) {

System.out.println(i);

break;

}

}

// main方法执行完,所有变量内存消失,如果main方法外部定义变量,会一直存在,知道垃圾回收机制执行

// i随着function方法,num随着main方法结束

// 局部变量存在的时间

public static void main(String[] args) {

// 全局变量

int num = 6;

// 这个是消耗内存的垃圾,没有用

Test t1;

// t2对象占一部分内存

Test t2 = new Test();

// 调用方法,i开始占用内存

t2.function(num);

// 方法调用完,执行

System.out.println("结束");

}

}

不谢,

07535817.gif

------解决思路----------------------

成员变量,里边也要区分静态变量和实例变量,静态变量是随类加载而加载,而实例变量是随对象而加载,准确的说,是稍微先于对象,但是是依附于对象的,存在于堆内存,当调用其对象中方法时,局部变量才开始加载,用完方法之后,局部变量被清空

------解决思路----------------------

当new Test()的时候, num才产生,应该是这样。静态的成员变量就是类的加载而产生

------解决思路----------------------

程序都是从main函数入口进来。这点是毋庸置疑的。然后依次执行每条语句

第一句Test t1;这是一个类类型的变量。

内存划分为五大块,我们要理解的是栈内存和堆内存这两个区域,栈内存用于存放局部变量;堆内存用于存放数组和对象实体,凡是用new建立的都是在堆中,内存存储是通过地址的方式来存储的。

这里Test t1;因为是定义在方法内的,所以是局部变量,那么首先就在栈内存中存在一个t1变量,类型是类。但并没有初始化赋值,所以后面根本无法使用这个t1。因为局部变量必须要初始化,虚拟机不会去给默认赋值。(成员变量虚拟机会先赋个默认值)。所以此时 int num不会进堆内存。

接下来执行第二句Test t2 = new Test(); 同样也是在栈内存中再存在一个叫t2的类类型变量,右边 new Test()就会在堆内存中新生成一个对象/实例。此时的int num就得进堆内存了,因为int num是定义在类里面的,叫做成员变量,成员变量会随着对象的创建而创建。你这现在new了一个 Test() 也就是新创建了一个对象,这个对象里面有个num的属性(为什么叫属性不叫变量,下面再讲)int类型的默认值是0 那么此时的堆内存中就会先存在一个 num = 0  这个值会有一个内存地址,然后这个地址指向了 t2 。

如果此时Test t2 = new Test();后面再加一句 t2.num = 2;那么又会对堆内存中对象的值 num = 0 进行重新赋值覆盖 num = 2了。如果不信,可以直接打印System.out.print(t2.num) 结果会是0

但是如果直接 打印System.out.print(t1);会报错,未初始化变量,因为没有new 生成对象,所以只能当成是类类型的局部变量来处理,局部变量未初始化就不能使用。

从实际理解的角度讲,我们定义成员变量,其实更确切的说应该是在定义一种属性 这里定义的 int  num

比如我们定义一个Student类,类里面定义一个成员变量 int  num 来表示学生的属性(比如说用num代表学号),意思是告诉这个类的使用者,这个Student类中有这么个表示学号的属性,等你创建对象/实例化的时候可以直接调用并赋值。

打个比方,我先打印一张空白的表格放在那,默认每一个行都是空的,开学后来一个新学生,我就填写一行(一般来说定义一个成员变量/属性都是有目的的,不然定义了就没有意义了)

然后第三句 t2.function(); 开始执行对象 t2 的方法,也就是开始调用public void function()这段代码

方法体内:先打印 执行方法 四个字,然后执行for循环 这也没有疑问

当执行到for循环的时候,定义一个int i = 0  这就又开始开辟内存空间

int i = 0 ;左边是int i   因为是局部变量所以我们在栈内存中会有一个变量 i 因为是 int 类型的,所以我们在堆内存中虚拟机会先有一个 默认初始化的赋值,整数的默认值是0,(小数的默认值是0.0或者0.0f bollean的默认值是false),

然后我们再执行右边的 = 0的赋值 将默认值改成0(虽然这里从默认值的0变成赋值的0没变化,如果int i = 1,那么实际内存中的操作是先赋个默认值,然后如果有实际赋值,再替换掉这个默认值从0变成1,不是直接变成1的)

这里就开始循环打印 i 的值。0 1 2

当i++ 的值变成3的时候不满足 i<3 的条件,循环结束。

这里要注意的是 这个 int i  他是定义在for循环内的。当for循环一结束,这个 i 就立刻释放掉了,没有用了啊,

不信你可以在for循环的下面输出这个 i 试试 System.out.println(i);

所以 int i 什么时候消失的,就是在for循环结束后消失的

但是如果 int i ;for(i = 0 ; i<3 ; i++)定义在外面,for循环内只是给 i 赋值。那就不会释放了

实际上这里的执行for循环是因为 t2.function(); t2调用才会执行到的,所以for循环执行完也就是t2.function();这条语句执行完了,

接下来执行主函数里的第四行语句 打印 结束 两个字

总结一下进内存的时间顺序:

1.t1 进栈内存

2.t2 进栈内存,

3.因为t2 右边是new了个对象 所以此时 num进堆内存 初始化赋值默认为0

4.t2.function(); 调用for循环

5.for循环内定义并赋值了 i 所以此时 i 进栈内存

6.i的初始化值进堆内存,然后开始循环 不断的修改 i 在堆内存中的值

7.循环结束 打印0 1 2,然后释放栈内存中的 i

8.打印 结束 两字

9.main函数结束,所以此时局部变量 t1、t2在栈内存中释放

10.然后就是Test类结束,java程序结束

注意的是num没有进过栈内存中,只是在new的时候进入堆内存

如果后面调用 t2.num = 2 ;这个时候才会有num进栈内存

堆内存中的对象(比如这里的new创建的Test对象 默认num初始化值0 num = 0)会通过java垃圾回收机制自动清理,不会释放。

内存中引用其实是地址的引用,比如 Student p = new Student() ; 在内存中是在栈内存中有一个p学生,在堆中默认 num 学号是 0;这时候会有一堆个内存地址比如0x345 是这个地址指向了p  并不是一个赋值。比如说北京西路200号是一家卖鱼的,生意不好然后倒闭了,又开了一家卖肉的,我们不管他做什么的,那么北京西路200号这个地址是铁打的事实吧,不会变的,你卖鱼,我按这个地址过来吃鱼,你卖肉我还是按这个地址我过来吃肉。

其实这里new 创建在堆内存中的对象 num = 0 默认初始化并没有释放,还一直在堆中,也就是所谓的垃圾。但是java语言有一个垃圾回收机制,会自动的不定期的自己清理垃圾。这个暂时不用我们插手去手动清理。以往的C语言就没有这种垃圾清理机制,必须要程序员手动去清理才行,不然垃圾就会越来越多。这也是java强大的一个地方。

其次成员变量在定义为static静态的时候才会随着类的加载而先加载

这点你可以这么去证实

public class A{

public static void main(String[] args) {

System.out.println("1");

}

static{

System.out.println("2");

}

}

这里的输出结果就是  2 1

static修饰的会先执行。

如果你这里修饰为 static int num;

那么类只要一加载,就先把num进入栈内存,然后再堆内存中初始化默认值 num = 0 ;

然后再执行main函数

static为静态的意思,也叫公共的

举个例子:

老同学聚餐一共有10个人,决定去聚餐吃饭。那么是不是得先有个聚餐地点address吧,那么此时的这个address就可以被定义成 static

第一个原因是因为这address是我们10个人都需要提前知道的吧。第二个原因是我们不确定哪个人会先到(先调用address),所以要把地址放到一个公共的地方。没见过哪次聚餐,10个人,张三说我准备好了要去了,然后再急急忙忙的去选地址(定义address),然后李四说他也准备好了,再去调用这个address,多麻烦啊。

所以说只要确定了聚餐,(这个类一创建)就立马先公告一个地址出来(static address)然后再去执行其他的操作,连main函数都得靠后。

这个static缺点就是存在的时间太长,一直会加载到类结束才会结束。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值