封装概念
面向对象具有四大特征:
- 封装
- 继承
- 多态
- 抽象
“封装”包含了两层含义“封”和“装”:
- 装的目的是将散的东西统一在一起,方便处理和使用
- 封是为了将信息隐藏,还包括对方法的具体实现的隐藏
- 前面学习过的类的定义、访问修饰符也都是属于面向对象中“封装”这个特性,访问修饰符存在3个关键字和4种情况:
- public 公共的,可以任意访问
- private 私有的,仅本类可以自己访问
- protected、不写的两种情况后面再做讨论
- 日常生活中能体会到封装实际就是打包装箱
在前面学习“类”的定义,其本质就是在学习“装”的这个过程。我们将所需要的数据、功能进行分门别类的放置,有些设计在 A 类中,有些在 B 类中。我们可以认为“类定义”的{}
就是装的边界。
包 package
包的引入也是一个“装”的体现。一个包里的类,理论上只能认识本包中其他类,来自于其他包的类必须使用 import
来导入,向编译器指定这个类型的位置和名称,例如经常使用到的 import java.util.Scanner
。以下是使用类时不需要用 import 来告知的情况:
- 来自于 java.lang 包中的 JDK 类,例如使用的 String 就属于 java.lang 包,在使用时并不需去提前 import
- 自定义于本包中的类
- 还有一种特殊情况,当我们要在自定义类中,使用多个需要 import 进来的同名类,该如何处理呢?就譬如在 java 类库里有两个类都叫 Date 类,一个来自于 java.util 包,一个来自 java.sql 包 。
java 中一个类的真正名称是 包名.类名 称为 “类的限定名”。使用自定义类来模拟一下不同包同名类的情况:
当前在 a 包中有 A 类,b 包中也有 A 类:
可以看到使用 包名.类
的方式可以清晰地区分 a.A 类 或是 b.A 类。在 new 构造函数时加上包名可以分别访问到不同包的同名类中的属性值,构造函数的名字与类名一致,所以其实构造方法的名字也是类的限定名。都不用 import 提前去导入。只是平时我们都是用的 “类的简单名” (simple name)
对象数组
回忆一下我们对于多个同类型的对象是如何处理管理的?
装进到一个集合里去,对不对?
就像当前存在 10 个 int 类型的数字,我们可以将它们装进到一个集合中去。目前对于集合的学习,只学习过一种 数组。
数组是通过 new
出来的,new int[5]
也就证明了数组本身就是一个引用数据类型,是对象,是存放在内存的“堆”中的。例如 int [] arr = new int[5]
可以看作:arr 这个变量名指向了一个对象,该对象有 5 个 int 类型的属性用来装数据,默认初始化为 0,只不过没有给 5 个属性命名,而是通过编号(下标)去表示,即 0-4。而且数组除了装数据的属性,至少还拥有一个 length 这样的公共属性。所以我们访问数组长度的语法是 数组名.length
。
数组 vs 基本数据类型数组 vs 引用数据类型数组
数组可以被分为两类:“基本数据类型数组”和“引用数据类型数组”,注意区分清楚:
- 数组,一种数据类型,它肯定是引用数据类型
- 基本数据类型数组是指数组里的 元素 是基本数据类型;引用数据类型数组 是指数组中的 元素 是引用数据类型
在// 基本数据类型数组
int [] intArray = new int[10];
// 引用数据类型数组
Person [] personArray = new Person[10];
new
数组对象时,不是将 10 个 Person 对象放在一起,而是 10 个 Person 变量。换句话说只是 new 了10 个位置,用来放对象的引用。数组的拷贝
System.arraycopy()
是 java 语言提供的一种本地静态方法,用于将元素从源数组复制到目标数组以实现数组拷贝:
语法:
参数说明:System.arraycopy(arr, oldStartIndex, newArr, newArrIndex, copyCount)
- 源数组
- 源数组的起始下标
- 目标新数组
- 从新数组的起始下标
- 拷贝的元素个数 ```java // 基本数据类型数组 int[] arr = {1, 3, 5, 7, 9}; // 原数组 int[] newArr = new int[arr.length]; // 新数组长度等于源数组长度
System.arraycopy(arr, 0, newArr, 0, arr.length);
// 打印拷贝数组各元素 for (int i = 0; i < newArr.length; i++) { System.out.println(“新数组第” + i + “位:” + newArr[i]); } // 修改源数组一项 arr[0] = 2;
// 原数组已被修改 for (int i = 0; i < arr.length; i++) { System.out.println(“老数组:” + arr[i]); }
// 新数组保持不变,拷贝成功 for (int i = 0; i < newArr.length; i++) { System.out.println(“新数组:” + newArr[i]); }
```java
// 引用类型数组
// Car.java
public class Car {
public String name;
public Car(String name) {
this.name = name;
}
}
// TestMain.java
Car cars[] = new Car[3];
cars[0] = new Car("BMW");
cars[1] = new Car("Porsche");
cars[2] = new Car("Land Rover");
for (int i = 0; i < cars.length; i++) {
System.out.println("原数组:" + cars[i].name);
}
Car newArr[] = new Car[cars.length];
System.arraycopy(cars, 0, newArr, 0, cars.length);
for (int i = 0; i < newArr.length; i++) {
System.out.println("新数组:" + newArr[i].name);
}
cars[0].name = "Audi";
for (int i = 0; i < newArr.length; i++) {
System.out.println("修改后新数组:" + newArr[i].name);
}