========================================================================================
1.抽象abstract
- 抽象类:如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类。
- **抽象类不能被实例化,**也就是不能使用 new 关键字创建对象。抽象类可以包含属性、方法、构造方法,但是构造方法不能用于实例化,主要用途是被子类调用。
- 抽象类和抽象方法都要使用 abstract 关键字声明;
- 包含抽象方法的一定是抽象类,但是抽象类不一定含有抽象方法;
- 一个子类继承一个抽象类,则子类必须实现父类抽象方法,否则子类也必须定义为抽象类;
- 抽象方法:不实现实际作用的的方法,称之为抽象方法;主要包含在抽象类中。
- 抽象方法没有方法体;
- 抽象方法必须存在于抽象类中;
- 子类重写父类时,必须重写父类所有的抽象方法;
// 注意:在使用 abstract 关键字修饰抽象方法时不能使用 private 修饰,因为抽象方法必须被子类重写,而如果使用了 private 声明,则子类是无法重写的。
//抽象类:形状
abstract public class Shape {
abstract public void showShape();
}
//派生类一:三角形
public class Triangle extends Shape{
// 对抽象父类的抽象方法进行重写——令其有实际含义
@Override
public void showShape(){
System.out.println("▲");
}
}
// 派生类二:方形
public class Square extends Shape{
// 对抽象父类的抽象方法进行重写——令其有特定含义
@Override
public void showShape() {
System.out.println("■");
}
}
public class Main {
public static void main(String[] args) {
//一个抽象父类引用指向不同形态的子类,创建不同形态的具体子类对象,并实现方法调用;
Shape shape1=new Triangle();
shape1.showShape();
Shape shape2=new Square();
shape2.showShape();
}
}
2.接口interface
- 接口:在软件工程中,接口泛指供别人调用的方法或者函数。
- 接口可以包含变量、方法;变量被隐士指定为public static final,方法被隐士指定为public abstract(JDK1.8之前);
- 接口支持多继承,即一个接口可以extends多个接口,间接的解决了Java中类的单继承问题;
- 一个类可以实现 implement 多个接口,必须对接口的方法全部进行重写,否则报错;
- JDK1.8中对接口增加了新的特性:
- (1)、默认方法(default method):JDK 1.8允许给接口添加非抽象的方法实现,但必须使用default关键字修饰;定义了default的方法可以不被实现子类所实现,但只能被实现子类的对象调用;如果子类实现了多个接口,并且这些接口包含一样的默认方法,则子类必须重写默认方法;
- 静态方法(static method):JDK 1.8中允许使用static关键字修饰一个方法,并提供实现,称为接口静态方法。接口静态方法只能通过接口调用(接口名.静态方法名)
package abstractInterface;
// 动物类
public class Animal {
protected String name;
public Animal(String name) {
this.name = name;
}
@Override
public String toString() {
return "Animal{" +
"name='" + name + '\'' +
'}';
}
}
//创建一个接口:声明跑步的方法
public interface IRunning {
void run();
}
//子类兔子Rabbit继承自父类动物Animal的名字与实现跑步的接口IRunning
public class Rabbit extends Animal implements IRunning{
public Rabbit(String name) {
super(name);
}
//重写接口的的方法
@Override
public void run() {
System.out.println(name+"正在蹦蹦跳跳!");
}
}
//创建一个有飞功能的接口
public interface IFlying {
void fly();
}
//创建游泳的接口
public interface ISwimming {
void swim();
}
//创建一个两栖接口继承自 跑步和游泳接口
public interface IAmphibious extends IRunning,ISwimming{
//继承于另外两个接口,等价于包含继承接口的方法;
// @Override
// void run();
// @Override
// void swim();
}
//子类青蛙Frog继承自父类动物类Animal 链接接口两栖(必须重写两栖接口的跑步和游泳方法)
public class Frog extends Animal implements IAmphibious{
public Frog(String name) {
super(name);
}
@Override
public void run() {
System.out.println(name+"正在双脚跳!");
}
@Override
public void swim() {
System.out.println(name+"正在蛙泳!");
}
}
//类可以连接 implement 多个接口,和上面代码等价
//public class frog extends Animal implements IRunning,ISwimming{
// public frog(String name) {
// super(name);
// }
//
// @Override
// public void run() {
// System.out.println(name+"正在双脚跑!");
// }
//
// @Override
// public void swim() {
// System.out.println(name+"正在游泳!");
// }
//}
//创建一个鸭子的子类继承自父类动物;链接三个接口
public class Duck extends Animal implements IRunning,ISwimming,IFlying{
public int age;
public Duck(String name, int age) {
super(name);
this.age = age;
}
@Override
public void fly() {
System.out.println(name+"正在飞!");
}
@Override
public void run() {
System.out.println(name+"正在慢慢跑!");
}
@Override
public void swim() {
System.out.println(name+"在湖里游!");
}
@Override
public String toString() {
return "Duck{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
//创建一个同包的主类主方法实现调用;
public class Main {
public static void main(String[] args) {
Animal animal1=new Duck("鸭子",2);
System.out.println(animal1.toString());
Duck duck=(Duck) animal1;
duck.fly();
System.out.println("=================================");
Frog frog=new Frog("青蛙");
frog.swim();
System.out.println("=================================");
Rabbit rabbit=new Rabbit("兔兔");
rabbit.run();
}
}
3.接口与抽象类的区别
相同点
(1)都不能被实例化 (2)接口的实现类或抽象类的子类都只有实现了接口或抽象类中的方法后才能实例化。
不同点
(1)接口只有定义,不能有方法的实现,java 1.8中可以定义default方法体,而抽象类可以有定义与实现,方法可在抽象类中实现。
(2)实现接口的关键字为implements,继承抽象类的关键字为extends。一个类可以实现多个接口,但一个类只能继承一个抽象类。所以,使用接口可以间接地实现多重继承。
(3)接口强调特定功能的实现,而抽象类强调所属关系。
(4)接口成员变量默认为public static final,必须赋初值,不能被修改;其所有的成员方法都是public、abstract的。抽象类中成员变量默认default,可在子类中被重新定义,也可被重新赋值;抽象方法被abstract修饰,不能被private、static、synchronized和native等修饰,必须以分号结尾,不带花括号
4.异常exception
- 异常:所谓异常指的就是程序在 运行时 出现错误时通知调用者的一种机制;
防御式编程
错误在代码中是客观存在的. 因此我们要让程序出现问题的时候及时通知程序猿. 我们有两种主要的方式
- LBYL: Look Before You Leap. 在操作之前就做充分的检查(参数校验).
- EAFP: It’s Easier to Ask Forgiveness than Permission. “事后获取原谅比事前获取许可更容易”. 也就是先操作, 遇到问题再处理.
Java内置异常类
-
Java中系统内置的异常类大致继承关系如下:
-
下图粉色框内的类是受查异常,蓝色为非受查异常;
-
顶层类 Throwable 派生出两个重要的子类, Error 和 Exception;
-
其中 Error 指的是 Java 运行时内部错误和资源耗尽错误. 应用程序不抛出此类异常. 这种内部错误一旦出现,除了告知用户并使程序终止之外, 再无能无力. 这种情况很少出现.
-
Exception 是我们程序猿所使用的异常类的父类.其中 Exception 有一个子类称为 RuntimeException , 这里面又派生出很多我们常见的异常类NullPointerException , IndexOutOfBoundsException 等.
-
如果一段代码可能抛出 受查异常, 那么必须显式进行处理.
-
处理方法:
- a) 利用try catch包裹;
- b) 在方法上加上异常说明, 相当于将处理动作交给上级调用者;
常见异常与基本用法
- 不处理异常的例子:
// 访问空指针异常:
- // 利用try包裹可能出现异常的代码,遇到异常运行语句catch处理异常:
- 异常基本语法:
try{
有可能出现异常的语句 ;
}[catch (异常类型 异常对象) {
} ... ]
[finally {
异常的出口
}]
- 使用 try 负责回收资源 等价于 在finally中使用 sc.close()关闭扫描器一样;
- catch 只能处理对应种类的异常;
- catch 可以有多个;也可以多个异常写在一个catch中,用‘ | ’ 链接;
- finally 表示最后的善后工作, 例如释放资源
// 数组下标越界异常:
// 算数异常:
- 如果本方法中没有合适的处理异常的方式,就会沿着调用栈向上查找处理异常的方式:
public static void main(String[] args) {
func(); // 异常
}
public static void func () {
int a = 1;
int b = 0;
System.out.println("a / b =");
try {
System.out.println("qian");
Integer ret = func1(a, b); //异常
System.out.println(ret);
System.out.println("hou");
} catch (ArithmeticException e) {
e.printStackTrace();
}finally {
System.out.println("ending");
}
}
public static Integer func1 ( int a, int b){
Integer num = a / b; //异常
return num;
}
- // 如果向上一直传递都没有合适的方法处理异常, 最终就会交给 JVM 处理, 程序就会异常终止(和我们最开始未使用 try catch 时是一样的).
抛出异常
- throw 关键字 抛出异常 的使用:
- throws 关键字 表示可能抛出的异常 的使用:
- 可以使用 throws 关键字, 把可能抛出的异常显式的标注在方法定义的位置. 从而提醒调用者要注意捕获这些异常.
- 一般不建议在finally中写 return 语句;
自定义异常类
- Java 中虽然已经内置了丰富的异常类, 但是我们实际场景中可能还有一些情况需要我们对异常类进行扩展, 创建符合我们实际情况的异常;
如下用户登录 例子:
package exception;
import java.util.Scanner;
public class Test {
public static String name="zwr";
public static String password="123";
public static void main5(String[] args) {
Scanner scanner=new Scanner(System.in);
try{
System.out.println("请输入用户名:");
String name=scanner.nextLine();
System.out.println("请输入密码:");
String password=scanner.nextLine();
login(name,password);
}catch (UserNameError | UserPasswordError user){
user.printStackTrace();
}
}
public static void login(String name,String password)throws UserNameError, UserPasswordError {
if(!Test.name.equals(name)){
throw new UserNameError("用户名错误!");
}
if (!Test.password.equals(password)) {
throw new UserPasswordError("用户密码错误!");
}
System.out.println("登陆成功!");
}
// 此时我们在处理用户名密码错误的时候可能就需要抛出两种异常. 我们可以基于已有的异常类进行扩展(继承), 创建和我们业务相关的异常类.