Java Personal Notes

This is a record of JAVA questions from Nowcoder.

!!!
Most original answers rights to the original author listed on Nowcoder websites if there is any wrong please contact me as soon as possible to do delete processing.[xjm.good@gmail.com]

Java Basic#

Basic grammar#

  1. 三元操作符如果遇到可以转换为数字的类型,会做自动类型提升。

    1
    2
    Object o1 = (false) ? new Double(1.0) : new Integer(2);
    System.out.println(o1);

    Output: 2.0

    三元操作符类型的转换规则:

    • 若两个操作数不可转换,则不做转换,返回值为Object类型
    • 若两个操作数是明确类型的表达式(比如变量),则按照正常的二进制数字来转换,int类型转换为long类型,long类型转换为float类型等。
    • 若两个操作数中有一个是数字S,另外一个是表达式,且其类型标示为T,那么,若数字S在T的范围内,则转换为T类型;若S超出了T类型的范围,则T转换为S类型。
    • 若两个操作数都是直接量数字,则返回值类型为范围较大者
  2. Java中的那些基本类型属于原生类,而数组是引用类型,不属于原生类,可以看成是一种对象。

  3. Synchronized保证三大性,原子性,有序性,可见性,volatile保证有序性,可见性,不能保证原子性

  4. Java的堆内存分为两块:permantspace(持久带) 和 heap space。
    持久带中主要存放用于存放静态类型数据,如 Java Class, Method 等, 与垃圾收集器要收集的Java对象关系不大。
    而heapspace分为年轻带和年老带
    年轻代的垃圾回收叫 Young GC, 年老代的垃圾回收叫 Full GC。
    在年轻代中经历了N次(可配置)垃圾回收后仍然存活的对象,就会被复制到年老代中。因此,可以认为年老代中存放的都是一些生命周期较长的对象
    年老代溢出原因有 循环上万次的字符串处理、创建上千万个对象、在一段代码内申请上百M甚至上G的内存,既A B D选项
    持久代溢出原因 动态加载了大量Java类而导致溢出。

  5. Java 内部类、成员类、局部类、匿名类等

    • 类指外部类,最大的类,修饰符有public(表示该类在项目所有类中可以被导入),default(该类只能在同一个package中使用),abstract,final
    • 内部类指位于类内部但不包括位于块、构造器、方法内,且有名称的类,修饰符有public,private,protected访问控制符,也可以用static,final关键字修饰,public和private比较简单,一个表示所有可以被所有类访问,一个表示只能被自身访问,protected修饰的成员类可以被同一个包中的类和子类访问。而default修饰的成员类只能被同一个包中的类访问。
    • 局部内部类指位于块、构造器、方法内的有名称类,最多只能有final修饰

    Reference Link

  6. Java几个常用程序

    程序名 用法
    java.exe java虚拟机
    javadoc.exe 制作java文档
    jdb.exe java的调试器
    javaprof.exe 剖析工具
    jps.exe 查看本机java进程信息
    jstack 打印线程的栈信息,制作线程dump文件
    jmap 打印内存映射,制作堆dump文件
    jstat 性能监控工具
    jhat 内存分析工具
    jconsole 简易的可视化控制台
    jvisualvm 功能强大的控制台
  7. Java中的位运算符:

    • >>表示右移,如果该数为正,则高位补0,若为负数,则高位补1;
    • >>>表示无符号右移,也叫逻辑右移,即若该数为正,则高位补0,而若该数为负数,则右移后高位同样补0。
  8. 抽象类中可以有普通成员变量,接口中没有普通成员变量

  9. HttpServletResponse完成:设置http头标,设置cookie,设置返回数据类型,输出返回数据;读取路径信息是HttpServletRequest做的

  10. ArrayList的空间浪费主要体现在在list列表的结尾预留一定的容量空间,而LinkedList的空间花费则体现在它的每一个元素都需要消耗相当的空间。//正确,因为ArrayList空间的增长率为1.5倍,所以,最后很可能留下一部分空间是没有用到的,因此,会造成浪费的情况。对于LInkedList的话,由于每个节点都需要额外的指针。

  11. 可以正确获取结果集的有
    +
    1
    2
    Statement sta=con.createStatement();
    ResultSet rst=sta.executeQuery(“select * from book”);
    #

    1
    2
    PreparedStatement pst=con.prepareStatement(“select * from book”);
    ResultSet rst=pst.executeQuery();
  12. Java允许对boolean类型的值进行按位“与”、“或”和“异或”操作,但不能进行按位“非”。对于boolean值,按位操作与逻辑操作有相同的结果,但是不会发生“短路”。

  13. Array的foreach可以用break直接跳出

  14. java初始化顺序

    • 不同类中 父类 > 子类
    • 同类中 静态变量 > 静态代码块 > 普通成员变量 > 普通代码块 > 构造方法
  15. (b1 + b4) 结果仍然转换成int //b1, b4均为byte

  16. Java复制的效率System.arraycopy>clone>Arrays.copyOf>for循环,这个有兴趣自己测试一下就知道了。这里面在System类源码中给出了arraycopy的方法,是native方法,也就是本地方法,肯定是最快的。而Arrays.copyOf(注意是Arrays类,不是Array)的实现,在源码中是调用System.copyOf的,多了一个步骤,肯定就不是最快的。

  17. 二维数组定义,一维长度必须定义,二维可以后续定义

    1
    2
    3
    4
    float f[][] = new float[6][6];
    float []f[] = new float[6][6];
    float [][]f = new float[6][6];
    float [][]f = new float[6][];
  18. java Collection and map

    • Collection
      • List
        • LinkedList 非同步
        • ArrayList 非同步,实现了可变大小的元素数组
        • Vector 同步
          • Stack
      • Set 不允许有相同的元素
    • Map
      • HashTable 同步,实现一个key–value映射的哈希表
      • HashMap 非同步,
      • WeakHashMap 改进的HashMap,实现了“弱引用”,如果一个key不被引用,则被GC回收
  19. 加载驱动方法

    • Class.forName(“com.microsoft.sqlserver.jdbc.SQLServerDriver”);
    • DriverManager.registerDriver(new com.mysql.jdbc.Driver());
    • System.setProperty(“jdbc.drivers”, “com.mysql.jdbc.Driver”);
  20. 1
    Arrays.asList(num1, num2, num3)
  21. 1
    2
    3
    Arrays.sort(intervals, (x, y) -> {
    return Integer.compare(x[0], y[0]);
    });
  22. 1
    2
    map.getOrDefault(t.charAt(i), 0);
    map.computeIfAbsent() //V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction)

