day10.final-内部类-异常-Object类
课前回顾:
1.抽象:abstract
2.注意:
抽象方法所在的类一定是抽象类
抽象类中不一定非得有抽象方法(还可有构造,成员变量,成员方法)
抽象类不能直接new对象,需要创建子类对象来实现
子类重写抽象父类时,需要重写父类中所有的抽象方法,除非子类也是一个抽象类
3.多态:
前提:
必须有子父类继承关系还有就是接口实现关系
必须有方法重写,没有方法重写多态没有意义
父类引用指向子类对象
好处:
当参数传递时,传递父类类型,到时候传递实参时,可以传递任何这个父类的子类,扩展性强
弊端:
不能调用子类特有功能
转型:
向上转型:默认的
向下转型:强转 ->调用子类的特有方法
关键字:instanceof 判断类型
对象名 instanceof 类型 ->判断对象是否属于这个类型
4.接口:标准
关键字: interface(接口) implenments(实现)
成员:
jdk7:
抽象方法:带abstract 不写也有
成员变量:public static final ->常量 接口名直接调用
jdk8:
默认方法:带default的方法->可重写可不重写
静态方法:带static的方法->接口名直接调用
5.接口和抽象类的区别:
相同点:
两者都是作为继承的顶端
不同点:
抽象类一般作为父类使用:可以抽取共有的变量,成员方法等
接口作为一个功能的标准:抽取的都是方法
6.接口特点:
接口可以多继承
一个类可以实现一个或者多个接口
一个类还可以继承一个父类的同时实现一个或者多个接口
7.final关键字:
a.被final修饰的类不能被继承
b.被final修饰的方法不能被重写 不能和abstract一起使用
今日重点:
1.会使用final关键字
2.会使用匿名内部类
3.会使用throws处理异常
4.会使用try...catch处理异常
5.分清楚什么是编译时期异常,什么是运行时期异常
第一章.final关键字
1.概述:最终的,最后的
1.final修饰类
1.格式:
public final class 类名{
}
2.特点:
被final修饰的类,不能被继承的
public final class Animal {
public void eat(){
System.out.println("动物都要吃饭");
}
}
public class Dog extends Animal{//因为Animal类被final修饰了,所以不能被继承
}
2.final修饰方法
1.格式:
修饰符 final 返回值类型 方法名(参数){
方法体
return 结果
}
2.特点:
被final修饰的方法不能被重写的
3.注意:
final和abstract不能同时修饰方法
public abstract class Animal {
public final void eat(){
System.out.println("动物都要吃饭");
}
/*
final和abstract不能一起修饰方法,因为冲突
final修饰的方法不能被重写
abstract修饰的方法必须要被重写
所以冲突
*/
//public final abstract void drink();
}
public class Dog extends Animal{
/*
被final修饰的方法不能被重写
public void eat(){
}
*/
}
3.final修饰局部变量
1.格式:
final 数据类型 变量名 = 值
2.特点:
被final修饰的局部变量,不能被二次赋值,可以理解为是一个常量
public class Test01 {
public static void main(String[] args) {
final int i = 10;
//i = 20;final修饰的变量不能被二次赋值
final int j;
j = 100;//属于给变量j第一次赋值
//j = 200;//属于给j第二次赋值
}
}
4.final修饰对象
1.格式:
final 类型 对象名 = new 类名()
2.特点:
被final修饰的对象地址值不能改变,但是对象的属性值可以变
public class Person {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
public class Test01 {
public static void main(String[] args) {
final Person p1 = new Person("柳岩", 36);
System.out.println(p1);//地址值
//p1 = new Person("金莲",32);p1被final修饰,地址值直接定死,不能改变地址值
p1.setName("杨幂");
p1.setAge(32);
System.out.println(p1.getName()+"..."+p1.getAge());
}
}
5.final修饰成员变量
1.格式:
final 数据类型 变量名 = 值
2.特点:
被final修饰的成员变量不能被二次赋值
被final修饰的成员变量需要手动赋值
public class Person {
final String name = "大郎";
public Person() {
//name = "大郎";
}
/* public Person(String name) {
this.name = name;
}*/
public String getName() {
return name;
}
/* public void setName(String name) {
this.name = name;
}*/
}
小结:
final修饰类:不能被继承
final修饰方法:不能被重写
final修饰局部变量:不能被二次赋值
final修饰对象:地址值不能变,属性值可以改变
final修饰成员变量:需要手动赋值 不能被二次赋值
第二章.内部类
当一个事物的内部,还有一个部分需要一个完整的结构进行描述,而这个内部的完整的结构又只为外部事物提供服务,那么整个内部的完整结构最好使 用内部类。
在Java中,允许一个类的定义位于另一个类的内部,前者称为内部类,后者 称为外部类。
public class A{
class B{
}
}
B就是A的内部类
A就是B的外部类
Inner class一般用在定义它的类或语句块之内,在外部引用它时必须给出完 整的名称。
Inner class的名字不能与包含它的外部类类名相同;
分类:
成员内部类(static成员内部类和非static成员内部类)
局部内部类
匿名内部类
1 静态成员内部类
1.格式:直接在声明内部类的时候加上static关键字即可
类名{
static class 类名{
}
}
2.注意:
a.类中可以定义属性,方法,构造等
b.静态内部类可以被final abstract修饰
被final修饰之后,不能被继承
被abstract修饰之后,不能被new
c.静态内部类不能调用外部类的非静态成员(静态不能直接调用非静态)
d.还可以被四种权限修饰符所修饰
3.如何调用静态内部类成员
外部类类名.静态内部类类名 对象名 = new 外部类类名.静态内部类类名()
public class Person {
public void eat(){
System.out.println("人要吃饭");
}
//静态成员内部类
static class Heart{
public void jump(){
System.out.println("心会跳");
//new Person().eat();在静态内部类中调用外部类中非静态成员
}
}
}
public class Test01 {
public static void main(String[] args) {
//外部类类名.静态内部类类名 对象名 = new 外部类类名.静态内部类类名()
Person.Heart heart = new Person.Heart();
heart.jump();
}
}
2 非静态成员内部类
1.格式:直接在声明内部类的时候不加上static关键字即可
类名{
class 类名{
}
}
2.注意:
a.类中可以定义属性,方法,构造等
b.静态内部类可以被final abstract修饰
被final修饰之后,不能被继承
被abstract修饰之后,不能被new
c.非静态内部类能调用外部类的非静态成员(非静态能直接调用非静态)
d.还可以被四种权限修饰符所修饰
3.如何调用非静态内部类成员
外部类.内部类 对象名 = new 外部类().new 内部类()
public class Person {
public void eat(){
System.out.println("人要吃饭");
}
//非静态成员内部类
class Heart{
public void jump(){
System.out.println("心会跳");
//eat();在非静态内部类中调用外部类中非静态成员
}
}
}
public class Test01 {
public static void main(String[] args) {
//外部类类名.静态内部类类名 对象名 = new 外部类类名.静态内部类类名()
/*Person.Heart heart = new Person.Heart();
heart.jump();*/
Person.Heart heart = new Person().new Heart();
heart.jump();
}
}
如果外部类的成员变量和内部类的成员变量以及局部变量重名,该如何区分?
public class Person {
String name = "柳岩";
class Heart{
String name = "心脏";
public void display(String name){
System.out.println(name);//局部变量name->就近原则
System.out.println(this.name);//内部类成员变量 name
System.out.println(Person.this.name);//外部类成员变量 name
}
}
}
public class Test {
public static void main(String[] args) {
//外部类.内部类 对象名 = new 外部类().new 内部类()
Person.Heart heart = new Person().new Heart();
heart.display("涛哥");
}
}
3 局部内部类
1.可以在方法中,代码块中,构造方法中使用
public class Person {
public void method(){
//局部内部类
class Heart{
public void jump(){
System.out.println("心在跳");
}
}
new Heart().jump();
}
}
public class Test01 {
public static void main(String[] args) {
Person person = new Person();
person.method();
}
}
public interface USB {
public abstract void open();
}
public class Test {
public static void main(String[] args) {
USB usb = method();//多态
usb.open();//调用的是实现类重写的方法
}
public static USB method(){
class Mouse implements USB{
@Override
public void open() {
System.out.println("鼠标打开了");
}
}
return new Mouse();
}
}
4.匿名内部类(重点)
1.是内部类的简化写法。它的本质是一个`带具体实现的` `父类或者父接口的` `匿名的` **子类对象**。开发中,最常用到的内部类就是匿名内部类了。以接口举例,当你使用一个接口时,似乎得做如下几步操作,
a. 定义实现类
b. 重写接口中的方法
c. 创建子类对象
d. 调用重写后的方法
我们的目的,最终只是为了调用方法,那么能不能简化一下,把以上四步合成一步呢?匿名内部类就是做这样的快捷方式。
2.可以定义的位置:可以在方法中,代码块中,构造方法中使用->匿名内部类也是局部的
3.格式:
new 父类/接口(){
重写方法
}.重写的方法;
或者
父类类型/接口类型 对象名 = new 父类/接口(){
重写方法
};
对象名.重写的方法;
4.注意:匿名内部类代表的是子类对象
public interface USB {
public abstract void open();
}
public class Test {
public static void main(String[] args) {
Mouse mouse = new Mouse();
mouse.open();
System.out.println("================");
new USB(){
@Override
public void open() {
System.out.println("鼠标开启啦");
}
}.open();
System.out.println("================");
USB usb = new USB(){
@Override
public void open() {
System.out.println("鼠标开启啦啦啦啦啦了");
}
};
usb.open();
}
}
2.2 匿名内部类复杂用法_当参数传递
public interface USB {
public abstract void open();
}
public class Test02 {
public static void main(String[] args) {
//method(new Mouse());
//使用匿名内部类传递
method(new USB() {
@Override
public void open() {
System.out.println("鼠标开启啦");
}
});
}
public static void method(USB usb){//USB usb = mouse
usb.open();
}
}
2.3 匿名内部类复杂用法_当返回值返回
public interface USB {
public abstract void open();
}
public class Test03 {
public static void main(String[] args) {
USB usb = method();
usb.open();//重写的open
}
public static USB method(){
//return new Mouse();
return new USB() {
@Override
public void open() {
System.out.println("键盘开启");
}
};
}
}
第三章.异常
1.异常介绍
1.已经见过的异常:
ArrayIndexOutOfBoundsException:索引越界异常
NullPinterException:空指针异常
ClassCastException:类型转换异常
2.概述:异常在java中其实是一个一个的类,一旦出现,代表我们的代码有问题
3.异常体系:如下图
public class Demo01Exception {
public static void main(String[] args) throws FileNotFoundException {
int[] arr = new int[3];
//System.out.println(arr[120]);//运行时期异常
//FileInputStream fis = new FileInputStream("day10\\1.txt");//编译时期异常
//错误
int[] arr1 = new int[999999999];
System.out.println(arr1[9]);
}
}
2.异常出现的过程
3.创建异常对象(了解)
1.格式:
throw new 异常对象()->创建异常对象并抛出
public class Demo03Exception {
public static void main(String[] args) {
String s = null;
method(s);
}
public static void method(String s){
if (s==null){
//创建异常对象,throw代表抛出
throw new NullPointerException("我的身体被掏空!");
}
System.out.println("哈哈哈哈");
}
}
4.异常处理方式(重点)
4.1 异常处理方式一_throws
1.格式:
在方法声明上->参数后面,方法体左半个大括号前面
throws 异常
public class Demo04Exception {
public static void main(String[] args)throws FileNotFoundException {
String s = "";
method(s);
System.out.println("涛哥涛哥涛哥涛哥");
}
public static void method(String s)throws FileNotFoundException{
if (!s.endsWith(".txt")){
//创建异常对象,throw代表抛出
throw new FileNotFoundException("文件没找到!");
}
System.out.println("哈哈哈哈");
}
}
4.2 异常处理方式一_throws多个异常
1.格式:throws 异常,异常...
2.注意:
a.如果要抛多个异常,且多个异常之间有子父类继承关系,我们只需要throws一个父类即可
b.如果要抛多个异常,先抛子类,再抛父类
c.如果我们不知道要抛具体什么异常,我们可以直接抛Exception
public class Demo04Exception {
public static void main(String[] args)throws Exception/*IOException*//*,FileNotFoundException*/ {
String s = "";
method(s);
System.out.println("涛哥涛哥涛哥涛哥");
}
public static void method(String s)throws Exception/*IOException*//*,FileNotFoundException*/{
if (s==null){
//创建异常对象
throw new IOException();
}
if (!s.endsWith(".txt")){
//创建异常对象,throw代表抛出
throw new FileNotFoundException("文件没找到!");
}
System.out.println("哈哈哈哈");
}
}
4.3 异常处理方式二_try…catch
1.格式:
try{
可能会出现异常的代码
}catch(异常 对象名){
异常的处理方式->将异常信息放到日志文件中
}
2.注意:
如果try中的代码有问题,被catch到了,相当于代码变正常了,后续的代码还能继续执行
如果try中的代码有问题,没有被catch到,相当于没将代码处理,默认就会给jvm处理
public class Demo06Exception {
public static void main(String[] args) {
String s = "";
//添加功能
try {
add(s);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
//删除功能
System.out.println("删除");
}
public static void add(String s) throws FileNotFoundException {
if (!s.endsWith(".txt")) {
//创建异常对象,throw代表抛出
throw new FileNotFoundException("文件没找到!");
}
}
}
4.4 异常处理方式二_多个catch
1.格式:
try{
可能会出现异常的代码
}catch(异常 对象名){
异常的处理方式->将异常信息放到日志文件中
}catch(异常 对象名){
异常的处理方式->将异常信息放到日志文件中
}catch(异常 对象名){
异常的处理方式->将异常信息放到日志文件中
}...
2.注意:
a.如果catch的多个异常之间有子父类继承关系,只catch一个父类即可
b.如果catch的多个异常之间有子父类继承关系,先catch子类,再catch父类
c.如果catch的多个异常之间有子父类继承关系,只catch一个Exception即可
public class Demo07Exception {
public static void main(String[] args) {
String s = "";
//添加功能
/*try {
add(s);
} catch (IOException e) {
e.printStackTrace();//将异常具体信息打印到控制台上
}catch (FileNotFoundException e){
e.printStackTrace();//将异常具体信息打印到控制台上
}*/
/* try {
add(s);
} catch (IOException e) {
e.printStackTrace();//将异常具体信息打印到控制台上
}*/
try {
add(s);
} catch (Exception e) {
e.printStackTrace();//将异常具体信息打印到控制台上
}
//删除功能
System.out.println("删除");
}
public static void add(String s) throws /*FileNotFoundException,*//*IOException*/Exception {
if (s==null){
throw new IOException();
}
if (!s.endsWith(".txt")) {
//创建异常对象,throw代表抛出
throw new FileNotFoundException("文件没找到!");
}
}
}
public class Demo08Exception {
public static void main(String[] args) {
String s = "";
//添加功能
try {
String s1 = null;
/*
出现空指针异常->NullPointerException
如果catch不到,相当于没有处理,后续代码会受到影响
*/
System.out.println(s1.length());
add(s);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
//删除功能
System.out.println("删除");
}
public static void add(String s) throws FileNotFoundException {
if (!s.endsWith(".txt")) {
//创建异常对象,throw代表抛出
throw new FileNotFoundException("文件没找到!");
}
}
}
5.finally关键字
1.finally:代表的是一定要执行的代码
2.用法:和try...catch结合使用
try{
可能会出现异常的代码
}catch(异常 对象名){
异常的处理方式->将异常信息放到日志文件中
}finally{
一定要执行的代码
}
public class Demo09Exception {
public static void main(String[] args) {
String s = "";
//添加功能
try {
String s1 = null;
/*
出现空指针异常->NullPointerException
如果catch不到,相当于没有处理,后续代码会受到影响
*/
System.out.println(s1.length());
add(s);
} catch (Exception e) {
e.printStackTrace();
}finally {
System.out.println("我一定要执行");
}
//删除功能
System.out.println("删除");
}
public static void add(String s) throws FileNotFoundException {
if (!s.endsWith(".txt")) {
//创建异常对象,throw代表抛出
throw new FileNotFoundException("文件没找到!");
}
}
}
注:快捷键
如果有编译时期异常:我们可以使用alt+回车->快速生成try…catch
如果有运行时期异常:我们可以选中->ctrl+alt+T
public class Test01 {
public static void main(String[] args) {
int method = method();
System.out.println(method);
}
private static int method() {
try{
String s = null;
System.out.println(s.length());
return 2;
}catch (Exception e){
e.printStackTrace();
// System.out.println("我是catch");
return 1;
}finally {
System.out.println("我一定要执行");
//return 3;
}
}
}
运行结果:
我一定要执行
1
如果finally中再写一个return 3执行结果为
我一定要执行
3
finally的使用场景:
关闭资源
当一个对象没有用时,GC(垃圾回收器)会将没有用的对象从内存中清理,释放内存
但是,有的对象GC清理不了,比如:IO流对象 Socket对象 数据库连接对象等,所以我们需要用完之后手动关闭资源,释放资源
所以,GC清理不了的对象,就需要我们再finally中手动清理
6.抛异常时注意的事项
1.问题:
父类中的方法抛了异常,那么子类重写方法之后要不要抛?
可抛可不抛
父类中的方法没有抛异常,那么子类重写方法之后要不要抛?
不抛
什么时候用try…catch 什么时候用throws
1.如果想不影响后面功能的执行,就可以用try…catch
2.如果方法之间有递进关系,我们可以先throws 但是到了最后一层,需要统一作用一个try…catch处理
7.自定义异常
需求:完成一个注册案例,如果注册失败,抛出RegisterException,提示:该用户已经被注册
步骤:
1.定义一个字符串,表示已经注册过的用户名
2.创建Scanner,调用next(),表示要注册的用户名
3.和已有的用户名进行比较,如果内容一样,证明注册过了,显示注册失败,抛出注册失败异常
4.否则注册成功
public class RegisterException extends Exception{
public RegisterException() {
}
public RegisterException(String message) {
super(message);
}
}
public class Test {
public static void main(String[] args) throws RegisterException {
//1.定义一个字符串,表示已经注册过的用户名
String username = "root";
//2.创建Scanner,调用next(),表示要注册的用户名
Scanner sc = new Scanner(System.in);
System.out.println("请您输入要注册的用户名:");
String name = sc.next();
//3.和已有的用户名进行比较,如果内容一样,证明注册过了,显示注册失败,抛出注册失败异常
if (name.equals(username)){
throw new RegisterException("注册失败!");
}else{
//4.否则注册成功
System.out.println("注册成功");
}
}
}
1.随便一个类
2.如果继承Exception,此自定义异常类为编译时期异常
如果继承RuntimeExecption,此自定义异常类为运行时期异常
3.提供有参和无参构造,方便设置异常提示信息
8.打印异常信息的三个方法
Throwable类中定义了一些查看异常信息的方法:
- public String getMessage():获取异常的描述信息,原因(提示给用户的时候,就提示错误原因。
- public String toString():获取异常的类型和异常描述信息(不用)。
- public void printStackTrace():打印异常的跟踪栈信息并输出到控制台。
public class Test02 {
public static void main(String[] args) {
String s = "";
try {
method(s);
} catch (FileNotFoundException e) {
//Throwable类中定义了一些查看异常信息的方法:
//- public String getMessage():获取异常的描述信息,原因(提示给用户的时候,就提示错误原因。
//System.out.println(e.getMessage());
//- public String toString():获取异常的类型和异常描述信息(不用)。
//System.out.println(e.toString());
//- public void printStackTrace():打印异常的跟踪栈信息并输出到控制台。
e.printStackTrace();
}
}
private static void method(String s) throws FileNotFoundException {
if (!s.endsWith(".txt")){
throw new FileNotFoundException("文件找不到");
}
}
}
第四章.Object类
1.概述:
所有的类的父类
2.如果一个类没有明确写明extends 父类 默认的父类就是Object
所有的类都会直接或者间接的去继承Object类
1.Object类中的toString方法
public String toString() {//返回该对象的字符串表示。
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
1.如果我们的类中没有重写toString,那么直接输出对象名时,默认会调用Object类中的toString方法,输出的就是地址值
2.如果我们的类中重写toString方法,默认会调用重写后的toString方法,所以我们重写之后,返回的应该是内容(属性值)
总结:
如果直接输出对象名,不想输出地址值的话,那么就在这个对象内部重写Object类中的toString方法,输出内容(和getxxx方法无关)
public class Person {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
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;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
2.Object类中的equals方法
Object类中的equals方法://判断两个对象是否相等
public boolean equals(Object obj) {
return (this == obj);
}
==:针对于基本类型,比较的是值
针对于引用类型,比较的是地址值
1.如果一个对象没有重写equals方法,调用equals方法时,是调用的Object类中的equals方法,比较的是地址值
2.如果一个对象重写了equals方法,再比较地址值就没意义了,所以我们重写equals之后应该比较内容(属性值)
public class Person {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
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;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
/*
问题1:如果传递的不是Person类型,向下转型会出现ClassCastException
解决问题1:类型判断
问题2:如果传递null咋整?
解决问题2:先做一个非空判断,如果是null直接返回false
问题3:如果传递自己呢?
解决问题3:如果传递同一个对象,直接返回 true
*/
/* @Override
public boolean equals(Object o) {//Object o = p2
if (this==o){
return true;
}
if (o==null){
return false;
}
if (o instanceof Person){
//向下转型
Person p2 = (Person)o;
return this.name.equals(p2.name) && this.age==p2.age;
}
return false;
}*/
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return age == person.age &&
/*
Objects.equals也是比较的字符串内容
可以防止空指针异常
*/
Objects.equals(name, person.name);
}
}
public class Test {
public static void main(String[] args) {
Person p1 = new Person("柳岩", 36);
Person p2 = new Person("柳岩", 36);
System.out.println(p1.equals(p2));
ArrayList<String> list = new ArrayList<>();
System.out.println(p1.equals(list));
System.out.println(p1.equals(null));
System.out.println(p1.equals(p1));
/* String s = null;
boolean b = s.equals("哈哈哈");
System.out.println(b);*/
boolean result = Objects.equals(null, "abc");
System.out.println(result);
}
}
总结:
1.如果直接输出对象名不是地址值的话,而是属性值->重写toString
2.如果比较的时候想比较内容而不是地址值->重写equals