一、BigDecimal 精准浮点运算
1. 问题
double/float 二进制存储存在精度丢失
double a=1.06,b=1.01; System.out.println(a+b); //2.07000000000000032. 核心说明
包:java.math.BigDecimal推荐构造:new BigDecimal("数值"),避免 double 入参二次失真
3. 常用运算方法
add()加、subtract()减、multiply()乘、divide()除toString()转字符串、doubleValue()转回 double
//题目需求 //商品单价 0.15 元,基础购买数量 1000 件; //基础总价 = 单价 × 数量(multiply 乘法) //额外运费 25.60 元,应付总额 = 基础总价 + 运费(add 加法) //店铺优惠减免 18.35 元,实付金额 = 应付总额 − 优惠(subtract 减法) //将实付金额平分给 7 个人,人均费用保留 2 位小数、四舍五入(divide 除法 + HALF_UP) //把人均费用转为字符串输出(toString ()) //将人均费用转为 double 类型打印(doubleValue ()) //要求:所有数值必须用字符串构造 BigDecimal,禁止直接传入 double。static void produce(){ BigDecimal price = new BigDecimal("0.15");//单价 BigDecimal count = new BigDecimal("1000");//基础购买数量 BigDecimal total = price.multiply(count);//基础总价 System.out.println("基础总价"+total); BigDecimal freight= new BigDecimal("25.6");//额外运费 BigDecimal Accounts_Payable=total.add(freight);//应付款 System.out.println("应付款"+Accounts_Payable); BigDecimal Discount = new BigDecimal("18.35");//优惠 BigDecimal Actual_payment= Accounts_Payable.subtract(Discount);//实付款 System.out.println("实付款"+Actual_payment); BigDecimal people = new BigDecimal("7"); //考点:divide必须指定保留位数与舍入模式,否则除不尽抛算术异常。 BigDecimal result=Actual_payment.divide(people,2, RoundingMode.HALF_DOWN);//人均 System.out.println("人均"+result); String str=result.toString(); System.out.println("字符串"+str); double d1=result.doubleValue(); System.out.println("数值"+d1); }运行结果: 基础总价150.00 应付款175.60 实付款157.25 人均22.46 字符串22.46 数值22.46二、包装类
1. 基本类型与包装类对应
byte→Byte、short→Short、int→Integer、long→Long 、float→Float、double→Double、boolean→Boolean、char→Character
2. 核心 API
- Character 字符工具
isDigit()判断数字、isLetter()判断字母、toLowerCase()/toUpperCase()大小写转换 - Integer 进制转换
toBinaryString()二进制、toOctalString()八进制、toHexString()十六进制 - 字符串转数值(抛
NumberFormatException)Integer.parseInt("123")、Float.parseFloat("3.14")
3. 装箱 & 拆箱
- 手动:
Integer.valueOf(10)装箱;intValue()拆箱 - 自动:
Integer a=5; int b=a; - 坑:包装类可为 null,null 自增直接空指针
4. Integer 缓存池
- 缓存范围:
-128 ~ 127,范围内直接复用常量对象,==为 true - 超出范围 /
new Integer(),堆新建对象,==为 false
练习 :Integer 缓存池对比题 Integer a = 127; Integer b = Integer.valueOf(127); Integer c = new Integer(127); Integer d = 125; Integer e = new Integer(125); System.out.println(a == b); System.out.println(a == c); System.out.println(d == e); Q:三行输出结果分别是什么?底层原理? A:输出依次:true、false、false 原理: Integer 缓存池范围 -128~127,自动装箱 /valueOf() 复用缓存对象; new Integer() 无论数值多少,都会在堆新建独立对象; == 比较对象内存地址: a、b 都取自缓存,地址相同 → true a 是缓存对象,c 是 new 新对象,地址不同 → false d 取自缓存,e 是 new 新对象,地址不同 → false5. 数值与包装类比较
int == Integer:包装类自动拆箱比数值
练习 1:null 包装类参与运算
Integer num = null; int res = num + 10;Q:运行报什么异常?原因? A:抛出
NullPointerException空指针异常。 原因:num + 10会自动拆箱,底层执行num.intValue();num 为 null,没有对象可以调用方法,触发空指针。
练习 2:Long 赋值字面量
Long l1 = 100L; Long l2 = 100;Q:
Long l2 = 100;能否编译通过?为什么? A:不能编译。 原因:100 是 int 类型字面量;自动装箱仅支持long转 Long,int 无法直接装箱为 Long,必须使用长整型字面量100L。
练习 3:Double 赋值字面量
Double db1 = 200d; Double db2 = 200;Q:
Double db2 = 200;能否编译通过?为什么? A:不能编译。 原因:200 是 int 整数;Double 对应基础类型是 double,int 不能直接自动装箱成 Double,需写成200.0/200d。
三、String 字符串
1. 底层结构
public final class String
- final 修饰,不可继承,里面所有的方法也不能被重写
- JDK9 + 底层
private final byte[] value;JDK8 及以前char[] value是私有,且用 final 修饰 - value 数组 final,无对外修改方法,字符串不可变
- 用 final修饰一个引用类型的时候,是个引用不能变,不是这个引用指向的容不能变,但String类本身没有提供任何能修改这个 byte [] value 内容的方法(地址不变,值可以变)
2. 两种创建方式
- 字面量
String s = "abc"先查字符串常量池,有则复用,无则池中创建 - new
String s = new String("abc")常量池先存 "abc",再在堆创建 String 对象,引用指向堆
3. 不可变设计优点
- 实现常量池,减少对象创建,节省内存;
- 线程安全,多线程共享无需同步锁;
- 缓存 hash 值,高效用作 Map 的 key;
- 内容不可篡改,保证数据使用安全,一处修改不影响其他引用。
四、字符串比较规则
==:比较地址equals():String 重写,逐字符对比内容
经典易错场景
Integer a = 128; Integer b = 128; System.out.println(a == b); // false System.out.println(a.equals(b)); // true128 超出 Integer 缓存池范围,自动装箱会 new 两个不同对象,地址不同,
==为 false;但数值相等,equals 为 true。
- 包装类用
==:比地址,地址相同才 true,不能判断数值;- 包装类比较数字:统一使用
.equals();
- 常量拼接编译优化
String s1="a"+"b"; //编译为"ab",走常量池 String s2="a"+1; String s3="a"+true; //均优化为常量- 变量拼接无优化
String a="hello",b="world"; String c=a+b; //底层new StringBuilder拼接,返回new String,不在常量池- final 常量参与拼接 = 常量优化
final String a="a"; String b=a+"b"; //编译优化,常量池对象五、内存对象数量速记
String a="xyz":常量池 1 对象new String("xyz"):常量池 1 + 堆 1,共 2 个对象new String("a")+new String("b")常量池:"a"、"b";堆:2 个 String、1 个 StringBuilder、toString 新建 String,合计 6 个对象
六、字符串的常用方法(全部返回新串,原串不变)
长度 / 索引
length()获取长度(数组 length 属性,字符串 length () 方法)charAt(int index)根据下标取字符- charAt()根据索引求字符
indexOf(str)正向查找,无返回 - 1;lastIndexOf()反向查找
判断类
contains()是否包含子串startsWith()/endsWith()首尾匹配isEmpty()判断空串 "";isBlank()(JDK11) 判断全空白equals()区分大小写;equalsIgnoreCase()忽略大小写
转换
toCharArray()转字符数组;getBytes()转字节数组(可指定编码 UTF-8/GBK)toUpperCase()/toLowerCase()大小写trim()去除首尾空格
截取替换分割
substring(start)、substring(start,end)含头不含尾replace(原,新)普通替换;replaceAll(正则,新)支持正则split(正则)按规则分割为字符串数组,特殊符号 |. 需转义\\|
类型互转
- 数组转 String:
new String(char[])/String.valueOf(数组) - 基本类型转 String:
变量+""/String.valueOf(数值)
七、StringBuffer & StringBuilder(可变字符串缓冲区)
1. 区别
- StringBuffer:方法
synchronized加锁,线程安全,效率低 - StringBuilder:无锁,线程不安全,拼接性能高(单线程优先)
2. 通用方法
append()追加、reverse()翻转、delete(start,end)区间删除、insert()插入、replace()替换
注意
未重写 equals (),两个内容相同对象==、equals()均为 false
性能对比
String 对象不可变,+=循环每次拼接都会生成新字符串对象,频繁字符拷贝、产生大量垃圾,GC 开销大; StringBuilder/StringBuffer 对象可变,内部使用可变字符数组缓冲区,复用内存,仅在最后生成一次字符串,减少对象创建与字符复制,性能大幅提升。
八、字符串暴力匹配算法
功能:查找子串起始下标,无匹配返回 - 1
static int getIndex(String main,String sub){ int i=0,j=0; while(i<main.length()&&j<sub.length()){ if(main.charAt(i)==sub.charAt(j)){i++;j++;} else {i=i-j+1;j=0;} } return j==sub.length()?i-j:-1; }