阅读手册后虽然知道手册上说的比较好, 但是其中原理还是想琢磨清楚, 所以整理了以下一些点, 供解惑

serialVersionUID

【强制】序列化类新增属性时,请不要修改 serialVersionUID 字段,避免反序列失败;如果 完全不兼容升级,避免反序列化混乱,那么请修改 serialVersionUID 值。 说明:注意 serialVersionUID 不一致会抛出序列化运行时异常。

所以然:

为什么阿里巴巴要求程序员谨慎修改serialVersionUID 字段的值

修复推荐:

依据所以然

包装类对象(Integer、String…)之间值的 == 判断

【强制】所有整型包装类对象之间值的比较,全部使用 equals 方法比较。 说明:对于 Integer var = ? 在-128 至 127 之间的赋值,Integer 对象是在 IntegerCache.cache 产生, 会复用已有对象,这个区间内的 Integer 值可以直接使用==进行判断,但是这个区间之外的所有数据,都 会在堆上产生,并不会复用已有对象,这是一个大坑,推荐使用 equals 方法进行判断。

所以然:

基础知识回顾

判断引用类型相等

在Java中,判断值类型的变量是否相等,可以使用==运算符。但是,判断引用类型的变量是否相等,==表示“引用是否相等”,或者说,是否指向同一个对象。例如,下面的两个String类型,它们的内容是相同的,但是,分别指向不同的对象,用==判断,结果为false

触发问题场景:

public static void main(String[] args) {
    Integer testA = 300;
    Integer testB = 300;
    System.out.println(testA == testB);
}

触发问题场景微调修复:

public static void main(String[] args) {
    Integer testA = 300;
    Integer testB = 300;
    System.out.println(testA.intValue() == testB.intValue());
}

触发问题场景修复思路:

来源于equals方法

public boolean equals(Object obj) {
    if (obj instanceof Integer) {
        return value == ((Integer)obj).intValue();
    }
    return false;
}

修复推荐:

还是继续使用equals方法方便快捷

DO 类时,属性类型要与数据库字段类型相匹配。

【强制】定义数据对象 DO 类时,属性类型要与数据库字段类型相匹配。

正例:数据库字段的 bigint 必须与类属性的 Long 类型相对应。

反例:某个案例的数据库表 id 字段定义类型 bigint unsigned,实际类对象属性为 Integer,随着 id 越来 越大,超过 Integer 的表示范围而溢出成为负数。

所以然:

java中int的取值范围为-2147483648到+2147483648

mysql中int、bigint、smallint 和 tinyint的区别详细介绍

修复推荐:

依据正例

POJO 类必须写 toString 方法

【强制】POJO 类必须写 toString 方法。使用 IDE 中的工具:source> generate toString 时,如果继承了另一个 POJO 类,注意在前面加一下 super.toString。 说明:在方法执行抛出异常时,可以直接调用 POJO 的 toString()方法打印其属性值,便于排查问题

所以然:

普通toString

java.lang.Object@7852e922

重写toString

toStringTest [name=zout, sex=man, No=1]

重写后直接打印或者使用log时候会显示其中值而不是内存地址

java实体类为什么要写.toString()方法?

修复推荐:

使用Lombok中的@ToString或使用@Data

Lombok基本使用

慎用 Object 的 clone 方法来拷贝对象。

【推荐】慎用 Object 的 clone 方法来拷贝对象。

说明:对象 clone 方法默认是浅拷贝,若想实现深拷贝,需覆写 clone 方法实现域对象的深度遍历式拷贝。

所以然

为什么阿里Java手册推荐慎用 Object 的 clone 方法来拷贝对象

修复推荐

依据所以然

hashCode 和 equals 的处理

【强制】关于 hashCode 和 equals 的处理,遵循如下规则:

1) 只要覆写 equals,就必须覆写 hashCode。

2) 因为 Set 存储的是不重复的对象,依据 hashCode 和 equals 进行判断,所以 Set 存储的对象必须覆写 这两种方法。

