Java基础整理

Java基础

try finally return执行顺序

https://juejin.cn/post/7081317835930075173

两个对象的 hashCode()相同,则 equals()是否也一定为 true

两个对象equals相等,则它们的hashcode必须相等,反之则不一定。
两个对象==相等,则其hashcode一定相等,反之不一定成立。

重写一个对象的hashcode

@Override
 public boolean equals(Object o) {
     if (this == o) return true;
     if (o == null || getClass() != o.getClass()) return false;
     Person person = (Person) o;
     return age == person.age && i == person.i && j == person.j && Objects.equals(name, person.name) && Objects.equals(p_Field, person.p_Field) && Objects.equals(sex, person.sex);
 }

 @Override
 public int hashCode() {
     return Objects.hash(age, name, i, j, p_Field, sex);
 }


 // Object的equals判断
 public static boolean equals(Object a, Object b) {
     return (a == b) || (a != null && a.equals(b));
 }
 // Object的hash判断
 public static int hash(Object... values) {
     return Arrays.hashCode(values);
 }
 // Arrays的hashcode,注意31和result的作用和写法
 public static int hashCode(Object a[]) {
     if (a == null)
         return 0;
     int result = 1;
     for (Object element : a)
         result = 31 * result + (element == null ? 0 : element.hashCode());
     return result;
 }

final关键字

final 修饰的类叫最终类,该类不能被继承。
final 修饰的方法不能被重写。
final 修饰的变量叫常量,常量必须初始化,初始化之后值就不能被修改,引用类型不能指向其他值。

继承与多态

父类

package javabasic.exceptionTest.inherit;
public class Person {
    public int age;
    public String name;
    
    // 静态变量
    public static String staticField = "父类--静态变量";
    protected int i = 1;
    protected int j = 8;

    // 变量
    public String p_Field = "父类--变量";

    // 静态初始化块
    static {
        System.out.println(staticField);
        System.out.println("父类--静态初始化块");
    }

    // 初始化块
    {
        System.out.println(p_Field);
        System.out.println("父类--初始化块");
    }


    public Person(){
        System.out.println("父类");
        // 子类继承了用子类的方法
        say();
        System.out.println("父类--构造器");
        System.out.println("i=" + i + ", j=" + j);
        j = 9;
    }
    protected void say(){
        System.out.println("有人说话。");
    }
    private String sex;

    public String getSex() {
        return sex;
    }
    public void setSex(String sex) {
        this.sex = sex;
    }
}

子类

package javabasic.exceptionTest.inherit;

public class SubClass extends Person{
    public String school;
    public SubClass(){
        System.out.println("子类");
    }

    // 静态变量
 // public static String staticField = "子类--静态变量";
    // 变量
    public String s_Field = "子类--变量";

    // 静态初始化块
    static {
        System.out.println(staticField);
        System.out.println("子类--静态初始化块");
    }
    // 初始化块
    {
        System.out.println(s_Field);
        System.out.println("子类--初始化块");
    }
    public SubClass(String name, int age){
        this.name = name;
        this.age = age;
        System.out.println("子类--构造器");
        System.out.println("i=" + i + ",j=" + j);
    }
    protected void say(){
        System.out.println("子类"+name+age);
    }
    public void subSelf(){
        System.out.println("子类"+"subSelf");
    }
}
package javabasic.exceptionTest.inherit;
public class MainTest {
    public static void main(String[] args) {
        Person person = new SubClass("xiaoma", 10);
        person.setSex("girl");
        System.out.println(person.getSex());
        System.out.println(SubClass.staticField);
        // 多态
        person.say();
    }
}
父类--静态变量
父类--静态初始化块
父类--静态变量
子类--静态初始化块
父类--变量
父类--初始化块
父类
学生null0
父类--构造器
i=1, j=8
子类--变量
子类--初始化块
子类--构造器
i=1,j=9
girl
父类--静态变量
学生xiaoma10

总结:

在这里插入图片描述

  1. 子类方法与父类同名,切参数一致,则修饰符范围要大于等于父类。
  2. 静态变量也可以继承,如果子类定义了相同的静态变量,则会覆盖。
  3. 子类和父类,继承时可以理解为父类的类空间在子类中,使用子类内容时,先在子类中寻找,找不到去父类空间寻找。

自动装箱和拆箱

1 package cn.zpoor.test;
 2 /**
 4  * 自动装箱拆箱*/
 5 public class Main {
 6     public static void main(String[] args) {
 7         Integer i = 10;
 8         int n = i;
 9     }
10 }

在这里插入图片描述
不要慌,冷静分析:在装箱的时候自动调用的是Integer的valueOf()方法,拆箱的时候是自动调用Integer的intValue()方法。
装箱过程是通过调用包装器的valueOf方法实现,拆箱过程是通过调用包装器的xxxValue方法实现的(xxx代表基本数据类型)。

