• 36个常见java面试题


    谈谈你对JAVA平台的理解

    java是一个面向对象编程语言,有两个显著特点,第一:“一次书写,到处运行”,二是JAVA垃圾回收机器,也就是GC。这使得JAVA不用像C++那样时刻考虑着垃圾回收和内存泄露,通过GC,可以将不可达的对象及时回收。说Java是“解释执行”的语言其实也不太准确,虽然我们写的.class文件会被javac编译成字节码,然后再通过JVM编译成机器码。同时,大多JVM其实都有动态编译器,比如HotSpot的JIT(JUST-IN-TIME),将热点代码编译成机器码。这部分就是动态编译了。属于编译执行,所以说java是个“解释执行”并不准确。

    Error和Exception的区别

    首先,两个都是“thowable”的子类,在Java中只有Throwable类型的实例才可以被抛出(throw)或者捕获(catch),然后Error一般指JVM运行时出现的错误,它出乎了程序执行的预计范围,所以是不可捕捉的,比如OutOfMemory。而Exception指的是由代码逻辑的引发的异常,是可以捕捉的,变量类型异常等。
    异常又分为可检查和不可检查,可检查是可指可以在代码中显式捕捉的异常,不可检查指的是代码逻辑上的异常,一般在编译的时候爆出,如空指针和索引超界。
    在编码过程中,除非十分有信心,不然不推荐直接捕捉exception通用异常,而是应该捕捉他的子类。

    final、 finally、 finalize有什么不同?

    首先,这三者除了名字看起来像之外,没有什么共同点。
    final 修饰符可以让一个变量不可变,让一个方法不可重写,让一个类不可继承。
    finally是try-catch异常捕捉中的一个语法,指的是无论捕捉到什么异常,都要执行finally中的代码,通常是关闭JDBC和unlock锁但也有些方法可以让代码逻辑跳过finally。
    finalize是垃圾回收的一个方法,现在已经很少人用了,JDK9中已经标记成deprecated

    强引用、软引用、弱引用、幻象引用有什么区别?

    定义这四个引用程度是为了更合理的进行垃圾回收。
    强引用是指声明对象时的引用,只要还有一个强引用指向该对象,那么它就不会垃圾回收器回收。
    软引用,在内存充足时,不会被回收,一旦JVM认为内存不足就会,它就会被回收,JVM通过一个公式判断内存是否充足,一般而言倾向于判断其内存不足。
    弱引用,只要有垃圾回收动作,就会被回收。弱应用同样可用于内存敏感的缓存
    虚引用:样,在任何时候都可能被垃圾回收器回收。虚引用必须和引用队列 (ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如
    果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。所以他可以用来跟踪对象是否别垃圾回收器回收。

    String、 StringBufer、 StringBuilder有什么区别?

    String是一个final class,他的所有成员也都是final,对String进行增删改操作,都会产生其他新的对象,又由于字符串类型经常使用,所以就有了StringBuffer,他的实质上是一个可修改的字符序列,它可以避免由于拼接等操作而导致产生过多中间对象,其次它还是线程安全,所以他在性能上有些不理想,因此就有了StringBuilder,他除了不是线程安全外,其他功能和StringBuffer一样的。
    出于安全考虑,String被设计为不可变,是为了不想再引用字符串时,被其他引用对象改变了值,比如路径,文件名,这种字符串变量,一旦改变了就会造成很大的问题,所以要保证String类型的不可变性和某种程度上的可变性,同时,由于String类型使用十分频繁,jdk也对其作出了优化,最开始的时候String里是char[]数组,但是现在的话是btye数组,因为如果用一个char去存字母的话,有点浪费,所以改成用两个BTYE去存,这样能节省一半的逻辑空间,但是理论上的String最大容量少了一半。还有一种优化方式就是字符串常量池,他在编译阶段,会把想用的字符串指向常量池里的同一个对象。

    反射机制与动态代理是什么原理

    虽然java是解释执行语言,但是java中的反射机制和动态代理使得java可以动态构造对象,获取对象的方法声明的属性等信息。
    实现动态代理的方式有很多,比如内置的JDK,还有ASM 与机遇ASM的cglib等。
    JDK就是通过反射机制来实现动态代理的,而ASM和cglib是通过更高性能的字节码操作实现动态代理。动态代理的常用场景有RPC调用,和AOP。
    动态代理相比于静态代理的优势是 不用像静态代理一样把代理类写死,一个类就要写一个代理类,它能统一的处理。
    通过动态代理可以更高效的实现许多代理模式,比如对于一个资源消耗很大实际调用类,我们可以创建一个比较小的代理类来代理他,只有在调用它的时候才会被实例化。

    int和Integer

    int是java中八个原始数据类型,而Integer是int的包装类,是一个对象,他有一个int类型的储存字段,并且提供了相关操作。引入包装类是因为原始数据类型不支持泛型,比如集合初始化的时候需要传对象参数。同时由于Integer比int大,java也做了相关优化,在调用valueOf方法时,会缓存-128到127之内的所有整数对象,把值相同的引用指向同一个对象。就像字符串常量池那样。因为在实际中,我们所使用的数字一般都不会很大。出了缓存Integer类型之外,java对其他原始数据类型的包装类也做了相关缓存。

    对比Vector、 ArrayList、 LinkedList有何区别?

    Vector相比其他两个,是线性安全的,所以性能开销也比其他两个。
    ArrayList和Vector都是基于对象数组的动态数组实现方式,在容量不足时都会自动扩容,Vetcot是扩容100%,而ArrayList扩容50%。
    LinkedList是双向链表,也是线程不安全的。
    LinkedList插入删除元素比动态数组方便很多,而且不用担心因扩容而导致的OOM问题。(没足够的连续地址空间)

    对比Hashtable、 HashMap、 TreeMap有什么不同?

    HashTable是线程安全的,但是他的实现方式都是暴力加锁,性能额外开销很大,而且不支持null键值对
    TreeMap是基于红黑树实现的顺序访问Map,取值和插入的复杂度都是Logn。
    HashMap是大多数场景下的首选数据结构,是基于数组和链表实现的Map,存取操作的时间复杂度都是LogN,但是不是线程安全的。所以在并发场合下会导致很多问题,
    比如死循环,在重哈希的时候,重新分配桶,多线程进行操作,会导致几个链表节点的指针形成一个圆形。(头插法导致?)
    如果想要使用线程安全的HashMap应该使用ConcurrentHashMap,他的实现更为高效合理。
    几个版本以来,Java对HashMap做了很大的优化,比如引入了红黑树,在链表长度大于8时,会进行树化,红黑树节点数小于6时重新链表化,但是根据源码里开发人员的注释,同一个位置的元素超过8个的概率只有百万分值6。

    为什么hashMap的容量要是2的次方

    HashMap源码中有个函数叫做tableSizeFor,他会返回一个最小大于传入参数的2次方数。具体实现是通过-1>>>n-1。
    所以每次扩容都通过这个函数进行计算容量。
    同时,2的次方可以使得HashMap的存入键值对的时候更为均匀分散,极大程度上的避免了冲突,因为在通过元素在HashMap底层数组的位置的时候,是通过当前容量-1与一个hash值与操作得出来的,如果当前容量不是2的次方的话,减去1得到的2进制就会有0的存在,而0无论与1还是0相与都是0,这样就会有更大的机会让两个值落在同一个桶内。与操作中的hash值,是hashcode函数的值的前16位数。

    如何保证容器是线程安全的? ConcurrentHashMap如何实现高效地线程安全

    利用并发包提供的线程安全容器类,它提供了:
    各种并发容器,比如ConcurrentHashMap、 CopyOnWriteArrayList。
    concurrentHashMap早期是使用分离锁和HashEntry实现的。
    分离锁,就是把底层数组成几个segment,里面这是HashEntry,也是链表形式存放的。
    HashEntry使用了volatile来保证可见性。

    相比于同为线性安全的hashtable,他的优势在于,使用了分离锁,分段锁而不是一下子锁住了整个表,所以他的性能自然比HashTable好。
    java8之后的concurrentHashMap在结构上去掉了分离锁,使用和HashMap一样的结构
    同时引入了CAS和synchronized 等操作,来进行无锁并发操作。

    synchronized和ReentrantLock有什么区别?有人说synchronized最慢,这话靠谱吗?

    reentrantLock比synchronized灵活,但是使用的时候要进行加锁解锁操作,
    早期的synchronized锁确实慢与ReentrantLock,但是经过优化,现在的sychronize在低竞争的场景性能会优于可重入锁。

    synchronized底层如何实现?什么是锁的升级、降级?

    底层是Monitor,而monitor有三种实现方式,分别是偏斜锁,轻量级锁,重量级锁。
    所谓的升级降级,就是JVM在不同的场景对锁的方式进行更换。
    在几乎无竞争的情况下,使用偏斜锁,轻度竞争使用轻量级锁,重度竞争使用重量级锁。

    一个线程两次调用start()方法会出现什么情况?

    一个线程不允许调用两次,一个线程只能有一个状态。
    新建,就绪,阻塞,等待,计时,等待,终止

    什么情况下Java程序会产生死锁?如何定位、修复?

    互相获取锁的时候

    public class DeadLockSample extends Thread {
        private String frs;
        private String second;
        public DeadLockSample(String name, String frs, String second) {
            super(name);
            this.frs = frs;
            this.second = second;
        }
        public void run() {
            synchronized (frs) {
                Sysem.out.println(this.getName() + " obtained: " + frs);
                try {
                    Thread.sleep(1000L);
                    synchronized (second) {
                        Sysem.out.println(this.getName() + " obtained: " + second);
                    }
                } catch (InterruptedException e) {
                        // Do nothing
                }
            }
        }
    public satic void main(String[] args) throws InterruptedException {
        String lockA = "lockA";
        String lockB = "lockB";
        DeadLockSample t1 = new DeadLockSample("Thread1", lockA, lockB);
        DeadLockSample t2 = new DeadLockSample("Thread2", lockB, lockA);
        t1.sart();
        t2.sart();
        t1.join();
        t2.join();
        }
    }
    

    并发包中的ConcurrentLinkedQueue和LinkedBlockingQueue有什么区别?

    concurrentLinkedList是无界非阻塞的,LinkedBlockingQueue是有界阻塞的。
    concurrentLinkedList是基于CAS的,所以是弱一致性,就算有线程在他进行修改,他也是可以继续遍历的。

    Java并发类库提供的线程池有哪几种? 分别有什么特点?

    Executors目前提供了5种不同的线程池创建配置:主要区别在于不同的ExecutorService类型或者不同的初始参数。
    1.newCachedThreadPool(),它是一种用来处理大量短时间工作任务的线程池,具有几个鲜明特点:它会试图缓存线程并重用,当无缓存线程可用时,就会创建新的工作线程;如
    果线程闲置的时间超过60秒,则被终止并移出缓存;长时间闲置时,这种线程池,不会消耗什么资源。其内部使用SynchronousQueue作为工作队列。
    2. newFixedThreadPool(int nThreads),重用指定数目(nThreads)的线程,其背后使用的是无界的工作队列,任何时候最多有nThreads个工作线程是活动的。这意味着,如
    果任务数量超过了活动队列数目,将在工作队列中等待空闲线程出现;如果有工作线程退出,将会有新的工作线程被创建,以补足指定的数目nThreads。
    3、newSingleThreadExecutor(),它的特点在于工作线程数目被限制为1,操作一个无界的工作队列,所以它保证了所有任务的都是被顺序执行,最多会有一个任务处于活动状
    态,并且不允许使用者改动线程池实例,因此可以避免其改变线程数目。
    4.newSingleThreadScheduledExecutor()和newScheduledThreadPool(int corePoolSize),创建的是个ScheduledExecutorService,可以进行定时或周期性的工作调度,
    区别在于单一工作线程还是多个工作线程。
    5.newWorkStealingPool(int parallelism),这是一个经常被人忽略的线程池, Java 8才加入这个创建方法,其内部会构建ForkJoinPool,利用Work-Stealing算法,并行地处
    理任务,不保证处理顺序。

    AtomicInteger底层实现原理是什么?如何在自己的产品代码中应用CAS操作?

    是int的一个封装类型,使用了CAS,提供了原子性的访问和更新操作;添加一个version

    Java提供了哪些IO方式? NIO如何实现多路复用?

    java.io nio nio2

    nio有buffer channel selector

    Java有几种文件拷贝方式?哪一种最高效?

    利用java.io类库,直接为源文件构建一个FileInputStream读取,然后再为目标文件构建一个FileOutputStream,
    或者,利用java.nio类库提供的transferTo或transferFrom方法实现

    Java常见的垃圾收集器有哪些?垃圾回收原理

    Serial GC,ParNew GC,CMS,G1 GC

    • 可达性
    • 计数法

    谈谈接口和抽象类有什么区别?

    抽象类是不能实例化的类,用abstractclass修饰符,而接口就是纯抽象的类,方法都留给实现类去实现,除了default和static之外的方法不能有具体实现。
    都是为了代码复用。

    谈谈JVM内存区域的划分,哪些区域可能发生OutOfMemoryError?

    程序计数器,java虚拟机栈,堆,方法区,, 运行时常量池, 本地方法栈。

    谈谈MySQL支持的事务隔离级别,以及悲观锁和乐观锁的原理和应用场景?

    读未提交
    读已提交
    可重复读
    串行化

    happen-before

    Happen-before关系,是Java内存模型中保证多线程操作可见性的机制,也是对早期语言规范中含糊的可见性概念的一个精确定义。
    它的具体表现形式,包括但远不止是我们直觉中的synchronized、 volatile、 lock操作顺序等方面,例如:
    线程内执行的每个操作,都保证happen-before后面的操作,这就保证了基本的程序顺序规则,这是开发者在书写程序时的基本约定。
    对于volatile变量,对它的写操作,保证happen-before在随后对该变量的读取操作。
    对于一个锁的解锁操作,保证happen-before加锁操作。
    对象构建完成,保证happen-before于fnalizer的开始动作。
    甚至是类似线程内部操作的完成,保证happen-before其他Thread.join()的线程等。
    这些happen-before关系是存在着传递性的,如果满足a happen-before b和b happen-before c,那么a happen-before c也成立。
    前面我一直用happen-before,而不是简单说前后,是因为它不仅仅是对执行时间的保证,也包括对内存读、写操作顺序的保证。仅仅是时钟顺序上的先后,并不能保证线程交互的
    可见性。

    个人小站:http://jun10ng.work/ 拥抱变化,时刻斗争,走出舒适圈。
  • 相关阅读:
    LightningChartJS2.0即将火热推出,敬请期待!
    html转word
    Windows上使用Python2.7安装pip
    人工智能?.netcore一样胜任!
    远程浏览服务器上的文件
    C# winform间窗体传值简单Demo
    C#发送QQ邮箱
    各种文件用JS转Base64之后的data类型
    当你的VS2019没法自动补全了,或者自动补全按回车直接换行的时候
    easyUI filebox 获取文件对象
  • 原文地址:https://www.cnblogs.com/Jun10ng/p/12402357.html
Copyright © 2020-2023  润新知