设计模式常见面试题汇总Anke

常见的数据结构:栈(又称为堆栈)、队列、数组、链表和红黑树

一、线性表(重点)

线性表是由N个元素组成的有序序列,也是最常见的一种数据结构。重点有两个数组和链表。

1、数组

数组是一种存储单元连续,用来存储固定大小元素的线性表。java中对应的集合实现,比如ArrayList。

2、链表

链表又分单链表和双链表,是在物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中指针的链接次序实现的。java中对应的集合实现,比如LinkedList。

二、栈与队列

1、栈

栈,是一种运算受限的线性表,重点掌握其后进先出的特点。表的末端叫栈顶,基本操作有push(进栈)和pop(出栈)。java中stack就是简单的栈实现。

2、队列

队列也是一种操作受限制的线性表,重点掌握其先进先出的特点。表的前端只允许进行删除操作,表的后端进行插入操作。进行插入操作的端称为队尾,进行删除操作的端称为队头。java中很多Queue的实现,消息中间件的队列本质也是基于此的。

三、树(重点)

在非线性结构里面,树是非常非常重要的一种数据结构。基于其本身的结构优势,尤其在查找领域,应用广泛,其中又以二叉树最为重要。树的话我们这里只重点说一下二叉树。

1、二叉搜索树

二叉搜索树又叫二叉查找树,又叫二叉排序树。性质如下:(1)若左子树不空,则左子树上所有结点的值均小于它的根结点的值;(2)若右子树不空,则右子树上所有结点的值均大于它的根结点的值;(3)左、右子树也分别为二叉排序树;(4)没有键值相等的结点。

2、平衡二叉树

平衡二叉树又叫AVL树。性质如下:它的左子树和右子树都是平衡二叉树,且左子树和右子树的深度之差的绝对值不超过1。

3、红黑树

红黑树是一种特殊的平衡二叉树,它保证在最坏情况下基本动态集合操作的事件复杂度为O(logn)。

字符流和字节流的使用非常相似,但是实际上字节流的操作不会经过缓冲区(内存)而是直接操作文本本身的,而字符流的操作会先经过缓冲区(内存)然后通过缓冲区再操作文件

属于处理流中的缓冲流,可以将读取的内容存在内存里面,有readLine()方法,它,用来读取一行

优点:

(1)多线程技术使程序的响应速度更快

(4)可以随时停止任务

(5)可以分别设置各个任务的优先级以及优化性能

缺点:

(1)等候使用共享资源时造成程序的运行速度变慢

(2)对线程进行管理要求额外的cpu开销

start()方法:

1)用start方法来启动线程,真正实现了多线程运行,这时无需等待run方法体代码执行完毕而直接继续执行下面的代码。

run()方法:

1)run()方法只是类的一个普通方法而已,如果直接调用Run方法,程序中依然只有主线程这一个线程,其程序执行路径还是只有一条。

总结:

1)调用start方法方可启动线程,

2)而run方法只是thread的一个普通方法调用,还是在主线程里执行。

3)把需要并行处理的代码放在run()方法中,start()方法启动线程将自动调用run()方法,这是由jvm的内存机制规定的。

4)并且run()方法必须是public访问权限,返回值类型为void.。

(1)多线程使用volatile关键字修饰的变量,保证了其在多线程之间的可见性,即每次读取到volatile变量,一定是最新的数据

(2)Java代码执行中,为了获取更好的性能JVM可能会对指令进行重排序,多线程下可能会出现一些意想不到的问题。使用volatile则会对禁止语义重排序,当然这也一定程度上降低了代码执行效率

1)volatile本质是在告诉jvm当前变量在寄存器中的值是不确定的,需要从主存中读取,synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住.

2)volatile仅能使用在变量级别,synchronized则可以使用在变量,方法.

3)volatile仅能实现变量的修改可见性,而synchronized则可以保证变量的修改可见性和原子性.

4)volatile不会造成线程的阻塞,而synchronized可能会造成线程的阻塞.

如果线程是因为调用了wait()、sleep()或者join()方法而导致的阻塞,可以中断线程,并且通过抛出InterruptedException来唤醒它;如果线程遇到了IO阻塞,无能为力,因为IO是操作系统实现的,Java代码并没有办法直接接触到操作系统。

dump文件的作用:

死循环、死锁、阻塞、页面打开慢等问题,打线程dump是最好的解决问题的途径。因此,线程dump也就是线程堆栈。

获取到线程堆栈dump文件内容分两步:

(1)第一步:获取到线程的pid,Linux环境下可以使用ps-ef|grepjava

(2)第二步:打印线程堆栈,可以通过使用jstackpid命令

相同点:

二者都可以让线程处于冻结状态。

不同点:

1)首先应该明确sleep方法是Thread类中定义的方法,而wait方法是Object类中定义的方法。

4)sleep方法不一定非要定义在同步中。

wait方法必须定义在同步中。

5)当二者都定义在同步中时,

线程执行到sleep,不会释放锁。

线程执行到wait,会释放锁。

1)通过平衡生产者的生产能力和消费者的消费能力来提升整个系统的运行效率,这是生产者消费者模型最重要的作用

2)解耦,这是生产者消费者模型附带的作用,解耦意味着生产者和消费者之间的联系少,联系越少越可以独自发展而不需要收到相互的制约

1)ThreadLocal用来解决多线程程序的并发问题

2)ThreadLocal并不是一个Thread,而是Thread的局部变量,当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本.

3)从线程的角度看,目标变量就象是线程的本地变量,这也是类名中“Local”所要表达的意思。

4)线程局部变量并不是Java的新发明,Java没有提供在语言级支持(语法上),而是变相地通过ThreadLocal的类提供支持.

wait()方法立即释放对象监视器;

notify()/notifyAll()方法则会等待线程剩余代码执行完毕才会放弃对象监视器。

1)Lock是一个接口,而synchronized是Java中的关键字,synchronized是内置的语言实现;

2)synchronized在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生;而Lock在发生异常时,如果没有主动通过unLock()去释放锁,则很可能造成死锁现象,因此使用Lock时需要在finally块中释放锁;

3)Lock可以让等待锁的线程响应中断,而synchronized却不行,使用synchronized时,等待的线程会一直等待下去,不能够响应中断;

4)通过Lock可以知道有没有成功获取锁,而synchronized却无法办到。

5)Lock可以提高多个线程进行读操作的效率。

6)在JDK1.5中,synchronized是性能低效的。因为这是一个重量级操作,它对性能最大的影响是阻塞式的实现,挂起线程和恢复线程的操作都需要转入内核态中完成,这些操作给系统的并发性带来了很大的压力。相比之下使用Java提供的Lock对象,性能更高一些。

但是,JDK1.6,发生了变化,对synchronize加入了很多优化措施,有自适应自旋,锁消除,锁粗化,轻量级锁,偏向锁等等。导致在JDK1.6上synchronize的性能并不比Lock差。因此。提倡优先考虑使用synchronized来进行同步。

1、具有1-5工作经验的,面对目前流行的技术不知从何下手,

需要突破技术瓶颈的。2、在公司待久了,过得很安逸,

3、如果没有工作经验,但基础非常扎实,对java工作机制,

常用设计思想,常用java开发框架掌握熟练的。

4、觉得自己很牛B,一般需求都能搞定。

但是所学的知识点没有系统化,很难在技术领域继续突破的。

\5.群号:高级架构群468897908备注好信息!

多年工作经验的梳理和总结,带着大家全面、

科学地建立自己的技术体系和技术认知!

14、ConcurrentHashMap的并发度是什么?

ConcurrentHashMap的并发度就是segment的大小,默认为16,这意味着最多同时可以有16条线程操作ConcurrentHashMap,这也是ConcurrentHashMap对Hashtable的最大优势,任何情况下,Hashtable能同时有两条线程获取Hashtable中的数据

15、ReadWriteLock是什么?

ReadWriteLock是一个读写锁接口,ReentrantReadWriteLock是ReadWriteLock接口的一个具体实现,实现了读写的分离,读锁是共享的,写锁是独占的,读和读之间不会互斥,读和写、写和读、写和写之间才会互斥,提升了读写的性能。

16、FutureTask是什么?

FutureTask表示一个异步运算的任务。FutureTask里面可以传入一个Callable的具体实现类,可以对这个异步运算的任务的结果进行等待获取、判断是否已经完成、取消任务等操作。由于FutureTask也是Runnable接口的实现类,所以FutureTask也可以放入线程池中。

17、Java中用到的线程调度算法是什么?

18、单例模式的线程安全性?

单例模式的线程安全意味着:某个类的实例在多线程环境下只会被创建一次出来。单例模式有很多种的写法,具体分析如下:

(1)饿汉式单例模式的写法:线程安全

(2)懒汉式单例模式的写法:非线程安全

(3)双检锁单例模式的写法:线程安全

19、什么是乐观锁和悲观锁?

(1)乐观锁:对于并发间操作产生的线程安全问题持乐观状态,乐观锁认为竞争不总是会发生,因此它不需要持有锁,将比较-设置这两个动作作为一个原子操作尝试去修改内存中的变量,如果失败则表示发生冲突,那么就应该有相应的重试逻辑。

(2)悲观锁:对于并发间操作产生的线程安全问题持悲观状态,悲观锁认为竞争总是会发生,因此每次对某资源进行操作时,都会持有一个独占的锁,就像synchronized,直接对操作资源上了锁。

死锁现象描述:

线程A和线程B相互等待对方持有的锁导致程序无限死循环下去。

死锁的实现步骤:

(1)两个线程里面分别持有两个Object对象:lock1和lock2。这两个lock作为同步代码块的锁;

