1、Java内存管理简介
Java内存管理分为两个方面:
(1)内存分配:创建Java对象时JVM为该对象在堆内存中所分配的内存空间。
(2)内存回收:当Java对象失去引用,变成垃圾时,JVM的垃圾回收机制自动清理该对象,并回收该对象所占用的内存。
注意:由于JVM的垃圾回收机制是由后台进程完成,本身也是非常消耗性能的,因此如果任意创建对象,让系统分配内存,那么这些分配的内存都将由垃圾回收机制进行回收,这样做有两个坏处:
(1)不断分配内存导致系统中可用内存减少,从而降低程序的运行性能。
(2)大量已分配内存的回收使得垃圾回收的负担加重,降低程序的性能。
也就是说,垃圾回收的过程也需要消耗系统资源!在程序设计的时候不能忽视这一点。
2、实例变量和类变量
Java变量可以分为成员变量和局部变量,成员变量是指在Java类体中定义的变量,局部变量则存在以下三种情况:
(1)形参:在方法签名中定义的局部变量,由方法调用者为其赋值,随着方法的结束而消亡。
(2)方法内的局部变量:在方法内定义的局部变量,必须在方法内对其进行显示初始化,这种变量从初始化完成后开始生效,随着方法的结束而消亡。
(3)代码块内的局部变量:必须在代码块中进行显示初始化,随着代码块的结束而消亡。(什么是代码块?比如if,while,for等包含在{}中的部分就是代码块)
实例变量和类变量说的都是成员变量,也就是Java类体中定义的变量。
实例变量:又称非静态变量,是指定义的时候没有使用static关键字修饰的成员变量。
类变量:又称静态变量,是指定义的时候使用了static关键字修饰的成员变量。
以上变量之间的关系可以用下图表示:
初始化时机
(1)、实例变量初始化时机:实例变量属于java对象本身,每次程序创建Java对象的时候都需要为实例变量分配内存空间。程序可以再三个地方对实例变量进行初始化:
(a)定义实例变量时指定初始值
(b)非静态代码初始化块中对指定值
(c)构造器中指定值
以上三种方式,前两种方式比第三种方式更早执行,而前两种方式的先后顺序按照它们在代码中的顺序执行。
定义实例变量时指定初始值、初始化块中指定初始值的语句地位是平等的,经过编译器处理后都将被提取到构造其中。
如下代码:
double salary = 80000;
实际上会被分成两次执行:
.1.double salary :创建Java对象时,系统根据该语句为对象分配内存。
.2.salary = 8000:这条语句将会被提取到Java类的构造器中执行。
通过javap命令可以查看代码的编译情况。
另:三种初始化方式作用完全类似,而且它们被编译器处理之后,它们对应的赋值语句都会被合并到构造其中,只是顺序有所区别:定义变量时的赋值语句和代码块中的赋值语句总是位于构造器的所有语句之前,而它们两者的先后顺序则与它们在源代码中的顺序一致。
(2)、类变量初始化时机:类变量属于Java类本身,每个JVM对每个java类只初始化一次,因此每当运行Java程序时,才会初始化java类,并且为该类的类变量分配内存空间,并执行初始化。
(a)定义类变量时指定值
(b)静态代码块中指定值
以上两种方式的执行顺序与它们在程序中的排列顺序相同。
下面的代码是个分析它们初始化时机的好例子(来自书中源码):
public class Price { final static Price INSTANCE= new Price(2.8); static double initPrice = 20; double currentPrice; public Price(double discount){ currentPrice = initPrice - discount; } } public class Test1 { public static void main(String[] args) { System.out.println(Price.INSTANCE.currentPrice); Price p = new Price(2.9); System.out.println(p.currentPrice); } }
提示:第一个输出的是-2.8,第二次输出的是17.1
需要记住的是:两个过程:先分配内存空间,然后再赋值!!!
而且:构造器只负责对Java对象的实例变量进行初始化,而实例变量的内存分配过程则是在执行构造器之前就完成了,此时的实例变量的值都是与其数据类型对应的默认的空值,对于基本类型的变量,默认空值为0或者false,对于引用类型的变量,默认值是null。
关于这个问题会在下一篇博客中写到。