一、IDE(集成开发环境)

1.1 IDEA 简介

  1. IDEA 全称 IntelliJ IDEA
  2. 在业界被公认为最好的Java开发工具
  3. IDEA 是 JetBrains 公司的产品,总部位于捷克的首都布拉格
  4. 除了支持Java开发,还支持HTML、CSS、PHP、MySQL、Python等
    下载地址

1.2 Eclipse 简介

  1. Eclipse 是一个开放源代码的、基于Java的可拓展开发平台
  2. 最初是由IBM公司耗资3000万美金开发的下一代IDE开发环境
  3. 2001年11月贡献给开源社区
  4. Eclipse是目前最优秀的Java开发IDE之一

1.3 IDEA 基本介绍和使用

使用 IEDA 创建Java项目(Project),看看 IDEA 是如何使用的,IDEA 是以项目的概念,来管理我们的Java源码的。

使用入门

  1. 安装IDEA
  2. 创建一个项目
    1. 选择SDK
    2. 键入项目名
    3. 选择项目存放位置
    4. 源码存放在 src 目录下
    5. 新建 Java Class
  3. 在IDEA中,当我们 Run 一个文件时,会先编译成 .class -> 再运行
    class文件会根据我们写的类生成,写了多少个类就会对应生成多少个文件
    文件保存在项目文件夹 -> out -> production 同 Java Class 名文件夹内

    IDEA相关设置

  4. 设置字体
    菜单栏 File -> Settings -> Editor -> Font

  5. 更换外观主题
    菜单栏 File -> Settings -> Appearance & Behavior -> Appearance
  6. 字体加粗
    菜单栏 File -> Settings -> Editor -> Color Scheme -> General -> Text -> Default text -> Bold
  7. 字符编码设置
    菜单栏 File -> Settings -> Editor -> File Encodings
  8. 配置 auto import
    菜单栏 File -> Settings -> Editor -> General -> Auto Import
    image.png
  9. 常用快捷键
    删除当前行 Ctrl + Y
    复制当前行 Ctrl + D
    补全代码 alt + /
    添加和取消注释 ctrl + /
    导入该行需要的类 先配置 auto import,然后使用 alt + enter 即可
    快速格式化代码 ctrl + alt + L
    快速运行程序 ctrl + shift + F10
    生成构造器方法等 alt + insert
    查看一个类的层级关系 ctrl + H
    将光标放在一个方法上,输入 ctrl + B,可以选择定位到哪个类的方法
    自动的分配变量名,通过 在后面 .var
    ……
  10. 自定义快捷键/热键
    菜单栏 File -> Settings -> Keymap
  11. 模板/自定义模板
    菜单栏 File -> Settings -> Editor -> Live Templates
    可以查看有哪些模板快捷键/可以自己增加模板
    模板可以高效的完成开发,提高编码速度

二、包

2.1 基本介绍

包的三大作用

  1. 区分相同名字的类
  2. 当类很多时,可以很好的管理
  3. 控制访问范围

    包基本语法
    package com.hspedu;
    说明:

  4. package 关键字,表示打包

  5. com.hspedu
  6. 包名

2.2 包的本质分析(原理)

包的本质实际上就是创建不同的文件夹/目录来保存类文件
面向对象编程(中级部分) - 图2

2.3 案例演示

使用打包技术实现不同包下同名Dog类

  1. 新建包

image.png

  1. 输入包名 com.xxx

image.png

  1. 引用包中的类 ```java package com.use;

import com.xiaoming.Dog; // 引入com.xiaoming 包的类 Dog // 同类名只能引入一个,再引入 com.xiaoqiang.Dog 会报错

public class Test { public static void main(String[] args) { Dog dog = new Dog(); // 引入后可以直接使用,不用再写包名加以区分 System.out.println(dog);

  1. // 解读
  2. // com.xiaoqiang.Dog 表示从com.xiaoqiang包引用的Dog类,用以区分
  3. com.xiaoqiang.Dog dog1 = new com.xiaoqiang.Dog();
  4. System.out.println(dog1);
  5. }

}

  1. <a name="udg2k"></a>
  2. #### 2.4 包的命名
  3. 命名规则 <br />只能包含数字、字母、下划线、小圆点,但不能用数字开头,不能是关键字或保留字
  4. 命名规范 <br />一般是小写字母 + 小圆点<br />com.公司名.项目名.业务模块名<br /> 比如:com.sina.crm.user // 用户模块<br /> com.sina.crm.order // 订单模块<br /> com.sina.crm.utils // 工具类
  5. <a name="fJX7N"></a>
  6. #### 2.5 常用的包
  7. 一个包下,包含很多的类,Java中常用的包有<br />java.lang.* // lang 包是基本包,默认引入,不需要再引入<br />java.util.* // util 包,系统提供的工具包,工具类,比如使用Scanner<br />java.net.* // 网络包,网络开发<br />java.awt.* // 是做Java的界面开发,GUI
  8. <a name="x1U02"></a>
  9. #### 2.6 引入包
  10. 基本语法 <br />**import **包;
  11. 我们引入包的主要目的是要使用该包下的类<br />比如 import java.util.Scanner; 就只是引入一个类 Scanner<br />import java.util.*; 表示将 java.util 包所有都引入<br />注意:一般需要哪个类就导入哪个类即可,不建议使用 * 的方式导入
  12. 案例演示
  13. ```java
  14. package com.hspedu.pkg;
  15. import java.util.Arrays;
  16. //import java.util.Scanner; 就只是引入一个类 Scanner
  17. //import java.util.*; 表示将 java.util 包所有都引入
  18. public class Import01 {
  19. public static void main(String[] args) {
  20. // 使用系统提供 Arrays 完成 数组排序
  21. int[] arr = {-1, 20, 2, 13, 3};
  22. // 比如对其进行排序
  23. // 传统方法是,自己编写排序(冒泡)
  24. // 系统提供了相关的类,可以方便完成 Arrays
  25. Arrays.sort(arr);
  26. // 输出排序结果
  27. for (int i = 0; i < arr.length; i++) {
  28. System.out.print(arr[i] + "\t");
  29. }
  30. }
  31. }

2.7 包的注意事项和细节

  1. package 的作用是声明当前类所在的包,需要放在 class 的最上面,一个类中最多只有一句 package
  2. import 指令 位置放在 package 的下面,在类定义的前面,可以有多句且没有顺序要求

