Java学习之接口与多态篇
0x00 前言
在前面写的几篇文章里,讲了封装和继承,那么这篇文章就来讲讲第三大面向对象的特性多态吧,在此之前先接着上篇文章的抽象先来看看Java里面接口的应用。
0x01 接口
接口在java里面属于引用数据类型,是方法的集合。如果说的类的内部封装了成员变量、构造方法和成员方法,那么接口内部主要封装了方法,包括抽象方法,默认方法和静态方法,私有方法。
接口的定义和定义类差不多,定义接口需要使用到interface关键字。虽然说他也会被编译为class文件,但是他并不是一个类,而是另一种引用数据类型。
引用数据类型:数组、接口、类
接口的使用,他和抽象类一样不能直接被创建,但是他可以被实现使用implements关键字。 一个实现接口的类可以看作是接口的子类,需要实现接口里面所有的抽象方法,创建该类的对象,就可以调用方法了,否则他必须是抽象的子类。
定义格式:
public interface 接口名{
}
抽象方法使用 abstract 关键字修饰,可以省略,没有方法体。该方法供子类实现使用。
public interface InterFaceName {
public abstract void method();
}
接口里的默认方法和静态方法
默认方法:使用default进行修饰,不能省略,提供给子类或者子类重写使用。
静态方法:使用static修饰,提供给接口直接调用。
public interface MyInterface {
public static void method(){
//执行语句
}
public default void method2(){
//执行语句
}
}
接口里的私有方法和静态方法
私有方法和定义私有变量一样,使用private修饰,提供给接口的默认方法和静态方法调用。
pubilc interface InterfaceName{
}
实现接口
类和接口的关系为实现关系,这被称为类实现接口,这类可以称为接口的实现类,基本上和抽象类有点类似,也可以称为接口的子类。实现的动作蕾仕于继承,格式几乎一致,但是是使用implements关键字,而不是extends。
实现接口必须重写所有的抽象方法
集成了接口的默认方法,可以直接调用也可以重写
接口代码:
public interface MyInterface {
void eat();
void sleep();
}
实现类代码:
public class Zi implements MyInterface {
@Override
public void eat() {
System.out.println("吃");
}
@Override
public void sleep() {
System.out.println("睡");
}
}
main方法代码:
public static void main(String[] args) {
Zi zi = new Zi();
zi.eat();
zi.sleep();
}
接口默认方法代码实现:
定义接口:
public interface MyInterface {
default void eat() {
System.out.println("吃");
}
public default void sleep(){
System.out.println("睡");
}
}
实现类代码:
public class Zi implements MyInterface {
}
main方法代码:
public static void main(String[] args) {
Zi zi = new Zi();
zi.sleep();
zi.eat();
}
这里接口定义的第一个方法直接defalut使用这个关键字定义默认方法,第二个加上了public这两种写法都可以,前者是省略式的写法。
实现类的任何代码都没写,只写了一个继承,默认方法是默认就定义好的,可以不用写,从实例化对象直接调用,当然也可以在实现类里面进行重写。
接口静态方法使用
静态与.class 文件相关,只能使用接口名调用,不可以通过实现类的类名或者实现类的对象调用。
接口定义:
public interface MyInterface {
static void eat() {
System.out.println("吃");
}
}
实现类定义:
public class Zi implements MyInterface {
}
main方法代码:
public static void main(String[] args) {
MyInterface.eat();
}
这里可以看到在main方法里面,实例化一个对象,然后去访问eat方法是报错的,定义为静态的方法必须使用接口名进行访问。
私有方法的使用
私有方法:只有默认方法可以访问。
私有静态方法:只有默认方法和静态方法可以调用。
如果一个接口有多个默认方法并且重复,那么就可以提取出来封装到私有方法里面,供默认方法去调用他。
public interface LiveAble {
default void func(){
func1();
func2();
} p
rivate void func1(){
System.out.println("跑起来~~~");
} p
rivate void func2(){
System.out.println("跑起来~~~");
}
}
这种写法是jdk9的新特性,jdk8没有,此段描述纯属复制。
0x02 接口多实现
在前面说到因为java只支持单继承,所以一个类只能继承一个父类,但是对于接口来说,一个类是可以实现多个接口的,这就叫做接口的多实现,并且一个类里面可以继承一个父类,同时也实现多个接口。
格式:
class 类名 extends 父类名 impls 接口1,接口2{
...
}
或者
class 父类名 impls 接口1,接口2{
...
}
接口多实现多抽象方法
如果在多个接口中有重名的方法,该重名的方法只需重写一次。
接口多实现多个默认方法
接口中多个默认方法,实现类都可以继承使用。但是如果有重名的方法就必须重写一次。
接口1:
public interface MyInterface {
default void method1(){
System.out.println("我是一个没有感情的默认方法");
}
}
接口2:
public interface MyInterface2 {
default void method1(){
System.out.println("我是一个没有感情的默认方法");
}
}
实现类:
public class Zi implements MyInterface,MyInterface1 {
@Override
public void method1() {
System.out.println("没有感情的方法");
}
}
main方法代码:
public static void main(String[] args) {
Zi zi = new Zi();
zi.method1();
}
而接口中的静态方法就不会存在重名的问他,静态方法访问可以直接使用接口名访问静态方法。
优先级问题
一个类中如果计策了一个父类又实现了多个接口时,父类中的成员方和接口中的默认方法重名,子类就近选择执行的父类成员方法。
注意事项:
接口中,无法定义成员变量,但是可以定义常量,其值不可以改变,默认使用public static final修饰。
接口中,没有构造方法,不能创建对象。
接口中,没有静态代码块。
0x03 多态
多态是面向对象的第三大特性。
多态:指同一种行为,具有不同的表现形式。
多态前提:
1.继承或者实现
2.方法的重写
3.父类引用指向子类[格式]
格式:
父类类型 变量名 = new 子类对象;
变量名.方法名();
父类类型:指向子类对象继承的父类类型,或者是实现的父类接口类型。
当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误;如果有,执行的是子类重写 后方法。
父类代码:
public abstract class Animal {
public abstract void play();
}
子类1代码:
public class Cat extends Animal {
@Override
public void play() {
System.out.println("撸猫");
}
}
子类2 代码:
public class Dog extends Animal{
@Override
public void play() {
System.out.println("撸狗");
}
}
main方法代码:
public static void main(String[] args) {
Animal cat = new Cat();
Animal dog = new Dog();
cat.play();
dog.play();
}
0x04 多态引用类型转换
多态的转型分为向上转型喝向下转型两种:
我们经常用到的就是向上转型,多态本身就是子类类型向父类类型向上转换的过程。
当父类引用指向子类对象的时候,这就是向上转型。 代码:
Antmal cat = new Cat();
向下转型:父类类型向子类类型向下转换的过程,这个过程是强制的。
一个已经向上转型的子类对象,将父类引用转为子类引用,可以使用强制类型转换的格式,便是向下转型。
格式:
子类类型 变量名 =(子类类型)父类变量名;
在这里来说一下为什么要使用转型
当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误。也就是说,不能调用子类拥 有,而父类没有的方法。编译都错误,更别说运行了。这也是多态给我们带来的一点”小麻烦”。所以,想要调用子 类特有的方法,必须做向下转型。
总结:如果要调用子类拥有而父类没有的方法的时候就要使用到向下转型。
父类代码:
public abstract class Animal {
public abstract void eat();
}
子类:
public class Cat extends Animal {
@Override
public void eat() {
System.out.println("吃鱼");
}
public void catchMouse(){
System.out.println("抓鱼");
}
}
main方法代码:
public static void main(String[] args) {
Animal cat = new Cat();
cat.eat();
Cat c =(Cat)cat;
c.catchMouse();
}
为了避免ClassCastException的发生,Java提供了 instanceof 关键字,给引用变量做类型的校验,格式如下:
变 量名 instanceof 数据类型 如果变量属于该数据类型,返回true。 如果变量不属于该数据类型,返回false。
所以,转换前,我们最好先做一个判断。
public class Test {
public static void main(String[] args) {
// 向上转型
Animal a = new Cat();
a.eat(); // 调用的是 Cat 的 eat
// 向下转型
if (a instanceof Cat){
Cat c = (Cat)a;
c.catchMouse(); // 调用的是 Cat 的 catchMouse
} else if (a instanceof Dog){
Dog d = (Dog)a;
d.watchHouse(); // 调用的是 Dog 的 watchHouse
}
}
}
0x05 结尾
接口与多态篇章更新完成,检测每天一个小总结。