java面试

多线程

1.创建线程的方式

  • 继承Thread类,重写run方法,通过new对象来调用start启动
  • 实现Runnable接口,重写run方法,通过new Thread(该类对象).start调用

    以上两种方式的区别

    相同点:Thread类也实现了Runnable接口,重写了run方法。

                   Thread类有形参为Runnable的构造器,所以本质上以上两种方式都是创建Thread对象。

    不同点:

                实现Runnable接口避免了单继承的局限性,适合多个相同代码的线程去处理同一资源。

                继承Thread类需要new多个对象,而实现Runnable接口只需要new一个对象,放到new Thread(对象).start()调用即可。

     优先选择实现Runnable接口的方式。

  • 实现Callable接口,重写call方法,创建这个实现类的对象,将该对象作为参数用FutureTask的构造器创建FutureTask对象,再创建new Thread(FutureTask对象).start()启动线程。

        FutureTask是Future接口的唯一实现类,它实现了Future接口和Runnable接口。

        Callable和Runnable的区别?

        1.Runnable接口没有返回值,Callable接口有返回值,可以由FutureTask对象调用get方法得到返回值

        2.call方法可以抛出异常,因为Callable接口里的call方法也抛了异常,run方法不能抛异常,只能在自己的方法体里解决。

        3.Callable支持泛型,FutureTask也支持泛型,泛型用于改变返回值类型

  • 使用线程池,Executors工具类

线程池种类:

        Executors.newCachedThreadPool()

        创建一个可缓存线程池,若长度超过需要则回收空闲线程,若不可回收则创建新线程

         Executors.newFixedThreadPool(n)

        创建一个固定线程数的线程池

         Executors.newSingleThreadPool()

        创建一个单线程的线程池,保证所有任务按优先级来执行

         Executors.newScheduledThreadPool(n)      

        创建一个定长线程池,支持定时和周期任务的执行

线程池对象的execute方法和submit方法提交任务的区别?

        1.execute只能传入Runnable的实现类对象,由于Runnable没有返回值所以返回值为null,出现异常会立即产生

       2. submit既能传入Runnable的实现类对象,也能传入Callable的实现类对象,返回值为call方法的返回值,异常会在调get方法的时候才出现,因为throws向上抛了异常。

    

2.线程的生命周期

 

新建态:创建了具体线程执行体的Thread对象就进入新建态

就绪态:调用start方法进入就绪态,获得除了CPU执行权的所有资源

运行态:获得CPU执行权,执行run方法。调用yeild方法让步交出CPU执行权回到就绪态,若优先级高很可能短暂的又回到运行态。

阻塞态:调用sleep(),wait(),join(),等待同步锁等情况进入阻塞态,情况处理完进入就绪态

死亡态(终止态):run方法执行完成,调用stop方法,出现异常未处理进入死亡态

3.线程安全

        方式一:同步代码块

        synchronized(同步监视器){

        //需要同步的代码

        }

         同步监视器俗称锁,可以由任何对象充当

        当这种方式用于继承Thread的类时需将锁定义为static的,因为会new很多个不同的对象,需确保锁唯一。

        

        方式二:同步方法

        1.在实现Runnable的类中,同步方法只需要在方法前加上synchronized,默认的锁对象为this即当前对象

        2.在继承Thread的类中,同步方法需加上static synchronized ,不加static默认的this会由于多对象的创建而不同,加了static后默认的this代表的是当前的类xxx.class,是唯一的。

         方式三:Lock锁

        在线程类里定义一个属性

        private ReetrantLock lock= ne w ReetrrantLock()

        同样的如果是继承方式这个属性需要定义为static的

        在代码前用lock.lock()加锁,最后用lock.unlock()解锁来保证线程安全

 synchronized和lock锁的区别?

synchronized自动释放,而lock手动设置以及手动释放。

4.一些问题

什么是线程死锁?

两个线程互相占用对方所需的资源,都不释放。

sleep方法和wait方法的区别?

同:都会使线程进入阻塞态

异:1.wait方法会释放锁,sleep方法不会释放锁

        wait方法用于线程间通信,sleep用于在执行时暂停

        wait方法需要notify()或者notifyall()唤醒一个或全部线程,一个则唤醒优先级高的,优先级           相同则随机唤醒。

     2.wait(),notify(),notifyall()只能使用在同步方法或者同步代码块中,wait()只能由锁调用,并且这三个方法是Object类中的方法。

        sleep()是Thread类的静态方法

start和run方法的区别?

调用start会让线程进入就绪态,获取除CPU执行权以外的其他所需资源,并将run方法作为线程执行体

直接调用run方法和调用普通方法没区别

5.同优先级先进先出,高优先级抢占式

集合

Collection

        List

                -ArrayList

                -LinkedList

                -Vector

        Set

                -HashSet

                -TreeSet

                -LinkedHashSet

Map

        -HashMap

                -LinkedHashMap

        -HashTable

                -properties

        -TreeMap            

1.ArrayList和Vector区别?

