Java基础知识 回顾
Yuxuan Wu Lv13

JAVA 基础知识复习

视频地址:

https://www.bilibili.com/video/BV12J41137hu?t=2607&p=80

相关笔记:

数据类型以及面试拓展

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
public class demo03 {
public static void main(String[] args) {
int i = 10;
int i2 = 010; // 八进制 0
int i3 = 0x10; //十六进制0x

System.out.println(i);
System.out.println(i2);
System.out.println(i3);
System.out.println("=================");

// ================================
// 浮点数
// 银行类用BigDecimal
// float 有限 离散 舍入误差
//
// 最好完全避免使用浮点数进行比较
// 最好完全避免使用浮点数进行比较
// 最好完全避免使用浮点数进行比较
float f = 0.1f;
double d = 1.0 / 10;
System.out.println(f==d);
System.out.println(f);
System.out.println(d);

// =================================
// 强制转换
System.out.println("=================");
char c1 = 'a';
System.out.println(c1);
System.out.println((int)c1); // 强制转换

// 所有的字符串本质还是数字
// 编码 Unicode 2 字节 (97 = a )
char c3 = '\u0061';
System.out.println(c3);

// 转义字符
// \t 制表符
// \n 换行

// =============================
System.out.println("=================");
String sa = new String("hello world");
String sb = new String("hello world");
System.out.println(sa==sb);
System.out.println("=================");

String sc = "hello world";
String sd = "hello world";
System.out.println(sc==sd);
// 对象 内存的关系


}
}

类型转换

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
public class demo04 {
public static void main(String[] args) {
int i = 128;
byte b = (byte) i; //内存溢出

// 小数优先级永远高于整数
// 强制转换,(类型)变量 高-低
// 自动转换 低-高

System.out.println(i);
System.out.println(b);

/*
注意点:
1。 不能对布尔值进行转换
2。 不能把对象类型转换为不相关的类型
3。 在把高容量转换到低容量的时候,强制转换
4。 转换的时候可能存在内存溢出,或者精度的问题
*/
System.out.println("===========");
System.out.println((int)23.7);
System.out.println((int)-45.89f);

System.out.println("===========");
char c = 'a';
int d = c + 1;
System.out.println(d);
System.out.println((char) d);

}
}

溢出的问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class demo5 {
public static void main(String[] args) {
// 操作比较大的数的时候,注意溢出的问题
// JDK7 的新特性,数字之间可以使用下划线来进行分割
int money = 10_0000_0000;
int years = 20;
int total = money * years;
long total2 = money * years;
long total3 = money * (long) years;

System.out.println(total);
System.out.println(total2);
System.out.println(total3);
}
}

变量

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
public class demo08 {
// 类变量 static
static double salary = 2500;

// 属性:变量

// 实例变量:从属于对象;如果不自省初始化,这个类型的默认值0,0.0
// 布尔值:默认是false
// 除了基本类型,其余的默认走都是null

String name;
int age;

// main 方法
public static void main(String[] args) {
//局部变量,必须声明和初始化值得量
int i = 10;
System.out.println(i);

// 变量类型 变量名字 = new demo08
demo08 Demo08 = new demo08();
System.out.println(Demo08.age);
System.out.println(Demo08.name);

// 类变量 static
System.out.println(salary);
}

// 其他方法
public void add(){

}
}

位运算(效率考虑)

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
package operator;

public class Demo06 {
public static void main(String[] args) {
/*
A = 0011 1100
B = 0000 1101

A&B = 0000 1100
A|B = 0011 1101
A^B = 0011 0001
~B = 1111 0010

2*8 = 16 怎么最快2*2*2*2 (计算机是不会乘除法的)

位运算,效率极高

<< 相当于 *2
>> 相当于 /2

0000 0000 0
0000 0001 1
0000 0010 2
0000 0011 3
0000 0100 4
0000 1000 8
0001 0000 16
*/
System.out.println(2<<3);

}
}

字符串操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package operator;

public class Demo07 {
public static void main(String[] args) {
int a = 10;
int b = 20;
a += b; //a = a+b
a -= b;// a = a-b
System.out.println(a);

// 字符串连接符 + , String
// 注意第一个是什么就会运行什么样子的操作
System.out.println("" + a + b);
System.out.println(a + b + "");
}
}

三元运算符(if的更简单的写法形式)

1
2
3
4
5
6
7
8
9
10
11
package operator;

public class Demo08 {
public static void main(String[] args) {
//x?y:z
//如果x==true,则结果为y,否则结果为z
int score = 80;
String type = score < 60 ? "不及格":"及格"; //必须掌握,让代码更加精简,且容易掌握
System.out.println(type);
}
}

javadoc

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.yuxuan.base;
/**
* @author yuxuan
* @version 1.0
* @since 1.8
*/

public class Doc {
String name;

/**
* @authore
* @param name
* @return
* @throws Exception
*/

public String test(String name) throws Exception{

return name;
}

}
// 通过命令行生成JavaDoc文档!
// 命令行进入到包所在的地址,然后输入:javadoc -encoding UTF-8 -charset UTF-8 Doc.java

Scanner 对象

Next():

1. 一定要读取到有效字符后菜可以结束输入
2. 对输入有效字符之前遇到的空白,next()会将其自动去掉
3. 只有输入有效字符后才讲其后面输入的空白作为分隔符或者结束符号
4. next()不能得到带有空格的字符串

nextLine():

  1. 以enter作为结束符号,也就是说nexline()方法返回的是回车之前的字母
  2. 可以获得空白
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.yuxuan.scanner;

import java.util.Scanner;

public class Demo01 {
public static void main(String[] args) {
// 创建一个扫描器对象,用于接受键盘数据
Scanner scanner = new Scanner(System.in);
System.out.println("使用next方式来接受: ");

if(scanner.hasNext()){
String str = scanner.next();
System.out.println("输出内容是: "+str);
}

// 凡是属于IO流的类如果不关闭会一直占用资源,要养成好习惯用完就关闭
scanner.close();

}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.yuxuan.scanner;

import java.util.Scanner;
public class Demo02 {
public static void main(String[] args) {
System.out.println("使用nextLine方式来接受: ");
Scanner scanner = new Scanner(System.in);
if (scanner.hasNextLine()){
String s = scanner.nextLine();
System.out.println("输出的内容为:" + s);
}
scanner.close();
}
}

Switch case 语句

注意case的穿透特性,建议加上break

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.yuxuan.struct;

public class SwitchDemo {
public static void main(String[] args) {
//case 穿透 //switch 匹配一个具体的值
char grade = 'c';
switch (grade){
case 'a':
System.out.println("优秀");
break; // 可选 ,但是建议加上,否则容易穿透
case 'b':
System.out.println("良好");
break; // 可选 ,但是建议加上,否则容易穿透
case 'c':
System.out.println("及格");
break; // 可选 ,但是建议加上,否则容易穿透
case 'd':
System.out.println("再接再厉");
break; // 可选 ,但是建议加上,否则容易穿透
default:
System.out.println("未知等级");

}
}
}

方法(methods)

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.yuxuan.method;

public class Demo01 {
public static void main(String[] args) {
// System.out.println(max(1, 2));
int max = max(10, 20);
System.out.println(max );

}

public static int max(int num1, int num2) {

int result = 0;
if (num1 == num2) {
System.out.println("num1==num2");
return 0; //终止方法

}

if (num1 > num2) {
result = num1;
} else {
result = num2;
}
return result;
}
}

Java 都是值传递

方法的重载(overload)