3) 如果自定义对象作为 Map 的键,那么必须覆写 hashCode 和 equals。 说明:String 因为覆写了 hashCode 和 equals 方法,所以可以愉快地将 String 对象作为 key 来使用。

所以然:

为什么重写equals必须重写hashCode

Java的Object.hashCode()的返回值到底是不是对象内存地址?

修复推荐

依据我平时的代码测试,如果实现了Lombok中的@EqualsAndHashCode则会重写hashCode以及equals, 对象中的值变动hashCode也会跟随变动, 如果使用并且场景有限, 可安稳使用.

在引用对象中也会重写(Integer,String)这两个方法,且可用, 所以也不用担心触发对比上的问题

其他场景还需要遵循

判断所有集合内部的元素是否为空,使用 isEmpty()方法

【强制】判断所有集合内部的元素是否为空,使用 isEmpty()方法,而不是 size()==0 的方式。 说明:在某些集合中,前者的时间复杂度为 O(1),而且可读性更好。 正例: Map map = new HashMap<>(16); if(map.isEmpty()) { System.out.println("no element in this map."); }

所以然:

ConcurrentLinkedQueue.java这个集合的size()时间复杂度就不是o(1)

/**
 * Returns {@code true} if this queue contains no elements.
 *
 * @return {@code true} if this queue contains no elements
 */
public boolean isEmpty() {
    return first() == null;
}

/**
 * Returns the number of elements in this queue.  If this queue
 * contains more than {@code Integer.MAX_VALUE} elements, returns
 * {@code Integer.MAX_VALUE}.
 *
 * <p>Beware that, unlike in most collections, this method is
 * <em>NOT</em> a constant-time operation. Because of the
 * asynchronous nature of these queues, determining the current
 * number of elements requires an O(n) traversal.
 * Additionally, if elements are added or removed during execution
 * of this method, the returned result may be inaccurate.  Thus,
 * this method is typically not very useful in concurrent
 * applications.
 *
 * @return the number of elements in this queue
 */
public int size() {
    restartFromHead: for (;;) {
        int count = 0;
        for (ConcurrentLinkedQueue.Node<E> p = first(); p != null;) {
            if (p.item != null)
                if (++count == Integer.MAX_VALUE)
                    break;  // @see Collection.size()
            if (p == (p = p.next))
                continue restartFromHead;
        }
        return count;
    }
}

而一般则为:

public int size() {
	return size;
}

public boolean isEmpty() {
	return size == 0;
}

修复推荐

统一使用 isEmpty()方法,而不是 size()==0 的方式

toMap()方法转为 Map 集合时,一定要注 意当 value 为 null 时会抛 NPE 异常

/*【强制】在使用 java.util.stream.Collectors 类的 toMap()方法转为 Map 集合时,一定要注
意当 value 为 null 时会抛 NPE 异常。
说明:在 java.util.HashMap 的 merge 方法里会进行如下的判断:*/
if (value == null || remappingFunction == null)
throw new NullPointerException();

所以然:

public static void main(String[] args) {
    List<Pair<String, Double>> pairArrayList = new ArrayList<>(3);
    pairArrayList.add(new Pair<>("version", 12.10));
    pairArrayList.add(new Pair<>("version", 12.19));
    pairArrayList.add(new Pair<>("version", null));
    Map<String, Double> map = pairArrayList.stream().collect(Collectors.toMap(Pair::getKey, Pair::getValue, (v1, v2) -> v2));
    System.out.println(map);
}

以上运行会报错

