0263 0360 第八章 面向对象编程(中级部分)
8.3 IDE(集成开发环境)- IDEA 的使用¶
8.3.5 IDEA 常用快捷键¶
1) 删除当前行, 默认是 ctrl + Y 自己配置 ctrl + d 2) 复制当前行, 自己配置 ctrl + alt + 向下光标 3) 补全代码 alt + / 4) 添加注释和取消注释 ctrl + / 【第一次是添加注释,第二次是取消注释】 5) 导入该行需要的类先配置 auto import , 然后使用 alt+enter 即可 6) 快速格式化代码 ctrl + alt + L 7) 快速运行程序自己定义 alt + R 8) 生成构造器等 alt + insert 9) 查看一个类的层级关系 ctrl + H 10) 将光标放在一个方法上,输入 ctrl + B , 可以定位到方法 11) 自动的分配变量名 , 通过在后面假 .var 12) 还有很多其它的快捷键...
8.4 包¶
8.4.2 包的三大作用¶
- 区分相同名字的类
- 当类很多时,可以很好的管理类
- 控制访问范围
8.4.3 包基本语法¶
package com.hsp
说明:
- package 关键字,表示打包
- com.hsp 标识包名
8.4.4 包的本质分析 (原理)¶
包的本质实际上就是创建不同的文件夹/目录来保存类文件,画出示意图 ![[8.4.4-1包的本质.png]]
8.4.6 包的命名¶
- 只能包含数字、字母、下划线、小圆点.胆不能用数字开头,
- 不能是关键字或保留字
demo.class.exec//错误 classæ关键字demo. 12a//错误 12 a 是数字开头demo.abl2oa//对
命名规范
一般是小写字母+小圆点一般是 com.公司名.项目名.业务模块名
- 比如:
- com.hspedu.oa.model;
- com.hspedu.oa.controller;
- com.sina.crm.user //用户模块
- com.sina.crm.order //订单模块
- com.sina.crm.utils //工具类
8.4.7 常用的包¶
一个包下, 包含很多的类, java 中常用的包有:
1) java.lang.* // lang 包是基本包,默认引入,不需要再引入.
2) java.util.* // util 包,系统提供的工具包, 工具类,使用 Scanner
3) java.net.* // 网络包,网络开发
4) java.awt.* // 是做 java 的界面开发,GUI
8.4.8 如何引入包¶
语法:import 包;com.hspedu.pkg
- 我们引入一个包的主要目的是要使用该包下的类
- 比如
- import java.util.Scanner; 就只是引入一个类 Scanner .
- importjava. util.*; //表示将 java. util 包所有都引入
- 建议用什么类导入什么类
8.4.9 注意事项和使用细节¶
package的作用是声明当前类所在的包,需要放在类的最上面,一个类中最多只有一句packageimport指令位置放在package的下面,在类定义前面,可以有多句且没有顺序要求。
8.5 访问修饰符¶
[[类的访问修饰符]]
8.5.1 基本介绍¶
- java 提供四种访问控制修饰符号,用于控制方法和属性 (成员变量) 的访问权限(范围) :
1) 公开级别: 用
public修饰, 对外公开 2) 受保护级别: 用protected修饰, 对子类和同一个包中的类公开 3) 默认级别: 没有修饰符号, 向同一个包的类公开. 4) 私有级别: 用private修饰, 只有类本身可以访问, 不对外公开
8.5.2 4 种访问修饰符的访问范围¶
| 访问级别 | 访问控制修饰符 | 同一个类 | 同一个包 | 子类 | 不同的包 |
|---|---|---|---|---|---|
| 公开 | public |
√ | √ | √ | √ |
| 受保护 | protected |
√ | √ | √ | × |
| 默认 | 无修饰符 |
√ | √ | × | × |
| 私有 | private |
√ | × | × | × |
- public: 全部公开 |
|||||
- protected 保护到包内和子类 |
|||||
- 无修饰符 保护到当前包 |
|||||
- private 保护到当前类 |
|||||
| --- | |||||
| ### 8.5.3 使用的注意事项 |
- 修饰符可以用来修饰类中的属性,成员方法以及类
- 只有
默认的和public才能修饰类,井且遵循上述访问权限的特点。 - 因为没有学习继承,因此关于在子类中的访问权限,我们讲完子类后,在回头讲解
- 成员方法的访问规则和属性完全一样.
8.6 面向对象编程三大特征¶
8.6.1 基本介绍¶
> 面向对象编程有三大特征: 封装、继承和多态¶
8.6.2 封装介绍¶
> 封装(encapsulation)就是把抽象的数据 属性 和对数据的操作 方法 封装在一起,数据被保护再数据内部,程序的其他部分只有通过被授权的操作 方法,才能对数据进行操作。¶
8.6.3 封装的理解和好处¶
- 隐藏实现细节:方法(连接数据库)← 调用(传入参数...)
- 可以对数据进行验证,保证安全合理
8.6.4 封装的实现步骤(三步)¶
- 将属性进行私有化
private不能直接修改属性 - 提供一个公共的
public set 方法,用于对属性判断并赋值public void setXXX(类型 参数名){// XXX 表示某个属性// 加入数据验证的业务逻辑属性 = 参数名;} - 提供一个公共的
public get 方法,用于获取属性的值public void getXXX(类型 参数名){// XXX 表示某个属性// 加入数据验证的业务逻辑return xx;}
8.7 快速入门案例¶
请大家看一个程序 (com.hspedu.encap), 不能随便查看人的年龄, 工资等隐私,并对设置的年龄进行合理的验证。
年龄合理就设置,否则给默认年龄, 必须在 \(1 - 120\), 年龄,工资不能直接查看, name 的长度在 \(2-6\) 字符之间
import java.util.Scanner;
public class Main{
public static void main(String[] args)
{
Person p = new Person();
Scanner scanner = new Scanner(System.in);
System.out.println("请输入姓名:");
p.setName(scanner.next());
System.out.println("请输入年龄:");
p.setAge(scanner.nextInt());
System.out.println("请输入薪水:");
p.setSalary(scanner.nextDouble());
int age = p.getAge();
double salary = p.getSalary();
String name = p.getName();
System.out.println("name:" + name +"\t" + "age:" + age + "\t" + "salary:" + salary);
}
}
class Person{
public String name;
private int age;
private double salary;
public String getName() {
return name;
}
public void setName(String name) {
if (name.length() <= 6 && name.length() >= 2){
this.name = name;
}else{
this.name = "未命名";
throw new IllegalArgumentException("输入的姓名有误");
}
}
public int getAge() {
return age;
}
public void setAge(int age) {
if (age >= 0 && age <= 120){
this.age = age;
}else{
this.age = 20;
}
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
}
8.7.1 将构造器和 setXxx 结合¶
//有三个属性的构造器
public Person(String name, int age, double salary) {
// this.name = name;
// this.age = age;
// this.salary = salary;
//我们可以将 set 方法写在构造器中,这样仍然可以验证
setName(name);
setAge(age);
setSalary(salary);
}
8.8 面向对象编程-继承¶
8.8.1 为什么需要继承¶
我们编写了两个类,一个 Pupil 类(小学生), 一个是 Graduate (大学毕业生).
问题:两个类的属性和方法有很多是相同的,怎么办?
→ 使用继承
8.8.2 继承基本介绍和示意图¶
继承可以解决代码复用, 让我们的编程更加靠近人类思维。 当多个类存在相同的属性 (变量) 和方法时, 可以从这些类中抽象出父类, 在父类中定义这些相同的属性和方法,所有的子类不需要重新定义这些属性和方法,只需要通过 extends 来声明继承父类即可。
graph LR
B("B类 (子类, 派生类) <br> 特有属性 <br> 特有方法") -- extends --> A("A类 (父类, 基类, 超类) <br> 共用属性 <br> 共有方法")
C("C类 (子类, 派生类) <br> 特有属性 <br> 特有方法") -- extends --> A
D("D类 (子类, 派生类) <br> 特有属性 <br> 特有方法") -- extends --> B
E("E类 (子类, 派生类) <br> 特有属性 <br> 特有方法") -- extends --> C
¶
graph LR
B("B类 (子类, 派生类) <br> 特有属性 <br> 特有方法") -- extends --> A("A类 (父类, 基类, 超类) <br> 共用属性 <br> 共有方法")
C("C类 (子类, 派生类) <br> 特有属性 <br> 特有方法") -- extends --> A
D("D类 (子类, 派生类) <br> 特有属性 <br> 特有方法") -- extends --> B
E("E类 (子类, 派生类) <br> 特有属性 <br> 特有方法") -- extends --> C
8.8.3 继承的基本语法¶
class 子类 extends 父类{ }
- 子类会自动拥有父类定义的属性和方法
- 父类又叫超类、基类
- 子类又叫派生类
8.8.5 继承给编程带来的便利¶
- 代码的复用性提高了
- 代码的扩展性和维护性提高了
8.8.6 继承的深入讨论/细节问题¶
- 子类继承了所有的属性和方法,非私有的属性和方法可以在子类直接访问, 但是私有属性和方法不能在子类直接访问,要通过父类提供公共的方法去访问
- 子类必须调用父类的构造器,完成父类的初始化
- → 子类的构造器第一句默认是
super()
- → 子类的构造器第一句默认是
- 当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器,
如果父类没有提供无参构造器,则必须在子类的构造器中用 super 去指定使用父类的哪个构造器完成对父类的初始化工作
否则,编译不会通过。
- →
super(a,b) - ![[0191 - 0262 第七章 面向对象编程(基础部分)#7.8.5 注意事项和使用细节]]
- →
- 如果希望指定去调用父类的某个构造器,则显式的调用一下 :
super(参数列表) - super 在使用时,必须放在构造器第一行 (super 只能在构造器中使用)
- super () 和 this () 都只能放在构造器第一行,因此这两个方法不能共存在一个构造器
- ![[0191 - 0262 第七章 面向对象编程(基础部分)#7.10.3 this 的注意事项和使用细节]]
- java 所有类都是 Object 类的子类, Object 是所有类的基类
- 父类构造器的调用不限于直接父类!将一直往上追溯直到 Object 类 (顶级父类)
- 子类最多只能继承一个父类 (指直接继承),即 java 中是单继承机制。
- 思考:如何让 A 类继承 B 类和 C 类? 【A 继承 B, B 继承 C】
- 不能滥用继承,子类和父类之间必须满足 is-a 的逻辑关系
Cat is a Animal
8.8.7 继承的本质分析 (重要)¶
我们看一个案例来分析当子类继承父类,创建子类对象时,内存中到底发生了什么?当子类对象创建好后,建立查找的关系 - 查找关系 - 首先看子类是否有该属性 - 如果子类有这个属性,并且可以访问,则返回信息 - 如果子类没有这个属性,就看父类有没有这个属性 (如果父类有该属性,并且可以访问,就返回信息..) - 如果父类没有就按照 (3) 的规则,继续找上级父类,直到 Object
![[继承的本质分析.png]]
graph TD
A[堆内存 Heap] --> B[ChildClass 对象实例]
B --> C[对象头 Object Header<br>12 字节]
B --> D[实例数据 Instance Data]
B --> E[对齐填充 Padding<br>0-7 字节]
C --> C1[Mark Word<br>8 字节<br>哈希码、锁状态、GC年龄等]
C --> C2[Class Pointer<br>4 字节<br>指向方法区中 ChildClass 元数据]
D --> D1[父类字段 Parent Fields]
D --> D2[子类字段 Child Fields]
D1 --> D1_1[parentLong: long<br>8 字节]
D1 --> D1_2[parentInt: int<br>4 字节]
D2 --> D2_1[childInt: int<br>4 字节]
D2 --> D2_2[childBoolean: boolean<br>1 字节]
E --> E1[填充字节<br>3 字节<br>确保总大小为 8 的倍数]
8.8.8 课堂练习¶
class A{
A(){ System.outprintlnC"a"); }
A(String name) {System.outprintlnC"a name");}
}
class B extends A{
B(){
this("abc"); System.out.printlnC"b");
}
B(String name){
System.out.println("b name");
}// 分析有默认的super。;
}
// main中:B b = new B(); 会输出什么?
// a ,b name ,b
// 栈内先压进B();--> 由于有 this() 不能 super()
// B();调用了B(String name);
// B(String name)默认包含 super
// 最后压栈A()
8.9 super 关键字¶
8.9.1 基本介绍¶
super代表父类的引用,用于访问父类的属性、方法、构造器
8.9.2 基本语法¶
访问父类的属性,但不能访问父类的
private属性 -super.属性名;访问父类的方法,不能访问父类的
private方法 -super.方法名(参数列表);访问父类的构造器(这点前面用过): -
super(参数列表);- 只能放在构造器的第一句,只能出现一句!
super的访问不限于直接父类,如果爷爷类和本类中有同名的成员,也可以使用super去访问爷爷类的成员;如果多个基类 (上级类) 中都有同名的成员,使用
super访问遵循就近原则。 - A->B->C
8.9.3 super 给编程带来的便利/细节¶
- 调用父类的构造器的好处(分工明确,父类属性由父类初始化,子类的属性由子类初始化)
- 当子类中有和父类中的成员(属性和方法)重名时,为了访问父类的成员,必须通过 super。
- 如果子类中没有和父类中的成员(属性和方法)重名,使用
super、this、直接访问是一样的效果!- 由于子类没用,会直接按顺序查找属性和方法
- [[#8.9.2 基本语法]]
- 不可以
super(). super()来越级访问父类的父类 1
8.9.4 super 和 this 的比较¶
| NO | 区别点 | this | super |
|---|---|---|---|
| 1 | 访问属性 | 访问本类中的属性,如果本类没有此属性,则从父类中继续查找 | 从父类开始查找属性 |
| 2 | 调用方法 | 访问本类中的方法,如果本类没有此方法,则从父类中继续查找 | 从父类开始查找方法 |
| 3 | 调用构造器 | 调用本类构造器,必须放在构造器的首行 | 调用父类构造器,必须放在子类构造器的首行 |
| 4 | 表示当前对象 | 表示当前对象的引用 | 表示父类对象的引用 |
8.10 方法重写/覆盖 (override)¶
8.10.1 基本介绍¶
简单的说: 方法覆盖(重写)就是子类有一个方法,和父类的某个方法的名称、返回类型、参数一样,那么我们就说子类的这个方法覆盖了父类的方
- 子类方法不能缩小父类方法的访问权限
public > protected > 默认 > private- 理解成父类公开的内容是传家宝,要一直遗传下去,不能被某一代私有
- 例如公共有的内容:人的头发等等
- 如果其中一代私有,那就不能遗传给下一代,所以出错
8.10.3 注意事项和使用细节¶
方法重写也叫方法覆盖,需要满足下面的条件
- 子类的方法的形参列表,方法名称,要和父类方法的形参列表,方法名称完全一样。
- 子类方法的返回类型和父类方法返回类型一样,或者是父类返回类型的子类
- 比如父类返回类型是
Object,子类方法返回类型是String public Object getInfo(){}public String getInfo(){}
- 比如父类返回类型是
- 子类方法不能缩小父类方法的访问权
| 名称 | 发生范围 | 方法名 | 形参列表 | 返回类型 | 修饰符 |
|---|---|---|---|---|---|
| 重载 (overload) | 本类 | 必须一样 | 类型、个数或顺序至少有一个不同 | 无要求 | 无要求 |
| 重写 (override) | 父子类 | 必须一样 | 相同 | 子类重写的方法,返回的类型和父类返回的类型一致,或者是其子类 | 子类方法不能缩小父类方法的访问范围 |
8.11 面向对象编程 - 多态¶
8.11.2 多 (多种) 态 (状态) 基本介绍¶
方法或对象具有多种形态。 是面向对象的第三大特征,多态是建立在封装和继承基础之上的。
8 .11.3 多态的具体体现¶
[[多态]]¶
- 介绍
- 方法的多态 [[多态#1. 编译时多态(静态多态)]]
- 重写和重载就体现多态
- 对象的多态 (核心,困难,重点) [[多态#2. 运行时多态(动态多态)]]
细节¶
- 创建的对象是
Cat,animal是一个引用,指向内存中的真正对象cat - 编译时,可以用父类的引用指向一个子类的对象
- 运行时,是什么类就是什么类
public class Main {
public static void main(String[] args) {
//animal 编译类型就是 Animal , 运行类型 Dog Animal animal = new Dog();
//因为运行时 , 执行到改行时, animal 运行类型是 Dog,所以 cry 就是 Dog 的 cry animal.cry();
//animal 编译类型 Animal,运行类型就是 Cat animal = new Cat();
animal.cry(); //小猫喵喵叫
System.out.println("===========================");
animal = new Animal();
animal.cry();
}
}
class Animal {
public void cry(){
System.out.println("Animal.cry() 动物叫");
}
}
class Cat extends Animal {
public void cry(){
System.out.println("Cat.cry() 猫叫");
}
}
class Dog extends Animal {
public void cry(){
System.out.println("Dog.cry() 狗叫");
}
}
8.11.5 多态注意事项和细节讨论¶
多态的前提是:两个对象(类)存在继承关系
多态的向上转型¶
本质:父类的引用指向了子类的对象 语法:父类类型引用名= new 子类类型0;
Animal a = new Dog()
- 特点:编译类型看左边,运行类型看右边。
- 可以调用父类中的所有成员(需遵守访问权限)
- 不能调用子类的私有成员,由于编译阶段,能调用哪些成员,是由编译类型决定的。
- 最终的运行效果看子类的具体实现
- 访问权限(
public、protected、默认、private)限制了多态的调用范围- 注意,这点因为子类不可访问会被限制
private,默认修饰符 - 由于编译器认为是父类,父类中被限制的方法如果调用会过不了编译器
- 而由于看待这个对象的时候是
animal所以不能访问子类的私有内容
- 注意,这点因为子类不可访问会被限制
[[多态#遵守访问权限]]
多态向下转型¶
语法:
子类类型 引用名 = (子类型) 父类引用;
Animal animal = new Cat()Cat animal_cat = (Cat) animal- animal 实则为 Cat 类的对象 - 把 Cat 类的对象转成 Cat 类,代表把原来隐藏的虚函数解开
- 只能强转父类的引用,不能强转父类的对象
- 要求父类的引用必须指向的是当前目标类型的对象
- 当向下转型后,可以调用子类类型中所有的成员
不能调用子类中特有成员;最终运行效果看子类的具体实现
instanceOf() 比较操作符,用于判断对象的运行类型是否为 XX 类型或 XX 类型的子类型 ,返回一个 boolean
8.11.7 java 的动态绑定机制(非常非常重要.)¶
- 当调用对象方法的时候,该方法会和该对象的内存地址/运行类型绑定
- 当调用对象属性时,没有动态绑定机制,哪里声明,就在那里使用
class A{
public int i = 10;
public int sum(){
return get() + 10;
}
public int sum1(){
retrn i += 10;
}
public int getI(){
return i;
}
}
class B extends A{
public int i = 20;
public int sum(){
return i + 20;
}
public int getI(){
return i;
}
public int sum1(){
return i + 10;
}
}
// main 方法
A a = new B();
System.out.println(a.sum());
System.out.println(a.sum1());
// 40 30
8.11.8 多态的应用¶
多态数组¶
数组的定义类型为父类类型,里面保存的实际元素类型为子类类型
- 应用实例:现有一个继承结构如下:要求创建 1 个 Person 对象、 2 个 Student 对象和 2 个 Teacher 对象, 统一放在数组中,并调用每个对象say 方法.
- 应用实例升级:如何调用子类特有的方法,比如Teacher 有一个 teach , Student 有一个 study怎么调用 ![[Polymorphic_Array.java]]
多态参数¶
方法定义的形参类型为父类型,实参类型允许为子类类型
- 定义员工类
Employee, 包含姓名和月工资private, 以及计算年工资getAnnual的方法。普通员工和经理继承了员工,经理类多了奖金bonus属性和管理manage方法,普通员工类多了work方法,普通员工和经理类要求分别重写getAnnual方法 - 测试类中添加一个方法
showEmpAnnual(Employee e), 实现获取任何员工对象的年工资,并在main方法中调用该方法e.getAnnual() - 测试类中添加一个方法,
testwork,如果是普通员工,则调用work方法,如果是经理,则调用manage方法〃眼高手低
![[Dynamic_Binding.java]]
8.12 Object 类详解¶
8.12.1 equals 方法¶
==¶
是一个比较运算符 1. == :既可以判断基本类型,又可以判断引用类型 2. == : 如果判断基本类型,判断的是值是否相等。 示例:inti=10; double d=10.0; 3. == : 如果判断引用类型,判断的是地址是否相等,即判定是不是同一个对 4. 只要有基本数据类型,就判断值是否相等
equals :¶
- 是 Object类中的方法,只能判断引用类型
- 默认判断的地址是否相等,子类中往往重写该方法,用于判断内容是否相等。
- 比如
IntegerStringInteger中被重写成两个值 是不是相等String中的被重写成判断字符串是不是相等
8.12.2 如何重写 equals 方法¶
判断两个 Person 对象的内容是否相等,如果两个 Person 对象的各个属性值都一样,则返回 true,反之 false。
8.12.4 hashCode 方法¶
public int hashCode ()反回该对象的哈希码值。支持此方法是为了提高哈希表(例如java.utiLHashtable 提供的哈希表)的性能。
- hashCode 的常规协定是:
- 在 Java 应用程序执行期间,在时同一时象多次调用hashCode 方法时,必须一致地返回相同的整数,前提是将时象进行
- equals 比较时所用的信息没有被修改。从某一应用程序的一次执行到同一应用程序的另一次执行,该整数无需保持一致。
- 如果根据equals (Object) 方法,两个对象是相等的,那么对这两个对象中的每个对象调用hashCode 方法都必须生成相同的整数结果。
- 如果根据equals(java. lang. Object) 方法,两个对象不相等,那么对这两个对象中的任一对象上调用hashCode 方法不要求一定生成不同的整数结果。但是,程序员应该意识到,为不相等的时象生成不同整数结果可以提高哈希表的性能。
-
实际上,由Object 类定义的hashCode 方法确实会针对不同的对象返回不同的整数。(这一般是通过将该对象的内部地址转换成一个整数来实现的,但是Javak 编程语言不需要这种实现技巧。)
-
返回:
- 此对象的一个哈希码值。
1) 提高具有哈希结构的容器的效率! 2) 两个引用,如果指向的是同一个对象,则哈希值肯定是一样的! 3) 两个引用,如果指向的是不同对象,则哈希值是不一样的 4) 哈希值主要根据地址号来的, 不能完全将哈希值等价于地址。
8.12.5 toString 方法¶
-
基本介绍
- 默认返回:
全类名 + @ + 哈希值的十六进制 - 子类往往重写
toString方法,用于返回对象的属性信息
- 默认返回:
-
重写
toString方法,打印对象或拼接对象时,都会自动调用该对象的 toString 形式. -
当直接输出一个对象时 ,
toString方法会被默认的调用 - 比如
System.out.println(monster); - 就会默认调用
monster.toString()
8.12.6 finalize 方法¶
-
当对象被回收时,系统自动调用该对象的 finalize 方法。子类可以重写该方法,做一些释放资源的操作【演示】
-
什么时候被回收:当某个对象没有任何引用时,则 jvm 就认为这个对象是一个垃圾对象,就会使用垃圾回收机制来销毁该对象,在销毁该对象前,会先调用 finalize 方法。
-
垃圾回收机制的调用,是由系统来决定(即有自己的 GC 算法), 也可以通过
System.gc()主动触发垃圾回收机制
8.14 项目-零钱通¶
8.14.2 项目需求说明¶
使用 Java 开发 零钱通项目 , 可以完成收益入账,消费,查看明细,退出系统等功能.
8.14.3 项目的界面¶
![[8.14.3-1零钱通界面.png]]
8.14.4 项目代码实现¶
8.14.5 项目代码实现改进¶
注:¶
-
在 Java 中,不能直接使用
super.super来访问父类的父类(即祖父类)的成员或方法。这种语法是不合法的,编译器会报错。原因分析¶
super的作用:super关键字用于访问当前类的直接父类的成员(属性、方法)或构造方法。- 它只提供对直接父类的引用,无法直接跳跃到更上层的祖父类。
- 为什么不支持
super.super:- Java 的设计中,
super是一个单层引用,旨在简化继承关系中的访问逻辑。允许super.super会增加复杂性,并可能破坏封装性。 - 如果需要访问祖父类的成员,可以通过父类间接访问,但不能直接使用
super.super语法。
- Java 的设计中,
- MRO(方法解析顺序):
- 在 Java 的单继承体系中,方法解析顺序(MRO)是线性的(从子类到父类再到祖父类)。
super只负责处理当前类的直接父类,跳跃访问会破坏这种顺序。
- 在 Java 的单继承体系中,方法解析顺序(MRO)是线性的(从子类到父类再到祖父类)。
-
编译时(Compile Time) 定义: - 编译时是指程序代码被编译器(在Java中是
javac)从源代码(.java文件)转换为字节码(.class文件)的阶段。 - 这一阶段由编译器完成,主要进行语法检查、类型检查和代码优化,生成可供JVM执行的字节码。特点: - 静态阶段:在程序运行之前,代码还未实际执行。 - 主要任务: - 检查代码的语法是否正确(如括号匹配、关键字拼写)。 - 进行类型检查(如变量类型是否匹配)。 - 解析方法重载(Method Overloading),根据方法签名(方法名、参数类型和数量)确定调用哪个方法。 - 生成字节码,供运行时使用。 ↩↩
-
运行时(Run Time) 定义: - 运行时是指程序字节码(
.class文件)被Java虚拟机(JVM)加载并执行的阶段。 - 这一阶段程序实际运行,JVM解释或编译字节码为机器码,并在操作系统上执行。特点: - 动态阶段:程序在运行时根据输入、对象状态和环境动态执行。 - 主要任务: - 加载类(Class Loading):将字节码加载到JVM内存。 - 动态绑定(Dynamic Binding):在运行时根据对象的实际类型决定调用哪个方法(常见于方法重写)。 - 执行程序逻辑,处理用户输入、文件操作、网络请求等。 ↩↩