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

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

栈:先进后出,是一种运算受限的线性表限制是仅允许在标的一端进行插入和删除操作

队列:简称队,先进先出,队列的入口、出口各占一侧。也是一种运算受限的线性表,仅允许在表的一端进行插入,而在表的另一端进行删除

数组:Array,是有序的元素序列,数组是在内存中开辟一段连续的空间,并在此空间存放元素。查找元素快、增删元素慢 通过for循环遍历 arr[i]

链表:linked list,由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时i动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。查找元素慢、增删元素快

一、线性表(重点)

线性表是由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(log n)。

红黑树放弃了追求完全平衡,追求大致平衡,在与平衡二叉树的时间复杂度相差不大的情况下,保证每次插入最多只需要三次旋转就能达到平衡,实现起来也更为简单。平衡二叉树追求绝对平衡,条件比较苛刻,实现起来比较麻烦,每次插入新节点之后需要旋转的次数不能预知。

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

属于处理流中的缓冲流,可以将读取的内容存在内存里面,有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.。

Runnable接口和Callable接口的相同点和不同点?

volatile关键字的作用是什么?

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

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

CyclicBarrier和CountDownLatch的区别是什么?

volatile和synchronized对比?

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 | grep java

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

相同点:

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

不同点:

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 区、 From Survivor 区和 To Survivor 区)和老年代。

是用来存放新生的对象。一般占据堆的 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。

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

加载器的说明:

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

垃圾回收可以有效的防止内存泄露,有效的使用可以使用的内存。垃圾回收器通常是作为一个单独的低优先级的线程运行,不可预知的情况下对内存堆中已经死亡的或者长时间没有使用的对象进行清除和回收,程序员不能实时的调用垃圾回收器对某个对象或所有对象进行垃圾回收。在 Java 诞生初期,垃圾回收是 Java最大的亮点之一,因为服务器端的编程需要有效的防止内存泄露问题,然而时过境迁,如今 Java 的垃圾回收机制已经成为被诟病的东。移动智能终端用户通常觉得 iOS 的系统比 Android 系统有更好的用户体验,其中一个深层次的原因就在于 Android 系统中垃圾回收的不可预知性。

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

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

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

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

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

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(Out of Memory)异常。

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

分区算法则将整个堆空间划分为连续的不同小区间, 每个小区间独立使用, 独立回收. 这样做的好处是可以控制一次回收多少个小区间 , 根据目标停顿时间, 每次合理地回收若干个小区间(而不是整个堆), 从而减少一次 GC 所产生的停顿。

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

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 模式下新生代的默认垃圾收集器。

Parallel Scavenge 收集器也是一个新生代垃圾收集器,同样使用复制算法,也是一个多线程的垃圾收集器, 它重点关注的是程序达到一个可控制的吞吐量(Thoughput, CPU 用于运行用户代码的时间/CPU 总消耗时间,即吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间)),高吞吐量可以最高效率地利用 CPU 时间,尽快地完成程序的运算任务,主要适用于在后台运算而不需要太多交互的任务。 自适应调节策略也是 ParallelScavenge 收集器与 ParNew 收集器的一个重要区别。

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

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

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

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

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

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

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

新生代 Parallel Scavenge 和年老代 Parallel Old 收集器搭配运行过程图

Concurrent mark sweep(CMS)收集器是一种年老代垃圾收集器,其最主要目标是获取最短垃圾回收停顿时间, 和其他年老代使用标记-整理算法不同,它使用多线程的标记-清除算法。最短的垃圾收集停顿时间可以为交互比较高的程序提高用户体验。CMS 工作机制相比其他的垃圾收集器来说更复杂。整个过程分为以下 4 个阶段:

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

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

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

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

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

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

(2)可以非常精确控制停顿时间,在不牺牲吞吐量前提下,实现低停顿垃圾回收。G1 收集器避免全区域垃圾收集,它把堆内存划分为大小固定的几个独立区域,并且跟踪这些区域的垃圾收集进度,同时在后台维护一个优先级列表,每次根据所允许的收集时间, 优先回收垃圾最多的区域。区域划分和优先级区域回收机制,确保 G1 收集器可以在有限时间获得最高的垃圾收集效率

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

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

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

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

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

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

