目录
一.关键字final
1.修饰变量
2.修饰方法
3.修饰类
二.继承与组合
三.多态
1.方法重写
2.方法重载(严格上来说非多态)
3.向上转型
4.向下转型
5.向上向下转型综合例子
四.重载和重写的区别
一.关键字final
在 Java 中,final关键字是一个修饰符,可以用于 变量、方法 和 类,其主要作用是限制修改。
1.修饰变量
(1)基本数据类型
final
修饰的基本数据类型的变量一旦被初始化,其值就不能再更改。
java">final int number = 10;
// number = 20; // 编译错误,无法更改
(2)引用类型
final
修饰引用类型变量后,引用本身不能更改(即不能指向新的对象),但对象的内容仍然可以修改。
java">final StringBuilder builder = new StringBuilder("Hello");
builder.append(" World"); // 可以修改对象内容
// builder = new StringBuilder("Hi"); // 编译错误,不能更改引用
2.修饰方法
final
修饰的方法不能被子类重写,但可以被继承和调用。
java">class Parent {public final void show() {System.out.println("This is a final method.");}
}class Child extends Parent {// @Override// public void show() { } // 编译错误,无法重写
}
3.修饰类
final
修饰的类不能被继承,因此所有的方法都被隐式地认为是final
的。
java">final class FinalClass {public void display() {System.out.println("This is a final class.");}
}// class SubClass extends FinalClass { } // 编译错误,无法继承
二.继承与组合
对于继承
继承是一种某某“是一个 (is-a)”某某的关系,子类通过继承父类,自动拥有父类的属性和方法,可以重写(override)父类的方法,也可以扩展新的功能。
对于组合
组合是一种某某“有一个 (has-a)”某某的关系,表示一个对象包含另一个对象作为其成员。通过组合,可以在一个类中直接调用另一个类的方法来实现功能。
举一个汽车例子,解释一下组合:
java">// 引擎类 Engine
class Engine {public void start() {System.out.println("Engine is starting.");}
}// 汽车类 Car 包含一个 Engine 实例(组合关系)
class Car {private Engine engine;// 通过构造器注入一个 Engine 实例public Car(Engine engine) {this.engine = engine;}public void startCar() {engine.start(); // 调用 Engine 的 start 方法System.out.println("Car is starting.");}
}public class CompositionExample {public static void main(String[] args) {Engine engine = new Engine(); // 创建 Engine 实例Car car = new Car(engine); // 将 Engine 组合到 Car 中car.startCar(); // 启动汽车}
}
三.多态
多态(Polymorphism),来源于希腊语,意为“多种形式”。在编程中,多态允许同一个方法在不同的上下文中表现出不同的行为。简单来说,同一接口,不同实现。
对于实现多态的条件,必须满足,缺一不可:
1. 必须在继承体系下
2. 子类必须要对父类中方法进行重写
3. 通过父类的引用调用重写的方法
原因:
没有继承,无法重写:
- 如果没有继承,子类和父类不存在关联,也就无法实现方法的动态绑定。
没有重写,行为无法变化:
- 如果子类没有对父类的方法进行重写,那么调用父类的引用时,执行的永远是父类的方法,体现不出多态的动态特性。
不通过父类引用,无法体现多态:
- 如果直接用子类引用调用方法,这只是普通的调用,不是多态。
1.方法重写
重写(override):也称为覆盖。重写是子类对父类非静态、非private修饰,非final修饰,非构造方法等的实现过程 进行重新编写, 返回值和形参都不能改变。即外壳不变,核心重写!
举个例子:
java">class Animal {void sound() {System.out.println("动物发出声音~");}
}class Dog extends Animal {@Overridevoid sound() {System.out.println("汪汪叫~");}
}class Cat extends Animal {@Overridevoid sound() {System.out.println("喵喵叫~");}
}public class Main {public static void main(String[] args) {Animal animal;animal = new Dog(); // 父类引用指向子类对象animal.sound(); // 输出: 汪汪叫~animal = new Cat();animal.sound(); // 输出: 喵喵叫~}
}
(1)注意事项
1.子类重写的方法的方法名,参数列表(类型和顺序)必须与父类方法一致。
2.返回类型可以相同或是父类返回类型的子类型
3.子类的重写方法的访问权限不能低于父类的访问权限。例如,如果父类的方法是
public
,子类的方法也必须是public
,不能改为protected
或private
。4.使用
@Override
注解(建议),不写也不会有报错。但使用@Override
注解可以帮助编译器检查是否正确进行了重写5.父类中使用
final
修饰的方法不能被子类重写。6.静态方法属于类本身,不属于对象,不能被重写。子类中的同名静态方法只是隐藏了父类的静态方法,不能称为重写。
7.构造方法是为初始化类而设计的,不继承自父类,因此不能被重写。
8.重写只适用于实例方法,不适用于类变量、类方法(静态方法)或实例变量。
9.通过父类的引用调用被重写的方法时,执行的是子类的实现(动态绑定)。
对于注意事项的第2项,举个例子:
java">class Parent {Number getValue() {return 10;}
}class Child extends Parent {@OverrideInteger getValue() { // Integer 是 Number 的子类return 20;}
}
对于注意事项的第6项,举个例子:
java">class Parent {static void show() {System.out.println("Parent static method");}
}class Child extends Parent {static void show() {System.out.println("Child static method");}
}public class Main {public static void main(String[] args) {Parent.show(); // 输出: Parent static methodChild.show(); // 输出: Child static method}
}
2.方法重载(严格上来说非多态)
方法重载(Overloading)是指在同一个类中,定义多个方法,它们具有相同的名称,但参数列表不同(参数类型、数量或顺序不同)。
下面举几个例子:
(1).通过参数类型重载
java">class Calculator {int add(int a, int b) {return a + b;}double add(double a, double b) {return a + b;}
}public class Main {public static void main(String[] args) {Calculator calc = new Calculator();System.out.println(calc.add(5, 10)); // 调用第一个 addSystem.out.println(calc.add(5.5, 10.2)); // 调用第二个 add}
}
(2).通过参数个数重载
java">class Greeting {void greet() {System.out.println("Hello!");}void greet(String name) {System.out.println("Hello, " + name + "!");}
}public class Main {public static void main(String[] args) {Greeting g = new Greeting();g.greet(); // 输出: Hello!g.greet("Alice"); // 输出: Hello, Alice!}
}
(3).通过参数顺序重载
java">class Display {void show(int a, String b) {System.out.println(a + " " + b);}void show(String b, int a) {System.out.println(b + " " + a);}
}public class Main {public static void main(String[] args) {Display d = new Display();d.show(10, "Test"); // 输出: 10 Testd.show("Test", 10); // 输出: Test 10}
}
(4).注意事项
1.同一类中方法名必须相同:重载的方法属于同一个类,方法名相同但参数列表不同。
2.参数列表必须不同:参数类型、参数的个数或参数的顺序至少有一个不同。
3.返回类型可以相同或不同:但仅靠返回类型的不同不能区分方法。
4.与访问修饰符无关:访问权限修饰符(如
public
、private
)可以不同,但它们不会影响重载的识别。5.与抛出的异常无关:重载方法可以声明不同的异常,但编译器不会依据异常区分方法。
6.构造方法也可以重载:在 Java 中,构造方法支持重载,可以通过不同的参数列表初始化对象。
7.与方法的修饰符无关:重载与方法是否为
static
或final
无关。
3.向上转型
向上转型是将子类对象的引用赋值给其父类类型的引用。
特点:
- 自动完成:编译器会自动执行向上转型。
- 使用父类引用:通过父类引用只能调用父类中声明的方法或属性,但这些方法会在运行时绑定到实际对象(子类)的实现。
- 实现多态:向上转型是多态的基础,可以统一对父类或接口的操作。
举个例子:
java">class Parent {void show() {System.out.println("父类show方法");}
}class Child extends Parent {@Overridevoid show() {System.out.println("子类show方法");}void display() {System.out.println("子类display方法");}
}public class Main {public static void main(String[] args) {Parent obj = new Child(); // 向上转型obj.show(); // 输出: 子类show方法// obj.display(); // 编译错误: 父类引用无法访问子类特有的方法}
}
4.向下转型
向下转型是将父类类型的引用强制转换为子类类型的引用。这需要显式地进行类型转换,并且可能会抛出
ClassCastException
。
特点
- 强制转换:必须显式使用强制类型转换语法
(Child)
。- 运行时检查:向下转型在运行时会验证对象是否是目标类型。如果父类引用实际上不指向目标子类类型的对象,会抛出异常。
- 恢复子类功能:向下转型后可以访问子类的特有方法和属性。
举个例子:
java">class Parent {void show() {System.out.println("父类show方法");}
}class Child extends Parent {@Overridevoid show() {System.out.println("子类show方法");}void display() {System.out.println("子类display方法");}
}public class Main {public static void main(String[] args) {Parent obj = new Child(); // 向上转型obj.show(); // 输出: 子类show方法// 向下转型if (obj instanceof Child) {Child childObj = (Child) obj; // 向下转型childObj.display(); // 输出: 子类display方法}}
}
5.向上向下转型综合例子
java">public class Animal {public Animal(String name, int age) {this.name = name;this.age = age;}String name;int age;public void eat(){System.out.println(name+"正在吃饭");}public void sleep(){System.out.println(name+"正在睡觉");}public void sound(){System.out.println(name+"正在叫");}
}
-------------------------------------------------
public class Cat extends Animal{public Cat(String name, int age) {super(name, age);}@Overridepublic void eat() {System.out.println(name+"正在吃喵粮");}@Overridepublic void sleep() {System.out.println(name+"正在猫窝睡觉");}@Overridepublic void sound() {System.out.println(name+"正在喵喵叫");}
}
--------------------------------------------------
public class Dog extends Animal{public Dog(String name, int age) {super(name, age);}@Overridepublic void eat() {System.out.println(name+"正在吃狗粮");}@Overridepublic void sleep() {System.out.println(name+"正在狗窝睡觉");}@Overridepublic void sound() {System.out.println(name+"正在发出狗叫");}public void run(){System.out.println(name+"正在跑步");}}
--------------------------------------------------
//在向上转型后,可以访问子类重写的方法,这是因为在 Java 中调用方法时采用的是*
// *动态绑定(Dynamic Binding)**的机制。动态绑定意味着在程序运行
// 时,JVM 会根据对象的实际类型来决定调用哪个方法,而不是根据引用的类型。这正
// 是多态的核心。public class TestAnimal {//2. 方法传参:形参为父类型引用,可以接收任意子类的对象public static void eatfood(Animal a){a.eat();}//作返回值:返回任意子类对象public static Animal buyAnimal(String var){if("狗".equals(var)){return new Dog("狗子",1);}else if("猫".equals(var)){return new Cat("咪咪",2);}else{return null;}}public static void main(String[] args) {// //向上转型//animal是一个 Animal 类型的引用,指向一个 Dog 类型的对象。Animal animal=new Dog("小七",2);animal.sound();
// animal.run(); //报错:向上转型后,只能访问父类中定义的方法和变量,而不能直接访问子类中特有的方法和变量。Animal cat=new Cat("元宝",1);cat.sound();eatfood(animal);eatfood(cat);Animal animal1=buyAnimal("狗");animal1.sleep();Animal animal2=buyAnimal("猫");animal2.sound();System.out.println("-----------------------");//向下转型//instanceof关键字检查后,可以确认它是 Dog 类型if(animal instanceof Dog){//(Dog) myAnimal 是将 myAnimal 转换为 Dog 类型的引用Dog myDog=(Dog)animal;//这样就可以执行子类专有的方法myDog.run();}
// myDog.run();//报错:直接写在外面会报错,因为在 if 外面 myDog 变量并不可见。myD
// og 是在 if 语句的代码块中声明的局部变量,所以它的作用范围仅限于 if 代码块内部,一旦
// 离开 if 代码块,myDog 就无法访问了。//可以这样写:// Dog myDog = null; // 提前声明 myDog
// if (animal instanceof Dog) {
// myDog = (Dog) animal; // 向下转型
// }
//
// if (myDog != null) {
// myDog.run(); // 现在可以在 if 外调用 myDog.run()
// }// 程序可以通过编程,但运行时抛出异常---因为:animal实际指向的是狗// 现在要强制还原为猫,无法正常还原,运行时抛出:ClassCastExceptioncat = (Cat)animal;}
}
四.重载和重写的区别
特性 | 重载(Overloading) | 重写(Overriding) |
---|---|---|
定义 | 方法名相同,参数列表不同 | 子类方法与父类方法具有相同签名 |
是否依赖继承 | 否 | 是 |
参数列表 | 必须不同 | 必须相同 |
返回类型 | 可以相同或不同 | 可以相同或是父类返回类型的子类型 |
访问修饰符 | 无要求 | 子类修饰符不能更严格 |
适用于静态方法 | 可以 | 不适用(只能隐藏) |
抛出异常 | 无要求 | 子类异常不能比父类更广 |