一. Java为何不支持多继承
1. 多继承是什么?
多继承:即一个子类可以同时继承多个基类,有多个父类,可以同时拥有多个父类的方法
2. 多继承有何优缺点?
多继承优点:扩展子类,使用更多的父类方法;
多继承缺点:当多个父类拥有相同的属性/方法时(且子类未进行重写时,重写后默认使用子类的方法),使用时会有歧义(不知该使用哪个父类属性/方法)。
3. Java为何不支持多继承?
相较于多继承的优点,它的缺点更为显著,因此Java只支持单继承,但是Java提供了办法可以实现多继承的功能。
4. 如果Java要实现多继承可以用什么办法?
Java中要实现多继承的功能,可以使用接口Interface:
接口与接口之间只能使用extends(继承),不能使用implements(实现)。
使用接口实现多继承例子:
//定义两个接口
public Interface interfaceA{
public void methodA();
}
//接口B继承接口A
public Interface interfaceB extends interfaceA{
public void methodB();
}
//定义实现类,实现接口B
public class classA implements interfaceB{
@Override
public void methodA() {
// TODO Auto-generated method stub
System.out.println("methodA");
}
@Override
public void methodB() {
System.out.println("methodB");
}
}
二. HashMap
参考文章:[https://blog.csdn.net/qq_42956048/article/details/91909184#HashMap__7]
1. HashMap是什么?
2. HashMap为什么是无序的?
与HashMap的Put方法有关,以下是put方法的实现顺序:
对数据的key值的hashcode进行Hash操作
根据hash值以及表长计算出在HashMap中的索引
查看索引地址是否有键值存在(即发生碰撞),若发生碰撞则遍历链表查看是否有相同key值存在,如存在,则覆盖其value,否则将其用链表方式链接在后(JDK8后,节点超过8个使用红黑树插入)
若未发生碰撞,则直接插入;
如map长度超过其扩容阈值(长度*扩容因子【默认0.75】)则先进行扩容,再进行插入;
null key的元素永远会放到index为0的链表里面
put的过程可参见下图:
三. 进程和线程相关知识(总结)
1. 进程和线程是什么
进程是资源分配的基本(最小)单位,有独立的内存单元。线程是执行调度的基本(最小)单位,共享内存资源。一个CPU至少有一个进程,一个进程至少有一个线程。线程可以说是进程的实体。
2. 进程和线程有什么区别
1.线程不能单独存在,必须依附于进程存在;
2.多线程程序中,一个线程死掉,整个进程就挂了,而进程不会有这种隐患,进程挂掉不会影响其他进程,因此多进程程序比多线程程序更为健壮,但进程切换的资源与效率比线程切换消耗更大;
3.线程之间通信比进程之间更快,更方便,因为他们共享静态变量、全局变量等。
3. 什么是线程安全?
当多个线程访问某个方法时,不管你通过怎样的调用方式、或者说这些线程如何交替地执行,我们在主程序中不需要去做任何的同步,这个类的结果行为都是我们设想的正确行为,那么我们就可以说这个类是线程安全的。
4. 如何保证线程安全?||synchronized和Volatile
synchronized
使用原因:synchronized是通过给当前对象(this)加锁。如,A线程正在访问方法P,使用synchronized会给P加上锁,线程B想访问P时必须先等待,进入等待状态,当A线程访问完毕后,再进场。这样保证了s的完整性synchronized保证了在多线程的环境下,数据的一致性。
如果多个线程共享一个对象,如果它们同时修改这个共享对象,这就产生了竞争现象。
此段内容参考: https://blog.csdn.net/csdnliu...
e.g 如下图所示,线程A和线程B共享一个对象obj。假设线程A从主存读取Obj.count变量到自己的CPU缓存,同时,线程B也读取了Obj.count变量到它的CPU缓存,并且这两个线程都对Obj.count做了加1操作。此时,Obj.count加1操作被执行了两次,不过都在不同的CPU缓存中。
但这种方法有个问题,就是当synchronized锁住一个对象后,别的进程如果想获取这个对象,必须等待当前线程执行完释放锁才可以,否则会一直处于等待状态。
注意点:虽然加synchronized关键字,可以让我们的线程变得安全,但是我们在用的时候,也要注意缩小synchronized的使用范围,如果随意使用时很影响程序的性能,别的对象想拿到锁,结果你没用锁还一直把锁占用,这样就有点浪费资源。
Volatile
由于线程需要使用一个变量对象时,先会将该变量拷贝到工作内存中,并且优先在工作内存中读取。因此可能会出现数据不一致的问题:
e.g 线程A读取变量a(a=1),将其拷贝到工作内存中,同时线程B也读取变量a并将其拷贝到工作内存中,A线程修改a(a=3;)在A线程将修改后的a放置在主存前,此时B线程也修改了a,但用过的不是A修改后的值(a=3)而是工作内存中的a(a=1),并对变量a进行了操作(a=a+1),此时就会出现数据不一致的问题。
使用Volatile可以保证线程读取变量时,是从主存中读取的,并且对变量的更新也会直接写到主存,保证了线程间的可见性。
lock
lock是在jdk1.6之后提出的,与synchronized类似,但是他更具有操作性。