JavaEE - 包装类、String 类

一、包装类

出现原因: java为纯面向对象语言,但是8种基本数据类型不能new对象,破坏了java为纯面向对象语言的特征,所以java为8种基本数据类型分别匹配了对应的类,这种类叫做包装类/封装类

Java中的基本数据类型没有方法和属性,而包装类就是为了让这些拥有方法和属性,实现对象化交互。

1. 基本数据类型的包装类

基本数据类型包装类
byteByte
shortShort
intInteger
longLong
floatFloat
doubleDouble
charCharacter
booleanBoolean

2. 层次结构

数值型包装类都继承至Number,而字符型和布尔型继承至Object。

包装类的层次结构

3. 基本数据和包装类之间的转换

应用场景:
集合只能存引用数据类型,如果要想存基本数据类型,可以把基本数据类型转换为对应包装类的对象

自jdk1.5开始,Java增加了对基本数据类型的自动装箱和自动拆箱操作。

装箱:基本数据类型转换为包装类。

手动装箱:

//利用构造函数
Integer integer1 = new Integer(100); 
//利用包装类中的静态方法
Integer integer2 = Integer.valueOf(100); 

自动装箱:

//直接把一个基本数据类型赋值给包装类
Integer integer1  = 100;
//底层实现:Integer integer1 = Integer.valueOf(100);

拆箱:包装类转换为基本数据类型。

手动拆箱:

//返回包装类对象integer1对应的基本数据
int int1= integer1.intValue();  

自动拆箱:

//直接把一个包装类对象,赋值给基本类型
int int2 = new Integer(100); 
//底层实现:int int2 = new Integer(100).intValue();

通过包装类Integer.toString()将整型转换为字符串;

String str1 = Integer.toString(100);

通过Integer.parseInt()将字符串转换为int类型;

int int1 = Integer.parseInt("100");

通过valueOf()方法把字符串转换为包装类然后自动拆箱;

int int2 = Integer.valueOf("110");

需求:将字符串数组转换为int数组

String[] ss = {"1","2","3","4","5","6"};
int[] is = new int[ss.length];
for (int i = 0; i < ss.length; i++) {
	String str = ss[i];
    //将字符串转换为int
	int num = Integer.parseInt(str);
	is[i] = num;
}

4. 深入包装类

整数缓冲区

Java为了提高拆装箱效率,在执行过程中提供了一个缓存区(对象池)(类似于常量数组)。整型对象通过使用相同的对象引用实现了缓存和复用。

如果传入的参数是在整数值区间-128 至 +127,会直接去缓存查找数据,如果有就对已创建的对象进行复用,如果没有就隐式调用new方法创建

只适用于自动装箱。使用构造函数创建对象不适用。

//利用构造方法
//one和对two是两个不同的对象
Integer one = new Integer(100);
Integer two = new Integer(100);
// == 在比较对象时比较的是内存地址,one和two是两个是不同的空间,放的值相同
System.out.println(one == two);//false

//自动装箱
//这时缓存区没有,就会构造一个对象
Integer three = 100;//底层实现:Integer three=Integer.valueOf(100);

//在创建对象之前先从IntegerCache.cache中寻找。如果没找到才使用new新建对象。
// 这时缓存区有,就会直接取来复用
Integer four = 100;//底层实现:Integer four=Integer.valueOf(100);
System.out.println(three == four);//true

//这里为200,超出了缓存区数组 [-128, 127],所以都需要新建
Integer five = 200;
Integer six = 200;
System.out.println(five == six);//false

二、String 类

String是不可变类,即一旦一个String对象被创建,包含在这个对象中的字符序列是不可改变的,直至该对象被销毁。String类是final类,不能有子类。

String为什么设计成不可变类?

  1. 符合Java字符串池的设计方式
String str1="abc";
String str2="abc";

Java通过字符串池的设计方式节省内存空间,如上面一段代码只会生成一个对象放在常量池当中。str1和str2都指向这个对象,如果String类可变,通过str1这个引用就可以修改这个对象,那其他引用就会受影响。

  1. 安全性