package javabasic.exceptionTest.chaixiangzhuangxiang;

import threadtest.DoubleCheckLock;

public class Test {
    public static void main(String[] args) {
        // 自动装箱
        Integer i = 10;
        // 自动拆箱
        int n = i;
        
        Integer j = Integer.valueOf(20);
        Long k = Long.valueOf(10);
        Double d = Double.valueOf(2.74);
        Character c = Character.valueOf('d');

		// 高类型向低类型转换。
        short a =10;
        n = a;

        Short st = Short.valueOf(a);

    }
}

//Integer,范围-128~127.

public static Integer valueOf(int i) {
     if (i >= IntegerCache.low && i <= IntegerCache.high)
         return IntegerCache.cache[i + (-IntegerCache.low)];
     return new Integer(i);
 }

//Long,范围-128~127.

public static Long valueOf(long l) {
    final int offset = 128;
    if (l >= -128 && l <= 127) { // will cache
        return LongCache.cache[(int)l + offset];
    }
    return new Long(l);
}

//Float, 每次都新生成个对象。

public static Float valueOf(float f) {
    return new Float(f);
}

// Double,每次都新生成个对象。

public static Double valueOf(double d) {
    return new Double(d);
}

// Character, 范围0~127.

public static Character valueOf(char c) {
      if (c <= 127) { // must cache
          return CharacterCache.cache[(int)c];
      }
      return new Character(c);
  }
  
private static class CharacterCache {
      private CharacterCache(){}
      static final Character cache[] = new Character[127 + 1];
      static {
          for (int i = 0; i < cache.length; i++)
              cache[i] = new Character((char)i);
      }
  }

Byte, 范围-128~127.

 public static Byte valueOf(byte b) {
     final int offset = 128;
     return ByteCache.cache[(int)b + offset];
 }

Short, 范围-128~127.

    public static Short valueOf(short s) {
        final int offset = 128;
        int sAsInt = s;
        if (sAsInt >= -128 && sAsInt <= 127) { // must cache
            return ShortCache.cache[sAsInt + offset];
        }
        return new Short(s);
    }

Boolean, 默认就两个类型。

   public static Boolean valueOf(boolean b) {
        return (b ? TRUE : FALSE);
    }

注意:

  1. java 字面量默认为int和double类型
  2. byte、char、short三种类型参与运算时,先一律转换成int类型再进行运算。示例如下:
byte b = 97;
int num = b + b; //num的值为194
  1. 注意float运算不会默认转成double。
  2. 低类型金可向高类型转换,高类型不能向低类型转换,(byte, short, char)–>int–>long–>float–>double, char不与byte和short进行转换。虽然float是4字节,但是由于是浮点数,存储方式与整形存储方式不一样,float类型的范围是:一3.403E383.403E38。而long类型的范围是:-2^63263-1(大概是9*1018), float存储数值范围要大于long类型。所以long可以向float进行转换。
    在这里插入图片描述

集合遍历过程中删除

remove操作 int 时,为操作索引, remove操作Object对象时,移除的是对象。
当集合遍历时,如果使用迭代器的方式遍历,但是不使用迭代器的改变元素数量(remove)或者add . 此时遍历就会报错。
报错原因 :

  1. 初始化遍历器时,会有 int expectedModCount = modCount;
  2. 遍历器遍历时,会判断
 final void checkForComodification() {
     if (modCount != expectedModCount)
         throw new ConcurrentModificationException();
 }
  1. 集合添加修改元素时,会修改expectedModCount的值。
package study.collectiontest;

import com.google.common.collect.Lists;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

public class ListTEst {
    public static void main(String[] args) {
        for(int i=0 ;i<10000;i++){
            testRemoveIII();
        }
    }


    public static void testRemove(){
        ArrayList<Integer> integers = Lists.newArrayList(1, 2, 3, 4);
        System.out.println(integers);
        integers.remove(new Integer(1));
        //1是索引 ,object是对象
        integers.remove(1);
        System.out.println(integers);
    }
     // 会报错
    public static void testRemoveII(){
        List<String> stringList = Lists.newArrayList("m", "g", "h");
        for (String s : stringList) {
            if (Arrays.asList("m", "h").contains(s)) {
                stringList.remove(s);
            }
        }
    }
    // 不会报错
    public static void testRemoveIII(){
        List<Integer> integers = new ArrayList<>(5);
        integers.add(1);
        integers.add(2);
        integers.add(2);
        integers.add(4);
        integers.add(5);

        for (int i = 0; i < integers.size(); i++) {
            if (integers.get(i)%2==0){
                integers.remove(i);
            }
        }
        System.out.println(integers);
    }