  • 重载就是在一个类中,有相同的函数名称,但是形式参数不同的函数
  • 方法的重载规则:
    • 方法名称必须相同
    • 参数列表必须不同(个数不同,或者类型不同,参数排列顺序不同等)
    • 方法的返回类型可以相同也可以不相同
    • 仅仅返回类型不同不足以成为方法的重载
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
public class Overloading {
public int test(){
System.out.println("test1");
return 1;
}

public void test(int a){
System.out.println("test2");
}

//以下两个参数类型顺序不同
public String test(int a,String s){
System.out.println("test3");
return "returntest3";
}

public String test(String s,int a){
System.out.println("test4");
return "returntest4";
}

public static void main(String[] args){
Overloading o = new Overloading();
System.out.println(o.test());
o.test(1);
System.out.println(o.test(1,"test3"));
System.out.println(o.test("test4",1));
}
}
/*
test1
1
test2
test3
returntest3
test4
returntest4

*/

img

img

命令行传参数

1
2
3
4
5
6
7
8
9
10
package com.yuxuan.method;

public class Demo03 {
public static void main(String[] args) {
//args.length 数组长度
for (int i = 0; i < args.length; i++) {
System.out.println("args[" + i + "]: " + args[i]);
}
}
}

先用javac 打包成一个class文件

然后注意要退回到src文件才可以调用,因为里面有一个包的成分

可变参数

在方法声明中,在指定参数类型后加一个省略好(…)

一个方法中只能指定一个可变参数,它必须是方法的最后一个参数,任何普通的参数都必须在它之前声明

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.yuxuan.method;

public class Demo04 {
public static void main(String[] args) {
printMax(34, 3, 3, 2, 56.5);
printMax(new double[]{1, 2, 3});
}

public static void printMax(double... numbers) {
if (numbers.length == 0) {
System.out.println("no argument passed");
return;
}

double result = numbers[0];
for (int i = 0; i < numbers.length; i++) {
if (numbers[i] > result) {
result = numbers[i];
}
}
System.out.println("The max value is " + result);

}
}

递归:

  • 递归头:什么时候不调用自身方法。如果没有头,就会陷入死循环
  • 递归体:什么时候需要调用自身的方法

基数比较小的数字建议用递归,如果数字太多,会出现压栈,然后内存溢出

数组

  • 数组是相同类型数据的有序集合
  • 数组描述的是相同类型的若干数据,按照一定的先后次序排列组合而成
  • 其中一个数据称作一个数组元素,每个数组元素可以通过一个下标来进行访问(从0开始)

数组的声明以及创建

  • 首先必须声明数组变量,才能在程序中使用数组
  • java使用new来创建数组
  • 数组的元素是通过索引来进行访问的,数组索引从
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.yuxuan.array;

public class ArrayDemo01 {
// 变量的类型 变量的名字 = 变量的值;
// 数组的类型
public static void main(String[] args) {
int[] nums; //1. 定义
// int nums2[]; //2.效果相同,但不是首选的方法
nums = new int[10]; // 这里面可以存在10个变量
//3. 给数组元素来进行赋值
nums[0] = 1;
nums[1] = 2;
nums[2] = 3;
nums[3] = 4;
nums[4] = 5;
nums[5] = 6;
nums[6] = 7;
nums[7] = 8;
nums[8] = 9;
nums[9] = 10;
// System.out.println(nums[1]);

int sum = 0;
for (int i = 0; i < nums.length; i++) {
sum = sum + nums[i];

}
System.out.println(sum);
}
}

内存分析

image-20210213225954841

Java 内存

  • 堆:
    • 存放new的对象和数组
    • 可以被所有的线程共享,不会存在别的对象引用
  • 栈:
    • 存放基本变量类型(包含这个基本类型的具体数值)
    • 引用对象的变量(会存放这个引用在堆里面的具体地址)
  • 方法区:
    • 可以被所有的线程共享
    • 包含了所有的class和static的变量

image-20210213231032055

数组的默认初始化

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.yuxuan.array;


public class ArrayDemo02 {
public static void main(String[] args) {
// 静态初始化: 创建+赋值
int a[] = {1, 2, 3, 4, 5, 6, 7, 8};
// System.out.println(a[0]);

// 动态初始化 包含默认初始化
int[] b = new int[10];
b[0] = 10;
System.out.println(b[0]);
System.out.println(b[1]);
System.out.println(b[2]);
System.out.println(b[3]);
}
}

数组的四个基本特点

  • 其长度是确定的。数组一旦被创建,它的大小就是不可以被改变的
  • 其元素必须是相同类型的,不允许出现混合类型
  • 数组中的元素可以是任何数据类型,包括基本类型和引用类型
  • 数组变量属于引用类型,数组也可以看成是对象,数组中的每个元素相当于该对象的成员变量。数组本身就是对象,java 的对象都是在堆中的,因此数组无论保存原始类型还是其他对象类型,==数组对象本身是在堆中的==
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
package com.yuxuan.array;

public class ArrayDemo04 {
public static void main(String[] args) {
int[] arrays = {1,2,3,4,5};
// 这种方式适合遍历数组 JDK1.5,没有下标

// for (int array : arrays) {
// System.out.println(array);
// }
// printArray(arrays);
int[] reverse = reverse(arrays);
printArray(reverse);
}

// 打印数组元素
public static void printArray(int []arrays) {
for (int i = 0; i < arrays.length; i++) {
System.out.print(arrays[i] + " " );
}
}

//反转数组
public static int[] reverse(int arrays[]) {
int[] result = new int[arrays.length];
for (int i = 0, j = result.length-1; i < arrays.length; i++,j--) {
result[j] = arrays[i];
}
return result;
}

}

多维数组

多维数组可以看成是数组的数组,比如二维数组就是一个特殊的一维数组,其中每一个元素都是一个一维数组

1
2
int a[][] = new int[2][5]
// 一个二行五列的数组

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
package com.yuxuan.array;

import com.sun.scenario.effect.impl.sw.sse.SSEBlend_SRC_OUTPeer;

import java.lang.reflect.Array;
import java.util.Arrays;

public class ArrayDemo06 {
public static void main(String[] args) {
int[] a = {1, 2, 3, 9090, 32132, 543, 21, 3, 23};
// System.out.println(a);
// 不建议重复造轮子

// 打印数组元素Arrays.toString(a)
System.out.println(Arrays.toString(a));

// 数组进行排序

Arrays.sort(a);
System.out.println(Arrays.toString(a));

// fill 填充
Arrays.fill(a,0);
System.out.println(Arrays.toString(a));
}

}

冒泡排序

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
package com.yuxuan.array;

import java.util.Arrays;

public class ArrayDemo07 {
public static void main(String[] args) {
int[] a = {1, 2, 3, 5, 22, 34, 56};
int[] sort = sort(a);
System.out.println(Arrays.toString(sort));
}

// 冒泡排序
// 1. 比较数组中两个相邻的元素,如果第一个数比第二个数大,我们就交换他们的位置
// 2. 每一次比较,都会产生出一个最大或者最小的数字
// 3. 下一次可以少一次排序
// 4. 以此循环,直到结束

public static int[] sort(int[] array ) {
int tmp = 0;
for (int i = 0; i < array.length - 1; i++) {
boolean flag = false; //通过flag标识符来减少没有意义比较
for (int j = 0; j < array.length-1-i; j++) {
if (array[j + 1] < array[j]) {
tmp = array[j];
array[j] = array[j + 1];
array[j + 1] = tmp;
flag = true;
}
if (flag == false) {
break;
}
}
}
return array;
}

}

我们看到嵌套循环,应该立马就可以得出这个算法的时间复杂度为O(n2)

稀疏数组

