java platform se binary已停止工作_JAVA面经(1)

JVM 有哪些分区?答案:程序计数器,Java 虚拟机栈,本地方法栈,堆,方法区(Java

栈中存放的是一个一个的栈帧,每一个栈帧对应一个被调用的方法。栈帧包括局部变量表, 操作数栈,方法的返回地址,指向当前方法所属的类的运行时常量池的引用,附加信息)。

JVM 中只有一个堆。方法区中最重要的是运行时常量池。

mysql 使用的引擎?

A:1)MyIsam 不支持事务,适用于选择密集型,插入密集型,mysql 默认的引擎

2)innodb 使用于更新密集型,支持事务,自动灾难恢复,行级锁,外键约束

  1. memory 出发点是速度 采用的逻辑存储介质是内存
  2. merge 一组 myisam 表的组合

linux 查看文件内容的命令

Cat:从第一行开始显示内容,并将所有内容输出

Tac:从最后一行开始显示内容,并将所有内容输出

Head:只显示前几行

Tail:只显示后几行

nl:和 cat 一样,只是 nl 要显示行号

线程池 ThreadPoolExecutor

corepoolsize:核心池的大小,默认情况下,在创建了线程池之后,线程池中线程数为 0, 当有任务来之后,就会创建一个线程去执行任务,当线程池中线程数达到 corepoolsize 后, 就把任务放在任务缓存队列中。

Maximumpoolsize:线程池中最多创建多少个线程。

Keeplivetime:线程没有任务执行时,最多保存多久的时间会终止,默认情况下,当线程 池中线程数>corepoolsize 时,Keeplivetime 才起作用,直到线程数不大于 corepoolsize。

workQueue:阻塞队列,用来存放等待被执行的任务

threadFactory:线程工厂,用来创建线程。

继承关系

Executor 接口

ExecutoServicer 接口

AbstractExecutoServicer 抽象类

ThreadPoolExecutor

线程池的状态

1.当线程池创建后,初始为 running 状态

2.调用 shutdown 方法后,处 shutdown 状态,此时不再接受新的任务,等待已有的任务执行完毕

3.调用 shutdownnow 方法后,进入 stop 状态,不再接受新的任务,并且会尝试终止正在执行的任务。

4.当处于 shotdown 或 stop 状态,并且所有工作线程已经销毁,任务缓存队列已清空,线程池被设为 terminated 状态。

当有任务提交到线程池之后的一些操作:

1.若当前线程池中线程数

  1. 若当前线程池中线程数>=corepoolsize,会尝试将任务添加到任务缓存队列中去,若添 加成功,则任务会等待空闲线程将其取出执行,若添加失败,则尝试创建线程去执行这个任务。
  2. 若当前线程池中线程数>= Maximumpoolsize,则采取拒绝策略(有 4 种,1)abortpolicy 丢弃任务, 抛出 RejectedExecutionException 2 ) discardpolicy 拒绝执行, 不抛异常 3 )

discardoldestpolicy 丢弃任务缓存队列中最老的任务,并且尝试重新提交新的任务 4)

callerrunspolicy 有反馈机制,使任务提交的速度变慢)。

生产者-消费者代码-blockingqueue 实现

java platform se binary已停止工作_JAVA面经(1)_第1张图片

java platform se binary已停止工作_JAVA面经(1)_第2张图片

java platform se binary已停止工作_JAVA面经(1)_第3张图片

GC

对象是否存活

1)引用计数法 缺点:很难解决对象之间循环引用的问题。

2)可达性分析法 基本思想:通过一系列的称为“GC roots”的对象作为起始点,从这些节点,开始向下搜索,搜索所走过的路径称为引用链,当一个对象到 GC root 没有任何引用链相连(用图论的话来说,就是从 GC roots 到这个对象不可达),则证明此对象是不可用的。

可作为 GC roots 的对象

1)java 虚拟机栈(栈帧中的本地变量表)中引用的对象

2)方法区中类的静态属性引用的对象

3)方法区中常量引用的对象

4)本地方法栈中 JNI 引用的对象

引用强度 强引用>软引用>弱引用>虚引用

任何一个对象的 finalize()方法都只会被系统调用一次。

若对象在进行可达性分析后发现没有与 GC roots 相连接的引用链,那么他将会被第一次标记并进行一次筛选,筛选的条件是该对象是否有必要执行 finalize()方法,当对象没有重写

finalize()方法或者 finalize()方法已经被虚拟机调用过,虚拟机将这两种情况都视为没必要执行。

若该对象被判定为有必要执行 finalize 方法,则这个对象会被放在一个 F-Queue 队列,

finalize 方法是对象逃脱死亡命运的最后一次机会,稍后 GC 将对 F-queue 中的对象进行第二次小规模的标记,若对象要在 finalize 中成功拯救自己—只要重新与引用链上的任何一个对象建立关联即可,那么在第二次标记时他们将会被移出“即将回收”集合。

Finalize 方法不是 c 或 c++的析构函数。

停止-复制算法:它将可用内存按照容量划分为大小相等的两块,每次只使用其中一块。当这一块的内存用完了,则就将还存活的对象复制到另一块上面,然后再把已经使用过的内存空间一次清理掉。商业虚拟机:将内存分为一块较大的 eden 空间和两块较小的 survivor 空间,默认比例是 8:1:1,即每次新生代中可用内存空间为整个新生代容量的 90%,每次使用 eden 和其中一个 survivour。当回收时,将 eden 和 survivor 中还存活的对象一次性复制到另外一块 survivor 上,最后清理掉 eden 和刚才用过的 survivor,若另外一块 survivor 空间没有足够内存空间存放上次新生代收集下来的存活对象时,这些对象将直接通过分配担保机制进入老年代。

标记-清除算法:缺点 1)产生大量不连续的内存碎片 2)标记和清除效率都不高

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

分代收集:新生代 停止-复制算法 老年代 标记-清理或标记-清除垃圾收集器,前 3 个是新生代,后 3 个是老年代

  1. serial 收集器:单线程(单线程的意义不仅仅说明它会使用一个 cpu or 一条垃圾收集

线程去完成垃圾收集工作,更重要的是在它进行垃圾收集的时候,必须暂停其他 all 工作线程,直到他收集结束)。对于运行在 client 模式下的虚拟机来说是个很好的选择。停止-复制

  1. parNew 搜集器:serial 收集器的单线程版本,是许多运行在 server 模式下的虚拟机首选的新生代收集器。停止-复制
  2. parallel scaverge:目标 达到一个可控制的吞吐量,适合在后台运算,没有太多的交互。停止-复制。
  3. serial old:serial 的老年代版本,单线程,标记-清理
  4. parallel old:parallel scaverge 老年代的版本,多线程 标记-清理
  5. cms 收集器:一种以获取最短回收停顿时间为目标的收集器 “标记-清除”,有 4 个过程初始标记(查找直接与 gc roots 链接的对象) 并发标记(tracing 过程) 重新标记(因为并发标记时有用户线程在执行,标记结果可能有变化) 并发清除 其中初始标记和重新标记阶段,要“stop the world”(停止工作线程)。优点:并发收集,低停顿 缺点:1)不能处理浮动垃圾 2)对 cpu 资源敏感 3)产生大量内存碎片 {每一天具体的详解请看《深入理解 Java 虚拟机:JVM 高级特性与最佳实践》}

对象的分配:1)大多数情况下,对象在新生代 eden 区中分配,当 Eden 区中没有足够的

内存空间进行分配时,虚拟机将发起一次 minor GC {minor gc:发生在新生代的垃圾收集动作,非常频繁,一般回收速度也比较快 full gc:发生在老年代的 gc} 2)大对象直接进入老年代 3)长期存活的对象将进入老年代 4)若在 survivor 空间中相同年龄 all 对象大小的总和 >survivor 空 间 的 一 半 , 则 年 龄 >= 改 年 龄 的 对 象 直 接 进 入 老 年 代 , 无 须 等 到

MaxTeuringThreshold(默认为 15)中的要求。

空间分配担保

在发生 minor gc 前,虚拟机会检测老年代最大可用的连续空间是否>新生代 all 对象总空间,若这个条件成立,那么 minor gc 可以确保是安全的。若不成立,则虚拟机会查看

HandlePromotionFailure 设置值是否允许担保失败。若允许,那么会继续检测老年代最大可用的连续空间是否>历次晋升到老年代对象的平均大小。若大于,则将尝试进行一次 minor

gc,尽管这次 minor gc 是有风险的。若小于或 HandlePromotionFailure 设置不允许冒险,则这时要改为进行一次 full gc。

单例模式

1)双重检测

2)枚举实现

java platform se binary已停止工作_JAVA面经(1)_第4张图片

3)静态内部类的

Out of Memory

1.程序计数器是唯一一个在 Java 虚拟机规范中没有规定任何 oom 情况的区域。

2.在 java 虚拟机规范中,对于 java 虚拟机栈,规定了 2 中异常,1)若线程请求的栈深度> 虚拟机所允许的深度,则抛出 Stack Overflowerror 异常 2)若虚拟机可以动态扩展,若扩展时无法申请到足够的内存空间,则抛出 oom 异常。

  1. java 虚拟机栈为执行 java 方法,本地方法栈为虚拟机使用 native 方法服务,本地方法栈也会抛出 Stack Overflowerror 和 oom。
  2. Java 堆可以处于物理上连续的内存空间,只要逻辑上是连续的即可。可固定,可扩展。

若堆中没有内存完成实例分配,并且堆也无法再扩展,则会抛出 oom。

java platform se binary已停止工作_JAVA面经(1)_第5张图片

5.直接内存不是运行时数据区的一部分。堆上的 OOM 测试

Java 内存模型和线程

每个线程都有一个工作内存,线程只可以修改自己工作内存中的数据,然后再同步回主内存,主内存由多个内存共享。

下面 8 个操作都是原子的,不可再分的:

  1. lock:作用于主内存的变量,它把一个变量标识为一个线程独占的状态。
  2. unlock:作用于主内存的变量,他把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定。
  3. read:作用于主内存变量,他把一个变量的值从主内存传输到线程的工作内存,以便随后的 load 操作使用。
  4. load:作用于工作内存的变量,他把 read 操作从主内存中得到的变量值放入工作内存的变量副本中。
  5. use:作用于工作内存的变量,他把工作内存中一个变量的值传递给执行引擎,每 当虚拟机遇到一个需要使用到变量的值得字节码指令时将会执行这个操作。
  6. assign:作用于工作内存的变量,他把一个从执行引擎接收到的值付给工作内存的变量,每当虚拟机遇到一个给变量赋值的字节码指令时执行这个操作。
  7. store:作用于工作内存的变量,他把工作内存中一个变量的值传送到主内存中,以便随后 write 使用。
  8. write:作用于主内存的变量,他把 store 操作从工作内存中得到的变量的值放入主内存的变量中。

关键字 volatile 是轻量级的同步机制。

Volatile 变量对于 all 线程的可见性,指当一条线程修改了这个变量的值,新值对于其他线程来说是可见的、立即得知的。

Volatile 变量在多线程下不一定安全,因为他只有可见性、有序性,但是没有原子性。

线程的状态

在任意一个时间点,一个线程只能有且只有其中的一种状态

1)新建:创建后尚未启动的线程处于这种状态。

2)运行:包括了 OS 中 Running 和 Ready 状态,也就是处于次状态的线程可能正在运行, 也可能正在等待 cpu 为他分配执行时间。

3)无限期等待:处于这种状态的线程不会被分配 cpu 执行时间,要等待其他线程显示唤醒。以下方法会让线程进入无限期等待 :1.没有设置 timeout 的 object.wait()方法 2.没有设置 timeout 参数的 Thread.join()方法 3.LockSupport.park();

4)有限期的等待:处于这种状态的线程也不会被分配 cpu 执行时间,不过无需等待被其他线程显示唤醒,而是在一定时间后,他们会由 os 自动唤醒 1.设置了 timeout 的

object.wait() 方法 2. 设 置 了 timeout 参数的 Thread.join() 3.LockSupport.parkNanos()

  1. LockSupport.parkUnit() 5)阻塞:线程被阻塞了与“等待状态”的区别是:阻塞状态在等待获取一个排它锁,

这个事件将在另外一个线程放弃这个锁的时候发生。等待状态在等待一段时间或者唤醒动

作。

