java知识点总结

1 bit = 1个2进制数,0或者1

1byte = 8 bit

1字符 = 1 byte

1汉字 = 2 byte

int 占4个字节,每个字节8位,因此是32位,大小为-2的32次方到2的32次方-1

long 是8个字节

----------------------------------------------------------------

JAVA中Object类中 有几个方法

Object类一共定义了九个方法:
equals(Object obj);
getClass();
hashCode();
notify();
notifyAll();
toString();
wait();
wait(long timeout);
wait(long timeout, int nanos);

-----------------------------------------------------------------------------

多线程同步的几种方式
1.同步方法
2.同步代码块
3.使用特殊域变量(volatile)实现线程同步
   volatile不能保证原子操作,因此volatile不能代替synchronized。
   它的原理是每次要访问volatile修饰的变量时都说从内存中取出。
   而不是缓存中读取,因此每个线程访问到的变量值都说一样的,这样就保证
   了同步。
4.使用reentrantlock
使用reentrantlock记得即时释放锁,不然会死锁
5.使用原子类

---------------------------------------------------------------------------------------------------

Static:

修饰变量:静态变量随着类加载时被完成初始化,内存中只有一个,且JVM也只会为它分配一次内存,所有类共享静态变量。

修饰方法:在类加载时就存在,不依赖任何实例:static方法必须实现,不能用abstract修饰。静态方法可以直接通过类名调用,

不能直接访问所属类的实例变量和实例方法,只能访问所属类的静态成员变量和方法。

修饰代码块:在类加载完之后会执行代码块中的内容。static代码块有多个,JVM将按照他们在类中出现的先后顺序依次执行它们,每个代码块只会被执行一次。

被static修饰的成员变量和成员方法独立于该类的任何对象。

static final用来修饰成员变量和成员方法,可简单理解为“全局常量”!
 对于变量,表示一旦给值就不可修改,并且通过类名可以访问。
 对于方法,表示不可覆盖,并且可以通过类名直接访问。

---------------------------------------------------------------------------------

volatile与synchronized的区别

同步:如用synchronized关键字,或者使用锁对象
使用volatile关键字:用一句话概括volatile,它能够使变量在值发生改变时能尽快地让其他线程知道。

1)volatile本质是在告诉jvm当前变量在寄存器中的值是不确定的,需要从主存中读取,synchronized则是锁定当前变量

,只有当前线程可以访问该变量,其他线程被阻塞住.violate还能禁止语义重排。

2)volatile仅能使用在变量级别,synchronized则可以使用在变量,方法.
3)volatile仅能实现变量的修改可见性,而synchronized则可以保证变量的修改可见性和原子性.
4)volatile不会造成线程的阻塞,而synchronized可能会造成线程的阻塞.


violate的作用实例:

很多线程用同一个标识判断某件事是否执行,当一个线程改变这个标识的时候,能立即被其他标识看见

------------------------------------------------------------------------------------------

jav,a sync,hronized详解

一、当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行。

另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。

     二、然而,当一个线程访问object的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该object中的非synchronized(this)同步代码块。

     三、尤其关键的是,当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对object中所有其它synchronized(this)同步代码块的访问将被阻塞。

     四、第三个例子同样适用其它同步代码块。也就是说,当一个线程访问object的一个synchronized(this)同步代码块时,

它就获得了这个object的对象锁。结果,其它线程对该object对象所有同步代码部分的访问都被暂时阻塞。-------------------------------------------------------------------------------------------------------------------------

并发程序正确地执行,必须要保证原子性、可见性以及有序性。只要有一个没有被保证,就有可能会导致程序运行不正确。

原子性:一个操作或多个操作要么全部执行完成且执行过程不被中断,要么就不执行。

可见性:当多个线程同时访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。

有序性:程序执行的顺序按照代码的先后顺序执行。

