思考为什么需要堆和栈
栈是运行时的单位,而堆是存储的单位,栈解决程序的运行问题,堆解决数据存储的问题。
一个线程对应一个线程栈,栈是运行单位,里面存储的信息都是跟当前线程相关的信息,包括局部变量、程序运行状态、方法返回值等,而堆只负责存储对象信息,堆被所有线程共享,堆就是一块共享的内存。
为什么要把堆和栈区分出来呢?栈中不是也可以存储数据吗?
第一,从软件设计的角度看,栈代表了处理逻辑,而堆代表了数据。这样分开,使得处理逻辑更为清晰。分而治之的思想。这种隔离、模块化的思想在软件设计的方方面面都有体现。
第二,堆与栈的分离,使得堆中的内容可以被多个栈共享(也可以理解为多个线程访问同一个对象)。这种共享的收益是很多的。一方面这种共享提供了一种有效的数据交互方式(如:共享内存),另一方面,堆中的共享常量和缓存可以被所有栈访问,节省了空间。
第三,栈因为运行时的需要,比如保存系统运行的上下文,需要进行地址段的划分。由于栈只能向上增长,因此就会限制住栈存储内容的能力。而堆不同,堆中的对象是可以根据需要动态增长的,因此栈和堆的拆分,使得动态增长成为可能,相应栈中只需记录堆中的一个地址即可。
1.java程序在内存中运行.
2.内存划分:
栈:存放局部变量和栈帧(main()在栈帧中运行).效率最高.
堆:所有new出来的东西存放在堆中.和成员属性,效率比栈低点.
方法区:存放.class文件,静态区(存放静态变量),常量池(存放常量).效率最低.
注意:常量池中数据不可重量.
3.一个对象内存分析:
eg:public class Test1 {
public static void main(String[] args) {
//创建对象
Student stu1=new Student();
//用对象调用方法给属性赋值
stu1.setSname("张三");
stu1.setSage(11);
System.out.println("学生姓名:"+stu1.getSname()+",年龄:"+stu1.getSage());
}
}
4.多个对象的内存分析
public class Test2 {
public static void main(String[] args) {
//创建对象
Student stu1=new Student();
Student stu2=new Student();
//用对象调用方法给属性赋值
stu1.setSname("张三");
stu1.setSage(11);
stu2.setSname("李四");
stu2.setSage(18);
System.out.println("学生姓名:"+stu1.getSname()+",年龄:"+stu1.getSage());
System.out.println("学生姓名:"+stu2.getSname()+",年龄:"+stu2.getSage());
}
}
5.方法传参
5.1:值传递:方法的形参是基本数据类型叫值传递,值传递传的是实参变量值,所以在方法中对值作了更改,在方法中这些值变了,方法执行完后,实参变量的值不变.
eg:public class Test3 {
public static void main(String[] args) {
//声明两个变量并赋值
int a=8;
int b=10;
System.out.println("交换前两个数为:a="+a+",b="+b);//8,10
//创建对象,用对象调用方法交换两个数
Change c=new Change();
c.changeNum(a, b);
System.out.println("交换后两个数为:a="+a+",b="+b);//8,10
}
}
5.2:引用传递:方法形参是引用数据类型叫引用传递.引用 传递传的是实参变量的内存地 址,在方法对内存地址中值作了更改,方法中值就改了,方法执行完后实参变量的值改变了.
eg:public static void main(String[] args) {
//创建对象
Student stu2=new Student();
stu2.setSname("王五");
stu2.setSage(100);
System.out.println("改变前对象的姓名:"+stu2.getSname()+",年龄:"+stu2.getSage());//王五,100
//创建对象调用方法改变对象的属性值
Change c=new Change();
c.changeStu(stu2);
System.out.println("改变后对象的姓名:"+stu2.getSname()+",年龄:"+stu2.getSage());//江展华,30
}
6.数组的内存分析
public static void main(String[] args) {
//声明一维数组
int[] nums=new int[3];
//给一维数组每个空间赋值
nums[0]=59;
nums[1]=49;
nums[2]=99;
System.out.println("一维数组:"+Arrays.toString(nums));
//声明二维数组
int[][] arrs=new int[2][2];
//给二维数组的每个空间一维数组每个空间赋值
arrs[0][0]=11;
arrs[0][1]=12;
arrs[1][0]=21;
arrs[1][1]=22;
System.out.println("二维数组:");
for (int[] a : arrs) {
System.out.println(Arrays.toString(a));
}
//声明对象数组,本质一维数组,数组的每个空间存的是一个对象
//对象数组整体遵循数组使用原则,数组中每个空间是一个对象遵循对象使用原则
Student[] stus=new Student[2];
//给对象数组每个空间赋值,数组名[索引]相当于对象名
stus[0]=new Student();
stus[0].setSname("小明");
stus[0].setSage(18);
stus[1]=new Student();
stus[1].setSname("小红");
stus[1].setSage(28);
System.out.println("对象数组:"+Arrays.toString(stus));
}
7.(扩展)对象数组:本质是一维数组,只是数组的每个空间存的对象的内存地址.
注意:对象数组整体上遵循数组使用原则;
对象数组的每个空间是一个对象要遵循对象使用原则;
对象数组的值实际存在对象的属性中.
eg:public static void main(String[] args) {
//声明int数组
int[] nums1=new int[3];
//给int数组赋值
nums1[0]=11;
nums1[1]=12;
nums1[2]=13;
System.out.println("int数组nums1:"+Arrays.toString(nums1));
//声明对象数组
Student[] stus1=new Student[2];
//给对象数组赋值
stus1[0]=new Student();
//用数组每个空间(即对象),用对象调出属性存值
stus1[0].setSname("aa");
stus1[0].setSage(18);
stus1[1]=new Student();
//用数组每个空间(即对象),用对象调出属性存值
stus1[1].setSname("bb");
stus1[1].setSage(38);
System.out.println("对象数组stus1:"+Arrays.toString(stus1));
//声明int数组的同时赋值
int[] nums2= {66,88,99};
System.out.println("int数组nums2:"+Arrays.toString(nums2));
//声明对象数组的同时赋值
Student[] stus2= {new Student("cc", 17),new Student("dd", 18)};
System.out.println("对象数组stus2:"+Arrays.toString(stus2));
}