在编译阶段会为 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 对象,不会触发类的初始化。

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

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

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

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

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

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

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

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

线程共享:堆,方法区

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

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

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

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

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

年轻代->标记-复制

老年代->标记-清除

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

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

栈内存是线程私有的。

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

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

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

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

误:

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

为避免Perm Gen占满造成Full GC现象,可采用的方法为增大Perm Gen空间或转为使用CMS GC。

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

promotionfailed是在进行Minor GC时,survivor space放不下、对象只能放入旧生代,而此时旧生代也放不下造成的;concurrentmode failure是在执行CMS GC的过程中同时有对象要放入旧生代,而此时旧生代空间不足造成的。

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

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

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

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

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

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

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

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

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

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

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

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

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

包括:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Sun JDK监控和故障处理命令有jps jstat jmap jhat jstack jinfo

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

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

(3)jmap,JVM Memory Map命令用于生成heap dump文件

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

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

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

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

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

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

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

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

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

设定堆内存大小

-Xmx:堆内存最大限制。

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

-XX:NewSize:新生代大小

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

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

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

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

下面会对常用的设计模式分别做详细的说明。

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

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

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

单例模式代码实现:

单例模式调用代码:

程序的输出结果:true

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

单例延迟加载代码:

以上为非线程安全的,单例模式如何支持多线程?

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

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

优点:

缺点:

简单工厂示意图如下:

简单工厂代码实现:

答:抽象工厂模式是在简单工厂的基础上将未来可能需要修改的代码抽象出来,通过继承的方式让子类去做决定。

比如,以上面的咖啡工厂为例,某天我的口味突然变了,不想喝咖啡了想喝啤酒,这个时候如果直接修改简单工厂里面的代码,这种做法不但不够优雅,也不符合软件设计的“开闭原则”,因为每次新增品类都要修改原来的代码。这个时候就可以使用抽象工厂类了,抽象工厂里只声明方法,具体的实现交给子类(子工厂)去实现,这个时候再有新增品类的需求,只需要新创建代码即可。

抽象工厂实现代码如下:

观察者模式是定义对象间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新。观察者模式又叫做发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模式。 优点:

缺点:

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

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

程序执行结果如下:

老王:更新了

Java:更新了

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

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

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

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

答:模板方法模式是指定义一个模板结构,将具体内容延迟到子类去实现。

优点:

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

程序执行结果:

开冰箱门

放香蕉

关冰箱门

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

优点:

缺点:

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

代理模式实现代码如下:

答:策略模式是指定义一系列算法,将每个算法都封装起来,并且使他们之间可以相互替换。

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

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

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

程序执行的结果:

骑自行车

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

优点:

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

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

适配器实现代码如下:

程序执行结果:

TypeC 充电

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

加密类

把其他类适配为集合类

如 JDK 本身的动态代理。

全局只允许有一个实例,比如:

为一个对象动态的加上一系列的动作,而不需要因为这些动作的不同而产生大量的继承类。

定义一个操作中算法的骨架,将一些步骤的执行延迟到其子类中。

答:IO 使用了适配器模式和装饰器模式。

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

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

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

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

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

UML 统一建模语言

计算机软件:

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

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

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

软件的生产过程:

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

项目的开发模型:

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

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

渐增模型:

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

演化模型:

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

螺旋模型:

定制计划: 确定软件开发目标, 选定实施方案, 弄清项目的限制条件

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

实施开发: 实施软件开发

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

智能模型:

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

软件生存周期:

软件设计,开发,使用

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

软件开发方法:

结构化程序设计方法

模块化程序设计方法

面向对象程序设计方法

UML的特点:

统一标准

面向对象

可视化,表达能力强

独立于过程

易掌握, 易用

软件系统体系结构:

视图:

用例视图

逻辑视图

构件视图

进程视图

配置视图

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

UML系统模型与建模:

三大模型图

用例模型图: 用例图

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

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

UML扩展:

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

UML开发的特征:

用例驱动的系统

以体系结构为中心

螺旋上升式的开发过程

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

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

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

类之间的关系:

关联关系

聚集关系

继承关系

依赖和细化关系

UML设计模式:

分类:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

答:代码如下:

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

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

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

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

