Java内存分配
1.内存区域
栈内存 stack
作用 : 展示程序的执行流程的 (进栈出栈) / 给方法的执行提供空间
特点 :
- 先进后出 -> 弹夹
- 栈内存中的数据使用完毕立刻回收
- 在方法内有局部变量,方法又在栈内存中,所以局部变量在栈里
堆内存 heap / memory
作用 : 存 new 出来的东西 -> 对象
特点 :
- 存对象
- 只要在堆中开辟空间那么堆内存会给这个空间一个唯一的地址值
- 对象内部空间的数据都是有默认值(数组,对象的属性)
整数 : 0 浮点数 : 0.0 字符 : ‘\u0000’ 布尔 : false 所有引用数据类型 : null- 堆中数据使用完毕后不是立即回收,等到垃圾回收机制(gc)空闲的时候回收
//空闲时机 : 无法预见(时间一定不会很长)
方法区
作用: 存放字节码对象 (.class 文件生成的对象) -> 通过类加载器(classloader)完成字节码文件到字节码对象的转换
//字节码对象中有啥 ? 类的目录/模板(成员变量,成员方法)
作用: 常量池 -> 存放程序执行过程中使用的常量值 //常量可以被共用
字面值常量 : 整数常量,小数常量,字符常量,字符串常量,布尔常量,空常量(null)
常量的作用: 给变量赋值
//常量池在JDK7时,搬到了堆内存中
本地方法区 : 给本地方法提供执行空间
本地方法 : 跟计算机系统进行交互的方法 -> native
寄存器 : 跟CPU运算相关
Java数据类型 :
基本数据类型 : 数量固定 (四类八种)
引用数据类型 : 无数种(五大类)
类 class ,数组 Array ,接口 interface , 枚举 enum , 注解 @interface
2.两个数组的内存图
public class Demo {
public static void main(String[] args) {
//动态初始化
int[] arr = new int[5];
System.out.println("arr = " + arr);//地址值
//对数组元素进行操作
for (int i = 0; i < arr.length; i++) {
System.out.println("arr[i] = " + arr[i]);
}
//修改数组的元素值
arr[3] = 444;
for (int i = 0; i < arr.length; i++) {
System.out.println("arr[i] = " + arr[i]);
}
//静态初始化
double[] ds = {1.1,2.2,3.3};
System.out.println("ds = " + ds);//地址值
for (int i = 0; i < ds.length; i++) {
System.out.println("ds[i] = " + ds[i]);
}
}
}
3.方法调用的内存图
调用方法的诀窍 :
要什么给什么 -> 类型
要多少给多少 -> 个数
怎么要怎么给 -> 类型顺序
有返回值方法三种调用方式 : 赋值调用,输出调用,直接调用
直接调用 : 方法虽然执行了,但是没有处理结果 -> 无意义
输出调用 : 把结果直接输出,不能继续操作
赋值调用 : 接收方法的结果,可以继续操作结果
无返回值的方法调用方式 : 直接调用
方法调用调错:
1. 实参位置报错 : 形参和实参不匹配
2. 方法名报错 : 没有这个方法
3. 整个调用方法语句报错 : a.接收结果的变量类型报错 b.调用方式错了
结论 :
1. 如果方法的形参是基本数据类型,形参接收的是实参的具体值 //形参的改变不影响实参
2. 如果方法的形参是引用数据类型,形参接收的是实参的地址值 //一变则都变
public class Demo {
public static void main(String[] args) {
//启动方法
int sum = sum(20, 10);
System.out.println("sum = " + sum);
int[] arr = {1,2,3,4,5};
System.out.println("arr = " + arr);
System.out.println(Arrays.toString(arr));
change(arr);
System.out.println(Arrays.toString(arr));
}
//基本数据类型的方法
public static int sum(int a, int b) {
int sum = a + b;
return sum;
}
public static void change(int[] arr){
System.out.println("arr = " + arr);
System.out.println(Arrays.toString(arr));
for (int i = 0; i < arr.length; i++) {
if (i % 2 == 0){
arr[i] *= 2;//2,2,6,4,10
}
}
System.out.println(Arrays.toString(arr));
}
}
4.两个对象的内存图
public class Student {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
//行为
public void eat(String food){
System.out.println("姓名是 : " + this.name + " 年龄是 : " + this.age + " 的学生对象在吃 " + food);
}
}
public class Demo {
public static void main(String[] args) {
//创建对象
Student stu1 = new Student();
System.out.println("stu1 = " + stu1);//地址
//给学生对象的属性赋值
stu1.setName("迪丽热巴");
stu1.setAge(18);
//调用学生对象的方法
stu1.eat("切糕");
//创建对象
Student stu2 = new Student("古力娜扎", 19);
System.out.println("stu2 = " + stu2);//地址
//调用学生对象的方法
stu2.eat("哈密瓜");
}
}
static
1.static关键字
static : adj. 静态的 -> 状态修饰符
被修饰的成员就是类中的静态成员
static 关键字可以作用在 : 成员位置
被 static 修饰的成员具备以下4个特点 :
- 被static所修饰的成员被所有对象共享 -> 一变则都变
- 被static所修饰的成员不再属于单个对象,属于类 -> static成员 : 类属性/类方法
- 被static修饰的成员可以直接使用类名调用 -> 类名.静态成员 (关乎static成员的使用方式)
- 被static修饰的成员随着类的加载而加载优先于对象 -> 早于对象加载的
public class Student {
//为了方便不写 private
String name;
int age;
static String graduateFrom;//毕业院校
//构造方法
public Student() {
}
public Student(String name, int age, String graduateFrom) {
this.name = name;
this.age = age;
this.graduateFrom = graduateFrom;
}
public void show(){
System.out.println(name +" --- "+ age +" --- "+ graduateFrom);
}
}
public class Demo {
public static void main(String[] args) {
//创建3个学生对象
Student stu1 = new Student("迪丽热巴", 18, "五道口职业技术学院");
Student stu2 = new Student("古力娜扎", 17, "厦门反散步职业学院");
Student stu3 = new Student("马尔扎哈", 20, "战争学院");
Student.graduateFrom = "武汉大学";
stu1.show();
stu2.show();
stu3.show();
}
}
2.static访问问题
非静态成员变量 | 非静态成员方法 | 静态成员变量 | 静态成员方法 | |
---|---|---|---|---|
非静态方法 | √ | √ | √ | √ |
静态方法 | × | × | √ | √ |
- 静态虽好,但是只能访问静态; -> 好在可以直接使用类名调用,不需要创建对象
- 为什么静态只能访问静态 ? 静态是随着类的加载而加载是更早出生的,不可以访问随对象加载的非静态成员
- 非要在静态方法内访问非静态成员 ? : 把对象在静态方法内创建出来
3.static 关键字的问题
冲突场景 :
- static 方法内不可以使用 this/super 关键字
- static 方法不能重写 //但是可以重载
4.static 关键字的常用场景
工具类的编写时,会大量的用到static关键字;
工具类的编写步骤 :
- 定义一个类,类名叫 XxxUtils.java -> 一般会放在util包下
- 私有无参构造方法,并不提供任何其他构造方法
//Do not let anyone instance this class !!!- 把所有成员变量和成员方法全部用static修饰即可
JDK中的很多工具类:
Arrays 数组操作的工具类
Math 数学操作工具类