Java面向对象07[Java接口详解、N种内部类]

Java接口详解

什么是接口

接口就是规范,定义的是一组规则。

接口的本质

接口的本质就是契约,制定好之后大家都遵守。

体现在现实生活中的话可以理解为:“如果要实现这个项目,那么就必须要完成项目规定的任务”

接口的声明

接口通过interface关键字来声明,接口中可以定义一些常量和方法

格式:

public interface 接口名{
	//常量和方法
}

示例:

public interface UserService {
    /**
     * 常量
     */
    int AGE = 20;
    
    /**
     * 抽象方法
     */
    void add(String name);
    void delete(String name);
    
    /**
     * 静态方法
     */
    static void a(){
		//代码块
    }
    
    /**
     * 默认方法
     */
    default void b(){
    	//代码块
    }
}
public class UserServiceImpl implements UserService,TimeService{
    @Override
    public void add(String name) {

    }

    @Override
    public void delete(String name) {

    }
   
}

定义接口的注意事项

1.接口中定义的变量编译器会在编译时帮我们自动加上修饰符 public static final ,即定义的均为常量

在Java官方文档里也有这样的一个声明:Every field declaration in the body of an interface is implicitly public,static,and final.

那么我们是否就可以将接口当作常量类来使用呢?答案是否定的,因为接口的本意就是对不同类中的方法进行抽象,而常量接口会对子类中的变量造成命名空间上的“污染”

一般情况下很少会有人在接口里面定义常量的,我们都是在接口中定义一些方法来约束,然后让子类去实现

2.接口中定义的抽象方法编译器会帮我们自动加上修饰符 public abstract (编译器帮我们实现了的,如果没有特别要求就不要去做多余的动作,如果想知道编译器帮我们做了哪些辅助,反编译就ok了,前面的构造器里面讲过)

3.接口中允许有静态方法(从Java 8开始)

静态方法无法由实现了该接口的类的对象来调用,它只能通过接口的名字来调用

接口中定义静态方法的目的是为了提供一种简单的机制,使我们不必创建对象就能调用方法,从而实现接口的竞争力

4.接口中允许定义默认方法(也从Java 8开始)

它始终由一个代码块组成,为实现该接口而不覆盖该方法的类提供默认实现,即default方法不能直接使用一个;来结束方法,要有本体。

接口中定义默认方法的理由是,因为一个接口可能由多个实现类,这些类就必须实现接口中定义的抽象方法,否则编译器报错。当我们需要在所有的实现类中追加某个具体的方法时,如果没有default方法的帮助,我们就必须逐一对实现类进行修改。

总结

1.类可以通过关键字implements实现多个接口

2.类实现接口后,必须要重写接口中的方法 类名一般以Impl结尾

3.接口中允许定义变量,但因为被public static final 隐式修饰,其值是无法改变的,即为常量。

4.接口中只允许定义抽象方法、静态方法(Java 8开始)和默认方法(Java 8开始)

5.接口不能直接实例化,必须先定义一个类去实现接口,然后再实例化。

6.接口可以是空的,即可以既不定义变量,也不定义方法

7.不能在声明接口的时候使用final关键字,否则编译器会报错!因为接口就是为了让子类实现的,而你非要整个final来阻止这种行为。(你这怕是要让人家断子绝孙😄😄)

8.接口中的方法的修饰符只能为public和default,不能使用protected、private或者final(public修饰抽象方法和静态方法,default为默认方法修饰符)

在这里插入图片描述

接口的作用

1.使某些实现类具有我们想要的功能。

比如,实现了Cloneable接口的类具有拷贝的功能,实现了Comparable或者Comparator的类具有比较功能。
Cloneable和Serializable一样,都属于标记型接口,它们的内部都是空的。实现了Cloneable接口的类可以使用Object.clone()方法,否则会抛出异常CloneNotSupportedException。

2.Java原则上类只支持单继承,但可通过接口实现多继承的目的

为什么说Java类只支持单继承呢?原因是容易引发意义不明确!⭐
举个栗子:有一个类TestA,如果允许它可以同时继承TestB类和TsetC类(即public class TestA extends TestB,TestC{}),且TestB、TestC两个类中都有同一方法run(),声明对象:TestA a = new TestA();
那么此时,a.run();应该调用哪一个父类的run()方法呢???显然无法给出答案,因此Java禁止类的多继承,提出了接口概念。但C++中是可以的,所以C++在这一点上就可以显示出C++语法是比Java要复杂一点。