Exception#

  1. Java的try后不一定必须有catch块,也可以加final块 但是一定要有(要么有catch要么有final)
  2. 异常是指程序运行时(非编译)所发生的非正常情况或错误,当程序违反了语音规则,jvm就会将出现的错误表示一个异常抛出。
    异常也是java 的对象,定义了基类 java。lang。throwable作为异常父类。 这些异常类又包括error和exception。两大类
    error类异常主要是运行时逻辑错误导致,一个正确程序中是不应该出现error的。当出现error一般jvm会终止。
    exception表示可恢复异常,包括检查异常和运行时异常。 检查异常是最常见异常比如 io异常sql异常,都发生在编译阶段。这类通过try、catch捕捉
    而运行时异常,编译器没有强制对其进行捕捉和处理。一般都会把异常向上抛出,直到遇到处理代码位置,若没有处理块就会抛到最上层,多线程用thread。run()抛出,单线程用main()抛出。常见的运行异常包括 空指针异常 类型转换异常 数组月结异常 数组存储异常 缓冲区溢出异常 算术异常等。

OOP#

  1. 面向对象程序设计方法的优点包含
    • 可重用性
    • 可扩展性
    • 易于管理和维护

Design Pattern#

  1. Java中四种线程安全的单例模式实现方式

    • 饿汉式(线程安全)
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      public class Single2 {

      private static Single2 instance = new Single2();

      private Single2(){
      System.out.println("Single2: " + System.nanoTime());
      }

      public static Single2 getInstance(){
      return instance;
      }
      }
    • 懒汉式(方法中没有synchronize关键字,不安全)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    public class Single3 {

    private static Single3 instance = null;

    private Single3(){
    System.out.println("Single3: " + System.nanoTime());
    }

    public static synchronized Single3 getInstance(){
    if(instance == null){
    instance = new Single3();
    }
    return instance;
    }
    }
    • 懒汉模式改良版(线程安全,使用了double-check,即check-加锁-check,目的是为了减少同步的开销)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    public class Single4 {

    private volatile static Single4 instance = null;

    private Single4(){
    System.out.println("Single4: " + System.nanoTime());
    }

    public static Single4 getInstance(){
    if(instance == null){
    synchronized (Single4.class) {
    if(instance == null){
    instance = new Single4();
    }
    }
    }
    return instance;
    }
    }
    • 利用私有的内部工厂类(线程安全,内部类也可以换成内部接口,不过工厂类变量的作用于要改为public了。)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    public class Singleton {

    private Singleton(){
    System.out.println("Singleton: " + System.nanoTime());
    }

    public static Singleton getInstance(){
    return SingletonFactory.singletonInstance;
    }

    private static class SingletonFactory{
    private static Singleton singletonInstance = new Singleton();
    }
    }

