String类型与八大包装类型
学习内容
String类型
String类型是引用数据类型,他的包路径是:java.lang.String
String类型在底层是一个char数组(Java9开始改为byte数组)
在Java中规定,双引号括起来的都是字符串对象,是不可变的,被放到字符串常量池中。
为什么要把双引号括起来的String对象放到字符串常量池中?
因为字符串在实际开发过程中使用频繁,为了执行效率,所以把字符串放到字符串常量池中。
String对象的创建方式
- 使用双引号""括起来的都是String对象,这种方式创建的对象都是存放在字符串常量池中
- 使用构造方法创建String对象,这种方式创建的对象都是存放在堆内存中
* public String(byte[] bytes) -----> 使用byte数组创建字符串对象
* public String(byte[] bytes,int offset,int count) -----> 使用byte数组指定位置创建字符串对象
* public String(char[] chars) -----> 使用char数组创建字符串对象
* public String(char[] chars,int offset,int count) -----> 使用char数组指定位置创建字符串对象
* public String(String original) -----> 使用字符串对象创建字符串对象
String字符串存储原理
先看下面程序
public class Test2 {
public static void main(String[] args){
String str1 = "abc";
String str2 = "abc";
String str3 = "bcd" + "efg";
String str4 = new String("abc");
String str5 = new String("abc");
String str6 = new String("bcd");
}
}
内存图如下:
通过内存图可以看出使用双引号括起来的字符串都放到 String Table(字符串常量池) 中;使用构造方法创建的字符串都放到了堆内存中。
那么问题来了,在上面的程序中一共创建了多少个String对象?
知道了程序的内存结构,我们来分析一下下面的程序
public class Test2 {
public static void main(String[] args){
String str1 = "abc";
String str2 = "abc";
String str3 = "bcd" + "efg";
String str4 = new String("abc");
String str5 = new String("abc");
String str6 = new String("bcd");
System.out.println(str1 == str2);
System.out.println(str4 == str5);
System.out.println(str1 == str4);
}
}
对于上面的程序,控制台输出的结果是什么?
因为"=="运算符比较的是变量的值,而引用数据类型中存放的值都是对象的内存地址,通过内存图我们可以看出
str1(0x111) == str2(0x111) ----> 内存地址相同,结果为true
str4(0x555) == str5(0x666) ----> 内存地址不相同,结果为false
str1(0x111) == str4(0x555) ----> 内存地址不相同,结果为false
String类型常用方法
比较
完成功能 | 方法 |
比较两个字符串是否相同 | public boolean equals(Object) |
忽略大小写比较两个字符串是否相同 | public boolean equalsIgnoreCase(String) |
按字典顺序比较字符串的大小 | public int compareTo(String) |
- equal方法具有对称性,即"s1.equal(s2)"与"s2.equal(s1)"效果一样
- 如果比较一个常量一个变量,建议将变量写在前面,即 “常量字符串”.equals(s2),这么写可以避免空指针异常
获取
功能 | 方法 |
获取当前字符串的字符个数 | public int length() |
拼接字符串(创建了新对象) | public String concat(String) |
获取当前字符串指定索引位置的单个字符 | public char charAt(int) |
返回字符串在当前字符串中第一次出现的索引位置 | public int indexOf(String) |
字符串截取(从指定索引位置开始直到到结束) | public String substring(int) |
字符串截取(从指定索引位置开始到指定索引位置结束)(左闭右开) | public String substring(int,int) |
转换
功能 | 方法 |
转换成字符数组 | public char[] toCharArray() |
转换成字节数组 | public byte[] getBytes() |
分割字符串 | 如果要使用.分割需要转义字符 public String[] split(String) |
替换字符串 (在当前字符串中将后一个参数字符替换前一个参数字符) | public String replace(char,char) |
替换字符串 (在当前字符串中将后一个参数字符串替换前一个参数字符串) | public String replace(charSecqence,charSecqence) |
转换大写 | public String toUpperCase() |
转换小写 | public String toLowerCase() |
String类还有一个静态方法String.valueOf(多类型重载),当该方法参数为Object是会调用该Object的toString()
字符串的拼接
使用字符串连接符"+"进行拼接
- 由于字符串是不可变的,所以在每次拼接后都会产生新的字符串,这样会占用大量的字符串常量池内存,造成空间浪费。
- 使用String实例方法concat(String)
- 调用concat()拼接字符串,方法内部代码执行会产生一个新的String对象
- 使用StringBuffer或StringBuilder
- 使用StringBuffer或StringBuilder追加内容,如果追加后不超出StringBuffer或StringBuilder的容量,那么并不会产生新的对象
StringBuffer和StringBuilder
String与StringBuffer区别:
String和StringBuffer底层都是数组(Java9之前是char数组,Java9(包括9)之后是byte数组。),String底层的数组被final修饰,StringBuffer底层的数组没有被final修饰,所以StringBuffer可以扩容,String不能进行扩容StringBuffer与StringBuilder区别
StringBuffer中方法都被synchronize关键字修饰,表示在多线程环境下运行时安全的,而StringBuilder中的方法没有被synchronize关键字修饰,所以StringBuilder时非线程安全的。
八大包装类型
为什么要使用包装类型?
在调用方法时,若方法参数是引用数据类型(Object类型),那么使用基本数据类型是无法传参进去的,所以需要包装类间接传参。
八种基本数据类型对应的八种包装类型都是什么?
基本类型 | 包装类型 |
byte | java.lang.Byte(父类Number) |
short | java.lang.Short(父类Number) |
int | java.lang.Integer(父类Number) |
long | java.lang.Long(父类Number) |
float | java.lang.Float(父类Number) |
double | java.lang.Double(父类Number) |
boolean | java.lang.Boolean(父类Object) |
char | java.lang.Character(父类Object) |
八种包装类常用的属性和方法(以Integer为例)
因为八种包装类的方法基本相似,所以通过学习Integer的常用方法可以对其他包装类照葫芦画瓢。
属性
• public static final int MIN_VALUE; -------> 获取int类型的最小值
• public static final int MAX_VALUE; -------> 获取int类型的最大值
构造方法
• public Integer(int); -------> 通过一个int类型数据创建一个Integer对象
• public Integer(String); -------> 通过一个String类型的数据创建一个Integer对象
如果构造方法中传入的实参是String类型,在程序运行时可能会出现数值格式化异常——NumberFormatException
实例方法
• public int intValue(); -------> 将当前Integer类型转换成int类型
• public float floatValue(); -------> 将当前Integer类型对象转换成float类型
- 等等…
对于所有的Number类型对象,都可以任意转换成相应的基本数据类型(byte,short,int,long,float,double)
静态方法
• public static int praseInt(String);
• public static Integer valueOf(int/String);
• public staitc toStirng(int);
• public static toBinaryString(int);
• public static toOctalString(int);
• public staitc toHexString(int);
手动装箱与手动拆箱
public static void main(String[] args){
//手动装箱
Integer i1 = new Integer(10);
//手动拆箱
int i2 = i1.intValue();
}
自动装箱与自动拆箱(Java5新特性)
public static void main(String[] args){
//自动装箱
Integer i1 = 10;
//自动拆箱
int i2 = i1;
}
包装类使用算数运算符(+ - * / %)进行算数运算时都会执行自动拆箱动作
整数型常量池
在Java中为了执行效率,在类加载时刻会在方法区中会声明并初始化一个长度为256的Integer静态常量数组,其中存储了范围在[-128,127]中的Integer对象,我们将这个Integer数组为称作整数型常量池(下图是Java8源码)
Integer面试题
public class Test2 {
public static void main(String[] args){
Integer i1 = 127;
Integer i2 = 127;
Integer i3 = 128;
Integer i4 = 128;
System.out.println(i1 == i2);
System.out.println(i3 == i4);
}
}