6)结束:已终止线程的线程状态,线程已经结束执行。

synchronized(S) VS lock(L)

1) L 是接口,S 是关键字

2)S 在发生异常时,会自动释放线程占有的锁,不会发生死锁。L 在发生异常时,若没有主动通过 unlock()释放锁,则很有可能造成死锁。所以用 lock 时要在 finally 中释放锁。

3)L 可以当等待锁的线程响应中断,而 S 不行,使用 S 时,等待的线程将会一直等下去, 不能响应中断。

4)通过 L 可以知道是否成功获得锁,S 不可以。

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

mysq 索引使用的是 B+的数据结构

索引:用于提高数据访问速度的数据库对象。

优点:1)索引可以避免全表扫描;2)对于非聚集索引,有些查询甚至可以不访问数据项;3)聚集索引可以避免数据插入操作集中于表的最后一个数据页;4)一些情况下,索引还可以避免排序。

虽然索引可以提高查询速度,但是他们也会导致数据库更新数据的性能下降,因为大部分数据更新时需要同时更新索引。

聚集索引:数据按索引顺序存储,叶子节点存储真实的数据行,不再有另外单独的数据页。在一张表上只能创建一个聚集索引,因为真实数据的物理顺序只能有 1 种,若一张表没有聚集索引,则他被称为堆集,这样表的数据行无特定的顺序,所有新行将被添加到表的末尾。

非聚集索引与聚集索引的区别:

1)叶子节点并非数据节点

2)叶子节点为每一个真正的数据行存储一个“键-指针”对

3)叶子节点中还存储了一个指针偏移量,根据页指针及指针偏移可以定位到具体的数据行。

4)在除叶节点外的其他索引节点,存储的是类似内容,只不过是指向下一级索引页。类加载

类从加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期包括: 加载-验证-准备-解析-初始化-使用-卸载,其中验证-准备-解析称为链接。

在遇到下列情况时,若没有初始化,则需要先触发其初始化(加载-验证-准备自然需要在此之前):

1)1.使用 new 关键字实例化对象 2.读取或设置一个类的静态字段 3.调用一个类的静态方法。

2)使用 java.lang.reflect 包的方法对类进行反射调用时,若类没有进行初始化,则需要触发其初始化

3)当初始化一个类时,若发现其父类还没有进行初始化,则要先触发其父类的初始

化。

4)当虚拟机启动时,用户需要制定一个要执行的主类(有 main 方法的那个类),虚拟机会先初始化这个类。

在加载阶段,虚拟机需要完成下面 3 件事:

1)通过一个类的全限定名获取定义此类的二进制字节流;

2)将这个字节流所表示的静态存储结构转化为方法区运行时数据结构

3)在内存中生成一个代表这个类的 class 对象,作为方法区的各种数据的访问入口。

验证的目的是为了确保 clsss 文件的字节流中包含的信息符合当前虚拟机的要求,且不会危害虚拟机自身的安全。验证阶段大致会完成下面 4 个阶段的检验动作:1)文件格式验证 2)元数据验证 3)字节码验证 4)符号引用验证{字节码验证将对类的方法进行校验分析,保证被校验的方法在运行时不会做出危害虚拟机的事,一个类方法体的字节码没有通过字节码验证,那一定有问题,但若一个方法通过了验证,也不能说明它一定安全}。

准备阶段是正式为类变量分配内存并设置变量的初始化值得阶段,这些变量所使用的内存都将在方法区中进行分配。(不是实例变量,且是初始值,若 public static int a=123;准备阶段后 a 的值为 0,而不是 123,要在初始化之后才变为 123,但若被 final 修饰,public static final int a=123;在准备阶段后就变为了 123)

解析阶段是虚拟机将常量池中的符号引用变为直接引用的过程。

静态代码块只能访问在静态代码块之前的变量,在它之后的变量,在前面的静态代码块中可以复制,但是不可以使用。

通过一个类的全限定名来获取定义此类的二进制字节流,实现这个动作的代码就是

“类加载器”。

比较两个类是否相同,只有这两个类是由同一个类加载器加载的前提下才有意义,否则即使这两个类来源于同一个 class 文件,被同一个虚拟机加载,只要加载他们的加载器不同,他们就是不同的类。

从 Java 虚拟机的角度来说,只存在两种不同的类加载器:一种是启动类加载器,这个类加载器使用 c++实现,是虚拟机自身的一部分。另一种就是所有其他的类加载器,这些类加载器都由 Java 实现,且全部继承自 java.lang.ClassLoader。

从 JAVA 开发人员角度,类加载器分为:

1)启动类加载器,这个加载器负责把lib 目录中或者 –Xbootclasspath

下的类库加载到虚拟机内存中,启动类加载器无法被 Java 程序直接引用。

2)扩展类加载器:负责加载libext 下或者 java.ext.dirs 系统变量指定路径下 all 类库,开发者可以直接使用扩展类加载器。

3)应用程序类加载器,负责加载用户路径 classpath 上指定的类库,开发者可以直接使用这个类加载器,若应用程序中没有定义过自己的类加载器,一般情况下,这个就是程序中默认的类加载器。

双亲委派模型:若一个类加载器收到了类加载请求,它首先不会自己去尝试加载这个类,而是把所这个请求委派给父类加载器去完成,每一层的加载器都是如此,因此 all 加载请求最终都应该传送到顶级的启动类加载器。只有当父类加载器反馈自己无法加载时(他的搜索范围中没有找到所需的类)时,子加载器才会尝试自己去加载。

双亲委派模型好处:eg,object 类。它存放在 rt.jar 中,无论哪个类加载器要加载这个类,最终都是委派给处于模型顶端的启动类加载器加载,因此 object 类在程序的各种加载环境中都是同一个类。

线程安全与锁优化 东西太多了,直接见《深入理解 Java 虚拟机:JVM 高级特性与最佳实践》最后一章。

同步:多个线程并发访问共享数据时,保证共享数据在同一个时刻,只被一个(or 一些,使用信号量)线程使用,而互斥是实现同步的一种手段,临界区,互斥量,信号量都是主要的互斥实现方式。互斥是因,同步是果;互斥是方法,同步是目的。

公平锁:(先申请先得到)多个线程在等待同一个锁时,必须按照申请锁的时间顺序来依次获取,而非公平锁则不保证这一点。Synchronized 不是公平锁,reetrantlock 默认下也是非公平的,但是在构造函数中,可以设置为公平的。

互斥同步对性能最大的影响是阻塞的实现,挂起线程和恢复线程的需要转入内核态中完成。若物理机器上有一个以上的处理器,能让 2 个 or2 个以上的线程同时并行执行,我们就可以让后面请求锁的那个线程“稍等一下”,但不放弃处理器的执行时间,看看持有锁的线程是否很快就释放锁,为了让线程等待,我们只需要让他执行一个忙循环(自旋),这项技术就是所谓的自旋锁。

自适应自旋锁的自旋时间不再固定,而是由前一次在同一个锁上的自旋时间及锁的拥有者的状态决定。

tcp 如何保证传输的可靠性?tcp 是面向连接,可靠的字节流服务。

面向连接意味着两个使用 tcp 的应用(通常是一个客户端和一个服务器)在彼此交换数据之前必须先建立一个 tcp 连接。在一个 tcp 连接中,仅有两方进行彼此通信,广播和多播不能用于 tcp。

Tcp 通过下列方式提供可靠性:

1)将应用数据分割为 tcp 认为最合适发送的数据块;

2)超时重传:当 tcp 发出一个段后,他启动一个定时器,等待目的端确认收到这个报文段。若不能及时收到一个确认,将重发这个报文段。

3)当 tcp 收到发自 tcp 链接另一端的数据时,它将发送一个确认(对于收到的请求,给

出确认响应)。这个确认不是立即发送,通常将推迟几分之一秒(之所以推迟,可能是要对包做完校验);

4)若 tcp 收到包,校验出包有错,丢弃报文段,不给出响应,tcp 发送端会超时重传;

5)对于失序数据进行重新排序,然后交给应用层(tcp 报文段作为 ip 数据报进行传输, 而 ip 数据报的到达会失序,因此 tcp 报文段的到达也可能失序。若必要,tcp 将对收到的数据进行重新排列,以正确的顺序交给应用层)。

6)对于重复数据,直接丢弃。

7)tcp 可以进行流量控制,防止较快主机致使较慢主机的缓冲区溢出。

字节流服务:两个应用程序通过 tcp 连接,tcp 不在字节中插入记录标识符,我们将这种为字节流服务。

Tcp 对字节流的内容不做任何解释,tcp 不知道传输的字节流数据是二进制数据还是 ascii

字符或其他类型数据,对字节流的解释由 tcp 连接双方的应用层。

https 其实是由两部分组成:http+ssl/tls,也就是在 http 上又加了一层处理加密信息的模块,服务端和客户端的信息传输都会通过 tls 加密,传输的数据都是加密后的数据。加解密过程:

1)客户端发起 https 请求(就是用户在浏览器里输入一个 https 网址,然后连接到 server

的 443 端口)

2)服务端的配置(采用 https 协议的服务器必须要有一塔数字证书,可以自己制作, 也可以向组织申请,这套证书就是一对公钥和私钥)。

3)传输证书(这个证书就是公钥,只是包含了很多信息)

4)客户端解析证书(由客户端 tls 完成,首先验证公钥是否有效,若发现异常,则弹出一个警示框,提示证书存在问题,若无问题,则生成一个随机值,然后用证书对随机值进行加密)

5)传输加密信息(这里传输的是加密后的随机值,目的是让服务端得到这个随机值, 以后客户端和服务端的通信就可以通过这个随机值来进行加密了)

6)服务端解密信息(服务端用私钥解密后得到了客户端传来的随机值,then 把内容通过该值进行对称加密。所谓对称加密就是,将信息和私钥通过某种算法混在一起,这样除非知道私钥,不然无法获取内容,而正好客户端和服务端都知道这个私钥,所以只要加密算法够彪悍,私钥够复杂,数据就够安全)

7)传输加密的信息

8)客户端解密信息,用随机数来解。

问:在客户端抓包抓到的是加密的还是没加密的?不知道是哪个面试官问的,我就乱说是加密的,然后面试官说错了,是没有加密的

java 集合类:太多了,自己网上查。

ArrayList VS Vector (都实现了 list 接口,都是数组实现) 不安全 安全

扩容 50% 扩容 100%

ArrayList VS LinkedList

数组 链表

适合检索和在末尾插入 or 删除 适合在中间插入 or 删除

LinkedList 还实现了 queue 接口,他还提供 peek(),pool(),offer()等方法。

hashmap 和 hashtable 底层原理(必问,每一个点都要清楚) 给几个网址:http://zhangshixi.iteye.com/blog/672697

http://www.admin10000.com/document/3322.html http://www.cnblogs.com/skywang12345/p/3310887.html http://blog.csdn.net/chdjj/article/details/38581035

使用线程池。我们只需要运行 Executors 类给我们提供的静态方法,就可以创建相应的线程池。

Public static ExecutorService newSingleThreadExecutor() Public static ExecutorService newFixedThreadPool() Public static ExecutorService newCahedThreadPool()

newSingleThreadExecutor 返回包含一个单线程的 Executor,将多个任务交给这个 Executor

时,这个线程处理完一个任务之后接着处理下一个任务,若该线程出现异常,将会有一个新线程来代替。

newFixedThreadPool 返回一个包含指定数目线程的线程池,若任务数多于线程数目,则没有执行的任务必须等待,直到有任务完成为止。

newCahedThreadPool 根据用户的任务数创建相应的线程数来处理,该线程池不会对线程数加以限制,完全依赖于 JVM 能创建线程的数量,可能引起内存不足。

我们只需要将待执行的方法放入 run 方法中,将 Runnable 接口的实现类交给线程池的

execute 方法,作为他的一个参数,比如:

Executor e=Executors.newSingleThreadExecutor(); e.execute(new Runnable(){ //匿名内部类public void run(){

//需要执行的任务

}

});

