java知识点总结

————————————————————

jav,a sync,hronized详解

———————————————————-

Java通过反射改变私有变量

———————————————————————————-

什么时候用接口,什么时候用抽象类

当描述一组方法的时候使用接口  当描述一个虚拟的物体的时候使用抽象类

————————————————————————————

线程同步方式

1.syncronized关键字修饰

2.同步代码块

3.使用violate实现线程同步

4.使用reentrantlock

5.使用局部变量threadlocal

————————————————————————————-

ArrayList是实现List接口的,底层采用数组实现

ArrayList提供了三个构造函数:

     ArrayList():默认构造函数,提供初始容量为10的空列表。

     ArrayList(int initialCapacity):构造一个具有指定初始容量的空列表。

            在每次添加新的元素时,ArrayList都会检查是否需要进行扩容操作,扩容操作带来数据向新数组的重新拷贝,所以如果我们知道具体业务数据量,

            在构造ArrayList时可以给ArrayList指定一个初始容量,这样就会减少扩容时数据的拷贝问题。
     ArrayList(Collection<? extends E> c):构造一个包含指定 collection 的元素的列表,这些元素是按照该 collection 的迭代器返回它们的顺序排列的。

  多个线程同时访问一个ArrayList实例:List list = Collections.synchronizedList(new ArrayList(…));

————————————————————————–

HashSet是基于HashMap来实现的,底层采用HashMap来保存元素

public Iterator<E> iterator() {        return map.keySet().iterator();    }

iterator()方法返回对此 set 中元素进行迭代的迭代器。返回元素的顺序并不是特定的。底层调用HashMap的keySet返回所有的key,

这点反应了HashSet中的所有元素都是保存在HashMap的key中

—————————————————————————

hashmap基于哈希表的 Map 接口的实现,以key-value的形式存在。在HashMap中,key-value总是会当做一个整体来处理,

系统会根据hash算法来来计算key-value的存储位置.

我们知道在Java中最常用的两种结构是数组和模拟指针(引用),几乎所有的数据结构都可以利用这两种来组合实现,HashMap也是如此。

实际上HashMap是一个“链表散列”,如下是它数据结构:

1.定义:HashMap实现了Map接口,继承AbstractMap

public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable

2.构造函数

HashMap提供了三个构造函数:

      HashMap():构造一个具有默认初始容量 (16) 和默认加载因子 (0.75) 的空 HashMap。

      HashMap(int initialCapacity):构造一个带指定初始容量和默认加载因子 (0.75) 的空 HashMap。

      HashMap(int initialCapacity, float loadFactor):构造一个带指定初始容量和加载因子的空 HashMap。

其中容量表示哈希表中桶的数量,初始容量是创建哈希表时的容量,加载因子是哈希表在其容量自动增加之前可以达到多满的一种尺度,

它衡量的是一个散列表的空间的使用程度,负载因子越大表示散列表的装填程度越高,反之愈小。

下面是java8的HashMap的数据结构,跟之前版本不一样的是当table达到一定的阀值(8)时,bucket就会由链表转换为红黑树的方式进行存储,

应该是考虑到链表的查找效率为O(n),红黑树的查找效率为O(lgn)


—————————————————————————-

CorrentHashMap的工作原理?

    jdk 1.6版: ConcurrenHashMap可以说是HashMap的升级版,ConcurrentHashMap是线程安全的,但是与Hashtablea相比,实现线程安全的方式不同。Hashtable是通过对hash表结构进行锁定,是阻塞式的,当一个线程占有这个锁时,其他线程必须阻塞等待其释放锁。ConcurrentHashMap是采用分离锁的方式,它并没有对整个hash表进行锁定,而是局部锁定,也就是说当一个线程占有这个局部锁时,不影响其他线程对hash表其他地方的访问。
    具体实现: ConcurrentHashMap内部有一个Segment数组, 该Segment对象可以充当锁。Segment对象内部有一个HashEntry数组,于是每个Segment可以守护若干个桶(HashEntry),每个桶又有可能是一个HashEntry连接起来的链表,存储发生碰撞的元素。
    每个ConcurrentHashMap在默认并发级下会创建包含16个Segment对象的数组,每个数组有若干个桶,当我们进行put方法时,通过hash方法对key进行计算,得到hash值,找到对应的segment,然后对该segment进行加锁,然后调用segment的put方法进行存储操作,此时其他线程就不能访问当前的segment,但可以访问其他的segment对象,不会发生阻塞等待。


    jdk 1.8版 在jdk 8中,ConcurrentHashMap不再使用Segment分离锁,而是采用一种乐观锁CAS算法来实现同步问题,但其底层还是“数组+链表->红黑树”的实现。

Concurren,tHashMap源码分析(JDK8版本)

-

CurrentHashMap的实现原理

——————————————————-

深入理解内存模型

——————————————————————

接口的成员变量都是public static final 修饰的是常量,   而抽象类则可以是各种类型。

抽象类可以有构造方法,接口中不能有构造方法

————————————————————————————————————————–

方法是可以和类名同名的,和构造方法唯一的区别就是,构造方法没有返回值
构造方法每次都是构造出新的对象,不存在多个线程同时读写同一对象中的属性的问题,所以不需要同步 。
如果父类中的某个方法使用了synchronized关键字,而子类中也覆盖了这个方法,默认情况下子类中的这个方法并不是同步的,
必须显示的在子类的这个方法中加上synchronized关键字才可。当然,也可以在子类中调用父类中相应的方法,这样虽然子类中的方法并不是同步的,
但子类调用了父类中的同步方法,也就相当子类方法也同步了

构造方法是一种特殊的方法,具有以下特点。
(1)构造方法的方法名必须与类名相同。
(2)构造方法没有返回类型,也不能定义为void,在方法名前面不声明方法类型。
(3)构造方法的主要作用是完成对象的初始化工作,它能够把定义对象时的参数传给对象的域。
(4)一个类可以定义多个构造方法,如果在定义类时没有定义构造方法,则编译系统会自动插入一个无参数的默认构造器,这个构造器不执行任何代码。
(5)构造方法可以重载,以参数的个数,类型,顺序。

—————————————————————————————————————————–

下面哪些是interface中合法方法定义?

boolean setFlags(Boolean [] results)

private float get(int x)

static int getCount()

static方法在interface中要有body
private修饰的方法不可以出现在interface中
选择第一个

——————————————————————————————————————————————–

Java 序列化 (Serializable) 的作用

序列化就是将一个对象的状态(各个属性量)保存起来,然后在适当的时候再获得。
序列化分为两大部分:序列化和反序列化。序列化是这个过程的第一部分,将数据分解成字节流,以便存储在文件中或在网络上传输。

反序列化就是打开字节流并重构对象。对象序列化不仅要将基本数据类型转换成字节表示,有时还要恢复数据。恢复数据要求有恢复数据的对象实例

序列化的什么特点:
如果某个类能够被序列化,其子类也可以被序列化。声明为static和transient类型的成员数据不能被序列化。因为static代表类的状态,

transient代表对象的临时数据。

—————————————————————————————————————————-