\2. Struts 1.x和Struts 2.x之间的区别?

3.什么是拦截器?

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

4.什么是价值栈和OGNL?

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

5.匿名内部阶级?

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

ClassA a =新的ClassA();

公共双重处理(){

});

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

静态Class <?> getProxyClass(ClassLoader loader,Class <?> ...接口)静态对象newProxyInstance(ClassLoader loader,Class <?> []接口,InvocationHandler h)

InvocationHandler handler =新的MyInvocationHandler(...);

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

公共抽象类ClassLoader扩展Object

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

findClass(字符串名称)

jre / lib / *。jar

jre /类

扩展ClassLoader和System ClassLoader的父级。

8.接口Serializable如何工作?

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

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

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

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

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

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

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

\9. Buffer如何工作?

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

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

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

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

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

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

可调用可具有返回值。

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

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

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

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

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

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

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

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

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

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

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

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

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

7)解释什么是堆排序?

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

8)说明什么是Skip list?

Skip list数据结构化的方法,它允许算法在符号表或字典中搜索、删除和插入元素。在Skip list中,每个元素由一个节点表示。搜索函数返回与key相关的值的内容。插入操作将指定的键与新值相关联,删除操作可删除指定的键。

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.单独创建一个比较类,这里以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的数据类型分两大类:

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

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

四类八种

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

八种:

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

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

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

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

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

类、接口、数组

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

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

DK 1.5 (以后的版本)的新特性自动装箱和拆箱

\1. 自动装箱:把基本类型转换为包装类类型int a =10;Integer i = new Integer(a);

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

举一反三

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

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

拆箱:devaning装箱:

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

看程序写结果1.

答案 : false true

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

答案:true true

