Day15-常用API
1. String
1.1 概述
java.lang.String : 字符串类,并且该类加 final修饰
底层就是char数组 private final char value[];
所以 字符串很多特性 就是数组的特性
1 字符串一旦创建不可更改
2 为了提升字符串的访问效率,Java中提出了字符串常量池,相当于是一个缓存区
引用类型对象应该保存在堆内存,但是字符串不同,保存在静态区的字符串常量池中
3 在程序的执行过程中,如果程序要用到某个字符串,如"abc",虚拟机会先去常量池中搜索,有没有这个字符串
如果已经有了,就直接指向该字符串即可,如果没有就新建一个字符串对象,并指向
1.2 基本使用
package com.demo.String;
/**
* String 会有个常量池,当有个一样的值会去从常量池找,所以找到的地址相同,没有的话就new String()在堆中建立新的内存地址,并且在常量池也会有这个值
* */
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));
}
}
1.3 不要频繁使用拼接
package com.demo.String;
/**
* 由于String数组 继承了数组的特性,一旦创建了了就不能修改
* 因为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;
}
//虽然拼接了abcd 但是在之前的还有 a ab abc 这三个在内存中
System.out.println(tmp);//abcd
}
}
1.4 String构造方法
package com.demo.String;
/**
* 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};//这种写法,少用了一个aaa变量进行传参
// byte[] aaa = new byte[5];
byte[] aaa = { 97, 98, 99 };
String s3 = new String(aaa);
//不能这样传入一维数组
// System.out.println({97, 98, 99});
String s4 = new String(new byte[] { 97, 98, 99 });
// abc : 因为把数字转换为char 97 就是a
System.out.println(s4);//abc,int型转化为char型,ASCII码值
// 4 字节数组 , 截取一部分
// 从下标 1 开始(包含),取两个
String s5 = new String(aaa, 1, 2);
System.out.println(s5);//bc
// 5 字符数组
char[] chars = {'a','b','c','d'};
String s6 = new String(chars);
System.out.println(s6);//abcd
// 6 截取一部分
String s7 = new String(chars,2,2);
System.out.println(s7);//cd
// 7 无参 空字符串
String s8 = new String();
System.out.println(s8);
}
}
1.5 常用方法
package com.demo.String;
/**
* String常用方法
*
* 1 方法是谁的(这里肯定是String)
*
* 2 什么方法,静态还是成员,知道是什么方法,就知道应该如何调用
*
* 3 方法名是什么,入参和出参是什么
*
* 4 方法功能是什么
*
*/
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.6 注意
String 不可以任意拼接的字符串
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是指向堆内存的
package com.demo.String;
/**
* java.lang.StringBuffer
*
* java.lang.StringBuilder
*
* 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);//a.b.c.d.
StringBuilder sbr = new StringBuilder();
for (String string : strs) {
sbr.append(string);
}
System.out.println(sbr.toString());//abcd
//反转
System.out.println(sbr.reverse());//dcba
}
}
StringBuilder 和 StringBuffer
2.1 概述
java.lang.StringBuffer
java.lang.StringBuilder
1 StringBuffer和StringBuilder是什么?
是一个可变的字符串缓冲区
2 原理
预先在内存中申请一块空间,可以容纳字符序列(字符数组)
如果 预留空间不够,会进行自动扩容
底层都是char[] ,并且默认初始化容量是16个字符
3 String,StringBuffer,StringBuilder最大的区别
1 String不可变字符序列,而StringBuilder和StringBuffer是可变字符序列
2 StringBuffer是线程安全,在多线程环境下,不会出现问题,所以效率低,一般常用于类中
3 StringBuilder是非线程安全,在多线程环境下可能出现问题,效率高,一般用于方法中
4 如何选择StringBuilder和StringBuffer
多线程环境下,是否有可能出现多个线程同时操作同一个数据 的可能(增,删,改)
2.3 使用
package com.demo.String;
/**
* java.lang.StringBuffer
*
* java.lang.StringBuilder
*
* 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);//a.b.c.d.
StringBuilder sbr = new StringBuilder();
for (String string : strs) {
sbr.append(string);
}
System.out.println(sbr.toString());//abcd
//反转
System.out.println(sbr.reverse());//dcba
}
}
3. 包装类
3.1 概述
包装类 : 封装了基本类型的操作,更方便我们使用
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
既然有了基本类型,为什么还要使用包装类呢?
1 方便
2 为了理论上的完整(面向对象)
问 : 想要创建一个方法,该方法可以接收任何类型
Object ,因为Object是所有类的祖类,由于多态的原因,可以接收任何对象
但是基本类型呢? 基本类型并不是Object的子类,怎么接收?
包装类 , 可以把基本类型转换为对应的包装类类型,而包装类也是个类,也是Object的子类
3.2 使用
package com.demo.Integer;
/**
* 包装类 : 封装了基本类型的操作,更方便我们使用
*
* 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
*
* 既然有了基本类型,为什么还要使用包装类呢?
* 1 方便
* 2 为了理论上的完整(面向对象)
*
* 问 : 想要创建一个方法,该方法可以接收任何类型
* Object ,因为Object是所有类的祖类,由于多态的原因,可以接收任何对象
* 但是基本类型呢? 基本类型并不是Object的子类,怎么接收?
* 包装类 , 可以把基本类型转换为对应的包装类类型,而包装类也是个类,也是Object的子类
*
*/
public class Integer_01 {
public static void main(String[] args) {
// 基本类型
byte b1 = 2;
// 引用类型
Byte b2 = null;
// 把 b1 基本类型封装到b2引用类型中
b2 = new Byte(b1);
// 可以把b2 传入 接收Object的方法中,因为多态 Object b2 = new Byte();
m1(b2);
}
public static void m1(Object obj){
// 由于 覆写了toString方法,所以可以直接打印 2 ,而不是内存地址
System.out.println(obj);//2
}
}
3.3 Integer
3.3.1 基本使用
package com.demo.Integer;
/**
* 以Integer为例 讲解八种包装类
*
*/
public class Integer_02 {
public static void main(String[] args) {
// 1 获取最大值和最小值
System.out.println("int最大值 : " + Integer.MAX_VALUE);//2147483647
System.out.println("int最小值 : " + Integer.MIN_VALUE);//-2147483648
System.out.println("long最大值 : " + Long.MAX_VALUE);//9223372036854775807
System.out.println("long最小值 : " + Long.MIN_VALUE);//-9223372036854775808
System.out.println(Short.MAX_VALUE);//32767
System.out.println(Short.MIN_VALUE);//-32768
System.out.println((int) (Character.MAX_VALUE));//65535
System.out.println((int) (Character.MIN_VALUE));//0
// 2 创建对象
Integer i1 = new Integer(12); // int -- Integer
// 可以传入纯数字的字符串
Integer i2 = new Integer("19776"); // String -- Integer
System.out.println(i1);//12
System.out.println(i2);//19776
// 编译可以通过,但是运行出错
// java.lang.NumberFormatException: For input string: "19776a"
// Integer ie = new Integer("19776a");
}
}
3.3.2 常用方法
package com.demo.Integer;
/**
* 常用方法
*
*/
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);//123 整形
// 4 重要
double d1 = Double.parseDouble("12.3");
System.out.println(d1);//12.3
// 5 static String toBinaryString(int value) : 把指定int值转换为二进制的字符串形式展示
String s1 = Integer.toBinaryString(10);
System.out.println(s1);//1010
// 6 十六进制展示
System.out.println(Integer.toHexString(30));//1e
// 7 八进制展示
System.out.println(Integer.toOctalString(10));//12
// 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);//28
System.out.println(s2);//28
}
}
3.3.3 类型转换
package com.demo.Integer;
/**
* 类型转换
*
* Integer , int , String 三者之间 相互转换
*
*/
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+"";
}
}
3.3.4 自动装箱和自动拆箱
java1.5新特性
自动装箱
把 基本数据类型 自动转换为 对应的包装类
自动拆箱
把 包装类 自动转换为 基本数据类型
自动装箱和自动拆箱是编译时 完成的
package com.demo.Integer;
/**
* java1.5新特性
*
* 自动装箱
* 把 基本数据类型 自动转换为 对应的包装类
*
* 自动拆箱
* 把 包装类 自动转换为 基本数据类型
*
* 自动装箱和自动拆箱是编译时 完成的
*
*/
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);
}
}
3.3.5 扩展之整型常量池
大概意思是 : 这是一个缓存机制,如果值再-128到127之间,就不再创建新对象,而是直接把数组中的引用赋值给你
如果不再这个范围内,就创建 新的Integer对象
package com.demo.Integer;
/**
* 深入理解自动装箱和自动拆箱
*
* 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);//true
// 等于 Integer i3 = Integer.valueOf(128); 不过 和 new Integer(128) 没啥区别,因为不符合常量池值的大小,所以是新new
Integer i3 = 128;
Integer i4 = 128;
System.out.println(i3 == i4);//false
// 以下 结果 和 上面结果 相同
Integer i5 = Integer.valueOf(123);
Integer i6 = Integer.valueOf(123);
System.out.println(i5 == i6);//true
Integer i7 = Integer.valueOf(128);
Integer i8 = Integer.valueOf(128);
System.out.println(i7 == i8);//false
// new 的方式 肯定是不行的,因为new了两次,对象内存地址不同,所以 == 比较内存地址,肯定是false,但是 使用equals 一定是true,因为都是1
Integer i9 = new Integer(1);
Integer i10 = new Integer(1);
System.out.println(i9 == i10);//false
System.out.println(i9.equals(i10));//true
}
}