Networking#

  1. 如果希望监听TCP端口9000,服务器端应该怎样创建socket?

    1
    Server serverProcess = new ServerSocket(9000);
  2. OSI七层协议

    1. 物理层 传输比特流 -> 网卡 接收发送比特流
    2. 数据链路层 物理寻址 同时将原始比特流转变为逻辑传输线路 如何格式化数据 如何控制对物理层的访问 错误校验
    3. 网络层 网络地址到网络地址 IP 协议
    4. 传输层 接受上一层数据 在必要的时候进行数据拆分 并且将保证这些数据段有效到达对端 流量控制 TCP/UDP
    5. 会话层 建立管理应用层通讯
    6. 表示层 不同系统间通讯语法问题 对信息进行格式化
    7. 应用层 消息头消息体 规定数据传递格式
  3. 自上而下封包 自下而上拆包

  4. TCP/IP 四层结构

    1. 应用层 文件传输 数据格式化 接触或建立去别的点的协议
    2. 传输层 提供端对端接口
    3. 网络层 为数据包选择路由
    4. 链路层 以二进制形式再物理媒体上传输数据
  5. TCP三次握手

    1. TCP简介

      1. 面向连接的 可靠的 基于字节流的传输层通信协议
      2. 将应用层的数据流分割成报文段并发送给目标节点的tcp层
      3. 数据包都有序号 对方收到则发送ACK确认 未收到则重传
      4. 使用校验和来检验数据再传输过程中是否有误
    2. TCP报文头部

      1. Source Port 源端口 2字节 唯一标示主机的一个进程
      2. Destination Port 目的端口 2字节
      3. Sequence Number 4个字节 按顺序对每个报文段进行编号
      4. Acknowledge Number 期望收到对方下一个报文的第一个数据字节的序号
      5. Offset 因为TCP头又可选字段 offset指出tcp的数据距离起始头部有多远
      6. Reserved 保留域
      7. TCP Flags
        1. URG 紧急指针标志 为1时紧急指针有效
        2. ACK 确认序号标志 为1表明确认信息有效 为0表明报文中不含确认信息 忽略确认号字段
        3. PSH push标志 为1表示带有push的数据 接收方收到后尽快交给应用层 而不是在缓冲区排队
        4. RST 重置连接标志 重置由于主机崩溃或其他原因出现错误的连接 拒绝非法报文段和拒绝连接请求
        5. SYN 同步序列号 用于建立连接过程
        6. FIN finsh标志 用于释放标志 为1时标识发送方没有数据发送了 即将关闭连接
      8. window 窗口大小 用户控制传输速率
      9. checksum 校验和
      10. Urgent Pointer 指出本报文段紧急数据的字节数
    3. 三次握手为了建立连接

      1. A B 同时处于closed 状态 主动打开的客户端 被动打开的是服务端 服务器端建立一个TCB 传输控制块 准备接受请求 服务器端进入listen状态
      2. A创建一个TCB 主动打开连接 向服务器发送连接请求报文 SYN seq = x x是一个任意正整数 发送后A进入SYN-SENT状态 不可携带报文段数据
      3. B接收到请求如果同意 则发送一个确认报文 确认报文中包含中 SYN = 1 ACK =1 seq = y ack = x+1 服务器进去SYN-RCVD状态 不可携带报文段数据
      4. A发送ACK = 1 seq = x + 1 ack = y + 1 A进入established状态 可以携带报文段数据
      5. B收到后进入established状态
    4. 为什么需要三次握手才能建立起连接

      1. 为了初始化Sequence Number的初始值 tcp会用这个序列号来拼接数据
      2. SYN 超时 如果Server收到Client的SYN,回复SYN-ACK的时候未收到确认 这个时候Server会不断重试直至超时 Linux默认5次 初始1秒 每次翻倍
      3. 针对SYN Flood的防护措施
        1. SYN队列满后 通过tcp_syncookies参数回发SYN Cookie
        2. 若为正常连接则Client会回发SYN Cookie 直接建立连接
    5. 建立连接后 Client出现故障怎么办

      保活机制

      在一段时间后 这个时间被称为保活时间 keep-alive time 在这段时间内连接出去非活动状态 开启保活功能的一段将向对方发送一个保活探测报文 如果发送端没有收到响应 每隔一段时间keep-alive-interval继续发送 知道尝试次数达到保活探测数仍未收到响应则中断连接 这时对方主机被称为不可达 连接也会被中断

  6. TCP的四次挥手

    挥手是为了终止连接 假设由客户端主动结束连接

    1. 四次挥手流程

      1. 最开始的时候客户端A服务端B都处于established状态 由客户端A主动关闭连接
      2. A发送连接释放报文 并且停止发送数据 FIN = 1 seq = u 发送后A进入FIN-WAIT-1状态 即使FIN 报文段不携带任何数据 也要消耗掉一个序列号
      3. B接收到连接释放报文 回复确认报文 ACK = 1 seq = v ack = u+1 v是自己的序列号 发送后 进入 close-wait状态 处于半关闭状态 服务器还可以发送最后的数据
      4. A收到第一次挥手确认报文后 进入close-wait2状态 等待服务器发送释放连接报文 A还可以接收服务器发送的数据
      5. B向A发送连接释放报文 FIN=1 ACK = 1 seq = w ack = u+1 进入last-ack状态等到最后确认
      6. A在接受到B发送的连接释放报文后 必须回复 确认报文 ACK = 1 seq = u+1 ack = w+1 客户端进入time-wait状态 等待2MSL后进入CLOSED状态
      7. B收到A发来的确认立即进入closed状态
    2. 为什么会有TIME_WAIT状态

      1. 确保有足够的时间让对方收到ACK包
      2. 避免新旧连接混淆
    3. 为什么需要4次握手才能断开连接

      因为TCP是全双工的 发送方和接收方都需要FIN报文和ACK报文

    4. 服务器出现大量close_wait原因

      客户端一直在请求 服务器端返回给客户端的信息是异常的或者错误的

      对方关闭socket连接 我方忙于读写 没有及时关闭连接

      检查代码 特别是释放资源的代码

      检查配置 特别是处理请求的线程配置不合理

  7. UDP简介

    1. UDP报文结构
      1. Source Port
      2. Destination Port
      3. Length
      4. Checksum
    2. 特点
      1. 面向非连接 双方不建立连接
      2. 不维护连接状态 支持同时向多个客户端传输相同的消息
      3. 数据包报头只有8个字节 额外开销小很多
      4. 吞吐量之受限于数据生成速率 传输速率以及机器性能
      5. 尽最大努力交付 不保证可靠交付 不需要维持复杂的链路状态表
      6. 面向报文 不对应用程序提交的报文信息进行拆分或者合并
  8. TCP和UDP 区别

    1. 面向连接 vs 无连接
    2. 可靠 vs 不可靠
    3. 有序 vs 无序
    4. 速度慢 vs 速度快
    5. 重量级 vs 轻量级
  9. TCP的滑动窗口

    1. RTT和RTO

      1. RTT round trip time发送一个数据包到收到对应的ACK所花费的时间
      2. RTO重传时间间隔 retransmission timeout
    2. TCP使用滑动窗口做流量控制与乱序重排

      1. 保证TCP的可靠性
      2. 保证TCP的流控特性
    3. 窗口数据计算过程

      滑动窗口包含已经发送的数据和即将允许发送的数据序列号长度之和 窗口外的数据不能发送

      接收端使用公式算出接收端advertisedwindow的值

      发送方要保证 LastByteSent - LastByteAcked小于这个值 通过计算EffectiveWindow来计算出还可以发送多少

      $$AdvertisedWindow = MaxRcvBuffer - (LastByteRcvd - LastByteRead)$$

      $$EffectiveWindow = AdvertisedWindow - (LastByteSent - LastByteAcked)$$

  10. HTTP简介

    1. 超文本传输协议HTTP主要特点
      1. 支持客户/服务器模式
      2. 简单快速
      3. 灵活 允许传输任意类型的数据对象
      4. 限制每次连接只处理一个请求 处理完就断开 从HTTP1.1起默认启用长连接 需要等待一定时间后才断开连接
      5. 无状态协议 对事务处理没有记忆能力 如果

