装箱与拆箱
包装类
包装类:基本类型对应的引用类型的类
基本类型: byte,short,int, long ,float, double, char, boolean
对应包装类:Byte,Short,Integer,Long,Float, Double, Character,Boolean
包装类装箱
装箱:从基本类型转换为对应的包装类对象。
1.以对应的基本类型为参数的构造器方式 Integer(int value) ,构造器2:Integer(String s)
2.包装类型的静态方法:static Integer valueOf(int i)
3.自动装箱(jdk5.0之后有的,由jvm隐式进行操作)Integer i=5;【推荐】
包装类拆箱
拆箱:从包装类对象转换为对应的基本类型。
1.包装类型的实例方法 int intValue()
2.自动拆箱 int value=new Integer(123);jdk5.0,由jvm隐式操作。【推荐】
基本类型---》字符串类型
基本类型转为字符串类型
1.字符串连接符 String str1=123+“”;
2.包装类的实例方法toString: String str2=new Integer(234).toString();
3.包装类型的静态方法toString: String str3=Integer.toString(235);
4.String的静态方法: String str4=String .valueOf(456) 【推荐】
字符串类型---》基本类型
字符串类型转基本类型
1.包装类型的构造器,再进行自动拆箱 int num01=new Integer(“123”)
2.包装类型的静态方法 parseInt(String s) : int num02= Integer.parseInt(“123”);【推荐】
注意事项:
Character的构造器只有:Character(char value) 。
Character类型没有提供字符串转char类型的方法
字符中内容必须在基本类型的取值范围内,否则发生数字格式化异常
包装类的笔试题1
当通过自动装箱方式的创建整数类的包装类对象,如果在-128-127,直接在整数常量池拿。不在的话再去堆里new
public static void main(String[] args) {
int num01=987;
int num02=987;
System.out.println(num01==num02);//true
Integer num03=new Integer(654);
Integer num04=new Integer(654);
System.out.println(num03==num04);//false
Integer num031=new Integer(120);
Integer num041=new Integer(120);
System.out.println(num031==num041);//false
// 当通过自动装箱方式的创建整数类的包装类对象,如果在-128-127,直接在整数常量池拿。不再的话再去堆里new
Integer num05=321;
Integer num06=321;
System.out.println(num05==num06);//false
Integer num07=123;
Integer num08=123;
System.out.println(num07==num08);//true
Integer num09=200;
int num10=200;
System.out.println(num09==num10);//true 一切从简原则
System.out.println(num10==num09);//true
}
包装类的笔试题2
public class Demo {
public static final Integer num01=200;
public static final Integer num02=200;
public static final Integer num03=100;
public static final Integer num04=100;
public static final Integer num05;
public static final Integer num06;
public static final Integer num07;
public static final Integer num08;
static{
num05=200;
num06=200;
num07=100;
num08=100;
}
public static void main(String[] args) {
// 比较的是引用类型,在-128-127,从整数常量池拿
System.out.println(num03==num07);//true
// 比较的是引用类型,超过-128-127从堆里new
System.out.println(num01==num02);//false
System.out.println(num05==num06);//false
System.out.println(num01==num05);//false
// 加法只能加基本类型,包装类型进行4则运算时,先进行自动拆箱再计算
System.out.println(num01==(num03+num04));//true
System.out.println(num05==(num07+num08));//true
}
}
包装类的笔试题3
public static void main(String[] args) {
// 在常量池中不存在浮点数。
Double v01=0.0;
Double v02=0.0;
System.out.println(v01==v02);//false
// 字符常量池:存0-127 对应的字符。就是AscII码
Character var03=129;
Character var04=129;
System.out.println(var03==var04);//false
Character var031=127;
Character var041=127;
System.out.println(var031==var041);//true
// 布尔常量池:
Boolean var05=true;
Boolean var06=true;
System.out.println(var05==var06);//true
}
String类
创建String类对象
java.lang
第一种:字面值直接赋值。String str1="abc";
第二种:new的方式
public static void main(String[] args) {
//字面值直接赋值
String str1="abc";
//String() 无参构造器方式
String str2=new String();创建长度为0的数组,类似于“”
//字节数组方式【和环境编码有关】
byte[] bytes={97,100,101};//字节是一个整数,一个汉字3个字节,一个英文一个字节
String str3=new String(bytes);
System.out.println("str3 "+str3);//ade
//指定范围的字节数组方式
byte[] bytes2={97,100,101,102};
String str4=new String(bytes2,1, 2);
System.out.println("str4 "+str4);//de
//字符数组
char[] chars={'a','b','c'};
String str5=new String(chars);
System.out.println("str5 "+str5);//abc
//指定范围的字符数组方式
char[] chars2={'a','b','c','d','e'};
String str6=new String(chars2,1,2);
System.out.println("str6 "+str6);//bc 从0索引开始
//通过另外一个字符串对象创建副本
String str7=new String("abc");//实际开发不用的
// String(StringBuffer buffer)
// String(StringBuilder builder)
}
字符串数据结构
程序中所有的字符串字面值都是String类的对象,包括""
字符串是常量,它的值在创建后不能再变。
究其原因:String类的底层数据结构是一个被final修饰的数组。private final char value[];
String类底层数组的数据类型是什么:
- jdk 8.0前(包含jdk8.0),是char[]数组
- jdk 9.0后(包含jdk9.0), 是byte[]数组
char[]数组转为byte[]数组有什么好处?主要是节省字符串占用的内存空间
1、从时间复杂度:计算机中一切数据皆为字节。
如果是char[]数组,jvm底层会先将char[]数组的元素转换为字节,再由字节转为二进制位。
如果是是byte[]数组,jvm底层操作时会直接将byte[]数组中的元素转为二进制位/22、从空间复杂度:占用内存越少越好
2.1 如果是char[]数组,字符串存储“abc”,每个字母字符占用2个字节(按照内码标准),共占6个字节; 字符串存储“小羽毛”,每个汉字字符占用2个字节(按照内码标准),共占6个字节2.2如果是是byte[]数组,根据开发环境编码决定存储规则。字符串存储“abc”,开发环境为utf-8,在utf-8中每个字母字符占用1个字节,总共占用3个字节; 字符串存储“小羽毛”,开发环境为utf-8,在utf-8中每个汉字字符占用3个字节,总共占用9个字节。
String类为什么可以存储中文
jvm的底层编码方式是utf-16,所以可以正常存储unicode码表中的中文。
字符串常量池
字符串常量是双引号引起的若干个字符。
从 Java 7 开始,为了解决永久代空间不足的问题,将字符串常量池从永久代中移动到堆中
在创建字符串时 JVM 会首先检查字符串常量池,如果该字符串已经存在池中,则返回它的引用,如果不存在,则实例化一个字符串放到池中,并返回其引用。
String str="小羽毛";//第一次
String str2="小羽毛";//不是第一次
第一次
在字符串常量池没有这个”小羽毛“字符串对象,那么在堆中的字符串常量池中创建”小羽毛“字符串对象并把地址返回赋值给栈中的变量str;
不是第一次
在 字符串常量池有这个”小羽毛“字符串对象,直接将字符串常量池中这个 “小羽毛 ” 的对象地址返回,赋值给栈中变量 str2
intern方法
如果不是用双引号声明的String对象,可以使用String提供的intern方法。intern方法会从字符串常量池中查询当前字符串是否存在,若不存在就会将当前字符串放入常量池中。
String myInfo=new String("I love atguigu").intern();
也就是说,如果在任意字符串上调用intern()方法,那么其返回结果所指向的那个类实例,必须和直接以常量形式出现的字符串实例完全相同。
举例:如何保证栈帧中变量s指向的是字符串常量池中的数据呢?
有两种方式:
//方式一:字面量定义的方式
String s ="xiaoyumao";
//方式二:调用intern(),不管前面是怎么花里胡哨的创建一个String对象
String s = new String("xiaoyumao").intern();
String s = new StringBuilder("xiaoyumao").toString().intern();
面试题举例:第一关
public static void main(String[] args) { //创建两个对象,一个是静态区的"1",一个是用new创建在堆上的对象。 String s = new String("1"); s.intern();//字符串常量池中已经有"1",所以调用该方法相当于没调 String s2="1"; System.out.println(s==s2);//不管哪个jdk版本都是false }
===========
如果接收了intern()返回值
public static void main(String[] args) { String s = new String("1"); //s.intern();//字符串常量池中已经有"1",所以调用该方法相当于没调 s=s.intern();//如果是接收了返回值,那么返回值指向的才是字符串常量池的数据 String s2="1"; System.out.println(s==s2); }
面试题举例:第二关
jdk6版本
public static void main(String[] args) { String s3 = new String("1")+new String("1"); s3.intern();//字符串常量池中没有"11",jdk6 调用该方法会在常量池创建"11" String s4="11"; System.out.println(s3==s4);//jdk6 s3指向的是堆中的11,s4指向的是字符串常量池的11,故为FALSE }
jdk6下该运行结构是false
====
怎么就可能是true呢
String s3 = new String("1")+new String("1"); s3.intern();//字符串常量池中没有"11",jdk7/jdk8 调用该方法并没有在常量池中创建"11" 而是创建一个指向堆空间的地址 String s4="11"; System.out.println(s3==s4);//此时不会去常量池中创建“11”,而是s4指向常量池中存放s3地址的地方,所以为true
String笔试题1
常量区存储特点:一旦常量区中存在数据,不会创建第二遍,会拿已有的数据进行使用。
public static void main(String[] args) {
String str1=new String("HelloWorld");
String str2=new String("HelloWorld");
System.out.println(str1==str2);//false
String str3="HelloWorld";
String str4="HelloWorld";
System.out.println(str3==str4);//true
String str5=new String("Hello");
String str6=new String("World");
System.out.println(str1==(str5+str6));//false
String str7="Hello";
String str8="World";
// str7+str8 相当于new StringBuilder().append(str7).append(str8).toString;
System.out.println(str3==(str7+str8));//false
/* 给变量进行初始化赋值的时候,初始化只是一个式子,且运算符俩边都是字面值常量,那么jvm会在编译时期将该式子
运算完毕。成为常量优化机制。*/
System.out.println(str3==("Hello"+"World")); //true 相当于str3==“HelloWorld”
}
String笔试题2
public class Demo {
public static final String str1 = "HelloWorld";
public static final String str2 = "HelloWorld";
public static final String str3 = "Hello";
public static final String str4 = "World";
public static final String str5;
public static final String str6;
public static final String str7;
public static final String str8;
static {
str5 = "HelloWorld";
str6 = "HelloWorld";
str7 = "Hello";
str8 = "World";
}
public static void main(String[] args) {
System.out.println(str1 == str2);//true 在常量池
System.out.println(str5 == str6);//true 还是在常量池
System.out.println(str5 == str1);//true 在常量池
// 被final修饰且直接声明初始化的自定义常量当成字面值常量
System.out.println(str1 == (str3 + str4));//true str=("Helllo"+"World")在编译时期就合并了
System.out.println(str5 == (str7 + str8));//false 在运行时期走,+相当于new 相当于new StringBuilder().append(str7).append(str8).toString
}
}
String笔试题3
public static void main(String[] args) {
String str1="HelloWorld";//1个,在常量区
String str2=new String("HelloWorld");//2个,一个在常量区一个在堆内存
//共创建一个String对象
String str3="H"+"e"+"l"+"l"+"o"+"W"+"o"+"r"+"l"+"d";//1个对象,编译时期就合并了相当于str1
//共创建8个String对象
String s1="H";
String s2="e";
String s3="l";
String s4="l";
String s5="o";
String s6="W";
String s7="o";
String s8="r";
String s9="l";
String s10="d";
String str4=s1+s2+s3+s4+s5+s6+s7+s8+s9+s10;//相当于new StringBuilder()然后toString
}
String类常见方法
判断功能的方法
public boolean equals (Object anObject) :判断俩个字符串的内容是否相同且区分大小写
public boolean equalsIgnoreCase (String anotherString) :判断俩个字符串的内容是否相同且忽略大小写【验证码,邮箱】
public boolean contains(CharSequence s) :当且仅当此字符串包含指定的 char 值序列时,返回true。
public boolean endsWith(String suffix) :测试此字符串是否以指定的后缀结束。
public boolean isEmpty() :当且仅当 length() 为 0 时返回 true。
public boolean startsWith(String prefix) :测试此字符串是否以指定的前缀开始。
public boolean startsWith(String prefix,int toffset) :测试此字符串从指定索引开始的子字符串是否以指定前缀开始。
获取功能的方法
public int length () :返回此字符串的长度。【数组没有()】
public String concat (String str) :将指定的字符串连接到该字符串的末尾。
public char charAt (int index) :返回指定索引处的字符值。
public int indexOf (String str) :返回指定子字符串第一次出现在该字符串内的索引,找不到返回-1
public int lastIndexOf (String str):返回指定子字符串最后一次出现在该字符串内的索引。
public String substring (int beginIndex) :返回一个子字符串,从beginIndex开始截取字符串到字符串结尾。
public String substring (int beginIndex, int endIndex) :返回一个子字符串,从beginIndex到endIndex截取字符串。含beginIndex,不含endIndex。
转换功能的方法
public char[] toCharArray () :将此字符串转换为新的字符数组。
public byte[] getBytes () :用于返回字符串的字节数组,可以指定编码方式。
String str="小羽毛"; byte[] bytes = str.getBytes((StandardCharsets.UTF_8)); System.out.println(Arrays.toString(bytes));//打印字节数组
public String replace (CharSequence target, CharSequence replacement) :将与target匹配的字符串使用replacement字符串替换。
public String toLowerCase() 将字符串中所有字母字符转换为小写
public String toUpperCase() 将字符串中所有字母字符转换为大写,汉字原样输出
分割功能的方法
public String[] split(String regex) :将此字符串按照给定的regex(规则)拆分为字符串数组。
一般情况
String str = "这世界很喧嚣,做你自己就好"; //使用split()拆分字符串 String[] strings = str.split(","); //打印拆分后的字符串数组 System.out.println( Arrays.toString(strings));
-------------------------
注意特殊分割符号
1.字符" | " , " * " , " + "都得加上转义字符,前面加上"\\"。 2.如果是" \ ",那么就得写成"\\\\"。
比如,分隔符是|
正则表达式中使用反斜杠
\
来转义下一个字符, 为什么用两个反斜杠呢反斜杠本身就是一个特殊字符,需要用反斜杠来转义
使用正则表达式分割String str = "这世界很喧嚣|做你自己就好"; Pattern pat = Pattern.compile("\\|"); //使用正则表达式()拆分字符串 String[] split = pat.split(str); //打印拆分后的字符串数组 System.out.println( Arrays.toString(split));
字符串替换
replace() 方法通过用 newChar 字符替换字符串中出现的所有 oldChar字符,并返回替换后的新字符串。
replace(String oldChar, String newChar)
将字符串中满足正则表达式的部分替换为给定内容
String replaceAll(String regex,Stirng replace)
public static void main(String[] args) throws IOException { String str = "ab123sdab4543das756as876asd"; str = str.replaceAll("\\d+", "#num#"); //"\d+",任意多个数字字符 System.out.println(str); }
打印输出:ab#num#sdab#num#das#num#as#num#asd
字符串拼接
使用StringBuilder
直接使用StringBuilder的方式是效率最高的。因为StringBuilder天生就是设计来定义可变字符串和字符串的变化操作的
StringBuilder sb = new StringBuilder("小羽毛:"); String str = "干杯~~~"; //使用StringBuilder 字符串拼接 System.out.println(sb.append(str));
---------------------------------
拼接null
StringBuilder sb = new StringBuilder("小羽毛:"); String str = null; //使用StringBuilder 字符串拼接 System.out.println(sb.append(str));
使用+
Java中的+对字符串的拼接,其实现原理是使用StringBuilder.append
String str1="小羽毛:"; String str2="干杯~~~"; //使用 + 字符串拼接 System.out.println(str1+str2);
---------------------------------
拼接null
String str1="小羽毛:"; String str2=null; //使用 + 字符串拼接 System.out.println(str1+str2);
“+” 号操作符会把 null 当做是 “null” 字符串来处理。
使用concat()String str1="小羽毛:"; String str2="干杯~~~"; //使用concat() 字符串拼接 System.out.println(str1.concat(str2));
------------------------------------
拼接null
String str1="小羽毛:"; String str2=null; //使用concat() 字符串拼接 System.out.println(str1.concat(str2));
使用String.join()第一个参数为字符串连接符String str1="小羽毛"; String str2="干杯~~~"; //使用join() 字符串拼接 System.out.println(String.join(":",str1,str2));
字符串格式化
String类的format()方法用于创建格式化的字符串以及连接多个字符串对象
//制定字符串格式和参数生成格式化的新字符串。
format(String format, Object... args)
转 换 符 | 说 明 | 示 例 |
%s | 字符串类型 | "mingrisoft" |
%c | 字符类型 | 'm' |
%b | 布尔类型 | true |
%d | 整数类型(十进制) | 99 |
public static void main(String[] args) {
DateFormat fmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String format = fmt.format(new Date());
System.out.println(String.format("时间:%s", format));
}
StringBuffer和StringBuilder
1、StringBuffer类和StringBuilder类
StringBuilder是个字符串的缓冲区,即它是一个容器,容器中可以装很多字符串。并且能够对其中 的字符串进行各种操作。
相同点:
1.二者都继承AbstractStringBuilder。
2.二者都是可变的字符序列
3.二者的方法除了同步修饰符外都是相同的。
不同点:
StringBuffer是线程安全的,适用于多线程程序,如果在单线程中使用,效率极低
StringBuilder是线程不安全的,适用于单线程程序,如果在多线程中进行使用,需要手动添加线程安全。
StringBuilder可变的原因:底层封装了一个没有被final修饰的数组
public StringBuilder() :构造一个空的StringBuilder容器。
public StringBuilder(String str) :构造一个StringBuilder容器,并将字符串添加进去。
public StringBuilder append(...) :添加任意类型数据的字符串形式,并返回当前对象自身。
public String toString() :将当前StringBuilder对象转换为String对象。
public static void main(String[] args) {
// StringBuilder s="Hello";//编译报错
StringBuilder sb = new StringBuilder();
//任意类型的参数。任何数据作为参数都会将对应的字符串内容添加到StringBuilder中。
sb.append("Hello");
sb.append(true);
sb.append(1.09);
sb.append('A');
sb.append(new Employee("张三",18));
System.out.println(sb);//Hellotrue1.09AEmployee{name='张三', age=18, salary=null, status=null}
//指定索引位置插入字符串内容
sb.insert(0, "HaHa");//HaHaHellotrue1.09AEmployee{name='张三', age=18, salary=null, status=null}
System.out.println(sb);
//字符串反转 因为StringBuilder是可变字符序列,所以可以对sb进行反转
sb.reverse();
System.out.println(sb);//}llun=sutats ,llun=yralas ,81=ega ,'三张'=eman{eeyolpmEA90.1eurtolleHaHaH
}
2、StringBuilder源码分析
1.StringBuilder类底层数组的初始容量是多少
初始容量是多少取决于所选择的构造器
StringBuilder() 初始容量:16字符
StringBuilder(CharSequence seq) 初始容量:为 16 加上参数的长度
StringBuilder(int capacity) 初始容量:自定义,初始容量由 capacity 参数指定, capacity >=0
StringBuilder(String str) 初始容量:为 16 加上参数的长度
2.StringBuilder类底层数组的扩容规则是多少
jdk8.0(包含)后:
扩容规则:(原来数组长度<<1)+2;
jdk7.0:
扩容规则: 原来数组长度*2+2;//国际笑话
jdk6.0(包含)前:
扩容规则:(原来数组长度+1)*2;
扩容规则计算的时候为什么进行+2处理? 为了防止底层数组的长度为0的特殊情况。