同:都实现了List接口,都是有序可重复的,底层都用Object[]存储,这种存储方式查询速度快但是增删速度慢。

异:Vector线程安全,ArrayList线程不安全但是效率高。

        在jdk1.7中两者底层都默认创建长度为10的数组,但是ArrayList在长度超过时扩容为原来的1.5倍,Vector扩容为原来的两倍。

        在jdk1.8中,ArrayList在new的时候并没有创建长度为10的数组,只是声明了一个数组类型的变量,在调用add方法的 时候才创建长度为10的数组。类似于单例模式的饿汉和懒汉

2.LinkedList:

        底层使用双向链表存储,插入删除操作效率高,线程不安全。

3.HashMap和HashTable的区别?

        同:都继承了Map接口,以key-value的形式存储数据

        异:HashTable线程安全效率低,HashMap线程不安全效率高。

                HashTable不能存储key或者value为null,HashMap允许key和value为null

4.List和Map的区别?

        List存储单列数据,Map存储键值对。

        List是有序可重复的集合,Map的key是无序不可重复的,用set存储。

                                                  Map的value是无序可重复的,用Collection存储

                                                  key和value构成entry对象在Map中存储,也是用set无序不可重复

        

5.对于无序不可重复的理解

        1.无序并不是随机,而是根据数据的哈希值在数组中存储

        2.不可重复性,如果类未重写HashCode和equals方法,那么两个属性值相同的对象用equals判断为不相同,因为用的是Object类的equals方法,实际上是==,比较地址。

        如果重写了这两个方法,那么首先哈希值通过某种算法转换为数组下标,添加元素看该位置是否有值,没有则插入,有则比较哈希值是否相同,若不同则插入,相同则通过equals比较,相同则插入失败。

        在jdk1.7中新加入的元素指向旧元素,

        而在jdk1.8中旧元素指向新元素

 6.HashMap实现原理

jdk7里(数组+链表):

        new HashMap默认创建长度为16的Entry[]数组,添加元素根据key所在类的HashCode方法得到哈希值对应的数组位置,若此位置为空插入,若不为空比较哈希值,若哈希值相同则equals比较。默认扩容为原来的两倍。

jdk8中(数组+链表+红黑树):

        底层是Node[],首次调用put时,底层创建长度为16的数组(懒加载),当索引上元素以链表形式存在个数大于8个且当前数组长度大于64时,此索引上的所有元素改为红黑树存储(有序)

        默认的加载因子 是0.75,扩容临界值=容量*0.75,也就是超过12就扩容 为两倍。

HashSet底层也是HashMap ,实现原理是一样的。

7.ConcurrentHashMap

        jdk1.7将整个桶数组进行分段(segment),每把锁锁住一部分数据,多线程可以同时访问不同数据段的数据,提高效率。

        jdk1.8用数组加链表加红黑树存储,用synchronized和CAS来加锁,类似于优化且线程安全的HashMap

        get操作不需要加锁是因为Node的成员val是用volatile修饰的,保证数组扩容的可见性。

8.LinkedHashMapHashMap,TreeMap

LinkedHashMap是HashMap的直接子类,使用双向链表将所有的entry对象按照插入顺序连接,保证迭代顺序和插入顺序相同。

9.LinkedHashMap和TreeMap

两者都是有序的,TreeMap按照比较器Comparator排序(默认key升序)

LinkedHashMap基于链表实现有序。

10.Comparable和Comparator区别?

自然排序Comparable,实现Comparable接口,重写CompareTo方法,自定义排序规则,例如String,包装类等都实现了这个接口

定制排序Comparator,当代码未实现Comparable且不能修改代码,用这个接口的匿名子类的方式Arrays.sort(arr,new Comparator(){

                //重写Compare方法

        })

对对象数组排序用Arrays.sort()

对对象列表排序用Collections.sort()

Collections内部实际上将列表转换为数组再按数组排序。

11.如何遍历集合?

方式一:迭代器

Collection接口实现了Iterable接口,所有在Collection下的所有集合类都有iterator()方法,用于创建迭代器对象。

1.Iterable iterator= 对象.iterator()

2.while(iterator.hasNext()){

        iterator.next();

}

默认游标在第一个元素之前

方式二:foreach

底层也是用迭代器实现的,

for(元素类型   局部变量:集合对象){

}

局部变量的改变并不影响原有的集合


IO流

字节输入流InputStream

字节输出流OutputStream

字符输入流Reader

字符输出流Writer

字节流和字符流的区别?

字节流的操作不会经过缓存区(内存)直接操作文件,而字符流的操作会经过缓存区再操作文件。

什么是java序列化?

序列化指将一个Java对象写入IO流中,对象的反序列化机制则指从IO流中恢复该Java对象.

对象需要实现Serializable接口,标记为可序列化的对象。同时加上一个全局常量,若不加,java会自动根据内容生成一个UID,但是当内容改变很可能UID也会改变,所有需要自己显示的声明,java虚拟机会将字节流传来的UID和本地UID比较,相同才能反序列化。

声明为static和transient的属性不能序列化。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值