三、访问修饰符

3.1 基本介绍

Java提供四种访问修饰符号,用于控制方法和属性( 成员变量 )的访问权限 ( 范围 ):

  1. 公开级别:用 public 修饰,对外公开
  2. 受保护级别:用 protected 修饰,对子类和同一个包中的类公开
  3. 默认级别:没有修饰符,向同一个包的类公开
  4. 私有级别:用 private 修饰,只有类本身可以访问,不对外公开

4种访问修饰符的访问范围

访问级别 访问修饰符 同类 同包 子类 不同包
公开 public
受保护 protected ×
默认 (none) × ×
私有 private × × ×

3.2 访问修饰符使用的注意事项

  1. 修饰符可以用来修饰类中的属性,成员方法以及类
  2. 只有默认的和 public 才能修饰类! 并且遵循上述访问权限的特点
  3. 因为还没学习继承,因此关于在子类中的访问权限,在讲子类的时候会再讲解
  4. 成员方法的访问规则和属性完全一样

四、封装

4.1 基本介绍

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

封装的理解和好处

  1. 隐藏实现细节
  2. 可以对数据进行验证,保证安全合理

4.2 封装的实现步骤(三步)

  1. 将属性进行私有化 private 【不能直接修改属性】
  2. 提供一个公共的(public) set 方法,用于对属性判断并赋值
    public void setXxx( 类型 参数名 ) { // Xxx表示某个属性
    // 加入数据验证的业务逻辑
    属性 = 参数名;
    }
  3. 提供一个公共的(public) get 方法,用于获取属性的值
    public XX getXxx() { // 权限判断 Xxx表示某个属性
    return xx;
    }

4.3 案例演示

com.hspedu.encap:Encapsulation01.java
不能随便查看人的年龄、工资等隐私,并对设置的年龄进行合理的验证。年龄合理就设置,否则给默认
年龄必须在 1- 120,年龄、工资不能直接查看, name 长度要在 2 - 6 个字符之间

  1. package com.hspedu.encap;
  2. public class Encapsulation01 {
  3. public static void main(String[] args) {
  4. Person person = new Person();
  5. person.setName("Jack");
  6. person.setAge(30);
  7. person.setSalary(30000);
  8. System.out.println(person.info());
  9. }
  10. }
  11. class Person {
  12. public String name; // name 公开
  13. private int age; // age 私有化
  14. private double salary;
  15. // 自己写 setXxx 和 getXxx 太慢,我们可以使用快捷键
  16. // Alt + insert -> Getter and Setter
  17. // 然后根据要求来完善我们的代码
  18. public String getName() {
  19. return name;
  20. }
  21. public void setName(String name) {
  22. // 加入对数据的校验 相当于增加了业务逻辑
  23. if (name.length() >= 2 && name.length() <= 6){
  24. this.name = name;
  25. } else {
  26. System.out.println("名字的长度不对,需要(2-6)个字符,默认名字");
  27. this.name = "佚名";
  28. }
  29. }
  30. public int getAge() {
  31. return age;
  32. }
  33. public void setAge(int age) {
  34. // 判断
  35. if (age >= 1 && age <= 120) { // 如果在合理范围
  36. this.age = age;
  37. } else {
  38. System.out.println("你设置的年龄不对,需要在(1-120),给默认年龄18");
  39. this.age = 18; //给一个默认年龄
  40. }
  41. }
  42. public double getSalary() {
  43. // 可以在这里增加密码验证等方法实现对当前对象的权限判断
  44. return salary;
  45. }
  46. public void setSalary(double salary) {
  47. this.salary = salary;
  48. }
  49. // 写一个方法,返回属性信息
  50. public String info() {
  51. return "信息为 name=" + name + " age=" + age + " salary=" + salary;
  52. }
  53. }

说明:
如果使用了构造器,可以与 setXxx 方法结合,保证封装的正常使用

  1. public Person(String name, int age, double salary) {
  2. // 我们可以将set方法写在构造器中,这样仍然可以验证
  3. setName(name);
  4. setAge(age);
  5. setSalary(salary);
  6. }

⏱ 小练习

创建程序,在其中定义两个类:Account 和 AccountTest 类 体会Java的封装性

  1. Account 类要求具有属性:姓名(长度为2位3位或4位)、余额(必须 > 20)、密码(必须是6位)、如果不满足,则给出提示信息,并给默认值
  2. 通过 setXxx 的方法给 Account 的属性赋值
  3. 在 AccountTest 中测试

com.hspedu.encap -> Account.java

  1. package com.hspedu.encap;
  2. public class Account {
  3. private String name;
  4. private double balance;
  5. private String password;
  6. // 提供两个构造器
  7. public Account() {
  8. }
  9. public Account(String name, double balance, String password) {
  10. this.setName(name);
  11. this.setBalance(balance);
  12. this.setPassword(password);
  13. }
  14. public String getName() {
  15. return name;
  16. }
  17. public void setName(String name) {
  18. if (name.length() >= 2 && name.length() <=4) {
  19. this.name = name;
  20. } else {
  21. System.out.println("姓名(长度为2位3位或4位),默认 佚名");
  22. this.name = "佚名";
  23. }
  24. }
  25. public double getBalance() {
  26. return balance;
  27. }
  28. public void setBalance(double balance) {
  29. if (balance > 20) {
  30. this.balance = balance;
  31. } else {
  32. System.out.println("余额(必须 > 20),默认 -1");
  33. this.balance = -1;
  34. }
  35. }
  36. public String getPassword() {
  37. return password;
  38. }
  39. public void setPassword(String password) {
  40. if (password.length() == 6) {
  41. this.password = password;
  42. } else {
  43. System.out.println("密码(必须是6位),默认000000");
  44. this.password = "000000";
  45. }
  46. }
  47. public void showAccount() {
  48. // 可以增加权限校验
  49. System.out.println("name=" + name + "\tbalance=" + balance + "\tpassword="
  50. + password);
  51. }
  52. }

com.hspedu.encap -> AccountTest.java

  1. package com.hspedu.encap;
  2. public class AccoutTest {
  3. public static void main(String[] args) {
  4. // 创建Account
  5. Account account = new Account();
  6. account.setName("JackSmith");
  7. account.setBalance(30);
  8. account.setPassword("123456789");
  9. account.showAccount();
  10. Account account1 = new Account("Tom", 10, "qweasd");
  11. account1.showAccount();
  12. }
  13. }