JAVA序列化与反序列化三种格式存取(默认格式、XML格式、JSON格式)

1.默认格式是二进制(需要对象实现Seralizable接口)

2.xml文件格式

3.json格式

———————————————————————————————————————————–

引起内存溢出的原因有很多种,常见的有以下几种:
l         内存中加载的数据量过于庞大,如一次从数据库取出过多数据;
l         集合类中有对对象的引用,使用完后未清空,使得JVM不能回收;
l         代码中存在死循环或循环产生过多重复的对象实体;
l         使用的第三方软件中的BUG;
l         启动参数内存值设定的过小;

内存溢出的解决

第一步,就是修改JVM启动参数,直接增加内存。这一点看上去似乎很简单,但很容易被忽略。JVM默认可以使用的内存为64M,

Tomcat默认可以使用的内存为128MB,对于稍复杂一点的系统就会不够用。在某项目中,就因为启动参数使用的默认值,经常报“OutOfMemory”错误。

因此,-Xms,-Xmx参数一定不要忘记加。
第二步,检查错误日志,查看“OutOfMemory”错误前是否有其它异常或错误。在一个项目中,使用两个数据库连接,

其中专用于发送短信的数据库连接使用DBCP连接池管理,用户为不将短信发出,有意将数据库连接用户名改错,使得日志中有许多数据库连接异常的日志,

一段时间后,就出现“OutOfMemory”错误。经分析,这是由于DBCP连接池BUG引起的,数据库连接不上后,没有将连接释放,

最终使得DBCP报“OutOfMemory”错误。经过修改正确数据库连接参数后,就没有再出现内存溢出的错误。
查看日志对于分析内存溢出是非常重要的,通过仔细查看日志,分析内存溢出前做过哪些操作,可以大致定位有问题的模块。
第三步,安排有经验的编程人员对代码进行走查和分析,找出可能发生内存溢出的位置。重点排查以下几点:
l         检查代码中是否有死循环或递归调用。
l         检查是否有大循环重复产生新对象实体。
l         检查对数据库查询中,是否有一次获得全部数据的查询。一般来说,如果一次取十万条记录到内存,就可能引起内存溢出。这个问题比较隐蔽,

在上线前,数据库中数据较少,不容易出问题,上线后,数据库中数据多了,一次查询就有可能引起内存溢出。因此对于数据库查询尽量采用分页的方式查询。
l         检查List、MAP等集合对象是否有使用完后,未清除的问题。List、MAP等集合对象会始终存有对对象的引用,使得这些对象不能被GC回收。
第四步,使用内存查看工具动态查看内存使用情况。某个项目上线后,每次系统启动两天后,就会出现内存溢出的错误。

这种情况一般是代码中出现了缓慢的内存泄漏,用上面三个步骤解决不了,这就需要使用内存查看工具了。
原因有很多种,比如:
1.数据量过于庞大;死循环 ;静态变量和静态方法过多;递归;无法确定是否被引用的对象;

原文地址

—————————————————————————————————————————————

子类继承父类的方法是,控制符必须大于或等于父类的访问控制符

类a继承类b并重写b类的protected方法func时,a中func方法的访问修饰符可以是?

protected/public

—————————————————————————————————————————-

hashCode与equals的区别与联系

hashCode是用于查找使用的,而equals是用于比较两个对象的是否相等的

1、hashCode的存在主要是用于查找的快捷性,如Hashtable,HashMap等,hashCode是用来在散列存储结构中确定对象的存储地址的;

2、如果两个对象相同,就是适用于equals(Java.lang.Object) 方法,那么这两个对象的hashCode一定要相同;

3、如果对象的equals方法被重写,那么对象的hashCode也尽量重写,并且产生hashCode使用的对象,一定要和equals方法中使用的一致,

否则就会违反上面提到的第2点;

4、两个对象的hashCode相同,并不一定表示两个对象就相同,也就是不一定适用于equals(java.lang.Object) 方法,

只能够说明这两个对象在散列存储结构中,如Hashtable,他们“存放在同一个篮子里”。

———————————————————————————–

==”和Equals的区别

“==”比较的是值【变量(栈)内存中存放的对象的(堆)内存地址】
equal用于比较两个对象的值是否相同【不是比地址】

—————————————————————————————

22.快速失败(fail-fast)和安全失败(fail-safe)的区别是什么?

1.什么是同步修改?

当一个或多个线程正在遍历一个集合Collection,此时另一个线程修改了这个集合的内容(添加,删除或者修改)。这就是并发修改

2.什么是 fail-fast 机制?

fail-fast机制在遍历一个集合时,当集合结构被修改,会抛出Concurrent Modification Exception。

fail-fast会在以下两种情况下抛出ConcurrentModificationException

(1)单线程环境

集合被创建后,在遍历它的过程中修改了结构。

注意 remove()方法会让expectModcount和modcount 相等,所以是不会抛出这个异常。

(2)多线程环境

当一个线程在遍历这个集合,而另一个线程对这个集合的结构进行了修改。

3. fail-fast机制是如何检测的?

迭代器在遍历过程中是直接访问内部数据的,因此内部的数据在遍历的过程中无法被修改。为了保证不被修改,迭代器内部维护了一个标记 “mode” ,

当集合结构改变(添加删除或者修改),标记”mode”会被修改,而迭代器每次的hasNext()和next()方法都会检查该”mode”是否被改变,

当检测到被修改时,抛出ConcurrentModification Exception

4. fail-safe机制

fail-safe任何对集合结构的修改都会在一个复制的集合上进行修改,因此不会抛出ConcurrentModificationException

fail-safe机制有两个问题

(1)需要复制集合,产生大量的无效对象,开销大

(2)无法保证读取的数据是目前原始数据结构中的数据。

6. fail-fast和 fail-safe 的区别

 

                                                                                     Fail Fast Iterator                                Fail Safe Iterator

Throw ConcurrentModification  Exception                   Yes                                                   No

Clone object                                                                       No                                                    Yes

Memory Overhead                                                              No                                                       Yes

Examples                                                            HashMap,Vector,                                                 CopyOnWriteArrayList,

                                                                           ArrayList,HashSet                                                ConcurrentHashMap

—————————————————————————————————————————————————————

Java中,什么是构造函数?什么是构造函数重载?

1、构造函数在新对象被创建的时候会调用。每一个类都有构造函数,构造函数的名字跟类名相同,没有返回值,每个类有一个默认的无参构造函数,

2、构造函数的重载:跟方法的重载类似,唯一的不同就是功能不一样,构造函数的重载,增加不同参数,来达到初始化属性的目的

构造函数的作用
   构造函数主要用来在创建对象时完成对对象属性的一些初始化等操作, 当创建对象时, 对象会自动调用它的构造函数。一般来说, 构造函数有以下三个方面的作用:
            ■ 给创建的对象建立一个标识符;
            ■ 为对象数据成员开辟内存空间;
            ■ 完成对象数据成员的初始化。

  1. public class test extends B{  
  2. test(){  
  3. System.out.print(”t”);  
  4. A s = new B();  
  5. }  
  6. public static void main(String[] args) {  
  7. test t = new test();  
  8. }  
  9. }  
  10. class A{  
  11. A(){  
  12. System.out.print(”AA”);  
  13. }  
  14. }  
  15. class B extends A{  
  16. B(){  
  17. System.out.print(”b”);  
  18. A a = new A();  
  19. }  
  20. }  
