疯狂java02

java内存管理—>内存分配和内存回收

JVM的垃圾回收机制由一条后台线程完成,本身也是非常消耗性能的,因此如果肆无忌惮地创建对象,让系统分配内存,那这些分配的内存都将由垃圾回收机制进行回收。这样做有两个坏处:

*不断分配内存使得系统中可用内存减少,降低程序运行性能

*垃圾回收负担加重,降低程序运行性能

2.1实例变量和类变量

局部变量:形参,方法内部定义的变量,代码块内定义的变量,存储在方法的栈内存中

成员变量:类体内定义;如果没有static修饰,又叫非静态变量或者实例变量,如果使用static修饰,则又被称为静态变量或者类变量。static不能修饰外部类,局部变量,局部内部类

定义成员变量或者类变量时必须采用合法的前向引用,但如果一个是实例变量,一个是类变量时,则实例变量总是可以引用类变量(类变量的定义可以放在实例变量的后面)

1)在同一个JVM内,每个类只对应一个Class对象,但每个类可以创建多个java对象。类变量只需要一块内存空间,但对于实例变量,该类每创建一个实例,就需要为实例变量分配一块内存空间

类也是对象,所有类都是Class的实例。程序可以通过反射来获取某个类所对应的Class实例(person.class,Class.forName("person"))

对象可以访问类变量,但底层依然会转换为通过类来访问类变量

2)实例变量的初始化时机(3个):

*定义实例变量时指定初始值

*非静态初始化块

*构造器

调用构造器来创建java对象时,非静态初始化块也将获得执行的机会,而且在构造器之前获得执行。

前两种按照在源程序的排列顺序执行,会发生覆盖现象,后执行的会覆盖先执行的。编译后,这两种都将被提取到构造器中。

3)类变量的初始化时机

*定义类变量时指定初始值

*静态初始化块中对类变量指定初始值

这两种方式的执行顺序与它们在源程序中排列顺序相同

2.2父类构造器

当创建任何java对象时,程序总会先一次调用每个父类非静态初始化块,父类构造器(总是从Object开始)执行初始化,最后才调用本类的非静态初始化块,构造器执行初始化

1)隐式调用和显式调用

*子类构造器执行体的第一行代码使用super显式调用父类构造器,系统将根据super调用里传入的实参列表来确定调用父类的哪个构造器

*子类构造器执行体的第一行代码使用this显式调用本类中重载的构造器,系统将根据this调用里传入的实参列表来确定本类的另一个构造器(执行另一个本类构造器即进入第一种情况)

*子类构造器没有显式调用super和this,系统将在执行子类构造器之前,隐式调用父类的无参数构造器。

2)访问子类对象的实例变量

代码:

class Base{

private int i=2;

public Base(){

this.display();

}

public void display(){

system.out.println(i);

}

}

class Derived extends Base{

private int i=22;

public Derived(){

i=222;

}

public void display(){

system.out.println(i);

}

}

public class Test{

main(){

new Derived();

}

}

该程序将输出0

解释:Derived对象将拥有两个i实例变量,系统为其分配两块内存。

this.属性-->当前类的属性

this.方法-->子类方法

3)调用被子类重写的方法

如果父类构造器调用了被子类重写的方法,且子类调用这个父类构造器,就会导致子类重写方法在子类构造器的所有代码之前被执行,从而导致子类的重写方法访问不到子类的实例变量值的情况

2.3父子实例的内存控制

1)继承成员变量和继承方法的区别

父类定义的成员变量,系统依然将其保留在父类中,并不会将他转移到子类中。所以父子类可以同时拥有同名的实例变量。

如果子类重写了父类的方法,就意味着子类里定义的方法彻底覆盖了父类里的同名方法。对于实例变量则不存在这样的现象。即使子类定义同名变量,这个变量不可能覆盖父类的实例变量。

所以对于一个引用类型的变量而言,如果访问实例变量,值取决于声明该变量的类型,访问方法,则取决于它实际所引用的对象的类型

2)内存中的子类实例

系统内存中并不存在父类的对象,程序内粗中只有一个子类对象,只是它还保存了所有父类所定义的全部同名的实例变量

java中允许通过return this,返回调用该方法的java对象,但是没有return super

程序不允许直接把super当成变量使用。例如super==a(一个引用变量)是不允许的。

3)父,子类的类变量 情况类似

2.4final修饰符

1)可以修饰*变量(不能重新赋值) *方法(不能被重写) *类(不能被继承)

被final修饰的实例变量必须显示指定初始值,而且只能在如下三个位置指定初始值:定义时,非静态初始化块,构造器。这三种方式最终都会被抽取到构造器中赋初始值。final类变量只能在两个地方赋初始值,定义时和静态初始化块中。

final修饰的变量将成为宏变量(定义时赋初始值),所有出现该变量的地方,系统将直接把它当成对应的值处理。

2)执行宏替换的变量

final修饰符的一个重要用途就是定义宏变量,定义final时就赋一个明确的值,那就成为宏变量,编译器会把程序中所有用到该变量的地方直接替换成该变量的值。如果赋值为表达式,只要是基本的算术运算表达式或者字符串连接运算,没有访问变量,调用方法,同样当成宏变量。

String str="疯狂java"+99;

String str2="疯狂java"+String.valueOf(99);

syso(str=str2);

3)final方法不能被重写

*父子类不在一个包下,父类方法使用默认及以下,子类不能重写父类的这个方法。

4)内部类中的局部变量

匿名内部类中访问和使用局部变量,那么这个局部变量必须使用final修饰符修饰。(隐式闭包)

内部类会扩大局部变量作用域。



猜你喜欢

转载自blog.csdn.net/little_____white/article/details/80949103