五、继承

5.1 基本介绍

继承可以解决代码复用,让我们的编程思维更加靠近人类思维。当多个类存在相同的属性(变量)和方法时,可以从这些类中抽象出父类,在父类中定义这些相同的属性和方法,所有的子类不需要重新定义这些属性和方法,只需要通过 extends 来声明继承父类即可。

基本语法
class 子类 extends 父类 {
}

带来的便利

  1. 代码的复用性提高了
  2. 代码的扩展性和维护性提高了

5.2 继承示意图

面向对象编程(中级部分) - 图5

  1. 子类会自动拥有父类定义的方法和属性
  2. 父类又叫超类、基类
  3. 子类又叫派生类

5.3 案例演示

父类:com.hspedu.extend_ : Students.java

  1. package com.hspedu.extend_;
  2. // 父类,是Pupil 和 Graduate类的父类
  3. public class Students {
  4. // 共有属性
  5. public String name;
  6. public int age;
  7. private double score;
  8. // 共有方法
  9. public void setScore(double score) {
  10. this.score = score;
  11. }
  12. public void showInfo() {
  13. System.out.println("学生名" + name + " 年龄" + age + " 分数" + score);
  14. }
  15. }

子类:com.hspedu.extend_ : Pupil.java

  1. package com.hspedu.extend_;
  2. // Students 的子类 Pupil
  3. public class Pupil extends Students{
  4. //特有的方法
  5. public void testing() {
  6. System.out.println("小学生" + name + "正在考数学.....");
  7. }
  8. }

子类:com.hspedu.extend_ : Graduate.java

  1. package com.hspedu.extend_;
  2. // Students 的子类 Graduate
  3. public class Graduate extends Students{
  4. public void testing() {
  5. System.out.println("大学生" + name + "正在考高数...");
  6. }
  7. }

调用演示:com.hspedu.extend_ : Extends01.java

  1. package com.hspedu.extend_;
  2. public class Extends01 {
  3. public static void main(String[] args) {
  4. Pupil pupil = new Pupil();
  5. pupil.name = "银角大王";
  6. pupil.age = 10;
  7. pupil.testing();
  8. pupil.setScore(60);
  9. pupil.showInfo();
  10. System.out.println("============");
  11. Graduate graduate = new Graduate();
  12. graduate.name = "金角大王";
  13. graduate.age = 23;
  14. graduate.testing();
  15. graduate.setScore(100);
  16. graduate.showInfo();
  17. }
  18. }

5.4 继承的使用细节

  1. 子类继承了所有的属性和方法,非私有的属性和方法可以在子类中直接访问,但是私有属性和方法不能在子类中直接访问,要通过公共(public)的方法去访问。
  2. 子类必须调用父类的构造器,完成父类的初始化。(系统会默认使用 super(); 完成对父类无参构造器的引用)
  3. 当创建子类时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器,如果父类没有提供无参构造器,则必须在子类的每个构造器中用 super 去指定使用父类的哪个构造器完成对父类的初始化工作,否则,编译不会通过。

    1. // 父类
    2. public class Sub {
    3. int age;
    4. String name;
    5. public Sub(int age, String name) {
    6. System.out.println("父类的有参构造器");
    7. }
    8. }
    1. // 子类
    2. public class Sup extends Sub {
    3. public Sup() {
    4. super(18,"Tom");
    5. System.out.println("子类的构造器");
    6. }
    7. }
  4. 如果希望指定去调用父类的某个构造器,则显示的调用一下。

  5. super 在使用时,需要放在构造器的第一行。( super() 只能在构造器中使用 )
  6. super() 和 this() 都只能放在第一行,因此这两个方法不能共存在一个构造器。
  7. Java 所有类都是 Object 类的子类,Object 类是所有类的基类。
  8. 父类构造器的调用不限于直接父类!将一直向上追溯到 Object 级( 顶级父类 )。
  9. 子类最多只能继承一个父类(指直接继承),即Java中是单继承机制。
    思考:如何让 A类继承 B类和 C类 [ 让 B类做中转 A -> B -> C ]
  10. 不能滥用继承,子类和父类之间必须满足 is - a 的逻辑关系
    Person is a Music ?
    Person Music
    Music extedns Person // 不合理

Animal
Cat extends Animal // 合理

5.5 继承的本质分析

  1. package com.hspedu.extend_;
  2. // 讲解继承的本质
  3. public class ExtendsTheory {
  4. public static void main(String[] args) {
  5. Son son = new Son(); //内存中的变化
  6. }
  7. }
  8. class Granpa { //父类
  9. String name = "大头爷爷";
  10. String hobby = "旅游";
  11. }
  12. class Father extends Granpa { //直接父类
  13. String name = "小头爸爸";
  14. int age = 39;
  15. }
  16. class Son extends Father { //子类
  17. String name = "大头儿子";
  18. }

面向对象编程(中级部分) - 图6

⏱ 小练习

  1. 案例1
    class A {
    A() { System.out.println(“a”); }
    A(String name) { System.out.println(“a name”); }
    }
    class B extends A {
    B() { this(“abc”); System.out.println(“b”); }
    B(String name) { System.out.println(“b name”); }
    }
    main 中:B b = new B(); 问:会输出什么?

  2. 案例2,问输出什么?
    public class ExtendsExercise02 {
    public static void main(String[] args) {
    C c = new C();
    }
    }
    class A {
    public A() {
    System.out.println(“我是A类”);
    }
    }
    class B extends A {
    public B() {
    System.out.println(“我是B的无参构造”);
    }
    public B(String name) {
    System.out.println(name + “我是B类的有参构造”);
    }
    }
    class C extends B {
    public C() {
    this(“hello”);
    System.out.println(“我是C类的无参构造”);
    }
    public C(String name) {
    super(“hahah”);
    System.out.println(“我是C类的有参构造”);
    }
    }

  3. 编写 Computer 类,包含 CPU、内存、硬盘等属性,getDetails 方法用于返回 Computer 的详细信息
    编写 PC 子类,继承 Computer 类,添加特有属性【品牌 brand】
    编写 NotePad 子类,继承 Computer 类,添加特有属性【颜色 color】
    编写 Test 类,在 main 方法中创建 PC 和 NotePad 对象,分别给对象中特有的属性赋值,以及从 Computer 类继承的属性赋值,并使用方法并打印输出信息

