zoukankan      html  css  js  c++  java
  • java面试 (六)

    1 String.split(String regex), 传入的参数是正则表达式,有一些特殊字符(比如.[]()| 等)需要转义。

    2  关于枚举类型,一般用作常量,理想情况下,枚举中的属性字段是私有的,并在私有构造函数中赋值,没有对应的 Setter 方法,最好加上 final 修饰符。

    public enum PersonSex2 {
    MALE("", "man"),
    FEMALE("", "female");

    // value/description用final,因为一般枚举类型被用作常量,不可以修改
    private final String value; // value可以是其他类型,一般用int
    private final String description; // description 一般用String。

    // 构造函数用private,是因为不想在外部被实例化。
    private PersonSex2(String value, String description) {
    this.value = value;
    this.description = description;
    }

    // 只有get方法,没有set方法,是因为不会setter。
    public String getValue() {
    return value;
    }

    public String getDescription() {
    return description;
    }
    }

     3 返回空数组和空集合而不是 null

      返回 null ,需要调用方强制检测 null ,否则就会抛出空指针异常。返回空数组或空集合,有效地避免了调用方因为未检测 null 而抛出空指针异常,还可以删除调用方检测 null 的语句使代码更简洁。

        public static List<String> getResults() {
            return new ArrayList<>(0);
        }
    
        public static List<String> getResultList() {
            return Collections.emptyList();
        }
    
        public static Map<String, String> getResultMap() {
            return Collections.emptyMap();
        }

     4 禁止使用构造方法 BigDecimal(double)

      浮点数进行计算,有精度损失,不准确,如下(Java中float的精度为6-7位有效数字。double的精度为15-16位):

    System.out.println(0.05 + 0.01);
    System.out.println(1.0 - 0.42);
    System.out.println(4.015 * 100);
    System.out.println(123.3 / 100);
    
    0.060000000000000005
    0.5800000000000001
    401.49999999999994
    1.2329999999999999
    很有可能造成我们手中有0.06元,却无法购买一个0.05元和一个0.01元的商品。因为如上所示,他们两个的总和为0.060000000000000005。这无疑是一个很严重的问题,尤其是当电商网站的并发量上去的时候,出现的问题将是巨大的。可能会导致无法下单,或者对账出现问题
     
    在《Effective Java》中提到一个原则,那就是float和double只能用来作科学计算或者是工程计算,但在商业计算中我们要用java.math.BigDecimal,通过使用BigDecimal类我们可以解决上述问题。
     
    我们在使用BigDecimal时,使用它的BigDecimal(String)构造器创建对象才有意义。其他的如BigDecimal b = new BigDecimal(1)这种,还是会发生精度丢失的问题 BigDecimal a = new BigDecimal(1.01);
     BigDecimal b = new BigDecimal(1.02);
     BigDecimal c = new BigDecimal("1.01");
     BigDecimal d = new BigDecimal("1.02");
     
    System.out.println(a.add(b)); // a和b是用float构造的,也有精度损失
    System.out.println(c.add(d)); // c和d是用String 类型构造的,没有精度损失
    2.0300000000000000266453525910037569701671600341796875 2.03
    long可以准确存储19位数字,而double只能准备存储16位数字。double由于有exp位(次方),可以存16位以上的数字,但是需要以低位的不精确作为代价。如果需要高于19位数字的精确存储,则必须用BigInteger来保存,当然会牺牲一些性能。所以我们一般使用BigDecimal来解决商业运算上丢失精度的问题的时候,声明BigDecimal对象的时候一定要使用它构造参数为String的类型的构造器。
      float和double只能用来做科学计算和工程计算。商业运算中我们要使用BigDecimal
         
    用BigDecimal value = BigDecimal.valueOf(0.1D)代替BigDecimal value = new BigDecimal(0.1D);
    在实际中,DB中存储的一般是float和doubel类型的,需要转换成为String再用,所以这里从网上抄袭了一个工具类,仅供参考:
    /**
     * @author: Ji YongGuang.
     * @date: 19:50 2017/12/14.
     */
    public class BigDecimalUtil {
    
        private BigDecimalUtil() {
    
        }
    
        public static BigDecimal add(double v1, double v2) {// v1 + v2
            BigDecimal b1 = new BigDecimal(Double.toString(v1));
            BigDecimal b2 = new BigDecimal(Double.toString(v2));
            return b1.add(b2);
        }
    
        public static BigDecimal sub(double v1, double v2) {
            BigDecimal b1 = new BigDecimal(Double.toString(v1));
            BigDecimal b2 = new BigDecimal(Double.toString(v2));
            return b1.subtract(b2);
        }
    
        public static BigDecimal mul(double v1, double v2) {
            BigDecimal b1 = new BigDecimal(Double.toString(v1));
            BigDecimal b2 = new BigDecimal(Double.toString(v2));
            return b1.multiply(b2);
        }
    
        public static BigDecimal div(double v1, double v2) {
            BigDecimal b1 = new BigDecimal(Double.toString(v1));
            BigDecimal b2 = new BigDecimal(Double.toString(v2));
            // 2 = 保留小数点后两位   ROUND_HALF_UP = 四舍五入
            return b1.divide(b2, 2, BigDecimal.ROUND_HALF_UP);// 应对除不尽的情况
        }
    }

    5 过时代码添加 @Deprecated 注解

    不要用NullPointerException判断空

      空指针异常应该用代码规避(比如检测不为空),而不是用捕获异常的方式处理。

    public String getUserName(User user) {
        if (Objects.isNull(user)) {
            return null;
        }
        return user.getName();
    }

        在JDK7版本的时候,Java引入了java.util.Objects工具类,用于封装一些平时使用频度很高或容易出错的操作,这些操作形成了Objects的各个方法,下来我们来看看这些方法。

    equals()

    public static boolean equals(Object a, Object b) {
        return (a == b) || (a != null && a.equals(b));
    }

    有别于Object.equals()这个方法可以避免空指针异常。

     deepEquals()

    Object.equals()用于比较两个对象的引用是否相同,而deepEquals()却扩展成了可以支持数组

    toString()

    归根结底,其内部最终调用了对象的toString()方法。额外多了空指针判断

    compare()

    用于比较两个对象。

    requireNonNull()

    在对象为空指针时,抛出特定message的空指针异常

    isNull() 和 nonNull()

    这两个方法用于判断对象为null和对象不为null。通常情况下,我们不会直接使用这两个方法,而是使用比较操作符==!=。这两个方法主要用在jdk8开始支持的流计算里面。

    7 公有静态常量应该通过类访问

      而不是用实例去访问,因为容易让人误解,以为每一个实例有一个静态常量。

    8 用 catch 语句捕获异常后,什么也不进行处理,就让异常重新抛出,这跟不捕获异常的效果一样(因为如果异常不捕获,就会一直往上层函数抛),可以删除这块代码或添加别的处理。

    工具类应该屏蔽构造函数

       工具类是一堆静态字段和函数的集合,不应该被实例化。但是,Java 为每个没有明确定义构造函数的类添加了一个隐式公有构造函数。所以,为了避免 java "小白"使用有误,应该显式定义私有构造函数来屏蔽这个隐式公有构造函数。

    10 建议使用 try-with-resources 语句更优雅的关闭资源

    传统的关闭资源的方式:在finally中判断,如果资源句柄不是NULL,关闭,而且还有再catch exception

    private void handle(String fileName) {
        BufferedReader reader = null;
        try {
            String line;
            reader = new BufferedReader(new FileReader(fileName));
            while ((line = reader.readLine()) != null) {
                ...
            }
        } catch (Exception e) {
            ...
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    ...
                }
            }
        }
    }

    java 1.7后,如果关闭的资源实现了AutoCloseable接口,就可以使用更简洁的try-with-resources 

       public interface Closeable extends AutoCloseable
    private void handle(String fileName) {
        try (BufferedReader reader = new BufferedReader(new FileReader(fileName))) {
            String line;
            while ((line = reader.readLine()) != null) {
                ...
            }
        } catch (Exception e) {
            ...
        }
    }

    11 频繁调用 Collection.contains 方法请使用 Set代替List,效率更高。

    12 List 的随机访问

      大家都知道数组和链表的区别:数组的随机访问效率更高。当调用方法获取到 List 后,如果想随机访问其中的数据,并不知道该数组内部实现是链表还是数组,怎么办呢?可以判断它是否实现* RandomAccess *接口。

    13 字符串拼接使用 StringBuilder  

       String: 拼接字符串时候会产生很多无用的中间对象,如果频繁的进行这样的操作对性能有影响。     

       StringBuilder: 一般的字符串拼接在编译期 java 会进行优化,但是在循环中字符串拼接, java 编译期无法做到优化,所以需要使用 StringBuilder 进行替换。性能好,但是线程不安全。

             StringBuffer: 提供append和add方法,可以将字符串添加到已有序列的末尾或指定位置,它的本质是一个线程安全的可修改的字符序列,把所有修改数据的方法都加上了synchronized。但是保证了线程安全是需要性能的代价的

    14 集合初始化尽量指定大小

      集合也是有大小限制的。每次扩容的时间复杂度很有可能是 O(n) ,所以尽量指定可预知的集合大小,能减少集合的扩容次数。

    15 需要 Map 的主键和取值时,应该迭代 entrySet()

      当循环中只需要 Map 的主键时,迭代 keySet() 是正确的。但是,当需要主键和取值时,迭代 entrySet() 才是更高效的做法,比先迭代 keySet() 后再去 get 取值性能更佳。(经过测试,后一种比前一种是快)

           for (String key : map.keySet()) {
                String value = map.get(key);
            }
            
           
            for (Map.Entry<String, String> entry : map.entrySet()) {
                String key = entry.getKey();
                String value = entry.getValue();
            }
  • 相关阅读:
    图形化代码阅读工具——Scitools Understand
    cocos studio UI 1.6.0.0 修改导出项目路径
    cocos2dx 中文路径编译错误记录
    利用特性区分查找方法,并通过反射调用方法
    WPF MVVM学习(二)
    wpf 控件模板、面板模板、数据模板
    blend 自定义控件
    wpf dataGrid样式
    blend 使用模板的几点说明
    WPF MVVM学习
  • 原文地址:https://www.cnblogs.com/liufei1983/p/11782304.html
Copyright © 2011-2022 走看看