Java 内存分析(程序实例),学会分析内存,走遍天下都不怕!!!

相信大多数的java初学者都会有这种经历:碰到一段代码的时候,不知该从何下手分析,不知道这段代码到底是怎么运行最后得到结果的… 等等吧,很多让人头疼的问题,作为一名合格的程序员呢,遇到问题一定要思路清晰,不要将错就错,蒙混过关,这一点很重要!
鉴于笔者最近恶补了java基础,在这儿给大家总结了一些java代码内存分析的经验,希望可以对家有所帮助。
在分析内存之前呢,通过这个图让大家明白计算机是怎么处理一个程序的。
在这里插入图片描述
简单总结一下:1.计算机首先从硬盘拿到程序,加载(load)到内存区
2.操作系统找到main方法开始执行
3.执行代码过程中的内存管理:
内存中分四块:分别是heap(堆)、stack(栈)、data segment
(数据区)、code segmen(代码区),各个区所存储的内容图中已
标注。

接下来,
给大家举几个例子程序,分别进行内存分析

**

程序一:

**

public class Person {

	static int id;
	static int age;
	
	Person(int _id, int _age) {
		id = _id;
		age = _age;
	}
	
	public static void main(String[] args) {
		Person tom = new Person(1,25);
		System.out.println("id= " + id + " age= " + age);

	}

}

内存分析图:

第一步:从main方法入手,首先看到要实现一个Person类对象,该对象的名字是tom,则在stack中立马分配出一块空间用来存tom这个对象名,它指向heap中的Person对象(上文提到heap中存储new出来的东西)

注:图中局部变量tom的内容为xxx,xxx实际为Person对象在堆中的地址,在此处用xxx
代替。
在这里插入图片描述

第二步:调用Person中的构造方法,此时定义了两个局部变量(要存储到stack中)_id和_age,则立马在stack中分配两块空间用于存储这两个局部变量,接下来把1和25分别传给这两个变量

在这里插入图片描述

第三步:执行id=_id; age=_age;这两句,把_id和_age的值传给Person对象

在这里插入图片描述

第四步:局部变量_id和_age消失(java的垃圾回收机制)

在这里插入图片描述

到此完成Person对象的创建,执行输出语句,程序运行结束。

**

程序二:super引用,动态绑定及多态

**
说到多态,就先给大家巩固一下动态绑定和多态的概念吧:
动态绑定:在执行期间(非编译期间)判断所引用对象的实际类型,根据其实际的类型调用其相应的方法。
简单的来说,动态绑定就是根据实际new的对象所属的类调用方法,帮助程序的扩展性达到极致。
多态的话,要知道多态产生的三个条件:
1.有继承
2.有重写
3.父类引用指向子类对象

//abstract 关键字 ---> 抽象类,抽象类一定被继承,抽象方法一定被重写
class Animal { //可以这样声明  absrtact class Animal
	private String name;
	Animal(String name) {  
		this.name = name;
	}
	
	public void enjoy() {  
		System.out.println("叫声...");
	}
}

class Cat extends Animal{
	private String eyescolor;
	
	Cat(String n,String c) {
		super(n);
		eyescolor = c;
	}
	public void enjoy() {
		System.out.println("猫叫声....");
	}
}

class Dog extends Animal {
	private String furCorlor;

	Dog(String n, String f) {
		super(n);
		furCorlor = f;
	}

	public void enjoy() {
		System.out.println("狗叫声...");
	}
}

class Lady {
	private String name;
	private Animal pet;

	Lady(String name, Animal pet) {
		this.name = name;
		this.pet = pet;
	}

	public void petEnjoy() {
		pet.enjoy();
	}
}
public class TestAnimal {
	public static void main(String[] args) {
		Cat c = new Cat("catname","blue");
		Dog d = new Dog("dogname","black");
		c.enjoy();
		d.enjoy();

		Lady l1 = new Lady("l1",c);
		Lady l2 = new Lady("l2",d);
		l1.petEnjoy();
		l2.petEnjoy();
	}
}

内存分析图:
老样子,从main方法入手:

第一步,Cat c = new Cat(“catname”,“blue”);

当你new一个子类对象出来的时候,其内部就已经包含父类对象,并且该子类对象的super引用会指向其父类对象。
此处分两小步给大家说明:(stack中局部变量的产生过程不再赘述,可参考程序一)
(1)new出来的Cat对象中包含Animal对象,Animal对象有一个成员变量name,自动初始化为null,C对象有一个成员变量eyescolor,自动初始化为null。
在这里插入图片描述
(2)传“catname”和“blue”这两个参数,“blue”直接赋值给eyescolor,而“catname”传进去后,通过super(n)方法,调用其父类对象的构造方法,使name值等于“catname”
在这里插入图片描述

第二步,Dog d = new Dog(“dogname”,“black”);

