Java基础6--面向对象--程序运行内存图解

来源:互联网 发布:淘宝举报卖家售假 编辑:程序博客网 时间:2024/05/19 15:42

6-1,二维数组的定义方式

1,定义:int[][] arr = new int[3][2];

创建一个二维数组,这个其实是有三个一维数组,一维数组中的每个元素又引用了一个一维数组。

2,打印而为数组:

Sop(arr);直接打印二维数组。 打印[[I@C17164,[[是二维数组的意思,I是int的意思。

Sop(arr[0]);直接打印二维数组中角标为0的一维数组。打印[I@b8e622,[是一维数组的意思。

Sop(arr[0][0]);直接打印二维数组中脚标为0的一位数组的角标为0的元素。打印0。

3,int[][] arr = new int[3][2];

    arr[1][1]= 88;

的内存图解:


步骤:

(1)从main函数进入,读到arr局部变量,把arr入栈。

(2)因为arr是数组类型的,所以在堆内存中创建实体,创建一个一维数组,附地址0X0045,并将三个元素初始化为null,因为每个元算又引用到一个一位数组。

(3)堆中的每个元素又是一个一维数组,在堆中新创建三个一维数组,并分配地址值,初始化为0,因为是int型。再把int型数组的地址值赋给之前创建的相应的一位数组的元素。

(4)将数组的引用地址赋值给栈空间的arr,arr指向了0X0045内存地址。

(5)第一句话读完,读下面的一句,给角标为[1][1]的元素赋值为88,这时根据arr的引用地址可以找到0X0045的数组位置,根据第一个[1]可以找到这个数组角标1指向的地址,找到0X0034指向的数组,再根据第二个[1]找到角标为1的元素,这时这个元素是0,然后直接赋值为88,执行完毕。

 

6-2,创建子数组长度不等的二维数组

方式:

int[][] arr = new int[3][];//分别对二维数组中的每一个小数组进行初始化arr[0] = new int[2];arr[1] = new int[1];arr[2] = new int[3];

步骤:

(1)从main开始程序,读到int数组型局部变量arr,将arr进栈操作。

(2)arr是一个数组,在堆内存中创建一个一位数组,分配内存地址。

(3)arr有三个元素,创建一个大小为3的一维数组,其中每一个元素又指向了另一个一维数组,所以赋给初始值null。但是因为创建二维数组时是以new int[3][]这种方式创建的,也就是第二个没值,所以这时不能创建那三个一维数组,此时内存中只有最上面的长度为三的一位数组。

(4)将数组的地址赋给栈内存中的arr,arr引用指向堆内存中的数组的地址。

(5)读到第二句,创建了一个一维数组,这时才在内存中创建地址为0X0034的数组,大小为2,初始化元素值为0,并将这个地址赋给原来数组中的[0]号元素。

(6)读到第三句,创建一个长度为1的一维数组,地址为0X0021,赋给arr[1]。

(7)读到第四句,创建一个长度为3的一维数组,地址为0X0078,赋给arr[2]。 结束。

 

另外:

int[][] arr = new int[3][];

Sop(arr); //[[I@c17164

Sop(arr[0]); //null

Sop(arr[0][0]); //错误,抛出异常NullPointerException空指针异常

因为这种定义方式当前没有定义arr[0][0]元素,只在堆中创建了一个长度为3的一维数组,三个小数组并没有开辟内存空间,也没有地址值,所以无法访问。

 

6-3,二维数组的另一种定义方式

在明确二维数组中的元素时可以这么定义:

int[][] arr = {{1,2,3},{4,5},{6,7,8,9}};

遍历上述数组:

for(int x=0;x<arr.length;x++) {for(int y=0;y<arr[x].length;y++) {System.out.println(arr[x][y]+",");}}

2,打印长度:

Sop(arr.length);//打印二维数组的长度,其实就是打印一维数组的个数。

Sop(arr[0].length);//打印二维数组中角标为0的数组的长度。

 

6-4,面向对象

1,面向过程强调的是过程,如C语言。

面向对象强调的是对象,如C++、Java、C#。

2,面向对象的特点:

(1)面向对象是一种常见的思想,符合人们的思考习惯。

(2)面向对象的出现,将复杂的问题简单化。

(3)面向对象的出现,让曾经在过程中的执行者,编程了对象中的指挥者。

3,如何理解面向对象?

在面试中可以这么说,这也有助于理解。

首先先说出上面的三个特点。然后再说下面。

其实面试官您就是按照面向对象的方式做事情,凭您的能力,您完全可以设计并开发项目的任务,达成目标,但这样是您一个人做,既费时又费力,这时您就需要找一些具备编程知识的人来帮您完成目标,您来指挥我们做事情,因为我们具备专业开发这种功能,所以能够帮您完成项目,达成目标,而我就是满足这一条件的人,您只需要通知我做什么事情即可,我会给您一个满意的答复,但至于我是如何完成这件事情的,您就不用关心了。

4,如果Java中已经有了这个对象,就直接用,如果没有,就创建对象。

5,面向对象的三个特征:封装、继承、多态。

 

6-5,类与对象之间的关系

用Java语言对现实生活中的事务进行描述,通过类的形式来表现,怎么描述呢?

对于事物描述通常只关注两个方面,一个是属性,一个是行为。

只要明确事物的属性和行为并定义在类中即可。

对象:其实就是该类事物实实在在存在的客体。

 

类与对象之间的关系:

类:事物的描述。

对象:该类事物的实例。在Java中是通过new创建的。

 

6-6,类与对象的体现

描述一个Car并运行。

//描述Car,通过类的形式class Car{//定义车轮的数量,是Car的属性int num;//定义车子的颜色,是Car的属性String color;//车子可以跑起来,是Car的行为void run() {System.out.println(num+"::"+color);}}class CarDemo{public static void main(String[] args) {//在计算机中创建一个Car的实例,通过new关键字//c就是一个类类型的引用变量,指向了该类的对象。Car c = new Car();//为这个Car实例添加属性,使它成为一个有四个轮子的红色车c.num = 4;c.color = "red";//让这个Car在公路上行驶c.run();}}

细节:

(1)定义类其实就是定义类中的成员。

成员:成员变量==属性,成员函数==行为。

(2)Car类中不需要定义主函数,因为Car不需要独立运行,只有被用到的时候才运行。

 

6-7,对象的内存体现:

1,首先明确,通过new建立的都在堆内存当中。

2,上例中Car类的内存图示如下:

Car c = new Car();

c.num = 4;

c.color = "red";

c.run();

步骤:

(1)从入口main开始执行程序,把局部变量c加载到栈内存。

(2)new Car();在堆内存中开辟了一片空间,并分配地址,将Car的非static的属性也加在到这片堆内存空间中,并默认初始化属性值,int型初始化为0,String初始化为null。

(3)c = new Car();将地址0X0078赋给变量c,c指向堆中的空间。

(4)c.num = 4,c.color = “red”;用c指向堆中的地址,找到这个变量,并赋值。

(5)最后一局c.run();运行run函数。

 

6-8,成员变量和局部变量的区别

1,区别:

(1)成员变量定义在类中,整个类都可以访问。

    局部变量定义在函数里、语句、局部代码块中,只在所属区域有效。

(2)成员变量存在于堆内存的对象中。

    局部变量存在于栈内存的方法中。

(3)成员变量随着对象的创建而存在,随着对象的消失而消失。

    局部变量随着所属区的执行而存在,随着所属区域的结束而释放。

(4)成员变量都有默认的初始值。

    局部变量没有默认的初始值。

2,上面的例子中,c.num = 4;不是给classCar中的num赋值,而是给对象中c中的num赋值。

 

6-9,成员变量和局部变量的同名&显示初始化

class Car{int num = 4;//在堆内存中显示初始化String color;void run() {int num = 10;System.out.println(num + "::" + color);}}

在run方法中有一个与成员变量同名的num。Car的run方法运行的时候,要加载到栈内存里,而且run中的num也加在到栈里的run方法中,run执行的时候,发现栈里有这个变量,就不再去堆内存找了,所以运行出来是10。

 

6-10,类类型参数

class Demo{public static void main(String[] args) {Car c1 = new Car();Car c2 = new Car();show(c1);show(c2);}//类类型的变量一定指向对象,要么就是nullpublic static void show(Car c) {c.num = 3;c.color = "black";System.out.println(num+"::"+color);}}

6-11,匿名对象

1,匿名对象:没有名字的对象。

2,用法:

(1)当对象方法仅进行一次调用的时候,就可以简化成匿名对象。

(2)匿名对象可以作为实际参数进行传递。

例如:

Car car = new Car();

car.run();

上面的两句可以使用new Car().run();代替。

 

Car car = new Car();

car.run();

car.run();

上面的三句就不可以使用:

new Car().run();

new Car().run();

代替,因为如果这么写,是两个不同的对象,每new一次就在内存中创建一个对象。

 

3,

new Car().num = 5;

new Car().color = "green";

new Car().run();

的内存图示:

由于没有局部变量,所以没有栈内存。


步骤:

(1)第一句new Car().num = 5;执行,创建了图中的第一个对象,赋值5以后,在栈中并没有任何的变量接收这个地址值,所以第一句结束时候,这个对象就变成垃圾了。

(2)第二个与第一个相同。

(3)所以,如果打印num和color,是0和null。

 

 

6-12,基本数据类型参数传递图解

class Demo{public static void main(String[] args) {int x = 3;show(x);System.out.println("x=" + x);}public static void show(int x) {x = 4;}}

本例没有实体,不涉及到堆内存。



步骤:

(1)从main函数开始,main函数进栈,局部变量x=3进栈。

(2)调用show方法,show方法进栈。

(3)将main中的x作为参数传递给show函数,这时show函数里也有一个x变量,且等于3。

(4)执行show中的x=4操作,show中的x变为4,main中的x不变,如图。

(5)show方法执行结束,show弹栈。回到main中,main中的x依然是3,所以最后打印x=3。

 

6-13,引用数据类型参数传递图解

class Demo{int x = 3;public static void main(String[] args) {Demo d = new Demo();d.x = 9;show(d);System.out.println(d.x);}public static void show(Demo d) {d.x = 4;}}

图解:


(1)从main函数开始,main进栈,有一个局部变量d,d也进栈,并在main中。

(2)new Demo();开辟了对内存空间,分配地址,并把成员变量默认初始化为0,类中定义x=3,就显示赋值为3,创建完毕,将地址0X0045赋值给d。

(3)读到d.x = 9;根据d的引用找到堆中的对象的x,并赋值为9。

(4)调用show方法,show中有一个局部变量Demo类型的d,这个局部变量也进入到栈的show方法中。

(5)将main中的d的引用地址值赋值给show中的d,此时可见,show中的d也指向了堆中的同一个对象,因为地址是相同的。

(6)在show方法中,调用d.x = 4;根据这个d的地址找到同一个对象,将对象中的x改为4。

(7)show方法运行结束,弹栈。

(8)接着执行main剩下的语句,打印d引用对象的x变量的值,此时,d依然指向那个对象,这个对象中的x最后被改为4,所以打印为4。

 

 

6-14,面向对象-封装

1,封装:是指隐藏对象的属性和实现细节,仅对外提供公共访问方式。

封装的好处:

(1)将变化隔离,安全性高,里面如何变化,外面使用不受影响。

(2)方便使用。

(3)提高重要性。

(4)提高安全性。

封装原则:

将不需要向外提供的内容都隐藏起来。

把属性都隐藏,提供公共方法对其访问。

 

2,封装思想:

Private :私有:是一个权限修饰符,用于修饰成员,不能修饰局部。

           私有的内容只在本类中有效。

私有仅仅是封装的一种体现而已。


原创粉丝点击