难以得到想要的offer, 只是因为身处CV中, 涉及一些面试技巧
在Java运行过程中,需要特别关注的是.class文件到机器码的转换环节。在此阶段,JVM类加载器首先会加载字节码文件,并通过解释器逐行执行,这种执行方式通常效率较低。此外,某些方法和代码块会被频繁调用,这些被称为热点代码。为了解决这一问题,后续引入了JIT编译器,该编译器属于运行时编译机制。当JIT完成首次编译后,它会将对应的字节码转化为机器码并保存下来,以便下次直接使用。众所周知,机器码的执行效率显然高于Java解释器。这也正是我们常说Java语言兼具编译与解释特性的原因。
HotSpot虚拟机采用了惰性评估策略,依据二八定律,在系统资源消耗方面占据主导地位的仅是少量的代码(即热点代码),这部分正是JIT需要进行编译的对象。JVM会根据每次代码执行的情况收集相关信息,并据此实施相应的优化措施。因此,代码被执行的次数越多,其运行速度也会随之提升。JDK 9 引入了一种新的编译方式AOT(Ahead of Time Compilation),该方式可直接将字节码转换为机器码,从而避免了JIT预热等带来的各种开销。JDK支持分层编译与AOT协同工作。然而,AOT编译器在生成代码的质量方面显然无法与JIT相提并论。
总结:Java虚拟机(JVM)是用于执行Java字节码的虚拟环境。针对不同操作系统(如Windows、Linux、macOS),存在特定版本的JVM实现,其目标在于确保相同的字节码能够在不同平台上产生一致的结果。字节码以及各平台对应的JVM实现共同构成了Java语言“一次编写、到处运行”的核心基础。
关于 JDK 与 JRE
JDK 是 Java Development Kit 的缩写,它是一个完整的 Java 开发工具包,在功能上具备全面性。除了包含 JRE 所提供的所有内容外还包括了诸如 javac 编译器以及 javadoc 和 jdb 等开发工具集,并能够用于程序的创建和编译过程。
而 JRE 则代表 Java 运行时环境,在其中包含了运行已编译 Java 程序所需的全部组件集合:包括 Java 虚拟机(JVM)、各类 Java 类库、java 命令以及其他基本组件等;但值得注意的是 JRE 并不具备新程序创建的功能能力。
如果您的目标仅仅是运行一些 Java 应用程序,则只需安装 JRE 即可满足需求;但如果您有从事 Java 编程工作的打算,则必须安装 JDK 才能实现相应目的——不过这并非绝对规则,在某些情况下即便您并不打算开展任何本地开发工作也可能需要安装 JDK 。例如当您计划使用 JSP 来部署 Web 应用时虽然表面上看只是在应用服务器中运行着 Java 程序;但实际上由于应用服务器会把 JSP 转换为 Java Servlet ,并且还需要利用到 JDK 来完成对 Servlet 的编译操作因此在这种情形下仍然需要安装 JDK 。
4. Oracle JDK 和 OpenJDK 的对比
在探讨这个问题之前,或许许多人的认知与我相似,尚未真正接触或使用过 OpenJDK。那么,Oracle 与 OpenJDK 之间是否存在显著差异?接下来,我将依据所搜集的相关资料,对这一常被忽略的问题进行说明。对于 Java 7 而言,并无特别关键的不同之处。OpenJDK 项目主要是基于 Sun 公司捐赠的 HotSpot 源代码构建而成。同时,OpenJDK 被指定为 Java 7 的参考实现,并由 Oracle 的工程师团队负责维护。至于 JVM、JDK、JRE 和 OpenJDK 之间的具体区别,在 Oracle 博客中于 2012 年曾有更为详尽的解释:
问:存储于 OpenJDK 存储库中的源代码与用于构建 Oracle JDK 的代码之间存在哪些不同?
答: 差异非常微小——我们的 Oracle JDK 版本是基于 OpenJDK 7 进行构建的,仅额外添加了若干部分代码,例如部署相关的组件,其中包括 Oracle 提供的 Java 插件以及 Java WebStart 的实现内容;此外还包含一些封闭源码的组件,如图形光栅化器等;同时亦包括了一些开源的第三方模块,如 Rhino;还有一些零散的内容,比如附加文档或第三方字体等。未来的目标是将 Oracle JDK 中的所有部分都开源处理,仅保留那些被认定为具有商业性质的部分除外。
总结:
- Oracle JDK 的版本更新周期为三年一次,而 OpenJDK 的版本则每三个月进行一次更新;
- OpenJDK 被视为一种参考实现,并且其源代码是完全开放的,而 Oracle JDK 是对 OpenJDK 的具体实现,但并非完全开源;
- 相较于 OpenJDK,Oracle JDK 在稳定性方面表现更优。尽管两者的核心代码高度相似,但 Oracle JDK 包含了更多的类库以及额外的错误修复内容。因此,在开发企业级或商业软件时,推荐使用 Oracle JDK,因为其经过了全面测试并具有更高的稳定性。在某些情况下,用户可能会在使用 OpenJDK 时遇到应用程序频繁崩溃的问题,此时切换至 Oracle JDK 即可有效解决该问题;
- 当前多家大型企业及知名软件开发工具均采用 Oracle JDK 进行开发工作,例如 Android Studio、Minecraft 及 IntelliJ IDEA 等开发环境,相比之下 OpenJDK 的使用率较低;
- 在响应速度以及 JVM 性能表现方面,Oracle JDK 相比于 OpenJDK 提供了更为优异的性能表现;
- 对于即将发布的版本而言,Oracle JDK 不会提供长期支持服务。用户如需获取最新功能与支持,则必须定期升级至最新版本;
- Oracle JDK 的使用遵循二进制代码许可协议进行授权,而 OpenJDK 则依据 GPLv2 协议获得授权。
5. Java 和 和 C++的区别
很多人并未接触过 C++,然而在面试过程中,面试官却常常喜欢将 Java 与 C++ 进行对比。尽管如此,即便未曾学习过 C++,也务必将其相关内容记录下来!
- 二者均为面向对象的编程语言,均具备封装、继承以及多态等特性
- Java 不允许直接使用指针操作内存,从而提升了程序运行时的内存安全性
- Java 的类仅支持单一继承,而 C++ 允许多重继承;虽然 Java 类无法实现多继承,但其接口可以支持多继承
- Java 拥有自动化的内存管理机制,无需开发者手动释放不再使用的内存空间
6. 什么是 Java 程序的主类 应用程序和小程序的主类有何不同
在一个程序中,可以包含多个类,但其中仅有一个类被定义为主类。在 Java 应用程序中,主类指的是包含 main() 方法的那个类。而在 Java 小程序中,主类则必须是一个继承自系统类 JApplet 或 Applet 的子类。对于应用程序的主类而言,并不要求其必须为 public 类,但小程序的主类则必须声明为 public 类。主类作为 Java 程序运行的起点,承担着程序执行入口的功能。
7. Java 应用程序与小程序之间有那些差别
应用程序的启动通常由主线程负责执行,即从 main() 方法开始运行。而 applet 小程序并不包含 main 方法,其运行方式主要依赖于嵌入在浏览器页面中,通过调用 init() 或 run() 方法来启动程序。这种嵌入浏览器的特性与 flash 游戏在网页中的运行方式存在相似之处。
8. 字符型常量和字符串常量的区别
1. 形式上: 字符常量由单引号括起,仅包含一个字符;字符串常量则由双引号括起,包含多个字符。
2. 含义上: 字符常量实质上对应一个整型数值(即其对应的 ASCII 码值),能够参与各类表达式运算;而字符串常量则表示其在内存中的存储地址。
3. 内存占用方面:字符常量仅需占用 2 个字节的存储空间;字符串常量则需要占用多个字节,至少包含一个用于标识字符串结束的字符(注意:在 Java 语言中,char 类型占据两个字节)。
9. 构造器 Constructor 是否可被 override
在讲解继承相关概念时,我们已经明确知晓父类中的私有属性以及构造方法无法被子类继承,因此Constructor(构造方法)也无法实现重写操作,但可以进行重载处理。这也解释了为何在一个类中会出现多个构造函数的情况。
10. 重载和重写的区别
重载: 该现象出现在同一类内部,要求方法名称保持一致,但参数的类型、数量或排列顺序需有所差异,而方法的返回值类型及访问权限修饰符则无特定限制,此类操作在编译阶段即被识别。
重写: 此行为存在于父类与子类之间,要求方法名称及参数列表完全一致,返回值类型所涵盖的范围应不大于父类方法的返回值类型,所抛出的异常范围也应不超过父类定义的异常范围,同时子类方法的访问权限修饰符范围需不低于父类方法的设定;若父类中的方法被标记为 private 访问权限,则子类无法对其进行重写操作。
11. Java 面向对象编程三大特性: 封装 继承 多态
封装
将对象的属性进行封装处理,使其具备私有特性,并通过特定方法为外部提供访问途径。若某些属性不希望被外部访问,则无需设置对应的访问方法。然而,若某一类完全不对外提供任何访问方式,则此类的存在价值将大打折扣。
继承
继承是一种基于已有类定义构建新类的技术手段,新类不仅能够沿用父类的属性与方法,还可以新增数据或功能,但无法对父类的特性进行有选择性的继承。借助继承机制,可以高效地实现代码的重复利用。
有关继承的以下三点内容需特别注意:
- 子类可访问并使用父类中非私有状态及操作。
- 子类具备独立定义自身属性与方法的能力,从而实现对父类功能的扩展。
- 子类可根据具体需求以自身方式重新实现父类中的方法。(相关内容将在后续进行讲解)。
多态
所谓多态,即在程序设计过程中,引用变量所指向的实际对象类型以及通过该变量调用的方法,在编译阶段无法确定,而需在程序执行过程中才能明确。具体而言,该引用变量究竟关联哪一个类的实例对象,其所调用的方法究竟属于哪个类的实现,均需在运行时才能最终判定。
Java 语言中实现多态的方式主要有两种:其一是通过继承机制,多个子类对父类中的相同方法进行重写;其二是借助接口形式,通过实现接口并对其内部的相同方法进行覆盖来达成。
String与可变字符串类的区别
可变性
【简而言之:String 类通过 final 关键字修饰的字符数组 private final char value[] 来存储字符串内容,因此 String 对象具有不可变性。相比之下,StringBuilder 与 StringBuffer 均继承自 AbstractStringBuilder 类,而该类内部同样采用字符数组 char[] value 进行字符串存储,但并未使用 final 关键字进行限定,这使得这两个类的对象具备可变性。在构建 StringBuilder 与 StringBuffer 对象时,其构造方法均会调用父类 AbstractStringBuilder 的构造方法以完成初始化操作,具体实现细节可参考相关源码。
AbstractStringBuilder.java
abstract class AbstractStringBuilder implements Appendable, CharSequence {
char[] value;
int count;
AbstractStringBuilder() {
}
AbstractStringBuilder(int capacity) {
value = new char[capacity];
}
线程安全性分析
String 中所包含的对象具有不可更改的特性,因此可将其视为常量,并具备线程安全的属性。AbstractStringBuilder 作为 StringBuilder 与 StringBuffer 的共同父类,负责定义一系列基础字符串操作方法,例如 expandCapacity、append、insert、indexOf 等。对于 StringBuffer 而言,其通过在方法层面添加同步锁机制或对相关方法实施同步控制,从而确保了线程安全性。而 StringBuilder 则未在方法上设置同步锁,因此不具备线程安全的特性。
性能
每当对 String 类型的数据进行修改时,系统会创建一个新的 String 实例,并将原有引用指向这个新生成的对象。而 StringBuffer 在执行操作时,始终是在原有对象的基础上进行修改,不会产生新的实例或改变引用地址。在相同的操作场景下,相较于 StringBuffer,使用 StringBuilder 虽然可以带来大约 10%~15% 的性能优化,但同时也需要承担多线程环境下可能出现的安全隐患。
对于三者使用的总结:
- 对有限数量的数据进行处理时,其数据类型为 String
- 在单线程环境下,若需对字符串缓冲区中的大量数据进行操作,应采用 StringBuilder
- 当多线程环境下需要对字符串缓冲区中的大量数据进行操作时,应使用 StringBuffer
13. 自动装箱与拆箱
**装箱:**将原始数据类型通过其对应的引用类型进行封装处理;
**拆箱:**将封装后的引用类型转换回原始的基本数据类型;
14. 在一个静态方法内调用一个非静态成员为什么是非法的
静态方法具备无需依赖对象实例即可调用的特性,因此在该类方法内部,无法引用或操作任何非静态变量及成员。
15. 在 Java 中定义一个不做事且没有参数的构造方法的作用
在 Java 程序执行子类构造方法的过程中,若未通过 super() 明确调用父类中特定的构造函数,则系统将自动寻找并调用父类中无参数的构造方法。然而,当父类仅定义了带参数的构造方法,而子类构造方法中又未使用 super() 明确指定调用父类某特定构造函数时,编译阶段将出现错误。这是由于 Java 在父类中无法找到无参数的构造方法可供调用所致。为避免此类问题,可以在父类中添加一个不执行任何操作且无参数的构造方法作为补充。
16. import java 和 和 javax 有什么区别
在最初阶段,JavaAPI 所依赖的包均以 java 开头,而 javax 仅作为扩展 API 包被引入使用。然而随着技术的发展,javax 不断扩展,最终成为了 Java API 的重要组成部分。不过,将原本属于 javax 的扩展内容迁移至 java 包中将会带来极大的麻烦,并可能导致大量现有代码失效。因此,最终确定 javax 包将被纳入标准 API 体系之中。由此可见,java 与 javax 实际上并无本质差异,这仅仅只是名称上的区别而已。
17. 接口和抽象类的区别是什么
- 在接口中定义的方法默认具有 public 访问权限,且在接口中方法原本不包含具体实现(自 Java 8 起,接口中允许存在默认实现),而抽象类则可以包含非抽象方法
- 接口中的变量默认被设定为 final 类型,但抽象类中的变量则无此限制
- 一个类可以同时实现多个接口,但只能继承一个抽象类
- 当类实现接口时,需要完成接口中所有方法的实现,而继承抽象类时则不一定需要覆盖所有方法
- 接口无法通过 new 关键字直接创建实例,但可以进行声明,并且必须指向实现了该接口的具体对象。从设计的角度来看,抽象是对类的提炼与概括,体现了一种模板式的设计理念;而接口则是对行为的提炼与规范,代表了一种行为上的标准。
18. 成员变量与局部变量的区别有那些
- 在语法结构方面,成员变量归属于类的范畴,而局部变量则是在方法内部定义的变量或作为方法参数存在的;成员变量可以使用 public、private、static 等修饰符进行限定,而局部变量则无法使用访问控制修饰符以及 static 修饰;不过,无论是成员变量还是局部变量,均可通过 final 修饰符进行限定;
- 在内存存储方式上,成员变量作为对象的一部分被存储在堆内存中,而局部变量则被存储在栈内存中;
- 关于变量在内存中的生命周期问题,成员变量作为对象的一部分,在对象被创建时即被初始化,并随着对象的销毁而消失;而局部变量的生命周期则与方法调用密切相关,在方法执行过程中存在,在方法执行结束后自动释放;
- 对于未显式初始化的成员变量而言,系统会根据其数据类型赋予默认值(特殊情况为被 final 修饰的成员变量必须显式赋值);相比之下,局部变量在未赋初始值的情况下不会自动获得默认值。
19. 创建一个对象用什么运算符? 对象实体与对象引用有何不同?
通过new运算符可以生成对象实例,该实例存储于堆内存中,而对象引用则保存在栈内存中,并指向对应的对象实例。一个对象引用可以不指向任何对象,也可以仅指向一个对象(如同一根绳子可以不系气球,也可以系一个气球);同时,一个对象可能被多个引用所指向(例如可以用多条绳子系住同一个气球)。
20. 什么是方法的返回值? 返回值在类的方法里的作用是什么?
方法的返回值指的是在执行某段代码过程中,由方法体内部运算所产生的输出结果(前提是该方法具备返回结果的能力)。其主要功能在于承接运算结果,从而为后续操作提供数据支持!
21. 一个类的构造方法的作用是什么 若一个类没有声明构造方法, 该程序能正确执行吗 ?
其主要功能在于实现对类对象的初始化操作,该过程具备可执行性。即便某一类未明确声明构造方法,系统仍会自动提供一个无参数的默认构造方法。
22. 构造方法有哪些特性
- 实例名称需与所属类的名称保持一致;
- 构造函数不具备返回值特性,但其定义时不可使用 void 关键字进行声明;
- 在创建类的实例对象过程中,该函数会自动触发执行,无需通过显式调用方式启动。
23. 静态方法和实例方法有何不同
- 当需要从外部调用静态方法时,有两种方式可供选择,分别是通过"类名.方法名"的格式或采用"对象名.方法名"的格式。而对于实例方法而言,仅能使用后者。这表明,在调用静态方法时无需预先创建对象即可实现。
- 静态方法在访问本类内部成员时,仅能够访问静态成员,包括静态变量以及静态方法,而无法对实例变量和实例方法进行访问;相比之下,实例方法在访问本类成员时则不存在此类限制。
24. 对象的相等与指向他们的引用相等,两者有什么不同?
判断两个对象是否相等,实质上是对比它们在内存中存储的数据内容是否一致;而判断引用是否相等,则是检查这两个引用所指向的内存地址是否相同。
25. 在调用子类构造方法之前会先调用父类没有参数的构造方法,其目的是?
协助子类完成初始化操作
26. == 与 equals
== : 其功能在于判定两个对象的内存地址是否一致,也就是说,用于判断这两个对象是否为同一实例。(对于基本数据类型而言,比较的是其数值;而对于引用数据类型,则比较的是存储位置)
equals() : 它的功能同样是判断两个对象是否相等。不过该方法通常存在两种使用场景:
- 情形 1: 当类未对 equals() 方法进行重写时,使用 equals() 方法来比较该类的两个对象,其效果等同于使用“==”运算符进行比较。
- 情形 2: 当类对 equals() 方法进行了重写后,通常我们通过重写该方法来判断两个对象的内容是否一致;如果内容一致,则返回 true(即认为这两个对象是相等的)。
举例说明:
简单介绍一下自己,笔者于2013年毕业于上海交通大学,在小公司工作过一段时间,并且也曾在华为、OPPO等大型企业任职,2018年加入阿里并一直工作至今。
深刻理解大多数处于初中级阶段的Android开发工程师在提升技能时往往依靠个人摸索或者参加培训课程学习,但面对培训机构动辄上万元的学费压力确实不小。缺乏系统性的自学过程不仅效率低下、耗时较长,而且很容易遇到技术瓶颈而停滞不前!
**因此整理收集了一份《2024年Android移动开发全套学习资料》,初衷非常简单,就是希望能够帮助那些想要自我提升却不知从何入手的朋友,并且尽量减轻大家的学习负担。





现有资源既包含适合初学者入门的零基础学习材料,也涵盖针对拥有三年以上经验开发者进阶提升的深入课程,几乎覆盖了Android开发领域95%以上的核心知识点,内容体系完整且结构清晰。
由于资料体量较大,此处仅展示部分内容目录的截图,每个目录节点内均包含知名企业的面试经验、学习笔记、源码解析、实战项目以及配套视频讲解,并将持续进行更新与完善。
如你认为这些内容对自身学习有所帮助,可通过扫描二维码获取相关资源(备注:Android)。

最后
无论说得再怎么天花乱坠,终究不如亲身实践来得实在。若想在移动互联网发展的后半阶段占据一席之地,就必须从当下做起,从今日起,立即对自己提出严格要求,既要注重业务实现的技能水平,也不能忽视基础理论与核心原理的掌握。唯有将基础打牢,方能构建稳固的体系,如同高楼平地而起般稳健可靠。
最后,为了便于大家深入理解Android相关知识的核心原理以及面试中常涉及的内容,我特意整理了24套腾讯、字节跳动、阿里、百度2020-2021年面试真题解析资料。这些内容我将其归纳为视频讲解与PDF文档(实际过程中花费了比预想更多的时间和精力),涵盖了系统化的知识结构以及诸多关键细节。
此外还提供了 高级架构技术进阶思维导图、Android开发面试专题资料 ,帮助大家在学习过程中实现能力提升,并节省在网络上搜索信息的时间。同时也可将这些资源分享给身边的朋友一同学习进步。



网络上关于Android学习的资源十分丰富,然而若所掌握的知识缺乏系统性,面对问题时仅停留在表面了解,未能进一步深入探索,将难以实现真正意义上的技术进步。期望这一结构清晰的技术框架能够为大家提供明确的学习指引。
Android学习资源汇总
当前关于Android开发的学习资源极为丰富,然而若所掌握的知识缺乏系统性,面对问题时仅停留在表面理解,未能进一步深入探讨,将难以实现实质性的技术进步。期望这一结构清晰的技术框架能够为大家提供明确的学习指引。