对于单线程,在执行代码时jvm会进行指令重排序,处理器为了提高效率,可以对输入代码进行优化,它不保证程序中各个语句的执行先后顺序同代码中的顺序一致,但是它会保证保存最终执行结果和代码顺序执行的结果是一致的。

Java语言对原子性、可见性、有序性的保证

1、原子性

Java中,对基本数据类型的变量的读取和赋值操作是原子性操作,即这些操作是不可被中断,要么执行,要么不执行。

X=10;  //原子性(简单的读取、将数字赋值给变量)

Y = x;  //变量之间的相互赋值,不是原子操作

X++;  //对变量进行计算操作

X = x+1;

语句2实际包括两个操作,它先要去读取x的值,再将y值写入,两个操作分开是原子性的。合在一起就不是原子性的。

语句34:x++  x=x+1包括3个操作:读取x的值,x+1,将x写入

注:可以通过 synchronizedLock实现原子性。因为synchronizedLock能够保证任一时刻只有一个线程访问该代码块。

 

2、可见性

Java提供了volatile关键字保证可见性。

当一个共享变量被volatile修饰时,它会保证修改的值立即被其他的线程看到,即修改的值立即更新到主存中,当其他线程需要读取时,它会去内存中读取新值。

SynchronizedLock也可以保证可见性,因为它们可以保证任一时刻只有一个线程能访问共享资源,并在其释放锁之前将修改的变量刷新到内存中,

 

3、有序性

Java里面,可以通过volatile关键字来保证一定的有序性。另外可以通过synchronizedLock来保证有序性,很显然,synchronizedLock保证每个时刻是有一个线程执行同步代码,相当于是让线程顺序执行同步代码,自然就保证了有序性。

 

Java内存模型:每个线程都有自己的工作内存(类似于前面的高速缓存)。线程对变量的所有操作都必须在工作内存中进行,而不能直接对主存进行操作。

并且每个线程不能访问其他线程的工作内存。

 

Java内存模型具备一些先天的“有序性”,即不需要通过任何手段就能够得到保证的有序性,这个通常也称为happens-before 原则。如果两个操作的执行次序无法从happens-before原则推导出来,那么它们就不能保证它们的有序性,虚拟机可以随意地对它们进行重排序。

指令重排序:java语言规范规定JVM线程内部维持顺序化语义。即只要程序的最终结果与它顺序化情况的结果相等,那么指令的执行顺序可以与代码顺序不一致,此过程叫指令的重排序。

指令重排序的意义:JVM能根据处理器特性(CPU多级缓存系统、多核处理器等)适当的对机器指令进行重排序,使机器指令能更符合CPU的执行特性,最大限度的发挥机器性能。

下面就来具体介绍下happens-before原则(先行发生原则):

程序次序规则:一个线程内,按照代码顺序,书写在前面的操作先行发生于书写在后面的操作

锁定规则:一个unLock操作先行发生于后面对同一个锁额lock操作

volatile变量规则:对一个变量的写操作先行发生于后面对这个变量的读操作。如果一个线程先去写一个变量,然后一个线程去进行读取,那么写入操作肯定会先行发生于读操作。

传递规则:如果操作A先行发生于操作B,而操作B又先行发生于操作C,则可以得出操作A先行发生于操作C

线程启动规则:Thread对象的start()方法先行发生于此线程的每个一个动作

线程中断规则:对线程interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生

线程终结规则:线程中所有的操作都先行发生于线程的终止检测,我们可以通过Thread.join()方法结束、Thread.isAlive()的返回值手段检测到线程已经终止执行

对象终结规则:一个对象的初始化完成先行发生于他的finalize()方法的开始

----------------------------------------------------------------------------------------------------------------------

多线程:一个程序运行时产生了不止一个线程。

并行:多个CPU或者多台机器同时执行一段处理逻辑,是真正的同时。

并发:通过CPU调度算法,让用户看上去同时执行,实际上从CPU操作层面不是真正的同时。

