第5章_方法的定义/调用/重载

方法的定义和调用

【1】什么是方法?
方法(method)就是一段用来完成特定功能的代码片段,类似于其它语言的函数(function)。
方法用于定义该类或该类的实例的行为特征和功能实现。 方法是类和对象行为特征的抽象。方法很类似于面向过程中的函数。面向过程中,函数是最基本单位,整个程序由一个个函数调用组成。面向对象中,整个程序的基本单位是类,方法是从属于类和对象的。

【2】方法声明格式:
[修饰符1 修饰符2 …] 返回值类型 方法名(形式参数列表){
Java语句;… … …
}

【3】方法的调用方式:
对象名.方法名(实参列表)
【4】方法的详细说明
形式参数:在方法声明时用于接收外界传入的数据。
实参:调用方法时实际传给方法的数据。
返回值:方法在执行完毕后返还给调用它的环境的数据。
返回值类型:事先约定的返回值的数据类型,如无返回值,必须显示指定为为void。

【5】代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public class TestMethod01{

//方法的定义:(写方法)
public static int add(int num1,int num2){
int sum = 0;
sum += num1;
sum += num2;
return sum;//将返回值返回到方法的调用处
}

public static void main(String[] args){
//10+20:
//方法的调用:(用方法)
int num = add(10,20);
System.out.println(num);
/*
int num1 = 10;
int num2 = 20;
int sum = 0;
sum += num1;
sum += num2;
System.out.println(sum);
*/
//30+90:
int sum = add(30,90);
System.out.println(sum);
/*
int num3 = 30;
int num4 = 90;
int sum1 = 0 ;
sum1 += num3;
sum1 += num4;
System.out.println(sum1);
*/
//50+48:
System.out.println(add(50,48));

}


}

【6】总结:

1.方法是:对特定的功能进行提取,形成一个代码片段,这个代码片段就是我们所说的方法
2.方法和方法是并列的关系,所以我们定义的方法不能写到main方法中
3.方法的定义–》格式:
修饰符 方法返回值类型 方法名(形参列表){
方法体;
return 方法返回值;
}

4.方法的作用:提高代码的复用性
5.总结方法定义的格式:

  1. 修饰符: 暂时使用public static —>面向对象一章讲解
  2. 方法返回值类型 : 方法的返回值对应的数据类型
    数据类型: 可以是基本数据类型(byte,short,int,long,float,double,char,boolean) 也可以是引用数据类型
  3. 方法名 :见名知意,首字母小写,其余遵循驼峰命名, eg: addNum ,一般尽量使用英文来命名
  4. 形参列表 :方法定义的时候需要的形式参数 : int num1, int num2 –>相当于告诉方法的调用者:需要传入几个参数,需要传入的参数的类型
    实际参数:方法调用的时候传入的具体的参数: 10,20 –>根据形式参数的需要传入的

5)方法体:具体的业务逻辑代码
6. return 方法返回值;
方法如果有返回值的话: return+方法返回值,将返回值返回到方法的调用处
方法没有返回值的话:return可以省略不写了,并且方法的返回值类型为:void

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class TestMethod02{

public static void add(int num1,int num2){
int sum = 0;
sum += num1;
sum += num2;
System.out.println(sum);
//return;
}

public static void main(String[] args){
//10+20:
//方法的调用:(用方法)
add(10,20);
//30+90:
add(30,90);
//50+48:
//System.out.println(add(50,48));//报错:TestMethod02.java:22: 错误: 此处不允许使用 '空' 类型

}
}

什么时候有返回值,什么时候没有返回值? 看心情–》看需求

6.方法的定义需要注意什么?
1)形参列表要怎么写:定义几个参数,分别是什么类型的 —》不确定因素我们会当做方法的形参
2) 方法到底是否需要返回值 ,如果需要的话,返回值的类型是什么

7.方法的调用需要注意什么?
1)实际参数要怎么传入:传入几个参数,传入什么类型的
2) 方法是否有返回值需要接受

练习

【1】基本功能:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import java.util.Scanner;
public class TestMethod03{
public static void main(String[] args){
//功能:我心里有一个数,你来猜,看是否猜对
//1.你猜一个数
Scanner sc = new Scanner(System.in);
System.out.println("请你猜一个数:");
int yourGuessNum = sc.nextInt();
//2.我心里有一个数
int myHeartNum = 5;
//3.将两个数比对:
System.out.println(yourGuessNum==myHeartNum?"猜对了":"猜错了");
}
}

对猜数功能提取为一个方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import java.util.Scanner;
public class TestMethod03{
public static void main(String[] args){
//功能:我心里有一个数,你来猜,看是否猜对
//1.你猜一个数
Scanner sc = new Scanner(System.in);
System.out.println("请你猜一个数:");
int yourGuessNum = sc.nextInt();

//调用猜数的方法:
guessNum(yourGuessNum);
}

//方法的定义:功能:实现猜数功能:
public static void guessNum(int yourNum){
//我心里有一个数(1-6)
int myHeartNum = (int)(Math.random()*6)+1;
//将两个数比对:
System.out.println(yourNum==myHeartNum?"猜对了":"猜错了");
}
}

面试题:两个数交换是否成功

【1】面试题:请问下面代码中两个数是否交换成功:
public class TestM{
public static void main(String[] args){
int a=10;
int b=20;
System.out.println(“输出交换前的两个数:”+a+”—“+b);
changeNum(a,b);
System.out.println(“输出交换后的两个数:”+a+”—“+b);
}
public static void changeNum(int num1,int num2){
int t;
t=num1;
num1=num2;
num2=t;
}
}

结果:没有交换成功:

image-20210921091627093

原因:

image-20210921091642355

方法的重载

【1】什么是方法的重载:
方法的重载是指一个类中可以定义多个方法名相同,但参数不同的方法。 调用时,会根据不同的参数自动匹配对应的方法。

注意本质:重载的方法,实际是完全不同的方法,只是名称相同而已!

【2】构成方法重载的条件:
❀不同的含义:形参类型、形参个数、形参顺序不同
❀ 只有返回值不同不构成方法的重载
如:int a(String str){}与 void a(String str){}不构成方法重载
❀ 只有形参的名称不同,不构成方法的重载
如:int a(String str){}与int a(String s){}不构成方法重载

【3】代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public class TestMethod05{
public static void main(String[] args){
//10+20:
int sum = add(10,20);
System.out.println(sum);

//20+40+80:
//System.out.println(add(add(20,40),80));
System.out.println(add(20,40,80));
//30+60+90+120:
//System.out.println(add(add(30,60),add(90,120)));
System.out.println(add(30,60,90,120));
//9.8+4.7:
//System.out.println(add(9.8,4.7));
System.out.println(add(9.8,4.7));
}

//定义一个方法:两个数相加:两个int类型数据相加
public static int add(int num1,int num2){
return num1+num2;
}

//定义一个方法:三个数相加:
public static int add(int num1,int num2,int num3){
return num1+num2+num3;
}

//定义一个方法:四个数相加:
public static int add(int num1,int num2,int num3,int num4){
return num1+num2+num3+num4;
}
//定义一个方法:两个数相加:两个double类型的数据相加
public static double add(double num1,double num2){
return num1+num2;
}


}

总结:
1.方法的重载:在同一个类中,方法名相同,形参列表不同的多个方法,构成了方法的重载。
2.方法的重载只跟:方法名和形参列表有关,与修饰符,返回值类型无关。
3.注意:形参列表不同指的是什么?
(1)个数不同
add() add(int num1) add(int num1,int num2)
(2)顺序不同
add(int num1,double num2) add(double num1,int num2)
(3)类型不同
add(int num1) add(double num1)

4.请问下面的方法是否构成了方法的重载?
(1)add(int a) 和 add(int b) —>不构成,相当于方法的重复定义
(2)public static int add(int a) 和 public static void add(int b) —>不构成

第6章_数组

数组的引入

【1】习题引入:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import java.util.Scanner;
public class TestArray01{
public static void main(String[] args){
//功能:键盘录入十个学生的成绩,求和,求平均数:
//定义一个求和的变量:
int sum = 0;
Scanner sc = new Scanner(System.in);
for(int i=1;i<=10;i++){//i:控制循环次数
System.out.print("请录入第"+i+"个学生的成绩:");
int score = sc.nextInt();
sum += score;
}

System.out.println("十个学生的成绩之和为:"+sum);
System.out.println("十个学生的成绩平均数为:"+sum/10);

//本题的缺点:
//求第6个学生的成绩:?????---》不能

}
}

缺点:就是不能求每个学生的成绩具体是多少

解决:将成绩进行存储 —-》 引入 : 数组

感受到数组的作用:数组用来存储数据的,在程序设计中,为了处理方便,数组用来将相同类型的若干数据组织起来。
这个若干数据的集合我们称之为数组。

数组的学习

【1】数组的定义
数组是相同类型数据的有序集合。数组描述的是相同类型的若干个数据,按照一定的先后次序排列组合而成。其中,每一个数据称作一个元素,每个元素可以通过一个索引(下标)来访问它们。
数组的四个基本特点:
1.长度是确定的。数组一旦被创建,它的大小就是不可以改变的。
2.其元素的类型必须是相同类型,不允许出现混合类型。
3.数组类型可以是任何数据类型,包括基本类型和引用类型。
4.数组有索引的:索引从0开始,到 数组.length-1 结束
5.数组变量属于引用类型,数组也是对象。
==PS:数组变量属于引用类型,数组也是对象,数组中的每个元素相当于该对象的成员变量。数组本身就是对象,Java中对象是在堆中的,因此数组无论保存原始类型还是其他对象类型,数组对象本身是在堆中存储的。==

【2】数组的学习:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public class TestArray02{
public static void main(String[] args){
//数组的作用:用来存储相同类型的数据
//以int类型数据为案例:数组用来存储int类型数据
//1.声明(定义数组)
int[] arr; //定义一个int类型的数组,名字叫arr
//int arr2[];
//如果数组只声明,没有后续操作,那么这个数组相当于没定义
//int[] arr3 = null;//空 辨别:数组赋值为null和什么都没有赋值 不一样的效果

//2.创建
arr = new int[4];//给数组开辟了一个长度为4的空间
//编译期声明和创建会被合为一句话: int[] arr = new int[4];

//3.赋值
arr[0] = 12;
arr[3] = 47;
arr[2] = 98;
arr[1] = 56;
arr[2] = 66;
/*
arr[4] = 93;
出现异常:Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 4
Array 数组
Index 索引
OutOf 超出
Bounds 界限
Exception 异常
---》数组索引越界异常
*/

//4.使用
System.out.println(arr[2]);
System.out.println(arr[0]+100);
//通过数组一个属性来获取 length 长度
System.out.println("数组的长度是:"+arr.length);
}
}

内存分析

image-20210921092020009

完善引入的习题_数组的遍历

【1】代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
import java.util.Scanner;
public class TestArray03{
public static void main(String[] args){
//功能:键盘录入十个学生的成绩,求和,求平均数:
//定义一个int类型的数组,长度为10 :
int[] scores = new int[10];
//定义一个求和的变量:
int sum = 0;
Scanner sc = new Scanner(System.in);
for(int i=1;i<=10;i++){//i:控制循环次数
System.out.print("请录入第"+i+"个学生的成绩:");
int score = sc.nextInt();
scores[i-1] = score;
sum += score;
}

System.out.println("十个学生的成绩之和为:"+sum);
System.out.println("十个学生的成绩平均数为:"+sum/10);


//求第6个学生的成绩:
//System.out.println(scores[5]);
/*
System.out.println(scores[0]);
System.out.println(scores[1]);
System.out.println(scores[2]);
System.out.println(scores[3]);
//....
System.out.println(scores[9]);
*/
//将数组中的每个元素进行查看--》数组的遍历:
//方式1:普通for循环---》正向遍历:
for(int i=0;i<=9;i++){
System.out.println("第"+(i+1)+"个学生的成绩为:"+scores[i]);
}

//方式2:增强for循环:
//对scores数组进行遍历,遍历出来每个元素都用int类型的num接收:
int count = 0;
for(int num:scores){
count++;
//每次都将num在控制台输出
System.out.println("第"+count+"个学生的成绩为:"+num);
}

/*
增强for循环:
优点:代码简单
缺点:单纯的增强for循环不能涉及跟索引相关的操作
*/

//方式3:利用普通for循环: 逆向遍历:
for(int i=9;i>=0;i--){
System.out.println("第"+(i+1)+"个学生的成绩为:"+scores[i]);
}

}
}

【2】用IDEA验证数组的确将数据进行存储了:

image-20210921092140218

数组的三种初始化方式