public class test extends B{
test(){
System.out.print("t");
A s = new B();
}
public static void main(String[] args) {
test t = new test();
}
}
class A{
A(){
System.out.print("AA");
}
}
class B extends A{
B(){
System.out.print("b");
A a = new A();
}
}

输出:AAbAAtAAbAA
原则:子类创建的时候先创建父类

————————————————————————————————————————————————————————————-

Statement和PreparedStatement

1、相对比较安全,可以防止sql注入
2、有预编译功能,相同操作批量数据效率较高
PreparedStatement 是预编译 ,使用Statement时 sql 中要进行很多的单引号拼接字符串,首先是容易出错也比较麻烦,还有就是存在sql 注入问题这是从安全方面说的。 PreparedStatement  传参数时候用 了占位符 “?”很好的解决了以上Statement的问题。我所体会到得的就是这些。
PreparedStatement是在执行前先输入sql语句,Statement正好相反,是在执行的时候传入sql语句的。
这样的区别在于,PreparedStatement可以在传入sql后,执行语句前,给参数赋值,避免了因普通的拼接sql字符串语句所带来的安全问题,

而且准备sql和执行sql是在两个语句里面完成的,也提高了语句执行的效率。
Statement,就没有以前所说的功能了,我一般很少用

—————————————————————————-

java项目中经常遇到的异常

1. java.lang.nullpointerexception
这个异常大家肯定都经常遇到,异常的解释是”程序遇上了空指针”,简单地说就是调用了未经初始化的对象或者是不存在的对象,

2. java.lang.classnotfoundexception

3. java.lang.arithmeticexception
这个异常的解释是”数学运算异常”,比如程序中出现了除以零这样的运算就会出这样的异常,对这种异常,

大家就要好好检查一下自己程序中涉及到数学运算的地方,公式是不是有不妥了。

4. java.lang.arrayindexoutofboundsexception

异常的解释是”数组下标越界”,,看自己调用的下标是不是超出了数组的范围

5.类型强制转换异常:ClassCastException

—————————————————————————————–

304、404、200、304等HTTP状态

2xx (成功)表示成功处理了请求的状态码。

3xx (重定向) 要完成请求,需要进一步操作。通常,这些状态码用来重定向。

4xx(请求错误) 这些状态码表示请求可能出错,妨碍了服务器的处理。

5xx(服务器错误)这些状态码表示服务器在处理请求时发生内部错误

100 服务请求中;
200 服务请求成功;
304 没有被修改,读取的内容为缓存;
403 禁止访问(Forbidden);
404 没有找到要访问的内容(Not Found);
500 内部服务器错误。


—————————————————————–

Cookies 和 Session的区别

(1)cookie数据存放在客户的浏览器上,session数据放在服务器上
(2)cookie不是很安全,别人可以分析存放在本地的COOKIE并进行COOKIE欺骗,如果主要考虑到安全应当使用session
(3)session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能,如果主要考虑到减轻服务器性能方面,应当使用COOKIE
(4)单个cookie在客户端的限制是3K,就是说一个站点在客户端存放的COOKIE不能3K。
(5)所以:将登陆信息等重要信息存放为SESSION;其他信息如果需要保留,可以放在COOKIE中

缓存的目的是为了避免重复计算

常用的缓存替换算法

1.Least-Recently-Used(LRU) - 最近最少使用
替换掉最近被请求最少的文档。这一传统策略在实际中应用最广。在CPU缓存淘汰和虚拟内存系统中效果很好。然而直接应用与代理缓存效果欠佳,因为Web访问的时间局部性常常变化很大。
2.Least-Frequently-Used(LFU) - 最不经常使用
替换掉访问次数最少的。这一策略意图保留最常用的、最流行的对象,替换掉很少使用的那些。然而,有的文档可能有很高的使用频率,但之后再也不会用到。传统 的LFU策略没有提供任何移除这类文件的机制,因此会导致“缓存污染(Cache Pollution)”,即一个先前流行的缓存对象会在缓存中驻留很长时间,这样,就阻碍了新进来可能会流行的对象对它的替代。
3.SIZE
替换size最大的对象。这一策略通过淘汰一个大对象而不是多个小对象来提高命中率。不过,可能有些进入缓存的小对象永远不会再被访问。SIZE策略没有提供淘汰这类对象的机制,也会导致“缓存污染”。
4.LRU-Threshold
不缓存超过某一size的对象,其它与LRU相同。

一、一级缓存二级缓存的概念解释

———————————————————

14.同步方法和同步代码块的区别是什么?

同步方法默认用this或者当前类class对象作为锁;

同步代码块可以选择以什么来加锁,比同步方法要更细颗粒度,我们可以选择只同步会发生同步问题的部分代码而不是整个方法;

同步方法锁的范围比较大,而同步代码块范围要小点,一般同步的范围越大,性能就越差,一般需要加锁进行同步的时候,肯定是范围越小越好,这样性能更好*。

——————————————————————–


————————————————————————————————–

Java NIO和IO之间的主要差别

IO                         NIO
面向流                 面向缓冲
阻塞IO                非阻塞IO
无                       选择器

阻塞与非阻塞IO

同步和异步关注的是消息通信机制

同步,就是在发出一个功能调用时,在没有得到结果之前,该调用就不返回。按照这个定义,其实绝大多数函数都是同步调用

异步的概念和同步相对。当一个异步过程调用发出后,调用者不能立刻得到结果。实际处理这个调用的部件在完成后,通过状态、通知和回调来通知调用者。

阻塞和非阻塞关注的是程序在等待调用结果(消息,返回值)时的状态.

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

举个通俗的例子:
你打电话问书店老板有没有《分布式系统》这本书,如果是同步通信机制,书店老板会说,你稍等,”我查一下”,然后开始查啊查,

等查好了(可能是5秒,也可能是一天)告诉你结果(返回结果)。
而异步通信机制,书店老板直接告诉你我查一下啊,查好了打电话给你,然后直接挂电话了(不返回结果)。然后查好了,

他会主动打电话给你。在这里老板通过“回电”这种方式来回调。

你打电话问书店老板有没有《分布式系统》这本书,你如果是阻塞式调用,你会一直把自己“挂起”,直到得到这本书有没有的结果,

如果是非阻塞式调用,你不管老板有没有告诉你,你自己先一边去玩了, 当然你也要偶尔过几分钟check一下老板有没有返回结果。

在这里阻塞与非阻塞与是否同步异步无关。跟老板通过什么方式回答你结果无关。
选择器(Selectors)

Java IO的各种流是阻塞的。这意味着,当一个线程调用read() 或 write()时,该线程被阻塞,

直到有一些数据被读取,或数据完全写入。该线程在此期间不能再干任何事情了。