六、super 关键字

6.1 基本介绍

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

基本语法

  1. 访问父类的属性,但不能访问父类的 private 属性 【 super.属性名 】
  2. 访问父类的方法,不能访问父类的 private 方法 【 super.方法名(参数列表) 】
  3. 访问父类的构造器【 super(参数列表) 】 ( 只能放在构造器的第一句,只能出现一句!)

6.2 super 的使用细节

  1. 调用父类构造器的好处(分工明确,父类属性由父类初始化,子类属性由子类初始化)
  2. 当子类中有和父类中的成员(属性和方法)重名时,为了访问父类的成员,必须通过 super 。如果没有重名,使用 super、this、直接访问,效果是一样的
  3. super 的访问不限于直接父类,如果爷爷类和本类中有同名的成员,也可以使用super去访问爷爷类的成员;如果多个基类中都有同名的成员,使用 super 访问遵循就近原则和访问权限规则。A -> B -> C

6.3 super 和 this 的比较

No. 区别点 this super
1 访问属性 访问本类中的属性,如果本类中没有此属性则从父类中继续查找 跳过本类访问父类中的属性,如果父类中没有则再往父类追溯
2 调用方法 访问本类中的方法,如果本类没有此方法则从父类继续查找 跳过本类直接访问父类中的方法
3 调用构造器 调用本类构造器,必须放在构造器的首行 调用父类构造器,必须放在子类构造器的首行
4 特殊 表示当前对象 子类中访问父类 对象

七、方法重写/覆盖(override)

7.1 基本介绍

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

7.2 方法重写的注意事项和使用细节

  1. 子类的方法的形参列表,方法名称,要和父类方法的形参列表,方法名称完全一样。
  2. 子类方法的返回类型和父类方法返回类型一样,或者是父类返回类型的子类
    比如:父类 返回类型是 Object,子类方法返回类型是 String,也构成方法重写
    如果不一样且不是子类,就会报错
  3. 子类方法不能缩小父类方法的访问权限

7.3 方法重载 与 方法重写 的比较

名称 发生范围 方法名 参数列表 返回类型 修饰符
重载(overload) 本类 相同 类型,个数,顺序至少有一个不同 无要求 无要求
重写(override) 子类和父类之间 相同 相同 与父类相同或是父类返回类型的子类 与父类相同或比父类修饰范围大

⏱ 小练习

  1. 编写一个 Person 类,包括属性/private ( name 、age ),构造器,方法 say( 返回自我介绍name,age的字符串 )。
  2. 编写一个 Student 类,继承 Person 类,增加 id、score 属性/private,以及构造器,定义 say 方法( 返回自我介绍name,age,id,score的信息 )
  3. 在 main 中,分别创建 Person 和 Student 对象,调用 say 方法输出自我介绍

com.hspedu.override_ : Person.java

  1. package com.hspedu.override_;
  2. public class Person {
  3. private String name;
  4. private int age;
  5. public Person(String name, int age) {
  6. setName(name);
  7. setAge(age);
  8. }
  9. public String say() {
  10. return "姓名:" + getName() + "\t年龄" + getAge();
  11. }
  12. public String getName() {
  13. return name;
  14. }
  15. public void setName(String name) {
  16. this.name = name;
  17. }
  18. public int getAge() {
  19. return age;
  20. }
  21. public void setAge(int age) {
  22. this.age = age;
  23. }
  24. }

com.hspedu.override_ : Student.java

  1. package com.hspedu.override_;
  2. public class Student extends Person {
  3. private int id;
  4. private double score;
  5. public Student(String name, int age, int id, double score) {
  6. super(name, age);
  7. setId(id);
  8. setScore(score);
  9. }
  10. public String say() {
  11. return super.say() + "\tID:" + getId() + "\t成绩:" + getScore();
  12. }
  13. public int getId() {
  14. return id;
  15. }
  16. public void setId(int id) {
  17. this.id = id;
  18. }
  19. public double getScore() {
  20. return score;
  21. }
  22. public void setScore(double score) {
  23. this.score = score;
  24. }
  25. }

com.hspedu.override_ : OverrideExercise.java

  1. package com.hspedu.override_;
  2. public class OverrideExercise {
  3. public static void main(String[] args) {
  4. Person person = new Person("Tom",18);
  5. System.out.println(person.say());
  6. Student student = new Student("Jack",20,369856,95.5);
  7. System.out.println(student.say());
  8. }
  9. }

八、多态

8.1 基本介绍

image.png
传统解决方案:
com.hspedu.poly_ :
Poly01.java
Master.java
Animal.java
Food.java
Dog.java
Cat.java
Bone.java
Fish.java
Pig.java
Rice.java
在 Master.java 中:

  1. package com.hspedu.poly_;
  2. public class Master {
  3. private String name;
  4. public Master(String name) {
  5. this.name = name;
  6. }
  7. public String getName() {
  8. return name;
  9. }
  10. public void setName(String name) {
  11. this.name = name;
  12. }
  13. //主人给小狗 喂食 骨头
  14. public void feed(Dog dog, Bone bone) {
  15. System.out.println("主人" + name + " 给小狗" + dog.getName() + " 吃" + bone.getName());
  16. }
  17. //主人给小猫 喂食 黄花鱼
  18. public void feed(Cat cat, Fish fish) {
  19. System.out.println("主人" + name + " 给小猫" + cat.getName() + " 吃" + fish.getName());
  20. }
  21. //如果动物很多,食物也很多
  22. //===> feed 方法就要写很多,不利于管理和维护
  23. }

传统解决方案的问题:
代码复用性不高,而且不利于代码维护。

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