(3)线程2的run)(方法中同步代码块先获取lock2的对象锁,接着获取lock1的对象锁,当然这时lock1的对象锁已经被线程1锁持有,线程2肯定是要等待线程1释放lock1的对象锁的

这样,线程1″睡觉”睡完,线程2已经获取了lock2的对象锁了,线程1此时尝试获取lock2的对象锁,便被阻塞,此时一个死锁就形成了。

死锁的实现代码:

输出结果是:

线程A锁住资源O1,等待O2

线程B锁住资源O2,等待O1

把对象以流的方式,写入到文件中保存,叫写对象,也叫序列化

把文件中保存的对象,以流的方式读取出来,叫做读取数据,也叫对象的反序列化

Java堆内存被划分为新生代和年老代两部分,新生代主要使用复制和标记-清除垃圾回收算法;年老代主要使用标记-整理垃圾回收算法

指内存的永久保存区域,主要存放Class和Meta(元数据)的信息,Class在被加载的时候被放入永久区域,它和和存放实例的区域不同,GC不会在主程序运行期对永久区域进行清理。所以这也导致了永久代的区域会随着加载的Class的增多而胀满,最终抛出OOM异常。

主要存放应用程序中生命周期长的内存对象。

Java堆从GC的角度还可以细分为:新生代(Eden区、FromSurvivor区和ToSurvivor区)和老年代。

![img](file://C:/Users/Jared/Desktop/img/%E9%9D%A2%E8%AF%95%E5%B0%8F%E7%BB%93.assets/172d0f3d61d605delastModify=1600007113)

是用来存放新生的对象。一般占据堆的1/3空间。由于频繁创建对象,所以新生代会频繁触发MinorGC进行垃圾回收。新生代又分为Eden区、ServivorFrom、ServivorTo三个区。

线程独占:栈,本地方法栈,程序计数器

线程共享:堆,方法区

又称方法栈,线程私有的,线程执行方法是都会创建一个栈阵,用来存储局部变量表,操作栈,动态链接,方法出口等信息.调用方法时执行入栈,方法返回式执行出栈.

与栈类似,也是用来保存执行方法的信息.执行Java方法是使用栈,执行Native方法时使用本地方法栈.

保存着当前线程执行的字节码位置,每个线程工作时都有独立的计数器,只为执行Java方法服务,执行Native方法时,程序计数器为空.

JVM内存管理最大的一块,对被线程共享,目的是存放对象的实例,几乎所欲的对象实例都会放在这里,当堆没有可用空间时,会抛出OOM异常.根据对象的存活周期不同,JVM把对象进行分代管理,由垃圾回收器进行垃圾的回收管理

又称非堆区,用于存储已被虚拟机加载的类信息,常量,静态变量,即时编译器优化后的代码等数据.1.7的永久代和1.8的元空间都是方法区的一种实现。

前言

会。自己实现堆载的数据结构时有可能会出现内存泄露。

Java中,int类型变量的长度是一个固定值,与平台无关,都是32位。意思就是说,在32位和64位的Java虚拟机中,int类型的长度是相同的。

Serial与Parallel在GC执行的时候都会引起stop-the-world。它们之间主要不同serial收集器是默认的复制收集器,执行GC的时候只有一个线程,而parallel收集器使用多个GC线程来执行。

32位和64位的JVM中,int类型变量的长度是相同的,都是32位或者4个字节。

虽然WeakReference与SoftReference都有利于提高GC和内存的效率,但是WeakReference,一旦失去最后一个强引用,就会被GC回收,而软引用虽然不能阻止被回收,但是可以延迟到JVM内存不足的时候。

当你将你的应用从32位的JVM迁移到64位的JVM时,由于对象的指针从32位增加到了64位,因此堆内存会突然增加,差不多要翻倍。这也会对CPU缓存(容量比内存小很多)的数据产生不利的影响。因为,迁移到64位的JVM主要动机在于可以指定最大堆大小,通过压缩OOP可以节省一定的内存。通过-XX:+UseCompressedOops选项,JVM会使用32位的OOP,而不是64位的OOP。

你可以检查某些系统属性如sun.arch.data.model或os.arch来获取该信息。

理论上说上32位的JVM堆内存可以到达2^32,即4GB,但实际上会比这个小很多。不同操作系统之间不同,如Windows系统大约1.5GB,Solaris大约3GB。64位JVM允许指定最大的堆内存,理论上可以达到2^64,这是一个非常大的数字,实际上你可以指定堆内存大小到100GB。甚至有的JVM,如Azul,堆内存到1000G都是可能的。

JRE代表Java运行时(Javarun-time),是运行Java引用所必须的。JDK代表Java开发工具(Javadevelopmentkit),是Java程序打开发工具,如Java编译器,它也包含JRE。JVM代表Java虚拟机(Javavirtualmachine),它的责任是运行Java应用。JIT代表即时编译(JustInTimecompilation),当代码执行的次数超过一定的阈值时,会将Java字节码转换为本地代码,如,主要的热点代码会被准换为本地代码,这样有利大幅度提高Java应用的性能。

当通过Java命令启动Java进程的时候,会为它分配内存。内存的一部分用于创建堆空间,当程序中创建对象的时候,就从对空间中分配内存。GC是JVM内部的一个进程,回收无效对象的内存用于将来的分配。

JVM内存区域主要分为线程私有区域【程序计数器、虚拟机栈、本地方法区】、线程共享区域【JAVA堆、方法区】、直接内存。

线程私有数据区域生命周期与线程相同,依赖用户线程的启动/结束而创建/销毁(在HotspotVM内,每个线程都与操作系统的本地线程直接映射,因此这部分内存区域的存/否跟随本地线程的生/死对应)。

线程共享区域随虚拟机的启动/关闭而创建/销毁。

直接内存并不是JVM运行时数据区的一部分,但也会被频繁的使用:在JDK1.4引入的NIO提供了基于Channel与Buffer的IO方式,它可以使用Native函数库直接分配堆外内存,然后使用DirectByteBuffer对象作为这块内存的引用进行操作(详见:JavaI/O扩展),这样就避免了在Java堆和Native堆中来回复制数据,因此在一些场景中可以显著提高性能。

一块较小的内存空间,是当前线程所执行的字节码的行号指示器,每条线程都要有一个独立的程序计数器,这类内存也称为“线程私有”的内存。

正在执行java方法的话,计数器记录的是虚拟机字节码指令的地址(当前指令的地址)。如果还是Native方法,则为空。

这个内存区域是唯一一个在虚拟机中没有规定任何OutOfMemoryError情况的区域。

是描述java方法执行的内存模型,每个方法在执行的同时都会创建一个栈帧(StackFrame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法从调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程。

栈帧(Frame)是用来存储数据和部分过程结果的数据结构,同时也被用来处理动态链接(DynamicLinking)、方法返回值和异常分派(DispatchException)。栈帧随着方法调用而创建,随着方法结束而销毁——无论方法是正常完成还是异常完成(抛出了在方法内未被捕获的异常)都算作方法结束。

本地方法区和JavaStack作用类似,区别是虚拟机栈为执行Java方法服务,而本地方法栈则为Native方法服务,如果一个VM实现使用C-linkage模型来支持Native调用,那么该栈将会是一个C栈,但HotSpotVM直接就把本地方法栈和虚拟机栈合二为一。

不能,虽然你可以调用System.gc()或者Runtime.gc(),但是没有办法保证GC的执行。

JVM中堆和栈属于不同的内存区域,使用目的也不同。栈常用于保存方法帧和局部变量,而对象总是在堆上分配。栈通常都比堆小,也不会在多个线程之间共享,而堆被整个JVM的所有线程共享。

JVM中类的装载是由类加载器(ClassLoader)和它的子类来实现的,Java中各类加载器是一个重要的Java运行时系统组件,它负责在运行时查找和装入类文件中的类。

由于Java的跨平台性,经过编译的Java源程序并不是一个可执行程序,而是一个或多个类文件。当Java程序需要使用某个类时,JVM会确保这个类已经被加载、连接(验证、准备和解析)和初始化。类的加载是指把类的.class文件中的数据读入到内存中,通常是创建一个字节数组读入.class文件,然后产生与所加载类对应的Class对象。

加载完成后,Class对象还不完整,所以此时的类还不可用。当类被加载后就进入连接阶段,这一阶段包括验证、准备(为静态变量分配内存并设置默认的初始值)和解析(将符号引用替换为直接引用)三个步骤。最后JVM对类进行初始化,包括:1)如果类存在直接的父类并且这个类还没有被初始化,那么就先初始化父类;2)如果类中存在初始化语句,就依次执行这些初始化语句。

类的加载是由类加载器完成的,类加载器包括:根加载器(BootStrap)、扩展加载器(Extension)、系统加载器(System)和用户自定义类加载器(java.lang.ClassLoader的子类)。

从Java2(JDK1.2)开始,类加载过程采取了父亲委托机制(PDM)。PDM更好的保证了Java平台的安全性,在该机制中,JVM自带的Bootstrap是根加载器,其他的加载器都有且仅有一个父类加载器。类的加载首先请求父类加载器加载,父类加载器无能为力时才由其子类加载器自行加载。JVM不会向Java程序提供对Bootstrap的引用。下面是关于几个类

加载器的说明:

(1)Bootstrap:一般用本地代码实现,负责加载JVM基础核心类库(rt.jar);

(2)Extension:从java.ext.dirs系统属性所指定的目录中加载类库,它的父加载器是Bootstrap;

(3)System:又叫应用类加载器,其父类是Extension。它是应用最广泛的类加载器。它从环境变量classpath或者系统属性

java.class.path所指定的目录中记载类,是用户自定义加载器的默认父加载器。

GC是垃圾收集的意思,内存处理是编程人员容易出现问题的地方,忘记或者错误的内存回收会导致程序或系统的不稳定甚至崩溃,Java提供的GC功能可以自动监测对象是否超过作用域从而达到自动回收内存的目的,Java语言没有提供释放已分配内存的显示操作方法。Java程序员不用担心内存管理,因为垃圾收集器会自动进行管理。要请求垃圾收集,可以调用下面的方法之一:System.gc()或Runtime.getRuntime().gc(),但JVM可以屏蔽掉线示的垃圾回收调用。

是被线程共享的一块内存区域,创建的对象和数组都保存在Java堆内存中,也是垃圾收集器进行垃圾收集的最重要的内存区域。由于现代VM采用分代收集算法,因此Java堆从GC的角度还可以细分为:新生代(Eden区、FromSurvivor区和ToSurvivor区)和老年代。

即我们常说的永久代(PermanentGeneration),用于存储被JVM加载的类信息、常量、静态变量即、时编译器编译后的代码等数据.HotSpotVM把GC分代收集扩展至方法区,即使用Java堆的永久代来实现方法区,这样HotSpot的垃圾收集器就可以像管理Java堆一样管理这部分内存,而不必为方法区开发专门的内存管理器(永久带的内存回收的主要目标是针对常量池的回收和类型的卸载,因此收益一般很小)。

运行时常量池(RuntimeConstantPool)是方法区的一部分。Class文件中除了有类的版本、字段、方法、接口等描述等信息外,还有一项信息是常量池(ConstantPoolTable),用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后存放到方法区的运行时常量池中。Java虚拟机对Class文件的每一部分(自然也包括常量池)的格式都有严格的规定,每一个字节用于存储哪种数据都必须符合规范上的要求,这样才会被虚拟机认可、装载和执行。

Java新对象的出生地(如果新创建的对象占用内存很大,则直接分配到老年代)。当Eden区内存不够的时候就会触发MinorGC,对新生代区进行一次垃圾回收。

上一次GC的幸存者,作为这一次GC的被扫描者。

保留了一次MinorGC过程中的幸存者。

MinorGC采用复制算法。

(1)eden、servicorFrom复制到ServicorTo,年龄+1

首先,把Eden和ServivorFrom区域中存活的对象复制到ServicorTo区域(如果有对象的年龄以及达到了老年的标准,则赋值到老年代区),同时把这些对象的年龄+1(如果ServicorTo不够位置了就放到老年区);

(2)清空eden、servicorFrom

然后,清空Eden和ServicorFrom中的对象;

(3)ServicorTo和ServicorFrom互换

最后,ServicorTo和ServicorFrom互换,原ServicorTo成为下一次GC时的ServicorFrom区。

老年代的对象比较稳定,所以MajorGC不会频繁执行。在进行MajorGC前一般都先进行了一次MinorGC,使得有新生代的对象晋身入老年代,导致空间不够用时才触发。当无法找到足够大的连续空间分配给新创建的较大对象时也会提前触发一次MajorGC进行垃圾回收腾出空间。

MajorGC采用标记清除算法:首先扫描一次所有老年代,标记出存活的对象,然后回收没有标记的对象。ajorGC的耗时比较长,因为要扫描再回收。MajorGC会产生内存碎片,为了减少内存损耗,我们一般需要进行合并或者标记出来方便下次直接分配。当老年代也满了装不下的时候,就会抛出OOM(OutofMemory)异常。

在Java8中,永久代已经被移除,被一个称为“元数据区”(元空间)的区域所取代。元空间的本质和永久代类似,元空间与永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存。因此,默认情况下,元空间的大小仅受本地内存限制。类的元数据放入nativememory,字符串池和类的静态变量放入java堆中,这样可以加载多少类的元数据就不再由MaxPermSize控制,而由系统的实际可用空间来控制。

在Java中,引用和对象是有关联的。如果要操作对象则必须用引用进行。因此,很显然一个简单的办法是通过引用计数来判断一个对象是否可以回收。简单说,即一个对象如果没有任何与之关联的引用,即他们的引用计数都不为0,则说明对象不太可能再被用到,那么这个对象就是可回收对象。

为了解决引用计数法的循环引用问题,Java使用了可达性分析的方法。通过一系列的“GCroots”对象作为起点搜索。如果在“GCroots”和一个对象之间没有可达路径,则称该对象是不可达的。要注意的是,不可达对象不等价于可回收对象,不可达对象变为可回收对象至少要经过两次标记过程。两次标记后仍然是可回收对象,则将面临回收。

最基础的垃圾回收算法,分为两个阶段,标注和清除。标记阶段标记出所有需要回收的对象,清除阶段回收被标记的对象所占用的空间。如图

从图中我们就可以发现,该算法最大的问题是内存碎片化严重,后续可能发生大对象不能找到可利用空间的问题。

为了解决Mark-Sweep算法内存碎片化的缺陷而被提出的算法。按内存容量将内存划分为等大小的两块。每次只使用其中一块,当这一块内存满后将尚存活的对象复制到另一块上去,把已使用的内存清掉,如图:

这种算法虽然实现简单,内存效率高,不易产生碎片,但是最大的问题是可用内存被压缩到了原本的一半。且存活对象增多的话,Copying算法的效率会大大降低。

结合了以上两个算法,为了避免缺陷而提出。标记阶段和Mark-Sweep算法相同,标记后不是清理对象,而是将存活对象移向内存的一端。然后清除端边界外的对象。如图:

分代收集法是目前大部分JVM所采用的方法,其核心思想是根据对象存活的不同生命周期将内存划分为不同的域,一般情况下将GC堆划分为老生代(Tenured/OldGeneration)和新生代(YoungGeneration)。老生代的特点是每次垃圾回收时只有少量对象需要被回收,新生代的特点是每次垃圾回收时都有大量垃圾需要被回收,因此可以根据不同区域选择不同的算法。

目前大部分JVM的GC对于新生代都采取Copying算法,因为新生代中每次垃圾回收都要回收大部分对象,即要复制的操作比较少,但通常并不是按照1:1来划分新生代。一般将新生代划分为一块较大的Eden空间和两个较小的Survivor空间(FromSpace,ToSpace),每次使用Eden空间和其中的一块Survivor空间,当进行回收时,将该两块空间中还存活的对象复制到另一块Survivor空间中。

而老年代因为每次只回收少量对象,因而采用Mark-Compact算法。

(1)JAVA虚拟机提到过的处于方法区的永生代(PermanetGeneration),它用来存储class类,常量,方法描述等。对永生代的回收主要包括废弃常量和无用的类。

(2)对象的内存分配主要在新生代的EdenSpace和SurvivorSpace的FromSpace(Survivor目前存放对象的那一块),少数情况会直接分配到老生代。

(3)当新生代的EdenSpace和FromSpace空间不足时就会发生一次GC,进行GC后,EdenSpace和FromSpace区的存活对象会被挪到ToSpace,然后将EdenSpace和FromSpace进行清理。

(4)如果ToSpace无法足够存储某个对象,则将这个对象存储到老生代。

(5)在进行GC后,使用的便是EdenSpace和ToSpace了,如此反复循环。

(6)当对象在Survivor去躲过一次GC后,其年龄就会+1。默认情况下年龄到达15的对象会被移到老生代中。

在Java中最常见的就是强引用,把一个对象赋给一个引用变量,这个引用变量就是一个强引用。当一个对象被强引用变量引用时,它处于可达状态,它是不可能被垃圾回收机制回收的,即使该对象以后永远都不会被用到JVM也不会回收。因此强引用是造成Java内存泄漏的主要原因之一。

软引用需要用SoftReference类来实现,对于只有软引用的对象来说,当系统内存足够时它不会被回收,当系统内存空间不足时它会被回收。软引用通常用在对内存敏感的程序中。

弱引用需要用WeakReference类来实现,它比软引用的生存期更短,对于只有弱引用的对象来说,只要垃圾回收机制一运行,不管JVM的内存空间是否足够,总会回收该对象占用的内存。

虚引用需要PhantomReference类来实现,它不能单独使用,必须和引用队列联合使用。虚引用的主要作用是跟踪对象被垃圾回收的状态。

当前主流VM垃圾收集都采用”分代收集”(GenerationalCollection)算法,这种算法会根据对象存活周期的不同将内存划分为几块,如JVM中的新生代、老年代、永久代,这样就可以根据各年代特点分别采用最适当的GC算法

每次垃圾收集都能发现大批对象已死,只有少量存活.因此选用复制算法,只需要付出少量存活对象的复制成本就可以完成收集

因为对象存活率高、没有额外空间对它进行分配担保,就必须采用“标记—清理”或“标记—整理”算法来进行回收,不必进行内存复制,且直接腾出空闲内存。

Java堆内存被划分为新生代和年老代两部分,新生代主要使用复制和标记-清除垃圾回收算法;年老代主要使用标记-整理垃圾回收算法,因此java虚拟中针对新生代和年老代分别提供了多种不同的垃圾收集器,JDK1.6中SunHotSpot虚拟机的垃圾收集器如下:

Serial(英文连续)是最基本垃圾收集器,使用复制算法,曾经是JDK1.3.1之前新生代唯一的垃圾收集器。Serial是一个单线程的收集器,它不但只会使用一个CPU或一条线程去完成垃圾收集工作,并且在进行垃圾收集的同时,必须暂停其他所有的工作线程,直到垃圾收集结束。

Serial垃圾收集器虽然在收集垃圾过程中需要暂停所有其他的工作线程,但是它简单高效,对于限定单个CPU环境来说,没有线程交互的开销,可以获得最高的单线程垃圾收集效率,因此Serial垃圾收集器依然是java虚拟机运行在Client模式下默认的新生代垃圾收集器。

ParNew垃圾收集器其实是Serial收集器的多线程版本,也使用复制算法,除了使用多线程进行垃圾收集之外,其余的行为和Serial收集器完全一样,ParNew垃圾收集器在垃圾收集过程中同样也要暂停所有其他的工作线程。

ParNew收集器默认开启和CPU数目相同的线程数,可以通过-XX:ParallelGCThreads参数来限制垃圾收集器的线程数。【Parallel:平行的】

ParNew虽然是除了多线程外和Serial收集器几乎完全一样,但是ParNew垃圾收集器是很多java虚拟机运行在Server模式下新生代的默认垃圾收集器。

SerialOld是Serial垃圾收集器年老代版本,它同样是个单线程的收集器,使用标记-整理算法,这个收集器也主要是运行在Client默认的

java虚拟机默认的年老代垃圾收集器。在Server模式下,主要有两个用途:

(1)在JDK1.5之前版本中与新生代的ParallelScavenge收集器搭配使用。

(2)作为年老代中使用CMS收集器的后备垃圾收集方案。新生代Serial与年老代SerialOld搭配垃圾收集过程图:

新生代ParallelScavenge收集器与ParNew收集器工作原理类似,都是多线程的收集器,都使用的是复制算法,在垃圾收集过程中都需要暂停所有的工作线程。新生代ParallelScavenge/ParNew与年老代SerialOld搭配垃圾收集过程图:

ParallelOld收集器是ParallelScavenge的年老代版本,使用多线程的标记-整理算法,在JDK1.6才开始提供。

在JDK1.6之前,新生代使用ParallelScavenge收集器只能搭配年老代的SerialOld收集器,只能保证新生代的吞吐量优先,无法保证整体的吞吐量,ParallelOld正是为了在年老代同样提供吞吐量优先的垃圾收集器,如果系统对吞吐量要求比较高,可以优先考虑新生代ParallelScavenge和年老代ParallelOld收集器的搭配策略。

新生代ParallelScavenge和年老代ParallelOld收集器搭配运行过程图

只是标记一下GCRoots能直接关联的对象,速度很快,仍然需要暂停所有的工作线程。

进行GCRoots跟踪的过程,和用户线程一起工作,不需要暂停工作线程。

为了修正在并发标记期间,因用户程序继续运行而导致标记产生变动的那一部分对象的标记记录,仍然需要暂停所有的工作线程。

清除GCRoots不可达对象,和用户线程一起工作,不需要暂停工作线程。由于耗时最长的并发标记和并发清除过程中,垃圾收集线程可以和用户现在一起并发工作,所以总体上来看CMS收集器的内存回收和用户线程是一起并发地执行。CMS收集器工作过程

Garbagefirst垃圾收集器是目前垃圾收集器理论发展的最前沿成果,相比与CMS收集器,G1收集器两个最突出的改进是:

(1)基于标记-整理算法,不产生内存碎片。

JVM类加载机制分为五个部分:加载,验证,准备,解析,初始化,下面我们就分别来看一下这五个过程。

加载是类加载过程中的一个阶段,这个阶段会在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的入口。注意这里不一定非得要从一个Class文件获取,这里既可以从ZIP包中读取(比如从jar包和war包中读取),也可以在运行时计算生成(动态代理),也可以由其它文件生成(比如将JSP文件转换成对应的Class类)。

这一阶段的主要目的是为了确保Class文件的字节流中包含的信息是否符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。

准备阶段是正式为类变量分配内存并设置类变量的初始值阶段,即在方法区中分配这些变量所使用的内存空间。注意这里所说的初始值概念,比如一个类变量定义为:

实际上变量v在准备阶段过后的初始值为0而不是8080,将v赋值为8080的putstatic指令是程序被编译后,存放于类构造器方法之中。

publicstaticfinalintv=8080;在编译阶段会为v生成ConstantValue属性,在准备阶段虚拟机会根据ConstantValue属性将v赋值为8080。

解析阶段是指虚拟机将常量池中的符号引用替换为直接引用的过程。符号引用就是class文件中的

在编译阶段会为v生成ConstantValue属性,在准备阶段虚拟机会根据ConstantValue属性将v赋值为8080。

解析

解析阶段是指虚拟机将常量池中的符号引用替换为直接引用的过程。符号引用就是class文件中的:

(1)CONSTANT_Class_info

(2)CONSTANT_Field_info

(3)CONSTANT_Method_info

等类型的常量。

符号引用与虚拟机实现的布局无关,引用的目标并不一定要已经加载到内存中。各种虚拟机实现的内存布局可以各不相同,但是它们能接受的符号引用必须是一致的,因为符号引用的字面量形式明确定义在Java虚拟机规范的Class文件格式中。

直接引用可以是指向目标的指针,相对偏移量或是一个能间接定位到目标的句柄。如果有了直接引用,那引用的目标必定已经在内存中存在。

初始化阶段是类加载最后一个阶段,前面的类加载阶段之后,除了在加载阶段可以自定义类加载器以外,其它操作都由JVM主导。到了初始阶段,才开始真正执行类中定义的Java程序代码。

初始化阶段是执行类构造器方法的过程。方法是由编译器自动收集类中的类变量的赋值操作和静态语句块中的语句合并而成的。虚拟机会保证子方法执行之前,父类的方法已经执行完毕,如果一个类中没有对静态变量赋值也没有静态语句块,那么编译器可以不为这个类生成()方法。注意以下几种情况不会执行类初始化:

(1)通过子类引用父类的静态字段,只会触发父类的初始化,而不会触发子类的初始化。

(2)定义对象数组,不会触发该类的初始化。

(3)常量在编译期间会存入调用类的常量池中,本质上并没有直接引用定义常量的类,不会触发定义常量所在的类。

(4)通过类名获取Class对象,不会触发类的初始化。

(5)通过Class.forName加载指定类时,如果指定参数initialize为false时,也不会触发类初始化,其实这个参数是告诉虚拟机,是否要对类进行初始化。

(6)通过ClassLoader默认的loadClass方法,也不会触发初始化动作。

虚拟机设计团队把加载动作放到JVM外部实现,以便让应用程序决定如何获取所需的类,JVM提供了3种类加载器:

负责加载JAVA_HOME\lib目录中的,或通过-Xbootclasspath参数指定路径中的,且被虚拟机认可(按文件名识别,如rt.jar)的类。

负责加载JAVA_HOME\lib\ext目录中的,或通过java.ext.dirs系统变量指定路径中的类库。

负责加载用户路径(classpath)上的类库。JVM通过双亲委派模型进行类的加载,当然我们也可以通过继承java.lang.ClassLoader实现自定义的类加载器。

当一个类收到了类加载请求,他首先不会尝试自己去加载这个类,而是把这个请求委派给父类去完成,每一个层次类加载器都是如此,因此所有的加载请求都应该传送到启动类加载其中,只有当父类加载器反馈自己无法完成这个请求的时候(在它的加载路径下没有找到所需加载的Class),子类加载器才会尝试自己去加载。

采用双亲委派的一个好处是比如加载位于rt.jar包中的类java.lang.Object,不管是哪个加载器加载这个类,最终都是委托给顶层的启动类加载器进行加载,这样就保证了使用不同的类加载器最终得到的都是同样一个Object对象

OSGi(OpenServiceGatewayInitiative),是面向Java的动态模型系统,是Java动态化模块化系统的一系列规范。

OSGi服务平台提供在多种网络设备上无需重启的动态改变构造的功能。为了最小化耦合度和促使这些耦合度可管理,OSGi技术提供一种面向服务的架构,它能使这些组件动态地发现对方。

OSGi旨在为实现Java程序的模块化编程提供基础条件,基于OSGi的程序很可能可以实现模块级的热插拔功能,当程序升级更新时,可以只停用、重新安装然后启动程序的其中一部分,这对企业级程序开发来说是非常具有诱惑力的特性。

OSGi描绘了一个很美好的模块化开发目标,而且定义了实现这个目标的所需要服务与架构,同时也有成熟的框架进行实现支持。但并非所有的应用都适合采用OSGi作为基础架构,它在提供强大功能同时,也引入了额外的复杂度,因为它不遵守了类加载的双亲委托模型。

年轻代->标记-复制

老年代->标记-清除

栈是运行时单位,代表着逻辑,内含基本数据类型和堆中对象引用,所在区域连续,没有碎片;堆是存储单位,代表着数据,可被多个栈共享(包括成员中基本数据类型、引用和引用对象),所在区域不连续,会有碎片。

栈内存用来存储局部变量和方法调用,而堆内存用来存储Java中的对象。无论是成员变量,局部变量,还是类变量,它们指向的对象都存储在堆内存中。

栈内存是线程私有的。

堆内存是所有线程共有的。

如果栈内存或者堆内存不足都会抛出异常。

栈空间不足:java.lang.StackOverFlowError。

堆空间不足:java.lang.OutOfMemoryError。

栈的空间大小远远小于堆的

除直接调用System.gc外,触发FullGC执行的情况有如下四种。

旧生代空间只有在新生代对象转入及创建为大对象、大数组时才会出现不足的现象,当执行FullGC后空间仍然不足,则抛出如下错

误:

java.lang.OutOfMemoryError:Javaheapspace

PermanetGeneration中存放的为一些class的信息等,当系统中要加载的类、反射的类和调用的方法较多时,PermanetGeneration可能会被占满,在未配置为采用CMSGC的情况下会执行FullGC。如果经过FullGC仍然回收不了,那么JVM会抛出如下错误信息:

java.lang.OutOfMemoryError:PermGenspace

为避免PermGen占满造成FullGC现象,可采用的方法为增大PermGen空间或转为使用CMSGC。

对于采用CMS进行旧生代GC的程序而言,尤其要注意GC日志中是否有promotionfailed和concurrentmodefailure两种状况,当这两种状况出现时可能会触发FullGC。

promotionfailed是在进行MinorGC时,survivorspace放不下、对象只能放入旧生代,而此时旧生代也放不下造成的;concurrentmodefailure是在执行CMSGC的过程中同时有对象要放入旧生代,而此时旧生代空间不足造成的。

应对措施为:增大survivorspace、旧生代空间或调低触发并发GC的比率,但在JDK5.0+、6.0+的版本中有可能会由于JDK的bug29导致CMS在remark完毕后很久才触发sweeping动作。对于这种状况,可通过设置-XX:CMSMaxAbortablePrecleanTime=5(单位为ms)来避免。

这是一个较为复杂的触发情况,Hotspot为了避免由于新生代对象晋升到旧生代导致旧生代空间不足的现象,在进行MinorGC时,做了一个判断,如果之前统计所得到的MinorGC晋升到旧生代的平均大小大于旧生代的剩余空间,那么就直接触发FullGC。

例如程序第一次触发MinorGC后,有6MB的对象晋升到旧生代,那么当下一次MinorGC发生时,首先检查旧生代的剩余空间是否大于6MB,如果小于6MB,则执行FullGC。

Java虚拟机是一个可以执行Java字节码的虚拟机进程。Java源文件被编译成能被Java虚拟机执行的字节码文件。Java被设计成允许应用程序可以运行在任意的平台,而不需要程序员为每一个平台单独重写或者是重新编译。Java虚拟机让这个变为可能,因为它知道底层硬件平台的指令长度和其他特性。

(1)对象优先分配在Eden区,如果Eden区没有足够的空间时,虚拟机执行一次MinorGC。

(2)大对象直接进入老年代(大对象是指需要大量连续内存空间的对象)。这样做的目的是避免在Eden区和两个Survivor区之间发生大量的内存拷贝(新生代采用复制算法收集内存)。

(3)长期存活的对象进入老年代。虚拟机为每个对象定义了一个年龄计数器,如果对象经过了1次MinorGC那么对象会进入Survivor区,之后每经过一次MinorGC那么对象的年龄加1,知道达到阀值对象进入老年区。

(4)动态判断对象的年龄。如果Survivor区中相同年龄的所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象可以直接进入老年代。

(5)空间分配担保。每次进行MinorGC时,JVM会计算Survivor区移至老年区的对象的平均大小,如果这个值大于老年区的剩余值大小则进行一次FullGC,如果小于检查HandlePromotionFailure设置,如果true则只进行MonitorGC,如果false则进行FullGC

JVM中类的装载是由类加载器(ClassLoader)和它的子类来实现的,Java中的类加载器是一个重要的Java运行时系统组件,它负责在运行时查找和装入类文件中的类。

由于Java的跨平台性,经过编译的Java源程序并不是一个可执行程序,而是一个或多个类文件。当Java程序需要使用某个类时,JVM会确保这个类已经被加载、连接(验证、准备和解析)和初始化。

类的加载是指把类的.class文件中的数据读入到内存中,通常是创建一个字节数组读入.class文件,然后产生与所加载类对应的Class对象。加载完成后,Class对象还不完整,所以此时的类还不可用。

当类被加载后就进入连接阶段,这一阶段包括验证、准备(为静态变量分配内存并设置默认的初始值)和解析(将符号引用替换为直接引用)三个步骤。最后JVM对类进行初始化,

包括:

(1)如果类存在直接的父类并且这个类还没有被初始化,那么就先初始化父类;

(2)如果类中存在初始化语句,就依次执行这些初始化语句。类的加载是由类加载器完成的,类加载器包括:根加载器(BootStrap)、扩展加载器(Extension)、系统加载器(System)和用户自定义类加载器(java.lang.ClassLoader的子类)。

从Java2(JDK1.2)开始,类加载过程采取了父亲委托机制(PDM)。PDM更好的保证了Java平台的安全性,在该机制中,JVM自带的Bootstrap是根加载器,其他的加载器都有且仅有一个父类加载器。类的加载首先请求父类加载器加载,父类加载器无能为力时才由其子类加载器自行加载。JVM不会向Java程序提供对Bootstrap的引用。下面是关于几个类加载器的说明

Bootstrap:一般用本地代码实现,负责加载JVM基础核心类库(rt.jar);

Extension:从java.ext.dirs系统属性所指定的目录中加载类库,它的父加载器是Bootstrap;

System:又叫应用类加载器,其父类是Extension。它是应用最广泛的类加载器。它从环境变量classpath或者系统属性java.class.path所指定的目录中记载类,是用户自定义加载器的默认父加载器。

(1)JVM遇到一条新建对象的指令时首先去检查这个指令的参数是否能在常量池中定义到一个类的符号引用。然后加载这个类(类加载过程在后边讲)

(2)为对象分配内存。一种办法“指针碰撞”、一种办法“空闲列表”,最终常用的办法“本地线程缓冲分配(TLAB)”

(3)将除对象头外的对象内存空间初始化为0

(4)对对象头进行必要设置

Java对象由三个部分组成:对象头、实例数据、对齐填充。

对象头由两部分组成,第一部分存储对象自身的运行时数据:哈希码、GC分代年龄、锁标识状态、线程持有的锁、偏向线程ID(一般占32/64bit)。第二部分是指针类型,指向对象的类元数据类型(即对象代表哪个类)。如果是数组对象,则对象头中还有一部分用来记录数组长度。

实例数据用来存储对象真正的有效信息(包括父类继承下来的和自己定义的)

对齐填充:JVM要求对象起始地址必须是8字节的整数倍(8字节对齐)

判断对象是否存活一般有两种方式:

引用计数:每个对象有一个引用计数属性,新增一个引用时计数加1,引用释放时计数减1,计数为0时可以回收。此方法简单,无法解决对象相互循环引用的问题。

可达性分析(ReachabilityAnalysis):从GCRoots开始向下搜索,搜索所走过的路径称为引用链。当一个对象到GCRoots没有任何引用链相连时,则证明此对象是不可用的,不可达对象。

垃圾回收不会发生在永久代,如果永久代满了或者是超过了临界值,会触发完全垃圾回收(FullGC)。如果你仔细查看垃圾收集器的输出信息,就会发现永久代也是被回收的。这就是为什么正确的永久代大小对避免FullGC是非常重要的原因。请参考下Java8:从永久代到元数据区(注:Java8中已经移除了永久代,新加了一个叫做元数据区的native内存区)

GC最基础的算法有三种:标记-清除算法、复制算法、标记-压缩算法,我们常用的垃圾回收器一般都采用分代收集算法。

“标记-清除”(Mark-Sweep)算法,如它的名字一样,算法分为“标记”和“清除”两个阶段:首先标记出所有需要回收的对象,在标记完成后统一回收掉所有被标记的对象。

“复制”(Copying)的收集算法,它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。

标记过程仍然与“标记-清除”算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存

“分代收集”(GenerationalCollection)算法,把Java堆分为新生代和老年代,这样就可以根据各个年代的特点采用最适当的收集算法

SunJDK监控和故障处理命令有jpsjstatjmapjhatjstackjinfo

(1)jps,JVMProcessStatusTool,显示指定系统内所有的HotSpot虚拟机进程。

(1)jstat,JVMstatisticsMonitoring是用于监视虚拟机运行时状态信息的命令,它可以显示出虚拟机进程中的类装载、内存、垃圾收集、JIT编译等运行数据。

(3)jmap,JVMMemoryMap命令用于生成heapdump文件

(4)jhat,JVMHeapAnalysisTool命令是与jmap搭配使用,用来分析jmap生成的dump,jhat内置了一个微型的HTTP/HTML服务器,生成dump的分析结果后,可以在浏览器中查看

(5)jstack,用于生成java虚拟机当前时刻的线程快照。

(6)jinfo,JVMConfigurationinfo这个命令作用是实时查看和调整虚拟机运行参数

常用调优工具分为两类,jdk自带监控工具:jconsole和jvisualvm,第三方有:MAT(MemoryAnalyzerTool)、GChisto。

(1)jconsole,JavaMonitoringandManagementConsole是从java5开始,在JDK中自带的java监控和管理控制台,用于对JVM中内存,线程和类等的监控

(2)jvisualvm,jdk自带全能工具,可以分析内存快照、线程快照;监控内存变化、GC变化等。

(3)MAT,MemoryAnalyzerTool,一个基于Eclipse的内存分析工具,是一个快速、功能丰富的Javaheap分析工具,它可以帮助我们查找内存泄漏和减少内存消耗

(4)GChisto,一款专业分析gc日志的工具

新生代内存不够用时候发生MGC也叫YGC,JVM内存不够的时候发生FGC

设定堆内存大小

-Xmx:堆内存最大限制。

设定新生代大小。新生代不宜太小,否则会有大量对象涌入老年代

-XX:NewSize:新生代大小

-XX:NewRatio新生代和老生代占比

-XX:SurvivorRatio:伊甸园空间和幸存者空间的占比

设定垃圾回收器年轻代用-XX:+UseParNewGC年老代用-XX:+UseConcMarkSweepGC

答:设计模式总共有23种,总体来说可以分为三大类:创建型模式(CreationalPatterns)、结构型模式(StructuralPatterns)和行为型模式(BehavioralPatterns)。

答:单例模式是一种常用的软件设计模式,在应用这个模式时,单例对象的类必须保证只有一个实例存在,整个系统只能使用一个对象实例。

优点:不会频繁地创建和销毁对象,浪费系统资源。

使用场景:IO、数据库连接、Redis连接等。

单例模式代码实现:

classSingleton{privatestaticSingletoninstance=newSingleton();publicstaticSingletongetInstance(){returninstance;}}单例模式调用代码:

publicclassLesson7\_3{publicstaticvoidmain(String[]args){Singletonsingleton1=Singleton.getInstance();Singletonsingleton2=Singleton.getInstance();System.out.println(singleton1==singleton2);}}程序的输出结果:true

可以看出以上单例模式是在类加载的时候就创建了,这样会影响程序的启动速度,那如何实现单例模式的延迟加载?在使用时再创建?

单例延迟加载代码:

//单例模式-延迟加载版classSingletonLazy{privatestaticSingletonLazyinstance;publicstaticSingletonLazygetInstance(){if(instance==null){instance=newSingletonLazy();}returninstance;}}以上为非线程安全的,单例模式如何支持多线程?

使用synchronized来保证,单例模式的线程安全代码:

classSingletonLazy{privatestaticSingletonLazyinstance;publicstaticsynchronizedSingletonLazygetInstance(){if(instance==null){instance=newSingletonLazy();}returninstance;}}3.什么是简单工厂模式?答:简单工厂模式又叫静态工厂方法模式,就是建立一个工厂类,对实现了同一接口的一些类进行实例的创建。比如,一台咖啡机就可以理解为一个工厂模式,你只需要按下想喝的咖啡品类的按钮(摩卡或拿铁),它就会给你生产一杯相应的咖啡,你不需要管它内部的具体实现,只要告诉它你的需求即可。

简单工厂示意图如下:

简单工厂代码实现:

classFactory{publicstaticStringcreateProduct(Stringproduct){Stringresult=null;switch(product){case"Mocca":result="摩卡";break;case"Latte":result="拿铁";break;default:result="其他";break;}returnresult;}}4.什么是抽象工厂模式?答:抽象工厂模式是在简单工厂的基础上将未来可能需要修改的代码抽象出来,通过继承的方式让子类去做决定。

抽象工厂实现代码如下:

在观察者模式中有如下角色:

观察者模式实现代码如下。

老王:更新了

Java:更新了

答:装饰器模式是指动态地给一个对象增加一些额外的功能,同时又不改变其结构。

优点:装饰类和被装饰类可以独立发展,不会相互耦合,装饰模式是继承的一个替代模式,装饰模式可以动态扩展一个实现类的功能。

装饰器模式的关键:装饰器中使用了被装饰的对象。

比如,创建一个对象“laowang”,给对象添加不同的装饰,穿上夹克、戴上帽子......,这个执行过程就是装饰者模式,实现代码如下。

interfaceIPerson{voidshow();}2)定义装饰器超类classDecoratorBaseimplementsIPerson{IPersoniPerson;publicDecoratorBase(IPersoniPerson){this.iPerson=iPerson;}@Overridepublicvoidshow(){iPerson.show();}}3)定义具体装饰器classJacketextendsDecoratorBase{publicJacket(IPersoniPerson){super(iPerson);}@Overridepublicvoidshow(){//执行已有功能iPerson.show();//定义新行为System.out.println("穿上夹克");}}classHatextendsDecoratorBase{publicHat(IPersoniPerson){super(iPerson);}@Overridepublicvoidshow(){//执行已有功能iPerson.show();//定义新行为System.out.println("戴上帽子");}}4)定义具体对象classLaoWangimplementsIPerson{@Overridepublicvoidshow(){System.out.println("什么都没穿");}}5)装饰器模式调用publicclassDecoratorTest{publicstaticvoidmain(String[]args){LaoWanglaoWang=newLaoWang();Jacketjacket=newJacket(laoWang);Hathat=newHat(jacket);hat.show();}}7.什么是模板方法模式?答:模板方法模式是指定义一个模板结构,将具体内容延迟到子类去实现。

以给冰箱中放水果为例,比如,我要放一个香蕉:开冰箱门→放香蕉→关冰箱门;如果我再要放一个苹果:开冰箱门→放苹果→关冰箱门。可以看出它们之间的行为模式都是一样的,只是存放的水果品类不同而已,这个时候就非常适用模板方法模式来解决这个问题,实现代码如下:

/\*\*添加模板方法\*/abstractclassRefrigerator{publicvoidopen(){System.out.println("开冰箱门");}publicabstractvoidput();publicvoidclose(){System.out.println("关冰箱门");}}classBananaextendsRefrigerator{@Overridepublicvoidput(){System.out.println("放香蕉");}}classAppleextendsRefrigerator{@Overridepublicvoidput(){System.out.println("放苹果");}}/\*\*调用模板方法\*/publicclassTemplateTest{publicstaticvoidmain(String[]args){Refrigeratorrefrigerator=newBanana();refrigerator.open();refrigerator.put();refrigerator.close();}}程序执行结果:

开冰箱门

放香蕉

关冰箱门

代理模式是给某一个对象提供一个代理,并由代理对象控制对原对象的引用。

举一个生活中的例子:比如买飞机票,由于离飞机场太远,直接去飞机场买票不太现实,这个时候我们就可以上携程App上购买飞机票,这个时候携程App就相当于是飞机票的代理商。

代理模式实现代码如下:

/\*\*定义售票接口\*/interfaceIAirTicket{voidbuy();}/\*\*定义飞机场售票\*/classAirTicketimplementsIAirTicket{@Overridepublicvoidbuy(){System.out.println("买票");}}/\*\*代理售票平台\*/classProxyAirTicketimplementsIAirTicket{privateAirTicketairTicket;publicProxyAirTicket(){airTicket=newAirTicket();}@Overridepublicvoidbuy(){airTicket.buy();}}/\*\*代理模式调用\*/publicclassProxyTest{publicstaticvoidmain(String[]args){IAirTicketairTicket=newProxyAirTicket();airTicket.buy();}}9.什么是策略模式?答:策略模式是指定义一系列算法,将每个算法都封装起来,并且使他们之间可以相互替换。

优点:遵循了开闭原则,扩展性良好。

缺点:随着策略的增加,对外暴露越来越多。

以生活中的例子来说,比如我们要出去旅游,选择性很多,可以选择骑车、开车、坐飞机、坐火车等,就可以使用策略模式,把每种出行作为一种策略封装起来,后面增加了新的交通方式了,如超级高铁、火箭等,就可以不需要改动原有的类,新增交通方式即可,这样也符合软件开发的开闭原则。策略模式实现代码如下:

骑自行车

答:适配器模式是将一个类的接口变成客户端所期望的另一种接口,从而使原本因接口不匹配而无法一起工作的两个类能够在一起工作。

缺点:过多地使用适配器,容易使代码结构混乱,如明明看到调用的是A接口,内部调用的却是B接口的实现。

以生活中的例子来说,比如有一个充电器是MicroUSB接口,而手机充电口却是TypeC的,这个时候就需要一个把MicroUSB转换成TypeC的适配器,如下图所示:

适配器实现代码如下:

/\*\*传统的充电线MicroUSB\*/interfaceMicroUSB{voidcharger();}/\*\*TypeC充电口\*/interfaceITypeC{voidcharger();}classTypeCimplementsITypeC{@Overridepublicvoidcharger(){System.out.println("TypeC充电");}}/\*\*适配器\*/classAdapterMicroUSBimplementsMicroUSB{privateTypeCtypeC;publicAdapterMicroUSB(TypeCtypeC){this.typeC=typeC;}@Overridepublicvoidcharger(){typeC.charger();}}/\*\*测试调用\*/publicclassAdapterTest{publicstaticvoidmain(String[]args){TypeCtypeC=newTypeC();MicroUSBmicroUSB=newAdapterMicroUSB(typeC);microUSB.charger();}}程序执行结果:

TypeC充电

答:JDK常用的设计模式如下:

publicfinalstaticDateFormatgetDateInstance();publicfinalstaticDateFormatgetDateInstance(intstyle);publicfinalstaticDateFormatgetDateInstance(intstyle,Localelocale);加密类

KeyGeneratorkeyGenerator=KeyGenerator.getInstance("DESede");Ciphercipher=Cipher.getInstance("DESede");2)适配器模式把其他类适配为集合类

ListarrayList=java.util.Arrays.asList(newInteger[]{1,2,3});ListarrayList=java.util.Arrays.asList(1,2,3);3)代理模式如JDK本身的动态代理。

