• java05 面向对象-接口、多态、final、权限、内部类


    1.接口interface的含义

          Java接口是一系列方法的声明,是一些方法特征的集合,一个接口只有方法的特征,但没有方法的实现,因此这些方法可以在不同的地方被不同的类实现,而这些实现可以具有不同的行为(功能)

    • 数据类型: 引用类型
    • 格式: public interface 接口名称{}  接口名称遵从大驼峰 各个英文单词首字母大写
    • 备注:一般情况下,.java文件生成.class。虽然接口用interface,源代码仍然是.java,字节码文件仍然是.class
    • 演变新增功能的历程:

    java7:接口可以包含的内容  常量、抽象方法。

    java8:接口可以包含的内容  常量、抽象方法、默认方法、静态方法

    java9:接口可以包含的内容  常量、抽象方法、默认方法、静态方法、私有方法


    1.1 java接口内容的详细介绍

    在IDEA中,新建class中,选择类别kind的interface;在任何版本java中,接口都能定义抽象方法。

    image

    • 抽象方法的定义格式

    public abstract 返回值类型  方法名称(参数类型、参数名称){

             方法体

    }

    1.2.接口的定义格式

    public interface 接口名称{

           抽象方法

           public abstract 返回值类型 方法名称(参数列表);

      }

    注意:接口中的抽象方法有以下特殊的地方:

    • 接口中的抽象方法,修饰符必须是两个固定的关键字,public abstract(可随意省略)
    • 接口中的抽象方法,三要素(返回值类型  方法名称(小驼峰) 参数列表)随便定义

    image


    1.3接口使用步骤

    1.接口不能直接使用,也就是不能new接口;必须有一个实现类(相当于子类)实现(相当于extends)该  接口

    参考:继承中  public class 子类名称  extends 父类名称{  方法体 }

    2.接口中  public class 实现类名称  implements 接口名称{   覆盖重写接口中全部抽象方法,也就是去掉abstract 并加上方法体 大括号

    3.创建实现类的对象,进行使用。

            一般而言,接口的实现类,建议名称由接口名称+Impl组成,其中impl为implement(实施)的简写。 


      1 package cn.itcast.day04.demo02;
      2 
      3 public interface MyInterfaceDemo02 {
      4     public abstract void methodAbs();
      5 
      6 }
      7 
      8 
      1 package cn.itcast.day04.demo02;
      2 
      3 public class MyInterfaceDemo02Impl implements MyInterfaceDemo02{
      4 
      5     @Override
      6     public void methodAbs() {
      7         System.out.println("接口方法已执行");
      8 
      9     }
     10 }
     11 
     12 
     13 
     14 
     15 
      1 package cn.itcast.day04.demo02;
      2 
      3 public class Demo02Main {
      4     public static void main(String[] args) {
      5         MyInterfaceDemo02Impl impl = new MyInterfaceDemo02Impl();
      6         impl.methodAbs();
      7     }
      8 }
      9 

    image

    java8中,增加默认方法,解决接口升级的问题,它的定义方法:

    public default  返回值类型 方法名称 (参数列表){方法体}

    切记:默认方法 可以有方法体。

           在一般开发环境中,接口的实现类已经完成某一个版本的接口抽象方法的覆盖重写,但是如果新增一个抽象方法,那么是否所有实现类全部增加重写,压力巨大,此时默认方法就起到了作用

    • 接口的默认方法的关键点

    1.可以通过接口实现类对象,直接调用

    2.可以被接口实现类进行覆盖重写。

    3.默认方法,会被实现类继承下去,也可以在实现类中进行覆盖重写

      1 package cn.itcast.day04.demo02;
      2 
      3 public class MyInterfaceDemo02Impl implements MyInterfaceDemo02{
      4 
      5     @Override
      6     public void methodAbs() {
      7         System.out.println("接口抽象方法在实现类中已执行");
      8 
      9     }
     10     @Override
     11     public  void m(){
     12         System.out.println("覆盖重写了接口的默认方法");
     13     }
     14 }


    在后面知识中,接口的默认方法可以拼接函数模型。

    java8中,增加静态方法,也就是带着static修饰符

    格式:public static 返回值类型 方法名称(参数列表){方法体}

    注意:接口方法不能通过实现类来调用,在main方法中直接用接口名称.静态方法(参数),直接使用。因为静态和对象没有关系,如果允许对象调用可能会冲突,所以不需要通过对象。

    1585823016(1)

      1 package cn.itcast.day04.demo02;
      2 
      3 public class Demo02Main {
      4     public static void main(String[] args) {
      5         MyInterfaceDemo02Impl impl = new MyInterfaceDemo02Impl();
      6         impl.methodAbs();
      7         impl.m();
      8         MyInterfaceDemo02.m1();
      9     }
     10 }
     11 
     12 

    java9中,增加私有方法,也就是带着static修饰符,私有方法在实现类中将无法调用。分为两种

    • 普通私有方法 解决默认方法的重复代码问题

    private 返回值类型 方法名称(参数列表){方法体}

    • 静态私有方法 解决静态方法的重复代码问题。

    private static 返回值类型 方法名称(参数列表){方法体}

    image

    1.4.接口中的常量定义和使用

          接口中,可以定义一些常量(类似于变量,但常量的不同在于不能变),但需要public,static ,final来修饰;

    格式    public static  final int 常量名称 = 10;使用了final表明不可变。只要在接口中就是常量,不带 public static  final 也是常量,必须赋值,不能不赋值。一般而言  常量名称用大写,如果多单词建议下划线分割如: NUM_OF

      1 package cn.itcast.day04.demo02;
      2 
      3 public interface MyInterfaceDemo02 {
      4     public static final int  XYZ = 20;
      5     public default void m(){
      6         System.out.println("接口的默认方法1");
      7         x();
      8     }
      9 }
      1 package cn.itcast.day04.demo02;
      2 
      3 public class Demo02Main {
      4     public static void main(String[] args) {
      5         MyInterfaceDemo02Impl impl = new MyInterfaceDemo02Impl();
      6         impl.methodAbs();
      7         impl.m();
      8         MyInterfaceDemo02.m1();
      9         System.out.println(MyInterfaceDemo02.XYZ);
     10     }
     11 }
     12 

    使用时,直接用接口名.常量名即可

    1.5接口的总结“

    在Java 9+版本中,接口的内容可以有:

    • 1. 成员变量其实是常量,格式:
      [public] [static] [final] 数据类型 常量名称 = 数据值;
      注意:
           常量必须进行赋值,而且一旦赋值不能改变。
           常量名称完全大写,用下划线进行分隔。
    • 2. 接口中最重要的就是抽象方法,格式:
      [public] [abstract] 返回值类型 方法名称(参数列表);
      注意:实现类必须覆盖重写接口所有的抽象方法,除非实现类是抽象类。
    • 3. 从Java 8开始,接口里允许定义默认方法,格式:
      [public] default 返回值类型 方法名称(参数列表) { 方法体 }
      注意:默认方法也可以被覆盖重写
    • 4. 从Java 8开始,接口里允许定义静态方法,格式:
      [public] static 返回值类型 方法名称(参数列表) { 方法体 }
      注意:应该通过接口名称进行调用,不能通过实现类对象调用接口静态方法
    • 5. 从Java 9开始,接口里允许定义私有很乏,格式:
      普通私有方法:private 返回值类型 方法名称(参数列表) { 方法体 }
      静态私有方法:private static 返回值类型 方法名称(参数列表) { 方法体 }
      注意:private的方法只有接口自己才能调用,不能被实现类或别人使用。


    同时注意:

    • 接口不能有静态代码块;不能有构造方法;

           原因是如果有构造方法,那么应该就可以new对象使用,实际接口的实现类不可以如此。

    • 一个类的直接父类只有一个,但是可以同时实现多个接口。

    public class MyInterfaceImpl implements MyInterfaceA,MyInterfaceB{

                  覆盖重写所有方法

      }

      1 package cn.itcast.day04.demo02;
      2 
      3 public interface MyInterfaceDemo02 {
      4     public abstract void methodA();
      5 
      6 }
      7 
      1 package cn.itcast.day04.demo02;
      2 
      3 public interface MyInterfaceDemo03 {
      4     public abstract void methodB();
      5 
      6 }
      7 
      8 
      9 
      1 package cn.itcast.day04.demo02;
      2 
      3 public class MyInterfaceDemo02Impl implements MyInterfaceDemo02,MyInterfaceDemo03{
      4 
      5     @Override
      6     public void methodA() {
      7         System.out.println("覆盖重写了接口A的抽象方法");
      8     }
      9     @Override
     10     public void methodB() {
     11         System.out.println("覆盖重写了接口B的抽象方法");
     12     }
     13 }
     14 
     15 
     16 
      1 package cn.itcast.day04.demo02;
      2 
      3 public class Demo02Main {
      4     public static void main(String[] args) {
      5         MyInterfaceDemo02Impl impl = new MyInterfaceDemo02Impl();
      6         impl.methodA();
      7         impl.methodB();
      8     }
      9 }
     10 
    • 所有的类都有父类,它相当于在定义处有extends Object

    1585880755(1)

    • 如果实现类的多个接口中,存在重复的抽象方法,那么只需要覆盖重写一次即可

    image

    • 如果实现类没有覆盖重写所有接口的所有抽象方法,那么实现类必须是抽象类
    • 如果实现类实现的所有接口中,存在多个重复的默认方法,那么一定要对冲突的默认方法进行覆盖重写
    • 一个类的直接父类的方法和接口中的默认方法产生了冲突,则优先使用父类方法。public class Zi extends Fu implements MyInterface{ 方法体}
    • 类与类之间是单继承的,直接父类只有一个
    • 类与接口之间是多实现的,一个类可以实现多个接口;
    • 接口与接口之间是多继承的。多个父接口的抽象方法重复,没关系,但多个父接口中的默认方法重复就得覆盖重写,且子接口必须default关键字。




    1.6 静态方法定义:

    ①、格式

      在java类中(方法中不能存在静态代码块)使用static关键字和{}声明的代码块:

    public class CodeBlock {

    static{

    System.out.println("静态代码块");

    }

    }

      ②、执行时机

      静态代码块在类被加载的时候就运行了,而且只运行一次,并且优先于各种代码块以及构造函数。如果一个类中有多个静态代码块,会按照书写顺序依次执行。后面在比较的时候会通过具体实例来证明。

      ③、静态代码块的作用

      一般情况下,如果有些代码需要在项目启动的时候就执行,这时候就需要静态代码块。比如一个项目启动需要加载的很多配置文件等资源,我们就可以都放入静态代码块中。

      ④、静态代码块不能存在任何方法体中

      这个应该很好理解,首先我们要明确静态代码块是在类加载的时候就要运行了。我们分情况讨论:

      对于普通方法,由于普通方法是通过加载类,然后new出实例化对象,通过对象才能运行这个方法,而静态代码块只需要加载类之后就能运行了。

      对于静态方法,在类加载的时候,静态方法也已经加载了,但是我们必须要通过类名或者对象名才能访问,也就是说相比于静态代码块,静态代码块是主动运行的,而静态方法是被动运行的。

      不管是哪种方法,我们需要明确静态代码块的存在在类加载的时候就自动运行了,而放在不管是普通方法还是静态方法中,都是不能自动运行的。

      ⑤、静态代码块不能访问普通变量

      这个理解思维同上,普通变量只能通过对象来调用,是不能放在静态代码块中的。

    2. 面向对象-多态(polymorphism)

    格式 父类名称 对象名 = new 子类名称();父类引用 指向 子类对象;

    或者 接口名称 对象名 = new 实现类名称();

    简单一句话就是 左父右子;右侧的子类对象就被当做父类使用。类似于一只猫当做一个动物来看,实现子类就是父类,从而进一步实现面向对象的多态性,对象的多态性。

    2.1. 多态中,成员变量的访问规则

    • 通过对象名.成员变量,看等号左边是谁,优先用谁,否则向上找
    • 通过成员方法访问成员变量,看方法属于谁,则优先用谁,否则向上找,主要看子类是否覆盖重写了父类。

    image

    2.2. 多态中,成员方法的访问规则

    看NEW的是谁,则优先用谁,否则向上找。

    但成员方法是 编译看左边,运行看右边

    而成员变量是 编译看左边,运行看左边

    image

    2.3 多态的好处

    多态在很多情况下,书写代码美观和统一性。无论等号右边创建的对象或者实现类的是谁,左边的接口或父类都不会变化。更加的灵活

    1586009885(1)2.4 对象的上下转型

    父类名称  对象名 =  new 子类名称();

    含义: 右侧创建一个子类对象,把它当成父类。向上转型一定是安全的

    类似于

    double  num = 100 正确

    image切记,一旦子类的对象向上转型成为父类,那么此时这个对象将无法调用子类的方法。

    1586011277(1)

      1 package cn.itcast.day04.Demo;
      2 
      3 public class Demo01 {
      4     public static void main(String[] args) {
      5         Animal animal = new Cat();
      6         animal.eat();// 向上转型。、
      7         animal.sleep();
      8     }
      9 }
     10 

      1 package cn.itcast.day04.Demo;
      2 
      3 public class Cat extends Animal{
      4     @Override
      5     public void eat() {
      6         System.out.println("猫吃鱼");
      7     }
      8     public void sleep(){
      9         System.out.println("睡觉");
     10     }
     11 }
     12 

      1 package cn.itcast.day04.Demo;
      2 
      3 public abstract class Animal {
      4     public abstract void eat();
      5 
      6 }
      7 

    解决方案:

    用对象的向下转型,才能访问子类的自有方法。

    子类名称 对象名 = (子类名称)父类对象

    含义:将父类对象,还原成原来的子类对象。

    未向下转型前,只能调用共有方法,也就是父类方法。向下转型后,将可以调用子类特有方法,但是必须是原来的子类方法,否则调用其他子类方法会出错,

     1586011823(1)

    向下转型的安全检测关键字 instanceof

    instanceof 严格来说是Java中的一个双目运算符,用来测试一个对象是否为一个类的实例,用法为 boolean result = obj instanceof Class;instanceof 运算符只能用作对象的判断。

      1 package cn.itcast.day04.Demo;
      2 
      3 public class Demo01 {
      4     public static void main(String[] args) {
      5         Animal animal = new Dog();
      6         animal.eat();// 向上转型。、
      7 
      8         if( animal instanceof Dog){
      9             Dog dog = (Dog) animal;
     10             dog.eat();
     11         }else{
     12             Cat cat = (Cat) animal;
     13             cat.eat();
     14         }
     15 
     16     }
     17 }
     18 

      1 package cn.itcast.day04.Demo;
      2 
      3 public class Dog extends Animal{
      4     @Override
      5     public void eat() {
      6         System.out.println("狗吃骨头");
      7     }
      8 }
      9 

      1 package cn.itcast.day04.Demo;
      2 
      3 public class Demo01 {
      4     public static void main(String[] args) {
      5         Animal animal = new Dog();
      6         animal.eat();// 向上转型。、
      7         System.out.println("-====");
      8         method(new Dog());
      9         method(animal);
     10     }
     11     public static void method(Animal animal) {
     12         if (animal instanceof Dog) {
     13             Dog dog = (Dog) animal;
     14             dog.eat();
     15         } else {
     16             Cat cat = (Cat) animal;
     17             cat.eat();
     18         }
     19     }
     20 }
     21 

    格式  对象名  instanceof 子类名称 

    3.final关键字 最终的 不可改变的

    常见的四种用法

    • 用来修饰一种类  public final class 类名称{} 含义:这个类再也没有子类,也就是说该类的方法将无法覆盖重写;但改类它可以继承,从而覆盖重写父类的成员方法

    image


      1 package cn.itcat.day07.demo01.demo01;
      2 // finale可以继承,但其他类不能继承final类
      3 public final class MyClass extends Demo01Final {
      4     public void methodA(){
      5         System.out.println("final类");
      6     }
      7 
      8 }
      9 

    • 用来修饰一个方法,那么这个方法就是最终方法,不可以由子类覆盖重写,并且抽象方法abstract是绝对不能加final的,因为abstract与final冲突。

    image

    image


    • 用来修饰一个局部变量,也就是在方法的参数或者方法大括号内,则用final修饰后,该变量将无法进行更改,第二次即便跟之前一样,赋值也不行。

    image

    当然对于引用类型,如对象来说,则不可变的是地址

    final Student  stu = new Student();

    则stu传值 只能一次。

    1586051362(1)

    • 用来修饰一个成员变量,由于成员变量具有默认值,所有一旦用final修饰,建议要么立刻赋值,要么构造方法赋值(全部构造各个赋值)且set注释掉(因为set会修改)。

    image3.1.四种权限修饰符

    1586059229(1)

    3.2.内部类概念与分类

           一个类包含另外一个类,简称内部类,例如人体与心脏的关系,汽车和发动机的关系。

    • 成员内部类(类当中,方法外)

    修饰符 class 外部类名称{

          修饰符 class 内部类名称{
         }

    }

    注意事项:

    • 内部类用外部类,无论啥修饰符都行
      1 package cn.itcat.day07.demo01.demo01.Demo02;
      2 
      3 public class Body { //外部类
      4     public class Heart{  // 成员内部类
      5         // 成员内部类的方法
      6         public void methodHeart(){
      7             System.out.println("心脏跳动");
      8             // 成员内部类访问外部内的私有化成员变量
      9             System.out.println("我叫" + name);
     10         }
     11 
     12     }
     13     private String name;// 私有化,访问获取需要get set
     14     // 外部类的方法
     15     public void methodBody(){
     16         System.out.println("外部类的方法");
     17     }
     18 
     19     public String getName() {
     20         return name;
     21     }
     22 
     23     public void setName(String name) {
     24         this.name = name;
     25     }
     26 }
     27 

    那么如何使用成员内部类呢

    分两种情况,

    • 间接方式:外部类使用内部类,然后main只是调用外部类的方法,在外部内的方法中创建内部类的对象,然后在外部类方法中内部类的对象点内部类的方法。
      1 package cn.itcat.day07.demo01.demo01.Demo02;
      2 
      3 public class Body { //外部类
      4     public class Heart{  // 成员内部类
      5         // 成员内部类的方法
      6         public void methodHeart(){
      7             System.out.println("心脏跳动");
      8             // 成员内部类访问外部内的私有化成员变量
      9             System.out.println("我叫" + name);
     10         }
     11 
     12     }
     13     private String name;// 私有化,访问获取需要get set
     14     // 外部类的方法
     15     public void methodBody(){
     16         System.out.println("外部类的方法");
     17         // 外部类的成员方法使用内部类成员方法,在外部类方法中,创建对象
     18         new Heart().methodHeart();//匿名对象访问(没有对象名)
     19     }
     20 
     21     public String getName() {
     22         return name;
     23     }
     24 
     25     public void setName(String name) {
     26         this.name = name;
     27     }
     28 }
     29 


        1 package cn.itcat.day07.demo01.demo01.Demo02;
        2 
        3 public class Main {
        4     public static void main(String[] args) {
        5         Body body = new Body();
        6         body.setName("王宝强");
        7         body.methodBody();//通过调用外部类的方法,使用内部类
        8 
        9     }
       10 }
       11 
      • 直接方式: 创建内部类对象

      通常对象定义是:类名称 对象名 = new 类名称();而对于嵌套内外类,需要

      外部类名称.内部类名称 对象名 = new 外部类名称().new 内部类名称();

      访问外部类成员变量、内部类成员变量和方法的局部变量

      1586073453(1)




      • 局部内部类(又包含匿名内部类)类当中,方法中,出了这个方法就不能用

      特点:

      • 不能使用任何的访问修饰符。
      • 生成两个.class文件,一个Outer.class ,另一个Outer$LocalInner.class
      • 局部内部类只能访问方法中声明的final类型的变量。

      修饰符 class 外部类名称{

           修饰符 返回值类型 外部方法名称(参数列表) {

                  修饰符 class 局部内部类名称{


                   }

            }

      }

      总结四种修饰符在内部类中的应用:

      1586088740(1)


      局部内部类,如果要访问所在方法的局部变量,那么这个局部变量必须是有效final的,也就是注意写上final。原因是

      1586089710(1)1586145234(1)局部内部类之匿名内部类:这是最为关键的部分。

      如果接口的实现类(或者是父类的子类)只需要使用一次,那么这种情况下就可以省略该类的定义,改为使用匿名内部类

      匿名内部类的格式:

      接口名称 对象名 = new 接口名称(){

             覆盖重写所有抽象方法

      };// 大括号里面是类,但这个类没有名称。

      1586178883(1)匿名内部类的注意事项:

      1.匿名内部类,在创建对象的时候,只能使用一次









    • 相关阅读:
      洛谷【P1177】【模板】归并排序
      洛谷【P1177】【模板】快速排序
      洛谷【P1104】生日(冒泡排序版)
      洛谷【P1104】生日(插入排序版)
      洛谷【P1104】生日(选择排序版)
      BZOJ5443:[CEOI2018]Lottery
      ReactNative---ref的用法和技巧
      ios---运用MJRefresh组件设置下拉刷新
      ReactNative---setState与性能的平衡
      ios---设置UITabBarController的字体颜色和大小
    • 原文地址:https://www.cnblogs.com/rango0550/p/10702425.html
    Copyright © 2020-2023  润新知