day05 面向对象内存原理和数组
我们在之前已经学习过创建对象了,那么在底层中他是如何运行的。
1.对象内存图
1.1 Java 内存分配
Java 程序在运行时,需要在内存中分配空间。为了提高运算效率,就对空间进行了不同区域的划分,因为每一片区域都有特定的处理数据方式和内存管理方式。
Cat cat = new Cat();
突出了解对于堆栈的学习。
1.2 堆和栈
栈:所有局部变量都会在栈内存中创建
局部变量:定义在方法中的变量或者方法声明上的变量
方法执行都会加载到栈中进行
局部变量特点:随着方法的调用而存在,随着方法的调用完毕而消失
代码:Student s = new Student();
堆:所有对象及其对应的实例变量和数组都将存储在此处
简单理解为:new出来的东西,都存储在堆内存
每一个new出来的东西都有一个地址值,使用完毕,会在垃圾回收器空闲时被回收
实例变量(成员变量)有初始化值:
基本数据类型:整数:0,浮点数:0.0,布尔:false,字符:空字符
引用数据类型:null
成员变量和局部变量的区别
区别 | 成员变量 | 局部变量 |
---|---|---|
类中位置不同 | 类中方法外 | 方法内或者方法声明上 |
内存中位置不同 | 堆内存 | 栈内存 |
生命周期不同 | 随着对象的存在而存在,随着对象的消失而消失 | 随着方法的调用而存在,随着方法的调用完毕而消失 |
初始化值不同 | 有默认的初始化值 | 没有默认的初始化值,必须先定义,赋值,才能使用 |
单个对象 指向一个地址
多个对象 指向对应数量的多个地址
多个引用指向相同 地址可以为同一个
1.3 堆-栈内存总结
1.new 对象会创建堆内存
2.声明变量 只创建栈内存
3.多个变量可以指向一个对象
4.局部变量存在栈内存,用完就消失,作用范围仅限方法或者局部{}范围
5.成员变量会存在堆内存,随着对象消失才消失,作用范围类范围
2.private
是一个权限修饰符
可以修饰成员(成员变量和成员方法)
作用是保护成员不被别的类使用,被 private 修饰的成员在本类中可以通过 实例对象.属性名进行访问。
针对 private 修饰的成员变量,如果需要被其他类使用,提供两个相应的操作:
提供“get变量名()”方法,用于获取成员变量的值,方法用 public 修饰
提供“set变量名(参数)”方法,用于设置成员变量的值,方法用 public 修饰
eg:
public class Student {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
//被 private 修饰的成员在本类中可以通过实例对象.属性名进行访问
public static void main(String[] args) {
Student student = new Student();
System.out.println(student.name);
}
}
public class People {
public static void main(String[] args) {
Student student = new Student();
System.out.println(student.name);//报错!!
}
}
3.this
this:方法被哪个对象调用,this就代表哪个对象。
① 什么时候使用this呢?
解决局部变量隐藏成员变量
局部变量和成员变量名字一致,局部变量赋值给成员变量。
② this限定的变量用于指代成员变量
方法的形参如果与成员变量同名,
不带this修饰的变量指的是形参,而不是成员变量
方法的形参没有与成员变量同名,
不带this修饰的变量指的是成员变量
public class Student {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public void setName(String username) {
name = username;//这里可以不用this
}
4.封装
1.封装概述
是面向对象三大特征之一(封装,继承,多态)
是面向对象编程语言对客观世界的模拟,客观世界里成员变量都是隐藏在对象内部的,外界是无法直接操作的
eg:相当于把水放在一个水杯中,只能通过水杯出水口喝水。 外界是无法拿到水,只能通过一个公共的出口取水。
2.封装原则
将类的某些信息隐藏在类内部,不允许外部程序直接访问,而是通过该类提供的方法来实现对隐藏信息的操作和访问
成员变量private,提供对应的**getXxx()/setXxx()**方法
3.封装好处
通过方法来控制成员变量的操作,提高了代码的安全性
把代码用方法进行封装,提高了代码的复用性
规范代码:
public class Student {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
private int age;
public int getAge() {
return age; }
public void setAge(int age) {
this.age = age;
}
}
快捷方式:alt+insert 选择getterAndSetter
5.构造方法
5.1 构造方法概述
构造方法是一种特殊的方法
作用:创建对象
格式:
public class 类名{
修饰符 类名(参数) {
}}
功能:主要是完成对象数据的初始化
5.2 构造方法的注意事项
① 构造方法的创建
如果没有定义构造方法,系统将给出一个默认的无参数构造方法
如果定义了构造方法,系统将不再提供默认的构造方法
② 构造方法的重载
如果自定义了带参构造方法,还要使用无参数构造方法,就必须再写一个无参数构造方法
③ 推荐的使用方式
永远提供无参数构造方法
5.3 JavaBean
就是一个Java中的类,其对象可以用于在程序中封装数据
举例:学生类,手机类
标准 JavaBean 须满足如下要求:
1.成员变量使用 private 修饰
2.提供每一个成员变量对应的 setXxx() / getXxx()
3.提供一个无参构造方法
5.4 练习
手机 JavaBean
需求:编写手机 JavaBean,并在测试类中使用
提示:手机有成员变量brand(品牌)和price(价格)
6.数组
数组概述
为了存储一个学员的考试成绩,我们可以采用一个变量进行存储,如果我有很多个学员的考试成绩,我们也可以用多个变量进行存储。但是,如果学员人数太多,这样定义变量就太麻烦了,这就对我们存储数据提出了新的诉求:一次性声明大量的用于存储数据的变量,要存储的数据通常都是同类型数据,例如:考试成绩。满足这种诉求的东西是什么呢?就是我们接下来要讲解的数组。来我们说一下什么是数组。
int a = 100int b = 100int c = 100int d = 100int e = 100int f = 100int g = 100… …
一次性声明大量的用于存储数据的变量
要存储的数据通常都是同类型数据,
例如:考试成绩int[] scores = {100,100,100,100,100,100,100…};
什么是数组
数组(array):是一种用于存储多个相同数据类型的存储模型(可以理解为容器)
1.数组定义格式
格式1:数据类型[] 变量名;
范例: int[] arr;
定义了一个int类型的数组,数组名是arr
格式2:数据类型 变量名**[];
范例: int arr[]**;
定义了一个int类型的变量,变量名是arr数组
推荐使用:格式1
2.数组初始化概述
静态初始化
动态初始化
2.1.数组初始化之静态初始化
静态初始化:初始化时指定每个数组元素的初始值,由系统决定数组长度
格式:数据类型[] 变量名 = new 数据类型[]{数据1,数据2,数据3,…};
范例:int[] arr = new int[]{1,2,3};
简化格式:数据类型[] 变量名 = {数据1,数据2,数据3,…};
范例:int[] arr = {1,2,3};
2.2 数组初始化之动态初始化
动态初始化:初始化时只指定数组长度,由系统为数组分配初始值
格式:数据类型[] 变量名 = new 数据类型[数组长度];
范例:int[] arr = new int[3];
使用场景:
静态初始化:开始就存入元素值,适合一开始就能确定元素值的业务场景
动态初始化:指定数组长度,后期赋值,适合开始知道数据的数量,但是不确定具体元素值的业务场景
注意:两种初始化的方式是独立的,不可以混用
int[] arr = new int[3]{30,40,50}; //错!!!!
2.3 数组元素来自键盘录入
需求:定义一个可以存储5个元素的int数组,数据来自于键盘录入,最后遍历数组,把元素输出在控制台
分析:
① 数组长度可知,元素未知,采用动态初始化
② 键盘录入,使用Scanner实现
eg:
public static void main(String[] args) {
int[]arr = new int[5];
Scanner sc = new Scanner(System.in);
for (int j = 0; j < 5; j++) {
System.out.println("请输入第" + (j+1) + "个数");
arr[j] = sc.nextInt();
}
printArray(arr);
}
public static void printArray(int[] arr) {
System.out.print("[");
for (int i = 0; i < arr.length; i++) {
if(i==arr.length-1) {
System.out.print(arr[i]);
}else {
System.out.print(arr[i] + ", ");
}
}
System.out.println("]");
}
3.数组元素访问
3.1 数组变量访问方式
格式:数组名
3.2 数组内部保存的数据的访问方式
格式:数组名[索引]
3.3 索引是数组中数据的编号方式
作用:索引用于访问数组中的数据使用,数组名[索引]等同于变量名,是一种特殊的变量名
特征①:索引从0开始
特征②:索引是连续的
特征③:索引逐一增加,每次加1
4.数组常见操作
4.1遍历
什么是数组遍历
获取数组中的每一个元素,我们可以把获取到的元素输出在控制台
获取数组长度(元素个数)
格式:数组名.length
范例:arr.length
数组遍历通用格式:
int[] arr = {...};
for (int i = 0; i < arr.length; i++) {
arr[i]; //对元素arr[i]进行操作
}
注意:数组遍历指的是把数组中的元素取出来,取出来之后可以(打印,求和,判断…)
4.2 获取最大值
获取数组中最大值
public static void main(String[] args) {
//定义数组
int[] arr = {12, 45, 98, 73, 60};
//定义变量max存储最大值,取第一个数据为变量的初始值
int max = arr[0];
//与数组中剩余数据逐个比对,每次比对将最大值保存到变量max中
for (int i=1; i<arr.length; i++) {
if(arr[i] > max) {
max = arr[i];
}
}
//循环结束后输出变量max的值
System.out.println("max:" + max);
}
4.3 元素打乱
把数组中的元素随机交换位置,每次运行都可能产生不一样的结果
比如:arr = {12, 45, 98, 73, 60};
遍历:
正常情况下:12,45,98,73,60
元素打乱后:45,73,12,60,98
涉及到的操作
获取数组中元素的随机索引
Random r = new Random();
int index = r.nextInt(arr.length);
数组中元素交换
int a = 10;
int b = 20;
int temp = a;
a = b;
b = temp;
System.out.println(a);
System.out.println(b);
元素打乱案例
public static void main(String[] args) {
//定义数组
int[] arr = {12, 45, 98, 73, 60};
// Random r = new Random();
// int index = r.nextInt(arr.length);
//
// //第一次交换
// int temp = arr[0];
// arr[0] = arr[index];
// arr[index] = temp;
//
// //第二次交换
// index = r.nextInt(arr.length);
// temp = arr[1];
// arr[1] = arr[index];
// arr[index] = temp;
Random r = new Random();
for (int i = 0; i < arr.length; i++) {
int index = r.nextInt(arr.length);
int temp = arr[i];
arr[i] = arr[index];
arr[index] = temp;
}
//遍历数组
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
5.思考问题数组常见问题
请问下列代码有问题吗?
int[] arr = new int[3];//输出元素
System.out.println(arr[3]);
如果有,是什么问题?
如何解决?
有问题访问了不存在的索引位置元素修改不存在的索引为正确的索引。范围(0~数组长度-1)
int[] arr = new int[3];
System.out.println(arr[2]);//把 null赋值给数组
arr = null;// 输出元素
System.out.println(arr[0]);
数组使用中的两个小问题
1:索引越界:访问了数组中不存在的索引对应的元素,造成索引越界问题
ArrayIndexOutOfBoundsException
2:空指针异常:对象不再指向堆内存,还想继续访问数据,访问失败
NullPointerException
null:空值,引用数据类型的默认值,表示不指向任何有效对象
6.二维数组(自学作业)
需求:我们要存储多个班级的学生的考试成绩,该怎么办呢?
如果要存储一个班级中多个学生的考试成绩,我们就可以采用数组来存储。
多个班级的考试成绩,我们针对每个班级都采用数组存储:
第一个班级:数组1
第二个班级:数组2
第三个班级:数组3
…
但是多个班级也可以采用数组存储啊?所以,Java就提供了二维数组供我们使用
6.1二维数组概述
二维数组:元素为一维数组的数组
定义格式:
数据类型[][] 变量名; int[][] arr;//重点
数据类型 变量名[][]; int arr[][];
数据类型[] 变量名[]; int[] arr[];
6.2二维数组初始化
6.21静态初始化
1.格式:
数据类型[][] 变量名 = new 数据类型[][]{{元素…},{元素…},{元素…},…};
2.范例:
int[][] arr = new int[][]{{1,2,3},{4,5,6},{7,8,9}};
3.解读
定义了一个二维数组
二维数组中有三个元素(一维数组)
每一个一维数组有三个元素(int类型数据)
4.注意:一维数组中元素的个数可以是不同的
举例: int[][] arr = new int[][]{{1,2,3},{4,5},{6,7,8,9}};
5.简化格式:
数据类型[][] 变量名 = {{元素…},{元素…},{元素…},…};
6.范例:
int[][] arr = {{1,2,3},{4,5,6},{7,8,9}};
6.22 动态初始化
1.格式:
数据类型[][] 变量名 = new 数据类型[m][n];
2.范例:
int[][] arr = new int[2][3];
3.解读
定义了一个二维数组
二维数组中有2个元素(一维数组)
每一个一维数组有3个元素(int类型数据)
6.23 二维数组元素访问
获取二维数组:数组名
获取每一个一维数组:数组名[索引] 从0开始
获取每一个二维数组元素:数组名[索引][索引] 从0开始
6.24 二维数组常见操作
遍历&元素打乱
1.二维数组遍历
需求:已知一个二维数组 arr = {{1,2,3},{4,5,6},{7,8,9}}; 请把元素在控制台输出
二维数组中循环嵌套的使用:
① 循环嵌套:循环语句中嵌套循环语句
② 外层循环得到一维数组
③ 内存循环得到每一个二维数组元素
public static void main(String[] args) {
int[][]arr = {{1,2,3},{4,5,6},{7,8,9}};
for (int i = 0; i < arr.length; i++) {
for (int j = 0; j < arr.length; j++) {
System.out.println(arr[i][j]);
}
}
}
2.二维数组元素打乱
需求:已知二维数组 arr = {{1,2,3},{4,5,6},{7,8,9}};
用程序实现把数组中的元素打乱,并在控制台输出打乱后的数组元素
public static void main(String[] args) {
int[][]arr = {{1,2,3},{4,5,6},{7,8,9}};
Random r = new Random();
for (int i = 0; i < arr.length; i++) {
for (int j = 0; j < arr.length; j++) {
//arr[i][j]
int a = r.nextInt(arr.length);
int b = r.nextInt(arr.length);
//元素交换
int temp = arr[i][j];
arr[i][j] = arr[a][b];
arr[a][b]= temp;
}
}
for (int i = 0; i < arr.length; i++) {
for (int j = 0; j < arr.length; j++) {
System.out.print(arr[i][j]+" ");
}
}
}
int[][]arr = {{1,2,3},{4,5,6},{7,8,9}};
Random r = new Random();
for (int i = 0; i < arr.length; i++) {
for (int j = 0; j < arr.length; j++) {
//arr[i][j]
int a = r.nextInt(arr.length);
int b = r.nextInt(arr.length);
//元素交换
int temp = arr[i][j];
arr[i][j] = arr[a][b];
arr[a][b]= temp;
}
}
for (int i = 0; i < arr.length; i++) {
for (int j = 0; j < arr.length; j++) {
System.out.print(arr[i][j]+" ");
}
}
}