排序,感觉这个写的很好(http://blog.csdn.net/hguisu/article/details/7776068/),每一个点都要懂,直接插入,选择,冒泡,归并,快排要会写,面试问的最多的是快排(时间复杂度,最好和平均都是 O(nlgn),最差是 O(n*n),当数据几乎有序时是最差的,这是退为冒泡, 空间复杂度 O(nlgn))。

当运行一个程序时,JVM 启动,运行 bootstrap classloader ,该 classloader 加载核心 API

(Ext classloader 和 app classloader 也在此时被加载),then 调用 ext classloader 加载扩展

API,最后 app classloader 加载 classpath 目录下定义的 class,这就是一个程序最基本的加载流程。

通过 classloader 加载类实际上就是加载的时候并不对该类进行解析,因此也不会初始化,而 class 类的 forName 方法则相反,使用 forName 方法加载的时候会将 class 进行解析与初始化。

Error 和 exception 的区别?Error 类一般指与虚拟机相关的问题,比如系统崩溃,虚拟机错误,内存空间不足,对于这种错误导致的应用程序中断,仅靠程序本身无法恢复和预防, 遇到这样的错误,建议让程序终止。Exception 表示程序可以处理的异常,遇到这类异常, 应该尽可能处理异常,使程序恢复运行,而不应该随意终止异常。

1)final 定义的常量,一旦初始化,不能被修改,对基本类型来说是其值不可变,而对引用来说是其引用不可变。其初始化只能在两个地方 1.其定义处 2.构造函数中,二者只能选其一,不能在定义时给了值,又在构造函数中赋值。2)不管有无异常发生,finally 总会执行,若 catch 中有 return 语句,也执行,在 return 语句之前执行。3)一个类不能既被声明为 abstract,也被声明为 final 4)finalize 方法定义在 object 中。

在 Java 中,内存泄漏就是存在一些被分配的对象,这些对象存在以下一些特点:1)对象是可达的,即在有向图中,存在通路可以与其相连 2)对象是无用的,即程序以后不会再使用这些对象。这些对象不会被 gc 所回收,然而他们却占用内存。

发生内存泄漏的第一个迹象通常是:在应用程序中出现了 OutOfMemoryErroe(OOM)

ArrayList 和 LinkedList 的 remove 和 contains 方法都依赖 equals 方法。

HashSet:底层 hash 表,若两个元素的 hash 值不同,则一定不是同一个元素,若 hash 值相同,则判断 euqals,若 equals 相同,则是同一个对象,若 equals 不同,则不是一个对象(hashset 的 contains 和 remove 依赖于和顺从的和 equals)。

TreeSet 集合的特点是可以对其中的元素进行排序,换句话说,一种情况是放进 treeset 中的元素必须具有比较性,集合的底层结构是二叉树,保证元素唯一性的方法是 compareTo 方法返回 0。另一种情况是元素没有比较性,但是集合有比较性,定义一个类,实现 conparator 接口,实现一个比较器。

IOC 控制反转 容器控制程序对象之间的关系,而不是传统实现中,有程序代码之间控制, 又名依赖注入。All 类的创建,销毁都由 Spring 来控制,也就是说控制对象生命周期的不是引用他的对象,而是 Spring。对于某个对象而言,以前是他控制其他对象,现在是 all 对象都被 spring 控制,这就叫控制反转。

依赖注入的思想时通过反射机制实现的。在实例化一个类时,它通过反射调用类中的 set

方法将事先保存在 hashmap 中的类属性注入到类中。

Spring 实现 aop:JDK 动态代理,cglib 动态代理。

JDK 动态代理:其代理对象必须是某个接口的实现,他是通过在运行期间创建一个接口的实现类来完成对目标对象的代理。核心类有:InnovactionHandler,Proxy。

Cgib 动态代理:实现原理类似于 jdk 动态代理,只是他在运行期间生成的代理对象是针对目标类扩展的子类。MethodInterceptor。

多态:父类引用指向子类对象,对于 static,编译运行都看左边。

二叉平衡树:他是一颗空树 or 他左右两个子树的高度差的绝对值不超过 1,并且左右两个子树都是一颗平衡二叉树。

图的深度优先遍历和广度优先遍历。深:设 x 是当前被访问的顶点,在对 x 做过访问标

记后,选择一条从 x 出发的未检测过的边(x,y),若发现顶点 y 已经访问过,则重新选择另一条从 x 出发的未检测的边,否则沿边(x,y)到达未曾访问过的 y,对 y 访问并将其标记为已经访问;然后从 y 开始搜索,直到搜索完从 y 出发的 all 路径,即访问完 al 从 y 出发可达的顶点后,才回溯回 x,并且再选择一条从 x 出发的未检测的边。上述过程直至从 x 出发的 all 边都已经检测过为止。遍历结果不唯一。广:设 x 和 y 是两个相继要被访问的未访问过的节点,他们的邻节点分别是 x1,x2,x3。。Xn 和 y1,y2.。。Yn。为确保先访问的节点其邻节点先被访问,在搜索过程中使用 FIFO 队列来保存已经访问过的顶点。当访问 x 和 y 时,这两个顶点相继入列。此后,当 x 和 y 相继出队时,我们分别从 x 和 y 出发搜索其邻节点 x1,x2,x3。。Xn 和 y1,y2.。。Yn。对其中未访问者进行访问并将其入队列。这种方法是将每个已经访问的顶点入队,故保证了每个顶点至多只有一次入队。广度优先遍历结果不唯一。

java platform se binary已停止工作_JAVA面经(1)_第6张图片

排序的代码

java platform se binary已停止工作_JAVA面经(1)_第7张图片

java platform se binary已停止工作_JAVA面经(1)_第8张图片

java platform se binary已停止工作_JAVA面经(1)_第9张图片

java platform se binary已停止工作_JAVA面经(1)_第10张图片

泛型,即参数化类型,泛型擦除:Java 编译器生成的字节码文件不包含有泛型信息,泛型信息将在编译时被擦除,这个过程称为泛型擦除。其主要过程为 1)将所有泛型参数用其最左边界(最顶级的父类型)类型替换;2)移除 all 的类型参数。

内存溢出:程序在申请内存时,没有足够的内存空间供其使用。内存泄漏:分配出去的内存不再使用,但是无法回收。

String s=”a”+”b”+”c”+”d”+”e”;改语句只创建了一个对象”abcde”;

String s1=”aaa111”; String s2=”aaa111”; 对于值相同的常量,在常量池中只会创建一个, 所以在编译好的 class 文件中,我们只能找到一个“aaa111”的表示。另外,对于“ ”内容为 null 的常量字符串,会创建一个长度为 0,内容为空的字符串放入常量池中。常量池在运行期间可以动态扩展。

String s1=”abcde”; String s2=”abc”+”de”; s1==s2 是 true

常量池中只会维护一个值相同的 String 对象。

当调用 String 类的 intern()方法时,若常量池中已经包含一个等于此 String 对象的字符串

(用 Object 的 equals 方法确定),则返回池中的字符串,否则将此 String 对象添加到池中, 并返回此 String 对象在常量池中的引用。比如,String s1=new String(“asd”); s1=s1.intern();

String s2=”asd”; s1==s2;是 true 的。几个 String 的例子如下:

String s1=new String(“777”); String s2=”aaa777”;

String s4=”aaa”+s1; S2==s4 ;是 false 的

String s1=”abcd”; Char[] ch={‘a’,’b’’c’,’d’};

S.O.P(s1.equals(ch));是 false 的,因为他们是不同的类型。

Char[] c= {‘a’,’b’’c’,’d’}; String s=”abcd”;

Char[] cc=s.toCharArray();

Cc==c;是 false 的

Cc.equals(c);是 false 的

StringBuffer 内部实现是 char 数组,默认初始化长度为 16,每当字符串长度>chae 数组长度时,JVM 会构造更大的 char 数组,并将原先的数组内容复制到新数组。

String s=”abc”+”de”; 在编译期间就能确定自字符串值的情况下,使用+效率最高。

String s=”a”+”b”; 只创建了一个对象。

String s1=”a”; s1+=”b”; 创建了两个对象,第一个是”a”,当执行第二句话时,因为 String

类不可变性,所以又创建了一个对象。

  1. String s1=new String(“abc”);
  2. String s2=new String(“abc”); 执行 1 时,”abc”本来就是池中的对象,而在运行时执行new String(“abc”)时,将池中的对象复制一份放入堆中,并且把堆中这个对象的引用交给 s1 持有,这条语句就创建了 2 个对象。执行 2 后,一共创建了三个对象。

MVC M:model entity 业务逻辑 V:view 视图,用户看到并与之交互的界面 C:controller 根据用户的输入,控制用户界面数据显示和更新 model 对象状态 功能模块和显示模块分离, 提高可维护性,可复用性,可扩展性。

数据库事务 ACID。

聚集索引的叶节点是数据节点,而非聚集索引的叶节点仍然是索引节点,只不过有一个指针指向对应数据块。

有索引就一定快?NO

tcp 与 udp 的差别在适用性上,并非在与速度上,泛泛地说谁比谁快都是不恰当的。

tcp:http、ftp、pop3、telnet、smtp udp:dns、rip、tftp

tcp 的三次握手,4 次分手的状态图一定要会画,一定要滚瓜烂熟。使用端口号来区分应用进程。

依赖注入:在运行期间由容器将依赖关系注入到组件中,就是在运行期,由 spring 根据配置文件将其他对象的引用通过组将提供的 setter 方法进行设定。 控制反转:对组件对象控制权的转移,从程序代码本身转移到了外部容器,通过容器来实现对象组件的装配和管理。

Hibernat 可以理解为一个中间件,他负责把 java 程序的 sql 语句接受过来并发送到数据库,而数据库返回来的信息由 hibernate 接收后直接生成一个对象传给 java。Hibernate 有 1) 一个配置文件 cfg.xml,包括基本配置信息,比如数据库的操作,username,password,url,

driver 和 format sql 和方言;2)还有一个映射文件 hbm.xml 对数据控中表的映射。

Hibernate 一级缓存是必要的, 位于 session 部分, 二级缓存则不是必须的,是由

sessionfactory 控制的进程级缓存,由开发人员自行指定。二级缓存可指定使用何种开源的

cache 工具。Hibernate3 以后的默认版本为 Ehcache。查询时间缓存的过程 1)查询一级缓存中是否有需要的额数据 2)若没有,则查二级缓存 3)若二级缓存中也没有,此时再执行查询数据库的操作。速度:一级>二级>数据库。

Hibernate 也会自行维护缓存中的数据,以保证缓存中的数据和数据库中的真实数据的一致性。无论如何,当你调用方法 or 获取一个对象时,该对象都将被加入到 session 的内部缓存中,当 flush()方法最后被调用时,对象的状态会和数据库同步。也就是说。删除、更新、增加数据的时候,同时更新缓存。

Session 接口:复制执行被持久化对象的 CRUD 操作(CRUD 的任务是完成与数据库的交流,包含很多的 sql 语句)session 对象是非线程安全的。

Sessionfactory 接口:复制初始化 hibernate,复制创建 session 对象,他并不是轻量级的。通常一个项目,一个 sessionfactory 对象。当需要操作多个数据库时,一个数据库,一个

sessionfactory。

Transaction 接口:与事务有关。

Query,criteria 接口:复制执行各种数据库查询,可使用 sql 和 hql。

Hibernate 的主键 1)assign:由程序生成主键,且要在 save()之前,否则抛出异常,特点是主键的生成值,完全由用户决定,与底层数据库无关。用户需要维护主键值,在调用

session.save()之前指定主键值。2)hilo:使用高低算法生成主键值,该方法需要额外的数据 表和字段提供高位值来源,默认,使用表 hibernate-unique-key。默认字段为 next-hilo。特点: 能保证同一个数据库主键的唯一性,但不能保证多个数据库中主键的唯一性。Hilo 与底层数据库无关,由 hibernate 维护。3)increament:由 hibernate 从数据库中取出主键的最大值, 以该值为基础,每次增量为 1,在内存中生成主键,不依赖于 DB。4)sequence:采用数据库提供的 sequence 机制生成主键,需要数据库支 sequence,比如 oracle,db2。Mysql 不支持。5)identity:由底层数据库生成标识符,identity 是有数据库自己生成的,但这个主键必须设置为自增长。使用 identity 前提是数据库支持自动类型增长字段类型。比如:db2,mysql, sql server。Oracle 不支持。6)native:由 hibernate 根据使用的数据库,自行判断采用 hilo,

sequence,identity,其中一种作为主键生成方式。比如 mysql 是 identity,oracle 是 sequence。