Java NIO的非阻塞模式,使一个线程从某通道发送请求读取数据,但是它仅能得到目前可用的数据,如果目前没有数据可用时,就什么都不会获取。

而不是保持线程阻塞,所以直至数据变的可以读取之前,该线程可以继续做其他的事情。 非阻塞写也是如此。一个线程请求写入一些数据到某通道,

但不需要等待它完全写入,这个线程同时可以去做别的事情。 线程通常将非阻塞IO的空闲时间用于在其它通道上执行IO操作,

所以一个单独的线程现在可以管理多个输入和输出通道(channel)。

Java NIO的选择器允许一个单独的线程来监视多个输入通道,你可以注册多个通道使用一个选择器,然后使用一个单独的线程来“选择”通道:

这些通道里已经有可以处理的输入,或者选择已准备写入的通道。这种选择机制,使得一个单独的线程很容易来管理多个通道。

———————————————————-

扫码登录是如何实现的?

—————————————————————

反射机制的定义:

是在运行状态中,对于任意的一个类,都能够知道这个类的所有属性和方法,对任意一个对象都能够通过反射机制调用一个类的任意方法,

这种动态获取类信息及动态调用类对象方法的功能称为java的反射机制。

反射的作用:

1、动态地创建类的实例,将类绑定到现有的对象中,或从现有的对象中获取类型。

2、应用程序需要在运行时从某个特定的程序集中载入一个特定的类。

java中有三种方式可以获得一个类的信息,一般采用第二种Class.forName方法

public static void main(String[] args) throws ClassNotFoundException {  
        Person p = new Person();  
        //根据一个类的实例获得  
        Class pclazz = p.getClass();  
        System.out.println(pclazz.getName());  
        //根据一个Class类的forName()方法获得 参数时这个类的权限定名称,也就是要带上具体的包名  
        Class pclazz1 = Class.forName(“up9527.com.Person”);  
        System.out.println(pclazz.getName());  
        //通过类名.class获得  
        Class pclazz2 = Person.class;  
        System.out.println(pclazz.getName());  
    }

newInstance getConstrot   getDeclaredConstructor 方法的区别

1. newInstance 只能获得无参构造函数

2. Constructor<T> getConstructor(Class<?>… parameterTypes)

    根据传入的参数,获得 非私有的构造函数


3.public Constructor<?> getDeclaredConstructors

 根据传入的参数 ,可以获得私有的构造函数

实例

package Wangyi;

import java.lang.reflect.Constructor;

public class Person{  
    private int id;  
    Person(){  
    }  
    private Person(int id){  
        this.id = id;  
    }  
    public int getId(){  
        return this.id;  
    }  
    public void setId(int id){  
        this.id = id;  
    }  
      
    public void print(){  
        System.out.println(“我的Id 是”+id);  
    } 
    public static void main(String[] args) throws Exception {  
        //根据一个Class类的forName()方法获得 参数时这个类的权限定名称,也就是要带上具体的包名  
        Class clazz = Class.forName(“Wangyi.Person”);  
          
        //通过反射来的 Person类的 Class类型的实例 创建一个 Person类的实例  
        //Class.newInstance() 只能够调用无参的构造函数,即默认的构造函数;   
        Person p = (Person) clazz.newInstance();  
        p.print();  
          
        //getDeclaredConstructor方法可以得到私有的构造函数,还有个getConstructor 这个不能得到私有的构造函数  
        Constructor<Person> pc = clazz.getDeclaredConstructor(int.class);  
        Person p1 =pc.newInstance(2);  
        p1.print();      
    }  
}

输出:我的Id 是0
我的Id 是2

————————————————————-

进程和线程的区别

进程是执行着的应用程序,而线程是进程内部的一个执行序列。一个进程可以有多个线程。线程又叫做轻量级进程。

进程和线程的主要差别在于它们是不同的操作系统资源管理方式。进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,

而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,

所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。

但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。

进程与线程的区别:
1.进程有独立的进程空间,进程中的数据存放空间(堆空间和栈空间)是独立的。
2.线程的堆空间是共享的,栈空间是独立的,线程消耗的资源也比进程小,二者之间可以相互影响。

线程的数据交换更快,因为他们在同一地址空间内

多进程里,子进程可获得父进程的所有堆和栈的数据;
进程比线程更健壮,但是进程比线程更容易杀掉

————————————————————————————————————————-

操作系统之进程的几种状态

就绪(Ready)状态:当进程已分配到除CPU以外的所有必要资源后,只要再获得CPU,便可立即执行,进程这时的状态称为就绪状态。

执行状态:进程已获得CPU,其程序正在执行。在单处理机系统中,只有一个进程处于执行状态; 在多处理机系统中,则有多个进程处于执行状态。

阻塞状态:正在执行的进程由于发生某事件而暂时无法继续执行时,便放弃处理机而处于暂停状态,亦即进程的执行受到阻塞,把这种暂停状态称为阻塞状态.


—————————————————————————————————————

操作系统中的死锁被定义为系统中两个或者多个进程无限期地等待永远不会发生的条件,系统处于停滞状态,这就是死锁。

产生死锁的四个必要条件:
(1) 互斥条件:一个资源每次只能被一个进程使用。
(2) 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
(3) 不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
(4) 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。

如何确保N个线程可以访问N个资源同时又不导致死锁?

使用多线程的时候,一种非常简单的避免死锁的方式就是:指定获取锁的顺序,并强制线程按照指定的顺序获取锁。因此,

如果所有的线程都是以同样的顺序加锁和释放锁,就不会出现死锁了。

——————————————————————————————————-

线程间通信:共享存储.消息传递.管道通信

共享存储

         1.低级方式:基于数据结构的共享

         2.高级方式:基于存储区的共享

消息传递

         1.直接通信方式:直接把消息挂到接收进程的消息队列

         2.间接通信方式:找到某个中间实体.接收进程找实体接收消息.类似电子邮件.

管道通信

         利用一种特殊的pipe文件连接两个进程,管道只能单向传输(半双工)当缓冲区还有数据时,写进程不会往缓冲区写数据.

java中的线程通信

  1.同步 synchronized   共享存储

  2.while轮询

  3.wait().notify()机制

  —————————————————————————————     

并发和并行

   并发:一个处理器同时处理多个任务.(根本不可能同时进行一个以上的线程,只能把cpu运行时间分成若干个时间段,

   再讲时间段分配给各个线程执行.在一个线程运行期间,其他线程处于挂起状态)

   并行:多个处理器或者多核处理器同时处理多个不同的任务.

———————————————————————————–

线程的生命周期
线程是一个动态执行的过程,它也有一个从产生到死亡的过程。

(1)生命周期的五种状态

   新建(new Thread)
当创建Thread类的一个实例(对象)时,此线程进入新建状态(未被启动)。
例如:Thread  t1=new Thread();

就绪(runnable)
线程已经被启动,正在等待被分配给CPU时间片,也就是说此时线程正在就绪队列中排队等候得到CPU资源。例如:t1.start();

