异常
Java Day10
一、函数式接口
@Funcitonallnterface (新版本)
加上改注解,编译器会默认这个类是一个函数式接口,只能存在一个类方法,如果存在两个方法编译器会报错
下面编译器会报错
1
2
3
4
5
interface One_method{
public abstract void Pt();
// public void p(); 不能声明第二方法
}
二、匿名内部类
- 本质上还是一个类
- 是一个内部类
- 该类没有名字(编译器会给该类一个代号)
传统思路,一个普通类继承了接口类,需要实例化对象我们才能调用接口中的抽象方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18public class Main implements InterFace{
public void Prt(){
System.out.println("重写了函数式接口的方法");
}
public static void main(String[] args) {
//传统思路 用类-》实现抽象类中方法-》实例化类并且调用方法
InterFace m = new Main();
m.Prt();
}
}
interface InterFace{
public abstract void Prt();
}当我们只是想单纯调用接口中的Prt方法,上述方法会显得非常冗余
此时我们就可以采用匿名类,来简化上述代码
1 | //匿名内部类 |
- 多内部中多可方法
1 | //匿名内部类 |
虽然匿名内部类没有名字, 但系统会给我自动生产一个类名
上述是在Main内种的内部类 所以该匿名内部类中的名字为class Main$1 如果还有第二个那么他的名字为Main$2
注意事项:
1、使用匿名内部类时,我们必须是继承一个类或者实现一个接口,但是两者不可兼得,同时也只能继承一个类或者实现一个接口。
2、匿名内部类中是不能定义构造函数的。
3、匿名内部类中不能存在任何的静态成员变量和静态方法。
4、匿名内部类为局部内部类,所以局部内部类的所有限制同样对匿名内部类生效。
5、匿名内部类不能是抽象的,它必须要实现继承的类或者实现的接口的所有抽象方法。
匿名内部类使用的最佳环境,当一个函数需要传入一个类时,并且只使用了该类的极少的方法,我们可以使用这种方法来简化。
小练习
数据库链接 oracle 、mysql
1 | import java.util.Scanner; |
三、成员变量的默认值
Java、c、c++ 这类语言中局部变量声明定义后必须要初始化,否则会出错
1
2
3
4
5
6
7
8public class Init{
public static void main(String[] args) {
int i;
System.out.println(i);
}
}
---
java: 可能尚未初始化变量i在类中,你为给定其值时编译器会自动给你赋值,(java类 c结构体 c++类 中都有这类特性)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30public class Init{
int a;
short b;
long c;
String d;
boolean e;
char f;
double g;
float h;
Chlid i;
public static void main(String[] args) {
//默认值
Init a = new Init();
System.out.println(a.a);
System.out.println(a.b);
System.out.println(a.c);
System.out.println(a.d);
System.out.println(a.e);
System.out.println(a.f);
System.out.println(a.g);
System.out.println(a.i);
System.out.println(a.i);
}
}
class Chlid{
}上述展示了大部分成员变量的默认值
四、object
1.object 是一个类
所有类默认继承了object,你定义一个空类,你会发现,类可以调用一些你从来没有定义过的方法
2.equals方法 、== 的区别
equals 是object 提供的判断一个对象是否等于另一个对象
== 用于判断两个内存地址的值是否相等
1
2
3
4
5
6
7
8
9
10
11
12
13
14public class Equals {
public static void main(String[] args) {
String a = "2333";
String b = "2333";
System.out.println(a == b);
System.out.println(a.equals(b));
}
}
---
true
true两种判断是不一样的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15public class Equals {
public static void main(String[] args) {
String a = new String("2333");
String b = new String("2333");
System.out.println(a == b);
System.out.println(a.equals(b));
}
}
---
false
true
a b 他们值都相同,但是使用equals和==判断会得到两种不同结果呢?
如果熟悉其他编程语言,对于这个问题可能理解的很快
在程序里最常用的数据类型是字符串,如果有大量相同的字符串会极大的占据空间,那么我们可以只开辟一个字符串的空间,让其他相同值的变量指向同一块内存地址,从而优化内存空间
1中 两个a b 都是同一个内存地址所以== 和 equals判断都为true
2中 注意我们使用了new 开辟了两块空间让对象a,b分别指向,又因为相同字符串在内存中只会存储一份a 和b的实际字符串数值还是会指向 内存中同一块地址
- 所以对象a 和 对象b 本身是两个不同地址的对象 == 只判断其地址相等与否 所以为false
- 而equals 判断的是其值得地址是否相等 所以为true
3.判断类是否相等
显然用== 判断 是不行的
1 | //定义一个猫类 |
- 重构object 中的equals 自定义条件来判断
1 | //定义一个猫类 |
总结:
在对字符串进行比较时候我们要使用object 提供的 equals() 来比较
基础数据类型用 ==
4.toString方法
打印输出类的相关信息
1 | package com.aaa.bbb; |
默认 包+类 @内存地址
显然这种输出我们是不满意的,我可以重写该方法
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
31package com.aaa.bbb;
public class Person {
String name;
int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public static void main(String[] args) {
Person a = new Person("小张", 21);
System.out.println(a);
System.out.println(a.toString());
}
//idea 自动生成重写的tostring()方法
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
---
Person{name='小张', age=21}
Person{name='小张', age=21}
五、instanceof 关键字
判断一个对象是不是否是某个对象
1 | class Animal{ |
六、参数传递
对于参数传递,如果是第一次了解,确实抽象。好在我之前有学过C++ 理解起来就很容易,一下是自己的一些见解
首先对于参数传递问题,我们可以了解下堆栈的机制和块作用域,我就拿块作用域来说吧,{ } ( ) 括号中的代码就是一个块作用域,在括号里面声明的变量,一旦运行结束 里面声明的变量什么的都会被销毁(栈回收,类会随着程序结束而回收),也就是我们称作的局部变量,它的什么周期只限于在他运行时候。理解完 下面的就会容易很多
1.值传递
基础类型传递,来看看下面代码
- 一个基础的int型 变量
1 | public class Demo01 { |
我们发现i在change 方法里是20 但是main方法里的i还是10
因为java是值传递 i这个变量的值原本为10 传入 到change 形参i2 这个形参是只作用于chage 这个方法内,他和这个块外的i不是同一个变量,只是值相同罢了。
所以chage方法内 赋值的20 一旦chage运行结束这个i2 变量就消失了
- 实例化了一个有两个成员变量的对象,并改变了提供的方法中改变了其值
1 | public class Demo01 { |
这个很好理解,形参n是先将n的值获取了 chage方法内使用new 为 n 在堆中开辟了另一块区域 但是这个新的区域也会随着 change生命的结束 而被回收
2.引用传递 — (地址传递)
引用传递,Java传递本质是依然是值传递,
1 | public class Demo01 { |
我们发现值改变了,为什么呢?
形参是对象时,这个对象和基础数据类型,是不一样的。对象中的值为指向堆中的地址,而基本数据类型的值就是他本身,所以一个是地址一个是值,他们都是值。。。
- 基础数据类型传入的是基础数据类型本身的值;
- 而对象传入时,对象本身的值是指向其在堆空间地址
明白上述两点,我们传入了n这个对象的地址, 通过n1这个新的变量来访问这片空间,并且改变了这个空间中的值。随着change函数的结束n1这个变量被销毁了。但是n这个变量,因为主函数main还在运行,所以依旧存在。
总结:知道java是值传递就行了。。。
七、异常处理
编译时异常,自己改、
运行时出现错误
1 | public class Demo01 { |
- 异常错误,运行时异常
- 抛出异常列,创建一个异常对象,并把对象抛出
- 补货异常对象,默认jvm来处理错误并把不能处理的错误打印出来,jvm会让程序停止
1.异常的分类
异常是一个对象因此也会有各种异常还继承关系:
- Throwable (父类)
- Error
- Exception
- RuntimeException
- 一堆Exception
- 其他的各类Exception
- RuntimeException
Error:一般是系统级的错误,并且比较难处理
我最常解决的是Exception的错误类抛出
其他各类Exception:必须处理才能编译运行 编译时异常
RuntimeException:是程序跑起来运行中的错误 运行时异常
2.异常处理
try..catch
因为jvm来处理会停止程序,但是我们依然想让程序继续运行下去,我就要自己处理异常,跳过jvm
try{
容易出错的代码
}catch(出错类 e){
处理异常的代码
}finally{
最终无论执行过try 还是catch
}
1 | public class Demo01 { |
throws 错误类型{}
表示方法可能抛出一个异常
1 | import java.io.File; |
read()方法可能会产生一个错误,向上抛出一个错误,我们可以用try catch 解决
throw
主动向外抛出一个异常
1 | public class Demo01 { |
自定义异常
自定义异常类必须成Exception或者RuntimeException
1 | public class AutoException extends Exception{ |