1.怎么理解面向对象?
- 面向对象是一种编程范式,将现实世界中的事件抽象为对象
- 事件具有属性(字段或属性),行为(方法)
- 面向对象的编程思想是以对象为中心,通过对象之间的交互来完成程序的功能,具有灵活性和可扩展性
- 通过封装和继承可以更好面对需求变化
| 编程方式 | 思维方式 | 举例 |
|---|---|---|
| 面向过程 | 以步骤为中心,强调“怎么做” | 先洗菜→再切菜→再炒菜 |
| 面向对象 | 以对象为中心,强调“谁去做” | 厨师对象:洗菜()、切菜()、炒菜() |
2.讲讲三大特性
- 封装
- 将对象属性,行为封装在一起,对外部隐藏细节,仅通过对象提供的接口与外界交互
- 目的:安全性,简化编程,使对象独立
- 继承
- 可使子类自动共享父类数据结构和方法的机制,代码复用的重要手段
- 目的:可建立类与类之间的层级关系,使结构清晰
- 多态
- 允许不同类的对象对同一消息作出响应,即同一接口,使用不同的实例而执行不同的操作
- 继承(extends):子类继承父类。
- 方法重写(override):子类重写父类的方法。
- 父类引用指向子类对象:
Animal a = new Dog();只有满足这三点,才能体现“多态”
3.多态体现在哪几个方面
- 方法重载(编译时多态
- 同一类可以有多个同名方法,但具有不同的参数列表(数量,顺序或参数类型)
- 方法重写(运行时多态
- 就是上面的狗的例子,JVM根据对象的实际类型来确定调用哪个
- 接口与实现
- 多个类可以实现同一个接口,并且通过结构类型的引用来调用这些类的方法,这使得程序在面对不同具体实现时保持一贯的调用方式
interface Animal {
void makeSound(); // 约定:所有动物都必须会发出声音
}
class Dog implements Animal {
public void makeSound() {
System.out.println("汪汪!");
}
}
class Cat implements Animal {
public void makeSound() {
System.out.println("喵喵!");
}
}
Animal a1 = new Dog();
Animal a2 = new Cat();
a1.makeSound(); // 输出:汪汪!
a2.makeSound(); // 输出:喵喵!
感觉和继承重载一样,只不过继承的父类变成了实现接口- 向上转型和向下转型
- 使用父类类型的引用指向子类对象
- 父类引用转回子类类型,必须明确指明`
Animal a = new Animal();Dog d = (Dog) a; // ❌ 这里会报 ClassCastException,因为 a 不是 Dog,需要使用instanceof4.类型互转会出现什么问题
4.多态解决了什么问题
提高代码复用性,扩展性,是很多设计模式,设计原则,编程技巧1的代码实现基础,如策略模式,基于接口而非实现编程,利用多态去掉冗余的if-else语句等1.策略模式 多态的实现需要编程语言提供特殊的机制完成,例如继承,接口.
5.面向对象的设计原则
| 原则 | 一句话解释 | 通俗理解 |
|---|---|---|
| 1. 单一职责原则(SRP) | 一个类只做一件事 | “一个人只干一份工”——员工类管员工信息,不该顺便干报销或考勤的活。 |
| 2. 开放封闭原则(OCP) | 对扩展开放,对修改封闭 | 想加功能就继承或实现接口,别去改老代码。像“插座留接口,接不同的电器都行”。 |
| 3. 里氏替换原则(LSP) | 子类能替换父类使用 | “狗是动物”,那所有动物能做的事,狗也能做,否则别继承。别让“正方形继承矩形”那种尴尬事发生。 |
| 4. 接口隔离原则(ISP) | 接口要小而专 | “不要强迫别人实现用不上的功能”。比如打印机接口不该要求实现扫描功能。 |
| 5. 依赖倒置原则(DIP) | 高层模块依赖抽象,不依赖细节 | “上级只管标准,不管具体怎么干”。老板(高层模块)只看“接口”,不关心底层是哪个员工。 |
| 6. 最少知识原则(LoD) | 对外尽量少暴露内部细节 | “只和朋友说话,不和陌生人交谈”。对象之间不要互相知道太多,以免牵一发动全身。 |
6.重载与重写什么区别
- 重载
- 重写(Overriding)
- 子类可以重新定义父类的方法,但方法名,参数列表,返回值必须一致,并通过@Override注解明确!
7._抽象类和普通类的区别
抽象类绝不能被 final 修饰,不然就绝育了?有什么意义?
- 实例化:
- 普通类可以实例化对象,但抽象类不能被实例化,只能被继承
- 实现限制:
- 普通类可以被其他类继承和使用,但抽象类一般作为基类,被其他类继承和扩展使用
- 方法实现:
- 抽象类中的方法可以没有被实现(跟接口挺像)
- 继承:
- 其实相同,一个类只能继承一个普通类或抽象类,可以继承多个接口(实现多个接口)
| 特性 | 普通类 (Concrete Class) | 抽象类 (Abstract Class) |
|---|---|---|
| 实例化 | 能 (new User()) | 不能 (严禁直接 new) |
| 抽象方法 | 绝对不能有 (方法必须都有身体) | 可以有,也可以没有 (可以包含普通方法和抽象方法) |
| 设计目的 | 具体的、完备的“实物” | 模版、半成品,为了被继承而生 |
| 被继承 | 可以 (除非加了 final) | 必须被继承 (否则毫无意义) |
| 继承限制 | 单继承 (extends A) | 单继承 (extends B) |
8._* 抽象类和接口的区别
-
抽象类
- 用于描绘类的共同特性和行为,可以有成员变量,构造方法和具体方法,适用于有明显继承关系的场景
-
接口
- 用于定义行为规范,可以多实现,只有常量和抽象类,默认方法和静态方法,适用于定义类的能力或功能
-
实现方式:关键字不同,7.抽象类和普通类的区别继承一个,接口多个
-
方法方式:接口只有定义,不能有方法的实现,而抽象类可以
-
访问修饰符:
-
接口成员变量默认为public static final,必须赋初值,不能被修改;
-
接口方法都是public,abstract(可以没有方法体)
-
抽象类成员默认default
-
抽象类方法被abstract修饰,不可被
private、static、synchronized、native修饰。
-
| 维度 | 抽象类 (Abstract Class) | 接口 (Interface) |
|---|---|---|
| 核心本质 | 是“是不是” (Is-a) | 是“有没有” (Has-a) |
| 设计目的 | 代码复用(模板设计模式) 老爸给儿子留家产,儿子可以接着用。 | 行为规范(解耦) USB 接口标准,不管你插鼠标还是键盘,都能用。 |
| 继承/实现 | 单继承 (extends) | 多实现 (implements) |
| 成员变量 | 可以是各种类型 (private, protected…) | 默认全是 public static final (常量) |
| 构造方法 | 有 (为了给子类初始化用) | 无 (根本不能造对象,也没状态要初始化) |
| 方法实现 | 可以有抽象方法,也可以有普通方法 | JDK 8+:可以有 default, static 方法JDK 9+:可以有 private 方法 |
| 速度/效率 | 略快 (直接类加载) | 略慢 (如果要查接口表) |
9.抽象类加final修饰吗
不能,互斥见上8.抽象类和接口的区别,抽象类就是用来继承啊?蚌埠住了
10.接口里面可以定义哪些方法?
① 抽象方法(abstract method)
- 接口的默认方法类型。
- 默认是:
public abstract,可以省略。 - 实现类必须实现。
示例:
void makeSound();
② 默认方法(default method)
- 接口可以“带方法体”。
- 主要为了解决接口扩展不破坏旧代码。
示例:
default void sleep() { System.out.println("Sleeping..."); }
③ 静态方法(static method)
- 属于接口本身,不属于实现类。
- 只能通过接口名调用,不能通过实现类对象调用。
示例:
static void log(String msg) { System.out.println(msg); }
④ 私有方法(private method)
—— Java 9 引入
- 用于接口内部复用逻辑。
- 不能被实现类访问,只能被 default / 其他 private 使用。
示例:
private void debug() { System.out.println("debug..."); }
11._抽象类可以被实例化吗?
- 抽象类包含抽象方法,没有具体实现,因此无法直接创建对象。
- 抽象类的设计目的就是让子类继承并实现抽象方法,而不是自己生成实例。
- 抽象类可以有构造方法,但该构造器只能在子类实例化时被调用(用于给父类初始化,并不代表抽象类可实例化)。
12._接口可以包含构造函数吗?
- 接口不能被实例化 构造函数的作用是初始化对象,但接口没有对象,因此不需要构造函数。
- 接口不负责状态初始化 接口是行为规范,不是具体实现,不承担成员变量初始化的职责。
- Java 语法层面禁止 如果你在接口里写构造方法,会直接报错: Interfaces cannot have constructors
13.解释Java中的静态变量和静态方法
[[4.拷贝与关键字#4—static-关键字-类级别静态比任何构造方法new都要早执行|4. * static 关键字 (类级别/静态)(比任何构造方法(new)都要早执行)]]
静态变量和静态方法属于类本身,被所有对象共享,不依赖实例;静态方法不能访问非静态成员。
1. 静态变量(static variable / 类变量)
属于类本身,不属于某个对象。内存中只有一份。 📌 特点总结:
- 所有对象共享一份数据
- 类加载时初始化,只加载一次
- 可通过“类名.变量”直接访问
- 常用于:计数器、全局配置、常量(配合
final) 📌 例子:
class User {
static int count = 0; // 静态变量
User() { count++; }
}创建 3 个对象 → count = 3(所有对象共享)
2. 静态方法(static method / 类方法)
属于类本身,无需创建对象即可调用。 📌 特点总结:
- 不能访问非静态成员(因为不依赖对象)
- 可以访问静态变量、静态方法
- 不支持重写(只能隐藏),因为与实例无关
- 常用于工具类:
Math.random()、Collections.sort()📌 例子:
class Util {
static void hello() {
System.out.println("Hello");
}
}
Util.hello(); // 不需要 new 对象14.非静态内部类和静态内部类的区别?
public class School {
String schoolName = "第一中学";
static String city = "北京";
// 1. 静态内部类:就像学校里的“小卖部”,它是独立的
public static class Shop {
public void sell() {
// System.out.println(schoolName); // 报错!小卖部不知道具体的学校名字(非静态)
System.out.println("我们在" + city + "卖货"); // 只能访问静态成员
}
}
// 2. 非静态内部类:就像学校里的“教务处”,必须依赖学校存在
public class Office {
public void work() {
System.out.println(schoolName + "正在办公"); // 随便访问
}
}
}当你使用它们时:
-
静态内部类:
new School.Shop();(直接进去买东西,不需要 new 学校) -
非静态内部类:
School s = new School(); s.new Office();(必须先有学校,才能进教务处)
为什么要这么设计?(有什么用)
既然静态内部类和普通外部类差不多,为什么不直接写在外面?
-
逻辑归类: 如果
Shop类只给School用,写在里面更整洁,别人一看就知道它们是一伙的。 -
隐藏性: 你可以把静态内部类设为
private,这样只有外部类能用它,外面的人根本看不见。 -
节省内存: 重点!非静态内部类会偷偷牵着外部类的手(隐式引用)。如果内部类对象一直不销毁,外部类也销毁不了,容易导致内存泄漏。静态内部类没有这个“牵手”动作,非常安全。
| 维度 | 静态内部类 (Static Nested Class) | 外部类 (Top-level Class) |
|---|---|---|
| 定义位置 | 在另一个类的内部 | 在包(Package)下直接定义 |
| 修饰符 | 可以用 static | 禁止使用 static |
| 与外部关系 | 逻辑上紧密耦合,物理上独立 | 完全独立 |
| 实例化 | new Outer.StaticInner() | new Outer() |
| 静态内部类是“住在别人家,但自立门户”的亲戚;非静态内部类是“住在别人家,且必须依赖房东才能生活”的租客;而外部类就是一栋独立的房子,它不需要也不可能标记为“静态”。 | ||
| |
14.非静态内部类可以直接访问外部方法,编译器是怎么做到的?
非静态内部类之所以能访问外部类的属性和方法,是因为编译器会为其自动生成一个指向外部类实例的隐藏引用(Outer.this),并在构造函数中传入外部类实例。
非静态内部类在字节码层面会被编译器加上一个指向外部类实例的隐藏引用 this$0,构造方法也会被改写为必须接收一个外部类实例。内部类所有访问外部类成员的语句都会被编译器翻译成 this$0.xxx,因此能直接访问外部类的属性和方法。