• 封装, 继承, 多态详解


    面向对象编程有三大特征:封装、继承和多态

    一.封装

    思维导图概览:

    1. 封装的概念

    —— 把抽象出的数据(属性)和对数据的操作(方法)封装到一起,数据被保护在内部,程序的其它部分只有通过被授权的操作(方法),才能对数据进行操作

     

    2. 封装的理解和优势

    1)隐藏实现细节:调用方法时才能传入参数,不能直接进行对数据的改动

    2)可以对数据进行验证,保证安全合理

    3. 封装的实现

    1)将属性进行私有化(private)【不能直接修改属性

    2)提供公共的(public)set/get方法,对数据进行判断并赋值

            a. 提供“set变量名(参数)”方法,用于设置成员变量的值,方法用public修饰

            b. 提供“get变量名()”方法,用于获取成员变量的值,方法用public修饰

    3)提供相应的构造器/构造方法,与set/get方法结合

     

    4.案例分析

    1. class Student {
    2. //成员变量
    3. private String name;
    4. private int age;
    5. //构造方法
    6. public Student() {
    7. }
    8. public Student(String name, int age) {
    9. this.name = name;
    10. this.age = age;
    11. }
    12. //成员方法
    13. public void setName(String name) {
    14. this.name = name;
    15. }
    16. public String getName() {
    17. return name;
    18. }
    19. public void setAge(int age) {
    20. //对数据进行校验
    21. if(age >= 0 && age <= 150) {
    22. this.age = age;
    23. } else {
    24. this.age = 0;
    25. }
    26. }
    27. public int getAge() {
    28. return age;
    29. }
    30. public void show() {
    31. System.out.println(name + "," + age);
    32. }
    33. }
    34. /*
    35. 创建对象并为其成员变量赋值的两种方式
    36. 1:无参构造方法创建对象后使用setXxx()赋值
    37. 2:使用带参构造方法直接创建带有属性值的对象
    38. */
    39. public class StudentDemo {
    40. public static void main(String[] args) {
    41. //无参构造方法创建对象后使用setXxx()赋值
    42. Student s1 = new Student();
    43. s1.setName("林青霞");
    44. s1.setAge(30);
    45. s1.show();
    46. //使用带参构造方法直接创建带有属性值的对象
    47. Student s2 = new Student("林青霞",30);
    48. s2.show();
    49. }
    50. }

      

    二.继承

    思维导图概览:

    1. 为什么需要继承?

    —— 如果两个类有很多相同的属性和方法,这个时候就可以使用继承来提高代码的复用性

     

    2. 继承基本了解和继承示意图

    1)继承的了解:继承可以解决代码复用,当多个类中存在相同的属性和方法时,可以从这些类中抽象出父类,在父类中定义这些相同的属性和方法,所有的子类不需要重新定义这些属性和方法,只需要通过 extends 来声明继承父类即可。

    2)继承的格式

    class Dog extends Animal { }

    3)继承示意图

    3. 继承的基本语法

    1)class 子类 extends 父类 {}

    2)子类就会自动拥有父类定义的属性和方法

    3)父类又叫做超类,基类

    4)子类又叫派生类

    4. 一个小case快递了解继承

    1. public class Student {
    2. public String name;
    3. public int age;
    4. private double score;
    5. public void setScore(double score) {
    6. this.score = score;
    7. }
    8. public double getScore() {
    9. return score;
    10. }
    11. public void show() {
    12. System.out.println("学生名:" + name + " 年龄:" + age + " 成绩:" + score);
    13. }
    14. }
    15. public class Pupil extends Student {
    16. public void testing() {
    17. System.out.println("小学生 " + name + "age = " + age + " 正在考小学数学");
    18. }
    19. }
    20. public class Graduate extends Student {
    21. public void testing() {
    22. System.out.println("大学生" + name + " 年龄为:" + age + " 正在考大学数学");
    23. }
    24. public void print() {
    25. System.out.println("大学生的分数为 " + getScore());
    26. }
    27. }
    28. public class Test {
    29. public static void main(String[] args) {
    30. Pupil pupil = new Pupil();
    31. Graduate graduate = new Graduate();
    32. pupil.name = "小米";
    33. pupil.age = 10;
    34. pupil.testing();
    35. graduate.name = "小强";
    36. graduate.age = 20;
    37. graduate.setScore(80.0);
    38. graduate.testing();
    39. graduate.print();
    40. }
    41. }

     

    5. 继承的优势与劣势

    1)优势

            a. 代码的复用性提高了

            b. 代码的扩展性和维护性提高

    2)劣势

            —— 继承让类与类之间产生了关系,类的耦合性增强了,当父类发生变化时子类实现也不得不跟着变化,削弱了子类的独立性

     

    6. 继承的细节讨论及实现

    1)子类继承了所有的属性和方法,非私有的属性和方法可以在子类中直接访问,但是私有属性和方法的访问,要通过父类提供公共的方法去访问

    1. public class Test1 {
    2. public static void main(String[] args) {
    3. //给A中的私有属性进行赋值
    4. A a = new A();
    5. a.setA(18);
    6. a.setAge(20);
    7. //B继承A后,可访问
    8. B b = new B();
    9. b.getMethod();
    10. System.out.print(b.getA());
    11. System.out.print(b.getAge());
    12. }
    13. }
    14. class A {
    15. public int age;
    16. private int a;
    17. //无参构造器
    18. public A() {
    19. }
    20. //有参构造器
    21. public A(int age, int a) {
    22. this.age = age;
    23. this.a = a;
    24. }
    25. //相关的set/get方法 --> 用来访问私有属性
    26. public int getAge() {
    27. return age;
    28. }
    29. public void setAge(int age) {
    30. this.age = age;
    31. }
    32. public int getA() {
    33. return a;
    34. }
    35. public void setA(int a) {
    36. this.a = a;
    37. }
    38. //私有方法
    39. private void method() {
    40. System.out.println("这是父类的私有方法~~~");
    41. }
    42. //通过公共方法去访问私有方法
    43. public void getMethod() {
    44. method();
    45. }
    46. }
    47. class B extends A {}

    2)子类必须调用父类的构造器,完成父类的初始化(子类中所有的构造方法默认都会访问父类中无参的构造方法)

    3)当创建子类对象时,不管使用子类的那个构造器,默认情况下总会去调用父类的无参构造器,如果父类没有提供无参构造器,则必须在子类的构造器中用super去指定使用父类的那个构造器完成对父类的初始化工作,否则,编译不会通过

    4)想要指定去调用父类的某个构造器,则可以显示的调用一下:super(参数列表);

    5)super在构造器中使用时,必须放在构造器的第一行

    1. public class Test1 {
    2. public static void main(String[] args) {
    3. }
    4. }
    5. class A {
    6. public int age;
    7. private int a;
    8. //无参构造器
    9. public A() {
    10. }
    11. //有参构造器
    12. public A(int age, int a) {
    13. this.age = age;
    14. this.a = a;
    15. }
    16. //相关的set/get方法 --> 用来访问私有属性
    17. public int getAge() {
    18. return age;
    19. }
    20. public void setAge(int age) {
    21. this.age = age;
    22. }
    23. public int getA() {
    24. return a;
    25. }
    26. public void setA(int a) {
    27. this.a = a;
    28. }
    29. }
    30. class B extends A {
    31. //1.若是一样都没有写,则存在一个默认的无参构造器public A() {},默认的无参构造器中存在父类的无参构造器
    32. public B(){
    33. //2. 写出了子类的无参构造器,在无参构造器中默认调用了父类的无参构造器,无法对父类进行初始化,因为父类的无参构造器中也没有属性,注意:super()只能放在第一行
    34. }
    35. public B(int age , int a) {
    36. super(age, a);// 3.显示调用了父类的有参构造器(则不再会调用父类的默认无参构造器),对父类的属性进行初始化,注意:super()只能放在第一行
    37. }
    38. }

    6)super() 和 this()都只能放在构造器的第一行,因此这两个关键字不能共存于同一个构造器中

    1. class A () {
    2. }
    3. class B extends A{
    4. public B() {
    5. this(age);//表示到进入到下面的这个有参构造器中去了
    6. //使用this的使用无法使用默认是super();
    7. }
    8. public B(int age) {
    9. //注意:里面默认存在一个super();
    10. }
    11. }

    7)Java所有类的父类都是Object类,Object类是所有类的父类(顶级父类)

    1. public class Test1 {
    2. public static void main(String[] args) {
    3. }
    4. }
    5. class A extends Object{
    6. }
    7. class B extends A {
    8. }

    8)子类最多只能继承一个父类(指直接父类),即Java是单继承机制

    1. public class Test {
    2. public static void main(String[] args) {
    3. }
    4. }
    5. class A {}
    6. class B {}
    7. class C extends B extends A {}//错误代码,Java是单继承模式

    注:不能滥用继承,子类和父类之间必须满足is - a的逻辑关系

    意思是如果有一个动物类(父类),你创建了一个猫类,继承动物类, 这是满足 is - a的形式,但是如果你创建了一个树木类,继承了动物类, 则不满足is - a 的形式

     

    7. 继承的本质分析

    1. public class ExtendsTheory {
    2. public static void main(String[] args) {
    3. Son son = new Son();//内存的布局
    4. //?-> 这时请大家注意,要按照查找关系来返回信息
    5. //(1) 首先看子类是否有该属性
    6. //(2) 如果子类有这个属性,并且可以访问,则返回信息
    7. //(3) 如果子类没有这个属性,就看父类有没有这个属性(如果父类有该属性,并且可以访问,就返回信息..)
    8. //(4) 如果父类没有就按照(3)的规则,继续找上级父类,直到 Object...
    9. System.out.println(son.name);//返回就是大头儿子
    10. System.out.println(son.age);//返回的就是 39
    11. System.out.println(son.getAge());//返回的就是 39
    12. System.out.println(son.hobby);//返回的就是旅游
    13. }
    14. }
    15. class GrandPa {//爷类
    16. String name = "大头爷爷";
    17. String hobby = "旅游";
    18. }
    19. class Father extends GrandPa {//父类
    20. String name = "大头爸爸";
    21. private int age = 39;
    22. public int getAge() {
    23. return age;
    24. }
    25. }
    26. class Son extends Father { //子类
    27. String name = "大头儿子";
    28. }

    三.super关键字

    1. super介绍

    —— super代表父类的引用,用于访问父类的属性、方法、构造器

    2. super语法

    1)访问父类的属性,但不能访问父类的private属性 ---> super.属性名

    2)访问父类的方法,但不能访问父类的private方法 ----> super.方法名(参数列表)

    3)访问父类的构造器 ----> super(参数列表);只能放在构造器的第一句,也只能出现一句

    3. 一个小case快速了解super

    1. public class Test1 {
    2. public static void main(String[] args) {
    3. }
    4. }
    5. class A {
    6. public int num1;//公共
    7. protected int num2;//受保护
    8. int num3;//默认
    9. private int num4;//私有
    10. //构造器
    11. public A() {
    12. }
    13. public A(int num1, int num2) {
    14. this.num1 = num1;
    15. this.num2 = num2;
    16. }
    17. public A(int num1, int num2, int num3, int num4) {
    18. this.num1 = num1;
    19. this.num2 = num2;
    20. this.num3 = num3;
    21. this.num4 = num4;
    22. }
    23. //set/get方法
    24. public int getNum1() {
    25. return num1;
    26. }
    27. public void setNum1(int num1) {
    28. this.num1 = num1;
    29. }
    30. public int getNum2() {
    31. return num2;
    32. }
    33. public void setNum2(int num2) {
    34. this.num2 = num2;
    35. }
    36. public int getNum3() {
    37. return num3;
    38. }
    39. public void setNum3(int num3) {
    40. this.num3 = num3;
    41. }
    42. public int getNum4() {
    43. return num4;
    44. }
    45. public void setNum4(int num4) {
    46. this.num4 = num4;
    47. }
    48. //私有方法
    49. private void print() {
    50. System.out.println("这是A类中的私有方法");
    51. }
    52. }
    53. class B extends A {
    54. public B(int num1, int num2 ,int num3 ,int num4) {
    55. //super();//访问父类的无参构造器,不写也默认有
    56. //super(num1,num2); 访问父类中的有有两个参数的构造器,使用这个构造器时,默认构造器不执行
    57. super(num1, num2, num3, num4);
    58. }
    59. public void pout() {
    60. super.num1 = 10;
    61. super.num2 = 20;
    62. super.num3 = 30;
    63. //super.num4 = 40; 错误,私有成员变量不能访问
    64. //super.print(); 错误,私有方法不能访问
    65. }
    66. }

    4. super的细节讨论及实现

    1)调用父类的构造器的好处(分工明确,父类属性由父类初始化,子类属性由子类初始化)

    2)当子类中有和父类中的成员(属性和方法)重名时,为了访问父类的成员,必须通过super。如果没有重名,使用super和this直接访问是一样的效果

    3)super的访问不限于直接父类,如果爷爷类和本类中有相同的成员,也可以使用super去访问爷爷类中的成员;如果有多个基类(上级类)中都有同名的成员,使用super访问时遵循就近原则。

    5. this和super的比较

    区别点

    this

    super

    访问属性

    访问本类中的属性,如果本类没有此属性则从父类中继续查找

    从父类开始查找

    调用方法

    访问本类中的方法,如果本类中没有此方法,则从父类中继续查找

    从父类中开始查找

    调用构造器

    调用本类的构造器,必须放在构造器首行

    调用父类的构造器,必须放在子类的首行

    特殊

    表示当前对象

    子类中访问父类的对象

     

     

    四.方法重写

    1. 方法重写概念

    —— 方法重写就是子类有一个方法和父类的某个方法的名称、返回类型、参数一样,那么我们就说子类的这个方法重写了父类的方法

    2. 一个小case快速了解方法重写

    1. public class Test {
    2. public static void main(String[] args) {
    3. B b = new B();
    4. b.print();//?输出什么? ---》 父类中的print()方法被子类中的print()方法重写了~
    5. //注意:重写的权限修饰,父类中的一定不能是private,不然不能重写,且子类重写的方法不能降低父类方法的访问权限,但可以提高
    6. }
    7. }
    8. class A {
    9. private String name;
    10. private int age;
    11. void print() {
    12. System.out.println("name = " + name + " age = " + age);
    13. }
    14. public A() {
    15. }
    16. public A(String name, int age) {
    17. this.name = name;
    18. this.age = age;
    19. }
    20. public String getName() {
    21. return name;
    22. }
    23. public void setName(String name) {
    24. this.name = name;
    25. }
    26. public int getAge() {
    27. return age;
    28. }
    29. public void setAge(int age) {
    30. this.age = age;
    31. }
    32. }
    33. class B extends A {
    34. public void print() {
    35. System.out.println("父类中的print()方法被子类中的print()方法重写了~");
    36. }
    37. }

    注:重写的权限修饰符,父类方法中的一定不能是private,不然不能重写,且子类重写的方法不能降低父类方法的访问权限,但可以提高【public > protected > 默认 > private】

     

    3. 方法重写的细节讨论及实现

    1)子类的方法的形参列表,方法名称,要和父类方法的形参列表,方法名称完全一样

    2)子类方法中的返回类型和父类方法中的返回类型一样,或者是父类返回类型的子类

    1. //如:父类返回类型是 Object,子类方法返回类型是String,String是Object的子类
    2. //父类方法:
    3. public Object getInfo() {}
    4. //子类方法
    5. public String getInfo() {}

    3)子类方法不能缩小父类方法的访问权限,但能提高【public > protected > 默认 > private】

    4. 方法重写与方法重载的比较

    名称

    发生范围

    方法名

    形参列表

    返回类型

    修饰符

    方法重载(overload)

    本类

    必须一样

    类型,个数或者顺序至少有一个不同

    无要求

    无要求

    方法重写(override)

    父子类

    必须一样

    相同

    子类重写的方法,返回的类型和父类返回的类型一致,或者是父类的子类

    子类不能缩小父类方法的访问范围

     

     

    五.多态

    思维导图概览:

    1. 多态的介绍

    —— 方法或对象具有多种形态。多态是面向对象的第三大特征,多态是建立在封装和继承的基础上的

     

    2. 多态的具体体现

    1)方法的重写和方法的重载就体现多态

    2)对象的多态

     

    3. 如何判断编译类型和运行类型

    1)编译类型看 = 号的左边,运行类型看 = 号的右边

    2)编译类型在定义对象时,就确定了,不能改变

    3)运行类型是可以改变的

    4)一个对象的编译类型和运行类型可以不一致

    1. //编译类型为animal,运行类型为Tiger
    2. Animal animal = new Tiger();
    3. //编译类型为animal,运行类型为Cat
    4. animal = new Cat();

     

    4. 一个小case快速了解对象的多态

    1. public class TestOne {
    2. public static void main(String[] args) {
    3. //编译类型是Animal, 运行类型是Tiger
    4. Animal tiger = new Tiger();
    5. tiger.print();//打印什么? 老虎会咆哮~~~
    6. //为什么会打印老虎会咆哮? ---> 我们使用多态的时候,先看看子类中里面是否有重写父类的方法,如果有,那就使用,
    7. //如果没有,在父类中查看有没有该方法,如果有则使用
    8. }
    9. }
    10. public class Animal {
    11. public String name;
    12. public int age;
    13. public void print() {
    14. System.out.println("动物会睡觉~~");
    15. }
    16. //构造器
    17. public Animal() {
    18. }
    19. public Animal(String name, int age) {
    20. this.name = name;
    21. this.age = age;
    22. }
    23. //提供成套的set/get方法
    24. public String getName() {
    25. return name;
    26. }
    27. public void setName(String name) {
    28. this.name = name;
    29. }
    30. public int getAge() {
    31. return age;
    32. }
    33. public void setAge(int age) {
    34. this.age = age;
    35. }
    36. }
    37. public class Tiger extends Animal{
    38. public void print() {
    39. System.out.println("老虎会咆哮~~");
    40. }
    41. }

     

    5. 多态的细节讨论及实现

    1)多态的向上转型

            a. 本质:父类的引用指向子类的对象

    1. //父类
    2. class Animal{}
    3. //子类
    4. class Cat() extends Animal {}
    5. //父类的引用指向了子类的对象
    6. Animal cat = new Cat(); //编译类型:Animal,运行类型:Cat

            b. 语法:父类类型 引用名 = new 子类类型();

            c. 特点:编译类型看左边,运行类型看右边。可以调用父类中的所有成员变量(需遵守访问权限),不能调用子类中的特有成员,可以调用方法重写,注意,在访问的时候,从运行类型开始访问。

    1. public class TestOne {
    2. public static void main(String[] args) {
    3. //编译类型是Animal, 运行类型是Tiger
    4. Animal tiger = new Tiger();
    5. tiger.print();//打印什么? 老虎会咆哮~~~
    6. //为什么会打印老虎会咆哮?
    7. // ---》 我们使用多态的时候,先判断编译类型中是否有该方法,如果没有,则报错,运行类型从子类开始查找,查找与访问相匹配的方法或属性,如果父类中的方法被重写了,则调用重写的方法
    8. //看看访问特有方法是否报错
    9. //tiger.output(); --> 错误,不能访问子类中特有的方法
    10. }
    11. }
    12. public class Animal {
    13. public String name;
    14. public int age;
    15. public void print() {
    16. System.out.println("动物会睡觉~~");
    17. }
    18. //构造器
    19. public Animal() {
    20. }
    21. public Animal(String name, int age) {
    22. this.name = name;
    23. this.age = age;
    24. }
    25. //提供成套的set/get方法
    26. public String getName() {
    27. return name;
    28. }
    29. public void setName(String name) {
    30. this.name = name;
    31. }
    32. public int getAge() {
    33. return age;
    34. }
    35. public void setAge(int age) {
    36. this.age = age;
    37. }
    38. }
    39. public class Tiger extends Animal {
    40. public void print() {
    41. System.out.println("老虎会咆哮~~~");
    42. }
    43. //特有的方法
    44. public void output() {
    45. System.out.println("老虎在打滚~~");
    46. }
    47. }

    2)多态的向下转型(在向上转型的基础上)

            a. 语法:子类类型 引用名 = (子类类型) 父类引用;

    1. /*
    2. 怎么理解?
    3. 1.因为向上转型无法访问子类中的特有方法,但是,向下转型则帮助我们解决了此问题
    4. 2.怎么理解 子类类型 引用名 = (子类类型) 父类引用?
    5. //一段代码搞定:
    6. //编译类型:Animal,运行类型:Tiger
    7. Aniaml animal = new Tiger();
    8. //向下转型之后,编译类型Tiger,运行类型:Tiger
    9. Tiger tiger = (Tiger)animal;
    10. //子类类型 引用名 = (子类类型)父类引用
    11. //解决!!!
    12. 3.思考以下代码能不能向下转型?
    13. Animal animal = new Cat();
    14. Dog dog = (Dog)animal;
    15. //错误,为什么?因为我们没有使用父类的引用类型指向子类的对象,前面说过了,向下转型是基于向上转型的,所以想要向下转型的前提是要使父类的引用指向子类对象,也就是向上转型
    16. //代码改正
    17. Animal animal = new Dog();
    18. Dog dog = (Dog)animal;
    19. */

            b. 只能强转父类的引用,不能强转父类的类型

    1. //代码解释
    2. Person p = new Xh();
    3. Xh xh = (Xh)Person;//错误,这样是强转父类的类型

            c. 要求父类的引用必须指向的是当前目标类型的对象(看a.的解释)

            d. 向下转型后,可以调用子类类型中的所有成员(属性和方法)

    1. public class TestOne {
    2. public static void main(String[] args) {
    3. //编译类型是Animal, 运行类型是Tiger
    4. Animal animal = new Tiger();
    5. animal.print();//打印什么? 老虎会咆哮~~~
    6. //看看访问特有方法是否报错
    7. //tiger.run(); --> 错误,不能访问子类中特有的方法
    8. //对父类的引用进行强转
    9. Tiger tiger = (Tiger)animal;
    10. tiger.run();//可以使用特有方法了
    11. }
    12. }
    13. public class Animal {
    14. public String name;
    15. public int age;
    16. public void print() {
    17. System.out.println("动物会睡觉~~");
    18. }
    19. //构造器
    20. public Animal() {
    21. }
    22. public Animal(String name, int age) {
    23. this.name = name;
    24. this.age = age;
    25. }
    26. //提供成套的set/get方法
    27. public String getName() {
    28. return name;
    29. }
    30. public void setName(String name) {
    31. this.name = name;
    32. }
    33. public int getAge() {
    34. return age;
    35. }
    36. public void setAge(int age) {
    37. this.age = age;
    38. }
    39. }
    40. public class Tiger extends Animal{
    41. //重写的方法
    42. public void print() {
    43. System.out.println("老虎爱睡懒觉~~~");
    44. }
    45. //特有方法
    46. public void run() {
    47. System.out.println("老虎跑得快~~~");
    48. }
    49. }

            3)属性没有重写的说法,属性的值看编译类型

    1. public class Test {
    2. public static void main(String[] args) {
    3. Person person = new Smith("jack",18,"smith",20);
    4. Smith smith = new Smith();
    5. Person p = new Person();
    6. System.out.println(person.name); //jack
    7. System.out.println(person.age);//18
    8. System.out.println(smith.name);//null ? --> 因为Smith是我们新开的对象,所有里面的值是默认值
    9. System.out.println(smith.age); //0
    10. Smith s = (Smith)person; //向下转型
    11. System.out.println(s.name); //smith
    12. System.out.println(s.age);20
    13. }
    14. }
    15. public class Person {
    16. public String name;
    17. public int age;
    18. public Person() {
    19. }
    20. public Person(String name, int age) {
    21. this.name = name;
    22. this.age = age;
    23. }
    24. public String getName() {
    25. return name;
    26. }
    27. public void setName(String name) {
    28. this.name = name;
    29. }
    30. public int getAge() {
    31. return age;
    32. }
    33. public void setAge(int age) {
    34. this.age = age;
    35. }
    36. }
    37. public class Smith extends Person{
    38. public String name;
    39. public int age;
    40. public Smith() {
    41. }
    42. public Smith(String name, int age, String name1,int age1) {
    43. super(name, age);
    44. this.name = name1;
    45. this.age = age1;
    46. }
    47. }

            4)instanceof 比较操作符,用于判断对象的运行类型是否是XX类型或XX类型的子类型

    1. public class Test {
    2. public static void main(String[] args) {
    3. BB bb = new BB();
    4. System.out.println(bb instanceof BB);//true
    5. System.out.println(bb instanceof AA);//true
    6. //编译类型:AA 运行类型:BB
    7. AA aa = new BB();
    8. System.out.println(aa instanceof AA); //true
    9. System.out.println(aa instanceof BB); //true
    10. Object obj = new Object();
    11. System.out.println(obj instanceof AA); //false
    12. String str = "hello";
    13. //System.out.println(str instanceof AA); //编译器报错,因为两者是不相同的类型
    14. System.out.println(str instanceof Object); //true
    15. }
    16. }
    17. class AA {}
    18. class BB extends AA {}

     

     

    六.Java的动态绑定机制

    1. Java重要特性:动态绑定机制

     

    2. Java动态绑定机制的特点

    1)当调用对象方法的时候,该方法会和对象的内存地址/运行类型进行绑定

    2)当调用对象属性的时,没有动态绑定机制,哪里声明,哪里使用

    1. public class Test {
    2. public static void main(String[] args) {
    3. AA aa = new BB();
    4. System.out.println(aa.sum()); //40
    5. System.out.println(aa.sum1()); //30
    6. //为什么会是以上答案?因为运行类型从子类开始执行,查找当中的方法,执行了父类中发方法重写
    7. //去掉子类中的sum()方法
    8. System.out.println(aa.sum()); //30 --> Java的动态绑定机制:父类中的sum()方法调用子类中重写的getI()方法
    9. }
    10. }
    11. class AA {
    12. public int i = 10;
    13. public int sum() {
    14. return getI() + 10;
    15. }
    16. public int sum1() {
    17. return i + 10;
    18. }
    19. public int getI() {
    20. return i;
    21. }
    22. }
    23. class BB extends AA {
    24. public int i = 20;
    25. //注意:以下方法全部为重写方法
    26. /* public int sum() {
    27. return i + 20;
    28. }*/
    29. public int getI() {
    30. return i;
    31. }
    32. public int sum1() {
    33. return i + 10;
    34. }
    35. }

    3.多态数组

    —— 数组的定义类型为父类类型,里面保存的对象类型为子类类型或父类类型

    1. public class Test {
    2. public static void main(String[] args) {
    3. Person[] person = new Person[2];
    4. person[0] = new Person("jack",19);
    5. person[1] = new Student("小红",10,100);
    6. //调用say()方法
    7. for (int i = 0; i < person.length; i++) {
    8. System.out.println(person[i].say());
    9. //调用子类的特有方法
    10. if(person[i] instanceof Student) {
    11. //向下转型,调用子类中特有的方法
    12. ((Student) person[i]).study();
    13. }
    14. }
    15. }
    16. }
    17. public class Person {
    18. private String name;
    19. private int age;
    20. public Person(String name, int age) {
    21. this.name = name;
    22. this.age = age;
    23. }
    24. public String getName() {
    25. return name;
    26. }
    27. public void setName(String name) {
    28. this.name = name;
    29. }
    30. public int getAge() {
    31. return age;
    32. }
    33. public void setAge(int age) {
    34. this.age = age;
    35. }
    36. public String say() {//返回名字和年龄
    37. return name + "\t" + age;
    38. }
    39. }
    40. public class Student extends Person {
    41. private double score;
    42. public Student(String name, int age, double score) {
    43. super(name, age);
    44. this.score = score;
    45. }
    46. public double getScore() {
    47. return score;
    48. }
    49. public void setScore(double score) {
    50. this.score = score;
    51. }
    52. //重写父类 say
    53. @Override
    54. public String say() {
    55. return "学生 " + super.say() + " score=" + score;
    56. }
    57. //特有的方法
    58. public void study() {
    59. System.out.println("学生 " + getName() + " 正在学 java...");
    60. }
    61. }

     

    2. 多态参数

    —— 方法定义的形参类型为父类类型,实参类型为父类类型或子类类型

    1. public class Test {
    2. public static void main(String[] args) {
    3. Test test = new Test();
    4. Person[] person = new Person[2];
    5. person[0] = new Person("jack",19);
    6. person[1] = new Student("小红",10,100);
    7. test.printSay(person[0]);
    8. test.printSay(person[1]);
    9. }
    10. public void printSay(Person p) {
    11. System.out.println(p.say());
    12. }
    13. }
    14. public class Person {
    15. private String name;
    16. private int age;
    17. public Person(String name, int age) {
    18. this.name = name;
    19. this.age = age;
    20. }
    21. public String getName() {
    22. return name;
    23. }
    24. public void setName(String name) {
    25. this.name = name;
    26. }
    27. public int getAge() {
    28. return age;
    29. }
    30. public void setAge(int age) {
    31. this.age = age;
    32. }
    33. public String say() {//返回名字和年龄
    34. return name + "\t" + age;
    35. }
    36. }
    37. public class Student extends Person{
    38. private double score;
    39. public Student(String name, int age, double score) {
    40. super(name, age);
    41. this.score = score;
    42. }
    43. public double getScore() {
    44. return score;
    45. }
    46. public void setScore(double score) {
    47. this.score = score;
    48. }
    49. //重写父类 say
    50. @Override
    51. public String say() {
    52. return "学生 " + super.say() + " score=" + score;
    53. }
    54. }

     

    七.问题思考?

    1.  一个类可以有几个直接父类?一个父类可有多少个子类?子类能获取直接父类的父类中的结构吗?子类能否获取父类中private权限的属性或方法?

    2. 方法的重写(override/overwrite)的具体规则有哪些,两者的区别?

    3. super调用构造器,有哪些具体的注意点?

    4. 如何实现向下转型?需要注意什么问题?如何解决此问题?

    5. == 和equals()有何区别?

  • 相关阅读:
    【入门-03】时钟系统
    1. 使用STM32CubeMX建立STM32G030C8T6项目工程
    深入了解MySQL内部细节
    HIFI测序揭示拟南芥MSH1参与介导的细胞器基因组重组与变异积累规律
    1944. 队列中可以看到的人数 单调栈
    通义千问1.5(Qwen1.5)大语言模型在PAI-QuickStart的微调与部署实践
    使用docker compose快速搭建spark集群
    [ubuntu]server网络配置
    英语学习笔记14——What color‘s your ... ?
    js实现pdf、excel文件无预览直接下载
  • 原文地址:https://blog.csdn.net/qq_63200557/article/details/134055605