目录
2.3.4 转型可能遇到的异常ClassCastException
一、接口
1.1 什么是接口
接口:接口就是多个类的公共规范 接口是一种引用数据类型,最重要的内容就是其中的抽象方法,其内部主要对方法进行了封装。
如果是java7,接口可以包含以下内容
1、常量
2、抽象方法
如果是java8
3、默认方法
4、静态方法
如果是java9
5、私有方法
1.2 接口的定义
将定义类中的class关键字换成interface即可
/*
在任何版本的java中,接口都可以定义抽象方法
基本格式:
public abstract 返回值类型 方法名称(参数列表)
注意事项:
1、接口当中的抽象方法,修饰符必须是两个固定的关键字public abstract
2、这两个关键字可以选择性不写
3、可以随意定义抽象方法
*/
public interface MyInterfaceAbstract {
//下面这四种写法都可以
public abstract void methodAbs1();
abstract void methodAbs2();
public void methodAbs3();
void methodAbs4();
}
1.3 接口的实现
接口的实现需要用到implements关键字
接口的实现是多态的前提;多态指的是使用接口接收实现类返回的类对象。
1.3.1 接口中的抽象方法
实现类必须覆盖重写接口中所有的抽象方法,如果没有全部覆盖重写这个实现类是一个抽象类。
public class MyInterfaceAbstractImpl implements MyInterfaceAbstract{
@Override
public void methodAbs1() {
System.out.println("1");
}
@Override
public void methodAbs2() {
System.out.println("2");
}
@Override
public void methodAbs3() {
System.out.println("3");
}
@Override
public void methodAbs4() {
System.out.println("4");
}
}
1.3.2 接口中的默认方法
需要用到default关键字来修饰
接口中的默认方法可以继承,可以覆盖重写,但是只能通过实现类调用。
应用场景:当一个接口已经投入使用时,如果我们需要对该接口添加新功能,直接往接口中添加抽象方法会引起该接口的实现类全部变成抽象类,会引发一系列的问题,此时默认方法可以解决这一问题,只需在该接口中添加默认方法即可,不会发生上述问题,声明该接口的实现类即可使用默认方法。
//定义含默认方法的接口
/*
从java8开始,接口中允许定义默认方法
格式:
public default 返回值类型 方法名称(参数列表){
方法体
}
备注:接口当中的默认方法,可以解决接口升级的问题
/*
1、接口的默认方法,可以通过接口实现对象,直接调用
2、接口的默认方法,也可以被接口实现类覆盖重写
*/
public interface MyInterfaceDefault {
//抽象方法
public abstract void methodAbs();
//接口新增了一个抽象方法
// public abstract void methodAbs2();
//新添加的方法改为默认方法就可以解决
public default void methodDefault(){
System.out.println("这是新添加的默认方法");
}
}
//接口实现类A 没有重写了默认方法
public class MyInterfaceDefaultA implements MyInterfaceDefault{
@Override
public void methodAbs() {
System.out.println("实现了抽象方法:AAA");
}
}
//接口实现类B 重写了默认方法
public class MyInterfaceDefaultB implements MyInterfaceDefault{
@Override
public void methodAbs() {
System.out.println("实现了抽象方法:BBB");
}
@Override
public void methodDefault() {
System.out.println("重写了默认方法");
}
}
1.3.3 接口中的静态方法
需要用到static来修饰接口方法
只能使用接口名调用,不可以通过实现类的类名或者实现类的对象调用。
//含有静态方法的接口
/*
从java8开始,接口当中允许定义静态方法
格式:
public static 返回值类型 方法名称(参数列表)
提示:就是将abstract或者default换成static即可,带上方法体
*/
public interface MyInterfaceStatic {
public static void methodStatic(){
System.out.println("这是接口的静态方法");
}
}
//实现类
public class MyInterfaceStaticImpl implements MyInterfaceStatic{
//没有报错,没有必要
}
1.3.4 接口中的私有方法
定义方式:使用private关键字修饰方法即可
应用场景:如果接口中的默认方法或者静态方法内部方法体有较多的重复代码在,这时我们可以将重复代码声明成私有方法,只对这个接口可见,然后被默认方法或者静态方法即可,因此其主要作用是增加代码的复用率。
//含有私有方法的接口
public interface MyInterfacePrivateB {
public static void methodDefault1(){
System.out.println("默认方法1");
// System.out.println("AAA");
// System.out.println("BBB");
// System.out.println("ccc");
methodStaticCommon();
}
public static void methodDefault2(){
System.out.println("默认方法2");
// System.out.println("AAA");
// System.out.println("BBB");
// System.out.println("ccc");
methodStaticCommon();
}
//将上面的重复代码抽取出来
private static void methodStaticCommon(){
System.out.println("AAA");
System.out.println("BBB");
System.out.println("ccc");
}
}
1.4 接口的多实现
前面的章节有说到Java中类是不支持多继承的,但是对于接口而言,一个类可以实现多个接口,称为接口的多实现,而且一个子类除了可以继承父类同时还可以实现多个接口。
如果默认方法、抽象方法发生重名时,只需要重写一次;
如果静态方法发生重名,不需要重写,因为抽象方法可以通过各自的接口名访问静态方法。
优先级的问题 :当一个类,既继承一个父类,又实现若干个接口时,父类中的成员方法与接口中的默认方法重名,子类就近选择执 行父类的成员方法。
//接口A
public interface MyInterfaceA {
// static {}
public abstract void methodA();
public abstract void method();
public default void methodDefault(){
System.out.println("A的默认方法");
}
public default void methodAnotherDefault(){
System.out.println("A的另一个默认方法");
}
}
//接口B
public interface MyInterfaceB {
public abstract void methodB();
public abstract void method();
public default void methodDefault(){
System.out.println("B的默认方法");
}
}
//实现类
public class MyInterfaceImpl implements MyInterfaceA,MyInterfaceB{
@Override
public void methodA() {
System.out.println("A接口的方法重写");
}
@Override
public void method() {
System.out.println("重写了AB接口的重名方法");
}
@Override
public void methodDefault() {
System.out.println("重写了AB接口重名的默认方法");
}
@Override
public void methodB() {
System.out.println("B接口的方法重写");
}
}
二、多态
2.1 什么是多态?
多态是面向对象的三大特性之一;
多态指的是:同一行为,具有多种不同的表现形式。在实际应用中体现在父类的应用(接口对象)指向子类的对象。
Animal animal = new Cat();
animal.eat();
多态的应用场景:多态在一个接口有很多实现类时,可以使用该接口应用指向不同的子类对象,可以使程序编写的更简单,并且有良好的拓展。
当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误;如果有,执行的是子类重写 后方法。
2.2 多态的使用
//父类接口
public abstract class Animal {
public abstract void eat();
}
//实现类A
public class Cat extends Animal{
@Override
public void eat() {
System.out.println("猫吃鱼");
}
//子类特有方法
public void catchMouse(){
System.out.println("猫抓老鼠");
}
}
//实现类B
public class Dog extends Animal{
@Override
public void eat() {
System.out.println("狗吃SHIT");
}
public void watchHouse(){
System.out.println("狗看家");
}
}
//测试
/*
向上转型弊端:一旦向上转型为父类,那么就无法调用子类原本特有的内容了
解决方案:用对象的向下转型还原
格式:子类名称 对象名 = (子类名称)父类对象;
*/
public class Demo01Main {
public static void main(String[] args) {
//向上转型
Animal animal = new Cat();
animal.eat();
//向下转型
Cat cat = (Cat)animal;
cat.catchMouse();
// Dog dog = (Dog)animal;//java.lang.ClassCastException
// dog.watchHouse();
}
}
2.3 引用类型转换
2.3.1 向上转型
定义:多态本身是子类类型向父类类型向上转换的过程,这个过程是默认的,当父类引用指向一个子类对象时,便是向上转型。
父类类型 变量名 = new 子类类型();
如:Animal a = new Cat();
//类比于高精度转低精度
int = 10.0;
2.3.2 向下转型
定义:父类类型向子类类型向下转换的过程,这个过程是强制的。一个已经向上转型的子类对象,将父类引用转为子类引用,可以使用强制类型转换的格式,便是向下转型。
子类类型 变量名 = (子类类型) 父类变量名;
如:Cat c =(Cat) a;
//类比于强制类型转换高精度转低精度
int i = (int)1.2;
2.3.3 转型的意义
更好的服务于多态,当使用多态方式调用方法时,
首先检查父类中是否有该方法,如果没有,则编译错误。
也就是说,不能调用子类拥有而父类没有的方法。编译都错误,更别说运行了。
这也是多态给我们带来的一点"小麻烦"。所以,想要调用子 类特有的方法,必须做向下转型。
//父类接口
public abstract class Animal {
public abstract void eat();
}
//实现类A
public class Cat extends Animal{
@Override
public void eat() {
System.out.println("猫吃鱼");
}
//子类特有方法
public void catchMouse(){
System.out.println("猫抓老鼠");
}
}
//实现类B
public class Dog extends Animal{
@Override
public void eat() {
System.out.println("狗吃SHIT");
}
public void watchHouse(){
System.out.println("狗看家");
}
}
//测试
/*
向上转型弊端:一旦向上转型为父类,那么就无法调用子类原本特有的内容了
解决方案:用对象的向下转型还原
格式:子类名称 对象名 = (子类名称)父类对象;
*/
public class Demo01Main {
public static void main(String[] args) {
//向上转型
Animal animal = new Cat();
animal.eat();
//向下转型
Cat cat = (Cat)animal;
cat.catchMouse();
// Dog dog = (Dog)animal;//java.lang.ClassCastException
// dog.watchHouse();
}
}
2.3.4 转型可能遇到的异常ClassCastException
当定义的实现类对象指向狗时,向上转型为动物,此时如果向下转型为猫时就会报错ClassCastException。
毕竟狗!=猫
//向上转型
Animal animal = new Cat();
animal.eat();
//向下转型
Dog dog = (Dog)animal;//java.lang.ClassCastException
dog.watchHouse();
2.3.5 instanceof关键字
为解决ClassCastException这个报错问题,我们需要在向下转型时判断我们的对象属于那个实现类,从而达到猫抓老鼠而不是狗抓老鼠的目的。
使用方法:
变量名 instanceof 数据类型
如果变量属于该数据类型,返回true。 如果变量不属于该数据类型,返回false。
实例:
/*
如何才能知道一个父类对象引用的对象,本来是什么类
对象 instanceof 类型
将得到一个Boolean值的结果,也就是判断前面的对象能不能当作后面类型的实例
*/
public class Demo02instanceof {
public static void main(String[] args) {
Animal animal = new Cat();
animal.eat();//猫吃鱼
//如果想要调用子类特有方法,需要向下转型
if (animal instanceof Dog){
Dog dog = (Dog)animal;
dog.watchHouse();
}
if(animal instanceof Cat){
Cat cat = (Cat)animal;
cat.catchMouse();
}
}
}
后记:接口的用法很多,除了作为父类被继承重写外,还可以作为引用指向子类对象(多态),还可以作为函数的参数和返回值,过程中自动发生了向上转型和向下转型,这也是多态的体现。