接口
接口概述
当一个抽象类,如果抽象类中的所有方法都是抽象的,那么我们就可以把它定义为一个接口,接口是对行为的抽象。类是对属性和行为的抽象。
接口就是规范,定义的是一组规则,体现了现实世界中“如果你是…则必须能…”的思想。如果你是天使,则必须能飞。如果你是汽车,则必须能跑。如果你是好人,则必须能干掉坏人。如果你是坏人,则必须欺负好人。
接口的本质是契约,就像我们人间的法律一样。制定好后大家都遵守。
面向对象的精髓,是对对象的抽象,最能体现这一点的就是接口。为什么我们讨论设计模式都只针对具备了抽象能力的语言(比如C++、Java、C#等),就是因为设计模式所研究的,实际上就是如何合理的去抽象。
语法
[访问修饰符] interface 接口名 [extends 父接口1,父接口2,...] {
//接口成员:常量定义、方法定义
}
接口定义
1、访问修饰符:只能是 public 或默认。
2、extends:接口可以多继承。
3、常量:接口中的属性只能是常量,总是 public static final 修饰。不写也是。
4、方法:接口中的方法只能是 public abstract。省略的话,也是 public abstract 的。
接口的特征
1、接口中的方法的定义不需要abstract来修饰,默认就是抽象的。
2、接口是不可以实例化的,需要有类来实现接口。
3、接口中的方法不能和private,static和final共存。
4、在接口中可以定义”属性”,可以通过接口的实现类的实例来访问(不推荐),推荐使用接口名.常量
,这是一个常量,默认是public、static、final。
注意:常量的定义命名的规范要使用大写,单词之间要用“_”分隔。
5、接口可以继承接口(多继承)
接口的继承和实现
接口中可以多继承,即接口可以继承接口。
子类通过implements来实现接口中的规范。接口不能创建实例,但是可用于声明引用变量类型。
一个类实现了接口,必须实现接口中所有的方法,并且这些方法只能是public的。
[修饰符] interface 接口名 extends 父接口1,父接口2,···{
//常量定义
int a = 10; //总是public static final 类型的
//方法定义
void run(); //总是public abstract void run();
}
实现接口的语法:
public class 类名 implements 接口名,接口名,…{
//实现每一个接口中的方法
}
接口的应用
使用接口方式计算圆和正方形的面积和周长。
interface Cal{
/**
定义圆周率
*/
public static final double PI = 3.14;
/**
求周长
*/
public double getLong();
/**
求面积
*/
public double getArea();
}
class Rect implements Cal{
//边长
double r;
public Rect(double r){
this.r = r;
}
public double getLong(){
return 4*r;
}
public double getArea(){
return r*r;
}
}
class Circle implements Cal{
//半径
double r;
public Circle(double r){
this.r = r;
}
public double getLong(){
return 2*Cal.PI *r;
}
public double getArea(){
return Cal.PI * r *r;
}
}
public class TestCal{
public static void main(String[] args){
//创建一个正方形
Rect rect = new Rect(10.0);
double rectLong = rect.getLong();
double rectArea = rect.getArea();
System.out.println("正方形的周长是:"+rectLong);
System.out.println("正方形的面积是:"+rectArea);
//创建一个圆
Circle c = new Circle(10.0);
double cLong = c.getLong();
double cArea = c.getArea();
System.out.println("圆的周长是:"+cLong);
System.out.println("圆的面积是:"+cArea);
}
//正方形的周长是:40.0
//正方形的面积是:100.0
//圆的周长是:62.800000000000004
//圆的面积是:314.0
}
接口在JDK1.8中的特性
JDK1.8(不含8)之前,接口中只能包含静态常量、抽象方法,不能有普通属性、构造方法、普通方法。
JDK1.8(含8)后,接口中包含普通的静态方法、默认方法。
默认方法
Java8及以上版本,允许给接口添加一个非抽象的方法实现,只需要使用default关键字即可,这个特征又叫做默认方法(也称为扩展方法)。
默认方法和抽象方法的区别是抽象方法必须要被实现,默认方法不是。作为替代方式,接口可以提供默认方法的实现,所有这个接口的实现类都会通过继承得到这个方法。
interface A {
default void run() {
System.out.println("默认方法");
}
}
静态方法
Java8以后,我们也可以在接口中直接定义静态方法的实现。这个静态方法直接从属于接口(接口也是类,一种特殊的类),可以通过接口名调用。
如果子类中定义了相同名字的静态方法,那就是完全不同的方法了,直接从属于子类。可以通过子类名直接调用。
public class Test {
public static void main(String[] args) {
A.staticMethod();
B.staticMethod();
}
}
interface A {
public static void staticMethod() {
System.out.println("A接口的静态方法");
}
}
class B implements A {
public static void staticMethod() {
System.out.println("B接口的静态方法");
}
}
使用接口的好处
接口定义的是一种标准,可以使我们的代码分层开发,分模块开发。
降低代码的耦合度,提高代码的可扩展性和可维护性。
接口改进了单继承的局限。
接口和抽象类的区别
接口是行为的抽象,可以有静态方法,抽象方法和默认方法,抽象类中可以有所有方法。
接口和抽象类都不能实例化,接口需要类来实现后实例化实现类,抽象类需要类来继承然后实例化子类。
抽象类只能单继承,接口可以多继承接口(JDK1.7),接口还可以多实现。
接口中的“属性”是public static final类型的,抽象类中的属性跟普通类中的属性没有区别。
接口中的方法默认就是抽象的不需要加absract,抽象类中的抽象方法需要加abstract关键字。
多态
父子类之间的转换
子类转换为父类(向上转型)
子类可以自动转型成父类
class Person{
String name;
public void eat(){
System.out.println(name+"在吃饭");
}
}
class Student extends Person{
public void eat(){
System.out.println(name+"在吃鱼");
}
}
public class TestPerson{
public static void main(String[] args){
//子类自动转换成父类
Person person = new Student();
person.name = "张三";
person.eat();//张三在吃鱼
}
}
特点:
-
如果子类对父类的方法有覆写,并且子类的实例赋值给父类的引用,通过这个引用来调用这个覆写的方法的时候,调用的是子类。
-
父类指向子类的实例的引用不能调用子类的特有的方法和属性。
-
如果父子类有同名的属性,那么父类指向子类的实例的引用调用这个属性的时候调用的还是父类的,不要和方法混淆。
父类转换为子类(向下转型)
特点:
-
父类转换成子类不能自动完成。
-
父类转换成子类的前提是父类的真身是这个子类,转回子类后,就可以访问子类内部的方法和属性。
-
如果这个父类的真身是子类B,不能强制转换成子类A,只能转换成B。
-
抽象类作为父类同样符合上述理论。
-
接口中也是完全符合上述理论,但是注意接口中没有属性。
class Person{
String name;
public void eat(){
System.out.println(name+"在吃饭");
}
}
class Student extends Person{
public void eat(){
System.out.println(name+"在吃鱼");
}
public void learn(){
System.out.println(name+"在学习");
}
}
public class TestPerson{
public static void main(String[] args){
Person p = new Student();
p.name = "张三";
//强制转换
Student student = (Student) p;
student.eat();//张三在吃鱼
student.learn();//张三在学习
}
}
多态定义
同一个事件发生在不同的对象上会产生不同的结果。
多态就是行为具有表现多种功能的能力。多态指的是同一个方法调用,由于对象不同可能会有不同的行为。
在程序设计的术语中,它意味着一个特定类型的变量可以引用不同类型的对象,并且能自动的调用引用的对象的方法,也就是根据作用到不同对象类型,响应不同的操作。
多态的要点:
-
多态是方法的多态,不是属性的多态(多态与属性无关)。
-
多态的存在要有三个必要条件:继承,方法重写,父类引用指向子类对象。
-
父类引用指向子类对象后,用该父类引用调用子类重写的方法,此时多态就出现了。
多态的优势
可替换性:多态对已存在的代码具有可替换性。
可扩充性:多态对代码具有扩充性。增加新的子类不影响已存在类的多态性、继承性,以及其他特性的运行和操作。
接口性:多态是父类向子类提供了一个共同接口,由子类来具体实现。
灵活性:多态的应用中体现了灵活多样的操作,提高了使用效率。
简化性:多态简化了应用软件的代码编程和修改过程。
接口的多态
interface Person{
public void eat();
}
class Student implements Person{
String name;
public void eat(){
System.out.println(name+"在吃饭");
}
public void learn(){
System.out.println(name+"在学习");
}
}
public class TestPerson{
public static void main(String[] args){
Student s = new Student();
s.name = "张三";
Person p = s;
p.eat();
Student student = (Student) p;
student.learn();
//张三在吃饭
//张三在学习
}
}
多态的应用
1、使用父类作为方法的形参
实例:假如狗、猫、鸭3种动物被一个主人领养,主人可以控制各种动物叫的行为,实现一个主人类,在该类中定义控制动物叫的方法。
//主人类
class Host{
//让动物叫的方法
public void letCry(Animal animal){
animal.cry();
}
}
class Animal{
public void cry(){
System.out.println("叫");
}
}
class Dog extends Animal{
public void cry(){
System.out.println("狗叫");
}
}
class Cat extends Animal{
public void cry(){
System.out.println("猫叫");
}
}
class Duck extends Animal{
public void cry(){
System.out.println("鸭叫");
}
}
public class Test {
public static void main(String[] args) {
Host host = new Host();
Animal animal;
animal = new Dog();
host.letCry(animal);//控制狗叫
animal = new Cat();
host.letCry(animal);//控制猫叫
animal = new Duck();
host.letCry(animal);//控制鸭叫
//狗叫
//猫叫
//鸭叫
}
}
2、使用父类作为方法的返回值
//动物类
class Animal{
public void cry(){
System.out.println("叫");
}
}
class Dog extends Animal{
public void cry(){
System.out.println("狗叫");
}
}
class Cat extends Animal{
public void cry(){
System.out.println("猫叫");
}
}
class Duck extends Animal{
public void cry(){
System.out.println("鸭叫");
}
}
//主人类
class Host{
//让动物叫的方法
public Animal letCry(String type){
Animal animal = new Animal();
if (type=="dog"){
animal = new Dog();
}else if (type=="cat"){
animal = new Cat();
}else if (type=="duck"){
animal = new Duck();
}
return animal;
}
}
public class Test {
public static void main(String[] args) {
Host host = new Host();
Animal animal;
animal = host.letCry("dog");
animal.cry();//狗叫
animal = host.letCry("cat");
animal.cry();//猫叫
animal = host.letCry("duck");
animal.cry(); //鸭叫
}
}
instanceof操作符
x instanceof A
:检验x是否为类A的对象,返回值为boolean类型。要求x所属的类与类A必须是子类和父类的关系,否则编译错误。
public class Test {
public static void main(String[] args) {
Test01 t = new Test01();
System.out.println(t instanceof Object);//true
}
}
面向对象设计原则
1、摘取代码中变化的行为,形成接口。
2、多用组合,少用继承。
3、针对接口编程,不依赖于具体实现。
4、针对扩展开放,针对改变关闭。
评论区