JDK提供的众多API当中,大多的参数都是String类型,如类加载函数,数据库的连接,Sql语句,Socket的参数等。如果String类可以被修改就会造成安全漏洞。而且多线程情况下,String类数据也可以保护数据不被其他线程修改。

String怎么实现不可变?

public final class String  
    implements java.io.Serializable, Comparable<String>, CharSequence {  
    private final char value[];
    ...

从String类的源码中可以看到,String是通过char value[]保存字符的。而且声明为private final,并且不提供我们写value的接口。所以String类不能够修改。

1. String类的使用

String str = "123abc";
//在目标字符串末尾追加新的字符串,并返回
System.out.println(str.concat("DEF123"));//123abcDEF123
//从开始下标处截取到字符串末尾,并返回
System.out.println(str.substring(2));//3abc
//从开始下标处(包含)截取到结束下标处(不包含),并返回
System.out.println(str.substring(1, 3));//23
//字母转小写,并返回
System.out.println(str.toLowerCase());//123abc
//字母转大写,并返回
System.out.println(str.toUpperCase());//123ABC

String str = "  123 abc DEF 123   ";
//去除首尾空格
System.out.println(str.trim());//123 abc DEF 123
//替换字符,并返回
System.out.println(str.replace('2', 'x'));//  1x3 abc DEF 1x3   
//替换第一个出现的字符串,并返回
System.out.println(str.replaceFirst("1", "哈"));//  哈23 abc DEF 123   
//替换字符串,并返回
System.out.println(str.replaceAll(" ", ""));//123abcDEF123
String str = "哈x3abcDEF1x3";
//判断两个字符串是否相等(区分大小写)
System.out.println(str.equals("哈x3ABCdef1x3"));//false
//判断两个字符串是否相等(不区分大小写)
System.out.println(str.equalsIgnoreCase("哈x3ABCdef1x3"));//true
//判断目标字符串是否以某个字符串开头
System.out.println(str.startsWith("哈"));//true
//判断目标字符串是否以某个字符串结尾
System.out.println(str.endsWith("DEF1x3"));//true
//获取指定下标上的字符
System.out.println(str.charAt(3));//a
//查询字符第一次出现的下标
System.out.println(str.indexOf("3"));//2
//查询字符最后一次出现的下标
System.out.println(str.lastIndexOf("3"));//11
//获取字符个数
System.out.println(str.length());//12

面试题:以下代码创建了几个String对象

//创建了1个String对象(“abc”在常量池中是唯一个)
String str1 = "abc";
String str2 = "abc";
System.out.println(str1 == str2);//true
//创建了3个String对象("abc"算一个,然后又new了两个)
String str1 = new String("abc");
String str2 = new String("abc");
System.out.println(str1 == str2);//false:判断的是内存地址
System.out.println(str1.equals(str2));//true:判断的是内容

2. StringBuilder类

在这里插入图片描述

StringBuilder称为字符串缓冲区,代表可变的字符序列。

工作原理:预先申请一块内存,存放字符序列,如果字符序列满了,会重新改变缓存区的大小,以容纳更多的字符序列。StringBuilder是可变对象,这个是和String最大的不同。

创建StringBuilder对象

//创建StringBuilder的对象(默认字符串缓冲区,初始容量16个字符)
StringBuilder sb1 = new StringBuilder();

//创建StringBuilder的对象(指定字符串缓冲区,初始容量20个字符)
StringBuilder sb2 = new StringBuilder(20);

//创建StringBuilder的对象(指定字符串缓冲区,初始容量16 + "123abc".length()个字符)
StringBuilder sb3 = new StringBuilder("123abc");

StringBuilder常用方法

StringBuilder sb = new StringBuilder("123abc");
//追加字符串,并返回
System.out.println(sb.append("DEF123"));//123abcDEF123
//在指定下标处插入字符串,并返回
System.out.println(sb.insert(6, "哈哈哈"));//123abc哈哈哈DEF123
//替换指定下标上的字符,无返回值
sb.setCharAt(7, '啊');
System.out.println(sb);//123abc哈啊哈DEF123
//从开始下标处(包含)替换到结束下标处(不包含),并返回
System.out.println(sb.replace(6, 9, "嘿嘿嘿"));//123abc嘿嘿嘿DEF123
//删除指定下标处的字符,并返回
System.out.println(sb.deleteCharAt(2));//12abc嘿嘿嘿DEF123
//从开始下标处(包含)删除到结束下标处(不包含),并返回
System.out.println(sb.delete(5, 8));//12abcDEF123
//反转字符串,并返回
System.out.println(sb.reverse());//321FEDcba21
//获取字符长度
System.out.println(sb.length());//11

3. StringBuffer类

StringBuffer与StringBuilder的用法完全一致,StringBuffer和StringBuilder类的区别也在于StringBuffer是线程安全的,很多方法都有synchronized关键字。所以StringBuilder的性能要比StringBuffer要好。多数情况下建议使用 StringBuilder 类。

//StringBuffer类的源码   
public synchronized void ensureCapacity(int minimumCapacity) {
        ...
    }
    public synchronized void trimToSize() {
        super.trimToSize();
    }
	...

单线程推荐使用StringBuilder,多线程使用StringBuffer。

4. String创建对象的问题

String str1 = "abc";
String str2 = "abc";
System.out.println(str1 == str2);//true

//"ab"和"c"都是常量,编译时直接拼接
String str3 = "ab" + "c";//编译时代码直接是String str3 = "abc";
System.out.println(str3 == str1);//true

final String s1 = "ab";
final String s2 = "c";
//s1和s2都是常量,编译时直接拼接
String str4 = s1+s2;
System.out.println(str4 == str1);//true

String s3 = "ab";
String s4 = "c";
//两个 变量 底层会new StringBuilder
String str5 = s3+s4;//底层:new StringBuilder(s3).append(s4).toString();
System.out.println(str5 == str1);//false

频繁的拼接String请使用StringBuilder或StringBuffer

//获取1970年1月1日0:0:0到现在的毫秒值
long startTime = System.currentTimeMillis();
String str = "椎名真白";
for (int i = 0; i < 10001; i++) {
	str = str + "小可爱,皇冠给你带";
    //new了10001次对象,非常的耗时
	//底层:str = new StringBuilder(str).append("小可爱,皇冠给你带").toString();
}
//获取1970年1月1日0:0:0到现在的毫秒值
long endTime = System.currentTimeMillis();
System.out.println("运行时长:" + (endTime-startTime));//610ms
//获取1970年1月1日0:0:0到现在的毫秒值
long startTime = System.currentTimeMillis();
StringBuilder sb = new StringBuilder("樱泽墨");
for (int i = 0; i < 10001; i++) {
    sb.append("小可爱,皇冠给你带");
}
//获取1970年1月1日0:0:0到现在的毫秒值
long endTime = System.currentTimeMillis();
System.out.println("运行时长:" + (endTime-startTime));//2ms

扩展:

面试题1: String为什么不可变?

String类源代码中有一个char数组,并且这个char数组是被final修饰的。因为数组一旦创建长度不可变。并且被final修饰的引用一旦指向某个对象之后,不可在指向其它对象,所以String是不可变的!

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    /** The value is used for character storage. */
    private final char value[];
    ...

面试题2: StringBuffer和StringBuilder为什么是可变的?

StringBuffer和StringBuilder内部实际上是一个char[ ]数组,这个char[ ]数组没有被final修饰,StringBuffer和StringBulider的初始化容量为16,当存满之后会进行扩容,底层调用了数组拷贝的方法:System.arraycopy()…扩容的,所以StringBuffer和StringBuilder适用于字符串的频繁拼接操作,并且StringBuffer是线程安全的,StringBuilder是非线程安全的。

父类 AbstractStringBuilder.java

abstract class AbstractStringBuilder implements Appendable, CharSequence {
	char[] value;
	...
    AbstractStringBuilder() {
    }
   
    AbstractStringBuilder(int capacity) {
        value = new char[capacity];
    }
    ...

子类 StringBuilder.java

public final class StringBuilder
    extends AbstractStringBuilder
    implements java.io.Serializable, CharSequence
{
    ...
   	public StringBuilder() {
        super(16);
    }
    public StringBuilder(int capacity) {
        super(capacity);
    }
    ...
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值