线程安全:在并发情况下,该代码经过线程的使用,线程的调度顺序不影响任何结果。

线程不安全:线程的调度顺序会影响结果。

同步:Java中的同步指的是通过人为的控制和调度,保证共享资源的多线程访问成为线程安全,来保证结果的准确。

-------------------------------------------------------------------------------------------------------------------------

Java通过反射改变私有变量

----------------------------------------------------------------------------------

对于多线程资源共享的问题,同步机制采用了“以时间换空间”的方式,而ThreadLocal采用了“以空间换时间”的方式。前者仅提供一份变量,

让不同的线程排队访问,而后者为每一个线程都提供了一份变量,因此可以同时访问而互不影响。

1.当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,

所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。

2.在ThreadLocal类中有一个Map,用于存储每一个线程的变量副本,Map中元素的键为线程对象,而值对应线程的变量副本。

3.当然ThreadLocal并不能替代同步机制,两者面向的问题领域不同。同步机制是为了同步多个线程对相同资源的并发访问,是为了多个线程之间进行通信的有效方式;而ThreadLocal是隔离多个线程的数据共享,从根本上就不在多个线程之间共享资源(变量),这样当然不需要对多个线程进行同步了。所以,如果你需要进行多个线程之间进行通信,则使用同步机制;如果需要隔离多个线程之间的共享冲突,可以使用ThreadLocal.

java.lang.ThreadLocal<T>的具体实现

那么到底ThreadLocal类是如何实现这种“为每个线程提供不同的变量拷贝”的呢?先来看一下ThreadLocal的set()方法的源码是如何实现的:

[java] view plain copy
  1. public void set(T value) {    
  2.        Thread t = Thread.currentThread();    
  3.        ThreadLocalMap map = getMap(t);    
  4.        if (map != null)    
  5.            map.set(this, value);    
  6.        else    
  7.            createMap(t, value);    
  8.    }    
线程隔离的秘密,就在于ThreadLocalMap这个类。ThreadLocalMap是ThreadLocal类的一个静态内部类,它实现了键值对的设置和获取(对比Map对象来理解),每个线程中都有一个独立的ThreadLocalMap副本,它所存储的值,只能被当前线程读取和修改。ThreadLocal类通过操作每一个线程特有的ThreadLocalMap副本,从而实现了变量访问在不同线程中的隔离。因为每个线程的变量都是自己特有的,完全不会有并发错误。还有一点就是,ThreadLocalMap存储的键值对中的键是this对象指向的ThreadLocal对象,而值就是你所设置的对象了。


为了加深理解,我们接着看上面代码中出现的getMap和createMap方法的实现:

[java] view plain copy
  1. ThreadLocalMap getMap(Thread t) {    
  2.     return t.threadLocals;    
  3. }    
  4. void createMap(Thread t, T firstValue) {    
  5.     t.threadLocals = new ThreadLocalMap(this, firstValue);    
  6. }    
再来看setInitialValue()方法:

[java] view plain copy
  1. private T setInitialValue() {    
  2.        T value = initialValue();    
  3.        Thread t = Thread.currentThread();    
  4.        ThreadLocalMap map = getMap(t);    
  5.        if (map != null)    
  6.            map.set(this, value);    
  7.        else    
  8.            createMap(t, value);    
  9.        return value;    
  10.    }    
获取和当前线程绑定的值时,ThreadLocalMap对象是以this指向的ThreadLocal对象为键进行查找的,这当然和前面set()方法的代码是相呼应的。
  进一步地,我们可以创建不同的ThreadLocal实例来实现多个变量在不同线程间的访问隔离,为什么可以这么做?因为不同的ThreadLocal对象作为不同键,当然也可以在线程的ThreadLocalMap对象中设置不同的值了。通过ThreadLocal对象,在多线程中共享一个值和多个值的区别,就像你在一个HashMap对象中存储一个键值对和多个键值对一样,仅此而已。

-------------------------------------------------------------------------------------------

所谓多态就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,