//java.util.HashMap#merge
@Override
    public V merge(K key, V value,
                   BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
        if (value == null)
            throw new NullPointerException();//null报错来源util
        if (remappingFunction == null)
            throw new NullPointerException();
        int hash = hash(key);
        Node<K,V>[] tab; Node<K,V> first; int n, i;
        int binCount = 0;
        TreeNode<K,V> t = null;
        Node<K,V> old = null;
        ...

修复推荐

确保value没有null

ArrayList 的 subList 结果不可强转成 ArrayList

【强制】ArrayList 的 subList 结果不可强转成 ArrayList,否则会抛出 ClassCastException 异 常:java.util.RandomAccessSubList cannot be cast to java.util.ArrayList。

所以然

subList()返回的是 ArrayList 的内部类 SubList,并不是 ArrayList 本身,而是 ArrayList 的一个视 图,对于 SubList 的所有操作最终会反映到原列表上。

修复推荐

ArrayList分为深拷贝和浅拷贝:https://www.cnblogs.com/luxd/p/11933686.html

另外延伸出来对象的拷贝:https://www.cnblogs.com/qian123/p/5710533.html

测试代码:

public static void main(String[] args) {

        class UserTest implements Serializable,Cloneable {

            String id;
            int age;

            public UserTest(String id, int age) {
                this.id = id;
                this.age = age;
            }

            public UserTest() {
            }

            public String getId() {
                return id;
            }

            public void setId(String id) {
                this.id = id;
            }

            public int getAge() {
                return age;
            }

            public void setAge(int age) {
                this.age = age;
            }

            @Override
            public String toString() {
                return "UserTest{" +
                        "id='" + id + '\'' +
                        ", age=" + age +
                        '}';
            }

            @Override
            public UserTest clone(){
                UserTest clone = null;
                try {
                    clone = (UserTest) super.clone();
                } catch (CloneNotSupportedException e) {
                    e.printStackTrace();
                }

                return clone;
            }
        }

        UserTest user1 = new UserTest("小明", 18);
        UserTest user2 = new UserTest("小红", 16);
        List<UserTest> list = new ArrayList<>();
        list.add(user1);
        list.add(user2);
        System.out.println("原List:" + list);

        // 进行深度复制
        List<UserTest> listNew = new ArrayList<>();
        for (int i = 0; i < list.size(); i += 1) {
            listNew.add((UserTest) list.get(i).clone());
        }
//
//        List<UserTest> listNew = null;
//        try {
//            listNew = deepCopy(list);
//        } catch (IOException e) {
//            e.printStackTrace();
//        } catch (ClassNotFoundException e) {
//            e.printStackTrace();
//        }
//
        System.out.println("对新list进行操作");
        for (UserTest userTest : listNew) {
            userTest.setAge(99);
        }

        System.out.println("原list" + list);
        System.out.println("新list" + listNew);
    }

    //关键代码 运行序列化和反序列化  进行深度拷贝
    public static <T> List<T> deepCopy(List<T> src) throws IOException, ClassNotFoundException {
        ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
        ObjectOutputStream out = new ObjectOutputStream(byteOut);
        try {
            out.writeObject(src);
        } catch (IOException e) {
            e.printStackTrace();
        }

        ByteArrayInputStream byteIn = new ByteArrayInputStream(byteOut.toByteArray());
        ObjectInputStream in = new ObjectInputStream(byteIn);
        @SuppressWarnings("unchecked")
        List<T> dest = (List<T>) in.readObject();
        return dest;
    }

使用工具类 Arrays.asList()把数组转换成集合时,不能使用其修改集合相关的方法, 它的 add/remove/clear 方法会抛出异常

【强制】使用工具类 Arrays.asList()把数组转换成集合时,不能使用其修改集合相关的方法, 它的 add/remove/clear 方法会抛出 UnsupportedOperationException 异常。 说明:asList 的返回对象是一个 Arrays 内部类,并没有实现集合的修改方法。Arrays.asList 体现的是适配 器模式,只是转换接口,后台的数据仍是数组。 String[] str = new String[] { "chen", "yang", "hao" }; List list = Arrays.asList(str); 第一种情况:list.add("yangguanbao"); 运行时异常。 第二种情况:str[0] = "change"; 也会随之修改,反之亦然。

所以然:

// 正例
if(CollectionUtil.isEmpty(this.warehouseWarrantDocs)) {
    List<WarehouseWarrantDoc> warehouseWarrantDocs = new ArrayList<>();
    warehouseWarrantDocs.add(warehouseWarrantDoc);
    this.warehouseWarrantDocs = warehouseWarrantDocs;
} else {
    this.warehouseWarrantDocs.add(warehouseWarrantDoc);
}

//反例1
if(CollectionUtil.isEmpty(this.warehouseWarrantDocs)) {
    this.warehouseWarrantDocs = Arrays.asList(warehouseWarrantDoc);
} else {
    this.warehouseWarrantDocs.add(warehouseWarrantDoc);
}

//反例2
if(CollectionUtil.isEmpty(this.warehouseWarrantDocs)) {
    this.warehouseWarrantDocs = Collections.singletonList(warehouseWarrantDoc);
} else {
    this.warehouseWarrantDocs.add(warehouseWarrantDoc);
}

反例会在接下来的add时候抛异常, 正例则不会

修复推荐

根据使用场景来选择

不要在foreach循环里进行元素的remove/add操作。remove元素请使用Iterator方式,如果并发操作,需要对Iterator对象加锁。

【强制】不要在foreach循环里进行元素的remove/add操作。remove元素请使用Iterator方式,如果并发操作,需要对Iterator对象加锁。正例:Listlist =newArrayList<>();list.add("1");list.add("2");Iteratoriterator =list.iterator();while(iterator.hasNext()){String item =iterator.next();if(删除元素的条件){iterator.remove();}}反例:for(String item :list){if("1".equals(item)){list.remove(item);}}

所以然

https://blog.csdn.net/qq_36827957/article/details/88415168

简单总结一下,之所以会抛出ConcurrentModificationException异常,是因为我们的代码中使用了增强for循环,而在增强for循环中,集合遍历是通过iterator进行的,但是元素的add/remove却是直接使用的集合类自己的方法。这就导致iterator在遍历的时候,会发现有一个元素在自己不知不觉的情况下就被删除/添加了,就会抛出一个异常,用来提示用户,可能发生了并发修改。

修复推荐

根据所以然

SimpleDateFormat是线程不安全的类,一般不要定义为static变量,如果定义为static,必须加锁,或者使用DateUtils工具类。

所以然

https://blog.csdn.net/zxh87/article/details/19414885

修复推荐

根据所以然

 这也同时提醒我们在开发和设计系统的时候注意下一下三点:

 1.自己写公用类的时候,要对多线程调用情况下的后果在注释里进行明确说明

 2.对线程环境下,对每一个共享的可变变量都要注意其线程安全性

 3.我们的类和方法在做设计的时候,要尽量设计成无状态的

【参考】HashMap在容量不够进行resize时由于高并发可能出现死链,导致CPU飙升,在开发过程中注意规避此风险。

【参考】HashMap在容量不够进行resize时由于高并发可能出现死链,导致CPU飙升,在开发过程中注意规避此风险。

所以然

//todo 仔细研读

https://juejin.cn/post/6844903554264596487

修复推荐

根据所以然

所以在并发的情况,发生扩容时,可能会产生循环链表,在执行get的时候,会触发死循环,引起CPU的100%问题,所以一定要避免在并发环境下使用HashMap。

曾经有人把这个问题报给了Sun,不过Sun不认为这是一个bug,因为在HashMap本来就不支持多线程使用,要并发就用ConcurrentHashmap。

作者:占小狼 链接:https://juejin.cn/post/6844903554264596487 来源:稀土掘金

服务器内部重定向必须使用forward;外部重定向地址必须使用URL统一代理模块生成

【强制】服务器内部重定向必须使用forward;外部重定向地址必须使用URL统一代理模块生成,否则会因线上采用HTTPS协议而导致浏览器提示“不安全”,并且还会带来URL维护不一致的问题。

所以然

理解跳转:https://www.liaoxuefeng.com/wiki/1252599548343744/1328761739935778

修复推荐

根据场景修复

在使用正则表达式时,利用好其预编译功能,可以有效加快正则匹配速度

在使用正则表达式时,利用好其预编译功能,可以有效加快正则匹配速度。说明:不要在方法体内定义:Pattern pattern = Pattern.compile(“规则”);

所以然

java预编译功能可以有效加快正则匹配速度

修复推荐

https://blog.csdn.net/qq_20051535/article/details/113572624

private static final Pattern pattern = Pattern.compile(regexRule);
 
private void func(...) {
    Matcher m = pattern.matcher(content);
    if (m.matches()) {
        ...
    }
}

避免用Apache Beanutils进行属性的copy

避免用Apache Beanutils进行属性的copy。说明:Apache BeanUtils性能较差,可以使用其他方案比如Spring BeanUtils, Cglib BeanCopier,注意均是浅拷贝。

所以然

分析: https://segmentfault.com/a/1190000019356477

修复推荐

根据所以然

如果JDK7及以上,可以使用try-with-resources方式对资源对象、流对象进行关闭

【强制】finally块必须对资源对象、流对象进行关闭,有异常也要做try-catch。说明:如果JDK7及以上,可以使用try-with-resources方式。

所以然

理解try-with-resources语句及示例: https://blog.csdn.net/frgod/article/details/53414813

修复推荐

根据所以然理解并使用try-with-resources

不要在finally块中使用return。

【强制】不要在finally块中使用return。

说明:try块中的return语句执行成功后,并不马上返回,而是继续执行finally块中的语句,如果此处存在return语句,则在此直接返回,无情丢弃掉try块中的返回点。

所以然

java异常体系---不要在finally块中使用return、throw

当在finally块中使用return、throw时,编译器不会再对try、catch块中的非运行时异常进行检查,JVM不会再去捕获try块、catch块中的异常,程序的输出以finally块为准,即finally块的返回值或者finally块中抛出的异常。

当在try块或catch块中遇到return语句时,finally块将在方法返回之前被执行。finally块中的return语句会覆盖try块、catch块中的return语句。合理的做法是在 finally 块之后使用return语句。

修复推荐

根据所以然和规范

避免重复打印日志,浪费磁盘空间,务必在日志配置文件中设置additivity=false

【强制】避免重复打印日志,浪费磁盘空间,务必在日志配置文件中设置additivity=false。正例:

所以然

测试代码

    public static void main(String[] args) {
        try {
            int a;
            a = 1;
            int b = a / 0;
        } catch (Exception e) {
            for (int i = 0; i < 10000; i++) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException ex) {
                    ex.printStackTrace();
                }
                System.out.println(e.getMessage() + getRandomString(10000));
                e.printStackTrace();
            }

        }
    }
    public static String getRandomString(int length){
        String str="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
        Random random=new Random();
        StringBuffer sb=new StringBuffer();
        for(int i=0;i<length;i++){
            int number=random.nextInt(62);
            sb.append(str.charAt(number));
        }
        return sb.toString();
    }