但一个类可以实现多个接口,这又是为什么?⭐
举个栗子:假设类TestImplementationA实现了TestInterfaceB、TestInterfaceC两个接口(即public class TestImplementationA implements TestInterfaceB,TestInterfaceC{}),且TestInterfaceB、TestInterfaceC两个接口都有一个抽象方法run(),现在调用方法时,就没有任务不明确的地方。因为抽象方法是没有具体实现的,并且超类的任何方法都需要在子类中覆盖实现,所以在调用的时候其实是调用自己覆盖实现的方法,不存在调用不明确的地方。

public interface TestInterfaceB {
    void run();
}
public interface TestInterfaceC {
   void run();
}
public class TestImplementationA implements TestInterfaceB, TestInterfaceC {
    @Override
    public void run() {
        System.out.println("实现接口类覆盖实现的方法");
    }
    public static void main(String[] args) {
        TestImplementationA a = new TestImplementationA();
        a.run();
    }
}
3.实现多态性

同一操作作用于不同对象,可以有不同的解释,产生不同的执行结果。
多态的实现方式有两种:子类继承父类类实现接口
多个子类继承一个父类,多个类实现一个接口

单继承(一个类只能继承一个类)是类的特性,多接口(一个类可以实现多个接口)是接口的特性。绝对不能将多接口的概念和多态的概念搞混,紧抓多态的定义!

Test接口

public interface Test {
    /**
     * 接口方法drink
     */
    void drink();
}

Beverage实现类

/**
 * 类Beverage实现了接口Test
 */
public class Beverage implements Test {
	/**
     * Beverage重写了接口Test
     */
    @Override
    public void drink() {
        System.out.println("饮料是可以喝的");
    }
}

Beer实现类

/**
 * 类Beer实现了接口Test
 */
public class Beer implements Test {
	/**
     * Beer重写了接口Test
     */
    @Override
    public void drink() {
        System.out.println("啤酒是可以喝的");
    }
}

Application测试类

public class Application {
    public static void main(String[] args) {
    	//接口型变量t1指向Beer实现类对象
    	//接口型变量t2指向Beverage实现类对象
        Test t1 = new Beer();
        Test t2 = new Beverage();
        t1.drink();
        t2.drink();
        /*
        匿名对象的使用
		new Beer().drink();
		new Beverage().drink();
		*/
    }
}

输出测试结果

接口与抽象类的区别

抽象类是用来捕获子类的通用特性的,接口是抽象方法的集合。
抽象类可以允许有任意变量和普通方法,说明做还不够抽象,抽象的不够彻底。那么进行更彻底的抽象,规定只能写抽象方法,而这种规定我们给它起名叫接口。因此我们可以发现,接口的意义就是为了更好地设计实现分离。
接口的定义与类的定义相似,但是使用interface关键字。它也会被编译成.class文件,但是一定要明确它不是类类型,而是一种特殊的引用类型。
接口是系统中最高层次的抽象模型,不属于类!

接口可以多继承,注意是接口!相关内容以后再扩充

设计层面⭐

从设计层面来说,抽象类是对类的抽象,是一种模板的设计;接口是行为的抽象,是一种行为规范。

语法层面⭐

相同点

  • 接口和抽象类都不能实例化
  • 都用于被其他类实现或继承
  • 都包含抽象方法,其子类都必须重写所有的抽象方法,不能选择性重写

不同点

\抽象类接口
声明抽象类使用abstract关键字声明接口使用interface关键字声明
实现子类使用extends关键字来继承抽象类,如果子类不是抽象类的话,它必须重写抽象类中的所有方法子类使用implements关键字来实现接口,子类必须重写接口中的所有抽象方法
构造器抽象类有构造器接口没有构造器
权限修饰符抽象类中的方法可以是任意访问修饰符接口方法默认修饰符为public,并且不允许定义private或者procted
继承实现关系一个类只能继承一个抽象类一个类可以实现多个接口
字段声明抽象类的字段可以是任意的接口的字段默认都是static和final的

备注:Java8之后接口中引入静态方法和默认方法,Java9引入了私有方法