内存分析同第一步(此处省略)

在这里插入图片描述

第三步,c.enjoy(); d.enjoy();

此时,c指向的是Cat对象,但是Cat对象中包含其父类Animal对象,这两个对象都含有enjoy()方法,那么应该调用哪一个呢?这就涉及到了java的动态绑定机制了,你new出来的对象实际是Cat对象,那么就调用Cat对象的方法,而不是调用Animal对象的enjoy()方法;同理,d.enjoy()调用的是Dog对象的enjoy()方法。
所以,输出结果为在这里插入图片描述
第四步,Lady l1 = new Lady(“l1”,c);
Lady l2 = new Lady(“l2”,d);

内存分析图:
在这里插入图片描述此处Animl的引用pet指向了其子类对象c,也就是上文提到的“父类引用指向子类对象”,并且,有继承、有重写,这就是多态。
每一个创建出来的对象都有this引用,指向他自身

第五步, l1.petEnjoy();
l2.petEnjoy();

调用Lady的petEnjoy()方法,输出对应信息,最终输出结果为
在这里插入图片描述

程序三:数组

3.1 一维数组

public class Test {
	public static void main(String[] args) {
		Date[] days = new Date[3];
		for(int i=0;i<3;i++) {
			days[i] = new Date(2020,4,i+1);
		}
	}  
}
class Date {
	int year,month,day;
	Date(int y, int m, int d) {
		year = y;
		month = m;
		day = d;
	}
}

内存分析图

在这里插入图片描述days指向堆中的一个数组,该数组中存放的是每一个Date对象的地址,每一个地址指向一个Date对象。(详细赋值过程可参考程序一)

3.2 二维数组
二维数组实际上是一维数组的数组

public class Array {
	public static void main(String[] args) {
		int[][] a = new int[][] {
			{1,2,3},
			{4,5,6},
			{7,8,9}
		};
		
	}
}

内存分析图
在这里插入图片描述栈空间中a指向一个一维数组,该数组每块区域存放的是int型数组的地址。

笔者创作初期,如有不当之处,望批评指正。

  • 23
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 9
    评论
回溯法是解决迷宫问题的一种常用方法。其基本思路是从起点开始,依次向四个方向探索,每次选择一个可行的方向前进,直到到达终点或者走到死路。如果走到死路,则回退到上一个节点,尝试其他可行的方向。直到找到一条到达终点的路径或者所有路都走遍了,才结束搜索。 下面是使用回溯法解决迷宫问题的详细步骤: 1. 定义一个二维数组表示迷宫,其中0表示可走的空地,1表示墙壁。 2. 定义一个二维数组表示走过的路径,初始时所有元素均为0。 3. 定义一个列表表示可能的方向,例如[(0,1), (1,0), (0,-1), (-1,0)],表示向右、向下、向左、向上四个方向。 4. 定义一个递归函数,输入参数为当前位置的坐标和走过的路径,输出为是否找到了终点。 5. 在递归函数内部,首先判断当前位置是否为终点,如果是,则返回True;如果不是,则依次尝试四个方向,判断是否可走。如果可走,则将当前位置标记为已走过,并调用递归函数处理下一步。如果不可走,则继续尝试其他方向。 6. 在递归函数返回后,需要将当前位置标记为未走过,以便进行下一次尝试。 7. 在主函数中调用递归函数,输入参数为起点坐标和走过的路径。 8. 如果递归函数返回True,则表示找到了一条通向终点的路径;否则表示不存在通向终点的路径。 下面是代码示例(使用Python语言实现): ```python maze = [[0,1,1,1,1], [0,0,0,0,1], [1,0,1,0,1], [1,0,0,0,0], [1,1,0,1,0]] def find_path(x, y, path): # 到达终点,返回True if x == len(maze) - 1 and y == len(maze[0]) - 1: path[x][y] = 1 return True # 尝试四个方向 for dx, dy in [(0,1), (1,0), (0,-1), (-1,0)]: nx, ny = x + dx, y + dy # 判断是否可走 if 0 <= nx < len(maze) and 0 <= ny < len(maze[0]) and maze[nx][ny] == 0 and path[nx][ny] == 0: path[nx][ny] = 1 # 递归处理下一步 if find_path(nx, ny, path): return True path[nx][ny] = 0 # 所有方向都尝试过了,未找到路径,返回False return False if __name__ == '__main__': path = [[0] * len(maze[0]) for _ in range(len(maze))] path[0][0] = 1 if find_path(0, 0, path): for row in path: print(row) else: print("不存在通向终点的路径") ``` 输出结果为: ``` [1, 0, 0, 0, 0] [1, 1, 1, 1, 0] [0, 0, 0, 1, 0] [0, 0, 0, 1, 1] [0, 0, 0, 0, 1] ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值