  • 当一个数组大部分的元素为0,或者为同一值的数组时候,可以用稀疏数组来保存该数组
  • 稀疏数组的处理方式一般是:
    • 记录数组一共有几航几列,有多少个不同的值
    • 把具有不同值的元素和行列及值记录在一个小规模的数组中,从而缩小程序的规模
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
package com.yuxuan.array;

public class ArrayDemo08 {
public static void main(String[] args) {
// 创建一个二维数组11*11, 0 没有棋子, 1:黑棋,2:白棋
int[][] array1 = new int[11][11];
array1[1][2] = 1;
array1[2][3] = 2;
// 输出原始数组
for (int[] ints : array1) {
for (int anInt : ints) {
System.out.print(anInt + "\t");
}
System.out.println();
}

// 转换为稀疏数组
// 获取有效值的个数
int sum = 0;
for (int i = 0; i < 11; i++) {
for (int j = 0; j < 11; j++) {
if (array1[i][j] != 0) {
sum++;
}

}

}
System.out.println("有效值的个数是:" + sum);

// 2. 创建一个稀疏数组
int[][] array2 = new int[sum + 1][3];
array2[0][0] = 11;
array2[0][1] = 11;
array2[0][2] = sum;

// 遍历二维数组,将非零的值,存放在稀疏数组中
int count = 0;
for (int i = 0; i < array1.length; i++) {
for (int j = 0; j < array1[i].length; j++) {
if (array1[i][j] != 0) {
count++;
array2[count][0] = i;
array2[count][1] = j;
array2[count][2] = array1[i][j];

}

}
}
System.out.println("==========");
System.out.println("输出稀疏数组");
for (int i = 0; i < array2.length; i++) {
System.out.println(array2[i][0] + "\t" +
array2[i][1] + "\t" +
array2[i][2]);
}
System.out.println("==========");
System.out.println("还原");
// 1. 读取稀疏数组
int [][] array3 = new int[array2[0][0]][array2[0][1]];

//2. 给其中的元素还原它的值
for (int i = 1; i < array2.length; i++) {
array3[array2[i][0]][array2[i][1]] = array2[i][2];

}
//3. 打印
for (int[] ints : array3) {
for (int anInt : ints) {
System.out.print(anInt + "\t");
}
System.out.println();
}

}
}

面向对象编程

  • 面向过程思想 (线性思维)
    • 步骤清晰简单,第一步做什么第二部做什么
    • 面对过程适合处理一些较为简单的问题
  • 面向对象思想
    • 物以类聚,分类的思维模式,思考问题首先会解决问题需要哪些分类,然后对这些类进行单独思考,最后才对某个分类下的细节进行面向过程的思索
    • 面向对象适合处理复杂的问题,适合处理需要多人协作的问题
    • 属性+方法就是一个类
  • 对于描述复杂的事物,为了从宏观上进行把握,从整体上进行合理分析,我们需要使用面向对象的思路来分析整个系统。但是,具体到围观操作,荏苒需要使用面向过程的思路来进行处理

OOP本质:==以类的方式组织代码,以对象的组织(封装)数据==

三大特性:

  • 封装
  • 继承
  • 多态

从认识角度考虑是先有对象,后有类

对象,是具体的事物

类,是抽象的,是对对象的抽象

类是一种抽象的数据类型,它是对某一类食物的整体描述/定义,但是并不能代表某一个具体的事物

  • 动物,植物,手机,电脑
  • Person 类,Pet类,Car类,这些类是用来描述/定义某一类具体的事物应该具备的特点和行为

对象是抽象概念的具体实例

