Java杂记

1、Object类

类层次结构的根类:每个类都使用 Object 作为超类(都直接或间接继承此类)。所有对象(包括数组)都实现这个类的所有方法

package java.lang;
public class Object {
    /*一个本地方法,具体是用C(C++)在DLL中实现的,然后通过JNI调用*/
    private static native void registerNatives();
    /*对象初始化时自动调用此方法*/
    static {
        registerNatives();
    }
    /*返回此Object的运行时类*/
    public final native Class getClass();
    /*
    hashCode的常规协定是:
    1.在java应用程序执行期间,在对同一对象多次调用hashCode()方法时,必须一致地返回相同的整数,前提是将对象进行equals比较时所用的信息没有被修改。从某一应用程序的一次执行到同一应用程序的另一次执行,该整数无需保持一致。
    2.如果根据equals(object)方法,两个对象是相等的,那么对这两个对象中的每个对象调用hashCode方法都必须生成相同的整数结果。
    3.如果根据equals(java.lang.Object)方法,两个对象不相等,那么对这两个对象中的任一对象上调用hashCode()方法不要求一定生成不同的整数结果。但是,程序员应该意识到,为不相等的对象生成不同整数结果可以提高哈希表的性能。
    */
    public native int hashCode();

    /*这里比较的是对象的内存地址,跟String.equals方法不同,它比较的只是对象的值*/
    public boolean equals(Object obj) {
        return (this == obj);
    }
    /*本地clone方法,用于对象的复制*/
    protected native Object clone() throws CloneNotSupportedException;
    /*
    返回该对象的字符串表示,非常重要的方法
    getClass().getName();获取字节码文件的对应全路径名例如java.lang.Object
    Integer.toHexString(hashCode());将哈希值转成16进制数格式的字符串。
    */
    public String toString(){
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }
    /*唤醒在此对象监视器上等待的单个线程*/
    public final native void notity();
    /*唤醒在此对象监视器上等待的所有线程*/
    public final native void notifyAll();
    /*
    在其他线程调用此对象的notify()方法或notifyAll()方法前,导致当前线程等待。换句话说,此方法的行为就好像它仅执行wait(0)调用一样。
    当前线程必须拥有此对象监视器。该线程发布对此监视器的所有权并等待,直到其他线程通过调用notify方法或notifyAll方法通知在此对象的监视器上等待的线程醒来,然后该线程将等到重新获得对监视器的所有权后才能继续执行。
    */
    public final void wait() throws InterruptedException(){
        wait(0);
    }
    /*在其他线程调用此对象的notify()方法或notifyAll()方法,或者超过指定的时间量前,导致当前线程等待*/
    public final native void wait(long timeout) throws InterruptedException;
    public final void wait() throws InterruptedException {
        if(timeout < 0){
            throw new IllegalArgumentException("timeout value is negative");
        }
        //nanos不能大于等于1000000ms也就是1000s
        if(nanos < 0 || nanos > 999999){
            throw new IllegalArgumentException("nanosecond timeout value out of range");
        }
        //
        if(nanos >= 500000 || (nanos != 0 && timeout == 0)){
            timeout++;
        }
        wait(timeout);
    }

    /*当垃圾回收期确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法。*/
    protected void finalize() throws Throwable{}
}

2、ArrayList与LinkedList

Java杂记_第1张图片
- ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构(LinkedList是双向链表,有next也有previous);
- 对于随机访问get和set,ArrayList觉得优于LinkedList,因为LinkedList要移动指针;
- 对于新增和删除操作add和remove,LinedList比较占优势,因为ArrayList要移动数据。
- LinkedList需要更多的内存,因为ArrayList的每个索引的位置是实际的数据,而LinkedList中的每个节点中存储的是实际的数据和前后节点的位置。

LinkedList使用场景
- 你的应用不会随机访问数据。因为如果你需要LinkedList中的第n个元素的时候,你需要从第一个元素顺序数到第n个数据,然后读取数据
- 你的应用更多的插入和删除元素,更少的读取数据。因为插入和删除元素不涉及重排数据,所以它要比ArrayList要快。

线程安全
ArrayList,LinkedList都是线程不安全的

使用Collections.synchronizedList()解决这个线程安全问题
List<Map<String,Object>> data=Collections.synchronizedList(new ArrayList<Map<String,Object>>());

3、HashMap实现原理

继承关系
public class HashMap<K,V>extends AbstractMap<K,V>
    implements Map<K,V>, Cloneable, Serializable

HashMap的工作原理http://www.importnew.com/7099.html

Java杂记_第2张图片
HashMap采用Entry数组来存储key-value对,每一个键值对组成了一个Entry实体,Entry类实际上是一个单向的链表结构,它具有Next指针,可以连接下一个Entry实体,依次来解决Hash冲突的问题,因为HashMap是按照Key的hash值来计算Entry在HashMap中存储的位置的,如果hash值相同,而key内容不相等,那么就用链表来解决这种hash冲突。
添加方法put:

public V put(K key, V value) {
        if (table == EMPTY_TABLE) { //是否初始化
            inflateTable(threshold);
        }
        if (key == null) //放置在0号位置
            return putForNullKey(value);
        int hash = hash(key); //计算hash值
        int i = indexFor(hash, table.length);  //计算在Entry[]中的存储位置
        for (Entry e = table[i]; e != null; e = e.next) {
            Object k;
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { //判断重复key替换value
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }

        modCount++;
        addEntry(hash, key, value, i); //添加到Map中
        return null;
}

JDK1.8数据结构的存储由数组+链表的方式,变化为数组+链表+红黑树的存储方式,在性能上进一步得到提升
Java杂记_第3张图片
put方法:

public V put(K key, V value) {
    //调用putVal()方法完成
    return putVal(hash(key), key, value, false, true);
}

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
               boolean evict) {
    Node[] tab; Node p; int n, i;
    //判断table是否初始化,否则初始化操作
    if ((tab = table) == null || (n = tab.length) == 0)
        n = (tab = resize()).length;
    //计算存储的索引位置,如果没有元素,直接赋值
    if ((p = tab[i = (n - 1) & hash]) == null)
        tab[i] = newNode(hash, key, value, null);
    else {
        Node e; K k;
        //节点若已经存在,执行赋值操作
        if (p.hash == hash &&
            ((k = p.key) == key || (key != null && key.equals(k))))
            e = p;
        //判断链表是否是红黑树
        else if (p instanceof TreeNode)
            //红黑树对象操作
            e = ((TreeNode)p).putTreeVal(this, tab, hash, key, value);
        else {
            //为链表,
            for (int binCount = 0; ; ++binCount) {
                if ((e = p.next) == null) {
                    p.next = newNode(hash, key, value, null);
                    //链表长度8,将链表转化为红黑树存储
                    if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                        treeifyBin(tab, hash);
                    break;
                }
                //key存在,直接覆盖
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k))))
                    break;
                p = e;
            }
        }
        if (e != null) { // existing mapping for key
            V oldValue = e.value;
            if (!onlyIfAbsent || oldValue == null)
                e.value = value;
            afterNodeAccess(e);
            return oldValue;
        }
    }
    //记录修改次数
    ++modCount;
    //判断是否需要扩容
    if (++size > threshold)
        resize();
    //空操作
    afterNodeInsertion(evict);
    return null;
}

HashMap采用hash算法来决定Map中key的存储,并通过hash算法来增加集合的大小。hash表里可以存储元素的位置称为桶,如果通过key计算hash值发生冲突时,那么将采用链表的形式,来存储元素。HashMap的扩容操作是一项很耗时的任务,所以如果能估算Map的容量,最好给它一个默认初始值,避免进行多次扩容。HashMap的线程是不安全的,多线程环境中推荐是ConcurrentHashMap。

3、static关键字

static变量也称作静态变量,静态变量和非静态变量的区别是:静态变量被所有的对象所共享,在内存中只有一个副本,它当且仅当在类初次加载时会被初始化。而非静态变量是对象所拥有的,在创建对象的时候被初始化,存在多个副本,各个对象拥有的副本互不影响。

4、多线程

Java杂记_第4张图片

5、Java IO

Java杂记_第5张图片

6、servlet

Java杂记_第6张图片

7、String、StringBuffer和StringBuilder

String类中使用字符数组保存字符串,因为有“final”修饰符,所以可以知道string对象是不可变的。
private final char value[];
StringBuilder与StringBuffer都继承自AbstractStringBuilder类,在AbstractStringBuilder中也是使用字符数组保存字符串,但是并没有用final修饰,数组的值都是可变的。
char[] value;

String中的对象是不可变的,也就可以理解为常量,显然线程安全
StringBuffer对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的
StringBuilder并没有对方法进行加同步锁,所以是非线程安全的

8、设计模式

百度脑图23种设计模式
Java杂记_第7张图片

9、JVM架构解析

虚拟机是物理机的软件实现。Java的设计理念是WORA(Write Once Run Anywhere,一次编写随处运行)。编译器将Java文件编译为Java .class文件,然后将.class文件输入到JVM中,JVM执行类文件的加载和执行的操作。请看以下的JVM架构图:
Java杂记_第8张图片
Java杂记_第9张图片
Java杂记_第10张图片
JVM分为三个主要子系统
1. 类加载器子系统
2. 运行时数据区
3. 执行引擎

9.1、类加载器

Java 中默认提供了三个类加载器,分别是BootstarapClassLoader、ExtClassLoader、AppClassLoader,它们各自只负载加载规定目录内的Class文件到JVM中
Java杂记_第11张图片
- 启动类加载器(Bootstrap ClassLoader):负责加载 JAVA_HOME\lib 目录中的,或通过-Xbootclasspath参数指定路径中的,且被虚拟机认可(按文件名识别,如rt.jar)的类
- 扩展类加载器(Extension ClassLoader):负责加载 JAVA_HOME\lib\ext 目录中的,或通过java.ext.dirs系统变量指定路径中的类库
- 应用程序类加载器(Application ClassLoader):负责加载用户路径(classpath)上的类库
Java杂记_第12张图片
Java杂记_第13张图片

9.2、运行时数据区

Java杂记_第14张图片
Java杂记_第15张图片

10、MYSQL事务

事务性质:ACID
原子性:构成事务的的所有操作必须是一个逻辑单元,要么全部执行,要么全部不执行;
稳定性:数据库在事务执行前后状态都必须是稳定的;
隔离性:事务之间不会相互影响;
持久性:事务执行成功后必须全部写入磁盘。
事务隔离级别:
DEFAULT :默认值,表示使用底层数据库的默认隔离级别。大部分数据库这值就是READ_COMMITTED; 
Read Uncommitted(读取未提交内容):可以读取另一个事务修改但还没有提交的数据;
Read Committed(读取提交内容):只能读取另一个事务已经提交的数据;
Repeatable Read(可重读):整个过程中可以多次重复执行某个查询,并且每次返回的记录都相同;
Serializable(可串行化):所有的事务依次逐个执行,但是这将严重影响程序的性能.

Spring Boot 使用事务非常简单,首先启动主类使用注解 @EnableTransactionManagement 开启事务支持后,然后在访问数据库的Service方法上添加注解 @Transactional 便可;
@Transactional(isolation = Isolation.DEFAULT,propagation = Propagation.REQUIRED)
Isolation :隔离级别,Propagation:传播行为
Java杂记_第16张图片

你可能感兴趣的:(Java)