Home 基础能力

0 106

前情概要

最近在一次发版前,代码被安全扫描出有漏洞,定睛一看,原来是fastjson的版本不安全,当时用的好像是1.2.57.sec10,安全推荐的包版本是1.2.83,因此开启了我的第一次依赖版本升级之旅。

头次碰壁

之前也有过升级依赖版本的操作,但都是自己公司的内部包,操作类似于在pom文件里面把1.0.0改成1.0.1这样子就行了;

于是这次我也是这么操作的,全局搜索fastjson的依赖,手动修改 <version>1.2.83<version>;看起来好像所有的fastjson都已经被升级成了1.2.83版本

可是当我查看Maven依赖时,不仅有1.2.83版本,原版本1.2.57.sec10的包也还在

我以为是缓存之类的问题,手动移除1.2.57.sec10的包,之后重新编译,1.2.57.sec10的依赖包居然又死灰复燃导入进来了

找出真相

那么…真相只有一个,在我们的项目里面有地方导入了fastjson – 1.2.57sec10的包,结合安全扫描给出的图,对fastjson不仅有直接引用,也有间接引用

我们全局搜索并手动升级fastjson的版本,并不能改掉所有的,可是至少应该改掉直接引入里面的呀,为什么直接引入、间接引入的都还在呢?

引入依赖优先级介绍

当依赖一个jar包多个版本时,优先使用哪个版本,原则如下:

1.本级优先于上级,上级优先于下级;
2.本级依赖版本优先于管理版本;
3.同一个包中后加载的版本覆盖先加载的版本;
4.上级管理版本和本级管理版本会覆盖下级依赖版本;
5.不同下级jar中依赖了不同版本,优先使用先加载下级jar中的版本;
6.与版本号大小没有关系
7.本级无法使用下级管理版本

总结起来就两条:路径最短,申明顺序其次

 

参考链接:(56条消息) maven中pom依赖相同jar包优先顺序加载版本_lizz666的博客-CSDN博客_mvn parent 相同jar包

Maven依赖传递 (biancheng.net)

0 103

一、字符串转指定格式的Date

开发时遇到一个问题,前端传入时间戳字符串,放进一个JSONObject对象中

后端使用alibaba的注解@JSONField(format = “yyyy-MM-dd HH:mm:ss”)去转换格式,得到的结果不正确

目标对象

这样格式转换倒是没有问题,但是转换后的值不正确

查阅资料得知,时间戳转换成日期也是要区分单位的(秒,毫秒)

资料链接:关于时间戳转换时间总是1970的问题__Cat_的博客-CSDN博客

obj.put("operateTime","1675048249");
@JSONField(format = "yyyy-MM-dd HH:mm:ss")转换的时候使用的单位是毫秒 所以如果要得到正确的时间格式,应该在时间戳字符串后面加上3个0(如果是时间戳的话则*1000) 正确的:obj.put("operateTime","1675048249" + 000);

注:时间戳,格林威治时间1970年01月01日00时00分00秒 (北京时间1970年01月01日08时00分00秒)起至现在的总秒数;
 北京时间,可以理解为Date对象;

0 199

Integer的装箱,拆箱及缓存机制:(54条消息) Integer 的缓存机制_MrYuTing的博客-CSDN博客

当我们了解Integer装拆箱及缓存机制后,再来观察Integer和Int的相等判断

一、Integer 和 Integer对比

  • == 比较的对象(地址)是否相等
  • equals 比较的是值是否相等

说明:

  1. Integer重写了equals方法,用于比较两个Integer对象的value值是否相等;如果没有重写equals方法的话,equals比较的也是对象地址是否相等(与==相同)。
  2. 在对比的两个Integer对象中,如果有对象是new出的,那么用 == 判断一定不相等
  3. 如果对比的两个Integer对象都不是new出的,且值范围在[-128 ~ 127]之间,判由于Integer的缓存机制,两个对象先装箱,指向同一个缓存对象,此时==方法会返回true,equals也返回true;如果值范围不在[-128 ~ 127]之间,将会直接 new Integer();进行新创建一个对象, == 方法会返回false,而equals方法会返回true;

 

总结:Integer对象的相等判断推荐使用 equals方法

二、Integer 和 Int 对比

Integer引用和int对比,和两个 int 判断是否相等是一样的,因为在编译期间编译器会把其中的一个 Integer 自动拆箱(Java 1.5 以后)给转换成一个 int,所以在字节码层面仍然是两个int做比较。

如果是1个Integer和1个int,==和equals是一样的,因为Integer会有一个拆箱的过程。只要这两个值一样,不管是==还是equals都是相等的。

下面是验证代码:

public class Test {
    public static void main(String[] args) {
        // 2个Integer
        Integer i1 = new Integer(12);
        Integer i2 = new Integer(12);
        System.out.println("i1 == i2 ? " + (i1 == i2)); // false
        System.out.println("i1.equals(i2) ? " + i1.equals(i2));  //true

        // 1个Integer  1个int
        Integer i3 = new Integer(13);
        int i4 = 13;
        System.out.println("i3 == i4 ? " + (i3 == i4)); // true
        System.out.println("i3.equals(i4) ? " + i3.equals(i4));  //true
    }
}

为什么Integer的equals是判断的值呢?在Integer.class中可以看到Integer类改写了equals方法,就是判断的值是否存在。代码如下:

/**
     * Compares this object to the specified object.  The result is
     * {@code true} if and only if the argument is not
     * {@code null} and is an {@code Integer} object that
     * contains the same {@code int} value as this object.
     *
     * @param   obj   the object to compare with.
     * @return  {@code true} if the objects are the same;
     *          {@code false} otherwise.
     */
    public boolean equals(Object obj) {
        if (obj instanceof Integer) {
            return value == ((Integer)obj).intValue();
        }
        return false;
    }

注意:Object中的equals方法就是判断的==,所以如果一个对象没有重写equals方法,那么equals和==的结果是一样的。下面是Object中equals的代码:

public boolean equals(Object obj) {
  return (this == obj);
}

补充:Integer 与 Integer的对比

Integer t1 = new Integer(1000);
Integer t2 = new Integer(1000);
System.out.println(t1<=t2);
==结果为false但是t1<=t2结果为true

原因:因为>、<、<=、>=是作用在基本类型上的,而不是对象类型。所以使用>、<、<=、>=的时候,Integer会自动拆箱成int,然后进行值的对比。但是==就是比较的是对象的地址,是说这2个对象在内存中是否占用同一个内存地址。

0 77

为什么子类引用不能指向父类对象 – 腾讯云开发者社区-腾讯云 (tencent.com)

父类引用可以指向子类对象,子类引用不能指向父类对象 – 简书 (jianshu.com)

因为在实例化申请空间时,是由对象大小决定的

[短文速读-3] 实例化子类会实例化父类么? – 简书 (jianshu.com)

会,会先调用父类的构造方法,而构造方法是用于对象的实例化并初始化

应该是实例化并初始化的过程,(我说的是JAVA)如:
Text t=new Text();
其中new 是为对象分配内存空间,Text();是初始化成员变量用的.t是此对象的一个引用.

0 180

(54条消息) 【避坑】FastJson稍微使用不当就会导致StackOverflow_Bolon0708的博客-CSDN博客

(87 封私信 / 80 条消息) json坑点 – 搜索结果 – 知乎 (zhihu.com)

记一次 FastJson 的踩坑经历 – 腾讯云开发者社区-腾讯云 (tencent.com)

fastjson的这些坑,你误入了没? | 码农家园 (codenong.com)

  • 写代码时候还是需要注意点,能稍微规范些,就尽量按照规范,就如本次提到的问题,向 JSONObject 中加入 List<map<string,object> 时,不妨先提前 toJSON 转换一番,这样版本升级也不会有问题。</map<string,object>
  • 由于依赖包升级导致不兼容的情况很常见,不过绝大多数都是向下兼容的,例如 JDK … 5、6、7、8 …,所以如果你正在开发核心代码,若涉及到版本更新,尽量考虑兼容性问题,如果涉及到老功能废弃时,不妨采用注解标注一下,这样后人会尽早发现问题。

在使用三方轮子时,尽可能做对三方的轮子了如之掌,知己知彼方能百战不殆。

知识记录

1.fastjson反序列化时,是能自动下划线转驼峰的。

例如:ReportExceptionDto dto = JSONObject.parseObject(jsonObject.toJSONString(), ReportExceptionDto.class);

参考文章:使用FastJson进行驼峰下划线相互转换写法及误区_氵奄不死的鱼的博客-CSDN博客_fastjson 驼峰转下划线

2.JSONObject.toJSONString()默认忽略值为null的属性

如下图:new出的对象中,如果存在属性的值为null,那么toJSONString生成的字符串不会有该属性;

怎么设置不忽略呢?让值为null的属性,写入空值,如下图

/**  * 过滤规则 - 避免toJSONString过滤空值对应的key  */ private static ValueFilter valueFilter = (o,s,o1) -> o1 == null ? "" : o1;

有人说,防止NullPointerException,是程序员的基本修养。

此篇博文,将介绍空指针的相关知识,一起往下看看吧~

 

小杨是一名初入职场的程序员,他很喜欢写代码。每次产品需要他实现的功能,他都能做出来,可当他信心满满的把代码交给组里前辈CR的时候,前辈总会语重心长的告诉他:

小杨, 你这代码用不了啊,可能会报空指针异常,诺,就是这个