而是在程序运行期间才确定,即一个引用变量倒底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,

必须在由程序运行期间才能决定。

多态的三要素:1.继承  2.重写  3.父类引用指向子类对象

-------------------------------------------------------------------------------------------

运行时异常及处理方法

把 Exception 看成 一块 石头
throws Exception 就是把石头丢出去
try catch 就是拿个网兜在那里接石头

(1)如果你不想编写捕获异常的具体代码的话,你可以使用 throws Exception 的形式,

把异常再次抛出,交给JVM(Java虚拟机)可以捕获。这是一种比较省事的办法。

(2)如果你想亲编写处理异常的代码的话,可以使用try{ }catch(){ }的形式,进行捕获,

一旦程序发生异常,它就会安照你catch{ }块编写的代码去执行。

-------------------------------------------------------------------------------------

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

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

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

2.抽象类中可以有普通成员变量,接口中没有普通成员变量!!!!!!!

3.抽象类中可以包含非抽象的普通方法,接口中的所有方法必须都是抽象的,不能有非抽象的普通方法。

4. 抽象类中的抽象方法的访问类型可以是public,protected和(默认类型,虽然eclipse下不报错,但应该也不行),

但接口中的抽象方法只能是public类型的,并且默认即为public abstract类型。

5. 抽象类中可以包含静态方法(static),接口中不能包含静态方法.

6. 抽象类和接口中都可以包含静态成员变量(static),抽象类中的静态成员变量的访问类型可以任意,

但接口中定义的变量只能是public static final类型,并且默认即为public static final类型。

7. 一个类可以实现多个接口,但只能继承一个抽象类。

------------------------------------------------------------------------------------

IO常用类

文件流:FileInputStream/FileOutputStream, FileReader/FileWriter

前面两个是操作字节流,后面两个是操作字符流。

StringReader/StringWriter 需要处理字符串的时候,可以将字符串保存为字符数组
PrintStream/PrintWriter 用来包装FileOutputStream 对象,方便直接将String字符串写入文件
Scanner 用来包装System.in流,很方便地将输入的String字符串转换成需要的数据类型
InputStreamReader/OutputStreamReader ,  字节和字符的转换桥梁,在网络通信或者处理键盘输入的时候用
BufferedReader/BufferedWriter , BufferedInputStream/BufferedOutputStream , 缓冲流用来包装字节流后者字符流,提升IO性能,

BufferedReader还可以方便地读取一行,简化编程。

-----------------------------------------------------------------------------------------

线程同步方式

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] view plain copy
  1. public V put(K key, V value) {    
  2.         if (key == null)    
  3.             return putForNullKey(value);    
  4.         int hash = hash(key.hashCode());    
  5.         int i = indexFor(hash, table.length);    
  6.         for (Entry<K,V> e = table[i]; e != null; e = e.next) {    
  7.             Object k;    
  8.             //判断当前确定的索引位置是否存在相同hashcode和相同key的元素,如果存在相同的hashcode和相同的key的元素,那么新值覆盖原来的旧值,并返回旧值。    
  9.             //如果存在相同的hashcode,那么他们确定的索引位置就相同,这时判断他们的key是否相同,如果不相同,这时就是产生了hash冲突。    
  10.             //Hash冲突后,那么HashMap的单个bucket里存储的不是一个 Entry,而是一个 Entry 链。    
  11.             //系统只能必须按顺序遍历每个 Entry,直到找到想搜索的 Entry 为止——如果恰好要搜索的 Entry 位于该 Entry 链的最末端  
  12.              //(该 Entry 是最早放入该 bucket 中),    
  13.             //那系统必须循环到最后才能找到该元素。    
  14.             if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {    
  15.                 V oldValue = e.value;    
  16.                 e.value = value;    
  17.                 return oldValue;    
  18.             }    
  19.         }    
  20.         modCount++;    
  21.         addEntry(hash, key, value, i);    
  22.         return null;    
  23.     }  