I/O#

  1. 哪个I / O类可以附加或更新文件?

    RandomAccessFile 可以通过 seek(long pos) 方法去移动文件指针进行追加更新写入.OutputStream() 是一个抽象类 不能直接实例化去写入.DataOutputStream() 也无法追加写入


Multi-threading#

  1. volatile与synchronized的区别:

    • volatile本质是在告诉jvm当前变量在寄存器中的值是不确定的,需要从主存中读取,synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住.
    • volatile仅能使用在变量级别,synchronized则可以使用在变量,方法.
    • volatile仅能实现变量的修改可见性,但不具备原子特性,而synchronized则可以保证变量的修改可见性和原子性.
    • volatile不会造成线程的阻塞,而synchronized可能会造成线程的阻塞.
    • volatile标记的变量不会被编译器优化,而synchronized标记的变量可以被编译器优化.
  2. 多进程与多线程

    • 一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程(通常说的主线程)。
    • 资源分配给进程,同一进程的所有线程共享该进程的所有资源。
    • 线程在执行过程中,需要协作同步。不同进程的线程间要利用消息通信的办法实现同步。
    • 处理机分给线程,即真正在处理机上运行的是线程。
    • 线程是指进程内的一个执行单元,也是进程内的可调度实体。

Java Web#

Java Web Basic#

  1. Servlet的生命周期分为5个阶段
    • 加载:容器通过类加载器使用servlet类对应的文件加载servlet
    • 创建:通过调用servlet构造函数创建一个servlet对象
    • 初始化:调用init方法初始化
    • 处理客户请求:每当有一个客户请求,容器会创建一个线程来处理客户请求
    • 卸载:调用destroy方法让servlet自己释放其占用的资源

Spring Framework#