极客时间Java学习笔记

&&第一课 Java平台

Java是解释执行吗?
--不准确,Javac编译成字节码属于解释执行,但Hotspot的JVM提供了JIT编译器,能在运行时将热点代码编译成机器码,这情况下热点代码属于编译执行.

###
对于笼统的问题,要尽量表现出自己的思维深入并系统化,要避免让面试官觉得你是个"知其然不知所以然"的人.
开放性问题,考察的是多方面的,很多面试者都会在这种问题上吃亏,不知从何说起,就给了简略的回答.
###

Java平台的特性
--语言特性(面向对象,范型,Lambda)
--基础类库(集合,IO/NIO,网络,并发)
--JVM概念和机制
    --类的加载机制(Bootstrap,Application和Extension ClassLoader)
    --类的记载过程(加载,验证,链接,初始化)
    --自定义ClassLoader
    --垃圾回收原理
    --常见垃圾收集器(SerialGC, ParallelGC, CMS, G1)
    --JDK中的工具(编译器,运行时环境,安全工具,诊断和监控工具)
--Java运行机制
    --解析执行:.class文件经过JVM内嵌的解析器解析执行
    --编译执行:JIT,把经常运行的代码作为热点代码,编译成本地平台的机器码
    --AOT编译:直接将所有代码编译成机器码
    
&&第二课  Exception和Error有什么区别?

--都继承自Throwable

--是Java对不同异常的分类,Exception是可预料的意外情况,且应该被捕获并进行处理;Error会导致程序处于非正常状态,不可以恢复.

--Exception分为checked和unchecked,checked必须显式地进行捕获,这也是编译期检查的一部分;unchecked就是运行时异常,如NullPointerException,通常需要根据具体情况判断是否需要捕获.

--Throw early, catch late原则
    --第一时间抛出异常
    --捕获到异常后,可以选择保留原有cause信息,直接再跑出去或构建新的异常抛出;在更高层面,因为有了清晰的业务逻辑,往往更清楚合适的处理方式.

--自定义异常两点考虑
    --是否需要定义成checked exception,这种类型设计的初衷是为了从异常恢复
    --在保证诊断信息足够的同时,也要考虑避免包含敏感信息,比如机器名,IP,端口等,类似的情况在日志中也有,如用户数据一般不输出到日志中.
    
--异常结构图
    --Throwable
        --Error
            --LinkageError
                --NoClassDefFoundError
                --UnsatisfiedLinkError
                --ExceptionInInitializerError
            --VirtualMachineError
                --OutOfMemoryError
                --StackOverflowError
        --Exception
            --IOException(checked)                
            --RuntimeException(unchacked)
                --NullPonterException
                --ClassCastException
                --SecurityException

--异常处理带来的性能影响
    --try-catch会产生额外性能开销,所以最好仅捕获有必要的代码段;利用异常控制代码流程远比通常意义上的条件语句要低效
    --Java每实例化一个Exception,都会对当前的栈进行快照,这是比较重的操作.


&&第三课    final、finally、 finalize有什么不同?

--final可用来修饰类,方法,变量
--finally保证重点代码一定被执行的一种机制,可以用try-finally或者try-catch-finally
--finalize是Object的方法,设计的目的是保证对象在被垃圾收集前完成特定资源的回收,JDK9已经标记为deprecated

--Java7 try-with-resources

##
    try {
        System.exit(1);
    } finally {
        System.out.println("Print from finally");
    }
##

--final的语义,逻辑意图

--final不等于immutable

##
    final List<String> strList = new ArrayList<>(); //只是表明strList这个引用不可以被赋值
    strList.add("Hello");
    strList.add("world");

    List<String> unmodifiableStrList = List.of("Hello", "world");
    unmodifiableStrList.add("error");
##

--如何实现immutable类
    --将class声明为final
    --将所有成员变量定义为private final,而且不要实现setter
    --构造对象时,成员变量使用深度拷贝来初始化,而不是直接赋值
    --如果确实需要实现getter,或者其他可能会返回内部状态的方法,使用copy-on-write原则,创建私有的copy

--finalize的问题
    --掩盖了资源回收时的出错信息
    --Throwable被生吞了
    
##
    private void runFinalizer(JavaLangAccess jla) {
        ...
        try {
            Object finalizee = this.get();
            if(finalizee != null && !(finalizee instanceof java.lang.Enum)) {
                jla.invokeFinalize(finalizee);
                finalizee = null;
            }
        } catch (Throwable x) {}
        super.clear();
    }
##

--替换finalize的机制
    --JDK Cleaner
    --幻象引用PhantomReference


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

--主要体现在对象不同的可达性状态和对垃圾收集器的影响

--'Strong' Reference
    --只要有强引用指向一个对象,就表明对象还活着,GC就不会回收这种对象
--SoftReference
    --可以让对象豁免一些垃圾收集
    --只有当JVM认为内存不足时,才会试图回收软引用指向的对象;JVM会确保在抛出OOM之前,清理软引用指向的对象.
    --通常用来实现内存敏感的缓存,如果还有空闲内存,就可以暂时保留缓存,否者,就清楚一部分缓存.
--WeakReference
    --并不能使对象豁免垃圾收集
    --仅仅提供一种在弱引用状态下访问对象的途径,这就可以用来构建一种没有特定约束的关系
    --如果试图获取时对象还在,就使用它,否则重新实例化.
    --也是很多缓存实现的选择
--幻象引用
    --不能通过它访问对象
    --仅仅提供了一种确保对象被finalize以后,做某些事情的机制,比如post-mortem清理机制
    --也可以用来监控对象的创建和销毁
    
--对象的可达性状态

--引用队列ReferenceQueue
    
--诊断JVM引用情况


第六课 动态代理是基于什么原理?

--实现动态代理的方式有很多:JDK自身提供的动态代理,利用反射机制;ASM, cglib, Javassist,基于字节码操作.

--AccessibleObject.setAccessible(boolean flag)
    
--绕过API访问控制

--动态代理
    
--JDK Proxy
    --最小化依赖关系
    --平滑进行JDK升级
    --代码实现简单
--cglib框架

--应用场景
    --RPC
    --AOP
        --弥补了OOP对于跨越不同对象或类的分散,纠缠,逻辑表现不足等问题,比如在不同模块的特定阶段做一些事情,类似日志,用户鉴权,全局性异常处理,性能监控,事务处理等
        
--好处
    --通过代理可以让调用者与实现者之间解藕.比如进行RPC调用,框架内部的寻址,序列化,反序列化等,对于调用者往往没有太大意义,通过代理,可以提供更加友善的界面.
    

第八课 对比Vector, ArrayList, LinkedList有什么区别?

--都是有序集合
--都提供迭代器以遍历内容
--Vecor,线程安全的动态数组,扩容时增加1倍
--ArrayList,非线程安全,扩容时增加50%
--LinkedList,双向链表,非线程安全

--排序算法
    --内部排序 or 外部排序
    --稳定 or 非稳定
    
三大类集合    
--List
    --有序集合,方便访问,插入,删除
--Set
    --不允许重复,保证了元素唯一性
    --无序
--Queue/Deque
    --支持FIFO,或LIFO
    --BlockingQueue,属于并发包

--TreeSet是以TreeMap实现的
--HashSet是以HashMap实现的

--适合场景
    --TreeSet支持自然顺序访问,但是添加,删除,包含等操作需要log(n)
    --HashSet,如果hash正常,可以提供常数时间的添加,删除,包含操作,但不保证有序
    --LinkedHashSet,内部构建了一个记录插入顺序的双向链表,因此提供了按照插入顺序遍历的能力,同时也保证了常数时间的添加,删除,包含等操作

--这些集合类都不是线程安全的,对于concurrent里面的线程安全容器,Collections工具提供了一系列的synchronized方法
    --原理:简单粗暴.将每个操作方法通过synchronized添加同步支持
    --List list = Collections.synchronezedList(new ArrayList());

--对于小数据集的创建,Java9提供了一系列静态工厂方法

第9讲 | 对比Hashtable、HashMap、TreeMap有什么不同?

--都是Map实现,key-value存储
--Hashtable,是同步的,不支持null
--HashMap,不同步,支持null
--TreeMap,基于红黑树,顺序访问,get/put/remove都是O(log(n)),具体的顺序由Comparator决定

--HashMap 的性能表现非常依赖于哈希码的有效性,请务必掌握hashCode和equals的约定
    --equals相等,hashCode一定要相等
    --重写了hashCode,也要重写equals
    --hashCode需要保持一致性,状态改变返回的哈希值要一致

--LinkedHashMap和TreeMap的区别?
    --LinkedHashMap提供的是遍历顺序符合插入顺序,通过为条目(key-value对)维护双向链表
    --TreeMap的整体顺序是由key的顺序关系决定的,通过Comparator或Comparable来决定
    
--HashMap源码
    --容量
    --负载系数
    --树化
    
    --内部结构
        --Node<K,V>[] table,通过hashCode决定key-value对在数组的寻址
        --链表,hashCode相同的key-value对,以链表形式存储
        
    --resize
        --创建初始数组
        --在容量不满足需求时,进行扩容
    
    --key-value对在哈希表中的位置(index)
        --i=(n-1)&hash
        --并不是key本身的hashCode,而是来自于HashMap中的一个hash方法
        --将高位数据移位到低位进行异或运算,因为有些数据计算出的hashCode差异在高位,而HashMap里的哈希寻址是忽略容量以上的高位的,简而言之,就是避免哈希碰撞.
        static final int hash(Object kye) {
            int h;
            return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>>16;
        }

    --扩容性能开销的来源:扩容后需要将老的数组中的元素放置到新的数组中
    
    --负载因子*容量 > 元素数量
    --预先设置的容量需要满足,1.预估数量/负载因子, 2.是2的幂
    --最好不超过3/4,过低会频繁扩容,过高会增加冲突
    
    --树化
    
    

第10讲 | 如何保证集合是线程安全的? ConcurrentHashMap如何实现高效地线程安全?

--Java提供了不同层面的线程安全支持
    --早期的有Hashtable
    --同步包装器
    --缺点,粗粒度;性能低
    
    --并发包提供了线程安全容器
        --各种并发容器
            --ConcurrentHashMap, CopyOnWriteArrayList
        --各种线程安全队列
            --ArrayBlockingQueue, SynchronousQueue
        --各种有序容器的线程安全版本
        
        
        
第11讲 | Java提供了哪些IO方式? NIO如何实现多路复用?

--synchronous/asynchronous
    --同步:后续任务等待当前调用返回,才会进行下一步
    --异步:其他任务不需要等待当前调用返回,通常依靠事件,回调等机制来实现任务间次序关系
    
--blocking/non-blocking
    --阻塞:在进行阻塞操作时,当前线程会处于阻塞状态,无法从事其他任务,只有当条件就绪才能继续
    --非阻塞:不管IO操作是否结束,直接返回,相应操作在后台继续处理
        
--InputStream/OutputStream
--Reader/Writer
--BufferedOutputStream

--Closable
    --File
    --RandomAccessFile
    --InputStream
        --FilterInputStream
            --BufferedInputStream
        --ByteArrayInputStream
        --ObjectInputStream
        --PipedInputStream
    --OutputStream
        --FilterOutputStream
            --BufferedOutputStream
        --ByteArrayOutputStream
        --ObjectOutputStream
        --PipedOutputStream
    --Reader
        --InputStreamReader
            --FileReader
        --BufferedReader
        --PipedReader
    --Writer
        --OutputStreamWriter
            --FileWriter
        --BufferedWriter
        --PipedWriter

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

--拷贝实现机制
    -IO
        --输入输出流进行读写,实际上进行了多次上下文切换
        --应用读取数据:先在内核态将数据从磁盘读取到内核缓存,再切换到用户态将数据从内核缓存读取到用户缓存
    -NIO
        --零拷贝
        --数据传输不需要用户态参与,省去了上下文切换和内存拷贝的开销
        

第15讲 | synchronized和ReentrantLock有什么区别呢?

--synchronized是Java内建的同步机制,提供了互斥的语义和可见性
--ReentrantLock,能实现synchronized无法做到的细节控制,比如faireness

--线程安全
    --多线程环境下正确性,保证多线程环境下共享的,可修改的状态的正确性
    
    --两个方法
        --封装,将对象内部状态隐藏保护起来
        --不可变,final和immutable
    
    --几个基本特征
        --原子性
            --相关操作不会中途被其他线程干扰,一般通过同步机制实现        
        --可见性
            --是一个线程修改了某个共享变量,其状态能够立即被其他线程知晓
            --通常被解释为将线程本地状态反映到内存上,volatile就是负责保证可见性的
        --有序性
            --保证线程内串行语义,避免指令重排
            
            

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

--由一对monitorenter/monitorexit指令实现的,Monitor对象是同步的基本实现单元
--三种不同的Monitor
    --偏斜锁 Biased Locking
    --轻量级锁
    --重量级锁
    
--当JVM检测到不同的竞争状况时,会自动切换到适合的锁的切换,这种切换就是锁的升级,降级

--synchronized是JVM内部的Intrinsic Lock,所以偏斜锁,轻量级锁,重量级锁的代码实现并不在核心类库,而是在JVM代码中.

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


    


 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值