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 变量 | 临时逻辑 |
| 匿名内部类 | 是 | 所有成员 | 一次性回调、事件处理 |

浙公网安备 33010602011771号