淮安专业做网站,哈尔滨专业建站免费咨询,千万别学建筑工程技术,深圳宝安区新安街道自动装箱和拆箱
Java中基础数据类型与它们对应的包装类见下表#xff1a;
原始类型包装类型booleanBooleanbyteBytecharCharacterfloatFloatintIntegerlongLongshortShortdoubleDouble
装箱#xff1a;将基础类型转化为包装类型。
拆箱#xff1a;将包装类型转化为基础类…自动装箱和拆箱
Java中基础数据类型与它们对应的包装类见下表
原始类型包装类型booleanBooleanbyteBytecharCharacterfloatFloatintIntegerlongLongshortShortdoubleDouble
装箱将基础类型转化为包装类型。
拆箱将包装类型转化为基础类型。
当基础类型与它们的包装类有如下几种情况时编译器会自动帮我们进行装箱或拆箱
赋值操作装箱或拆箱进行加减乘除混合运算 拆箱进行,,比较运算拆箱调用equals进行比较装箱ArrayList、HashMap等集合类添加基础类型数据时装箱
示例代码
Integer x 1; // 装箱 调⽤ Integer.valueOf(1)
int y x; // 拆箱 调⽤了 X.intValue()看一道常见的面试题
Integer a 100;
Integer b 100;
System.out.println(a b);Integer c 200;
Integer d 200;
System.out.println(c d);输出
true
false为什么第三个输出是false看看 Integer 类的源码。
public static Integer valueOf(int i) {if (i IntegerCache.low i IntegerCache.high)return IntegerCache.cache[i (-IntegerCache.low)];return new Integer(i);
}Integer c 200; 会调用 调⽤Integer.valueOf(200)。而从Integer的valueOf()源码可以看到这里的实现并不是简单的new Integer而是用IntegerCache做一个cache。
private static class IntegerCache {static final int low -128;static final int high;static final Integer cache[];static {// high value may be configured by propertyint h 127;String integerCacheHighPropValue sun.misc.VM.getSavedProperty(java.lang.Integer.IntegerCache.high);if (integerCacheHighPropValue ! null) {try {int i parseInt(integerCacheHighPropValue);i Math.max(i, 127);// Maximum array size is Integer.MAX_VALUEh Math.min(i, Integer.MAX_VALUE - (-low) -1);} catch( NumberFormatException nfe) {// If the property cannot be parsed into an int, ignore it.}}high h;}...
}这是IntegerCache静态代码块中的一段默认Integer cache 的下限是-128上限默认127。当赋值100给Integer时刚好在这个范围内所以从cache中取对应的Integer并返回所以a和b返回的是同一个对象所以比较是相等的当赋值200给Integer时不在cache 的范围内所以会new Integer并返回当然比较的结果是不相等的。
String 为什么不可变
先看看什么是不可变的对象。
如果一个对象在它创建完成之后不能再改变它的状态那么这个对象就是不可变的。不能改变状态的意思是不能改变对象内的成员变量包括基本数据类型的值不能改变引用类型的变量不能指向其他的对象引用类型指向的对象的状态也不能改变。
接着来看Java8 String类的源码
public final class Stringimplements java.io.Serializable, ComparableString, CharSequence {/** The value is used for character storage. */private final char value[];/** Cache the hash code for the string */private int hash; // Default to 0
}从源码可以看出String对象其实在内部就是一个个字符存储在这个value数组里面的。
value数组用final修饰final 修饰的变量值不能被修改。因此value不可以指向其他对象。
String类内部所有的字段都是私有的也就是被private修饰。而且String没有对外提供修改内部状态的方法因此value数组不能改变。
所以String是不可变的。
那为什么String要设计成不可变的
主要有以下几点原因
线程安全。同一个字符串实例可以被多个线程共享因为字符串不可变本身就是线程安全的。支持hash映射和缓存。因为String的hash值经常会使用到比如作为 Map 的键不可变的特性使得 hash 值也不会变不需要重新计算。出于安全考虑。网络地址URL、文件路径path、密码通常情况下都是以String类型保存假若String不是固定不变的将会引起各种安全隐患。比如将密码用String的类型保存那么它将一直留在内存中直到垃圾收集器把它清除。假如String类不是固定不变的那么这个密码可能会被改变导致出现安全隐患。字符串常量池优化。String对象创建之后会缓存到字符串常量池中下次需要创建同样的对象时可以直接返回缓存的引用。
既然我们的String是不可变的它内部还有很多substring replace replaceAll这些操作的方法。这些方法好像会改变String对象怎么解释呢
其实不是的我们每次调用replace等方法其实会在堆内存中创建了一个新的对象。然后其value数组引用指向不同的对象。
为何JDK9要将String的底层实现由char[]改成byte[]?
主要是为了节约String占用的内存。
在大部分Java程序的堆内存中String占用的空间最大并且绝大多数String只有Latin-1字符这些Latin-1字符只需要1个字节就够了。
而在JDK9之前JVM因为String使用char数组存储每个char占2个字节所以即使字符串只需要1字节它也要按照2字节进行分配浪费了一半的内存空间。
到了JDK9之后对于每个字符串会先判断它是不是只有Latin-1字符如果是就按照1字节的规格进行分配内存如果不是就按照2字节的规格进行分配这样便提高了内存使用率同时GC次数也会减少提升效率。
不过Latin-1编码集支持的字符有限比如不支持中文字符因此对于中文字符串用的是UTF16编码两个字节所以用byte[]和char[]实现没什么区别。
String, StringBuffer 和 StringBuilder区别
1. 可变性
String 不可变StringBuffer 和 StringBuilder 可变
2. 线程安全
String 不可变因此是线程安全的StringBuilder 不是线程安全的StringBuffer 是线程安全的内部使用 synchronized 进行同步
什么是StringJoiner
StringJoiner是 Java 8 新增的一个 API它基于 StringBuilder 实现用于实现对字符串之间通过分隔符拼接的场景。
StringJoiner 有两个构造方法第一个构造要求依次传入分隔符、前缀和后缀。第二个构造则只要求传入分隔符即可前缀和后缀默认为空字符串。
StringJoiner(CharSequence delimiter, CharSequence prefix, CharSequence suffix)
StringJoiner(CharSequence delimiter)有些字符串拼接场景使用 StringBuffer 或 StringBuilder 则显得比较繁琐。
比如下面的例子
ListInteger values Arrays.asList(1, 3, 5);
StringBuilder sb new StringBuilder(();for (int i 0; i values.size(); i) {sb.append(values.get(i));if (i ! values.size() -1) {sb.append(,);}
}sb.append());而通过StringJoiner来实现拼接List的各个元素代码看起来更加简洁。
ListInteger values Arrays.asList(1, 3, 5);
StringJoiner sj new StringJoiner(,, (, ));for (Integer value : values) {sj.add(value.toString());
}另外像平时经常使用的Collectors.joining(,)底层就是通过StringJoiner实现的。
源码如下
public static CollectorCharSequence, ?, String joining(CharSequence delimiter,CharSequence prefix,CharSequence suffix) {return new CollectorImpl(() - new StringJoiner(delimiter, prefix, suffix),StringJoiner::add, StringJoiner::merge,StringJoiner::toString, CH_NOID);
}String 类的常用方法有哪些
indexOf()返回指定字符的索引。charAt()返回指定索引处的字符。replace()字符串替换。trim()去除字符串两端空白。split()分割字符串返回一个分割后的字符串数组。getBytes()返回字符串的 byte 类型数组。length()返回字符串长度。toLowerCase()将字符串转成小写字母。toUpperCase()将字符串转成大写字符。substring()截取字符串。equals()字符串比较。
new String(dabin)会创建几个对象
使用这种方式会创建两个字符串对象前提是字符串常量池中没有 dabin 这个字符串对象。
dabin 属于字符串字面量因此编译时期会在字符串常量池中创建一个字符串对象指向这个 dabin 字符串字面量使用 new 的方式会在堆中创建一个字符串对象。
什么是字符串常量池
字符串常量池String Pool保存着所有字符串字面量这些字面量在编译时期就确定。字符串常量池位于堆内存中专门用来存储字符串常量。在创建字符串时JVM首先会检查字符串常量池如果该字符串已经存在池中则返回其引用如果不存在则创建此字符串并放入池中并返回其引用。
String最大长度是多少
String类提供了一个length方法返回值为int类型而int的取值上限为2^31 -1。
所以理论上String的最大长度为2^31 -1。
达到这个长度的话需要多大的内存吗
String内部是使用一个char数组来维护字符序列的一个char占用两个字节。如果说String最大长度是2^31 -1的话那么最大的字符串占用内存空间约等于4GB。
也就是说我们需要有大于4GB的JVM运行内存才行。
那String一般都存储在JVM的哪块区域呢
字符串在JVM中的存储分两种情况一种是String对象存储在JVM的堆栈中。一种是字符串常量存储在常量池里面。
什么情况下字符串会存储在常量池呢
当通过字面量进行字符串声明时比如String s 程序新大彬;这个字符串在编译之后会以常量的形式进入到常量池。
那常量池中的字符串最大长度是2^31-1吗
不是的常量池对String的长度是有另外限制的。。Java中的UTF-8编码的Unicode字符串在常量池中以CONSTANT_Utf8类型表示。
CONSTANT_Utf8_info {u1 tag;u2 length;u1 bytes[length];
}length在这里就是代表字符串的长度length的类型是u2u2是无符号的16位整数也就是说最大长度可以做到2^16-1 即 65535。
不过javac编译器做了限制需要length 65535。所以字符串常量在常量池中的最大长度是65535 - 1 65534。
最后总结一下
String在不同的状态下具有不同的长度限制。
字符串常量长度不能超过65534堆内字符串的长度不超过2^31-1