Java——面向对象进阶(三)

2024-06-18 1296阅读

Java——面向对象进阶(三)

前言:

抽象类,接口,内部类


文章目录

  • 一、抽象类
    • 1.1 抽象方法
    • 1.2 抽象类
    • 1.3 抽象类的使用
    • 二、 接口
      • 2.1 接口的定义和实现
      • 2.2 default 关键字
      • 2.3 实现接口时遇到的问题
      • 三、内部类
        • 3.1 成员内部类
        • 3.2 静态内部类
        • 3.3 成员内部类
        • 3.4 匿名内部类(最常用)

          一、抽象类

          在Java中,抽象类是一种不能被实例化的类,它通常用于定义一些共用的方法和字段,在该类中不知道具体的实现,但还是想让子类必须去实现这些方法,所以将成员方法定义成抽象方法,该类定义成抽象类。abstract 用于修饰方法方法和类,修饰的方法是抽象方法,修饰的类是抽象类。

          1.1 抽象方法

          使用abstract 关键字修饰方法,该方法就成了抽象方法,抽象方法只包含一个方法名,而没有方法体。

          定义格式:
          修饰符 abstract 返回值类型 方法名 (参数列表);
          示例:
          public abstract void run();
          

          1.2 抽象类

          使用abstract 关键字修饰类,

          形式:
          abstract class 类名字 { 
          }
          示例:
          public abstract class Animal {
              public abstract void run();
          }
          

          !注意:抽象类不一定有抽象方法,但有抽象方法的类一定要定义成抽象类

          1.3 抽象类的使用

          1. 继承抽象类:

            • 如果一个类继承了一个抽象类,并且这个类不是抽象类,那么它必须实现所有从抽象类继承的抽象方法。
            • 如果一个类继承了一个抽象类,但没有实现所有的抽象方法,那么这个类必须声明为抽象类。
            • 抽象类的继承链:

              • 一个抽象类可以继承另一个抽象类,并且可以选择性地实现部分或全部继承的抽象方法。
              • 最终,必须有一个具体(非抽象)的子类实现所有抽象方法。

          示例代码:

          1. 子类实现所有抽象方法

            abstract class Animal {
                abstract void makeSound();
                abstract void eat();
            }
            class Dog extends Animal {
                @Override
                void makeSound() {
                    System.out.println("Woof!");
                }
                @Override
                void eat() {
                    System.out.println("Dog is eating.");
                }
            }
            public class Main {
                public static void main(String[] args) {
                    Animal myDog = new Dog();
                    myDog.makeSound();
                    myDog.eat();
                }
            }
            

            Dog类继承了Animal类,并且实现了所有的抽象方法。

          2. 子类没有实现所有抽象方法,必须声明为抽象类

            abstract class Animal {
                abstract void makeSound();
                abstract void eat();
            }
            abstract class Canine extends Animal {
                @Override
                void makeSound() {
                    System.out.println("Howl!");
                }
                // `eat` 方法没有实现,所以 Canine 仍然是抽象类
            }
            class Dog extends Canine {
                @Override
                void eat() {
                    System.out.println("Dog is eating.");
                }
            }
            public class Main {
                public static void main(String[] args) {
                    Animal myDog = new Dog();
                    myDog.makeSound();
                    myDog.eat();
                }
            }
            

            Canine类实现了makeSound方法,但没有实现eat方法,因此Canine必须声明为抽象类。Dog类继承了Canine并实现了eat方法,使得Dog成为具体类,可以被实例化。


          二、 接口

          接口(Interface)在Java中是一个重要的概念,用于定义一组方法的规范,而不提供任何实现。接口主要用于定义类之间的契约和规范,确保实现这些接口的类遵循相同的方法签名。

          2.1 接口的定义和实现

          接口的定义:

          接口使用interface关键字定义,接口中的所有方法默认是public和abstract,所有成员变量默认是public static final。以下是一个简单的接口示例:

          public interface Animal {
              void makeSound();
              void eat();
          }
          

          实现接口

          类通过implements关键字实现接口,一个类可以实现多个接口,这与抽象类的单继承限制不同。以下是实现Animal接口的两个类:

          public class Dog implements Animal {
              @Override
              public void makeSound() {
                  System.out.println("Woof!");
              }
              @Override
              public void eat() {
                  System.out.println("Dog is eating.");
              }
          }
          public class Cat implements Animal {
              @Override
              public void makeSound() {
                  System.out.println("Meow!");
              }
              @Override
              public void eat() {
                  System.out.println("Cat is eating.");
              }
          }
          

          类实现接口的要求:

          1. 必须重写实现的全部接口中所有抽象方法。
          2. 如果一个类实现了接口,但是没有重写完全部接口的全部抽象方法,这个类也必须定义成抽象类。

          如果一个类实现了多个接口,而这些接口有相同的抽象方法,那么这个类只需要实现这个抽象方法一次即可,因为Java不允许同一个类中出现重复的方法签名。这种情况下,类只需要提供一次方法的实现,而不需要重复实现多次。


          2.2 default 关键字

          default 关键字主要用于接口中的默认方法(default methods),这是从Java 8开始引入的特性。默认方法允许在接口中为方法提供默认的实现,这样可以在不破坏现有实现的情况下,向接口添加新的方法。

          默认方法(Default Methods)定义方式:

          public interface InterfaceName {
              // 抽象方法
              void regularMethod();
              // 默认方法
              default void defaultMethod() {
                  // 默认实现
                  System.out.println("This is a default method.");
              }
          }
          

          在上面的例子中,defaultMethod 就是一个默认方法。默认方法使用 default 关键字修饰,它提供了一个默认的实现。所有实现了这个接口的类都会继承这个默认方法,如果需要的话,可以在实现类中重写默认方法。

          示例:

          public interface Vehicle {
              void start();
              
              default void honk() {
                  System.out.println("Vehicle is honking.");
              }
          }
          public class Car implements Vehicle {
              @Override
              public void start() {
                  System.out.println("Car is starting.");
              }
              // Car可以选择不重写honk方法,使用默认实现
          }
          public class Bike implements Vehicle {
              @Override
              public void start() {
                  System.out.println("Bike is starting.");
              }
              @Override
              public void honk() {
                  System.out.println("Bike is honking."); // Bike提供了自己的honk方法实现
              }
          }
          

          当需要扩展一个接口,但又不能破坏所有实现这个接口的类时,可以使用默认方法。

          注意事项:

          如果一个类实现了多个接口,而这些接口有相同的默认方法,实现类必须重写这个默认方法来解决冲突。

          interface A {
              default void hello() {
                  System.out.println("Hello from A");
              }
          }
          interface B {
              default void hello() {
                  System.out.println("Hello from B");
              }
          }
          class C implements A, B {
              @Override
              public void hello() {
                  A.super.hello(); // 明确指定调用接口A的默认方法
                  //B.super.hello(); 或明确指定调用接口B的默认方法
              }
          }
          

          2.3 实现接口时遇到的问题

          1. 如果一个类继承了一个类并实现了一个接口,且父类和接口中有都有相同签名的方法。

            • 处理办法一:如果父类中的方法体,能满足当前业务的需求,在子类中可以不用重写。
            • 处理办法二:如果父类中的方法体,不能满足当前业务的需求,需要在子类中重写。
            • 如果一个接口中,有多个抽象方法,但在实现类中,只需要用其中一个或部分

              • 方法一:实现类声明为抽象类

                你可以将实现类声明为抽象类,并只实现部分抽象方法,这样子类就可以根据需要选择性地实现接口的方法。

                // 定义一个接口
                interface MyInterface {
                    void method1(); // 抽象方法1
                    void method2(); // 抽象方法2
                    void method3(); // 抽象方法3
                }
                // 实现类声明为抽象类,并只实现部分方法
                abstract class MyAbstractClass implements MyInterface {
                    @Override
                    public void method1() {
                        System.out.println("Implemented method1");
                    }
                    
                    // 不需要实现 method2 和 method3
                }
                // 子类继承抽象类,选择性实现部分方法
                class MyClass extends MyAbstractClass {
                    @Override
                    public void method2() {
                        System.out.println("Implemented method2");
                    }
                }
                
              • 方法二:使用适配器模式(Adapter Pattern)

                如果不想使用抽象类,可以使用适配器模式,它允许提供接口的默认实现,然后子类可以选择性地覆盖它们。

                // 定义一个接口
                interface MyInterface {
                    void method1(); // 抽象方法1
                    void method2(); // 抽象方法2
                    void method3(); // 抽象方法3
                }
                // 创建适配器类,提供接口的默认实现
                abstract class MyInterfaceAdapter implements MyInterface {
                    @Override
                    public void method1() {
                        // 默认实现
                    }
                    
                    @Override
                    public void method2() {
                        // 默认实现
                    }
                    
                    @Override
                    public void method3() {
                        // 默认实现
                    }
                }
                // 子类选择性地覆盖需要的方法
                class MyClass extends MyInterfaceAdapter {
                    @Override
                    public void method2() {
                        System.out.println("Implemented method2");
                    }
                }
                

          三、内部类

          内部类(Inner Class)是定义在另一个类内部的类。Java 提供了多种内部类,包括成员内部类、局部内部类、匿名内部类和静态内部类。

          按定义的位置来分:

          1. 成员内部内,类定义在了成员位置 (类中方法外称为成员位置,无static修饰的内部类)
          2. 静态内部类,类定义在了成员位置 (类中方法外称为成员位置,有static修饰的内部类)
          3. 局部内部类,类定义在方法内
          4. 匿名内部类,没有名字的内部类,可以在方法中,也可以在类中方法外。

          3.1 成员内部类

          定义格式:

          class OuterClass {
              class InnerClass {
                  // Inner class members
              }
          }
          

          内部类的使用格式:

           外部类.内部类。 // 访问内部类的类型都是用 外部类.内部类
          

          创建内部类实例:

          • 方式一:外部直接创建成员内部类的对象

            OuterClass outer = new OuterClass();
            OuterClass.InnerClass inner = outer.new InnerClass();
            //或
            OuterClass.InnerClass inner = new OuterClass().new InnerClass();
            
          • 方法二:在外部类中定义一个方法提供内部类的对象

            public class Outer {
                private class Inner{
                }
                
                public Inner getInstance(){
                    return new Inner();
                }
            }
            public class Test {
                public static void main(String[] args) {
                    Outer outer = new Outer();
            	    Inner inner = outer.getInstance();
                }
            }
            

            成员内部类的细节

            1. 成员内部类可以被一些修饰符所修饰,比如: private,默认,protected,public,static等

            2. 成员内部类可以访问外部类的所有成员,包括私有成员。这是因为成员内部类持有一个对外部类实例的引用Outer.this。

              public class Outer {
                  private String outerField = "Outer field";
                  class Inner {
                      void display() {
                          System.out.println("Inner class accessing: " + outerField);
                          System.out.println("Inner class accessing through Outer.this: " + Outer.this.outerField);
                      }
                  }
                  public static void main(String[] args) {
                      Outer outer = new Outer(); // 创建外部类对象
                      Outer.Inner inner = outer.new Inner(); // 创建内部类对象
                      inner.display(); // 调用内部类的方法
                  }
              }
              //输出:
              Inner class accessing: Outer field
              Inner class accessing through Outer.this: Outer field
              

              内存图示例:Java——面向对象进阶(三)

            3. 在 JDK 8 之前,局部变量必须显式声明为 final 才能被成员内部类。在 JDK 8 及之后,只要局部变量在初始化后没有被修改,它们就被隐式地视为 final(即有效 final),不再需要显式声明为 final。


            3.2 静态内部类

            定义格式:

            class OuterClass {
                static class StaticInnerClass {
                    // 静态内部类的成员
                }
            }
            

            创建实例:

            OuterClass.StaticInnerClass innerObject = new OuterClass.StaticInnerClass();
            

            静态内部类的特点

            1. 静态内部类可以直接访问外部类的静态成员,但不能访问外部类的非静态成员,如果要访问需要创建外部类的对象,用对象调用。

              public class Outer {
                  private static String staticOuterField = "Static Outer Field";
                  private String nonStaticOuterField = "Non-Static Outer Field";
                  static class StaticInner {
                      void display() {
                          // 静态内部类可以访问外部类的静态成员
                          System.out.println("Static Inner Class accessing static outer field: " + staticOuterField);
                          
                          // 但不能直接访问外部类的非静态成员
                          // System.out.println("Static Inner Class accessing non-static outer field: " + nonStaticOuterField); // 编译错误
                      }
                  }
                  public static void main(String[] args) {
                      // 创建静态内部类的实例
                      StaticInner inner = new StaticInner();
                      inner.display(); // Output: Static Inner Class accessing static outer field: Static Outer Field
                      
                      // 如果要访问外部类的非静态成员,需要创建外部类的对象
                      Outer outer = new Outer();
                      System.out.println("Access non-static outer field through outer object: " + outer.nonStaticOuterField);
                  }
              }
              
            2. 无需外部类实例: 可以直接创建静态内部类的实例,而不需要外部类的实例。

            3. :静态内部类中没有隐含的 Outer.this。


            3.3 成员内部类

            局部内部类是定义在方法或作用域内部的内部类。

            特点

            • 作用域限制: 局部内部类的作用域仅限于定义它的方法或代码块中,不能在外部访问。
            • 访问权限: 局部内部类可以访问外部类的所有成员,包括私有成员。
            • 访问局部变量: 局部内部类可以访问方法内的 final 局部变量(JDK 8+版本可以访问非 final 的局部变量,但其值不能在局部类中被修改)。
            • 生命周期: 局部内部类的生命周期仅限于方法调用,方法结束后局部内部类实例也会被销毁。

              示例

              public class Outer {
                  private int outerField = 10;
                  public void methodWithLocalInnerClass() {
                      final int localVar = 20; // JDK 8+ 可以不用final修饰,但不可变
                      class LocalInner {
                          void display() {
                              System.out.println("Outer field: " + outerField);
                              System.out.println("Local variable: " + localVar);
                          }
                      }
                      // 创建局部内部类的实例并调用方法
                      LocalInner inner = new LocalInner();
                      inner.display();
                  }
                  public static void main(String[] args) {
                      Outer outer = new Outer();
                      outer.methodWithLocalInnerClass();
                  }
              }
              

              3.4 匿名内部类(最常用)

              匿名内部类是一种没有显式声明类名的内部类,它在创建对象的同时定义类的实现。匿名内部类通常用于创建只需使用一次且没有额外的逻辑的类实例。

              格式:

              new 类名或者接口名() {
                   重写方法;
              };
              这里同时包含了继承或实现,方法重写,创建对象
              从new关键字到";"整个整体是匿名内部类的实例,"{}"内的是匿名内部类,没有类名
              

              使用:

              以接口为例,匿名内部类的使用,代码如下:

              interface Swim {
                  public abstract void swimming();
              }
              public class Test {
                  public static void main(String[] args) {
                      // 接口 变量 = new 实现类(); // 多态,走子类的重写方法
                      Swim s2 = new Swim() {
                          @Override
                          public void swimming() {
                              System.out.println("蛙泳...");
                          }
                      };
                      s2.swimming();
                  }
              }
              

              Java——面向对象进阶(三)

              如果你喜欢这篇文章,点赞👍+评论+关注⭐️哦!

              欢迎大家提出疑问,以及不同的见解。

VPS购买请点击我

免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们,邮箱:ciyunidc@ciyunshuju.com。本站只作为美观性配图使用,无任何非法侵犯第三方意图,一切解释权归图片著作权方,本站不承担任何责任。如有恶意碰瓷者,必当奉陪到底严惩不贷!

目录[+]