运行(running)
线程获得CPU资源正在执行任务(run()方法),此时除非此线程自动放弃CPU资源或者有优先级更高的线程进入,线程将一直运行到结束。

死亡(dead)
当线程执行完毕或被其它线程杀死,线程就进入死亡状态,这时线程不可能再进入就绪状态等待执行。

自然终止:正常运行run()方法后终止

异常终止:调用stop()方法让一个线程终止运行

堵塞(blocked)
由于某种原因导致正在运行的线程让出CPU并暂停自己的执行,即进入堵塞状态。

正在睡眠:用sleep(long t) 方法可使线程进入睡眠方式。一个睡眠着的线程在指定的时间过去可进入就绪状态。

正在等待:调用wait()方法。(调用motify()方法回到就绪状态)

被另一个线程所阻塞:调用suspend()方法。(调用resume()方法恢复)

2.常用方法

void run()   创建该类的子类时必须实现的方法

void start() 开启线程的方法

static void sleep(long t) 释放CPU的执行权,不释放锁

static void sleep(long millis,int nanos)

final void wait()释放CPU的执行权,释放锁

final void notify()

static void yied()可以对当前线程进行临时暂停(让线程将资源释放出来)

3.(1)结束线程原理:就是让run方法结束。而run方法中通常会定义循环结构,所以只要控制住循环即可

(2)方法—-可以boolean标记的形式完成,只要在某一情况下将标记改变,让循环停止即可让线程结束

(3)public final void join()//让线程加入执行,执行某一线程join方法的线程会被冻结,等待某一线程执行结束,该线程才会恢复到可运行状态

4. 临界资源:多个线程间共享的数据称为临界资源

(1)互斥锁

a.每个对象都对应于一个可称为“互斥锁”的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象。

b.Java对象默认是可以被多个线程共用的,只是在需要时才启动“互斥锁”机制,成为专用对象。

c.关键字synchronized用来与对象的互斥锁联系

d.当某个对象用synchronized修饰时,表明该对象已启动“互斥锁”机制,在任一时刻只能由一个线程访问,即使该线程出现堵塞,

该对象的被锁定状态也不会解除,其他线程任不能访问该对象。

——————————————————————————————————————————————————————–

下面代码输出的结果是?

  1. public class NULL {  
  2.   
  3. public static void print(){  
  4. System.out.println(“MTDP”);  
  5. }  
  6. public static void main(String[] args) {  
  7. try{  
  8. ((NULL)null).print();  
  9. }catch(NullPointerException e){  
  10. System.out.println(”NullPointerException”);  
  11. }  
  12. }  
  13. }  
public class NULL {

public static void print(){
System.out.println(“MTDP”);
}
public static void main(String[] args) {
try{
((NULL)null).print();
}catch(NullPointerException e){
System.out.println("NullPointerException");
}
}
}
答案:MTDP
null不是对象,它可以看成是指向不确定对象的引用。
赋值时,基本类型赋初值不能为null,如int=0只能是这种,换而言之,int也没有为空这一说法,如果非要勉强说有,那就是0。
而对象赋初值可以将其设为null。
本列,null是java的关键字,故不用事先声明它,直接把null作为NULL对象的一个引用,将其实例化,故而有输出。

—————————————————————————————————————————-

线程池的作用:
在程序启动的时候就创建若干线程来响应处理,它们被称为线程池,里面的线程叫工作线程
第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
第二:提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
第三:提高线程的可管理性。
常用线程池:ExecutorService 是主要的实现类,其中常用的有
线程池的理解及使用

———————————————————————————————————————————————————————

java concurrent包下的4个类

Semaphore:类,控制某个资源可被同时访问的个数;

ReentrantLock:类,具有与使用synchronized方法和语句所访问的隐式监视器锁相同的一些基本行为和语义,但功能更强大;
 Future:接口,表示异步计算的结果;
 CountDownLatch: 类,可以用来在一个线程中等待多个线程完成任务的类。

—————————————————————-

Lock与synchronized 的区别

1、ReentrantLock 拥有Synchronized相同的并发性和内存语义,此外还多了 锁投票,定时锁等候和中断锁等候
线程A和B都要获取对象O的锁定,假设A获取了对象O锁,B将等待A释放对O的锁定,
如果使用 synchronized ,如果A不释放,B将一直等下去,不能被中断
如果 使用ReentrantLock,如果A不释放,可以使B在等待了足够长的时间以后,中断等待,而干别的事情

ReentrantLock获取锁定与三种方式:
a) lock(), 如果获取了锁立即返回,如果别的线程持有锁,当前线程则一直处于休眠状态,直到获取锁
b) tryLock(), 如果获取了锁立即返回true,如果别的线程正持有锁,立即返回false;

c)tryLock(long timeout,TimeUnit unit), 如果获取了锁定立即返回true,如果别的线程正持有锁,会等待参数给定的时间,在等待的过程中,

如果获取了锁定,就返回true,如果等待超时,返回false;

d) lockInterruptibly:如果获取了锁定立即返回,如果没有获取锁定,当前线程处于休眠状态,直到或者锁定,或者当前线程被别的线程中断

2、synchronized是在JVM层面上实现的,不但可以通过一些监控工具监控synchronized的锁定,而且在代码执行时出现异常,JVM会自动释放锁定,

但是使用Lock则不行,lock是通过代码实现的,要保证锁定一定会被释放,就必须将unLock()放到finally{}中


3、在资源竞争不是很激烈的情况下,Synchronized的性能要优于ReetrantLock,但是在资源竞争很激烈的情况下,Synchronized的性能会下降几十倍,

但是ReetrantLock的性能能维持常态;

 4.Lock要手动在finally里释放锁,而syncronized不需要

—————————————————————————————————————-

MySQL的悲观锁:

      其实理解起来非常简单,当数据被外界修改持保守态度,包括自身系统当前的其他事务,以及来自外部系统的事务处理,因此,在整个数据处理过程中,

将数据处于锁定状态。悲观锁的实现,往往依靠数据库提供的锁机制,但是也只有数据库层提供的锁机制才能真正保证数据访问的排他性,否则,

即使在自身系统中实现了加锁机制,也无法保证外部系统不会修改数据。

      来点实际的,当我们使用悲观锁的时候我们首先必须关闭mysql数据库的自动提交属性,因为MySQL默认使用autocommit模式,也就是说,

当你执行一个更新操作后,MySQL会立刻将结果进行提交。

关闭命令为:set autocommit=0;

悲观锁可以使用select…for update实现,在执行的时候会锁定数据,虽然会锁定数据,但是不影响其他事务的普通查询使用。

此处说普通查询就是平时我们用的:select * from table 语句。在我们使用悲观锁的时候事务中的语句例如:

//开始事务

begin;/begin work;/start transaction; (三选一)

//查询信息

select * from order where id=1 for update;

//修改信息

update order set name=’names’;

//提交事务

commit;/commit work;(二选一)

此处的查询语句for update关键字,在事务中只有SELECT … FOR UPDATE 或LOCK IN SHARE MODE 同一条数据时会等待其它事务结束后才执行,

一般的SELECT查询则不受影响。

