Java基础知识(9)- Java 面向对象(一)| 继承(Extends)、重写(Override)/重载(Overload)、多态 (Polymorphism)


1. 继承(Extends)

    1) 继承的概念

        Java 中的继承就是在已有类的基础上进行扩展,从而产生新的类。已有的类称为父类、基类或超类,而新产生的类称为子类或派生类。
        
        (1) 继承格式:

            class Human {
            }

            class Male extends Human {
            }

            class Boy extends Male {
            }

            使用 extends 关键字,Boy 继承了 Male,Male 继承了 Human,Human 是 Boy 的间接父类。

        (2) 单继承:

            Java 语言摒弃了 C++ 中难以理解的多继承特征,即 Java 不支持多继承,只允许一个类直接继承另一个类,即子类只能有一个直接父类,extends 关键字后面只能有一个类名。

            一个类只能有一个直接父类,但是它可以有多个间接的父类。

        (3) IS-A 关系:

            上文中的 Human -> Male -> Boy

            IS-A 关系如下:

                Man IS-A Human
                Boy IS-A Male
                因此: Boy IS-A Human

            通过使用 instanceof 关键字,能够确定 Male,Boy 是 Human 类的实例, 也就是 IS-A Human。

                public static void main(String args[]) {
                    Male m = new Male();
                    Boy b = new Boy();
                    System.out.println(m instanceof Human);     // 输出 true
                    System.out.println(b instanceof Human);     // 输出 true
                }


    2) 继承的规则

        (1) 类的继承不改变成员变量和方法的访问权限;    
        (2) 子类不能继承父类的构造方法(构造函数),如果要调用父类的构造方法,可以使用 super 关键字 (super 可以用来访问父类的构造方法、成员变量和方法);
        (3) 子类拥有父类所有成员变量和方法,但在子类中不能直接访问父类中的 private 成员变量和方法(它们在子类中是不可见的);
        (4) 子类可以拥有自己的成员变量和方法,即子类可以对父类进行扩展;
        (5) 子类可以重写父类的方法;
        (6) final 修饰的类不能被继承,即这样的类是最终类;


    3) 继承的优点和缺点

        继承的优点:

            (1) 实现代码共享,减少创建类的工作量,使子类可以拥有父类的成员变量和方法;
            (2) 提高代码维护性和可重用性;
            (3) 提高代码的可扩展性,更好的实现父类的方法;

        继承的缺点:

            (1) 继承是侵入性的。只要继承,就必须拥有父类的成员变量和方法;
            (2) 降低代码灵活性。子类拥有父类的成员变量和方法后多了些约束;
            (3) 增强代码耦合性(开发项目的原则为高内聚低耦合)。当父类的常量、变量和方法被修改时,需要考虑子类的修改,有可能会导致大段的代码需要重构;

    4)this 关键字

        当一个对象创建后,Java虚拟机(JVM)就会给这个对象分配一个引用自身的指针,这个指针的名字就是 this。

        this 关键字的用法:

            (1) 使用 this 来区分当前对象,比如在构造方法(构造函数)和对象实例的方法中,实例变量和局部变量名字冲突时,可以给实例变量加上 this. 前缀;
            (2) 在构造方法中使用 this 来调用对象本身的其他构造方法;
            (3) 返回类的引用。如在代码中,可以使用return this来返回某个类的引用。此时,这个this关键字就代表类的名称;

        不能在 static 方法中使用 this 关键字,this 和 super 都无法出现在 static 修饰的方法中。

        Static 方法是类方法,先于任何的实例(对象)存在。即 Static方法在类加载时就已经存在了,但是对象是在创建时才在内存中生成,而 this 指代的是当前对象。

 

实例:

复制代码
 1 public class App {
 2     public static void main( String[] args ) {
 3         Boy b = new Boy("Boy name");
 4         System.out.println("Boy is an instance of Human: " + (b instanceof Human));
 5 
 6         b.test();
 7     }
 8 }
 9 
10 // final 类不能继承, 即最终类
11 final class Group {
12 
13     public Group() {
14         System.out.println("Group class initialize");
15     }
16 
17 }
18 
19 class Human {
20     private String name;
21     protected String label = "Human label";
22     public int age = 10;
23 
24     // 构造方法(构造函数)
25     public Human(String name) {
26         // this 指向自己的引用
27         this.name = name;
28 
29         System.out.println("Human class initialize");
30     }
31 
32     public String getName() {
33         return this.name;
34     }
35 
36     protected String getName1() {
37         return this.name;
38     }
39 
40     private String getName2() {
41         return this.name;
42     }
43 
44 }
45 
46 class Male extends Human {
47     public Male(String name) {
48         super(name);
49 
50         System.out.println("Male class initialize");
51     }
52 }
53 
54 class Boy extends Male {
55 
56     private Group group = new Group();
57 
58     // 子类不能继承父类的构造方法(构造函数)
59     public Boy(String name) {
60         // 使用 super 调用父类构造方法
61         super(name);
62 
63         System.out.println("Boy class initialize");
64     }
65 
66     public void test() {
67         System.out.println("Can access parent public variable 'age': " + this.age);
68         System.out.println("Can access parent protected variable 'label': " + this.label);
69         System.out.println("Can NOT access parent private variable 'name'");
70         System.out.println("Can access parent public method 'getName()': " + this.getName());
71         System.out.println("Can access parent protected method 'getName1()': " + this.getName1());
72         System.out.println("Can NOT access parent private method 'getName2()'");
73     }
74 }   
复制代码

