面试day4

#java hashcode和equals

hashcode是JVM对对象的内存地址做一定的计算后的到的一个int值

对于equals()与hashcode(),比较通用的规则:
①两个obj,如果equals()相等,hashCode()一定相等
②两个obj,如果hashCode()相等,equals()不一定相等

2)在设计一个类的时候往往需要重写equals方法,比如String类,在重写equals方法的同时,必须重写hashCode方法。
如果我们对一个对象重写了euqals,意思是只要对象的成员变量值都相等那么euqals就等于true,但不重写hashcode,那么我们再new一个新的对象,当原对象.equals(新对象)等于true时,两者的hashcode却是不一样的,由此将产生了理解的不一致,如在存储散列集合时(如Set类),将会存储了两个值一样的对象,导致混淆,因此,就也需要重写hashcode()。

#内部类

###为什么成员内部类可以无条件访问外部类的成员?

编译器会默认为成员内部类添加了一个指向外部类对象的引用,那么这个引用是如何赋初值的呢?

虽然在定义的内部类的构造器是无参构造器,编译器还是会默认添加一个参数,该参数的类型为指向外部类对象的一个引用,所以成员内部类中的Outter this&0 指针便指向了外部类对象,因此可以在成员内部类中随意访问外部类的成员。从这里也间接说明了成员内部类是依赖于外部类的,如果没有创建外部类的对象,则无法对Outter this&0引用进行初始化赋值,也就无法创建成员内部类的对象了。

###为什么局部内部类和匿名内部类只能访问局部final变量?

 当外部类方法执行完毕之后,变量的生命周期就结束了,而此时内部类对象的生命周期很可能还没有结束,那么在内部类的方法中继续访问变量就变成不可能了,但是又要实现这样的效果,怎么办呢?Java采用了final复制的手段来解决这个问题。将这段代码的字节码反编译可以得到下面的内容    

#synchronized与Lock的区别

###synchronized与Lock的区别

synchronized

Java的关键字,在jvm层面上

1、以获取锁的线程执行完同步代码,释放锁 2、线程执行发生异常,jvm会让线程释放锁

假设A线程获得锁,B线程等待。如果A线程阻塞,B线程会一直等待

可重入 不可中断 非公平

Lock

是一个类

在finally中必须释放锁,不然容易造成线程死锁

分情况而定,Lock有多个锁获取的方式,具体下面会说道,大致就是可以尝试获得锁,线程可以不用一直等待

可重入 可判断 可公平(两者皆可)

###两种锁的底层实现方式

synchronized映射成字节码指令就是增加来两个指令:monitorentermonitorexit。当一条线程进行执行的遇到monitorenter指令的时候,它会去尝试获得锁,如果获得锁那么锁计数+1(为什么会加一呢,因为它是一个可重入锁,所以需要用这个锁计数判断锁的情况),如果没有获得锁,那么阻塞。当它遇到monitorexit的时候,锁计数器-1,当计数器为0,那么就释放锁。

那么有的朋友看到这里就疑惑了,那图上有2个monitorexit呀?马上回答这个问题:上面我以前写的文章也有表述过,synchronized锁释放有两种机制,一种就是执行完释放;另外一种就是发送异常,虚拟机释放。

而Lock用的是乐观锁方式。所谓乐观锁就是,每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止。乐观锁实现的机制就是CAS操作(Compare and Swap)。我们可以进一步研究ReentrantLock的源代码,会发现其中比较重要的获得锁的一个方法是compareAndSetState。这里其实就是调用的CPU提供的特殊指令。

###索引

  • 数据唯一性差的字段不要使用索引

比如性别,只有两种可能数据。意味着索引的二叉树级别少,多是平级。这样的二叉树查找无异于全表扫描。

  • 频繁更新的字段不要使用索引

比如logincount登录次数,频繁变化导致索引也频繁变化,增大数据库工作量,降低效率。

  • 字段不在where语句出现时不要添加索引

只有在where语句出现,mysql才会去使用索引

  • 数据量少的表不要使用索引

使用了改善也不大
另外。如果mysql估计使用全表扫描要比使用索引快,则不会使用索引。