Hibernate 查询数据的方式:sql;query、criteria,以对象的方式添加查询条件;sql,直接使用 sql 语句操作数据库。

JDBC 中,链接操作是是由 Driver Manager 管理,JDBC 建立和关闭时及其耗资源的,

connection 接口实现链接数据库。 JDBC 链 接 数 据 库 分 为 4 部: 1 )加载驱动

Class.forName(“con.mysql.jdbc.Driver”);2 ) 创 建 连 接 Connection

con=DriverManager.getConnection(url,username,password) ; 3 ) 创 建 statement staticment s=con.createStatement() 4)执行 sql s.executeQuery();

prepareStatement 是预先编译的语句,statement 不是 防止 sql 注入 性能提高 ,

callableStatement:存储过程调用 DatabaseMetaDate 类的很多方法都可以知道数据库中存储过程的详情。

servlet:是用于 java 编写的服务器端程序,其使用 java servlet API,当客户机发送请求到服务器时,服务器可以将请求信息发送给 servlet,并让 servlet 建立起服务器返回给客户机的响应。当启动 web 服务器 or 客户机第一次请求服务时,可以自动装入 servlet,装入后,

servlet 继续运行直到其他客户机发出请求。

servlet 生命周期?Servlet 生命周期分为 3 个阶段 1)初始化阶段:调用 init()方法 2)响应客户请求:调用 service()3)终止:调用 destory().1)初始化阶段:在下列时刻 servlet 容器装载 servlet 1.servlet 容器启动时,自动装载某些 servlet2. 在 servlet 容器启动后,客户首次向 servlet 发送请求 3.servlet 类文件被更新以后,重新装载 servlet。Servlet 被装载后,servlet容器创建一个 servlet 对象并调用 servlet 的 init 方法。在 servlet 生命周期内,init()方法只被调用过一次。Servlet 工作原理:客户端发起一个请求,servlet 调用 service()方法时请求进行响应,service 对请求的方式进行了匹配,选择调用 dopost 或者 doget 等这些方法,然后进入对应方法中调用逻辑层上的方法,实现对客户的响应。2)响应客户请求:对于用户到达servlet 的请求,servlet 容器会创建特定于该请求的 servletRequest 和 servletresponse 对象, 然后调用 servlet 的 service 方法,service 方法从 servletrequest 对象中获得客户请求的信息, 处理该请求,并通过 servletresponse 对象向客户返回响应消息。3)终止:当 web 应用终止或者 servlet 容器终止或 servlet 容器重新装载 servlet 新实例时,servlet 容器会调用 servlet 对象的第 destory 方法,在 destory 方法中可释放 servlet 占用的资源。

hashset 不是线程安全的。

若两个对象的 equals 为 true,则 hashcode 为 true。

tomcat 就是servlet 容器,servlet 容器为javaweb 应用提供运行时环境,它复制管理servlet

和 jsp 的生命周期,以及管理他们的共享数据。

Servlet 生命周期以下方法:以下方法都由 servlet 容器调用。1)构造函数:只有第一次请求 servlet 时,创建 servlet 实例,调用构造器,这说明 servlet 是单例的,所以又线程安全问题,只被调用一次。2)init:只被调用一次,在创建好实例后,立即被调用,用来初始化

servlet3)service:被多次调用,每次请求都会调用 service,实际响应请求。4)destory:只被调用一次,在当前 servlet 所在的 web 应用被卸载前调用,用于释放当前 servlet 所占用的资源。

MVC1)m:model。Dao。与数据库打交道 2)V:view, jsp 在页面上填写 java 代码, 实现显示 3)C,controller, servlet 受理请求,获取请求参数,调用 dao 方法,转发 or 重定向页面。

Cookie 和 session http 都是无状态的协议,web 服务器不能识别出哪些请求是同一个浏览器发的,浏览器的每一次请求都是完全独立的。

Cookie 放在浏览器端,浏览器第一次范围服务器时,没有 cookie,then 服务器给浏览器一个 cookie,以后每次浏览器访问服务器都要带上这个 cookie。Jsp 是服务端

<% 1.创建一个 cookie 对象 Cookie cookie =new Cookie(“name”,”jhb”);

2.调用 response 的一个方法把 cookie 传输给客户端

Response.addCookie(cookie);

%>

默认情况下 cookie 是会话级别,存储在浏览器内存中,用户退出浏览器之后被删除, 若希望浏览器将 cookie 存在磁盘上,则要使用 maxage,并给一个以秒为单位的时间,表示

cookie 的存活时间。

Cookie 作用范围:可以作用当前目录和当前目录的子目录,但不能作用与当前目录的上一级。

Cookie:在客户端保持 http 状态信息的方案,会话跟踪。

Session:在服务器端保持 http 状态信息。

当浏览器第一次范围服务器时,没有 cookie,服务器也就得不到 JSession_id,then 服务器创建一个 session 对象,并返回一个 JSession_id 给浏览器,通过 cookie 返回,下次浏览器再访问服务器时就带上 cookie(其中有 JSession_id),服务器就能找到对应的 session 对象。JSession_id 可以通过 cookie 传输,也可以通过 url 传送。

一个 session,对应一个 sessionID。

Session:浏览器和服务器的一次会话,若浏览器不关,至始至终都是一个 session,因为用 cookie 传送。

URL 重写:response 的 encodeURL(String URL) 方法和 encoderedirectURL(String URL)两个都一样。

当程序需要为某给客户端的请求创建衣蛾 session 时,服务器首先检查这个客户端的请求是否包含一个 session 标识,即 JSession_id,如果包含,则说明以前已经为此客户创建过JSession_id,服务器就按照这个 JSession_id 检索出来(若检索不到,可能会新建一个,这种情况下可能会出现在服务器已经删除了该用户对应的 session 对象。但用户人为的在请求的

URL 上附加衣蛾 JSession_id 的参数。)如不包含则创建一个 session,并生成与这个 session

有关的 JSession_id,这个 JSession_id 将在本次响应中返回给客户端。

默认 session 用 cookie。

若第一次访问某 web 应用程序的一个 jsp 页面,且该 jsp 页面的 page 指定 session=true, 服务器会创建一个 httpsession 对象。

注:关闭浏览器只是使存储在浏览器内存中的 session cookie 失效,不会使服务器端的

session 对象失效。

多线程安全隐患的原因:当多条语句在操作同一个线程共享语句时,一个线程对多条语句只执行了一部分,还没执行完,另一个线程参与执行,导致共享数据错误。解决办法:对多条操作共享数据的语句,只能让一个线程执行完,在执行过程中,其他行程不能执行。

Synchronized(对象){

需要被同步的代码

}

Sleep 只释放 cpu 执行权,不释放锁。

找多个线程的共享数据及操作共享数据的语句。同步函数使用的锁:this

静态同步函数使用的锁:函数所在类的 class 对象

SpringMVC

使用 DispatcherServlet 进 行 分 发 , 到 达 合 适 的 额 controller 。 每 个 请 求 来 到

dispatcherServlet,dispatcherServlet 来决定到哪个 controller,通过 handlermapping。先要在

web.xml 中配置 dispatcherServlet 的,在 Servlet 中需要一个 xml

文件,自己创建,里面来配置 SpringMVC 的属性。

代理设计模式

代理类和委托类有相同的接口,一个代理类的的对象与一个委托类的对象关联。代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法来提供特定的服务。

JDK 动态代理(掌握程度是自己会写,知道每个函数的每个参数的作用)反射 1)proxy 类: 类的静态方法用来生成动态代理的实例 2)innovationhandler 接口有一个 invoke 方法,用来集中处理在动态代理类对象的方法调用,通常在该方法中实现对委托类的代理访问,每次生成动态代理对象时都要指定一个对应的调用处理器。CGlib 动态代理:jdk 代理机制只能代理实现了接口的类,而没有实现接口的类不能用 jdk 动态代理。CGlib 是针对类来实现代理, 他的原理是对指定的目标类生成一个子类,并且覆盖其中方法实现增强,因为采用的是继承, 所以不能对 final 修饰的类进行代理,methodinterceptor {我写的远远不够,网上很多例子, 自己多看几遍就回了,一定要滚瓜烂熟,被问了很多次}

第一个被加载的类是所谓的“初始化类”,也就是有 main 方法的类,这个类由 jvm 本身加载。

一个抽象类可以没有抽象方法。

一个接口可以继承任意数量的接口,一个抽象类只能继承一个抽象类。集合里放的是对象。

Finally 除了 try 块调用了 System.exit(0),finally 都会执行,有 return,也会先执行 finally

中的内容,再执行 return。

向上转型:子类转为父类向下转型:父类转为子类

ClasscastException:当试图将对象强制转换为不是实例的子类时,抛出该异常。

内部类一共有 4 种。1)成员内部类:外部类的成员。2)局部内部类。3)静态内部类: 类似于静态成员。4)匿名内部类:实现一个接口 or 继承一个抽象类。

外部类不能任意访问内部类的成员,要做到这一点,外部类必须创建其内部类的对象(静态内部类除外)。内部类可以访问 all 外部类的成员,因为内部类就像是一个类的成员。

Java 本地方法:该方法的实现是由非 java 写的,可以用 java 调用 c 或 c++的代码,这是出于性能的考虑 or 访问底层操作系统。

Java 反射:可以在运行时获取类 or 对象的相关信息。

一个类可以有一个 or 多个静态代码块。静态代码块在类被加载时执行,优于构造函数的执行,并且按照各静态代码块放在类中的顺序执行。

java 用 Observer 接口和 Obserable 类实现观察者模式。1)创建被观察者类 observable 类 2 ) 创建观察者类 Observer 接口 3 ) 对于被观察者, 添加他的观察者 void add Observer(Observer o):该方法把观察者对象添加到观察者对象列表中,当被观察事件发生时, 调用 1.setchange()用来设置一个内部标志,注明数据发生了变换;2.notifyObservers():调用观察者对象列表中 all Observer 的 update 方法,通知他们数据发生变换,只有在 setchange 被调用以后,notifyObservers 才会调用 update。4)对于观察者类,实现 Observer 接口的唯一方法 update。例子:员工看老板来了,就不玩手机了。员工是观察者,老板是被观察者。

突然想起来,面试的时候被问道代理模式和装饰模式的区别?我自己理解的是:代理是在内部生成一个代理对象,构造函数参数为空。装饰的构造函数参数有一个对象,就是对这个对象进行装饰。

堆排序空间复杂度 O(1) 建堆的时间复杂度 O(n) 调整堆的时间复杂度 O(logn)

B-tree 索引可以用于使用< <= = >= >或者 between 运算符的列比较。如果 Like 的参数是一个没有以通配符起始的常量字符串也可以使用这种索引。Hash 索引:1)只能够用于使用

=或<=>运算符的相比较,不能用于有范围的运算符,因为键经过 hash 以后的值没有意义 2) 优化器不能使用 hash 索引来加速 order by 操作。这种类型的索引不能够用于按照顺序查找下一个条目 3)mysql 无法使用 hash 索引来估计两个值之间有多少行 4)查找某行记录必须全键匹配,而且 B-tree 索引,任何该键的左前缀都可以用以查找记录 5)若将一张 myisam 或 innodb 表换为一个 hash 索引的内存表,一些查询可能受影响。

输出保留两位小数 S.O.printf(“%.2fn”,cc);其中 cc 是需要输出的数。

Java 接口的修饰符只可以是 abstract 和 public。

Interator 和 collection、map 无关。

JDBC:桥接模式

局部变量没有默认值。

-Xmx:最大堆大小

-Xms:初始堆大小

-Xmn:年轻代大小

-XXSurvivorRatio:年轻代中 eden 和 survivor 区的大小比值。

ThreadLocal 不继承 Thread,也不实现 Runable 接口,ThreadLocal 类为每一个线程都维护了自己独有的变量拷贝。每个线程都拥有自己独立的变量,其作用在于数据独立。

ThreadLocal 采用 hash 表的方式来为每个线程提供一个变量的副本。

多个静态变量或静态代码块,按顺序加载。

Object 的 wait()方法要使用 try,不然就抛出 InterruptedException。

Hibernate 持久化对象的生命周期 1)临时状态(transient),也称为自由状态,他只存在于内存中,且在数据库中无相应的数据。2)持久化状态(Persist),与 session 关联,且在数据库中有对应数据。3)游离状态(detached),已经持久化,但不在 session 缓存中。