数组的初始化方式总共有三种:静态初始化、动态初始化、默认初始化。

静态初始化
除了用new关键字来产生数组以外,还可以直接在定义数组的同时就为数组元素分配空间并赋值。

eg:
int[] arr = {12,23,45};
int[] arr = new int[]{12,23,45};
注意:
1.new int[3]{12,23,45};–>错误
2.int[] arr ;
arr = {12,23,45}; —>错误

动态初始化
数组定义与为数组元素分配空间并赋值的操作分开进行。

eg:
int[] arr ;
arr = new int[3]
arr[0] = 12;
arr[1] = 23;
arr[2] = 45;

默认初始化
数组是引用类型,它的元素相当于类的实例变量,因此数组一经分配空间,其中的每个元素也被按照实例变量同样的方式被隐式初始化。

int[] arr = new int[3]; —> 数组有默认的初始化值

image-20210921092353758

数组的应用题

最值问题

【1】实现一个功能:给定一个数组int[] arr = {12,3,7,4,8,125,9,45}; ,求出数组中最大的数。
思路图:

image-20210921092442152

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class TestArray04{
public static void main(String[] args){
//实现一个功能:给定一个数组int[] arr = {12,3,7,4,8,125,9,45}; ,求出数组中最大的数。
//1.给定一个数组
int[] arr = {12,3,7,4,8,125,9,45,666,36};

//2.求出数组中的最大值:
//先找一个数上擂台,假定认为这个数最大:
int maxNum = arr[0];
for(int i=0;i<arr.length;i++){
if(arr[i]>maxNum){
maxNum = arr[i];
}
}
System.out.println("当前数组中最大的数为:"+maxNum);

}
}

【2】将求最大值的方法提取出来:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class TestArray04{
public static void main(String[] args){
//实现一个功能:给定一个数组int[] arr = {12,3,7,4,8,125,9,45}; ,求出数组中最大的数。
//1.给定一个数组
int[] arr = {12,3,7,4,8,725,9,45,666,36};

//2.求出数组中的最大值:
//调用方法:
int num = getMaxNum(arr);
System.out.println("当前数组中最大的数为:"+num);
}

/*
想提取一个方法:求数组中的最大值
求哪个数组中的最大值 ---》不确定因素:哪个数组 (形参)---》返回值:最大值
*/
public static int getMaxNum(int[] arr){
//先找一个数上擂台,假定认为这个数最大:
int maxNum = arr[0];
for(int i=0;i<arr.length;i++){
if(arr[i]>maxNum){
maxNum = arr[i];
}
}
return maxNum;

}
}

【3】画内存:
==方法的实参传递给形参的时候一定要注意:一切都是值传递:==
==如果是基本数据类型,那么传递的就是字面值==
==如果是引用数据类型,那么传递的就是地址值==

image-20210921092552492

查询问题

【1】查询指定位置的元素

1
2
3
4
5
6
7
8
9
public class TestArray05{
public static void main(String[] args){
//查询指定位置的元素
//给定一个数组:
int[] arr = {12,34,56,7,3,10};
//查找索引为2的位置上对应的元素是什么?
System.out.println(arr[2]);
}
}

上面代码体现了数组的一个优点:
在按照位置查询的时候,直接一步到位,效率非常高

【2】查询指定元素的位置–》找出元素对应的索引

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class TestArray06{
public static void main(String[] args){
//查询指定元素的位置--》找出元素对应的索引
//给定一个数组:
int[] arr = {12,34,56,7,3,56};
// 0 1 2 3 4 5

//功能:查询元素888对应的索引:
int index = -1; //这个初始值只要不是数组的索引即可
for(int i=0;i<arr.length;i++){
if(arr[i]==12){
index = i;//只要找到了元素,那么index就变成为i
break;//只要找到这个元素,循环就停止
}
}
if(index!=-1){
System.out.println("元素对应的索引:"+index);
}else{//index==-1
System.out.println("查无次数!");
}
}
}

【3】将查指定元素对应的索引的功能提取为方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public class TestArray06{
public static void main(String[] args){
//查询指定元素的位置--》找出元素对应的索引
//给定一个数组:
int[] arr = {12,34,56,7,3,56};
// 0 1 2 3 4 5

//功能:查询元素888对应的索引:
//调用方法:
int index = getIndex(arr,999);
//后续对index的值进行判断:
if(index!=-1){
System.out.println("元素对应的索引:"+index);
}else{//index==-1
System.out.println("查无次数!");
}
}

/*
定义一个方法:查询数组中指定的元素对应的索引:
不确定因素:哪个数组,哪个指定元素 (形参)
返回值:索引

*/
public static int getIndex(int[] arr,int ele){
int index = -1; //这个初始值只要不是数组的索引即可
for(int i=0;i<arr.length;i++){
if(arr[i]==ele){
index = i;//只要找到了元素,那么index就变成为i
break;//只要找到这个元素,循环就停止
}
}
return index;
}
}

添加元素

【1】实现一个功能:
添加逻辑:

image-20210921092736210

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
public class TestArray07{
public static void main(String[] args){
//功能:给定一个数组,在数组下标为2的位置上添加一个元素91

//1.给定一个数组:
int[] arr = {12,34,56,7,3,10,55,66,77,88,999,89};
// 0 1 2 3 4 5
//2.输出增加元素前的数组:
System.out.print("增加元素前的数组:");
for(int i=0;i<arr.length;i++){
if(i!=arr.length-1){
System.out.print(arr[i]+",");
}else{//i==arr.length-1 最后一个元素不用加,
System.out.print(arr[i]);
}
}

//3.增加元素
/*
arr[5] = arr[4];
arr[4] = arr[3];
arr[3] = arr[2];
*/
int index = 1;//在这个指定位置添加 元素
for(int i=arr.length-1;i>=(index+1);i--){
arr[i] = arr[i-1];
}
arr[index] = 666;


//4.输出增加元素后的数组:
System.out.print("\n增加元素后的数组:");
for(int i=0;i<arr.length;i++){
if(i!=arr.length-1){
System.out.print(arr[i]+",");
}else{//i==arr.length-1 最后一个元素不用加,
System.out.print(arr[i]);
}
}

}
}

【2】将添加功能提取为一个 方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
import java.util.Scanner;
public class TestArray07{
public static void main(String[] args){
//功能:给定一个数组,在数组下标为2的位置上添加一个元素91

//1.给定一个数组:
int[] arr = {12,34,56,7,3,10,55,66,77,88,999,89};
// 0 1 2 3 4 5
//2.输出增加元素前的数组:
/*
System.out.print("增加元素前的数组:");
for(int i=0;i<arr.length;i++){
if(i!=arr.length-1){
System.out.print(arr[i]+",");
}else{//i==arr.length-1 最后一个元素不用加,
System.out.print(arr[i]);
}
}
*/

//从键盘接收数据:
Scanner sc = new Scanner(System.in);
System.out.println("请录入你要添加元素的指定下标:");
int index = sc.nextInt();
System.out.println("请录入你要添加的元素:");
int ele = sc.nextInt();

//3.增加元素
//调用方法:
insertEle(arr,index,ele);



//4.输出增加元素后的数组:
System.out.print("\n增加元素后的数组:");
for(int i=0;i<arr.length;i++){
if(i!=arr.length-1){
System.out.print(arr[i]+",");
}else{//i==arr.length-1 最后一个元素不用加,
System.out.print(arr[i]);
}
}

}


/*
提取一个添加元素的方法:
在数组的指定位置上添加一个指定的元素。
在哪个数组的哪个位置添加哪个元素!
不确定因素:形参:哪个数组,哪个位置,哪个元素
返回值:无

*/
public static void insertEle(int[] arr,int index,int ele){
for(int i=arr.length-1;i>=(index+1);i--){
arr[i] = arr[i-1];
}
arr[index] = ele;
}
}

删除元素

【1】实现一个功能:删除指定位置上的元素
逻辑:

image-20210921092826831

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import java.util.Arrays;
public class TestArray08{
public static void main(String[] args){
//功能:给定一个数组,删除下标为2元素

//1.给定一个数组:
int[] arr = {12,34,56,7,3,10,34,45,56,7,666};
// 0 1 2 3 4 5
//2.输出删除前的数组:
System.out.println("删除元素前的数组:"+Arrays.toString(arr));

//3.删除
/*
arr[2] = arr[3];
arr[3] = arr[4];
arr[4] = arr[5];
*/
int index = 0;
for(int i=index;i<=arr.length-2;i++){
arr[i] = arr[i+1];
}
arr[arr.length-1] = 0;

//4.输出删除后的数组:
System.out.println("删除元素后的数组:"+Arrays.toString(arr));
}
}

【2】实现一个功能:删除指定元素

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import java.util.Arrays;
public class TestArray09{
public static void main(String[] args){
//功能:给定一个数组,删除元素3:

//1.给定一个数组:
int[] arr = {12,34,56,7,3,10,34,45,56,7,666};

//2.输出删除前的数组:
System.out.println("删除元素前的数组:"+Arrays.toString(arr));


//找到要删除的元素对应的索引即可:
int index = -1 ;
for(int i=0;i<arr.length;i++){
if(arr[i]==1200){
index = i;
break;
}
}

//3.删除

if(index!=-1){
for(int i=index;i<=arr.length-2;i++){
arr[i] = arr[i+1];
}
arr[arr.length-1] = 0;
}else{//index==-1
System.out.println("根本没有你要删除的元素!");
}


//4.输出删除后的数组:
System.out.println("删除元素后的数组:"+Arrays.toString(arr));
}
}

详述main方法

【1】main方法:程序的入口,在同一个类中,如果有多个方法,那么虚拟机就会识别main方法,从这个方法作为程序的入口
【2】main方法格式严格要求:
public static void main(String[] args){}

public static —>修饰符 ,暂时用这个 –>面向对象一章
void —>代表方法没有返回值 对应的类型void
main —>见名知意名字
String[] args —>形参 —》不确定因素

【3】问题:程序中是否可以有其他的方法也叫main方法?
可以,构成了方法的重载。

1
2
3
4
5
6
7
8
public class TestArray10{
public static void main(String[] args){

}
public static void main(String str){

}
}

【4】形参为String[] 那么实参到底是什么?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class TestArray10{
public static void main(String[] args){
//从侧面验证:
//int[] arr1; //如果对数组只声明,没有后续操作,那么相当于 白定义了。
//int[] arr2 = null;
//System.out.println(arr2.length);//Exception in thread "main" java.lang.NullPointerException
//int[] arr3 = new int[0];
//System.out.println(arr3.length);
//int[] arr4 = new int[4];
//System.out.println(arr4.length);

//System.out.println(args.length);//0
//从这个结果证明,参数是String[],实参是 new String[0]
//默认情况下,虚拟机在调用main方法的时候就是传入了一个长度为0的数组

System.out.println(args.length);
for(String str:args){
System.out.println(str);
}
}
}

手动传入实参:
有特殊符号的时候可以加上“”

image-20210921093024714

没有特殊符号用空格隔开即可:

image-20210921093035609

可变参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class TestArray12{
/*
1.可变参数:作用提供了一个方法,参数的个数是可变的 ,解决了部分方法的重载问题
int...num
double...num
boolean...num


2.可变参数在JDK1.5之后加入的新特性
3.方法的内部对可变参数的处理跟数组是一样
4.可变参数和其他数据一起作为形参的时候,可变参数一定要放在最后
5.我们自己在写代码的时候,建议不要使用可变参数。
*/
public static void main(String[] args){
//method01(10);
//method01();
//method01(20,30,40);
method01(30,40,50,60,70);
//method01(new int[]{11,22,33,44});
}
public static void method01(int num2,int...num){
System.out.println("-----1");
for(int i:num){
System.out.print(i+"\t");
}
System.out.println();

System.out.println(num2);
}
}

Arrays工具类

为了方便我们对数组进行操作,系统提供一个类Arrays,我们将它当做工具类来使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import java.util.Arrays;
public class TestArray13{
public static void main(String[] args){
//给定一个数组:
int[] arr = {1,3,7,2,4,8};
//toString:对数组进行遍历查看的,返回的是一个字符串,这个字符串比较好看
System.out.println(Arrays.toString(arr));

//binarySearch:二分法查找:找出指定数组中的指定元素对应的索引:
//这个方法的使用前提:一定要查看的是一个有序的数组:
//sort:排序 -->升序
Arrays.sort(arr);
System.out.println(Arrays.toString(arr));
System.out.println(Arrays.binarySearch(arr,4));

int[] arr2 = {1,3,7,2,4,8};
//copyOf:完成数组的复制:
int[] newArr = Arrays.copyOf(arr2,4);
System.out.println(Arrays.toString(newArr));

//copyOfRange:区间复制:
int[] newArr2 = Arrays.copyOfRange(arr2,1,4);//[1,4)-->1,2,3位置
System.out.println(Arrays.toString(newArr2));

//equals:比较两个数组的值是否一样:
int[] arr3 = {1,3,7,2,4,8};
int[] arr4 = {1,3,7,2,4,8};
System.out.println(Arrays.equals(arr3,arr4));//true
System.out.println(arr3==arr4);//false ==比较左右两侧的值是否相等,比较的是左右的地址值,返回结果一定是false

//fill:数组的填充:
int[] arr5 = {1,3,7,2,4,8};
Arrays.fill(arr5,10);
System.out.println(Arrays.toString(arr5));
}
}

