文章目录
多态(Polymorphism)
概述
多态指同一个对象在不同情境下的多种表现形式。它分为对象的多态性和类型的多态性。
-
对象的多态性:同一个对象可以被不同类型的引用指向,从而在不同情境下以不同方式使用。这体现了一个对象能够通过不同的名称和描述来表现自己。
-
类型的多态性:同一类型的引用可以指向该类型的不同子类对象。在不同的情境下,可以使用同一个类型的引用来操作不同的对象实例。
- 本质:在不同场景中,同一个方法调用可以有不同的实现。
多态的前提条件
- 继承/实现关系:必须存在类的继承或接口的实现关系。
- 方法重写:子类需要对父类的方法进行重写。
- 父类引用指向子类对象:通过使用父类类型的引用来指向子类的实例。
代码示例
public class Simple01 {
public static void main(String[] args) {
Animal a = new Cat(); // 父类引用指向子类对象
a.speak(); // 调用的是 Cat 类的 speak 方法
}
}
class Animal {
public void speak() {
System.out.println("动物发声");
}
}
class Cat extends Animal {
@Override
public void speak() {
System.out.println("喵喵喵喵喵");
}
}
多态中成员变量的访问特点
- 编译时和运行时都看引用类型(左边)。
- 编译时检查引用类型中是否有相应的成员变量,若有则编译成功,否则失败。
- 运行时访问的也是引用类型中的成员变量。
代码示例
public class Simple02 {
public static void main(String[] args) {
Animal a = new Cat();
System.out.println(a.age); // 访问的是父类的 age
}
}
class Animal {
int age = 2;
}
class Cat extends Animal {
int age = 3; // 不会被访问到
String color = "black";
}
多态中成员方法的访问特点
- 编译看左边(引用类型),运行看右边(实际对象类型)。
- 编译时检查引用类型中是否有相应的方法声明,若有则编译成功,否则失败。
- 运行时调用实际对象所属类中重写的方法。
代码示例
public class Simple03 {
public static void main(String[] args) {
Animal3 a = new Cat3();
a.speak(); // 调用的是 Cat3 类的 speak 方法
}
}
class Animal3 {
public void speak() {
System.out.println("动物发声");
}
}
class Cat3 extends Animal3 {
@Override
public void speak() {
System.out.println("喵喵");
}
public void catchMouse() {
System.out.println("抓老鼠");
}
}
向上转型与向下转型
-
向上转型(Upcasting):
- 子类对象赋值给父类引用。
- 访问范围被限制为父类的定义。
-
向下转型(Downcasting):
- 父类引用强制转换为子类引用。
- 允许访问子类特有的成员。
代码示例
public class Simple05 {
public static void main(String[] args) {
Man m = new SuperMan(); // 向上转型
m.dealBusiness(); // 调用的是 SuperMan 重写的方法
SuperMan sm = (SuperMan) m; // 向下转型
sm.fly(); // 访问子类的特有方法
}
}
class Man {
String name = "Jack";
public void dealBusiness() {
System.out.println("普通的谈生意");
}
}
class SuperMan extends Man {
String name = "James";
@Override
public void dealBusiness() {
System.out.println("几个亿的大生意");
}
public void fly() {
System.out.println("到处飞着救人");
}
}
多态的好处
- 提高代码可扩展性:无需修改已有代码即可添加新功能。
- 灵活的参数传递:方法参数使用父类类型,可以接收任何子类实例。
- 广泛的对象来源:不仅限于实例化创建的对象,还可通过反射、文件读取等方式获取对象。
代码示例
public class Simple06 {
public static void main(String[] args) {
JuiceMachine jm = new JuiceMachine();
jm.makeJuice(new Apple()); // 调用 Apple 的 flow 方法
}
}
class JuiceMachine {
public void makeJuice(Fruit f) {
f.flow(); // 多态机制
}
}
class Fruit {
public void flow() {}
}
class Apple extends Fruit {
@Override
public void flow() {
System.out.println("流出苹果汁");
}
}
class Orange extends Fruit {
@Override
public void flow() {
System.out.println("流出橙汁");
}
}
抽象类(Abstract Class)
概述
- 定义:抽象类是包含抽象方法的类,无法直接实例化。
- 使用场景:用来捕获子类的通用特征和行为,实现代码复用。
抽象类的定义格式
abstract class 类名 {
// 抽象方法和非抽象方法
}
代码示例
abstract class Animal {
public abstract void eat();
}
class Dog extends Animal {
@Override
public void eat() {
System.out.println("狗吃肉");
}
}
class Cat extends Animal {
@Override
public void eat() {
System.out.println("猫吃鱼");
}
}
抽象方法
- 定义:没有方法体的声明方式,只提供方法名和参数,没有具体实现。
- 用法:子类必须实现所有抽象方法,否则子类也必须声明为抽象类。
代码示例
abstract class Animal2 {
public abstract void eat(); // 抽象方法
}
class Dog2 extends Animal2 {
@Override
public void eat() {
System.out.println("狗吃肉");
}
}
抽象类的特点
- 必须使用
abstract
关键字:用于标记类和方法。 - 抽象方法和抽象类关系:抽象方法必须在抽象类中,抽象类不一定要有抽象方法。
- 不能直接实例化:需要通过具体子类来创建对象。
- 子类可以是抽象类:如果子类没有实现所有抽象方法,子类仍然需要声明为抽象类。
抽象类成员的特点
- 成员变量:可以定义普通变量和常量,但不能是抽象变量。
- 构造方法:抽象类可以有构造方法,用于子类实例化时初始化操作。
- 成员方法:可以是抽象方法,也可以是具体方法,以提高代码复用。
代码示例
abstract class Fu {
int i = 10;
final int j = 20;
public abstract void test();
public Fu() {
i = 100;
}
}
class Zi extends Fu {
@Override
public void test() {
System.out.println("Zi:" + i);
}
}
接口(Interface)
概述
- 定义:接口是一组方法规范的集合,所有方法都必须由实现类实现。
- 作用:分离方法定义和实现,降低代码耦合性。
接口的特点
- 定义接口:使用
interface
关键字,接口中只能定义抽象方法。 - 实现接口:类使用
implements
关键字来实现接口中的方法。 - 接口不能实例化:必须通过实现类来创建对象。
- 接口的实现类可以是普通类或抽象类:如果是普通类,必须实现所有接口方法;如果是抽象类,可以不实现接口方法。
代码示例
interface MyInterA {
void test1(); // 接口中的方法默认为 public abstract
}
class MyInterImplB implements MyInterA {
@Override
public void test1() {
System.out.println("MyImplB");
}
}
接口中成员的特点
- 成员变量
:只能定义常量,默认修饰符为public static final
。
2. 构造方法:接口中不能有构造方法。
3. 成员方法:默认修饰符为public abstract
。
接口中成员示例
interface MyInterface {
int NUM = 10; // 常量,默认 public static final
void test(); // 抽象方法,默认 public abstract
}
接口与抽象类的区别
- 设计理念:接口用于定义行为标准,抽象类用于提供基本功能。
- 成员特点:
- 抽象类可以有非抽象方法和成员变量。
- 接口只能定义常量和抽象方法。
代码示例
abstract class Animal4 {
public abstract void eat();
}
interface Flyable {
void fly();
}
class Bird extends Animal4 implements Flyable {
@Override
public void eat() {
System.out.println("鸟吃虫子");
}
@Override
public void fly() {
System.out.println("鸟在飞");
}
}
内部类(Inner Class)
概述
内部类是定义在另一个类内部的类,可以分为成员内部类和局部内部类。
- 成员内部类:与成员变量定义在同一位置,属于类的成员。
- 局部内部类:定义在方法或代码块内部,只能在其作用域内使用。
代码示例
class Outer {
private int num = 10;
// 成员内部类
class Inner {
public void show() {
System.out.println(num);
}
}
// 局部内部类
public void method() {
class InnerLocal {
public void display() {
System.out.println(num);
}
}
InnerLocal il = new InnerLocal();
il.display();
}
}
使用内部类的注意事项
- 访问外部类成员:内部类可以直接访问外部类的成员,甚至是私有成员。
- 创建内部类对象:需要通过外部类对象来创建内部类对象。
代码示例
public class Simple08 {
public static void main(String[] args) {
Outer.Inner oi = new Outer().new Inner();
oi.show();
}
}
这份文档旨在以更清晰和更结构化的方式来阐述多态、抽象类、接口和内部类的概念。