• Java 基础知识点


    很多 Java 基础的东西都忘记了, 有必要再复习一些基本的知识点.
    本文主要参考 https://github.com/Snailclimb/JavaGuide


    ===========================
    Java 访问限定符的可见性
    ===========================
    参考: https://o7planning.org/en/10319/access-modifiers-in-java
    Java 实际上有 private/default/protected/public 四种访问限定符.
    如果一个类属性或方法不加任何限定符, 就是 default 限定, default 是一个介于 private 和 protected 之间的限定.
    如果接口中的方法没加任何限定符, 其实是 public 级而不是 default.

    修饰符 类内能否访问 包内能否访问 包外的子类访问性 包外访问性
    private Y      
    default Y Y    
    protected Y Y Y  
    public Y Y Y Y

    ===========================
    logger 的正确使用
    ===========================
    使用 logger 打印出完整的 exception stacktrace, 需要使用 logger.error() 两个参数的重载, 将 exception 对象传给第二个参数.

    try {
         // 
        }
    } catch (Exception e) {
        logger.error(e.getMessage(), e);
        // e.printStackTrace();
    }

    如果日志输出有字符串拼接, 最好先做一个 precondition 检查, 而不是直接调用 logger.debug(), 这样能避免字符串对象创建.
    推荐:

    if (logger.isDebugEnabled()) {
        logger.debug("Some message" + ", message2");
    }

    不推荐:

    logger.debug("Some message" + ", message2");

    ===========================
    Optional<> 类型正确使用场景:
    ===========================

    1. 类属性不应该使用 Optional<T> 类型, Optional<T> 不能序列化.
    2. 方法形参不应该使用 Optional<T> 类型, 形参声明为 Optional<T> 其实没有人任何意义.
    3. 方法返回值鼓励使用 Optional<T> 类型, 告知方法使用者, 该方法可能"no result".

      

    ===========================
    String/StringBuilder/StringBuffer
    ===========================
    1. String 类型是不可变对象, 所以线程安全.
    2. StringBuffer 可以看作是 StringBuilder 的线程安全版, 它对于数据操作访问都加了同步锁.
    3. StringBuilder 并不是线程安全. 相同情况下, 使用 StringBuilder 仅比 StringBuffer 有 10~15%的性能提升, 但却要冒线程不安全的风险.
    字符串不太变动的场景下, 推荐使用 String.
    单线程下需要对字符串有大量的修改, 推荐使用 StringBuilder.
    多线程下需要对字符串有大量的修改, 推荐使用 StringBuffer.

    ===========================
    子类构造子中写或不写 super(arg1...) 的区别
    ===========================
    子类的构造子中, 如果没有明确写出 super(arg...), Java 编译器会自动调用父类中的 "无参构造子".
    如果一个类没有定义任何构造子, Java 编译器自动提供一个无参构造子, 如果我们已经写了一个构造子, Java 编译器就不会自动提供那个无参构造子.


    ===========================
    接口成员可视级别
    ===========================
    接口访问默认的访问级别是 public
    接口中的实例变量默认为 final 类型.

    ==========================
    ==比较符 与 equals()
    ===========================
    ==比较符, 对于基本数据类型, 只要值相等返回为 true, 对于引用类型, 如果引用的是同一个对象才返回 true.
    equals() 成员函数, 本意是用来判断两个对象的内容是否相等. 但如果类没有重写 equals() 方法, 等价于使用 == 比较符.


    ===========================
    hashCode() 与 equals() 方法
    ===========================
    hashCode() 方法是用来返回对象的 hash 值, hashCode() 方法在很多地方被用到, 比如在使用 HashMap 和 HashSet 集合的过程中, 在比较两个元素的时候, 会先比较两个元素的 hash 值, 如果 hash 不相等, 则两元素肯定不相等, 如果 hash 值相等, 才调用 equals() 方法做进一步的检查.
    如果我们的类没有实现 hashCode() 方法的话, 默认的 hash 值为堆上对象的一个独特值, 所以如果没有重写 hashCode() 的话, 两个对象的 hash 值是无论如何不相等的. 因此一个类如果重写 了 equals(), 则一定要重写 hashCode() 方法, 否则重写 equals()是没有意义的. 


    ===========================
    final 关键词
    ===========================
    1. final 修饰一个变量, 如果是基本的数据类型, 则数值在初始化之后不允许被修改. 如果是引用类型, 则在初始化之后不能再指向其他对象.
    2. final 修饰一个类, 表明这个类不能再被继承.
    3. final 修饰一个方法, 表明该方法不能被子类重写.


    ===========================
    static{} 静态代码块 和 {} 构造代码块
    ===========================
    在一个类中, 可以有多个 static {} 代码块, 这些代码块是在构造子之前被执行的, 如果有多个这样的代码块, 按照定义的顺序执行. 一般 static{} 代码块是对 static 变量进行赋值.

    类中还可以有一种特殊的代码块, 使用 {} 包着, 被叫做"构造代码块"或"非静态代码块". 如果构造代码块有多个, 也是按照定义的顺序指定的.

    代码的执行顺序:
    static 初始化语句 --> 静态代码块 --> 构造代码块 --> 构造子
    static 初始化语句和静态代码块仅仅在类加载时被执行一次. 构造代码块和构造子是在对象实例化的时候被调用, 一个类可能有多个构造子, 但不管调用了哪个构造子, 在这之前, 构造代码块总会被自动执行.


    ===========================
    Map
    ===========================
    1. Hashtable (线程安全). 内部方法都经过了 synchronized 过, 是线程安全的, 但效率较差, 基本已经被淘汰, 如果需要考虑线程安全问题, 推荐使用 ConcurrentHashMap .
    2. HashMap (线程不安全) 因为没有锁机制, 所以不是线程安全. HashMap 允许 key 为 null.
    3. LinkedHashMap (线程不安全) 继承自 HashMap, 在 HashMap 基础上, 增加了一条双向链表, 使得可以保持键值对的插入顺序.
    3. ConcurrentHashMap (线程安全) 对整个桶数组进行了分段 segement, 然后在每个分段上使用了 lock 锁, 比 Hashtable 的 synchronized 粒度更细, 并发性能更好. ConcurrentHashMap 不允许 key 为 null.
    4. TreeMap 红黑树 (自平衡的排序二叉树)
    使用场景: 需要线程安全, 选用 ConcurrentHashMap; 如果需要保持插入的顺序, 选用 LinkedHashMap; 如果需要排序, 选用 TreeMap; 其他场景可选用 HashMap.


    ===========================
    Set 类
    ===========================
    HashSet (无序, 唯一) 哈希表. 线程不安全.
    LinkedHashSet: 链表和哈希表组成, 由链表保证元素的排序, 由哈希表保证元素的唯一性. 线程不安全
    TreeSet: (有序, 唯一) 红黑树 (自平衡的排序二叉树). 线程不安全


    ===========================
    List 类
    ===========================
    ArrayList, (底层是 Object 数组) 查询快,增删慢, 线程不安全, 效率高.
    Vector, 查询快, 增删慢, 线程安全. 单线程不推荐,  多线程场景推荐使用, 或者使用 ArrayLIst/LinkedList 自己保证同步
    LinkedList,(底层是链表) 查询慢, 增删块, 线程不安全, 效率高.
    JDK 中没有专门的排好序LIst类型, 可以使用  Collections.sort() 排序, 或者使用 TreeMap 来模拟一个有序List. 

    ===========================
    ArrayList<T> 转 T[] 数组
    ===========================
    List<String> list= new ArrayList<>();
    如何将该 list 转成 String[]?
    错误的写法是:
    String [] strings = (String[])list.toArray();
    原因是: list.toArray() is creating an Object[] rather than a String[], 得到结果后不能直接将Object[]强转为 String[], 因为String[]和Object[]不是基类子类关系.

    正确的写法是利用其泛型重载:
    String [] strings = list.toArray(new String[0]);
    或:
    String[] strings = list.stream().toArray(String[]::new);

    ===========================
    巧用 Collections package: 避免在 API 层返回 null 对象
    ===========================
    当API返回值尽量避免使用 null, 下面是推荐写法:
    (1)String 类型: 推荐返回 ""
    (2)List/Set/Map 类型: 推荐使用 Collections.emptyList(), emptySet(), emptyMap()
    (3)Array 类型: 推荐返回一个零长度的数组
    (4)其他类型: 推荐使用 Optional 包装

    ===========================
    巧用 Collections package: 返回自读集合类型
    ===========================
    Collections.unmodifiableCollection(c)
    Collections.unmodifiableList(list)
    Collections.unmodifiableMap(m)
    Collections.unmodifiableSet(s)


    ===========================
    线程安全的集合
    ===========================
    如果要考虑线程安全的集合类, 推荐使 java.util.concurency 包下定义的类, 而不是老的 Vector 和 Hashtable 类, 也尽量不要使用 synchronized 来封装.
    List: CopyOnWriteArrayList
    Map: ConcurrentHashMap
    Set: ConcurrentSkipListSet 和 CopyOnWriteArraySet


    ===========================
    Atomic 类型
    ===========================
    Java 中的原子类型, 主要用于多线程场景, 一个操作一旦开始,就不会被其他线程干扰, 保证数据操作是以原子方式进行的. 原子类位于 java.util.concurency.atomic 包, 最常用的有:
    AtomicInteger/AtomicIntegerArray
    AtomicLong/AtomicLongArray
    AtomicReference/AtomicReferenceArray

    Atomic 类型比 synchronized 同步访问的开销要小, 使用的是 CAS(compare and set 比较并替换) + volatile + native 的实现机制.


    ===========================
    快速构建数组和List
    ===========================
    构建一个数组
    Long[] longs=new Long[] {1L,2L};
    String[] strings= new String[] {"a","b","c"};
    String[] array = { "1", "kk", "a", "b", "c", "a" };

    快速构建一个 ArrayList
    List<String> lst=Arrays.asList("a","b","c");


    ===========================
    生成Stream, 将Stream转成List
    ===========================
    构建Stream的方法:
    1. 使用Collection 接口的 stream() 方法, 比如 list.stream()
    2. 通过Stream接口的静态工厂方法, 比如 Stream<Integer> integerStream = Stream.of(1, 2, 3, 5);


    将 Stream 转换为 List
    targetLongList = sourceLongList.stream()
    .filter(l -> l > 100)
    .collect(Collectors.toList());

    Stream 使用文档:
    http://ifeve.com/stream/
    http://www.runoob.com/java/java8-streams.html
    https://www.liaoxuefeng.com/article/001411309538536a1455df20d284b81a7bfa2f91db0f223000
    https://www.ibm.com/developerworks/cn/java/j-lo-java8streamapi/


    ===========================
    Java 8中处理日期和时间
    ===========================
    参考:
    https://www.liaoxuefeng.com/article/00141939241051502ada88137694b62bfe844cd79e12c32000
    http://www.importnew.com/14140.html

    Java 8 以后终于可以不使用下面这些类型了, 包括:
    java.util.Date 和 java.util.Calendar 和 java.util.TimeZone 和 java.text.SimpleDateFormat.
    它们不仅线程不安全, 关键是难用.

    Java 8 推出的一系列日期时间类, 都是在 java.time 包下面.
    java.time.format.DateTimeFormatter 代替 SimpleDateFormat
    java.time.LocalDate, 仅包含日期
    java.time.LocalTime, 仅包含时间
    java.time.LocalDateTime, 包含日期和时间
    java.time.ZoneId, 用来指定时区
    java.time.Period, 是一个时间区间, 有开始有结束.
    java.time.Duration, 是一个时间长度, 比如几天
    java.time.temporal.TemporalAdjusters, LocalDate等类已经包含了日期加加减减等功能, 如果需要更高级的日期推导, 可以调用 LocalDate 中一些带有 TemporalAdjusters 参数的方法.

    相关几个类的细微差别:
       Instant 类: UTC 时间线上的瞬时值, 具体取值是: 从1970 epoch起到现在的纳秒数.
       ZonedDateTime 类: 该是有时区概念的时间, ZonedDateTime = ( Instant + ZoneId )
       LocalDateTime,LocalDate,LocalTime 类: 这三个类没有时区概念, 比如全世界的圣诞节都是从"12月25日零点"开始算起.

    Java 8 中, 和 JDBC 类型搭配关系是:
    JDBC  < -- > Java
    date <--> LocalDate
    time <--> LocalTime
    timestamp <--> LocalDateTime

    ===========================
    调试过程中判断获取对象的类名
    ===========================
    在调试过程中, 经常要看 someObject 的类名, 方法是在 debugger expressions 窗口, 增加一个表达式: someObject.getClass().toString()

  • 相关阅读:
    drf中 连表深度查询和ListSerializer类包括drf中Response二次封装
    drf中表之间断关联操作
    drf中的序列化家族
    rest_framework框架的封装特点和APIView请求生命周期
    vue项目和django项目交互补充,drf介绍,restful规范
    Vue成绩单
    面向对象编程day2
    面向对象编程day3
    面向对象编程day1
    day13
  • 原文地址:https://www.cnblogs.com/harrychinese/p/java_basics.html
Copyright © 2020-2023  润新知