概述
Java对象创建分为两个过程:声明对象和创建对象实体。类信息,对象引用,对象实体均在内存的不同区域。
内存结构
每一个Java引用程序均会唯一的对应一个JVM实例,而这个JVM实例将会完成对象内存分配,程序运行,垃圾回收等工作。JVM将其内存大致分类三个区域:方法区,栈区,堆区。
- 方法区:是JVM在装载类文件时,用于存储类的所有描述信息的,这些信息主要包括,类的基本信息(访问修饰符,类名),字段信息(修饰符,类型,字段名),方法信息(修饰符,返回值类型,方法名,参数列表类型,异常,方法体字节码等等),常量池,静态区,classloader以及class引用。
- 栈区:存放局部变量,基本数据类型和对象引用类型
- 堆区:用于存放new产生对象的实体,每一个对象实体均会有自己的内存地址,一旦这个内存地址不被任何栈区对象引用,就会成为垃圾,随时会被GC回收。
成员变量和局部变量
- 成员变量:在类中方法外,存储在堆内存中,随着对象的存在而存在,随着对象的消失而消失。有默认的初始化值。
- 局部变量:在方法体中,存储在栈内存中,随着方法的调用而存在,随着方法的调用完毕而消失。没有默认的初始化值,所以必须先定义,赋值后才可以被使用。
对象创建过程
package combendy;
public class Demo_04 {
static{
System.out.println("Hello");
}
public static void main(String[] args) {
Inner inner1 = new Inner();
Inner inner2 = new Inner(30);
System.out.println((inner1.ii==inner2.ii)+" "+Inner.ii);
System.out.println(inner1.str==inner2.str);
}
static class Inner{
private String str="hello"; //成员变量 存储在堆内存中
private int i;
private static int ii=10; //静态变量,存储在方法区中(这也是为什么静态变量能在多个对象共享的原因)
private static int iii;
// 静态代码块
static{
System.out.println("ii="+ii+",iii="+iii);
}
//
{
System.out.println("str="+str+",i="+i+",ii="+ii+",iii="+iii);
}
public Inner(){
ii=20;
System.out.println("non-arguments");
}
public Inner(int i){
this.i=i;
System.out.println("one-arguments");
}
}
}
运行结果
Hello
ii=10,iii=0
str=hello,i=0,ii=10,iii=0
non-arguments
str=hello,i=0,ii=20,iii=0
one-arguments
true 20
true
步骤
- 将JVM加载进内存中
- JVM首先检查public类是否存在,存在就会将他加载进内存方法去中,否者抛出异常,结束进程
- 对类中静态字段进行初始化(若无显示初始化,就会被赋值为默认值),接着执行静态代码块
- 在栈中创建局部变量,堆中创建对象实体,为堆对象实体的成员变量(非静态)初始化,对于静态字段只需要从方法区的静态区中引用其值即可,执行构造代码块,接着执行构造函数(这里调用顺序会一直上溯到Object类,每一个构造方法的第一行为super()语句),最后将对象实体堆地址赋值给栈中引用变量。
要注意的是,实例字段包括自身定义的和从父类继承下来的(即使父类的实例字段被子类覆盖或者被private修饰,都照样为其分配内存)
C++中的引用和JAVA的引用作对比,其实他们两个只是“名称”一样,本质并没什么关系,C++中的引用只是给现存变量起了一个别名(引用变量只是一个符号引用而已,编译器并不会给引用分配新的内存),而JAVA中的引用变量却是真真正正的变量,具有自己的内存空间,只是不同的引用变量可以“指向”同一个对象而已。因此,如果要拿C++和JAVA引用对象的方式相对比,C++中的指针倒和JAVA中的引用如出一辙,毕竟,JAVA中的引用其实就是对指针的封装