此时,小杨还没意识到事情的严重性,他想:这太正常了,谁写个代码还不会出错呢,修复就行了

public class InterToInt{
    public static void main(String[] args) {
        Person person = new Person();
        person.setName("张三");
        Integer count = person.getCount();
        if (count == 0) {
            System.out.println("该变量未填写count");
        }
        // 装箱操作 int -> Integer
        int i = 5;
        Integer integer = new Integer(i);

        // 拆箱操作:Integer -> int
        int num = integer.intValue();
    }
}

上述代码,究竟为什么会出现空指针异常呢?
经过调试,同时结合报错信息可以判断,问题出在if(count == 0)这条语句上,仔细看,我们并没有对person对象的count属性进行赋值,难道是因为count默认值为null,才报的空指针异常吗?

由上图可知,Integer类型的默认值确实为Null,好,那我们给count赋值,这样的话就不会报空指针了吧

果然,没有报空指针错误了,难道问题到这里就解决了吗?未必
在开发中,有时候对于一个属性,我们不需要给其赋值,它也不需要默认值,它就需要用Null来表示。阿里巴巴开发手册规定所有的属性必须用Integer来定义而非int
借助上例,如果我们从库表里面查询一个person对象xiaohuang,xiaohuang的count属性正好是Null,难道我们能给它随便赋一个值吗?显然不能
那我们怎么解决空指针的问题呢?
public class InterToInt{
    public static void main(String[] args) {
        Person person = new Person();
        person.setName("张三");
        Integer count = person.getCount();
        /**拆箱的时候实际上是调用了intValue()方法。
         * Integer类型和int作比较会自动拆箱,由于Interger类型的对象是null,
         * 这时候自动拆箱调用intValue()方法就会报NullPointerException。
         * 所以基本类型和对应包装类作比较时要判断包装类是否有可能为null
         * 不然就会出现这种错误
         */
        if (count != null && 1 == count) { //改写这一句即可
            System.out.println("count == 0");
        }
        // 装箱操作 int -> Integer
        int i = 5;
        Integer integer = new Integer(i);

        // 拆箱操作:Integer -> int
        int num = integer.intValue();
    }
}

到这里,是不是似懂非懂,若有若无?如果你以为你每次在使用count这样的属性前进行非空判断就能避免所有的空指针的话,那就太天真了,空指针要比这狡猾的多,接下来我们一起去揭开空指针的神奇面纱吧~

一、Java中,什么是指针?

Java中究竟有没有指针?你可以就这个问题好好探索下,此处不作深究,本文中的指针可以理解为指针对象

String str = new String(“str是指针对象”);

如上,str就是一个指针,它被存放在Jvm的栈区;留几个问题给你思考吧~ 1.new String(“str是指针对象”)的空间是什么时候分配的? 2.new String(“str是指针对象”)存储在Jvm的哪一块区域?

 

二、什么是空指针?

当指针不指向任何内存地址时,就叫做空指针,例如:int[] array = null

 

三、什么叫空指针异常?

就是一个指针不指向任何内存地址,但是你还调用他了,例如:

int[] array = null;
System.out.println(array[0]);
这个时候原本array数组是个空指针,没有创建新的对象,在调用这个数组的时候就会产生空指针异常的错误!
程序运行会显示Exception in thread “main” java.lang.NullPointerException的异常提示

四、为什么会产生空指针异常呢?

这里我们用上面举的例子进行说明,int[] array = null在内存中的栈内存中创建了一个叫做array的变量,而堆内存中并没有开辟int类型的数组空间,所以在栈内存中的这个array变量没有存放任何内存地址,由此我们可以理解为什么会产生空指针异常,调用没有的东西显然时不可以的。(栈区和堆区之间存放的东西有什么关系呢?详情可在本博文搜索Jvm、栈、堆等关键词)

 

四、NullPointerException以及其产生的场景

1.产生NullPointerException的场景

Java中定义:在应用程序中尝试使用null时会抛出异常。

其中以下的情况会产生NullPointerException

  1. 调用 null 对象的实例方法。
  2. 访问或修改 null 对象的字段。
  3. 将 null 作为一个数组,获得其长度。
  4. 将 null 作为一个数组,访问或修改其时间片。
  5. 将 null 作为 Throwable 值抛出。

 

2.怎么产生的?

《阿里巴巴开发手册》中提到,

1)返回类型为基本数据类型,return 包装数据类型的对象时,自动拆箱有可能产生 NPE。反例:public int f() { return Integer 对象}, 如果为 null,自动解箱抛 NPE。

2) 数据库的查询结果可能为 null。

3) 集合里的元素即使 isNotEmpty,取出的数据元素也可能为 null。

4) 远程调用返回对象时,一律要求进行空指针判断,防止 NPE。

5) 对于 Session 中获取的数据,建议 NPE 检查,避免空指针。