    // 会报错,索引错误
    public void testRemoveIIII(){
        List<String> strings = new ArrayList<>();
        strings.add("a");
        strings.add("b");
        strings.add("c");
        strings.add("d");

        int size = strings.size();
        for (int i = 0; i < size; i++) {
            strings.remove(i);
        }
    }
    // 会报错,CurrentModifyTion
    public void testRemoveIIIII(){
        List<String> strings = new ArrayList<>();
        strings.add("a");
        strings.add("b");
        strings.add("c");
        strings.add("d");

        Iterator<String> iterator = strings.iterator();
        while (iterator.hasNext()){
            String next = iterator.next();
            strings.remove(next);
        }

        System.out.println(strings);
    }

    //正确做法
    public void testRemoveIIIIII(){
        List<String> strings = new ArrayList<>();
        strings.add("a");
        strings.add("b");
        strings.add("c");
        strings.add("d");

        Iterator<String> iterator = strings.iterator();
        while (iterator.hasNext()){
            String next = iterator.next();
            iterator.remove();
        }

        System.out.println(strings);
    }
}

ConcurrentHashMap

  1. jdk1.7采用的是segment方法,初始化容量为16,每次插入entry时,需要先hash定位segment,在segment的table里定位索引,在插入数据,加锁力度是segment。
  2. jdk1.8采用node+cas+synchronized方法,实现并发线程安全,

4.如果没有hash冲突,直接通过CAS将数据放置到tab中,如果发生冲突了,则通过synchronized对node上锁,哈希冲突超过8个节点就生成红黑树结构。

    /** Implementation for put and putIfAbsent */
    final V putVal(K key, V value, boolean onlyIfAbsent) {
        if (key == null || value == null) throw new NullPointerException();
        int hash = spread(key.hashCode());
        int binCount = 0;
        for (Node<K,V>[] tab = table;;) {
            Node<K,V> f; int n, i, fh;
            if (tab == null || (n = tab.length) == 0)
                tab = initTable();
            else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
                if (casTabAt(tab, i, null,
                             new Node<K,V>(hash, key, value, null)))
                    break;                   // no lock when adding to empty bin
            }
            else if ((fh = f.hash) == MOVED)
                tab = helpTransfer(tab, f);
            else {
                V oldVal = null;
                synchronized (f) {
                    if (tabAt(tab, i) == f) {
                        if (fh >= 0) {
                            binCount = 1;
                            for (Node<K,V> e = f;; ++binCount) {
                                K ek;
                                if (e.hash == hash &&
                                    ((ek = e.key) == key ||
                                     (ek != null && key.equals(ek)))) {
                                    oldVal = e.val;
                                    if (!onlyIfAbsent)
                                        e.val = value;
                                    break;
                                }
                                Node<K,V> pred = e;
                                if ((e = e.next) == null) {
                                    pred.next = new Node<K,V>(hash, key,
                                                              value, null);
                                    break;
                                }
                            }
                        }
                        else if (f instanceof TreeBin) {
                            Node<K,V> p;
                            binCount = 2;
                            if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key,
                                                           value)) != null) {
                                oldVal = p.val;
                                if (!onlyIfAbsent)
                                    p.val = value;
                            }
                        }
                    }
                }
                if (binCount != 0) {
                    if (binCount >= TREEIFY_THRESHOLD)
                        treeifyBin(tab, i);
                    if (oldVal != null)
                        return oldVal;
                    break;
                }
            }
        }
        addCount(1L, binCount);
        return null;
    }

5.get时,没有使用synchronized,也没有使用cas,因为get时,Node节点结构。

public V get(Object key) {
        Node<K,V>[] tab; Node<K,V> e, p; int n, eh; K ek;
        int h = spread(key.hashCode());
        if ((tab = table) != null && (n = tab.length) > 0 &&
            (e = tabAt(tab, (n - 1) & h)) != null) {
            if ((eh = e.hash) == h) {
                if ((ek = e.key) == key || (ek != null && key.equals(ek)))
                    return e.val;
            }
            else if (eh < 0)
                return (p = e.find(h, key)) != null ? p.val : null;
            while ((e = e.next) != null) {
                if (e.hash == h &&
                    ((ek = e.key) == key || (ek != null && key.equals(ek))))
                    return e.val;
            }
        }
        return null;
    }

其中Node的数据结构为这样,其中hash和key是不变的,val,和next都用了violate关键字修饰,保证了可见性,当有修改时,get也能够查找到。
在这里插入图片描述

ConcurrentModificationException

举例:
https://blog.csdn.net/zymx14/article/details/78394464?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.control&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.control
其中:
集合类的内部的迭代器使用时,如果集合修改元素会抛出异常,在迭代器初始化时,固定修改值,next会进行比较,如果失败,就会抛出异常。

ConcurrentHashmap是弱一致性的?

