先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7
深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年最新Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
如果你需要这些资料,可以添加V获取:vip1024b (备注Java)
正文
单例模式用静态工厂方法
这是我最喜欢的在 Java 中影响 Singleton 模式的方法之一,因为 Singleton 实例是静态的,并且最后一个变量在类首次加载到内存时初始化,因此实例的创建本质上是线程安全的。
/**
- 单例模式示例与静态工厂方法
*/
public class Singleton{
//initailzed during class loading
private static final Singleton INSTANCE = new Singleton();
//to prevent creating another instance of Singleton
private Singleton(){}
public static Singleton getSingleton(){
return INSTANCE;
}
}
你可以调用 Singleton.getSingleton()
来获取此类的访问权限。
- 枚举单例自行处理序列化
传统单例的另一个问题是,一旦实现可序列化接口,它们就不再是 Singleton, 因为 readObject() 方法总是返回一个新实例, 就像 Java 中的构造函数一样。通过使用 readResolve() 方法, 通过在以下示例中替换 Singeton 来避免这种情况:
//readResolve to prevent another instance of Singleton
private Object readResolve(){
return INSTANCE;
}
如果 Singleton 类保持内部状态, 这将变得更加复杂, 因为你需要标记为 transient(不被序列化),但使用枚举单例, 序列化由 JVM 进行。
- 创建枚举实例是线程安全的
如第 1 点所述,因为 Enum 实例的创建在默认情况下是线程安全的, 你无需担心是否要做双重检查锁定。
总之, 在保证序列化和线程安全的情况下,使用两行代码枚举单例模式是在 Java 5 以后的世界中创建 Singleton 的最佳方式。你仍然可以使用其他流行的方法, 如你觉得更好, 欢迎讨论。
7. 编写 Java 程序时, 如何在 Java 中创建死锁并修复它?
经典但核心Java面试问题之一。
如果你没有参与过多线程并发 Java 应用程序的编码,你可能会失败。
如何避免 Java 线程死锁?
如何避免 Java 中的死锁?是 Java 面试的热门问题之一, 也是多线程的编程中的重口味之一, 主要在招高级程序员时容易被问到, 且有很多后续问题。尽管问题看起来非常基本, 但大多数 Java 开发人员一旦你开始深入, 就会陷入困境。
面试问题总是以“什么是死锁🔒?”开始
当两个或多个线程在等待彼此释放所需的资源(锁定)并陷入无限等待即是死锁。它仅在多任务或多线程的情况下发生。
如何检测 Java 中的死锁?
虽然这可以有很多答案, 但我的版本是首先我会看看代码, 如果我看到一个嵌套的同步块,或从一个同步的方法调用其他同步方法, 或试图在不同的对象上获取锁, 如果开发人员不是非常小心,就很容易造成死锁。
另一种方法是在运行应用程序时实际锁定时找到它, 尝试采取线程转储,在 Linux 中,你可以通过kill -3
命令执行此操作, 这将打印应用程序日志文件中所有线程的状态, 并且你可以看到哪个线程被锁定在哪个线程对象上。
你可以使用 fastthread.io 网站等工具分析该线程转储, 这些工具允许你上载线程转储并对其进行分析。
另一种方法是使用 jConsole 或 VisualVM, 它将显示哪些线程被锁定以及哪些对象被锁定。
如果你有兴趣了解故障排除工具和分析线程转储的过程, 我建议你看看 Uriah Levy 在多元视觉(PluraIsight)上《分析 Java 线程转储》课程。旨在详细了解 Java 线程转储, 并熟悉其他流行的高级故障排除工具。
编写一个将导致死锁的Java程序?
一旦你回答了前面的问题,他们可能会要求你编写代码,这将导致Java死锁。
这是我的版本之一
/**
-
Java 程序通过强制循环等待来创建死锁。
*/
public class DeadLockDemo {
/*
- 此方法请求两个锁,第一个字符串,然后整数
*/
public void method1() {
synchronized (String.class) {
System.out.println(“Aquired lock on String.class object”);
synchronized (Integer.class) {
System.out.println(“Aquired lock on Integer.class object”);
}
}
}
/*
-
此方法也请求相同的两个锁,但完全
-
相反的顺序,即首先整数,然后字符串。
-
如果一个线程持有字符串锁,则这会产生潜在的死锁
-
和其他持有整数锁,他们等待对方,永远。
*/
public void method2() {
synchronized (Integer.class) {
System.out.println(“Aquired lock on Integer.class object”);
synchronized (String.class) {
System.out.println(“Aquired lock on String.class object”);
}
}
}
}
如果 method1() 和 method2() 都由两个或多个线程调用,则存在死锁的可能性, 因为如果线程 1 在执行 method1() 时在 Sting 对象上获取锁, 线程 2 在执行 method2() 时在 Integer 对象上获取锁, 等待彼此释放 Integer 和 String 上的锁以继续进行一步, 但这永远不会发生。
此图精确演示了我们的程序, 其中一个线程在一个对象上持有锁, 并等待其他线程持有的其他对象锁。
你可以看到, Thread1 需要 Thread2 持有的 Object2 上的锁,而 Thread2 希望获得 Thread1 持有的 Object1 上的锁。由于没有线程愿意放弃, 因此存在死锁, Java 程序被卡住。
其理念是, 你应该知道使用常见并发模式的正确方法, 如果你不熟悉这些模式,那么 Jose Paumard 《应用于并发和多线程的常见 Java 模式》是学习的好起点。
如何避免Java中的死锁?
现在面试官来到最后一部分, 在我看来, 最重要的部分之一; 如何修复代码中的死锁?或如何避免Java中的死锁?
如果你仔细查看了上面的代码,那么你可能已经发现死锁的真正原因不是多个线程, 而是它们请求锁的方式, 如果你提供有序访问, 则问题将得到解决。
下面是我的修复版本,它通过避免循环等待,而避免死锁, 而不需要抢占, 这是需要死锁的四个条件之一。
public class DeadLockFixed {
/**
-
两种方法现在都以相同的顺序请求锁,首先采用整数,然后是 String。
-
你也可以做反向,例如,第一个字符串,然后整数,
-
只要两种方法都请求锁定,两者都能解决问题
-
顺序一致。
*/
public void method1() {
synchronized (Integer.class) {
System.out.println(“Aquired lock on Integer.class object”);
synchronized (String.class) {
System.out.println(“Aquired lock on String.class object”);
}
}
}
public void method2() {
synchronized (Integer.class) {
System.out.println(“Aquired lock on Integer.class object”);
synchronized (String.class) {
System.out.println(“Aquired lock on String.class object”);
}
}
}
}
现在没有任何死锁,因为两种方法都按相同的顺序访问 Integer 和 String 类文本上的锁。因此,如果线程 A 在 Integer 对象上获取锁, 则线程 B 不会继续, 直到线程 A 释放 Integer 锁, 即使线程 B 持有 String 锁, 线程 A 也不会被阻止, 因为现在线程 B 不会期望线程 A 释放 Integer 锁以继续。
8. 如果你的Serializable类包含一个不可序列化的成员,会发生什么?你是如何解决的?
任何序列化该类的尝试都会因NotSerializableException
而失败,但这可以通过在 Java中 为 static 设置瞬态(trancient)变量来轻松解决。
Java 序列化相关的常见问题
Java 序列化是一个重要概念, 但它很少用作持久性解决方案, 开发人员大多忽略了 Java 序列化 API。根据我的经验, Java 序列化在任何 Java核心内容面试中都是一个相当重要的话题, 在几乎所有的网面试中, 我都遇到过一两个 Java 序列化问题, 我看过一次面试, 在问几个关于序列化的问题之后候选人开始感到不自在, 因为缺乏这方面的经验。 他们不知道如何在 Java 中序列化对象, 或者他们不熟悉任何 Java 示例来解释序列化, 忘记了诸如序列化在 Java 中如何工作, 什么是标记接口, 标记接口的目的是什么, 瞬态变量和可变变量之间的差异, 可序列化接口具有多少种方法, 在 Java 中,Serializable
和 Externalizable
有什么区别, 或者在引入注解之后, 为什么不用 @Serializable
注解或替换 Serializalbe
接口。
在本文中,我们将从初学者和高级别进行提问, 这对新手和具有多年 Java 开发经验的高级开发人员同样有益。
关于Java序列化的10个面试问题
大多数商业项目使用数据库或内存映射文件或只是普通文件, 来满足持久性要求, 只有很少的项目依赖于 Java 中的序列化过程。无论如何,这篇文章不是 Java 序列化教程或如何序列化在 Java 的对象, 但有关序列化机制和序列化 API 的面试问题, 这是值得去任何 Java 面试前先看看以免让一些未知的内容惊到自己。
对于那些不熟悉 Java 序列化的人, Java 序列化是用来通过将对象的状态存储到带有.ser
扩展名的文件来序列化 Java 中的对象的过程, 并且可以通过这个文件恢复重建 Java对象状态, 这个逆过程称为 deserialization。
什么是 Java 序列化
序列化是把对象改成可以存到磁盘或通过网络发送到其他运行中的 Java 虚拟机的二进制格式的过程, 并可以通过反序列化恢复对象状态. Java 序列化API给开发人员提供了一个标准机制, 通过 java.io.Serializable
和 java.io.Externalizable
接口, ObjectInputStream
及ObjectOutputStream
处理对象序列化. Java 程序员可自由选择基于类结构的标准序列化或是他们自定义的二进制格式, 通常认为后者才是最佳实践, 因为序列化的二进制文件格式成为类输出 API的一部分, 可能破坏 Java 中私有和包可见的属性的封装.
如何序列化
让 Java 中的类可以序列化很简单. 你的 Java 类只需要实现 java.io.Serializable
接口, JVM 就会把 Object 对象按默认格式序列化。 让一个类是可序列化的需要有意为之。 类可序列会可能为是一个长期代价, 可能会因此而限制你修改或改变其实现. 当你通过实现添加接口来更改类的结构时, 添加或删除任何字段可能会破坏默认序列化, 这可以通过自定义二进制格式使不兼容的可能性最小化, 但仍需要大量的努力来确保向后兼容性。序列化如何限制你更改类的能力的一个示例是 SerialVersionUID。如果不显式声明 SerialVersionUID, 则 JVM 会根据类结构生成其结构, 该结构依赖于类实现接口和可能更改的其他几个因素。 假设你新版本的类文件实现的另一个接口, JVM 将生成一个不同的 SerialVersionUID 的, 当你尝试加载旧版本的程序序列化的旧对象时, 你将获得无效类异常 InvalidClassException。
问题 1) Java 中的可序列化接口和可外部接口之间的区别是什么?
这是 Java 序列化访谈中最常问的问题。下面是我的版本 Externalizable 给我们提供 writeExternal() 和 readExternal() 方法, 这让我们灵活地控制 Java 序列化机制, 而不是依赖于 Java 的默认序列化。 正确实现 Externalizable 接口可以显著提高应用程序的性能。
问题 2) 可序列化的方法有多少?如果没有方法,那么可序列化接口的用途是什么?
可序列化 Serializalbe 接口存在于java.io
包中,构成了 Java 序列化机制的核心。它没有任何方法, 在 Java 中也称为标记接口。 当类实现 java.io.Serializable
接口时, 它将在 Java 中变得可序列化, 并指示编译器使用 Java 序列化机制序列化此对象。
问题 3) 什么是 serialVersionUID ?如果你不定义这个, 会发生什么?
我最喜欢的关于Java序列化的问题面试问题之一。serialVersionUID 是一个 private static final long
型 ID, 当它被印在对象上时, 它通常是对象的哈希码,你可以使用 serialver
这个 JDK 工具来查看序列化对象的 serialVersionUID。SerialVerionUID 用于对象的版本控制。 也可以在类文件中指定 serialVersionUID。不指定 serialVersionUID的后果是,当你添加或修改类中的任何字段时, 则已序列化类将无法恢复, 因为为新类和旧序列化对象生成的 serialVersionUID 将有所不同。Java 序列化过程依赖于正确的序列化对象恢复状态的, ,并在序列化对象序列版本不匹配的情况下引发 java.io.InvalidClassException
无效类异常,了解有关 serialVersionUID 详细信息,请参阅这篇文章, 需要 FQ。
问题 4) 序列化时,你希望某些成员不要序列化?你如何实现它?
另一个经常被问到的序列化面试问题。这也是一些时候也问, 如什么是瞬态 transient 变量, 瞬态和静态变量会不会得到序列化等,所以,如果你不希望任何字段是对象的状态的一部分, 然后声明它静态或瞬态根据你的需要, 这样就不会是在 Java 序列化过程中被包含在内。
问题 5) 如果类中的一个成员未实现可序列化接口,会发生什么情况?
关于Java 序列化过程的一个简单问题。如果尝试序列化_实现了可序列化接口的类_的对象,但该对象包含对不可序列化类的引用,则在运行时将引发不可序列化异常 NotSerializableException
, 这就是为什么我始终将一个可序列化警报(在我的代码注释部分中),作为代码注释最佳实践之一, 提示开发人员记住这一事实, 在可序列化类中添加新字段时要注意。
问题 6) 如果类是可序列化的, 但其超类不是, 则反序列化后从超级类继承的实例变量的状态如何?
Java 序列化过程仅在对象层级都是_可序列化_的类中继续, 即:实现了可序列化接口, 如果从超级类没有实现可序列化接口,则超级类继承的实例变量的值将通过调用构造函数初始化。且一旦构造函数链启动, 就不可能停止, 因此, 即使层次结构中更高的类成员变量实现了可序列化接口, 也将通过执行构造函数创建,而不再是反序列化得到。如你所见, 这个序列化面试问题看起来非常不易回答, 但如果你熟悉关键概念, 则并不难。
问题 7) 是否可以自定义序列化过程, 或者是否可以覆盖 Java 中的默认序列化过程?
答案是肯定的, 你可以。我们都知道,对于序列化一个对象需调用 ObjectOutputStream.writeObject(saveThisObject)
, 并用 ObjectInputStream.readObject()
读取对象, 但 Java 虚拟机为你提供的还有一件事, 是定义这两个方法。 如果在类中定义这两种方法, 则 JVM 将调用这两种方法, 而不是应用默认序列化机制。 你可以在此处通过执行任何类型的预处理或后处理任务来自定义对象序列化和反序列化的行为。需要注意的重要一点是要声明这些方法为私有方法, 以避免被继承、重写或重载。 由于只有 Java 虚拟机可以调用类的私有方法, 你的类的完整性会得到保留, 并且 Java 序列化将正常工作。在我看来, 这是在任何 Java 序列化面试中可以问的最好问题之一, 一个很好的后续问题是, 为什么要为你的对象提供自定义序列化表单?
问题 8) 假设新类的超级类实现可序列化接口, 如何避免新类被序列化?
这是在 Java 序列化中不好回答的问题。如果类的 Super 类已经在 Java 中实现了可序列化接口, 那么它在 Java 中已经可以序列化, 因为你不能取消接口,它不可能真正使它无法序列化类, 但是有一种方法可以避免新类序列化。为了避免 Java 序列化,你需要在类中实现 writeObject()
和 readObject()
方法, 并且需要从该方法引发不序列化异常NotSerializableException
。 这是自定义 Java 序列化过程的另一个好处, 如上述序列化面试问题中所述, 并且通常随着面试进度, 它作为后续问题提出。
问题 9) 在 Java 中的序列化和反序列化过程中使用哪些方法?
这是很常见的面试问题, 在序列化基本上面试官试图知道: 你是否熟悉 readObject()
的用法、writeObject()
、readExternal()
和 writeExternal()
。 Java 序列化由java.io.ObjectOutputStream
类完成。 该类是一个筛选器流, 它封装在较低级别的字节流中, 以处理序列化机制。要通过序列化机制存储任何对象, 我们调用 ObjectOutputStream.writeObject(savethisobject)
, 并反序列化该对象, 我们称之为 ObjectInputStream.readObject()
方法。调用以 writeObject()
方法在 java 中触发序列化过程。关于 readObject()
方法, 需要注意的一点很重要一点是, 它用于从持久性读取字节, 并从这些字节创建对象, 并返回一个对象, 该对象需要类型强制转换为正确的类型。
问题 10) 假设你有一个类,它序列化并存储在持久性中, 然后修改了该类以添加新字段。如果对已序列化的对象进行反序列化, 会发生什么情况?
这取决于类是否具有其自己的 serialVersionUID。正如我们从上面的问题知道, 如果我们不提供 serialVersionUID, 则 Java 编译器将生成它, 通常它等于对象的哈希代码。通过添加任何新字段, 有可能为该类新版本生成的新 serialVersionUID 与已序列化的对象不同, 在这种情况下, Java 序列化 API 将引发 java.io.InvalidClassException
, 因此建议在代码中拥有自己的 serialVersionUID, 并确保在单个类中始终保持不变。
- Java序列化机制中的兼容更改和不兼容更改是什么?
真正的挑战在于通过添加任何字段、方法或删除任何字段或方法来更改类结构, 方法是使用已序列化的对象。根据 Java 序列化规范, 添加任何字段或方法都面临兼容的更改和更改类层次结构或取消实现的可序列化接口, 有些接口在非兼容更改下。对于兼容和非兼容更改的完整列表, 我建议阅读 Java 序列化规范。
- 我们可以通过网络传输一个序列化的对象吗?
是的 ,你可以通过网络传输序列化对象, 因为 Java 序列化对象仍以字节的形式保留, 字节可以通过网络发送。你还可以将序列化对象存储在磁盘或数据库中作为 Blob。
- 在 Java 序列化期间,哪些变量未序列化?
这个问题问得不同, 但目的还是一样的, Java开发人员是否知道静态和瞬态变量的细节。由于静态变量属于类, 而不是对象, 因此它们不是对象状态的一部分, 因此在 Java 序列化过程中不会保存它们。由于 Java 序列化仅保留对象的状态,而不是对象本身。瞬态变量也不包含在 Java 序列化过程中, 并且不是对象的序列化状态的一部分。在提出这个问题之后,面试官会询问后续内容, 如果你不存储这些变量的值, 那么一旦对这些对象进行反序列化并重新创建这些变量, 这些变量的值是多少?这是你们要考虑的。
9. 为什么Java中 wait 方法需要在 synchronized 的方法中调用?
另一个有难度的 Java 问题,wait 和 notify。它们是在有 synchronized 标记的方法或 synchronized 块中调用的,因为 wait 和 nodify 需要监视对其调用的 Object。
大多数Java开发人员都知道对象类的 wait(),notify() 和 notifyAll() 方法必须在 Java 中的 synchronized 方法或 synchronized 块中调用, 但是我们想过多少次, 为什么在 Java 中 wait, notify 和 notifyAll 来自 synchronized 块或方法?
最近这个问题在Java面试中被问到我的一位朋友,他思索了一下,并回答说: 如果我们不从同步上下文中调用 wait() 或 notify() 方法,我们将在 Java 中收到 IllegalMonitorStateException。
他的回答从实际效果上是正确的,但面试官对这样的答案不会完全满意,并希望向他解释这个问题。面试结束后他和我讨论了这个问题,我认为他应该告诉面试官关于 Java 中 wait()和 notify()之间的竞态条件,如果我们不在同步方法或块中调用它们就可能存在。
让我们看看竞态条件如何在 Java 程序中产生。它也是流行的线程面试问题之一。因此,如果你正在准备Java面试,那么你应该准备这样的问题,并且可以真正帮助你的一本书是《Java程序员面试公开书》的。这是一本罕见的书,涵盖了Java访谈的几乎所有重要主题,例如核心Java,多线程,IO 和 NIO 以及 Spring 和 Hibernate 等框架。
为什么要等待来自 Java中的 synchronized 方法的 wait方法为什么必须从 Java 中的 synchronized 块或方法调用 ? 我们主要使用 wait(),notify() 或 notifyAll() 方法用于 Java 中的线程间通信。一个线程在检查条件后正在等待,例如,在经典的生产者 - 消费者问题中,如果缓冲区已满,则生产者线程等待,并且消费者线程通过使用元素在缓冲区中创建空间后通知生产者线程。调用notify() 或 notifyAll() 方法向单个或多个线程发出一个条件已更改的通知,并且一旦通知线程离开 synchronized 块,正在等待的所有线程开始获取正在等待的对象锁定,幸运的线程在重新获取锁之后从 wait() 方法返回并继续进行。
让我们将整个操作分成几步,以查看Java 中 wait() 和 notify() 方法之间的竞争条件的可能性,我们将使用Produce Consumer 线程示例更好地理解方案:
-
Producer 线程测试条件(缓冲区是是否已满)并确认是否需要等待(如果发现缓冲区已满则需要等待)。
-
Consumer 线程在使用缓冲区中的元素后,设置条件。
-
Consumer 线程调用 notify() 方法; 这是不会被听到的,因为 Producer 线程还没有等待。
-
Producer 线程调用 wait() 方法并进入等待状态。
因此,由于竞态条件,我们可能会丢失通知,如果我们使用缓冲区或只使用一个元素,生产线程将永远等待,你的程序将挂起。“在Java 同步中等待 notify 和 notifyAll 现在让我们考虑如何解决这个潜在的竞态条件?这个竞态条件通过使用 Java 提供的 synchronized 关键字和锁定来解决。为了调用 wait(),notify() 或 notifyAll(),必须获得对我们调用方法的对象的锁定。由于 Java 中的 wait() 方法在等待之前释放锁定并在从 wait() 返回之前重新获取锁定方法,我们必须使用这个锁来确保检查条件(缓冲区是否已满) 和设置条件 (从缓冲区获取元素) 是原子的,这可以通过使用 synchronized 方法或块来实现。
我不确定这是否是面试官实际期待的,但这个我认为至少有意义,请纠正我如果我错了,请告诉我们是否还有其他令人信服的理由调用 wait(),notify() 或 Java 中的 notifyAll() 方法。
总结一下,我们用 Java 中的 synchronized 方法或 synchronized 块调用 Java 中的 wait(),notify() 或 notifyAll() 方法来避免:
-
Java 会抛出
IllegalMonitorStateException
,如果我们不调用来自同步上下文的wait(),notify()或者notifyAll()方法。 -
Javac 中 wait 和 notify 方法之间的任何潜在竞争条件。
10.你能用Java覆盖静态方法吗?如果我在子类中创建相同的方法是编译时错误?
不,你不能在Java中覆盖静态方法,但在子类中声明一个完全相同的方法不是编译时错误,这称为隐藏在Java中的方法。
你不能覆盖Java中的静态方法,因为方法覆盖基于运行时的动态绑定,静态方法在编译时使用静态绑定进行绑定。虽然可以在子类中声明一个具有相同名称和方法签名的方法,看起来可以在Java中覆盖静态方法,但实际上这是方法隐藏。Java不会在运行时解析方法调用,并且根据用于调用静态方法的 Object 类型,将调用相应的方法。这意味着如果你使用父类的类型来调用静态方法,那么原始静态将从父类中调用,另一方面如果你使用子类的类型来调用静态方法,则会调用来自子类的方法。简而言之,你无法在Java中覆盖静态方法。如果你使用像Eclipse或Netbeans这样的Java IDE,它们将显示警告静态方法应该使用类名而不是使用对象来调用,因为静态方法不能在Java中重写。
/**
-
Java program which demonstrate that we can not override static method in Java.
-
Had Static method can be overridden, with Super class type and sub class object
-
static method from sub class would be called in our example, which is not the case.
*/
public class CanWeOverrideStaticMethod {
public static void main(String args[]) {
Screen scrn = new ColorScreen();
//if we can override static , this should call method from Child class
scrn.show(); //IDE will show warning, static method should be called from classname
}
}
class Screen{
/*
- public static method which can not be overridden in Java
*/
public static void show(){
System.out.printf(“Static method from parent class”);
}
}
class ColorScreen extends Screen{
/*
-
static method of same name and method signature as existed in super
-
class, this is not method overriding instead this is called
-
method hiding in Java
*/
public static void show(){
System.err.println(“Overridden static method in Child Class in Java”);
总结
上述知识点,囊括了目前互联网企业的主流应用技术以及能让你成为“香饽饽”的高级架构知识,每个笔记里面几乎都带有实战内容。
很多人担心学了容易忘,这里教你一个方法,那就是重复学习。
打个比方,假如你正在学习 spring 注解,突然发现了一个注解@Aspect,不知道干什么用的,你可能会去查看源码或者通过博客学习,花了半小时终于弄懂了,下次又看到@Aspect 了,你有点郁闷了,上次好像在哪哪哪学习,你快速打开网页花了五分钟又学会了。
从半小时和五分钟的对比中可以发现多学一次就离真正掌握知识又近了一步。
人的本性就是容易遗忘,只有不断加深印象、重复学习才能真正掌握,所以很多书我都是推荐大家多看几遍。哪有那么多天才,他只是比你多看了几遍书。
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Java)
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
method in Child Class in Java");
总结
上述知识点,囊括了目前互联网企业的主流应用技术以及能让你成为“香饽饽”的高级架构知识,每个笔记里面几乎都带有实战内容。
很多人担心学了容易忘,这里教你一个方法,那就是重复学习。
打个比方,假如你正在学习 spring 注解,突然发现了一个注解@Aspect,不知道干什么用的,你可能会去查看源码或者通过博客学习,花了半小时终于弄懂了,下次又看到@Aspect 了,你有点郁闷了,上次好像在哪哪哪学习,你快速打开网页花了五分钟又学会了。
从半小时和五分钟的对比中可以发现多学一次就离真正掌握知识又近了一步。
[外链图片转存中…(img-lta0NCKZ-1713375699179)]
人的本性就是容易遗忘,只有不断加深印象、重复学习才能真正掌握,所以很多书我都是推荐大家多看几遍。哪有那么多天才,他只是比你多看了几遍书。
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Java)
[外链图片转存中…(img-c3lYUzSS-1713375699179)]
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!