Java中打印日志,这4点很重要!

additivity属性简介:

它是子Logger是否继承父Logger的输出源(appender)的标志位,默认情况下子Logger会继承父Logger的appender,也就是说子Logger会在父Logger的appender里输出。把additivity设为false,则子Logger只会在自己的appender里输出,而不会在父Logger的appender里输出。

修复推荐

根据所以然和规范

Java代码用正则来验证客户端的输入,有些正则写法验证普通用户输入没有问题,但是如果攻击人员使用的是特殊构造的字符串来验证,有可能导致死循环的结果。

【强制】用户请求传入的任何参数必须做有效性验证。说明:忽略参数校验可能导致:⚫page size过大导致内存溢出⚫恶意order by导致数据库慢查询⚫缓存击穿⚫SSRF⚫任意重定向⚫SQL注入,Shell注入,反序列化注入⚫正则输入源串拒绝服务ReDoSJava代码用正则来验证客户端的输入,有些正则写法验证普通用户输入没有问题,但是如果攻击人员使用的是特殊构造的字符串来验证,有可能导致死循环的结果。

所以然

主要讨论正则回溯计算导致的cpu占用问题: https://www.cnblogs.com/Eleven-Liu/p/10826488.html

修复推荐

根据所以然的理解改进正则

如果有order by的场景,请注意利用索引的有序性。order by 最后的字段是组合索引的一部分,并且放在索引组合顺序的最后,避免出现file_sort的情况,影响查询性能

