① 这两个方法来自不一样的类分别是,sleep来自Thread类,和wait来自Object类。 sleep是Thread的静态类方法,谁调用的谁去睡觉,即便在a线程里调用b的sleep方法,实际上仍是a去睡觉,要让b线程睡觉要在b的代码中调用sleep。 ② 锁: 最主要是sleep方法没有释放锁,而wait方法释放了锁,使得其余线程能够使用同步控制块或者方法。 sleep不出让系统资源;wait是进入线程等待池等待,出让系统资源,其余线程能够占用CPU。通常wait不会加时间限制,由于若是wait线程的运行资源不够,再出来也没用,要等待其余线程调用notify/notifyAll唤醒等待池中的全部线程,才会进入就绪队列等待OS分配系统资源。sleep(milliseconds)能够用时间指定使它自动唤醒过来,若是时间不到只能调用interrupt()强行打断。 Thread.sleep(0)的做用是“触发操做系统马上从新进行一次CPU竞争”。 ③ 使用范围:wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用,而sleep能够在任何地方使用。 synchronized(x){ x.notify() //或者wait() }
下图单位是bit,非字节 1B=8bit javascript
不能够,由于String类有final修饰符,而final修饰的类是不能被继承的,实现细节不容许改变。日常咱们定义的String str=”a”;其实和String str=new String(“a”)仍是有差别的。 css
前者默认调用的是String.valueOf来返回String实例对象,至于调用哪一个则取决于你的赋值,好比String num=1,调用的是 html
public static String valueOf(int i) { 前端
return Integer.toString(i); java
} mysql
后者则是调用以下部分: react
public String(String original) { jquery
this.value = original.value; linux
this.hash = original.hash; nginx
}
最后咱们的变量都存储在一个char数组中
private final char value[];
String 字符串常量(final修饰,不可被继承),String是常量,当建立以后即不能更改。(能够经过StringBuffer和StringBuilder建立String对象(经常使用的两个字符串操做类)。)
StringBuffer 字符串变量(线程安全),其也是final类别的,不容许被继承,其中的绝大多数方法都进行了同步处理,包括经常使用的Append方法也作了同步处理(synchronized修饰)。其自jdk1.0起就已经出现。其toString方法会进行对象缓存,以减小元素复制开销。
public synchronized String toString() {
if (toStringCache == null) {
toStringCache = Arrays.copyOfRange(value, 0, count);
}
return new String(toStringCache, true);
}
StringBuilder 字符串变量(非线程安全)其自jdk1.5起开始出现。与StringBuffer同样都继承和实现了一样的接口和类,方法除了没使用synch修饰之外基本一致,不一样之处在于最后toString的时候,会直接返回一个新对象。
public String toString() {
// Create a copy, don’t share the array
return new String(value, 0, count);
}
ArrayList和LinkedList都实现了List接口,有如下的不一样点:
一、ArrayList是基于索引的数据接口,它的底层是数组。它能够以O(1)时间复杂度对元素进行随机访问。与此对应,LinkedList是以元素列表的形式存储它的数据,每个元素都和它的前一个和后一个元素连接在一块儿,在这种状况下,查找某个元素的时间复杂度是O(n)。
二、相对于ArrayList,LinkedList的插入,添加,删除操做速度更快,由于当元素被添加到集合任意位置的时候,不须要像数组那样从新计算大小或者是更新索引。
三、LinkedList比ArrayList更占内存,由于LinkedList为每个节点存储了两个引用,一个指向前一个元素,一个指向下一个元素。
父类静态代变量--->父类静态代码块--->子类静态变量--->子类静态代码块---->父类非静态变量(父类实例成员变量)--->
父类构造函数--->子类非静态变量(子类实例成员变量)--->子类构造函数。
测试demo:http://blog.csdn.net/u014042066/article/details/77574956
参阅个人博客《深刻理解类加载》:http://blog.csdn.net/u014042066/article/details/77394480
hashMap是线程不安全的,HashMap是数组+链表+红黑树(JDK1.8增长了红黑树部分)实现的,采用哈希表来存储的,
参照该连接:https://zhuanlan.zhihu.com/p/21673805
JAVA8 的 ConcurrentHashMap 为何放弃了分段锁,有什么问题吗,若是你来设计,你如何设计。
参照:https://yq.aliyun.com/articles/36781
TreeMap和LinkedHashMap是有序的(TreeMap默认升序,LinkedHashMap则记录了插入顺序)。
参照:http://uule.iteye.com/blog/1522291
一、抽象类和接口都不能直接实例化,若是要实例化,抽象类变量必须指向实现全部抽象方法的子类对象,接口变量必须指向实现全部接口方法的类对象。
二、抽象类要被子类继承,接口要被类实现。
三、接口只能作方法申明,抽象类中能够作方法申明,也能够作方法实现
四、接口里定义的变量只能是公共的静态的常量,抽象类中的变量是普通变量。
五、抽象类里的抽象方法必须所有被子类所实现,若是子类不能所有实现父类抽象方法,那么该子类只能是抽象类。一样,一个实现接口的时候,如不能所有实现接口方法,那么该类也只能为抽象类。
六、抽象方法只能申明,不能实现。abstract void abc();不能写成abstract void abc(){}。
七、抽象类里能够没有抽象方法
八、若是一个类里有抽象方法,那么这个类只能是抽象类
九、抽象方法要被实现,因此不能是静态的,也不能是私有的。
十、接口可继承接口,并可多继承接口,但类只能单根继承。
继承指的是一个类(称为子类、子接口)继承另外的一个类(称为父类、父接口)的功能,并能够增长它本身的新功能的能力,继承是类与类或者接口与接口之间最多见的关系;在Java中此类关系经过关键字extends明确标识,在设计时通常没有争议性;
聚合是关联关系的一种特例,他体现的是总体与部分、拥有的关系,即has-a的关系,此时总体与部分之间是可分离的,他们能够具备各自的生命周期,部分能够属于多个总体对象,也能够为多个总体对象共享;好比计算机与CPU、公司与员工的关系等;表如今代码层面,和关联关系是一致的,只能从语义级别来区分;
参考:http://www.cnblogs.com/jiqing9006/p/5915023.html
IO是面向流的,NIO是面向缓冲区的
参考:https://zhuanlan.zhihu.com/p/23488863
http://developer.51cto.com/art/201103/252367.htm
http://www.jianshu.com/p/3f703d3d804c
参照:http://www.jianshu.com/p/3ea4a6b57f87?amp
http://blog.csdn.net/yongjian1092/article/details/7364451
反射中,Class.forName 和 ClassLoader 区别。
https://my.oschina.net/gpzhang/blog/486743
描述动态代理的几种实现方式,分别说出相应的优缺点。
Jdk cglib jdk底层是利用反射机制,须要基于接口方式,这是因为
Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(), this);
Cglib则是基于asm框架,实现了无反射机制进行代理,利用空间来换取了时间,代理效率高于jdk
http://lrd.ele.me/2017/01/09/dynamic_proxy/
动态代理与 cglib 实现的区别
同上(基于invocationHandler和methodInterceptor)
为何 CGlib 方式能够对接口实现代理。
同上
final 的用途
类、变量、方法
http://www.importnew.com/7553.html
写出三种单例模式实现。
懒汉式单例,饿汉式单例,双重检查等
参考:https://my.oschina.net/dyyweb/blog/609021
同时复写hashcode和equals方法,优点能够添加自定义逻辑,且没必要调用超类的实现。
参照:http://java-min.iteye.com/blog/1416727
访问修饰符,主要标示修饰块的做用域,方便隔离防御
同一个类 同一个包 不一样包的子类 不一样包的非子类
Private √
Default √ √
Protected √ √ √
Public √ √ √ √
public: Java语言中访问限制最宽的修饰符,通常称之为“公共的”。被其修饰的类、属性以及方法不
仅能够跨类访问,并且容许跨包(package)访问。
private: Java语言中对访问权限限制的最窄的修饰符,通常称之为“私有的”。被其修饰的类、属性以
及方法只能被该类的对象访问,其子类不能访问,更不能容许跨包访问。
protect: 介于public 和 private 之间的一种访问修饰符,通常称之为“保护形”。被其修饰的类、
属性以及方法只能被类自己的方法及子类访问,即便子类在不一样的包中也能够访问。
default:即不加任何访问修饰符,一般称为“默认访问模式“。该模式下,只容许在同一个包中进行访
问。
http://www.oschina.net/translate/java-copy-shallow-vs-deep-in-which-you-will-swim
数组和链表数据结构描述,各自的时间复杂度
http://blog.csdn.net/snow_wu/article/details/53172721
error 和 exception 的区别,CheckedException,RuntimeException 的区别
http://blog.csdn.net/woshixuye/article/details/8230407
请列出 5 个运行时异常。
同上
类加载无须等到“首次使用该类”时加载,jvm容许预加载某些类。。。。
http://www.cnblogs.com/jasonstorm/p/5663864.html
参考上边试题
泛型的本质是参数化类型,也就是说所操做的数据类型被指定为一个参数,泛型的好处是在编译的时候检查类型安全,而且全部的强制转换都是自动和隐式的,以提升代码的重用率
http://baike.baidu.com/item/java%E6%B3%9B%E5%9E%8B
hashcode
hashcode()方法提供了对象的hashCode值,是一个native方法,返回的默认值与System.identityHashCode(obj)一致。
一般这个值是对象头部的一部分二进制位组成的数字,具备必定的标识对象的意义存在,但毫不定于地址。
做用是:用一个数字来标识对象。好比在HashMap、HashSet等相似的集合类中,若是用某个对象自己做为Key,即要基于这个对象实现Hash的写入和查找,那么对象自己如何实现这个呢?就是基于hashcode这样一个数字来完成的,只有数字才能完成计算和对比操做。
hashcode是否惟一
hashcode只能说是标识对象,在hash算法中能够将对象相对离散开,这样就能够在查找数据的时候根据这个key快速缩小数据的范围,但hashcode不必定是惟一的,因此hash算法中定位到具体的链表后,须要循环链表,而后经过equals方法来对比Key是不是同样的。
equals与hashcode的关系
equals相等两个对象,则hashcode必定要相等。可是hashcode相等的两个对象不必定equals相等。
https://segmentfault.com/a/1190000004520827
有
底层是基于hashmap实现的
http://wiki.jikexueyuan.com/project/java-collection/hashset.html
什么是序列化,怎么序列化,为何序列化,反序列化会遇到什么问题,如何解决。
http://www.importnew.com/17964.html
① 历史缘由: Hashtable是给予陈旧的Dictonary类的, HashMap是Java1.2引进的Map接口的一个实现 ② HashMap容许空的键值对, 而HashTable不容许 ③ HashTable同步,而HashMap非同步,效率上比HashTable要高
类似点:
这两种同步方式有不少类似之处,它们都是加锁方式同步,并且都是阻塞式的同步,也就是说当若是一个线程得到了对象锁,进入了同步块,其余访问该同步块的线程都必须阻塞在同步块外面等待,而进行线程阻塞和唤醒的代价是比较高的(操做系统须要在用户态与内核态之间来回切换,代价很高,不过能够经过对锁优化进行改善)。
区别:
这两种方式最大区别就是对于Synchronized来讲,它是java语言的关键字,是原生语法层面的互斥,须要jvm实现。而ReentrantLock它是JDK 1.5以后提供的API层面的互斥锁,须要lock()和unlock()方法配合try/finally语句块来完成。
Synchronized进过编译,会在同步块的先后分别造成monitorenter和monitorexit这个两个字节码指令。在执行monitorenter指令时,首先要尝试获取对象锁。若是这个对象没被锁定,或者当前线程已经拥有了那个对象锁,把锁的计算器加1,相应的,在执行monitorexit指令时会将锁计算器就减1,当计算器为0时,锁就被释放了。若是获取对象锁失败,那当前线程就要阻塞,直到对象锁被另外一个线程释放为止。
因为ReentrantLock是java.util.concurrent包下提供的一套互斥锁,相比Synchronized,ReentrantLock类提供了一些高级功能,主要有如下3项:
1.等待可中断,持有锁的线程长期不释放的时候,正在等待的线程能够选择放弃等待,这至关于Synchronized来讲能够避免出现死锁的状况。
2.公平锁,多个线程等待同一个锁时,必须按照申请锁的时间顺序得到锁,Synchronized锁非公平锁,ReentrantLock默认的构造函数是建立的非公平锁,能够经过参数true设为公平锁,但公平锁表现的性能不是很好。
3.锁绑定多个条件,一个ReentrantLock对象能够同时绑定对个对象。
什么状况下会发生栈内存溢出。
若是线程请求的栈深度大于虚拟机所容许的深度,将抛出StackOverflowError异常。 若是虚拟机在动态扩展栈时没法申请到足够的内存空间,则抛出OutOfMemoryError异常。
参照:http://wiki.jikexueyuan.com/project/java-vm/storage.html
JVM 的内存结构,Eden 和 Survivor 比例。
eden 和 survior 是按8比1分配的
http://blog.csdn.net/lojze_ly/article/details/49456255
jvm 中一次完整的 GC 流程是怎样的,对象如何晋升到老年代,说说你知道的几种主要的jvm 参数。
对象诞生即新生代->eden,在进行minor gc过程当中,若是依旧存活,移动到from,变成Survivor,进行标记代数,如此检查必定次数后,晋升为老年代,
http://www.cnblogs.com/redcreen/archive/2011/05/04/2037056.html
http://ifeve.com/useful-jvm-flags/
https://wangkang007.gitbooks.io/jvm/content/jvmcan_shu_xiang_jie.html
你知道哪几种垃圾收集器,各自的优缺点,重点讲下 cms,包括原理,流程,优缺点
Serial、parNew、ParallelScavenge、SerialOld、ParallelOld、CMS、G1
https://wangkang007.gitbooks.io/jvm/content/chapter1.html
垃圾回收算法的实现原理。
http://www.importnew.com/13493.html
当出现了内存溢出,你怎么排错。
首先分析是什么类型的内存溢出,对应的调整参数或者优化代码。
https://wangkang007.gitbooks.io/jvm/content/4jvmdiao_you.html
JVM 内存模型的相关知识了解多少,好比重排序,内存屏障,happen-before,主内存,工做内存等。
内存屏障:为了保障执行顺序和可见性的一条cpu指令
重排序:为了提升性能,编译器和处理器会对执行进行重拍
happen-before:操做间执行的顺序关系。有些操做先发生。
主内存:共享变量存储的区域便是主内存
工做内存:每一个线程copy的本地内存,存储了该线程以读/写共享变量的副本
http://ifeve.com/java-memory-model-1/
http://www.jianshu.com/p/d3fda02d4cae
http://blog.csdn.net/kenzyq/article/details/50918457
简单说说你了解的类加载器。
类加载器的分类(bootstrap,ext,app,curstom),类加载的流程(load-link-init)
http://blog.csdn.net/gjanyanlig/article/details/6818655/
讲讲 JAVA 的反射机制。
Java程序在运行状态能够动态的获取类的全部属性和方法,并实例化该类,调用方法的功能
http://baike.baidu.com/link?url=C7p1PeLa3ploAgkfAOK-4XHE8HzQuOAB7K5GPcK_zpbAa_Aw-nO3997K1oir8N–1_wxXZfOThFrEcA0LjVP6wNOwidVTkLBzKlQVK6JvXYvVNhDWV9yF-NIOebtg1hwsnagsjUhOE2wxmiup20RRa#7
大家线上应用的 JVM 参数有哪些。
-server Xms6000M -Xmx6000M -Xmn500M -XX:PermSize=500M -XX:MaxPermSize=500M -XX:SurvivorRatio=65536 -XX:MaxTenuringThreshold=0 -Xnoclassgc -XX:+DisableExplicitGC -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=0 -XX:+CMSClassUnloadingEnabled -XX:-CMSParallelRemarkEnabled -XX:CMSInitiatingOccupancyFraction=90 -XX:SoftRefLRUPolicyMSPerMB=0 -XX:+PrintClassHistogram -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintHeapAtGC -Xloggc:log/gc.log
g1 和 cms 区别,吞吐量优先和响应优先的垃圾收集器选择。
Cms是以获取最短回收停顿时间为目标的收集器。基于标记-清除算法实现。比较占用cpu资源,切易形成碎片。
G1是面向服务端的垃圾收集器,是jdk9默认的收集器,基于标记-整理算法实现。可利用多核、多cpu,保留分代,实现可预测停顿,可控。
http://blog.csdn.net/linhu007/article/details/48897597
请解释以下 jvm 参数的含义:
-server -Xms512m -Xmx512m -Xss1024K
-XX:PermSize=256m -XX:MaxPermSize=512m -XX:MaxTenuringThreshold=20
XX:CMSInitiatingOccupancyFraction=80 -XX:+UseCMSInitiatingOccupancyOnly。
Server模式启动
最小堆内存512m
最大512m
每一个线程栈空间1m
永久代256
最大永久代256
最大转为老年代检查次数20
Cms回收开启时机:内存占用80%
命令JVM不基于运行时收集的数据来启动CMS垃圾收集周期
简单讲讲 tomcat 结构,以及其类加载器流程。
Server- –多个service
Container级别的:–>engine–》host–>context
Listenter
Connector
Logging、Naming、Session、JMX等等
经过WebappClassLoader 加载class
http://www.ibm.com/developerworks/cn/java/j-lo-tomcat1/
http://blog.csdn.net/dc_726/article/details/11873343
http://www.cnblogs.com/xing901022/p/4574961.html
http://www.jianshu.com/p/62ec977996df
tomcat 如何调优,涉及哪些参数。
硬件上选择,操做系统选择,版本选择,jdk选择,配置jvm参数,配置connector的线程数量,开启gzip压缩,trimSpaces,集群等
http://blog.csdn.net/lifetragedy/article/details/7708724
讲讲 Spring 加载流程。
经过listener入口,核心是在AbstractApplicationContext的refresh方法,在此处进行装载bean工厂,bean,建立bean实例,拦截器,后置处理器等。
https://www.ibm.com/developerworks/cn/java/j-lo-spring-principle/
讲讲 Spring 事务的传播属性。
七种传播属性。
事务传播行为
所谓事务的传播行为是指,若是在开始当前事务以前,一个事务上下文已经存在,此时有若干选项能够指定一个事务性方法的执行行为。在TransactionDefinition定义中包括了以下几个表示传播行为的常量:
TransactionDefinition.PROPAGATION_REQUIRED:若是当前存在事务,则加入该事务;若是当前没有事务,则建立一个新的事务。
TransactionDefinition.PROPAGATION_REQUIRES_NEW:建立一个新的事务,若是当前存在事务,则把当前事务挂起。
TransactionDefinition.PROPAGATION_SUPPORTS:若是当前存在事务,则加入该事务;若是当前没有事务,则以非事务的方式继续运行。
TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事务方式运行,若是当前存在事务,则把当前事务挂起。
TransactionDefinition.PROPAGATION_NEVER:以非事务方式运行,若是当前存在事务,则抛出异常。
TransactionDefinition.PROPAGATION_MANDATORY:若是当前存在事务,则加入该事务;若是当前没有事务,则抛出异常。
TransactionDefinition.PROPAGATION_NESTED:若是当前存在事务,则建立一个事务做为当前事务的嵌套事务来运行;若是当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED。
https://www.ibm.com/developerworks/cn/education/opensource/os-cn-spring-trans/
Spring 如何管理事务的。
编程式和声明式
同上
Spring 怎么配置事务(具体说出一些关键的 xml 元素)。
说说你对 Spring 的理解,非单例注入的原理?它的生命周期?循环注入的原理, aop 的实现原理,说说 aop 中的几个术语,它们是怎么相互工做的。
核心组件:bean,context,core,单例注入是经过单例beanFactory进行建立,生命周期是在建立的时候经过接口实现开启,循环注入是经过后置处理器,aop其实就是经过反射进行动态代理,pointcut,advice等。
Aop相关:http://blog.csdn.net/csh624366188/article/details/7651702/
Springmvc 中 DispatcherServlet 初始化过程。
入口是web.xml中配置的ds,ds继承了HttpServletBean,FrameworkServlet,经过其中的init方法进行初始化装载bean和实例,initServletBean是实际完成上下文工做和bean初始化的方法。
http://www.mamicode.com/info-detail-512105.html
Java虚拟机在执行Java程序的时候会把他所管理的内存划分为若干个不一样的数据区域,各个区域有各自的用途,以及建立和销毁的时间。有的区域随着虚拟机进程的启动而存在,有的区域则依赖用户线程的启动和结束而建立和销毁。
Java虚拟机会把运行时的数据区域分为如下几个区域:
程序计数器:
程序计数器是一块很小的内存空间,表明着当前线程执行的字节码的行号指示器,记录着所执行的行号,java虚拟机执行的是由后缀名为.java的文件编译而来的.class文件(字节码文件),因此字节码解释器根据程序计数器来执行字节码文件。每一个线程都有本身的程序解释器,这样才能保证程序的正确执行,也就是说程序计数器是线程私有的。
Java虚拟机栈:
java虚拟机栈是用来描述java方法的内存模型,每一个方法在执行的同时都会建立一个栈帧,而这个栈帧存储的是方法中的局部变量,操做的数据,方法的入口返回值等信息,当一个方法被调用的时候,就表明着栈帧的入栈直至方法的结束表明着栈帧的出栈。由于虚拟机栈存储的数据决定了他也是线程私有的,每一个线程都拥有一个虚拟机栈记录着方法的内容。咱们平时所说的栈就是指的是虚拟机栈,其中存储着基本数据类型和指向堆内存中对象的指针(对象的引用)。
本地方法栈:
这块区域和虚拟机栈执行的操做实际上是一致的,可是他们之间的服务对象不同,虚拟机栈为java方法服务,而本地方法栈为native方法服务,咱们在看源码的时候常常了一看到用native关键字修饰的方法,这种方法的实现是用c/c++实现的,咱们在平时是看不到他的源码实现的。
Java堆:
堆内存是这几块内存区域中最大的一块,堆内存存在的目的是存放对象的实例(经过new建立的对象,对象的引用放在虚拟机栈中指向堆中的实例),在虚拟机启动的时候堆内存也就被建立了,这块内存被全部线程共享,在虚拟机运行期间的全部线程建立的对象的实例都被存储在堆内存中。既然堆被线程所共享,那么线程建立的对象不能一直存放在这里,总会有装不下的时候,在必定条件下,java虚拟机会触发垃圾回收机制(GC),来回收这里被看做没有用的对象,虚拟机所管理的垃圾回收器老是会对这块区域进行管理操做。关于垃圾回收(GC)机制能够看另外一篇文章:Java GC垃圾回收机制
方法区:
方法区和堆内存同样,是各个线程共享的数据区域,看到这个方法区这个名字很快能想到这个区域存方法信息,事实上方法区存放的数据不少,包括被虚拟机加载的类信息,用final修饰的常量,String对象,用static修饰的静态变量。
运行时常量池:
准确的说这块区域属于方法区,也就受到了方法区的空间限制,以前所说的String对象,就是字符串常量就是存放在这里,编译期生成各类字面值和符号引用,将在类价在后放入方法区的运行时常量池的。运行时常量池的存储具备动态性,并非在类加载时才能放入数据,在程序运行期间也能够有新的常量放入。String类中的intern()方法就是这种特色,详看以前转载的一篇文章:String的intern()方法详解
直接内存:
这块区域和java中的新的io方式(NIO)有关,不属于虚拟机的运行时数据区。NIO是一种基于通道,缓冲区的io方式。
XSS是一种常常出如今web应用中的计算机安全漏洞,它容许恶意web用户将代码植入到提供给其它用户使用的页面中。好比这些代码包括HTML代码和客户端脚本。攻击者利用XSS漏洞旁路掉访问控制——例如同源策略(same origin policy)。这种类型的漏洞因为被黑客用来编写危害性更大的网络钓鱼(Phishing)攻击而变得广为人知。对于跨站脚本攻击,黑客界共识是:跨站脚本攻击是新型的“缓冲区溢出攻击“,而JavaScript是新型的“ShellCode”。
CSRF(Cross-site request forgery)跨站请求伪造,也被称为“One Click Attack”或者Session Riding,一般缩写为CSRF或者XSRF,是一种对网站的恶意利用。尽管听起来像跨站脚本(XSS),但它与XSS很是不一样,XSS利用站点内的信任用户,而CSRF则经过假装来自受信任用户的请求来利用受信任的网站。与XSS攻击相比,CSRF攻击每每不大流行(所以对其进行防范的资源也至关稀少)和难以防范,因此被认为比XSS更具危险性。
SQL注入,就是经过把SQL命令插入到Web表单提交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令。具体来讲,它是利用现有应用程序,将(恶意的)SQL命令注入到后台数据库引擎执行的能力,它能够经过在Web表单中输入(恶意)SQL语句获得一个存在安全漏洞的网站上的数据库,而不是按照设计者意图去执行SQL语句。 [1] 好比先前的不少影视网站泄露VIP会员密码大多就是经过WEB表单递交查询字符暴出的,这类表单特别容易受到SQL注入式攻击.
有两种实现方法,分别是继承Thread类与实现Runnable接口用synchronized关键字修饰同步方法反对使用stop(),是由于它不安全。它会解除由线程获取的全部锁定,并且若是对象处于一种不连贯状态,那么其余线程能在那种状态下检查和修改它们。结果很难检查出真正的问题所在。suspend()方法容易发生死锁。调用suspend()的时候,目标线程会停下来,但却仍然持有在这以前得到的锁定。此时,其余任何线程都不能访问锁定的资源,除非被“挂起”的线程恢复运行。
对任何线程来讲,若是它们想恢复目标线程,同时又试图使用任何一个锁定的资源,就会形成死锁。因此不该该使用suspend(),而应在本身的Thread类中置入一个标志,指出线程应该活动仍是挂起。若标志指出线程应该挂起,便用wait()命其进入等待状态。若标志指出线程应当恢复,则用一个notify()从新启动线程。
① throw表明动做,表示抛出一个异常的动做;throws表明一种状态,表明方法可能有异常抛出 ② throw用在方法实现中,而throws用在方法声明中 ③ throw只能用于抛出一种异常,而throws能够抛出多个异常
forward: A -> B -> C redirect: A -> B A -> C 1.从地址栏显示来讲 forward是服务器请求资源,服务器直接访问目标地址的URL,把那个URL的响应内容读取过来,而后把这些内容再发给浏览器.浏览器根本不知道服务器发送的内容从哪里来的,因此它的地址栏仍是原来的地址. redirect是服务端根据逻辑,发送一个状态码,告诉浏览器从新去请求那个地址.因此地址栏显示的是新的URL. 2.从数据共享来讲 forward:转发页面和转发到的页面能够共享request里面的数据. redirect:不能共享数据. 3.从运用地方来讲 forward:通常用于用户登录的时候,根据角色转发到相应的模块. redirect:通常用于用户注销登录时返回主页面和跳转到其它的网站等. 4.从效率来讲 forward:高. redirect:低.
内存溢出 out of memory,是指程序在申请内存时,没有足够的内存空间供其使用,出现out of memory;好比申请了一个integer,但给它存了long才能存下的数,那就是内存溢出。 内存泄露 memory leak,是指程序在申请内存后,没法释放已申请的内存空间,一次内存泄露危害能够忽略,但内存泄露堆积后果很严重,不管多少内存,早晚会被占光。 memory leak会最终会致使out of memory!内存泄漏是指你向系统申请分配内存进行使用(new),但是使用完了之后却不归还(delete),结果你申请到的那块内存你本身也不能再访问(也许你把它的地址给弄丢了),而系统也不能再次将它分配给须要的程序。
若是不被重写(原生)的hashCode和equals是什么样的?
为何须要重写equals和hashCode方法?
在咱们的业务系统中判断对象时有时候须要的不是一种严格意义上的相等,而是一种业务上的对象相等。在这种状况下,原生的equals方法就不能知足咱们的需求了
因此这个时候咱们须要重写equals方法,来知足咱们的业务系统上的需求。那么为何在重写equals方法的时候须要重写hashCode方法呢?
咱们先来看一下Object.hashCode的通用约定(摘自《Effective Java》第45页)
1.在一个应用程序执行期间,若是一个对象的equals方法作比较所用到的信息没有被修改的话,那么,对该对象调用hashCode方法屡次,它必须始终如一地返回 同一个整数。在同一个应用程序的屡次执行过程当中,这个整数能够不一样,即这个应用程序此次执行返回的整数与下一次执行返回的整数能够不一致。
2.若是两个对象根据equals(Object)方法是相等的,那么调用这两个对象中任一个对象的hashCode方法必须产生一样的整数结果。
3.若是两个对象根据equals(Object)方法是不相等的,那么调用这两个对象中任一个对象的hashCode方法,不要求必须产生不一样的整数结果。然而,程序员应该意识到这样的事实,对于不相等的对象产生大相径庭的整数结果,有可能提升散列表(hash table)的性能。
若是只重写了equals方法而没有重写hashCode方法的话,则会违反约定的第二条:相等的对象必须具备相等的散列码(hashCode)
同时对于HashSet和HashMap这些基于散列值(hash)实现的类。HashMap的底层处理机制是以数组的方法保存放入的数据的(Node<K,V>[] table),其中的关键是数组下标的处理。数组的下标是根据传入的元素hashCode方法的返回值再和特定的值异或决定的。
若是该数组位置上已经有放入的值了,且传入的键值相等则不处理,若不相等则覆盖原来的值,若是数组位置没有条目,则插入,并加入到相应的链表中。检查键是否存在也是根据hashCode值来肯定的。因此若是不重写hashCode的话,可能致使HashSet、HashMap不能正常的运做、
若是咱们将某个自定义对象存到HashMap或者HashSet及其相似实现类中的时候,若是该对象的属性参与了hashCode的计算,那么就不能修改该对象参数hashCode计算的属性了。有可能会移除不了元素,致使内存泄漏。
扩展:
在重写equals方法的时候,须要遵照下面的通用约定:
一、自反性。
对于任意的引用值x,x.equals(x)必定为true。
二、对称性。
对于任意的引用值x和y,当且仅当y.equals(x)返回true时,x.equals(y)也必定返回true。
三、传递性。
对于任意的引用值x、y和z,若是x.equals(y)返回true,而且y.equals(z)也返回true,那么x.equals(z)也必定返回true。
四、一致性。
对于任意的引用值x和y,若是用于equals比较的对象没有被修改的话,那么,对此调用x.equals(y)要么一致地返回true,要么一致的返回false。
五、对于任意的非空引用值x,x.equals(null)必定返回false。
重写hashCode方法的大体方式:
a、把某个非零常数值,好比说17(最好是素数),保存在一个叫result的int类型的变量中。
b、对于对象中每个关键域f(值equals方法中考虑的每个域),完成一些步骤:
一、为该域计算int类型的散列吗c:
1)、若是该域是boolean类型,则计算(f?0:1)。
2)、若是该域是byte、char、short或者int类型,则计算(int)f。
3)、若是该域是float类型,则计算Float.floatToIntBits(f)。
4)、若是该域是long类型,则计算(int)(f ^ (f>>>32))。
5)、若是该域是double类型,则计算Double.doubleToLongBits(f)获得一个long类型的值,而后按照步骤4,对该long型值计算散列值。
6)、若是该域是一个对象引用,而且该类的equals方法经过递归调用equals的方式来比较这个域,则一样对这个域递归调用hashCode。若是要求一个更为复杂的比较,则为这个域计算一个“规范表示”,而后针对这个范式表示调用hashCode。若是这个域的值为null,则返回0(或者其余某个常数)
7)、若是该域是一个数组,则把每个元素当作单独的域来处理。也就是说,递归地应用上述规则,对每一个重要的元素计算一个散列码,而后根据步骤下面的作法把这些散列值组合起来。
二、按照下面的公式,把步骤1中计算获得的散列码C组合到result中:
result = 31*result+c。
c、返回result。
d、写完hashCode方法以后,问本身“是否相等的实例具备相等的散列码”。若是不是的话,找出缘由,并修改。
能够经过org.apache.commons.lang.builder.HashCodeBuilder这个工具类来方便的重写hashCode方法。
枚举类型的那些事:http://www.javashuo.com/article/p-gaxjjntx-a.html
枚举类型符合通用模式 Class Enum<E extends Enum<E>>,而 E 表示枚举类型的名称。枚举类型的每个值都将映射到 protected Enum(String name, int ordinal) 构造函数中,在这里,每一个值的名称都被转换成一个字符串,而且序数设置表示了此设置被建立的顺序。
301:- 永久移动。请求的资源已被永久的移动到新URI,返回信息会包括新的URI,浏览器会自动定向到新URI。从此任何新的请求都应使用新的URI代替
302:- 临时移动。与301相似。但资源只是临时被移动。客户端应继续使用原有URI
304:- 若是客户端发送了一个带条件的GET 请求且该请求已被容许,而文档的内容(自上次访问以来或者根据请求的条件)并无改变,则服务器应当返回这个304状态码..
403:- 表明客户端错误,指的是服务器端有能力处理该请求,可是拒绝受权访问。
404:- 服务器没法根据客户端的请求找到资源(网页)。经过此代码,网站设计人员可设置"您所请求的资源没法找到"的个性页面
500:- 服务器内部错误,没法完成请求
1.start()方法来启动线程,真正实现了多线程运行,这时无需等待run方法体代码执行完毕而直接继续执行下面的代码:
经过调用Thread类的start()方法来启动一个线程,这时此线程是处于就绪状态,并无运行。
而后经过此Thread类调用方法run()来完成其运行操做的,这里方法run()称为线程体,它包含了要执行的这个线程的内容,
Run方法运行结束,此线程终止,而CPU再运行其它线程。
2.run()方法看成普通方法的方式调用,程序仍是要顺序执行,仍是要等待run方法体执行完毕后才可继续执行下面的代码:
而若是直接用Run方法,这只是调用一个方法而已,程序中依然只有主线程--这一个线程,其程序执行路径仍是只有一条,
这样就没有达到写线程的目的。
若是一个类继承Thread,则不适合资源共享。可是若是实现了Runable接口的话,则很容易的实现资源共享。
实现Runnable接口比继承Thread类所具备的优点:
1):适合多个相同的程序代码的线程去处理同一个资源
2):能够避免java中的单继承的限制
3):增长程序的健壮性,代码能够被多个线程共享,代码和数据独立。
提醒一下你们:main方法其实也是一个线程。在java中因此的线程都是同时启动的,至于何时,哪一个先执行,彻底看谁先获得CPU的资源。在java中,每次程序运行至少启动2个线程。一个是main线程,一个是垃圾收集线程。由于每当使用java命令执行一个类的时候,实际上都会启动一个JVM,每个JVM实际就是在操做系统中启动了一个进程。
在java程序中,只要前台有一个线程在运行,整个java程序进程不会小时,因此此时能够设置一个后台线程,这样即便java进程小时了,此后台线程依然可以继续运行。
线程状态从大的方面来讲,可归结为:初始状态、可运行状态、不可运行状态和消亡状态,具体可细分为上图所示7个状态,说明以下:
1)线程的实现有两种方式,一是继承Thread类,二是实现Runnable接口,但无论怎样,当咱们new了thread实例后,线程就进入了初始状态;
2)当该对象调用了start()方法,就进入可运行状态;
3)进入可运行状态后,当该对象被操做系统选中,得到CPU时间片就会进入运行状态;
4)进入运行状态后case就比较多,大体有以下情形: ﹒run()方法或main()方法结束后,线程就进入终止状态; 当线程调用了自身的sleep()方法或其余线程的join()方法,就会进入阻塞状态(该状态既停 止当前线程,但并不释放所占有的资源)。当
sleep()结束或join()结束后,该线程进入可运行状态,继续等待OS分配时间片; 当线程刚进入可运行状态(注意,还没运行),发现将要调用的资源被锁牢(synchroniza,lock),将会当即进入锁池状态,等待获取锁标记(这时的锁池里也许已经有了其余线
程在等待获取锁标记,这时它们处于队列状态,既先到先得),一旦线程得到锁标记后,就转入可运行状态,等待OS分配 CPU时间片; 当线程调用wait()方法后会进入等待队列(进入这个状态会释放所占有的全部资源,与阻塞状态不一样),进入这个状态后,
是不能自动唤醒的,必须依靠其余线程调用notify()或notifyAll()方法才能被唤醒(因为notify()只是唤醒一个线程,但咱们由不能肯定具体唤醒的是哪个线程,也许咱们须要唤醒的线程不可以被唤醒,所以在实际使用时,通常都用notifyAll()方法,唤醒
有所线程),线程被唤醒后会进入锁池,等待获取锁标记。 当线程调用stop方法,便可使线程进入消亡状态,可是因为stop方法是不安全的,不鼓励使用,你们能够经过run方法里的条件变通实现线程的 stop。
boolean数据类型非true即false。
这个数据类型表示1 bit,可是它的大小并无精肯定义。
《Java虚拟机规范》中如是说:“虽然定义了boolean这种数据类型,可是只对它提供了很是有限的支持。在Java虚拟机中没有任何供boolean值专用的字节码指令,Java语言表达式所操做的boolean值,在编译以后都使用Java虚拟机中的int数据类型来代替,而boolean数组将会被编码成Java虚拟机的byte数组,每一个元素boolean元素占8位”。这样咱们能够得出boolean类型单独使用是4个字节,在数组中又是1个字节。
那虚拟机为何要用int来代替boolean呢?为何不用byte或short,这样不是更节省内存空间吗?
实际上,使用int的缘由是,对于当下32位的CPU来讲,一次进行32位的数据交换更加高效。
综上,咱们能够知道:官方文档对boolean类型没有给出精确的定义,《Java虚拟机规范》给出了“单独时使用4个字节,boolean数组时1个字节”的定义,具体还要看虚拟机实现是否按照规范来,因此1个字节、4个字节都是有可能的。这实际上是一种时空权衡。 boolean类型的封装类是Boolean。
案例演示 >>,>>>,<<的用法:
<<:左移左边最高位丢弃,右边补齐0
>>:右移最高位是0,左边补齐0;最高为是1,左边补齐1
>>>:无符号右移 不管最高位是0仍是1,左边补齐0
最有效率的算出2 * 8的结果
//最有效率的算出2 * 8的结果
System.out.println(2 << 3);
《Think in Java》中说:“关系操做符生成的是一个boolean结果,它们计算的是操做数的值之间的关系”。 "=="判断的是两个对象的内存地址是否同样,适用于原始数据类型和枚举类型(它们的变量存储的是值自己,而引用类型变量存储的是引用);equals是Object类的方法,Object对它的实现是比较内存地址,咱们能够重写这个方法来自定义“相等”这个概念。好比类库中的String、Date等类就对这个方法进行了重写。 综上,对于枚举类型和原始数据类型的相等性比较,应该使用"==";对于引用类型的相等性比较,应该使用equals方法。
请参考散列表的基本原理与实现
更详细的说明请参考官方文档,对相关数据结构不太熟悉的同窗能够参考《算法导论》或其余相关书籍。
简单的说,HashMap的底层实现是“基于拉链法的散列表”。
详细分析请参考 Map源码解析之HashMap源码分析
ConcurrentHashMap是支持并发读写的HashMap,它的特色是读取数据时无需加锁,写数据时能够保证加锁粒度尽量的小。因为其内部采用“分段存储”,只需对要进行写操做的数据所在的“段”进行加锁。关于ConcurrentHashMap底层实现的详细分析请参考 Java并发编程:并发容器之ConcurrentHashMap
会执行。只有两种状况finally块中的语句不会被执行:
Java中的异常层次结构
Java中的异常层次结构以下图所示:
咱们能够看到Throwable类是异常层级中的基类。
Error类表示内部错误,这类错误使咱们没法控制的;Exception表示异常,RuntimeException及其子类属于未检查异常,这类异常包括ArrayIndexOutOfBoundsException、NullPointerException等,咱们应该经过条件判断等方式语句避免未检查异常的发生。IOException及其子类属于已检查异常,编译器会检查咱们是否为全部可能抛出的已检查异常提供了异常处理器,若没有则会报错。对于未检查异常,咱们无需捕获(固然Java也容许咱们捕获,但咱们应该作的事避免未检查异常的发生)。
三大特征:封装、继承、多态。
抽象类中能够包含属性,方法(包含抽象方法与有着具体实现的方法),常量;接口只能包含常量和方法声明。
抽象类中的方法和成员变量能够定义可见性(好比 public、private等);而接口中的方法只能为public(缺省为public)。
一个子类只能有一个父类(具体类或抽象类);而一个接口能够继承一个多个接口,一个类也能够实现多个接口。
子类中实现父类中的抽象方法时,可见性能够大于等于父类中的;而接口实现类中的接口 方法的可见性只能与接口中相同(public)。
静态内部类不会持有外围类的引用,而非静态内部类会隐式持有外围类的一个引用。
所谓多态,指的就是父类引用指向子类对象,调用方法时会调用子类的实现而不是父类的实现。多态的实现的关键在于“动态绑定”。详细介绍请戳 Java动态绑定的内部实现机制
Java中能够对类、对象、方法或是代码块上锁。
给出“生产者-消费者”问题的一种解决方案
使用阻塞队列:
public class BlockingQueueTest { private int size = 20; private ArrayBlockingQueue<Integer> blockingQueue = new ArrayBlockingQueue<>(size); public static void main(String[] args) { BlockingQueueTest test = new BlockingQueueTest(); Producer producer = test.new Producer(); Consumer consumer = test.new Consumer(); producer.start(); consumer.start(); } class Consumer extends Thread{ @Override public void run() { while(true){ try { //从阻塞队列中取出一个元素 queue.take(); System.out.println("队列剩余" + queue.size() + "个元素"); } catch (InterruptedException e) { } } } } class Producer extends Thread{ @Override public void run() { while (true) { try { //向阻塞队列中插入一个元素 queue.put(1); System.out.println("队列剩余空间:" + (size - queue.size())); } catch (InterruptedException e) { } } } } }
ThreadLocal的做用是提供线程内的局部变量,在多线程环境下访问时能保证各个线程内的ThreadLocal变量各自独立。也就是说,每一个线程的ThreadLocal变量是本身专用的,其余线程是访问不到的。ThreadLocal最经常使用于如下这个场景:多线程环境下存在对非线程安全对象的并发访问,并且该对象不须要在线程间共享,可是咱们不想加锁,这时候能够使用ThreadLocal来使得每一个线程都持有一个该对象的副本。
关于这个问题咱们直接看《Effective Java》给咱们作的解答:
for-each可以让代码更加清晰,而且减小了出错的机会。 下面的惯用代码适用于集合与数组类型:
for (Element e : elements) {
doSomething(e);
}
使用for-each循环与常规的for循环相比,并不存在性能损失,即便对数组进行迭代也是如此。实际上,在有些场合下它还能带来微小的性能提高,由于它只计算一次数组索引的上限。
Java IO是面向流的,这意味着咱们须要每次从流中读取一个或多个字节,直到读取完全部字节;NIO是面向缓冲的,也就是说会把数据读取到一个缓冲区中,而后对缓冲区中的数据进行相应处理。
Java NIO中存在一个称为选择器(selector)的东西,它容许你把多个通道(channel)注册到一个选择器上,而后使用一个线程来监视这些通道:若这些通道里有某个准备好能够开始进行读或写操做了,则开始对相应的通道进行读写。而在等待某通道变为可读/写期间,请求对通道进行读写操做的线程能够去干别的事情。
反射的做用归纳地说是运行时获取类的各类定义信息,好比定义了哪些属性与方法。原理是经过类的class对象来获取它的各类信息。
关于泛型机制的详细介绍请直接戳 Java核心技术点之泛型
所谓“设计模式”,不过是面向对象编程中一些经常使用的软件设计手法,而且通过实践的检验,这些设计手法在各自的场景下能解决一些需求,所以它们就成为了现在广为流传的”设计模式“。也就是说,正式由于在某些场景下产生了一些棘手的问题,才催生了相应的设计模式。明确了这一点,咱们在学习某种设计模式时要充分理解它产生的背景以及它所解决的主要矛盾是什么。
经常使用的设计模式能够分为如下三大类:
注解能够看做是“加强版的注释”,它能够向编译器、虚拟机说明一些事情。 注解是描述Java代码的代码,它可以被编译器解析,注解处理工具在运行时也可以解析注解。注解自己是“被动”的信息,只有主动解析它才有意义。 除了向编译器/虚拟机传递信息,咱们也能够使用注解来生成一些“模板化”的代码。
1.一、HashMap的实现原理
1.1.一、结构
HashMap其实是一个“链表散列”的数据结构,即数组和链表的结合体,HashMap底层就是一个数组结构,数组中的每一项又是一个链表。以下图所示:
image.png
当新建一个HashMap的时候,就会初始化一个数组。哈希表是由数组+链表组成的,一个长度为16的数组中,每一个元素存储的是一个链表的头结点。这些元素通常状况是经过hash(key)%len的规则存储到数组中,也就是元素的key的哈希值对数组长度取模获得。
1.1.二、核心变量
image.png
1.1.三、put存储逻辑
当咱们往HashMap中put元素的时候,先根据key的hashCode从新计算hash值,根据hash值获得这个元素在数组中的位置(即下标), 若是数组该位置上已经存放有其余元素了,那么在这个位置上的元素将以链表的形式存放,新加入的放在链头,最早加入的放在链尾。若是数组该位置上没有元素,就直接将该元素放到此数组中的该位置上。
这里有一个特殊的地方。在JDK1.6中,HashMap采用位桶+链表实现,即便用链表处理冲突,同一hash值的链表都存储在一个链表里。可是当位于一个桶中的元素较多,即hash值相等的元素较多时,经过key值依次查找的效率较低。而JDK1.8中,HashMap采用位桶+链表+红黑树实现,当链表长度超过阈值(8)时,将链表转换为红黑树,这样大大减小了查找时间。
红黑树
红黑树和平衡二叉树(AVL树)相似,都是在进行插入和删除操做时经过特定操做保持二叉查找树的平衡,从而得到较高的查找性能。
红黑树和AVL树的区别在于它使用颜色来标识结点的高度,它所追求的是局部平衡而不是AVL树中的很是严格的平衡。
1.1.四、get读取逻辑
从HashMap中get元素时,首先计算key的hashCode,找到数组中对应位置的某一元素,而后经过key的equals方法在对应位置的链表中找到须要的元素。
若是第一个节点是TreeNode,说明采用的是数组+红黑树结构处理冲突,遍历红黑树,获得节点值。
1.1.五、概括
简单地说,HashMap 在底层将 key-value 当成一个总体进行处理,这个总体就是一个 Node 对象。HashMap 底层采用一个 Node<K,V>[] 数组来保存全部的 key-value 对,当须要存储一个 Node 对象时,会根据hash算法来决定其在数组中的存储位置,在根据equals方法决定其在该数组位置上的链表中的存储位置;当须要取出一个Entry时,也会根据hash算法找到其在数组中的存储位置,再根据equals方法从该位置上的链表中取出该Node。
1.1.六、HashMap的resize(rehash)
当HashMap中的元素愈来愈多的时候,hash冲突的概率也就愈来愈高,由于数组的长度是固定的。因此为了提升查询的效率,就要对HashMap的数组进行扩容,在对HashMap数组进行扩容时,就会出现性能问题:原数组中的数据必须从新计算其在新数组中的位置,并放进去,这就是resize。
那么HashMap何时进行扩容呢?当HashMap中的元素个数超过数组大小loadFactor时,就会进行数组扩容,loadFactor的默认值为0.75,这是一个折中的取值。也就是说,默认状况下,数组大小为16,那么当HashMap中元素个数超过160.75=12的时候,就把数组的大小扩展为 216=32,即扩大一倍,而后从新计算每一个元素在数组中的位置,而这是一个很是消耗性能的操做,因此若是咱们已经预知HashMap中元素的个数,那么预设元素的个数可以有效的提升HashMap的性能。
1.二、HashMap在并发场景下的问题和解决方案
1.2.一、多线程put后可能致使get死循环
问题缘由就是HashMap是非线程安全的,多个线程put的时候形成了某个key值Entry key List的死循环,问题就这么产生了。
当另一个线程get 这个Entry List 死循环的key的时候,这个get也会一直执行。最后结果是愈来愈多的线程死循环,最后致使服务器dang掉。咱们通常认为HashMap重复插入某个值的时候,会覆盖以前的值,这个没错。可是对于多线程访问的时候,因为其内部实现机制(在多线程环境且未做同步的状况下,对同一个HashMap作put操做可能致使两个或以上线程同时作rehash动做,就可能致使循环键表出现,一旦出现线程将没法终止,持续占用CPU,致使CPU使用率居高不下),就可能出现安全问题了。
为HashMap以链表组形式存在,初始数组16位(为什么16位,又是一堆移位算法,下一篇文章再写吧),若是长度超过75%,长度增长一倍,多线程操做的时候,恰巧两个线程插入的时候都须要扩容,造成了两个链表,这时候读取,size不同,报错了。其实这时候报错都是好事,至少不会陷入死循环让cpu死了,有种状况,假如两个线程在读,还有个线程在写,恰巧扩容了,这时候你死都不知道咋死的,直接就是死循环,假如你是双核cpu,cpu占用率就是50%,两个线程恰巧都进入死循环了,得!中奖了。
1.2.二、多线程put的时候可能致使元素丢失
主要问题出在addEntry方法的new Entry (hash, key, value, e),若是两个线程都同时取得了e,则他们下一个元素都是e,而后赋值给table元素的时候有一个成功有一个丢失。
1.2.三、解决方案
ConcurrentHashMap替换HashMap
Collections.synchronizedMap将HashMap包装起来
1.三、ConcurrentHashMap PK HashTable
ConcurrentHashMap具体是怎么实现线程安全的呢,确定不多是每一个方法加synchronized,那样就变成了HashTable。
从ConcurrentHashMap代码中能够看出,它引入了一个“分段锁”的概念,具体能够理解为把一个大的Map拆分红N个小的HashTable,根据key.hashCode()来决定把key放到哪一个HashTable中。
以空间换时间的结构,跟分布式缓存结构有点像,建立的时候,内存直接分为了16个segment,每一个segment实际上仍是存储的哈希表(Segment其实就是一个HashMap ),写入的时候,先找到对应的segment,而后锁这个segment,写完,解锁,锁segment的时候,其余segment还能够继续工做。
ConcurrentHashMap如此的设计,优点主要在于: 每一个segment的读写是高度自治的,segment之间互不影响。这称之为“锁分段技术”;
2.一、线程的各个状态及切换
Java中的线程的生命周期大致可分为5种状态:新建、可运行、运行、阻塞、死亡。
一、新建(NEW):新建立了一个线程对象。
二、可运行(RUNNABLE):线程对象建立后,其余线程(好比main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取cpu 的使用权 。
三、运行(RUNNING):可运行状态(runnable)的线程得到了cpu 时间片(timeslice) ,执行程序代码。
四、阻塞(BLOCKED):阻塞状态是指线程由于某种缘由放弃了cpu 使用权,也即让出了cpu timeslice,暂时中止运行。直到线程进入可运行(runnable)状态,才有机会再次得到cpu timeslice 转到运行(running)状态。
阻塞的状况分三种:
1)等待阻塞:运行(running)的线程执行o.wait()方法,JVM会把该线程放入等待队列(waitting queue)中。
2)同步阻塞:运行(running)的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池(lock pool)中。
3)其余阻塞:运行(running)的线程执行Thread.sleep(long ms)或t.join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程从新转入可运行(runnable)状态。
五、死亡(DEAD):线程run()、main() 方法执行结束,或者因异常退出了run()方法,则该线程结束生命周期。死亡的线程不可再次复生。
几个方法的比较:
1)Thread.sleep(long millis),必定是当前线程调用此方法,当前线程进入阻塞,但不释放对象锁,millis后线程自动苏醒进入可运行状态。做用:给其它线程执行机会的最佳方式。
2)Thread.yield(),必定是当前线程调用此方法,当前线程放弃获取的cpu时间片,由运行状态变会可运行状态,让OS再次选择线程。做用:让相同优先级的线程轮流执行,但并不保证必定会轮流执行。实际中没法保证yield()达到让步目的,由于让步的线程还有可能被线程调度程序再次选中。Thread.yield()不会致使阻塞。
3)t.join()/t.join(long millis),当前线程里调用其它线程1的join方法,当前线程阻塞,但不释放对象锁,直到线程1执行完毕或者millis时间到,当前线程进入可运行状态。
4)obj.wait(),当前线程调用对象的wait()方法,当前线程释放对象锁,进入等待队列。依靠notify()/notifyAll()唤醒或者wait(long timeout)timeout时间到自动唤醒。
5)obj.notify(),唤醒在此对象监视器上等待的单个线程,选择是任意性的。notifyAll()唤醒在此对象监视器上等待的全部线程。
2.二、多线程的实现方式Thread、Runnable、Callable
继承Thread类,实现Runnable接口,实现Callable接口。
这三种方法的介绍和比较:
一、实现Runnable接口相比继承Thread类有以下优点:
1)能够避免因为Java的单继承特性而带来的局限
2)加强程序的健壮性,代码可以被多个线程共享,代码与数据是独立的
3)适合多个相同程序代码的线程去处理同一资源的状况
二、实现Runnable接口和实现Callable接口的区别
1)Runnable是自从java1.1就有了,而Callable是1.5以后才加上去的
2)实现Callable接口的任务线程能返回执行结果,而实现Runnable接口的任务线程不能返回结果
3)Callable接口的call()方法容许抛出异常,而Runnable接口的run()方法的异常只能在内部消化,不能继续上抛
4)加入线程池运行,Runnable使用ExecutorService的execute方法,Callable使用submit方法
注:Callable接口支持返回执行结果,此时须要调用FutureTask.get()方法实现,此方法会阻塞主线程直到获取返回结果,当不调用此方法时,主线程不会阻塞
2.三、线程池原理和运行机制
java.uitl.concurrent.ThreadPoolExecutor类是线程池中最核心的一个类。
在ThreadPoolExecutor类中提供了四个构造方法,主要参数包括下面的参数:
一、int corePoolSize:核心池的大小。
线程池的基本大小,即在没有任务须要执行的时候线程池的大小,而且只有在工做队列满了的状况下才会建立超出这个数量的线程。这里须要注意的是:在刚刚建立ThreadPoolExecutor的时候,线程并不会当即启动,而是要等到有任务提交时才会启动,除非调用了prestartCoreThread/prestartAllCoreThreads事先启动核心线程。再考虑到keepAliveTime和allowCoreThreadTimeOut超时参数的影响,因此没有任务须要执行的时候,线程池的大小不必定是corePoolSize。
二、int maximumPoolSize:线程池最大线程数,它表示在线程池中最多能建立多少个线程,注意与corePoolSize区分。
线程池中容许的最大线程数,线程池中的当前线程数目不会超过该值。若是队列中任务已满,而且当前线程个数小于maximumPoolSize,那么会建立新的线程来执行任务。
三、long keepAliveTime:表示线程没有任务执行时最多保持多久时间会终止。
四、TimeUnit unit:参数keepAliveTime的时间单位,有7种取值,在TimeUnit类中有7种静态属性。
五、BlockingQueue<Runnable> workQueue:一个阻塞队列,用来存储等待执行的任务。
六、ThreadFactory threadFactory:线程工厂,主要用来建立线程。
七、RejectedExecutionHandler handler:表示当拒绝处理任务时的策略。
还有一个成员变量比较重要:poolSize
线程池中当前线程的数量,当该值为0的时候,意味着没有任何线程,线程池会终止。同一时刻,poolSize不会超过maximumPoolSize。
2.四、线程池对任务的处理
一、若是当前线程池中的线程数目小于corePoolSize,则每来一个任务,就会建立一个线程去执行这个任务;
二、若是当前线程池中的线程数目>=corePoolSize,则每来一个任务,会尝试将其添加到任务缓存队列当中,若添加成功,则该任务会等待空闲线程将其取出去执行;若添加失败(通常来讲是任务缓存队列已满),则会尝试建立新的线程去执行这个任务(maximumPoolSize);
三、若是当前线程池中的线程数目达到maximumPoolSize(此时线程池的任务缓存队列已满),则会采起任务拒绝策略进行处理;
任务拒绝策略,一般有如下四种策略:
1)ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。
2)ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,可是不抛出异常。
3)ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,而后从新尝试执行任务(重复此过程)
4)ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务
四、若是线程池中的线程数量大于 corePoolSize时,若是某线程空闲时间超过keepAliveTime,线程将被终止,直至线程池中的线程数目不大于corePoolSize;若是容许为核心池中的线程设置存活时间,那么核心池中的线程空闲时间超过keepAliveTime,线程也会被终止。
2.五、线程池的状态
一、线程池的状态说明:
RUNNING(运行):接受新任务并处理排队任务。
SHUTDOWN(关闭):不接受新任务,会处理队列任务。
STOP(中止):不接受新任务,不处理队列任务,而且中断进程中的任务。
TIDYING(整理):全部任务都已终止,工做计数为零,线程将执行terminated()方法终止线程。
TERMINATED(已终止):terminated()方法已完成。
二、各个状态之间的转换
RUNNING -> SHUTDOWN:调用shutdown()方法。
RUNNING or SHUTDOWN-> STOP:调用shutdownNow()方法。
SHUTDOWN -> TIDYING:当队列和池均都为空时。
STOP -> TIDYING:当池为空时。
TIDYING -> TERMINATED:当terminated()方法已完成。
进程和线程的区别:
对于进程来讲,子进程是父进程的复制品,从父进程那里得到父进程的数据空间,堆和栈的复制品。
而线程,相对于进程而言,是一个更加接近于执行体的概念,能够和同进程的其余线程之间直接共享数据,并且拥有本身的栈空间,拥有独立序列。
共同点: 它们都能提升程序的并发度,提升程序运行效率和响应时间。线程和进程在使用上各有优缺点。 线程执行开销比较小,但不利于资源的管理和保护,而进程相反。同时,线程适合在SMP机器上运行,而进程能够跨机器迁移。
他们之间根本区别在于 多进程中每一个进程有本身的地址空间,线程则共享地址空间。全部其余区别都是由于这个区别产生的。好比说:
1. 速度。线程产生的速度快,通信快,切换快,由于他们处于同一地址空间。
2. 线程的资源利用率好。
3. 线程使用公共变量或者内存的时候须要同步机制,但进程不用。
而他们通讯方式的差别也仍然是因为这个根本缘由形成的。
通讯方式之间的差别
由于那个根本缘由,实际上只有进程间须要通讯,同一进程的线程共享地址空间,没有通讯的必要,但要作好同步/互斥,保护共享的全局变量。
而进程间通讯不管是信号,管道pipe仍是共享内存都是由操做系统保证的,是系统调用.
线程间的通讯目的主要是用于线程同步,因此线程没有像进程通讯中的用于数据交换的通讯机制。
3.一、JVM的结构
每一个JVM都包含:方法区、Java堆、Java栈、本地方法栈、程序计数器等。
image.png
一、方法区:共享
各个线程共享的区域,存放类信息、常量、静态变量。
二、Java堆:共享
也是线程共享的区域,咱们的类的实例就放在这个区域,能够想象你的一个系统会产生不少实例,所以Java堆的空间也是最大的。若是Java堆空间不足了,程序会抛出OutOfMemoryError异常。
三、Java栈:私有
每一个线程私有的区域,它的生命周期与线程相同,一个线程对应一个Java栈,每执行一个方法就会往栈中压入一个元素,这个元素叫“栈帧”,而栈帧中包括了方法中的局部变量、用于存放中间状态值的操做栈。若是Java栈空间不足了,程序会抛出StackOverflowError异常,想想什么状况下会容易产生这个错误,对,递归,递归若是深度很深,就会执行大量的方法,方法越多Java栈的占用空间越大。
四、本地方法栈:私有
角色和Java栈相似只不过它是用来表示执行本地方法的,本地方法栈存放的方法调用本地方法接口,最终调用本地方法库,实现与操做系统、硬件交互的目的。
五、程序计数器:私有
说到这里咱们的类已经加载了,实例对象、方法、静态变量都去了本身该去的地方,那么问题来了,程序该怎么执行,哪一个方法先执行,哪一个方法后执行,这些指令执行的顺序就是程序计数器在管,它的做用就是控制程序指令的执行顺序。
六、执行引擎固然就是根据PC寄存器调配的指令顺序,依次执行程序指令。
3.二、Java堆的介绍及典型的垃圾回收算法介绍
3.2.一、Java堆的介绍
Java堆是虚拟机管理的最大的一块内存,堆上的全部线程共享一块内存区域,在启动虚拟机时建立。此内存惟一目的就是存放对象实例,几乎全部对象实例都在这里分配,这一点Java虚拟机规范中的描述是:全部对象实例及数组都要在堆上分配。
Java堆是垃圾收集器管理的主要区域,也被称为“GC堆”,因为如今收集器基本都采用分代收集算法,因此Java堆中还能够分为:新生代和老年代。
堆的内存模型大体为:
image.png
默认的,新生代 ( Young ) 与老年代 ( Old ) 的比例的值为 1:2 ( 该值能够经过参数 –XX:NewRatio 来指定 ),即:新生代 ( Young ) = 1/3 的堆空间大小,老年代 ( Old ) = 2/3 的堆空间大小。
其中,新生代 ( Young ) 被细分为 Eden 和 两个 Survivor 区域,这两个 Survivor 区域分别被命名为 from 和 to以示区分。 默认的,Edem : from : to = 8 : 1 : 1 ( 能够经过参数 –XX:SurvivorRatio 来设定 ),即: Eden = 8/10 的新生代空间大小,from = to = 1/10 的新生代空间大小。
JVM 每次只会使用 Eden 和其中的一块 Survivor 区域来为对象服务,因此不管何时,老是有一块 Survivor 区域是空闲着的。 所以,新生代实际可用的内存空间为 9/10 ( 即90% )的新生代空间。
新生代是 GC 收集垃圾的频繁区域。
当对象在 Eden ( 包括一个 Survivor 区域,这里假设是 from 区域 ) 出生后,在通过一次 Minor GC 后,若是对象还存活,而且可以被另一块 Survivor 区域所容纳 ( 上面已经假设为 from 区域,这里应为 to 区域,即 to 区域有足够的内存空间来存储 Eden 和 from 区域中存活的对象 ),则使用复制算法将这些仍然还存活的对象复制到另一块 Survivor 区域 ( 即 to 区域 ) 中,而后清理所使用过的 Eden 以及 Survivor 区域 ( 即 from 区域 ),而且将这些对象的年龄设置为1,之后对象在 Survivor 区每熬过一次 Minor GC,就将对象的年龄 + 1,当对象的年龄达到某个值时 ( 默认是 15 岁,能够经过参数 -XX:MaxTenuringThreshold 来设定 ),这些对象就会成为老年代。
但这也不是必定的,对于一些较大的对象 ( 即须要分配一块较大的连续内存空间 ) 则是直接进入到老年代。
From Survivor区域与To Survivor区域是交替切换空间,在同一时间内二者中只有一个不为空。
3.2.二、如何肯定某个对象是可回收的(垃圾)
一、引用计数法
给对象中添加一个引用计数器,每当有一个地方引用它时,计数器就加1;当引用失效时,计数器值就减1;任什么时候刻计数器都为0的对象就是不可能再被使用的。
这种方式的问题是没法解决循环引用的问题,当两个对象循环引用时,就算把两个对象都设置为null,由于他们的引用计数都不为0,这就会使他们永远不会被清除。
二、根搜索算法(可达性分析)
为了解决引用计数法的循环引用问题,Java使用了可达性分析的方法。经过一系列的“GC roots”对象做为起点搜索。若是在“GC roots”和一个对象之间没有可达路径,则称该对象是不可达的。要注意的是,不可达对象不等价于可回收对象,不可达对象变为可回收对象至少要通过两次标记过程。两次标记后仍然是可回收对象,则将面临回收。
比较常见的将对象视为可回收对象的缘由:
显式地将对象的惟一强引用指向新的对象。
显式地将对象的惟一强引用赋值为Null。
局部引用所指向的对象(如,方法内对象)。
只有弱引用与其关联的对象。
1)强引用(StrongReference)
强引用是使用最广泛的引用。若是一个对象具备强引用,那垃圾回收器毫不会回收它。当内存空间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具备强引用的对象来解决内存不足的问题。 ps:强引用其实也就是咱们平时A a = new A()这个意思。
2)软引用(SoftReference)
若是一个对象只具备软引用,则内存空间足够,垃圾回收器就不会回收它;若是内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就能够被程序使用。软引用可用来实现内存敏感的高速缓存(下文给出示例)。
软引用能够和一个引用队列(ReferenceQueue)联合使用,若是软引用所引用的对象被垃圾回收器回收,Java虚拟机就会把这个软引用加入到与之关联的引用队列中。
3)弱引用(WeakReference)
弱引用与软引用的区别在于:只具备弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程当中,一旦发现了只具备弱引用的对象,无论当前内存空间足够与否,都会回收它的内存。不过,因为垃圾回收器是一个优先级很低的线程,所以不必定会很快发现那些只具备弱引用的对象。
弱引用能够和一个引用队列(ReferenceQueue)联合使用,若是弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。
4)虚引用(PhantomReference)
“虚引用”顾名思义,就是形同虚设,与其余几种引用都不一样,虚引用并不会决定对象的生命周期。若是一个对象仅持有虚引用,那么它就和没有任何引用同样,在任什么时候候均可能被垃圾回收器回收。
虚引用主要用来跟踪对象被垃圾回收器回收的活动。虚引用与软引用和弱引用的一个区别在于:虚引用必须和引用队列 (ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,若是发现它还有虚引用,就会在回收对象的内存以前,把这个虚引用加入到与之 关联的引用队列中。
3.2.三、典型的垃圾回收算法介绍
一、标记-清除算法(Mark-Sweep)
最基础的垃圾回收算法,分为“标注”和“清除”两个阶段:首先标记出全部须要回收的对象,在标记完成后统一回收掉全部被标记的对象。
标记过程:为了可以区分对象是live的,能够为每一个对象添加一个marked字段,该字段在对象建立的时候,默认值是false。
清除过程:去遍历堆中全部对象,并找出未被mark的对象,进行回收。与此同时,那些被mark过的对象的marked字段的值会被从新设置为false,以便下次的垃圾回收。
缺点:效率低,空间问题(产生大量不连续的内存碎片),后续可能发生大对象不能找到可利用空间的问题。
image.png
二、复制算法(Copying)——新生代的收集算法就是这种,可是比例不是1:1,而是(8+1):1
为了解决Mark-Sweep算法内存碎片化的缺陷而被提出的算法。按内存容量将内存划分为大小相等的两块,每次只使用其中一块。当这一块内存满后将尚存活的对象复制到另外一块上去,把已使用的内存空间一次清理掉。这种算法虽然实现简单,内存效率高,不易产生碎片,可是最大的问题是可用内存被压缩到了本来的一半。且存活对象增多的话,Copying算法的效率会大大下降。
image.png
三、标记-整理算法(Mark-Compact)——老年代的收集算法
结合了以上两个算法,标记阶段和Mark-Sweep算法相同,标记后不是清理对象,而是将全部存活对象移向内存的一端,而后清除端边界外的对象。如图:
image.png
四、分代收集算法(Generational Collection)
分代收集法是目前大部分JVM所采用的方法,其核心思想是根据对象存活的不一样生命周期将内存划分为不一样的域,通常状况下将GC堆划分为老生代(Tenured/Old Generation)和新生代(Young Generation)。
老生代的特色是每次垃圾回收时只有少许对象须要被回收,新生代的特色是每次垃圾回收时都有大量垃圾须要被回收,所以能够根据不一样区域选择不一样的算法。
目前大部分JVM的GC对于新生代都采起复制算法(Copying),由于新生代中每次垃圾回收都要回收大部分对象,即要复制的操做比较少,但一般并非按照1:1来划分新生代。通常将新生代划分为一块较大的Eden空间和两个较小的Survivor空间(From Space, To Space),每次使用Eden空间和其中的一块Survivor空间,当进行回收时,将该两块空间中还存活的对象复制到另外一块Survivor空间中。
老年代中的对象存活率高,没有额外空间对它进行分配,使用“标记-清理”或“标记-整理”算法来进行回收。
3.三、JVM处理FULLGC经验
3.3.一、内存泄漏
一、产生缘由
1)JVM内存太小。
2)程序不严密,产生了过多的垃圾。
二、通常状况下,在程序上的体现为:
1)内存中加载的数据量过于庞大,如一次从数据库取出过多数据。
2)集合类中有对对象的引用,使用完后未清空,使得JVM不能回收。
3)代码中存在死循环或循环产生过多重复的对象实体。
4)使用的第三方软件中的BUG。
5)启动参数内存值设定的太小。
3.3.二、Java内存泄漏的排查案例
一、肯定频繁Full GC现象,找出进程惟一ID
使用jps(jps -l)或ps(ps aux | grep tomat)找出这个进程在本地虚拟机的惟一ID(LVMID,Local Virtual Machine Identifier)
二、再使用“虚拟机统计信息监视工具:jstat”(jstat -gcutil 20954 1000)查看已使用空间站总空间的百分比,能够看到FGC的频次。
image.png
三、找出致使频繁Full GC的缘由,找出出现问题的对象
分析方法一般有两种:
1)把堆dump下来再用MAT等工具进行分析,但dump堆要花较长的时间,而且文件巨大,再从服务器上拖回本地导入工具,这个过程有些折腾,不到万不得已最好别这么干。
2)更轻量级的在线分析,使用“Java内存影像工具:jmap”生成堆转储快照(通常称为headdump或dump文件)。
jmap命令格式:jmap -histo:live 20954
四、一个工具:BTrace,没有使用过
5.一、HTTP请求报文和响应报文
image.png
5.二、HTTPS为何是安全的?HTTPS的加密方式有哪些?
5.2.一、HTTPS的工做原理说明HTTPS是安全的
image.png
客户端在使用HTTPS方式与Web服务器通讯时有如下几个步骤,如图所示。
一、客户使用https的URL访问Web服务器,要求与Web服务器创建SSL链接。
二、Web服务器收到客户端请求后,会将网站的证书信息(证书中包含公钥)传送一份给客户端。
三、客户端的浏览器与Web服务器开始协商SSL链接的安全等级,也就是信息加密的等级。
四、客户端的浏览器根据双方赞成的安全等级,创建会话密钥,而后利用网站的公钥将会话密钥加密,并传送给网站。
五、Web服务器利用本身的私钥解密出会话密钥。
六、Web服务器利用会话密钥加密与客户端之间的通讯。
image.png
5.2.二、HTTPS的加密方式有哪些?
一、对称加密
对称加密是指加密和解密使用相同密钥的加密算法。它要求发送方和接收方在安全通讯以前,商定一个密钥。对称算法的安全性依赖于密钥,泄漏密钥就意味着任何人均可以对他们发送或接收的消息解密,因此密钥的保密性对通讯相当重要。
二、更多的加密方式的了解
5.三、TCP三次握手协议,四次挥手
第一次握手:主机A发送位码为syn=1,随机产生seq number=1234567的数据包到服务器,主机B由SYN=1知道,A要求创建联机;
第二次握手:主机B收到请求后要确认联机信息,向A发送ack number=(主机A的seq+1),syn=1,ack=1,随机产生seq=7654321的包;
第三次握手:主机A收到后检查ack number是否正确,即第一次发送的seq number+1,以及位码ack是否为1,若正确,主机A会再发送ack number=(主机B的seq+1),ack=1,主机B收到后确认seq值与ack=1则链接创建成功。
完成三次握手,主机A与主机B开始传送数据。
5.四、OAuth协议介绍
image.png
5.五、防盗链Referer
Referer请求头: 表明当前访问时从哪一个网页链接过来的。
当Referer未存在或者是从其余站点访问咱们资源的时候就直接重定向到咱们的主页,这样既能够防止咱们的资源被窃取。
6.一、AOP的实现原理
spring框架对于这种编程思想的实现基于两种动态代理模式,分别是JDK动态代理 及 CGLIB的动态代理,这两种动态代理的区别是 JDK动态代理须要目标对象实现接口,而 CGLIB的动态代理则不须要。下面咱们经过一个实例来实现动态代理,进而帮助咱们理解面向切面编程。
JDK的动态代理要使用到一个类 Proxy 用于建立动态代理的对象,一个接口 InvocationHandler用于监听代理对象的行为,其实动态代理的本质就是对代理对象行为的监听。
6.二、Spring MVC工做原理
Spring的MVC框架主要由DispatcherServlet、处理器映射、处理器(控制器)、视图解析器、视图组成。
6.2.一、SpringMVC原理图
image.png
6.2.二、SpringMVC运行原理
一、客户端请求提交到DispatcherServlet
二、由DispatcherServlet控制器查询一个或多个HandlerMapping,找处处理请求的Controller
三、DispatcherServlet将请求提交到Controller
四、Controller调用业务逻辑处理后,返回ModelAndView
五、DispatcherServlet查询一个或多个ViewResoler视图解析器,找到ModelAndView指定的视图
六、视图负责将结果显示到客户端
6.2.三、SpringMVC核心组件
一、DispatcherServlet:中央控制器,把请求给转发到具体的控制类
二、Controller:具体处理请求的控制器
三、HandlerMapping:映射处理器,负责映射中央处理器转发给controller时的映射策略
四、ModelAndView:服务层返回的数据和视图层的封装类
五、ViewResolver:视图解析器,解析具体的视图
六、Interceptors :拦截器,负责拦截咱们定义的请求而后作处理工做
6.2.四、Servlet 生命周期
Servlet 生命周期可被定义为从建立直到毁灭的整个过程。如下是 Servlet 遵循的过程:
一、Servlet 经过调用 init () 方法进行初始化。
二、Servlet 调用 service() 方法来处理客户端的请求。
三、Servlet 经过调用 destroy() 方法终止(结束)。
四、最后,Servlet 是由 JVM 的垃圾回收器进行垃圾回收的。
6.2.五、Spring容器初始化过程
Spring 启动时读取应用程序提供的Bean配置信息,并在Spring容器中生成一份相应的Bean配置注册表,而后根据这张注册表实例化Bean,装配号Bean之间的依赖关系,为上层应用提供准备就绪的运行环境。
image.png
7.一、分布式下如何保证事务一致性
分布式事务,常见的两个处理办法就是两段式提交和补偿。
7.1.一、两段式提交
分布式事务将提交分红两个阶段:
prepare;
commit/rollback
在分布式系统中,每一个节点虽然能够知晓本身的操做是成功或者失败,却没法知道其余节点的操做的成功或失败。当一个事务跨越多个节点时,为了保持事务的ACID特性,须要引入一个做为协调者的组件来统一掌控全部节点(参与者)的操做结果并最终指示这些节点是否须要把操做结果进行真正的提交。算法步骤以下:
第一阶段:
一、协调者会问全部的参与者,是否能够执行提交操做。
二、各个参与者开始事务执行的准备工做,如:为资源上锁,预留资源。
三、参与者响应协调者,若是事务的准备工做成功,则回应“能够提交”,不然回应“拒绝提交”。
第二阶段:
一、若是全部的参与者都回应“能够提交”。那么协调者向全部的参与者发送“正式提交”的命令。参与者完成正式提交并释放全部资源,而后回应“完成”,协调者收集各节点的“完成”回应后结束这个Global Transaction
二、若是有一个参与者回应“拒绝提交”,那么协调者向全部的参与者发送“回滚操做”,并释放全部资源,而后回应“回滚完成”,协调者收集各节点的“回滚”回应后,取消这个Global Transaction。
7.1.二、三段式提交
三段提交的核心理念是:在询问的时候并不锁定资源,除非全部人都赞成了,才开始锁资源。他把二段提交的第一个段break成了两段:询问,而后再锁资源。最后真正提交。
7.1.二、事务补偿,最终一致性
补偿比较好理解,先处理业务,而后定时或者回调里,检查状态是否是一致的,若是不一致采用某个策略,强制状态到某个结束状态(通常是失败状态)。
9.一、kafka消息队列
一、避免数据丢失
producer:
加大重试次数
同步发送
对于单条数据过大,要设置可接收的单条数据的大小
对于异步发送,经过回调函数来感知丢消息
block.on.buffer.full = true
consumer:
enable.auto.commit=false 关闭自动提交位移
二、避免消息乱序
假设a,b两条消息,a先发送后因为发送失败重试,这时顺序就会在b的消息后面,能够设置max.in.flight.requests.per.connection=1来避免。
max.in.flight.requests.per.connection:限制客户端在单个链接上可以发送的未响应请求的个数,设置此值是1表示kafka broker在响应请求以前client不能再向同一个broker发送请求,但吞吐量会降低。
三、避免消息重复
使用第三方redis的set
9.二、ZooKeeper的原理
9.三、SOA相关,RPC两种实现方式:基于HTTP和基于TCP
9.四、Netty
何时会触发full gc
JAVA 线程状态转换图示
为何说B+比B树更适合实际应用中操做系统的文件索引和数据库索引?
JVM :英文名称(Java Virtual Machine),就是咱们耳熟能详的 Java 虚拟机。它只认识 xxx.class 这种类型的文件,它可以将 class 文件中的字节码指令进行识别并调用操做系统向上的 API 完成动做。因此说,jvm 是 Java 可以跨平台的核心,具体的下文会详细说明。
JRE :英文名称(Java Runtime Environment),咱们叫它:Java 运行时环境。它主要包含两个部分,jvm 的标准实现和 Java 的一些基本类库。它相对于 jvm 来讲,多出来的是一部分的 Java 类库。
JDK :英文名称(Java Development Kit),Java 开发工具包。jdk 是整个 Java 开发的核心,它集成了 jre 和一些好用的小工具。例如:javac.exe,java.exe,jar.exe 等。
显然,这三者的关系是:一层层的嵌套关系。JDK>JRE>JVM。
借一张图简单说明一下
而后是前端技术一些概念,虽然如今基本是先后端分离的模式开发,但也须要理解一些基础吧!
javascript==js
js是一种脚本语言,在html中,css管理位置,html主管内容,而js主管动做,减轻后台的操做,能够很简单的就实现对输入的数据的验证。好比说注册的时候,用js中的ajax到后台实时验证本用户名是否已经被注册,验证码是否正确或者用来实现异步更新,为用户带来更好的体验。用jquery来验证密码的合法性,输入密码与确认密码是否同样,从而反馈到html页面上,能够经过操控css或者html页面内容来确认输入内容是否有错。可是ajax和jquery都是js的一个库。
js!=jsp
js(javascript) jsp(java server pages)
之前在知乎里面看到了一句至关经典的回答,js与jsp的关系就是雷锋和雷峰塔的关系。从这句话能够看出它们俩没有任何联系,而实际上也是这样。jsp其实能够看作一种脚本语言,须要用servlet来编译实现,然而做为一种脚本语言它有至关强大,在其中能够嵌入java代码,jsp中几乎能够使用所有的java类 。其实就是能够把jsp当作html来做为网页显示出来,并且其上还能够嵌套java语言,也能够嵌套其余的语言相似,固然都序言用servlet来编译实现。jsp做为字节码文件执行后能够直接运行,没必要每次都要编译,速度快。可能我表述仍是有点问题,可是jsp和js大多都应用于web网页的编写上,jsp能够看作html和java的结合体,固然js就能够在jsp上实现一些动做,特效,验证功能,与在html中所实现的效果同样。由于jsp是在服务器端解释执行的,服务器执行转化后的.class程序,客户端接收到的只是服务器发来的html代码,看不到jsp代码。而js能够在客户端经过查看源代码显示出来。
java、jsp
java是一种编程语言,jsp只是至关于java里面的servlet部分
后言
记得刚开始开发网站的时候,个人一个学长叫我去看jsp,而后我就用了一个星期看js去了,我还觉得js就是jsp,而后就jj了,javascript和jsp也没差多少,因此作了不少的无用功,多走了不应走的弯路,不事后来两个东西都用上了,并且用处都还很是大。
重要的事情说三遍,
js!=jsp,js!=jsp,js!=jsp。
js==javascript
jsp==java server pages
基础题目
问了对Java的Map了解吗,请说出其中的几个接口,怎么获取key,怎么判断是否包含key,哪些实现了Map接口,map的hash以及数据结构,1.7和1.8的区别等。
二、写一个栈的实现,用数组,确保最大队列长度为k(我当时问了满了要不要什么策略),第一次我用front和end两个指针,而后他问我一个变量能够不(我说的不能够),而后我说front + size(队列中的元素)能够,而后手写。
三、问了一些juc并发库有哪些类(futrue,futruetask,excutors,executorthreadpool,countdownlatch,cyclicbarrier,symphone等)
四、怎么确保当全部线程执行到某个点等待,直到全部线程都执行到时一块儿往下执行(cyclicbarrier)。
五、cyclicbarrier和countdownlatch有什么区别,以及应用场景。
怎么查看是哪一条SQL执行慢(这个以前在面拼多多的时候也被问到过,但楼主没去看,被本身不善于总结坑了),slow_query_log,long_query_time,slow_query_log_file,log_queries_not_using_indexes这个命令你们能够去看下
四、ACID的意义(原子性,一致性,隔离性,持久性,当时脑子懵逼了,只说出了两个)
五、数据库的四种隔离级别,怎么避免脏读
六、hashmap和currenthashmap的数据结构,volatile了解吗,写个volatile的应用场景(我就说了单列的双重检验)
七、jdk1.8的特性。
九、还有什么我没有问到的吗(我说我什么方向的都有一点研究),而后面试官看了下学历,渣渣本科,好吧不问了。
1. junit用法,before,beforeClass,after, afterClass的执行顺序
2. 分布式锁
3. nginx的请求转发算法,如何配置根据权重转发
4. 用hashmap实现redis有什么问题(死锁,死循环,可用ConcurrentHashmap)
5. 线程的状态
5. 线程的阻塞的方式
6. sleep和wait的区别
7. hashmap的底层实现
8. 一万我的抢100个红包,如何实现(不用队列),如何保证2我的不能抢到同一个红包,可用分布式锁
9. java内存模型,垃圾回收机制,不可达算法
10. 两个Integer的引用对象传给一个swap方法在方法内部交换引用,返回后,两个引用的值是否会发现变化
11. aop的底层实现,动态代理是如何动态,假若有100个对象,如何动态的为这100个对象代理
12. 是否用过maven install。 maven test。git(make install是安装本地jar包)
13. tomcat的各类配置,如何配置docBase
14. spring的bean配置的几种方式
15. web.xml的配置
16. spring的监听器。
17. zookeeper的实现机制,有缓存,如何存储注册服务的
18. IO会阻塞吗?readLine是否是阻塞的
19. 用过spring的线程池仍是java的线程池?
20. 字符串的格式化方法 (20,21这两个问题问的过低级了)
21. 时间的格式化方法
22. 定时器用什么作的
23. 线程如何退出结束
24. java有哪些锁?乐观锁 悲观锁 synchronized 可重入锁 读写锁,用过reentrantlock吗?reentrantlock与synmchronized的区别
25. ThreadLocal的使用场景
26. java的内存模型,垃圾回收机制
27. 为何线程执行要调用start而不是直接run(直接run,跟普通方法没什么区别,先调start,run才会做为一个线程方法运行)
28. qmq消息的实现机制(qmq是去哪儿网本身封装的消息队列)
29. 遍历hashmap的三种方式
30. jvm的一些命令
31. memcache和redis的区别
32. mysql的行级锁加在哪一个位置
33. ConcurrentHashmap的锁是如何加的?是否是分段越多越好
34. myisam和innodb的区别(innodb是行级锁,myisam是表级锁)
35. mysql其余的性能优化方式
36. linux系统日志在哪里看
37. 如何查看网络进程
38. 统计一个整数的二进制表示中bit为1的个数
39. jvm内存模型,java内存模型
40. 如何把java内存的数据所有dump出来
41. 如何手动触发全量回收垃圾,如何当即触发垃圾回收
42. hashmap若是只有一个写其余全读会出什么问题
43. git rebase
44. mongodb和hbase的区别
45. 如何解决并发问题
46. volatile的用途
47. java线程池(好像以前个人理解有问题)
48. mysql的binlog
49. 代理模式
50. mysql是如何实现事务的
51. 读写分离什么时候强制要读主库,读哪一个从库是经过什么方式决定的,从库的同步mysql用的什么方式
52. mysql的存储引擎
53. mysql的默认隔离级别,其余隔离级别
54. 将一个链表反转(用三个指针,可是每次只发转一个)
55. spring Aop的实现原理,具体说说
56. 什么时候会内存泄漏,内存泄漏会抛哪些异常
57. 是否用过Autowire注解
58. spring的注入bean的方式
59. sql语句各类条件的执行顺序,如select, where, order by, group by
60. select xx from xx where xx and xx order by xx limit xx; 如何优化这个(看explain)
61. 四则元算写代码
62. 统计100G的ip文件中出现ip次数最多的100个ip
63. zookeeper的事物,结点,服务提供方挂了如何告知消费方
64. 5台服务器如何选出leader(选举算法)
65. 适配器和代理模式的区别
66. 读写锁
67. static加锁
68. 事务隔离级别
69. 门面模式,类图(外观模式)
70. mybatis如何映射表结构
71. 二叉树遍历
72. 主从复制
73. mysql引擎区别
74. 静态内部类加载到了哪一个区?方法区
75. class文件编译后加载到了哪
76. web的http请求如何总体响应时间变长致使处理的请求数变少,该如何处理?用队列,当处理不了那么多http请求时将请求放到队列
中慢慢处理,web如何实现队列
77. 线程安全的单例模式
78. 快速排序性能考虑
79. volatile关键字用法
80. 求表的size,或作数据统计可用什么存储引擎
81. 读多写少可用什么引擎
82. 假如要统计多个表应该用什么引擎
83. concurrenhashmap求size是如何加锁的,若是刚求完一段后这段发生了变化该如何处理
84. 1000个苹果放10个篮子,怎么放,能让我拿到全部可能的个数
85. 可重入的读写锁,可重入是如何实现的?
86. 是否用过NIO
87. java的concurrent包用过没
88. sting s=new string("abc")分别在堆栈上新建了哪些对象
89. java虚拟机的区域分配,各区分别存什么
90. 分布式事务(JTA)
91. threadlocal使用时注意的问题(ThreadLocal和Synchonized都用于解决多线程并发访问。可是ThreadLocal与synchronized有本质的区别。synchronized是利用锁的机制,使变量或代码块在某一时该只能被一个线程访问。而ThreadLocal为每个线程都提供了变量的副本,使得每一个线程在某一时间访问到的并非同一个对象,这样就隔离了多个线程对数据的数据共享。而Synchronized却正好相反,它用于在多个线程间通讯时可以得到数据共享)
92. java有哪些容器(集合,tomcat也是一种容器)
93. 二分查找算法
94. myisam的优势,和innodb的区别
95. redis能存哪些类型
96. http协议格式,get和post的区别
97. 可重入锁中对应的wait和notify
98. redis能把内存空间交换进磁盘中吗(这个应该是能够的,可是那个面试官非跟我说不能够)
99. java线程池中基于缓存和基于定长的两种线程池,当请求太多时分别是如何处理的?定长的事用的队列,若是队列也满了呢?交换进磁盘?基于缓存的线程池解决方法呢?
100. synchronized加在方法上用的什么锁
101. 可重入锁中的lock和trylock的区别
102. innodb对一行数据的读会枷锁吗?不枷锁,读实际读的是副本
103. redis作缓存是分布式存的?不一样的服务器上存的数据是否重复?guava cache呢?是否重复?不一样的机器存的数据不一样
104. 用awk统计一个ip文件中top10
105. 对表作统计时可直接看schema info信息,即查看表的系统信息
106. mysql目前用的版本
107. 公司经验丰富的人给了什么帮助?(通常boss面会问这些)
108. 本身相对于同样的应届生有什么优点
109. 本身的好的总结习惯给本身从此的工做带了什么帮助,举例为证
110. 原子类,线程安全的对象,异常的处理方式
111. 4亿个int数,如何找出重复的数(用hash方法,建一个2的32次方个bit的hash数组,每取一个int数,可hash下2的32次方找到它在hash数组中的位置,而后将bit置1表示已存在)
112. 4亿个url,找出其中重复的(考虑内存不够,经过hash算法,将url分配到1000个文件中,不一样的文件间确定就不会重复了,再分别找出重复的)
有1万个数组,每一个数组有1000个整数,每一个数组都是降序的,从中找出最大的N个数,N<1000
113. LinkedHashmap的底层实现
114. 类序列化时类的版本号的用途,若是没有指定一个版本号,系统是怎么处理的?若是加了字段会怎么样?
115. Override和Overload的区别,分别用在什么场景
116. java的反射是如何实现的
手撕字符串转int
2.数据库事务隔离级别
3.spring 事务
4.数据库索引失效的场景
5.组合索引(A,B)若查询B列是否用到了索引
6.若是A是string类型,而查询的时候是1314151617 用到了索引吗?能查到这条数据吗?
7.数据库怎么实现乐观锁?
8.redis pipeline了解吗?
9.秒杀场景?怎么实现。redis怎么限流,限流算法。
10.redis实现队列、实现优先级队列。
11.分布式锁。
0.基础部分
不管是哪种编程语言,基础永远是你不能忽视的部分。如下是比较常出现的十个点,固然最好是全都能熟悉。
1.Java高级部分
一、Java内存结构,spring的aop的实现方法,java数据库问题定位和性能调优;
二、关于Java异常的续承层次结构,讲述异常的续承关系;
三、java中重载和重写有什么区别,分别用什么关键字;
四、关于分布式消息队列,分布式缓存;
五、关于hashmap源码实现, jdk
六、关于设计模式,uml,jvm 内存回收机制问题
七、java线程如何启动?java中加锁的方式有哪些,怎么个写法?
八、对乐观锁和悲观锁的理解;
九、ORACLE中的SQL如何进行优化,都有哪些方式?事务有哪些特性,在ORACLE中隔离有哪些级别?
十、介绍一下本身最近作的一个典型的项目;
十一、在项目中遇到了哪些问题,本身是如何解决的 ;
十二、目前系统支撑的用户量是多少,假如用户量提高10倍,系统会出现什么样的问题,如何从新设计系统【这里主要是想了解您的问题预见能力以及问题解决能力,考查思路】
1三、使用memcached是一个什么样的原理
1四、如何存放数据到memcached集群中,介绍一下这个过程。跟进的问题,讲一下一致性哈希算法的实现原理。
1五、JVM中堆是如何管理的,JVM的内存回收机制,介绍一下
1六、分布式事务实现方式
1七、热点帐户问题(项目中有就会问)
阿里技术面试(电面)涉及Java基础点(可参考):
Java面试题分享:
2.框架部分
关于这部分,主要考的也是一些框架部门中较为基础的内容。
3.数据库
5.前端基础
Set接口
Set不容许包含相同的元素,若是试图把两个相同元素加入同一个集合中,add方法返回false。
Set判断两个对象相同不是使用==运算符,而是根据equals方法。也就是说,只要两个对象用equals方法比较返回true,Set就不会接受这两个对象。
HashSet与TreeSet都是基于Set接口的实现类。其中TreeSet是Set的子接口SortedSet的实现类。Set接口及其子接口、实现类的结构以下所示:
|——SortedSet接口——TreeSet实现类
Set接口——|——HashSet实现类
|——LinkedHashSet实现类
HashSet
HashSet有如下特色
不能保证元素的排列顺序,顺序有可能发生变化
不是同步的
集合元素能够是null,但只能放入一个null
当向HashSet结合中存入一个元素时,HashSet会调用该对象的hashCode()方法来获得该对象的hashCode值,而后根据 hashCode值来决定该对象在HashSet中存储位置。
简单的说,HashSet集合判断两个元素相等的标准是两个对象经过equals方法比较相等,而且两个对象的hashCode()方法返回值相等
注意,若是要把一个对象放入HashSet中,重写该对象对应类的equals方法,也应该重写其hashCode()方法。其规则是若是两个对象经过equals方法比较返回true时,其 hashCode也应该相同。另外,对象中用做equals比较标准的属性,都应该用来计算 hashCode的值。
TreeSet
TreeSet类型是J2SE中惟一可实现自动排序的类型
TreeSet是SortedSet接口的惟一实现类,TreeSet能够确保集合元素处于排序状态。TreeSet支持两种排序方式,天然排序 和定制排序,其中天然排序为默认的排序方式。向 TreeSet中加入的应该是同一个类的对象。
TreeSet判断两个对象不相等的方式是两个对象经过equals方法返回false,或者经过CompareTo方法比较没有返回0
天然排序
天然排序使用要排序元素的CompareTo(Object obj)方法来比较元素之间大小关系,而后将元素按照升序排列。
Java提供了一个Comparable接口,该接口里定义了一个compareTo(Object obj)方法,该方法返回一个整数值,实现了该接口的对象就能够比较大小。
obj1.compareTo(obj2)方法若是返回0,则说明被比较的两个对象相等,若是返回一个正数,则代表obj1大于obj2,若是是 负数,则代表obj1小于obj2。
若是咱们将两个对象的equals方法老是返回true,则这两个对象的compareTo方法返回应该返回0
定制排序
天然排序是根据集合元素的大小,以升序排列,若是要定制排序,应该使用Comparator接口,实现 int compare(To1,To2)方法
LinkedHashSet
LinkedHashSet集合一样是根据元素的hashCode值来决定元素的存储位置,可是它同时使用链表维护元素的次序。这样使得元素看起 来像是以插入顺 序保存的,也就是说,当遍历该集合时候,LinkedHashSet将会以元素的添加顺序访问集合的元素。
LinkedHashSet在迭代访问Set中的所有元素时,性能比HashSet好,可是插入时性能稍微逊色于HashSet。
有许多人学了很长时间的Java,但一直不明白hashCode方法的做用,
我来解释一下吧。首先,想要明白hashCode的做用,你必需要先知道Java中的集合。
java的HashCode方法
总的来讲,Java中的集合(Collection)有两类,一类是List,再有一类是Set。
你知道它们的区别吗?前者集合内的元素是有序的,元素能够重复;后者元素无序,但元素不可重复。
那么这里就有一个比较严重的问题了:要想保证元素不重复,可两个元素是否重复应该依据什么来判断呢?
这就是Object.equals方法了。可是,若是每增长一个元素就检查一次,那么当元素不少时,后添加到集合中的元素比较的次数就很是多了。 也就是说,若是集合中如今已经有1000个元素,那么第1001个元素加入集合时,它就要调用1000次equals方法。这显然会大大下降效率。
因而,Java采用了哈希表的原理。哈希(Hash)其实是我的名,因为他提出一哈希算法的概念,因此就以他的名字命名了。 哈希算法也称为散列算法,是将数据依特定算法直接指定到一个地址上。若是详细讲解哈希算法,那须要更多的文章篇幅,我在这里就不介绍了。
初学者能够这样理解,hashCode方法实际上返回的就是对象存储的物理地址(实际可能并非)。 这样一来,当集合要添加新的元素时,先调用这个元素的hashCode方法,就一会儿能定位到它应该放置的物理位置上。 若是这个位置上没有元素,它就能够直接存储在这个位置上,不用再进行任何比较了;若是这个位置上已经有元素了, 就调用它的equals方法与新元素进行比较,相同的话就不存了,不相同就散列其它的地址。 因此这里存在一个冲突解决的问题。这样一来实际调用equals方法的次数就大大下降了,几乎只须要一两次。 因此,Java对于eqauls方法和hashCode方法是这样规定的:
一、若是两个对象相同,那么它们的hashCode值必定要相同;
二、若是两个对象的hashCode相同,它们并不必定相同
上面说的对象相同指的是用eqauls方法比较。你固然能够不按要求去作了,但你会发现,相同的对象能够出如今Set集合中。同时,增长新元素的效率会大大降低。
hashcode这个方法是用来鉴定2个对象是否相等的。 那你会说,不是还有equals这个方法吗? 不错,这2个方法都是用来判断2个对象是否相等的。可是他们是有区别的。 通常来说,equals这个方法是给用户调用的,若是你想判断2个对象是否相等,你能够重写equals方法,而后在代码中调用,就能够判断他们是否相等 了。简单来说,equals方法主要是用来判断从表面上看或者从内容上看,2个对象是否是相等。
举个例子,有个学生类,属性只有姓名和性别,那么咱们能够 认为只要姓名和性别相等,那么就说这2个对象是相等的。
hashcode方法通常用户不会去调用,好比在hashmap中,因为key是不能够重复的,他在判断key是否是重复的时候就判断了hashcode 这个方法,并且也用到了equals方法。这里不能够重复是说equals和hashcode只要有一个不等就能够了!因此简单来说,hashcode相 当因而一个对象的编码,就好像文件中的md5,他和equals不一样就在于他返回的是int型的,比较起来不直观。咱们通常在覆盖equals的同时也要 覆盖hashcode,让他们的逻辑一致。
举个例子,仍是刚刚的例子,若是姓名和性别相等就算2个对象相等的话,那么hashcode的方法也要返回姓名 的hashcode值加上性别的hashcode值,这样从逻辑上,他们就一致了。 要从物理上判断2个对象是否相等,用==就能够了。
10.一、系统作了哪些安全防御
一、XSS(跨站脚本攻击)
全称是跨站脚本攻击(Cross Site Scripting),指攻击者在网页中嵌入恶意脚本程序。
XSS防范:
XSS之因此会发生,是由于用户输入的数据变成了代码。所以,咱们须要对用户输入的数据进行HTML转义处理,将其中的“尖括号”、“单引号”、“引号”之类的特殊字符进行转义编码。
二、CSRF(跨站请求伪造)
攻击者盗用了你的身份,以你的名义向第三方网站发送恶意请求。
CSRF的防护:
1)尽可能使用POST,限制GET
2)将cookie设置为HttpOnly
3)增长token
4)经过Referer识别
三、SQL注入
使用预编译语句(PreparedStatement),这样的话即便咱们使用sql语句伪形成参数,到了服务端的时候,这个伪造sql语句的参数也只是简单的字符,并不能起到攻击的做用。
作最坏的打算,即便被’拖库‘('脱裤,数据库泄露')。数据库中密码不该明文存储的,能够对密码使用md5进行加密,为了加大破解成本,因此能够采用加盐的(数据库存储用户名,盐(随机字符长),md5后的密文)方式。
四、DDOS
最直接的方法增长带宽。可是攻击者用各地的电脑进行攻击,他的带宽不会耗费不少钱,但对于服务器来讲,带宽很是昂贵。
云服务提供商有本身的一套完整DDoS解决方案,而且能提供丰富的带宽资源
1. 什么是spring?
Spring 是个java企业级应用的开源开发框架。Spring主要用来开发Java应用,可是有些扩展是针对构建J2EE平台的web应用。Spring 框架目标是简化Java企业级应用开发,并经过POJO为基础的编程模型促进良好的编程习惯。
2. 使用Spring框架的好处是什么?
3. Spring由哪些模块组成?
如下是Spring 框架的基本模块:
4. 核心容器(应用上下文) 模块
这是基本的Spring模块,提供spring 框架的基础功能,BeanFactory 是 任何以spring为基础的应用的核心。Spring 框架创建在此模块之上,它使Spring成为一个容器。
5. BeanFactory – BeanFactory 实现举例
Bean 工厂是工厂模式的一个实现,提供了控制反转功能,用来把应用的配置和依赖从正真的应用代码中分离。最经常使用的BeanFactory 实现是XmlBeanFactory 类。
6. XMLBeanFactory
最经常使用的就是org.springframework.beans.factory.xml.XmlBeanFactory ,它根据XML文件中的定义加载beans。该容器从XML 文件读取配置元数据并用它去建立一个彻底配置的系统或应用。
7. 解释AOP模块
AOP模块用于发给咱们的Spring应用作面向切面的开发, 不少支持由AOP联盟提供,这样就确保了Spring和其余AOP框架的共通性。这个模块将元数据编程引入Spring。
8. 解释JDBC抽象和DAO模块
经过使用JDBC抽象和DAO模块,保证数据库代码的简洁,并能避免数据库资源错误关闭致使的问题,它在各类不一样的数据库的错误信息之上,提供了一个统一的异常访问层。它还利用Spring的AOP 模块给Spring应用中的对象提供事务管理服务。
9. 解释对象/关系映射集成模块
Spring 经过提供ORM模块,支持咱们在直接JDBC之上使用一个对象/关系映射映射(ORM)工具,Spring 支持集成主流的ORM框架,如Hiberate,JDO和 iBATIS SQL Maps。Spring的事务管理一样支持以上全部ORM框架及JDBC。
10. 解释WEB 模块
Spring的WEB模块是构建在application context 模块基础之上,提供一个适合web应用的上下文。这个模块也包括支持多种面向web的任务,如透明地处理多个文件上传请求和程序级请求参数的绑定到你的业务对象。它也有对Jakarta Struts的支持。
12. Spring配置文件
Spring配置文件是个XML 文件,这个文件包含了类信息,描述了如何配置它们,以及如何相互调用。
13. 什么是Spring IOC 容器?
Spring IOC 负责建立对象,管理对象(经过依赖注入(DI),装配对象,配置对象,而且管理这些对象的整个生命周期。
14. IOC的优势是什么?
IOC 或 依赖注入把应用的代码量降到最低。它使应用容易测试,单元测试再也不须要单例和JNDI查找机制。最小的代价和最小的侵入性使松散耦合得以实现。IOC容器支持加载服务时的饿汉式初始化和懒加载。
15. ApplicationContext一般的实现是什么?
16. Bean 工厂和 Application contexts 有什么区别?
Application contexts提供一种方法处理文本消息,一个一般的作法是加载文件资源(好比镜像),它们能够向注册为监听器的bean发布事件。另外,在容器或容器内的对象上执行的那些不得不禁bean工厂以程序化方式处理的操做,能够在Application contexts中以声明的方式处理。Application contexts实现了MessageSource接口,该接口的实现以可插拔的方式提供获取本地化消息的方法。
17. 一个Spring的应用看起来象什么?
依赖注入
18. 什么是Spring的依赖注入?
依赖注入,是IOC的一个方面,是个一般的概念,它有多种解释。这概念是说你不用建立对象,而只须要描述它如何被建立。你不在代码里直接组装你的组件和服务,可是要在配置文件里描述哪些组件须要哪些服务,以后一个容器(IOC容器)负责把他们组装起来。
19. 有哪些不一样类型的IOC(依赖注入)方式?
20. 哪一种依赖注入方式你建议使用,构造器注入,仍是 Setter方法注入?
你两种依赖方式均可以使用,构造器注入和Setter方法注入。最好的解决方案是用构造器参数实现强制依赖,setter方法实现可选依赖。
Spring Beans
21.什么是Spring beans?
Spring beans 是那些造成Spring应用的主干的java对象。它们被Spring IOC容器初始化,装配,和管理。这些beans经过容器中配置的元数据建立。好比,以XML文件中<bean/> 的形式定义。
Spring 框架定义的beans都是单件beans。在bean tag中有个属性”singleton”,若是它被赋为TRUE,bean 就是单件,不然就是一个 prototype bean。默认是TRUE,因此全部在Spring框架中的beans 缺省都是单件。点击这里一图Spring Bean的生命周期。
22. 一个 Spring Bean 定义 包含什么?
一个Spring Bean 的定义包含容器必知的全部配置元数据,包括如何建立一个bean,它的生命周期详情及它的依赖。
23. 如何给Spring 容器提供配置元数据?
这里有三种重要的方法给Spring 容器提供配置元数据。
XML配置文件。
基于注解的配置。
基于java的配置。
24. 你怎样定义类的做用域?
当定义一个<bean> 在Spring里,咱们还能给这个bean声明一个做用域。它能够经过bean 定义中的scope属性来定义。如,当Spring要在须要的时候每次生产一个新的bean实例,bean的scope属性被指定为prototype。另外一方面,一个bean每次使用的时候必须返回同一个实例,这个bean的scope 属性 必须设为 singleton。
25. 解释Spring支持的几种bean的做用域
Spring框架支持如下五种bean的做用域:
缺省的Spring bean 的做用域是Singleton。
26. Spring框架中的单例bean是线程安全的吗?
不,Spring框架中的单例bean不是线程安全的。
27. 解释Spring框架中bean的生命周期
点击这里一图Spring Bean的生命周期。
28. 哪些是重要的bean生命周期方法? 你能重载它们吗?
有两个重要的bean 生命周期方法,第一个是setup , 它是在容器加载bean的时候被调用。第二个方法是 teardown 它是在容器卸载类的时候被调用。
The bean 标签有两个重要的属性(init-method和destroy-method)。用它们你能够本身定制初始化和注销方法。它们也有相应的注解(@PostConstruct和@PreDestroy)。
29. 什么是Spring的内部bean?
当一个bean仅被用做另外一个bean的属性时,它能被声明为一个内部bean,为了定义inner bean,在Spring 的 基于XML的 配置元数据中,能够在 <property/>或 <constructor-arg/> 元素内使用<bean/> 元素,内部bean一般是匿名的,它们的Scope通常是prototype。
30. 在 Spring中如何注入一个java集合?
Spring提供如下几种集合的配置元素:
31. 什么是bean装配?
装配,或bean 装配是指在Spring 容器中把bean组装到一块儿,前提是容器须要知道bean的依赖关系,如何经过依赖注入来把它们装配到一块儿。
32. 什么是bean的自动装配?
Spring 容器可以自动装配相互合做的bean,这意味着容器不须要<constructor-arg>和<property>配置,能经过Bean工厂自动处理bean之间的协做。
33. 解释不一样方式的自动装配
有五种自动装配的方式,能够用来指导Spring容器用自动装配方式来进行依赖注入
34.自动装配有哪些局限性?
自动装配的局限性是:
35. 你能够在Spring中注入一个null 和一个空字符串吗?
能够。
Spring注解
36. 什么是基于Java的Spring注解配置? 给一些注解的例子
基于Java的配置,容许你在少许的Java注解的帮助下,进行你的大部分Spring配置而非经过XML文件。
以@Configuration 注解为例,它用来标记类能够当作一个bean的定义,被Spring IOC容器使用。另外一个例子是@Bean注解,它表示此方法将要返回一个对象,做为一个bean注册进Spring应用上下文。点击这里学习JAVA几大元注解。
37. 什么是基于注解的容器配置?
相对于XML文件,注解型的配置依赖于经过字节码元数据装配组件,而非尖括号的声明。
开发者经过在相应的类,方法或属性上使用注解的方式,直接组件类中进行配置,而不是使用xml表述bean的装配关系。
38. 怎样开启注解装配?
注解装配在默认状况下是不开启的,为了使用注解装配,咱们必须在Spring配置文件中配置 <context:annotation-config/>元素。
39. @Required 注解
这个注解代表bean的属性必须在配置的时候设置,经过一个bean定义的显式的属性值或经过自动装配,若@Required注解的bean属性未被设置,容器将抛出BeanInitializationException。
40. @Autowired 注解
@Autowired 注解提供了更细粒度的控制,包括在何处以及如何完成自动装配。它的用法和@Required同样,修饰setter方法、构造器、属性或者具备任意名称和/或多个参数的PN方法。
41. @Qualifier 注解
当有多个相同类型的bean却只有一个须要自动装配时,将@Qualifier 注解和@Autowire 注解结合使用以消除这种混淆,指定须要装配的确切的bean。点击这里学习更多经常使用注解。
Spring数据访问
42.在Spring框架中如何更有效地使用JDBC?
使用SpringJDBC 框架,资源管理和错误处理的代价都会被减轻。因此开发者只需写statements 和 queries从数据存取数据,JDBC也能够在Spring框架提供的模板类的帮助下更有效地被使用,这个模板叫JdbcTemplate (例子见这里here)
43. JdbcTemplate
JdbcTemplate 类提供了不少便利的方法解决诸如把数据库数据转变成基本数据类型或对象,执行写好的或可调用的数据库操做语句,提供自定义的数据错误处理。
44. Spring对DAO的支持
Spring对数据访问对象(DAO)的支持旨在简化它和数据访问技术如JDBC,Hibernate or JDO 结合使用。这使咱们能够方便切换持久层。编码时也不用担忧会捕获每种技术特有的异常。
45. 使用Spring经过什么方式访问Hibernate?
在Spring中有两种方式访问Hibernate:
46. Spring支持的ORM
Spring支持如下ORM:
47.如何经过HibernateDaoSupport将Spring和Hibernate结合起来?
用Spring的 SessionFactory 调用 LocalSessionFactory。集成过程分三步:
48. Spring支持的事务管理类型
Spring支持两种类型的事务管理:
49. Spring框架的事务管理有哪些优势?
50. 你更倾向用那种事务管理类型?
大多数Spring框架的用户选择声明式事务管理,由于它对应用代码的影响最小,所以更符合一个无侵入的轻量级容器的思想。声明式事务管理要优于编程式事务管理,虽然比编程式事务管理(这种方式容许你经过代码控制事务)少了一点灵活性。
Spring面向切面编程(AOP)
51. 解释AOP
面向切面的编程,或AOP, 是一种编程技术,容许程序模块化横向切割关注点,或横切典型的责任划分,如日志和事务管理。
52. Aspect 切面
AOP核心就是切面,它将多个类的通用行为封装成可重用的模块,该模块含有一组API提供横切功能。好比,一个日志模块能够被称做日志的AOP切面。根据需求的不一样,一个应用程序能够有若干切面。在Spring AOP中,切面经过带有@Aspect注解的类实现。
52. 在Spring AOP 中,关注点和横切关注的区别是什么?
关注点是应用中一个模块的行为,一个关注点可能会被定义成一个咱们想实现的一个功能。
横切关注点是一个关注点,此关注点是整个应用都会使用的功能,并影响整个应用,好比日志,安全和数据传输,几乎应用的每一个模块都须要的功能。所以这些都属于横切关注点。
54. 链接点
链接点表明一个应用程序的某个位置,在这个位置咱们能够插入一个AOP切面,它其实是个应用程序执行Spring AOP的位置。
55. 通知
通知是个在方法执行前或执行后要作的动做,其实是程序执行时要经过SpringAOP框架触发的代码段。
Spring切面能够应用五种类型的通知:
56. 切点
切入点是一个或一组链接点,通知将在这些位置执行。能够经过表达式或匹配的方式指明切入点。
57. 什么是引入?
引入容许咱们在已存在的类中增长新的方法和属性。
58. 什么是目标对象?
被一个或者多个切面所通知的对象。它一般是一个代理对象。也指被通知(advised)对象。
59. 什么是代理?
代理是通知目标对象后建立的对象。从客户端的角度看,代理对象和目标对象是同样的。
60. 有几种不一样类型的自动代理?
BeanNameAutoProxyCreator
DefaultAdvisorAutoProxyCreator
Metadata autoproxying
61. 什么是织入。什么是织入应用的不一样点?
织入是将切面和到其余应用类型或对象链接或建立一个被通知对象的过程。
织入能够在编译时,加载时,或运行时完成。
62. 解释基于XML Schema方式的切面实现
在这种状况下,切面由常规类以及基于XML的配置实现。
63. 解释基于注解的切面实现
在这种状况下(基于@AspectJ的实现),涉及到的切面声明的风格与带有java5标注的普通java类一致。
Spring 的MVC
64. 什么是Spring的MVC框架?
Spring 配备构建Web 应用的全功能MVC框架。Spring能够很便捷地和其余MVC框架集成,如Struts,Spring 的MVC框架用控制反转把业务对象和控制逻辑清晰地隔离。它也容许以声明的方式把请求参数和业务对象绑定。
65. DispatcherServlet
Spring的MVC框架是围绕DispatcherServlet来设计的,它用来处理全部的HTTP请求和响应。
66. WebApplicationContext
WebApplicationContext 继承了ApplicationContext 并增长了一些WEB应用必备的特有功能,它不一样于通常的ApplicationContext ,由于它能处理主题,并找到被关联的servlet。
67. 什么是Spring MVC框架的控制器?
控制器提供一个访问应用程序的行为,此行为一般经过服务接口实现。控制器解析用户输入并将其转换为一个由视图呈现给用户的模型。Spring用一个很是抽象的方式实现了一个控制层,容许用户建立多种用途的控制器。
68. @Controller 注解
该注解代表该类扮演控制器的角色,Spring不须要你继承任何其余控制器基类或引用Servlet API。
69. @RequestMapping 注解
该注解是用来映射一个URL到一个类或一个特定的方处理法上。
【学习参考】
[荐]https://www.toutiao.com/c/user/84982888621/#mid=1589035076683780
[荐]https://www.toutiao.com/i6592700941210747405/
https://www.toutiao.com/a6590673631498469901/