数组的复制操作

image-20210921093208578

image-20210921093217873

原理:

image-20210921093229921

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import java.util.Arrays;
public class TestArray14{
public static void main(String[] args){
//给一个源数组:
int[] srcArr = {11,22,33,44,55,66,77,88};
//给一个目标数组:
int[] destArr = new int[10];

//复制:
System.arraycopy(srcArr,1,destArr,3,3);
//遍历查看目标数组:
System.out.println(Arrays.toString(destArr));
}

}

结果:

image-20210921093253540

二维数组

【1】引入:本质上全部都是一维数组:

image-20210921093329868

【2】基本代码

1
2
3
4
5
6
7
8
9
10
11
12
13
public class TestArray15{
public static void main(String[] args){
//定义一个二维数组:
int[][] arr = new int[3][];//本质上定义了一个一维数组,长度为3

int[] a1 = {1,2,3};
arr[0] = a1;

arr[1] = new int[]{4,5,6,7};

arr[2] = new int[]{9,10};
}
}

对应内存:

image-20210921093402636

【3】四种遍历方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
public class TestArray15{
public static void main(String[] args){
//定义一个二维数组:
int[][] arr = new int[3][];//本质上定义了一个一维数组,长度为3

int[] a1 = {1,2,3};
arr[0] = a1;

arr[1] = new int[]{4,5,6,7};

arr[2] = new int[]{9,10};

//读取6这个元素:
//System.out.println(arr[1][2]);

//对二维数组遍历:
//方式1:外层普通for循环+内层普通for循环:
for(int i=0;i<arr.length;i++){
for(int j=0;j<arr[i].length;j++){
System.out.print(arr[i][j]+"\t");
}
System.out.println();
}

//方式2:外层普通for循环+内层增强for循环:
for(int i=0;i<arr.length;i++){
for(int num:arr[i]){
System.out.print(num+"\t");
}
System.out.println();
}

//方式3:外层增强for循环+内层增强for循环:
for(int[] a:arr){
for(int num:a){
System.out.print(num+"\t");
}
System.out.println();
}

//方式4:外层增强for循环+内层普通for循环:
for(int[] a:arr){
for(int i=0;i<a.length;i++){
System.out.print(a[i]+"\t");
}
System.out.println();
}
}
}

二维数组的初始化方式

数组的初始化方式总共有三种:静态初始化、动态初始化、默认初始化。

静态初始化
除了用new关键字来产生数组以外,还可以直接在定义数组的同时就为数组元素分配空间并赋值。

eg:

1
2
int[][] arr = {{1,2},{4,5,6},{4,5,6,7,8,9,9}};
int[][] arr =new int[][] {{1,2},{4,5,6},{4,5,6,7,8,9,9}};

动态初始化
数组定义与为数组元素分配空间并赋值的操作分开进行。
eg:
int[][] arr = new int[3][]; //本质上定义了一维数组长度为3,每个“格子”中放入的是一个数组
arr[0] = new int[]{1,2};
arr[1] = new int[]{3,4,5,6};
arr[2] = new int[]{34,45,56};

eg:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
int[][] arr = new int[3][2]; 
public class TestArray16{
public static void main(String[] args){
int[][] arr = new int[3][2];
//本质上:定义一维数组,长度为3,每个数组“格子”中,有一个默认的长度为2的数组:

arr[1] = new int[]{1,2,3,4};

//数组遍历:
for(int[] a:arr){
for(int num:a){
System.out.print(num+"\t");
}
System.out.println();
}

}
}

默认初始化
数组是引用类型,它的元素相当于类的实例变量,因此数组一经分配空间,其中的每个元素也被按照实例变量同样的方式被隐式初始化。

第7章_IDEA的使用

IDEA

IDE

集成开发环境(IDE,Integrated Development Environment )是用于提供程序开发环境的应用程序,一般包括代码编辑器、编译器、调试器和图形用户界面等工具。集成了代码编写功能、分析功能、编译功能、调试功能等一体化的开发软件服务套。所有具备这一特性的软件或者软件套(组)都可以叫集成开发环境。如微软的Visual Studio系列,Borland的C++ Builder、Delphi系列等。该程序可以独立运行,也可以和其它程序并用。IDE多被用于开发HTML应用软件。例如,许多人在设计网站时使用IDE(如HomeSite、DreamWeaver等),因为很多项任务会自动生成。编程开发软件将编辑、编译、调试等功能集成在一个桌面环境中,这样就大大方便了用户。

❀优点
节省时间和精力。IDE的目的就是要让开发更加快捷方便,通过提供工具和各种性能来帮助开发者组织资源,减少失误,提供捷径。
建立统一标准。当一组程序员使用同一个开发环境时,就建立了统一的工作标准,当IDE提供预设的模板,或者不同团队分享代码库时,这一效果就更加明显了。
管理开发工作。首先,IDE提供文档工具,可以自动输入开发者评论,或者迫使开发者在不同区域编写评论。其次,IDE可以展示资源,更便于发现应用所处位置,无需在文件系统里面艰难的搜索。

❀缺点
学习曲线问题。IDE基本上是比较复杂的工具,为了更好的熟练使用,需要一定的时间和耐心。
初学者的困难。对初学者来说,使用IDE来学习开发有相当的难度,不适合学习一种新语言时使用。
无法修复坏代码或设计。开发者不能完全依赖工具的便捷,还是必须保持专业水准和熟练度,开发的成果好坏主要还是看开发员的技术。

JetBrains公司介绍

【1】IntelliJ IDEA就是Java的IDE。
【2】市场占有率竹节攀升,超过了Eclipse。
【3】JetBrains公司介绍:
JetBrains是一家捷克的软件开发公司,该公司位于捷克的布拉格,并在俄罗斯的圣彼得堡及美国麻州波士顿都设有办公室,该公司最为人所熟知的产品是Java编程语言开发撰写时所用的集成开发环境:IntelliJ IDEA。公司旗下还有其它产品,比如:
➢WebStorm: 用于开发JavaScript、HTML5、 CS3等前端技术;
➢PyCharm: 用于开发python(python语言热度排行榜排名第一,在人工智能大数据领域应用)
➢PhpStorm: 用于开发PHP
➢RubyMine: 用于开发Ruby/Rails
➢AppCode: 用于开发Objective - C/Swift,替换xcode的
➢CLion: 用于开发C/C++
➢DataGrip: 用于开发数据库和SQL
➢Rider: 用于开发.NET
➢GoLand: 用于开发Go(区块链主流开发语言就是Go语言)

【4】官网:https://www.jetbrains.com/

IntelliJ_IDEA介绍

【1】IDEA 全称IntelliJ IDEA,是用于java语言开发的集成环境IDE(Integrated Development Environment),也可用于其他语言。
IntelliJ在业界被公认为最好的java开发工具之一,尤其在智能代码助手、代码自动提示、重构、J2EE支持、Ant、JUnit、CVS整合、代码审查、 创新的GUI设计等方面的功能可以说是超常的。

IDEA是JetBrains公司的产品,这家公司总部位于捷克共和国的首都布拉格,开发人员以严谨著称的东欧程序员为主。

【2】IDEA的支持:

image-20210921094123288

image-20210921094130578

【3】IDEA的优势(相对于Eclipse)
①强大的整合能力。比如: Git、 Maven、 Spring 等
②提示功能的快速、便捷
③提示功能的范围广
④好用的快捷键和代码模板
⑤精准搜索

IntelliJ_IDEA的下载和安装的准备

【1】官网:https://www.jetbrains.com/idea/download/#section=windows

image-20210921094215038

【2】安装的准备:
(1)硬件环境:
内存8G以上
CPU i5以上
安装在固态硬盘下
(2)软件环境:
需要安装JDK

IDEA的卸载

对于免安装的idea:
(1)删除安装文件
(2)到用户下将idea的缓存,配置的目录删除掉即可

安装idea:
(1)可以用控制面板–》程序

IDEA的使用

对于免安装的idea:
(1)删除安装文件
(2)到用户下将idea的缓存,配置的目录删除掉即可

安装idea:
(1)可以用控制面板–》程序

image-20210921094427335

选择主题:

image-20210921094439563

创建一个项目:

image-20210921094503359

image-20210921094514827

image-20210921094528919

image-20210921094538000

image-20210921094548860

IDEA页面展示

【1】项目下内容:
➢工程下的src类似于Eclipse下的src目录,用于存放代码。。
➢工程下的.idea 和TestProject.iml文件都是IDEA工程特有的。类似于Eclipse 工程下的settings、.classpath、.project 等。
【2】配置:

image-20210921094620691

image-20210921094626769

Module的概念和使用

【1】在Eclipse中我们有Workspace (工作空间)和Project (工程)的概念,在IDEA中只有Project (工程)和Module (模块)的概念。
这里的对应关系为:
IDEA官网说明:
An Eclipse workspace is similar to a project in IntelliJ IDEA
An Eclipse project maps to a module in IntelliJ IDEA
翻译:
Eclipse中 workspace 相当于 IDEA中的Project
Eclipse中 Project 相当于 IDEA中的Module

在IntelliJ IDEA中Project(工程) 是最顶级的级别,次级别是Module(模块)。
一个Project下可以有多个Module。

【2】从Eclipse 转过来的人总是下意识地要在同一个窗口管理n个项目,这在Intellij IDEA是无法做到的。Intellij IDEA提供的解决方案是打开多个项目实例,即打开多个项目窗口。即:一个Project 打开一个Window窗口。

【3】IDEA这样设置的原因:
目前主流的大型项目都是分布式部署的,结构都是类似这种多Module的。
这类项目一般是这样划分的,比如: 积分模块、任务模块、活动模块等等,模块之间彼此可以相互依赖。这些Module之间都是处于同一个项目业务下的模块,彼此之间是有不可分割的业务关系的。

image-20210921094721769

【4】out目录的说明:里面存放的是编译后的字节码文件

image-20210921094736799

【5】删除模块:

image-20210921094754144

IDEA的常用设置

【1】进入设置:

image-20210921094832183

image-20210921094837891

image-20210921094844294

【2】设置主题:

image-20210921094857918

【3】编辑区的字体变大或者变小:

image-20210921094911417

【4】鼠标悬浮在代码上有提示:

image-20210921094923827

【5】自动导包和优化多余的包:
手动导包:快捷键:alt+enter
自动导包和优化多余的包:

image-20210921094935715

【6】同一个包下的类,超过指定个数的时候,导包合并为*

image-20210921095000033

image-20210921095008985

【7】显示行号 , 方法和方法间的分隔符:

image-20210921095021320

【8】忽略大小写,进行提示:

image-20210921095034097

【9】多个类不隐藏,多行显示:

image-20210921095045331

【10】设置默认的字体,字体大小,字体行间距:(编辑区和控制台都会变化)

image-20210921095058169

【11】修改代码中注释的字体颜色:

image-20210921095113054

【12】修改类头的文档注释信息:—》注意:对新建的类才有效
/**

  • @Auther: zhaoss
  • @Date: ${DATE} - ${MONTH} - ${DAY} - ${TIME}
  • @Description: ${PACKAGE_NAME}
  • @version: 1.0
    */

image-20210921095129942

【13】设置项目文件编码:

image-20210921095143010

文件右下角可以调节编码格式:

image-20210921095154111

【14】自动编译:

image-20210921095210112

【15】省电模式:

image-20210921095222109

【16】代码显示结构:

image-20210921095236748

【17】导入jar包:

image-20210921095250483

【18】生成序列化版本号:

image-20210921095303591

image-20210921095310642

IDEA的常用快捷键

【1】创建内容:alt+insert
【2】main方法:psvm
【3】输出语句:sout
【4】复制行:ctrl+d
【5】删除行:ctrl+y

image-20210921095358903

