JavaSE
基础
基础 01 注释
-
单行注释
-
// 单行注释
-
-
多行注释
-
/* 多行注释 */
-
-
文档注释
-
/** *@Description Hello World *@Author */
-
基础 02 标识符、关键字
-
关键字
-
abstract; boolean; break; byte; case; catch; char; class; continue; default; do; double; else; extends; false; final; finally; float; for; if; implements; import; instanceof; int; interface; long; native; new; null; package; private; protected; public; return; short; static; super; switch; synchronized; this; throw; throws; transient; true; try; void; volatile; while
-
-
-
标识符
- 规则见[Java开发手册(黄山版).pdf]
基础 03 数据类型
-
基本数据类型
-
数值类型
-
整数类型
byte
占 1 个字节,min:-128(-27)max:127(27-1)short
占 2 个字节,min:-32768(-215)max:32767(215-1)int
占 4 个字节,默认值 0,min:-2,147,483,648(-231)max:2,147,483,647(231-1)long
占 8 个字节,min:-9,233,372,036,854,775,808(-263)max:9,223,372,036,854,775,807(263-1)
-
浮点类型
float
占 4 个字节double
占 8 个字节
-
字符类型
char
占 2 个字节,默认值null
-
-
boollean
类型:占 1 位,其值只有true
和false
两个,默认值false
-
-
引用数据类型
- 类
- 接口
- 数组
基础 04 数据类型拓展
-
整数
- 二进制 0b
- 十进制
- 八进制 0
- 十六进制 0x
-
浮点数
- 最好完全避免使用浮点数进行比较
- 银行业务怎么表示? ==> BigDecimal 数学工具类
-
字符
- 所有字符的本质还是数字
- 编码 Unicode 表:(97 = a 65 = A)
- 2 字节 65536
- U0000 UFFFF
-
转义字符
- \t 制表符
- \n 换行符
- \a 响铃
- \b 退格
- \f 换页
- \r 回车
- \v 垂直制表
- \\ 反斜线字符
- \’ 单引号(撇号)字符
- \" 双引号字符
- \0 空字符
基础 05 类型转换
低 -----------------------------------------------------------------------> 高
byte
-> short
, char
-> int
-> long
-> float
-> double
-
强制类型转换
- (类型)变量名 高 --> 低
-
自动类型转换
- 低 --> 高
-
注意点
-
不能对布尔值进行转换
-
不能把对象类型转换为不相干的类型
-
在把高容量转换到低容量的时候,强制转换
-
转换的时候可能存在内存溢出,或者精度问题
-
//JDK7新特性,数字之间可以用下划线分割 int money = 10_0000_0000; int years = 20; int total = money*years; // -1474836480,计算的时候溢出了 long total2 = money*years; // 还是-1474836480,因为右边还是int类型,转换之前已经存在问题了 long total3 = money*((long)years); // 2000000000,先把一个数转换为long,则全部转换为long
-
基础 06 变量、常量、作用域
-
变量
-
类变量
-
实例变量(从属于对象)
-
局部变量(从属于方法,必须声明和初始化值)
-
public class Variable { static int allClick = 0; //类变量 String str = "Hello world"; //实例变量 public void method() { int i = 0; //局部变量 } }
-
-
常量(Constant)
-
初始化(initialize)后不能再改变值
-
常量名一般使用大写字符
-
final double PI = 3.14;
-
-
命名规范
- 所有变量、方法、类名:见名知意
- **类成员变量:**首字母小写和驼峰原则:monthSalary
- **局部变量:**首字母小写和驼峰原则
- **常量:**大写字母和下划线:MAX_VALUE
- **类名:**首字母大写和驼峰原则:Man,GoodMan
- **方法名:**首字母小写和驼峰原则:run(),runRun()
基础 07 运算符
-
算术运算符 +, -, *, /, %, ++, –
-
int
以下精度运算(short
,byte
),结果自动转化为int
-
幂运算 2^3
-
double pow = Math.pow(2,3);
-
-
赋值运算符 =
-
关系运算符 >, <, >=, <=, ==, !=, instanceof
-
逻辑运算符 &&, ||, !
-
注意短路运算
-
boollean a = true; boollean b = false; System.out.println(a && b); //false,先执行判断a,后执行判断b System.out.println(b && a); //false,先执行判断b,直接短路,不执行判断a
-
-
位运算符 &, |, ^, ~, >>, <<, >>>
-
&, |, ~, ^:位运算的与,或,非,异或
-
A = 0011 1100 B = 0000 1101 A & B //与,0000 1100 A | B //或,0011 1101 A ^ B //异或,0011 0001 ~ B //非,1111 0010
-
2 × 8 最快运算方法?
-
<<
左移,相当于 × 2 -
>>
右移,相当于 ÷ 2 -
System.out.println(2 << 3);
-
-
-
条件运算符 ? :
-
扩展赋值运算符 +=, -=, *=, /=
基础 08 包机制
-
为了更好地组织类,Java 提供了包机制,用于区别类名的命名空间。
-
包语句的语法格式为:
-
package pkg1[. pkg2[. pkg3...]];
-
-
一般利用公司域名倒置作为包名;
-
为了能够使用某一个包的成员,需要在Java程序中明确导入该包,使用
import
语句可完成此功能:-
import package1[. package2...].(classname|*);
-
基础 09 javadoc
生成文档
javadoc
命令是用来生成自己 API 文档的- 参数信息
@author
作者名@version
版本号@since
指明需要最早使用的jdk版本@param
参数名@return
返回值情况@throws
异常抛出情况
流程控制
流程控制 01 用户交互Scanner
-
基本语法
-
Scanner scanner = new Scanner(System.in);
-
-
通过
Scanner
类的next()
与nextLine()
方法获取输入的字符串,在读取前一般需要使用hasNext()
与hasNextLine()
判断是否还有输入的数据。-
import java.util.Scanner; public class Hello { 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(); } }
-
-
next()
:- 一定要读取到有效字符后才可以结束输入
- 对输入有效字符之前遇到的空白,
next()
方法会自动将其去掉 - 只有输入有效字符后才将其后面输入的空白作为分隔符或者结束符
next()
不能得到带有空格的字符串
-
nextLine()
:- 以 Enter 为结束符,也就是说
nextLine()
方法返回的是输入回车之前的所有字符 - 可以获得空格
- 以 Enter 为结束符,也就是说
流程控制 02 Scanner
进阶使用
-
输入整数
-
import java.util.Scanner; public class Hello { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); int i; if (scanner.hasNextInt()) { i = scanner.nextInt(); System.out.println(i); } else { System.out.println("输入的不是整数。"); } scanner.close(); } }
-
-
输入多个数字,并求其总和与平均数,每输入一个数字用回车确认,通过输入非数字来结束输入并输出执行结果
-
import java.util.Scanner; public class Hello { public static void main(String[] args) { double sum = 0.0D; int m = 0; Scanner scanner = new Scanner(System.in); while (scanner.hasNextDouble()) { sum += scanner.nextDouble(); m++; } System.out.println("总和为:" + sum + "\n平均数为:" + sum / m); scanner.close(); } }
-
流程控制 03 顺序结构
- Java 的基本结构就是顺序结构,除非特别指明,否则就按顺序逐句执行
流程控制 04 if
选择结构
-
if
单选择结构-
if (布尔表达式) { // 如果布尔表达式的值为 true 执行代码 }
-
-
if
双选择结构-
if (布尔表达式) { // 如果布尔表达式的值为 true 执行代码 } esle { // 如果布尔表达式的值为 false 执行代码 }
-
-
if
多选择结构-
// 一旦其中一个 else if 语句检测为 true,其他的 else if 以及 else 语句都将跳过执行 if (布尔表达式 1) { // 如果布尔表达式 1 的值为 true 执行代码 } esle if (布尔表达式 2) { // 如果布尔表达式 2 的值为 true 执行代码 } esle if (布尔表达式 3) { // 如果布尔表达式 3 的值为 true 执行代码 } esle { // 如果以上布尔表达式的值均为 false 执行代码 }
-
-
嵌套的
if
结构-
if (布尔表达式 1) { // 如果布尔表达式 1 的值为 true 执行代码 if(布尔表达式 2) { // 如果布尔表达式 2 的值为 true 执行代码 } }
-
流程控制 05 switch
多选择结构
-
switch
语句中的变量类型可以是byte
、short
、int
或者char
-
从 Java SE 7 开始,
switch
支持字符串String
类型了,同时case
标签必须为字符串常量或字面量-
switch (expression) { case value: // 语句 break; // 可选 case value: // 语句 break; // 可选 default: // 可选 // 语句 }
-
-
case
穿透现象:从满足条件的case value
开始,执行后面所有的case
,直到遇到break
停止
流程控制 06 while
循环
-
while
是最基本的循环,其结构为:-
while (布尔表达式) { // 循环内容 }
-
-
只要布尔表达式为
true
循环就会一直执行下去 -
大多数情况会让循环停止下来,需要一个让表达式失效的方式来结束循环
-
少部分情况需要循环一直执行,比如服务器的请求响应监听等
流程控制 07 do
…while
循环
-
对于
while
语句而言,如果不满足条件,则不能进入循环 -
do
…while
循环和while
循环相似,不同的是,do
…while
循环至少会执行一次-
do { // 代码语句 } while (布尔表达式);
-
流程控制 08 for
循环
-
for
循环执行的次数在执行前就确定,语法格式如下:-
for (初始化;布尔表达式;更新) { // 代码语句 }
-
-
IDEA 快捷键:
100.for
后回车,自动生成:-
for (int i = 0; i < 100; i++) { }
-
-
练习1:计算0到100之间的奇数和偶数的和
-
public static void main(String[] args) { int oddsum = 0; int evensum = 0; for (int i = 0; i < 100; i++) { if (i % 2 != 0) { oddsum += i; } else { evensum += i; } } System.out.println("奇数的和:" + oddsum); System.out.println("偶数的和:" + evensum); }
-
-
练习2:用
while
或for
循环输出 1-1000 之间能被 5 整除的数,每行输出 3 个-
public static void main(String[] args) { for (int i = 1; i < 1000; i++) { if (i % 5 == 0) { System.out.print(i + "\t"); } if (i % 15 == 0) { System.out.println(); } } }
-
-
练习3:打印九九乘法表
-
public static void main(String[] args) { for (int i = 1; i <= 9; i++) { for (int j = 1; j <= i; j++) { System.out.print(j+"×"+i+"="+j*i+"\t"); } System.out.println(); } }
-
流程控制 09 增强for
循环
-
Java 5 引入了一种主要用于数组或集合的增强型
for
循环 -
Java 增强
for
循环语法格式如下:-
for (声明语句 : 表达式) { // 代码句子 }
-
-
声明语句:声明新的局部变量,该变量的类型必须和数组元素的类型匹配。其作用域限定在循环语句块,其值与此时数组元素的值相等。
-
表达式:表达式是要访问的数组名,或者是返回值为数组的方法。
-
public static void main(String[] args) { int[] numbers = {10, 20, 30, 40, 50}; for (int x : numbers) { System.out.println(x); } }
-
流程控制 10break
、continue
、goto
-
在任何循环语句的主体部分,均可用
break
控制循环的流程。break
用于强行退出循环,不执行循环中剩余的语句。-
public static void main(String[] args) { int i = 0; while (i < 100) { i++; System.out.println(i); if (i == 30) { break; // 打破 while 循环,至此只输出到 30 } } System.out.println("123"); // 证明只是打破 while 循环,函数仍在执行。 }
-
-
continue
用于终止某次循环过程,即跳过循环体中尚未执行的语句,接着进行下一次是否执行循环的判定。-
public static void main(String[] args) { int i = 0; while (i < 100) { i++; if (i % 10 == 0) { System.out.println(); continue; // 后面输出 i 不执行,直接进入下一次 while 循环 } System.out.print(i); } }
-
-
关于
goto
关键字-
goto
关键字很早就在程序设计语言中出现。尽管goto
仍是 Java 的一个保留字,但并未在语言中得到正式使用 —— Java 没有goto
。然而,在break
和continue
这两个关键字的身上,我们仍能看出一些goto
的影子 —— 带标签的break
和continue
。例如label:
-
// 输出 101 到 150 之间的质数 public static void main(String[] args) { outer: for (int i = 101; i < 150; i++) { for (int j = 2; j < i/2; j++) { if (i % j == 0) { continue outer; // 直接跳转到 outer: } } System.out.print(i + " "); } }
-
-
对 Java 来说唯一用到标签的地方是在循环语句之前。而在循环之前设置标签的唯一理由是:我们希望在其中嵌套另一个循环,由于
break
和continue
关键字通常只中断当前循环,但若随同标签使用,它们就会终端到存在标签的地方。
流程控制 12 打印三角形及菱形
-
打印三角形
-
import java.util.Scanner; public class Hello { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); int n = scanner.nextInt(); for (int i = 0; i < n; i++) { for (int j = 0; j < n-i-1; j++) { System.out.print(" "); } for (int j = 0; j < 2*i+1; j++) { System.out.print("*"); } System.out.println(); } } }
-
-
打印菱形
-
import java.util.Scanner; public class Hello { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); int n = scanner.nextInt(); for (int i = 0; i < 2*n-1; i++) { if (i < n) { for (int j = 0; j < n-i-1; j++) { System.out.print(" "); } for (int j = 0; j < 2*i+1; j++) { System.out.print("*"); } } else { for (int j = 0; j < i-n+1; j++) { System.out.print(" "); } for (int j = 0; j < 4*n-2*i-3; j++) { System.out.print("*"); } } System.out.println(); } } }
-
方法
方法 01 什么是方法
- Java 方法是语句的集合,它们在一起执行一个功能
- 方法是解决一类问题的步骤的有序组合
- 方法包含于类或对象中
- 方法在程序中被创建,在其他地方被引用
- 设计方法的原则:
- 方法的本意是功能块,即实现某个功能的语句块的集合
- 最好保持方法的原子性, 即一个方法只完成一个功能,以利于后期扩展
- 方法的命名规则:
- 首字母小写驼峰原则
方法 02 方法的定义及调用
-
方法的定义
-
修饰符:可选,告诉编译器如何调用该方法。定义了该方法的访问类型。
-
返回值类型:
returnValueType
是方法返回值的数据类型。有些方法没有返回值,在此情况下,returnValueType
是关键字void
。 -
方法名:方法的实际名称。方法名和参数表共同构成方法签名。
-
参数类型:当方法被调用时,传递值给参数。这个值被称为实参或变量。参数列表是指方法的参数类型、顺序和参数的个数。参数是可选的,方法可以不包含任何参数。
-
形式参数
-
实参
-
-
方法体:包含具体的语句,定义该方法的功能。
-
修饰符 返回值类型 方法名(参数类型 参数名) { …… 方法体 …… return 返回值; }
-
-
方法调用
-
调用方法:对象名.方法名(实参列表)
-
Java 支持两种调用方法的方式,根据方法是否返回值来选择
-
当方法返回一个值的时候,方法调用通常被当作一个值
-
int larger = max(30, 40);
-
-
如果方法返回值是
void
,方法调用一定是一条语句-
System.out.println("Hello, world!");
-
-
值传递(Java) 和 引用传递
-
方法 03 方法的重载
- 重载就是在一个类中,有相同的函数名称,但形参不同的函数
- 方法的重载规则:
- 方法名称必须相同
- 参数列表必须不同(个数不同、类型不同、参数排列顺序(需要形参类型不同为前提)不同等)
- 方法的返回类型可以相同也可以不同
- 仅仅返回类型不同不足以成为方法的重载
- 实现理论
- 方法名称相同时,编译器会根据调用方法的参数个数、参数类型等去逐个匹配,以选择对应的方法,如果匹配失败,则编译器报错。
方法 04 命令行传递参数
-
运行一个程序时候再传递给它消息,需要靠传递命令行参数给
main()
方法实现-
package method; public class Demo03 { public static void main(String[] args) { for (int i = 0; i < args.length; i++) { System.out.println("args[" + i + "]: " + args[i]); } } }
-
D:\Coding\Java\Learning>javac Demo03 错误: 仅当显式请求注释处理时才接受类名称 'Demo03' 1 个错误 D:\Coding\Java\Learning>cd D:\Coding\Java\Learning\src\method D:\Coding\Java\Learning\src\method>javac Demo03.java D:\Coding\Java\Learning\src\method>java Demo03 错误: 找不到或无法加载主类 Demo03 D:\Coding\Java\Learning\src\method>cd..\ D:\Coding\Java\Learning\src>java method.Demo03 D:\Coding\Java\Learning\src>java method.Demo03 this is Enchant_sword args[0]: this args[1]: is args[2]: Enchant_sword D:\Coding\Java\Learning\src>
-
方法 05 可变参数(不定项参数)
-
JDK 1.5 开始,Java 支持传递同类型的可变参数给一个方法
-
在方法声明中,在指定参数类型后加一个省略号(…)
-
一个方法中只能指定一个可变参数,它必须是方法的最后一个参数。任何普通的参数必须在它之前声明。
-
package 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 = 1; i < numbers.length; i++) { if (numbers[i] > result) { result = numbers[i]; } } System.out.println("The max value is " + result); } }
-
方法 06 递归
-
递归头:什么时候不调用自身方法。如果没有头,将陷入死循环。
-
递归体:什么时候需要调用自身方法。
-
package method; public class Demo06 { public static void main(String[] args) { System.out.println(f(5)); } public static int f(int n) { if (n == 1) { //递归头 return 1; } else { //递归体,阶乘 return n * f(n-1); } } }
-
数组
数组 01 数组声明创建
-
声明数组变量
-
dataTypep[] arrayRefVar; // 首选方法 或 dataTypep arrayRefVar[]; // 效果相同,但非首选方法
-
-
Java 使用
new
操作符来创建数组-
dataTypep[] arrayRefVar = new dataType[arraySize];
-
-
数组通过索引访问,数组索引从 0 开始
-
获取数组长度:
-
arrays.length
-
package array; public class Demo01 { public static void main(String[] args) { int[] nums; // 1.声明1个数组 nums = new int[10]; // 2.创建1个数组 nums[0] = 1; // 3.给数组中元素赋值 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; int sum = 0; for (int i = 0; i < nums.length; i++) { // 增强型 for 循环: for (int num : nums) sum += nums[i]; } System.out.println("总和为:" + sum); } }
-
数组 02 内存分析 及 三种初始化
-
Java 内存分析:
-
堆
- 存放
new
的对象和数组 - 可以被所有的线程共享,不会存放别的对象引用
- 存放
-
栈
- 存放基本变量类型(会包含这个基本类型的具体数值)
- 引用对象的变量(会存放这个引用在堆里面的具体地址)
-
方法区
- 可以被所有的线程共享
- 包含了所有的
class
和static
变量
-
-
三种初始化
-
静态初始化
-
int[] a = {1,2,3,4,5,6,7,8}; Man[] men = {new Man(1,1), new Man(2,2)};
-
动态初始化
-
int[] a = new int[2]; a[0] = 1; a[1] = 2;
-
-
数组的默认初始化
- 数组是引用类型,它的元素相当于类的实例变量,因此数组一经分配空间,其中的每个元素也被按照实例变量同样的方式被隐式初始化。
-
-
数组 03 下标越界 及 小结
-
数组的四个基本特点
- 长度确定。数组一旦被创建,其长度不可改变。
- 数组元素必须是相同类型,不允许出现混合类型。
- 数组元素可以是任何数据类型,包括基本类型和引用类型。
- 数组变量属引用类型,数组也可以看成是对象,数组中的每个元素相当于该对象的成员变量。数组本身就是对象,Java 中对象是在堆中的,因此数组无论保存原始类型还是其他对象类型,数组对象本身是在堆中的。
-
数组边界
- 下标的合法区间:[0, length-1],如果越界就会报错
ArrayIndexOutOfBoundsException
:数组下标越界异常!
数组 04 数组的使用
-
普通的
for
循环 -
for - each
循环 (增强型for
循环) -
数组作方法入参
-
数组作返回值
-
package array; public class Demo03 { public static void main(String[] args) { int[] arrays = {1,2,3,4,5}; printArray(arrays); printArray(reverseArray(arrays)); } // 打印数组元素(数组作方法入参) public static void printArray(int[] arrays) { // 普通的 for 循环 for (int i = 0; i < arrays.length; i++) { // 增强的 for 循环: for (int i : arrays) { System.out.print(arrays[i] + " "); } } } // 反转数组(数组作返回值) public static int[] reverseArray(int[] arrays) { int[] result = new int[arrays.length]; for (int i = 0; i < arrays.length; i++) { result[arrays.length - i - 1] = arrays[i]; } return result; } }
-
数组 05 多维数组
-
多维数组可以看成是数组的数组,比如二维数组就是一个特殊的一维数组,其每一个元素都是一个一维数组
-
二维数组
-
// 可视作两行五列的数组 int a[][] = new int[2][5]
-
package array; public class Demo04 { public static void main(String[] args) { // [4][2] int[][] array = {{1,2},{2,3},{3,4},{4,5}}; for (int i = 0; i < array.length; i++) { for (int j = 0; j < array[i].length; j++) { System.out.print(array[i][j] + "\t"); } System.out.println(); } } }
-
数组 06 Arrays
类
-
Arrays
类中的方法都是static
修饰的静态方法,在使用的时候可以直接使用类名进行调用,而不用使用对象来调用(注意:是“不用”而非“不能”) -
具有以下常用功能:
-
给数组赋值:通过
fill
方法 -
对数组排序:通过
sort
方法,按升序 -
比较数组:通过
equals
方法比较数组中元素值是否相等 -
查找数组元素:通过
binarySearch
方法能对排序好的数组进行二分查找法操作 -
package array; import java.util.Arrays; public class Demo05 { public static void main(String[] args) { int[] a = {4,7,23,534,76,334,2,56,67,33,45}; // 数组升序排序 Arrays.sort(a); // 转成字符串输出 System.out.println(Arrays.toString(a)); // 将数组下标[2]到下标[8-1]的元素用 0 填充 Arrays.fill(a,2,8,0); // 转成字符串输出 System.out.println(Arrays.toString(a)); } }
-
数组 07 冒泡排序
-
嵌套循环,时间复杂度为 O(n^2)
-
package array; import java.util.Arrays; public class Demo06 { public static void main(String[] args) { int[] array = {3,5,756,23,5,4334,65,445,6,4,3,56}; int[] sort = sort(array); System.out.println(Arrays.toString(sort)); } public static int[] sort(int[] a) { // 临时变量 int temp; // 外层循环,判断循环执行次数 for (int i = 0; i < a.length - 1; i++) { // 内层循环,比较两个数大小,并互换位置 for (int j = 0; j < a.length - 1 - i ; j++) { // 改为 < 则降序排序 if (a[j] > a[j+1]) { /* 核心代码,若不引入临时变量 temp,也可简化为 a[j] += a[j+1]; a[j+1] = a[j] - a[j+1]; a[j] -= a[j+1]; */ temp = a[j]; a[j] = a[j+1]; a[j+1] = temp; } } } return a; } }
-
-
优化算法
-
for (int i = 0; i < a.length - 1; i++) { // 放置标识位,减少无意义的比较 boollean flag = false; for (int j = 0; j < a.length - 1 - i ; j++) { if (a[j] > a[j+1]) { temp = a[j]; a[j] = a[j+1]; a[j+1] = temp; // 只要发生过 1 次 if 交换,则 flag 赋值 true,后面继续比较 flag = true; } } if (!flag) { break; } }
-
数组 08 稀疏数组
-
当一个数组中大部分元素为 0,或者为同一值时,可以使用稀疏数组来保存
-
稀疏数组的处理方式是:
-
记录数组一共有几行几列,有多少个不同值
-
把具有不同值的元素和行列及值记录在一个小规模的数组中,从而缩小程序的规模
-
原始数组:
[000220015011000170000−6000000003909100000000280000] \left [ \begin{array}{c} 0 & 0 & 0 & 22 & 0 & 0 & 15 \\ 0 & 11 & 0 & 0 & 0 & 17 & 0 \\ 0 & 0 & 0 & -6 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 39 & 0 \\ 91 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 28 & 0 & 0 & 0 & 0 \\ \end{array} \right ] ⎣⎡000091001100000000028220−6000000000017039001500000⎦⎤ -
稀疏数组:
[行列值(row)(col)(value)[0]678[1]0322[2]0615[3]1111[4]1517[5]23−6[6]3539[7]4091[8]5228] \left [ \begin{array}{c|cc} & 行 & 列 & 值 \\ & (row) & (col) & (value) \\ \hline [0] & 6 & 7 & 8 \\ [1] & 0 & 3 & 22 \\ [2] & 0 & 6 & 15 \\ [3] & 1 & 1 & 11 \\ [4] & 1 & 5 & 17 \\ [5] & 2 & 3 & -6 \\ [6] & 3 & 5 & 39 \\ [7] & 4 & 0 & 91 \\ [8] & 5 & 2 & 28 \\ \end{array} \right ] ⎣⎡[0][1][2][3][4][5][6][7][8]行(row)600112345列(col)736153502值(value)822151117−6399128⎦⎤ -
原始数组和稀疏数组互相转换
-
package array; public class Demo07 { 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; // 输出原始的数组(①使用 2 层增强型 for 循环) System.out.println("输出原始的数组"); for (int[] ints : array1) { for (int anInt : ints) { System.out.print(anInt + "\t"); } System.out.println(); } // 转换为稀疏数组保存 // 1.获取有效值的个数 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.创建一个稀疏数组的数组 // 因第 0 行存放数组信息,稀疏数组行数为 sum + 1 int[][] array2 = new int[sum + 1][3]; // 列数固定为 3,分别为行坐标、列坐标、值 array2[0][0] = array1.length; array2[0][1] = array1.length; array2[0][2] = sum; // 3.遍历二维数组,将非零的值存放于稀疏数组中 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]; } } } // 4.输出转换后的稀疏数组(②使用 1 层常规 for 循环) System.out.println("输出转换后的稀疏数组"); for (int[] item : array2) { System.out.println(item[0] + "\t" + item[1] + "\t" + item[2] + "\t"); } // 还原为原始数组 System.out.println("还原为原始数组"); int rowArray3 = array2[0][0]; int colArray3 = array2[0][1]; int countArray3 = array2[0][2]; int[][] array3 = new int[rowArray3][colArray3]; for (int i = 0; i < countArray3; i++) { int row = array2[i + 1][0]; int col = array2[i + 1][1]; int value = array2[i + 1][2]; array3[row][col] = value; } // 输出还原后的原始数组(③使用 2 层常规 for 循环) for (int i = 0; i < array3.length; i++) { for (int j = 0; j < array3[i].length; j++) { System.out.print(array3[i][j] + "\t"); } System.out.println(); } } }
-
-
面向对象
面向对象 01 何为面向对象
-
面向过程 & 面向对象
-
面向过程思想
-
步骤清晰简单,第一步、第二步……
-
适合处理较为简单的问题
-
面向对象思想
- 分类的思维模式,先思考解决问题需要哪些分类,然后对这些分类进行单独思考,最后才对某个分类的细节进行面向过程的思索。
- 面向对象适合处理复杂的问题、需要多人协作的问题。
-
-
描述复杂的事物,为从宏观上把握、从整体上分析,需要使用面向对象的思路。但具体到微观操作,仍需要面向过程的思路去处理。
-
-
何为面向对象
-
面向对象编程(Object - Oriented Programming, OOP)
-
面向对象编程的本质是:以类的方式组织代码,以对象的方式组织(封装)数据。
-
抽象
- 三大特性
- 封装
- 继承
- 多态
- 三大特性
-
从认识论角度考虑,是先有对象后有类。对象是具体的事物;类是对对象的抽象。
-
从代码运行角度考虑,是先有类后有对象。类是对象的模板。
-
面向对象 02 方法的定义
- 修饰符
- 返回类型
break
和return
的区别:break
结束循环;return
结束方法。- 方法名:驼峰原则、见名知意
- 参数列表:(参数类型,参数名)…(可变参数)
- 异常抛出
面向对象 03 方法的调用
-
静态方法
-
package oop; public class Demo02 { public static void main(String[] args) { // 调用静态方法 // 类名.方法名 Student.say1(); } }
-
-
非静态方法
-
package oop; public class Demo02 { public static void main(String[] args) { // 调用非静态方法 // 对象类型 对象名 = new 对象值 Student student = new Student(); student.say2(); } }
-
-
形参和实参
-
值传递和引用传递
-
package oop; // 值传递 public class Demo04 { public static void main(String[] args) { int a = 1; new Demo04().change(a); System.out.println(a); // 输出 a = 1,而非 a = 10 } public void change(int a) { a = 10; } }
-
package oop; // 引用传递 public class Demo05 { public static void main(String[] args) { Person aaa = new Person(); System.out.println(aaa.name);// 输出 null Demo05.change(aaa); System.out.println(aaa.name);// 输出 狗蛋 } public static void change(Person bbb) { bbb.name = "狗蛋"; } } // 定义了一个 Person 类,有一个属性:name class Person { String name; // 默认初始值 null }
-
-
-
-
this
关键字 -
面向对象 04 类 与 对象
-
类与对象的关系
-
类是一种抽象的数据类型,它是对某一类事物的整体描述 / 定义,但是并不能代表某一个具体的事物。
- 动物、植物、手机、电脑……
- Person 类、Pet 类、Car 类等,这些类都是用来描述 / 定义某一类具体的事物应该具备的特征
-
对象是抽象概念的具体实例
- “张三”是“人”的具体实例
- 能体现出特点、展现出功能,而非抽象概念
- “张三”是“人”的具体实例
-
-
创建与初始化对象
-
使用
new
关键字创建对象 -
使用
new
关键字创建的时候,除了分配内存空间之外,还会给创建好的对象进行默认初始化,以及调用类中构造器。 -
类中构造器也称为构造方法,是创建对象时必须要调用的。构造器有以下特点:
- 必须和类的名字相同
- 必须没有返回类型,也不能写
void
-
package oop.demo; // 一个项目应该只存在一个 main 方法 public class Application { public static void main(String[] args) { // 类:抽象的,实例化 // 类实例化后会返回一个自己的对象 // goudan 对象就是一个 Student 类的具体实例 Student goudan = new Student(); goudan.name = "狗蛋"; goudan.age = 3; System.out.println(goudan.name); System.out.println(goudan.age); new Student().study(); } }
-
package oop.demo; // 学生类 public class Student { // 属性:字段 String name; int age; // 方法 public void study() { System.out.println(this.name + "在学习"); } }
-
面向对象 05 构造器详解
-
构造器:
- 和类名相同
- 没有返回值
-
作用:
new
(本质在调用构造方法)- 初始化对象的值
-
注意点:
-
定义有参构造后,若想使用无参构造,需显式地定义一个无参构造
-
构造快捷键 alt + ins
-
this. =
(this 代表本方法) -
package oop.demo; // 一个项目应该只存在一个 main 方法 public class Application { public static void main(String[] args) { // new 实例化了一个对象 // 1.使用 new 关键字,本质是在调用构造器 // 2.用来初始化值 Person person = new Person("狗蛋"); System.out.println(person.name); } }
-
package oop.demo; public class Person { // 一个类即使什么都不写,它也会存在一个方法 String name; // 显式地定义无参构造器则如下 public Person() { } // 有参构造:一旦定义了有参构造,无参就必须显式定义 // 本质是 Person 方法的重载 public Person(String name) { this.name = name; } // 快捷键 alt + insert 自动生成构造器 }
-
面向对象 06 创建对象内存分析
面向对象 07 类与对象小结
- 类与对象
- 类是一个抽象的模板;对象是一个具体的实例。
- 方法
- 定义
- 调用
- 对象的引用
- 引用类型:8 大基本类型之外基本都是引用类型
- 对象是通过引用来操作的:栈 —> 堆(地址)
- 属性:字段(Field)成员变量
- 默认初始化
- 数字:0 0.0
- char:u0000
- boolean:false
- 引用:null
- 修饰符 属性类型 属性名 = 属性值
- 默认初始化
- 对象的创建和使用
- 必须使用
new
关键字创造对象(Person goudan = new Person();
) - 对象的属性
goudan.name
- 对象的方法
goudan.sleep()
- 必须使用
- 类:
- 静态的属性 属性
- 动态的行为 方法
面向对象 08 封装
-
该露的露,该藏的藏
- 程序设计追求**“高内聚,低耦合”**。高内聚:类的内部数据操作细节自己完成,不允许外部干涉;低耦合:仅暴露少量的方法给外部使用。
-
封装(数据的隐藏)
- 通常,应禁止直接访问一个对象中数据的实际表示,应通过操作接口来访问。
-
属性私有,
get
/set
-
package oop.demo; // 一个项目应该只存在一个 main 方法 public class Application { public static void main(String[] args) { Student s1 = new Student(); s1.setName("狗蛋"); System.out.println(s1.getName()); s1.setAge(18); System.out.println(s1.getAge()); } }
-
package oop.demo; // 学生类 public class Student { private String name; private int age; // alt + insert 快捷键,设置 setter & getter 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; } }
-
面向对象 09 继承
-
继承的本质是对某一批类的抽象,从而实现更好地建模
-
extends
(扩展),子类是父类的扩展 -
Java 中只有单继承,没有多继承
-
继承是类和类之间的一种关系。除此之外,类和类之间的关系还有依赖、组合、聚合等
-
继承关系的两个类,一个为子类(派生类),一个为父类(基类)。子类继承父类,使用关键字
extends
来表示。 -
子类和父类之间,从意义上讲应该具有 “is a” 的关系
-
package oop.demo; public class Application { public static void main(String[] args) { Student s1 = new Student(); //Student 类已继承 Person 类的全部方法 s1.say(); } }
-
public class Person { // public // protected // default // private private int money = 10_0000_0000; // Person 类的 say() 方法 public void say() { System.out.println("说了一句话"); } public void setMoney(int money) { this.money = money; } public int getMoney() { return money; } }
-
// extends 关键字,继承 Person 类的 say() 方法 public class Student extends Person { }
-
面向对象 10 super
详解
-
super
调用父类的属性-
package oop.demo; public class Application { public static void main(String[] args) { Student s1 = new Student(); s1.test1("狗蛋"); // print "狗蛋" \n "学生" \n "人类" } }
-
package oop.demo; public class Person { protected String name = "人类"; }
-
package oop.demo; // Student 类继承 Person 类 public class Student extends Person { private String name = "学生"; public void test1(String name) { System.out.println(name); // 局部变量 形式参数name("狗蛋") System.out.println(this.name); // 实例变量 "学生" System.out.println(super.name); // 实例变量 "人类" } }
-
-
super
调用父类的方法-
package oop.demo; public class Application { public static void main(String[] args) { Student s1 = new Student(); s1.test2(); // print "学生" \n "学生" \n "人类" } }
-
package oop.demo; public class Person { public void print() { System.out.println("人类"); } }
-
package oop.demo; public class Student extends Person { public void print() { System.out.println("学生"); } public void test2() { print(); // "学生" this.print(); // "学生" super.print(); // "人类" } }
-
-
super
调用构造器(无参)-
package oop.demo; public class Application { public static void main(String[] args) { // 虽然只调用了 Student 类,但因为 Student 类 extends 了 Person 类,故会先生成父类 Person 的构造器,再生成子类 Student 的构造器 Student s1 = new Student(); // print "人类 无参" \n "学生 无参" } }
-
package oop.demo; public class Person { // 显式无参构造 public Person() { System.out.println("人类 无参"); } }
-
package oop.demo; public class Student extends Person { // 显式无参构造 public Student() { // 隐藏代码 super(); ,调用了父类 Person 的无参构造。调用父类的构造器,必须要在子类构造器的第一行 System.out.println("学生 无参"); } }
-
-
super
调用构造器(有参)-
package oop.demo; public class Application { public static void main(String[] args) { // 调用子类 有参构造器 Student s1 = new Student("狗蛋"); // print "人类" \n "狗蛋" } }
-
package oop.demo; public class Person { // 父类 有参构造器 public Person(String name) { System.out.println(name); } }
-
package oop.demo; public class Student extends Person { // 子类 显式无参构造器 public Student() { super("人类"); System.out.println("学生 无参"); } // 子类 有参构造器 // 因父类构造器有参,必须用 super("String") 调用,否则报错 public Student(String name) { super("人类"); System.out.println(name); } }
-
-
小结
super
注意点:super
调用父类的构造方法,必须在构造方法的第一个super
只能出现在子类的方法或构造方法中super
和this
不能同时调用构造方法
- VS
this
:- 代表的对象不同
this
:本身调用者这个对象super
:代表父类对象的引用
- 前提
this
:没有继承也可以使用super
:只能在继承条件下使用
- 构造方法
this()
:本类的构造super()
:父类的构造
- 代表的对象不同
面向对象 11 方法重写
-
静态方法调用
-
package oop.demo; public class Application { public static void main(String[] args) { // 静态方法的调用只和左边定义的数据类型(B)有关 B b = new B(); b.test(); // print "B --> test()" // 父类的引用指向了子类 A a = new B(); a.test(); // print "A --> test()" } }
-
package oop.demo; public class A { // static 静态方法 public static void test() { System.out.println("A --> test()"); } }
-
package oop.demo; public class B extends A { // static 静态方法 public static void test() { System.out.println("B --> test()"); } }
-
-
非静态方法重写
-
package oop.demo; public class Application { public static void main(String[] args) { B b = new B(); b.test(); // print "B --> test()" // 父类的引用指向了子类 A a = new B(); // 子类重写了父类的方法 a.test(); // print "B --> test()" } }
-
package oop.demo; public class A { // 非静态方法 public void test() { System.out.println("A --> test()"); } }
-
package oop.demo; public class B extends A { @Override // 注解:有功能的注释 // 非静态方法 public void test() { System.out.println("B --> test()"); } }
-
-
小结
- 重写:需要有继承关系,子类重写父类的方法
- 方法名必须相同
- 参数列表必须相同
- 修饰符:范围可以扩大
private
<default
<protected
<public
- 抛出的异常:范围可以被缩小,但不能扩大
- 为何需要重写:
- 父类的功能,子类不一定需要,或者不一定满足
- 快捷键 alt + insert:
override
- 重写:需要有继承关系,子类重写父类的方法
面向对象 12 多态
-
即同一方法可以根据发送对象的不同而采用多种不同的行为方式
-
一个对象的实际类型是确定的,但可以指向对象的引用的类型有很多
-
多态是方法的多态,属性没有多态性
-
instanceof
类型转换(引用类型) -
重写前的多态
-
package oop.demo; public class Application { public static void main(String[] args) { Student s1 = new Student(); Person s2 = new Student(); Object s3 = new Student(); // 子类型 Student 的对象 s1 可以直接调用父类型 Person 的方法 s1.run(); // print "人类 跑" s2.run(); // print "人类 跑" } }
-
package oop.demo; public class Person { public void run() { System.out.println("人类 跑"); } }
-
package oop.demo; public class Student extends Person { }
-
-
重写后的多态,拓展
-
package oop.demo; public class Application { public static void main(String[] args) { Student s1 = new Student(); Person s2 = new Student(); // 父类的引用指向子类 Object s3 = new Student(); // run() 已被重写 s2.run(); // print "重写为:飞" // s2.eat();报错:父类型 Person 的对象 s2 不能直接调用子类型 Student 的方法,需强制转换类型(Person 转 Student) ((Student) s2).eat(); // print "吃" }
-
package oop.demo; public class Person { public void run() { System.out.println("跑"); } }
-
package oop.demo; public class Student extends Person { // 重写 run() @Override public void run() { System.out.println("重写为:飞"); } public void eat() { System.out.println("吃"); } }
-
面向对象 13 instanceof
和类型转换
-
instanceof
是 Java 的保留关键字,其作用是:测试左边的对象是否是右边的类的实例,返回boolean
的数据类型-
比如
Person X = new Student()
,我们知道左边 Person 是引用类型,右边 Student 是实际类型 -
X instanceof Y
,若 X 的引用类型(Person)和 Y 有没有继承关系,则编译报错 -
若 X 的实际类型(Student)是 Y 本身,或 Y 的子类,则返回 true,否则返回 false
-
若 X 的实际类型是 Y 的父类,也返回 false
-
package oop.demo; public class Application { public static void main(String[] args) { // Object > String // Object > Person > Teacher // Object > Person > Student Object o = new Student(); System.out.println(o instanceof Student); // true System.out.println(o instanceof Person); // true System.out.println(o instanceof Object); // true System.out.println(o instanceof Teacher); // false System.out.println(o instanceof String); // false Person p = new Student(); System.out.println(p instanceof Student); // true System.out.println(p instanceof Person); // true System.out.println(p instanceof Object); // true System.out.println(p instanceof Teacher); // 引用类型 Person 和 Teacher 无继承关系,编译报错 System.out.println(p instanceof String); // 引用类型 Person 和 String 无继承关系,编译报错 Student s = new Student(); System.out.println(s instanceof Student); // true System.out.println(s instanceof Person); // true System.out.println(s instanceof Object); // true System.out.println(s instanceof Teacher); // 引用类型 Student 和 Teacher 无继承关系,编译报错 System.out.println(s instanceof String); // 引用类型 Student 和 String 无继承关系,编译报错 } }
-
package oop.demo; public class Person { }
-
package oop.demo; public class Student extends Person { }
-
package oop.demo; public class Teacher { }
-
-
基本类型不能用
instanceof
判断
面向对象 14 static
关键字详解
-
静态属性
-
package oop.demo; public class Student { private static int age; // 静态变量 private double score; // 非静态变量 public static void main(String[] args) { Student s1 = new Student(); System.out.println(Student.age); System.out.println(Student.score); // 报错 System.out.println(s1.age); System.out.println(s1.score); } }
-
-
静态方法
-
package oop.demo; public class Student { // 静态方法 public static void run() { } // 非静态方法 public void go() { } public static void main(String[] args) { run(); go(); // 报错 } }
-
-
静态代码块
-
package oop.demo; public class Student { // 执行顺序第 2 ,可用来赋初始值 { System.out.println("匿名代码块"); // 代码块(匿名代码块) } // 执行顺序第 1 ,只执行 1 次 static { System.out.println("静态代码块"); // 静态代码块 } // 执行顺序第 3 public Student() { System.out.println("构造方法"); // 构造器 } public static void main(String[] args) { Student s1 = new Student(); // print "静态代码块" \n "匿名代码块" \n "构造方法" \n Student s2 = new Student(); // print "匿名代码块" \n "构造方法" \n } }
-
-
静态导入包
-
package oop.demo; import static java.lang.Math.random; // 静态导入 Math.random() 方法 import static java.lang.Math.PI; // 静态导入 Math.PI 常量 public class Student { public static void main(String[] args) { System.out.println(random()); System.out.println(PI); } }
-
面向对象 15 抽象类
-
abstract
修饰符可以用来修饰方法(抽象方法),也可以用来修饰类(抽象类)。 -
抽象类中可以没有抽象方法,但有抽象方法的类一定要声明为抽象类。
-
抽象类不能使用
new
关键字来创建对象,它是用来让子类继承的。 -
抽象方法只有声明,没有实现,它是用来让子类实现的。
-
子类继承抽象类,则必须要实现抽象类未实现的抽象方法,否则该子类也要声明为抽象类
-
package oop.demo; public abstract class Person { public abstract void doSomething(); // 抽象方法只有声明,没有方法体 }
-
package oop.demo; public abstract class Student extends Person { // 子类继承抽象类,重写实现抽象类未实现的抽象方法 @Override public void doSomething() { System.out.println("抽象类"); } }
-
面向对象 16 接口
-
普通类:只有具体实现
-
抽象类:具体实现和规范(抽象方法)都有
-
接口:只有规范
-
接口的本质是契约
-
声明类的关键字是
class
,声明接口的关键字是interface
-
package oop.demo; public interface UserService { // 接口中所有定义的属性其实都是静态常量 public static final int AGE = 99; // 接口中所有定义的方法其实都是抽象的 public abstract void add(String name); void delete(String name); void update(String name); void query(String name); }
-
package oop.demo; public interface TimeService { void timer(); }
-
package oop.demo; public class UserServiceImpl implements UserService, TimeService { // 实现了接口的类,就需要重写接口中的方法 @Override public void add(String name) { } @Override public void delete(String name) { } @Override public void update(String name) { } @Override public void query(String name) { } @Override public void timer() { } }
-
-
接口的作用:
- 约束
- 定义一些方法,让不同的人实现
- 属性默认都是
public static final
常量 - 方法默认都是
public abstract
抽象 - 接口中没有构造方法,不能被实例化
implements
可以实现多个接口- 必须要重写接口中的方法
面向对象 17 内部类
-
内部类就是在一个类的内部再定义一个类。 A 类中定义一个 B 类,则 B 是 A 的内部类,A 是 B 的外部类。
-
种类
-
成员内部类
-
创建成员内部类对象,必须先创建外部类对象,再通过外部类对象.new 构造内部类对象
-
成员内部类中不能有静态成员
-
成员内部类可以访问外部类的私有属性
-
package oop.demo; public class Application { public static void main(String[] args) { Outer outer = new Outer(); outer.out(); // print "这是外部类的方法" // 语法 outer.new Inner(); Outer.Inner inner = outer.new Inner(); inner.in(); // print "这是内部类的方法" inner.getID(); // print 10 inner.getOut(); // print "这是外部类的方法" } }
-
package oop.demo; // 外部类 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); // print 10 } // 可直接调用外部类的方法 public void getOut() { out(); // print "这是外部类的方法" } } }
-
-
静态内部类
-
静态内部类中只能访问外部类的静态成员
-
package oop.demo; public class Application { public static void main(String[] args) { Outer outer = new Outer(); outer.out(); // print "这是外部类的方法" // 语法 new Outer.Inner(); Outer.Inner inner = new Outer.Inner(); inner.in(); // print "这是内部类的方法" } }
-
package oop.demo // 外部类 public class Outer { public void out() { System.out.println("这是外部类的方法"); } // 静态内部类 public static class Inner { public void in() { System.out.println("这是内部类的方法"); } } }
-
-
局部内部类
-
局部内部类创建对象时,只能在定义它的方法内部进行
-
局部内部类可以去访问外部类的私有属性
-
局部内部类也可以去访问它所在方法的局部变量,但是要求局部变量必须被final修饰
-
jdk 8.0开始,默认为局部变量添加final
-
package oop.demo; public class Application { public static void main(String[] args) { Outer outer = new Outer(); outer.out(); // print "这是外部类的方法" \n "这是外部类的方法" } }
-
package oop.demo; public class Outer { public void out() { System.out.println("这是外部类的方法"); class Inner { public void in() { System.out.println("这是内部类的方法"); } } // 局部内部类创建对象,只能在 out() 方法内进行 Inner inner = new Inner(); inner.in(); } }
-
-
匿名内部类
-
异常
异常 01 Error
和Exception
- Java 把异常当作对象来处理,并定义一个基类
java.lang.Throwable
作为所有异常的超类 - 检查性异常:最具代表的是用户错误或问题引起的异常,这是程序员无法预见的。例如打开一个不存在的文件时,一个异常就发生了。
- 运行时异常:是可能被程序员避免的异常。与检查性异常相反,运行时异常可以在编译时被忽略。
- 在Java API 中已经定义了许多异常类,这些异常类分为两大类,错误
Error
和异常Exception
Throwable
Error
VirtualMachineError
虚拟机运行错误StacOverFlowError
栈溢出错误OutOfMemoryError
内存不足错误
AWTError
Exception
IOExcption
EOFException
FileNotFoundException
找不到文件异常
RuntimeException
运行时异常ArrithmeticException
算术异常MissingResourceException
丢失资源ClassNotFoundException
找不到类NullPointerException
空指针异常IllegalArgumentException
不合法参数ArrayIndexOutOfBoundsException
数组下标越界UnknownTypeException
不明类型异常
- 错误
Error
:错误不是异常,而是脱离程序员控制的问题。错误在代码中通常被忽略。例如,当栈溢出时,一个错误就发生了,编译中也检查不到。Error
类对象由 Java 虚拟机生成并抛出,大多数错误与代码编写者所执行的操作无关- 当 JVM 不再有继续执行操作所需的内存资源时,将出现 Java 虚拟机运行错误
VirtualMachineError
- 发生在虚拟机试图执行应用时,如类定义错误
NoClassDefFoundError
,链接错误LinkageError
,这些错误是不可查的,因为它们在应用程序的控制和处理能力之外,而且绝大多数是程序运行时不允许出现的状况。
Error
和Exception
的区别:Error
通常是灾难性的致命错误,是程序无法控制和处理的,当出现这些异常时,JVM 一般会选择终止线程。Exception
通常情况下是可以被程序处理的,并且在程序中应该尽可能地去处理这些异常。
异常 02 捕获和抛出异常
-
快捷键 Ctrl + Alt + T
-
抛出异常
-
捕获异常
-
异常处理 5 个关键字
-
try
、catch
、finally
、throw
、throws
-
package exception; public class Demo01 { public static void main(String[] args) { int a = 1; int b = 0; // 假设要捕获多个异常:按照范围从小到大的顺序 try { // try 监控区域 System.out.println(a / b); } catch (ArithmeticException e) { // catch 捕获异常 System.out.println("运行异常!除数不能为0!"); e.printStackTrace(); // 打印错误的栈信息 } catch (Error e) { System.out.println("Error"); } catch (Exception e) { System.out.println("Exception"); } catch (Throwable e) { System.out.println("Throwable"); } finally { // 处理善后工作 System.out.println("finally"); } } }
-
package exception; public class Demo02 { public static void main(String[] args) { Demo02 test = new Demo02(); test.test(1,0); } // 假设该方法中处理不了这个异常 // public void test(int a, int b) throws ArithmeticException() { public void test(int a, int b) { if (b == 0) { // 主动抛出异常,一般在方法中使用 throw new ArithmeticException(); } } }
-
异常 03 自定义异常及经验小结
-
使用 Java 内置的异常类可以描述在编程时出现的大部分异常情况。除此之外,用户还可以自定义异常。用户自定义异常类,只需继承 Exception 类即可
-
创建自定义异常类
-
在方法中通过
throw
关键字抛出异常对象 -
如果在当前抛出异常的方法中处理异常,可以使用
try
-catch
语句捕获并处理(程序继续正常运行);否则在方法的声明处通过throws
关键字指明要抛出给方法调用者的异常(程序中断不继续运行) -
在出现异常方法的调用者中捕获并处理异常
-
package exception; // 自定义的异常类 public class MyException extends Exception{ // 传递数字 >10 private int detail; public MyException(int a) { this.detail = a; } // toString:异常的打印信息 @Override public String toString() { return "MyException{" + "detail=" + detail + '}'; } }
-
package exception; public class Test { // 可能会存在异常的方法,`throws`关键字抛出 static void test(int a) throws MyException { System.out.println("传递的参数为:" + a); if (a > 10) { throw new MyException(a); } System.out.println("OK"); } public static void main(String[] args) { // `try`-`catch`关键字捕获 try { test(11); } catch (MyException e) { System.out.println("自定义异常:" + e); // print 传递的参数为:11 \n 自定义异常:MyException{detail=11} } } }
-
-
实际应用中的经验总结
- 处理运行时异常时,采用逻辑去合理规避,同时辅助
try
-catch
处理 - 在多重
catch
块后面,可以加一个尽可能大的catch (Exception e)
来处理可能会被遗漏的异常 - 对于不确定的代码,也可以加上
try
-catch
,处理潜在的异常 - 尽量去处理异常,切忌简单地调用
printStackTrace()
去打印输出 - 具体如何处理异常,要根据不同地业务需求和异常类型去决定
- 尽量添加
finally
语句块去释放占用的资源
- 处理运行时异常时,采用逻辑去合理规避,同时辅助