Java基础面试题day03

一、怎样理解面向对象?简单说明封装,继承,多态

核心理解:面向对象(OOP)是一种编程思想,核心是"万物皆对象"------把现实中的事物抽象成程序里的"对象",每个对象都有自己的"属性"(比如人的年龄、姓名)和"行为"(比如人会吃饭、走路),通过对象之间的交互完成程序功能,比面向过程更灵活、易维护。

三大核心特性:

  • 封装:把对象的属性和行为"包裹"起来,隐藏内部细节,只对外提供可访问的接口(比如手机,我们只需要按按键、触屏操作,不需要知道内部芯片、电路的工作原理),核心是"隐藏细节、暴露接口",比如Java中用private修饰属性,提供get/set方法访问。

  • 继承:子类继承父类的属性和方法,减少重复代码,实现代码复用(比如狗和猫都继承"动物"类,动物有的"吃饭、睡觉"方法,狗和猫不用再重复写),Java中只有单继承(一个子类只能有一个父类),但可以多实现。

  • 多态:同一个行为,作用在不同对象上,产生不同的结果(比如"叫声"这个行为,狗叫是"汪汪",猫叫是"喵喵"),核心是"父类引用指向子类对象",实现程序的灵活性和扩展性。

二、多态体现在哪几个方面?

Java中多态主要体现在3个方面

  1. 方法重写(最核心):子类重写父类的方法,父类引用指向子类对象时,调用的是子类重写后的方法(比如父类Animal有call()方法,子类Dog重写call()为"汪汪",Cat重写为"喵喵",Animal a = new Dog(); a.call() 执行Dog的call())。

  2. 方法重载:同一个类中,有多个方法名相同,但参数列表(参数个数、类型、顺序)不同的方法,调用时根据参数自动匹配(比如Math.abs(),可以接收int、double等不同类型参数,执行不同的逻辑)。

  3. 接口多实现:一个类可以实现多个接口,不同接口有相同的方法名,子类实现后,调用该方法时体现多态(比如接口A和接口B都有show()方法,类C实现A和B,重写show(),调用时执行C的show())。

补充:多态的前提是"继承/实现"、"方法重写"、"父类/接口引用指向子类对象",三者缺一不可。

三、多态解决了什么问题?

  1. 减少代码冗余,提高代码复用性:不用为每个子类单独写调用逻辑,统一用父类引用调用,比如遍历不同的动物,不用分别写Dog.call()、Cat.call(),直接用Animal引用调用即可。

  2. 提高程序的扩展性和灵活性:新增子类时,不用修改原有父类和调用逻辑,直接新增子类并重写方法即可,符合"开闭原则"(对扩展开放,对修改关闭),比如新增"猪"类,继承Animal,重写call(),原有代码无需改动就能调用。

简单说:多态让程序"写一次,适配多种场景",后期维护更省心。

四、面向对象的设计原则你知道有哪些吗?

  • 单一职责原则:一个类只做一件事(比如User类只处理用户相关逻辑,不处理订单逻辑),目的是降低耦合,便于维护。

  • 开闭原则(核心):对扩展开放,对修改关闭(新增功能靠新增类/方法,不修改原有代码),比如多态就体现了开闭原则。

  • 里氏替换原则:子类可以完全替代父类,且不影响程序的正常运行(子类不能破坏父类的原有逻辑),比如Dog可以替代Animal,调用Animal的方法时,用Dog对象也能正常执行。

  • 依赖倒置原则:依赖抽象(接口/抽象类),不依赖具体实现(子类),比如调用方法时,参数用接口类型,而不是具体的子类类型,提高灵活性。

  • 接口隔离原则:一个接口只定义一个单一功能,不定义无关的方法(比如UserService接口只定义用户相关方法,不定义商品相关方法),避免接口臃肿。

  • 迪米特法则(最少知道原则):一个类尽量少了解其他类的内部细节,只和直接关联的类交互(比如A类调用B类的方法,不用知道B类内部怎么实现的),降低耦合。

  • 合成复用原则:优先用"组合/聚合"的方式复用代码,而不是继承(比如Car类可以包含Engine类,复用Engine的功能,而不是继承Engine),避免继承带来的耦合过高。

五、重载与重写有什么区别?

