Java基础

Java基础

1. i++++i,在字节码中如何体现的

  • i++ :先读取i的值,然后在将本地变量表中的i增加1
    • int b = a++在字节码中的表现,假设a在本地变量表中的index是1,b的index是2
      1. iload_1 //先读取a的值,放入栈顶
      2. iinc 1 by 1 // iinc,修改本地变量的值,第一个1是变量在本地变量表中的的下标,后一个1是增加的值
      3. istore_2 // 将栈顶元素赋值给本地变量表中的第二个变量,也就是b
  • ++i :先将本地变量表中的i值增加1,然后在读取i的值
    • int b = ++a在字节码中的表现
      • iinc 1 by 1
      • iload_1
      • istore_2

2. java的自动装箱与拆箱

  • Java的自动装箱拆箱其实就是Java的语法糖,简化代码.实际上在编译阶段JVM已经自动将类型转换写入到了字节码中. 装箱时自动调用Integer.valueOf(int),拆箱时调用Integer.intValue()
  • Integer a = 6在字节码中的表现,假设a在本地变量表中的index为1
    • sipush 6 // 将int型6压入栈顶
    • invokestatic #2 <java/lang/Integer.valueOf> // 调用静态方法Integer.valueOf(6)
    • astore_1
  • int b = a在字节码中的表现,a的index为1,值为Integer类型的6,b的index为2
    • aload_1 //将a压入栈顶
    • invokevirtual #3 <java/lang/Integer.intValue> // 调用实例方法a.intValue()
    • astore_2

3. Object类中的equals和hashCode方法的作用?什么时候要重写hashCode?

  • Object中的equals和hashCode方法
    • equals:直接比较两个对象地址是否相同
    • hashCode:native方法,根据对象地址算出一个hash值
  • 为啥修改equals的时候要同时修改hashCode?

因为在使用散列表的情况下,比如HashMap,HashSet
会先根据hashCode来计算对象在散列表中的位置,
如果修改了equals的逻辑,但是没有修改hashCode,就可能造成错误.
比方说现在有个Ball类,它有个color属性,我们重写了equals方法,只要color相同就行,我们将各种颜色的球都放入HashSet中,要求如果HashSet中不能有相同颜色的球.
如果没有重写hashCode方法,那么hashCode返回的是对象内存地址相关的值,不同的对象肯定不相同
HashMap在put时,会根据hash值算出它在table中的index,如果当前table的index位置没有数据,就直接放入,如果有数据,依次遍历这些数据,判断原有数据是否等于要put的对象,如果等于的话,根据需要替换对象或者不替换.
关键就在这个判断原有数据是否等于要加入的数据:
hashMap中先判断两个数据的hash值是否相同,如果不相同,就认为这两个对象不相同
只有hash值相同了,才会去调用equals看是否相同.
p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))
p表示hashMap中现有的数据,
首先判断要put的数据的hash值是否等于现有的数据,如果不等于,就认为put数据和原有数据不相等.

  • HashMap中对hashCode的运用:

在HashMap内部维护了一个table数组,
对象通过一定的hash算法确定在table中的index
如何获得对象放入HashMap中table的index:
1, 调用自身的hashCode获取一个hash值
2, hash ^ (hash>>>16) 获取对象在HashMap中的hash值
3, index = (length-1) & hash
通常HashMap长度不会很大,使用(length-1) & hash获取到index的时候,
大多只有hash低位的在参与运算,决定index的值,
而通过第2步,hash值与自身右移16位后亦或,将hash值的高位信息融入到低位,
这样就能减小hash冲突的概率

3-1. Runnable和Callable接口

  • 实现方法:
    • Runnable需要实现public abstract void run();
    • Callable需要实现V call() throws Exception;
  • 相同之处:
    • Runnable和Callable都是接口,都是为了要执行某一任务
  • 不同之处:
    1. call方法有返回值,而run没有返回值
    2. call方法能够抛出checked exception,而run不行
    3. Callable和Runnable都可以用于Executors,而Thread类只支持Runnable
      • Future接口,一般都是取回Callable执行状态用的,主要方法有:
        • cancel,取消Callable的执行,当Callable还没有完成时
        • get,获取Callable的返回值
        • isCancled,判断是否取消了
        • isDone,判断是否完成

3-2. synchronized锁升级过程


原文地址:https://www.cnblogs.com/Serenity1994/p/12497703.html