输出:

        Human class initialize
        Male class initialize
        Group class initialize
        Boy class initialize
        Boy is an instance of Human: true
        Can access parent public variable 'age': 10
        Can access parent protected variable 'label': Human label
        Can NOT access parent private variable 'name'
        Can access parent public method 'getName()': Boy name
        Can access parent protected method 'getName1()': Boy name
        Can NOT access parent private method 'getName2()'


2. 重写(Override)/重载(Overload)

    1)重写(Override)

        在子类中如果创建了一个与父类中相同名称、相同返回值类型、相同参数列表的方法,只是方法体中的实现不同,以实现不同于父类的功能,这种方式被称为方法重写(Override),又称为方法覆盖。

        当父类中的方法无法满足子类需求或子类具有特有功能的时候,需要方法重写。

        下面程序演示了重写:

            class Male extends Human {

                public void display() {
                }

                public static void test() {
                }
            }

            class Boy extends Male {

                @Override
                public void display() {
                }       

                // 父类的 static 方法不能被重写,可以被再次声明和定义
                //@Override
                public static void test() {
                }
            }   

        (1) @Override
        
            是伪代码,表示方法重写, 使用 @Override 标签的好处:

            1) 作为注释,帮助自己检查是否正确的复写了父类中已有的方法;
            2) 便于别人理解代码;
            3) 编译器可以给你验证 @Override 下面的方法名是否是你父类中所有的,如果没有则报错;

        (2) 重写的规则

            a) 参数列表与被重写方法的参数列表必须完全相同;
            b) 返回类型与被重写方法的返回类型可以不相同,但是必须是父类返回值的派生类(java5 及更早版本返回类型要一样,java7 及更高版本可以不同);
            c) 访问权限不能比父类中被重写的方法的访问权限更低。例如:如果父类的一个方法被声明为 public,那么在子类中重写该方法就不能声明为 protected;
            d) 父类的成员方法只能被它的子类重写;
            e) 声明为 final 的方法不能被重写;
            f) 声明为 static 的方法不能被重写,但是能够被再次声明和定义,父类和子类有各自同名的 static 方法;
            g) 子类和父类在同一个包中,那么子类可以重写父类所有方法,除了声明为 private 和 final 的方法;
            h) 子类和父类不在同一个包中,那么子类只能够重写父类的声明为 public 和 protected 的非 final 方法;
            i) 重写的方法能够抛出任何非强制异常,无论被重写的方法是否抛出异常。但是,重写的方法不能抛出新的强制性异常,或者比被重写方法声明的更广泛的强制性异常,反之则可以;
            j) 构造方法不能被重写

 

    2) 重载(Overload)

        重载(Overload) 是在一个类里面,方法名字相同,而参数不同,返回类型可以相同也可以不同。最常用的地方就是构造函数的重载。

        下面程序演示了重写:

            public class Male extends Human {

                @Override
                public void display() {
                }

                // 重载本类的 display()
                public void display(String str) {
                }  
            }   

        (1) 重载的规则

            a) 被重载的方法必须改变参数列表(参数个数或类型不一样);
            b) 被重载的方法可以改变返回类型;
            c) 被重载的方法可以改变访问修饰符;
            d) 被重载的方法可以声明新的或更广的检查异常;
            e) 方法能够在同一个类中或者在一个子类中被重载;
            f) 无法以返回值类型作为重载函数的区分标准;

        (2) 重载与重写的区别

            项目         重载方法    重写方法
            参数列表    必须修改    不能修改
            返回类型    可以修改    不能修改
            异常        可以修改    可以减少或删除,一定不能抛出新的或者更广的异常
            访问        可以修改    不能做更严格的限制(可以降低限制)


实例:

复制代码
 1 public class App {
 2     public static void main( String[] args ) {
 3 
 4         Male m = new Male("Male name");
 5         Boy b = new Boy("Boy name");
 6 
 7         b.display();
 8         b.display("Boy: Overload display(String str)");
 9         b.displayParent();
10 
11         b.show();   // Boy 类的 static show()
12         Male m2 = b;
13         m2.show();  // Male 类的 static show()
14     }
15 }
16 
17 class Human {
18     private String name;
19 
20     public Human(String name) {
21         this.name = name;
22     }
23 
24     public static void show() {
25         // 不能在 static 方法中使用 this 关键字
26         //System.out.println("Human: show() -> name = " + this.name);
27     }
28 }
29 
30 class Male extends Human {
31 
32     public Male(String name) {
33         super(name);
34     }
35 
36     // final 方法不能被子类重写(Override)
37     final public void testFinalFunction() {
38         System.out.println("Male: test final function");
39     }
40 
41     public void display() {
42         System.out.println("Male: display()");
43     }
44 
45     // static show() 方法不能被子类重写(Override)
46     public static void show() {
47         System.out.println("Male: static show()");
48     }
49 }
50 
51 class Boy extends Male {
52 
53     public Boy(String name) {
54         super(name);
55     }
56 
57     public String getName() {
58         return super.getName();
59     }
60 
61     /*
62     // 不能重写(Override)父类的 final 方法
63     @Override
64     public void testFinalFunction() {
65         System.out.println("Boy: test final function");
66     }
67     */
68 
69     // 重写(Override)
70     @Override
71     public void display() {
72         System.out.println("Boy: Override display()");
73     }
74 
75     // 重载(Overload)
76     public void display(String str) {
77         System.out.println(str);
78     }
79 
80     // 使用 super 关键字可以调用父类的被重写方法
81     public void displayParent() {
82         super.display();
83     }
84 
85     // 父类的 static test() 方法不能被重写(Override),可以被再次声明和定义
86     //@Override
87     public static void show() {
88         System.out.println("Boy: static show()");
89     }
90 }
复制代码


输出:

        Boy: Override display()
        Boy: Overload display(String str)
        Male: display()
        Boy: static show()
        Male: static show()

3. 多态 (Polymorphism)

    多态性是面向对象编程的又一个重要特征,它是指在父类中定义的属性和方法被子类继承之后,可以具有不同的数据类型或表现出不同的行为,这使得同一个属性或方法在父类及其各个子类中具有不同的含义。

    对面向对象来说,多态分为编译时多态和运行时多态。其中编译时多态是静态的,主要是指方法的重载,它是根据参数列表的不同来区分不同的方法。
    
    通过编译之后会变成两个不同的方法,在运行时谈不上多态。而运行时多态是动态的,它是通过动态绑定来实现的,也就是大家通常所说的多态性。

    多态存在的三个必要条件:
   
        1) 继承:在多态中必须存在有继承关系的子类和父类。
        2) 重写:子类对父类中某些方法进行重新定义,在调用这些方法时就会调用子类的方法。
        3) 向上转型:在多态中需要将子类的引用赋给父类对象,只有这样该引用才既能可以调用父类的方法,又能调用子类的方法。
    
    虚函数:

        Java 中没有虚函数的概念,它的普通函数就相当于 C++ 的虚函数,动态绑定是 Java 的默认行为。如果 Java 中不希望某个函数具有虚函数特性,可以加上 final 关键字变成非虚函数。


    
    实例:

复制代码
 1     public class App {
 2         public static void main( String[] args ) {
 3 
 4             Human h = new Male(;
 5             h.sayHello();   // 没有调用 Human 的 sayHello,而是调用了 Male 的 sayHello
 6 
 7             h = new Female();
 8             h.sayHello();   // 没有调用 Human 的 sayHello,而是调用了 Female 的 sayHello
 9             
10             h = new Human();
11             h.sayHello();
12         }
13     }
14 
15     class Human {
16         public Human() {
17         }
18 
19         public void sayHello() {
20             System.out.println("Human sayHello()");
21         }
22     }
23 
24     class Male extends Human {
25         public Male() {
26         }
27 
28         public void sayHello() {
29             System.out.println("Male sayHello()");
30         }
31     }
32 
33     class Female extends Human {
34         public Female() {
35         }
36 
37         public void sayHello() {
38             System.out.println("Female sayHello()");
39         }
40     }
复制代码

    输出:

        Male sayHello()
        Female sayHello()
        Human sayHello()

posted @   垄山小站  阅读(360)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全网最简单!3分钟用满血DeepSeek R1开发一款AI智能客服,零代码轻松接入微信、公众号、小程
· .NET 10 首个预览版发布,跨平台开发与性能全面提升
· 《HelloGitHub》第 107 期
· 全程使用 AI 从 0 到 1 写了个小工具
· 从文本到图像:SSE 如何助力 AI 内容实时呈现?(Typescript篇)
点击右上角即可分享
微信分享提示