1.配置文件配置包扫描路径
2.递归包扫描获取.class文件
3.反射,确定需要交给IOC管理的类
4.对需要注入的类进行依赖注入
1.配置文件中指定需要扫描的包路径
2.定义一些注解,分别表示访问控制层, 业务服务层, 数据持久层, 依赖注入注解, 获取配置文件注解
3.从配置文件中获取需要扫描的包路径, 获取到当前路径下的文件信息及文件夹信息, 我们将当前路径下所有以.class结尾的文件添加到一个Set集合中进行存储
4.遍历这个set集合, 获取在类上有指定注解的类, 并将其交给IOC容器, 定义一个安全的Map用来存储这些对象
5.遍历这个IOC容器, 获取到每一个类的实例, 判断里面是有依赖其他的类的实例, 然后进行递归注入
java中引入了虚拟机的概念, 即在机器和编译程序之间加入了一层抽象的虚拟的机器.这台虚拟的机器在任何平台上都提供给编译程序一个共同的接口.
编译程序只需要面向虚拟机, 生成虚拟机能够理解的代码, 然后由解释器来将虚拟机代码转换为特定系统的机器码执行.在Java中,这种供虚拟机理解的代码叫做字节码(即扩展名为.class的文件), 它不面向特定的处理器, 只面向虚拟机.
每一种平台的解释器是不同的, 但是实现的虚拟机是相同的.Java源程序经过编译器编译后变成字节码, 字节码由虚拟机解释执行, 虚拟机将每一条要执行的字节码送给解释器, 解释器将其翻译成特定机器上的机器码, 然后在特定的机器上执行.这也就是解释了Java的编译与解释并存的特点.
Java源代码—>编译器---->JVM可执行的Java字节码(即虚拟指令)—>JVM---->JVM中解析器---->机器可执行的二进制机器码---->程序运行
Java语言通过字节码的方式,在一定程度上解决了传统解析型语言执行效率低的问题, 同时又保留了解析型语言可移植的特点.所以Java程序运行时比较高效,而且,由于字节码并不专对一种特定的机器,因此,Java程序无须重新编译便可在多种不同的计算机上运行.
JDK自带有三个类加载器:BootStrap ClassLoader, ExtClassLoader, AppClassLoader.
BootStrapClassLoader是ExtClassLoader的父类加载器, 默认负责加载 %JAVA_HOME%lib下的jar包和class文件.
ExtClassLoader是AppClassLoader的父加载器, 负责加载%JAVA_HOME%lib/ext文件下的jar包和class类.
AppClassLoader是自定义类加载器的父类,负责加载classpath下的类文件. 系统类加载器,线程上下文加载器
继承ClassLoader实现自定义类加载器
向上委派:实际上就是查找缓存,是否加载了该类, 有则直接返回, 没有继续向上(查找缓存)
向下查找:查找加载路径, 有则加载返回, 没有则继续向下查找(查加载路径)
委派到顶层之后,缓存中还是没有, 则到加载路径中查找, 有则加载返回,没有则向下查找
双亲委派模型的好处:
主要是为了安全性, 避免用户自己编写的类动态替换Java的一些核心类, 比如String
同时也避免了类的重复加载,因为JVM中区分不同类,不仅仅根据类名,相同的class文件被不同的ClassLoader加载就是不同的两个类
Java中的所有异常来自顶级父类Throwable.
Throwable下有两个子类Exception和Error
Error是程序无法处理的错误, 一旦出现这个错误, 则程序将被迫停止运行.
Exception不会导致程序停止, 又分为两个部分RunTimeExceotion运行时异常和CheckedException检查异常.
RunTimeException常常发生在程序运行过程中,会导致程序当前线程执行失败.CheckedException常常发生在程序编译过程中, 会导致程序编译不通过.
引用计数法:每个对象有一个引用计数属性, 新增一个引用时计数加一,引用释放时计数减一,计数为零时可以回收
可达性分析法:从GC Roots开始向下搜索,搜索所走过的路径称为引用链.当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的,那么虚拟机就判断是可回收对象.
GC Roots的对象有:
一:虚拟机栈(栈帧中的本地变量表)中引用的对象
二:方法区中类静态属性引用的对象
三:方法区中常量引用的对象
四:本地方法栈中Native本地方法引用的对象
可达性算法中的不可达对象并不是立即死亡的,对象拥有一次自我拯救的机会.对象被系统宣告死亡至少要经历两次标记过程:第一次是经历可达性分析发现没有与GC Roots相连接的引用链, 第二次是在虚拟机自动建立的Finalizer队列中判断是否需要执行finalize()方法.
当对象变成(GC Roots)不可达时, GC会判断该对象是否覆盖了finalize方法, 若未覆盖,则直接将其回收.否则,
若对象未执行过finalize方法,将其放入F-Queue队列,由-低优先级线程执行该队列中对象的finalize方法,执行finalize方法完毕后,GC会再次判断该对象是否可达,若不可达,则进行回收,否则,对象复活.
JVM包含两个子系统和两个组件,两个子系统为Class loader(类装载),Execution engine(执行引擎);两个组件为Runtime data area(运行时数据区),Native Interface(本地接口).
作用:首先通过编译器把Java代码装换成字节码,类加载器(ClassLoader)再把字节码加载到内存中,将其放在运行时数据区(Runtime data area)的方法区内,而字节码文件只是JVM的一套指令集规范,并不能直接交给底层操作系统去执行,因此需要特定的命令解释器执行引擎(Execution Engine),将字节码翻译成底层系统指令,再交由CPU去执行,而这个过程中需要调用其他语言的本地库接口(Native Interface)来实现整个程序的功能.
浅拷贝只是增加了一个指针指向已存在的内存地址
深拷贝是增加了一个指针并且申请了一个新的内存,使这个增加的指针指向这个新的内存
使用深拷贝的情况下,释放内存的时候不会因为出现浅拷贝时释放同一内存的错误.
物理地址:
堆的物理地址分配对象是不连续的.因此性能要慢一些,在GC的时候也要考虑到不连续的分配,所以有各种算法.比如,标记-清除,复制,标记-压缩,分代......
栈使用数据结构中的栈,地址是连续的,所以性能快.
内存分别:
堆因为是不连续的,所以分配的内存是在运行期确认的,因此大小不固定.一般堆远大于栈.
栈是连续的,所以分配的内存大小要在编译器就确认,大小是固定的.
存放的内容
堆存放的是对象的实例和数组.因此该区关注的是数据的存储
栈存放:局部变量,操作数栈,返回结果.该区更关注的是程序方法的执行.
JDK自带了很多监控工具,都位于JDK的bin目录下,其中最常用的是jconsole和jvisualvm这两款视图监控工具.
[2]Java开发实习面试打卡