如果有order by的场景,请注意利用索引的有序性。order by 最后的字段是组合索引的一部分,并且放在索引组合顺序的最后,避免出现file_sort的情况,影响查询性能。正例:where a=? and b=? order by c; 索引:a_b_c反例:索引如果存在范围查询,那么索引有序性无法利用,如:WHERE a>10 ORDER BY b; 索引a_b无法排序。

所以然

根据手册

ps: MySQL如何利用索引优化ORDER BY排序语句

修复推荐

根据所以然的理解改进

利用覆盖索引来进行查询操作,避免回表

利用覆盖索引来进行查询操作,避免回表。说明:如果一本书需要知道第11章是什么标题,会翻开第11章对应的那一页吗?目录浏览一下就好,这个目录就是起到覆盖索引的作用。正例:能够建立索引的种类分为主键索引、唯一索引、普通索引三种,而覆盖索引只是一种查询的一种效果,用explain的结果,extra列会出现:using index。

所以然

根据实际情况和相关教程改为覆盖索引如何避免回表查询?什么是索引覆盖? | 1分钟MySQL优化系列

修复推荐

根据所以然的理解改进

所有pom文件中的依赖声明放在语句块中,所有版本仲裁放在语句块中。

【推荐】所有pom文件中的依赖声明放在语句块中,所有版本仲裁放在语句块中。说明:里只是声明版本,并不实现引入,因此子项目需要显式的声明依赖,version和scope都读取自父pom。而所有声明在主pom的里的依赖都会自动引入,并默认被所有的子项目继承。