interfaceAnimal{voideat();}classDogimplementsAnimal{@Overridepublicvoideat(){System.out.println("Thedogiseating");}}classCatimplementsAnimal{@Overridepublicvoideat(){System.out.println("Thecatiseating");}}//JDK代理类classAnimalProxyimplementsInvocationHandler{privateObjecttarget;//代理对象publicObjectgetInstance(Objecttarget){this.target=target;//取得代理对象returnProxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);}@OverridepublicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable{System.out.println("调用前");Objectresult=method.invoke(target,args);//方法调用System.out.println("调用后");returnresult;}}publicstaticvoidmain(String[]args){//JDK动态代理调用AnimalProxyproxy=newAnimalProxy();AnimaldogProxy=(Animal)proxy.getInstance(newDog());dogProxy.eat();}4)单例模式全局只允许有一个实例,比如:

Runtime.getRuntime();5)装饰器为一个对象动态的加上一系列的动作,而不需要因为这些动作的不同而产生大量的继承类。

java.io.BufferedInputStream(InputStream);java.io.DataInputStream(InputStream);java.io.BufferedOutputStream(OutputStream);java.util.zip.ZipOutputStream(OutputStream);java.util.Collections.checkedList(Listlist,Classtype);6)模板方法模式定义一个操作中算法的骨架,将一些步骤的执行延迟到其子类中。

