String
字符串类 引用类型 默认值是null而不是""(也是一种字符串)
声明字符串
String str="ABC";
字符串的构造方法
str=new String();
str=new String("ABC");
char[] arr= {'a','b','c',23};
str=new String(arr);
str=String.valueOf(12);
字符串的拼接
+ 加号在拼接字符串是和数学运算时优先级一样
字符串和所有类型相加得到的都是字符串
str=123+"123";
str=456.0+"456";//456.0456
str="123"+new Object();//"123"+这个对象的toString的结果
str="123"+new int[] {1,2,3};//123[I@6d06d69c
笔试题
System.out.println("123"+123+123);//123123123
System.out.println(123+123+"123");//246123
String对象定义后都不可改变 常量(本身靠private final char[] value去储存)
字符串常量池 池(容器) 作用:重用
字符串怎么加入常量池中 使用量的方式声明的字符串就会加入到常量池
使用量的方式 带双引号的
String str="123";
int a=1;
Integer.valueOf("123");
这种不会
char[] arr= {'a','b','c'};
str=new String(arr);
程序中第一次使用量的方式定义"123",会存入字符串常量池中,之后再以量的方式使用该对象,就会执行使用常量池的对象
String strA="123";//加入常量池(运行阶段)
String strB="123";//调用常量池里的赋值
System.out.println(strA==strB);//true
strB=new String("123");//这种会开辟新地址存入数据"123";
System.out.println(strA==strB);//false
常量优化 解析源文件时能明确的就优化(编译阶段)
String strE="12"+"3";//"123" ==strA
String strF="1"+"2"+"3";//"123" ==strA
String item="12";
String strG=item+"3";//变量没有常量优化 方法返回值toString()+3也没有优化
String strH="12"+3;//"123"常量 ==strA
final String aa="12";//常量就明确,明确就优化
String strI=aa+"3";//"123" ==strA
final String bb=new String("12");//后面是new 只有在运行时才明确,不能优化
String strJ=bb+"3";//!=strA
intern
想把字符串对象添加到字符串常量池中
过程 检查str是否在字符串常量池存在副本,没有就复制到常量池,然后返回常量池中的副本,如果已经存在就直接返回副本对象
strA.intern();
返回strA对象在字符串常量池的副本对象
总结:
(1)常量与常量的拼接结果在常量池,原理是编译期优化;
(2)常量池中不会存在相同内容的常量;
(3)只要其中一个是变量,结果在堆中。 如: String s2 = s1+"DEF" ;
(4)变量拼接的原理是StringBuilder 。
(5)如果拼接的结果是调用 intern() 方法,则主动将常量池中还没有的字符串对象引用放入池中,并返回地址。
(6)String s="a"+"b";运行后“ab”加入常量池,编译阶段进行常量优化,运行阶段系统就认为为String s="ab";故仅"ab"加入字符串常量池。
常用方法
//valueOf 将传入的参数转为字符串
//String.valueOf(null);//所有类型都可以 引用都调用toString赋值 传入null运行时会有空指针异常
//查找子串出现的下标 找不到则返回-1
str="0123456123";
System.out.println(str.indexOf("123"));
//最后一次出现的下标
System.out.println(str.lastIndexOf("123"));
//获取指定位置的字符
System.out.println(str.charAt(1));//不在范围报数组下标越界异常
//截取字符串
//str.substring(beginIndex);beginIndex 开始位置(包含) 截取到最后一个
//str.substring(beginIndex, endIndex)endIndex 结束位置(不包含)如果越界则和上面一样报错
System.out.println(str.substring(1, 3+1));
//替换字符串
//"012345".replace(oldChar, newChar)//以旧换新并且所有符合oldChar都被替换成newChar
str="02342356".replace("23", "45");//04544556
System.out.println(str);
str="12.34.56".replaceAll(".", "4");//符合正则表达式的字符替换为4,"."表示任意字符
System.out.println(str);//44444444
//分割字符串
String[] strArr=" 12 34 56 78 ".split(" ");//根据空格分割 前面有分割符则有空串,后面有则没有空串
System.out.println(Arrays.toString(strArr));//[, 12, 34, 56, 78]
//获取字符串长度
str.length();//此处是方法
for(int i=0;i<str.length();i++)
System.out.println(str.charAt(i));
//去除前后空白位 空格 \n \r \t \f等等
str="\n\r \t 12 3 \n\t";
System.out.println(str);// 12 3 加换行结束
System.out.println(str.trim());//12 3
//大写 小写
str="123abc".toUpperCase();
System.out.println(str);//123ABC
System.out.println(str.toLowerCase());//123abc
//判断空串
System.out.println(str.isEmpty());//false null还是空指针异常
//是否是以"123"开始 str.startsWith("123");
//是否是以"456"结束 str.endsWith("456");
字符串的比较
==与equals:==比较引用地址,而equals比较具体数据内容,equals比较步骤:判断是是同一个对象、判断是否是同一种类、判断内容是否一致。
笔试题
如果两个字符串的equals是true 内容一样 ,两个字符串的intern方法==是true
面试题
new String("abc")创建几个对象 1个或两个
如果常量池有就1个,没有就2个
new String("abc"+"12");//运行时把"abc"和"12"进行常量优化拼接,把abc12加入常量池
StringBuilder与StringBuffer
引言:字符串定义后就不可改变,拼接过程产生太多中间字符串占用内存
在拼接字符串时不要产生字符串 StringBuilder StringBuffer
其中没有产生字符串对象
默认容量是16
StringBuilder 扩容
追歼字符时容量不够就需要扩容 默认扩到原来容量的*2+2,不够就扩容到够的长度
StringBuilder strB=new StringBuilder();
strB.append("123");
strB.append("abc");
strB.append("456");
System.out.println(str.toString());
StringBuilder与StringBuffer区别
StringBuffer 与StringBuilder几乎一模一样,不过每个方法加了应该同步修饰符synchronized
StringBuffer线程安全 多线程效率快
StringBuilder线程不安全 单线程效率更高
拓展:System.arraycopy方法
作用:从源数组src取元素,范围为下标srcPos到srcPos+length-1,取出共length个元素,存放到目标数组中,存放位置为下标destPos到destPos+length-1。
System.arraycopy(src, srcPos, dest, destPos, length);
解释:
src是源数组,它从中将要复制元素。
srcPos是在源数组中开始复制的位置。
dest是目标数组,元素将被复制到其中。
destPos是在目标数组中开始复制元素的位置。
length是要复制的元素数量。
应用:
常用作数组的扩容,如ArrayList底层数组的扩容
当下标等数组越界时,抛出异常:ArrayIndexOutOfBoundsException
这个方法提供了一种在Java中在数组之间高效地复制数据的方式,无需手动迭代。
随机数
真随机
//真正随机
double ran=Math.random();//[0,1);
double ranNum=ran*82+8;//[8,90);
假随机
//随机数对象,假随机
Random ranObj=new Random(12);//传入随机数种子
//随机种子和获取次数相同则获取值也相同
Random ranObjA=new Random(12);
System.out.println(ranObj.nextInt(200));//ranObj.nextInt(200)界限是0~199
System.out.println(ranObjA.nextInt());
取整
//四舍五入 传入double返回long -0.5取0,0.5这个数无论正负向上取整
long num=Math.round(ranNum);
System.out.println(num);
//四舍五入 传入float返回int
int intNum=Math.round(1.4F);
System.out.println(intNum);
//向上取整 返回double 去符号取
double ceilNum=Math.ceil(ranNum);
System.out.println(ceilNum);
//向下取整 返回double 去符号取
double floorNum=Math.floor(ranNum);
System.out.println(floorNum);
System.out.println(Math.floor(-1.1));
注意点:
1.Math.round(四舍五入) 传入double返回long,传入float返回int; -0.5取0,0.5取1,0.5这个数无论正都负向上取整;
2.Math.ceil(向上取整)和Math.floor(向下取整),返回double类型,如返回Math.floor(-1.1)=-2.0
3.一般向下取整,[0,1);[0.1)*n取号是对0不友好
时间类
底层是long类型储存 从1970-1-1 00:00:00 每一毫秒加1
Date 时间类型
注意点:计算机是上海时区Asia/Shanghai
//获取当前时间 执行时间
Date date=new Date();
System.out.println(date);
long time=date.getTime();//获取现在的时间累加值
System.out.println(time);
//传参当作时间戳
Date dateA=new Date(0);
//过了两天
time+=2*24*60*60*1000;
Date dateB=new Date(time);
System.out.println(dateB);
System.out.println(date.getMonth());//月份 0~11 返回值+1是实际月份
System.out.println(date.getDay());//这些都废弃了
//时间格式化
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//hh是12小时制,HH是24小时制
System.out.println(sdf.format(date));
System.out.println();
//本地时间
LocalDateTime ldt=LocalDateTime.now();
//时区时间
ZonedDateTime zdt=ZonedDateTime.now();
//计算机是上海市区Asia/Shanghai
System.out.println(zdt.getZone().toString());
两个对象哈希值相同相同怎么解决?
如果两个对象的哈希值相同,这被称为哈希冲突。哈希冲突是一种常见的情况,可以通过以下几种方法来解决:
1. 开放寻址法:当发生哈希冲突时,继续在哈希表中寻找下一个可用的位置来存储数据。
2. 链表法:当发生哈希冲突时,将冲突的元素存储在一个链表中,链表的头节点保存在哈希表的相应位置。
3. 完全散列:使用一个哈希函数来计算哈希值,并为具有相同哈希值的元素分配一个二级哈希表。
这些方法中,链表法是最常用的解决哈希冲突的方法,它能够处理大部分情况下的哈希冲突。当然,解决哈希冲突的具体方法要根据实际情况来决定,例如数据类型、数据规模等。