美文网首页
面试篇:JAVA基础

面试篇:JAVA基础

作者: 文大奇Quiin | 来源:发表于2020-05-17 11:44 被阅读0次

    1. Java基础数据类型

    基础数据类型

    2. Java容器

    2.1 Iterable 接口

    Iterable是一个超级接口,被Collection所继承。它只有一个方法: Iterator<T> iterator() //即返回一个迭代器

    Iterator是Java迭代器最简单的实现,为List设计的ListIterator具有更多的功能,它可以从两个方向遍历List,也可以从List中插入和删除元素。

    2.2 Collection接口

    JDK 不提供此接口的任何直接实现:它提供更具体的子接口(Set 和 List)实现。

    2.2.1 List接口

    List是有序的Collection。此接口的用户可以对列表中每个元素的插入位置进行精确地控制。用户可以根据元素的整数索引访问元素,并搜索列表中的元素。


    ArrayList是基于数组实现的List类,它封装了一个动态的、增长的、允许再分配的Object[]。它允许对元素进行快速随机访问。当从ArrayList的中间位置插入或者删除元素时,需要对数组进行复制、移动、代价比较高。因此,它适合随机查找和遍历,不适合插入和删除。


    Vector与ArrayList一样,也是通过数组实现的,不同的是它支持线程的同步,但实现同步需要很高的花费,因此,访问它比访问ArrayList慢。所以现在已经不太常用了


    LinkedList是用链表结构存储数据的,很适合数据的动态插入和删除,随机访问和遍历速度比较慢。另外,它还实现了Deque接口,专门用于操作表头和表尾元素,可以当作堆栈、队列和双向队列使用。

    2.2.2 Set接口

    Set是无序、不可重复的。同时,如果有多个null,则不满足单一性了,所以Set只能有一个null。


    HashSet使用HASH算法来存储集合中的元素,因此具有良好的存取和查找性能。当向HashSet集合中存入一个元素时,HashSet会调用该对象的 hashCode()方法来得到该对象的hashCode值,然后根据该HashCode值决定该对象在HashSet中的存储位置。HashSet集合判断两个元素相等的标准是两个对象通过equals()方法比较相等,并且两个对象的hashCode()方法的返回值相等。


    LinkedHashSet也是根据元素的hashCode值来决定元素的存储位置,但和HashSet不同的是,它同时使用链表维护元素的次序。LinkedHashSet需要维护元素的插入顺序,因此性能略低于HashSet的性能,但在迭代访问Set里的全部元素时(遍历)将有很好的性能(链表很适合进行遍历)LinkedHashSet需要维护元素的插入顺序,因此性能略低于HashSet的性能,但在迭代访问Set里的全部元素时(遍历)将有很好的性能(链表很适合进行遍历)。


    TreeSet是SortedSet接口的实现类,TreeSet可以确保集合元素处于排序状态。

    2.3 Map接口

    Map不是Collection的子接口,Map用于保存具有“映射关系”的数据。其中,Value可能重复,但是Key不允许重复。可以有多个Value为null,但是只能有一个Key为null。


    HashMap不能保证key-value对的顺序。并且类似于HashSet判断两个key是否相等的标准一样。


    LinkedHashMap使用双向链表来维护key-value对的次序,该链表负责维护Map的迭代顺序。


    TreeMap是一个红黑树结构,每个键值对都作为红黑树的一个节点。TreeMap存储键值对时,需要根据key对节点进行排序,TreeMap可以保证所有的key-value对处于有序状态。TreeMap也有两种排序方式:自然排序、定制排序。


    WeakHashMap的key只保留了对实际对象的弱引用,这意味着如果WeakHashMap对象的key所引用的对象没有被其他强引用变量所引用,则这些key所引用的对象可能被垃圾回收,当垃圾回收了该key所对应的实际对象之后,WeakHashMap也可能自动删除这些key所对应的key-value对。

    3. 面向对象的特征

    继承:继承是从已有的类中得到继承信息创建新类的过程。

    封装:封装是把数据和操作数据的方法绑定起来,只能通过已定义的接口去访问。

    多态:多态是指允许不同子类型的对象对同一消息做出不同的响应。

    4. 阐明final、finally、finalize的区别

    final:如果一个类被声明为final,则这个类不能被继承;将变量声明为final,在声明时必须赋初始值,且在以后的引用中不可修改;将方法声明为final,则不能被重写。

    finally:通常放在try。。。catch。。。的后面,通常放一些释放外部资源的代码。

    finalize:Object中定义的方法,这个方法是在由垃圾收集器在销毁对象时调用的,通过重写finalize()整理系统资源或者执行其他清理工作

    5. 面向对象的六大原则

    单一职责原则:一个类只有单一职责,就是高内聚。

    开放封闭原则:软件实体应该对扩展开放,对修改关闭。

    依赖倒置原则:面向接口编程,声明方法和变量尽量使用抽象类,因为抽象类可以被他的任何一个子类型替代。

    里氏替换原则:任何时候都可以用子类型替换父类型。

    接口隔离原则:一个接口只应该描述一种能力,接口也是高度内聚的。

    合成聚合复用原则:优先使用聚合或合成关系,优先采用Has-A(关联)关系而不是Is-A(继承)关系复用代码。

    迪米特法则:又叫最小知识法则,一个对象应该对其他对象尽可能少的了解,就是低耦合。

    6. BIO、NIO和AIO介绍

    同步:同步I,Java自己处理IO读写。

    异步:异步IO,Java将IO读写委托给OS处理,需要将数据缓冲区地址和大小传给OS,完成后OS通知Java处理。

    阻塞:使用阻塞IO时,Java调用会一直阻塞到读写完成才返回。

    非阻塞:使用非阻塞IO时,如果不能立马读写,Java调用会马上返回,当IO事件分发器通知可读写时在进行读写,不断循环直到读写完成。

    6.1 BIO

    同步并阻塞,服务器的实现模式是一个连接一个线程,这样的模式很明显的一个缺陷是由于客户端连接数与服务器线程数成正比关系,可能造成不必要的线程开销,严重的还将导致服务器内存溢出。当然,这种情况可以通过线程池机制改善,但并不能从本质上消除这个弊端。

    6.2 NIO

    从JDK1.4开始,JDK引入的新的IO模型NIO。它是同步非阻塞,服务器的实现模式是多个请求一个线程,即请求会注册到多路复用器Selector上,多路复用器轮询到连接有IO请求时才启动一个线程处理。

    6.3 AIO

    JDK1.7发布了NIO2.0,这就是真正意义上的异步非阻塞,服务器的实现模式为多个有效请求一个线程,客户端的IO请求都是由OS先完成再通知服务器应用去启动线程处理。

    7. IO流

    7.1 字节流

    字节流

    7.2 字符流

    字符流

    8. 序列化和反序列化

    序列化:把对象转换为字节序列的过程称为对象的序列化。

    反序列化:把字节序列恢复为对象的过程称为对象的反序列化。

    注:被序列化的对象需要实现serializable接口,不想存放在硬盘上的字段加@transient。

    9. Servlet

    Servlet生命周期分为三个阶段:

    1. 初始化阶段,调用init()方法。

    2.响应请求阶段,调用service()方法。

    3.终止阶段,调用destory()方法。


    响应请求阶段:

    1. Client向Servlet容器(tomcat)发出Http请求。

    2. Servlet容器接收 Client的请求,创建一个HttpRequest对象,将Client请求的信息封装到这个对象中;创建一个HttpResponse对象。

    3. Servlet容器调用HttpServlet对象的service方法,把HttpRequest对象和HttpResponse对象作为参数传递给HttpServlet对象。

    4. HttpServlet调用HttpRequest对象的有关方法,获取Http请求信息。

    5. HttpServlet调用HttpResponse对象的有关方法,生成响应数据。

    6. Servlet容器把HttpServlet的响应结果返回Client。

    10. Session和Cookie

    Cookie是客户端技术,程序把每个用户的数据以Cookie的形式写给用户各自的浏览器。当用户使用浏览器再去访问服务器中的web资源时,就会带着各自的数据去。

    Session是服务器端技术,服务器在运行时可以为每一个用户的浏览器创建一个其独享的Session对象。

    10.1 Cookie的特点

    1.不同的浏览器存放的Cookie位置不一样。

    2.Cookie的存储是以域名形式进行区分的。

    3.一个域名下存放的Cookie的个数是有限制的,不同的浏览器存放的个数不一样。(50个)

    4.每个Cookie存放的内容大小也是有限制的,不同的浏览器存放大小不一样。(4kb)

    5.Cookie无法设置除当前域名或者其父域名之外的其他domain。

    6.Cookie只能存储字符串。

    10.2 Session的特点

    1.Session的实现依赖于Cookie,如果Cookie禁用,重写url。

    2.Session的默认时长是30分钟。

    3.Session可以存储对象。

    11. Socket

    网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个socket。

    11.1 TCP编程

    TCP协议是面向连接的、可靠的、有序的、以字节流的方式发送数据,通过三次握手方式建立连接,形成传输数据的通道,在连接中进行大量数据的传输,效率会稍低。

    TCP协议

    服务器端:

             1.创建ServerSocket对象,绑定监听的端口。

             2.通过accept()方法监听客户端请求。

             3.连接建立后,通过输入流读取客户端发送的信息。

             4.通过输出流向客户端发送信息。

             5.关闭资源。

    客户端:

             1.创建Socket对象,指明服务器的ip和端口号。

             2.连接建立后,通过输出流向服务器发送信息。

             3.通过输入流读取服务器发送的信息。

             4.关闭资源。

    11.2 UDP编程

    UDP协议是无连接的、不可靠的、无序的、速度快。数据传输时,首先将传输的数据定义成数据报(Datagram),大小限制在64k,在数据报中指明数据索要达到的Socket,然后再将数据报发送出去

    服务器端:

            1. 创建DatagramSocket,指定端口号。

            2. 创建DatagramPacket。

            3. 接受客户端发送的数据信息。

            4. 读取数据。

    客户端:

            1.创建DatagramPacket,指定发送的信息,接收方的地址和端口号。

            2. 创建DatagramSocket。

            3. 发送数据。

    11.3 拆包和黏包

    拆包:当一次发送(Socket)的数据量过大,而底层(TCP/IP)不支持一次发送那么大的数据量,则会发生拆包现象。


    黏包:当在短时间内发送(Socket)很多数据量小的包时,底层(TCP/IP)会根据一定的算法把一些包合作为一个包发送。


    如何应对拆包:对于已知数据长度的模式,可以构造相同大小的数组,循环读取。


    如果不希望发生黏包:

    Socket中也有相应的方法:setTcpNoDelay(booleanon)。但是当发送的速率大于读取的速率时,在服务端也会发生黏包,即因服务端读取过慢,导致它一次可能读取多个包。

    12. 多线程

    12.1 线程的生命周期

    线程生命周期

    12.2 线程的5种状态

    新建状态(New):当线程对象创建后,即进入了新建状态;


    就绪状态(Runnable):当调用线程对象的start()方法,即进入就绪状态。处于就绪状态的线程,只是说明此线程已经做好了准备,随时等待CPU调度执行;


    运行状态(Running):当CPU开始调度处于就绪状态的线程时,即进入到运行状态。


    阻塞状态(Blocked):处于运行状态中的线程由于某种原因,暂时放弃对CPU的使用权,停止执行,此时进入阻塞状态,直到其进入到就绪状态,才有机会再次被CPU调用进入到运行状态。根据阻塞产生的原因不同,阻塞状态又可以分为三种:

            1. 等待阻塞:运行状态中的线程执行wait()方法,使本线程进入到等待阻塞状态;

            2. 同步阻塞:线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态;

            3. 其他阻塞:通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。


    死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。

    12.3 实现线程的方式

    第一种:继承Thread,重写run()方法。

    第二种:实现Runnable接口,重写run()方法。

    第三种:实现Callable接口,重写call()方法。结合ExecutorService类,返回Future。

    12.4 ThreadLocal

    ThreadLocal 的属性包括threadLocalHashCode,getMap(Threadt) 到当前线程去获取一个 ThreadLocalMap 的内部类对象,ThreadLocal 用于提供线程局部变量,通过它的 get() 和 set() 方法访问某个变量相应线程自己的局部变量。


    ThreadLocalMap有一个Entry 数组,通过Entry数组保存局部变量。通过key(ThreadLocal类型)的hashcode来计算数组存储的索引位置i,如果i位置已经存储了对象,那么就往后挪一个位置依次类推,直到找到空的位置,再将对象存放。另外,在最后还需要判断一下当前的存储的对象个数是否已经超出了阈值(threshold的值)大小,如果超出了,需要重新扩充并将所有的对象重新计算位置。


    弱引用是Java中四档引用的第三档,当某个ThreadLocal已经没有强引用可达,则随着它被垃圾回收,在ThreadLocalMap里对应的Entry的键值会失效,这为ThreadLocalMap本身的垃圾清理提供了便利。

    12.5 原子类

    原子性布尔AtomicBoolean

             get()、set(value)、getAndSet(value)、compareAndSet(compareValue,setValue)


    原子性整型AtomicInteger

             get()、set(value)、compareAndSet(compareValue,setValue)

             addAndGet(value)、getAndAdd(value)、getAndIncrement(value)、incrementAndGet(value)

             decrementAndGet(value)、getAndDecrement(value)


    原子性长整型AtomicLong

            get()、set(value)、compareAndSet(compareValue,setValue)

             addAndGet(value)、getAndAdd(value)、getAndIncrement(value)、incrementAndGet(value)

             decrementAndGet(value)、getAndDecrement(value)


    原子性引用型AtomicReference

             get()、set(value)、compareAndSet(compareValue,setValue)

    12.6 线程池

    线程池(ThreadPoolExecutor)常用的参数:

            corePoolSize:核心线程数量

            maximumPoolSize:线程池允许的最大线程池数量

            keepAliveTime:线程数量超过corePoolSize,空闲线程的最大超时时间

            unit:超时时间的单位

            workQueue:工作队列,保存未执行的Runnable任务

            threadFactory:创建线程的工厂类

            handler:当线程已满,工作队列也满了的时候,会被调用。被用来实现各种拒绝策略。

    12.7 线程间通信

    12.7.1 CountDownLatch

    CountDownLatch用一个给定的计数器来初始化,该计数器的操作是原子操作,即同时只能有一个线程去操作该计数器。调用该类await()的线程会一直处于阻塞状态,直到其他线程调用countDown(),每次调用countDown()计数器的值减1。当计数器值减至零时,所有因调用await()处于等待状态的线程就会继续往下执行。这种现象只会出现一次,因为计数器不能被重置。

    12.7.2 CyclicBarrier

    CyclicBarrier它允许一组线程相互等待,直到到达某个公共屏障点。通过它可以完成多个线程之间相互等待,只有当每个线程都准备就绪后,才能各自继续往下执行后面的操作。它也是通过计数器来实现的,当线程调用await()时,该线程进入等待状态,且计数器加1,当计数器的值达到设置的初始值时,所有因调用await()进入等待状态的线程被唤醒,继续执行后续操作。因为CyclicBarrier在释放等待线程后可以重用,所以称为循环barrier。

    12.7.3 Semaphore

    Semaphore它被更多地用来限制流量,类似阀门的功能。如果限定某些资源最多有N个线程可以访问,那么超过N个就不允许再有线程来访问,同时当现有线程结束后就会释放,然后允许新的线程进来。

    12.7.4 volatile

    volatile是成员变量的修饰符,能够保证该变量在多线程中的可见性。实现原理是将写缓存刷新到主存中,然后根据缓存一致性原则保证可见性。

    12.7.5 同步方法

    wait()、notify()、notifyAll()是Object的本地final方法,而不在Thread类中。因为java中锁的级别是对象级而不是线程级,每个对象都有锁,通过线程获得。如果wait()在线程中,线程正在等待的是哪个锁就不明显了。

    wait()和notify方法要在同步块中调用?因为wait和notify都需要获得对象锁。

    相关文章

      网友评论

          本文标题:面试篇:JAVA基础

          本文链接:https://www.haomeiwen.com/subject/ffijohtx.html