因为我下半年也要开始秋招了,所以最近准备以面试为导向开始写文章
考虑到目前网络上提供的面试题大部分都是短短的几行字,也即背诵版,俗称八股文,我觉得这无法满足大部分同学的诉求,知其然而不知所以然
我希望的是以面试题为导向,建立完整的知识体系,让八股文变得有价值,而不是东一锤西一棒,所以后续准备以牛客上的面经帖为导向,对每个面试题提供背诵版 + 详解版,已经会的同学呢可以直接看背诵版,还不太了解的同学呢可以结合详解版一起看
所有的面试题都已经按照知识体系的顺序进行过排序,并且我会加入一些补充题用于完善
目前已经整理了一部分汇总在一起 Notion 上,各位小伙伴可以翻到文末点击 阅读原文直达
Java 反射机制对于小白来说,真的是一道巨大的坎儿,其他的东西吧,无非就是内容多点,多看看多背背就好了,反射真的就是不管看了多少遍不理解就还是不理解,而且学校里面的各大教材应该都没有反射这个章节,有也是一带而过。说实话,在这篇文章之前,我对反射也并非完全了解,毕竟平常开发基本用不到,不过,看完这篇文章相信你对反射就没啥疑点了。
文章末尾贴了本题的背诵版
全文脉络思维导图如下:
1. 抛砖引玉:为什么要使用反射
前文我们说过,接口的使用提高了代码的可维护性和可扩展性,并且降低了代码的耦合度。来看个例子:
首先,我们拥有一个接口 X 及其方法 test,和两个对应的实现类 A、B:
public class Test {
interface X {
public void test();
}
class A implements X{
@Override
public void test() {
System.out.println("I am A");
}
}
class B implements X{
@Override
public void test() {
System.out.println("I am B");
}
}
通常情况下,我们需要使用哪个实现类就直接实现 new 一个就好了,看下面这段代码:
public class Test {
......
public static void main(String[] args) {
X a = create1("A");
a.test();
X b = create1("B");
b.test();
}
public static X create1(String name){
if (name.equals("A")) {
return new A();
} else if(name.equals("B")){
return new B();
}
return null;
}
}
按照上面这种写法,如果有成百上千个不同的 X 的实现类需要创建,那我们岂不是就需要写上千个 if 语句来返回不同的 X 对象?
我们来看看看反射机制是如何做的:
public class Test {
public static void main(String[] args) {
X a = create2("A");
a.test();
X b = create2("B");
b.testReflect();
}
// 使用反射机制
public static X create2(String name){
Class<?> class = Class.forName(name);
X x = (X) class.newInstance();
return x;
}
}
向 create2() 方法传入包名和类名,通过反射机制动态的加载指定的类,然后再实例化对象。
看完上面这个例子,相信诸位对反射有了一定的认识。反射拥有以下四大功能:
- 在运行时(动态编译)获知任意一个对象所属的类。
- 在运行时构造任意一个类的对象。
- 在运行时获知任意一个类所具有的成员变量和方法。
- 在运行时调用任意一个对象的方法和属性。
上述这种动态获取信息、动态调用对象的方法的功能称为 Java 语言的反射机制。
2. 理解 Class 类
要想理解反射,首先要理解 Class 类,因为 Class 类是反射实现的基础。
在程序运行期间,JVM 始终为所有的对象维护一个被称为运行时的类型标识,这个信息跟踪着每个对象所属的类的完整结构信息,包括包名、类名、实现的接口、拥有的方法和字段等。可以通过专门的 Java 可以访问这些信息,这个类就是 Class 类。我们可以把Class 类型理解为类的类型,一个 Class 对象,称为类型的类型对象,一个 Class 对象对应一个加载到 JVM 中的一个 .class 文件。
在通常情况下,一定是先有对象再有对象。以下面这段代码为例,类的正常加载过程是这样的:
import java.util.Date; // 先有类
public class Test {
public static void main(String[] args) {
Date date = new Date(); // 后有对象
System.out.println(date);
}
}
首先 JVM 会将你的代码编译成一个 .class 字节码文件,然后被类加载器(Class Loader)加载进 JVM 内存中,同时会创建一个 Date 类的 Class 对象存到堆中(注意这个不是 new 出来的对象,而是类的类型对象)。JVM 在创建 Date 对象前,会先检查其类是否加载,寻找类对应的 Class 对象,若加载好,则为其分配内存,然后再进行初始化 new Date()。