本章目标
- 掌握Java中的内存划分
- 初窥Java引用传递
- 垃圾的产生分析
内存划分:对象创建之初
public class ClassDemo02{
public static void main(String args[]){
Person per = new Person() ; // 创建并实例化对象
}
}
Person per = new Person();
- 声明对象:Person per, 栈内存中声明,与数组一样,数组名称就保存在栈内存中,只开辟了 栈内存的对象是无法使用的,必须有其堆内存的引用才可以使用。
-
实例化对象:new Person(),在堆中开辟空间,所有的内容都是默认值。
- String:是一个字符串,本身是一个类,就是一个引用数据类型,则此时默认值就是null。
- int:是一个整数,本身是一个数,所以是基本数据类型,则此时的默认值就是0。
如果进一步划分以上的代码,则可以写成如下的形式:
Person per = null ; // 声明对象
per = new Person() ; // 实例化对象
开发中,数据最好赋一个初值,这样操作会比较方便。
了解了其分配关系,就可以为属性赋值,并调用类中的方法。
使用格式:
- 调用属性:对象.属性
- 调用方法:对象.方法()
class Person{
String name ; // 声明姓名属性
int age ; // 声明年龄属性
public void tell(){ // 取得信息
System.out.println("姓名:" + name + ",年龄:" + age) ;
}
};
public class ClassDemo03{
public static void main(String args[]){
Person per = null ; // 声明对象
per = new Person() ; // 实例化对象
per.name = "张三" ; // 为姓名赋值
per.age = 30 ; // 为年龄赋值
per.tell() ; // 调用方法,打印信息
}
};
输出结果:
姓名:张三,年龄:30
此时,内容显示出来后,实际上所有的操作都是要在内存中直接体现出来的。
问题:
对象是保存在栈内存之中的,属性是保存在堆内存之中的,那么方法呢?
在程序中,所有方法是保存在全局代码区之中的。此区域中的内容是共享的。
注意:
在使用对象的时候,对象必须被实例化之后才可以使用(实例化对象,并不单单指的是通过new关键字实现的,只要有堆内存的空间指向,则就表示实例化成功。)
如果不实例化会如何呢?
Exception in thread "main" java.lang.NullPointerException
at ClassDemo03 main(ClassDemo03.java:12)
此时出现了一个空指向异常。
在引用操作中,如果一个对象没有堆内存的引用,而调用了类中的属性或方法,就会出现此问题。
那么既然可以产生一个对象,能不能产生多个对象呢?
可以,只要按照操作格式完成即可。
class Person{
String name ; // 声明姓名属性
int age ; // 声明年龄属性
public void tell(){ // 取得信息
System.out.println("姓名:" + name + ",年龄:" + age) ;
}
};
public class ClassDemo04{
public static void main(String args[]){
Person per1 = null ; // 声明per1对象
Person per2 = null ; // 声明per2对象
per1 = new Person() ; // 实例化per1对象
per2 = new Person() ; // 实例化per2对象
per1.name = "张三" ; // 设置per1中的name属性内容
per1.age = 30 ; // 设置per1中的age属性内容
per2.name = "李四" ; // 设置per2中的name属性内容
per2.age = 33 ; // 设置per2中的age属性内容
System.out.print("per1对象中的内容 --> ") ;
per1.tell() ; // 调用类中的方法
System.out.print("per2对象中的内容 --> ") ;
per2.tell() ; // 调用类中的方法
}
};
输出结果:
per1对象中的内容 --> 姓名:张三,年龄:30
per2对象中的内容 --> 姓名:李四,年龄:33
类是引用数据类型,那么这些是如何引用的呢?
所谓的引用数据类型,实际上就是传递的就是是堆内存的使用权,可以同时为一个堆内存空间定义多个栈内存的引用操作 。
class Person{
String name ; // 声明姓名属性
int age ; // 声明年龄属性
public void tell(){ // 取得信息
System.out.println("姓名:" + name + ",年龄:" + age) ;
}
};
public class ClassDemo05{
public static void main(String args[]){
Person per1 = null ; // 声明per1对象
Person per2 = null ; // 声明per2对象
per1 = new Person() ; // 只实例化per1一个对象
per2 = per1 ; // 把per1的堆内存空间使用权给per2
per1.name = "张三" ; // 设置per1中的name属性内容
per1.age = 30 ; // 设置per1中的age属性内容
// 设置per2对象的内容,实际上就是设置per1对象的内容
per2.age = 33 ; // 设置per2中的age属性内容
System.out.print("per1对象中的内容 --> ") ;
per1.tell() ; // 调用类中的方法
System.out.print("per2对象中的内容 --> ") ;
per2.tell() ; // 调用类中的方法
}
};
输出结果:
per1对象中的内容 --> 姓名:张三,年龄:33
per2对象中的内容 --> 姓名:张三,年龄:33
再看一道引用传递的程序。
class Person{
String name ; // 声明姓名属性
int age ; // 声明年龄属性
public void tell(){ // 取得信息
System.out.println("姓名:" + name + ",年龄:" + age) ;
}
};
public class ClassDemo06{
public static void main(String args[]){
Person per1 = null ; // 声明per1对象
Person per2 = null ; // 声明per2对象
per1 = new Person() ; // 实例化per1对象
per2 = new Person() ; // 实例化per2对象
per1.name = "张三" ; // 设置per1中的name属性内容
per1.age = 30 ; // 设置per1中的age属性内容
per2.name = "李四" ;
per2.age = 33 ; // 设置per2中的age属性内容
per2 = per1 ; // 把per1的堆内存空间使用权给per2
System.out.print("per1对象中的内容 --> ") ;
per1.tell() ; // 调用类中的方法
System.out.print("per2对象中的内容 --> ") ;
per2.tell() ; // 调用类中的方法
}
};
输出结果:
per1对象中的内容 --> 姓名:张三,年龄:30
per2对象中的内容 --> 姓名:张三,年龄:30
因为per2改变了指向,所以其原本的内存空间就没有任何栈的引用,则这样的内存就被称为垃圾,等待着垃圾收集机制进行回收。
GC垃圾收集机制的简称。
总结
1、掌握栈与堆内存的关系
2、对象保存在栈内存之中,而具体内容保存在堆内存之中
3、对象间的引用传递,实际上传递的就是堆内存空间的使用权
4、垃圾的产生。