6) 级联调用 obj.getA().getB().getC();一连串调用,易产生 NPE。正例:使用 JDK8 的 Optional 类来防止 NPE 问题。

 

3.怎么预防NPE?

1) 对象防止,直接!=null

2)集合类判空:一般采用!=null&&判断size(),或者调用isEmpty()方法,或者用Collection工具类判空,java8种Optional类

3)字符串判空:需要判断是否==null&&””.equals(str)来判断,或者StringUtils工具类判断

4)另外项目中要对所有前台参数,对象判空,数据库查询语句判空,JSON对象,JSON数组判空,get()后的值判空

5)对于级联调用 obj.getA().getB().getC(),可以使用Optional类

6)主动进行参数检查,对方法中传入的参数进行检验

7)在已知字符串上使用equals(),equalsIgnoreCase()等方法

8)尽量避免方法中返回null。一些返回数组或者List的方法,如果没有值,尽量返回空集合,避免返回null


总结

记住一句话:避免空指针异常的最好的方法就是总是检查哪些不是自己创建的对象。


补充

针对第一点,我们进行实验

定义Person类

定义测试类

我们对count进行了非空判断,可它还是报了空指针异常,错误信息帮我们定位到第25行

public int getCount(){
    return this.count;
}

这是因为 
返回类型为基本数据类型,return 包装数据类型的对象时,自动拆箱有可能产生 NPE。反例:public int f() { return Integer 对象}, 如果为 null,自动解箱抛 NPE。
拆箱的时候实际上是调用了intValue()方法。Integer类型和int作比较会自动拆箱,由于Interger类型的对象是null,这时候自动拆箱调用intValue()方法就会报NullPointerException。
所以要判断包装类是否有可能为null,不然就会出现这种错误
class VerdictInter{
    public static void main(String[] args) {
        Person person = new Person();
        person.setName("张三");
        person.setCount(null);
        Integer count = person.getCount();
        int res = (count == null) ? 0:count;
        if (res == 0) {
            System.out.println("该变量未填写count");
        }
    }
}


参考资料:
防止NullPointerException,是程序员的基本修养 - 知乎 (zhihu.com)

空指针异常--java.lang.NullPointerException - 云+社区 - 腾讯云 (tencent.com)

(36条消息) java中什么是空指针异常以及为什么会产生空指针异常_imagpie的博客-CSDN博客_为什么会出现空指针异常

0 89

MySQL 数据类型选择

MySQL 中定义数据字段的类型对你数据库的优化是非常重要的。

MySQL 支持多种类型,大致可以分为三类:数值、日期/时间和字符串(字符)类型。


数值类型

MySQL 支持所有标准 SQL 数值数据类型。

这些类型包括严格数值数据类型(INTEGER、SMALLINT、DECIMAL 和 NUMERIC),以及近似数值数据类型(FLOAT、REAL 和 DOUBLE PRECISION)。

关键字INT是INTEGER的同义词,关键字DEC是DECIMAL的同义词。

BIT数据类型保存位字段值,并且支持 MyISAM、MEMORY、InnoDB 和 BDB表。

作为 SQL 标准的扩展,MySQL 也支持整数类型 TINYINT、MEDIUMINT 和 BIGINT。下面的表显示了需要的每个整数类型的存储和范围。

img

无论在什么数据库中,数值和字符串这两种数据字段类型都用的最多,并且往往量级非常大,在这种背景下,选择的字段类型的优劣对性能影响非常大,因此我们有必要熟知上表中各种类型的使用及差别。

一、TINYINT

  1. 表示范围 :(-128,127) or (0,255)

  2. 占用字节:1字节

  3. 【强制】表达是与否概念的字段,必须使用is_xxx的方式命名,数据类型是unsigned tinyint

  4. 最小显示长度(TINYINT(1) 和 TINYINT(2)的区别)

    TINYINT(m)

 m表示的是显示数据宽度,不同中数据类型它的数据宽度也是有差别的。数据宽度和数据类型的取值范围彼此之间是相互独立的,没有任何联系。 bigint(m)默认宽度为20,如果创建时表中设置了zerofill关键字(默认是用0填充的)。zerofill含义是:往表中插入的数值比定义的长度小的时候,会在数 值前进行补值。 img

  1. TINYINT和ENUM的区别

  TINYINT(1)或ENUM(’真’,’假’)?
        用ENUM枚举当存储只有2个值时只占用一个位的宽度,0或1,但会花更多的时间去寻找枚举查询的开始。
        用TINYINT(1)默认就会占用4个位的宽度(0000)
        得出结论:比如要存储一个介于0-9之间的值,为了查询获取这个值,建议用TINYINT(1)会更快,但如果你是为了大量记录枚举(“真”,“假”),那么用ENUM( ‘true’ , ‘false’) 搜索会更快。一般的,咱们如果存的是纯数字的话,建议用tinyint,如果是字符串,且是固定长度的,建议用char,而enum的枚举字段,使用的使用需要慎重考虑,避免带来不必要的麻烦

       注:TINYINT(1)在使用Mybatis代码生成器时,会自动转为Boolean类型!!!