针对get,clear, 遍历, 整体理解,因为这三种操作无锁,所以多线程是,元素put时,最终得到的结果可能会发生不一致。所以导致弱一致。
如果加锁,控制集合访问,因为只有一个线程能访问集合,一个线程得到的结果固定(加锁顺序固定)。
理解: 如果两个线程,同时执行get,clear,遍历操作, 另一个线程put,remove时,两个线程得到的结果可能是不一样的。

CopyOnWriteArrayList:

CopyOnWrite 容器即写时复制的容器,也就是当我们往一个容器添加元 素的时候,不直接往当前容器添加,而是先将当前容器进行 Copy,复制出一个 新的容器,然后新的容器里添加元素,添加完元素之后,再将原容器的引用指 向新的容器(改变引用的指向)。这样做的好处是我们可以对 CopyOnWrite 容器进行并发的读,而不需要加锁,因为当前容器不会添加任何元素。所以 CopyOnWrite 容器也是一种读写分离的思想,读和写在不同的容器上进行, 注意,写的时候需要加锁。

初始化时,getArray()返回的是一个元素为0的空数组,每次都会add都会生成新的数组。

添加

   public boolean add(E e) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            Object[] elements = getArray();
            int len = elements.length;
            Object[] newElements = Arrays.copyOf(elements, len + 1);
            newElements[len] = e;
            setArray(newElements);
            return true;
        } finally {
            lock.unlock();
        }
    }

读不加锁,读写分离

    public E get(int index) {
        return get(getArray(), index);
    }

ArrayList、LinkedList、 Vector

  1. 其中ArrayList是基于数组。
  2. LinkedList是基于双向链表
  3. Vector线程安全

HashMap、LinkedHashMap、TreeMap

HashMap
public V put(K key, V value) {
     return putVal(hash(key), key, value, false, true);
 }
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
               boolean evict) {
    Node<K,V>[] tab; Node<K,V> p; int n, i;
    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<K,V> 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<K,V>)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);
                    if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                        treeifyBin(tab, hash);
                    break;
                }
                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;
}

扩容

final Node<K,V>[] resize() {
        Node<K,V>[] oldTab = table;
        int oldCap = (oldTab == null) ? 0 : oldTab.length;
        int oldThr = threshold;
        int newCap, newThr = 0;
        if (oldCap > 0) {
            if (oldCap >= MAXIMUM_CAPACITY) {
                threshold = Integer.MAX_VALUE;
                return oldTab;
            }
            else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                     oldCap >= DEFAULT_INITIAL_CAPACITY)
                newThr = oldThr << 1; // double threshold
        }
        else if (oldThr > 0) // initial capacity was placed in threshold
            newCap = oldThr;
        else {               // zero initial threshold signifies using defaults
            newCap = DEFAULT_INITIAL_CAPACITY;
            newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
        }
        if (newThr == 0) {
            float ft = (float)newCap * loadFactor;
            newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
                      (int)ft : Integer.MAX_VALUE);
        }
        threshold = newThr;
        @SuppressWarnings({"rawtypes","unchecked"})
        Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
        table = newTab;
        if (oldTab != null) {
            for (int j = 0; j < oldCap; ++j) {
                Node<K,V> e;
                if ((e = oldTab[j]) != null) {
                    oldTab[j] = null;
                    if (e.next == null)
                        newTab[e.hash & (newCap - 1)] = e;
                    else if (e instanceof TreeNode)
                        ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
                    else { // preserve order
                        Node<K,V> loHead = null, loTail = null;
                        Node<K,V> hiHead = null, hiTail = null;
                        Node<K,V> next;
                        do {
                            next = e.next;
                            if ((e.hash & oldCap) == 0) {
                                if (loTail == null)
                                    loHead = e;
                                else
                                    loTail.next = e;
                                loTail = e;
                            }
                            else {
                                if (hiTail == null)
                                    hiHead = e;
                                else
                                    hiTail.next = e;
                                hiTail = e;
                            }
                        } while ((e = next) != null);
                        if (loTail != null) {
                            loTail.next = null;
                            newTab[j] = loHead;
                        }
                        if (hiTail != null) {
                            hiTail.next = null;
                            newTab[j + oldCap] = hiHead;
                        }
                    }
                }
            }
        }
        return newTab;
    }

HashMap不安全的原因:put 操作时如果key值不相等。但是hashcode值相等,同时放入一个table[index]中时会发生消息丢失,会发生消息丢失。
jdk1.7和jdk1.8
jdk1.7
采用头插法,
hash运算方式为4次位运算,5次异或。
hash冲突采用拉链法解决。
先扩容,再将数据放入到hash表中。
两个线程一起扩容时,可能会发生死循环。
jdk1.8
采用尾插法
hash运算方式为1次位运算,一次异或。
hash冲突采用红黑树解决
是想将数据放入hash表中,在进行扩容。

LinkedHashMap

1,底层采用双向链表加hash表的形式存储数据。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值