  • 张三是人的一个具体实例,张三家里的旺财就是狗的一个具体实例
  • 能狗体现出特点,展现出功能的是具体的实例,而不是一个抽象的概念
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.oop.demo01;

public class Demo02 {
// 静态方法 static
public static void main(String[] args) {
Student.say();

// 非静态方法
// 实例化这个类 new
Student student = new Student();
student.say02();
}

// static 是和类一起加载的
public static void a() {
// b(); //会报错 (但如果两个method都是static不会,都不是,也不会;只有一个是另一个不是才出错)
}

// 类实例化之后才存在的
public void b() {

}

}
1
2
3
4
5
6
7
8
9
10
11
12
package com.oop.demo01;

// 学生类
public class Student {
public static void say(){
System.out.println("学生说话了");
}

public void say02(){
System.out.println("学生说话了");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
package com.oop.demo01;

public class Demo03 {
public static void main(String[] args) {
// 实际参数和形式参数的值需要保持一致
int add = Demo03.add(1, 2);
System.out.println(add);
}

public static int add(int a, int b) {
return a + b;
}
}

值传递

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.oop.demo01;

// 值传递
public class Demo04 {
public static void main(String[] args) {
int a = 1;
System.out.println(a);
Demo04.change(a);
System.out.println(a);

}
// 返回值是空

public static void change(int a) {
a = 10;
}
}

引用传递

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.oop.demo01;

// 引用传递:对象, 本质还是值传递
// 引用传递是传递的类的地址

//对象,内存

public class Demo05 {
public static void main(String[] args) {
Person person = new Person();
System.out.println(person.name);

Demo05.change(person);
System.out.println(person.name);
}

public static void change(Person person) {
// person 是一个对象:指向的是---> Person person = new Person() 这是一个具体的人,属性可以改变
person.name = "yuxuan";
}
}

// 定义了一个Person 类,有一个属性:name
class Person {
String name; // null

}

创建与初始化对象

  • 使用new关键字创建对象
  • 使用new关键字创建的时候,除了分配内存空间之外,还会给创建好的对象进行默认的初始化以及类中构造器的调用
  • 类中构造器也被称为构造方法,实在进行穿件对象的时候必须要调用的。并且构造器有一下几个特点
    • 必须和类的名字相同
    • 必须没有返回类型,也不能写void
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.oop.demo02;
// 一个项目最好只有一个main方法,测试类
public class Application {
public static void main(String[] args) {
// 类抽象的,需要实例化
// 类实例化后会返回一个自己的对象!
// student 对象就是一个Student类的具体实例!
Student student = new Student();
Student xiaoming = new Student();

System.out.println(xiaoming.age);
System.out.println(xiaoming.name);

xiaoming.name = "小明";
xiaoming.age = 3;

System.out.println(xiaoming.age);
System.out.println(xiaoming.name);

}
}

构造器

构造器
1. 和类名相同
2. 没有返回值
作用
1. new 本质在调用构造方法
2. 初始化对象的值
注意点:
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
package com.oop.demo02;

public class Person {
// 一个类即使什么都不写,也会存在一个方法
// 显示的定义一个构造器

String name;

// 实例化初始值
// 1. 使用new关键字,必须是在调用构造器
// 2. 用来初始化值

public Person() {
// this.name = "yuxuan";
}


// 有参构造:一旦定义了有参构造,无参就必须显示定义
public Person(String name) {
this.name = name;
}

//control + N 自动生成构造器
}

/*
// Person person = new Person();
Person person = new Person("yuxuan");

System.out.println(person.name);
构造器:
1。 和类名相同
2。 没有返回值
作用
1。 new 本质在调用构造方法
2。 初始化对象的值
注意点:
1。 定义有参构造后,如果想使用无参构造,显示的定义一个无参的构造

alt+ N

this. = 参数传进来的值
*/

创建对象内存与分析

堆:存储对象和数组元素,栈:存储系统调用变量(例如引用)

堆:存放new的数组和对象,栈:存放基本变量类型和引用变量

image-20210214170938226

静态方法

类加载过程中将静态变量,静态方法,常量存入到方法区

静态方法只执行一次

Final 后面断子绝孙

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.oop.demo07;

public class Person {
//2 赋初始值
{
// 代码块(匿名)
System.out.println("代码快");
}

//1
static {
// 静态代码块
System.out.println("静态代码块");
}

//3
public Person() {
System.out.println("构造方法 ");
}

public static void main(String[] args) {
Person person = new Person();
Person person2 = new Person();

}

}

作者:愚公要移山
链接:https://zhuanlan.zhihu.com/p/70110497
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

Static 关键字

(1)特点:

  1、static是一个修饰符,用于修饰成员。(成员变量,成员函数)static修饰的成员变量 称之为静态变量或类变量。

  2、static修饰的成员被所有的对象共享。

  3、static优先于对象存在,因为static的成员随着类的加载就已经存在。

  4、static修饰的成员多了一种调用方式,可以直接被类名所调用,(类名.静态成员)。

  5、static修饰的数据是共享数据,对象中的存储的是特有的数据。

(2)成员变量和静态变量的区别:

  1、生命周期的不同:

    成员变量随着对象的创建而存在随着对象的回收而释放。

    静态变量随着类的加载而存在随着类的消失而消失。

  2、调用方式不同:

    成员变量只能被对象调用。

    静态变量可以被对象调用,也可以用类名调用。(推荐用类名调用)

  3、别名不同:

    成员变量也称为实例变量。

    静态变量称为类变量。

  4、数据存储位置不同:

    成员变量数据存储在堆内存的对象中,所以也叫对象的特有数据。

    静态变量数据存储在方法区(共享数据区)的静态区,所以也叫对象的共享数据。

(3)静态使用时需要注意的事项:

  1、静态方法只能访问静态成员。(非静态既可以访问静态,又可以访问非静态)

  2、静态方法中不可以使用this或者super关键字。

  3、主函数是静态的

类与对象的小结

  1. 类与对象
    1. 类是一个模版,对象是一个具体的实例
  2. 方法
    1. 定义,调用
  3. 对象的引用
    1. 引用类型:八大基本类型(8)
    2. 对象是通过引用来操作的:栈–》堆
  4. 属性:字段field 成员变量
    1. 默认初始化
      1. 数字: 0, 0.0
      2. char: u0000
      3. boolean:false
      4. 引用: null
      5. 修饰符 属性类型 属性名 = 属性值
  5. 对象的创建和使用
      • 必须使用new 关键字创建对象 构造器 Person Yuxuan = new Person
      • 对象的属性:yuxuan.name
      • 对象的方法:yuxuan.sleep()
    1. 静态的属性 属性
    2. 动态的行为 方法
  6. 封装,继承,多态

封装

高内聚,低耦合:

高内聚:类的内部数据操作细节自己完成,不允许外部干涉

低耦合:尽量暴露少量的方法给外部使用

封装(数据的隐藏)

  • 通常,应该禁止访问一个对象中数据的实际表示,而应该通过操作接口来进行访问,这称为信息的隐藏

==属性私有,get/set==

好处

  1. 提高程序的安全性,保护数据
  2. 隐藏代码的实现细节
  3. 统一接口
  4. 提高系统的可维护性

继承

extends

  • 继承是类和类之间的关系
  • 继承关系的两个类,一个是父类,一个是子类
  • 在Java中,所有的类,都默认直接或者间接继承Object类

control+h 可以看结构

java类中只有单继承,没有多继承

私有的可以被继承,但拒绝被访问(无法继承)

调用弗雷德构造器,必须要在子类的第一行

1
2
3
4
5
6
7
8
9
10
11
12
package com.oop.demo05;

//子类继承了父类,就会拥有父类的全部方法
public class Student extends Person {
private String name = "dacy";

public void test(String name) {
System.out.println(name);
System.out.println(this.name);
System.out.println(super.name);
}
}

super 注意点

  1. super调用父类的构造方法,必须在狗仔方法的第一个
  2. super 必须只能出现在子类的方法或者构造方法中!
  3. super 和this不能同时调用构造方式

VS this:

  • 代表的对象不同:

    this: 本身调用者的这个对象

    super:代表父类对象的应用

  • 前提

    this:没有继承也可以使用

    super:只能在继承条件下才可以使用

  • 构造方法

    this() 本类的构造

    super() :父类的构造

重写

需要有继承关系,子类重写父类的方法!

  1. 方法名称必须相同
  2. 参数列表必须相同
  3. 修饰符:范围可以扩大:public>Protected>default>private
  4. 抛出的异常:范围,可以被缩小,但不能扩大:ClassNotFoundException –> Exception

重写,子类的方法和父类的必须要一致:方法体不同

为什么需要重写:

  1. 父类的功能,子类不一定需要,或者不一定满足!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.oop.demo05;

public class Application {
public static void main(String[] args) {
// 方法的调用只和左边,定义的数据类型有关
// 静态方法: 方法的调用只和左边,定义的数据类型有关

// 非静态:重写
A a = new A();
a.test();

// 父类的引用指向了子类
B b = new A(); // 子类重写了父类的方法
b.test();
}

}

抽象

抽象概念

05-抽象的概念

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 cn.itcast.day09.demo11;

/*
抽象方法:就是加上abstract关键字,然后去掉大括号,直接分号结束。
抽象类:抽象方法所在的类,必须是抽象类才行。在class之前写上abstract即可。

如何使用抽象类和抽象方法:
1. 不能直接创建new抽象类对象。
2. 必须用一个子类来继承抽象父类。
3. 子类必须覆盖重写抽象父类当中所有的抽象方法。
覆盖重写(实现):子类去掉抽象方法的abstract关键字,然后补上方法体大括号。
4. 创建子类对象进行使用。
*/
public abstract class Animal {

// 这是一个抽象方法,代表吃东西,但是具体吃什么(大括号的内容)不确定。
public abstract void eat();

// 这是普通的成员方法
// public void normalMethod() {
//
// }

}
1
2
3
4
5
6
7
8
9
10
package cn.itcast.day09.demo11;

public class Cat extends Animal {

@Override
public void eat() {
System.out.println("猫吃鱼");
}

}
1
2
3
4
5
6
7
8
9
10
11
12
package cn.itcast.day09.demo11;

public class DemoMain {

public static void main(String[] args) {
// Animal animal = new Animal(); // 错误写法!不能直接创建抽象类对象

Cat cat = new Cat();
cat.eat();
}

}

关于抽象类的使用,以下为语法上要注意的细节,虽然条目较多,但若理解了抽象的本质,无需死记硬背。

  1. 抽象类不能创建对象,如果创建,编译无法通过而报错。只能创建其非抽象子类的对象。

理解:假设创建了抽象类的对象,调用抽象的方法,而抽象方法没有具体的方法体,没有意义。

  1. 抽象类中,可以有构造方法,是供子类创建对象时,初始化父类成员使用的。

理解:子类的构造方法中,有默认的super(),需要访问父类构造方法。

  1. 抽象类中,不一定包含抽象方法,但是有抽象方法的类必定是抽象类。

理解:未包含抽象方法的抽象类,目的就是不想让调用者创建该类对象,通常用于某些特殊的类结构设计。

  1. 抽象类的子类,必须重写抽象父类中所有的抽象方法,否则,编译无法通过而报错。除非该子类也是抽象类。

理解:假设不重写所有抽象方法,则类中可能包含抽象方法。那么创建对象后,调用抽象的方法,没有

意义。

抽象类

1
2
3
4
5
6
//1. 不能new这个抽象类,只能靠子类去实现它:约束
//2. 抽象类中可以写普通的方法~
//3. 抽象方法必须在抽象类中~
// 抽象的抽象:约束

// 存在的意义,提高开发效率
1
2
3
4
5
6
7
8
9
10
11
12
13
package com.oop.demo08;

// abstract 抽象类:类 extends: 单继承 ~ 接口可以多继承 插座
public abstract class Action {
// 约束~有人帮我实现~
// abstract,抽象方法,只有方法名字,没有方法的实现!
public abstract void doSomething();

//1. 不能new这个抽象类,只能靠子类去实现它:约束
//2. 抽象类中可以写普通的方法~
//3. 抽象方法必须在抽象类中~
// 抽象的抽象:约束
}

接口

本质:契约

接口:interface

作用:

  1. 约束
  2. 定义一些方法,让不同的人实现
  3. public abstract final
  4. 接口不能被实例化,接口中没有构造方法~
  5. implements可以实现多个接口
  6. 必须要重写接口中的方法

01-生活中接口的举例

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
package cn.itcast.day10.demo01;

/*
接口就是多个类的公共规范。
接口是一种引用数据类型,最重要的内容就是其中的:抽象方法。

如何定义一个接口的格式:
public interface 接口名称 {
// 接口内容
}

备注:换成了关键字interface之后,编译生成的字节码文件仍然是:.java --> .class。

如果是Java 7,那么接口中可以包含的内容有:
1. 常量
2. 抽象方法

如果是Java 8,还可以额外包含有:
3. 默认方法
4. 静态方法

如果是Java 9,还可以额外包含有:
5. 私有方法

接口使用步骤:
1. 接口不能直接使用,必须有一个“实现类”来“实现”该接口。
格式:
public class 实现类名称 implements 接口名称 {
// ...
}
2. 接口的实现类必须覆盖重写(实现)接口中所有的抽象方法。
实现:去掉abstract关键字,加上方法体大括号。
3. 创建实现类的对象,进行使用。

注意事项:
如果实现类并没有覆盖重写接口中所有的抽象方法,那么这个实现类自己就必须是抽象类。
*/
public class Demo01Interface {

public static void main(String[] args) {
// 错误写法!不能直接new接口对象使用。
// MyInterfaceAbstract inter = new MyInterfaceAbstract();

// 创建实现类的对象使用
MyInterfaceAbstractImpl impl = new MyInterfaceAbstractImpl();
impl.methodAbs1();
impl.methodAbs2();
}

}
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 cn.itcast.day10.demo01;

/*
在任何版本的Java中,接口都能定义抽象方法。
格式:
public abstract 返回值类型 方法名称(参数列表);

注意事项:
1. 接口当中的抽象方法,修饰符必须是两个固定的关键字:public abstract
2. 这两个关键字修饰符,可以选择性地省略。(今天刚学,所以不推荐。)
3. 方法的三要素,可以随意定义。
*/
public interface MyInterfaceAbstract {

// 这是一个抽象方法
public abstract void methodAbs1();

// 这也是抽象方法
abstract void methodAbs2();

// 这也是抽象方法
public void methodAbs3();

// 这也是抽象方法
void methodAbs4();

}

接口使用步骤:

  1. 接口不能直接使用,必须有一个“实现类”来“实现”该接口。
    格式:
    public class 实现类名称 implements 接口名称 {
    // …
    }
  2. 接口的实现类必须覆盖重写(实现)接口中所有的抽象方法。
    实现:去掉abstract关键字,加上方法体大括号。
  3. 创建实现类的对象,进行使用。

注意事项:
如果实现类并没有覆盖重写接口中所有的抽象方法,那么这个实现类自己就必须是抽象类。

默认方法Default

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
package cn.itcast.day10.demo01;

/*
从Java 8开始,接口里允许定义默认方法。
格式:
public default 返回值类型 方法名称(参数列表) {
方法体
}

备注:接口当中的默认方法,可以解决接口升级的问题。
*/
public interface MyInterfaceDefault {

// 抽象方法
public abstract void methodAbs();

// 新添加了一个抽象方法
// public abstract void methodAbs2();

// 新添加的方法,改成默认方法
public default void methodDefault() {
System.out.println("这是新添加的默认方法");
}

}
1
2
3
4
5
6
7
8
package cn.itcast.day10.demo01;

public class MyInterfaceDefaultA implements MyInterfaceDefault {
@Override
public void methodAbs() {
System.out.println("实现了抽象方法,AAA");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
package cn.itcast.day10.demo01;

public class MyInterfaceDefaultB implements MyInterfaceDefault {
@Override
public void methodAbs() {
System.out.println("实现了抽象方法,BBB");
}

@Override
public void methodDefault() {
System.out.println("实现类B覆盖重写了接口的默认方法");
}
}

Demo02-interface

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package cn.itcast.day10.demo01;

/*
1. 接口的默认方法,可以通过接口实现类对象,直接调用。
2. 接口的默认方法,也可以被接口实现类进行覆盖重写。
*/
public class Demo02Interface {

public static void main(String[] args) {
// 创建了实现类对象
MyInterfaceDefaultA a = new MyInterfaceDefaultA();
a.methodAbs(); // 调用抽象方法,实际运行的是右侧实现类。

// 调用默认方法,如果实现类当中没有,会向上找接口
a.methodDefault(); // 这是新添加的默认方法
System.out.println("==========");

MyInterfaceDefaultB b = new MyInterfaceDefaultB();
b.methodAbs();
b.methodDefault(); // 实现类B覆盖重写了接口的默认方法
}

}

接口的静态关键词

注意事项:不能通过接口实现类的对象来调用接口当中的静态方法。
正确用法:通过接口名称,直接调用其中的静态方法。
格式:
接口名称.静态方法名(参数);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package cn.itcast.day10.demo01;

/*
从Java 8开始,接口当中允许定义静态方法。
格式:
public static 返回值类型 方法名称(参数列表) {
方法体
}
提示:就是将abstract或者default换成static即可,带上方法体。
*/
public interface MyInterfaceStatic {

public static void methodStatic() {
System.out.println("这是接口的静态方法!");
}

}

Final 关键字

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package cn.itcast.day10.demo01;

/*
接口当中也可以定义“成员变量”,但是必须使用public static final三个关键字进行修饰。
从效果上看,这其实就是接口的【常量】。
格式:
public static final 数据类型 常量名称 = 数据值;
备注:
一旦使用final关键字进行修饰,说明不可改变。

注意事项:
1. 接口当中的常量,可以省略public static final,注意:不写也照样是这样。
2. 接口当中的常量,必须进行赋值;不能不赋值。
3. 接口中常量的名称,使用完全大写的字母,用下划线进行分隔。(推荐命名规则)
*/
public interface MyInterfaceConst {

// 这其实就是一个常量,一旦赋值,不可以修改
public static final int NUM_OF_MY_CLASS = 12;

}

多继承

  1. 类与类之间是单继承的。直接父类只有一个。
  2. 类与接口之间是多实现的。一个类可以实现多个接口。
  3. 接口与接口之间是多继承的。

注意事项:

  1. 多个父接口当中的抽象方法如果重复,没关系。
  2. 多个父接口当中的默认方法如果重复,那么子接口必须进行默认方法的覆盖重写,【而且带着default关键字】。

接口小结

在Java 9+版本中,接口的内容可以有:

  1. 成员变量其实是常量,格式:
    [public] [static] [final] 数据类型 常量名称 = 数据值;
    注意:
    常量必须进行赋值,而且一旦赋值不能改变。
    常量名称完全大写,用下划线进行分隔。
  2. 接口中最重要的就是抽象方法,格式:
    [public] [abstract] 返回值类型 方法名称(参数列表);
    注意:实现类必须覆盖重写接口所有的抽象方法,除非实现类是抽象类。
  3. 从Java 8开始,接口里允许定义默认方法,格式:
    [public] default 返回值类型 方法名称(参数列表) { 方法体 }
    注意:默认方法也可以被覆盖重写
  4. 从Java 8开始,接口里允许定义静态方法,格式:
    [public] static 返回值类型 方法名称(参数列表) { 方法体 }
    注意:应该通过接口名称进行调用,不能通过实现类对象调用接口静态方法
  5. 从Java 9开始,接口里允许定义私有方法,格式:
    普通私有方法:private 返回值类型 方法名称(参数列表) { 方法体 }
    静态私有方法:private static 返回值类型 方法名称(参数列表) { 方法体 }
    注意:private的方法只有接口自己才能调用,不能被实现类或别人使用。

==使用接口的时候,需要注意:==

  1. 接口是没有静态代码块或者构造方法的。
  2. 一个类的直接父类是唯一的,但是一个类可以同时实现多个接口。
    格式:
    public class MyInterfaceImpl implements MyInterfaceA, MyInterfaceB {
    // 覆盖重写所有抽象方法
    }
  3. 如果实现类所实现的多个接口当中,存在重复的抽象方法,那么只需要覆盖重写一次即可。
  4. 如果实现类没有覆盖重写所有接口当中的所有抽象方法,那么实现类就必须是一个抽象类。
  5. 如果实现类锁实现的多个接口当中,存在重复的默认方法,那么实现类一定要对冲突的默认方法进行覆盖重写。
  6. 一个类如果直接父类当中的方法,和接口当中的默认方法产生了冲突,优先用父类当中的方法。```

狂神java Demo

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
package com.oop.demo09;

// 抽象类: extends
// 类可以实现接口 implement 接口
// 实现了接口的类,就需要重写接口中的方法

// 多继承~利用接口实现多继承~

public class UserServiceImpl implements UserService,TimeService{

@Override
public void run(String name) {

}

@Override
public void delete(String name) {

}

@Override
public void update(String name) {

}

@Override
public void query(String name) {

}

@Override
public void timer() {

}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.oop.demo09;

// 抽象的思维 Java
// interface 是定义的关键字,接口都需要有实现的类
public interface UserService {
// 接口中的所有定义其实都是抽象的public
//常量
// public static int final AGE = 99;

void run(String name);
void delete(String name);
void update(String name);
void query(String name);


}

多态

03-多态的概述

代码当中体现多态性,其实就是一句话:父类引用指向子类对象。

格式:

1
父类名称 对象名 = new 子类名称();

或者:

1
接口名称 对象名 = new 实现类名称();

或者:
接口名称 对象名 = new 实现类名称();

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
package cn.itcast.day10.demo04;

/*
代码当中体现多态性,其实就是一句话:父类引用指向子类对象。

格式:
父类名称 对象名 = new 子类名称();
或者:
接口名称 对象名 = new 实现类名称();
*/
public class Demo01Multi {

public static void main(String[] args) {
// 使用多态的写法
// 左侧父类的引用,指向了右侧子类的对象
Fu obj = new Zi();

obj.method();
obj.methodFu();
}
}

// 父类方法

package cn.itcast.day10.demo04;

public class Fu {

public void method() {
System.out.println("父类方法");
}

public void methodFu() {
System.out.println("父类特有方法");
}

}

// 子类方法
package cn.itcast.day10.demo04;

public class Zi extends Fu {

@Override
public void method() {
System.out.println("子类方法");
}
}


访问成员变量

两种方式

  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
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
package cn.itcast.day10.demo05;

/*
访问成员变量的两种方式:

1. 直接通过对象名称访问成员变量:看等号左边是谁,优先用谁,没有则向上找。
2. 间接通过成员方法访问成员变量:看该方法属于谁,优先用谁,没有则向上找。
*/
public class Demo01MultiField {

public static void main(String[] args) {
// 使用多态的写法,父类引用指向子类对象
Fu obj = new Zi();
System.out.println(obj.num); // 父:10
// System.out.println(obj.age); // 错误写法!
System.out.println("=============");

// 子类没有覆盖重写,就是父:10
// 子类如果覆盖重写,就是子:20
obj.showNum();
}

}

package cn.itcast.day10.demo05;

public class Fu /*extends Object*/ {

int num = 10;

public void showNum() {
System.out.println(num);
}

public void method() {
System.out.println("父类方法");
}

public void methodFu() {
System.out.println("父类特有方法");
}

}

package cn.itcast.day10.demo05;

public class Zi extends Fu {

int num = 20;

int age = 16;

@Override
public void showNum() {
System.out.println(num);
}

@Override
public void method() {
System.out.println("子类方法");
}

public void methodZi() {
System.out.println("子类特有方法");
}
}

成员方法的访问规则

在多态的代码当中,成员方法的访问规则是:
看new的是谁,就优先用谁,没有则向上找。

口诀:编译看左边,运行看右边。

编译:编译通过代码就不会出现红线;不通过IDE会出现红色的字

对比一下:
成员变量:编译看左边,运行还看左边。
成员方法:编译看左边,运行看右边。

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 cn.itcast.day10.demo05;

/*
在多态的代码当中,成员方法的访问规则是:
看new的是谁,就优先用谁,没有则向上找。

口诀:编译看左边,运行看右边。

对比一下:
成员变量:编译看左边,运行还看左边。
成员方法:编译看左边,运行看右边。
*/
public class Demo02MultiMethod {

public static void main(String[] args) {
Fu obj = new Zi(); // 多态

obj.method(); // 父子都有,优先用子
obj.methodFu(); // 子类没有,父类有,向上找到父类

// 编译看左边,左边是Fu,Fu当中没有methodZi方法,所以编译报错。
// obj.methodZi(); // 错误写法!
}

}

使用多态的好处

04-使用多态的好处

向上转型 & 向下转型

05-对象的上下转型

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
package cn.itcast.day10.demo06;

/*
向上转型一定是安全的,没有问题的,正确的。但是也有一个弊端:
对象一旦向上转型为父类,那么就无法调用子类原本特有的内容。

解决方案:用对象的向下转型【还原】。
*/
public class Demo01Main {

public static void main(String[] args) {
// 对象的向上转型,就是:父类引用指向之类对象。
Animal animal = new Cat(); // 本来创建的时候是一只猫
animal.eat(); // 猫吃鱼

// animal.catchMouse(); // 错误写法!

// 向下转型,进行“还原”动作
Cat cat = (Cat) animal;
cat.catchMouse(); // 猫抓老鼠

// 下面是错误的向下转型
// 本来new的时候是一只猫,现在非要当做狗
// 错误写法!编译不会报错,但是运行会出现异常:
// java.lang.ClassCastException,类转换异常
Dog dog = (Dog) animal;
}

}

package cn.itcast.day10.demo06;

public abstract class Animal {

public abstract void eat();

}

package cn.itcast.day10.demo06;

public class Cat extends Animal {
@Override
public void eat() {
System.out.println("猫吃鱼");
}

// 子类特有方法
public void catchMouse() {
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
40
41
package cn.itcast.day10.demo06;

/*
如何才能知道一个父类引用的对象,本来是什么子类?
格式:
对象 instanceof 类名称
这将会得到一个boolean值结果,也就是判断前面的对象能不能当做后面类型的实例。
*/
public class Demo02Instanceof {

public static void main(String[] args) {
Animal animal = new Dog(); // 本来是一只狗
animal.eat(); // 狗吃SHIT

// 如果希望掉用子类特有方法,需要向下转型
// 判断一下父类引用animal本来是不是Dog
if (animal instanceof Dog) {
Dog dog = (Dog) animal;
dog.watchHouse();
}
// 判断一下animal本来是不是Cat
if (animal instanceof Cat) {
Cat cat = (Cat) animal;
cat.catchMouse();
}

giveMeAPet(new Dog());
}

public static void giveMeAPet(Animal animal) {
if (animal instanceof Dog) {
Dog dog = (Dog) animal;
dog.watchHouse();
}
if (animal instanceof Cat) {
Cat cat = (Cat) animal;
cat.catchMouse();
}
}

}

狂神说java部分笔记

  • 动态编译:类型:可扩展性更强
  • 统一方法可以根据发送对象的不同而采取不同的行为方式
  • 一个对象的实际类型是确定的,但可以指向对象的引用类型有很多(父类,有关系的类)

多态存在的条件

  1. 有继承的关系
  2. 子类重写父类方法
  3. 父类引用指向子类的对象

注意事项

  1. 多态是方法的多态,属性没有多态
  2. 父类和子类,有联系 类型转换异常!ClassCastException
  3. 存在的条件:继承关系,方法需要重写,父类的引用指向子类对象
    1. static 方法,属于累,它不属于实例
    2. final 常量
    3. private 方法

instanceof (类型转换) 引用类型,判断一个对象是什么类型

  1. 父类引用指向子类的对象
  2. 把子类转换为父类,向上转型;可以直接转过去
  3. 把父类转换为子类,向下转型,强制转换
  4. 方便方法的调用,减少重复的代码!简洁
  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
package com.oop.demo06;

public class Application {
public static void main(String[] args) {
// 一个对象的实际类型是确定的
//new Student();
// new Person();

// 可以指向的引用类型就不确定了:父类的引用指向了子类

// Student 能调用的方法都是自己的或者继承父类的
Student s1 = new Student();
//Person 父类型,但是不能调用子类独有的方法
Person s2 = new Student();
Object s3 = new Student();

// 对象能执行哪些方法,主要看对象左边的类型,和右边关系不大

s2.run(); // 子类重写了父亲的方法。执行子类的方法
((Student)s2).eat(); //子类重写类父类的方法,执行了子类的方法
s1.run();

}
}
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
package com.oop.demo06;

public class Application {
public static void main(String[] args) {

//Object>String
//Obj>Person>Student
//Obj>Person>Teacher

//System.out.println(X instanceof Y); // 能不能编译通过!判断X和Y是否有父子关系
System.out.println("=================================");
Object object = new Student();
System.out.println(object instanceof Student);
System.out.println(object instanceof Person);
System.out.println(object instanceof Object);
System.out.println(object instanceof Teacher);
System.out.println(object instanceof String);
System.out.println("=================================");

Person person = new Student();
System.out.println(person instanceof Student);
System.out.println(person instanceof Object);
System.out.println(person instanceof Teacher);
// System.out.println(person instanceof String); 编译报错
System.out.println("=================================");

Student student = new Student();
System.out.println(student instanceof Student);
System.out.println(student instanceof Object);
// System.out.println(student instanceof Teacher);编译报错
// System.out.println(student instanceof String); 编译报错

}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.oop.demo06;

public class Application {
public static void main(String[] args) {
Person obj = new Student();
//obj 将这个对象转换为Student类,我们就可以使用Student类型的方法了
// 高 > 低 转需要强制转换

//子类转换成父类,可能丢失自己本来的一些方法
Student student = (Student) obj;
student.go();

((Student)obj).go(); // 想要用子类的方法就直接强制转换就行啦
}
}

接口多态的综合案例

案例分析

06-笔记本电脑案例分析

https://github.com/yuxuanwu17/USB_demo

修饰符

在Java中提供了四种访问权限,使用不同的访问权限修饰符修饰时,被修饰的内容会有不同的访问权限,

1
2
3
4
5
6
7
8
public:公共的。

protected:受保护的

default:默认的

private:私有的

Java中有四种权限修饰符:

public > protected > (default) > private

public protected (default) private
同一个类(我自己) YES YES YES YES
同一个包(我邻居) YES YES YES NO
不同包子类(我儿子) YES YES NO NO
不同包非子类(陌生人) YES NO NO NO

注意事项:(default)并不是关键字“default”,而是根本不写。

内部类

什么是内部类

将一个类A定义在另一个类B里面,里面的那个类A就称为内部类,B则称为外部类。

成员内部类 :定义在类中方法外的类。

定义格式:

1
2
3
4
5
6
7
class 外部类 {

class 内部类{

}

}

【外部类名称.内部类名称 对象名 = new 外部类名称().new 内部类名称();】

代码示例

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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
package cn.itcast.day11.demo03;

/*
如果一个事物的内部包含另一个事物,那么这就是一个类内部包含另一个类。
例如:身体和心脏的关系。又如:汽车和发动机的关系。

分类:
1. 成员内部类
2. 局部内部类(包含匿名内部类)

成员内部类的定义格式:
修饰符 class 外部类名称 {
修饰符 class 内部类名称 {
// ...
}
// ...
}

注意:内用外,随意访问;外用内,需要内部类对象。

==========================
如何使用成员内部类?有两种方式:
1. 间接方式:在外部类的方法当中,使用内部类;然后main只是调用外部类的方法。
2. 直接方式,公式:
类名称 对象名 = new 类名称();
【外部类名称.内部类名称 对象名 = new 外部类名称().new 内部类名称();】
*/
public class Demo01InnerClass {

public static void main(String[] args) {
Body body = new Body(); // 外部类的对象
// 通过外部类的对象,调用外部类的方法,里面间接在使用内部类Heart
body.methodBody();
System.out.println("=====================");

// 按照公式写:
Body.Heart heart = new Body().new Heart();
heart.beat();
}

}

//================================================================================
package cn.itcast.day11.demo03;

public class Body { // 外部类

public class Heart { // 成员内部类

// 内部类的方法
public void beat() {
System.out.println("心脏跳动:蹦蹦蹦!");
System.out.println("我叫:" + name); // 正确写法!
}

}

// 外部类的成员变量
private String name;

// 外部类的方法
public void methodBody() {
System.out.println("外部类的方法");
new Heart().beat();
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
}

//================================================================================


package cn.itcast.day11.demo03;

// 如果出现了重名现象,那么格式是:外部类名称.this.外部类成员变量名
public class Outer {

int num = 10; // 外部类的成员变量

public class Inner /*extends Object*/ {

int num = 20; // 内部类的成员变量

public void methodInner() {
int num = 30; // 内部类方法的局部变量
System.out.println(num); // 局部变量,就近原则
System.out.println(this.num); // 内部类的成员变量
System.out.println(Outer.this.num); // 外部类的成员变量
}

}

}

局部内部类

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 cn.itcast.day11.demo04;

/*
局部内部类,如果希望访问所在方法的局部变量,那么这个局部变量必须是【有效final的】。

备注:从Java 8+开始,只要局部变量事实不变,那么final关键字可以省略。

原因:
1. new出来的对象在堆内存当中。
2. 局部变量是跟着方法走的,在栈内存当中。
3. 方法运行结束之后,立刻出栈,局部变量就会立刻消失。
4. 但是new出来的对象会在堆当中持续存在,直到垃圾回收消失。
*/
public class MyOuter {

public void methodOuter() {
int num = 10; // 所在方法的局部变量

class MyInner {
public void methodInner() {
System.out.println(num);
}
}
}

}
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 cn.itcast.day11.demo04;

/*
如果一个类是定义在一个方法内部的,那么这就是一个局部内部类。
“局部”:只有当前所属的方法才能使用它,出了这个方法外面就不能用了。

定义格式:
修饰符 class 外部类名称 {
修饰符 返回值类型 外部类方法名称(参数列表) {
class 局部内部类名称 {
// ...
}
}
}

小节一下类的权限修饰符:
public > protected > (default) > private
定义一个类的时候,权限修饰符规则:
1. 外部类:public / (default)
2. 成员内部类:public / protected / (default) / private
3. 局部内部类:什么都不能写
*/
class Outer {

public void methodOuter() {
class Inner { // 局部内部类
int num = 10;
public void methodInner() {
System.out.println(num); // 10
}
}

Inner inner = new Inner();
inner.methodInner();
}

}

局部内部类的final问题

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 cn.itcast.day11.demo04;

/*
局部内部类,如果希望访问所在方法的局部变量,那么这个局部变量必须是【有效final的】。

备注:从Java 8+开始,只要局部变量事实不变,那么final关键字可以省略。

原因:
1. new出来的对象在堆内存当中。
2. 局部变量是跟着方法走的,在栈内存当中。
3. 方法运行结束之后,立刻出栈,局部变量就会立刻消失。
4. 但是new出来的对象会在堆当中持续存在,直到垃圾回收消失。
*/
public class MyOuter {

public void methodOuter() {
/*final*/ int num = 10; // 所在方法的局部变量

class MyInner {
public void methodInner() {
System.out.println(num);
}
}
}

}

匿名类部类

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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
package cn.itcast.day11.demo05;

/*
如果接口的实现类(或者是父类的子类)只需要使用唯一的一次,
那么这种情况下就可以省略掉该类的定义,而改为使用【匿名内部类】。

匿名内部类的定义格式:
接口名称 对象名 = new 接口名称() {
// 覆盖重写所有抽象方法
};

对格式“new 接口名称() {...}”进行解析:
1. new代表创建对象的动作
2. 接口名称就是匿名内部类需要实现哪个接口
3. {...}这才是匿名内部类的内容

另外还要注意几点问题:
1. 匿名内部类,在【创建对象】的时候,只能使用唯一一次。
如果希望多次创建对象,而且类的内容一样的话,那么就需要使用单独定义的实现类了。
2. 匿名对象,在【调用方法】的时候,只能调用唯一一次。
如果希望同一个对象,调用多次方法,那么必须给对象起个名字。
3. 匿名内部类是省略了【实现类/子类名称】,但是匿名对象是省略了【对象名称】
强调:匿名内部类和匿名对象不是一回事!!!
*/
public class DemoMain {

public static void main(String[] args) {
// MyInterface obj = new MyInterfaceImpl();
// obj.method();

// MyInterface some = new MyInterface(); // 错误写法!

// 使用匿名内部类,但不是匿名对象,对象名称就叫objA
MyInterface objA = new MyInterface() {
@Override
public void method1() {
System.out.println("匿名内部类实现了方法!111-A");
}

@Override
public void method2() {
System.out.println("匿名内部类实现了方法!222-A");
}
};
objA.method1();
objA.method2();
System.out.println("=================");

// 使用了匿名内部类,而且省略了对象名称,也是匿名对象
new MyInterface() {
@Override
public void method1() {
System.out.println("匿名内部类实现了方法!111-B");
}

@Override
public void method2() {
System.out.println("匿名内部类实现了方法!222-B");
}
}.method1();
// 因为匿名对象无法调用第二次方法,所以需要再创建一个匿名内部类的匿名对象
new MyInterface() {
@Override
public void method1() {
System.out.println("匿名内部类实现了方法!111-B");
}

@Override
public void method2() {
System.out.println("匿名内部类实现了方法!222-B");
}
}.method2();
}

}

/*
My interface
*/
package cn.itcast.day11.demo05;

public interface MyInterface {

void method1(); // 抽象方法

void method2();

}

/*
My interface implemention
*/
package cn.itcast.day11.demo05;

public class MyInterfaceImpl implements MyInterface {
@Override
public void method1() {
System.out.println("实现类覆盖重写了方法!111");
}

@Override
public void method2() {
System.out.println("实现类覆盖重写了方法!222");
}
}


接口作为变量传递

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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
package cn.itcast.day11.demo07;

public class Hero {

private String name; // 英雄的名称
private Skill skill; // 英雄的技能

public Hero() {
}

public Hero(String name, Skill skill) {
this.name = name;
this.skill = skill;
}

public void attack() {
System.out.println("我叫" + name + ",开始施放技能:");
skill.use(); // 调用接口中的抽象方法
System.out.println("施放技能完成。");
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public Skill getSkill() {
return skill;
}

public void setSkill(Skill skill) {
this.skill = skill;
}
}

/*==================*/
package cn.itcast.day11.demo07;

public interface Skill {

void use(); // 释放技能的抽象方法

}
/*==================*/


package cn.itcast.day11.demo07;

public class SkillImpl implements Skill {
@Override
public void use() {
System.out.println("Biu~biu~biu~");
}
}

/*==================*/


package cn.itcast.day11.demo07;

import java.util.ArrayList;
import java.util.List;

/*
java.util.List正是ArrayList所实现的接口。
*/
public class DemoInterface {

public static void main(String[] args) {
// 左边是接口名称,右边是实现类名称,这就是多态写法
List<String> list = new ArrayList<>();

List<String> result = addNames(list);
for (int i = 0; i < result.size(); i++) {
System.out.println(result.get(i));
}
}

public static List<String> addNames(List<String> list) {
list.add("迪丽热巴");
list.add("古力娜扎");
list.add("玛尔扎哈");
list.add("沙扬娜拉");
return list;
}

}
/*==================*/


package cn.itcast.day11.demo07;

public class DemoGame {

public static void main(String[] args) {
Hero hero = new Hero();
hero.setName("艾希"); // 设置英雄的名称

// 设置英雄技能
// hero.setSkill(new SkillImpl()); // 使用单独定义的实现类

// 还可以改成使用匿名内部类
// Skill skill = new Skill() {
// @Override
// public void use() {
// System.out.println("Pia~pia~pia~");
// }
// };
// hero.setSkill(skill);

// 进一步简化,同时使用匿名内部类和匿名对象
hero.setSkill(new Skill() {
@Override
public void use() {
System.out.println("Biu~Pia~Biu~Pia~");
}
});

hero.attack();
}

}

抢红包实例

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
package cn.itcast.day11.demo08;

import cn.itcast.day11.red.OpenMode;

/*
场景说明:
红包发出去之后,所有人都有红包,大家抢完了之后,最后一个红包给群主自己。
大多数代码都是现成的,我们需要做的就是填空题。
我们自己要做的事情有:
1. 设置一下程序的标题,通过构造方法的字符串参数
2. 设置群主名称
3. 设置分发策略:平均,还是随机?

红包分发的策略:
1. 普通红包(平均):totalMoney / totalCount,余数放在最后一个红包当中。
2. 手气红包(随机):最少1分钱,最多不超过平均数的2倍。应该越发越少。
*/
public class Bootstrap {

public static void main(String[] args) {
MyRed red = new MyRed("传智播客双元课程");
// 设置群主名称
red.setOwnerName("王思聪");

// 普通红包
// OpenMode normal = new NormalMode();
// red.setOpenWay(normal);

// 手气红包
OpenMode random = new RandomMode();
red.setOpenWay(random);
}

}

狂神说java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.oop.demo10;

public class Outer {
private int id=10;
public void out() {
System.out.println("这是外部类的方法");
}

public class Inner {
public void in() {
System.out.println("这是内部类的方法");
}

// 获得外部类的私有属性~
public void getID() {
System.out.println(id);
}
}
}
1
2
3
4
5
6
7
8
9
10
11
package com.oop.demo10;

public class Application {
public static void main(String[] args) {
Outer outer = new Outer();
// 通过这个外部类来实例化内部类
Outer.Inner inner = outer.new Inner();
inner.in();

}
}

一个java类中可以有多个class类,但是只能有一个public class

异常处理

image-20210215111612243

image-20210215111941652

异常的处理机制

抛出/捕获异常

Idea 快捷键 option+command+T

主动跑出异常

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.exception;

import com.oop.demo07.Test;

public class Test2 {
public static void main(String[] args) {

new Test2().test(1, 0);
}

public void test(int a ,int b) {
if (b == 0) {
throw new ArithmeticException();
}
}
}

java protected关键字: 同包不同级

  • 基类的 protected 成员是包内可见的,并且对子类可见;
  • 若子类与基类不在同一包中,那么在子类中,子类实例可以访问其从基类继承而来的protected方法,而不能访问基类实例的protected方法
  • Post title:Java基础知识 回顾
  • Post author:Yuxuan Wu
  • Create time:2021-02-10 04:24:23
  • Post link:yuxuanwu17.github.io2021/02/10/2021-02-10-Java-回顾/
  • Copyright Notice:All articles in this blog are licensed under BY-NC-SA unless stating additionally.