Java复习
基础
-
一个.java文件可以创建多个class,但只有一个class可以声明为public,且声明为pulic的class的名称要与.java文件名称相同。
-
.java文件编译后可以产生一个或多个.class文件(也称字节码文件),对应着.java文件中的一个或多个类。
-
java中null关键字:是空指针,与地址对应,存放在变量名地址指向的内存中,不能将null赋给基本数据类型的变量。
//null用来表示那些仅有名字而没有实例空间的变量的具体内容 //在Java中,假设房间类为Room,则: Room room=null; //房间为空,房间room只是一个名字,实际不存在。 Room room= new Room(); //一间空的房间,这间房里没东西。
命名
-
标识符命名规则:
只能以字母、下划线_、美元符$命名,且数字不能作为开头,不能包含空格,不能使用关键字作为标识符(可以包含),严格区分大小写。
-
命名规范:
包名:所有单词均小写(xxxyyy)
类名、接口名:所有单词首字母大写(XxxYyy)
方法名、变量名:第一个单词首字母小写,其余单词首字母均大写(xxxYyyZzz)
常量名:所有字母均大写,若有多个单词则每个单词用下划线连接(XXX_YYY)
变量
-
变量作用域:其定义所在的一对{}中,变量只在作用域内有效
-
数据类型:基本数据类型(bit、short、int、long、float、double、char、boolean)+引用数据类型(类class、接口interface、数组)
-
long型变量定义时需以‘L’或‘l’结尾,例:long a = 12345678L 否则编译不报错,不加L**默认为int(超过int范围会出错)**然后赋值时自动类型提升为long
范围:-263~263-1
-
float型变量定义时需以‘f’或‘F’结尾,例:float f = 123.4f 否则编译出错,不加f默认为double
范围:-3.408*1038~3.408*1038-1
-
java中char型变量占两个字节,转义字符算作一个字符
-
基本数据类型之间的运算规则(不包含boolean,boolean与其他基本数据类型之间不能比较也不能进行自动类型提升)
自动类型提升:当表示范围小的变量与表示范围大的变量之间做运算时,表示范围小的先自动提升为表示范围大的再进行运算 byte、char、short —》int—》 long—》 float—》 double
(注意:char、byte、short之间做运算时都先自动提升为int型再运算)
强制类型转换:当需要将大范围变量转换为小范围变量时,用强转符()实现强制类型转换,可能会出现精度丢失。
例:float s = 12.3f; int S = (int)s; 执行后,S中存储的内容为12
-
整型常量默认为int型,浮点型常量默认为double型,与其他变量运算时按默认型进行相应转换。
-
String类型为引用数据类型中的类,翻译为字符串变量,声明时用一对双引号**“”**赋值!
-
String可以与8种基本数据类型做运算,包括boolean型,且运算只能是连接运算,用字符+作为运算符!
-
运算顺序从左向右逐个运算,类型转换只在即将运算时发生。
String str2 = 3.5f + “”;
System.out.println(str2); //输出:”3.5”
System.out .println(3+4+“Hello!”); //输出:7Hello!
System.out.println(“Hello!”+3+4); //输出:Hello!34
System.out.println(‘a’+1+“Hello!”); //输出:98Hello!
System.out.println(“Hello”+‘a’+1); //输出:Helloa1
-
变量的赋值
如果变量是基本数据类型,此时赋值的是变量所保存的数据值。
如果变量是引用数据类型,此时赋值的是变量所保存的数据的地址值。
运算符
- 逻辑运算符:&& || &(非短路与) |(非短路或) ^ ! 逻辑运算符只能用于布尔型变量的操作
- 位运算符:<< >> >>>(无符号右移) & | ^ ~ 位运算符只能用于数值型变量的操作
选择控制
- switch结构中的表达式,只能是如下的6种数据类型之一:byte 、short、char、int、枚举类型(5.0新增)、String类型(7.0新增)
- 根据switch表达式中的值,依次匹配各个case中的常量。一旦匹配成功,则进入相应case结构中,调用其执行语句。
当调用完执行语句以后,则仍然继续向下执行其他case结构中的执行语句,直到遇到break关键字或此switch-case结构
末尾结束为止。
循环控制
-
java语言中循环条件为boolean型
-
“无限循环”结构: while(true) 或 for( ; ; )
-
带标签的break,continue关键字的使用
label0 for(int i = 1;i<10;i++){ for(int j = 1;j<100;j++){ System.out.println("hahaha"); if(i == 6) //continue label0; break label0;//i==6时跳出整个循环 } }
Scanner使用
- 如果输入的数据类型与要求的类型不匹配时,会报异常:InputMisMatchException
1.导包:import java.util.Scanner;
2.Scanner的实例化:Scanner scan = new Scanner(System.in);
3.调用Scanner类的相关方法(next() / nextXxx()),来获取指定类型的变量
//1.导包
import java.util.Scanner;
class ScannerTest{
public static void main(String[] args){
//2.Scanner的实例化
Scanner scan = new Scanner(System.in);
//3.调用Scanner类的相关方法
System.out.println("请输入你的姓名:");
String name = scan.next(); //读取字符串
System.out.println(name);
System.out.println("请输入你的芳龄:");
int age = scan.nextInt(); //读取Int
System.out.println(age);
System.out.println("请输入你的体重:");
double weight = scan.nextDouble(); //读取Double
System.out.println(weight);
System.out.println("你是否相中我了呢?(true/false)");
boolean isLove = scan.nextBoolean(); //读取Boolean
System.out.println(isLove);
//对于char型的获取,Scanner没有提供Boolean相关的方法。只能获取一个字符串
System.out.println("请输入你的性别:(男/女)");
String gender = scan.next();//"男"
char genderChar = gender.charAt(0);//获取索引为0位置上的字符
System.out.println(genderChar);
}
}
数组
java中数组属于引用数据类型(即对象),数组元素可以是基本数据类型,也可以是引用数据类型。(java中的地址是hash函数算出来的哈希值,屏蔽了复杂的内存地址)
-
数组元素为引用数据类型的数组称为对象数组,其数组内存放的内容为对象的地址信息。
-
一维数组初始化
//1.静态初始化 int [] test = new int[]{1,2,3,4,5}; //2.动态初始化 int [] test = new int[5]; //也是正确的写法 int test[] = new int[]{1,2,3,4,5}; int [] test = {1,2,3,4,5};//类型推断 //错误写法: // int []test; test = {1,2,3,4,5}; // int [] test = new int[5]{1,2,3,4,5};
-
二维数组的声明初始化
//1.静态初始化 int[][] arr1 = new int[][]{{1,2,3},{4,5},{6,7,8}}; //2.动态初始化 String[][] arr2 = new String[3][2]; String[][] arr3 = new String[3][]; //也是正确的写法: int[] arr4[] = new int[][]{{1,2,3},{4,5,9,10},{6,7,8}}; int[] arr5[] = {{1,2,3},{4,5},{6,7,8}};//类型推断 //错误写法: // int[] arr5[];arr5 = {{1,2,3},{4,5},{6,7,8}}; // String[][] arr4 = new String[][4]; // String[4][3] arr5 = new String[][]; // int[][] arr6 = new int[4][3]{{1,2,3},{4,5},{6,7,8}};
-
一维数组内存解析
方法中的变量均为局部变量,存储在栈区。例:arr、arr1(存储的是堆区对应数组的首地址)
**new出来的结构存储在堆区。**例:arr数组的内容和arr1数组的内容。
-
对象数组的内存解析
//对象数组的内存解析
String []s = new String[]{"a","b","c"};
//1.在栈区申请一个空间s,用于存储数组首地址(初始信息为null,当堆空间new出数组后将null更新成数组首地址)
//2.在堆空间new一个数组,数组元素为3个String对象的地址值
// (数组元素初始值为null,当堆空间new出对象后将null更新成对象地址)
//3.在堆空间new三个String对象,分别存储三个字符串信息
-
二维数组的内存解析
方法中的变量均为局部变量,存储在栈区。例:arr(存储的是堆区对应外层数组的首地址)
**new出来的结构存储在堆区。**例:外层arr数组的内容(即内层一维数组的首地址)、内层一维arr[i]数组的内容(具体的元素)。
arr存储的实际上是外层一维数组的首地址,因此求arr长度时得到的结果是外层数组的长度。
-
获取数组长度:.length
//一维数组 int []test = new int[]{1,2,3,4,5}; System.out.println("数组test的长度为:"+test.length);//数组test的长度为:5 //二维数组 int [][]test = new[][]{{1,2,3},{1,2,3,4,5},{1,3,4,5,6,8},{1,4,2,5,7}} System.out.println("数组test的长度为:"+test.length);//数组test的长度为:4 System.out.println("数组test[0]的长度为:"+test[0].length);//数组test[0]的长度为:3
-
数组的遍历
//一维数组 for(int i = 0;i<test.length;i++) System.out.println(test[i]); //二维数组 for(int i = 0;i<test.length;i++) for(int j = 0;j<test[i].length;i++) SyStem.out.println(test[i][j]);
-
数组元素的默认初始化值
-
一维数组
-
数组元素是整型:0
-
数组元素是浮点型:0.0
-
数组元素是char型:0或’\u0000’,而非’0’
-
数组元素是boolean型:false
-
数组元素是引用数据类型:null
-
-
二维数组
/* 规定:二维数组分为外层数组的元素,内层数组的元素 * * 数组元素的默认初始化值 * 针对于动态初始化方式一:int[][] arr = new int[4][3]; * 外层元素的初始化值为:地址值(指向一维数组) * 内层元素的初始化值为:与一维数组初始化情况相同 * * 针对于动态初始化方式二:int[][] arr = new int[4][]; * 外层元素的初始化值为:null(内层数组未实例化,用null表示未实例化的对象) * 内层元素的初始化值为:不能调用,否则nullpointer报错。 */
-
-
数组的赋值与复制
-
赋值:array2 = array1;
如何理解:将array1保存的数组的地址值赋给了array2,使得array1和array2共同指向堆空间中的同一个数组实体。
-
复制:利用循环遍历数组逐个元素复制,array2[i] = array1[i];
如何理解:我们通过new的方式,给array2在堆空间中新开辟了数组的空间。将array1数组中的元素值一个一个的赋值到array2数组中。
-
-
Arrays工具类常用方法
//1.boolean equals(int[] a,int[] b):判断两个数组是否相等。 System.out.println(Arrays.equals(arr1, arr2)); //2.String toString(int[] a):输出数组信息。 System.out.println(Arrays.toString(arr1)); //3.void fill(int[] a,int val):将指定值填充到数组之中。 Arrays.fill(arr1,10); //4.void sort(int[] a):对数组进行排序。(升序) Arrays.sort(arr2); //5.int binarySearch(int[] a,int key):在数组中二分查找key int index = Arrays.binarySearch(arr3, 210);
-
数组常见异常
1.数组角标越界异常:ArrayIndexOutOfBoundsException 2.空指针异常:NullPointerException //情况一: int[] arr1 = new int[]{1,2,3}; arr1 = null; System.out.println(arr1[0]); //情况二: int[][] arr2 = new int[4][]; System.out.println(arr2[0][0]); //情况三: String[] arr3 = new String[]{"AA","BB","CC"}; arr3[0] = null; System.out.println(arr3[0].toString());
内存结构
-
栈:存放局部变量
-
堆:存放new出来的结构:对象、数组
-
方法区
- 常量池:
-
静态域:
面向对象
-
Java类及类的成员:属性、方法、构造器;代码块、内部类;
-
面向对象的大特征:封装性、继承性、多态性、(抽象性)
-
属性与局部变量不同点:
-
在类中声明的位置的不同
-
属性:直接定义在类的一对{}内
-
局部变量:声明在方法内、方法形参、代码块内、构造器形参、构造器内部的变量
-
-
关于权限修饰符的不同
-
属性:可以在声明属性时,指明其权限,使用权限修饰符。(常用的权限修饰符:private、public、缺省、protected )
-
局部变量:不可以使用权限修饰符。
-
默认初始化值的情况
-
属性:类的属性,根据其类型,都默认初始化值。
(整型(0)、浮点型(0.0)、字符型(char:0 (或’\u0000’))、布尔型(false)、引用数据类型(null))
-
**局部变量:没默认初始化值。意味着,我们在调用局部变量之前,一定要显式赋值。**特别地,形参在调用时赋值即可。
-
在内存中加载的位置
-
属性:加载到堆空间中 (非static)
-
局部变量:加载到栈空间
-
-
方法
-
方法的声明:
权限修饰符 返回值类型 方法名(形参列表){
方法体
}
-
说明:
-
关于权限修饰符:默认方法的权限修饰符先都使用public(4种权限修饰符:private、public、缺省、protected)
-
返回值类型:
有返回值,则必须在方法声明时指定返回值的类型。同时,方法中需要使用return关键字来返回指定类型的数据。
没返回值,则方法声明时使用void来表示。如果使用的话,只能“return;”且该语句之后不可再直接跟有可执行语句。
-
形参列表: 方法可以声明0个,1个,或多个形参
-
方法的使用中,可以调用当前类的属性或方法。但方法内不可以定义方法。
-
-
return关键字的注意事项:return关键字后面不可以声明执行语句。
-
-
方法重载:在同一个类中,允许存在一个以上的同名方法,只要它们的参数列表:参数个数or参数类型不同即可。(注意:参数列表相同的方法返回值类型、权限修饰符必须相同,否则不能存在)
方法是否重载跟方法的权限修饰符、返回值类型、形参变量名、方法体都没关系,只需要看参数列表是否相同即可!
//如下的4个方法构成了重载 public void getSum(int i,int j){ System.out.println("1"); } //参数类型不同 public void getSum(double d1,double d2){ System.out.println("2"); } //参数类型不同(注意返回值类型也不同,说明重载方法的返回值类型未必相同) public int getSum(String s ,int i){ System.out.println("3"); return 1; } //参数类型且顺序不同 public void getSum(int i,String s){ System.out.println("4"); } //不构成重载的举例: //如下的3个方法不能与上述4个方法构成重载 //参数列表与第一个方法相同,但返回值类型不同,不能存在 // public int getSum(int i,int j){ // return 0; // } //参数列表与第一个方法相同,但权限修饰符不同,不能存在 // private void getSum(int i,int j){ // // } //参数列表与第一个方法相同,只有形参变量名不同,不构成重载(若此时方法体与第一个方法不一样则该方法不能存在) // public void getSum(int m,int n){ // // }
-
方法可变个数形参:
- 可变个数形参的格式:数据类型 … 变量名
- 当调用可变个数形参的方法时,传入的参数个数可以是:0个,1个,2个,。。。
- 可变个数形参的方法与本类中方法名相同,形参不同的方法之间构成重载
- 可变个数形参的方法与本类中方法名相同,形参类型也相同的数组作为形参的方法之间不构成重载,二者不能共存。
- 可变个数形参必须声明在方法的形参末尾
- 在方法的形参中,最多只能声明一个可变形参
public void show(String s){
System.out.println("show(String)");
}
public void show(String ... strs){
System.out.println("show(String ... strs)");
//方法内调用strs时,将strs看作数组即可,因此可变形参不能与形参类型相同的数组作为形参的方法共存
for(int i = 0;i < strs.length;i++){
System.out.println(strs[i]);
}
}
// 不能与上一个方法同时存在
// public void show(String[] strs){
//
// }
//调用时:
test.show("hello");
test.show("hello","world");
test.show();
test.show("AA","BB","CC");与test.show(new String[]{"AA","BB","CC"});等价,都可匹配可变个数形参的方法
- 匿名对象:创建的对象,没显式的赋给一个变量名,即为匿名对象。(匿名对象只能在new的同时调用,只能调用这一次)
//使用举例:
new Phone().sendEmail();
//应用场景:
class PhoneMall{
public void show(Phone phone){
phone.sendEmail();
phone.playGame();
}
}
PhoneMall mall = new PhoneMall();
//匿名对象的使用:
mall.show(new Phone());
-
值传递机制
- 如果参数是基本数据类型,此时实参赋给形参的是实参真实存储的数据值。
- 如果参数是引用数据类型,此时实参赋给形参的是实参存储数据的地址值。
-
封装性
我们程序设计追求“高内聚,低耦合”。
高内聚 :类的内部数据操作细节自己完成,不允许外部干涉;
低耦合 :仅对外暴露少量的方法用于使用。- 封装性的体现:
-
将类的属性xxx私化(private),同时,提供公共的(public)方法来获取(getXxx)和设置(setXxx)此属性的值
private double radius; public void setRadius(double radius){ this.radius = radius; } public double getRadius(){ return radius; }
-
不对外暴露的私有的方法(只用于类内调用)
-
单例模式(将构造器私有化)
-
如果不希望类在包外被调用,可以将类设置为缺省的。
-
权限修饰符范围
4种权限都可以用来修饰类的内部结构**:属性、方法、构造器、内部类**。修饰类(内部类除外)的话,只能使用:缺省、public。
-
构造器(构造方法)
-
构造器的特点:
名称与类名相同、不声明返回值(不是void)、不能被static,final,synchronized,abstract,native修饰,无return语句。
-
构造器的作用:
1.创建对象
2.初始化对象的信息
-
使用说明:
1.如果没显式的定义类的构造器的话,则系统默认提供一个空参的构造器。
2.定义构造器的格式:权限修饰符 类名(形参列表){}。
3.一个类中定义的多个构造器,彼此构成重载。
4.一旦我们显式的定义了类的构造器之后,系统就不再提供默认的空参构造器。
5.一个类中至少会有一个构造器。
-
-
属性赋值顺序
① 默认初始化② 显式初始化③ 构造器中初始化④ 通过"对象.方法" 或 "对象.属性"的方式⑤在代码块中赋值
- 以上操作的先后顺序:① - ② / ⑤ - ③ - ④ ,①②③只在创建对象时执行,④可以多次调用执行
-
JavaBean,指符合如下标准的Java类:(数据库交互时创建对象时会用到)
>类是公共的
>一个无参的公共的构造器 -
MVC设计模式:
M: modmel, 模型层, 主要处理数据
C: controller, 控制层, 处理业务逻辑
V: view, 视图层, 显示数据
-
this关键字:this表示当前对象或当前正在创建的对象
-
this关键字调用属性、方法
1)在类的方法中,我们可以使用"this.属性"或"this.方法"的方式,调用当前对象属性或方法。但是,通常情况下,我们都择省略"this."。特殊情况下,如果方法的形参和类的属性同名时,我们必须显式的使用"this.变量"的方式,表明此变量是属性,而非形参。 2)在类的构造器中,我们可以使用"this.属性"或"this.方法"的方式,调用当前正在创建的对象属性或方法。但是,通常情况下,我们都择省略"this."。特殊情况下,如果构造器的形参和类的属性同名时,我们必须显式的使用"this.变量"的方式,表明此变量是属性,而非形参。
-
this关键字调用构造器
① 我们在类的构造器中,可以显式的使用"this(形参列表)"方式,调用本类中指定的其他构造器
② 构造器中不能通过"this(形参列表)“方式调用自己,否则死循环
③ 如果一个类中有n个构造器,则最多有 n - 1构造器中使用了"this(形参列表)”
④ 规定:"this(形参列表)"必须声明在当前构造器的首行
⑤ 构造器内部,最多只能声明一个"this(形参列表)",用来调用其他的构造器
-
-
package关键字
-
1.为了更好的实现项目中类的管理,提供包的概念
-
2.使用package声明类或接口所属的包,声明在源文件的首行
-
3.包,属于标识符,遵循标识符的命名规则、规范(xxxyyyzzz)、“见名知意”
-
4.每"."一次,就代表一层文件目录。
-
补充:同一包下不能有同名的类、接口,但不同的包下可以。
-
java包介绍:
-
-
import关键字
- 在源文件中显式的使用import结构导入指定包下的类、接口
- 声明在包的声明和类的声明之间
- 如果需要导入多个结构,则并列写出即可
- 可以使用"xxx.*"的方式,表示可以导入xxx包下的所有.java结构,但是如果使用的是xxx子包下的结构,则仍需要显式导入,不能用"*"导入
- 如果使用的类或接口是java.lang包(核心类包)下定义的,则可以省略import结构
- 如果使用的类或接口是本包下定义的,则可以省略import结构
- 如果在源文件中,使用了不同包下的同名的类,则必须至少一个类需要以全类名的方式显示。
- import static:导入指定类或接口中的静态.java结构:属性或方法。
-
继承性(extends:延展、扩展)
-
格式:class A extends B{ }
A:被称为子类或派生类或subclass,B:被称为父类或超类或基类或superclass
注意:子类是对父类的拓展,功能和属性都比父类更复杂,子类的范围比父类更大
-
体现:一旦子类A继承父类B以后,子类A中就获取了父类B中声明的所有的属性和方法。
当子类声明出与父类相同的属性(类型相同,名称也相同)时,子类在实例化时会在父类属性的基础上多开辟一块空间存储子类的该属性(并不会覆盖父类的该属性)。
-
**注意:父类中声明为private的属性或方法,子类继承父类以后,仍然获取了父类中private结构。**但是由于封装性的影响,使得子类不能直接调用父类的结构而已。若需要访问父类中private属性,可通过父类提供的非private得getXxx()和setXxx()方法实现。
-
Java中继承性的说明
1.一个父类可以有多个子类,但是一个子类只能有一个父类
2.子类直接继承的父类,称为:直接父类。间接继承的父类称为:间接父类
3.子类继承父类以后,就获取了直接父类以及所间接父类中声明的属性和方法 -
未显式的声明一个类的父类,则此类默认继承于java.lang.Object类。所的java类(除java.lang.Object类之外)都直接或间接的继承于java.lang.Object类,即所有的java类具有java.lang.Object类声明的功能。
-
-
方法的重写注意事项
-
子类重写的方法抛出的异常类型不大于父类被重写的方法抛出的异常类型
-
子类和父类中的同名同参数的方法要么都声明为非static的(考虑重写),要么都声明为static的(不是重写)
-
子类重写的方法的方法名和形参列表与父类被重写的方法的方法名和形参列表相同
-
子类重写的方法的权限修饰符不小于父类被重写的方法的权限修饰符(子类不能重写父类中声明为private权限的方法)
-
返回值类型:
-
父类方法的返回值类型是void,则子类重写的方法的返回值类型只能是void
父类方法的返回值类型是A类型,则子类重写的方法的返回值类型可以是A类或A类的子类
父类方法的返回值类型是基本数据类型(比如:int),则子类重写的方法的返回值类型必须是相同的基本数据类型(必须也是int)
-
super关键字
-
"super.属性"或"super.方法"的方式调用父类中属性或方法
-
我们可以在子类的方法或构造器中。通过使用"super.属性"或"super.方法"的方式,显式的调用父类中声明的属性或方法。但是,通常情况下,我们习惯省略"super."
-
特殊情况:当子类和父类中定义了同名的属性时,我们要想在子类中调用父类中声明的属性,则必须显式的使用"super.属性"的方式,表明调用的是父类中声明的属性。
-
特殊情况:当子类重写了父类中的方法以后,我们想在子类的方法中调用父类中被重写的方法时,则**必须显式的使用"super.方法"**的方式,表明调用的是父类中被重写的方法。
-
-
“super(参数列表)”的方式调用构造方法
- 我们可以在子类的构造器中显式的使用"super(形参列表)"的方式,调用父类中声明的指定的构造器
- "super(形参列表)"的使用,必须声明在子类构造器的首行!
- 我们在类的构造器中,针对于**"this(形参列表)"或"super(形参列表)"只能二选一,不能同时出现**
- 在构造器的首行,没显式的声明"this(形参列表)“或"super(形参列表)”,则默认调用的是父类中空参的构造器:super()(若父类此时未提供空参构造器,则声明子类的时候会报错)
- 在类的多个构造器中,至少一个类的构造器中使用了"super(形参列表)",调用父类中的构造器
-
-
子类对象实例化
-
从结果上看:继承性
子类继承父类以后,就获取了父类中声明的属性或方法。
创建子类的对象,在堆空间中,就会加载所有父类中声明的属性,而子类新声明特有的重名属性不会覆盖父类属性。 -
从过程上看:
当我们通过子类的构造器创建子类对象时,我们一定会直接或间接的调用其父类的构造器,进而调用父类的父类的构造器,…直到调用了java.lang.Object类中空参的构造器为止。正因为加载过所的父类的结构,所以才可以看到内存中父类中的结构,子类对象才可以考虑进行调用。 -
虽然创建子类对象时,调用了父类的构造器,但是自始至终就创建过一个对象,即为new的子类对象。
-
-
多态性(前提:类的继承关系和方法的重写)
-
对象的多态性:父类的引用指向子类的对象(或子类的对象赋给父类的引用)
-
多态性的使用:有了对象的多态性以后,我们在编译期,只能调用父类中声明过的方法,但在运行期,我们实际执行的是子类重写父类声明过的方法。(即只能调用子类重写父类的方法,而子类特有的方法不能调用)
总结:编译,看左边;运行,看右边。
class Driver{ //MySQLConnection,OracleConnection都是Connection的子类 //conn = new MySQlConnection(); //conn = new OracleConnection(); public void doData(Connection conn){ //规范的步骤去操作数据 conn.getData(); } } Driver test = new Driver(); test.doData(new MySQlConnection());//执行的是子类MySQLConnection实例的getData()方法 test.doData(new OracleConnection());//执行的是子类OracleConnection实例的getData()方法
-
多态性使用的注意点:对象的多态性,只适用于方法,不适用于属性(编译和运行时,调用的都是父类的属性)
class Person{ int id = 1001; void eat() { System.out.println("吃饭"); } } class Man extends Person{ int id = 1003; void eat() { System.out.println("吃很多饭"); } void work() { System.out.println("干体力活") } } Person temp = new Man(); System.out.println(temp.id);//输出结果为1001,而不是1003
-
多态是运行时行为,不是编译性行为。(只有在运行时才确定调用的是子类重写的方法,而在编译时并不确定调用的是父类的还是子类的方法。这种现象也称为晚绑定或动态绑定。)
-
-
引用数据类型的类型提升与强制类型转换
-
向上转型:将子类对象赋给父类的引用,即多态
-
向下转型:利用强制类型转换符()将对象转换成另一个类的对象
- 为什么使用向下转型:
有了对象的多态性以后,内存中实际上是加载了子类特有的属性和方法的,但是由于变量声明为父类类型,导致编译时,只能调用父类中声明的属性和方法。子类特有的属性和方法不能调用。如何才能调用子类特的属性和方法?使用向下转型。 - 应用:将父类引用指向的多态对象强制转换成子类的对象,以便达到调用该子类特有属性和方法的目的。
//父类的指针向子类强转时,编译不报错,但运行时可能报错 Person temp = new Man();//此时,不能通过temp调用该子类特有的work方法 //向下转型 Man man = (Man)temp; man.work();//此时即可调用该子类特有的work方法 /*也会出现强转失败的情况,下面编译时不出错,但运行时会报ClassCastException错 * Woman woman = (Woman)temp;//类型不匹配,强转失败 */
- 使用时的注意点:
① 使用强转时,可能出现ClassCastException的异常。()
② 为了避免在向下转型时出现ClassCastException的异常,我们在向下转型之前,先进行instanceof的判断,一旦返回true,就进行向下转型。如果返回false,不进行向下转型。
- 为什么使用向下转型:
-
-
instanceof关键字
-
a instanceof A:判断对象a是否是类A的实例。如果是,返回true;如果不是,返回false。
-
假设类B是类A的父类,如果 a instanceof A返回true,则 a instanceof B也返回true。
(a是类A的实例,则a也是类A父类的实例。)
Person temp = new Man();//此时,不能通过temp调用该子类特有的work方法 //向下转型 Man man = (Man)temp; man.work();//此时即可调用该子类特有的work方法 /*也会出现强转失败的情况,下面编译时不出错,但运行时会报ClassCastException错 * Woman woman = (Woman)temp;//类型不匹配,强转失败 */ //正确写法 if(man instanceof Woman) Woman woman = (Woman)temp;
-
-
Eclipse单元测试方法步骤:
-
当前工程 - 右键选择:build path - add libraries - JUnit 4 - 下一步
-
创建Java类,进行单元测试。
-
此时的Java类要求:① 此类是public的 ②此类提供公共的无参的构造器
-
此类中声明单元测试方法。
方法要求:方法的权限是public,没返回值,没形参
-
此单元测试方法上需要声明注解:@Test,并在单元测试类中导入:import org.junit.Test;
-
声明好单元测试方法以后,就可以在方法体内测试相关的代码。
-
写完代码以后,左键双击单元测试方法名,右键:run as - JUnit Test
-
说明:
-
如果执行结果无异常:绿条,否则:红条
-
-
Object类
-
Object类是所Java类的根父类
-
如果在类的声明中未使用extends关键字指明其父类,则默认父类为java.lang.Object类
-
Object类中的功能(属性、方法)就具通用性。
-
属性:无
-
方法:equals() / toString() / getClass() /hashCode() / clone() / finalize()/wait() 、 notify()、notifyAll()
clone()方法:复制本类的对象,返回一个Object实例,可通过向下转型成本类的对象。
finalize()方法:当一个对象不再被指针引用时,垃圾回收机制在回收该对象前会调用该对象的finalize方法,然后该对象就进入等待被回收阶段。
-
-
Object类只声明了一个空参的构造器
-
== 运算符
- 如果比较的是基本数据类型变量:比较两个变量保存的数据是否相等。(不一定类型要相同)
- 如果比较的是引用数据类型变量:比较两个对象的地址值是否相同。
- 补充: == 符号使用时,必须保证符号左右两边的变量类型一致。(否则编译报错)
-
equals()方法
-
只能适用于引用数据类型
-
Object类中equals()的定义:
Object类中定义的equals()和==的作用是相同的:比较两个对象的地址值是否相同
public boolean equals(Object obj) { return (this == obj); }
-
像String、Date、File、包装类等都重写了Object类中的equals()方法,比较两个对象的"实体内容"是否相同。
-
通常情况下,我们自定义的类如果使用equals()的话,也通常是比较两个对象的"实体内容"是否相同。那么,我们
就需要对Object类中的equals()进行重写。重写的原则:比较两个对象的实体内容是否相同。
-
equals()方法的重写
//手动重写eqauls()的方法模板 class Tampalte{ //属性 ... ... ... //重写其equals()方法,注意这里传入的参数为Object类及其子类的实例 public boolean equals(Object obj){ if(obj == this){ return true; } if(obj instanceof Tamplate){ Tamplate u = (Tamplate)obj; //比较具体内容的代码 ... ... } return false; } } //手动重写一般可能会出现逻辑漏洞,推荐使用IDE提供的自动重写代码生成功能
-
-
toString()方法
- 当我们用System.out.println()输出一个对象的引用时,实际上就是调用当前对象的toString()方法
-
Object类中toString()的定义:
public String toString() { return getClass().getName() + "@" + Integer.toHexString(hashCode()); }
-
像String、Date、File、包装类等都重写了Object类中的toString()方法,使得在调用对象的toString()时返回"实体内容"信息。
-
自定义类也可以重写toString()方法,当调用此方法时,返回对象的"实体内容"。
-
-
包装类
-
为了使基本数据类型的变量具有类的特征,引入包装类。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Clr3gVy3-1657435102268)(https://s2.loli.net/2022/07/08/oKi5VQfqJIwGced.png)]
-
基本数据类型、包装类、String之间的转换
//基本数据类型 -》包装类 Integer T = new Integer(123); Integer T = 123;//自动装箱 //包装类-》基本数据类型 int t = T.intValue(); int t = T;//自动拆箱 //基本数据类型-》String String S = 123+"";//利用String的基本运算符'+' String S = String.valueOf(123);//利用String类的静态方法valueOf() //String-》基本数据类型 int t = String.parseInt("123");//利用String类的静态方法parseInt() int t = new Integer("123");//利用构造器和自动拆箱 //包装类-》String Integer T = 123;//创建一个包装类 String S = T.toString();//利用包装类toString()方法 //String-》包装类 Integer T = new Integer("123"); //若格式填写错误将会报NumberFormatException Integer T = new Integer("123Ad");//报NumberFormatException
-
包装类面试题
Object test = true?new Integer(1):new Double(2.0); System.out.println(test);//1.0 //这里三元运算时首先在编译时就进行了自动类型提升!
Object s1 = new Object(); Object s2 = new Object(); System.out.println(s1==s2);//false Integer a = new Integer(123); Integer b = new Integer(123); System.out.println(a==b);//true //-128~127的Integer包装类不是在堆区new出来的,而是Integer内部提供IntegerCache数组,处于该范围的Integer包装类指向同一个实体 Integer A = new Integer(128); Integer B = new Integer(128); System.out.println(A==B);//false //不在上述范围的Integer包装类指向不同实体,是在堆空间分别new出来的
-
-
static关键字
-
静态变量(或类变量):随类的加载而在方法区的静态域而加载,该类的所有对象共用该变量地址。
-
属性,是否使用static修饰,又分为:静态属性 vs 非静态属性(实例变量)
-
实例变量:我们创建了类的多个对象,每个对象都独立的拥一套类中的非静态属性。当修改其中一个对象中的非静态属性时,不会导致其他对象中同样的属性值的修改。
-
静态变量:我们创建了类的多个对象,多个对象共享同一个静态变量。当通过某一个对象修改静态变量时,会导致其他对象调用此静态变量时,是修改过了的。
-
static修饰属性的其他说明: - 静态变量随着类的加载而加载。可以通过"**类.静态变量**"的方式进行调用 - 静态变量的加载要早于对象的创建。 - 由于类只会加载一次,则静态变量在内存中也只会存在一份:存在方法区的静态域中。 - **类变量既可以以”类.属性“的形式调用,也可以以”对象.属性“的形式调用,但实例变量只能通过”对象.属性“的形式调用**。
-
静态属性举例:System.out; Math.PI;
-
属性声明为static情况:
属性是可以被多个对象所共享的,不会随着对象的不同而不同的常常声明为static。类中的常量也常常声明为static。
-
-
-
静态方法(类方法):随类的加载而在方法区的静态域而加载,该类的所有对象共用该方法地址。
-
随着类的加载而加载,可以通过"类.静态方法"的方式进行调用
-
类方法既可以以”类.方法“的形式调用,也可以以”对象.方法“的形式调用,但实例方法只能通过”对象.方法“的形式调用
-
静态方法中,只能调用静态的方法或属性
非静态方法中,既可以调用非静态的方法或属性,也可以调用静态的方法或属性 -
在静态的方法内,不能使用this关键字、super关键字
-
方法声明为static情况:
操作静态属性的方法,通常设置为static的;工具类中的方法,习惯上声明为static的。
-
-
-
单例模式:一个类只创建一个对象,构造器私有化(系统开销小)。
//饿汉式:类加载时就创建一个对象 //饿汉式1: class Bank{ //1.私化类的构造器 private Bank(){ } //2.内部创建类的对象 //4.要求此对象也必须声明为静态的 private static Bank instance = new Bank(); //3.提供公共的静态的方法,返回类的对象 public static Bank getInstance(){ return instance; } } //饿汉式2:使用了静态代码块 class Order{ //1.私化类的构造器 private Order(){ } //2.声明当前类对象,没初始化 //4.此对象也必须声明为static的 private static Order instance = null; static{ instance = new Order(); } //3.声明public、static的返回当前类对象的方法 public static Order getInstance(){ return instance; } } //懒汉式:只有在调用时才创建对象 class Order{ //1.私化类的构造器 private Order(){ } //2.声明当前类对象,没初始化 //4.此对象也必须声明为static的 private static Order instance = null; //3.声明public、static的返回当前类对象的方法 public static Order getInstance(){ if(instance == null){ instance = new Order(); } return instance; } }
-
两种方式的对比:
-
饿汉式:
坏处:对象加载时间过长。
好处:饿汉式是线程安全的
-
懒汉式:
好处:延迟对象的创建。
目前的写法坏处:线程不安全。—>到多线程内容时,再修改
-
-
-
代码块(初始化块)
静态代码块:
内部可以输出语句
随着类的加载而执行,而且只执行一次
作用:初始化类的信息
如果一个类中定义了多个静态代码块,则按照声明的先后顺序执行
静态代码块的执行要优先于非静态代码块的执行
静态代码块内只能调用静态的属性、静态的方法,不能调用非静态的结构非静态代码块:
内部可以输出语句
随着对象的创建而执行,每创建一个对象就执行一次
作用:可以在创建对象时,对对象的属性等进行初始化
如果一个类中定义了多个非静态代码块,则按照声明的先后顺序执行
非静态代码块内可以调用静态的属性、静态的方法,或非静态的属性、非静态的方法 -
final关键字
- final 用来修饰一个类:此类不能被其他类所继承。比如:String类、System类、StringBuffer类
final 用来修饰方法:表明此方法不可以被重写。比如:Object类中getClass();
final 用来修饰变量:此时的"变量"就称为是一个常量
-
final修饰属性:可以考虑赋值的位置:**显式初始化、代码块中初始化、构造器中初始化**
-
final修饰局部变量:尤其是使用final修饰形参时,表明此形参是一个常量。当我们调用此方法时,给常量形参赋一个实参。一旦赋值以后,就只能在方法体内使用此形参,但**不能进行重新赋值**。
-
static final 用来修饰属性:全局常量
-
abstract关键字(只能修饰:类、方法)
- abstract修饰类:抽象类
- 此类不能实例化
- 抽象类中一定有构造器,便于子类实例化时调用(涉及:子类对象实例化的全过程)
- 开发中,都会提供抽象类的子类,让子类对象实例化,完成相关的操作 —>抽象的使用前提:继承性
- abstract修饰方法:抽象方法
- 抽象方法只有方法的声明,没方法体。包含抽象方法的类,一定是一个抽象类。反之,抽象类中可以没有抽象方法的。
- 若子类重写了父类中的所有的抽象方法后,此子类方可实例化
- 若子类没重写父类中的所有的抽象方法,则此子类也是一个抽象类,需要使用abstract修饰。
- 注意点:
- abstract不能用来修饰:属性、构造器等结构
- abstract不能用来修饰私有方法、静态方法、final的方法、final的类
- abstract修饰类:抽象类
-
匿名类:当某些抽象类的子类只是临时调用一次时,可通过匿名类的方式临时声明一个类并在声明的同时创建该类的对象。
abstract class Person{ String name; int age; public abstract void eat(); public abstract void behave(); } //创建匿名类 Person man = new Person(){ public void eat(){ System.out.println("狼吞虎咽"); }; public void behave(){ System.out.println("血气方刚"); }; } //创建匿名类的匿名对象 //method(Object obj); method(new Person(){ public void eat(){ System.out.println("狼吞虎咽"); }; public void behave(){ System.out.println("血气方刚"); }; });
-
模板类设计:设计一个抽象模板类,调用相应的功能时再声明相应的子类
//抽象类的应用:模板方法的设计模式 abstract class BankTemplateMethod { // 具体方法 public void takeNumber() { System.out.println("取号排队"); } public abstract void transact(); // 办理具体的业务 //钩子方法 public void evaluate() { System.out.println("反馈评分"); } // 模板方法,把基本操作组合到一起,子类一般不能重写 public final void process() { this.takeNumber(); this.transact();// 像个钩子,具体执行时,挂哪个子类,就执行哪个子类的实现代码 this.evaluate(); } } class DrawMoney extends BankTemplateMethod { public void transact() { System.out.println("我要取款!!!"); } } class ManageMoney extends BankTemplateMethod { public void transact() { System.out.println("我要理财!我这里有2000万美元!!"); } } public class TemplateMethodTest { public static void main(String[] args) { BankTemplateMethod btm = new DrawMoney(); btm.process(); BankTemplateMethod btm2 = new ManageMoney(); btm2.process(); } }
-
interface关键字(接口的使用):一种特殊的抽象类
- 接口和类是并列的两个结构,可以将接口理解成一种特殊的抽象类
-
如何定义接口:定义接口中的成员
-
JDK7及以前:只能定义全局常量和抽象方法
-
全局常量:**public static final**的.但是书写时,可以省略不写
-
抽象方法:**public abstract**的
-
-
JDK8:除了定义全局常量和抽象方法之外,还可以定义静态方法、默认方法
-
接口中定义的静态方法,只能通过**”接口名.方法“**来调用
-
通过实现类的对象,可以调用接口中的默认方法,”对象名.方法“。
如果实现类重写了接口中的默认方法,调用时,仍然调用的是重写以后的方法
-
如果子类(或实现类)继承的父类和实现的接口中声明了同名同参数的默认方法,
那么子类在没有重写此方法的情况下,默认调用的是父类中的同名同参数的方法。–>类优先原则 -
如果实现类实现了多个接口,而这多个接口中定义了同名同参数的默认方法,
那么在实现类没有重写此方法的情况下,报错。这就需要我们必须在实现类中重写此方法。 -
如何在子类(或实现类)的方法中调用父类、接口中已经被重写的方法
调用本类重写的方法:”方法名“
调用父类中被重写的方法:“super.方法名”
调用接口中被重写的方法:”接口名.super.方法名“
-
public class SubClassTest { public static void main(String[] args) { SubClass s = new SubClass(); // s.method1(); // SubClass.method1();//不能通过实现类或实现类的对象调用接口的静态方法 //知识点1:接口中定义的静态方法,只能通过接口来调用。 CompareA.method1(); //知识点2:通过实现类的对象,可以调用接口中的默认方法。 //如果实现类重写了接口中的默认方法,调用时,仍然调用的是重写以后的方法 s.method2(); //知识点3:如果子类(或实现类)继承的父类和实现的接口中声明了同名同参数的默认方法, //那么子类在没有重写此方法的情况下,默认调用的是父类中的同名同参数的方法。-->类优先原则 //知识点4:如果实现类实现了多个接口,而这多个接口中定义了同名同参数的默认方法, //那么在实现类没有重写此方法的情况下,报错。-->接口冲突。 //这就需要我们必须在实现类中重写此方法 s.method3(); } } interface CompareA{ public static void method1(){ System.out.println("CmopareA:静态方法"); } public default void method2(){ System.out.println("CmopareA:默认方法2"); } public default void method3(){ System.out.println("CmopareA:默认方法3"); } } interface CompareB{ public static void method1(){ System.out.println("CmopareB:静态方法"); } public default void method2(){ System.out.println("CmopareB:默认方法2"); } public default void method3(){ System.out.println("CmopareB:默认方法3"); } } class SuperClass{ public void method3(){ System.out.println("SuperClass:默认方法3"); } } class SubClass extends SuperClass implements CompareA,CompareB{ public void method2(){ System.out.println("SubClass:上海"); } public void method3(){ System.out.println("SubClass:深圳"); } //知识点5:如何在子类(或实现类)的方法中调用父类、接口中被重写的方法 public void myMethod(){ method3();//调用自己定义的重写的方法 super.method3();//调用的是父类中声明的 //调用接口中的默认方法 CompareA.super.method3(); CompareB.super.method3(); } }
-
-
接口中不能定义构造器的!意味着接口不可以实例化
-
Java开发中,接口通过让类去实现(implements)的方式来使用.
-
只有实现类覆盖了接口中的所抽象方法,此实现类才可以实例化
如果实现类没覆盖接口中所的抽象方法,则此实现类仍为一个抽象类
-
Java类可以实现多个接口 —>弥补了Java单继承性的局限性
格式:class AA extends BB implements CC,DD,EE
-
接口与接口之间可以继承,而且可以多继承
-
接口的匿名实现(匿名实现类、匿名实现类的匿名对象):该实现(及对象)只使用一次,故不赋予变量名,匿名实现;
-
接口的具体使用,体现多态性
class Computer{ //这里将接口作为形参,实际调用时传入实现类的对象,体现了多态性 public void transferData(USB usb){ usb.start(); System.out.println("具体传输数据的细节"); usb.stop(); } } interface USB{ void start(); void stop(); } class Flash implements USB{ @Override public void start() { System.out.println("U盘开启工作"); } @Override public void stop() { System.out.println("U盘结束工作"); } } class Printer implements USB{ @Override public void start() { System.out.println("打印机开启工作"); } @Override public void stop() { System.out.println("打印机结束工作"); } } public class USBTest { public static void main(String[] args) { Computer com = new Computer(); //1.创建了接口的非匿名实现类的非匿名对象 Flash flash = new Flash(); com.transferData(flash); //2. 创建了接口的非匿名实现类的匿名对象 com.transferData(new Printer()); //3. 创建了接口的匿名实现类的非匿名对象 USB phone = new USB(){ @Override public void start() { System.out.println("手机开始工作"); } @Override public void stop() { System.out.println("手机结束工作"); } }; com.transferData(phone); //4. 创建了接口的匿名实现类的匿名对象 com.transferData(new USB(){ @Override public void start() { System.out.println("mp3开始工作"); } @Override public void stop() { System.out.println("mp3结束工作"); } }); } }
-
代理模式:当需要执行被代理对象的方法时,我们不直接通过被代理类对象的方法,而是通过代理类的对象完成该操作
interface NetWork{ public void browse(); } //被代理类 class Server implements NetWork{ @Override public void browse() { System.out.println("真实的服务器访问网络"); } } //代理类 class ProxyServer implements NetWork{ private NetWork work; public ProxyServer(NetWork work){ this.work = work; } public void check(){ System.out.println("联网之前的检查工作"); } @Override public void browse() { check(); work.browse(); } } public class NetWorkTest { public static void main(String[] args) { Server server = new Server(); // server.browse();//无需直接通过被代理类调用该方法 ProxyServer proxyServer = new ProxyServer(server); proxyServer.browse(); } }
-
内部类
-
定义:Java中允许将一个类A声明在另一个类B中,则类A就是内部类,类B称为外部类.
-
内部类的分类:
成员内部类(静态、非静态 ) vs 局部内部类(类方法内、类代码块内、类构造器内) -
成员内部类的理解:
一方面,作为外部类的成员:- 调用外部类的结构
- 可以被static修饰
- 可以被4种不同的权限修饰
另一方面,作为一个类:
- 类内可以定义属性、方法、构造器等
- 可以被final修饰,表示此类不能被继承。言外之意,不使用final,就可以被继承
- 可以被abstract修饰
-
局部内部类的使用
-
创建成员内部类的对象
-
在成员内部类中调用外部类的结构
-
局部内部类的使用
class Person{ String name; int age; //静态成员内部类 public static class Cat{ String name; public void behave(){ System.out.println("喵喵喵"); } } //非静态成员内部类 public class Dog{ String name; public void behave(){ System.out.println("汪汪汪"); } //在成员内部类中调用外部类的结构 public void test(String name){ System.out.println(name);//方法的形参 System.out.println(this.name);//内部类的属性 System.out.println(Person.this.name);//外部类的属性 } } //开发中局部内部类常见的应用方式即下方的方式 public Comparable getComparable() { //局部内部类 class Cmp implements Comparable{ @Override public int compareTo(Object o) { return 0; } } } //方式二 public Comparable getComparable() { //直接返回接口的匿名实现类的匿名对象 return new Comparable(){ @Override public int compareTo(Object o) { return 0; } }; } } //创建内部类对象 //创建静态成员内部类 Person.Cat cat = new Person.cat(); //创建非成员 Person p = new Person(); Person.Dog dog = p.new Dog(); //在成员内部类中调用外部的结构 public class Dog{ String name; public void behave(){ System.out.println("汪汪汪"); } //在成员内部类中调用外部类的结构 public void test(String name){ System.out.println(name);//方法的形参 System.out.println(this.name);//内部类的属性 System.out.println(Person.this.name);//外部类的属性 } } //局部内部类的使用(开发中常用方式) //方式一 public Comparable getComparable() { //局部内部类 class Cmp implements Comparable{ @Override public int compareTo(Object o) { return 0; } } } //方式二 public Comparable getComparable() { //直接返回接口的匿名实现类的匿名对象 return new Comparable(){ @Override public int compareTo(Object o) { return 0; } }; }
-
-
异常处理
-
异常体系
- java.lang.Throwable
- java.lang.Error:一般不编写针对性的代码进行处理。
- java.lang.Exception:可以进行异常的处理
- 编译时异常(checked)
- IOException
- FileNotFoundException
- ClassNotFoundException
- IOException
- 运行时异常(unchecked,RuntimeException)
- NullPointerException
- ArrayIndexOutOfBoundsException
- ClassCastException
- NumberFormatException
- InputMismatchException
- ArithmeticException
- 编译时异常(checked)
- java.lang.Throwable
-
异常举例
//ArithmeticException @Test public void test6(){ int a = 10; int b = 0; System.out.println(a / b); } //InputMismatchException @Test public void test5(){ Scanner scanner = new Scanner(System.in); int score = scanner.nextInt(); System.out.println(score); scanner.close(); } //NumberFormatException @Test public void test4(){ String str = "123"; str = "abc"; int num = Integer.parseInt(str); } //ClassCastException @Test public void test3(){ Object obj = new Date(); String str = (String)obj; } //IndexOutOfBoundsException @Test public void test2(){ String str = "abc"; System.out.println(str.charAt(3)); } //NullPointerException @Test public void test1(){ String str = "abc"; str = null; System.out.println(str.charAt(0)); } //******************以下是编译时异常*************************** @Test public void test7(){ // File file = new File("hello.txt"); // FileInputStream fis = new FileInputStream(file); // // int data = fis.read(); // while(data != -1){ // System.out.print((char)data); // data = fis.read(); // } // // fis.close(); }
-
异常处理注意事项
-
java异常处理的抓抛模型
- 过程一:“抛”:程序在正常执行的过程中,一旦出现异常,就会在异常代码处生成一个对应异常类的对象。
并将此对象抛出,一旦抛出对象以后,其后的代码就不再执行。
关于异常对象的产生:① 系统自动生成的异常对象② 手动的生成一个异常对象,并抛出(throw)
- 过程二:“抓”:可以理解为异常的处理方式:① try-catch-finally ② throws
-
异常处理方式一:try-catch-finally
try{ //可能出现异常的代码 }catch(异常类型1 变量名1){ //处理异常的方式1 }catch(异常类型2 变量名2){ //处理异常的方式2 }catch(异常类型3 变量名3){ //处理异常的方式3 } .... finally{ //一定会执行的代码 }
-
说明:
-
使用try将可能出现异常代码包装起来,在执行过程中,一旦出现异常,就会生成一个对应异常类的对象,根据此对象的类型,去catch中进行匹配。
-
一旦try中的异常对象匹配到某一个catch时,就进入catch中进行异常的处理。一旦处理完成,就跳出当前的try-catch结构(在没写finally的情况)。继续执行其后的代码
-
catch中的异常类型如果没子父类关系,则谁声明在上,谁声明在下无所谓。catch中的异常类型**如果满足子父类关系,则要求子类一定声明在父类的上面**。否则,报错
-
常用的异常对象处理的方式: ① **String getMessage()** ② **printStackTrace()**
-
在try结构中声明的变量,再出了try结构以后,就不能再被调用
-
try-catch-finally结构可以嵌套
-
finally是可选的,finally中声明的是一定会被执行的代码,即使**catch中又出现异常**或**try中有return语句**,**catch中有return语句**等情况。
-
像数据库连接、输入输出流、网络编程Socket等资源,JVM是不能自动的回收的,我们需要自己手动的进行资源的释放。此时的资源释放,就需要声明在finally中。
-
-
总结:如何看待代码中的编译时异常和运行时异常?
-
体会1:使用try-catch-finally处理编译时异常,是得程序在编译时就不再报错,但是运行时仍可能报错。相当于我们使用try-catch-finally将一个编译时可能出现的异常,延迟到运行时出现。
-
体会2:开发中,由于运行时异常比较常见,所以我们通常就不针对运行时异常编写try-catch-finally了。针对于编译时异常,我们说一定要考虑异常的处理。
-
-
-
异常处理方式二:
"throws + 异常类型"写在方法的声明处。指明此方法执行时,可能会抛出的异常类型。
一旦当方法体执行时,出现异常,仍会在异常代码处生成一个异常类的对象,此对象满足throws后异常类型时,就会被抛出。异常代码后续的代码,就不再执行! -
对比两种处理方式
try-catch-finally:真正的将异常给处理掉了。
throws的方式只是将异常抛给了方法的调用者。并没真正将异常处理掉。 -
体会开发中应该如何选择两种处理方式?
-
如果父类中被重写的方法没throws方式处理异常,则子类重写的方法也不能使用throws,意味着如果子类重写的方法中异常,必须使用try-catch-finally方式处理。
-
执行的方法a中,先后又调用了另外的几个方法,这几个方法是递进关系执行的。我们建议这几个方法使用throws的方式进行处理。而执行的方法a可以考虑使用try-catch-finally方式进行处理。
//异常处理实例 public class FinallyTest { @Test public void test2(){ FileInputStream fis = null; try { File file = new File("hello1.txt"); fis = new FileInputStream(file); int data = fis.read(); while(data != -1){ System.out.print((char)data); data = fis.read(); } } catch (FileNotFoundException e) { e.printStackTrace();//异常对象处理方式一 } catch (IOException e) { System.out.println(e.getMessage());//异常对象处理方式二 }finally{ try { if(fis != null) fis.close(); } catch (IOException e) { e.printStackTrace(); } } } public int method(){ try{ int[] arr = new int[10]; System.out.println(arr[10]); return 1; }catch(ArrayIndexOutOfBoundsException e){ e.printStackTrace(); return 2; }finally{ System.out.println("我一定会被执行"); return 3;//最终执行finally语句,返回3 } } }
-
-
-
手动抛出异常
- throw 和 throws区别:
throw 表示抛出一个异常类的对象,生成异常对象的过程。声明在方法体内。
throws 属于异常处理的一种方式,声明在方法的声明处。
class Student{ private int id; public void regist(int id) throws Exception { if(id > 0){ this.id = id; }else{ //手动抛出异常对象 throw new RuntimeException("您输入的数据非法!");//这里调用了RuntimeException类的构造器 } } }
- throw 和 throws区别:
-
自定义异常类
-
继承于现的异常结构:RuntimeException 、Exception
-
提供全局常量:serialVersionUID
-
提供重载的构造器
public class MyException extends Exception{ static final long serialVersionUID = -7034897193246939L; public MyException(){ } public MyException(String msg){ super(msg); } }
fis = new FileInputStream(file);
int data = fis.read();
while(data != -1){
System.out.print((char)data);
data = fis.read();
}
} catch (FileNotFoundException e) {
e.printStackTrace();//异常对象处理方式一
} catch (IOException e) {
System.out.println(e.getMessage());//异常对象处理方式二
}finally{
try {
if(fis != null)
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public int method(){try{ int[] arr = new int[10]; System.out.println(arr[10]); return 1; }catch(ArrayIndexOutOfBoundsException e){ e.printStackTrace(); return 2; }finally{ System.out.println("我一定会被执行"); return 3;//最终执行finally语句,返回3 }
}
}
-
-
手动抛出异常
- throw 和 throws区别:
throw 表示抛出一个异常类的对象,生成异常对象的过程。声明在方法体内。
throws 属于异常处理的一种方式,声明在方法的声明处。
class Student{ private int id; public void regist(int id) throws Exception { if(id > 0){ this.id = id; }else{ //手动抛出异常对象 throw new RuntimeException("您输入的数据非法!");//这里调用了RuntimeException类的构造器 } } }
- throw 和 throws区别:
-
自定义异常类
-
继承于现的异常结构:RuntimeException 、Exception
-
提供全局常量:serialVersionUID
-
提供重载的构造器
public class MyException extends Exception{ static final long serialVersionUID = -7034897193246939L; public MyException(){ } public MyException(String msg){ super(msg); } }
-