Java Programming 【Chapter 5_QA】

1、如何理解如下这句话?

首先看上半句:

使用组合替代继承,可以复用代码,但不能统一处理

含义

  • 组合:一个类通过包含另一个类的实例(对象)来使用其功能(“has-a”关系),而不是通过继承(“is-a”关系)来获取代码。
  • 复用代码:组合通过调用其他类的实例方法实现代码复用,灵活且低耦合。
  • 不能统一处理:组合本身不提供统一的接口或行为契约,不同类的实例可能有不同的方法名或行为,无法像继承那样通过父类类型统一调用。

道理

  • 继承通过父类定义统一接口(如抽象方法),子类实现后可以用父类类型统一调用(如多态)。
  • 组合只负责将功能嵌入类中,但没有强制要求这些功能的接口一致。如果没有接口约束,组合对象的调用方式可能各不相同,缺乏统一性。

代码示例

继承(统一处理)

abstract class Animal {
    abstract void move();
}
class Dog extends Animal {
    void move() { System.out.println("Dog runs"); }
}
class Bird extends Animal {
    void move() { System.out.println("Bird flies"); }
}
// 统一处理
Animal dog = new Dog();
Animal bird = new Bird();
dog.move(); // Dog runs
bird.move(); // Bird flies

 

组合(复用代码,但不统一):

class Run { void run() { System.out.println("Running"); } }
class Fly { void fly() { System.out.println("Flying"); } }

class Dog {
    Run run = new Run();
    void move() { run.run(); } // 手动调用
}
class Bird {
    Fly fly = new Fly();
    void move() { fly.fly(); } // 方法名不同
}
// 无法统一处理,方法名不一致
Dog dog = new Dog();
Bird bird = new Bird();
dog.move(); // Running
bird.move(); // Flying

总结

  • 组合通过包含 Run 或 Fly 实例复用了代码,但 Dog 和 Bird 的 move() 方法没有统一接口,无法像继承那样用同一类型(如 Animal)调用。
  • 要统一处理,需引入接口(如 Moveable),让组合对象实现统一的方法名。

 

再看中间这句

 使用接口替代继承,针对接口编程,可以实现统一处理不同类型的对象,但接口没有代码实现,无法复用代码

 

含义

  • 接口:定义一组方法签名(契约),没有实现代码。实现接口的类必须提供具体实现。
  • 针对接口编程:通过接口类型引用对象,调用接口定义的方法,从而统一处理不同类型的对象(多态性)。
  • 统一处理:接口确保不同类有相同的方法名和行为契约,可以用接口类型统一调用。
  • 无法复用代码:接口只定义方法签名,没有实现逻辑,代码必须在每个实现类中重复编写。

道理

  • 继承通过父类提供统一接口和部分代码实现,子类可以直接使用或覆盖。接口只提供统一接口,强制实现类遵循契约,但所有方法实现都需各自定义,缺乏代码共享机制。

代码示例

继承(统一处理+代码复用)

abstract class Animal {
    abstract void move();
    void eat() { System.out.println("Eating food"); } // 复用代码
}
class Dog extends Animal {
    void move() { System.out.println("Dog runs"); }
}
class Bird extends Animal {
    void move() { System.out.println("Bird flies"); }
}
// 统一处理 + 复用 eat()
Animal dog = new Dog();
Animal bird = new Bird();
dog.move(); // Dog runs
bird.eat(); // Eating food
bird.move(); // Bird flies
bird.eat(); // Eating food

接口(统一处理,无代码复用):

interface Animal {
    void move();
    void eat();
}
class Dog implements Animal {
    public void move() { System.out.println("Dog runs"); }
    public void eat() { System.out.println("Dog eats food"); } // 需自行实现
}
class Bird implements Animal {
    public void move() { System.out.println("Bird flies"); }
    public void eat() { System.out.println("Bird eats seeds"); } // 重复实现
}
// 统一处理
Animal dog = new Dog();
Animal bird = new Bird();
dog.move(); // Dog runs
dog.eat(); // Dog eats food
bird.move(); // Bird flies
bird.eat(); // Bird eats seeds