【6】代码向上/下移动:Ctrl + Shift + Up / Down
【7】搜索类: ctrl+n
【8】生成代码 :alt + Insert(如构造函数等,getter,setter,hashCode,equals,toString)
【9】百能快捷键 : alt + Enter (导包,生成变量等)
【10】单行注释或多行注释 : Ctrl + / 或 Ctrl + Shift + /
【11】重命名 shift+f6
【12】for循环 直接 :fori 回车即可
【13】代码块包围:try-catch,if,while等 ctrl+alt+t
【14】 代码自动补全提示:

image-20210921095422717

【15】 idea代码字体大小放大和缩小的快捷键

image-20210921095458364

【16】代码一层一层调用的快捷键:
点进源码:ctrl+鼠标悬浮在代码上+点进去即可:

image-20210921095517106

【17】显示代码结构 : alt + 7
【18】显示导航栏: alt +1
【19】撤回:ctrl+z
【20】REDO操作:
如果跟搜狗输入法的快捷键冲突,可以选择将搜狗的快捷键取消。

image-20210921095530601

【21】缩进:tab 取消缩进: shift+tab

模板的使用

代码模板是什么

它的原理就是配置一些==常用代码字母缩写==,在输入简写时可以出现你预定义的固定模式的代码,使得开发效率大大提高,同时也可以增加个性化。最简单的例子就是在Java中输入sout会出现System.out.println();

(一)所处位置:
(1)Live Templates
(2)Postfix Completion

image-20210921095650984

(二)区别:
【1】
Live Templates中可以做用户的个性化定制。
Postfix Completion中只能用,不能修改。
【2】使用方式不同

image-20210921095703767

image-20210921095724632

修改现有模板

【1】案例1:改main方法: psvm

image-20210921095802566

image-20210921095810739

【2】案例2:修饰属性的修饰符:

image-20210921095825562

image-20210921095833815

常用的代码模板

【1】模板1: main方法:
main 或者 psvm

【2】模板2:输出语句:
sout 或者 .sout
一些变型:
soutp:打印方法的形参
soutm:打印方法的名字
soutv:打印变量

【3】模板3: 循环
普通for循环: fori(正向) 或者 .fori (正向) . forr(逆向)
增强for循环: iter 或者 .for
(可以用于数组的遍历,集合的遍历)

【4】模板4: 条件判断
ifn 或者 .null :判断是否为null (if null)
inn 或者 .nn :判断不等于null (if not null)

【5】模板5: 属性修饰符:
prsf : private static final
psf :public static final

自定义模板

【1】测试方法:

image-20210921095916821

【2】常用属性:($$中的内容其实就是在定义光标的位置,光标可以切换,用回车切换)

image-20210921095931833

【3】方法注释模板:
/**

  • 功能描述:
  • @param: $param$
  • @return: $return$
  • @auther: $user$
  • @date: $date$ $time$
    */

image-20210921095948604

IDEA中的断点调试

常用断点调试快捷键

调试在开发中大量应用:
【1】Debug的优化设置:更加节省内存空间:
设置Debug连接方式,默认是Socket。 Shared memory是Windows 特有的一个属性,一般在Windows系统下建议使用此设置,
内存占用相对较少。

image-20210921100037440

【2】常用断点调试快捷键:

image-20210921100142054

条件判断,查看表达式的值

【1】条件判断:
说明:
调试的时候,在循环里增加条件判断,可以极大的提高效率,心情也能惧悦。
具体操作:
在断点处右击调出条件断点。可以在满足某个条件下,实施断点。

image-20210921100214572

【2】查看表达式的值:
选择行,alt+f8。

image-20210921100238798

第8章_面向对象

面向过程和面向对象的区别

面向过程:当事件比较简单的时候,利用面向过程,注重的是事件的具体的步骤/过程,注重的是过程中的具体的行为,以函数为最小单位,考虑怎么做。

面向对象:注重找“参与者”,将功能封装进对象,强调具备了功能的对象,以类/对象为最小单位,考虑谁来做。

案例:
人把大象装进冰箱:
面向过程:
函数1:打开冰箱(){人站在冰箱前,打开冰箱,冰箱卡到30度角的时候,冰箱的灯打开了………}
函数2:储存大象(){大象先迈左腿,再迈右退,考虑冰箱能不能装下……}
函数3:关闭冰箱(){人站在冰箱前,关闭冰箱,冰箱开到30度角的时候,冰箱的灯关闭了……….}

面向对象:
人{
打开(冰箱){
冰箱.打开();
} 存储(大象){
大象.进入();
}

关闭(冰箱){
冰箱.关闭();
}

}

冰箱{

​ 打开(){ 1.2.3.}

关闭(){}

}

柜子{

}

大象{
进入(冰箱){

​ }

}

面向过程 —> 面向对象 , 其实就是由执行者 —> 指挥者的 一个过渡

面向过程:编年体
面向对象:纪传体

二者相辅相成,并不是对立的。解决复杂问题,通过面向对象方式便于我们从宏观上把握事物之间复杂的关系、方便我们分析整个系统;具体到微观操作,仍然使用面向过程方式来处理

类和对象的关系

【1】万事万物皆对象

【2】
对象:具体的事物,具体的实体,具体的实例,模板下具体的产品
类:对对象向上抽取出像的部分,公共的部分,形成类,类是抽象的,是一个模板

【3】一般在写代码的时候先写类,然后在根据类创建对应的对象。

面向对象三个阶段

面向对象三个阶段:
【1】面向对象分析OOA – Object Oriented Analysis
对象:张三,王五,朱六,你,我
抽取出一个类—-》人类

类里面有什么:
动词–》动态特性–》方法
名词–》静态特性–》属性
【2】面向对象设计OOD – Object Oriented Design
先有类,再有对象:
类:人类: Person
对象:zhangsan ,lisi,zhuliu
【3】面向对象编程OOP – Object Oriented Programming

创建类

创建类:
(1)属性(field 成员变量)
属性用于定义该类或该类对象包含的数据或者说静态特征。属性作用范围是整个类体。
属性定义格式:
[修饰符] 属性类型 属性名 = [默认值] ;

(2)方法
方法用于定义该类或该类实例的行为特征和功能实现。方法是类和对象行为特征的抽象。方法很类似于面向过程中的函数。面向过程中,函数是最基本单位,整个程序由一个个函数调用组成。面向对象中,整个程序的基本单位是类,方法是从属于类和对象的。
方法定义格式:
[修饰符] 方法返回值类型 方法名(形参列表) {
// n条语句
}
void代表没有返回值;方法的作用:重用代码,封装功能,便于修改

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package com.msb;
/**
* @Auther: msb-zhaoss
* 创建类:人类
*/
public class Person {
//名词---》属性---》成员变量---》放在类中方法外(注意:我们只把有需要的内容写到代码里,不相关的东西不要放在代码中)
int age ;//年龄
String name;//姓名
double height;//身高
double weight;//体重
//动词---》方法
//吃饭
public void eat(){
int num = 10;//局部变量:放在方法中
System.out.println("我喜欢吃饭");
}
//睡觉:
public void sleep(String address){
System.out.println("我在"+address+"睡觉");
}
//自我介绍:
public String introduce(){
return "我的名字是:"+name+",我的年龄是:"+age+",我的身高是:"+height+",我的体重是:"+weight;
}
}

创建对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package com.msb;
/**
* @Auther: msb-zhaoss
*/
public class Test {//测试类
//这是一个main方法,是程序的入口:
public static void main(String[] args) {
//创建一个人类的具体的对象/实例:
//创建一个对象,对象的名字叫:zs
//Person 属于 引用数据类型
//第一次加载类的时候,会进行类的加载,初始化创建对象的时候,对象的属性没有给赋值,有默认的初始化的值。
Person zs = new Person();
zs.name = "张三";
zs.age = 19;
zs.height = 180.4;
zs.weight = 170.4;
//再创建一个对象:
//再次创建类的时候,就不会进行类的加载了,类的加载只在第一次需要的时候加载一次
Person ls = new Person();
ls.name = "李四";
ls.age = 18;
ls.height = 170.6;
ls.weight = 160.5;
//对属性值进行读取:
System.out.println(zs.name);
System.out.println(ls.age);
//对方法进行操作:
//不同的对象,属性有自己的特有的值,但是方法都是调用类中通用的方法。
//属性:各个对象的属性是独立的,
//方法:各个对象的方法是共享的。
zs.eat();
ls.eat();
zs.sleep("教室");
/*String str = zs.introduce();
System.out.println(str);*/
System.out.println(zs.introduce());
}
}

局部变量和成员变量的区别

区别1:代码中位置不同
成员变量:类中方法外定义的变量
局部变量:方法中定义的变量 代码块中定义的变量
区别2:代码的作用范围
成员变量:当前类的很多方法
局部变量:当前一个方法(当前代码块)

区别3:是否有默认值
成员变量:有
局部变量:没有

image-20210921100933452

引用数据类型: null
区别4:是否要初始化
成员变量:不需要,不建议初始化,后续使用的时候再赋值即可
局部变量:一定需要,不然直接使用的时候报错

区别5:内存中位置不同
成员变量:堆内存
局部变量:栈内存
区别6:作用时间不同
成员变量:当前对象从创建到销毁
局部变量:当前方法从开始执行到执行完毕

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
package com.msb;
/**
* @Auther: msb-zhaoss
*/
public class Student {
byte e;
short s;
int c ;//成员变量:在类中方法外
long num2;
float f ;
double d;
char ch;
boolean bo;
String name;
public void study(){
int num = 10 ; //局部变量:在方法中
System.out.println(num);//10
//int num ;重复命名,出错了
{
int a;//局部变量:在代码块中
}
int a;
if(1==3){
int b;
}
System.out.println(c);
}
public void eat(){
System.out.println(c);
}
//这是一个main方法,是程序的入口:
public static void main(String[] args) {
Student s = new Student();
System.out.println(s.c);
System.out.println(s.bo);
System.out.println(s.ch);
System.out.println(s.d);
System.out.println(s.e);
System.out.println(s.f);
System.out.println(s.name);
System.out.println(s.num2);
System.out.println(s.s);
s.d = 10.4;
}
}

运行结果:

image-20210921101026241