8.2 多态的具体体现

  1. 方法的多态:重写和重载就体现了多态 ```java package com.hspedu.poly_;

public class PolyMethod { public static void main(String[] args) { B b = new B(); //这里我们传入不同的参数,就会调用不同的sum方法,就体现了多态 System.out.println(b.sum(10, 20)); System.out.println(b.sum(10, 20, 30)); //方法重写体现多态 A a = new A(); b.say(); a.say(); } }

class A { public void say() { System.out.println(“A 的 say()方法被调用…”); } }

class B extends A { public int sum(int a, int b) { return a + b; }

  1. public int sum(int a, int b, int c) {
  2. return a + b + c;
  3. }
  4. public void say() {
  5. System.out.println("B 的 say()方法被调用...");
  6. }

}

  1. 2. **对象的多态**(核心,重点)
  2. 1. 一个对象的编译类型和运行类型可以不一致
  3. 1. 编译类型在定义对象时,就确定了,不能改变
  4. 1. 运行类型是可以变化的
  5. 1. 编译类型看定义时 = 的左边,运行类型看 = 的右边
  6. 案例:<br /> Animal animal = new Dog(); animal 编译类型是 Animal,运行类型是 Dog 】<br />animal = new Cat(); animal 的运行类型变成了 Cat,编译类型仍然是 Animal
  7. <a name="fIcsv"></a>
  8. #### 8.3 案例演示
  9. 改进后的 Master.java 中:
  10. ```java
  11. package com.hspedu.poly_;
  12. public class Master {
  13. private String name;
  14. public Master(String name) {
  15. this.name = name;
  16. }
  17. public String getName() {
  18. return name;
  19. }
  20. public void setName(String name) {
  21. this.name = name;
  22. }
  23. //使用多态机制,可以统一的管理主人喂食的问题
  24. //animal 的编译类型是Animal,可以指向(接收)Animal 的子类对象
  25. //food 的编译类型是Food,可以指向(接收)Food 的子类对象
  26. public void feed(Animal animal,Food food) {
  27. System.out.println("主人" + name + " 给" + animal.getName() + " 喂" + food.getName());
  28. }
  29. }

8.4 多态的注意事项和细节讨论

  1. 多态的前提是:两个对象(类)存在继承关系
  2. 多态的向上转型
    1. 本质:父类的引用指向了子类的对象
    2. 语法:父类类型 引用名 = new 子类类型();
    3. 特点:编译类型看左边,运行类型看右边。
      可以调用父类中的所有成员(需遵守访问权限),
      不能调用子类中的特有成员,
      最终运行效果看子类(运行类型)的具体实现!即调用方法时,按照从子类(运行类型)开始查找方法
  3. 多态的向下转型
    1. 语法:子类类型 引用名 = ( 子类类型 ) 父类引用;
    2. 只能强转父类的引用,不能强转父类的对象
    3. 要求父类的引用必须指向的是当前目标类型的对象
    4. 当向下转型后,就可以调用子类类型中的所有成员
  4. 属性没有重写之说!属性的值看编译类型
  5. instanceOf 比较操作符,用于判断对象的运行类型是否为XX类型或XX类型的子类型

⏱ 小练习

  1. 请说出下面的每条语言,哪些是正确的,哪些是错误的,为什么?

public class PolyExercise01{
public static void main(String[] args) {
double d = 13.4;
long I = (long)d;
System.out.println();
int in = 5;
boolean b = (boolean)in; // 错误
Object obj = “Hello”; // 可以,向上转型
String objStr = (String)obj; // 可以,向下转型
System.out.printIn(objStr);
Object objPri = new Integer(5); // 可以,向上转型
String str = (String)objPri; // 错误,指向Integer的父类引用,转成String
Integer str1 = (Integer)objPri; //可以
}
}

  1. 请分析下面代码的运行结果:

class Base{
int count = 10;
public void display() {
System.out.println(this.count);
}
}
class Sub extends Base {
int count = 20;
public void display() {
System.out.println(this.count);
}
}

public class PolyExercise02 {
public static void main(String[] args) {
Sub s = new Sub();
System.out.println(s.count);
s.display();
Base b = s;
System.out.println(b.count);
b.display();
}
}

8.5 Java的动态绑定机制(非常非常重要)

  1. 当调用对象方法的时候,该方法会和该对象的内存地址/运行类型绑定。
  2. 当调用对象属性时,没有动态绑定机制,哪里声明,哪里使用。

案例演示

  1. package com.hspedu.poly_.dynamic_;
  2. public class DynamicBinding {
  3. public static void main(String[] args) {
  4. //a 编译类型 A, 运行类型 B
  5. A a = new B();
  6. System.out.println(a.sum()); //输出30
  7. System.out.println(a.sum1()); //输出20
  8. }
  9. }
  10. class A { //父类
  11. public int i = 10;
  12. public int sum() { //子类没有,到父类中找
  13. return getI() + 10; //调用 getI() 会根据运行类型寻找调用,因此调用子类中的
  14. }
  15. public int sum1() { //子类没有,到父类中找
  16. return i + 10; //属性没有动态绑定机制,哪里声明,哪里使用
  17. }
  18. public int getI() {
  19. return i;
  20. }
  21. }
  22. class B extends A {
  23. public int i = 20;
  24. public int getI() {
  25. return i;
  26. }
  27. }

8.6 多态的应用

  1. 多态数组
    数组的定义类型为父类类型,里面保存的实际元素类型为子类类型

应用实例:现有一个继承结构如下:要求创建1个Person、2个Student对象和2个Teacher对象,统一放在数组中,并调用每个对象say方法。
应用实例升级:如何调用子类特有的方法,比如Teacher有一个tech,Student有一个study,怎么调用?