总结

  • 接口通过 Animal 接口统一了 move() 和 eat() 的调用方式,实现多态性。
  • 但 eat() 的逻辑在 Dog 和 Bird 中需重复编写,无法像继承那样通过父类复用代码。
  • 要复用代码,需结合组合,将 eat() 的实现提取到单独类中,通过组合注入。

 

最后看下最后一句

将组合和接口结合起来替代继承,就既可以统一处理,又可以复用代码了。

含义

  • 组合+接口:通过接口定义统一的行为契约(方法签名),用组合将具体行为的实现注入到类中。
  • 统一处理接口确保不同类实现相同的方法名,允许用接口类型统一调用(多态性)。
  • 复用代码组合通过包含可重用的行为类实例,将实现代码抽取出来,避免重复编写
  • 替代继承:结合两者,既有继承的多态性(统一处理),又有组合的灵活性和代码复用,克服了继承的紧耦合问题。

道理

  • 继承通过父类提供统一接口和代码复用,但耦合度高、扩展性差。
  • 接口提供统一处理但无代码实现,组合提供代码复用但无统一接口。
  • 组合+接口通过接口保证一致性,通过组合复用行为实现,灵活且模块化。

代码示例

继承(统一处理+代码复用,但耦合)

abstract class Animal {
    abstract void move();
    void eat() { System.out.println("Eating food"); } // 复用
}
class Dog extends Animal {
    void move() { System.out.println("Dog runs"); }
}
class Bird extends Animal {
    void move() { System.out.println("Bird flies"); }
}
// 统一处理 + 复用 eat()
Animal dog = new Dog();
Animal bird = new Bird();
dog.move(); // Dog runs
dog.eat(); // Eating food
bird.move(); // Bird flies
bird.eat(); // Eating food

组合+接口(统一处理+代码复用,灵活):

interface Animal {
    void move();
    void eat();
}
// 行为实现(可复用)
class RunMove { public void move() { System.out.println("Running"); } }
class FlyMove { public void move() { System.out.println("Flying"); } }
class EatBehavior { public void eat() { System.out.println("Eating food"); } }
// 动物类(组合行为)
class Dog implements Animal {
    RunMove move = new RunMove();
    EatBehavior eat = new EatBehavior();
    public void move() { move.move(); }
    public void eat() { eat.eat(); }
}
class Bird implements Animal {
    FlyMove move = new FlyMove();
    EatBehavior eat = new EatBehavior();
    public void move() { move.move(); }
    public void eat() { eat.eat(); }
}
// 统一处理
Animal dog = new Dog();
Animal bird = new Bird();
dog.move(); // Running
dog.eat(); // Eating food
bird.move(); // Flying
bird.eat(); // Eating food

个人体会:

接口能够实现统一处理:也就是如上代码的 dog.move(), bird.move() 名称一致,可以这样理解

组合能够实现代码复用:即如果还要写其他Class的动物(比如Cat),需要增加RunMove时,不需要重写一遍代码,直接new 一个 RunMove()作为Cat的属性即可

接口+组合:两个结合起来用,既可以复用代码,又可以统一处理,还不用担心破坏封装。

class Catimplements Animal {
    RunMove move = new RunMove();
    EatBehavior eat = new EatBehavior();
    public void move() { move.move(); }
    public void eat() { eat.eat(); }
}

总结

  • 接口:Animal 接口定义 move() 和 eat(),确保 Dog 和 Bird 能用同一类型统一调用。
  • 组合:EatBehavior 类的实例被 Dog 和 Bird 共享,复用 eat() 的实现;RunMove 和 FlyMove 分别实现不同 move() 行为。
  • 优势:比继承更灵活(可动态更换行为),同时实现统一处理(接口)和代码复用(组合)。

 

 2、在Java中,根据定义的位置和方式不同,主要有如下4种内部类,分别该如何理解?

静态内部类。

成员内部类。

方法内部类。

匿名内部类。

1. 静态内部类 (Static Inner Class)

概念
静态内部类是定义在另一个类内部并使用 static 关键字修饰的类。它与外部类的实例无关,可以直接通过外部类名访问。

