语法基础
8种基本数据类型
基本类型是不同于类(Class)的特殊类型,在栈内存中管理
-
字符型 char
-
布尔型 boolean
-
数值型
不存在无符号的整型,范围也是固定的与环境无关- 整型
- byte 一个字节存储,范围-128~127,初始化时默认为0
- short 两个字节存储,范围-32768~32767,初始化时默认为0
- int 四个字节存储,范围-2147483648~2147483647,初始化默认为0
- long 八个字节存储,范围-9223372036854775808~9223372036854775807,初始化默认为0L
- 浮点型
- float
- double
- 整型
自动拆装箱
包装类型(Wrapper Class)
解决基本类型不面向对象(不继承Object)的缺陷,丰富了基本类型的操作
基本数据类型 | 包装类 |
---|---|
byte | Byte |
boolean | Boolean |
short | Short |
char | Character |
int | Integer |
long | Long |
float | Float |
double | Double |
拆箱与装箱
将基本类型和包装类型(箱指代包装类型)转换
在Java SE5 前实现装箱需要手动new一个包装类
在Java SE5 后实现了自动的装箱和拆箱
原理:
自动装箱都是通过包装类的 valueOf() 方法来实现的。
自动拆箱都是通过包装类对象的 xxxValue() 来实现的。
自动拆装箱与缓存
如果一个变量 p 的值是:
- -128 至 127 之间的整数
- true 和 false 的布尔值
- \u0000 至 \u007f 之间的字符
那么将p包装成a、b两个对象时,可以直接使用a==b判断a和b值是否相等,但是超过范围还是要用equals判断
可以通过java.lang.Integer.IntegerCache.high设置缓存最大值
其他类型的缓存机制也类似
自动拆装箱带来的问题
- 逻辑运算更复杂
- 如果包装对象为null,则可能拆箱时出现空指针错误
- 大量的拆装箱操作会浪费资源
布尔型接口最佳实践
成员变量名用 success 不加is前缀,存在序列化时出错的可能,自动生成set和get方法时也会有问题
类型用包装类,没有初值时是null,会抛出错误
String
字符串的不可变性
不可变对象:创建之后内部状态保持不变的对象,既不能更新引用也不能改变内部状态
正常的改变String变量实际上是创建了新的字符串
为什么设计成不可变
- 缓存
大量字符串的创建非常耗费资源,利用对字符串的缓存功能可以节省大量堆空间
JVM中专门开辟了一个空间存储字符串,称为字符串池 - 安全性
字符串不可修改来保存各种信息安全性更高 - 线程安全
多线程访问字符串或者更改字符串时,只会创建值不会修改值 - hashcode缓存
不可变性保证了字符串的值不会改变。因此,hashCode()方法在String类中被重写,以方便缓存,这样在第一次hashCode()调用期间计算和缓存散列,并从那时起返回相同的值。 - 性能
因为字符串不可变,所以可以用字符串池缓存,可以大大节省堆内存。而且还可以提前对hashcode进行缓存,更加高效
subString()方法
subString(int beginIndex, int endIndex) 方法的作用:
截取字符串并返回其[beginIndex,enIndex-1]范围内的内容
- JDK6中的subString:
在JDK6中String类包含三个成员变量:char valuep[], int offset,int count,分别存储真正的字符串数组,字符串第一个位置的索引,以及字符串的长度。
调用subString方法时,创建新的String对象,但是string的value指向堆中的同一个字符数组,只有count和offset不同
导致的问题:一个字符串被反复引用,无法被回收,可能导致内存泄漏 - JDK7中的subString:
为了解决JDK6中内存泄漏的风险,JDK7中subString方法会创建一个新的数组
replace相关方法
都用于替换字符
-
replace(CharSequence target,CharSequence replacement):用replacement替换所有的target,两个参数都是字符串
-
replaceAll(String regex,String replacement):replacement替换所有的regex匹配项,其中regex是个正则表达式,replacement是字符串
-
replaceFirst(String regex,String replacement):与replaceAll基本一致,但是只替换第一个匹配的部分
String的拼接
使用+拼接
Java中+对字符串进行拼接,类似于对运算符重载,但其实Java是不支持运算符重载的,这仅仅是一种语法糖。
底层的拼接是靠向StringBuilder中添加字符串然后toString
特殊情况:如果是两个字面量拼接,那么在编译期就会用一个拼接后的字符串(如 “a”+“b” 用"ab"替代)
阿里巴巴Java开发手册中不建议在循环体里使用+进行字符串拼接
concat方法
用法: String 拼接后string = string.concat(“字符串”).concat(String对象);
底层实现:先创建字符数组,后将待拼接的对象的字符串值复制到数组中,最后返回Stirng对象,本质上也是new了一个新的String
StringBuffer
String如果理解成字符串常量,那么StringBuffer类就是字符串变量,它的对象是可以扩充和修改的
用法 StringBuffer stringBuffer = stringBuffer对象.append(“字符串”).append(String对象);
底层实现:与Stirng类似的封装了一个字符数组value,但是并不是final的,可以修改,调用append将字符拷贝到内部的value中,如果长度不够还会进行扩展
此外,StringBuffer线程安全的
StringBuilder
用法和StringBuffer基本一致
底层实现也基本一致,但是线程不安全
StringUtils.join
StringUtils由apache.commons提供,其中的join方法可以拼接字符串
用法 StringUtils.join(“字符串”,Strng对象,…);
也可以将数组或集合以拼接符拼接到一起形成字符串如
String []list ={"Univero","更新Java相关技术文章"};
String result= StringUtils.join(list,",");
System.out.println(result);
//结果:Univero,更新Java相关技术文章
底层实现:底层也是通过StringBuilder实现的
StringJoiner
StringJoiner是java.util包中的一个类,用于构造一个由分隔符分隔的字符序列(可选),并且可以从提供的前缀开始并以提供的后缀结尾。虽然这也可以在StringBuilder类的帮助下在每个字符串之后附加分隔符,但StringJoiner提供了简单的方法来实现,而无需编写大量代码。
用法:StringJoiner(CharSequence delimiter,CharSequence prefix,CharSequence suffix),参数依次为分隔符、前缀、后缀
拼接时使用add()方法,用法与StringBuilder的append方法类似
底层实现:依赖于StringBuilder
为什么使用StringJoiner:
可以简化Stream通过Collectors.joining(CharSequence)拼接字符串,如
list.stream().collect(Collectors.joining(":"));
Java 8中提供了StringJoiner来丰富Stream的用法,而且StringJoiner也可以方便的增加前缀和后缀。
效率比较
StringBuilder < StringBuffer < concat < + < StringUtils.join
+ 每次都要创建StirngBuilder,再将StringBuilder,再进行append,会耗费更多的时间
所以,阿里巴巴Java开发手册建议:循环体内,字符串的连接方式,使用 StringBuilder 的 append 方法进行扩展。而不要使用+。
1、如果不是在循环体中进行字符串拼接的话,直接使用+就好了。
2、如果在并发场景中进行字符串拼接的话,要使用StringBuffer来代替StringBuilder。
3、如果是通过一个List进行字符串拼接,则考虑使用StringJoiner。
String.valueOf 和 Integer.toString的区别
int i = 5;String i1 = "" + i;
String i2 = String.valueOf(i);
String i3 = Integer.toString(i);
上述三种方式都可以将int类型转换成字符串
第一种方法其实是String i1 = (new StringBuilder()).append(i).toString();
而第二种和第三种方法没有区别,因为String.valueOf(i)也是调用Integer.toString(i)来实现的。
switch对String类型的支持
字符串的switch是通过equals()和hashCode()方法来实现的。记住,switch中只能使用整型,比如byte。short,char(ASCII码是整型)以及int。而hashCode返回的是int
String的长度限制
字符串有长度限制,在编译期,要求字符串常量池中的常量不能超过65535,并且在javac执行过程中控制了最大值为65534。
在运行期,长度不能超过Int的范围,否则会抛异常。