接口(interface)
引入:
接口是大家一起约定俗成的对一个对于类型的规范(仅仅是一套规范),抽象类存在的意义是被继承,它是一个模板,它里面也可以包含方法体被实现的方法(普通方法),这时如果子类继承了抽象类,忘了重写这个普通方法,但是他要用的是被重写的方法,这时虽然说代码编译不会出错,但是逻辑上出错了。这就是抽象类的不好的地方,另一个不好的是抽象类是类的范畴,它也是只能单继承,不能实现多继承。怎么办呢?这里就开辟了一个接口范畴,引入了接口。(接口编译后也被编译为.class文件)
接口的命名规则:
-
只要是接口一定要以大写的“I”开头,后面跟接口名
-
如果类要实现接口,则命名是最好在类名后加Impl
public interface IUsb {
/*public static final*/int HIGHT = 3;
/*public abstract*/ void foo();
Object input();
Object output();
}
注意事项:
-
接口中定义的所有变量默认是public static final的,不能是其他类型的
-
接口中只能有抽象方法(只关心有没有这个方法,不关心怎么实现它),且方法一定是public的
-
接口中不会有任何一个方法是公共的(有方法体),但抽象类中有
-
接口用来做一套模板规范,关心的是整个体系,而不关心某个个体,实现的类来关心个体(使用抽象类和接口只是为了说明要干什么事,而让他们的实现类去根据自己的需要去实现这些方法 )
-
类和接口是实现的关系,可以多实现
-
abstract跟final一定不能放一起,因为abstract是必须被继承重写,而final是一定不能被继承
-
static 与 abstract也不能一起用,因为static是放在元数据区的,一定是被实现了的,实际存在的。但是abstract没有被实现,实际并不存在
-
接口里面不能放普通属性,但是想个办法可以放普通方法
接口里面的方法,其实可以有方法体
public interface IUsb { //1.静态方法 static void foo() { System.out.println("s"); } //2.默认方法,且可以被重写 default void output() { System.out.println("s"); } }
接口里面可以放:
1.属性:PSF(public static final)如果不显示的声明,默认就是PSF,显示的声明也是必须是PSF
2.普通方法,都是PA(public abstract) 没有方法体
3.静态方法,static方法,可以有方法体,这个方法放在方法区的元数据区,但是静态方法不能由实现他的类继承( 因为一个类可以实现多个接口。如果多个接口具有相同的静态方法,它们都将被继承,编译器就不知道要调用哪个接口)
4.默认方法,有方法体,代表显式的去除了abstract的修饰
抽象类有构造方法,接口里没有构造方法,没有能力创建对象
其实接口也可以创建对象,通过匿名类创建!
public class Main {
public static void main(String[] args) {
IUsb iUsb = new IUsb() {
@Override
public void doo() {
}
};
}
}
注意这是用匿名类的构造方法创建的对象,而不是接口创建的
有构造方法的类就可以创建对象。 错
抽象类也不能直接创建对象,必须要用匿名类来创建对象。匿名类创建对象是用抽象类在创建对象吗?不是,他是一个没有名字,继承了抽象类的一个类,只是在这里实现了他的抽象方法,是通过自己的构造方法创建的对象
java里的继承是单继承 错
条件不明确,java里类范畴的继承是单继承,多实现。接口范畴没有实现,但可以多继承
代码实现
接口可以继承扩展,但是只能继承接口,不能继承抽象类
public interface IPlastic {
}
public interface IMetel {
}
public interface IToy extends IPlastic,IMetel {
}
接口与接口之间没有实现关系,只有继承关系 ;类与接口是实现的关系,类与类之间是继承的关系
总结:
接口是继承的一种,符合里氏替换原则,父类能干的事,他的实现类也能干
public interface IMetel {
public void asyourwish();
}
public interface IUsb {
void doo();
void input();
void output();
}
public class USBImpl implements IUsb,IMetel {
@Override
public void doo() {
//这个方法是实现的
System.out.println("我是实现类的doo方法");
}
@Override
public void input() {
System.out.println("我是实现类的input方法");
}
@Override
public void output() {
//这个方法是重写的
System.out.println("我是实现类的output方法");
}
@Override
public void asyourwish() {
System.out.println("我是实现类的asyouwish方法");
}
}
场景类
public class Main {
public static void main(String[] args) {
IUsb usb1 = new USBImpl;
usb1.input(); //这个input走的是USBImpl的input
//李氏替换原则:我要的是父类,你给我的是哪个子类我不关心
//usb1.asyourwish();//不能这样写,上面的声明过程是把usb1当成IUsb接口来用,而IUsb里面没有asyouwish方法,USBImpl有这个方法,但不能用。除非类型转换,如下
USBImpl myUsb = (USBImpl)usb1;//myUsb与usb1的内存地址是一样的,只是类型不一样了,也就是栈里认为他可以多指几个东西了,但是堆里的东西是没变的(这里可以结合我博客继承里面画的内存分配图理解)
myUsb.asyourwish();//这样就可以调用了
//或者可以这样写((USBImpl)usb1).asyourwish();
USBImpl usb = new USBImpl();
((IUsb)usb)
}
}
可以把上面代码这样想:
比如公司把你当成普通员工招进来的,现在让你做精英员工的加班功能,你愿意做吗,肯定不
父类能做的子类一定能做,这里指的是声明的这个父类。普通员工要做加班工作那就应该把他转为精英员工
回顾一下类型转换:小类型向大类型转可以直接转,大类型向小类型转有风险(大类型不能保证小类型就有你要调的某个方法),兄弟类之间进行转换大概率会出错