`3.

答案: false true

总结: 自动装箱,范围在 -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、Spring DI 的三种方式?

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

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

接口注入

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”

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

在配置文件中配置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对象不一致,如何处理?

映射键值对即可

column:数据库中表的列名

property:实体Bean中的属性名

6、Mybatis的好处?

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

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

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

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

7、Mybatis配置一对多?

property:属性名

column:共同列

ofType:集合中元素的类型

select:要连接的查询

8、Mybatis配置一对一?

property:属性名

select:要连接的查询

column:共同列

javaType:集合中元素的类型

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

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

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

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

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

11、Mybatis如何分页,分页原理?

RowBounds对象分页

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

12、Mybatis工作原理?

原理:

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—Inversion of Control,即“控制反转”,不是什么技术,而是一种设计思想。

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

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

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

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

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

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

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

方式:在pom问题添加依赖

AOP—Aspect Oriented Programming,即“面向切面编程”,不是什么技术,而是一种设计思想。在程序开发中主要用来解决一些系统层面上的问题,比如日志,事务,权限等。利用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的配置实现。

基于注解的切面实现

Spring beans 是那些形成Spring应用的主干的java对象。它们被Spring IOC容器初始化,装配,和管理。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(Object obj, String s)方法,BeanPostProcessor经常被用作是Bean内容的更改,并且由于这个是在Bean初始化结束时调用那个的方法,也可以被应用于内存或缓存技术;

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

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

注:以上工作完成以后就可以应用这个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在每个Spring ioc 容器中只有一个实例。缺省的Spring bean 的作用域是Singleton。默认的(估计是为了体现轻量级和性能,而且一般情况下单例足够了,若需要其他方式可自行更改)

prototype:

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

request:

session:

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

global-session:

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

在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的定义,XML Bean 配置文件的全路径名必须提供给它的构造函数。

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

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

Application contexts提供一种方法处理文本消息,一个通常的做法是加载文件资源(比如镜像),它们可以向注册为监听器的bean发布事件。另外,在容器或容器内的对象上执行的那些不得不由bean工厂以程序化方式处理的操作,可以在Application contexts中以声明的方式处理。Application contexts实现了MessageSource接口,该接口的实现以可插拔的方式提供获取本地化消息的方法。

初始化区别

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

ApplicationContext则在初始化应用上下文时就实例化所有单实例的Bean。因此ApplicationContext的初始化时间会比BeanFactory稍长一些,不过稍后的调用则没有”第一次惩罚”的问题

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

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

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

大多数Spring框架的用户选择声明式事务管理,因为它对应用代码的影响最小,因此更符合一个无侵入的轻量级容器的思想。声明式事务管理要优于编程式事务管理,虽然比编程式事务管理(这种方式允许你通过代码控制事务)少了一点灵活性。

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

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

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

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

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

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

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

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

开启配置

<context:component-scan base-package="扫描的包名"></context:component-scan>

注解装配在默认情况下是不开启的,为了使用注解装配,我们必须在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配置类,当创建容器时会从该类上加载注解

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

@Bean

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

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

@Import

用于导入其他配置类

@PropertySource

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

指明运行的测试环境

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

aop:aspectj-autoproxy/开启AOP注解

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

@Before 前置通知

@AfterReturning 后置通知

@Around 环绕通知

@AfterThrowing 异常抛出通知

@After 最终通知

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

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后请求视图解析器View Resolver据逻辑视图名解析成物理视图名即具体的页面地址;再生成 View 视图对象,最后对 View 进行渲染将处理结果通过页面展示给用户。

DispatcherServlet****前端控制器

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

HandlerMapping****处理器映射器

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

HandlAdapter****处理器适配器

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

Handler****处理器

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

View Resolver****视图解析器

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

View****视图

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

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

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

POJO 类中List

POJO 类中Map<String,Account>

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项目对象模型(Project Object Model,POM),定义了对其他库的传递依赖,这些东西加在一起即支持某项功能。

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

自动配置

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

下面有三个重要的注解:

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

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

我们可以通过basePackages等属性来细粒度的定制@ComponentScan自动扫描的范围,如果不指定,则默认Spring框架实现会从声明@ComponentScan所在类的package进行扫描当前包及其子包下的文件。

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

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

AutoConfigurationImportSelector 继承了 DeferredImportSelector 继承了 ImportSelector

ImportSelector有一个方法为:selectImports。

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

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

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

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

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

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

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

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

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

第三步:创建并配置当前Spring Boot应用将要使用的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协议。

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

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

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

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

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

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

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

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

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

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

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

服务端(注册中心)开发

创建子工程tensquare_eureka添加依赖

server:

port: 6868 端口

eureka:

client:

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

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

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

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

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

eureka:

client:

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

WeightResponseTimeRule: 根据平均响应时间就算所有服务的权重,响应时间越快服务权重越大被选中 的概率越高.刚启动时如果统计信息不足,则使用RoudRobinRule策略,等统计信息足够,会切换到WeightedResponseTimeRule

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

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

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

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

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

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

“断路器”本身是一种开关装置,当某个服务单元发生故障之后,通过断路器的故障监控(类似熔断保险丝),向调用方返回一个符合预期的、可处理的备选响应(FallBack),而不是长时间的等待或者抛出调用方无法处理的异常,这样就保证了服务调用方的线程不会被长时间、不必要地占用,从而避免了故障在分布式系统中的蔓延,乃至雪崩。

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

设置处理机制

其实需要继承这个类 HystrixCommand

设置参数:

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

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

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

执行命令的几种方法

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

execute()

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

queue()

observe()

事件注册前执行run()/construct(),支持接收多个值对象,取决于发射源。调用observe()会返回一个hot Observable,也就是说,调用observe()自动触发执行run()/construct(),无论是否存在订阅者。

如果继承的是HystrixCommand,hystrix会从线程池中取一个线程以非阻塞方式执行run();如果继承的是HystrixObservableCommand,将以调用线程阻塞执行construct()。

toObservable()

事件注册后执行run()/construct(),支持接收多个值对象,取决于发射源。调用toObservable()会返回一个cold Observable,也就是说,调用toObservable()不会立即触发执行run()/construct(),必须有订阅者订阅Observable时才会执行。

如果继承的是HystrixCommand,hystrix会从线程池中取一个线程以非阻塞方式执行run(),调用线程不必等待run();如果继承的是HystrixObservableCommand,将以调用线程堵塞执行construct(),调用线程需等待construct()执行完才能继续往下走。

返回方法

创建熔断实现类,实现自接口LabelClient ,并注入到spring容器

修改调用接口注解配置

@FeignClient中添加fallback = 实现类.class

在微服务多的情况下,相互之间的请求会出现复杂,而且出现跨域、认证复杂,服务之间相互调时重构问题。

上述问题,都可以借助微服务网关解决。微服务网关是介于客户端和服务器端之间的中间层,所有的外部请求都会先经过微服务网关。

Zuul组件的核心是一系列的过滤器,这些过滤器可以完成以下功能:身份认证和安全、动态路由、压力测试、负载分配

建立网关微服务

path: /article/** #配置路由规则

serviceId: tensquare-article #指定Eureka注册中心中的服务id

配置端口,注入到注册中心,配置需要通过zuul的微服务信息

微服务名字+配置路由规则+在Eureka注册中心中的服务的id

编写启动类

@EnableZuulProxy

@EnableEurekaClient

@SpringBootApplication

创建WebFilter过滤器并继承ZuulFilter类

可重写里面方法:

filterType:控制过滤器的调用的位置,前、后、路由请求、发送错误

filterOrder:通过int值来定义过滤器的执行顺序 越小优先级越高。优先级为0,数字越大,优先级越低

shouldFilter:执行该过滤器,此处为true,说明需要过滤

Run:过滤器的具体逻辑

1、后台管理

管理后台使用,所以需要在过滤器中对token进行验证。 验证不通过,禁止访问。

2、Token丢失的坑

经过网关的时候,header会丢失导致token丢失. 我们现在在过滤器中接收header,转发给微服务。

public Object run() throws ZuulException {

​ //向header中添加鉴权令牌

​ //获取header

​ if (authorization != null) {

​ }

​ return null;

为了方便服务配置文件统一管理,实时更新。支持放在远程Git仓库中。在spring cloud config 组件中,在spring cloud config 组件中,分两个角色,一是config server,二是config client。

Config Server是一个可横向扩展、集中式的配置服务器,它用于集中管理应用程序各个环境下的配置,默认使用Git存储配置文件内容,也可以使用SVN存储,或者是本地文件存储。

Config Client是Config Server的客户端,用于操作存储在Config Server中的配置内容。微服务在启动时会请求Config Server获取配置文件的内容,请求到后再启动容器。

配置中心微服务

1、在git上建立项目,上传配置文件

2、创建工程模块 配置中心微服务 tensquare_config

6、启动项目,添加注解@EnableConfigServer

配置客户端

1、客户端添加依赖spring-cloud-starter-config

修改服务器中的配置并没有更新立刻到工程,只有重新启动程序才会读取配置。 那我们如果想在不重启微服务的情况下更新配置如何来实现呢? 我们使用SpringCloudBus来实现配置的自动更新。目前唯一的实现是使用AMQP代理作为传输。

内部依赖消息队列,rabbitmq

Activemq 用jms协议

Rabbitmq 用AMQP协议

配置服务端(配置中心****tensquare_config)

在配置工程项目中添加:spring-cloud-bus、spring-cloud-stream-binder-rabbit

1、添加rabbitmq的ip,放在Spring的容器下

2、暴露触发消息总线的地址,bus-refresh

配置客户端(微服务****base)

改完后需要通知微服务

引入依赖:spring-cloud-bus、spring-cloud-stream-binder-rabbit、spring-boot-starter-actuator

在码云的配置文件:配置rabbitMQ的地址,和服务端相同

增加自定义配置 sms:ip

在Controller层中通过

@RefreshScope:在Controller添加,此注解用于刷新配置

SpringData是 Spring 基于 ORM 框架、JPA 规范的基础上封装的一套JPA应用框架,解脱了DAO层的操作,基本上所有CRUD都可以依赖于它来实现,切换不同的ORM框架时提供了极大的方便,同时也使数据库层操作更加简单,方便解耦。

JPA规范本质上就是一种ORM规范,注意不是ORM框架——因为JPA并未提供ORM实现,它只是制订了一些规范,提供了一些编程的API接口,但具体实现则由服务厂商来提供实现,其中Hibernte是服务厂商之一。

创建一个Dao层接口,并实现JpaRepository和JpaSpecificationExecutor。

查询方法以findBy开头,条件的属性用条件关键字连接,条件属性首字母需大写。

有时候我们在查询某个实体的时候,给定的条件是不固定的,这时就需要动态构建相应的查询语句,相比JPQL,其优势是类型安全,更加的面向对象。

@Table(name="cst_customer") 建立实体类和表的映射关系

IDENTITY:主键由数据库自动生成(主要是自动增长型),其中mysql数据库可以支持自增长

SEQUENCE:根据底层数据库的序列来生成主键,条件是数据库支持序列。其中oracle数据库支持序列

AUTO:主键由程序控制(默认采用sequence的形式维护)

TABLE:使用一个特定的数据库表格来保存主键

@Column(name="cust_id") 指定和表中cust_id字段的映射关系

@Query 注解的使用非常简单,只需在方法上面标注该注解,同时提供一个JPQL查询语句即可

@JoinColumn(name = "lkm_cust_id",referencedColumnName = "cust_id") 关系映射

@JoinTable 多对多的中间表

@Transactional 配置事务

@Rollback(false) 不自动回滚

相同点

都是java中orm框架,屏蔽了jdbc的api底层访问细节,可直接完成数据库的持久化操作。

不同点

Hibernate要比mybatis功能强大很多,因为hibernate自动生成sql语句

Hibernate无法直接写控制sql语句,对于特定、高效、复杂的的sql语言难以适应了

mybatis是在xml中配置sql语言,支持sql语句自动化

mybatis要比hibernate简单,是面向sql的,不用考虑对象见一些复杂的映射关系。

减少入侵?这是什么鬼呢?

涉及到一个设计方面的概念, 也就是耦合性的问题

拓展性强的设计的标准是”高内聚, 低耦合”, 侵入式强指的是耦合太强了, 判断的标准就是当引入了这个组件导致其他代码或者设计要做相应的更改以适应新组件. 这样的情况我们就认为这个新组件具有入侵性.

Java Transaction API (Java 事务API)

THE END
0.2020年中考语文总复习资料大全:说明文阅读阅读理解在局部或细节上,准确抓住文段的中心句,给不同结构的段落划分层次,要言不烦地对文段的内容、事物的特点进行概括或诠释,品析说明的语言,说明词或句子的含义、词或句子的表达作用及语言的表达特点,根据文章内容对陌生的科技内容进行阐释,对答题的理由进行解说,对文中的有关内容进行创造性的文字“再表达”,阅读图形,jvzq<84yyy4{jxsimcu/exr1g1813B5:2856f=>545:88:jf0unuou
1.1Z301000建设工程基本法律知识机械是由若干个零、部件连接构成,其中至少有一个零、部件是可运动的,并且配备或预定配备动力 系统,是具有特定应用目的的组合。机械包括: (1) 单台的机械:例如,木材加工机械、金属切削机床、起重机等。 (2) 实现完整功能的机组或大型成套设备。即为同一目的由若干台机械组合成一个综合整体,如:自 动生产线、加工jvzq<84yyy4h|q}cs0ipo8r{fczb1ufy14634:6138528?=7:9:38<9770nuo
2.北京车展新能源车汇总纯电当道混动式微在外观上,和姐妹花车型雷凌一样,新车与现款车型保持一致,犀利的前大灯组造型,与中网相连。此外,雷凌前脸大嘴式下进气格栅、前灯组以及雾灯造型均与丰田最新的家族前脸特征吻合。在同级别车型中较为少见的是,该车后尾灯由50多颗LED光源组成。 动力方面,卡罗拉插电混动版搭载1.8L自然吸气发动机+电动机,同时还配备容量jvzquC41o0vdc~yq0eun0ls1z172:<433:998A>0jvsm
3.酒店客房服务培训(全文)3.什么叫服务意识:当进入正常工作状态时,便能自缘而缘的为宾客提供良好服务的一种意识,则称之为服务意识。 礼貌服务的基础是树立和增强服务意识,其前提是微笑服务。 4.微笑的作用:它是一种永不过时的服务及沟通方式,也是一种文化,更是评估服务质量的标准之一;微笑能给人一种亲切友好的感觉,使客人产生宾至如归jvzquC41yy}/;B}wgunv0lto1y5ue=n8;{h:skm0jvsm
4.安全管理工作总结(15篇)这个料台搭设就是不赚钱,也不能不要安全,否则你可能赔进去,多花几个人工是小事。我在发隐患通知单的时候,均附上了隐患整改的照片和具体意见,之后架子组按照要求在限期内整改到位。此外,在超载控制方面,一直控制的较好,班组长和信号工也配合的不错。从料台正常使用以后,由施工组设置了料台沉降变形监测记录,每周jvzquC41yy}/rrsfc0ipo8jkenbpp4iqpm{wxqpipjg876;3630qyon
5.教育教学工作成效总结报告(精选23篇)一、加强领导班子建设,充分发挥班子的主导作用 本学期,领导班子团结一致。期初,领导深入分析各学校的教学情况,制订出工作计划,规章制度做到有章可循,心中有数。要求各校教导处能指导教师制订出切合本班实际的班务计划,行之有效的班级工作计划,并热心帮助、指导年轻教师工作,大胆进行了课堂教学尝试。本学期来,在领导的jvzquC41yy}/fr~khctxgw3eqo5gcw|gp1mpppwqdgpijt134;35>=50jznn
6.亚士创能:首次公开发行股票并上市招股意向书股票频道不由股份公司收购该部分股份。 (2)股份公司上市后 6 个月内如股份公司股票连续 20 个交易日的收盘价均 低于发行价,或者上市后 6 个月期末收盘价低于发行价,承诺人直接或间接持有 的股票的锁定期限自动延长 6 个月;如遇除权除息事项,上述发行价作相应调整。 (3)在锁定期满二年内不减持承诺人持有的发行人首次jvzquC41uvudm7xvqeqtvjw0eqs0pxykeg5KE;5392?239522289;h840unuou
7.装配部门质量工作计划大全11篇积极主动地把工作做到点上、落到实处。我将尽我的能力减轻领导的压力。 三、熟悉公司新的规章制度和业务开展工作 公司在不断改革,订立了新的规定,特别在诉讼业务方面安排了专业法律事务人员协助。作为公司一名老业务人员,必须以身作责,在遵守公司规定的jvzquC41omgr0ofdkcu/exr0ep5icx|gp1;76;3jvor
8.电力基础设施建设模板(10篇)此方式与与常规施工企业单独实施BT的区别在于BT项目主办人联合金融机构(或联合投资方)、施工、设计企业共同完成项目,融资工作由专业金融机构完成。此模式的联合体成员可更好地专业分工,优势互补,减少建设管理和协调环节,利于形成融资建设的一体化优势和规模效益,同时又能发挥联合体各方技术和资金的综合优势。 2经营开发和jvzquC41yy}/jjthcdobq7hqo1nbq€jp15?5;A3jvor
9.股票投资评估汇总十篇刘建容等强调股票的投资价值是一种相对价值,结合层次分析法和因子分析法建立了上市公司内在价值评估模型,借助相对内在价值与股价动态变化趋势之间的联系构造出上市公司投资价值分析模型,实证研究表明该模型在投资者进行股票选择时具有指导作用。孙霞指出,资本结构、股票投资结构、公司治理结构以及企业所处的行业和宏观经济形势jvzquC41yy}/z~jujw4dqv3ep1nbq€jp1681393jvor
10.高频前端面试题总结(附答案)1、的title与alt有什么区别 -title是提示文本,鼠标放在图片上提示的文字 -alt 是图片显示不出来时的文字替代,能显示出来就不用替换 2、label的作用是什么?怎么用 Label与表单input配合使用,当点击label中的文字时,浏览器会自动对焦到表单控件上,扩大范围男 3、input的type属性: password、text、radio单选、checkboxjvzquC41dnuh0lxfp0tfv8|gkzooa=:3249788ftvkimg8igvcomu86477864=<
11.课程——心理咨询及其作用 孙爱军 ● 12.2 心理咨询师值得信任吗?——心理咨询的原则 孙爱军 ● 12.3 同桌的你能给他心理帮助吗?——朋辈心理辅导的角色与职责 孙爱军 ● 12.4 说话还需要技术吗?——谈话技巧 孙爱军 第一章泛海心舟——大学生心理健康 大学生的心理健康是大学生成长、成才的重要前提,大学jvzquC41jkmig{3uocxugmz0ep5dq~wug1<35>9e:g?:2?jceg65:l>hf: