Java继承与多态:深入理解继承、组合和多态的精髓!
引言
欢迎来到这篇关于Java继承与多态的博客!在Java编程中,继承与多态是两个非常重要的概念,它们为我们构建灵活而高效的代码提供了强大的支持。本文将深入探讨Java继承与多态的原理、语法和实践,帮助读者更好地理解和应用这两个概念。
1. 继承
概念
继承(inheritance)机制:是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加新功能,这样产生新的类,称派生类。继承呈现了面向对象程序设计的层次结构, 体现了由简单到复杂的认知过程。继承主要解决的问题是:共性的抽取,实现代码复用。
语法
在Java中,使用extends关键字可以创建一个类的子类(派生类),子类会继承父类(基类)的成员变量和方法。
class Animal{
String name;
int age;
public void eat(){
System.out.println(name + "正在吃饭");
}
public void sleep(){
System.out.println(name + "正在睡觉");
}
}
class Dog extends Animal{
void bark(){
System.out.println(name + "汪汪汪~~~");
}
}
public class TestExtend {
public static void main(String[] args) {
Dog dog = new Dog();
// dog类中并没有定义任何成员变量,name和age属性肯定是从父类Animal中继承下来的
System.out.println(dog.name);
System.out.println(dog.age);
// dog访问的eat()和sleep()方法也是从Animal中继承下来的
dog.eat();
dog.sleep();
dog.bark();
}
}
public class Base {
int a;
int b;
}
public class Derived extends Base{
int c;
public void method(){
a = 10; // 访问从父类中继承下来的a
b = 20; // 访问从父类中继承下来的b
c = 30; // 访问子类自己的c
}
}
父类成员访问
子类可以访问父类的公有成员变量和方法,但不能访问父类的私有成员变量和方法。
class ParentClass {
public int publicVar;
private int privateVar;
public void publicMethod() {
System.out.println("Public method");
}
private void privateMethod() {
System.out.println("Private method");
}
}
class ChildClass extends ParentClass {
void accessParentMembers() {
publicVar = 10; // 可以访问父类的公有成员变量
publicMethod(); // 可以调用父类的公有方法
// privateVar = 20; // 无法访问父类的私有成员变量
// privateMethod(); // 无法调用父类的私有方法
}
}
子类中访问父类的成员方法
总结:
- 通过子类对象访问父类与子类中不同名方法时,优先在子类中找,找到则访问,否则在父类中找,找到
则访问,否则编译报错。 - 通过派生类对象访问父类与子类同名方法时,如果父类和子类同名方法的参数列表不同(重载),根据调用方法适传递的参数选择合适的方法访问,如果没有则报错;
super关键字
在子类方法中,如果想要明确访问父类中成员时,借助super关键字即可。该关键字主要作用:在子类方法中访问父类的成员。
class Base {
public int a = 1;
public int b = 2;
}
class Derived extends Base {
public int a = 100;
public int c = 3;
public void test() {
System.out.println(super.a);
System.out.println(this.a);
System.out.println(this.b);
System.out.println(super.b);
System.out.println(this.c);
}
}
public class Test3 {
public static void main(String[] args) {
Derived derived = new Derived();
derived.test();
}
}
子类构造方法
父子父子,先有父再有子,即:子类对象构造时,需要先调用基类构造方法,然后执行子类的构造方法。
public class Base {
public Base(){
System.out.println("Base()");
}
}
public class Derived extends Base{
public Derived(){
// super(); // 注意子类构造方法中默认会调用基类的无参构造方法:super(),
// 用户没有写时,编译器会自动添加,而且super()必须是子类构造方法中第一条语句,
// 并且只能出现一次
System.out.println("Derived()");
}
}
public class Test {
public static void main(String[] args) {
Derived d = new Derived();
}
}
结果打印:
Base()
Derived()
super和this
super:可以当作一个关键字,可以访问父类继承内容。
this:代表父类对象引用。
再谈初始化
class Person {
public String name;
public int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
System.out.println("Person:构造方法执行");
}
{
System.out.println("Person:实例代码块执行");
}
static {
System.out.println("Person:静态代码块执行");
}
}
class Student extends Person{
public Student(String name,int age) {
super(name,age);
System.out.println("Student:构造方法执行");
}
{
System.out.println("Student:实例代码块执行");
}
static {
System.out.println("Student:静态代码块执行");
}
}
public class TestDemo4 {
public static void main(String[] args) {
Student student1 = new Student("张三",19);
System.out.println("===========================");
Student student2 = new Student("gaobo",20);
}
public static void main1(String[] args) {
Person person1 = new Person("bit",10);
System.out.println("============================");
Person person2 = new Person("gaobo",20);
}
}
结论:
- 父类静态代码块优先于子类静态代码块执行,且是最早执行
- 父类实例代码块和父类构造方法紧接着执行
- 子类的实例代码块和子类构造方法紧接着再执行
- 第二次实例化子类对象时,父类和子类的静态代码块都将不会再执行
protected 关键字
//Test.java
public class Test {
public int a;
protected int c = 99;
public void func() {
System.out.println(c);
}
}
//TestProtected.java
public class TestProtected {
public static void main(String[] args) {
Test test = new Test();
System.out.println(test.c);
}
}
//TestDemo.java
public class TestDemo extends Test{
public void test() {
System.out.println(super.c);
}
public static void main(String[] args) {
Test test = new Test();
///System.out.println(test.c);
//System.out.println(super.c);
}
}
结论:在不同包的子类中,可以用super进行调用
final 关键字
final关键可以用来修饰变量、成员方法以及类。
- 修饰变量或字段,表示常量(即不能修改)
final int a = 10;
a = 20; // 编译出错
- 修饰类:表示此类不能被继承
final public class Animal {
...
}
public class Bird extends Animal {
...
}
// 编译出错
Error:(3, 27) java: 无法从最终com.bit.Animal进行继承
- 修饰方法:表示该方法不能被重写(后序介绍)
2. 多态
概念
通俗来说,就是多种形态,具体点就是去完成某个行为,当不同的对象去完成时会产生出不同 的状态。
多态实现条件
在java中要实现多态,必须要满足如下几个条件,缺一不可:
- 必须在继承体系下
- 子类必须要对父类中方法进行重写
- 通过父类的引用调用重写的方法
多态体现:在代码运行时,当传递不同类对象时,会调用对应类中的方法。
class Animal {
void sound() {
System.out.println("Animal makes sound");
}
}
class Dog extends Animal {
void sound() {
System.out.println("Dog barks");
}
}
class Cat extends Animal {
void sound() {
System.out.println("Cat meows");
}
}
class Main {
public static void main(String[] args) {
Animal dog = new Dog();
Animal cat = new Cat();
dog.sound(); // 输出:Dog barks
cat.sound(); // 输出:Cat meows
}
}
重写(override)
规则:
- 方法名相同
- 方法的参数列表相同(个数,顺序,类型)
- 方法的返回值相同
class ParentClass {
void show() {
System.out.println("Parent class method");
}
}
class ChildClass extends ParentClass {
void show() {
System.out.println("Child class method");
}
}
class Main {
public static void main(String[] args) {
ParentClass obj = new ChildClass();
obj.show(); // 输出:Child class method
}
}
总结:
- 静态方法不能被重写
- 被private修饰的方法不能被重写
- 被final修饰的方法不能被重写
- 如果方法被重写,那么子类权限>=父类权限
private<包访问权限<protected<public
动态绑定
class B {
public B() {
func();
}
public void func() {
System.out.println("B::FUNC()!");
}
}
class D extends B {
private int num = 1;
public void func() {
System.out.println("D::FUNC()!" + num);
}
}
public class Test3 {
public static void main(String[] args) {
D d = new D();
}
}
向上转型
向上转型:实际就是创建一个子类对象,将其当成父类对象来使用。
语法格式:父类类型 对象名 = new 子类类型()
Animal animal = new Cat("元宝",2)
animal是父类类型,但可以引用一个子类对象,因为是从小范围向大范围的转换。
【使用场景】
- 直接赋值
- 方法传参
- 方法返回
public class TestAnimal {
// 2. 方法传参:形参为父类型引用,可以接收任意子类的对象
public static void eatFood(Animal a){
a.eat();
}
// 3. 作返回值:返回任意子类对象
public static Animal buyAnimal(String var){
if("狗".equals(var) ){
return new Dog("狗狗",1);
}else if("猫" .equals(var)){
return new Cat("猫猫", 1);
}else{
return null;
}
}
public static void main(String[] args) {
Animal cat = new Cat("元宝",2); // 1. 直接赋值:子类对象赋值给父类对象
Dog dog = new Dog("小七", 1);
eatFood(cat);
eatFood(dog);
Animal animal = buyAnimal("狗");
animal.eat();
animal = buyAnimal("猫");
animal.eat();
}
}
总结:
向上转型的优点:让代码实现更简单灵活。
向上转型的缺陷:不能调用到子类特有的方法。
结尾
通过本文的学习,相信你对Java继承与多态有了更深入的理解和掌握。继承与多态是Java编程中非常重要的概念,可以帮助我们编写更加灵活、可扩展和易维护的代码。希望本文能够帮助你更好地应用继承与多态,提升自己的编程水平。如果有任何疑问或想法,欢迎在评论区分享,让我们一起学习进步!