第一阶段主要问题汇总
1. Array和ArrayList的区别:
1.长度方面:Array的长度是固定的 ,在初始化的时候已经定义好的。 ArrayList是可以动态扩容的(当发现空间不足的时候会动态扩容1.5倍)
2.数据方面:Array可以存储基本数据类型和对象(封装数据类型),而且一段Array只能存储一个数据类型的数据。ArrayList只能存储对象(默认是以Objcet类型存储,所以一个ArrayList数组里面可以存储不同数据类型),但是也导致了Array在使用时比较安全,因为其他数据类型无法添加进数组里。
3.用法方面:ArrayList是更深层次的封装,ArrayList在Array的基础上增加了很多的方法。比如add,addAll,remove,removeAll,contains,以及iterator等等多种丰富的功能方法。所以ArrayList在使用上比较方便。
4.运行速度上:由于ArrayList封装了许多方法,且存取的时候要对Objcet类型数据进行自动转换,而且基本数据类型在存取的时候会被java自动装箱拆箱,所以内存耗费大。
2. Vector和ArrayList的区别:
两者都是List接口的实现类,都是有序集合,能通过索引访问,可以存储重复数据。
1.动态扩容上:vector会扩容为原来的2倍,而ArrayList会扩容为原来的1.5倍。
2.线程安全上:vector是线程安全的,ArrayList是线程不安全的,在多线程的时候使用vector,在单线程的时候使用ArrayList速度会比较快。
3.HashMap,TreeMap,HashTable的区别:
三者都实现了Map接口,都是通过键值对存储,都是不允许Key值重复
1.HashMap和TreeMap是线程不安全的,HashTable是添加了synchronized关键字,所以线程安全。
2.HashTable不允许key-value中出现null值,HashMap和TreeMap允许一个null的key和多个null的value。
3.HashMap和TreeMap的主要区别于,TreeMap是通过红黑树存储的,所以遍历和查找比较方便(复杂度为O(logn))。
4.HashMap 的工作原理是什么?
个人理解:HashMap的存储单位是一个键值对(Key-Value)对象。其中key允许一个null值,value允许0-n个null值。
HashMap的存储方式在于利用了哈希函数和(数组+链表)的形式存储。
当传入一个数据的时候,会通过hash函数计算关键字,得出一个index,存入对应的index的数组中,当出现同一个index数值的时候,根据先后顺序,用链表的形式存储数据。
HashMap中查找所花费的时间主要是在链表的遍历上,所以要提高性能的话,就要找到一个相对较好的Hash函数,使得链表的长度相对平均,不会出现链表过长的现象。
5.什么是序列化,如何实现序列化?
个人理解:序列化是把数据,对象等数据转化为可以传输的字节序列,可以把这些字节序列通过输入输出流传输,也可以把这些字节序列以文件的形式保存。其最大的作用就是对象的存储。
JAVA中,实现序列化首先需要对象继承Serializable接口,这个接口没有实现方法,可以把它当作一个标记,只有拥有这个标记的对象才能实现序列化。
然后通过输入流输出流
(这里一般用ObjectInputstream/ObjectOutputStream
进行流传输)
读取的时候记得强制转化类型,就可以获得相对应的对象。
6.进程和线程有什么区别?
进程:是指占用一个独立的内存空间,在这块独立空间运行的应用程序。(可以理解为一个软件or一个app,虽然当前市面上许多程序都是多进程的)
线程:是进程中的一个执行路径,在进程中,线程可以随意切换,并行,并发运行,一个进程最少有一个线程。线程相当于对进程的多次划分。(可以理解成一个软件中的多项功能,类似于打游戏的同时可以语音,缓存音乐的时候可以播放音乐,)
7.Java 当中如何实现线程呢?
public class Demo1 {
public static void main(String[] args) throws InterruptedException {
/**
* 多线程技术 Thread 和 Runnable的区别
* Thread 是继承的 ,Runnable是接口实现的
* 1. Runnable通过创建任务,导入线程的方式运行
* 2. Thread通过继承线程类,对父类的任务方法重写的方式运行
* Runnable相比Thread有如下好处:
* 1. 多线程执行相同任务时,Runnable效果好
* 2. Thread只能单继承,不能再继承其他类。
* 3. 任务和线程分离,提高健壮性
* 4. 线程池技术接受Runnable任务
*/
//Thread
MyThread myThread = new MyThread(); //直接继承一个线程对象,重写其运行任务
//Runnable
MyRunnable myRunnable = new MyRunnable();//创建一个任务对象
Thread t = new Thread(myRunnable); //把任务传给一个新建的线程
/**
* java是抢占式调度的,所以执行顺序是不确定的
*/
myThread.start(); //副1线程启动
t.start(); //副2线程启动
for (int i =2;i<4097;i=i*2){
//主线程启动
System.out.println("主线程:"+i);
}
//匿名内部类实现多线程
new Thread(){
@Override
public void run() {
for (int i =2;i<4097;i=i*2){
System.out.println("副3线程:"+i);//副线3程启动
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
Thread ts = new Thread(new MyRunnables());
ts.start();
ts.sleep(1000);
new Thread(new MyRunnables()).start();
new Thread(new MyRunnables()).start();
}
//任务类Runnable
public static class MyRunnables implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
}
8.线程的生命周期
1.新建状态:一般是new Thread 的时候 线程刚被创建
2.就绪状态:一般是等待调用start() 方法前的状态,等待被运行。
3.运行状态:一般是调用start后,线程运行代码的状态
4.死亡状态:线程运行代码结束,自动死亡(用户让其死亡,或者JAVA有垃圾回收机制,会让运行完的线程死亡。)
5.阻塞状态:阻塞状态一般指同步锁orSynchronized关键字的原因,使得该代码还在被其他线程运行中,在等待运行的状态。
6.等待状态:这里指的是人为的调用sleep方法,刻意让线程延迟等待一段时间再运行。
.9.多线程并发或线程安全问题如何解决
三种方法:
1.使用同步代码块:synchonized(){}
2.同步方法:方法前缀加 synchonized
3.显式锁 :Lock() (解锁的时候调用UnLock())
10.synchronized 和 ReentrantLock 的区别
相同点:都是防止多线程使得线程不安全,都是通过阻塞式调度控制线程。
不同点:
1.代码书写中:Synchronized关键字只需要添加在方法体或者前缀中即可,会自动释放锁,而ReentrantLock 则需要 lock 和 unlock 以及 try-catch-finally ,代码简易性上Synchronized要比较好
2.使用中:由于ReentrantLock 有 lock 和 unlock ,用户可以任意标记位置,灵活性大于Synchronized关键字。且ReentrantLock 可以通过try-catch中断,而Synchronized关键字不行。
3.锁的类型上:synchronized为非公平锁 ReentrantLock则即可以选公平锁也可以选非公平锁,通过构造方法new ReentrantLock时传入boolean值进行选择,为空默认false非公平锁,true为公平锁。