持久化

游离

暂时

是否处于 session 缓存中

数据库中是否有记录

最小生成树不唯一。

在单链表中,增加一个头结点是方便运算。哈夫曼树有左右子树之分。

稀疏矩阵,三分组的方法:非零元素所在的行,列以及他的值构成一个三元组(x,y, v),然后按照某种规律存放三元组,这样可以节约空间。还要 3 个成员来记录矩阵的行数、列数和总的元素数。

BFS:广度优先遍历,使用队列,先进先出,正拓扑排序。DFS:深度优先遍历,使用栈,先进后出,逆拓扑排序。

若 try,finally 都有 return,则忽略 try 的 return 语句。求字符串的 next 数组 自己百度吧

快速排序在无序时效率最高,有序时效率低。

堆排序、基数排序、归并排序、选择排序的排序次数和初始化状态无关,即最好情况和最坏情况一样

知道中序,且知道前序或后序任何一个就可以确定一颗二叉树。

Integer.parseInt(s,x);s 是需要转换的字符串,x 为按什么进制 2,10 ,16 ,8,默认是

10。16 进制的字符串前面有 0x。

哈希表查找的时间复杂度与原始数量无关,hash 表在查找元素时是通过计算 hash 值来定位元素的位置的,从而直接访问元素。所以 hash 表的插入、删除、查找都是 O(1)。

空间复杂度 归并 O(n) 快速 O(logn) 其余 5 种 O(1)。

观察者模式 1)推模型:传递的信息多,更具体 2)拉模型:目标对象在通知观察者时, 只传递少量信息,由观察者主动到目标对象中获取,相当于是观察者从目标对象中拉数据,

update 方法中传目标对象的引用为拉模型 Observer 接口 Observable 类。

用代理模式,代理类可以对他的用户隐藏一个对象的具体信息。因此,使用代理模式的时候,我们常常在一个代理类中创建一个对象的实例。当我们使用装饰模式时,我们通常的做法是将原始对象作为一个参数传给装饰者的构造器。

Synchronized 1)同步代码块 使用 monitorenter 和 moniterexit 指令实现,monitorenter 指令插入到同步代码块的开始位置,moniterexit 指令插入到同步代码块的结束位置,jvm 需要保证每一个 monitorenter 都有一个 moniterexit 与之对应。任何对象都有一个 monitor 与之相关联,当且一个 monitor 被持有之后,他将处于锁定状态。线程执行到 monitor 指令前, 将会尝试获取对象所对应的 monitor 所有权,即尝试获取对象的锁。2)同步方法。依靠的是方法修饰符上的 ACC_SYNCHRONIZED 实现。Synchronized 方法则会被翻译为普通的方法调用和返回指令,比如 invokevirtual 指令,在 jvm 字节码层面并没有任何特别的指令来实现

synchronized 修饰的方法,而是在 class 文件的方法表中将该方法的 access_flags 字段中的

synchronized 标志位置为 1,表示该方法为 synchronized 方法,且使用调用该方法的对象 or

该方法所属的 class 在 jvm 内部对象表示作为锁对象。

在 java 设计中,每一个对象自打娘胎里出来就带了一把看不见的锁,即 monitor 锁。Monitor 是线程私有的数据结构,每一个线程都有一个可用 monitor record 列表,同时还有一个全局可用列表。每一个被锁住对象都会和一个 monitor 关联。Monitor 中有一个 owner 字段存放拥有该对象的线程的唯一标识,表示该锁这这个线程占有。Owner:初始时为 null, 表示当前没有任何线程拥有该 monitor record,当线程成功拥有该锁后保存线程唯一标识, 当锁被释放时,又设为 null。Entry Q:关联一个系统互斥锁,阻塞 all 试图锁住 monitor entry 失败的线程。Next:用来实现重入锁的计数。

锁主要有 4 中状态:无锁状态、偏向状态、轻量级状态、重量级状态。他们会随着竞争的激烈而逐渐升级,锁可以升级但不可以降级。

自旋锁、自适应自旋锁、锁消除。锁消除的依据是逃逸分析的数据支持。锁粗化:将多个连续加锁解锁操作链接起来扩展成一个范围更大的锁。

轻量级锁:传统的锁是重量级锁,他是用系统的互斥量来实现。轻量级锁的本意是在没

有多线程竞争的前提下,减少传统的重量级锁使用操作系统互斥量产生的性能消耗。

偏向锁:目的是消除数据在无竞争情况下的同步原语。如果说轻量级锁是在无竞争的情况下使用 CAS 操作去消除同步使用的互斥量,那么偏向锁就是在无竞争的情况下把整个同步消除掉。

Synchronized 用的锁时存在 java 对象头里。对象头包含标记字段和类型指针。类型指针是对象指向它类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。标记字段用于存储对象自身运行时数据,他是实现轻量级锁和偏向锁的关键。

JVM 可以通过对象的元数据信息确定对象的大小,但是无法从数组的元数据来确定数组的大小。

Redis 是 Nosql 数据库系统,高并发,高性能 面向 key/value。 Java 的序列化算:1)当前类的描述 2)当前类属性的描述 3)父类描述 4)父类属性

描述 5)父类属性值描述 6)子类属性值描述 类描述是从下到上,类属性描述是从上到下。

Unicode 码是 2 个字节

B 树、B-树、B+树、B*树

1.B 树就是 B-树,是一种多路索引树(并不是二叉的)1)任意非叶子节点最多只有 m 个儿子,且 m>2 。2)根节点的儿子数最多为[2,m]。3)除了跟节点以外的非叶子节点的儿子数为[m/2,m]。4)每个节点至少存放 m/2-1 和最多 m-1 个关键字。5)非叶子节点的关键字个数=儿子数-1。6)非叶子节点的关键字,k[1], k[2] ,k[3]…. k[m-1],且 k[i]< k[i+1].7)非叶子节点的指针p[1], p[2], p[3]…..p[m],其中p[i]指向关键字< k[i]的子树,p[m]指向关键字> p[m-1]的子树,其他 p[i]指向关键字属于(k[i-1],k[i])的子树。8)所有叶子节点位于同一层。

  1. B+数:适合文件系统索引,1)其基本定义和 B-树相同。2)非叶子节点的儿子数=关键字个数 3)非叶子节点的子树指针 p[i]指向关键字属于[k[i], k[i+1])的子树(B-树为开区间)。

4)all 叶子节点增加一个键指针。5)all 关键字都在叶子节点出现,且叶子节点本身依赖关键字的大小顺序排序。

查找某一个关键字时,B-树可能在非叶子节点中命中,B+树只有到达叶子节点才命中。

  1. B* 树是 B+树的变体,B*树中非跟和非叶子节点增加指向兄弟的指针。B*树中非叶子节点关键字个数>=2m/3,即块的最低使用率为 2/3(B+树为 1/2)。

B+树比 b-树根适合 OS 的文件索引和数据库索引。

B-树:有序数组+平衡多叉树。

B+树:有序数组链表+平衡多叉树

B*树:一个丰满的 B+树。

乐观锁 VS 悲观锁 1)悲观锁:就是很悲观,每次去拿数据的时候都认为别人会修改, 所以每次在拿数据的时候都会上锁。这样别人想拿这个数据就会 block 直到它拿到锁。传统的关系型数据库就用到了很多这种机制,比如行锁,写锁等,都是在操作之前上锁。2)乐观锁:就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据。适用于多读,比如 write_condition. 两种锁各有优缺点,不能认为一种比一种好。

JVM 中,一个字节以下的整形数据 byte,-128 到 127 会在 jvm 启动时加载入内存,除非用 new Integer()显示的创建对象,否则都是同一个对象。Integer 的 value 方法返回一个对

象,先判断传入的参数是否在-128 到 127 之间,若已经存在引用,则直接返回引用,否则返回 new Integer(n).

Integer i1=59;

Int i2=59;

Integer i3=Integer.valueOf(59);

Integer i4=new Integer(59); i1==i2 是 true,因为只有一份;i1==i3 是 true,因为 59 在-128 到 127 之间,因为内存中已经有了 59 的引用,则直接返回;i3==i4 是 false ;i4==i2 是 true,因为 i2 为 int 类型,在和 i4 比较时,i4 会自动拆箱。

Integer i=100;相当于编译器为我们做了 Integer i=Integer.value(100).

Hashmap 的 resize 在多线程的情况下可能产生条件竞争。因为如果两个线程都发现hashmap 需要进行 resize 了,他们会同时试着调整大小。在调整大小的过程中,存储在链表中的元素的次序会反过来。因为移动到新的位置时,hashmap 并不会将元素放在链表的尾部,而是放在头部在,这是为了避免尾部遍历。(否则,针对 key 的 hashcode 相同的 entry 每次添加还要定位到尾节点)。如果条件竞争发生了,可能出现环形列表。之后,当我们调用

get(key)操作时就可能发生死循环。

Concurrenthashmap:初始化时除了 initialCapacity,loadfactor 参数,还有一个重要的参数 concurrency level,它决定了 segment 数组的长度,默认为 16(长度需要为 2 的 n 次方, 与采用的 hash 算法有关)。每次 get/put 操作都会通过 hash 算法定位到一个 segment,then 再通过 hash 算法定位到某个具体的 entry。

Get 操作时不需要加锁的。因为 get 方法里将要使用的共享变量都定义为 volatile。定义为 volatile 的变量,能够在线程之间保存可见性,能够被多个线程同时读,并且保证不会读到过期的值。但是只能被单线程写(有一种情况可以被多线程写,就是写入的值不依赖于原值,像直接 set 值就可以,而 i++这种就非线程安全。)

Put 必须加锁。

ReenTrantLock 1)可重入,单线程可以重复进入,但要重复退出。synchronized 的也是可重入锁。2)可中断,可响应中断。3)可限时:超时不能获得锁,就返回 false,不会永久等待构成死锁。4)公平锁:线程先来,先得到锁,可在构造函数中设置。

Condition 的 await()与 single()和 object 的 wait()和 notify()类似。

对于锁来说,它是互斥的排它的,意思就是只要我获得了锁,没人再能获得,而对于

somaphore 来说,它允许多个线程同时进入临界区,可认为它是一个共享锁,但是共享的额度是有限的,额度用完了,其他没有拿到额度的线程还是要阻塞在临界区外,当额度为 1 时,相当于 lock。

ReadWriteLock:读-读不互斥,读-写互斥,写-写互斥,并发性提高。

CountDownLatch:倒数计时器,一种典型的场景就是火箭发射,在火箭发射前,往往还 要进行各项设备仪器的检查,只能等所有检查完成后,引擎才能点火,这种场景就非常适合使用 CountDownLatch,它可以使点火线程使得所有检查线程全部完工后,再执行。

cyclicBarrier:模拟高并发。

Select * from tablename orderby liename limit m,n (从 m+1 条开始,取 n 条数据)。

a 表

Id

Name

1

张三

2

李四

3

王五

b 表

Id

Age

parent_id

1

23

1

2

34

2

3

34

4

A 的 id 和 b 的 parent_id 关联

1)内连接

Select a.*,b.*from a inner join b on a.id=b. parent_id;

结果为: 1 张三 1 23 1

2 李四 2 34 2

2)左外链接(左表 all 元素都有)

Select a.*,b.*from a left join b on a.id=b. parent_id;

结果为: 1 张三 1 23 1

2 李四 2 34 2

3 王五 null

3)右外链接(右表 all 元素都有)

Select a.*,b.*from a right join b on a.id=b. parent_id;

结果为: 1 张三

1

23

1

2 李四

2

34

2

Null

3

34

2

4)完全链接(返回左边和右表中所有行,若某行在另外一个表中无匹配,则另一表的选择列表列包含空值)

Select a.*,b.*from a full join b on a.id=b. parent_id;

结果为: 1 张三

1 23 1

2 李四

2 34 2

Null

3 34 2

3 王五

null

Not in 的例子:

Select emp_no from emplyees where emp_no not in (select emp_no from dept_manager).

Sql 的不等为<>

设计模式(大类为 5 种)1)创建型模式:工厂方法,抽象工厂,单例,建造者,原型 2)

结构型模式:适配器,桥接,装饰,代理,外观,组合,享元 3)行为型模式:策略,模板方法,责任列,观察者,命令,备忘录,访问者,中介者,解析器,状态,迭代子模式。