执行事务时关键字select…for update会锁定数据,防止其他事务更改数据。但是锁定数据也是有规则的。

查询条件与锁定范围:

1、具体的主键值为查询条件

比如查询条件为主键ID=1等等,如果此条数据存在,则锁定当前行数据,如果不存在,则不锁定。

2、不具体的主键值为查询条件

比如查询条件为主键ID>1等等,此时会锁定整张数据表。

3、查询条件中无主键

会锁定整张数据表。

4、如果查询条件中使用了索引为查询条件

明确指定索引并且查到,则锁定整条数据。如果找不到指定索引数据,则不加锁。

       悲观锁的确保了数据的安全性,在数据被操作的时候锁定数据不被访问,但是这样会带来很大的性能问题。因此悲观锁在实际开发中使用是相对比较少的。

mysql的乐观锁:

      相对悲观锁而言,乐观锁假设数据一般情况下不会造成冲突,所以在数据进行提交更新的时候,才会对数据的冲突与否进行检测,如果发现冲突,

则让返回用户错误的信息,让用户决定如何去做。

      一般来说,实现乐观锁的方法是在数据表中增加一个version字段,每当数据更新的时候这个字段执行加1操作。这样当数据更改的时候,

另外一个事务访问此条数据进行更改的话就会操作失败,从而避免了并发操作错误。当然,还可以将version字段改为时间戳,不过原理都是一样的。

例如有表student,字段:

id,     name,      version
 
1         a              1

当事务一进行更新操作:update student set name=’ygz’ where id = #{id} and version = #{version};

此时操作完后数据会变为id = 1,name = ygz,version = 2,当另外一个事务二同样执行更新操作的时候,却发现version != 1,此时事务二就会操作失败,

从而保证了数据的正确性。

—————————————————————————————–

CAS是乐观锁:每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止。

CAS 操作包含三个操作数 —— 内存位置(V)、预期原值(A)和新值(B)。如果内存位置的值与预期原值相匹配,

那么处理器会自动将该位置值更新为新值。否则,处理器不做任何操作。

———————————————————————————————————————————————-

synchronized的底层实现主要依靠Lock-Free的队列,基本思路是自旋后阻塞,竞争切换后继续竞争锁,稍微牺牲了公平性,但获得了高吞吐量。

—————————————————————————————————————————————————————–

自旋锁

自旋锁是采用让当前线程不停地的在循环体内执行实现的,当循环的条件被其他线程改变时 才能进入临界区。如下

  1. public class SpinLock {  
  2.   
  3.   private AtomicReference<Thread> sign =new AtomicReference<>();  
  4.   
  5.   public void lock(){  
  6.     Thread current = Thread.currentThread();  
  7.     while(!sign .compareAndSet(null, current)){  
  8.     }  
  9.   }  
  10.   
  11.   public void unlock (){  
  12.     Thread current = Thread.currentThread();  
  13.     sign .compareAndSet(current, null);  
  14.   }  
  15. }  
public class SpinLock {

  private AtomicReference<Thread> sign =new AtomicReference<>();

  public void lock(){
    Thread current = Thread.currentThread();
    while(!sign .compareAndSet(null, current)){
    }
  }

  public void unlock (){
    Thread current = Thread.currentThread();
    sign .compareAndSet(current, null);
  }
}
使用了CAS原子操作,lock函数将owner设置为当前线程,并且预测原来的值为空。unlock函数将owner设置为null,并且预测值为当前线程。

当有第二个线程调用lock操作时由于owner值不为空,导致循环一直被执行,直至第一个线程调用unlock函数将owner设置为null,第二个线程才能进入临界区。

由于自旋锁只是将当前线程不停地执行循环体,不进行线程状态的改变,所以响应速度更快。但当线程数不停增加时,性能下降明显,

因为每个线程都需要执行,占用CPU时间。如果线程竞争不激烈,并且保持锁的时间段。适合使用自旋锁。


注:该例子为非公平锁,获得锁的先后顺序,不会按照进入lock的先后顺序进行。

—————————————————————————————————————–

类加载器工作机制:
1.装载:将Java二进制代码导入jvm中,生成Class文件。
2.连接:a)校验:检查载入Class文件数据的正确性 b)准备:给类的静态变量分配存储空间 c)解析:将符号引用转成直接引用
3:初始化:对类的静态变量,静态方法和静态代码块执行初始化工作。

双亲委派模型:类加载器收到类加载请求,首先将请求委派给父类加载器完成
用户自定义加载器->应用程序加载器->扩展类加载器->启动类加载器。

————————————————————————————————————

.常用jVM调有工具有哪些(Jstatus,JStack,Jmap等)

————————————————————————————————–

Java内存模型:

Java虚拟机规范中将Java运行时数据分为六种。

1.程序计数器:是一个数据结构,用于保存当前正常执行的程序的内存地址。Java虚拟机的多线程就是通过线程轮流切换并分配处理器时间来实现的,

为了线程切换后能恢复到正确的位置,每条线程都需要一个独立的程序计数器,互不影响,该区域为“线程私有”。

2.Java虚拟机栈:线程私有的,与线程生命周期相同,用于存储局部变量表,操作栈,方法返回值。局部变量表放着基本数据类型,还有对象的引用。

3.本地方法栈:跟虚拟机栈很像,不过它是为虚拟机使用到的Native方法服务。

4.Java堆:所有线程共享的一块内存区域,对象实例几乎都在这分配内存。

5.方法区:各个线程共享的区域,储存虚拟机加载的类信息,常量,静态变量,编译后的代码。

6.运行时常量池:代表运行时每个class文件中的常量表。包括几种常量:编译时的数字常量、方法或者域的引用。

堆为什么要分代

让新创建的对象都在young gen里创建,然后频繁收集young gen,则大部分垃圾都能在young GC中被收集掉。

由于young gen的大小配置通常只占整个GC堆的较小部分,而且较高的对象死亡率(或者说较低的对象存活率)让它非常适合使用copying算法来收集,

这样就不但能降低单次GC的时间长度,还可以提高GC的工作效率。

JDK1.8中JVM做了那些改变(主要是撤销了永久代,引入元空间)

——————————————————————————————————————-

JVM中什么时候会进行垃圾回收

首先需要知道,GC又分为minor GC 和 Full Gc(也称为Major GC)。Java 堆内存分为新生代和老年代,新生代中又分为1个Eden区域 和两个 Survivor区域。

那么对于 Minor GC 的触发条件: 大多数情况下,直接在 Eden 区中进行分配 。如果 Eden区域没有足够的空间,那么就会发起一次 Minor GC;

对于 Full GC(Major GC)的触发条件:也是如果老年代没有足够空间的话,那么就会进行一次 Full GC。

—————————————————————————————————————————-

java垃圾回收机制:

根据对象的存活周期,将内存划分为几块。一般是把 Java 堆分为新生代和老年代,这样就可以根据各个年代的特点,采用最适当的收集算法。
新生代:每次垃圾收集时会有大批对象死去,只有少量存活,所以选择复制算法,只需要少量存活对象的复制成本就可以完成收集。

