Java基本知识点回顾二

2022/4/6 1:19:21

本文主要是介绍Java基本知识点回顾二,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

1、字节流和字符流的区别

  字节流读取的时候,读到一个字节就返回一个字节;字符流使用了字节流读到一个或多个字节(中文对应的字节数是两个,在utf-8码表中是三个字节)时,先去查指定的编码表,将查到的字符返回。字节流可以处理所有类型数据,如:图片、MP3,AVI视频文件,而字符流只能处理字符数据,只要是处理纯文本数据,就要优先考虑使用字符流,除此之外都用字节流,字节流主要是操作byte类型数据,以byte数组为准,主要操作类就是OutputStream、InputStream。

  字符流处理的单元为2个字节的Unicode字符,分别操作字符、字符数组或字符串,而字节流处理单元为1个字节,操作字节和字节数组,所以字符流是由Java虚拟机将字节转化为2个字节的Unicode字符为单元的字符而成的,所以它对多国语言支持性比较好。


2、System.arrayCopy是浅拷贝还是深拷贝

  结论是浅拷贝,引用类型是复制其引用。建议可以自己写一个方法验证(可参考:https://blog.csdn.net/qq_35152037/article/details/84235894)


 3、Comparator与Comparable的区别

  Comparable接口实际上是出自java.lang包,它有一个compareTo(Object obj)方法用来排序。

  Comparator接口实际上是出自java.util包,它有一个compare(Object obj1,Object obj2)方法用来排序。

  ⼀般我们需要对⼀个集合使⽤⾃定义排序时,我们就要重写 compareTo() ⽅法或 compare() ⽅法,当我们需要对某⼀个集合实现两种排序⽅式,⽐如⼀个song对象中的歌名和歌⼿名分别采⽤⼀种排序⽅法的话,我们可以重写 compareTo() ⽅法和使⽤⾃制的Comparator⽅法或者以两个Comparator来实现歌名排序和歌星名排序,第⼆种代表我们只能使⽤两个参数版的 Collections.sort()。

  (以上内容来自JavaGuide面试突击版文档)

  个人总结:两个比较明显的区别是如果我们要比较的实体类是来自第三方包,然后我们又不能去改动对应的实体类,也就是说我们不能给这个类添加Comparable接口的话,那么在排序的时候我们就得去写一个比较器,也就是Comparator。


 4、进程与线程定义

  进程:进程是程序的一次执行过程,是系统运行程序的基本单位,因此进程是动态的,系统运行一个程序即是一个进程从创建,运行到消亡的过程。在Java中,当我们启动main函数时其实就是启动一个JVM的进程,而main函数所在的线程就是这个进程中的一个线程,也称主线程。

  线程:线程与进程相似,但线程是一个比进程更小的执行单位,一个进程在其执行的过程中可以以产生多个线程,与进程不同的是同类的多个线程共享进程的堆和方法区资源,但每个线程有自己的程序计数器、虚拟机栈和本地方法栈,所以系统在产生一个线程,或是在各个线程之间作切换工作时,负担要比进程小得多,也正因为如此,线程也被称为轻量级进程。


 5、产生死锁的条件

  1)互斥条件:该资源任意一个时刻只由一个线程占用

  2)请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放

  3)不剥夺条件:线程已获得的资源在未使用完之前不能被其他线程强行剥夺,只有自己使用完毕后才释放资源

  4)循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。


 6、如何避免线程死锁

  如何避免死锁的话,我们可以从死锁的四个产生条件去入手

  1)破坏互斥条件:这个条件没法破坏,因为我们用锁本来就是想让他们互斥(临界资源需要互斥访问)

  2)破坏请求与保持条件:一次性申请所有的资源

  3)破坏不剥夺条件:占用部分资源的线程进一步申请其他资源时,如果申请不到,可以主动释放它占用的资源。

  4)破坏循环等待条件:靠按序申请资源来预防,按某一顺序申请资源,释放资源则反序释放,破坏循环等待条件。


 7、单例模式的双重校验锁实现

public class Singleton {
    private volatile static Singleton instance;
    
    private Singleton(){

    }

    public static Singleton getInstance(){
        if(instance == null){
            synchronized(Singleton.class){
                if(instance == null){
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
    
}

  这里为什么要加volatile关键字呢?因为new Singleton()这段代码其实是分三步执行

  1)为instance分配内存空间

  2)初始化instance

  3)将instance指向分配的内存地址

  但由于JVM具有指令重拍的特性,所以顺序有可能变成1—》3——》2,指令重排在单线程环境下不会出现问题,但是在多线程环境下会导致一个线程获得还没有初始化的实例,例如,线程T1执行了1和3,此时T2调用getInstance()后发现instance不为空,因此返回instance,但此时instance还未被初始化。

  还有一个问题就是这里为什么不在静态方法加synchronized关键字呢?理由是因为加在静态方法的话,锁粒度太大,在高并发的情况下,例如有100个线程,有一个线程获取了这个锁话,后面99个都得等待前面那个线程释放锁;但如果我们只把synchronized加在方法里面的话,多线程的情况下,其实只有一开始初始化的时候,多个线程需要等待,而在初始化成功后,多线程获取单例则可以直接获取到,因为instance不为空了,所以不会走到synchronized的代码行那里了。


 8、synchronized关键字和volatile关键字的区别

  1)volatile关键字是线程同步的轻量级实现,所以volatile性能肯定比synchronized关键字要好,但是volatile关键字只能用于变量而synchronized关键字可以修饰方法和代码块

  2)多线程访问volatile关键字不会发生阻塞,而synchronized关键字可能会发生阻塞。

  3)volatile关键字能保证数据的可见性,但不能保证数据的原子性,synchronized关键字两者都能保证。

  4)volatile关键字主要用于解决变量在多个线程之间的可见性,而synchronized关键字解决的是多个线程之间访问资源的同步性


 9、ThreadLocal是什么

   通常情况下,我们创建的变量是可以被任何一个线程访问并修改的,如果想实现每一个线程都有自己的专属本地变量该如何解决呢?JDK中提供的ThreadLocal类正是为了解决这样的问题。

  ThreadLocal类主要解决的就是让每个线程绑定自己的值,可以将ThreadLocal类形象的比喻成存放数据的盒子,盒子中可以存储每个线程的私有数据。

  如果你创建了一个ThreadLocal变量,那么访问这个变量的每个线程都会有这个变量的本地副本,这也是ThreadLocal变量名的由来,他们可以使用get()和set()方法来获取默认值或将其值更改为当前线程所存的副本的值,从而避免了线程安全问题。



这篇关于Java基本知识点回顾二的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程