ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Java字面量六种类型全解析:字符串、数字、null与避坑指南

Java字面量六种类型全解析:字符串、数字、null与避坑指南 1. 字面量是什么先搞懂这个概念Java字面量这几个字听起来像教科书里的名词但说穿了就是我们每天都在写的那些“裸值”。int a 10;里的10String name Tom;里的Tomboolean flag true;里的truePerson p null;里的null——这些不需要任何计算、直接写在代码里的值就是字面量。我刚开始学Java的时候最困惑的就是为什么1和1明明都是“1”程序行为却完全不一样为什么1加上2等于3但1加上2是12。这些问题的答案全都藏在字面量的类型规则里。这篇文章就想把六种字面量——整数、小数、字符串、字符、布尔、空值——一次讲透包括它们的底层存储方式、类型推断规则、实际使用中的常见坑以及面试题里最容易被问到的细节。适合刚入门Java的新手系统打基础也适合有几年经验的开发者查漏补缺很多你以为知道的知识点其实藏在细节里。1.1 为什么写代码到处都在用字面量因为我们没办法凭空造出一个值。你想让程序记住你的年龄25最简单的方式就是直接写int age 25;。这里的25就是一个整数字面量。如果你想在控制台打印“你好”就要写System.out.println(你好);这里的你好就是一个字符串字面量。字面量是源代码里最底层的“原材料”变量只是给这些原材料贴了个名字。就像菜谱盐 3克里的3克是字面量盐是变量名。你完全可以不定义变量直接用字面量System.out.println(3);但大多数时候我们都会把值先存到变量里方便后续复用。字面量和变量的区别一个是“值本身”一个是“存放值的容器”。Java编译器在编译阶段就会检查字面量的合法性。你写int x 10;编译器知道10是合法整数你写int y 10;编译器立刻报错因为字符串字面量不能塞进int变量。这种“类型检查发生在编译期”的特性是Java比很多动态语言更早暴露问题的主要原因。1.2 六种字面量类型一览Java官方文档把字面量分成六大类每一类都有自己独特的语法规则字面量类型示例核心特征整数字面量42、0xFF、0b1010、1_000_000L默认是int加L后缀变long浮点字面量3.14、1.5e10、2.5f默认是double加F后缀变float字符串字面量Hello、\n双引号包裹可包含转义序列字符字面量a、\u4e2d、1单引号包裹只能是单个字符布尔字面量true、false只有两个值不参与数值运算空值字面量null代表引用为空不是对象这六类字面量前两类是数字中间两类是“文本”最后两类是特殊值。我见过很多工作两三年的开发者把a和a混用也见过有人用比较字符串导致线上bug根源都是对字面量类型理解不透彻。1.3 关于默认类型Java设计者的小心思Java有个很有意思的设计整数写在代码里默认就是int小数默认就是double。这意味着你写float f 3.14;会编译报错——3.14默认是double精度比float高不能自动“降级”到float。正确的写法是float f 3.14f;。为什么要这样设计因为Java设计者希望代码是“显式”的。long类型你想清楚才写L后缀float类型你想清楚才写F后缀这样就不会出现“我明明想要float精度却因为忘记后缀而在某些机器上计算出浮点误差”的尴尬。我第一次踩这个坑是在计算金额时float price 19.99;直接编译不过当时还很纳闷后来才明白这其实是Java在保护我。2. 数字字面量整数与小数的底层规则2.1 整数字面量的四种进制与下划线整数字面量不只是我们日常用的十进制Java还支持十六进制、八进制和二进制。我去年排查过一个位运算bug同事用十进制写了一个掩码int mask 123456;代码读起来完全不知道哪几位是1。改成二进制int mask 0b1111000010101010;之后一眼就能看出每一位的开关状态。十进制123最常用十六进制0xFF或0Xff0x开头FF就是255常用于颜色值、内存地址、二进制协议解析八进制077数字0开头现在已经不推荐使用因为容易和十进制混淆Java 7开始允许二进制后八进制的地位就更尴尬了二进制0b1010Java 7加入0b或0B开头很适合做位运算和状态标志位还有一个很多人不知道的语法数字下划线。Java 7开始允许在数字字面量中插入下划线来提升可读性比如int million 1_000_000;编译器会自动忽略下划线。但要注意下划线只能放在数字之间不能放在开头、结尾、小数点旁边也不能放在L/F后缀前面。int x 100_;编译会报错long l 10_L;也会报错。关于整数字面量的类型有几个细节必须说清楚int maxInt 2147483647; // 合法int上限 // int overflow 2147483648; // 编译报错超出int范围数字过大 long big 2147483648L; // 合法必须有L后缀 byte b 127; // 合法127在byte范围内 // byte b2 128; // 编译报错128超出byte范围 char c 65535; // 合法char本质是16位无符号整数我见过不少新手直接写long time 1000 * 60 * 60 * 24;以为自动就变成long了其实右边所有乘数都是int1000 * 60 * 60 * 24 86400000还在int范围内没问题。但如果乘到1000 * 60 * 60 * 24 * 365结果就溢出int变成负数了。正确写法是第一个数就加L1000L * 60 * 60 * 24 * 365。2.2 浮点字面量默认double与精度陷阱浮点字面量有两种书写形式普通小数3.14和科学计数法1.5e10就是1.5乘以10的10次方。默认类型是double要想表示float必须加后缀F或f。这里有一个区分建议代码里统一用大写L和F后缀。因为小写l长得太像数字1团队代码评审时容易看错。浮点字面量最经典的问题是精度。看这段代码double a 0.1; double b 0.2; System.out.println(a b); // 输出 0.30000000000000004原因很简单二进制无法精确表示0.1就像十进制无法精确表示1/3一样。Java的double用64位存储只能无限接近0.1的真实值。很多刚入行的同事第一次看到这个结果都以为是JDK的bug其实这是IEEE 754浮点数的固有缺陷所有语言都一样。所以在涉及金额、利率、单位转换等对精度敏感的场景不能用double直接算要使用BigDecimal或者把金额转换成整数分存储。我记得有个同事在处理佣金比例时直接写double ratio 0.33;然后去乘本金结果线上对账差了几分钱排查了很久才发现是浮点精度问题。浮点字面量还有一个比较冷门的点0.0除以0.0会得到Double.NaNNot a Number而1.0 / 0.0会得到Double.POSITIVE_INFINITY。这些特殊值也算浮点字面量的边界情况面试里偶尔会被问到。2.3 字面量与实际项目的真实连接字面量最常见的应用场景是配置文件里的数字解析比如读取超时时间、重试次数等需要把字符串转换成对应的数字类型。这里有一个我在实际项目中总结的经验配置项尽量使用Integer.parseInt()或Long.parseLong()接收字符串参数再赋值给int或long变量如果配置项可能为空先用StringUtils.isNumeric()判断合法性再转换避免NumberFormatException在代码里硬编码数字字面量时尽量给常量起一个有意义的名字比如private static final int RETRY_COUNT 3;而不是散落一地的裸数字3硬编码数字字面量在代码里到处出现时阅读者会非常痛苦。我接手过一套老系统里面全是if (status 1)这种判断完全不知道1代表什么。后来维护变成了噩梦。哪怕只是把字面量提成常量可读性就能提升一个档次。3. 字符串和字符最容易混淆的一对3.1 字符串字面量双引号里的世界字符串字面量是用双引号包裹的任意字符序列Hello、空字符串、123都是合法的字符串字面量。它在Java里是最常用的类型没有之一。但大多数人对它的认识停留在“双引号包起来的东西”这个层面。字符串字面量的底层存储有一个很重要的机制字符串常量池。当你在代码里写String s1 abc;时JVM会先去常量池里找有没有abc这个字符串对象如果没有就创建一个如果有就直接复用。这意味着String s1 abc; String s2 abc; System.out.println(s1 s2); // 输出 true同一个对象但如果你换成new String(abc)那就强制在堆中新建一个对象s1 s2就变成了false。这是面试里出现频率最高的字符串陷阱。判断字符串内容是否相等永远用equals()绝对不要用因为你永远不确定对方是不是从外部传进来的。字符串字面量还支持转义序列\n换行、\t制表符、\\反斜杠、\双引号、\单引号、\uXXXXUnicode字符。比如String greeting 你好\n世界;打印出来就是两行。这里有一个非常隐蔽的陷阱Unicode转义\uXXXX在Java里不只是字符串和字符字面量中有效它在任何代码位置都会被预先处理。这意味着你写注释// 这是 \u000a 换行编译器会把\u000a替换成真正的换行符然后注释就提前结束了后面可能出现无法预料的编译错误。我在一次代码生成工具的开发中踩过这个坑生成的Java文件里包含了一个\u000a结果整个类编译失败排查了很久才定位到是注释里的Unicode转义闹的鬼。3.2 字符字面量单引号里只能是单个字符字符字面量用单引号包裹而且必须且只能包含一个字符a、0、\n、\u4e2d。字符字面量不能为空编译会直接报错。空字符需要用\u0000或者写成\0。很多人不知道的是char在Java里本质是一个16位无符号整数范围从0到65535。这意味着字符字面量可以直接参与数值运算char c A; System.out.println(c 1); // 输出66A的ASCII是65 System.out.println((char)(c 1)); // 输出 B这种数字特性在处理字母大小写转换时特别有用char lower (char)(upper 32);因为小写字母ASCII码比大写字母大32。我在写一个字符串翻转工具时就用字符字面量和ASCII计算的配合做了大小写敏感的比较完全不需要调用系统API。字符字面量和字符串字面量最大的区别就是1是字符参与数值计算时值就是49ASCII码1是字符串只有文本意义。很多初学者在char和String之间做拼接时感到困惑System.out.println(A 1); // 输出 66字符先变成数字再加 System.out.println(A 1); // 输出 A1字符串拼接这种细微差别笔试和机试里经常出现。记住char是数字类型String是引用类型。3.3 字符串运算这些操作你每天都在做字符串字面量最常见的应用就是拼接。Java里号既能做数字加法也能做字符串拼接String name Java; int version 17; String msg name version; // Java17大部分时候没问题但在循环里拼接字符串要特别小心。我接手过一个日志模块代码在循环里用拼接几千条日志的字符串结果运行一次要快两秒。因为字符串是不可变对象每次拼接都会产生新字符串对象循环几千次就创建几千个中间对象。后来改用StringBuilder性能瞬间提升。所以循环拼接字符串一定要用StringBuilder的append()方法。字符串的常用操作还有substring截取子串、split按分隔符分割返回字符串数组、contains判断是否包含子串、equals比较内容是否相等、toCharArray转换成字符数组。这些都是面试里被问烂了的基础API但真正写的时候有不少细节要注意比如split(.)是不行的——点号是正则表达式的元字符需要转义成\\.。我第一次拆分IP地址时就被这个坑绊了一次。4. 布尔与空值两个容易被忽略的“特殊值”4.1 true和false布尔字面量的规则与陷阱布尔字面量只有两个值true和false。它们不像数字那样有各种进制和精度问题但有一个规则让很多学C语言转过来的程序员不适应Java的boolean不接受数字替代。在C语言里if (1)是合法的因为非零即真但Java里if (1)直接编译报错只能写if (true)或者if (flag)。这个设计是为了让代码意图更明确——你永远不会看到“到底是1还是2才是真”这种模糊状态。布尔字面量常见陷阱是包装类型拆箱时的空指针。比如Boolean flag null; if (flag) { ... }一行if (flag)就会触发NullPointerException因为包装类型会自动拆箱为boolean但null无法拆箱。我在审核项目代码时经常看到这种情况多数是用数据库查询结果直接赋值给Boolean包装类型导致的。还有一个在业务中经常看到的写法问题if (isSuccess true) { ... } // 冗余写法 if (isSuccess) { ... } // 推荐写法isSuccess true这种和布尔字面量直接比较的写法是典型的冗余代码。布尔变量本身就是布尔值再和true比较一次完全多余。我的代码评审规范里明确禁止这种写法。4.2 null空值字面量的本质与使用规范null这个字面量比较特殊它不属于任何基本类型也不属于任何对象类型它可以赋值给任意引用类型变量表示“这个变量没有指向任何对象”。String s null; Integer num null; Object obj null; // int x null; // 编译报错基本类型不能为null很多人对null的理解就是“空”但null和空字符串、空集合完全是两回事。是“内容为空的字符串对象”null是“根本没有对象”。我把这个区别类比成口袋里有空气是口袋里空无一物是null两者有天壤之别。实际代码里null最常见的坑是对null调用方法或访问字段直接触发空指针异常。所以Java里处理null有一整套方法论返回集合时优先返回空集合而不是null——我见过很多API返回null导致调用方循环遍历时报NPE判断是否为空用Objects.isNull()或 null不要调用 equals因为abc.equals(null)返回false没问题但如果字符串本身就是nulls.equals(abc)就炸了使用Optional包装可能为null的值强迫调用方处理null情况有一个业务上经常出现的隐蔽问题字符串null。前端传参时如果某个字段没有值前端框架可能序列化成null这个字符串而不是真正的null。服务端拿到null再用StringUtils.isEmpty()判断发现它不为空于是拿着这个字符串去查库什么都查不到。我之前排查过一个接口莫名其妙的空结果问题就是这种“字符串null”与“空值null”混淆导致的。4.3 业务代码里的null处理策略null的可怕之处在于它无处不在数据库字段可能为nullJSON解析可能返回null接口参数可能传null。我在项目里定了几条铁律所有方法入参如果有null的可能第一行就做null校验用Objects.requireNonNull或者显式判断配置中心取到的值统一做null处理比如String timeout config.get(timeout);可能是null要兜底用默认值JSON反序列化时字段缺失可能得到null也可能得到空字符串不同框架行为不同要统一封装处理有一次排查线上问题发现某个接口在高并发时会偶发NPE最后定位到是缓存查询返回了null代码里直接cacheResult.getTitle()没有判空。这种问题的解决方案很简单就是养成“取对象后先判断再使用”的习惯。null用得好代码稳定用不好就是生产事故的温床。5. 从字面量到真实项目高频场景、面试题与避坑手册5.1 字符串与数字字面量的转换parse与valueOf日常开发中字符串和数字字面量之间的转换频率极高。从HTTP请求拿到的参数全是字符串存到数据库需要变成数字类型从数据库读出来的数字显示到前端又要变成字符串。// 字符串转数字 int num Integer.parseInt(123); // 最常用 long longNum Long.parseLong(456); // 长整型 double d Double.parseDouble(3.14); // 浮点型 // 数字转字符串 String str String.valueOf(123); // 最推荐null安全 String str2 Integer.toString(456); // 等价 String str3 123 ; // 不推荐会产生多余对象使用parseInt时有个非常常见的坑Integer.parseInt(123abc)会抛出NumberFormatException。所以我在接收外部参数时一定会先做合法性校验再转换。如果是数据库查询结果还要考虑null的情况——Integer.parseInt(null)会抛NumberFormatException但Integer.valueOf(null)会抛NumberFormatException而String.valueOf(null)返回字符串null。这里顺便解答一个很多人在数据库端碰到的问题像DB2、Oracle里要判断一个字符串是不是纯数字本质就是在数据库端做一次“字符串转数字前置校验”。正确的做法是使用正则表达式或数据库内置的转换函数确保数据能被安全转换后再操作否则就会出现隐式转换报错或截断问题。前端传来的id如果特别长也需要用Long而不是Integer接收否则会溢出。这些都是字面量类型边界带来的真实问题。5.2 字符串与JSON字面量序列化的细节JSON序列化时字符串字面量、数字字面量、布尔字面量、null各自有不同的表现。比如Fastjson或Gson把一个对象转成JSON字符串时MapString, Object map new HashMap(); map.put(name, test); map.put(count, 100); map.put(active, true); map.put(extra, null); String json JSON.toJSONString(map); // 输出类似 {active:true,count:100,name:test}默认配置下null字段可能被序列化为extra:null在JSON里也可能直接不输出取决于框架配置。接收反序列化时JSON.parseObject会把100转成Integer或Long把true转成Boolean把null转成null。这些类型转换里最容易出问题的是数字精度丢失。比如用double接收一个超长整数会丢失精度用Integer接收字符串超过2^31的数字会抛异常。我在对接一个老系统时对方返回的JSON里金额是100.00这种字符串我们这边直接用Double接收再转换BigDecimal经常出现0.30000000000000004这类精度惊吓。后来统一改成new BigDecimal(str)用字符串构造BigDecimal才彻底解决。5.3 面试高频考点字面量相关问题速查表字面量是Java基础面试题里的常客我把被问频率最高的问题整理成了一张速查表面试问题考察点标准答案要点1和1L有什么区别整数字面量默认类型默认是int1L是long范围更大为什么0.1 0.2不等于0.3浮点字面量精度二进制无法精确表示十进制小数和equals比较字符串有什么区别字符串常量池比较对象地址equals比较内容a和a有什么区别字符与字符串char是16位整数String是引用类型true和1能否互换布尔字面量Java禁止boolean与数值互相转换null能不能赋给intnull字面量特性null只能赋给引用类型Java 7以后数字字面量有什么新语法新特性二进制0b、下划线分隔符switch可以作用在哪些字面量类型上类型兼容性int、String、enum、char、short、byte这些问题看起来简单但很多面试者只答出表层深挖一层就露馅。我一般会再问一句“Integer a 100; Integer b 100; a b的结果是什么”答案是true因为Integer缓存了-128到127。但Integer a 200; Integer b 200; a b就是false因为超过了缓存范围。这个点虽然不是纯字面量问题但和包装类型的自动装箱机制强相关值得一块吃透。5.4 我在实际项目中关于字面量的几点体会这些年写了十几万行Java代码回头再看字面量这个基础主题最大的感慨是基础不牢代码质量天花板就很低。很多线上bug追根溯源都逃不开字面量的细节——浮点精度、null判断、字符串比较、类型溢出。这些问题说白了不是什么高深算法就是基础概念没吃透。我自己的习惯是在代码里使用字面量时始终问三个问题这个字面量的类型是什么它会不会超出目标类型的范围它能不能为null多问这几句很多坑都能在写代码的瞬间避开。如果你刚开始学Java建议把今天讲的六种字面量每种都写几个示例跑一遍把转义字符的规则、进制的换算、parse的边界都亲手验证一次。知道一个知识点和真正理解一个知识点之间隔着的就是这几次实际动手。
返回列表