复制算法:将内存划分为8:1:1三部分,较大那份内存交Eden区,其余是两块较小的内存区叫Survior区。每次都会优先使用Eden区,若Eden区满,

就将对象复制到第二块内存区上,

然后清除Eden区,如果此时存活的对象太多,以至于Survivor不够时,会将这些对象通过分配担保机制复制到老年代中。

老年代:对象存活率高、没有额外空间对它进行分配担保,必须使用“标记-清除”或“标记-整理”算法进行回收。

标记-清除:它的思想就是标记哪些要被回收的对象,然后统一回收。这种方法很简单,但是会有两个主要问题:1.效率不高,标记和清除的效率都很低;

2.会产生大量不连续的内存碎片,导致以后程序在分配较大的对象时,由于没有充足的连续内存而提前触发一次GC动作。

标记-整理:该算法主要是为了解决标记-清除,产生大量内存碎片的问题;当对象存活率较高时,也解决了复制算法的效率问题。

它的不同之处就是在清除对象的时候现将可回收对象移动到一端,然后清除掉端边界以外的对象,这样就不会产生内存碎片了。

Minor GC:新生代 GC,指发生在新生代的垃圾收集动作,因为 Java 对象大多死亡频繁,所以 Minor GC 非常频繁,一般回收速度较快。
Full GC:老年代 GC,也叫 Major GC,速度一般比 Minor GC 慢 10 倍以上。

判断一个对象是否存活有两种方法:
1. 引用计数法
所谓引用计数法就是给每一个对象设置一个引用计数器,每当有一个地方引用这个对象时,就将计数器加一,引用失效时,计数器就减一。

当一个对象的引用计数器为零时,说明此对象没有被引用,也就是“死对象”,将会被垃圾回收.
引用计数法有一个缺陷就是无法解决循环引用问题,也就是说当对象A引用对象B,对象B又引用者对象A,那么此时A,B对象的引用计数器都不为零,

也就造成无法完成垃圾回收,所以主流的虚拟机都没有采用这种算法。

2.可达性算法(引用链法)
该算法的思想是:从一个被称为GC Roots的对象开始向下搜索,如果一个对象到GC Roots没有任何引用链相连时,则说明此对象不可用。

虽然这些算法可以判定一个对象是否能被回收,但是当满足上述条件时,一个对象比不一定会被回收。当一个对象不可达GC Root时,

这个对象并不会立马被回收,而是出于一个死缓的阶段,若要被真正的回收需要经历两次标记

————————————————————————————————————————–

java内存模型(JMM)是线程间通信的控制机制.JMM定义了主内存和线程之间抽象关系。线程之间的共享变量存储在主内存(main memory)中,

每个线程都有一个私有的本地内存(local memory),本地内存中存储了该线程以读/写共享变量的副本。

从上图来看,线程A与线程B之间如要通信的话,必须要经历下面2个步骤:
1. 首先,线程A把本地内存A中更新过的共享变量刷新到主内存中去。
2. 然后,线程B到主内存中去读取线程A之前已更新过的共享变量

————————————————————————————————————————–

创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。

结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。

行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、

备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

—————————————————————————————————————-

Java IO中涉及到哪些设计模式

1、适配器模式
//file 为已定义好的文件流
FileInputStream fileInput = new FileInputStream(file);
InputStreamReader inputStreamReader = new InputStreamReader(fileInput);

以上就是适配器模式的体现,FileInputStream是字节流,而并没有字符流读取字符的一些api,因此通过InputStreamReader将其转为Reader子类,

因此有了可以操作文本的文件方法。
2、装饰者模式

BufferedReader bufferedReader=new BufferedReader(inputStreamReader);

构造了缓冲字符流,将FileInputStream字节流包装为BufferedReader过程就是装饰的过程,刚开始的字节流FileInputStream只有read一个字节的方法,

包装为inputStreamReader后,就有了读取一个字符的功能,在包装为BufferedReader后,就拥有了read一行字符的功能。

——————————————————————————————————————————————————–

单例模式有以下特点:
  1、单例类只能有一个实例。
  2、单例类必须自己创建自己的唯一实例。
  3、单例类必须给所有其他对象提供这一实例。

饿汉式和懒汉式区别
饿汉就是类一旦加载,就把单例初始化完成,保证getInstance的时候,单例是已经存在的了,

以后不再改变,所以天生是线程安全的。

一、饿汉式单例

  1. //饿汉式单例类.在类初始化时,已经自行实例化     
  2. public class Singleton1 {    
  3.     private Singleton1() {}    
  4.     private static final Singleton1 single = new Singleton1();    
  5.     //静态工厂方法     
  6.     public static Singleton1 getInstance() {    
  7.         return single;    
  8.     }    
  9. }  
//饿汉式单例类.在类初始化时,已经自行实例化   
public class Singleton1 {  
    private Singleton1() {}  
    private static final Singleton1 single = new Singleton1();  
    //静态工厂方法   
    public static Singleton1 getInstance() {  
        return single;  
    }  
}


而懒汉比较懒,只有当调用getInstance的时候,才回去初始化这个单例。
二、懒汉式单例

  1. //懒汉式单例类.在第一次调用的时候实例化自己     
  2. public class Singleton {    
  3.     private Singleton() {}    
  4.     private static Singleton single=null;    
  5.     //静态工厂方法     
  6.     public static Singleton getInstance() {    
  7.          if (single == null) {      
  8.              single = new Singleton();    
  9.          }      
  10.         return single;    
  11.     }    
  12. }    
    //懒汉式单例类.在第一次调用的时候实例化自己   
    public class Singleton {  
        private Singleton() {}  
        private static Singleton single=null;  
        //静态工厂方法   
        public static Singleton getInstance() {  
             if (single == null) {    
                 single = new Singleton();  
             }    
            return single;  
        }  
    }  

它是线程不安全的,并发环境下很可能出现多个Singleton实例,要实现线程安全,有以下三种方式

1、在getInstance方法上加同步

  1. public static synchronized Singleton getInstance() {    
  2.          if (single == null) {      
  3.              single = new Singleton();    
  4.          }      
  5.         return single;    
  6. }    
    public static synchronized Singleton getInstance() {  
             if (single == null) {    
                 single = new Singleton();  
             }    
            return single;  
    }  

2、双重检查锁定

  1. public static Singleton getInstance() {    
  2.         if (singleton == null) {      
  3.             synchronized (Singleton.class) {      
  4.                if (singleton == null) {      
  5.                   singleton = new Singleton();     
  6.                }      
  7.             }      
  8.         }      
  9.         return singleton;     
  10.     }    
    public static Singleton getInstance() {  
            if (singleton == null) {    
                synchronized (Singleton.class) {    
                   if (singleton == null) {    
                      singleton = new Singleton();   
                   }    
                }    
            }    
            return singleton;   
        }  

3、静态内部类

  1. public class Singleton {      
  2.     private static class LazyHolder {      
  3.        private static final Singleton INSTANCE = new Singleton();      
  4.     }      
  5.     private Singleton (){}      
  6.     public static final Singleton getInstance() {      
  7.        return LazyHolder.INSTANCE;      
  8.     }      
  9. }  