面向对象编程(中级部分) - 图8

  1. package com.hspedu.poly_.polyarray_;
  2. public class PolyArray {
  3. public static void main(String[] args) {
  4. Person[] person = new Person[5];
  5. person[0] = new Person("Tom",15);
  6. person[1] = new Student("小明",19,88.6); //向上转型
  7. person[2] = new Student("小强",20,85.5); //向上转型
  8. person[3] = new Teacher("张飞",38,20000); //向上转型
  9. person[4] = new Teacher("关羽",47,25000); //向上转型
  10. for (int i = 0; i < person.length; i++) {
  11. //person[i] 编译类型是Person,运行类型根据实际情况由JVM来判断
  12. System.out.println(person[i].say()); //动态绑定机制
  13. if (person[i] instanceof Student) { //类型判断
  14. System.out.println(((Student) person[i]).study()); //向下转型
  15. } else if (person[i] instanceof Teacher) {
  16. System.out.println(((Teacher) person[i]).teach()); //向下转型
  17. }
  18. }
  19. }
  20. }
  21. class Person {
  22. private String name;
  23. private int age;
  24. public Person(String name, int age) {
  25. setName(name);
  26. setAge(age);
  27. }
  28. public String say() {
  29. return "name:" + getName() + "\tage:" + getAge();
  30. }
  31. public String getName() {
  32. return name;
  33. }
  34. public void setName(String name) {
  35. this.name = name;
  36. }
  37. public int getAge() {
  38. return age;
  39. }
  40. public void setAge(int age) {
  41. this.age = age;
  42. }
  43. }
  44. class Student extends Person {
  45. private double score;
  46. public Student(String name, int age, double score) {
  47. super(name, age);
  48. setScore(score);
  49. }
  50. public double getScore() {
  51. return score;
  52. }
  53. public void setScore(double score) {
  54. this.score = score;
  55. }
  56. public String say() { //重写父类 say() 方法
  57. return "学生 | " + super.say() + "\tscore:" + getScore();
  58. }
  59. //特有方法
  60. public String study() {
  61. return getName() + "在学习...";
  62. }
  63. }
  64. class Teacher extends Person {
  65. private double salary;
  66. public Teacher(String name, int age, double salary) {
  67. super(name, age);
  68. setSalary(salary);
  69. }
  70. public double getSalary() {
  71. return salary;
  72. }
  73. public void setSalary(double salary) {
  74. this.salary = salary;
  75. }
  76. public String say() { //重写父类 say() 方法
  77. return "老师 | " + super.say() + "\tsalary:" + getSalary();
  78. }
  79. //特有方法
  80. public String teach() {
  81. return getName() + "在教书...";
  82. }
  83. }
  1. 多态参数
    方法定义的形参类型为父类类型,实参类型允许为子类类型。

应用实例1:前面的主人喂动物

应用实例2:
定义员工类 Employee,包含姓名和月工资[private],以及计算年工资getAnnual 的方法。普通员工和经理继承了员工,经理类多了奖金 bonus 属性和管理 manage 方法,普通员工多了work 方法,普通员工和经理类要求分别重写getAnnual 方法

测试类中添加一个方法 showEmpAnnal(Employee e),实现获取任何员工对象的年工资,并在 main 方法中调用该方法【e.getAnnual()】
测试类添加一个方法,testWork,如果是普通员工,则调用work 方法,如果是经理,则调用 manage 方法

com.hspedu.poly.emply
Test.java
NormalEmployee.java
Manager.java
Employee.java

九、Object 类详解

image.png

9.1 equals 方法

== 与 equals 的对比

==:是一个 比较运算符,既可以判断基本类型,又可以判断引用类型;如果判断基本类型,判断的是值是否相等;如果判断引用类型,判断的是地址是否相等,即判断是不是同一个对象。

equals:是 Object 类中的方法,只能判断引用类型,默认判断的是地址是否相等,子类中往往重写该方法,用于判断内容是否相等。比如Integer,String 【查看 String 和 Integer 的 equals 源代码】

Tips:如何查看 Jdk 源代码?
IDEA 中,默认会配置好源代码关联,只需在用 equals方法时,在equals 上按 Ctrl + B 即可跳转到关于 equals 的源代码的位置。
如果没有默认关联,可以通过以下方式建立关联:

  1. 菜单栏 File -> Project Structure… -> SDKs -> Sourcepath
  2. 点击右侧加号“+”,找到本地jdk安装位置,将 javafx-src.zipsrc.zip 两个文件添加进来
  3. 点击 Apply 以应用关联设置,点击 OK 保存。

9.2 重写 equals 方法

应用实例:判断两个Person对象的内容是否相等,如果两个 Person 对象的各个属性值都一样,则返回 true,反之 false。

  1. package com.hspedu.poly_.object_;
  2. public class EqualsExercise {
  3. public static void main(String[] args) {
  4. Person person = new Person("jack", 10, '男');
  5. Person person1 = new Person("jack", 10, '男');
  6. Person person2 = new Person("Alice", 8, '女');
  7. System.out.println(person.equals(person1));
  8. System.out.println(person.equals(person2));
  9. }
  10. }
  11. class Person {
  12. private String name;
  13. private int age;
  14. private char gender;
  15. public Person(String name, int age, char gender) {
  16. this.name = name;
  17. this.age = age;
  18. this.gender = gender;
  19. }
  20. public boolean equals(Object obj) {
  21. //如果比较的两个对象是同一个对象,直接返回 true
  22. if (this == obj) {
  23. return true;
  24. }
  25. //类型判断
  26. if (obj instanceof Person) { //是 Person 才比较
  27. //进行向下转型,因为要得到 obj 的各个属性
  28. Person p = (Person) obj;
  29. return this.name.equals(p.name) && this.age == p.age && this.gender == p.gender;
  30. }
  31. return false;
  32. }
  33. }

这里可能会绕晕,要搞清楚 equals 运行本质:

  1. AAA.equals(BBB) :AAA 调用了 equals 对象,传入的参数(实参)是 BBB
  2. 到了 public boolean equals(Object obj) 方法中:this 指代的是 AAA,obj 指代的是 BBB