接口和抽象类各有优缺点,在接口和抽象类的选择上,必须要遵守一个原则:

  • 行为模型使用接口,少或避免使用抽象类
  • 如果需要定义子类的行为,又要为子类提供通用的功能,可以选择抽象类

不过随着JDK的不断更新,接口的性能变得越来越强大,因此尽可能地使用接口。


OO的精髓就是,对对象的抽象,而最能体现这一点的就是接口。这也是为什么我们讨论设计模式都只针对具备了抽象能力的语言(比如C++、Java、C#等)的原因。设计模式所研究的,实际上就是如何合理的去抽象。


内部类

现阶段当作选学,如果JavaSE学完还不明白这部分内容,那就得将它作为必学了。

内部类就是在一个类的内部再定义一个类,比如在A类中定义一个B类,那么B类相对A来说就称为内部类,而A类相对B类来说就是外部类了。

  1. 成员内部类(只是定义在外部类中)
  2. 静态内部类(定义在外部类中,且被static修饰)
  3. 局部内部类(定义在外部类的方法里)
  4. 匿名内部类(实现接口的类,)
  5. Lambda表达式(JDK8的新特性,知道有这么个概念就行,多线程的时候学)

一个java文件里并列的类,只能有一个public class,但是可以有多个class!

1.成员内部类

  • 启动类
public class Application {
    public static void main(String[] args) {

        Outer outer = new Outer();
        //调用外部类
        outer.out();
        //通过这个外部类来调内部类的实例化对象并将其地址赋给inner
        Outer.Inner inner = outer.new Inner();
        //调用内部类
        inner.in();
        inner.getId();
    }
}
  • 成员内部类
public class Outer {
    /**
     * 这里的实例变量id是可以转换为getId方法的局部变量的
     * 但是没有转的原因,只是为了说明内部类方法是可以获取外部类属性这一问题
     */
    private int id = 10;
    public void out(){
        System.out.println("这是外部类的方法");
    }
    public class Inner{
        public void in(){
            System.out.println("这是内部类的方法");
        }
        //获得外部类的私有属性   从这里我们看出了内鬼的可怕性😄😄
        //外部类都可以获得自己的方法,它是内部类必然也可以,就不方法代码了
        public void getId(){
            System.out.println(id);
        }
    }
}

2.静态内部类

public class Outer {

    private int Id = 10;
    public void out(){
        System.out.println("这是外部类的方法");
    }

    public static class Inner{
        public void in(){
            System.out.println("这是静态内部类的方法");
        }
        //无法获得外部类的私有属性   不能从静态境况中引入非静态字段Id
        //即前面方法调用一样,你不能用一个已经存在的去调一个还不存在的东西
        /*public void getId(){
            System.out.println(id);
        }
        */
    }
}

3.局部内部类

public class Outer {
    /**
     * 局部内部类   和局部变量一样定义在方法中,不允许加修饰符public,否则编译器就报错
     */
    public void method(){
        
        class Inner{   
        }   
    }
}

4.匿名内部类

public class Test {
    public static void main(String[] args) {
        /*
        没有名字初始化类
        匿名对象的使用,不用把实例对象保存在变量(引用)里
         */
        new Apple().eat();

        /*
        正常的创建类的实例化对象及方法的调用
        Apple apple = new Apple();
        apple.eat();
         */

        /*
        实现了接口的类的匿名使用
        现阶段不要求全部掌握,后面在分析源码的时候,见一个说一个
        多线程里面很常见  另外Android的监听...
         */
        new UserService(){
            @Override
            public void pay() {

            }
        };

        /*
        类“从UserService派生的匿名类”必须声明为抽象,或在“UserService”中实现抽象方法“pay()”
        去和前面学习的抽象类对比学习,你能发现一些相似之处
        UserService userService = new UserService(){
            @Override
            public void pay() {

            }
        };
         */
    }
}

class Apple{
    public void eat(){
        System.out.println("1");
    }
}
/**
 * 实现用户服务接口
 */
interface UserService{
    /**
     * pay来实现支付的用户服务
     */
    void pay();
}

如果我的博客对你有一点点帮助,望请大侠可以给文章点个赞再走~
声明:因个人能力有限,博文中必有不足之处,望学术同仁不吝赐教!一起学习一起进步!
以上内容均为原创,转载请注明出处!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值