public class Singleton {    
    private static class LazyHolder {    
       private static final Singleton INSTANCE = new Singleton();    
    }    
    private Singleton (){}    
    public static final Singleton getInstance() {    
       return LazyHolder.INSTANCE;    
    }    
}
—————————————————————————————————————
原型模式的结构

  原型模式要求对象实现一个可以“克隆”自身的接口,这样就可以通过复制一个实例对象本身来创建一个新的实例。这样一来,通过原型实例创建新的对象,

就不再需要关心这个实例本身的类型,只要实现了克隆自身的方法,就可以通过这个方法来获取新的对象,而无须再去通过new来创建。

———————————————————————————————————————————

观察者模式中,一个被观察者管理所有相依于它的观察者物件,并且在本身的状态改变时主动发出通知。这通常通过呼叫各观察者所提供的方法来实现。

此种模式通常被用来实现事件处理系统。

————————————————————————————————————————————-

(Abstract Factory)抽象工厂模式的Java实现

抽象工厂模式(Abstract Factory):为创建一组相关或者互相依赖的对象提供一个接口,而无需指定它们对应的具体类。

缺点:在添加新的产品对象时,难以扩展抽象工厂来生产新种类的产品,这是因为在抽象工厂角色中规定了所有可能被创建的产品集合,

要支持新种类的产品就意味着要对该接口进行扩展,而这将涉及到对抽象工厂角色及其所有子类的修改,显然会带来较大的不便。

————————————————————————————————————

Builder模式

作用:将一个复杂对象的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。

Builder模式是把复杂对象的创建和部件的创建分别开来,分别用Builder类和Director类来表示。

一个复杂的对象,不但有很多大量组成部分,如汽车,有很多部件:车轮、方向盘、发动机,还有各种小零件等等,部件很多,但远不止这些,

如何将这些部件装配成一辆汽车,这个装配过程也很复杂(需要很好的组装技术),Builder模式就是为了将部件和组装过程分开。

————————————————————————————————————-

Adapter模式将一个类的接口转换成客户希望的另外一个接口。Adapter 模式使原本由于接口不兼容而不能一起工作的那些类可以一起工作。

适配器模式有类的适配器模式和对象的适配器模式两种不同的形式

类的适配器模式

在上图中可以看出,Adaptee类并没有sampleOperation2()方法,而客户端则期待这个方法。为使客户端能够使用Adaptee类,提供一个中间环节,

即类Adapter,把Adaptee的API与Target类的API衔接起来。Adapter与Adaptee是继承关系,这决定了这个适配器模式是类的:

  模式所涉及的角色有:

  ●  目标(Target)角色:这就是所期待得到的接口。注意:由于这里讨论的是类适配器模式,因此目标不可以是类。

  ●  源(Adapee)角色:现在需要适配的接口。

  ●  适配器(Adaper)角色:适配器类是本模式的核心。适配器把源接口转换成目标接口。显然,这一角色不可以是接口,而必须是具体类。


对象适配器模式

  与类的适配器模式一样,对象的适配器模式把被适配的类的API转换成为目标类的API,与类的适配器模式不同的是,

对象的适配器模式不是使用继承关系连接到Adaptee类,而是使用委派关系连接到Adaptee类。

从上图可以看出,Adaptee类并没有sampleOperation2()方法,而客户端则期待这个方法。为使客户端能够使用Adaptee类,

需要提供一个包装(Wrapper)类Adapter。这个包装类包装了一个Adaptee的实例,从而此包装类能够把Adaptee的API与Target类的API衔接起来。

Adapter与Adaptee是委派关系,这决定了适配器模式是对象的。

—————————————————————————————————————————-

Bridge(桥接)模式将抽象化与实现化脱耦,使得二者可以独立的变化就是说将他们之间的强关联变成弱关联,

也就是指在一个软件系统的抽象化和实现化之间使用组合/聚合关系而不是继承关系,从而使两者可以独立的变化。

传统做法


桥接模式做法


———————————————————————————————————————————————

装饰者模式Decorator

定义:动态给一个对象添加一些额外的职责,就象在墙上刷油漆

下面这个例子有助于理解 装饰的流程和作用

现在需要一个汉堡,主体是鸡腿堡,可以选择添加生菜、酱、辣椒等等许多其他的配料,这种情况下就可以使用装饰者模式。

————————————————————————————————

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

———————————————————————————————————-

门面模式(外观模式)facade

为子系统中的一组接口提供一个统一接口。Facade模式定义了一个更高层的接口,使子系统更加容易使用。


——————————————————————————————————————-

命令模式(commond)将请求封装成对象,以便使用不同的请求、队列或者日志来参数化其他对象。命令模式支持可撤销的操作。

———————————————————————————————————————–

中介者模式(Mediator Pattern)定义一个中介对象来封装系列对象之间的交互。中介者使各个对象不需要显示地相互引用,

从而使其耦合性松散,而且可以独立地改变他们之间的交互。

原始交流方式

引入中介QQ的交流方式


——————————————————————————————————————————–

模板方法template模式定义一个操作中的算法的骨架,而将步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义算法的某些特定步骤。


—————————————————————————————————————————-

责任链模式避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,

直到有对象处理它为止,这就是职责链模式。


—————————————————————————————————————————————————-

组合模式组合多个对象形成树形结构以表示“整体-部分”的结构层次。

—————————————————————————————————————————————————-

Strategy(策略)模式也叫策略模式是行为模式之一,它对一系列的算法加以封装,为所有算法定义一个抽象的算法接口,

并通过继承该抽象算法接口对所有的算法加以封装和实现,具体的算法选择交由客户端决定(策略)。Strategy模式主要用来平滑地处理算法的切换

——————————————————————————————————————————————

Iterator模式也叫迭代模式,是行为模式之一,它把对容器中包含的内部对象的访问委让给外部类,使用Iterator按顺序进行遍历访问的设计模式。

简单地说,Iterator模式提供一种有效的方法,可以屏蔽聚集对象集合的容器类的实现细节,而能对容器内包含的对象元素按顺序进行有效的遍历访问。

————————————————————————————————————————————————–

response.setContentType()方法来指定jsp页面显示的编码格式

—————————————————————————————————————

Ajax(Asynchronous JavaScript and xml)主要目的:不断刷新页面的情况下通过与服务器进行少量数据交互来提高页面的交互性,减少相应时间,从而改善用户的体验.

————————————————————————————————————————–

当forward方式可以满足需求时,尽可能使用forward方式,但在有些情况.例如,需要跳转到一个其他服务器资源,则必须用redirect

———————————————————————————————————————————

get主要用来获取服务器端的资源信息,post同时还可以向服务器上传数据.

不用get传数据的两个原因

      1.上传大小限制在1024byte左右

       2.get方法上传的数据添加在url中,因此上传的数据被彻底暴漏.

如果请求是get,调用doget()方法,

如果请求是post,调用dopost()方法

———————————————————————————————————————————-

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值