所以然

dependencyManagement使用简介

参考:https://blog.csdn.net/weixin_42114097/article/details/81391024

dependencyManagement 意义及与dependencies的区别

参考:https://www.cnblogs.com/zhangmingcheng/p/10984036.html

修复推荐

根据所以然的理解改进

高并发服务器建议调小TCP协议的time_wait超时时间。

【推荐】高并发服务器建议调小TCP协议的time_wait超时时间。说明:操作系统默认240秒后,才会关闭处于time_wait状态的连接,在高并发访问下,服务器端会因为处于time_wait的连接数太多,可能无法建立新的连接,所以需要在服务器上调小此等待值。

所以然

解决TIME_WAIT过多造成的问题

理解TIME_WAIT,彻底弄清解决TCP: time wait bucket table overflow

解读TIME_WAIT--你在网上看到的大多数帖子可能都是错误的

修复推荐

根据所以然的理解改进

在线上生产环境,JVM的Xms和Xmx设置一样大小的内存容量,避免在GC后调整堆大小带来的压力。

【推荐】高并发服务器建议调小TCP协议的time_wait超时时间。说明:操作系统默认240秒后,才会关闭处于time_wait状态的连接,在高并发访问下,服务器端会因为处于time_wait的连接数太多,可能无法建立新的连接,所以需要在服务器上调小此等待值。

所以然

https://blog.csdn.net/qq_34556414/article/details/112648113

面对上面的问题,为了避免在生产环境由于heap内存扩大或缩小导致应用停顿,降低延迟,同时避免每次垃圾回收完成后JVM重新分配内存。所以,-Xmx和-Xms一般都是设置相等的。

当然,如果生产系统上线前有一段预热时间的话,也可以不设置相等。对于需要高吞吐量的应用来说,可以不在乎这种停顿,比如一些后台的应用之类,那么内存可以适当调大一些。(停顿时间越长,吞吐量反而越大),需要根据具体情况权衡。

注意事项


其实虽然设置为相同值有很多好处,但也会有一些不足。比如,如果两个值一样,会减少GC的操作,也意味着只有当JVM即将使用完时才会进行回收,此前内存会不停的增长。

并且同一JDK的GC策略也有很多种,不能一概而论。另外,对于Hotspot虚拟机,Xms和Xmx设置为一样的,可以减轻伸缩堆大小带来的压力。但对于IBM虚拟机,设置为一样会增大堆碎片产生的几率,并且这种负面影响足以抵消前者产生的益处。

修复推荐

根据所以然的理解改进

设计规约

多做设计, 理清思路, 方便你我他