zoukankan      html  css  js  c++  java
  • Java开发常犯的错误及基本知识(持续更新)

    为什么lambda表达式无法声明throws异常,而必须要在内部捕获?

    实际上这是跟重写的方法有关,比如重写Runnable的run方法,就必须在内部捕获异常:

    Runnable runnable =
            () -> {
              synchronized (objectWaitMainClass) {
                try {
                  objectWaitMainClass.wait();
                } catch (InterruptedException e) {
                  e.printStackTrace();
                }
              }
            };

    因为Runnablerun方法没有声明任何异常,所以实现了该方法的方法就无法声明任何异常,只能内部捕获

    下面自定义一个接口,接口方法中声明了异常

    @FunctionalInterface
      public interface ThrowException {
    
        /**
         * 可能会抛出异常
         *
         * @throws Exception
         */
        public void throwException() throws Exception;
      }

    用lambda表达式重写:

    ThrowException throwException =
            () -> {
              synchronized (objectWaitMainClass) {
                objectWaitMainClass.wait();
              }
            };

    就不用lambda内部捕获,基类接口中已经声明了该异常

    整个测试类:

    /**
     *
     *
     * <ul>
     *   <li>由于基类中的方法没有声明异常比如Runnable#run方法,所以重写该run方法就不能声明异常,比如要内部捕获;
     *   <li>如果基类中方法声明了异常,那么重写该方法就可以不再内部捕获,比如本类中的内部接口中的方法{@link ThrowException#throwException()}
     * </ul>
     *
     * @see ThrowException#throwException()
     * @see Runnable#run()
     * @author rhyme
     * @date 2020/5/30 12:02
     */
    public class LambdaExceptionMain {
      public static void main(String[] args) throws InterruptedException {
    
        Class<LambdaExceptionMain> objectWaitMainClass = LambdaExceptionMain.class;
        Runnable runnable =
            () -> {
              synchronized (objectWaitMainClass) {
                try {
                  objectWaitMainClass.wait();
                } catch (InterruptedException e) {
                  e.printStackTrace();
                }
              }
            };
    
        ThrowException throwException =
            () -> {
              synchronized (objectWaitMainClass) {
                objectWaitMainClass.wait();
              }
            };
      }
    
      /**
       * 自定义一个函数式接口,方法中声明了异常
       */
      @FunctionalInterface
      public interface ThrowException {
    
        /**
         * 可能会<b>抛出异常</b>的函数式接口中的方法
         *
         * @throws Exception
         */
        public void throwException() throws Exception;
      }

    CRLF,CR,LF的区别和联系

    windows系统文件格式默认换行"是 ",即CRLF;

    UNIX/Linux系统文件格式默认换行是" ",即LF;

    MacOS系统文件格式默认换行由" ",即CR。

    所以比如在windows上,用Notepad++打开一个文件,想要匹配里面的换行,就要用" "匹配。

    数据库字段最好不要用基本类型

    比如 一个字段设定为@Column(name = "PRIORITY", nullable = false) int priority;

    虽然有注解检查该字段不能为null,但是如果不给该字段传值,该字段也不为null,因为它是基本类型int,默认值是0,就以0作为值赋给了字段priority,与期望不符合;

    所以应该@Column(name = "PRIORITY", nullable = false) Integer priority;

    这样如果该字段为null,插入到数据库时就会报该字段不能为null的错误!

    自定义异常

    领域模型命名规约

    建议使用 try-with-resources 语句

    https://github.com/RhymeXY/java-basic-demos/blob/master/common/src/main/java/com/xy/java/basic/demos/utils/InputFromSystemInUtil.java
    Java 7 中引入了 try-with-resources 语句,该语句能保证将相关资源关闭,优于原来的 try-catch-finally 语句,并且使程序代码更安全更简洁。
    反例

    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) {
                    ...
                }
            }
        }
    }
    
    

    正例:

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

    spring boot 多模块开发时,依赖注入其他模块的bean失败

    比如A模块依赖B模块,A需要自动注入B模块的一个Bean,启动时找不到B模块的Bean依赖,极大的可能是A模块启动类所在的包和B模块所在的包名不一样,所以扫描不到B模块,在A模块的启动类加上@ComponentScan注解扫描B模块的包,如果需要B模块的Bean需要配置文件,A模块还需要配置。

    被final修饰的变量

    被final修饰的变量只能初始化一次,所以它的引用不能改变,引用的对象实例可以改变!

    public class FinalObjectTest {
    
        @Test
        public void test(){
            A a = new A(1);
            A temp = new A(9);
            final A finalA = a;
            a.setValue(2);
            System.out.println(finalA.value); // 2
            System.out.println(a.value); // 2
            // finalA = temp; 会报错
            // 被final修饰的变量只能初始化一次,所以它的引用不能改变,引用的对象实例可以改变!
    
        }
    
        @Data
        @AllArgsConstructor
        private class A {
            private int value;
        }
    }

    Java interface

    Java的接口interface,无父类,不是Object类的子类。

    在Java里用正则表达式,注意性能

    private static final Pattern pattern = Pattern.compile("^[A-Za-z\u4e00-\u9fa5][A-Za-z\u4e00-\u9fa50-9_\-]{0,31}$");

    Pattern.compile底层代码方法会编译一次正则表达式

    Matcher matcher = pattern.matcher(内容)

    pattern对象的matcher方法,如果没有编译,会编译一次,上面Pattern.compile方法如果编译了这里不会编译;

    注意!!!一定要保证一个正则表达式只编译(初始化)一次。

    再比如String类中的replacereplaceAll方法底层也是用的Pattern.compile,每调用一次replace*方法都会编译一次,效率太低了,所以像上面一样定义一个private static final Pattern pattern = Pattern.compile(regex),保证一个正则表达式值初始化一次,再利用matcher匹配字符串调用replace(String replacement)方法。

    利用策略模式和工厂模式优化多个if-else

    多个if-else通常逻辑看起来很复杂,不方便维护,用策略模式和工厂模式优化

    https://www.cnblogs.com/theRhyme/p/10339382.html

    Collection (如List、Set) 的 "All elements are null"的情况

    一个Java集合的Size是1,虽然它!=null && size>0,但是它内容是"All elements are null",相当于它就是空的!

    这时候通过集合类的removeAll方法移除这些值为"All elements are null"的属性:

    tourists.removeAll(Collections.singleton(null));

    https://stackoverflow.com/questions/4819635/how-to-remove-all-null-elements-from-a-arraylist-or-string-array 

    调用Set.addAll()方法时抛UnsupportedOperationException异常

    上面的Set是Map中keySet的返回结果。

    程序中这样两句代码运行时,抛UnsupportedOperationException异常。
    最初感觉很奇怪,Map.keySet()方法返回一个Set呀,Set明明是支持add()、addAll()方法的,怎么会抛“不支持操作”异常呢。
    结果发现,问题不是出在Set上,而是出在Map的keySet()方法上。

    下面是摘自API帮助文档的说明

    Map中的方法public Set keySet()返回此映射中所包含的键的 set 视图。该集合受映射的支持,所以映射的变化也反映在该集合中,反之亦然。该集合支持元素的移除,通过 Iterator.remove、Set.remove、removeAll、retainAll 和 clear 操作,从该映射中移除相应的映射关系。它不支持 add 或 addAll 操作。

    相当于在Map.keySet()得到的集合中插入元素,故此时Set不再支持addAll()方法。

    所以应该new一个Set,再使用addAll:

    Array转ArrayList

    当需要把Array转成ArrayList的时候,开发人员经常这样做:

    List<String> list = Arrays.asList(arr);

    Arrays.asList()会返回一个ArrayList,但是要特别注意,这个ArrayList是Arrays类的静态内部类,并不是java.util.ArrayList类。java.util.Arrays.ArrayList类实现了set(), get(),contains()方法,但是并没有实现增加元素的方法(事实上是可以调用add方法,但是没有具体实现,仅仅抛出UnsupportedOperationException异常),因此它的大小也是固定不变的。为了创建一个真正的java.util.ArrayList,你应该这样做:

    ArrayList<String> arrayList = new ArrayList<String>(Arrays.asList(arr));

    ArrayList的构造方法可以接收一个Collection类型,而java.util.Arrays.ArrayList已经实现了该接口。

    判断一个数组是否包含某个值

    开发人员经常这样做:

    Set<String> set = new HashSet<String>(Arrays.asList(arr));
    return set.contains(targetValue);

    以上代码可以正常工作,但是没有必要将其转换成set集合,将一个List转成Set需要额外的时间,其实我们可以简单的使用如下方法即可:

    Arrays.asList(arr).contains(targetValue);

    或者

    for(String s: arr){
        if(s.equals(targetValue))
            return true;
    }
    return false;

    第一种方法可读性更强。

    RabbitMQ消费者抛异常未捕获控制台日志持续打印问题

    消费者接受消息,进行一系列处理,但是由于某些原因处理过程中该消费者的抛出了异常,并且不捕获(直接 throws IOException 抛出去);

    由于抛出了IOException,那么这条消息就会再次被发送到该队列,消费者就再次收到,而消费者抛出异常,所以就形成了一个死循环(除非不再有类似IO的异常),那么控制台日志就一直打印该消费者的抛出异常。

    解决方法:https://www.cnblogs.com/theRhyme/p/10758249.html

    TimeUnit枚举

    TimeUnit是java.util.concurrent包下面的一个枚举类,TimeUnit提供了可读性更好的线程暂停操作。

    在JDK5之前,一般我们暂停线程是这样写的:

    Thread.sleep(2400000)//可读性差

    可读性相当的差,一眼看去,不知道睡了多久;

    在JDK5之后,我们可以这样写:

     TimeUnit.SECONDS.sleep(4);
     TimeUnit.MINUTES.sleep(4);
     TimeUnit.HOURS.sleep(1);
     TimeUnit.DAYS.sleep(1);

    清晰明了;

    另外,TimeUnit还提供了便捷方法用于把时间转换成不同单位,例如,如果你想把秒转换成毫秒,你可以使用下面代码

    TimeUnit.SECONDS.toMillis(44);// 44,000

    Java访问控制 

    Java中的private、默认、protected、public访问控制。

    这个老是忘记。

    Java代码优化

    https://www.cnblogs.com/xrq730/p/4865416.html

    参考来源:

    https://blog.csdn.net/woyaoxuejavaya/article/details/52472508

    https://www.cnblogs.com/chenpi/p/5508949.html#_label1

    https://www.cnblogs.com/chenpi/p/5614290.html#_label9

    https://www.cnblogs.com/theRhyme/p/10758249.html

    https://zhidao.baidu.com/question/1510955557965485900.html

    https://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&mid=2247485599&idx=1&sn=d83ff4e6b1ee951a0a33508a10980ea3&chksm=cea24754f9d5ce426d18b435a8c373ddc580c06c7d6a45cc51377361729c31c7301f1bbc3b78&token=1328169465&lang=zh_CN&scene=21#wechat_redirect

    https://github.com/alibaba/p3c/blob/master/阿里巴巴Java开发手册(华山版).pdf

    https://stackoverflow.com/questions/10963775/cannot-reference-x-before-supertype-constructor-has-been-called-where-x-is-a

  • 相关阅读:
    【原】泛型委托
    【原】web页面登陆验证
    【原】在一般处理程序中设置session
    16Aspx.com-PHP企业整站源码 景观石材大理石类织梦模板 含手机移动端 完整源码 APP+PC
    16Aspx.com-将15位身份证转换成18位
    16Aspx.com-书通网中小学生免费在线学习网站源码 带采集带手机版帝国cms内核
    16Aspx.com源码2014年7月详细
    Web电子商务网(三层)V2.0源码
    毫秒级百万数据分页存储过程
    C#做的一个加密/解密的类
  • 原文地址:https://www.cnblogs.com/theRhyme/p/10709809.html
Copyright © 2011-2022 走看看