我们知道在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、构造函数的重载:跟方法的重载类似,唯一的不同就是功能不一样,构造函数的重载,增加不同参数,来达到初始化属性的目的

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

[java] view plain copy
  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. }  

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

-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------


----------------------------------------------------------------------------

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 没有被修改,读取的内容为缓存;
401 未授权
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

-------------------------------------------------------------


--------------------------------------------------------------------------------------------------------------------------------------------------------------------

下面代码输出的结果是?

[java] view plain copy
  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. }  
答案:MTDP
null不是对象,它可以看成是指向不确定对象的引用。
赋值时,基本类型赋初值不能为null,如int=0只能是这种,换而言之,int也没有为空这一说法,如果非要勉强说有,那就是0。
而对象赋初值可以将其设为null。
本列,null是java的关键字,故不用事先声明它,直接把null作为NULL对象的一个引用,将其实例化,故而有输出。

----------------------------------------------------------------------------------------------------------------------------

线程池的作用:
在程序启动的时候就创建若干线程来响应处理,它们被称为线程池,里面的线程叫工作线程
第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
第二:提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
第三:提高线程的可管理性。
常用线程池:ExecutorService 是主要的实现类,其中常用的有常见线程池
①newSingleThreadExecutor
单个线程的线程池,即线程池中每次只有一个线程工作,单线程串行执行任务
②newFixedThreadExecutor(n)
固定数量的线程池,没提交一个任务就是一个线程,直到达到线程池的最大数量,然后后面进入等待队列直到前面的任务完成才继续执行
③newCacheThreadExecutor(推荐使用)
可缓存线程池,当线程池大小超过了处理任务所需的线程,那么就会回收部分空闲(一般是60秒无执行)的线程,当有任务来时,又智能的添加新线程来执行。
④newScheduleThreadExecutor
大小无限制的线程池,支持定时和周期性的执行线程

线程池的理解及使用

线程池的主要工作流程如下图

首先线程池判断“基本线程池”(corePoolSize)是否已满?没满,创建一个工作线程来执行任务。满了,则进入下个流程。
- 其次线程池判断工作队列(workQueue)是否已满?没满,则将新提交的任务存储在工作队列里。满了,则进入下个流程。
- 最后线程池判断整个线程池的线程数是否已超过maximumPoolSize?没满,则创建一个新的工作线程来执行任务,满了,则交给拒绝策略来处理这个任务。

---------------------------------------------------------------------------------------------------------------------------------------------------------------------

java concurrent包下的4个类

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

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

----------------------------------------------------------------

Lock与synchronized 的区别

1、lock是一个接口,而synchronized是一个关键字。

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

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

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


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:如果获取了锁定立即返回,如果没有获取锁定,当前线程处于休眠状态,直到或者锁定,或者当前线程被别的线程中断

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

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


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

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

 7、Lock要手动在finally里释放锁,而syncronized不需要

----------------------------------------------------------------------------------------------------------------


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

-----------------------------------------------------------------------------------------------------------------------------------------------------------------

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

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

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

-----------------------------------------------------------------------------------------

自旋锁

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

[java] view plain copy
  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. }  
使用了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的时候,单例是已经存在的了,

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

一、饿汉式单例

[java] view plain copy
  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. }  


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

[java] view plain copy
  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. }    

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

1、在getInstance方法上加同步

[java] view plain copy
  1. public static synchronized Singleton getInstance() {    
  2.          if (single == null) {      
  3.              single = new Singleton();    
  4.          }      
  5.         return single;    
  6. }    

2、双重检查锁定
[java] view plain copy
  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.     }    

3、静态内部类
[java] view plain copy
  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. }  
---------------------------------------------------------------------------------------------------------------
原型模式的结构

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

就不再需要关心这个实例本身的类型,只要实现了克隆自身的方法,就可以通过这个方法来获取新的对象,而无须再去通过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()方法
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值