JAVA数据类型有哪些?
八种基本数据类型分别是:
1、4种整数类型(byte、short、int、long);
2、2种浮点类型(float、double);
3、1种字符类型“char”;
4、1种布尔类型“boolean”。
Java 数据类型取值范围?
short -2 ^15 2 ^15 -1
int -2 ^31 2 ^31-1
long -2^63 2^63-1
java中常见的数据结构?
链表(LinkedList)、数组(ArrayList)、栈(Stack)、队列(ArrayBlockingQueue)、哈希表(HashMap)
说说&和&&的区别?
&和&&都可以用作逻辑与的运算符,表示逻辑与(and),当运算符两边的表达式的结果都为true时,整个运算结果才为true,否则,只要有一方为false,则结果为false。
&&还具有短路的功能,即如果第一个表达式为false,则不再计算第二个表达式,例如,对于if(str != null && !str.equals(“”))表达式,当str为null时,后面的表达式不会执行
&还可以用作位运算符,当&操作符两边的表达式不是boolean类型时,&表示按位与操作,我们通常使用0x0f来与一个整数进行&运算,来获取该整数的最低4个bit位,例如,0x31 & 0x0f的结果为0x01。
i++ 和 ++i 的区别?
i++ 是先运算,再加1; ++i 是先加1,再运算;
什么是硬拷贝?什么是软拷贝?
软拷贝就是复制对象里面的值;
硬拷贝是复制对象里面的所有东西,如果该对象引用了其他对象,则引用也会复制到新对象那边;
软拷贝一般是java.lang.Object 类里面的clone() 方法;
一般硬拷贝是使用序列化,然后再反析出来;(把对象写到流里面的过程叫序列化过程,从流中读取对象的过程叫反序列化过程)
transient 修饰的不能序列化,也就不能硬拷贝。
去重列表里面的重复元素?
java 8 新特性List 中stream 流,stream.distinct();
LinkedHashSet删除arraylist中的重复数据;
用List的contains方法循环遍历,重新排序,只添加一次数据,避免重复;
双重for循环去重;
简单说明什么是同步?什么是异步?
同步交互:指发送一个请求,需要等待返回,然后才能发送下一个请求,有个等待的过程;
异步请求:指发送一个请求,不需要等待返回,随时可以再发送下一个请求,即不需要等待;
switch语句能否作用在byte上,能否作用在long上,能否作用在String上?
在switch(expr1)中,expr1只能是一个整数表达式或者枚举常量,整数表达式可以是int基本类型或Integer包装类型,由于,byte,short,char都可以隐含转换为int,所以,这些类型以及这些类型的包装类型也是可以的。显然,long和String类型都不符合switch的语法规定,并且不能被隐式转换成int类型,所以,它们不能作用于swtich语句中。
short s1 = 1; s1 = s1 + 1;有什么错? short s1 = 1; s1 += 1;有什么错?
对于short s1 = 1; s1 = s1 + 1; 由于s1+1运算时会自动提升表达式的类型,所以结果是int型,再赋值给short类型s1时,编译器将报告需要强制转换类型的错误。
对于short s1 = 1; s1 += 1;由于 += 是java语言规定的运算符,java编译器会对它进行特殊处理,因此可以正确编译。
char型变量中能不能存贮一个中文汉字?为什么?
char型变量是用来存储Unicode编码的字符的,unicode编码字符集中包含了汉字,所以,char型变量中当然可以存储汉字啦。不过,如果某个特殊的汉字没有被包含在unicode编码字符集中,那么,这个char型变量中就不能存储这个特殊汉字。补充说明:unicode编码占用两个字节,所以,char类型的变量也是占用两个字节。
用最有效率的方法算出2乘以8等於几?
2 << 3,
因为将一个数左移n位,就相当于乘以了2的n次方,那么,一个数乘以8只要将其左移3位即可,而位运算cpu直接支持的,效率最高,所以,2乘以8等於几的最效率的方法是2 << 3。
使用final关键字修饰一个变量时,是引用不能变,还是引用的对象不能变?
使用final关键字修饰一个变量时,是指引用变量不能变,引用变量所指向的对象中的内容还是可以改变的。例如,对于如下语句:
final StringBuffer a=new StringBuffer(“immutable”);
执行如下语句将报告编译期错误:
a=new StringBuffer(“”);
但是,执行如下语句则可以通过编译:
a.append(" broken!");
有人在定义方法的参数时,可能想采用如下形式来阻止方法内部修改传进来的参数对象:
public void method(final StringBuffer param){
}
实际上,这是办不到的,在该方法内部仍然可以增加如下代码来修改参数对象:
param.append(“a”);
"== " 和equals方法究竟有什么区别?
对于字符串的比较“== ”比较的是两个字符串的地址
对于字符串的比较 “equals”比较的是两个字符串的内容
Integer与int的区别?
1.int是java 八种数据类型之一,Integer 为int 的封装类;
2.int的默认值为0,而Integer的默认值为null,即Integer可以区分出未赋值和值为0的区别,int则无法表达出未赋值的情况,
Math.round(11.5)等於多少? Math.round(-11.5)等於多少?
Math类中提供了三个与取整有关的方法:ceil、floor、round,这些方法的作用与它们的英文名称的含义相对应,例如,ceil的英文意义是天花板,该方法就表示向上取整,Math.ceil(11.3)的结果为12,Math.ceil(-11.3)的结果是-11;floor的英文意义是地板,该方法就表示向下取整,Math.floor(11.6)的结果为11,Math.floor(-11.6)的结果是-12;最难掌握的是round方法,它表示“四舍五入”,算法为Math.floor(x+0.5),即将原来的数字加上0.5后再向下取整,所以,Math.round(11.5)的结果为12,Math.round(-11.5)的结果为-11。
Overload和Override的区别。Overloaded的方法是否可以改变返回值的类型?
1.Overload是重载的意思,Override是覆盖的意思,也就是重写。
2.重载Overload表示同一个类中可以有多个名称相同的方法,但这些方法的参数列表各不相同(即参数个数或类型不同)。
3.重写Override表示子类中的方法可以与父类中的某个方法的名称和参数完全相同,通过子类创建的实例对象调用这个方法时,将调用子类中的定义方法,这相当于把父类中定义的那个完全相同的方法给覆盖了。
构造器Constructor是否可被override?
构造器Constructor不能被继承,因此不能重写Override,但可以被重载Overload。
面向对象的特征有哪些方面?
封装,继承,多态,抽象。
封装:封装数据和对数据的操作,对外提供一个最简单的接口,实现专业的分工,减少代码的耦合
继承:继承是从已有的类中派生出新的类,新的类可以吸收已有的属性、行为,并扩展新的能力。在Java中不支持多继承,单继承使Java的继承关系很简单,一个类只能有一个父类,这样易于我们的管理,同时父类也是子类的抽象化,而子类反过来就是父类的具体化
多态:多态是同一个行为具有多个不同表现形式或形态的能力
String和StringBuffer和StringBuilder的区别?
String变量不可修改,StringBuffer和StringBuilder可以修改
StringBuffer是线程安全的,StringBuilder不是线程安全的。
StringBuffer使用了缓存区,StringBuilder没有使用缓存区,所以没有修改数据的情况下,多次调用StringBuffer的toString方法获取的字符串是共享底层的字符数组的。而StringBuilder不是共享底层数组的,每次都生成了新的字符数组。
因为方法被上锁,所以StringBuffer的性能一般会比StringBuilder差,单线程中建议使用StringBuilder。
final, finally, finalize的区别?
final 用于声明属性,方法和类,分别表示属性不可变,方法不可覆盖,类不可继承。内部类要访问局部变量,局部变量必须定义成final类型
finally是异常处理语句结构的一部分,表示总是执行。
finalize是Object类的一个方法,在垃圾收集器执行的时候会调用被回收对象的此方法,可以覆盖此方法提供垃圾收集时的其他资源回收,例如关闭文件等。JVM不保证此方法总被调用
开启线程的三种方法?
1.继承Thread 类:
(1)定义Thred子类,重写该类的run方法,run方法代表了线程需要完成的任务,run方法被称为线程的执行体;
(2)创建Thread 子类的实例,即创建了线程的对象。
(3)调用线程对象的start()方法来启动该线程;
2.实现Runnable接口:
(1)定义Runnable 接口的实现类,重写该接口的run方法,该run方法的方法体同样是该线程的线程执行体;
(2)创建Runnable实现类的实例对象,并以此实例对象作为Thread的target来创建Thread类,该Thread对象才是真正的线程对象;
(3)调用此线程的start方法类启动该线程;
3.匿名内部类:
匿名内部类本质上也是一个类实现了Runnable接口,重写了run 方法,只不过这个类没有名字,直接作为参数传入Thread 类;
sleep() 和 wait() 有什么区别?
1、sleep是Thread的方法,wait是Object的方法
2、sleep方法没有释放锁,而wait方法释放了锁
3、wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用,而sleep可以在任何地方使用
4、sleep必须捕获异常,而wait,notify和notifyAll不需要捕获异常
5.sleep 不需要唤醒,wait 需要唤醒;
JDK JRE JVM ?
JDK 是开发工具,JRE 是运行环境,JVM是虚拟机
接口和抽象类的区别?
抽象类要被子类继承,只能继承一个,接口要被实现,可以实现多个。
抽象类设计的目的是解决代码复用的问题,接口设计是对行为的抽象;
抽象类中的变量是普通变量,接口里定义的变量只能是公共的静态变量;
抽象类可以有具体的方法和属性,接口只能有抽象方法和不可变常量;
List 和 Set 的区别?
List 和 Set 都是Collection 类的子类;
List: 有序,按照对象插入的顺序保存对象,可以重复,允许多个null 值,取值可以通过Iterator
遍历和get(int index) 通过下标取出指定元素;
Set 无序,不可重复,最多允许一个有null元素对象,取数据时只能用Iterator 接口取出所有元素,再逐一遍历各个元素;
ArrayList 和 LinkedList 区别?
ArrayList 和 LinkedList 是List 类的子类;
ArrayList基于动态数组,连续内存存储,适合下标访问(随机访问),查询快;
LinkedList 是链表,插入快,遍历用迭代器iterator,不能用for循环,性能消耗大。
HashTable 和 HashMap 的区别?
HashTable 和 HashMap 都是Map 的子类;
区别:HashMap 方法没有synchronized修饰,线程非安全,使用哈希算法对键去找冲,效率高,但无序;
HashTable 线程安全;
HashMap 允许Key 和 value 为null,HashTable 不允许;
底层实现:
jdk8开始链表高度到8、数组长度超过64,链表转变为红黑树,元素以内部类Node节点存在 计算key的hash值,二次hash然后对数组长度取模,对应到数组下标, 如果没有产生hash冲突(下标位置没有元素),则直接创建Node存入数组, 如果产生hash冲突,先进行equal比较,相同则取代该元素,不同,则判断链表高度插入链表,链 表高度达到8,并且数组长度到64则转变为红黑树,长度低于6则将红黑树转回链表 key为null,存在下标0的位置
Java 中的异常体系?
java中的所有异常都来自顶级父类Throwable。
Throwable下有两个子类Exception和Error。
Error是程序无法处理的错误,一旦出现这个错误,则程序将被迫停止运行。
Exception不会导致程序停止,又分为两个部分RunTimeException运行时异常和CheckedException检 查异常。 RunTimeException常常发生在程序运行过程中,会导致程序当前线程执行失败。CheckedException常 常发生在程序编译过程中,会导致程序编译不通过。
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加载就是不同的两个类
线程的生命周期?
线程通常有五种状态,创建,就绪,运行、阻塞和死亡状态。
1.新建状态(New):新创建了一个线程对象。
2.就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start方法。该状态的线程位于 可运行线程池中,变得可运行,等待获取CPU的使用权。
3.运行状态(Running):就绪状态的线程获取了CPU,执行程序代码。
4.阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进 入就绪状态,才有机会转到运行状态。
5.死亡状态(Dead):线程执行完了或者因异常退出了run方法,该线程结束生命周期。
阻塞的情况又分为三种:
(1)、等待阻塞:运行的线程执行wait方法,该线程会释放占用的所有资源,JVM会把该线程放入“等待 池”中。进入这个状态后,是不能自动唤醒的,必须依靠其他线程调用notify或notifyAll方法才能被唤 醒,wait是object类的方法
(2)、同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放 入“锁池”中。
(3)、其他阻塞:运行的线程执行sleep或join方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状 态。当sleep状态超时、join等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。 sleep是Thread类的方法
sleep()、wait()、join()、yield()的区别?
锁池 所有需要竞争同步锁的线程都会放在锁池当中,比如当前对象的锁已经被其中一个线程得到,则其他线 程需要在这个锁池进行等待,当前面的线程释放同步锁后锁池中的线程去竞争同步锁,当某个线程得到 后会进入就绪队列进行等待cpu资源分配。
等待池 当我们调用wait()方法后,线程会放到等待池当中,等待池的线程是不会去竞争同步锁。只有调用了 notify()或notifyAll()后等待池的线程才会开始去竞争锁,notify()是随机从等待池选出一个线程放 到锁池,而notifyAll()是将等待池的所有线程放到锁池当中
1.sleep 是 Thread 类的静态本地方法,wait 则是 Object 类的本地方法。
2、sleep方法不会释放lock,但是wait会释放,而且会加入到等待队列中。
sleep就是把cpu的执行资格和执行权释放出去,不再运行此线程,当定时时间结束再取回cpu资源,参与cpu 的调度,获取到cpu资源后就可以继续运行了。而如果sleep时该线程有锁,那么sleep不会释放这个锁,而 是把锁带着进入了冻结状态,也就是说其他需要这个锁的线程根本不可能获取到这个锁。也就是说无法执行程 序。如果在睡眠期间其他线程调用了这个线程的interrupt方法,那么这个线程也会抛出 interruptexception异常返回,这点和wait是一样的。
3、sleep方法不依赖于同步器synchronized,但是wait需要依赖synchronized关键字。
4、sleep不需要被唤醒(休眠之后推出阻塞),但是wait需要(不指定时间需要被别人中断)。
5、sleep 一般用于当前线程休眠,或者轮循暂停操作,wait 则多用于多线程之间的通信。
6、sleep 会让出 CPU 执行时间且强制上下文切换,而 wait 则不一定,wait 后可能还是有机会重新竞 争到锁继续执行的。
yield()执行后线程直接进入就绪状态,马上释放了cpu的执行权,但是依然保留了cpu的执行资格, 所以有可能cpu下次进行线程调度还会让这个线程获取到执行权继续执行 join()执行后线程进入阻塞状态,例如在线程B中调用线程A的join(),那线程B会进入到阻塞队 列,直到线程A结束或中断线程;
并发、并行、串行的区别?
串行在时间上不可能发生重叠,前一个任务没搞定,下一个任务就只能等着
并行在时间上是重叠的,两个任务在同一时刻互不干扰的同时执行。
并发允许两个任务彼此干扰。统一时间点、只有一个任务运行,交替执行
为什么用线程池?解释下线程池参数?
1、降低资源消耗;提高线程利用率,降低创建和销毁线程的消耗。
2、提高响应速度;任务来了,直接有线程可用可执行,而不是先创建线程,再执行。
3、提高线程的可管理性;线程是稀缺资源,使用线程池可以统一分配调优监控。
corePoolSize 代表核心线程数,也就是正常情况下创建工作的线程数,这些线程创建后并不会 消除,而是一种常驻线程
maxinumPoolSize 代表的是最大线程数,它与核心线程数相对应,表示最大允许被创建的线程 数,比如当前任务较多,将核心线程数都用完了,还无法满足需求时,此时就会创建新的线程,但 是线程池内线程总数不会超过最大线程数
keepAliveTime 、 unit 表示超出核心线程数之外的线程的空闲存活时间,也就是核心线程不会 消除,但是超出核心线程数的部分线程如果空闲一定的时间则会被消除,我们可以通过
setKeepAliveTime 来设置空闲时间 workQueue 用来存放待执行的任务,假设我们现在核心线程都已被使用,还有任务进来则全部放 入队列,直到整个队列被放满但任务还再持续进入则会开始创建新的线程 ThreadFactory 实际上是一个线程工厂,用来生产线程执行任务。我们可以选择使用默认的创建 工厂,产生的线程都在同一个组内,拥有相同的优先级,且都不是守护线程。当然我们也可以选择 自定义线程工厂,一般我们会根据业务来制定不同的线程工厂 Handler 任务拒绝策略,有两种情况,第一种是当我们调用 shutdown 等方法关闭线程池后,这 时候即使线程池内部还有没执行完的任务正在执行,但是由于线程池已经关闭,我们再继续想线程 池提交任务就会遭到拒绝。另一种情况就是当达到最大线程数,线程池已经没有能力继续处理新提 交的任务时,这是也就拒绝
线程池中阻塞队列的作用?为什么是先添加列队而不是先创建最大线程?
1、一般的队列只能保证作为一个有限长度的缓冲区,如果超出了缓冲长度,就无法保留当前的任务 了,阻塞队列通过阻塞可以保留住当前想要继续入队的任务。 阻塞队列可以保证任务队列中没有任务时阻塞获取任务的线程,使得线程进入wait状态,释放cpu资 源。 阻塞队列自带阻塞和唤醒的功能,不需要额外处理,无任务执行时,线程池利用阻塞队列的take方法挂 起,从而维持核心线程的存活、不至于一直占用cpu资源
2、在创建新线程的时候,是要获取全局锁的,这个时候其它的就得阻塞,影响了整体效率。
什么是GC,为什么会有GC?
gc是指垃圾回收机制,当一个对象不再被后续程序所引用时,这个对象占用的内存空间就没有存在的意义了,java 虚拟机会不定时的去检测内存中这样的对象,然后回收这块内存空间;
gc 的基本原理:
1.用new 关键字在堆中分配了内存,我们称为“可达”,对GC来说,只要所有被对象引用为null时,我们称之为"不可达,就将内存进行回收;
2.当一个对象被创建时,gc 开始监控这个对象的大小、内存及使用情况。GC采用有向图的方式记录和管理堆中的所有对象,通过这种方式可以明确对象时可达的,那些不是。当确确定为不可达时,则对其进行回收。
3.保证gc 在不同平台实现问题,java 规范对其很多行为没有进行严格规定,对于采用什么算法,什么时候回收等;
队列和栈取数据的顺序?
队列是先进先出,栈是先进后出;
堆和栈的区别?
栈内存存储的是局部变量,堆内存存储的是实体,栈内存的更新速度高于堆内存,栈内存的生命周期一结束就会被释放,堆内存会被垃圾回收机制不定时回收;
栈中存放的对象是引用对象方法中局部变量的值(参数的值);
堆中存放的是实例对象及成员变量的值(属性的值);
方法区存放类的方法代码,变量名,方法名,访问权限,返回值等;
局部变量,如果是基本数据类型,直接存储在栈中,如果是引用数据类型(类,接口,数组).eg:String str = new String(“12”),会把对象存在堆中,对象的引用(指针)存在栈中;
成员变量:类的成员变量在不同对象中各不相同(属性和方法),基本数据类型和引用数据类型都存储在这个对象中,作为整体存储在堆中,对象的引用(指针)存在栈中;而类的方法是对象工享的,方法是存在方法区的,只有调用的时候才会被压栈,不用时实占内存的。
成员变量和局部变量的区别?
(1)在类中的位置不同:
成员变量:类中方法外
局部变量:方法定义中或方法声明上;
(2)在内存中的位置不同
成员变量:在堆中
局部变量:在栈中
(3)生命周期不同
成员变量:有默认值
局部变量:没有默认值,必须定义,赋值才能使用,局部变量是基本方法上的参数;
JVM(虚拟机)中内存的划分?
任何软件在运行时都需要空间,java虚拟机也不例外,JVM运行时内存中会开辟一片内存区域,启动时在自己的内存区域中进行更细致的划分,因为虚拟机中的每一片内存处理方式都不同,所以要单独进行管理。JVM内存的划分分别是:
寄存器;
本地方法区;
方法区;
栈内存;
堆内存
java 的四种引用?
强引用,软引用,弱引用,虚引用;
强引用:Object obj = new Object;obj 就是强引用Object 实例,强引用属于不可回收资源,如果一个实例化对象被强引用中,那么垃圾回收器一定不会回收它,相反我们想要某个实例化想要尽早的被回收,可以将obj赋值未null 即可,这样jvm就回将来回收它。
软引用:如果一个对象被软引用,则说明它本身是可有可无的,垃圾回收器会在内存空间不足时回收此类对象,因此使用软引用时,要做好对象被置为null 的准备。
弱引用:和软引用相比,垃圾回收器回再第一次扫描时将其回收掉;
虚引用:虚引用几乎等同于无引用,不可获取目标对象,如果一个对象仅持有虚引用,那么它就和没有任何引用一样,再任何时候都会被垃圾回收器回收;
Spring 如何实现一个IOC 容器?
IOC 控制反转;
1、配置文件配置包扫描路径
2、递归包扫描获取.class文件
3、反射、确定需要交给IOC管理的类
4、对需要注入的类进行依赖注入
配置文件中需要扫描的包路径;
定义一些注解,分别表示控制层,数据持久层,依赖注入注解,获取配置文件注解
配置文件中获取扫描的包路径,获取到当前文件信息,将所有.class 结尾的文件添加到一个Set集合中进行存储;
遍历Set 集合,获取类上有指定注解的类,将其交给IOC 容器,定义一个安全的Map 来存储这些对象;
遍历IOC 容器,获取到每一个类的实例,判断里面是有其让他类的实例,然后递归注入;
spring是什么?
轻量级的开源的J2EE框架。它是一个容器框架,用来装javabean(java对象),中间层框架(万能胶) 可以起一个连接作用, Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架 --从大小与开销两方面而言Spring都是轻量级的。
–通过控制反转(IoC)的技术达到松耦合的目的
–提供了面向切面编程的丰富支持,允许通过分离应用的业务逻辑与系统级服务进行内聚性的 开发
–包含并管理应用对象(Bean)的配置和生命周期,这个意义上是一个容器。
–将简单的组件配置、组合成为复杂的应用,这个意义上是一个框架。
谈谈你对AOP 的理解?
系统是由许多不同的组件所组成的,每一个组件各负责一块特定功能。除了实现自身核心功能之外,这 些组件还经常承担着额外的职责。例如日志、事务管理和安全这样的核心服务经常融入到自身具有核心 业务逻辑的组件中去。这些系统服务经常被称为横切关注点,因为它们会跨越系统的多个组件。 当我们需要为分散的对象引入公共行为的时候,OOP(面向对象编程)则显得无能为力。也就是说,OOP允许你定义从 上到下的关系,但并不适合定义从左到右的关系。例如日志功能。 日志代码往往水平地散布在所有对象层次中,而与它所散布到的对象的核心功能毫无关系。 在OOP(面向对象)设计中,它导致了大量代码的重复,而不利于各个模块的重用。
AOP:将程序中的交叉业务逻辑(比如安全,日志,事务等),封装成一个切面,然后注入到目标对象 (具体业务逻辑)中去。AOP可以对某个对象或某些对象的功能进行增强,比如对象中的方法进行增 强,可以在执行某个方法之前额外的做一些事情,在某个方法执行之后额外的做一些事情。
谈谈你对IOC的理解?
容器概念、控制反转、依赖注入
ioc容器:实际上就是个map(key,value),里面存的是各种对象(在xml里配置的bean节点、 @repository、@service、@controller、@component),在项目启动的时候会读取配置文件里面的 bean节点,根据全限定类名使用反射创建对象放到map里、扫描到打上上述注解的类还是通过反射创 建对象放到map里。
这个时候map里就有各种对象了,接下来我们在代码里需要用到里面的对象时,再通过DI注入 (autowired、resource等注解,xml里bean节点内的ref属性,项目启动的时候会读取xml节点ref属性 根据id注入,也会扫描这些注解,根据类型或id注入;id就是对象名)。
控制反转: 没有引入IOC容器之前,对象A依赖于对象B,那么对象A在初始化或者运行到某一点的时候,自己必须 主动去创建对象B或者使用已经创建的对象B。无论是创建还是使用对象B,控制权都在自己手上。 引入IOC容器之后,对象A与对象B之间失去了直接联系,当对象A运行到需要对象B的时候,IOC容器会 主动创建一个对象B注入到对象A需要的地方。 通过前后的对比,不难看出来:对象A获得依赖对象B的过程,由主动行为变为了被动行为,控制权颠倒 过来了,这就是“控制反转”这个名称的由来。 全部对象的控制权全部上缴给“第三方”IOC容器,所以,IOC容器成了整个系统的关键核心,它起到了一 种类似“粘合剂”的作用,把系统中的所有对象粘合在一起发挥作用,如果没有这个“粘合剂”,对象与对 象之间会彼此失去联系,这就是有人把IOC容器比喻成“粘合剂”的由来。
依赖注入: “获得依赖对象的过程被反转了”。控制被反转之后,获得依赖对象的过程由自身管理变为了由IOC容器 主动注入。依赖注入是实现IOC的方法,就是由IOC容器在运行期间,动态地将某种依赖关系注入到对 象之中。
SpringMvc工作流程?

1)用户发送请求至前端控制器 DispatcherServlet。
2)DispatcherServlet 收到请求调用 HandlerMapping 处理器映射器。
3)处理器映射器找到具体的处理器(可以根据 xml 配置、注解进行查找),生成处理器及处理器拦截器 (如果有则生成)一并返回给 DispatcherServlet。
4)DispatcherServlet 调用 HandlerAdapter 处理器适配器。 5)HandlerAdapter 经过适配调用具体的处理器(Controller,也叫后端控制器)
6)Controller 执行完成返回 ModelAndView。
7)HandlerAdapter 将 controller 执行结果 ModelAndView 返回给 DispatcherServlet。
8) DispatcherServlet 将 ModelAndView 传给 ViewReslover 视图解析器。
9)ViewReslover 解析后返回具体 View。
10)DispatcherServlet 根据 View 进行渲染视图(即将模型数据填充至视图中)。
11)DispatcherServlet 响应用户
SpringBoot 自动配置原理?
@Import + @Configuration + Spring spi
动配置类由各个starter提供,使用@Configuration + @Bean定义配置类,放到METAINF/spring.factories下 使用Spring spi扫描META-INF/spring.factories下的配置类 使用@Import导入自动配置类