目录
五、数组
5.1 数组的概念
Java数组是一个包含类似数据类型的元素的对象。 它是一个数据结构,我们存储类似的元素。 只能在java数组中存储固定的元素集合。Java中的数组是基于索引的,数组的第一个元素存储的索引为 0
。
数组元素的类型:可以是基本类型,也可以是引用类型
int[] nums; //每个元素是int类型,int就是基本类型
String[] args //数组中的每个元素都是String类型,String就是引用类型
5.2 数组声明
两种方式:建议采用第一种方式
dataType[] arrayRefVar; // 首选的方法
或
dataType arrayRefVar[]; // 效果相同,但不是首选方法
5.3 数组创建与初始化
5.3.1 使用new创建数组
int[] nums; //声明数组,并没有创建数组,没有开辟堆内存。
nums = new int[5]; //创建数组,必须设置长度 开辟堆内存
new:用于分配内存的特殊运算符。通过new分配的数组,其元素会被自动初始化为0(对于数值类型)、false(对于boolean类型)或null(对于引用类型)。
说明:
获得一个数组需要两个步骤,第一步是声明,第二步是创建数组对象。
一个数组就是一个对象。数组是动态创建的,所有对象都是动态创建的。
对于引用类型的变量:在内存中有两部分:引用变量 具体的对象
5.3.2 创建数组的三种方式
int[] nums = new int[5]; //初始化为默认值
int[] nums = {1,2,3,4,5}; //初始化为{}中指定的值,静态初始化
int[] nums = new int[] {1,2,3,4,5};//初始化为{}中指定的值 静态初始化
5.3.3 创建数组需要注意的问题
1、创建数组时必须知道数组的长度,否则new不知道要开辟多大的内存
2、第二种方式创建数组,必须在声明数组的同时创建数组
3、创建数组之后,数组的长度不能再改变。
说明:
数组的初始化分为静态初始化和动态初始化,静态初始化在初始化时由程序员显式指定每个数组元素的初始值,由系统决定数组长度。
5.4 数组操作
1、数组排序及元素查找
示例:
import java.util.Arrays;
public class MainClass {
public static void main(String args[]) throws Exception {
int array[] = { 2, 5, -2, 6, -3, 8, 0, -7, -9, 4 };
Arrays.sort(array);
printArray("数组排序结果为", array);
int index = Arrays.binarySearch(array, 2);
System.out.println("元素 2 在第 " + index + " 个位置");
}
private static void printArray(String message, int array[]) {
System.out.println(message
+ ": [length: " + array.length + "]");
for (int i = 0; i < array.length; i++) {
if(i != 0){
System.out.print(", ");
}
System.out.print(array[i]);
}
System.out.println();
}
}
以上代码运行输出结果为:
数组排序结果为: [length: 10] -9, -7, -3, -2, 0, 2, 4, 5, 6, 8
元素 2 在第 5 个位置
2、通过循环处理数组(打印输出所有数组元素)
示例:
public class TestArray {
public static void main(String[] args) {
double[] myList = {1, 3, 2, 5};
// 打印所有数组元素
for (int i = 0; i < myList.length; i++) {
System.out.println(myList[i] + " ");
}
}
}
以上代码运行输出结果为:
1
3
2
5
3、使用length属性获取数组的长度,从而可以避免数组越界。
示例:
public class Test {
public static void main(String[] args) {
// 定义一个一维数组
int[] numbers = {1, 2, 3, 4, 5};
// 获取数组的长度
int length = numbers.length;
// 打印数组的长度
System.out.println("数组的长度是: " + length);
}
}
以上代码运行输出结果为:
数组的长度是: 5
5.5 数组排序
5.5.1 排序(冒泡)
冒泡排序的基本原理
冒泡排序(Bubble Sort)是一种简单的排序算法,它通过多次遍历待排序的元素,比较相邻元素的大小,并交换它们直到整个序列有序。冒泡排序的基本思想是将较大的元素逐渐“浮”到数组的右端,而较小的元素逐渐“沉”到数组的左端。其基本原理如下:
- 从数组的第一个元素开始,比较相邻的两个元素。
- 如果前一个元素大于后一个元素(升序排序),则交换它们的位置。
- 步骤1和步骤2,直到遍历整个数组。
- 上步骤,每次遍历都将最大的元素“冒泡”到数组的末尾。
- 复以上步骤,但不包括已排序的最大元素,直到整个数组排序完成。
示例:
import java.util.Arrays;
public class Test{
public static void main(String[] args) {
int[] arr = new int[]{5, 7, 4, 3, 6, 2};
bubbleSort(arr);
System.out.println("冒泡排序完的数组:" + Arrays.toString(arr));
}
public static void bubbleSort(int[] arr) {
int n = arr.length;
//外部循环控制排序的趟数。冒泡排序的每一趟会将最大的元素"冒泡"到数组的末尾,因此需要执行 n-1 趟,其中 n 是元素的总数
for (int i = 0; i < n - 1; i++) {
//内循环控制每趟比较的次数。由于每一趟都会将一个最大的元素沉到数组末尾,所以内循环次数逐渐减小。
for (int j = 0; j < n - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
// 交换arr[j]和arr[j+1]
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
System.out.println("第" + (i + 1) + "趟:" + Arrays.toString(arr));
}
}
}
以上代码运行输出结果为:
第1趟:[5, 4, 3, 6, 2, 7]
第2趟:[4, 3, 5, 2, 6, 7]
第3趟:[3, 4, 2, 5, 6, 7]
第4趟:[3, 2, 4, 5, 6, 7]
第5趟:[2, 3, 4, 5, 6, 7]
冒泡排序完的数组:[2, 3, 4, 5, 6, 7]
5.5.2 Arrays.sort()
Arrays是Java提供的一个工具类。在Java中我们经常需要对数组进行排序。而对于字符数组来说,我们可以使用Java提供的Arrays类的sort()方法来实现排序操作。
示例:
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
// 定义一个字符数组
char[] charArray = {'b', 'c', 'a', 'e', 'd'};
// 使用Arrays类的sort()方法对字符数组进行排序
Arrays.sort(charArray);
// 打印排序后的结果
for (char c : charArray) {
System.out.print(c + " ");
}
System.out.println();
}
}
以上代码运行输出结果为:
a b c d e
5.6 Arrays工具类
Arrays类为java.util包里的一个工具类,提供对数组的一些操作方法,常用的有排序(sort)、填充(fill)、拷贝(copyOf)、查找(binarySearch)、判断相等(equals)等。
5.6.1 排序(sort)
Arrays.sort() 支持的类型有char、double、int、byte、float等,同时还支持泛型,但需要自己实现Comparator接口
int ary1[] = new int[]{5,23,15,35,24,6,80,18};
//使用Arrys.sort()对ary1进行从小到大排序
Arrays.sort(ary1);
for (int i : ary1) {
System.out.print(i+" ");
}
以上代码运行输出结果为:
5 6 15 18 23 24 35 80
5.6.2 填充(fill)
将数组所有元素赋值为统一的填充值,如果对应元素已经有值了,则将覆盖原来的值
//Arrays.fill(int[] a, int val) 的实现:
public static void fill(int[] a, int val) {
for (int i = 0, len = a.length; i < len; i++)
a[i] = val;
}
对数组ary2进行填充,将所有元素填充为6:
int ary2[] = new int[10];
Arrays.fill(ary2, 6);
for (int i : ary2) {
System.out.print(i+" ");
}
以上代码运行输出结果为:
6 6 6 6 6 6 6 6 6 6
5.6.3 拷贝(copyOf)
参数:
int[] original:被拷贝的数组
int newLength:被拷贝的长度,即original中被拷贝元素的长度
Arrays.copyOf() 函数会返回一个全新的数组,数组中的元素依次被赋值为orignal数组中前n个元素的值,n即被拷贝的长度,当n大于original数组的长度时,可用来对数组长度进行扩展,扩展部分元素值为0
int ary0[] = new int[]{1,2,3,4,5,6};
int ary1[];
//将ary0前三个元素拷贝给ary1
ary1 = Arrays.copyOf(ary0, 3);
//打印ary1的长度与元素值
System.out.println(ary1.length);
for (int i : ary1) {
System.out.print(i+" ");
}
以上代码运行输出结果为:
3
1 2 3
表明ary0中前3个元素【1,2,3】被复制给了一块新的内存空间,同时ary1指向该空间:
5.6.4 二分查找(binarySearch)
1、使用二分查找时,必须先将数组排序 ! ! !
2、一般用在查找元素是否存在
3、如果数组没有排序,结果无法确定
4、如果包含多个指定值,无法保证找到的是哪一个,如果找不到,则返回负数
int ary0[] = new int[]{1,2,5,4,3,6};
//对ary0中3这个元素进行查找
int index = Arrays.binarySearch(ary0, 3);
System.out.println("未排序的数组为:");
showAry(ary0);
System.out.println("未排序进行查找结果:"+index);
//对ary0进行排序,再次查找
Arrays.sort(ary0);
System.out.println("排序的数组为:");
showAry(ary0);
index = Arrays.binarySearch(ary0, 3);
System.out.println("排序后进行查找结果:"+index);
//ary0指向新的、存在多个相同元素数组
ary0 = new int[]{1,2,3,4,5,2,6,3,8};
//再次进行查找
Arrays.sort(ary0);
System.out.println("多值相同且排序过的数组:");
showAry(ary0);
index = Arrays.binarySearch(ary0, 3);
System.out.println("多值相同进行查找结果:"+index);
以上代码运行输出结果为:
未排序的数组为:
1 2 5 4 3 6
未排序进行查找结果:-3
排序的数组为:
1 2 3 4 5 6
排序后进行查找结果:2
多值相同且排序过的数组:
1 2 2 3 3 4 5 6 8
多值相同进行查找结果:4
5.6.5 判断相等(equals)
使用Arrays.equals() 判断相等时,进行的是数组各元素的值的比对,而非对ary0和ary1所指向地址的比对,具体实现如下:
public static boolean equals(int[] a, int[] a2) {
if (a==a2)
return true;
if (a==null || a2==null)
return false;
int length = a.length;
if (a2.length != length)
return false;
for (int i=0; i<length; i++)
if (a[i] != a2[i])
return false;
return true;
}
以上代码运行输出结果为:
false
true
5.7 多维数组
5.7.1 二维数组的声明、创建
在Java中,多维数组(multidimensional array)实际上是数组的数组。
下面声明并创建了一个名为twoD的二维数组:
int[][] twoD = new int[4][5]; //4行5列 这种说法是不严谨
twoD是一维数组,但是数组的元素也是一个数组
int[] 表示int类型的数组,即数组元素为int类型
int[][] 表示int[]类型的数组,即数组元素为int[]类型
5.7.2 为二维数组使用初始化器
int[][] a = new int[3][];
a[0] = new int[]{1,2,3};
a[1] = new int[]{4,-5,6,9,0,8}; //a[1][2]
a[2] = new int[]{7,8};
int[][] nums = {{1,2,3}, {2,3}, {4,5,6}};
nums[0][1] ?
nums[0] = {1,2,3}
注意:
二维数组,创建数组是,[][] 第一个必须指明长度
声明的有效方式:
int[][ ] ia1 = new int[2][3];
int[][ ] ia2 = new int[2][];
ia2[0] = new int[2], ia2[1] = new int[3];
Teacher[][] ta;
ta = new Teacher[2][];
ta[0] = new Teacher[3];
ta[1] = new Teacher[4];
无效的声明方式
int[][ ] ia1 = new int[][3];
Teacher[][] ta = new Teacher[][5];
六、面向对象-类和对象
6.1 类与对象的概念
6.1.1 面向对象的概念
面向对象编程:OOP(Object-Oriented Programming)。
一切皆对象!比如学生对象、空调对象等等。
面向对象的三大特征是:封装、继承、多态,(抽象)。
到目前为止我们已经遇到了String、Scanner、Math、Arrays等类,这些类都是Java类库中提供的类。除了可以使用类库提供的类之外,我们也可以定义自己的类,并使用自定义的类。
6.1.2 使用类和对象开发程序的基本步骤
对于面向对象编程,主要工作就是编写类。面向对象开发的步骤:
- 开发类,类 = 属性(成员变量) + 方法(成员方法)
- 通过new关键字和类创建对象
- 使用类中的属性和方法:对象.属性名 对象.方法名()
6.1.3 类与对象
从概念上讲,类表示一类事物,从语法上讲,类是一种数据类型
(1)类是一种逻辑结构,对具有公共特征和行为(功能)的一个群体进行描述。例如可以定义Student类描述学生的公共属性和行为,定义一个Teacher类,描述老师的公共属性和行为。
(2)定义了类之后,就可以根据类创建(new)出一个对象(实例)。比如学生张三,王老师。
通俗地说:
类定义了一种新的数据类型(复合类型)。对象就是根据类定义的变量。可以将类看做是复合类型。
类是对象的模板(template),对象是类的实例(instance)。因为对象是类的实例,所以经常会看到交换使用“对象”和“实例”这两个词。
6.2 定义类
类 = 属性 + 方法
6.2.1 类的一般形式
class 类名 { //类名通常以大写字母开始
类型 变量1;
类型 变量2;
…
类型 方法名(参数列表) {
// 方法体
}
…
}
在类中定义的变量和方法都称为类的成员。所以变量又称为成员变量,方法又称为成员方法。
6.2.2 类的属性
类的成员变量又称为类的属性。
public class Student {
/**
* 属性 成员变量
* 类的{}内直接声明(定义)的变量 叫 成员变量/实例变量
*/
String name;
int age;
double score;
}
属性属于类的某个具体对象。类的每个实例(即,类的每个对象)都包含这些变量的副本,因此在类中定义的变量又被称为实例变量
6.2.2 类的方法
方法是对象行为特征的抽象,类具有的共性的功能操作,称之为方法。方法是个“黑匣子”,完成某个特定的应用程序功能。
方法的基本语法:
修饰符 返回类型 方法名(形参列表){
//功能代码
}
形参可以为空,可以有多个,形参的类型可以是基本类型也可以是引用类型。p
ublic class Student {
String name;
int age;
double score;
void study(){
//
}
void show(){
//
}
}
注意:
方法中定义变量称为局部变量。
如果没有返回值,则方法的返回类型必须为void
当方法有具体的返回类型时,则必须使用return语句返回一种值。
6.3 对象的创建于使用
6.3.1 对象的声明与创建
基本类型和引用类型的区别:
基本类型:int a = 10;
Student stu1; //声明对象的引用
stu1 = new Student(); //创建对象
变量a保存的是实际数据;变量stu1保存的是实际数据在内存的中的位置。
说明:
引用相当于标签,对象相当于具体的物体
6.3.2 访问对象的成员
public static void main(String[] args) {
Student stu1 = new Student();
stu1.name = "张三"; //访问对象的属性
stu1.age = 20;
stu1.score=95.5;
stu1.show(); //方法调用
Student stu2 = new Student();
stu2.name = "李四"; //访问对象的属性
stu2.age = 22;
stu2.score=98;
stu2.show(); //方法调用
Student stu3 = stu2;
sut2.show();
}
注意:
属性属于类的具体对象,不同对象的属性值通常是不同的。
虽然方法也是通过对象调用的,但是各对象共享相同的方法。
6.3.3 为引用变量赋值
Box b1 = new Box(); //创建对象,让b1指向(引用)所创建的对象
Box b2 = b1;
注意:
b1和b2引用同一个对象。 对象b1:本质是指b1指向的对象
b2.length = 5; //将b2指向的Box对象的长度设置为5,
System.out.println(b1.length) //输出??
对象引用与对象的关系:
(1)对象引用,有时也称为对象引用变量,或称为引用变量。
(2)对象引用与对象在物理上是两个不同的东西。
(3)对于上图,我们通常说b1引用(有时也称为指向)图中的那个Box对象。
(4)对象只能通过对象引用来操作。有时直接使用对象引用代指对象,例如对于上面的例子,有时会直接将b1引用的对象称为“对象b1”或“”b1对象。
(5)将一个对象引用赋值给另一个对象引用,则两个引用变量指向同一个对象。
6.3.4 对象比较
对引用变量进行相等性比较,例如b1==b2,是比较两个引用变量是否引用同一个对象,所以b1==b2的结果为true。对对象引用进行相等性比较,有时也直接称为对象的相等性比较。
6.4 构造器/构造方法
6.4.1 构造方法的语法
构造方法也叫做构造器
构造方法的作用:开辟内存空间、创建实例、初始化属性值。
构造方法的特点:
(1)方法名与类名相同 (大写敏感)public Student( );
(2)不能声明返回类型,void也不行 //没有返回类型(也算对,但是不严谨)
(3)不能使用return语句返回内容/不能有返回值
(4)通常为public
注意:
(1)如果没有明确提供构造方法,则系统会提供一个默认的构造方法,默认构造方法(也称为缺省构造方法)没有参数。
(2)如果我们提供了一个构造方法,则系统不再提供无参数的默认构造方法。
(3)如果我们提供了一个有参数的构造方法,同时又需要无参构造方法的话,则必须同时提供一个无参数的构造方法。
6.4.2 new运算符
new运算符实际是就是调用构造方法,创建一个新对象。当进入到构造方法内部时,实际上对象已经创建完毕,可以在构造方法中为各成员变量赋值。
6.4.3 this关键字
在类的内部,可以在任何方法中使用this引用当前对象。
使用this关键字解决在实例变量和局部变量之间可能发生的任何名称冲突。
局部变量,包括方法的形参,可以和类的实例变量重名。当局部变量和实例变量具有相同的名称时,局部变量隐藏了实例变量。
6.5 方法深入分析
方法可以看做是独立的功能模块,供其他模块调用,功能模块要有输入、输出,对于方法而言输入就是方法的参数,输出就是方法的返回值。调用者通过参数将需要输入的数据传递给方法,方法通过返回值将输出返回给调用者。
6.5.1 方法定义
1、方法的六个要素:访问修饰符、返回类型、方法名、形参、方法体、返回值
2、方法必须有返回类型(构造方法除外),可以省略访问修饰符 默认default
3、可以有参数,也可以没有参数
6.5.2 方法调用
在一个方法中调用另外一个方法
方法调用有两种情况:
调用相同类中的方法:可以直接调用。(本质上是使用this关键字调用)
调用其他类中的方法:对象.方法名
1、同一个类中的方法相互调用:可以直接调用(本质上是使用this关键字调用)
2、不同类中的方法相互调用:通过对象调用
案例演示:
通过Box类中的calVolume()方法演示方法调用。
6.5.3 参数传递与返回值
1、实参与形参的概念
2、方法调用的执行过程
案例演示:Calculator类
6.5.4 return关键字
(1)return语句用于明确地从一个方法返回。即,return语句导致程序的执行控制转移回到方法的调用者。
(2)如果return之后还有代码,也不会执行。
public static int test1(int a){
if(a>10){
return 1;
}
if(a>20){
return 2;
}
else{
return 3;
}
}
(3)如果方法的返回类型为void,可以使用return跳出方法,但是不能使用return返回数据。
public static void test2(int b){
if(b > 10){
System.out.println(1);
return;
}
if(b > 20){
System.out.println(2);
return;
}
else{
System.out.println(3);
return;
}
}
(4)方法的返回值可以是对象。
6.5.5 参数传递的两种方式
方法调用中发生的数据传送是单向的。即只能把实参的值传送给形参,而不能把形参的值反向地传送给实参。因此在函数调用过程中,形参的值发生改变,而实参中的值不会变化。
两种参数传递方式:
值传递:参数类型为基本类型,参数传递时是将实参的值赋给形参,实参和形参是不同的变量。
引用传递:引用类型(对象)作为参数,参数传递时,实参和形参指向同一个对象。所以在方法中修改形参指向的对象会影响到实参所指向的对象(本来就是同一对象)。
6.6 方法重载
6.6.1 方法重载基础
直接切入主题:
在同一个类中名称相同、形参列表不同的多个方法称为重载方法。
特点:
1、方法名相同
2、形参列表不同:形参的类型、形参的个数,形参的顺序
说明:
方法名+形参列表,共同构成了方法的唯一标识。(方法原型/方法签名)
注意的地方:
1、返回值在区分重载方法时不起作用。访问修饰符也不起作用
2、当调用重载方法时,Java使用实参的类型和/或数量确定实际调用哪个版本。
方法重载案例:
完善Calculator类,添加add()方法,计算两个数的和。
两个参数的类型分别为:int、float、double
6.6.2 重载构造方法
示例1:
Box(double length, double width, double height)
Box(double dim)
Box(Box box)
Box()
特别说明:
在某个构造方法中可以使用this()调用重载的构造方法:
public Box(double dim){
this(dim, dim, dim)
}
示例2:
重载Student类的构造器
Student(String name, int age, double score)
Student(String name, int age)
6.7 static关键字
类:成员变量+成员方法
对于类中的成员变量和成员方法都可以使用static进行修饰。
main()方法是最常见的静态成员的例子。main()方法被声明为静态的,因为需要在创建所有对象之前调用该方法。
6.7.1 静态变量
对于类中的成员变量可以使用static进行修饰,这种变量叫做静态变量。
没有使用static修饰的变量叫做实例变量。
实例变量与静态变量的区别:
(1)是否共享
实例变量:不共享,类的每个对象(实例)都有自己的实例变量
静态变量:共享,类的所有对象都共享同一个变量
(2)访问方式
实例变量:只能通过对象访问
静态变量:类名.变量名(推荐)、对象.变量名(不推荐)
(3)初始化时机
实例变量:创建类的对象时初始化
静态变量:当类被虚拟机加载初始化,创建类的任意对象之前已经初始化。
案例:(演示静态变量的访问方式、不同实例共享相同的值)
6.7.2 静态方法
对于类中的方法也可以使用static进行修饰,这种方法称为静态方法。
没有使用static修饰的方法称为实例方法。
静态方法和实例方法的区别(访问方式):
静态方法:可以通过类名(推荐)和对象(不推荐)
实例方法:只能通过对象访问
静态方法的特点:
不需要创建类的对象就可以直接通过类名调用静态方法,例如Math.random()。
静态的方法有几个限制:
- 只能访问类中的其他静态成员(静态变量、静态方法)
- 不能以任何方式引用this或super关键字
6.7.3 关于static的几点说明
1、static的本质作用是区分成员属于类还是属于实例(对象)。
2、通常把使用static修饰的变量和方法称为类变量和类方法,有时也称为静态变量和静态方法,把不使用static修饰的变量和方法称为实例变量和实例方法。
七、面向对象-封装
封装(英语:Encapsulation)是面向对象的三大特征之一
类 = 属性 + 方法,类是对属性和方法的封装。类封装了类的成员。
1)封装:是指隐藏对象的属性和实际细节,仅对外提供公共的访问方式
2)封装的优点:
-
1. 良好的封装能够减少耦合。
-
2. 类内部的结构可以自由修改。
-
3. 可以对成员变量进行更精确的控制。
-
4. 隐藏信息,实现细节。
3)封装的原则:
- 隐藏内部实现细节:通过将类的属性和方法设置为私有,仅通过公共接口进行访问和操作,从而隐藏类的内部实现细节。
- 提供公共接口:提供公共的方法来操作类的属性,确保外部只能通过这些公共方法访问和修改类的内部状态。
- 单一职责原则:每个类应该只负责一项明确的功能,避免一个类承担过多的责任。
- 安全性:通过封装可以保证数据的安全性和一致性,防止未经授权的访问和修改。
- 简化使用:对外提供简捷的接口,使用者不必了解具体的实现细节,只需通过特定的访问权限使用类的成员。
7.1 Java封装的步骤
通常的做法:
(1)将类的属性的访问权限设置为private,提供访问权限为public的set和get方法。在有些情况下不提供set和get方法,有时只提供其中的一个
(2)提供合理的构造方法,即开发相应参数的构造方法,方便实例化对象。
(3)类的全部或部分方法的访问权限为public,类的公有方法是类对外开放的接口,供外部代码使用类的功能。类的私有方法通常是辅助方法实现部分功能,供类的公有方法调用。
让我们来看一个java封装类的例子:
public class EncapTest{
private String name;
private String idNum;
private int age;
public int getAge(){
return age;
}
public String getName(){
return name;
}
public String getIdNum(){
return idNum;
}
public void setAge( int newAge){
age = newAge;
}
public void setName(String newName){
name = newName;
}
public void setIdNum( String newId){
idNum = newId;
}
}
以上实例中public方法是外部类访问该类成员变量的入口。
通常情况下,这些方法被称为getter和setter方法。
因此,任何要访问类中私有成员变量的类都要通过这些getter和setter方法。
通过如下的例子说明EncapTest类的变量怎样被访问:
public class RunEncap{
public static void main(String args[]){
EncapTest encap = new EncapTest();
encap.setName("James");
encap.setAge(20);
encap.setIdNum("12343ms");
System.out.print("Name : " + encap.getName()+
" Age : "+ encap.getAge());
}
}
7.2 包
(1)包的概念
类通常位于某个包中,包(packages)是多个类的容器。它们用于保持类的名称空间相互隔离,类的全名是“包名.类名”。因此包是一种命名机制。
例如:
Java类库中的类,分别包含在不同的包中:java.lang;java.util。
例如:
String类位于java.lang包中 java.lang.String java.util.Scanner
(2)定义包
package pack1;
包名的命名规范:
1、不能包含特殊符号;2、不能以数字开头;3、通常是小写字母开头
类的全名为:包名.类名。
(3)层次化的包
例如:
package org.xiaoxie.com
package org.xiaoxie
包org.xiaoxie与org.xiaoxie.com是两个不同的包,没有关系。
(4)包与目录结构
位于包中的类,在文件系统中也必须有与包名层次相同的目录结构。
需要指出的是,包名与文件目录结构一致是针对.class文件而言的,对于源代码文件没有这一要求,但是通常也将源代码文件放到与包名一致的目录结构中。并且建议将源文件与类文件分开存放。
class文件的存储位置与包名必须一致:因为执行class文件时,只指定类的全名,然后根据类的全名去查找class文件。
(5)导包
类的全名为:包名.类名。例如在org.xiaoxie包中定义的Student类,其全名为org.xiaoxie.Student。
当在不同的包中使用某个类时,需要使用类的全名,如果包名很长的话,使用类的全名不是很方便,这时可以通过导入包来避免使用类的全名。
导入包的目的:减少输入
导入包的两种方式
import java.util.Scanner;
import java.util.*;
包既是一种命名机制,也是一种可见性控制机制。可以在包中定义该包外部的代码不能访问的类成员。
7.3 访问范围
在定义类时,可以通过访问修饰符控制类中成员的访问范围。
四种访问范围:
本类、本包、本包+不同包中的子类、所有包(全局)
说明:
1、类的全名是:包名.类名,因此包p1中类A的全名是p1.A,包p2中类A的全面是p2.A。
2、假定类SubB是类B的子类
7.4 访问修饰符与访问控制
在定义类时,可以通过访问修饰符控制类中成员的访问范围。
可以通过不同的访问修饰符控制类成员的可见范围,即控制类的成员在多大的范围是可见的。
private | 默认(default) | protected | public | |
本类 | 是 | 是 | 是 | 是 |
本包 | 否 | 是 | 是 | 是 |
本包+子类(不同包) | 否 | 否 | 是 | 是 |
全局(所有包) | 否 | 否 | 否 | 是 |
访问范围: 本类 < 本包 < 本包+不同包的子类 < 全局(所有包)
访问修饰符:private < 缺省(default) < protected < public
两种用法:
1、指定类成员的访问范围
2、指定整个类的访问范围(本包、全局)
八、向对象-继承
8.1 继承基础
继承是面向对象的基本特征之一。
8.1.1 继承的概念
使用继承可以为一系列相关对象定义共同特征的一般类,然后其他类(更特殊的类)可以继承这个一般类,每个进行继承的类都可以添加其特有的内容。
被继承的类称为超类(super class)/父类,继承的类称为派生类/子类(subclass)。
一旦创建了一个父类,就可以使用该超类创建任意数量的更特殊的子类。
继承类型:
需要注意的是 Java 不支持多继承,但支持多重继承。
8.1.2 继承的语法
继承使用关键字extends(扩展)实现。
public class A extends SuperA{
//扩展
}
优点:
子类可以从父类继承属性和部分方法,自己再增加新的属性和方法。通过继承子类可以重用父类的方法和属性,减少代码重复编写,便于维护、代码扩展。
示例:
// 父类
class ParentClass {
int parentVariable;
void parentMethod() {
System.out.println("This is a parent method.");
}
}
// 子类
class ChildClass extends ParentClass {
int childVariable;
void childMethod() {
System.out.println("This is a child method.");
}
}
// 测试类
public class Main {
public static void main(String[] args) {
ChildClass child = new ChildClass();
child.parentMethod(); // 继承自父类
child.childMethod(); // 子类自己的方法
}
}
以上代码运行输出结果为:
This is a parent method.
This is a child method.
ChildClass通过extends关键字继承了ParentClass。因此,ChildClass拥有ParentClass中的parentMethod()方法和parentVariable变量。同时,ChildClass还可以有它自己的方法和变量,例如childMethod()和childVariable。
8.1.3 成员继承的规则
(1)子类不能从父类继承构造方法。
(2)属性的继承:理论上子类会继承父类的全部成员变量(属性),但是子类不能访问父类的私有成员变量,如果子类与父类在不同包中,子类也不能访问父类中具有默认访问权限的成员变量。
(3)方法的继承:根据访问权限,在子类中能够访问的那些方法都可以继承。
- 父类中访问权限为public和protected的方法,子类可以继承。
- 父类中访问权限为private的方法,不能继承
- 父类中访问权限为默认的方法,如果子类和父类同包,可以继承,不同包不能继承。
8.1.4 子类的构造方法
(1)构造方法的调用顺序
在类继承层次中按照继承的顺序从父类到子类调用构造方法
(2)在子类的构造方法中,一定会先调用父类的构造方法。super();
(3)子类的每个构造方法都会隐式的调用父类的无参数构造方法,如果想调用父类的其他构造方法,必须使用super(参数列表)来显式调用。通常需要提供无参数构造方法 如果子类的构造犯法中IE 你很漂亮 但是我不喜欢
说明:编写类时,通常需要提供无参数构造方法。
(4)如果父类没有无参的构造方法,或者想调用父类的有参构造方法,则在子类的构造方法中必须显式使用super(xxx)调用父类有参构造方法。这时super(xxx)必须是子类中的第一条语句。
(5)通常的做法:
在父类中定义有参数的构造方法,负责初始化父类的成员变量。
在子类的构造方法中,先调用父类的构造方法完成从父类继承来的那些成员变量,然后初始化子类中特有的成员变量。
通常为每个类添加无参的构造方法。
注意:
如果父类中定义了一个有参数的构造方法,系统就不会再为父类提供默认的构造方法。这时,在子类的构造方法中,必须使用super(xxx)显示调用父类的有参构造方法。
8.1.5 创建多级继承层次
//多继承(类不支持多继承)
public Person( ){
}
public Student( ) extends Person{
}
public BawieStudent( ) extends Student{
}
注意:
多级继承和多继承。多继承是指一个类有多个父类,Java不支持多继承。
一个类只能有一个父类
一个父类可以有多个子类
8.1.6 超类引用变量可以引用子类对象
Animal ani = null; //声明超类的变量
Dog dog = new Dog(); //创建子类对象
ani = dog; //将子类对象赋给父类的引用
ani = new Dog(); //创建一个新的子类对象,赋给父类引用变量
可以将子类的对象赋给父类的引用变量,但是这时使用父类的引用变量只能访问父类中定义的那些成员变量。换句话说,可以访问哪些成员是由引用变量的类型决定的,而不是由所引用的对象类型决定的。
8.1.7 对象的转型
Animal a = new Dog(); //小转大 可以自动进行
a.getLayal(); //语法错误,这时通过a只能使用父类中定义的成员变量。编译时就确定
a.eat(); //
Animal a = new Dog();
Dog d = (Dog)a; //正确 大转小, 需要强制转换
Dog d = new Animal(); //错误
Dog d = (Dog)new Animal(); //语法没错,可以编译,但运行时会抛出异常
Cat c = new Cat();
d = (Dog)c; //不正确
1、子类对象 赋给 父类引用 可以,自动转换
2、父类引用 赋给 子类引用 需要强转,前提:父类引用确实指向了正确的子类对象
8.1.8 方法重写介绍
当子类从父类中继承来的方法不能满足需要时,子类可以重写该方法,重写方法要求方法名与参数列表都相同。
8.1.9 变量隐藏
当在子类中定义的成员变量与父类中的变量具有相同名称时,子类中定义的变量会隐藏父类中定义的变量。
class Father{
double money = 100_000;
public void showMoney(){
System.out.println(money);
}
}
class Son extends Father{
double money = 10_000;
public void showMoney(){
System.out.println(super.money);
System.out.println(money);
}
}
8.2 super关键字
关键字super用于调用/访问从父类中继承来的实例变量和方法。
super有两种用法。第一种用于调用超类的构造方法。第二种用于访问超类中被子类的某个成员隐藏的成员(变量和方法)。
8.2.1 使用super()调用父类的构造方法
- 在子类中使用super()调用父类的构造方法,必须是第一条语句
- 在本类中可以使用this()调用重载的构造方法,也必须是第一条语句
- 在子类的构造方法中this()和super()不能同时使用
8.2.2 使用super访问父类中被子类隐藏的成员
父类的属性被子类继承,如果子类又添加了名称相同的属性,则子类有两个相同名称的属性,如果父类型对象调用属性,就是父类的,如果是子类型对象调用就是子类的属性。
当子类需要引用它的直接超类时,就可以使用关键字super。
this的两个作用:
第一个作用调用重载的构造方法
第二个作用:当局部变量和成员变量的名称重名时,进行区分
8.3 Object
8.3.1 Object类介绍
所有其他类都是Object的子类。也就是说,Object是所有其他类的超类。这意味着Object类型的引用变量可以引用任何其他类的对象。此外,因为数组也是作为类实现的,所以Object类型的变量也可以引用任何数组。
Object类定义了下面列出的方法,这意味着所有对象都可以使用这些方法。
方 法 | 用 途 |
Object clone() | 创建一个和将要复制的对象完全相同的新对象。 |
boolean equals(Object object) | 确定一个对象是否和另外一个对象相等 |
void finalize() | 在回收不再使用的对象前调用 |
Class<?> getClass() | 在运行时获取对象的类 |
int hashCode() | 返回与调用对象相关联的散列值(哈希码) |
void notify() | 恢复执行在调用对象上等待的某个线程 |
void notifyAll() | 恢复执行在调用对象上等待的所有线程 |
String toString() | 返回一个描述对象的字符串 |
void wait() void wait(long milliseconds) void wait (ling milliseconds, int nanoseconds) | 等待另一个线程的执行 |
8.3.2 equals()方法与对象相等性比较
Object类中的equals()方法实现等价于“==”运算符
对于引用类型“==”的含义是:两个引用是否是指向同一个对象
如果希望使用不同的规则,就需要重写equals()方法。
例如:
String类对equals()方法进行了重写,重写后的equals方法比较两个字符串的内容是否相同。
String s1 = new String( “abc”);
String s2 = new String(“abc”);
对于引用类型都比较:
if(s1 == s2) //比较是s1与s2是否指向同一个对象
s1.equals(s2);
因为不同的类可以重写equals方法,所以euqals方法比较的行为可能不同
equals方法最初是Object类中定义,在该类中的默认实现等价于“==”
String类对equals进行了重写,比较两个字符串中内容是否一致
案例:
重写Box类的equals()方法
8.3.3 重写toString()方法
直接打印对象时,默认调用对象的toString()方法
Object类的toString方法输出格式:
getClass().getName() + '@' + Integer.toHexString(hashCode())
自己定义的类可以重写toString()
8.4 final
8.4.1 final修饰变量、方法、类
- 如果final修饰变量,变量就是常量,常量不可修改,定义时必须初始化
- 如果final修饰方法,方法就不能被子类重写
- 如果final修饰类,类就不能再被扩展,不能再有子类。Java类库中的String、Math就是final类。
8.4.2 引用类型的常量
如果常量是基本数据类型,不可以再修改。
如果常量是引用类型,不能再将其他对象赋给该引用,但可以使用该引用改变对象内部的属性。
例如:
final Student s = new Student(“zhangsan”,20);
s = new Student(“李四”,20); //错误
s.setName(“李四”); //正确
标签:Java,详细,构造方法,int,收藏,对象,数组,方法,变量
From: https://blog.csdn.net/qq_41320700/article/details/143376909