比如,Arrays.sort()方法,它要求对象实现Comparable接口。

classPersonimplementsComparable{privateIntegerage;publicPerson(Integerage){this.age=age;}@OverridepublicintcompareTo(Objecto){Personperson=(Person)o;returnthis.age.compareTo(person.age);}}publicclassSortTest(){publicstaticvoidmain(String[]args){Personp1=newPerson(10);Personp2=newPerson(5);Personp3=newPerson(15);Person[]persons={p1,p2,p3};//排序Arrays.sort(persons);}}12.IO使用了什么设计模式?答:IO使用了适配器模式和装饰器模式。

答:Spring框架使用的设计模式如下。

1、什么是UML?具体包括哪些内容?答:标准建模语言UML。包括用例图,静态图(包括类图、对象图和包图),行为图,交互图(顺序图和合作图)和实现图。

2、JavaEE常用的设计模式?答:Java中的23种设计模式包括:Factory(工厂模式),Builder(建造模式),FactoryMethod(工厂方法模式),Prototype(原始模型模式),Singleton(单例模式),Facade(门面模式),Adapter(适配器模式),Bridge(桥梁模式),Composite(合成模式),Decorator(装饰模式),Flyweight(享元模式),Proxy(代理模式),Command(命令模式),Interpreter(解释器模式),Visitor(访问者模式),Iterator(迭代子模式),Mediator(调停者模式),Memento(备忘录模式),Observer(观察者模式),State(状态模式),Strategy(策略模式),TemplateMethod(模板方法模式),ChainOfResponsibleity(责任链模式)

3、说说你是如何理解工厂模式的?答:工厂模式是一种经常被使用到的模式,根据工厂模式实现的类可以根据提供的数据生成一组类中某一个类的实例,通常这一组类都拥有一个公共的抽象父类并且实现了相同的方法,但是,这些方法针对不同的数据进行了不同的操作。工厂模式的具体实现方法是:首先需要定义一个基类,该类的子类通过不同的方法实现了基类中的方法。然后需要定义一个工厂类,工厂类可以根据条件生成不同的子类实例。当得到子类的实例后,开发人员可以调用基类中的方法而不必考虑到底返回的是哪一个子类的实例。

4、确定模块的功能和模块的接口是在软件设计的那个队段完成的答:概要设计阶段。

UML统一建模语言

计算机软件:

程序:是按照实现设计的算法要求执行的指令序列

数据:是程序能正常操作的信息

文档:是程序开发维护和使用有关的各种图文资料

软件的生产过程:

软件的生产过程:分为需求分析,系统分析,系统设计,功能设计,实现,测试,运行和维护等几个主要阶段

项目的开发模型:

瀑布模型:它将软件开发过程划分为若干个相互区别而又彼此关联的阶段,每个阶段的工作都是以上一阶段的结果为依据,同时为下一阶段的工作提供了前提

适用于:已明确了客户需求,切记是明确客户需求切需求不会发生改变时

渐增模型:

是由一组有计划的,循环渐进的,不断改进的过程版本组成

演化模型:

对事先不能完整定义需求的软件项目的开发可以使用演化模型,演化模型可以减少由于需求不明给项目带来的风险

螺旋模型:

螺旋模型结合了瀑布模型和演化模型的优点,并增加了风险分析,该模块将其活动划分为**定制计划,风险分析,实施开发和客户评估**四类,已采用了循环往复,逐渐完善的方式工作定制计划:确定软件开发目标,选定实施方案,弄清项目的限制条件

风险分析:分析所选方案,考虑如何识别的消除风险

实施开发:实施软件开发

客户评估:评价软件的功能和性能,提出修正建议

智能模型:

基于知识库和专家的软件开发模型,是知识工程的软件工程在开发模型商相结合的人工智能产物

软件生存周期:

软件设计,开发,使用

包括:需求分析,概要设计,详细设计,实现,组装测试,确认测试,使用,维护,更新换代

软件开发方法:

结构化程序设计方法

模块化程序设计方法

面向对象程序设计方法

UML的特点:

统一标准

面向对象

可视化,表达能力强

独立于过程

易掌握,易用

软件系统体系结构:

视图:

用例视图

逻辑视图

构件视图

进程视图

配置视图

UML软件已用例为中心,已系统体系结构为主线,采用循环,迭代,渐增的方式进行开发

UML系统模型与建模:

三大模型图

用例模型图:用例图

静态模型图:类图,对象图,包图,构建图和配置图

动态模型图:活动图,顺序图,状态图和合作图

UML扩展:

UML扩展机制包括三种:构造型,标记型和约束型

UML开发的特征:

用例驱动的系统

以体系结构为中心

螺旋上升式的开发过程

以质量控制和风险管理为目标

项目的可行性研究与风险分析:

可行性研究分为:经济可行性研究,技术可行性研究和法律可行性研究

类之间的关系:

关联关系

聚集关系

继承关系

依赖和细化关系

UML设计模式:

分类:

创建型设计模式:工厂模式,建造模式,原型模式,单例模式

工厂模式分为三种:简单工厂,工厂方法,抽象方法

结构型设计模式:适配器对象模式,桥接模式,组合模式,装饰模式,外观模式,享元模式,代理模式

行为型审计模式:命令模式,迭代器模式,责任链模式,中介者模式,备忘录模式,观察者模式,状态模式,策略模式,访问者模式

答:字节流,字符流。字节流继承于InputStreamOutputStream,字符流继承于ReaderWriter。在java.io包中还有许多其他的流,低层流与调层流,高层流主要是为了提高性能和使用方便。

2、启动一个线程是用run()还是start()

答:启动一个线程是调用start()方法,启动线程并调用run方法。

3、线程的基本概念、线程的基本状态以及状态之间的关系是什么?

答:线程是进程内的并发,没有自已内存空间,共享进程的,线程间的通信成本较低。Java中的线程有四种状态分别是:运行、就绪、挂起、结束。

答:多线程有两种实现方法,分别是继承Thread类与实现Runnable接口。

同步的实现方面有两种,分别是synchronized,wait与notify。

反对使用stop(),是因为它不安全。它会解除由线程获取的所有锁定,而且如果对象处于一种不连贯状态,那么其他线程能在那种状态下检查和修改它们。结果很难检查出真正的问题所在。suspend()方法容易发生死锁。调用suspend()的时候,目标线程会停下来,但却仍然持有在这之前获得的锁定。此时,其他任何线程都不能访问锁定的资源,除非被"挂起"的线程恢复运行。对任何线程来说,如果它们想恢复目标线程,同时又试图使用任何一个锁定的资源,就会造成死锁。所以不应该使用suspend(),而应在自己的Thread类中置入一个标志,指出线程应该活动还是挂起。若标志指出线程应该挂起,便用wait()命其进入等待状态。若标志指出线程应当恢复,则用一个notify()重新启动线程。

5、同步和异步有和异同,在什么情况下分别使用他们?举例说明。

答:同步:上一段代码没的完成,下一段必须等到上一段代码完成后才可以执行。如买票排队

异步:上一段代码没的完成,下一段不必等到上一段代码完成就可以执行。如手机发送短信。

6、所知道的线程同步的方法都有什么?

7、如何列出某个目录下的所有文件?

答:代码如下:

->1.启动应用程序服务器时,容器将加载web.xml文件。

->2.从客户端浏览器向服务器发出带有特定URL的第一个请求时,控件首先到达web.xml文件。它在web.xml中检查URL的映射,并找到过滤器分派器。

->3.过滤器分派器确定是否使用动作映射器调用动作,并将控件委派给动作代理。

->4.ActionProxy从ConfigurationManager获得帮助,ConfigurationManager是从struts.xml初始化的。ActionProxy创建一个ActionInvocation。

->5.ActionInvocation查找要为此请求调用的Action类,并发现与动作映射关联的intereptor。

->6.现在,ActionInvocation调用堆栈中第一个拦截器的intercept()方法。执行第一个拦截器后,Invoke()将检查下一个拦截器。

->7.执行完所有拦截器后,将调用动作类。最后,将返回结果字符串,并将呈现相应的视图。

\2.Struts1.x和Struts2.x之间的区别?

3.什么是拦截器?

拦截器是在请求的预处理和后处理中调用的对象。在struts2中,拦截器用于执行诸如验证,异常处理,国际化,显示中间结果等操作。

4.什么是价值栈和OGNL?

值堆栈是Struts2在其中存储应用程序数据以处理客户端请求的存储区域。数据存储在ActionContext对象中,该对象使用ThreadLocal对特定请求线程具有特定的值。

对象图导航语言(OGNL)是一种功能强大的表达语言,用于处理存储在ValueStack上的数据。从体系结构图中可以看到,两个拦截器和结果页都可以使用OGNL访问存储在ValueStack上的数据。

\5.Struts2动作和拦截器是否是线程安全的?

Struts2动作类是线程安全的,因为会为处理该请求的每个请求实例化一个对象。

Struts2拦截器是单例类,并且创建了一个新线程来处理请求,因此它不是线程安全的,我们需要仔细实现它们,以避免共享数据出现任何问题。

6.什么是动作上下文和动作调用?

ActionContext是在其中执行动作的对象的容器。每个线程(即ThreadLocal)中存储在ActionContext中的值是唯一的。因此,我们不需要使操作线程安全。

我们可以通过调用ActionContext类的getContext()方法来获取ActionContext的引用。这是静态工厂方法。例如:ActionContextctx=ActionContext.getContext();

ActionInvocation表示动作的执行状态。它包含动作和拦截器对象。

Struts框架提供了ActionInvocation接口来处理ActionInvocation。它提供了许多方法,其中一些方法可用于获取ValueStack,ActionProxy,ActionContext,Result等的实例。

===============================================================

\1.java.lang之间的差异。StringBuffer和java.lang。StringBuilder?

java.lang.StringBuffer:线程安全的,同步的,并且没有那么快。

java.lang.StringBuilder:更快,不执行同步。

2.如何处理线程的未捕获异常?

@FunctionalInterface公共静态接口线程。UncaughtExceptionHandler

\3.[已检查的例外]与[未检查的例外]之间的区别?

CheckedExceptions应该用于预期的但无法预防的错误,可以从中恢复。

4.如何使用非静态内部类和静态内部类?

非静态:Out.Inin=newOut()。newIn();

静态:Out.StaticInin=newOut.StaticIn();

5.匿名内部阶级?

匿名内部类必须扩展超类或实现接口。

ClassAa=新的ClassA();

a.test(新产品(){

公共双重处理(){

}

});

6.如何创建动态代理类和动态代理实例?

java.lang.reflect。代理接口InvocationHandler

静态Class<?>getProxyClass(ClassLoaderloader,Class<?>...接口)静态对象newProxyInstance(ClassLoaderloader,Class<?>[]接口,InvocationHandlerh)

InvocationHandlerhandler=新的MyInvocationHandler(...);

类proxyClass=Proxy.getProxyClass(Foo.class.getClassLoader(),新Class[]{Foo.class});构造函数ctor=proxyClass.getConstructor(newClass[]{InvocationHandler.class});Foof=(Foo)ctor.newInstance(newObject[]{handler});

Foof=(Foo)Proxy.newProxyInstance(Foo.class.getClassLoader(),newClass[]{Foo.class},handler);

7.三种类型的类加载器之间有什么区别?

公共抽象类ClassLoader扩展Object

loadClass(字符串名称,布尔值解析)

findClass(字符串名称)

BootstrapClassLoader:加载Java的核心类。(java.exe-Xbootclasspath可以加载其他类)。不是java.lang.ClassLoader的子类。

jre/lib/*。jar

jre/类

扩展ClassLoader:jre/lib/ext/*或由java.ext.dirs系统属性定义。

系统ClassLoader:java-classpath或由java.class.path定义

java.lang.Objectjava.lang.ClassLoaderjava.security.SecureClassLoaderjava.net。URLClassLoader

扩展ClassLoader和SystemClassLoader的父级。

8.接口Serializable如何工作?

可序列化类的所有子类型本身都是可序列化的。

在序列化和反序列化期间需要特殊处理的类:

私有无效writeObject(java.io.ObjectOutputStream输出)抛出IOException私有无效readObject(java.io.ObjectInputStream输入)抛出IOException,ClassNotFoundException;私有voidreadObjectNoData()抛出ObjectStreamException;

指定将对象写入流时要使用的替代对象:

ANY-ACCESS-MODIFIER对象writeReplace()抛出ObjectStreamException;

从流中读取时指定一个替换:

ANY-ACCESS-MODIFIER对象readResolve()抛出ObjectStreamException;

ANY-ACCESS-MODIFIER静态最终长serialVersionUID=42L;

\9.Buffer如何工作?

java.nio.Buffer容量是它包含的元素数量。limit是不应读取或写入的第一个元素的索引。position是下一个要读取或写入的元素的索引。mark是调用reset方法时将其位置重置到的索引。

0<=标记<=位置<=极限<=容量

clear()将限制设置为容量,并将位置设置为零。

flip()将限制设置为当前位置,然后将位置设置为零。

rewind()保持限制不变,并将位置设置为零。

缓冲区不能安全用于多个并发线程。

b.flip()。position(23).limit(42);

\10.runnable和callable接口之间的区别?

可调用可具有返回值。

\11.java.util之间的区别。集合和接口Map

集合仅包含项目,而地图包含键值对。

12.接口Set(java.util.HashSet)和接口List(java.util.ArrayList)之间的区别?

Set无序且无重复,List无序且重复。

\13.HashSet,TreeSet和EnumSet之间的区别?

\14.HashTable,HashMap,EnumMap和TreeMap之间的区别?

对于HashMap,非线程安全,键和值可以为null。

哈希表的线程安全,键和值不能为null。

1)请简单解释算法是什么?

算法是一个定义良好的计算过程,它将一些值作为输入并产生相应的输出值。简单来说,它是将输入转换为输出的一系列计算步骤。

2)解释什么是快速排序算法?

快速排序算法能够快速排序列表或查询。它基于分割交换排序的原则,这种类型的算法占用空间较小,它将待排序列表分为三个主要部分:

5)解释二分法检索如何工作?

在二分法检索中,我们先确定数组的中间位置,然后将要查找的值与数组中间位置的值进行比较,若小于数组中间值,则要查找的值应位于该中间值之前,依此类推,不断缩小查找范围,直至得到最终结果。

6)解释是否可以使用二分法检索链表?

7)解释什么是堆排序?

堆排序可以看成是选择排序的改进,它可以定义为基于比较的排序算法。它将其输入划分为未排序和排序的区域,通过不断消除最小元素并将其移动到排序区域来收缩未排序区域。

8)说明什么是Skiplist?

9)解释插入排序算法的空间复杂度是多少?

插入排序是一种就地排序算法,这意味着它不需要额外的或仅需要少量的存储空间。对于插入排序,它只需要将单个列表元素存储在初始数据的外侧,从而使空间复杂度为O(1)。

10)解释什么是“哈希算法”,它们用于什么?

“哈希算法”是一个哈希函数,它使用任意长度的字符串,并将其减少为唯一的固定长度字符串。它用于密码有效性、消息和数据完整性以及许多其他加密系统。

11)解释如何查找链表是否有循环?

要知道链表是否有循环,我们将采用两个指针的方法。如果保留两个指针,并且在处理两个节点之后增加一个指针,并且在处理每个节点之后,遇到指针指向同一个节点的情况,这只有在链表有循环时才会发生。

12)解释加密算法的工作原理?

加密是将明文转换为称为“密文”的密码格式的过程。要转换文本,算法使用一系列被称为“键”的位来进行计算。密钥越大,创建密文的潜在模式数越多。大多数加密算法使用长度约为64到128位的固定输入块,而有些则使用流方法。

13)列出一些常用的加密算法?

一些常用的加密算法是:

14)解释一个算法的最佳情况和最坏情况之间有什么区别?

15)解释什么是基数排序算法?

基数排序又称“桶子法”,是通过比较数字将其分配到不同的“桶里”来排序元素的。它是线性排序算法之一。

16)解释什么是递归算法?

递归算法是一个解决复杂问题的方法,将问题分解成较小的子问题,直到分解的足够小,可以轻松解决问题为止。通常,它涉及一个调用自身的函数。

17)提到递归算法的三个定律是什么?

所有递归算法必须遵循三个规律

18)解释什么是冒泡排序算法?

冒泡排序算法也称为下沉排序。在这种类型的排序中,要排序的列表的相邻元素之间互相比较。如果它们按顺序排列错误,将交换值并以正确的顺序排列,直到最终结果“浮”出水面。

—List有序,可重复

—Set无序,唯一

针对Collection集合我们到底使用谁呢(掌握)

唯一吗

是:Set

排序吗

是:TreeSet或LinkedHashSet否:HashSet如果你知道是Set,但是不知道是哪个Set,就用HashSet。

否:List

要安全吗

是:Vector否:ArrayList或者LinkedList

查询多:ArrayList增删多:LinkedList如果你知道是List,但是不知道是哪个List,就用ArrayList。

如果你知道是Collection集合,但是不知道使用谁,就用ArrayList。如果你知道用集合,就用ArrayList。

说完了Collection,来简单说一下Map.

上图:

Map接口有三个比较重要的实现类,分别是HashMap、TreeMap和HashTable。

这就意味着:

1.介绍

2.相同点

3.不同点

自然排序要进行一下操作:1.Student类中实现Comparable接口2.重写Comparable接口中的Compareto方法

比较器排序步骤:1.单独创建一个比较类,这里以MyComparator为例,并且要让其继承Comparator接口2.重写Comparator接口中的Compare方法

3.在主类中使用下面的构造方法

安全(三次交互,以保证连接的可靠),不会发生数据丢失,但是效率没有UDP高

当对网络通讯质量有要求的时候,比如:整个数据要准确无误的传递给对方,这往往用于一些要求可靠的应用,比如HTTP、HTTPS、FTP等传输文件的协议,POP、SMTP等邮件传输的协议。

UDP是无连接通信协议,即在数据传输时,数据的发送端和接收端不建立逻辑连接。

当对网络通讯质量要求不高的时候,要求网络通讯速度能尽量的快,这时就可以使用UDP。

HTTP协议的特点

1、如果元素个数是固定的推荐用数组,效率高。

2、如果元素个数不是固定的推荐用集合

部分集合内部是用数组实现(当然还有是哈希,二叉树等),当每次创建一个集合对象是,默认创建10个长度的数组。当添加到第11个时,它会按照原数组1.5倍创建,在把原来的数组值复制过去,原来10长度的数组将变成垃圾被回收。当添加到16或者更多,以后都是按照原数组1.5倍创建,复制这个过程。所以,当固定长度为100的时候,你选择了集合就是低效率,选择数组就是高效率,因为集合里面有很多创建,复制,销毁过程。

java的数据类型分两大类:

方法的参数为基本数据类型时,传递的是数据值。

基本类型是通过诸如inta=5;longb=6L;的形式来定义的,称为自动变量,自动变量存放的是字面值,不是类的实例,它存放在内存的堆栈中,数据大小和生存期必须是确定的,存取速度比较快,在堆栈中的字面值可以共享,也就是说我们定义一个inta=5;然后又定义了一个intb=5;这时a与b在内存中指向的是同一个字面常量。

四类八种

四类:整形、浮点型、布尔型、字符型

八种:

8种基本类型在java中都有对应的封装类型,也就是引用类型:整数类型Byte、Short、Integer(-128~127)、Long浮点数类型Float、Double字符型Character布尔类型Boolean

在参数传递时,基本类型都是传值,也就是传递的都是原变量的值得拷贝,改变这个值不会改变原变量

方法的参数为引用数据类型时传递的是地址值。

除了基本数据就是引用数据类型

基本数据类型加上[]就变成引用数据类型

类、接口、数组

引用类型一般是通过new关键字来创建,比如Integernum=newInteger(3);它存放在内存的堆中,可以在运行时动态的分配内存大小,生存期也不必事先告诉编译器,当引用类型变量不被使用时,Java内部的垃圾回收器GC会自动回收走。引用变量中存放的不是变量的内容,而是存放变量内容的地址。

引用类型传递的是地址,也就是参数与原变量指向的是同一个地址,所以如果改变参数的值,原变量的值也会改变

DK1.5(以后的版本)的新特性自动装箱和拆箱

\1.自动装箱:把基本类型转换为包装类类型inta=10;Integeri=newInteger(a);

Integervalue=10;为什么基本类型就能直接转化为Integer,Integer不应该是new出来的吗内部会自动的newInteger(10)自动装箱

\2.自动拆箱:把包装类型转换为基本类型Integervalue2=newInteger(120);inta=value2;对象赋值给基本数据类型,为什么会不报错,因为内部会调用value2.intValue()这种就是自动拆箱

举一反三

Doubled1=9.14//内部会自动new一个Double然后传递进去

new出来的东西每个都会分配一个内存地址

拆箱:devaning装箱:

装箱拆箱面试题:考点(Integer内部装箱的内部实现)

看程序写结果1.

Integervalue1=newInteger(97);Integervalue2=newInteger(97);System.out.println(value1==value2);System.out.println(value.equals(value2));//这个就是比较值System.out.println("-------------------");

答案:falsetrue

\2.自动装箱,如果值一样、地址也一样

Integervalue3=127;//自动装箱Integervalue4=127;System.out.println(value3==value4);System.out.println(value3.equals(value4));//这个也是比较值

答案:truetrue

`3.

Integervalue5=128;Integervalue6=128;System.out.println(value5==value6);//falseSystem.out.println(value5.equals(value6));//true

答案:falsetrue

总结:自动装箱,范围在-128~127【256个数字】的地址是一样的,其他范围地址不一样-128到127之间的有个自动装箱的缓存池如果不在这个范围,就会使用new新创建

栈:stack存储局部变量,方法在栈中开辟一块栈并运行

堆:heap存储对象及其成员变量,以及成员方法的内存地址,所有new的都在堆中

1、Spring在ssm中起什么作用?

Spring:轻量级框架

作用:Bean工厂,用来管理Bean的生命周期和框架集成。

两大核心:

①.IOC/DI(控制反转/依赖注入):把dao依赖注入到service层,service层反转给action层,Spring顶层容器为BeanFactory。

②.AOP:面向切面编程

2、Spring的事务?

编程式事务管理:编程方式管理事务,极大灵活性,难维护。

3、IOC在项目中的作用?

作用:Ioc解决对象之间的依赖问题,把所有Bean的依赖关系通过配置文件或注解关联起来,降低了耦合度。

4、Spring的配置文件中的内容?

开启事务注解驱动

事务管理器

开启注解功能,并配置扫描包

配置数据库

配置SQL会话工厂,别名,映射文件

不用编写Dao层的实现类

5、Spring下的注解?

注册:@Controller@Service@Component

注入:@Autowired@Resource

请求地址:@RequestMapping

返回具体数据类型而非跳转:@ResponseBody

6、SpringDI的三种方式

构造器注入:通过构造方法初始化

1setter方法注入:通过setter方法初始化

1接口注入

7、Spring主要使用了什么模式?

工厂模式:每个Bean的创建通过方法

单例模式:默认的每个Bean的作用域都是单例

代理模式:关于Aop的实现通过代理模式

8、IOC,AOP的实现原理?

IOC:通过反射机制生成对象注入

AOP:动态代理

二、SpringMvc面试题

1、SpringMvc的控制器是不是单例模式,如果是,有什么问题,怎么解决?

问题:单例模式,在多线程访问时有线程安全问题

解决方法:不要用同步,在控制器里面不能写字段

2、SpringMvc中控制器的注解?

@Controller:该注解表明该类扮演控制器的角色

3、@RequestMapping注解用在类上的作用?

作用:用来映射一个URL到一个类或者一个特定的处理方法上

4、前台多个参数,这些参数都是一个对象,快速得到对象?

5、SpringMvc中函数的返回值?

String,ModelAndView,List,Set等

一般String,Ajax请求,返回一个List集合

6、SpringMvc中的转发和重定向

转发:return:“hello”

重定向:return:“redirect:hello.jsp”

通过JackSon框架把java里面对象直接转换成js可识别的json对象,具体步骤如下:

加入JackSon.jar

在配置文件中配置json的映射

在接受Ajax方法里面直接返回Object,list等,方法前面需要加上注解@ResponseBody

8、SpringMvc的工作流程图

9、Struts2和SpringMvc的区别

入口不同:

Struts2:filter过滤器

SpringMvc:一个Servlet即前端控制器

开发方式不同:

Struts2:基于类开发,传递参数通过类的属性,只能设置为多例

SpringMvc:基于方法开发(一个url对应一个方法),请求参数传递到方法形参,可以为单例也可以为多例(建议单例)

请求方式不同:

Struts2:值栈村塾请求和响应的数据,通过OGNL存取数据

SpringMvc:通过参数解析器将request请求内容解析,给方法形参赋值,将数据和视图封装成ModelAndView对象,最后又将ModelAndView中的模型数据通过request域传输到页面,jsp视图解析器默认使用的是jstl。

三、Mybatis面试题

1、Ibatis和Mybatis?

Ibatis:2010年,apache的Ibatis框架停止更新,并移交给了google团队,同时更名为MyBatis。从2010年后Ibatis在没更新过,彻底变成了一个孤儿框架。一个没人维护的框架注定被mybatis拍在沙滩上。

Mybatis:Ibatis的升级版本。

2、什么是Mybatis的接口绑定,有什么好处?

Mybatis实现了DAO接口与xml映射文件的绑定,自动为我们生成接口的具体实现,使用起来变得更加省事和方便。

3、什么情况用注解,什么情况用xml绑定?

注解使用情况:Sql语句简单时

xml绑定使用情况:xml绑定(@RequestMap用来绑定xml文件)

4、Mybatis在核心处理类叫什么

SqlSession

5、查询表名和返回实体Bean对象不一致,如何处理?

映射键值对即可

1column:数据库中表的列名

property:实体Bean中的属性名

6、Mybatis的好处?

把Sql语句从Java中独立出来。

封装了底层的JDBC,API的调用,并且能够将结果集自动转换成JavaBean对象,简化了Java数据库编程的重复工作。

自己编写Sql语句,更加的灵活。

入参无需用对象封装(或者map封装),使用@Param注解

7、Mybatis配置一对多?

1property:属性名

column:共同列

ofType:集合中元素的类型

select:要连接的查询

8、Mybatis配置一对一?

1property:属性名

javaType:集合中元素的类型

9、${}和#{}的区别?

:简单字符串替换,把{}:简单字符串替换,把:简单字符串替换,把{}直接替换成变量的值,不做任何转换,这种是取值以后再去编译SQL语句。

#{}:预编译处理,sql中的#{}替换成?,补全预编译语句,有效的防止Sql语句注入,这种取值是编译好SQL语句再取值。

总结:一般用#{}来进行列的代替

10、获取上一次自动生成的主键值?

selectlast_insert_id()111、Mybatis如何分页,分页原理?

RowBounds对象分页

在Sql内直接书写,带有物理分页

12、Mybatis工作原理?

原理:

通过SqlSessionFactoryBuilder从mybatis-config.xml配置文件中构建出SqlSessionFactory。

SqlSessionFactory开启一个SqlSession,通过SqlSession实例获得Mapper对象并且运行Mapper映射的Sql语句。

完成数据库的CRUD操作和事务提交,关闭SqlSession。

Spring的IOC和AOP应用,将组件的耦合度降至最低,即解耦,便于系统的维护和升级;

可以与第三方框架和技术整合应用,可以自由选择技术进行开发。

轻量:Spring是轻量的,基本的版本大约2MB

控制反转IOC:Spring通过控制反转实现了松散耦合,为对象提供所需的依赖,而不需要对象去创建或查找依赖

面向切面的编程(AOP):Spring支持面向切面的编程,并且把应用业务逻辑和系统服务分开

容器:Spring包含并管理应用中对象的生命周期和配置

MVC****框架:Spring的WEB框架是个精心设计的框架,是Web框架的一个很好的替代品

Core:核心模块提供了框架的基本组成部分,包括IoC(控制反转)和DI(依赖注入)功能。

Bean:(注入bean)工厂是工厂模式的一个实现,提供了控制反转功能,用来把应用的配置和依赖从真正的应用代码中分离。最常用的BeanFactory实现是XmlBeanFactory类。

XMLBeanFactory,它根据XML文件中的定义加载beans。该容器从XML文件读取配置元数据并用它去创建一个完全配置的系统或应用。

Context:上下文模块建立在由核心和Bean模块提供的坚实基础上,它是访问定义和配置的任何对象的媒介。ApplicationContext接口是上下文模块的重点。

SpEl:表达式语言模块在运行时提供了查询和操作一个对象图的强大的表达式语言。

OXM:模块提供了抽象层,它支持对JAXB,Castor,XMLBeans,JiBX和XStream的对象/XML映射实现。

JMS:Java消息服务JMS模块包含生产和消费的信息的功能。

Web:模块提供了基本的面向web的集成功能,例如多个文件上传的功能和使用servlet监听器和面向web应用程序的上下文来初始化IoC容器。

Web-MVC:模块包含Spring的model模型-view视图-controller控制器(MVC),实现了web应用程序。

Web-Socket:模块为WebSocket-based提供了支持,而且在web应用程序中提供了客户端和服务器端之间通信的两种方式。

Web-Portlet:模块提供了在portlet环境中实现MVC,并且反映了Web-Servlet模块的功能。

Aspects:模块提供了与AspectJ的集成,这是一个功能强大且成熟的面向切面编程(AOP)框架。

Instrumentation:模块在一定的应用服务器中提供了类instrumentation的支持和类加载器的实现。

Messaging:模块为STOMP提供了支持作为在应用程序中WebSocket子协议的使用。它也支持一个注解编程模型,它是为了选路和处理来自WebSocket客户端的STOMP信息。

test:测试模块支持对具有JUnit或TestNG框架的Spring组件的测试。

Ioc—InversionofControl,即“控制反转”,不是什么技术,而是一种设计思想。

原始:当某个角色需要另外一个角色协助的时候,在传统的程序设计过程中,通常由调用者来创建被调用者的实例。例如:service需要调用dao,就需要在service中创建一个dao对象

Spring:在spring中创建被调用者的工作不再由调用者来完成,创建被调用者的工作由spring容器来完成,然后用注解注入调用者,因此称为控制反转。

核心原理:配置文件(Bean)+反射(工厂)+容器(map)

作用:它能指导我们如何设计出松耦合、更优良的程序。在传统应用中,程序都是由我们在类内部主动创建依赖对象,从而导致类与类之间高耦合,难于测试;现在通过IoC容器,把创建和查找依赖对象的控制权交给了容器,由容器进行注入组合对象,所以对象与对象之间是松散耦合,这样也方便测试,利于功能复用,更重要的是使得程序的整个体系结构变得非常灵活。

总之,IoC很好的体现了面向对象设计法则之一——好莱坞法则:“别找我们,我们找你”;即由IoC容器帮对象找相应的依赖对象并注入,而不是由对象主动去找。其设计方式是从顶层开始逐步添加细节,而不是以前传统的底层细节决定顶层。

DI—DependencyInjection,即“依赖注入”:是组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中。

作用:依赖注入的目的并非为软件系统带来更多功能,而是为了提升组件重用的频率。其重点是完成自身的业务逻辑,而不需要关心具体的资源来自何处,由谁实现。

方式:在pom问题添加依赖

AOP—AspectOrientedProgramming,即“面向切面编程”,不是什么技术,而是一种设计思想。在程序开发中主要用来解决一些系统层面上的问题,比如日志,事务,权限等。利用AOP可以将那些与业务无关,却被业务模块共同调用的逻辑提取并封装起来,减少了系统中的重复代码,降低了模块间的耦合度,同时提高了系统的可维护性。

Aspect切面:通常是一个类,里面可以定义切入点和通知

JointPoint连接点:程序执行过程中明确的点,一般是方法的调用

Advice通知:AOP在特定的切入点上执行的增强处理,有before前置,after后置,afterReturning,afterThrowing,around环绕

Pointcut切入点:就是带有通知的连接点,在程序中主要体现为书写切入点表达式

AOP****代理:AOP框架创建的对象,代理就是目标对象的加强。Spring中的AOP代理可以使用JDK动态代理,也可以是CGLIB代理,前者基于接口(InvocationHandler),后者基于子类。

织入是将切面和到其他应用类型或对象连接或创建一个被通知对象的过程。

织入可以在编译时,加载时,或运行时完成。

编译时织入:需要特殊的java编译器,如Aspectj

类加载时织入:需要特殊的java编译器,如Aspectj和AspectWerkz

运行时织入:Spring采用的方式,通过动态代理的方式实现。

Spring中的AOP代理还是离不开Spring的IOC容器,代理的生成,管理及其依赖关系都是由IOC容器负责,Spring默认使用JDK动态代理,在需要代理类而不是代理接口的时候,Spring会自动切换为使用CGLIB代理,不过现在的项目都是面向接口编程,所以JDK动态代理相对来说用的还是多一些。

基于XML的实现

在这种情况下,切面由常规类以及基于XML的配置实现。

基于注解的切面实现

Springbeans是那些形成Spring应用的主干的java对象。它们被SpringIOC容器初始化,装配,和管理。Spring容器理解成一个大型工厂,Bean就是该工厂的产品,工厂(Spirng容器)里能生产出来什么样的产品(Bean),完全取决于我们在配置文件中的配置。

在说明前可以思考一下Servlet的生命周期:实例化,初始init,接收请求service,销毁destroy;

1、实例化一个Bean--也就是我们常说的new;

2、按照Spring上下文对实例化的Bean进行配置--也就是IOC注入;

实现一系列Aware接口,主要描述Bean和容器直接关系。

3、如果这个Bean已经实现了BeanNameAware接口,会调用它实现的setBeanName(String)方法,此处传递的就是Spring配置文件中Bean的id值

4、如果这个Bean已经实现了BeanFactoryAware接口,会调用它实现的setBeanFactory(setBeanFactory(BeanFactory)传递的是Spring工厂自身(可以用这个方式来获取其它Bean,只需在Spring配置文件中配置一个普通的Bean就可以);

5、如果这个Bean已经实现了ApplicationContextAware接口,会调用setApplicationContext(ApplicationContext)方法,传入Spring上下文(同样这个方式也可以实现步骤4的内容,但比4更好,因为ApplicationContext是BeanFactory的子接口,有更多的实现方法);

6、如果这个Bean关联了BeanPostProcessor接口,将会调用postProcessBeforeInitialization(Objectobj,Strings)方法,BeanPostProcessor经常被用作是Bean内容的更改,并且由于这个是在Bean初始化结束时调用那个的方法,也可以被应用于内存或缓存技术;

7、如果Bean在Spring配置文件中配置了init-method属性会自动调用其配置的初始化方法。

8、如果这个Bean关联了BeanPostProcessor接口,将会调用postProcessAfterInitialization(Objectobj,Strings)方法、;

注:以上工作完成以后就可以应用这个Bean了,那这个Bean是一个Singleton的,所以一般情况下我们调用同一个id的Bean会是在内容地址相同的实例,当然在Spring配置文件中也可以配置非Singleton****,这里我们不做赘述。

9、当Bean不再需要时,会经过清理阶段,如果Bean实现了DisposableBean这个接口,会调用那个其实现的destroy()方法;

10、最后,如果这个Bean的Spring配置中配置了destroy-method属性,会自动调用其配置的销毁方法。

以上10步骤可以作为面试或者笔试的模板,另外我们这里描述的是应用Spring上下文Bean的生命周期,如果应用Spring的工厂也就是BeanFactory的话去掉第5步就Ok了。

singleton:

bean在每个Springioc容器中只有一个实例。缺省的Springbean的作用域是Singleton。默认的(估计是为了体现轻量级和性能,而且一般情况下单例足够了,若需要其他方式可自行更改)

prototype:

一个bean的定义可以有多个实例。

request:

session:

在一个HTTPSession中,一个bean定义对应一个实例。该作用域仅在基于web的SpringApplicationContext情形下有效。

global-session:

在一个全局的HTTPSession中,一个bean定义对应一个实例。该作用域仅在基于web的SpringApplicationContext情形下有效。

在Spring容器中把bean组装到一起,前提是容器需要知道bean的依赖关系,如何通过依赖注入来把它们装配到一起。因此体现配置好bean的依赖关系,Spring容器能够自动装配相互合作的bean,能通过Bean工厂自动处理bean之间的协作。

装配方式

no:默认的方式是不进行自动装配,通过显式设置ref属性来进行装配。

byName:通过参数名自动装配,Spring容器在配置文件中发现bean的autowire属性被设置成byname,之后容器试图匹配、装配和该bean的属性具有相同名字的bean。

byType:通过参数类型自动装配,Spring容器在配置文件中发现bean的autowire属性被设置成byType,之后容器试图匹配、装配和该bean的属性具有相同类型的bean。如果有多个bean符合条件,则抛出错误。

constructor:这个方式类似于byType,但是要提供给构造器参数,如果没有确定的带参数的构造器参数类型,将会抛出异常。

autodetect:首先尝试使用constructor来自动装配,如果无法工作,则使用byType方式。

局限性

重写:你仍需用和配置来定义依赖,意味着总要重写自动装配。

基本数据类型:你不能自动装配简单的属性,如基本数据类型,String字符串,和类。

模糊特性:自动装配不如显式装配精确,如果有可能,建议使用显式装配。

实例化Bean的三种方式:

构造器方法创建Bean:调用构造方法创建Bean是最常用的一种情况Spring容器通过new关键字调用构造器来创建Bean实例,注:无参使用的是property标签,有参使用的是constructor-arg标签。

静态工厂方法创建Bean:需要额外提供工厂类,工厂方法是静态方法

实例工厂方法创建Bean:创建实例化工厂类,先注册工程然后引用工厂

依赖配置方式

构造方法方式注入:构造器依赖注入通过容器触发一个类的构造器来实现的,该类有一系列参数,每个参数代表一个对其他类的依赖。构造器参数实现强制依赖

set****方法方式的注入:Setter方法注入是容器通过调用无参构造器或无参static工厂方法实例化bean之后,调用该bean的setter方法,即实现了基于setter的依赖注入。setter****方法实现可选依赖。

名称空间注入:Spring2.5以后提供了一种p名称空间和C名称空间的属性注入.通过直接写属性名的方式注入属性值。

SpEL****的属性注入:使用spring提供的一种表达式方式去赋值,最大的好处在于,它能像EL表达式一般,在里面进行运算、逻辑判断,还可以调用其它Bean的属性和方法给当前属性赋值。

相同点

BeanFactory和ApplicationContext,其中ApplicationContext是BeanFactory的子接口。他们都可代表Spring容器,Spring容器是生成Bean实例的工厂,并且管理容器中的Bean。

不同点

BeanFactory核心库是在bean中,实现类是XMLBeanFactory,它根据XML文件中的定义加载beans。该容器从XML文件读取配置元数据并用它去创建一个完全配置的系统或应用。

ApplicationContext核心库是context中,ApplicationContext通常的实现方式:

FileSystemXmlApplicationContext:此容器从一个XML文件中加载beans的定义,XMLBean配置文件的全路径名必须提供给它的构造函数。

ClassPathXmlApplicationContext:此容器也从一个XML文件中加载beans的定义,这里,你需要正确设置classpath因为这个容器将在classpath里找bean配置。

WebXmlApplicationContext:此容器加载一个XML文件,此文件定义了一个WEB应用的所有bean。

初始化区别

BeanFactory在初始化容器时,并未实例化Bean,直到第一次访问某个Bean时才实例目标Bean;

JavaBean****:JavaBean是遵循了Sun制定的JavaBean标准的类。其三个特性是:包含默认(无参数)的构造函数,允许通过访问器(getter和setter方法)来访问类的成员属性,实现java.io.Serializable接口。

POJO****:POJO是PlainOldJavaObject(简单的Java对象)的缩写。是一种花式的对普通Java对象的称呼。这个词主要用来区分简单、轻量的Java对象和“重量级“的类

Springbean****:Springbean表示受到Spring管理的对象。具体说来,它是被Spring框架容器初始化、配置和管理的对象。

编程式事务管理:这意味你通过编程的方式管理事务,给你带来极大的灵活性,但是难维护。

propagation_required需要如果存在一个事务,则指出当前事情。如果没有事务则开启事务

propagation_supports支持如果存在一个事务,支持当前事务,如果没有事务,则非事务的执行

propagation_mandatory必要的如果已经存在一个事务,支持当前事务。如果没有一个活动的事务,则抛出异常

propagation_requires_new总是开启一个新的事务,如果一个事务已经存在,则将这个存在的事务挂起

propagation_not_supported总是非事务的执行,并挂起任何存在的事务

propagation_never总是非事务的执行,如果存在一个活动事务,则抛出异常

propagation_nested如果一个活动的事务存在,则运行在一个嵌套的事务中,如果没有就开启事务

开发者通过在相应的类,方法或属性上使用注解的方式,直接组件类中进行配置,而不是使用xml表述bean的装配关系。

开启配置

注解装配在默认情况下是不开启的,为了使用注解装配,我们必须在Spring配置文件中配置元素。

@Component****()

将该类注入到Spring容器中。

value:指定bean的id。如果不指定value属性,默认bean的id是当前类的类名。首字母小写。

衍生注解:@Controller修饰WEB层类;@Service修饰业务层类;@Repository修饰DAO层类。

@scope

注解用来描述类的作用范围的,默认值singleton;多例的使用prototype。

@PostConstrut

设置spring框架初始化此类实例时调用的初始化方法,标注在此类的初始化方法上。

@PreDestroy

用来设置spring框架销毁此类实例时调用的销毁方法,标注在此类的销毁方法上。

@Value

注入基本数据类型和String类型数据的。

@Autowired

自动按照类型注入,找到变量名一致的id对象给注入进去。

@Qualifier(value="accountDao02")

value:指定bean的id。再按照Bean的id注入,必须和@Autowired一起使用。

@Resource(name="accountDao02")

指定找具体的某一个实现,name参数设置要找到类的名称。

@Configuration

用于指定当前类是一个spring配置类,当创建容器时会从该类上加载注解

@ComponentScan("com.itheima")

用于指定spring在初始化容器时要扫描的包。

@Bean

该注解只能写在方法上,表明使用此方法创建一个对象,并且放入spring容器。

name:给当前@Bean注解方法创建的对象指定一个名称(即bean的id)。

@Import

用于导入其他配置类

@PropertySource

用于加载.properties文件中的配置。@Value结合该注解可以直接给类的属性初始化

@RunWith(SpringJUnit4ClassRunner.class)

指明运行的测试环境

@ContextConfiguration("classpath:applicationContext.xml")

指明spring框架的加载的配置文件有applicationContext.xml

@ContextConfiguration(classes=SpringConfiguration.class)

指明spring框架的加载的配置类纯注解

aop:aspectj-autoproxy/开启AOP注解

@Aspect在切面类中定义切入点方法

@Before前置通知

@AfterReturning后置通知

@Around环绕通知

@AfterThrowing异常抛出通知

@After最终通知

在spring的配置文件中设置bean默认为单例模式。

jdk的java.lang.reflect.Proxy类代理:若目标对象实现了若干接口,IOC,AOP

spring使用CGLIB库生成目标类的子类:若目标兑现没有实现任何接口

用来解决代码重复的问题:RestTemplate、JmsTemplate、JpaTemplate

spring提供了前端控制器DispatherServlet来对请求进行分发

贯穿于BeanFactory/ApplacationContext接口的核心理念。

我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用同一个接口来指向新创建的对象。Spring中使用beanFactory来创建对象的实例。

SpringMVC是一种基于Java实现的MVC设计模型的请求驱动类型的轻量级WEB框架。主要作用是处理所有的HTTP请求和响应。

1、清晰的角色划分

2、分工明确,而且扩展点相当灵活

3、命令对象就是一个POJO,无需继承框架特定API,可以使用命令对象直接作为业务对象

4、和Spring其他框架无缝集成,是其它Web框架所不具备的

5、可适配,通过HandlerAdapter可以支持任意的类作为处理器

6、可定制性,HandlerMapping、ViewResolver等能够非常简单的定制

7、功能强大的数据验证、格式化、绑定机制

8、强大的JSP标签库,使JSP编写更容易

9、RESTful风格的支持、简单的文件上传、约定大于配置的契约式编程支持、基于注解的零配置支持

1、客户端发送request请求,服务器前端控制器DispatcherServlet接受请求响应;DisoatcherServlet对请求URL进行解析,得到请求资源标识符URI;将请求转发给HandlerMapping;

2、HandlerMapping处理映射器接受后,将请求映射到对应的处理器来处理请求,将映射结果返回DispatcherServlet

3、DispatcherServlet再请求HandlerAdapter处理器适配器,由适配器制定具体的Handler的去执行

4、Handler处理器是编写的具体业务控制器,在Spring当中如果写一些处理器组件,一般实现Controller接口;

5、Handler处理器处理的结果返回ModelAndView给适配器,适配器再返回给前端控制器

6、前端控制器接收ModelAndView后请求视图解析器ViewResolver据逻辑视图名解析成物理视图名即具体的页面地址;再生成View视图对象,最后对View进行渲染将处理结果通过页面展示给用户。

DispatcherServlet****前端控制器

整个流程控制的中心,它就相当于mvc模式中的c,由它调用其它组件处理用户的请求,dispatcherServlet的存在降低了组件之间的耦合性。

HandlerMapping****处理器映射器

负责根据用户请求找到Handler即处理器;SpringMVC提供了不同的映射器实现不同的映射方式;例如:配置文件方式,实现接口方式,注解方式等。

HandlAdapter****处理器适配器

通过HandlerAdapter对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行。

Handler****处理器

开发中要编写的具体业务控制器。由DispatcherServlet把用户请求转发到Handler,对具体的用户请求进行处理。

ViewResolver****视图解析器

首先根据逻辑视图名解析成物理视图名即具体的页面地址,再生成View视图对象。

View****视图

View视图过页面标签或页面模版技术将模型数据通过页面展示给用户。

String类型:普通的字符串类型;

POJO类型:要求表单中参数名称和POJO类的属性名称保持一致。并且控制器方法的参数类型是POJO类型,表单中name属性必须和pojo的对象属性名一致;

POJO类中List

POJO类中Map

字符串:经过视图解析器解析为jsp物理路径:/WEB-INF/pages/success.jsp;

Void:RequestMapping的取值作为视图名称;

ModelAndView:ModelAndView是SpringMVC为我们提供的一个对象,该对象也可以用作控制器方法的返回值。

forward转发:如果用了formward:则路径必须写成实际视图url,不能写逻辑视图。

Redirect重定向:返回一个新的url并跳转。

响应json:使用@ResponseBody注解实现将controller方法返回对象转换为json响应给客户端

作用在方法和类上

path:指定请求路径的url

method:指定该方法的请求方式

Params:指定限制请求参数的条件

Headers:发送的请求中必须包含的请求头

用于获取请求体内容。直接使用得到是key=value&key=value...结构的数据

用于绑定url中的占位符。

url支持占位符是spring3.0之后加入的。是springmvc支持restful风格URL的一个重要标志。

@RequestHeader

用于获取请求消息头,value:提供消息头名称

@CookieValue

用于把指定cookie名称的值传入控制器方法参数,value:指定cookie的名称。

@SessionAttributes

用于多次执行(多次请求)控制器方法间的参数共享。(该注解定义在类上)

@ModelAttribute

出现在方法上,表示当前方法会在控制器的方法执行之前,出现在参数上,获取指定的数据给参数赋值。

核心控制器(前端控制器、预处理控制器)

核心控制器的主要用途是处理所有的请求,然后对那些有特殊请求统一进行处理(字符编码、文件上传、参数接受、异常处理)

SpringMVC核心控制器是Servlet,SrpingMVC控制器基于方法级别的拦截,处理器设计为单实例。

struct2是Filter,Struts2控制器基于类级别的拦截,处理器设计为多实例,而且只能设计为多实例模式,消耗更多的服务器内存。

控制器实例

SpringMVC会比Struts快一些,SpringMVC是基于方法设计

Sturts是基于对象,每一次请求都有有个action实例,每次请求执行对应的方法即可。

管理方式

大部分的公司的核心框架结构中,就会使用到spring,而springmvc又是spring中一个模块,所以spring对于springmvc的控制器管理更加方便,而且提供了全注解方式进行管理,各种功能的注解会比较全面,使用简单。

而使用struct2需要采用xml很多配置参数来管理,方式会比较麻烦。

参数传递

springmvc是通过方法和参数进行接收。

Struts2中自身提供多种参数接收,其实都是通过valuestack进行传递和赋值的。

学习难度

springmvc学习成本比较简单,容易上手。

Struts增加了很多新的技术点,比如拦击器,值栈及OGNL表达式,学习成本较高。

intercept实现机制

springmvc是使用的独立的AOP方式,是方法级别的拦截。

Struts是有自己的interceptor机制,配置文件量比较大。

数据返回

springmvc处理ajax请求,直接返回数据,方法中使用注解@ResponseBody或者@RestController,自动帮转换为json对象。

拦截器

相对于spring方式开发中需要进行大量的配置文件,而且还需要考虑jar包之间的冲突,因此,人们基于约定优于配置的思想,将一些约定俗称的的配置信息添加进行配置好,在开发过程只需要进行简单的起步就完成项目基本框架搭建。SpringBoot不是对Spring功能上的增强,而是提供了一种快速使用Spring的方式。

起步依赖

起步依赖本质上是一个Maven项目对象模型(ProjectObjectModel,POM),定义了对其他库的传递依赖,这些东西加在一起即支持某项功能。

简单的说,起步依赖就是将具备某种功能的坐标打包到一起,并提供一些默认的功能。

自动配置

SpringBoot的自动配置是一个运行时(更准确地说,是应用程序启动时【main方法】)的过程,考虑了众多因素,才决定Spring配置应该用哪个,不该用哪个。该过程是Spring自动完成的。

下面有三个重要的注解:

它就是JavaConfig形式的SpringIoc容器的配置类使用的那个@Configuration,SpringBoot社区推荐使用基于JavaConfig的配置形式,所以,这里的启动类标注了@Configuration之后,本身其实也是一个IoC容器的配置类。

@ComponentScan的功能其实就是自动扫描并加载符合条件的组件(比如@Component和@Controller,@Service,@Repository等)或者bean定义,最终将这些bean定义加载到IoC容器中。

@EnableAutoConfiguration也是借助@Import的帮助,将所有符合自动配置条件的bean定义加载到IoC容器,仅此而已!会扫描到spring.factories文件,加载application.properties或者application.yal(yaml)文件

它其实是注册了一个Bean的定义。

newPackageImport(metadata).getPackageName(),它其实返回了当前主程序类的同级以及子级的包组件。

Import(AutoConfigurationImportSelector.class)注解:

AutoConfigurationImportSelector继承了DeferredImportSelector继承了ImportSelector

ImportSelector有一个方法为:selectImports。

可以看到第九行,它其实是去加载publicstaticfinalStringFACTORIES_RESOURCE_LOCATION="META-INF/spring.factories";外部文件。这个外部文件,有很多自动配置的类。如下:

@Import(EnableAutoConfigurationImportSelector.class),借助EnableAutoConfigurationImportSelector,@EnableAutoConfiguration可以帮助SpringBoot应用将所有符合条件的@Configuration配置都加载到当前SpringBoot创建并使用的IoC容器。就像一只“八爪鱼”一样。

借助于Spring框架原有的一个工具类:SpringFactoriesLoader的支持,@EnableAutoConfiguration可以智能的自动配置功效才得以大功告成!

SpringFactoriesLoader属于Spring框架私有的一种扩展方案,其主要功能就是从指定的配置文件META-INF/spring.factories加载配置。@EnableAutoConfiguration借助于Spring框架原有的一个工具类:SpringFactoriesLoader的支持,才可以智能的自动配置功效才得以大功告成!

配合@EnableAutoConfiguration使用的话,它更多是提供一种配置查找的功能支持,即根据@EnableAutoConfiguration的完整类名org.springframework.boot.autoconfigure.EnableAutoConfiguration作为查找的Key,获取对应的一组@Configuration类。

@EnableAutoConfiguration自动配置的魔法骑士就变成了:从classpath中搜寻所有的META-INF/spring.factories配置文件,并将其中org.springframework.boot.autoconfigure.EnableutoConfiguration对应的配置项通过反射(JavaRefletion)实例化为对应的标注了@Configuration的JavaConfig形式的IoC容器配置类,然后汇总为一个并加载到IoC****容器。

第一步:使用的是SpringApplication的静态run方法创建一个SpringApplication对象实例。

个创建好的SpringApplication的实例方法。在SpringApplication实例初始化的时候,它会提前做几件事情:

根据classpath里面是否存在某个特征类

(org.springframework.web.context.ConfigurableWebApplicationContext)来决定是否应该

一个为Web应用使用的ApplicationContext类型。

使用SpringFactoriesLoader在应用的classpath中查找并加载所有可用的ApplicationContextInitializer。

使用SpringFactoriesLoader在应用的classpath中查找并加载所有可用的ApplicationListener。

推断并设置main方法的定义类。

第二步:遍历执行所有通过SpringFactoriesLoader可以查找到并加载的SpringApplicationRunListener。调用它们的started()方法,告诉这些SpringApplicationRunListener,“嘿,SpringBoot应用要开始执行咯!

第三步:创建并配置当前SpringBoot应用将要使用的Environment(包括配置要使用的PropertySource以及Profile)。

遍历调用所有SpringApplicationRunListener的environmentPrepared()的方法,告诉他们:“当前SpringBoot应用使用的Environment准备好了咯!”。

第四步:如果SpringApplication的showBanner属性被设置为true,则打印banner。

第五步:根据用户是否明确设置了applicationContextClass类型以及初始化阶段的推断结果,决定该为当前SpringBoot应用创建什么类型的ApplicationContext并创建完成,然后根据条件决定是否添加ShutdownHook,决定是否使用自定义的BeanNameGenerator,决定是否使用自定义的ResourceLoader,当然,最重要的,将之前准备好的Environment设置给创建好的ApplicationContext使用。

第六步:ApplicationContext创建好之后,SpringApplication会再次借助Spring-FactoriesLoader,查找并加载classpath中所有可用的ApplicationContext-Initializer,然后遍历调用这些ApplicationContextInitializer的initialize(applicationContext)方法来对已经创建好的ApplicationContext进行进一步的处理。

第七步:遍历调用所有SpringApplicationRunListener的contextPrepared()方法和contextLoaded()方法。将之前通过@EnableAutoConfiguration获取的所有配置以及其他形式的IoC容器配置加载到已经准备完毕的ApplicationContext。

第八步:调用ApplicationContext的refresh()方法,完成IoC容器可用的最后一道工序。查找当前ApplicationContext中是否注册有CommandLineRunner,如果有,则遍历执行它们。正常情况下,遍历执行SpringApplicationRunListener的finished()方法

由于微服务是也是分布式的,需要一个解决方案来统一管理和配置这些零散的微服务。于是SpringCloud诞生了,就是基于SpringBoot提供的一套微服务解决方案。他巧妙的简化了分布式系统基础设施的开发,提供了快速构建分布式系统的一些工具,提供了服务注册与发现,配置中心,全链路监控,服务网关,负载均衡,熔断器等组件,是各个微服务架构落地技术的集合体。

SpringBoot可以离开SpringCloud独立使用开发项目,但是SpringCloud离不开SpringBoot,属于依赖的关系。

Dubbo在服务注册中心用Zookeeper,服务调用方式RPC(方式)内部封装了Netty,用了TCP协议。

SpringCloud使用Eureka做为注册中心,服务调用方式是基于HTTP协议的restful风格的方式调用。此外,SpringCloud作为spring全家桶成员的一组,

服务发现——****NetflixEureka:主要做项目的注册与发现管理,在负载均衡方面采用轮询算法。

服务调用——****NetflixFeign:实现远微服务程调用,例子:问答微服务与基础微服务之间的调用

熔断器——****NetflixHystrix:在需要调用的服务之间,开启了熔断器,防止出现雪崩。

消息总线——SpringCloudBus:了实现配置文件更改后,我们采用rabbitmq消息中间件实现配置的实时更新,配合SpringCloudConfig一起使用。

SpringCloud将它集成在自己的子项目spring-cloud-netflix中,实现SpringCloud的服务发现功能。Eureka包含两个组件:EurekaServer和EurekaClient

EurekaServer提供服务注册服务,各个节点(各个微服务)启动后,会在EurekaServer中进行注册,这样EurekaServer中的服务注册表中将会存储所有可用服务节点的信息。服务节点的信息可以在界面中直观的看到。

EurekaClient是一个java客户端(微服务),用于简化与EurekaServer的交互,客户端同时也就别一个内置的、使用轮询(round-robin)负载算法的负载均衡器。

服务发现心跳包:在应用启动后,将会向EurekaServer发送心跳,默认周期为30秒,如果EurekaServer在多个心跳周期内没有接收到某个节点的心跳,EurekaServer将会从服务注册表中把这个服务节点移除(默认90秒)

由于会出现因网络故障原因导致心跳超过90s,微服务本身其实是健康的,所以为了避免这样的误删所以有个保护模式的机制。

它的架构哲学是宁可同时保留所有微服务(健康的微服务和不健康的微服务都会保留),也不盲目注销任何健康的微服务。使用自我保护模式,可以让Eureka集群更加的健壮、稳定.

在SpringCloud中,可以使用eureka.server.enable-self-preservation=false禁用自我保护模式。(不建议使用)

服务端(注册中心)开发

在父工程pom.xml定义SpringCloud版本;

创建子工程tensquare_eureka添加依赖

子工程tensquare_eureka添加application.yml

server:

port:6868端口

eureka:

client:

register-with-eureka:false#是否将自己注册到Eureka服务中,本身就是所有无需注册

fetch-registry:false#是否从Eureka中获取注册信息

serviceUrl:#Eureka客户端与Eureka服务端进行交互的地址格式

编写启动类,创建包com.tensquare.eureka,包下建立类,添加@EnableEurekaServer开启eureka服务

服务注册(配置客户端****)

在每个需要注册的微服务(客户端)添加依赖

修改每个微服务的application.yml,添加注册eureka服务的配置

serviceUrl:#Eureka客户端与Eureka服务端进行交互的地址

instance:

prefer-ip-address:true#把自己的ip地址报告给Eureka(远程需要,本地测试没有问题)

修改每个服务类的启动类,添加注解

@EnableEurekaClient

启动测试:将每个微服务启动起来,会发现eureka的注册列表中可以看到这些微服务了

在1号模块添加依赖,在项目包下创建接口LabelClient

@FeignClient注解用于指定从哪个服务(依赖的服务)中调用功能,名称必须一样

@RequestMapping注解用于对被调用的微服务进行地址映射;

@PathVariable注解一定要指定参数名称

在需要的类中

注入该接口进行调用,

此时启动类添加两个注解

@EnableDiscoveryClient

@EnableFeignClients

IRule是一个接口用来定义负载均衡的,SpringCloud为我们提供了7种负载均衡算法

RoudRobinRule:轮询(默认的)

RandomRule:随机

过滤掉故障的,对身剩下的进行轮询

还有几种获取服务失败则后处理的优化配置,用的不对,不记得了

AvailabilityFilteringRule:会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,还有并发的连接数超过阈值的服务,然后对剩余的服务列表按照轮询策略进行访问

BestAvailableRule:会过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务

ZoneAvoidanceRule:复合判断server所在区域的性能和server的可用性选择服务器

在启动类中注入IRule类,需要什么负载均衡类型就返回对应的名称即可。

对于高流量的应用来说,单一的后端依赖可能会导致所有服务器上的所有资源都在几秒钟内饱和。比失败更糟糕的是,这些应用程序还可能导致服务之间的延迟增加,备份队列,线程和其他系统资源紧张,导致整个系统发生更多的级联故障。这些都表示需要对故障和延迟进行隔离和管理,以便单个依赖关系的失败,不能取消整个应用程序或系统。

为了防止雪崩效应而产生的解决方案。

Hystrix能使你的系统在出现依赖服务失效的时候,通过隔离系统所依赖的服务,防止服务级联失败,同时提供失败回退机制,更优雅地应对失效,并使你的系统能更快地从异常中恢复。

Feign本身支持Hystrix,不需要额外引入依赖。

在需要依赖的地方配置application.yml,开启hystrix

设置处理机制

其实需要继承这个类HystrixCommand

设置参数:

//至少有10个请求,熔断器才进行错误率的计算;

//熔断器中断请求5秒后会进入半打开状态,放部分流量过去重试;

//错误率达到50开启熔断保护

执行命令的几种方法

Hystrix提供了4种执行命令的方法,execute()和queue()适用于HystrixCommand对象,而observe()和toObservable()适用于HystrixObservableCommand对象。

execute()

以同步堵塞方式执行run(),只支持接收一个值对象。hystrix会从线程池中取一个线程来执行run(),并等待返回值。

queue()

以异步非阻塞方式执行run(),只支持接收一个值对象。调用queue()就直接返回一个Future对象。可通过Future.get()拿到run()的返回结果,但Future.get()是阻塞执行的。若执行成功,Future.get()返回单个返回值。当执行失败时,如果没有重写fallback,Future.get()抛出异常。

observe()

事件注册前执行run()/construct(),支持接收多个值对象,取决于发射源。调用observe()会返回一个hotObservable,也就是说,调用observe()自动触发执行run()/construct(),无论是否存在订阅者。

如果继承的是HystrixCommand,hystrix会从线程池中取一个线程以非阻塞方式执行run();如果继承的是HystrixObservableCommand,将以调用线程阻塞执行construct()。

toObservable()

事件注册后执行run()/construct(),支持接收多个值对象,取决于发射源。调用toObservable()会返回一个coldObservable,也就是说,调用toObservable()不会立即触发执行run()/construct(),必须有订阅者订阅Observable时才会执行。

如果继承的是HystrixCommand,hystrix会从线程池中取一个线程以非阻塞方式执行run(),调用线程不必等待run();如果继承的是HystrixObservableCommand,将以调用线程堵塞执行construct(),调用线程需等待construct()执行完才能继续往下走。

返回方法

创建熔断实现类,实现自接口LabelClient,并注入到spring容器

returnnewResult(false,StatusCode.ERROR,"熔断器启动了");

修改调用接口注解配置

@FeignClient中添加fallback=实现类.class

在微服务多的情况下,相互之间的请求会出现复杂,而且出现跨域、认证复杂,服务之间相互调时重构问题。

上述问题,都可以借助微服务网关解决。微服务网关是介于客户端和服务器端之间的中间层,所有的外部请求都会先经过微服务网关。

Zuul组件的核心是一系列的过滤器,这些过滤器可以完成以下功能:身份认证和安全、动态路由、压力测试、负载分配

建立网关微服务

创建子模块,pom.xml引入依赖zuul,需要一个独立的模块

创建application.yml

path:/article/**#配置路由规则

serviceId:tensquare-article#指定Eureka注册中心中的服务id

配置端口,注入到注册中心,配置需要通过zuul的微服务信息

微服务名字+配置路由规则+在Eureka注册中心中的服务的id

编写启动类

@EnableZuulProxy

@SpringBootApplication

创建WebFilter过滤器并继承ZuulFilter类

可重写里面方法:

filterType:控制过滤器的调用的位置,前、后、路由请求、发送错误

filterOrder:通过int值来定义过滤器的执行顺序越小优先级越高。优先级为0,数字越大,优先级越低

shouldFilter:执行该过滤器,此处为true,说明需要过滤

Run:过滤器的具体逻辑

1、后台管理

管理后台使用,所以需要在过滤器中对token进行验证。验证不通过,禁止访问。

2、Token丢失的坑

经过网关的时候,header会丢失导致token丢失.我们现在在过滤器中接收header,转发给微服务。

publicObjectrun()throwsZuulException{

System.out.println("zuul过滤器...");

//向header中添加鉴权令牌

RequestContextrequestContext=RequestContext.getCurrentContext();

//获取header

HttpServletRequestrequest=requestContext.getRequest();

Stringauthorization=request.getHeader("Authorization");

if(authorization!=null){

requestContext.addZuulRequestHeader("Authorization",authorization);

returnnull;

为了方便服务配置文件统一管理,实时更新。支持放在远程Git仓库中。在springcloudconfig组件中,在springcloudconfig组件中,分两个角色,一是configserver,二是configclient。

ConfigServer是一个可横向扩展、集中式的配置服务器,它用于集中管理应用程序各个环境下的配置,默认使用Git存储配置文件内容,也可以使用SVN存储,或者是本地文件存储。

ConfigClient是ConfigServer的客户端,用于操作存储在ConfigServer中的配置内容。微服务在启动时会请求ConfigServer获取配置文件的内容,请求到后再启动容器。

配置中心微服务

1、在git上建立项目,上传配置文件

2、创建工程模块配置中心微服务tensquare_config

3、配置中心微服务tensquare_config,pom.xml引入依赖

4、复制对应项目git地址,放到编写配置文件application.yml

6、启动项目,添加注解@EnableConfigServer

配置客户端

1、客户端添加依赖spring-cloud-starter-config

2、bootstrap.yml先于application.yml加载,bootstrap.yml用来程序引导时执行,记录配置中心的请求路径和统一管理的配置名称

修改服务器中的配置并没有更新立刻到工程,只有重新启动程序才会读取配置。那我们如果想在不重启微服务的情况下更新配置如何来实现呢我们使用SpringCloudBus来实现配置的自动更新。目前唯一的实现是使用AMQP代理作为传输。

内部依赖消息队列,rabbitmq

Activemq用jms协议

Rabbitmq用AMQP协议

配置服务端(配置中心****tensquare_config)

在配置工程项目中添加:spring-cloud-bus、spring-cloud-stream-binder-rabbit

修改application.yml,添加配置:

1、添加rabbitmq的ip,放在Spring的容器下

2、暴露触发消息总线的地址,bus-refresh

配置客户端(微服务****base)

改完后需要通知微服务

引入依赖:spring-cloud-bus、spring-cloud-stream-binder-rabbit、spring-boot-starter-actuator

在码云的配置文件:配置rabbitMQ的地址,和服务端相同

增加自定义配置sms:ip

在Controller层中通过

@Value("${sms.ip}"):读取配置文件中ip属性

@RefreshScope:在Controller添加,此注解用于刷新配置

@RequestMapping(value="/ip",method=RequestMethod.GET)读取配置文件信息

SpringData是Spring基于ORM框架、JPA规范的基础上封装的一套JPA应用框架,解脱了DAO层的操作,基本上所有CRUD都可以依赖于它来实现,切换不同的ORM框架时提供了极大的方便,同时也使数据库层操作更加简单,方便解耦。

JPA规范本质上就是一种ORM规范,注意不是ORM框架——因为JPA并未提供ORM实现,它只是制订了一些规范,提供了一些编程的API接口,但具体实现则由服务厂商来提供实现,其中Hibernte是服务厂商之一。

创建一个Dao层接口,并实现JpaRepository和JpaSpecificationExecutor。

查询方法以findBy开头,条件的属性用条件关键字连接,条件属性首字母需大写。

有时候我们在查询某个实体的时候,给定的条件是不固定的,这时就需要动态构建相应的查询语句,相比JPQL,其优势是类型安全,更加的面向对象。

@Table(name="cst_customer")建立实体类和表的映射关系

@GeneratedValue(strategy=GenerationType.IDENTITY)配置主键的生成策略

IDENTITY:主键由数据库自动生成(主要是自动增长型),其中mysql数据库可以支持自增长

SEQUENCE:根据底层数据库的序列来生成主键,条件是数据库支持序列。其中oracle数据库支持序列

AUTO:主键由程序控制(默认采用sequence的形式维护)

TABLE:使用一个特定的数据库表格来保存主键

@Column(name="cust_id")指定和表中cust_id字段的映射关系

@Query注解的使用非常简单,只需在方法上面标注该注解,同时提供一个JPQL查询语句即可

@OneToMany(targetEntity=LinkMan.class)一的一方

@JoinColumn(name="lkm_cust_id",referencedColumnName="cust_id")关系映射

@ManyToOne(targetEntity=Customer.class)多的一方

@ManyToMany(targetEntity=Role.class)多对多关系

@JoinTable多对多的中间表

@Transactional配置事务

@Rollback(false)不自动回滚

都是java中orm框架,屏蔽了jdbc的api底层访问细节,可直接完成数据库的持久化操作。

Hibernate要比mybatis功能强大很多,因为hibernate自动生成sql语句

Hibernate无法直接写控制sql语句,对于特定、高效、复杂的的sql语言难以适应了

mybatis是在xml中配置sql语言,支持sql语句自动化

mybatis要比hibernate简单,是面向sql的,不用考虑对象见一些复杂的映射关系。

减少入侵?这是什么鬼呢?

涉及到一个设计方面的概念,也就是耦合性的问题

拓展性强的设计的标准是”高内聚,低耦合”,侵入式强指的是耦合太强了,判断的标准就是当引入了这个组件导致其他代码或者设计要做相应的更改以适应新组件.这样的情况我们就认为这个新组件具有入侵性.

THE END
1.软件工程发展方向软件开发工程师的职业路径csdn一、软件工程方向分类 1.基础 二、市场岗位 1. 软件研发 Golang开发工程师 脚本开发工程师 需求分析工程师 4. 通信及硬件研发 嵌入式硬件工程师 单片机开发工程师 5. 人工智能 机器视觉工程师 机器学习工程师 深度学习工程师 自然语言处理(NLP)工程师 https://blog.csdn.net/2302_77293761/article/details/140049589
2.信创产业二三事(6.136.19)自主可控新鲜事首届“华为伙伴暨开发者大会”于6月15日在线召开,华为公司副总裁、计算产品线总裁邓泰华发表《共建计算产业,共创数智未来》主题演讲,分享鲲鹏、昇腾、openEuler在商业、生态、技术方面的最新进展。会上,统信软件携手华为共同发布基于openEuler 22.03 LTS的操作系统商业发行版——统信服务器操作系统(创新版),将社区创新成果https://www.shangyexinzhi.com/article/4941248.html
3.巡展?慕尼黑工业大学碳纤维复合材料研究所ACTLCC提供从原材料开发到成品应用的全工艺链研发,推动学术与工业界的技术转化。研究所内设有五个研究小组,涵盖纤维加工、树脂成型、仿真、材料性能和交叉领域研究,与国内外研究机构和工业界有紧密合作。 德国慕尼黑工业大学碳纤维复合材料研究所(LCC)(官网:http://www.lcc.mw.tum.de/)创建于2009年5月,由德国西格里http://www.fangzhenxiu.com/post/8476744/
4.寻找下一个特斯拉暴涨奇迹!ARK“牛市女皇”重磅报告:牛年15大投资到2030年,ARM可以占领PC软件开发市场中的多数份额 几乎所有软件开发人员都在装载运行着Windows,Mac或Linux操作系统的英特尔x86 PC架构系统上编写代码。 苹果公司计划在未来两年内将Mac机(由三分之一的开发人员使用)从x86过渡到基于ARM的中央处理器(CPU)。 https://wallstreetcn.com/articles/3618592
5.企业如何通过安全左移加强生产过程中的安全对安全软件开发生命周期的日益增长的需求引发了围绕“安全左移”概念的讨论。几十年来,安全性一直处于软件开发周期的末端,软件开发基本上是线性规划的。 在传统上,保护运营环境意味着采取防火墙等安全措施。然而,像在2020年遭遇的SolarWinds这样的网络攻击在软件供应链上的威胁越来越大。研究表明,网络攻击者变得更加深思https://www.51cto.com/article/754700.html