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.匿名内部类,在创建对象的时候,只能使用一次









    原文地址:https://www.cnblogs.com/rango0550/p/10702425.html