Skip to content

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 的作用是声明当前类所在的包,需要放在类的最上面,一个类中最多只有一句 package
  • import 指令位置放在 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 封装的理解和好处

  1. 隐藏实现细节:方法(连接数据库)← 调用(传入参数...)
  2. 可以对数据进行验证,保证安全合理
    Person {name,age}
    Person p = new Person();
    p.name = "jack";
    p.age = 1200;
    

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

  1. 将属性进行私有化 private 不能直接修改属性
  2. 提供一个公共的 public set 方法,用于对属性判断并赋值

    public void setXXX(类型 参数名){// XXX 表示某个属性 // 加入数据验证的业务逻辑 属性 = 参数名; }

  3. 提供一个公共的 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

8.8.3 继承的基本语法

class 子类 extends 父类{ }

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

8.8.5 继承给编程带来的便利

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

8.8.6 继承的深入讨论/细节问题

  1. 子类继承了所有的属性和方法,非私有的属性和方法可以在子类直接访问, 但是私有属性和方法不能在子类直接访问,要通过父类提供公共的方法去访问
  2. 子类必须调用父类的构造器,完成父类的初始化
    • 子类的构造器第一句默认是 super()
  3. 当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器, 如果父类没有提供无参构造器,则必须在子类的构造器中用 super 去指定使用父类的哪个构造器完成对父类的初始化工作 否则,编译不会通过。
    • super(a,b)
    • ![[0191 - 0262 第七章 面向对象编程(基础部分)#7.8.5 注意事项和使用细节]]
  4. 如果希望指定去调用父类的某个构造器,则显式的调用一下 : super(参数列表)
  5. super 在使用时,必须放在构造器第一行 (super 只能在构造器中使用)
  6. super () 和 this () 都只能放在构造器第一行,因此这两个方法不能共存在一个构造器
    • ![[0191 - 0262 第七章 面向对象编程(基础部分)#7.10.3 this 的注意事项和使用细节]]
  7. java 所有类都是 Object 类的子类, Object 是所有类的基类
  8. 父类构造器的调用不限于直接父类!将一直往上追溯直到 Object 类 (顶级父类)
  9. 子类最多只能继承一个父类 (指直接继承),即 java 中是单继承机制
    • 思考:如何让 A 类继承 B 类和 C 类? 【A 继承 B, B 继承 C】
  10. 不能滥用继承,子类和父类之间必须满足 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 给编程带来的便利/细节

  1. 调用父类的构造器的好处(分工明确,父类属性由父类初始化,子类的属性由子类初始化)
  2. 当子类中有和父类中的成员(属性和方法)重名时,为了访问父类的成员,必须通过 super。
  3. 如果子类中没有和父类中的成员(属性和方法)重名,使用 superthis直接访问 是一样的效果!
    • 由于子类没用,会直接按顺序查找属性和方法
  4. [[#8.9.2 基本语法]]
  5. 不可以 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 注意事项和使用细节

方法重写也叫方法覆盖,需要满足下面的条件

  1. 子类的方法的形参列表,方法名称,要和父类方法的形参列表,方法名称完全一样
  2. 子类方法的返回类型和父类方法返回类型一样,或者是父类返回类型的子类
    • 比如父类返回类型是 Object ,子类方法返回类型是 String
    • public Object getInfo(){}
    • public String getInfo(){}
  3. 子类方法不能缩小父类方法的访问权
名称 发生范围 方法名 形参列表 返回类型 修饰符
重载 (overload) 本类 必须一样 类型、个数或顺序至少有一个不同 无要求 无要求
重写 (override) 父子类 必须一样 相同 子类重写的方法,返回的类型和父类返回的类型一致,或者是其子类 子类方法不能缩小父类方法的访问范围

8.11 面向对象编程 - 多态

8.11.2 多 (多种) 态 (状态) 基本介绍

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


8 .11.3 多态的具体体现

[[多态]]

  1. 介绍
    • 编译时:2
    • 运行时:3
  2. 方法的多态 [[多态#1. 编译时多态(静态多态)]]
    • 重写和重载就体现多态
  3. 对象的多态 (核心,困难,重点) [[多态#2. 运行时多态(动态多态)]]
    • 一个对象的编译类型和运行类型可以不一致
    • 编译类型在定义对象时,就确定了,不能改变
    • 运行类型是可以变化的.
    • 编译类型2看定义时 = 号的左边,运行类型3= 号的右边

细节

  • 创建的对象是 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()

  • 特点:编译类型看左边,运行类型看右边。
    • 可以调用父类中的所有成员(需遵守访问权限)
    • 不能调用子类的私有成员,由于编译阶段,能调用哪些成员,是由编译类型决定的。
    • 最终的运行效果看子类的具体实现
  • 访问权限(publicprotected、默认、private)限制了多态的调用范围
    • 注意,这点因为子类不可访问会被限制 private默认修饰符
    • 由于编译器认为是父类,父类中被限制的方法如果调用会过不了编译器
    • 而由于看待这个对象的时候是 animal 所以不能访问子类的私有内容

[[多态#遵守访问权限]]

多态向下转型

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

Animal animal = new Cat() Cat animal_cat = (Cat) animal - animal 实则为 Cat 类的对象 - 把 Cat 类的对象转成 Cat 类,代表把原来隐藏的虚函数解开

  • 只能强转父类的引用,不能强转父类的对象
  • 要求父类的引用必须指向的是当前目标类型的对象
  • 当向下转型后,可以调用子类类型中所有的成员

不能调用子类中特有成员;最终运行效果看子类的具体实现

instanceOf() 比较操作符,用于判断对象的运行类型是否为 XX 类型或 XX 类型的子类型 ,返回一个 boolean


8.11.7 java 的动态绑定机制(非常非常重要.)

  1. 当调用对象方法的时候,该方法会和该对象的内存地址/运行类型绑定
  2. 当调用对象属性时,没有动态绑定机制,哪里声明,就在那里使用
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类中的方法,只能判断引用类型
    1. 默认判断的地址是否相等,子类中往往重写该方法,用于判断内容是否相等。
    2. 比如 Integer String
      • Integer 中被重写成两个 是不是相等
      • 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 方法

  1. 基本介绍

    • 默认返回:全类名 + @ + 哈希值的十六进制
    • 子类往往重写 toString 方法,用于返回对象的属性信息
  2. 重写 toString 方法,打印对象或拼接对象时,都会自动调用该对象的 toString 形式.

  3. 当直接输出一个对象时 ,toString 方法会被默认的调用

  4. 比如 System.out.println(monster)
  5. 就会默认调用 monster.toString()

8.12.6 finalize 方法

  1. 当对象被回收时,系统自动调用该对象的 finalize 方法。子类可以重写该方法,做一些释放资源的操作【演示】

  2. 什么时候被回收:当某个对象没有任何引用时,则 jvm 就认为这个对象是一个垃圾对象,就会使用垃圾回收机制来销毁该对象,在销毁该对象前,会先调用 finalize 方法

  3. 垃圾回收机制的调用,是由系统来决定(即有自己的 GC 算法), 也可以通过 System.gc() 主动触发垃圾回收机制


8.14 项目-零钱通

8.14.2 项目需求说明

使用 Java 开发 零钱通项目 , 可以完成收益入账,消费,查看明细,退出系统等功能.


8.14.3 项目的界面

![[8.14.3-1零钱通界面.png]]

8.14.4 项目代码实现

8.14.5 项目代码实现改进



注:


  1. 在 Java 中,不能直接使用 super.super 来访问父类的父类(即祖父类)的成员或方法。这种语法是不合法的,编译器会报错。

    原因分析

    1. super 的作用
      • super 关键字用于访问当前类的直接父类的成员(属性、方法)或构造方法。
      • 它只提供对直接父类的引用,无法直接跳跃到更上层的祖父类。
    2. 为什么不支持 super.super
      • Java 的设计中,super 是一个单层引用,旨在简化继承关系中的访问逻辑。允许 super.super 会增加复杂性,并可能破坏封装性。
      • 如果需要访问祖父类的成员,可以通过父类间接访问,但不能直接使用 super.super 语法。
    3. MRO(方法解析顺序)
      • 在 Java 的单继承体系中,方法解析顺序(MRO)是线性的(从子类到父类再到祖父类)。super 只负责处理当前类的直接父类,跳跃访问会破坏这种顺序。

  2. 编译时(Compile Time) 定义: - 编译时是指程序代码被编译器(在Java中是 javac)从源代码(.java 文件)转换为字节码(.class 文件)的阶段。 - 这一阶段由编译器完成,主要进行语法检查、类型检查和代码优化,生成可供JVM执行的字节码。

    特点: - 静态阶段:在程序运行之前,代码还未实际执行。 - 主要任务: - 检查代码的语法是否正确(如括号匹配、关键字拼写)。 - 进行类型检查(如变量类型是否匹配)。 - 解析方法重载(Method Overloading),根据方法签名(方法名、参数类型和数量)确定调用哪个方法。 - 生成字节码,供运行时使用。 

  3. 运行时(Run Time) 定义: - 运行时是指程序字节码(.class 文件)被Java虚拟机(JVM)加载并执行的阶段。 - 这一阶段程序实际运行,JVM解释或编译字节码为机器码,并在操作系统上执行。

    特点: - 动态阶段:程序在运行时根据输入、对象状态和环境动态执行。 - 主要任务: - 加载类(Class Loading):将字节码加载到JVM内存。 - 动态绑定(Dynamic Binding):在运行时根据对象的实际类型决定调用哪个方法(常见于方法重写)。 - 执行程序逻辑,处理用户输入、文件操作、网络请求等。