⏱ 小练习

  1. 问下面代码输出结果是什么?

    1. public class EqualsExercise02 {
    2. public static void main(String[] args) {
    3. Persons p1 = new Persons();
    4. p1.name = "hspedu";
    5. Persons p2 = new Persons();
    6. p2.name = "hspedu";
    7. System.out.println(p1 == p2);
    8. System.out.println(p1.name.equals(p2.name));
    9. System.out.println(p1.equals(p2));
    10. String s1 = new String("asdf");
    11. String s2 = new String("asdf");
    12. System.out.println(s1.equals(s2));
    13. System.out.println(s1 == s2);
    14. }
    15. }
    16. class Persons {
    17. public String name;
    18. }
  2. 问下面代码输出什么? ```java package com.hspedu.poly.object;

public class EqualsExercise03 { public static void main(String[] args) { int it = 65; float fl = 65.0f; System.out.println(“65和65.0f是否相等?” + (it == fl));
char ch1 = ‘A’; char ch2 = 12; System.out.println(“65和‘A’是否相等?” + (it == ch1));
System.out.println(“12和ch2是否相等?” + (12 == ch2));

  1. String str1 = new String("hello");
  2. String str2 = new String("hello");
  3. System.out.println("str1和str2是否相等?" + (str1 == str2));
  4. System.out.println("str1和str2是否equals?" + (str1.equals(str2)));
  5. System.out.println("hello" == new java.sql.Date());
  6. }

}

  1. <a name="Vozcm"></a>
  2. #### 9.3 hashCode 方法
  3. hashCode 用于返回该对象哈希码值。
  4. 使用细节:(重要)
  5. 1. 提高具有哈希结构的容器效率!
  6. 1. 两个引用,如果指向的是同一个对象,则哈希值肯定是一样的!
  7. 1. 两个引用,如果指向的是不同对象,则哈希值是不一样的
  8. 1. 哈希值主要根据地址号生成!不能完全将哈希值等价于地址
  9. 1. 案例演示:HashCode_.java:obj.hashCode()
  10. 1. 后面在集合中 hashCode 如果需要的话,也会重写
  11. <a name="fyWk3"></a>
  12. #### 9.4 toString 方法
  13. 1. 基本介绍<br />默认返回:全类名+@+哈希值的十六进制,【查看 Object 的 toString 方法】<br />子类往往重写 toString 方法,用于返回对象的属性信息
  14. 1. 重写 toString 方法,打印对象或拼接对象时,都会自动调用该对戏对象的 toString 形式
  15. 1. 当直接输出一个对象时,toString 方法会被默认的调用<br />比如:System._out_.println(monster);
  16. <a name="FgGrk"></a>
  17. #### 9.5 finalize 方法
  18. 1. 当对象被回收时,系统自动调用该对象的 finalize方法。子类可以重写该方法,做一些释放资源的操作
  19. 1. 什么时候被回收:当某个对象没有任何引用时,则 jvm 就认为这个对象是一个垃圾对象,就会使用垃圾回收机制来销毁该对象,在销毁对象前,会先调用 finalize 方法
  20. 1. 垃圾回收机制的调用,是由系统来决定(GC算法),也可以通过 System.gc() 主动触发垃圾回收机制
  21. <a name="w8Jzv"></a>
  22. ### 十、断点调试(debug)
  23. <a name="LzdDo"></a>
  24. #### 10.1 基本介绍
  25. 一个实际需求
  26. 1. 在开发中,新手程序员在查找错误时,这时老程序员就会温馨提示,可以用断点调试,一步一步的看源码执行的过程,从而发现错误所在。
  27. 1. 重要提示:在断点调试过程中,是运行状态,是以对象的**运行类型**来执行的。
  28. 断点调试介绍
  29. 1. 断点调试是指在程序的某一行设置一个断点,调试时,程序运行到这一行就会停住,然后你可以一步一步往下调试,调试过程中可以看到各个变量当前的值,出错的话,调试到出错的代码行即显示错误,停下。进行分析从而找到这个Bug
  30. 1. 断点调试是程序员必须掌握的技能
  31. 1. 断点调试也能帮助我们查看Java底层源代码的执行过程,提高程序员的Java水平
  32. <a name="mM1RI"></a>
  33. #### 10.2 断点调试的快捷键
  34. 1. F7:跳入方法内
  35. 1. F8:跳过,逐行执行代码时使用
  36. 1. Shift + F8:跳出方法
  37. 1. F9:resume,执行到下一个断点
  38. ![image.png](https://cdn.nlark.com/yuque/0/2021/png/12677686/1630986326279-f13913f8-83db-4096-abba-ac110095c9ae.png#clientId=uad773c06-d2ca-4&from=paste&height=477&id=u0a3c1cb7&margin=%5Bobject%20Object%5D&name=image.png&originHeight=477&originWidth=1001&originalType=binary&ratio=1&size=244880&status=done&style=none&taskId=u9e3654cc-ba14-4041-b8a8-c5bd870f290&width=1001)
  39. <a name="lvfNm"></a>
  40. #### 10.3 debug 应用
  41. 1. 逐行解析运行过程中变量的变化情况<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/12677686/1630996057915-7bc55d7c-8c2f-420f-9b38-1f1dcba2b209.png#clientId=u29cf051a-288b-4&from=paste&height=1117&id=uf34b9e51&margin=%5Bobject%20Object%5D&name=image.png&originHeight=1117&originWidth=1364&originalType=binary&ratio=1&size=155908&status=done&style=shadow&taskId=u5a4c736f-baba-4f65-b726-28dda9abc2d&width=1364)
  42. 1. 查看数组越界异常<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/12677686/1630996457161-ff47393b-f957-42b9-b417-ab15addfc88b.png#clientId=u29cf051a-288b-4&from=paste&height=1074&id=u36df74c3&margin=%5Bobject%20Object%5D&name=image.png&originHeight=1074&originWidth=1479&originalType=binary&ratio=1&size=184709&status=done&style=shadow&taskId=u0538ea38-ad10-45fe-bdf0-204cdb5a739&width=1479)
  43. 1. 追源代码,看看Java的底层实现<br />小技巧:将光标放在某个变量上,可以看到最新的数据。
  44. ![](https://cdn.nlark.com/yuque/0/2021/jpeg/12677686/1630998074164-bd0eb0ac-055b-4bbb-b04d-0fd7a77e82b3.jpeg)<br />[IDEA Debug 如何进入 JDK源码](https://www.yuque.com/harborgao/java/hv1cya?view=doc_embed)
  45. 4. 执行到下一个断点 F9 resume<br />提示:可以在 Debug 过程中,动态的下断点
  46. 5. 使用断点调试的方法,追踪下一个对象创建的过程。
  47. 5. 使用断点调试,查看动态绑定机制如何工作
  48. <a name="v0ogN"></a>
  49. ### 十一、本章作业
  50. 1. 定义一个Person类{name, age, job},初始化Person对象数组,有3个person对象,并按照age从大到小进行排序,提示,使用冒泡排序 Homework01.java
  51. 1. 写出四种访问修饰符和各自的访问权限 Homework02.java
  52. 3. 编写老师类 Homework03.java
  53. 1. 要求有属性”姓名name","年龄age" ,“职称post” ,“基本工资salary”
  54. 1. 编写业务方法,introduce (),实现输出一个教师的信息。
  55. 1. 编写教师类的三个子类:教授类、副教授类、讲师类。工资级别分别为:教授为1.3、副教授为1.2、讲师类1.1。在三个子类里面都重写父类的introduce ()方法。
  56. 1. 定义并初始化一个老师对象,调用业务方法,实现对象基本信息的后台打印。
  57. 4. 通过继承实现员工工资核算打印功能
  58. 父类:员工类<br />子类:部门经理类、普通员工类<br />(1) 部门经理工资 = 1000+单日工资*天数*等级(1.2)<br />(2) 普通员工工资 = 单日工资*天数*等级(1.0) ;<br />(3) 员工属性:姓名,单日工资,工作天数<br />(4) 员工方法(打印工资)<br />(5) 普遍员工及部门经理都是员工子类,需要重写打印工资方法。<br />(6) 定义并初始化普通员工对象,调用打印工资方法输入工资,定义并初始化部门经理对象,调用打印工资方法输入工资
  59. 5. 设计父类一员工类。 子类:工人类,农民类,教师类,科学家类,服务生类。
  60. (1) 其中工人,农民,服务生只有基本工资<br />(2) 教师除基本工资外,还有课酬(元/天)<br />(3) 科学家除基本工资外,还有年终奖<br />(4) 编写一个测试类,将各种类型的员工的全年工资打印出来
  61. 6. 在父类和子类中通过this和super都可以调用哪些属性和方法,假定 Grand、Father 和 Son 在同一个包
  62. ```java
  63. class Grand {
  64. String name = "AA";
  65. private int age = 100;
  66. public void g1(){}
  67. }
  68. class Father extends Grand {
  69. String id="001";
  70. private double score;
  71. public void f1(){}
  72. //super可以访问哪些成员(属性和方法) ?
  73. //this可以访问哪些成员?
  74. }
  75. class Son extends Father {
  76. String name="BB";
  77. public void g1(){}
  78. private void show() {}
  79. //super可以访问哪些成员(属性和方法)?
  80. //this可以访问哪些成员?
  81. }
  1. 写出程序结果

    1. class Test{
    2. String name = "Rose";
    3. Test(){
    4. System.out.println("Test");
    5. }
    6. Test(String name){
    7. this.name = name;
    8. }
    9. }
    10. class Demo extends Test {
    11. String name= "Jack";
    12. Demo() {
    13. super();
    14. System.out.println("Demo");
    15. }
    16. Demo(String s){
    17. super(s);
    18. }
    19. public void test(){
    20. System.out.println(super.name);
    21. System.out.println(this.name);
    22. }
    23. public static void main(String[] args) {
    24. new Demo().test();
    25. new Demo("john").test();
    26. }
    27. }
  2. 扩展如下的BankAccount类

    1. class BankAccount {
    2. private double balance ;
    3. public BankAccount(double initialBalance) {
    4. this.balance = initialBalance;
    5. }
    6. public void deposit(double amount) {
    7. balance += amount;
    8. }
    9. public withdraw(double amount) {
    10. balance -= amount;
    11. }
    12. }

    要求:
    (1) 在上面类的基础上扩展新类CheckingAccount 对每次存款和取款都收取1美元的手续费
    (2) 扩展前一个练习的BankAccount类,新类SavingsAccount每个月都有利息产生(earnMonthlyInterest方法被调用),并且有每月三次免手续费的存款或取款。在earnMonthlyInterest方法中重置交易计数

  3. 设计一个Point类,其x和y坐标可以通过构造器提供。提供一个子类 LabeledPoint,其构造器接受一个标签值和x,y坐标,比如:new LabeledPoint( “BlackThursday” ,1929,230.07),写出对应的构造器即可

  4. 编写Doctor类{name, age, job, gender, sal}相应的getter(和setter()方法,5个参数的构造器,重写父类的equals()方法: public boolean equals(Object obj),并判断测试类中创建的两个对象是否相等。相等就是判断属性是否相同

  5. 现有Person类,里面有方法run、eat, Student类继承了Person类, 并重写了run方法,自定义了study方法,试写出对象向上转型和向下转型的代码,并写出各自都可以调用哪些方法,并写出方法输出什么?

    1. class Person {
    2. public void run() {System.out.println("person run"); }
    3. public void eat() {System.out.println(" person eat" ); }
    4. }
    5. class Student extends Person {
    6. public void run() {System.out.println("student run"); }
    7. public void study) {System.out.println(' 'student study.' ); }
    8. }
  6. 说出==和equals的区别

  7. 打印效果如下

老师的信息:
姓名:王飞
年龄: 30
性别:男
工龄: 5
我承诺,我会认真教课。
王飞爱玩象棋
———————————————-
学生的信息:
姓名:小明
年龄: 15
性别:男
学号: 00023102
我承诺,我会好好学习。
小明爱玩足球。

案例题目描述:
(1) 做一个Student类,Student类有名称 (namne) ,性别(sex),年龄(age),学号(stu id),做合理封装,通过构造器在创建对象时将4个属性赋值。
(2) 写一个Teacher类,Teacher类有名称(name) ,性别(sex) ,年龄(age) ,工龄(work age),做合理封装,通过构造器在创建对象时将4个属性赋值。
(3) 抽取一个父类Person类, 将共同属性和方法放到Person类
(4) 学生需要有学习的方法(study) ,在方法里写生“我承诺,我会好好学习。
(5) 教师需要有教学的方法(teach) ,在方法里写.上“我承诺,我会认真教学。
(6) 学生和教师都有玩的方法(play) ,学会玩的是足球,老师玩的是象棋,此方法是返回字符串的,分别返回”xx爱玩足球”和”xx爱玩象棋”(其中xx分别代表学生和老师的姓名) 。因为玩的方法名称都一样,所以要求此方法定义在父类中,子类实现重写。
(7) 定义多态数组,里面保存2个学生和2个教师,要求按年龄从高到低排序,
(8) 定义方法,形参为Person类型,功能:调用学生的study或教师的teach方法

  1. 程序阅读题在mian方法中执行: C c =new C();输出什么内容?

    1. class A{
    2. public A(){
    3. System.out.println("我是A类");
    4. }
    5. }
    6. class B extends A{
    7. public B(){
    8. System.out.println("我是B类的无参构造");
    9. }
    10. public B(String name){
    11. System.out.println(name+ "我是B类的有参构造");
    12. }
    13. }
    14. class C extends B{
    15. public C(){
    16. this( "hello");
    17. System.out.println("我是c类的无参构造");
    18. }
    19. public C(String name){
    20. super("hahah");
    21. System.out.println("我是c类的有参参构造");
    22. }
    23. }
  2. 什么是多态,多态具体体现有哪些? (可举例说明)

  3. java的动态绑定机制是什么?

学习参考(致谢):

  1. B站 @程序员鱼皮 Java学习一条龙
  2. B站 @韩顺平 零基础30天学会Java