其实还有两种,并发行模式和线程池模式。

工厂方法模式适合,凡是出现了大量的产品需要创建,并且具有公共的接口,可以通过工厂方法模式进行创建,一个工厂里,不同方法创建不同的类。

抽象工厂:工厂方法模式有一个问题就是,类的创建依赖于工厂,也就是说想要扩展程序,必须对工厂类进行修改,用抽象工厂,创建多个工厂类,这样一旦需要增加新的功能, 直接增加新的工厂类就可以了,不用改以前的代码,一个工厂生产一个具体对象。

建造者模式,工厂模式提供的是创建单个对象的模式,而建造者模式则是将各种产品集中起来进行管理,用来创建复合对象。

原型模式,将一个对象作为原型,对其进行复制克隆,产生一个和原对象类似的新对象。适配器模式:将某个类的接口转换为客户端期望的另一个接口表示。1)接口的适配器

模式:有时我们写的接口中有多个抽象方法,当我们写该接口的实现类时,必须实现该接口的所有方法,但并不是所有方法都是我们必须的,有时只需要某一些,此时,我们可以借助于一个抽象类,该抽象类实现该接口,实现所有的方法,我们不和原始接口打交道,只和抽象类联系,所以我们写一个类继承抽象类,重写我们需要的方法就行。2)类的适配:当希望将一个类转换为满足另一个接口的类时,创建一个新类,继承原有的类,实现新的接口。3)对象的适配:当希望将一个对象转换为满足另一个新接口的对象时,创建一个 wrapper 类,持有原类实例,在 wrapper 类的方法中,调用实例的方法

外观模式 façade:为了解决类与类之间的依赖关系,将他们的关系放在 façade 类中。组合模式,在处理类似树形结构的问题时很方便。

Concurrenthashmap

采用了二次哈希的方式,第一次哈希将 key 映射到对应的 segment 中,而第二次哈希则是映射到 segment 的不同桶中。

主要实现类为 Concurrenthashmap(整个 hash 表)、segment(桶)、HashEntry(每个哈希链中的一个节点)

HashEntry 中,除了 value,其余 3 个 key、hash、next 都是 final 的。这意味着不能从中间或者尾部添加或删除节点,因为这需要改变 next 的引用值,所有节点修改只能从头部开始,对于 put 操作,可以一律添加到哈希链头部,对于 remove 操作,可能需要从中间删一个节点,这就需要将要删除节点的前面所有节点整个复制一遍,最后一个节点指向要删除节点的下一个节点

Segment 类中,count 用来统计该段数据的个数,每次修改操作做了结构上的改变,比如增加或删除(修改节点的值不算结构上的改变),都要写 count 的值,每次读取操作开始都要读 count 的值。

put 操作也是委托给段的 put 方法;remove 操作也是委托给段的 remove 方法;get 操作也是委托给段的 get 方法。都是在持有段锁的情况下进行,lock 和 unlock。

由于所有修改操作,在进行结构修改时,都会在最后一步写 count 变量,通过这种机制保证 get 操作,能够得到几乎最新的结构更新。count 是 volatile 的,对 hash 链进行遍历不需要加锁的原因在于 next 指针是 final 的。读的时候,若 value 不为空,直接返回;若为空则,加锁重读

Segment 是一种可重入锁

Segments 数组的长度 size 通过 Concurrencylevel 计算得出(Concurrencylevel 为 14、15、

16 时,size 都为 16。Concurrencylevel 默认为 16),Segments 数组长度必须为 2n(一个

Concurrenthashmap 有一个 Segments 数组,每个 Segment 相当于一个 hashtable)。

Concurrenthashmap 的 get 操作时如何做到不加锁的呢?因为 get 方法里将需要使用的共享变量都定义为 volatile(定义为 volatile 的变量,可以在线程之间保证可见性,可以被多线程同时读,但只能被单线程写)。Get 中只需要读,不需要写,所以不用加锁。之所以不会读到过期值,因为 java 内存模型的 happen-before 原则,对 volatile 字段的写入操作先于读操作,即使两个线程同时修改 volatile 变量,get 也能拿到最新的值。

Resize 时,只对 Segment 扩容,不对整个 Concurrenthashmap 扩容,先判断是否需要扩容,再 put,传统 hashmap 是先 put 再判断。

Size(),求大小:最安全的做法是统计 size()时把 all Segment 的 put、remove、clean 方法全部锁住,效率低。所以 Concurrenthashmap 的做法是:先尝试两次不锁住 Segment 的方法来统计各个 Segment 大小。若统计的过程中,容器的 count 发生了变化,则采用加锁的方式来统计。使用 modcount 变量来判断统计过程中,count 是否变化。在 put、remove、clean 方法操作元素前 modcount 要加 1,那么在统计 size 前后比较 modcount 是否发生变化,从而得知容器大小是否变化。

事务隔离级别

脏读

不可重复读

幻读

1. 读未提交

2. 读已提交

3.重复读

4.序列化

悲观锁大多数情况下,依靠数据库的锁机制。乐观锁大多基于数据版本,记录机制实现。数据版本。为数据增加一个版本标识,比如增加一个 version 字段。读数据时,将版本号一块儿读出,之后更新时,版本号加 1,将提交数据的版本号与数据库表对应记录的当前版本号进行对比。若提交的大于数据库里面的,则可以更新,否则认为是过期数据。将乐观锁策略在存储过程中实现,对外只开放基于此存储过程的数据更新途径,而不是将数据表直接对外公开。

解决超卖的方法 1)乐观锁 2)队列,建立一条先进先出的队列,每个请求加入到队列中,然后异步获取队列数据进行处理,把多线程变为单线程,处理完一个就从队列中移出一个,因为高并发,可能一下将内存撑爆,然后系统又陷入异常状态。或者设计一个极大的队列,但是系统处理速度和涌入速度根本没办法比,平均响应时间小。

建堆 O(n),调整堆 O(logn).

索引(B-树,B+树),一般来说,应该在这些列上加索引:1)经常需要搜索的列,加 快搜索速度 2)第作为主键的列,强制该列的唯一性 3)在经常用在连接的列上,这些列主要是外键,可以加快连接速度 4)在经常需要根据范围进行搜索的列上创建索引,因为索引已经排序,指定的范围是连续的 5)在经常需要排序的列上。

唯一索引:不允许其中任何两行有相同的值。

主键索引:为表定义主键自动创建主键索引,是唯一索引的特定类型:

聚集索引:b-树中一次检索最多需要 h-1 次 io(根节点常驻内存,h 为树的高度),一般用磁盘 io 来评价索引的优劣,b 树中一个节点对应一页,所以一个 node 只要一次 io。

下列事件索引会失效:1)条件中有 or,即使其中有条件带索引也不会使用(要想使用

or 又想让索引生效,只能将 or 条件中每个列加上索引)2)like 查询,以%开头 3)若列类型为字符串,则一定要在条件中将数据用引号引起来,否则不使用索引 4)若 mysql 估计使用全表扫描要比索引快,则不使用索引 5)对索引进行运算导致索引列失效 6)使用内部函数导致索引失效,这样应当创建基于函数的索引 7)b-树,is null 不会用,is not null 会用。

同步阻塞,用户空间的应用程序执行一个系统调用,这意味着应用程序会一直阻塞,直到系统调用完成为止(数据传输完成或者发生错误)。

同步非阻塞,设备以非阻塞形式打开,这意味着 io 操作不会立刻完成,需要应用程序调用多次来等待完成。

同步和异步 1)同步:发出一个调用时,在没有得到结果前,该调用就不返回,一旦返回就有结果。2)异步:调用在发出之后就直接返回,所以没有返回结果,换句话说,当一个异步调用发生后,调用者不会立即得到结果,而是在调用发生后,被调用者通过状态通知来通知调用者,或者通过回调函数来处理这个调用。

阻塞和非阻塞 1)阻塞:调用结果返回之前,当前线程会被挂起,调用线程只有在得到结果之后才会返回。2)非阻塞:不能立刻得到结果之前,该调用不会阻塞当前线程。

BIO:同步并阻塞,一个连接一个线程,适用于链接数量小且固定的架构。

NIO:同步非阻塞:一个请求一个线程,客户端发送的链接请求都会注册到多路复用器上,多路复用器轮训到链接有 io 请求时才启动一个线程进行处理,适用于链接比较多,比较短。

AIO:异步非阻塞,一个有效请求一个线程,适用于链接数目多且长。

装饰模式:新增行为。代理模式:控制访问。

线程池中线程任务就是不断检测任务队列是否有任务并不断执行队列中的任务。

Treemap 的底层就是红黑树。

<%@include file=.... %> 静态 include 伪指令 使用高速缓存

动态

Hibernate.cfg.xml 和 Hibernate.hbm.xml

Hibernate 一级缓存(session 缓存)是事务级别的,每个事务(session)都有单独的一级缓存。这一级别的缓存是由 hibernate 进行管理。一般情况下无须进行干预。每个事务都有单独的一级缓存,不会出现并发问题,因此无须提供并发访问策略。

一级或二级缓存,若查询的是对象的属性,则不会把对象加入缓存中。

双亲委派模型是通过 loadclass 方法实现:先检查类是否被加载过,若没有,则调用父类加载器的 loadclass 方法,若父类加载器为空,则使用启动类加载器为父类加载器。若父类加载器加载失败,先抛出 classNotFoundException,然后调用自己 findclass 方法进行加载。

要实现自定义类加载器:只需要继承 java.lang.classLoader。

HashSet 基于 hashmap 实现,hashset 的元素都放在 hashmap 的可以上面,而 value 统一为 private static final object PERSENT=new Object();允许 null 值,不允许重。Hashset 的 add 调用的是 hashmap 的 put()。

sprigMVC 运行原理 1)客户端请求提交到 DispatcherServlet。 2)由 DispatcherServlet 控制器查查询一个 or 多个 handlermapping,找到处理请求的 controller。3)DispatcherServlet 将请求提交给 controller。4)controller 调用逻辑处理完后,返回 methodAndView。5)

DispatcherServlet 查询一个 or 多个 ViewResolver 视图解析器,找到 modleAndView 指定的视图。6)视图负责将结果显示到客户端。

排序:1)内部排序:在内存中进行排序。2)外部排序:因排序的数据很大,一次不能容纳 all 的排序记录,在排序工程中需要访问外存。

堆对应一颗完全二叉树。

选择排序:1)从 n 个记录中找出关键字最小的记录与第一个记录交换。2)从第二个记录开始的 n-1 个记录中再选出关键码最小的记录与第二个交换。以此类推。

对冒泡排序常见的改进方式是加入一个标志变量 exchange,用于标志某一趟排序过程中是否有数据交换,若进行某一趟排序时并没有数据交换,则说明数据已经按要求排好,可立即结束排序,避免不必要的比较过程。

不受初始元素影响的排序:选择,堆,归并。

应用服务器:1)Tomcat:应用十分广泛的 web 服务器,支持部分 j2ee,免费,支持 servlet、

jsp。2)JBoss:开源应用服务器。3)Weblogic、webSphere:业界第一的 appserver。4)Apache: 全球应用最广泛的 http 服务器,但只支持静态网页,比如 asp、PHP 等动态网页不支持。

Memcached:是高性能分布式内存缓存服务器,本质是一个内存 key-value 数据库,但不支持数据持久化,服务器关闭后,数据全丢失。只支持 key-value 结构。

Redis:将大部分数据放在内存中,支持的数据类型有:字符串、hash 表、链表、集合、有序集合以及基于这些数据类型的相关操作。Redis 内部使用一个 redisobject 对象来表示 all

key 和 value。

他们的区别:1)redis 中并不是 all 数据都一直存储在内存中,这是和 memcached 相比一个最大的区别。2)redis 不仅仅支持简单的 key-value 类型的数据,同时还支持字符串、hash 表、链表、集合、有序集合。3)redis 支持数据备份,即 master-slave 模式的备份。4)redis 支持数据的持久化,可以将内存中的数据保存在磁盘上,重启的时候可以再次加载进内存使用。Memcached 服务器关闭后,数据丢失。5)memcached 挂掉后,数据不可以恢复,redis 数据丢失后可通过 AOF 恢复(灾难恢复)。

Mysql 使用 B+树。