二、INT或INTEGER

  1. 表示范围 :(-2 147 483 648,2 147 483 647) or (0,4 294 967 295)

  2. 占用字节:4字节

  3. The keyword INT is a synonym for INTEGER

  4. 如果不需要存取负值,最好加上unsigned

三、BIGINT

1.Mysql里有个数据类型bigint在java转换成实体对象时,处理不当容易出现以下异常:

java.lang.ClassCastException: java.lang.Long cannot be cast to java.math.BigInteger

只需要注意以下情况,就可避免此类异常:当数据库中该属性添加unsigned,则在对象中对应的属性类型应该为BigInteger; ​ 当数据库中该属性未添加unsigned,则在对象中对应的属性类型应该为Long。可以成功映射为Long的表用的是BIGINT(20),但是出问题的表使用的是BIGINT(20) UNSIGNED。如果不是无符号类型,BIGINT(20)的取值范围为-9223372036854775808~9223372036854775807。与Java.lang.Long的取值范围完全一致,mybatis会将其映射为Long;而BIGINT(20) UNSIGNED的取值范围是0 ~ 18446744073709551615,其中一半的数据超出了Long的取值范围,Mybatis将其映射为BigInteger。mysql数据库字段bigint使用 – 一心二念 – 博客园 (cnblogs.com)

2.存储手机号码用bigint(11)还是varchar(11)

空间:BIGINT——8字节CHAR——11字节
效率:BIGINT效率更高
使用:32位某些应用中bigint太大会溢出,要进行各种转换,麻烦
结论:使用char(11)更好

3.用 BIGINT 做主键,而不是 INT;

在真实业务场景中,整型类型最常见的就是在业务中用来表示某件物品的数量。例如上述表的销售数量,或电商中的库存数量、购买次数等。在业务中,整型类型的另一个常见且重要的使用用法是作为表的主键,即用来唯一标识一行数据。

整型结合属性 auto_increment,可以实现自增功能,但在表结构设计时用自增做主键,希望你特别要注意以下两点,若不注意,可能会对业务造成灾难性的打击:

用 BIGINT 做主键,而不是 INT;自增值并不持久化,可能会有回溯现象(MySQL 8.0 版本前)。 从表 1 可以发现,INT 的范围最大在 42 亿的级别,在真实的互联网业务场景的应用中,很容易达到最大值。例如一些流水表、日志表,每天 1000W 数据量,420 天后,INT 类型的上限即可达到。可以看到,当达到 INT 上限后,再次进行自增插入时,会报重复错误,MySQL 数据库并不会自动将其重置为 1。

第二个特别要注意的问题是,MySQL 8.0 版本前,自增不持久化,自增值可能会存在回溯问题!(23条消息) MySQL-数字类型自增是真的坑_Five在努力的博客-CSDN博客

其实,在海量互联网架构设计过程中,为了之后更好的分布式架构扩展性,不建议使用整型类型做主键,更为推荐的是字符串类型。合适的字符存储长度,不但节约数据库表空间、节约索引存储,更重要的是提升检索速度。

正例:人的年龄用unsigned tinyint(表示范围0-255,人的寿命不会超过255岁);海龟就必须是smallint,但如果是太阳的年龄,就必须是int;如果是所有恒星的年龄都加起来,那么就必须使用bigint。

四、FLOAT,DOUBLE,DECIMAL

  1. 表示范围 :较大

  2. 占用字节:4字节,8字节

  3. MySQL 中使用浮点数和定点数来表示小数。

浮点类型有两种,分别是单精度浮点数FLOAT)和双精度浮点数DOUBLE);定点类型只有一种,就是 DECIMAL。浮点类型和定点类型都可以用(M, D)来表示,其中M称为精度,表示总共的位数;D称为标度,表示小数的位数。浮点数类型的取值范围为 M(1~255)和 D(1~30,且不能大于 M-2),分别表示显示宽度和小数位数。M 和 D 在 FLOAT 和DOUBLE 中是可选的,FLOAT 和 DOUBLE 类型将被保存为硬件所支持的最大精度。DECIMAL 的默认 D 值为 0、M 值为 10。

  1. DOUBLE 实际上是以字符串的形式存放的(精准)

  2. 长度一定下,浮点数表示范围更大,缺点则是精度问题

在 MySQL 中,定点数以字符串形式存储,在对精度要求比较高的时候(如货币、科学数据),使用 DECIMAL 的类型比较好,另外两个浮点数进行减法和比较运算时也容易出问题,所以在使用浮点数时需要注意,并尽量避免做浮点数比较。

  1. 单精度float比双精度效率更高

  2. 浮点数判断相等不能用== 或者equals,应当使用

  3. 【强制】小数类型为decimal,禁止使用float和double。

