您的位置:首页 > 编程语言 > Java开发

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

2013-12-01 18:55 525 查看

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);
}
//类类型的变量一定指向对象,要么就是null
public 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 :私有:是一个权限修饰符,用于修饰成员,不能修饰局部。
私有的内容只在本类中有效。
私有仅仅是封装的一种体现而已。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: