上次我们讲到创建对象。这次我们深入来研究下对象在内存中的关系。这些对象在内存中的究竟发生哪些操作了?
我们先来看下上次的这个代码。
Public class Puppy{
public Puppy(String name){
System.out.println("它的名字是:"+name);
}
public static void main(String[] args){
//创建一个Puppy对象。
Puppy myPuppy = new Puppy("tommy");
}
}
重点:Puppy myPuppy = new Puppy(“tommy”);
在解释之前,我们先补充下。因为我们介绍的类和对象是引用数据类型。
而引用数据类型必然存在栈内存-----堆内存的引用关系。
见代码哦亲:
代码:
Puppy myPuppy //实例化myPuppy,在栈内存中创建myPuppy对象 new Puppy("tommy"); //初始化Puppy,把堆内存中的name赋值为tommy
一图值千金,下面我们来看张图吧。
![图片描述][1]
首先初始化Puppy,在堆内存中把name赋值为tommy.
在实例化myPuppy,在栈内存中创建myPuppy对象,并指向堆内存。
我们在来看个代码:
class Person{ //定义个Person类。(人.)
String name; //定义name属性。
int age; //定义age属性。
public void tell(){ //定义tell()行为。方法。
System.out.println("姓名:"+name+"年龄:"+age);
}
};
public class Demo{
public static void main(String args[]){
Person per=new Person(); //创建并实例化对象per.
}
}
其中Person的类图是这样的。
![图片描述][2]
这里来介绍下类图的概念:
类图包含3个组成部分。第1个是Java中定义的类名。第2个是属性。第3个是该类提供的方法。(用UML图来表示的话,第1层为类名,第2层为类的属性,第3层为类的方法。属性和方法之前可以添加一个修饰符。"+"号表示具有公共可见性 "-"号:私有可见性。"#"号:受保护的可见性 "_"号:静态的)
看了上面的类图概念是不是有晕了?码段代码来清醒下。
public class Employee{ //定义雇员类。
private int empID; //雇员ID.
public double calcSalary(){ //calcSalary()方法。(薪水)
}
}
类图
![图片描述][3]
第1层:为java中的Employee类名。
第2层:为Employee类中empID属性,该属性为公共。且是int型的。
第3层:为Employee类中calcSelary();行为,该行为为公共。且是double型
其实上面的代码
Person per =new Person();
还可以这样划分。Person per=null; //声明对象。(赋初为空null) per=new Person(); //实例化对象。(指向堆内存。)
为了理解,我们还是在码几个程序。
class Person{ String name; int age; public void tell(){ System.out.println("姓名:"+name+"年龄:"+age); } }
public class Demo01{
public static void main(String args[]){
Person per1=null; //声明对象(赋初值。)
Person per2=null; //声明对象(赋初值。)
per1=new Person(); //实例化对象per1(指向对内存Person)
per2=new Person(); //实例化对象per2.
per1.name="张三"; //改变per1指向的堆内存中name的值
per1.age=30; //改变per1指向的堆内存中age的值.
per2.name="李四"; //改变per2指向的堆内存中name的值
per2.age="33"; //改变per2指向的堆内存中age的值
System.out.println("per1对象中的内容为:");
per1.tell();
System.out.println("per2对象中的内存为:");
per2.tell();
}
}
>代码**运行效果**:
![图片描述][4]
代码在**内存中表现**:
![图片描述][5]
**代码逻辑:**这个per1=new Person();和per2=new Person();是分别指向不同的堆内存空间。然后分别更改per1和per2里指向的堆内存空间里的name和age的值,在输出。
> **所谓的引用数据类型,实际上传递的就是堆内存的使用权,可以同时为一个堆内存空间定义多个栈内存的引用操作。**
来看段代码吧,亲。。
class Person{ //定义Person类。
String name; //定义名字属性。
int age; //定义年龄属性。
public void tell(){ //定义tell行为。
System.out.println("姓名为: "+name+"年龄为: "+age);
}
};
public class Demo02{
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.name="李四"; //设置per2中name属性内容。
per2.age=33; //设置per2中age属性内容。
System.out.println("per1中对象的内容为:");
per1.tell(); //通过per1对象调用tell()方法。
System.out.println("per2中对象的内容为:");
per2.tell(); //通过per2对象调用tell()方法。
}
}
> **代码运行效果:**
![图片描述][6]
**代码逻辑:**首先声明per1和per对象并赋初值,实例化per1对象,并把per1的堆内存空间所有权分配给per2。也就是说per1和per2指向同一个堆内存空间。首先通过per1变量,更改name="张三",age=30。在通过per2变量,更改为name="李四",age=33。**(覆盖原来的值)**在输出。。。
这段代码在内存中表现形式为:
![图片描述][7]
![图片描述][8]
因为per1和per2都是指向同一个堆内存空间。通过per2来改变name及age的属性值。就等同于per1来改变的。
*我们在来看一道引用传递的例子。*
class Person{ //定义个Person类.(人.)
String name; //定义name属性。
int age; //定义age属性。
public void tell(){
System.out.println("姓名:"+name+"年龄:"+age);
}
}
public class Demo03{
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=per1; //per2指向per1的堆内存空间。之前per2所指向的堆内存空间为垃圾。
System.out.println("per1对象的内容:");
per1.tell();
System.out.println("per2对象的内容:");
per2.tell();
}
}
代码逻辑:我们首先声明per1和per2对象,并实例化per1和per2。指向对应的堆内存空间。
![图片描述][9]
然后我们给per1所指向的堆内存空间中name赋值为”张三”,age为30。
给per2所指向的堆内存空间中name赋值为”李四”,age为33。
![图片描述][10]
但是后来per2有重新指向per1所指向的堆内存空间。(之前per2所指向的堆内存空间被jvm视为垃圾空间。)
![图片描述][11]
在执行“System.out.println(“”).....per1.tell();”
”System.out.println(“”)........per2.tell();”
分别调用tell();方法。输出内容。
代码运行效果:
![图片描述][12]
上面的代码因为per2改变了指向,所以其原本的内存空间就没有任何栈的引用。则这样的内存就被称为垃圾空间,等待着垃圾收集机制GC的回收。
我们了解这块内容java中内存是如何划分的,以后不管多复杂的程序都可以按照这个内存划分图来分析滴,亲。
**亲,这次我们就聊的这了,我们下期再见了。荆轲刺秦王。**
> 感谢老铁们再次把宝贵的时间浪费在我的文章上。哀心的感谢各位老铁们。谢谢。。
这里我推荐一个在线UML制图工具。[ProcessOn][13]挺好用的。老铁们有兴趣的可以去逛逛。
[1]: http://img.mukewang.com/59f212e000014b1705960318.png
[2]: http://img.mukewang.com/59f217b50001234602030155.png
[3]: http://img.mukewang.com/59f2c7900001e39e03010126.png
[4]: http://img.mukewang.com/59f34d720001e00404030150.png
[5]: http://img.mukewang.com/59f34da9000119ab07420323.png
[6]: http://img.mukewang.com/59f359e60001a59202780170.png
[7]: http://img.mukewang.com/59f35fbe00015dfb06150194.png
[8]: http://img.mukewang.com/59f35fed0001a9bf06330185.png
[9]: http://img.mukewang.com/59f367650001d04c04910231.png
[10]: http://img.mukewang.com/59f3680e00012dcb04520145.png
[11]: http://img.mukewang.com/59f3692800018c9604320163.png
[12]: http://img.mukewang.com/59f369c70001e05402970165.png
[13]: https://www.processon.com/