day_15_常用API(String,Integer,常量池)
/*
- java.lang.String : 字符串类,并且该类加 final修饰
-
底层就是char数组 private final char value[];
-
所以 字符串很多特性 就是数组的特性
-
1 字符串一旦创建不可更改
-
2 为了提升字符串的访问效率,Java中提出了字符串常量池,相当于是一个缓存区
-
引用类型对象应该保存在堆内存,但是字符串不同,保存在静态区的字符串常量池中
-
3 在程序的执行过程中,如果程序要用到某个字符串,如"abc",虚拟机会先去常量池中搜索,有没有这个字符串
-
如果已经有了,就直接指向该字符串即可,如果没有就新建一个字符串对象,并指向
public class String_01_ {
public static void main(String[]args){
String s1 = "abc";
// 这里并没有改变字符串abc的值,而是新建了一个字符串叫a2 并让s1指向a2
s1 = "a2";
System.out.println(s1);
String s2 = "abc";
String s3 = "abc";
String s4 = "Abc";
// true , == 比较基本类型比较值,比较引用类型比较内存地址
// 而 s2 和 s3 指向的地址 是相同的
System.out.println(s2 == s3);
// 创建两个对象,abc和Abc
// true 比较的是值
System.out.println(s2.equals(s3));
// 第三部分
// new的方式,s5不再指向常量池,而是指向堆内存对象,堆内存中保存常量池的地址
String s5 = new String("123");
String s6 = new String("123");
// 以上两句 : 创建三个对象(堆内存两个,常量池一个),占用5块空间(栈内存两个,堆内存两个,常量池一个)
// == 就是在比较 堆内存地址,而不是常量池中 123的地址了,因为new了两次,所以不一致
System.out.println(s5 == s6);
System.out.println(s5.equals(s6));
}
}
因为String一旦创建不可更改,所以使用的时候,要注意,不要频繁拼接字符串
`因为效率比较低,还浪费空间,并且垃圾回收也会有一定问题
public class String_02_ {
public static void main(String[] args) {
String[] strs = {"a","b","c","d"};
String tmp = "";
for (String string : strs) {
tmp+=string;
}
System.out.println(tmp);
// a,b,c,d,"",ab,abc,abcd
// TODO Auto-generated method stub
}
}
- String中常用构造方法
- 创建String对象的方式
public class String_03_ {
public static void main(String[] args) {
// 1
String s1 = "abc";
// 2
String s2 = new String("xxx");
// 3 字节数组
// byte[] aaa = {1,2,3};
// byte[] aaa = new byte[]{1,2,3,4};
// byte[] aaa = new byte[5];
byte[] aaa = { 97, 98, 99 };
String s3 = new String(aaa);
String s4 = new String(new byte[] { 97, 98, 99 });
// abc : 因为把数字转换为char 97 就是a
System.out.println(s4);
// 4 字节数组 , 截取一部分
// 从下标 1 开始(包含),取两个
String s5 = new String(aaa, 1, 2);
System.out.println(s5);
// 5 字符数组
char[] chars = {'a','b','c','d'};
String s6 = new String(chars);
System.out.println(s6);
// 6 截取一部分
String s7 = new String(chars,2,2);
System.out.println(s7);
// 7 无参 空字符串
String s8 = new String();
System.out.println(s8);
}
// TODO Auto-generated method stub
}
public class String_04_ {
public static void main(String[] args) {
// 1 int length() : 返回该字符串长度
String s1 = "qwert!";
System.out.println(s1.length()); // 6
// 2 char charAt(int index) : 获取字符串某个位置上的字符
char c1 = s1.charAt(2);
System.out.println(c1); // e
// 3 boolean endsWith(String suffix) : 判断字符串是否以指定字符串结尾
// boolean startsWith(String prefix) : 判断字符串是否以指定自己开始
System.out.println("HelloWord.java".endsWith(".java"));// true
System.out.println("HelloWord.java".endsWith(".java "));// false 有空格
// 4 boolean equalsIgnoreCase(String anotherString) : 不区分大小写比较两个字符串是否相等
System.out.println("abc".equals("AbC")); // false
System.out.println("abc".equalsIgnoreCase("AbC")); // true
// 5 byte[] getBytes() : 把字符串转换为字节数组
byte[] byteArr = "abc".getBytes();
for (byte b : byteArr) {
System.out.println(b);
}
System.out.println("----------");
// 6 int indexOf(String str) : 获取指定字符串的起始索引值,找不到返回-1,如果有多个 ,找到第一个就不找了
System.out.println("abcdce".indexOf("cd"));
// 7 int indexOf(String str,int fromIndex) : 从指定位置开始找(包含),找不到返回-1
System.out.println("abcdeabc".indexOf("a",1));
// 8 int lastIndexOf(String str) : 获取最后一次出现的位置,找不到返回-1(倒着遍历,第一次出现,就是最后一次)
// int lastIndexOf(String str,int fromIndex) : 获取最后一次出现的位置,找不到返回-1,从指定位置反向搜索,第一次出现的位置
System.out.println("abcdeabc".lastIndexOf("a"));
// 9 String replaceAll(String regex, String replacement) : 把一个字符串替换为指定字符串
// 一个类似的 replace : 这两个功能是一样的,只不过 replace 不支持正则表达式
System.out.println("123321".replaceAll("1", "a")); // a2332a
System.out.println("1.2.3".replaceAll(".", "-")); // ----- , 因为 会把 . 解析为正则表达式,而 . 代表 任何字符
// 1-2-3 , 正则表达式中 可以通过 \ 把 . 转义为无意义字符,但是在java中 \ 是转移符,所以 要写 \\ 对\进行转义才可以
System.out.println("1.2.3".replaceAll("\\.", "-"));
System.out.println("1.2.3".replace(".", "-")); // 1-2-3 , 因为不支持正则表达式,所以 . 就是 .
// 10 String[] split(String regex) : 分割字符串,通过指定分隔符,来分割字符串,返回分割后的新字符串数组,支持正则表达式
String myTime = "2021-1-20";
String[] myTimes = myTime.split("-");
for (String string : myTimes) {
System.out.println(string);
}
System.out.println("==========");
// 11 String substring(int begin) : 获取该字符从某个下标开始到结尾的子字符串(包含)
System.out.println("abcde".substring(3));
// 12 String substring(int beginIndex,int endIndex) : 获取该字符从某个下标开始(包含) 到某个下标结束的子字符串(不包含)
System.out.println("qwerttyuyikjn".substring(3, 6));
// 13 char[] toCharArray() : 转换为字符数组
char[] c2 = "abc".toCharArray();
for (char c : c2) {
System.out.println(c);
}
System.out.println("+++++++++++++++");
// 14 String toUpperCase() : 转为大写
System.out.println("asdDGDHshdjbsaj".toUpperCase());
// 15 String toLowerCase() : 转为小写
System.out.println("asdDGDHshdjbsaj".toLowerCase());
// 16 String trim() : 删除字符串两边的空格
System.out.println(" ab cd ");
System.out.println(" ab cd ".trim());
// 17 static String valueOf(Object obj) : 调用指定对象的toString方法,并且 避免了空指针异常
// (obj == null) ? "null" : obj.toString();
String_04_ ss = null;
System.out.println(ss);
}
}
1 StringBuffer和StringBuilder是是一个可变的字符串缓冲区
*
- 2 原理
-
预先在内存中申请一块空间,可以容纳字符序列(字符数组)
-
如果 预留空间不够,会进行自动扩容
-
底层都是char[] ,并且默认初始化容量是16个字符
- 3 String,StringBuffer,StringBuilder最大的区别
-
1 String不可变字符序列,而StringBuilder和StringBuffer是可变字符序列
-
2 StringBuffer是线程安全,在多线程环境下,不会出现问题,所以效率低,一般常用于类中
-
3 StringBuilder是非线程安全,在多线程环境下可能出现问题,效率高,一般用于方法中
- 4 如何选择StringBuilder和StringBuffer
-
多线程环境下,是否有可能出现多个线程同时操作同一个数据 的可能(增,删,改)
public class String_05_ {
public static void main(String[] args) {
StringBuffer sb = new StringBuffer();
String[] strs = {"a","b","c","d"};
for (String string : strs) {
sb.append(string+".").append(".");
}
// 引用类型转String 调用toString方法
String str = sb.toString();
System.out.println(str);
StringBuilder sbr = new StringBuilder();
for (String string : strs) {
sbr.append(string);
}
System.out.println(sbr.toString());
System.out.println(sbr.reverse());
}
}
tring 不可以任意拼接的字符串
-
String s2 = "a" + "b"; 这种写法,在编译阶段,就会把+ 去掉
-
"a" +"b" a和b都是字面量,需要在编译阶段说明临时空间,所以需要通过值确定类型
-
编译时看到是两个字符串相加,就直接把+省略,保存ab
-
所以 s1 ==s2 是true
-
String s3 = a + b;
-
a和b是变量,编译阶段是不确定变量的值的
-
在运行的时候,由于是两个字符串变量相加,
-
所以会自动创建一个StringBuffer对象,然后把两个变量拼接到一起,最终转换为String类型
-
而是以 return new String(value, 0, count); 所以 s3是指向堆内存的
public class String_06_ {
public static void main(String[] args) {
String s1 = "ab";
String a = "a";
String b = "b";
String s2 = "a" + "b";
String s3 = a + b;
String s4 = new String("ab");
// true
System.out.println(s1 == s2);
// false
System.out.println(s1 == s3);
System.out.println(s1.equals(s3));
// false
System.out.println(s1 == s4);
}
}
包装类 : 封装了基本类型的操作,
-
byte -- java.lang.Byte
-
short -- java.lang.Short
-
int -- java.lang.Integer
-
long -- java.lang.Long
-
float -- java.lang.Float
-
double -- java.lang.Double
-
char -- java.lang.Character
-
boolean -- java.lang.Boolean
public class Intrger_01_ {
public static void main(String[] args) {
// 基本类型
byte b1 = 2;
// 引用类型
Byte b2 = null;
// 把 b1 基本类型封装到b2引用类型中
b2 = new Byte(b1);
// 可以把b2 传入 接收Object的方法中,因为发生了多态
m1(b2);
}
public static void m1(Object obj){
// 由于 覆写了toString方法,所以可以直接打印 2 ,而不是内存地址
System.out.println(obj);
}
}
public class Integer_02_ {
public static void main(String[] args) {
// 1 获取最大值和最小值
System.out.println("int最大值 : " + Integer.MAX_VALUE);
System.out.println("int最小值 : " + Integer.MIN_VALUE);
System.out.println("long最大值 : " + Long.MAX_VALUE);
System.out.println("long最小值 : " + Long.MIN_VALUE);
System.out.println(Short.MAX_VALUE);
System.out.println(Short.MIN_VALUE);
System.out.println((int) (Character.MAX_VALUE));
System.out.println((int) (Character.MIN_VALUE));
// 2 创建对象
Integer i1 = new Integer(12); // int -- Integer
// 可以传入纯数字的字符串
Integer i2 = new Integer("19776"); // String -- Integer
System.out.println(i1);
System.out.println(i2);
// 编译可以通过,但是运行出错
// java.lang.NumberFormatException: For input string: "19776a"
Integer ie = new Integer("19776a");
}
}
public class Integer_03_ {
public static void main(String[] args) {
// 1 int --> Integer
Integer i1 = new Integer(22);
// 2 Integer --> int
int i2 = i1.intValue();
// 3 重要 : static int parseInt(String s) : 把字符串转换为int
// String --> int
int i3 = Integer.parseInt("123");
System.out.println(i3);
// 4 重要
double d1 = Double.parseDouble("12.3");
System.out.println(d1);
// 5 static String toBinaryString(int value) : 把指定int值转换为二进制的字符串形式展示
String s1 = Integer.toBinaryString(10);
System.out.println(s1);
// 6 十六进制展示
System.out.println(Integer.toHexString(30));
// 7 八进制展示
System.out.println(Integer.toOctalString(10));
// 8 int --> Integer
Integer i31 = Integer.valueOf(28);
// String --> Integer
Integer i32 = Integer.valueOf("123");
// 9 Integer -- String
String s2 = i31.toString();
System.out.println(i31);
System.out.println(s2);
}
}
//类型转换
public class Integer_04_ {
public static void main(String[] args) {
// 1 int -- > Integer
Integer i1 = new Integer(222);
Integer i2 = Integer.valueOf(222);
// 2 Integer --> int
int i3 = i2.intValue();
// 3 String --> Integer
Integer i4 = Integer.valueOf("123");
Integer i5 = new Integer("12313");
// 4 Integer --> String
String s1 = i5.toString();
// 5 String --> int
int i6 = Integer.parseInt("12321");
// 6 int --> String
String s2 = 333+"";
}
}
/*
- 自动装箱
- 把 基本数据类型 自动转换为 对应的包装类
- 自动拆箱
- 把 包装类 自动转换为 基本数据类型
- 自动装箱和自动拆箱是编译时 完成的
public class Integer_05_ {
public static void main(String[] args) {
//1.5版本之前
// 自动装箱之前的转换方式
Integer i1 = new Integer(223); // Integer.vlaueOf(2222)
// 自动拆箱之前的转换方式
int i3 = i1.intValue();
// 1.5 开始
// 自动装箱之后的转换方式
Integer i2 = 222; // 编译完后 就等于是 : Integer i2 = Integer.vlaueOf(222);
// 自动拆箱之后的转换方式
int i4 = i1; // 拆箱
// 2 是基本类型,会先自动装箱转换为Integer类型,然后再向上转型(多态) 转换为Object
m1(2);
}
public static void m1(Object obj){
// 由于 覆写了toString方法,所以可以直接打印 2 ,而不是内存地址
System.out.println(obj);
}
}
深入理解自动装箱和自动拆箱
*
-
1 都是编译时的概念,和运行时无关
-
2 装箱的时候,会在编译时自动把赋值操作变成 Integer.valueOf(222)
-
String , Integer,Double 等 八种包装类 和String 都覆写了toString(),equals() , hashCode() 方法
-
Integer.valueOf(xxx) : 方法实现
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
} -
valueOf : 把基本类型转换为Integer类型
-
但是里面初始化了一个整型常量池,值的范围是在 -128~127之间
-
其实就是一个Integer[] 数组 有 256个对象,对象中的int值 分别为 -128,-127.....126,127 下标 是 0~ 255
-
在 private static class IntegerCache 类中 是 Integer中的一个静态内部类
-
三个变量
-
static final int low = -128; static final int high; static final Integer cache[]; 并且在 static 代码块中 对这个数组进行了初始化操作
-
如果值在 -128~127之间 就直接去这个缓存数组中找对应的对象即可,不用再重新创建Integer对象
-
return IntegerCache.cache[i + (-IntegerCache.low)];
-
// IntegerCache.low 是 -128
-
// (-IntegerCache.low) : 就是 128 负负得正
-
// 加上 我们要添加的数据,就能得到 对应的值所在缓存数组中的下标索引
-
// 把该对象返回
-
那么这样的话,如果是通过自动装箱或者是通过valueOf方法赋值的两个值都符合该范围,那么 这两个变量保存的内存地址是一致的,则使用 == 也是true
-
如 Integer i1 = 123; Integer i2 = 123; 或者 Integer i3 = Integer.valueOf(123); 他们的内存地址是相等的,找到的是同一个对象
-
反之 , 就等于new Integer(xxx)
-
return new Integer(i); 既然是new 那么 堆内存会重新创建新的对象,那么尽管初始化的int值一样,但是内存地址也不同
-
所以 使用 == 也是false,此时 应该使用equals 来比较是否相等
-
如 Integer i4 = Integer.valueOf(128); Integer i5 = Integer.valueOf(128); 或者是 Integer i6 = new Integer(1)
public class Integer_06_ {
public static void main(String[] args) {
// 自动装箱 , 就等于 Integer i1 = Integer.valueOf(123);
Integer i1 = 123;
Integer i2 = 123;
System.out.println(i1 == i2);
// 等于 Integer i3 = Integer.valueOf(128); 和 new Integer(128) 没区别,
Integer i3 = 128;
Integer i4 = 128;
System.out.println(i3 == i4);
// 以下 结果 和 上面结果 相同
Integer i5 = Integer.valueOf(123);
Integer i6 = Integer.valueOf(123);
System.out.println(i5 == i6);
Integer i7 = Integer.valueOf(128);
Integer i8 = Integer.valueOf(128);
System.out.println(i7 == i8);
// new 的方式是不行的,因为new了两次,对象内存地址不同,所以 == 比较内存地址,是false,但是 使用equals 是true,因为都是1
Integer i9 = new Integer(1);
Integer i10 = new Integer(1);
System.out.println(i9 == i10);
System.out.println(i9.equals(i10));
}
}