索引:1)可以避免全表扫描。2)若一个表没有聚集索引,则这样表中的数据没有特定的顺序,all 新行将被添加到表的末尾。3)聚集索引是一种稀疏索引,数据页上一页的索引

页存储的是页指针,不是行指针。对于非聚集索引,则是密集索引,数据页的上一级索引为为每一个每一个数据行存储一条索引记录。

定义一个对象的过程:加载类,分配对象大小,默认初始化(方法),把对象按程序员的意愿进行初始化。

CAS:Compare and swap

Java.util.concurrent 包完全建立在 cas 上,没有 cas 就不会有此包。Cas 有 3 种操作数: 内存值 V,旧的预期值 A 和要修改的新值 B。当且仅当预期值 A 和内存值 V 相同时,将内存值 V 改为 B,返回 V。

CAS 通过调用 JNI 的代码实现。

Java 虚拟机里的对象由 3 部分组成:1)对象头:标记字段(在 32 位和 64 位 jvm 中分别为 32bits 和 64bits)+类型指针 2)实例数据 3)对齐填充。

若对象是一个 java 数组,则对象头中还必须有一块用于记录数组长度的数据。因为虚拟机可以通过普通 java 对象的元数据信息确定 java 对象的大小,但是从数组的元数据中无法确定数组的大小。

对象大小必须是 8 字节的整数倍。

Xms:堆初始大小。Xmx:堆最大大小。

内存溢出:程序在申请内存时,没有足够的空间供其使用。内存泄漏:分配出的内存不再使用,但无法回收。

Xss:设置栈的大小。

标记-清除:首先标记出 all 需要回收的对象,在标记完成后统一回收 all 被标记的对象。

Cms:并发收集、低停顿。

Gc 进行时必须停顿 all java 执行线程(stop-the-world),即使在号称几乎不会发生停顿的 cms 收集器中,枚举根节点时必须停顿。(因为不可以发生在分析过程中对象引用关系还在不断变化)。

在大多数情况下,对象在新生代 eden 区中分配,当 eden 区域没有足够的内存空间时, 虚拟机发起一次 minor gc。

Xmn:分给新生代的大小。

虚拟机给每一个对象定义一个对象年龄计数器,若对象在 eden 出生并经过第一次 minor gc 后仍然存活,并且能被 survivor 容纳的话,将被移到 survivor 空间中,并且对象年龄设为

1.对象在 survivor 中每熬过一次 minor gc,年龄就+1,当他年龄达到一定程度(默认为 15), 就会晋升到老年代。

虚拟机可以从方法表中的 acc_synchronized 访问标志得知一个方法是否为同步方法。当方法调用时,调用指令将会检查方法的 acc_synchronized 访问标志是否被设置了。若被设置了,执行线程就要求先成功持有 moniter,然后才能执行方法,最后当方法完成(无论是正

常完成还是非正常完成)时释放 moniter。在方法执行期间,执行线程持有了 moniter,其他任何线程都无法再获取到同一个 moniter,若一个同步方法执行期间抛出了异常,并且在方法内部无法处理此异常,那么这个方法所持有的 moniter 将在异常执行到同步方法之外时自动释放。

加载、验证、准备、初始化卸载这 5 个阶段的顺序是确定的。

到初始化的阶段才真正开始执行类中定义的 Java 程序代码(或者说是字节码)。初始化阶段是执行类的构造器(())方法的过程。

()方法是由编译器自动收集类的 all 类变量的赋值动作和静态语句块(static{}块) 中的语句合并产生的。编译器收集的顺序是由语义在源文件中出现的顺序决定的。

()方法与类的构造函数不同,他不需要显示的调用父类的构造器,虚拟机会保证在子类的()方法执行之前,父类的()方法已经执行完毕。因此在虚拟机中第一 个被执行的()方法的类肯定是 java.lang.object.

由于父类的()方法先执行,也就意味着父类中的静态语句块要优于子类的变量赋值。

接口中,不能有静态代码块。

虚拟机会保证一个类的()方法在多线程环境中被正确的加锁同步。若多个线程同时去初始化一个类,那么只会有一个线程去执行这个类的()方法,其他线程都要阻塞等待,直到活动线程执行()完毕。

若自己编写一个类 java.lang.Object 并放在 classpath 下,可以正常编译,但永远无法被加载。

若有一个类加载请求,先检查是否已经被加载,若没有被加载,则调用父类加载器的

loadclass 方法,若父类加载器为空,则默认使用启动类加载器作为父类加载器,若父类加载失败,抛出 classNotFoundException,再调用自己的 findclass 方法进行加载。

语法糖,在计算机语言中添加的某种语法。这种语法对语言的功能并没有影响,但是更方便程序员使用。java 中最常用的语法糖有:泛型、变长参数,自动拆箱/装箱。虚拟机运行时不支持这些语法,他们在编译阶段还原回简单的基础语法结构,这个过程为解语法糖。包装类的“==”运算,在不遇到算术运算的情况下,不会自动拆箱,遇到了就自动拆箱。

java 中的运算并非原子操作,导致 volatile 变量的运算在并发下并不一定安全。

只有使用 invokespecial 指令调用的私有方法,实例构造器,父类方法,以及 invokestatic 指令调用的静态方法,才是在编译期进行解析的,除了上述四种方法,其他 java 方法调用都需要在运行期进行方法接收者的多态选择,并且很有可能存在多于一个版本的方法接收者

(最多再除去被 final 修饰的方法,尽管它用 invokevirtual 调用,但也是非虚方法),java 中默认的实例方法是虚方法。

每次使用 use volatile 变量之前,都必须先从主内存刷新最新的值,用于保证能看见其他线程对该变量所做的修改后的值。在工作内存中,每次修改 volatile 变量后,都必须立刻同步回主内存中,用于保证其他线程可以看到自己对该变量所做的修改。volatile 修饰的变

量不会被指令重排序优化,保证代码的执行顺序与程序顺序相同。原子性、可见性、有序性。

Happen-before:对于一个 volatile 变量的写操作,先行发生于后面对这个变量的读操作。

各个线程可以共享进程资源(内存地址、文件 Io),也可以独立调度。线程是 CPU 调度的基本单位。

synchronized 关键字经过编译后,会在同步块的前后分别形成moniterenter 和mointerexit 这两个字节码指令,这两个字节码指令都需要一个 reference 类型的参数来指明要锁定和解锁的对象。若 java 程序中的 synchronized 明确指定了对象参数,那就是这个对象的 reference; 若没有明确指定,那就根据 synchronized 修饰的是实例方法或类方法去取对应的对象实例或

class 对象来作为搜索对象。

在执行 moniterenter 指令时,首先要尝试获取对象的锁,若这个对象没被锁定,或当前线程已经拥有那个对象的锁,把锁的计数器加一。相应的,在执行 mointerexit 指令时,会将计数器-1,当计数器为零时,锁被释放。若获取对象锁失败,则当前线程就要阻塞等待, 直到对象锁被另外一个线程释放。

对于线程的阻塞或唤醒,需要用户态和核心态切换,状态切换需要很多处理器时间。

类只可以是 public 或包访问权限的。组合:只需要将对象引用置入新类中。

字符串与任意数据类型相连接都用“+”,最终都变为字符串。S.O.P(a);调用的是 a 的 toString 方法。“source”+aa;由于只能将一个 String 对象与另一个 String 对象相加,所以编译器会调用 aa 的 toString 方法,将 aa 转为 string 对象。对于空指针 Person p=null;S.O.P(p);不会报 null 指针异常,而是直接打印 null;

Arrays.sort 对于基本数据类型用快速排序,对象数组用改进的归并排序。

通常加载发生于创建的第一个对象之时,但是当访问 static 变量或 static 方法时也会发生加载。

初次使用之处也是 static 初始化发生之处,所有 static 对象和 static 代码都会在加载时按程序中的顺序(定义类时的书写顺序)而依次初始化。当然,定义为 static 的东西只会被初始化一次。

private 不能被继承。

将一个方法调用同一个方法主体关联起来叫做绑定。java 中除了 static 方法和 final 方法, 其他所有方法都是动态绑定。

private 方法默认为 final 的,private 方法不能被覆盖。

初始化顺序:1)成员变量默认初始化 2)调用基类的构造器,一层一层调用 3)按声明顺序调用成员的初始化方法 4)调用子类构造器主体。

Collection.sort(list list) 按自然顺序排序,元素必须有可比较性,实现 comparable 接口。

Collection.sort(list list,comparator)

Listiterator 是一个更强大的 iterator 子类型,只能用于各种 list 访问。

Tree-set 将元素放在红黑树。

Linkedlist 是 queue 的实现。

PriorityQueue:优先级越高,越在头,越早出来。0-1 背包问题 自己网上去看,一定要懂。

对于一个给定的问题,若具有以下两条性质,可用动态规划 1)最优子结构:若一个问题的最优解包含了其子问题的最优解,就说明该问题具有最优子结构。2)重叠子问题。

一致性 hash:在移除 or 添加一个 cache 时,他能够尽可能小的改变已经存在 key 映射关系。

Hash 冲突解决办法:1)链地址法 2)开发地址法。

字节是一种计量单位,表示数据量多少,他是计算机信息技术用于计量存储容量的一种单位。字符:计算机中使用的文字和符号。不用编码里,字符和字节对应关系不同。1)ASCII 中,一个英文字母(不论大小写)占一个字节,一个汉字占 2 个字节。2)UTF-8,一个英文

1 个字节,一个中文,3 个字节。3)unicode ,中文、英文都是两个字节。

验证证书是否有效。1)验证证书是否在有效期内:证书中会包含证书的有效期的起始时间和结束时间。2)验证证书是否被吊销。被吊销的证书是无效的,验证吊销有两种:CRL 和 OCSP。CRL:吊销证书列表,证书被吊销后会被记录在 CRL 中,CA 定期发布 CRL,应用程序可以依靠 CRL 来验证证书是否被吊销。有两个缺点:1)可能会很大,下载很麻烦。2)有滞后性,就算证书被吊销了,应用也只能等到发布最新的 CRL 才能知道。OCSP:在线证书状态检查协议,应用按照标准发送一个请求,对某张证书进行查询,之后服务器返回证书状态。OCSP 可认为是即时的(实际实现中可能有延迟)。3)验证证书上是否有上级 CA 签名: 每一级证书都是由上一级 CA 证书签发的,上级 CA 证书还可能有上级,最后会找到根证书, 根证书即自签证书,自己签自己。以上三点只要有一个没通过,这张证书就是无效的,不该信任。

Java 的包有:http://java.io;http://java.net;java.lang;java.util;java.awt;java.sql;javax.swing;java.math

(javax 开头的都是扩展包)。

Mysql 分页查询:客户端通过传递 start(页码),limit(每页显示的条数)两个参数去分页查询数据库中的数据。 Limit m,n 从 m+1 条开始,取 n 条。1)查询第一条到第十条的是:select * from table limit 0,10;对应的就是第一页的数据。2)查询第 10 条到第 20 条的是:select * from table limit 10,10;对应的就是第二页的数据。3)查询第 20 条到第 30 条的是: select * from table limit 20,10;对应的就是第三页的数据。总结:select * from table limit (页数-1)*每页条数, 每页条数;

动态代理类的字节码在程序运行时由 java 反射机制形成。cglib 动态代理原理是生成被代理类的子类,并覆盖其中方法进行增强。

系统架构师是一个既需要控制整体,又需要洞悉局部瓶颈,并依据具体的应用场景给出解决方案的人。确定和评估需求,给出开发规范。在需求阶段,负责理解和管理非功能性需求(可维护性、可复用性),还需要确定客户及市场人员所提出的需求,确定开发团队所提出的设计。在软件设计阶段,负责对软件体系结构关键构件、接口。在编码阶段,详细设计者和编码者的顾问,并且经常举行技术研讨会。随着集成和测试,集成和测试支持将成为软件架构师的工作重点。

二分查找返回的是:-(插入点)-1

Myisam:不支持事务行级锁和外键约束。所以当执行 insert 和 update 时,执行写操作时,要锁定整个表,所以效率低。但是它保存了表的行数,执行 select count(*) from table 时,不需要全表扫描,而是直接读取保存的值。所以若读操作远远多于写操作,并且不需要事务,myisam 是首选。