说明:float和double在存储的时候,存在精度损失的问题,很可能在值的比较时,得到不正确的结果。如果存储的数据范围超过decimal的范围,建议将数据拆成整数和小数分开存储。


日期类型

表示时间值的日期和时间类型为DATETIME、DATE、TIMESTAMP、TIME和YEAR。

每个时间类型有一个有效值范围和一个”零”值,当指定不合法的MySQL不能表示的值时使用”零”值。

TIMESTAMP类型有专有的自动更新特性,将在后面描述。

img


字符串类型

字符串类型指CHAR、VARCHAR、BINARY、VARBINARY、BLOB、TEXT、ENUM和SET。该节描述了这些类型如何工作以及如何在查询中使用这些类型。img

一、CHAR和VARCHAR

CHAR数据类型是MySQL中的固定长度的字符类型。我们经常声明CHAR类型的长度,指定我们要存储的最大字符数。例如,CHAR(20)最多可以容纳20个字符。

如果要存储的数据是固定大小,则应使用CHAR数据类型。与VARCHAR这种情况相比,您将获得更好的性能。

CHAR数据类型的长度可以是0到255之间的任何值。当您存储CHAR值时,MySQL会将其值用空格填充到您声明的长度。

当您查询CHAR值时,MySQL将删除尾随空格。

请注意,如果启用PAD_CHAR_TO_FULL_LENGTH SQL模式,MySQL将不会删除尾随空格。

区别:

  1. CHAR的长度是不可变的,而VARCHAR的长度是可变的

  2. CHAR的存取速度要比VARCHAR快得多

  3. 存储方式不同

varchar比char节省空间,但在效率上比char稍微差一些。varchar比char节省空间,是因为varchar是可变字符串,比如:用varchar(5)存储字符串“abc”,只占用3个字节的存储空间,而用char(5)存储,则占用5个字节(“abc ”)。varchar比char效率稍差,是因为,当修改varchar数据时,可能因为数据长度不同,导致数据迁移(即:多余I/O)。其中,oracle对此多余I/O描述的表达是:“行迁移”(Row Migration)。

  1. “行迁移”(Row Migration)

    *“当一行的记录初始插入时是可以存储在一个block中的,由于更新操作导致行增加了,而block的自由空间已经完全满了,这个时候就产生了行迁移。在这种情况下,oracle将会把整行数据迁移到一个新的block中,oracle会保留被迁移的行的原始指针指向新的存放行数据的block,这就意味着被迁移行的ROW ID是不会改变的。"*
    
      其中要解释一下:block是oracle中最小的数据组织与管理单位,是数据文件磁盘储存空间单位,也是**数据库I/O最小单位(****也就是说,读和写都是一个block的大小,所以如果block没满时,更新内容长度变更的varchar字段,和更新内容长度没变的varchar字段,I/O次数是一样,不存在额外消耗,只有在block满时,才会出现额外I/O,所以char和varchar性能之间的性能差异,是相当细微的,绝大多数情况下可以忽略不计,所以上文描述的“稍”差的含义)**。
    
      所以,我的开发经验是:“**用varchar代替char的效率有所下降,但不大**”。
  2. 占用空间不同

CHAR的存储方式是,一个英文字符(ASCII)占用1个字节,一个汉字占用两个字节;而VARCHAR的存储方式是,一个英文字符占用2个字节,一个汉字也占用2个字节。

varchar是可变长字符串,不预先分配存储空间(这里说的是存储时不预先分配存储空间,但是在查询需要创建临时表时,会按照设置的长度预先分配内存空间,因此这个设置长度时不可以随意,详情见下varchar的长度设计),长度不要超过5000,如果存储长度大于此值,定义字段类型为TEXT,独立出来一张表,用主键来对应,避免影响其它字段索引效率。

     6.VARCHAR类型的实际长度是它的值的实际长度+1。为什么”+1″呢?

这一个字节用于保存实际使用了多大的长度。


二、MySQL中的varchar长度设计

 之前看到一篇博文,讲的是在定义varchar(n)时,需要用多大就设置多大,而我当时在分析时,对数据量估算错误了,设置了varchar(5000),被leader和导师批评了一番,因此特意学习varchar的长度设计:
尽管varchar(n)是存储可变长字符串的,但是其n也不是越大越好,理论如下
当MySql在查询需要创建临时表的时候(union,order by、group by,子查询),在MySql读取数据之前,是只知道varchar的长度n,不知道实际数据的长度的,但是读取数据之前需要预分配内存空间,MySql是根据varchar(n)中的n来进行分配内存的,这样也是最合理的方式,不可能分配小于n个字符的空间,因此针对varchar(1000)设置就会预先分配1000个字符空间,很显然这个是十分不靠谱的设计。