通俗解释
就像一个“独立”的内部小助手,虽然住在外部类里,但不需要外部类的对象就能工作。它可以访问外部类的静态成员,但不能直接访问非静态成员。

举例

public class OuterClass {
    static class StaticInnerClass {
        void show() {
            System.out.println("我是静态内部类");
        }
    }

    public static void main(String[] args) {
        // 直接通过外部类名创建
        OuterClass.StaticInnerClass inner = new OuterClass.StaticInnerClass();
        inner.show();
    }
}

重点:静态内部类无需外部类实例,常用于工具类或单例模式。

 

2. 成员内部类 (Member Inner Class)

概念
成员内部类是定义在外部类中但不使用 static 修饰的类。它依赖外部类的实例,必须先创建外部类对象才能创建内部类对象。

通俗解释
像外部类的“私人秘书”,必须依附于外部类的对象存在。可以直接访问外部类的所有成员(包括私有)。

举例

public class OuterClass {
    private String name = "外部类";

    class MemberInnerClass {
        void show() {
            System.out.println("访问外部类字段: " + name);
        }
    }

    public static void main(String[] args) {
        OuterClass outer = new OuterClass();
        OuterClass.MemberInnerClass inner = outer.new MemberInnerClass();
        inner.show();
    }
}

重点:需要外部类实例来创建,适合需要紧密关联外部类的情况。

 

3. 方法内部类 (Local Inner Class)

概念
方法内部类是定义在方法或代码块中的类,仅在定义它的方法或块内有效,且只能在方法内使用。

通俗解释
像方法里的“临时工”,只在方法执行时干活,方法结束就消失。可以访问外部类的成员和方法的局部变量(如果是 final 或 effectively final)。

举例

public class OuterClass {
    void outerMethod() {
        class LocalInnerClass {
            void show() {
                System.out.println("我是方法内部类");
            }
        }
        LocalInnerClass inner = new LocalInnerClass();
        inner.show();
    }

    public static void main(String[] args) {
        OuterClass outer = new OuterClass();
        outer.outerMethod();
    }
}

重点作用域局限在方法内,适合临时逻辑封装。


4. 匿名内部类 (Anonymous Inner Class)

比另外几种稍难理解些,主要要抓住“匿名”两个字,并且理解他的独特的语法,最后培养成直觉即可。

概念
匿名内部类是没有名字的内部类,通常用于实现接口或继承抽象类,一次性使用,常与事件监听或回调结合。

通俗解释
像一个“一次性快递员”,用完即丢,通常用来快速实现某个接口或抽象类的功能,代码简洁但不可复用。

举例:

public class OuterClass {
    interface MyInterface {
        void doSomething();
    }

    public static void main(String[] args) {
        MyInterface instance = new MyInterface() {
            @Override
            public void doSomething() {
                System.out.println("我是匿名内部类");
            }
        };
        instance.doSomething();
    }
}

1. “匿名”体现在哪里?

“匿名”是指这个类没有名字

在你的例子中:

MyInterface instance = new MyInterface() {
    @Override
    public void doSomething() {
        System.out.println("我是匿名内部类");
    }
};
  • new MyInterface() { ... } 这段代码创建了一个实现了 MyInterface 接口的对象。
  • 大括号 { ... } 里的内容实际上定义了一个类,但这个类没有显式命名(不像 class MyClass implements MyInterface 这样给类取个名字)。
  • 因为没有名字,这个类被称为“匿名内部类”。它在定义的同时直接创建了对象,用完即丢,无法复用。

通俗理解
就像你临时雇了个“无名氏”来完成一项任务(实现 doSomething() 方法),干完活就走人,你不会给这个“无名氏”取名字,也不会重复使用。

 

总结对比

 
内部类类型是否依赖外部类实例可访问的外部类成员典型使用场景
静态内部类 静态成员 工具类、单例
成员内部类 所有成员 强关联逻辑
方法内部类 所有成员 + 方法内 final 变量 临时逻辑
匿名内部类 所有成员 一次性回调、事件处理

 

 

 

 

 

 

posted @ 2025-07-02 12:02  AlphaGeek  阅读(8)  评论(0)    收藏  举报