构造器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.msb2;
/**
* @Auther: msb-zhaoss
*/
public class Person {
//构造器:没有任何参数的构造器我们叫做:空参构造器--》空构造器
public Person(){
/*age = 19;
name = "lili";
height = 169.5;*/
}
//属性:
String name;
int age;
double height;
//方法:
public void eat(){
System.out.println("我喜欢吃饭");
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package com.msb2;
/**
* @Auther: msb-zhaoss
*/
public class Test {
//这是一个main方法,是程序的入口:
public static void main(String[] args) {
//创建一个Person类的具体的对象/实例/实体:
/*
创建对象的过程:
1.第一次遇到Person的时候,进行类的加载(只加载一次)
2.创建对象,为这个对象在堆中开辟空间
3.为对象进行属性的初始化动作
new关键字实际上是在调用一个方法,这个方法叫构造方法(构造器)
调用构造器的时候,如果你的类中没有写构造器,那么系统会默认给你分配一个构造器,只是我们看不到罢了。
可以自己显式 的将构造器编写出来:
构造器的格式:
[修饰符] 构造器的名字(){
}
构造器和方法的区别:
1.没有方法的返回值类型
2.方法体内部不能有return语句
3.构造器的名字很特殊,必须跟类名一样
构造器的作用:不是为了创建对象,因为在调用构造器之前,这个对象就已经创建好了,并且属性有默认的初始化的值。
调用构造器的目的是给属性进行赋值操作的。
注意:我们一般不会在空构造器中进行初始化操作,因为那样的话每个对象的属性就一样了。
实际上,我们只要保证空构造器的存在就可以了,里面的东西不用写
*/
Person p = new Person();
System.out.println(p.age);
System.out.println(p.name);
System.out.println(p.height);
Person p2 = new Person();
System.out.println(p2.age);
System.out.println(p2.name);
System.out.println(p2.height);
}
}

构造器的重载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package com.msb3.msb2;
/**
* @Auther: msb-zhaoss
*/
public class Person {
//属性:
String name;
int age;
double height;
//空构造器
public Person(){
}
public Person(String name,int age,double height){
//当形参名字和属性名字重名的时候,会出现就近原则:
//在要表示对象的属性前加上this.来修饰 ,因为this代表的就是你创建的那个对象
this.name = name;
this.age = age;
this.height = height;
}
public Person(String a,int b){
name = a;
age = b;
}
//方法:
public void eat(){
System.out.println("我喜欢吃饭");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.msb3.msb2;
/**
* @Auther: msb-zhaoss
*/
public class Test {
//这是一个main方法,是程序的入口:
public static void main(String[] args) {
/*
1.一般保证空构造器的存在,空构造器中一般不会进行属性的赋值操作
2.一般我们会重载构造器,在重载的构造器中进行属性赋值操作
3.在重载构造器以后,假如空构造器忘写了,系统也不会给你分配默认的空构造器了,那么你要调用的话就会出错了。
4. 当形参名字和属性名字重名的时候,会出现就近原则:
在要表示对象的属性前加上this.来修饰 ,因为this代表的就是你创建的那个对象
*/
Person p = new Person();
/*p.age = 19;
p.name = "lili";
p.height = 180.4;*/
Person p2 = new Person("lili",19,180.4);
System.out.println(p2.age);
System.out.println(p2.height);
System.out.println(p2.name);
}
}

内存分析

this

【1】创建对象的过程:
(1)在第一次遇到一个类的时候,对这个类要进行加载,只加载一次。
(2)创建对象,在堆中开辟空间
(3)对对象进行初始化操作,属性赋值都是默认的初始值。
(4)new关键字调用构造器,执行构造方法,在构造器中对属性重新进行赋值

this:

image-20210921101320836

image-20210921101329632

从上面的效果能够看到:this指代的就是当前对象:

内存:

image-20210921101343478

this关键字 用法:
==(1)this可以修饰属性:==
总结:当属性名字和形参发生重名的时候,或者 属性名字 和局部变量重名的时候,都会发生就近原则,所以如果我要是直接使用变量名字的话就指的是离的近的那个形参或者局部变量,这时候如果我想要表示属性的话,在前面要加上:this.修饰

如果不发生重名问题的话,实际上你要是访问属性也可以省略this.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package com.msb4;
/**
* @Auther: msb-zhaoss
*/
public class Person {
//属性
int age;
String name;
double height;
//空构造器
public Person(){
}
//有参构造器
public Person(int age,String name,double height){
this.age = age;
this.name = name;
this.height = height;
}
//方法:
public void eat(){
int age = 10;
System.out.println(age);//就近原则,age指的是离它近的age--》局部变量的age
System.out.println(this.age);//这里指代的就是属性的age
System.out.println("我喜欢吃饭");
}
}

==(2)this修饰方法:==
总结:在同一个类中,方法可以互相调用,this.可以省略不写。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package com.msb4;
/**
* @Auther: msb-zhaoss
*/
public class Person {
//属性
int age;
String name;
double height;
//空构造器
public Person(){
}
//有参构造器
public Person(int age,String name,double height){
this.age = age;
this.name = name;
this.height = height;
}
//方法:
/*public void eat(){
int age = 10;
System.out.println(age);//就近原则,age指的是离它近的age--》局部变量的age
System.out.println(this.age);//这里指代的就是属性的age
System.out.println("我喜欢吃饭");
}*/
public void play(){
/*this.*/eat();
System.out.println("上网");
System.out.println("洗澡");
}
public void eat(){
System.out.println(/*this.*/age);
System.out.println("吃饭");
}
}

==(3)this可以修饰构造器:==
总结:同一个类中的构造器可以相互用this调用,注意:this修饰构造器必须放在第一行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package com.msb4;
/**
* @Auther: msb-zhaoss
*/
public class Person {
//属性
int age;
String name;
double height;
//空构造器
public Person(){
}
//有参构造器
public Person(int age,String name,double height){
this(age,name);
this.height = height;
}
public Person(int age,String name){
this(age);
this.name = name;
}
public Person(int age){
this.age = age;
}
//方法:
/*public void eat(){
int age = 10;
System.out.println(age);//就近原则,age指的是离它近的age--》局部变量的age
System.out.println(this.age);//这里指代的就是属性的age
System.out.println("我喜欢吃饭");
}*/
public void play(){
/*this.*/eat();
System.out.println("上网");
System.out.println("洗澡");
}
public void eat(){
System.out.println(/*this.*/age);
System.out.println("吃饭");
}
}

static

【1】static可以修饰:属性,方法,代码块,内部类。

【2】static修饰属性;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package com.msb5;
/**
* @Auther: msb-zhaoss
*/
public class Test {
//属性:
int id;
static int sid;
//这是一个main方法,是程序的入口:
public static void main(String[] args) {
//创建一个Test类的具体的对象
Test t1 = new Test();
t1.id = 10;
t1.sid = 10;
Test t2 = new Test();
t2.id = 20;
t2.sid = 20;
Test t3 = new Test();
t3.id = 30;
t3.sid = 30;
//读取属性的值:
System.out.println(t1.id);
System.out.println(t2.id);
System.out.println(t3.id);
System.out.println(t1.sid);
System.out.println(t2.sid);
System.out.println(t3.sid);
}
}

内存分析:

image-20211022183130083

一般官方的推荐访问方式:可以通过类名.属性名的方式去访问:

image-20211022183154617

static修饰属性总结:
(1)在类加载的时候一起加载入方法区中的静态域中
(2)先于对象存在
(3)访问方式: 对象名.属性名 类名.属性名(推荐)

static修饰属性的应用场景:某些特定的数据想要在内存中共享,只有一块 –》这个情况下,就可以用static修饰的属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package com.msb5;
/**
* @Auther: msb-zhaoss
*/
public class MsbStudent {
//属性:
String name;
int age;
static String school;
//这是一个main方法,是程序的入口:
public static void main(String[] args) {
MsbStudent.school = "马士兵教育";
//创建学生对象:
MsbStudent s1 = new MsbStudent();
s1.name = "张三";
s1.age = 19;
//s1.school = "马士兵教育";
MsbStudent s2 = new MsbStudent();
s2.name = "李四";
s2.age = 21;
//s2.school = "马士兵教育";
System.out.println(s2.school);
}
}

属性:
静态属性 (类变量)
非静态属性(实例变量)

【3】static修饰方法;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package com.msb5;
/**
* @Auther: msb-zhaoss
*/
public class Demo {
int id;
static int sid;
public void a(){
System.out.println(id);
System.out.println(sid);
System.out.println("------a");
}
//1.static和public都是修饰符,并列的没有先后顺序,先写谁后写谁都行
static public void b(){
//System.out.println(this.id);//4.在静态方法中不能使用this关键字
//a();//3.在静态方法中不能访问非静态的方法
//System.out.println(id);//2.在静态方法中不能访问非静态的属性
System.out.println(sid);
System.out.println("------b");
}
//这是一个main方法,是程序的入口:
public static void main(String[] args) {
//5.非静态的方法可以用对象名.方法名去调用
Demo d = new Demo();
d.a();
//6.静态的方法可以用 对象名.方法名去调用 也可以 用 类名.方法名 (推荐)
Demo.b();
d.b();

}
}

代码块

【1】类的组成:属性,方法,构造器,代码块,内部类
【2】代码块分类:普通块,构造块,静态块,同步块(多线程)
【3】代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
package com.msb6;
/**
* @Auther: msb-zhaoss
*/
public class Test {
//属性
int a;
static int sa;
//方法
public void a(){
System.out.println("-----a");
{
//普通块限制了局部变量的作用范围
System.out.println("这是普通块");
System.out.println("----000000");
int num = 10;
System.out.println(num);
}
//System.out.println(num);
//if(){}
//while(){}
}
public static void b(){
System.out.println("------b");
}
//构造块
{
System.out.println("------这是构造块");
}
//静态块
static{
System.out.println("-----这是静态块");
//在静态块中只能方法:静态属性,静态方法
System.out.println(sa);
b();
}
//构造器
public Test(){
System.out.println("这是空构造器");
}
public Test(int a){
this.a = a;
}
//这是一个main方法,是程序的入口:
public static void main(String[] args) {
Test t = new Test();
t.a();
Test t2 = new Test();
t2.a();
}
}

总结:
(1)代码块执行顺序:
最先执行静态块,只在类加载的时候执行一次,所以一般以后实战写项目:创建工厂,数据库的初始化信息都放入静态块。
一般用于执行一些全局性的初始化操作。

再执行构造块,(不常用)
再执行构造器,
再执行方法中的普通块。

包,import

【1】生活案例:
邮寄快递:中国.北京.通州区.****小区.5号楼.3单元.101房.赵珊珊
历史:常山赵子龙

【2】包的作用:
为了解决重名问题(实际上包对应的就是盘符上的目录)
解决权限问题

【3】创建包:

image-20211022183339208

image-20211022183346074

包名定义:
(1)名字全部小写
(2)中间用.隔开
(3)一般都是公司域名倒着写 : com.jd com.msb
(4)加上模块名字:
com.jd.login com.jd.register
(5)不能使用系统中的关键字:nul,con,com1—com9…..
(6)包声明的位置一般都在非注释性代码的第一行:

image-20211022183400581

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//声明包:
package com.msb7;
import com.msb2.Person; //导包:就是为了进行定位
import java.util.Date;
/**
* @Auther: msb-zhaoss
*/
public class Test {
//这是一个main方法,是程序的入口:
public static void main(String[] args) {
new Person();
new Date();
new java.sql.Date(1000L);//在导包以后,还想用其他包下同名的类,就必须要手动自己写所在的包。
new Demo();
}
}

总结:
(1)使用不同包下的类要需要导包: import **..; 例如:import java.util.Date;
(2)在导包以后,还想用其他包下同名的类,就必须要手动自己写所在的包。
(3)同一个包下的类想使用不需要导包,可以直接使用。
(4)在java.lang包下的类,可以直接使用无需导包:

(5)IDEA中导包快捷键:alt+enter
可以自己设置自动导包

(6)可以直接导入*:

image-20211022183429954

【5】在Java中的导包没有包含和被包含的关系:
设置目录平级的格式(不是包含和被包含的显示):

image-20211022183450017

image-20211022183457701

【6】静态导入:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.msb11;
//静态导入:
import static java.lang.Math.*;
//导入:java.lang下的Math类中的所有静态的内容
/**
* @Auther: msb-zhaoss
*/
public class Test {
//这是一个main方法,是程序的入口:
public static void main(String[] args) {
System.out.println(random());
System.out.println(PI);
System.out.println(round(5.6));
}
//在静态导入后,同一个类中有相同的方法的时候,会优先走自己定义的方法。
public static int round(double a){
return 1000;
}
}

三大特性

封装(Encapsulation)、

【1】生活案例:
ATM , 电线
【2】Java中封装的理解:
将某些东西进行隐藏,然后提供相应的方式进行获取。

我们程序设计追求“高内聚,低耦合”。
➢高内聚:类的内部数据操作细节自己完成,不允许外部干涉;
➢低耦合:仅对外暴露少量的方法用于使用。
隐藏对象内部的复杂性,只对外公开简单的接口。便于外界调用,从而提
高系统的可扩展性、可维护性。通俗的说,把该隐藏的隐藏起来,该暴露
的暴露出来。这就是封装性的设计思想。
【3】封装的好处:
提高代码的安全性

【4】代码:通过一个属性感受封装:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.msb.test01;
/**
* @Auther: msb-zhaoss
*/
public class Girl {//女孩
//属性:
private int age;
//读取年龄:
public int duquAge(){
return age;
}
//设置年龄:
public void shezhiAge(int age){
if(age >= 30 ){
this.age = 18;
}else{
this.age = age;
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.msb.test01;
/**
* @Auther: msb-zhaoss
*/
public class Test {
//这是一个main方法,是程序的入口:
public static void main(String[] args) {
//创建一个Girl类的对象:
Girl g = new Girl();
/*g.age = 33;
System.out.println(g.age);*/
//设置年龄:
g.shezhiAge(31);
//读取年龄:
System.out.println(g.duquAge());
}
}

上面的代码,对于属性age来说,我加了修饰符private,这样外界对它的访问就受到了限制,现在我还想加上其他的限制条件,但是在属性本身上没有办法再加了,所以我们通过定义方法来进行限制条件的添加。
以属性为案例:
进行封装:
(1)将属性私有化,被private修饰–》加入权限修饰符
一旦加入了权限修饰符,其他人就不可以随意的获取这个属性
(2)提供public修饰的方法让别人来访问/使用
(3)即使外界可以通过方法来访问属性了,但是也不能随意访问,因为咱们在方法中可以加入 限制条件。

【5】实际开发中,方法一般会写成 setter,getter方法:
可以利用IDEA快捷键生成:alt+insert –>getter and setter:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Girl {//女孩
//属性:
private int age;
//读取年龄:
public int getAge(){
return age;
}
//设置年龄:
public void setAge(int age){
if(age >= 30 ){
this.age = 18;
}else{
this.age = age;
}
}
}

【6】加深练习:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package com.msb.test2;
/**
* @Auther: msb-zhaoss
*/
public class Student {
//属性:
private int age;
private String name;
private String sex;
//加入对应的setter和getter方法:
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
if("男".equals(sex) || "女".equals(sex) ){//sex是男 或者 是 女
this.sex = sex;
}else{
this.sex = "男";
}
}
//加入构造器:
public Student(){
}
public Student(int age,String name,String sex){
this.age = age;
this.name = name;
//this.sex = sex;
this.setSex(sex);
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.msb.test2;
/**
* @Auther: msb-zhaoss
*/
public class Test {
//这是一个main方法,是程序的入口:
public static void main(String[] args) {
//创建一个Student对象:
Student s1 = new Student();
s1.setName("nana");
s1.setAge(19);
s1.setSex("女");
System.out.println(s1.getName()+"---"+s1.getAge()+"----"+s1.getSex());
Student s2 = new Student(18,"菲菲","asdfasdfsadf");
System.out.println(s2.getName()+"---"+s2.getAge()+"----"+s2.getSex());
}
}

继承(Inheritance)

【1】类是对对象的抽象:
举例:
荣耀20 ,小米 红米3,华为 p40 pro —> 类:手机类

【2】继承是对类的抽象:
举例:
学生类:Student:
属性:姓名,年龄,身高,学生编号
方法:吃饭,睡觉,喊叫,学习

教师类:Teacher:
属性:姓名,年龄,身高,教师编号
方法:吃饭,睡觉,喊叫,教学

员工类:Emploee:
属性:姓名,年龄,身高,员工编号
方法:吃饭,睡觉,喊叫,工作

共同的东西:
人类:
属性:姓名,年龄,身高
方法:吃饭,睡觉,喊叫

学生类/教师类/员工类 继承 自 人类

以后定义代码:
先定义人类:
人类: —》父类,基类,超类
属性:姓名,年龄,身高
方法:吃饭,睡觉,喊叫

再定义 : —》子类,派生类

学生类:Student:
属性:学生编号
方法:学习

教师类:Teacher:
属性:教师编号
方法:教学

员工类:Emploee:
属性:员工编号
方法:工作

子类 继承自 父类

狗类:
属性:姓名,年龄,身高
方法:吃饭,睡觉,喊叫

我们的继承关系,是在合理的范围中进行的抽取 ,抽取出子类父类的关系:
上面的案例中:
学生类/教师类/员工类 继承 自 人类 —》合理
学生类/教师类/员工类 继承 自 狗类 —》不合理

区分:
学生是一个人
教师是一个人
员工是一个人 —》合理

学生是一个狗 —》不合理

总结:继承 就是 is - a 的关系

【3】代码层面的解释:
先写父类,再写子类:

父类:人类 Person
子类:学生类 Student

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package com.msb.test03;
/**
* @Auther: msb-zhaoss
*/
public class Person {
//属性:
private int age;
private String name;
private double height;
//提供setter getter方法:
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getHeight() {
return height;
}
public void setHeight(double height) {
this.height = height;
}
//方法:
public void eat(){
System.out.println("可以吃饭。。。");
}
public void sleep(){
System.out.println("可以睡觉。。。");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.msb.test03;
/**
* @Auther: msb-zhaoss
*/
public class Student extends Person {//子类Student 继承 父类Person
//属性:
private int sno;//学号
public int getSno() {
return sno;
}
public void setSno(int sno) {
this.sno = sno;
}
//方法:
public void study(){
System.out.println("学生可以学习");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.msb.test03;
/**
* @Auther: msb-zhaoss
*/
public class Test {
//这是一个main方法,是程序的入口:
public static void main(String[] args) {
//创建子类Student的对象
Student s = new Student();
s.setSno(1001);
s.setAge(18);
s.setName("菲菲");
s.setHeight(180.4);
System.out.println("学生名字为:"+s.getName()+",学生的年纪:"+s.getAge());
//访问方法:
s.study();
s.eat();
s.sleep();
}
}

【4】继承的好处:提高代码的复用性
父类定义的内容,子类可以直接拿过来用就可以了,不用代码上反复重复定义了

需要注意的点:
父类private修饰的内容,子类实际上也继承,只是因为封装的特性阻碍了直接调用,但是提供了间接调用的方式,可以间接调用。

【5】总结:

(1)继承关系 :
父类/基类/超类
子类/派生类
子类继承父类一定在合理的范围进行继承的 子类 extends 父类
(2)继承的好处:
1.提高了代码的复用性,父类定义的内容,子类可以直接拿过来用就可以了,不用代码上反复重复定义了
2.便于代码的扩展
3.为了以后多态的使用。是多态的前提。

(3)父类private修饰的内容,子类也继承过来了。
(4)一个父类可以有多个子类。
(5)一个子类只能有一个直接父类。
但是可以间接的继承自其它类。

image-20211022183928788

(6)继承具有传递性:

Student –》继承自 Person —》继承自Object
Object类是所有类的根基父类。
所有的类都直接或者间接的继承自Object。

内存分析

image-20211022184027165

权限修饰符

image-20211022184123371

【1】private:权限:在当前类中可以访问

image-20211022184137565

image-20211022184146576

image-20211022184154208

【2】default:缺省修饰符:权限:到同一个包下的其他类都可以访问

image-20211022184212441

image-20211022184222065

image-20211022184232245

【3】protected:权限:最大到不同包下的子类

image-20211022184246111

image-20211022184256562

image-20211022184311925

【4】public:在整个项目中都可以访问

总结:
属性,方法:修饰符:四种:private,缺省,protected,public
类:修饰符:两种:缺省,public

以后写代码
一般属性:用private修饰 ,方法:用public修饰

方法的重写

【1】重写:
发生在子类和父类中,当子类对父类提供的方法不满意的时候,要对父类的方法进行重写。

【2】重写有严格的格式要求:
子类的方法名字和父类必须一致,参数列表(个数,类型,顺序)也要和父类一致。

【3】代码:

1
2
3
4
5
6
7
8
9
public class Person {
public void eat(){
System.out.println("吃食物");
}
public void sleep(){
System.out.println("睡觉");
}
}

1
2
3
4
5
6
7
8
9
public class Student extends Person {
public void study(){
System.out.println("学习");
}
@override
public void eat(){
System.out.println("我喜欢吃小龙虾喝啤酒。。");
}
}

image-20211022184413065

1
2
3
4
5
6
7
8
public class Test {
//这是一个main方法,是程序的入口:
public static void main(String[] args) {
//创建一个Student类的对象:
Student s = new Student();
s.eat();
}
}

【4】内存:

image-20211022184438204

【5】重载和重写的区别:
重载:在同一个类中,当方法名相同,形参列表不同的时候 多个方法构成了重载
重写:在不同的类中,子类对父类提供的方法不满意的时候,要对父类的方法进行重写。

image-20211022184453356

super

【1】super:指的是: 父类的
【2】super可以修饰属性,可以修饰方法;

在子类的方法中,可以通过 super.属性 super.方法 的方式,显示的去调用父类提供的属性,方法。在通常情况下,super.可以省略不写:

image-20211022184519515

在特殊情况下,当子类和父类的属性重名时,你要想使用父类的属性,必须加上修饰符super.,只能通过super.属性来调用
在特殊情况下,当子类和父类的方法重名时,你要想使用父类的方法,必须加上修饰符super.,只能通过super.方法来调用
在这种情况下,super.就不可以省略不写。

image-20211022184533355

【3】super修饰构造器:
其实我们平时写的构造器的第一行都有:super() –>作用:调用父类的空构造器,只是我们一般都省略不写
(所有构造器的第一行默认情况下都有super(),但是一旦你的构造器中显示的使用super调用了父类构造器,那么这个super()就不会给你默认分配了。如果构造器中没有显示的调用父类构造器的话,那么第一行都有super(),可以省略不写)

image-20211022184549268

如果构造器中已经显示的调用super父类构造器,那么它的第一行就没有默认分配的super();了

image-20211022184604238

在构造器中,super调用父类构造器和this调用子类构造器只能存在一个,两者不能共存:
因为super修饰构造器要放在第一行,this修饰构造器也要放在第一行:

image-20211022184624433

改正二选一即可:

image-20211022184637997

【4】以后写代码构造器的生成可以直接使用IDEA提供的快捷键:alt+insert

image-20211022184708956

继承条件下构造方法的执行过程

image-20211022184730962

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.msb.test10;
/**
* @Auther: msb-zhaoss
*/
public class Person {
int age;
String name;
public Person(int age, String name) {
super();
this.age = age;
this.name = name;
}
public Person() {
}
}

1
2
3
4
5
6
7
8
9
10
public class Student extends Person {
double height ;
public Student() {
}
public Student(int age, String name, double height) {
super(age, name);
this.height = height;
}
}

1
2
3
4
5
6
public class Test {
//这是一个main方法,是程序的入口:
public static void main(String[] args) {
Student s = new Student(19,"feifei",160.8);
}
}
Object类

所有类都直接或间接的继承自Object类,Object类是所有Java类的根基类。
也就意味着所有的Java对象都拥有Object类的属性和方法。
如果在类的声明中未使用extends关键字指明其父类,则默认继承Object类。

image-20211022184904823

toString()方法

【1】Object类的toString()的作用:

image-20211022185004724

方法的原理:

image-20211022185018528

现在,使用toString方法的时候,打印出来的东西 “不好看”,对于其他人来说不友好,可读性不好
我们现在是想知道对象的信息,名字,年龄,身高。。。。。。
现在的格式不好:

image-20211022185031331

出现的问题:子类Student对父类Object提供的toString方法不满意,不满意–》对toString方法进行重写:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
package com.msb.test01;
/**
* @Auther: msb-zhaoss
*/
public class Student /*extends Object*/{
private String name;
private int age;
private double height;
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 double getHeight() {
return height;
}
public void setHeight(double height) {
this.height = height;
}
public Student() {
}
public Student(String name, int age, double height) {
this.name = name;
this.age = age;
this.height = height;
}
public String toString() {
return "这是一个Student对象,这个对象的名字:"+name+",年龄:"+age+",身高:"+height;
}
}

测试类:

image-20211022185107331

总结:toString的作用就是对对象进行“自我介绍”,一般子类对父类提供的toString都不满意,都要进行重写。

IDEA提供了快捷键:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
package com.msb.test01;
/**
* @Auther: msb-zhaoss
*/
public class Student /*extends Object*/{
private String name;
private int age;
private double height;
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 double getHeight() {
return height;
}
public void setHeight(double height) {
this.height = height;
}
public Student() {
}
public Student(String name, int age, double height) {
this.name = name;
this.age = age;
this.height = height;
}
/*public String toString() {
return "这是一个Student对象,这个对象的名字:"+name+",年龄:"+age+",身高:"+height;
}*/
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", height=" + height +
'}';
}
}

equals方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
package com.msb.test02;
/**
* @Auther: msb-zhaoss
*/
public class Phone {//手机类:
//属性:
private String brand;//品牌型号
private double price;//价格
private int year ;//出产年份
//方法:
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public int getYear() {
return year;
}
public void setYear(int year) {
this.year = year;
}
@Override
public String toString() {
return "Phone{" +
"brand='" + brand + '\'' +
", price=" + price +
", year=" + year +
'}';
}
//构造器:
public Phone() {
}
public Phone(String brand, double price, int year) {
this.brand = brand;
this.price = price;
this.year = year;
}
//对equals方法进行重写:
public boolean equals(Object obj) {//Object obj = new Phone();
//将obj转为Phone类型:
Phone other = (Phone)obj;//向下转型,为了获取子类中特有的内容
if(this.getBrand()==other.getBrand()&&this.getPrice()==other.getPrice()&&this.getYear()==other.getYear()){
return true;
}
return false;
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.msb.test02;
/**
* @Auther: msb-zhaoss
*/
public class Test {
//这是一个main方法,是程序的入口:
public static void main(String[] args) {
//创建Phone类的对象:
Phone p1 = new Phone("华为P40",2035.98,2020);
Phone p2 = new Phone("华为P40",2035.98,2020);
//比较两个对象:p1和p2对象:
//==的作用:比较左右两侧的值是否想的,要么相等,返回true,要么不相等,返回false
System.out.println(p1==p2);//-->>>对于引用数据类型来说,比较的是地址值。--->一定返回的是false
//Object类提供了一个方法 equals方法 :作用:比较对象具体内容是否相等。
boolean flag = p1.equals(p2);//点进源码发现:底层依旧比较的是==,比较的还是地址值。
System.out.println(flag);
}
}

总结:
equals作用:这个方法提供了对对象的内容是否相等 的一个比较方式,对象的内容指的就是属性。
父类Object提供的equals就是在比较==地址,没有实际的意义,我们一般不会直接使用父类提供的方法,
而是在子类中对这个方法进行重写。

instanceof

image-20211022185302001

利用集成开发工具生成equals方法

【1】利用eclipse:

image-20211022185333280

【2】利用idea:

image-20211022185348782

类和类的关系

代码

总结:
【1】面向对象的思维:找参与者,找女孩类,找男孩类
【2】体会了什么叫方法的性擦,什么叫方法的实参:

image-20211022185506315

具体传入的内容 实参:

image-20211022185524051

【3】类和类可以产生关系:
(1)将一个类作为另一个类中的方法的形参
(2)将一个类作为另一个类的属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class Girl {
//属性:
String name;
double weight;
Mom m /*= new Mom()*/;
//方法:
public void add(int a){//参数是基本数据类型
System.out.println(a);
System.out.println(a+100);
}
//谈恋爱的方法:
public void love(Boy b){//参数是引用数据类型Boy
System.out.println("我男朋友的名字是:"+b.name+",我男朋友的年龄是:"+b.age);
b.buy();
}
//女孩跟妈妈聊天:
public void wechat(){
m.say();
}
//构造器:
public Girl(String name, double weight) {
this.name = name;
this.weight = weight;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Boy {
//属性:
int age;
String name;
//方法:
public void buy(){
System.out.println("跟我谈恋爱,我给你买买买。。。");
}
//构造器:
public Boy(int age, String name) {
this.age = age;
this.name = name;
}
}
1
2
3
4
5
6
public class Mom {
//方法:
public void say(){
System.out.println("妈妈唠唠叨叨 都是爱,听妈妈的话。。");
}
}

总结

一、继承关系
继承指的是一个类(称为子 类、子接口)继承另外的一个类(称为父类、父接口)的功能,并可以增加它自己的新功能的能力。在Java中继承关系通过关键字extends明确标识,在设计时一般没有争议性。在UML类图设计中,继承用一条带空心三角箭头的实线表示,从子类指向父类,或者子接口指向父接口。

image-20211022193345486

二、实现关系
实现指的是一个class类实现interface接口(可以是多个)的功能,实现是类与接口之间最常见的关系。在Java中此类关系通过关键字implements明确标识,在设计时一般没有争议性。在UML类图设计中,实现用一条带空心三角箭头的虚线表示,从类指向实现的接口。

image-20211022193404155

三、依赖关系
简单的理解,依赖就是一个类A使用到了另一个类B,而这种使用关系是具有偶然性的、临时性的、非常弱的,但是类B的变化会影响到类A。比如某人要过河,需要借用一条船,此时人与船之间的关系就是依赖。表现在代码层面,==让类B作为参数被类A在某个method方法中使用。==在UML类图设计中,依赖关系用由类A指向类B的带箭头虚线表示。

image-20211022202151501

四、关联关系
关联体现的是两个类之间语义级别的一种强依赖关系,比如我和我的朋友,这种关系比依赖更强、不存在依赖关系的偶然性、关系也不是临时性的,一般是长期性的,而且双方的关系一般是平等的。关联可以是单向、双向的。表现在代码层面,==为被关联类B以类的属性形式出现在关联类A中==,也可能是关联类A引用了一个类型为被关联类B的全局变量。在UML类图设计中,关联关系用由关联类A指向被关联类B的带箭头实线表示,在关联的两端可以标注关联双方的角色和多重性标记。

image-20211022202228571

五、聚合关系
聚合是关联关系的一种特例,它体现的是整体与部分的关系,即has-a的关系。此时整体与部分之间是可分离的,它们可以具有各自的生命周期,部分可以属于多个整体对象,也可以为多个整体对象共享。比如计算机与CPU、公司与员工的关系等,比如一个航母编队包括海空母舰、驱护舰艇、舰载飞机及核动力攻击潜艇等。==表现在代码层面,和关联关系是一致的,只能从语义级别来区分==。在UML类图设计中,聚合关系以空心菱形加实线箭头表示。

image-20211022202329462

六、组合关系
组合也是关联关系的一种特例,它体现的是一种contains-a的关系,这种关系比聚合更强,也称为强聚合。它同样体现整体与部分间的关系,但此时整体与部分是不可分的,整体的生命周期结束也就意味着部分的生命周期结束,比如人和人的大脑。==表现在代码层面,和关联关系是一致的,只能从语义级别来区分==。在UML类图设计中,组合关系以实心菱形加实线箭头表示。

image-20211022202355853

七、总结
对于继承、实现这两种关系没多少疑问,它们体现的是一种类和类、或者类与接口间的纵向关系。其他的四种关系体现的是类和类、或者类与接口间的引用、横向关系,是比较难区分的,有很多事物间的关系要想准确定位是很难的。前面也提到,==这四种关系都是语义级别的,所以从代码层面并不能完全区分各种关系==,但总的来说,后几种关系所表现的强弱程度依次为:组合>聚合>关联>依赖。

多态(Polymorphism)

【1】多态跟属性无关,多态指的是方法的多态,而不是属性的多态。
【2】案例代入:

1
2
3
4
5
public class Animal {//父类:动物:
public void shout(){
System.out.println("我是小动物,我可以叫。。。");
}
}
1
2
3
4
5
6
7
8
9
public class Cat extends Animal{
//喊叫方法:
public void shout(){
System.out.println("我是小猫,可以喵喵叫");
}
public void scratch(){
System.out.println("我是小猫,我可以挠人");
}
}
1
2
3
4
5
6
7
8
9
public class Dog extends Animal{
//喊叫:
public void shout(){
System.out.println("我是小狗,我可以汪汪叫");
}
public void guard(){
System.out.println("我是小狗,我可以看家护院,保护我的小主人。。。");
}
}
1
2
3
4
5
6
7
8
public class Pig extends Animal{
public void shout(){
System.out.println("我是小猪,我嗯嗯嗯的叫");
}
public void eat(){
System.out.println("我是小猪,我爱吃东西。。");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Girl {
//跟猫玩耍:
/*public void play(Cat cat){
cat.shout();
}*/
//跟狗玩耍:
/*public void play(Dog dog){
dog.shout();
}*/
//跟小动物玩耍:
public void play(Animal an){
an.shout();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Test {
//这是一个main方法,是程序的入口:
public static void main(String[] args) {
//具体的猫:--》猫的对象
//Cat c = new Cat();
//具体的小女孩:--》女孩的对象
Girl g = new Girl();
//小女孩跟猫玩:
//g.play(c);
//具体的狗---》狗的对象:
//Dog d = new Dog();
//小女孩跟狗玩:
//g.play(d);
//具体的动物:--》动物的对象:
//Cat c = new Cat();
//Dog d = new Dog();
Pig p = new Pig();
Animal an = p;
g.play(an);
}
}

【3】总结:
(1)先有父类,再有子类:–》继承 先有子类,再抽取父类 —-》泛化
(2)什么是多态:
多态就是多种状态:同一个行为,不同的子类表现出来不同的形态。
多态指的就是同一个方法调用,然后由于对象不同会产生不同的行为。

(3)多态的好处:
为了提高代码的扩展性,符合面向对象的设计原则:开闭原则。
开闭原则:指的就是扩展是 开放的,修改是关闭的。
注意:多态可以提高扩展性,但是扩展性没有达到最好,以后我们会学习 反射

(4)多态的要素:
一,继承: Cat extends Animal ,Pig extends Animal, Dog extends Animal
二,重写:子类对父类的方法shout()重写
三, 父类引用指向子类对象:

1
2
Pig p = new Pig();
Animal an = p;

将上面的代码合为一句话:
Animal an = new Pig();
=左侧:编译期的类型
=右侧:运行期的类型

Animal an = new Pig();
g.play(an); //

1
2
3
public void play(Animal an){//Animal an = an = new Pig();
an.shout();
}

上面的代码,也是多态的一种非常常见的应用场合:父类当方法的形参,然后传入的是具体的子类的对象,
然后调用同一个方法,根据传入的子类的不同展现出来的效果也不同,构成了多态。

内存分析

image-20211022202736994

向下转型,向上转型

image-20211022202807415

image-20211022202817885

现在我就想访问到eat()方法和weight属性:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Demo {
//这是一个main方法,是程序的入口:
public static void main(String[] args) {
Pig p = new Pig();
Animal an = p;//转型:向上转型
an.shout();
//加入转型的代码:
//将Animal转为Pig类型:
Pig pig = (Pig)an ;//转型:向下转型
pig.eat();
pig.age = 10;
pig.weight = 60.8;
}
}

对应内存:

image-20211022202849392

思考之前的equals方法:

image-20211022202907063

简单工厂设计模式

不仅可以使用父类做方法的形参,还可以使用父类做方法的返回值类型,真实返回的对象可以是该类的任意一个子类对象。

简单工厂模式的实现,它是解决大量对象创建问题的一个解决方案。将创建和使用分开,工厂负责创建,使用者直接调用即可。简单工厂模式的基本要求是

² 定义一个static方法,通过类名直接调用

² 返回值类型是父类类型,返回的可以是其任意子类类型

² 传入一个字符串类型的参数,工厂根据参数创建对应的子类产品

1
2
3
4
5
6
7
8
9
10
public class Test {
public static void main(String[] args) {
Girl g = new Girl();
//Cat c = new Cat();
//Dog d = new Dog();
//Pig p = new Pig();
Animal an = PetStore.getAnimal("狗");
g.play(an);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class PetStore {//宠物店 ---》工厂类
//方法:提供动物
public static Animal getAnimal(String petName){//多态的应用场合(二)
Animal an = null;
if("猫".equals(petName)){//petName.equals("猫") --》这样写容易发生空指针异常
an = new Cat();
}
if("狗".equals(petName)){
an = new Dog();
}
if("猪".equals(petName)){
an = new Pig();
}
return an;
}
}
final

【1】修饰变量;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class Test {
//这是一个main方法,是程序的入口:
public static void main(String[] args) {
//第1种情况:
//final修饰一个变量,变量的值不可以改变,这个变量也变成了一个字符常量,约定俗称的规定:名字大写
final int A = 10;//final修饰基本数据类型
//A = 20; 报错:不可以修改值
//第2种情况:
final Dog d = new Dog();//final修饰引用数据类型,那么地址值就不可以改变
//d = new Dog(); -->地址值不可以更改
//d对象的属性依然可以改变:
d.age = 10;
d.weight = 13.7;
//第3种情况:
final Dog d2 = new Dog();
a(d2);
//第4种情况:
b(d2);
}
public static void a(Dog d){
d = new Dog();
}
public static void b(final Dog d){//d被final修饰 ,指向不可以改变
//d = new Dog();
}
}

【2】修饰方法;

final修饰方法,那么这个方法不可以被该类的子类重写:

image-20211022203102224

【3】修饰类;
final修饰类,代表没有子类,该类不可以被继承:
一旦一个类被final修饰,那么里面的方法也没有必要用final修饰了(final可以省略不写)

image-20211022203119826

【4】案例:JDK提供的Math类:看源码发现:
(1)使用Math类的时候无需导包,直接使用即可:

image-20211022203139546

(2)Math类没有子类,不能被其他类继承了

image-20211022203207781

(3)里面的属性全部被final修饰,方法也是被final修饰的,只是省略不写了
原因:子类没有必要进行重写。

(4)外界不可以创建对象:
Math m = new Math();

image-20211022203229166

(5)发现Math类中的所有的属性,方法都被static修饰
那么不用创建对象去调用,只能通过类名.属性名 类名.方法名 去调用

抽象类,抽象方法

【1】抽象类和抽象方法的关系:
抽象类中可以定义0-n个抽象方法。
【2】抽象类作用:
在抽象类中定义抽象方法,目的是为了为子类提供一个通用的模板,子类可以在模板的基础上进行开发,先重写父类的抽象方法,然后可以扩展子类自己的内容。抽象类设计避免了子类设计的随意性,通过抽象类,子类的设计变得更加严格,进行某些程度上的限制。
使子类更加的通用。

【3】代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
package com.msb.test03;
//4.一个类中如果有方法是抽象方法,那么这个类也要变成一个抽象类。
//5.一个抽象类中可以有0-n个抽象方法
public abstract class Person {
//1.在一个类中,会有一类方法,子类对这个方法非常满意,无需重写,直接使用
public void eat(){
System.out.println("一顿不吃饿得慌");
}
//2.在一个类中,会有一类方法,子类对这个方法永远不满意,会对这个方法进行重写。
//3.一个方法的方法体去掉,然后被abstract修饰,那么这个方法就变成了一个抽象方法
public abstract void say();
public abstract void sleep();
}
//6.抽象类可以被其他类继承:
//7.一个类继承一个抽象类,那么这个类可以变成抽象类
//8.一般子类不会加abstract修饰,一般会让子类重写父类中的抽象方法
//9.子类继承抽象类,就必须重写全部的抽象方法
//10.子类如果没有重写父类全部的抽象方法,那么子类也可以变成一个抽象类。
class Student extends Person{
@Override
public void say() {
System.out.println("我是东北人,我喜欢说东北话。。");
}
@Override
public void sleep() {
System.out.println("东北人喜欢睡炕。。");
}
}
class Demo{
//这是一个main方法,是程序的入口:
public static void main(String[] args) {
//11.创建抽象类的对象:-->抽象类不可以创建对象
//Person p = new Person();
//12.创建子类对象:
Student s = new Student();
s.sleep();
s.say();

//13.多态的写法:父类引用只想子类对象:
Person p = new Student();
p.say();
p.sleep();
}
}

【4】面试题:
(1)抽象类不能创建对象,那么抽象类中是否有构造器?
抽象类中一定有构造器。构造器的作用 给子类初始化对象的时候要先super调用父类的构造器。

(2)抽象类是否可以被final修饰?
不能被final修饰,因为抽象类设计的初衷就是给子类继承用的。要是被final修饰了这个抽象类了,就不存在继承了,就没有子类。

接口

【1】接口声明格式:

1
2
3
4
[访问修饰符]  interface 接口名   [extends  父接口1,父接口2…]  {
常量定义;
方法定义;
}

【2】代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
package com.msb.test04;
/**
* 1.类是类,接口是接口,它们是同一层次的概念。
* 2.接口中没有构造器
* 3.接口如何声明:interface
* 4.在JDK1.8之前,接口中只有两部分内容:
* (1)常量:固定修饰符:public static final
* (2)抽象方法:固定修饰符:public abstract
* 注意:修饰符可以省略不写,IDE会帮你自动补全,但是初学者建议写上,防止遗忘。
*/
public interface TestInterface01 {
//常量:
/*public static final*/ int NUM = 10;
//抽象方法:
/*public abstract*/ void a();
/*public abstract*/ void b(int num);
/*public abstract*/ int c(String name);
}
interface TestInterface02{
void e();
void f();
}
/*
5.类和接口的关系是什么? 实现关系 类实现接口:
6.一旦实现一个接口,那么实现类要重写接口中的全部的抽象方法:
7.如果没有全部重写抽象方法,那么这个类可以变成一个抽象类。
8.java只有单继承,java还有多实现
一个类继承其他类,只能直接继承一个父类
但是实现类实现接口的话,可以实现多个接口
9.写法:先继承 再实现:extends Person implements TestInterface01,TestInterface02
*/
class Student extends Person implements TestInterface01,TestInterface02 {
@Override
public void a() {
System.out.println("---1");
}
@Override
public void b(int num) {
System.out.println("---2");
}
@Override
public int c(String name) {
return 100;
}
@Override
public void e() {
System.out.println("---3");
}
@Override
public void f() {
System.out.println("---4");
}
}
class Test{
//这是一个main方法,是程序的入口:
public static void main(String[] args) {
//10.接口不能创建对象:
//TestInterface02 t = new TestInterface02();
TestInterface02 t = new Student();//接口指向实现类 ---》多态
//11.接口中常量如何访问:
System.out.println(TestInterface01.NUM);
System.out.println(Student.NUM);
Student s = new Student();
System.out.println(s.NUM);
TestInterface01 t2 = new Student();
System.out.println(t2.NUM);
}
}

【3】接口的作用是什么?

定义规则,只是跟抽象类不同地方在哪?它是接口不是类。
接口定义好规则之后,实现类负责实现即可。

【4】
继承:子类对父类的继承
实现:实现类对接口的实现

手机 是不是 照相机

继承:手机 extends 照相机 “is-a”的关系,手机是一个照相机

上面的写法 不好:

实现: 手机 implements 拍照功能 “has-a”的关系,手机具备照相的能力

案例:飞机,小鸟,风筝
定义一个接口: Flyable

【5】多态的应用场合:
(1)父类当做方法的形参,传入具体的子类的对象
(2)父类当做方法的返回值,返回的是具体的子类的对象
(3)接口当做方法的形参,传入具体的实现类的对象
(4)接口当做方法的返回值,返回的是具体的实现类的对象

【6】接口和抽象类的区别:

image-20211022203953084

image-20211022204022646

JDK1.8以后的接口新增内容

在JDK1.8之前,接口中只有两部分内容:
(1)常量:固定修饰符:public static final
(2)抽象方法:固定修饰符:public abstract

在JDK1.8之后,新增非抽象方法:
(1)被public default修饰的非抽象方法:
注意1:default修饰符必须要加上,否则出错
注意2:实现类中要是想重写接口中的非抽象方法,那么default修饰符必须不能加,否则出错。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public interface TestInterface {
//常量:
public static final int NUM= 10;
//抽象方法:
public abstract void a();
//public default修饰的非抽象方法:
public default void b(){
System.out.println("-------TestInterface---b()-----");
}
}
class Test implements TestInterface{
public void c(){
//用一下接口中的b方法:
b();//可以
//super.b();不可以
TestInterface.super.b();//可以
}
@Override
public void a() {
System.out.println("重写了a方法");
}
@Override
public void b() {
}
}

(2)静态方法:
注意1:static不可以省略不写
注意2:静态方法不能重写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public interface TestInterface2 {
//常量:
public static final int NUM = 10;
//抽象方法:
public abstract void a();
//public default非抽象方法;
public default void b(){
System.out.println("-----TestInterface2---b");
}
//静态方法:
public static void c(){
System.out.println("TestInterface2中的静态方法");
}
}
class Demo implements TestInterface2{
@Override
public void a() {
System.out.println("重写了a方法");
}
public static void c(){
System.out.println("Demo中的静态方法");
}
}
class A {
//这是一个main方法,是程序的入口:
public static void main(String[] args) {
Demo d = new Demo();
d.c();
Demo.c();
TestInterface2.c();
}
}

疑问:为什么要在接口中加入非抽象方法???
如果接口中只能定义抽象方法的话,那么我要是修改接口中的内容,那么对实现类的影响太大了,所有实现类都会受到影响。
现在在接口中加入非抽象方法,对实现类没有影响,想调用就去调用即可。

内部类

成员内部类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
package com.msb.test07;
/**
* 1.类的组成:属性,方法,构造器,代码块(普通块,静态块,构造块,同步块),内部类
* 2.一个类TestOuter的内部的类SubTest叫内部类, 内部类 :SubTest 外部类:TestOuter
* 3.内部类:成员内部类 (静态的,非静态的) 和 局部内部类(位置:方法内,块内,构造器内)
* 4.成员内部类:
* 里面属性,方法,构造器等
* 修饰符:private,default,protect,public,final,abstract
*/
public class TestOuter {
//非静态的成员内部类:
public class D{
int age = 20;
String name;
public void method(){
//5.内部类可以访问外部类的内容
/*System.out.println(age);
a();*/
int age = 30;
//8.内部类和外部类属性重名的时候,如何进行调用:
System.out.println(age);//30
System.out.println(this.age);//20
System.out.println(TestOuter.this.age);//10
}
}
//静态成员内部类:
static class E{
public void method(){
//6.静态内部类中只能访问外部类中被static修饰的内容
/*System.out.println(age);
a();*/
}
}
//属性:
int age = 10;
//方法:
public void a(){
System.out.println("这是a方法");
{
System.out.println("这是一个普通块");
class B{
}
}
class A{
}
//7.外部类想要访问内部类的东西,需要创建内部类的对象然后进行调用
D d = new D();
System.out.println(d.name);
d.method();
}
static{
System.out.println("这是静态块");
}
{
System.out.println("这是构造块");
}
//构造器:
public TestOuter(){
class C{
}
}
public TestOuter(int age) {
this.age = age;
}
}
class Demo{
//这是一个main方法,是程序的入口:
public static void main(String[] args) {
//创建外部类的对象:
TestOuter to = new TestOuter();
to.a();
//9.创建内部类的对象:
//静态的成员内部类创建对象:
TestOuter.E e = new TestOuter.E();
//非静态的成员内部类创建对象:
//错误:TestOuter.D d = new TestOuter.D();
TestOuter t = new TestOuter();
TestOuter.D d = t.new D();
}
}

局部内部类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
package com.msb.test08;
/**
* @Auther: msb-zhaoss
*/
public class TestOuter {
//1.在局部内部类中访问到的变量必须是被final修饰的
public void method(){
final int num = 10;
class A{
public void a(){
//num = 20;
System.out.println(num);
}
}
}
//2.如果类B在整个项目中只使用一次,那么就没有必要单独创建一个B类,使用内部类就可以了
public Comparable method2(){
class B implements Comparable{
@Override
public int compareTo(Object o) {
return 100;
}
}
return new B();
}
public Comparable method3(){
//3.匿名内部类
return new Comparable(){
@Override
public int compareTo(Object o) {
return 200;
}
};
}
public void teat(){
Comparable com = new Comparable(){
@Override
public int compareTo(Object o) {
return 200;
}
};
System.out.println(com.compareTo("abc"));
}
}

面向对象项目

项目需求

image-20211022204310963

项目结构分析

image-20211022204335125

最终代码

==匹萨父类:==

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package com.msb.test01;
/**
* @Auther: msb-zhaoss
* 父类:匹萨类
*/
public class Pizza {
//属性
private String name;//名称
private int size;//大小
private int price;//价格
//方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getSize() {
return size;
}
public void setSize(int size) {
this.size = size;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
//展示匹萨信息:
public String showPizza(){
return "匹萨的名字是:"+name+"\n匹萨的大小是:"+size+"寸\n匹萨的价格:"+price+"元";
}
//构造器
public Pizza() {
}
public Pizza(String name, int size, int price) {
this.name = name;
this.size = size;
this.price = price;
}
}

==培根匹萨:==

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package com.msb.test01;
/**
* @Auther: msb-zhaoss
*/
public class BaconPizza extends Pizza {
//属性:
private int weight;
public int getWeight() {
return weight;
}
public void setWeight(int weight) {
this.weight = weight;
}
//构造器:
public BaconPizza() {
}
public BaconPizza(String name, int size, int price, int weight) {
super(name, size, price);
this.weight = weight;
}
//重写父类showPizza方法:
@Override
public String showPizza() {
return super.showPizza()+"\n培根的克数是:"+weight+"克";
}
}

==水果匹萨:==

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package com.msb.test01;
/**
* @Auther: msb-zhaoss
*/
public class FruitsPizza extends Pizza{
//属性:
private String burdening;
public String getBurdening() {
return burdening;
}
public void setBurdening(String burdening) {
this.burdening = burdening;
}
//构造器:
public FruitsPizza() {
}
public FruitsPizza(String name, int size, int price, String burdening) {
super(name, size, price);
this.burdening = burdening;
}
//重写父类showPizza方法:
@Override
public String showPizza() {
return super.showPizza()+"\n你要加入的水果:"+burdening;
}
}

==测试类:==

1
2
3
4
5
6
7
8
9
10
11
12
public class Test {
//这是一个main方法,是程序的入口:
public static void main(String[] args) {
//选择购买匹萨:
Scanner sc = new Scanner(System.in);
System.out.println("请选择你想要购买的匹萨(1.培根匹萨 2.水果匹萨):");
int choice = sc.nextInt();//选择
//通过工厂获取匹萨:
Pizza pizza = PizzaStore.getPizza(choice);
System.out.println(pizza.showPizza());
}
}

==工厂类:==

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package com.msb.test01;
import java.util.Scanner;
/**
* @Auther: msb-zhaoss
*/
public class PizzaStore {
public static Pizza getPizza(int choice){
Scanner sc = new Scanner(System.in);
Pizza p = null;
switch (choice){
case 1:
{
System.out.println("请录入培根的克数:");
int weight = sc.nextInt();
System.out.println("请录入匹萨的大小:");
int size = sc.nextInt();
System.out.println("请录入匹萨的价格:");
int price = sc.nextInt();
//将录入的信息封装为培根匹萨的对象:
BaconPizza bp = new BaconPizza("培根匹萨",size,price,weight);
p = bp;
}
break;
case 2:
{
System.out.println("请录入你想要加入的水果:");
String burdening = sc.next();
System.out.println("请录入匹萨的大小:");
int size = sc.nextInt();
System.out.println("请录入匹萨的价格:");
int price = sc.nextInt();
//将录入的信息封装为水果匹萨的对象:
FruitsPizza fp = new FruitsPizza("水果匹萨",size,price,burdening);
p = fp;
}
break;
}
return p;
}
}