三、varchar(50)和varchar(255)有性能上的差别么?

对于INNODB,varchar(50)varchar(255)这两者在存放方式上完全一样:1-2 byte保存长度,实际的字符串存放在另外的位置,每个字符1 byte到4 byte不定(视编码和实际存储的字符而定)。所以将一个字段从varchar(50)长度改成varchar(100)长度不会导致表的重建。但如果把长度从varchar(50)改成varchar(256)就不一样了,表示长度会需要用到2 byte或更多。既然255长度以下对INNODB都一样,而且我们平时基本上也不太会使用到MYISAM,那么是不是为了省心,我们就可以把255长度以下的字段的类型都设置成varchar(255)了呢?
非也。
因为内存表(临时表)介意。
虽然我们不会明文创建内存表,但所有的中间结果都会被数据库引擎存放在内存表(MySQL在有些查询情况下需要创建内存表)。我们可以通过EXPLAIN或者SHOW STATUS可以查看MYSQL是否使用了内存表用来帮助完成某个操作。
而内存表会按照定义的varchar(n)的n来分配内存。以utf-8编码为例,对于varchar(255),每一行所占用的内存就是长度的2 byte + 3 * 255 byte。对于100条数据,光一个varchar字段就占约1GB内存。如果我们该用varchar(50),就可以剩下来约80%的内存空间。
除此之外,255长度也可能会对索引造成坑。MySQL在5.6版本及之前的最大长度是767 byte。但MySQL 5.5版本后开始支持4个byte的字符集utf8mb4(沙雕表情用到的字符太多,长度不够用)。255 * 4 > 767,所以索引就放不下varchar(255)长度的字段了。虽然MySQL在5.7版本后将限制改成了3072 byte,但如果是多字段的联合索引还是有可能会超过这个限制

所以我们的结论就是:在长度够用的情况下,越短越好。

四、varchar(255)和varchar(256)的区别

在varchar长度接近256时,varchar长度设置成255的好处:

1、方便InnoDB建索引,对于 MyISAM,可以对前 1000 个字节做索引,对于 InnoDB,则只有 767 字节。(来源依据)。255X3=765

2、少申请一个字节,记录字符创长度,一个8位的tinyint,可以表示的无符号数值的范围是,0-255,如果长度超过了255,需要在申请个字节

五、varchar(n)长度定义知识总结

1.在长度够用的情况下,越短越好(因为长了会导致查询生成临时表时降低性能且占用内存)。

2.在varchar长度接近256时,varchar长度设置成255更好

六、参考文章

一、什么是JSON?

JSON:JavaScript Object Notation[JavaScript 对象表示法]

