🤔思考题:
什么是面向对象编程,什么是面向过程编程
题目:
面向对象三大特性是什么?为什么Java是平台无关性的?
解答:
1、三大特性
- 封装:访问修饰符(private/protected/public/default)的作用域
- 继承:
extends实现、方法重写规则(两同两小一大原则)- 多态:编译时多态(方法重载) vs 运行时多态(方法重写+父类引用指向子类对象)
2、平台无关性
JVM工作机制:.java → .class → 机器码
字节码验证过程(类加载时的4次验证)
详解:
Java 中的访问修饰符(Access Modifiers)用于控制类、方法、变量等成员的可见性 和可访问性。它们决定了这些成员在哪些范围内可以被访问或使用。
Java 有四种访问修饰符:
| 修饰符 | 作用域(Scope) | 可见性说明 |
|---|---|---|
public |
所有包(All Packages) | 任何地方都可以访问 |
protected |
当前类、子类、同一包 | 子类和同包内可访问 |
default(默认,不写修饰符) |
同一包(Same Package) | 仅限于当前包内 |
private |
当前类(Current Class) | 仅限于定义它的类内部 |
一、详细说明
1. public
- 作用范围:所有类、包、子类。
- 适用对象:类、接口、方法、变量。
- 特点 :
- 最宽松的访问权限。
- 可以被其他任何类访问,无论是否在同一包中。
java
public class MyClass {
public int publicVar;
public void publicMethod() { ... }
}
2. protected
- 作用范围:当前类、子类、同一包。
- 适用对象:方法、变量。
- 特点 :
- 允许子类继承并访问。
- 在同一包中的其他类也可以访问。
- 不允许跨包访问(除非是子类)。
java
class Parent {
protected int protectedVar;
protected void protectedMethod() { ... }
}
class Child extends Parent {
void accessProtected() {
System.out.println(protectedVar); // 可以访问
}
}
⚠️ 注意:
protected不能用于类(即不能声明一个protected class)。
3. default(默认,不写修饰符)
- 作用范围:同一包(Same Package)。
- 适用对象:类、方法、变量。
- 特点 :
- 如果不写任何修饰符,则默认为
default。 - 仅限于当前包内的类访问。
- 如果不写任何修饰符,则默认为
java
class DefaultClass {
int defaultVar;
void defaultMethod() { ... }
}
✅ 示例:如果另一个类在同一个包中,就可以访问
defaultVar和defaultMethod()。
4. private
- 作用范围:当前类(Current Class)。
- 适用对象:方法、变量。
- 特点 :
- 最严格的访问权限。
- 只能在定义它的类内部访问。
- 不能被子类继承或访问。
java
class MyClass {
private int privateVar;
private void privateMethod() { ... }
void accessPrivate() {
System.out.println(privateVar); // 可以访问
}
}
❌ 示例:在其他类中无法直接访问
privateVar或privateMethod()。
二、访问修饰符的作用域对比表
| 修饰符 | 同类 | 同包 | 子类 | 其他包 |
|---|---|---|---|---|
public |
✅ | ✅ | ✅ | ✅ |
protected |
✅ | ✅ | ✅ | ❌ |
default |
✅ | ✅ | ❌ | ❌ |
private |
✅ | ❌ | ❌ | ❌ |
三、使用建议
- 使用
public:当需要对外公开接口或数据时。 - 使用
protected:当希望子类能访问但不希望外部直接访问时。 - 使用
default:当只需要在本包内使用时。 - 使用
private:当希望完全隐藏实现细节时。
四、总结
Java 的访问修饰符通过控制成员的可见性和可访问性,实现了对代码的封装和模块化管理。合理使用这些修饰符有助于提高程序的安全性、可维护性和可扩展性。
继承:
在 Java 中,继承(extends) 是面向对象编程的重要特性之一,它允许一个类(子类)继承另一个类(父类)的属性和方法。通过继承,可以实现代码复用、扩展功能等。
一、extends 实现
1. 基本语法
java
class 子类名 extends 父类名 {
// 子类的成员变量和方法
}
2. 特点
- Java 不支持多继承(即一个类不能同时继承多个类),但可以通过接口实现多继承。
Object是所有类的默认父类。- 子类可以访问父类的 非私有 成员(包括
public和protected)。
3. 示例
java
// 父类
class Animal {
public void eat() {
System.out.println("Animal is eating.");
}
}
// 子类
class Dog extends Animal {
public void bark() {
System.out.println("Dog is barking.");
}
}
public class Main {
public static void main(String[] args) {
Dog dog = new Dog();
dog.eat(); // 继承自 Animal
dog.bark(); // 子类自己的方法
}
}
二、方法重写(Override)
1. 定义
方法重写是指在子类中重新定义从父类继承来的方法。目的是为了改变或扩展父类的行为。
注意:重写必须与父类方法具有相同的签名(方法名、参数列表、返回类型)。
三、方法重写的规则(两同两小一大原则)
这是 Java 方法重写的核心规则,也称为 "两同两小一大"原则:
| 规则 | 说明 |
|---|---|
| 两同 | - 方法名相同 - 参数列表相同 |
| 两小 | - 返回值类型要小于等于父类方法的返回值类型(协变返回) - 异常抛出范围要小于等于父类方法的异常范围 |
| 一大 | - 访问权限要大于或等于父类方法的访问权限 |
1. 两同(Same Name, Same Parameters)
- 方法名相同:子类方法名必须与父类方法名一致。
- 参数列表相同:参数的数量、类型、顺序必须完全一致。
java
class Parent {
public void show(int x) {
System.out.println("Parent: " + x);
}
}
class Child extends Parent {
@Override
public void show(int x) { // 符合"两同"
System.out.println("Child: " + x);
}
}
2. 两小(Smaller Return Type, Smaller Exception)
- 返回值类型要小于等于父类方法的返回值类型 (协变返回):
- 允许子类方法返回更具体的类型(如
String可以覆盖Object)。 - 但不能是更宽泛的类型(如
Object不能覆盖String)。
- 允许子类方法返回更具体的类型(如
java
class Parent {
public Object getObject() {
return new Object();
}
}
class Child extends Parent {
@Override
public String getObject() { // 协变返回(合法)
return "Hello";
}
}
- 异常抛出范围要小于等于父类方法的异常范围 :
- 子类方法可以不抛出异常,也可以抛出比父类更少或更具体的异常。
- 不能抛出父类没有声明的检查型异常。
java
class Parent {
public void method() throws IOException {
// ...
}
}
class Child extends Parent {
@Override
public void method() throws FileNotFoundException { // 合法(更具体)
// ...
}
}
3. 一大(Larger Access Modifier)
- 访问权限要大于或等于父类方法的访问权限 :
- 父类方法是
private,子类不能重写(因为无法访问)。 - 父类方法是
default(包访问),子类方法可以是protected或public。 - 父类方法是
protected,子类方法可以是public。 - 父类方法是
public,子类方法只能是public。
- 父类方法是
java
class Parent {
protected void display() {
System.out.println("Parent's display");
}
}
class Child extends Parent {
@Override
public void display() { // 更大的访问权限(合法)
System.out.println("Child's display");
}
}
四、注意事项
final方法不能被重写。static方法不能被重写(只能被隐藏)。private方法不能被重写(因为无法访问)。- 使用
@Override注解可以帮助编译器验证是否为正确重写。
五、总结
| 规则 | 说明 |
|---|---|
| 两同 | 方法名相同,参数列表相同 |
| 两小 | 返回值类型更小/相同,异常范围更小/相同 |
| 一大 | 访问权限更大或相同 |
合理使用方法重写可以增强程序的灵活性和可维护性,是面向对象设计中的重要手段。
多态:
在 Java 中,多态(Polymorphism) 是面向对象编程的三大特性之一,它允许不同类的对象对同一消息做出不同的响应。根据实现方式的不同,多态可以分为 编译时多态 和 运行时多态。
一、编译时多态:方法重载(Overloading)
1. 定义
方法重载 (Method Overloading)是指在同一个类中,方法名相同,但参数列表不同(参数类型、数量或顺序不同),从而实现不同的功能。
2. 特点
- 发生在编译阶段,由编译器决定调用哪个方法。
- 不涉及继承关系,是类内部的方法定义。
- 返回值类型可以不同,但不能仅靠返回值类型来区分重载方法(否则会报错)。
3. 示例
java
public class Calculator {
public int add(int a, int b) {
return a + b;
}
public double add(double a, double b) {
return a + b;
}
public int add(int a, int b, int c) {
return a + b + c;
}
}
4. 调用示例
java
public class Main {
public static void main(String[] args) {
Calculator calc = new Calculator();
System.out.println(calc.add(2, 3)); // 输出 5
System.out.println(calc.add(2.5, 3.5)); // 输出 6.0
System.out.println(calc.add(1, 2, 3)); // 输出 6
}
}
5. 优点
- 提高代码可读性和灵活性。
- 减少重复代码。
二、运行时多态:方法重写 + 父类引用指向子类对象
1. 定义
运行时多态 (Runtime Polymorphism)是通过 方法重写(Override) 和 父类引用指向子类对象 实现的。它在程序运行时才确定具体调用哪个方法。
2. 核心要素
- 方法重写:子类重新定义从父类继承的方法。
- 父类引用指向子类对象:使用父类变量引用子类对象,通过该变量调用方法时,实际执行的是子类的方法。
3. 示例
java
// 父类
class Animal {
public void sound() {
System.out.println("Animal makes a sound");
}
}
// 子类
class Dog extends Animal {
@Override
public void sound() {
System.out.println("Dog barks");
}
}
class Cat extends Animal {
@Override
public void sound() {
System.out.println("Cat meows");
}
}
4. 调用示例
java
public class Main {
public static void main(String[] args) {
Animal animal1 = new Dog(); // 父类引用指向子类对象
Animal animal2 = new Cat();
animal1.sound(); // 输出 "Dog barks"
animal2.sound(); // 输出 "Cat meows"
}
}
5. 原理说明
- 在编译时,编译器只知道
animal1是Animal类型,无法确定具体是哪个子类。 - 在运行时,JVM 会根据实际对象的类型来调用对应的方法,这就是 动态绑定(Dynamic Binding)。
三、编译时多态 vs 运行时多态 对比
| 特性 | 编译时多态(方法重载) | 运行时多态(方法重写 + 父类引用) |
|---|---|---|
| 实现方式 | 同一类中方法名相同,参数不同 | 不同类中方法名相同,参数相同 |
| 决定时间 | 编译时 | 运行时 |
| 是否需要继承 | 无需继承 | 需要继承 |
| 方法修饰符 | 可以不同 | 必须满足"两同两小一大"原则 |
| 调用方式 | 通过参数不同调用 | 通过父类引用调用 |
| 典型应用 | 重载操作符、统一接口 | 多态行为、接口实现 |
四、总结
| 多态类型 | 实现方式 | 特点 | 应用场景 |
|---|---|---|---|
| 编译时多态 | 方法重载 | 编译时确定 | 提供统一接口,增强可读性 |
| 运行时多态 | 方法重写 + 父类引用 | 运行时确定 | 实现灵活的行为扩展,支持接口和抽象类设计 |
五、补充说明
- 方法重写 是实现 运行时多态 的核心机制。
- 父类引用指向子类对象 是实现运行时多态的关键手段。
- 多态 是面向对象设计的重要思想,有助于提高代码的可扩展性、可维护性和灵活性。
思考题:
面向对象编程(Object-Oriented Programming,简称 OOP)和面向过程编程(Procedural Programming)是两种不同的编程范式,它们在设计思想、代码组织方式以及解决问题的思维方式上存在显著差异。
一、面向过程编程(Procedural Programming)
1. 定义
面向过程编程是一种以过程/函数 为中心的编程方式。程序被看作是一系列操作步骤的集合,通过调用一系列函数来完成任务。
2. 核心特点
- 以函数为核心:程序由多个函数组成,每个函数完成特定的功能。
- 数据与操作分离:数据和处理数据的函数是分开的。
- 强调流程控制:关注"怎么做",即如何一步步执行操作。
3. 示例(C语言)
cpp
#include <stdio.h>
// 函数:计算两个数的和
int add(int a, int b) {
return a + b;
}
int main() {
int x = 5;
int y = 10;
int result = add(x, y);
printf("Result: %d\n", result);
return 0;
}
4. 优点
- 简单直观,适合小型项目。
- 执行效率高,适合底层开发。
5. 缺点
- 数据与功能分离,难以维护和扩展。
- 重复代码多,不利于复用。
二、面向对象编程(Object-Oriented Programming,OOP)
1. 定义
面向对象编程是一种以对象 为核心的编程方式。程序被看作是由多个对象 组成的系统,每个对象包含数据(属性)和行为(方法)。
2. 核心特点
- 以对象为核心:程序由多个对象构成,每个对象具有自己的状态和行为。
- 封装性:将数据和操作封装在一起,对外隐藏实现细节。
- 继承性:子类可以继承父类的属性和方法。
- 多态性:同一操作在不同对象上有不同的表现形式。
3. 示例(Java)
java
// 定义一个类:Person
class Person {
// 属性(数据)
String name;
int age;
// 方法(行为)
void speak() {
System.out.println("My name is " + name + ", I am " + age + " years old.");
}
}
public class Main {
public static void main(String[] args) {
// 创建对象
Person person = new Person();
person.name = "Alice";
person.age = 25;
person.speak(); // 调用方法
}
}
4. 优点
- 更符合现实世界的建模方式,易于理解和维护。
- 代码复用性强,便于扩展和维护。
- 支持模块化开发,提高团队协作效率。
5. 缺点
- 学习曲线较陡,需要理解类、对象、继承等概念。
- 对于简单任务可能显得复杂。
三、面向过程 vs 面向对象
| 特性 | 面向过程编程 | 面向对象编程 |
|---|---|---|
| 核心 | 函数/过程 | 对象 |
| 数据与操作 | 分离 | 封装在一起 |
| 重点 | 如何做 | 什么对象做什么 |
| 可维护性 | 较低 | 较高 |
| 复用性 | 一般 | 强 |
| 适用场景 | 小型程序、底层开发 | 中大型系统、复杂应用 |
四、总结
- 面向过程 更注重步骤和流程,适合简单的逻辑处理。
- 面向对象 更注重对象和关系,适合复杂系统的构建和维护。
在实际开发中,现代编程语言(如 Java、C++、Python)普遍采用面向对象的方式进行开发,因为其更符合软件工程的需求。不过,面向过程的思想仍然在某些场景下有其价值,比如嵌入式系统或性能敏感的代码中。