对比维度 重载(Overload) 重写(Override)
定义 同一个类中,方法名相同,参数列表不同 子类与父类中,方法名、参数列表、返回值(兼容)完全相同
范围 同一个类 子类与父类(或接口与实现类)
修饰符 无限制(可任意修饰) 子类修饰符不能严于父类(比如父类是public,子类不能是private)
核心目的 方便调用,同一功能适配不同参数 重写父类逻辑,实现子类特有功能
示例 add(int a)、add(int a, int b) 父类Animal.call(),子类Dog.call()

补充:重载不看返回值类型(哪怕返回值不同,参数列表相同也不算重载);重写必须看返回值类型(要和父类兼容,比如父类返回int,子类不能返回String)。

六、抽象类和普通类有什么区别?

核心区别:抽象类"不完整",不能直接实例化;普通类"完整",可以直接实例化,具体区别如下:

  1. 实例化能力:普通类可以直接用new关键字实例化(比如new User());抽象类不能直接实例化(用abstract修饰,new AbstractClass()会报错),只能被子类继承后,实例化子类。

  2. 方法定义:普通类只能定义具体方法(有方法体,能直接执行);抽象类可以定义抽象方法(用abstract修饰,没有方法体,必须由子类重写),也可以定义具体方法。

  3. 修饰符:抽象类必须用abstract修饰;普通类不能用abstract修饰。

简单说:抽象类是"模板",定义了子类的通用结构,子类必须补充完整抽象方法才能使用;普通类是"成品",可以直接使用。

七、Java抽象类和接口的区别是什么?

对比维度 抽象类(Abstract Class) 接口(Interface)
修饰符 用abstract修饰 用interface修饰
继承/实现 子类用extends继承,单继承 类用implements实现,多实现(一个类可实现多个接口)
方法定义 可定义抽象方法、具体方法(有方法体) JDK8前:只能定义抽象方法;JDK8后:可定义默认方法(default)、静态方法(static),无构造方法
属性定义 可定义普通属性、静态属性,可修改 只能定义常量(默认public static final),不可修改
实例化 不能直接实例化,子类继承后可实例化 不能实例化,只能由实现类实例化
核心作用 代码复用(子类继承通用属性和方法) 定义规范(约束实现类必须实现的方法),实现多态

八、抽象类能加final修饰吗?

不能!核心原因:final修饰的类不能被继承,而抽象类的核心作用是被子类继承,子类重写其抽象方法才能使用;如果抽象类加final,就无法被继承,抽象方法也无法被重写,失去了抽象类的意义,编译器会直接报错。

补充:final可以修饰抽象类中的具体方法(表示该方法不能被子类重写),但不能修饰抽象类本身,也不能修饰抽象方法(抽象方法必须被重写,final禁止重写,冲突)。

九、接口里面可以定义哪些方法?

分JDK版本,面试要说明版本差异(避免踩坑),核心记JDK8及以后的情况:

  1. JDK8之前:只能定义抽象方法(默认public abstract修饰,可省略),没有方法体,必须由实现类重写。

  2. JDK8及以后:新增2种方法,加上抽象方法,共3种:

    1. 抽象方法:public abstract(可省略),无方法体,需实现类重写;

    2. 默认方法(default):有方法体,实现类可直接使用,也可重写(重写时不用加default);

    3. 静态方法(static):有方法体,只能通过接口名调用(比如InterfaceName.method()),实现类不能重写,也不能通过实现类对象调用。

补充:接口中的所有方法,默认都是public修饰(无论是否省略),不能用private、protected修饰。

十、抽象类可以被实例化吗?

不能直接实例化,但可以间接实例化

  • 直接实例化:用new关键字创建抽象类对象(比如new AbstractClass()),编译器直接报错,因为抽象类不完整(可能有抽象方法,没有方法体),无法直接使用。

  • 间接实例化:创建抽象类的子类,子类重写抽象类中所有的抽象方法,然后实例化子类,本质上是通过子类实现抽象类的功能,间接使用抽象类(比如AbstractClass a = new SubClass(); 这里a是抽象类引用,指向子类对象)。

十一、接口可以包含构造函数吗?

不能!核心原因有2点:

  1. 构造函数的作用是初始化对象,而接口不能被实例化(没有对象需要初始化),所以构造函数没有意义。

  2. 接口的核心作用是定义规范,不负责具体实现,而构造函数是类的具体实现的一部分,与接口的设计初衷不符。

补充:抽象类可以有构造函数(用于子类继承时初始化父类的属性),但接口绝对没有,这也是抽象类和接口的重要区别之一。

十二、解释Java中的静态变量和静态方法

核心:静态变量和静态方法属于"类",不属于单个对象,所有对象共享,用static修饰,通俗解释+核心特点如下:

1. 静态变量(类变量)

定义:用static修饰的变量,属于整个类,不是某个对象的属性,所有对象共享同一个静态变量的值(比如Student类的static int count = 0; 所有Student对象的count都共享,一个对象修改,其他对象看到的也会变)。

特点:① 加载时机:类加载时就初始化,比对象创建早;② 调用方式:可通过类名调用(推荐,比如Student.count),也可通过对象调用;③ 内存存储:存放在方法区的静态区,只有一份。

2. 静态方法(类方法)

定义:用static修饰的方法,属于整个类,不依赖于对象,无需创建对象就能调用(比如Math.random(),不用new Math()就能调用)。

特点:① 不能访问非静态变量和非静态方法(因为非静态属于对象,静态方法加载时可能没有对象);② 可以访问静态变量和其他静态方法;③ 调用方式:类名.方法名(推荐),或对象.方法名。

补充:static不能修饰局部变量(比如方法内部不能定义static int a = 10;),只能修饰类级别的变量和方法。

十三、非静态内部类和静态内部类的区别

对比维度 非静态内部类(成员内部类) 静态内部类(嵌套内部类)
修饰符 无static修饰 有static修饰
依赖外部类 依赖外部类对象,必须先创建外部类对象,才能创建内部类对象 不依赖外部类对象,可直接通过外部类名创建内部类对象
访问外部类成员 可直接访问外部类的非静态成员和静态成员 只能访问外部类的静态成员,不能访问非静态成员
创建方式 外部类对象.new 内部类()(比如Outer outer = new Outer(); Outer.Inner inner = outer.new Inner();) 外部类名.内部类()(比如Outer.Inner inner = new Outer.Inner();)
是否有this引用 有两个this:this(内部类对象)、Outer.this(外部类对象) 只有一个this(内部类对象),没有外部类this引用

十四、非静态内部类可以直接访问外部方法,编译器是如何做到的?

核心原理:编译器会自动为非静态内部类添加一个"外部类对象的引用",通过这个引用,非静态内部类就能直接访问外部类的成员(包括方法和属性),底层过程分3步:

  1. 编译阶段:当我们编写非静态内部类时,编译器会在内部类的构造方法中,悄悄添加一个参数------外部类对象的引用(比如Outer this$0),这个参数是隐式的,我们看不到,但确实存在。

  2. 对象创建阶段:当我们创建非静态内部类对象时(outer.new Inner()),编译器会自动将外部类对象(outer)作为参数,传递给内部类的构造方法,给this$0赋值,相当于"绑定"了外部类对象。

  3. 访问阶段:当非静态内部类访问外部类的方法时,底层会通过这个隐式的外部类引用(this0),调用外部类的方法(比如this0.outerMethod()),所以我们写代码时,不用手动调用,就能直接访问。

举个通俗例子:就像孩子(非静态内部类)天生知道自己的父母(外部类),不用父母主动告知,就能直接找父母帮忙(访问外部方法),这个"天生知道",就是编译器自动添加的外部类引用在起作用。

补充:正因为非静态内部类持有外部类的引用,如果内部类对象长期存活,而外部类对象本应被回收,就会导致内存泄漏(比如Android中,非静态内部类作为Handler,容易导致Activity内存泄漏)。

相关推荐
nglff2 小时前
蓝桥杯抱佛脚第一天|简单模拟,set,map的使用
算法·职场和发展·蓝桥杯
实心儿儿2 小时前
算法7:两个数组的交集
算法·leetcode·职场和发展
风止何安啊3 小时前
数字太长看花眼?一招教它排好队:千分位处理的实现
前端·javascript·面试
Java面试题总结4 小时前
2026Java面试八股文合集(持续更新)
java·spring·面试·职场和发展·java面试·java八股文
城沐小巷4 小时前
【无标题】
面试·职场和发展·毕业设计·课程设计·毕设
uzong5 小时前
为什么是你来做?面试中犀利问题的底层逻辑是什么和标准回答模版
后端·面试
Sailing5 小时前
🚀AI 写代码越来越快,但我开始不敢上线了
前端·后端·面试
郝学胜-神的一滴5 小时前
贪心策略实战Leetcode 860题:柠檬水找零问题的优雅解法
数据结构·c++·算法·leetcode·职场和发展
Mia惠枫5 小时前
INFP人格认知功能深度分析与优势整合策略研究 ——基于个体八维数据的实证汇报
职场和发展