JSON 是存储和交换文本信息的语法。类似 XML。(什么是XML?XML 简介 – 什么是 XML? | 菜鸟教程 (runoob.com)

JSON采用完全独立于任何程序语言的文本格式,使JSON成为理想的数据交换语言


二、为什么需要JSON

https://zhuanlan.zhihu.com/p/33792109


三、JSON、JSON格式、JSON对象、JSON字符串是什么关系?(重点)

1.JSON

其实,JSON并不复杂,之所以新手会对JSON犯迷糊,原因是简称。(至少我是这个原因)无论是在工作中,还是在论坛上,绝大多数时候,我们把JSON对象和JSON字符串都简称为JSON,以至于你常常会听到这样的回答:

“这个响应你用JSON接收就行”

“你测试接口的时候要记得用JSON格式”

“把这个JSON解析一下”

久而久之,新手就把JSON格式、JSON字符串、JSON对象(又可分普通对象、JSON数组)三者搞混了,现在,我们就将它们的关系梳理清楚。

2.JSON格式

JSON格式是一种数据交互的格式

当客户端和服务端进行数据交互的时候(不仅是C/S模型,其他模型也可以使用JSON格式交互),需要定义数据格式,JSON格式就是其中一种。

JSON格式是从JavaScript演变而来的

JSON是从 JavaScript 脚本语言中演变而来,使用Javascript语法来描述数据对象,文件名扩展是 .json,但是JSON格式仅仅是一个文本,仍然独立于语言和平台。JSON 解析器和 JSON 库支持许多不同的编程语言。 目前非常多的动态(PHP,JSP,.NET)编程语言都支持JSON

JavaScript对象的定义语法如下:

var person = {firstName:"Bill",
              lastName:"Gates", 
              age:62, 
              eyeColor:"blue"};
而JSON格式则长这样:
{
    "book": [
        {
            "id":"01",
            "language": "Java",
            "edition": "third",
            "author": "Herbert Schildt"
        },
        {
            "id":"07",
            "language": "C++",
            "edition": "second"
            "author": "E.Balagurusamy"
    }]
}

 JSON格式和JavaScript的区别

 二者之间的详细总结可以参考下述文章:JavaScript JSON的总结 – 知乎 (zhihu.com)

3.JSON字符串

首先,请清晰一点,JSON字符串本质上就是字符串。

String url = “https://restapi.amap.com/v3/config/district?output=JSON&key=75bf29a927a4ce42358665b507493393&extensions=base” + “&” + “keywords=” + keywords;
String result = HttpUtil.get(url);
上述代码是高德查询省市区的接口,返回值如下:诺,我们一眼就能发现,这不就是JSON格式的数据吗,我们直接用字符串类型的result去接收,一点问题都没有。
其实用什么类型去接收返回值,是由方法决定的
如果方法返回的是一个JSONObject类型,那你直接使用JSONObject去接收
如果方法返回的是一个JSON格式的字符串,那你直接使用String去接收,然后再把这个String类型转换成JSONObject类型或者JSONArray即可
那么,我们为什么要把String类型转成JSONObject或者JSONArray呢?

4.JSON对象(JSONObject、JSONArray)

为什么要把String类型转成转成JSONObject或者JSONArray,其实是业务决定的。你想想,如果我们需要通过高德接口查询某一个省下面的市信息,那么如果返回值是String类型的话,我们单单想获取市名称列表,即List<name>,需要对字符串进行截断,拼接等等一系列的麻烦操作,这个过程还极其容易出错。所以,我们直接使用JSONObject类型,其本质是k-v结构


四、JSON字符串和JSONObject对象的相互转换

我们可以使用FastJson 的JSONObject.parseObject(jsonStr)将JSON字符串转换为JSONObject对象,之后可以直接通过get(key)获取对应的属性值

JSONObject parse = JSONObject.parseObject(result);
//通过key取JSONArray
JSONArray puisneList = parse.getJSONArray("districts");
//通过key取JSONObject
JSONObject name = parse.get("jsonObject");
//通过key取属性值
String name = parse.get("name");

四、JSON字符串和Java对象的相互转换

JSON字符串转Java对象:JSON.parseObject(jsonStr, Target.class)

Java对象转JSON字符串:JSON.toJSONString(TargetObject)


五、JSONObject和Java对象的相互转换

Java对象—>JSON对象

Student stu = new Student(“公众号BiggerBoy”, “m”, 2);
JSONObject jsonObject = (JSONObject) JSONObject.toJSON(stu);

JSON对象—>Java对象

Student stu = new Student(“公众号BiggerBoy”, “m”, 2);
JSONObject jsonObject = (JSONObject) JSONObject.toJSON(stu);
Student student = JSONObject.toJavaObject(jsonObject, Student.class);

JSON字符串—>Java对象

ReportExceptionDto frontReportExceptionDto = JSONObject.parseObject(jsonObject.toJSONString(), ReportExceptionDto.class);

注意区别:toJavaObject和parseObject两个方法都可以将jsonObject转换为Java对象,
但是toJavaObjec没有处理json转换对象$ref关键词,而parseObject方法处理了!

toJavaObject和parseObject,区别是:

parseObject的第一个参数是json字符串;

toJavaObject的第一个参数是json对象;

参照:JSONObject.toJavaObject的使用以及其他方法的使用JSON与Java对象的转换_sucsmanw的博客-CSDN博客_jsonobject to object
      Fastjson-JSON.toJavaObject(String text, Class<T>)与JSONObject.toJavaObject(JSON json, Class<T> clazz)_hanjq_code的博客-CSDN博客_fastjson tojavaobject


五、JSON字符串和JavaScript对象的相互转换

JSON和JS对象的相互转换 (biancheng.net)

 JSON 是 JS 对象的字符串表示法,它使用文本表示一个 JS 对象的信息,所以 JSON 本质是一个字符串。通过 JSON 调用相应的方法,JSON 和 JS 对象可以相互转换。

将JSON字符串转换为JS对象

通过 JSON字符串的 parse() 方法,可以将一个 JSON字符串 解析为一个JS对象,解析格式如下:JSON.parse(json)【JSON —> JS对象】

例如:

var json = '{"name":"张三", "age":36}';//定义一个JSON
var obj = JSON.parse(json);//调用parse()将json解析为一个JS对象
console.log(obj);//输出:{name: "张三", age: 36}

将JS对象转换为JSON字符串

通过 JSON 的 stringify() 方法,可以将一个 JS 对象转换为 JSON,转换格式如下:JSON.stringify(obj)【JS对象—>JSON字符串】

例如:

var obj = {name:"张三", age:36};//定义一个JS对象
var json = JSON.stringify(obj);//调用stringify()将一个JS对象转换为JSON
console.log(json);//输出:{"name":"张三","age":36}