Innodb:支持事务、行级锁和外键,mysql 运行时,Innodb 会在内存中建立缓冲池,用于缓冲数据和索引。不保存表的行数,执行 select count(*) from table 时要全表扫描。写不锁定全表,高效并发时效率高。

短链接一般只会在 client/server 间传递一次读写操作。

Tcp 保活功能,主要为服务器应用程序提供,服务器应用程序需要知道客户主机是否崩溃,从而可以代表客户使用资源.

如果一个给定的连接,在两个小时内没有任何的动作,则服务器就向客户发送一个探测报文段,客户主机必须处于以下四个状态之一:1)客户主机依然正常运行,并从服务器可达,客户的 tcp 响应正常,而服务器也知道对方是正常的,服务器在两个小时后将保活定时器复位。2)客户主机已经崩溃,并且关闭或正在重新启动。在任何一种情况下,客户的 tcp 都没有响应,服务器不能收到对探测的响应,并在 75s 后超时,服务器总共发送 10 个这样的探测,每个间隔 75s。若服务器没有收到一个响应,他就认为客户主机已经关闭,并终止连接。3)客户主机崩溃并已重新启动,服务器将收到一个对其保活探测的响应,这个效应是一个复位,使得服务器终止这个链接。4)客户机正常运行,但服务器不可达,和 2)类似。

tcp 保护功能是探测长连接存活状况。

Tcp 的 keep-alive 是检查当前 tcp 是否还活着;http 的 keep-alive 是让一个 tcp 连接活多久,他们是不同层次的概念。

满二叉树:除叶子结点外的所有节点均有两个子节点,叶子节点数达到最大,所有叶子节点都在同一层;完全二叉树:满是完全的特例;哈夫曼树:每个节点要么没子节点点,要么有两个子节点。

链表倒数第 k 个节点,见《剑指 offer》108 页。

建造者模式,对于一个复杂产品,建造的过程一样,但过程可以有不同的表示。

可重入锁:同一线程,外层函数获得锁所之后,内层递归函数仍有获得该锁的机会但不受影响。Synchronized 和 reentrantlock 都是可重入锁。

如何快速查找链表的中间节点?只遍历一次。答案:建立两个指针,一个指针一次遍历两个节点,一个指针一次遍历一个节点,当快指针遍历到控节点时,慢指针指向的位置为链表中间位置。

Hashmap 的实现用 hash 表,tree-map 采用红黑树。

Collections 类中提供了一个方法,返回一个同步版本的 hashmap,该方法返回的是一个synchronized Map 的实例,synchronized Map 类是定义在 Collections 中的一个静态内部类。它实现 map 接口,并对其中的每一个方法通过 synchronized 关键字进行了同步控制。但synchronized Map 在使用过程中不一定线程安全。

一、Runnable 的 run 方法没有返回值,并且不可以抛出异常;callable 的 call 方法有返回值,并且可以抛出异常,且返回值可以被 future 接收。

Callable c=new Callable(){ Public Integer call() throws Exception{

Return new Random().nextInt(100);

}}

Future 是一个接口。Futuretask 是他的一个实现类,可通过 futuretask 的 get 方法得到返回值。

Futuretask future =new Futuretask< Integer >( callable); New Thread(future).start();

二、使用 ExecutorService 的 submit 方法执行 callable;

ExecutorService threadpoll=Executors.new SingleThreadExecutor(); Futuretask future= threadpoll.submit(new callable(){

Public Integer call() throws Exception{ Return new Random().nextInt(100);

}

})

Future 对象表示异步计算的结果,他提供了检查计算是否完成的方法,以等待计算的完成,并获取计算的结果。计算完成后只能使用 get 方法来获取结果,若没有执行完,get 方法可能会阻塞,直到线程执行完。取消由 cancel 方法执行。isDone 确定任务是正常执行还是被取消。一旦计算完成,就不能再取消了。

共享锁 VS 排他锁:1)共享锁,又称读锁,若事务 T 对数据对象 A 加了 S 锁,则是事务 T 可以读 A 但不能修改 A,其它事务只能再对他加 S 锁,而不能加 X 锁,直到 T 释放 A 上的 S 锁。这保证了其他事务可以读 A,但在事务 T 释放 S 锁之前,不能对 A 做任何操作。2) 排他锁,又称写锁,若事务 T 对数据对象加 X 锁,事务 T 可以读 A 也可以修改 A,其他事务不能对 A 加任何锁,直到 T 释放 A 上的锁。这保证了,其他事务在 T 释放 A 上的锁之前不能再读取和修改 A。

DNS 即使用 tcp,又使用 udp。

a 发送给 b 一个信息,但是 a 不承认他发送了,防止这个可用数字签名。

Spring1)ioc 容器——BeanFactory 是最原始的 ioc 容器,有以下方法 1.getBean2.判断是否有 Bean,containsBean3.判断是否单例 isSingleton。BeanFactory 只是对 ioc 容器最基本行为作了定义,而不关心 Bean 是怎样定义和加载的。如果我们想要知道一个工厂具体产生对象的过程,则要看这个接口的实现类。在 spring 中实现这个接口有很多类,其中一个

xmlBeanFactory。xmlBeanFactory 的功能是建立在 DefaultListablexmlBeanFactory 这个基本容器的基础上的, 并在这个基本容器的基础上实行了其他诸如 xml 读取的附加功能。xmlBeanFactory(Resource resource)构造函数,resource 是 spring 中对与外部资源的抽象, 最常见的是文件的抽象,特别是 xml 文件,而且 resource 里面通常是保存了 spring 使用者的

Bean 定义,eg.applicationContext.xml 在被加载时,就会被抽象为 resource 处理[我自己理解,

resource 就是定义 Bean 的 xml 文件]。

loc 容器建立过程:1)创建 ioc 配置文件的抽象资源,这个抽象资源包含了 BeanDefinition的定义信息。2)创建一个 BeanFactory,这里使用的是 DefaultListablexmlBeanFactory。3) 创建一个载入 BeanDefinition 的读取器,这里使用 xmlBeanDefinitionReader 来载入 xml 文件形式的 BeanDefinition。4)然后将上面定义好的 resource 通过一个回调配置给 BeanFactory。

5)从资源里读入配置信息,具体解析过程由 xmlBeanDefinitionReader 完成。6)ioc 容器建立起来。

BeanDefinition 类似于 resource 接口的功能,起到的作用就是对所有的 Bean 进行一层抽象的统一,把形式各样的对象统一封装为一个便于 spring 内部进行协调管理和调度的数据结构。BeanDefinition 屏蔽了不同对象对于 spring 框架的差异。

Resource 里有 inputStrea。

解析 xml,获得 document 对象,接下来只要再对 document 结构进行分析便可知道 Bean

在 xml 中是怎么定义的,也就可以将其转化为 BeanDefinition 对象。我们配置的 Bean 的信息经过解析,在 spring 内部已经转换为 BeanDefinition 这种统一的结构,但这些数据还不能供 ioc 容器直接使用,需要在 ioc 容器中对这些 BeanDefinition 数据进行注册,注册完成的 BeanDefinition,都会以 BeanName 为 Key,BeanDefinition 为 value,交由 map 管理。注册完之后,一个 ioc 容器就可以用了。

自己理解的,xml 文件抽象为 resource 对象,Bean 抽象为 BeanDefinition 对象。

2)依赖注入——依赖注入发生在 getBean 方法中,getBean 又调用 dogetBean 方法。

getBean 是依赖注入的起点,之后调用 createBean 方法,创建过程又委托给了 docreateBean

方法。在 docreateBean 中有两个方法:1)createBeanInstance,生成 Bean 包含的 java 对象

2)populateBean 完成注入。在创建 Bean 的实例中,getInstantiationstrategy 方法挺重要,该方法作用是获得实例化策略对象,也就是指通过哪种方案进行实例化的过程。spring 当中提供两种方案进行实例化:BeanUtils 和 cglib。BeanUtils 实现机制是 java 反射,cglib 是一个第三方类库,采用的是一种字节码加强方式。Spring 中默认实例化策略为 cglib。populateBean 进行依赖注入,获得 BeanDefinition 中设置的 property 信息,简单理解依赖注入的过程就是对这些 property 进行赋值的过程,在配置 Bean 的属性时,属性可能有多种类型,我们在进行注入的时候,不同类型的属性我们不能一概而论地进行处理。集合类型属性和非集合类型属性差别很大,对不同的类型应该有不同的处理过程。所以要先判断 value 类型,再调用具体方法。

3)aop——将那些与业务无关,却为业务模块所公共调用的逻辑或责任封装起来,称其

为 aspect,便于减少系统的重复代码。使用模块技术,aop 把软件系统分为两个部分:核心关注点和横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。实现 aop 的两大技术:1)采用动态代理,利用截取消息的方式,对该消息进行装饰, 以获取原有对象行为的执行。2)采用静态织入,引入特定的语法创建切面,从而可以使编译器可在编译期间织入有关切面的代码。

spring 提供两种方式生成代理对象,jdkProxy 和 cglib。默认的策略是,若目标类是接口则用 jdk 动态代理技术,否则使用 cglib 生成代理。在 jdk 动态代理中使用

Proxy.newProxyInstance() 生成代理对象( JdkDynamicAopProxy 类的 getProxy 方法) ,

JdkDynamicAopProxy 也实现了 invocationhandler 接口,有 invoke 方法,就是在该方法中实现了切片织入。主流程可以简述为:获取可应用到此方法上的通知链(Interceptor chain),若有,则应用通知,并执行 joinpoint,若没有,则直接反射执行 joinpoint。

Introduction 是指给一个已有类添加方法或字段属性,Introduction 还可以在不改变现有类代码的情况下,让现有 java 类实现新的接口,或为其指定一个父类实现多继承,相对于

advice 可以动态改变程序的功能或流程来说,Introduction 用来改变类的静态结构。

拦截器,是对连接点进行拦截,从而在连接点前后加入自定义的切面模块功能。作用于同一个连接点的多个拦截器组成一个拦截器链,拦截器链上的每一个拦截器,通常会调用下一个拦截器。

连接点,程序执行过程中的行为,比如方法调用或特定异常被抛出。

切入点,指定一个 advice 将被引发的一系列的连接点的集合。aop 框架必须允许开发者指定切入点。

通知(advice):在特定的连接点,aop 框架执行的动作。Spring 以拦截器作通知模型, 维护一个围绕连接点的拦截器链。

拦截器(advisor),自己理解,在 invoke 前后加入的方法就是通知。使用 spring 的

PointCutadvisor,只拦截特定的方法,一个 advisor 定义订一个 PointCut 和一个 advice,满足PointCut(指定哪些方面需要拦截),则执行相应的 advice (定义了增强的功能) 。

PointCutadvisor 有 两 个 常 用 实 现 类 : NameMatchMethodPointCutadvisor 和

regexMethodPointCutadvisor。前者需要注入 mappedname 和 advice 属性,后者需要注入

pattern 和 advice 属性。mappedname 指明要拦截的方法名,pattern 按照正则表达式的方法指明了要拦截的方法名, advice 定义一个增强, 即要加入的操作( 需要自己实现

MethodBeforeAdvice、MethodafterAdvice、throwAdvice、Methodinterceptor 接口之一),然后在 ProxyBeanFactory 的拦截器中注入这个 PointCutadvisor。注:一个 ProxyFactoryBean 只能指定一个代理目标。

在 spring 中 配 置 aop 很 麻 烦 , 首 先 需 要 编 写 xxxadvice 类 ( 需 要 自 己 实 现MethodBeforeAdvice、MethodafterAdvice、throwAdvice、Methodinterceptor 接口之一),然后在 xml 配置 advisor。还要在 advisor 中注入 advice,然后将 advisor 加入 ProxyFactoryBean 中。而在 spring2.x 以后引入了 aspect J 注解,只需要定义一个 aspect 类,在 aspect 中声明

advice 类(可同时声明多个),然后在 xml 配置这个 aspect 类,最后添加一行

proxy>就可以搞定。

通知类型

接口

描述

前置通知

MethodBeforeAdvice

在目标方法调用前调用

后置通知

MethodafterAdvice

在目标方法调用后调用

异常通知

throwAdvice

在目标方法抛出异常时调用

环绕通知

Methodinterceptor

拦截对目标方法调用

还有一类是引入通知,用来定义切入点的。

你可能感兴趣的:(java,platform,se,binary已停止工作)