JAVA开发学习-day05
1. 字符串 String
java中除了8大基本数据类型,其他的类型都是引入类型
使用String来声明字符串,String是引用类型,所有的引用类型的默认值都为null。
//声明字符串
String str = "abc";
str = new String("你好");
char[] arr = new char[10];
char[] arr1 = {'a','b','c',97};
str = new String(); //构造方法参数可以为空
System.out.println(arr1);//输出为abca
//String.valueOf(),将传输的参数转化为字符串
str = String.valueOf(439028);
System.out.println(str);//439028
1.1 字符串的拼接
字符串使用+来拼接
字符串和所有类型相加(+),结果都为字符串
str = 123 + "abc"; // "123abc"
str = "123" + 123; //"123123"
//加对象,就是字符串加上对象的toString结果
str = "abc" + 123 + new Object();
System.out.println(str);
//输出为 abc123java.lang.Object@1b6d3586
//数组是一个特殊的类
str = "123" + new int[]{1,2,3};
//输出为 123[I@4554617c
System.out.println(str);
加号在做字符串拼接和数学运算时,优先级是一样的
System.out.println("123" + 123 + 123);
// 结果为123123123
System.out.println(123 + 123 + "123");
// 结果为246123
1.2 字符串的比较
使用equals方法来比较字符串
boolean bool = "123".equals("123"); //返回值为布尔值
System.out.println(bool); // true
//比较的是字符串是否相同,而不是对象
bool = new String("123").equals(new String("123"));
System.out.println(bool); // true
String中的重写的equals方法
public boolean equals(Object anObject) {
if (this == anObject) {
return true; //如果是同一对象,则直接返回true
}
//instanceof用于检查对象是否是指定类型
if (anObject instanceof String) { //检查anObject是否是字符串类型
String anotherString = (String)anObject; //将anObject转换为String,为了下面使用String的属性和方法
int n = value.length;
//this.value.length,对字符串的长度进行判断
if (n == anotherString.value.length) {
//value是String的 private final char[] value存储字符串的字符串数组
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
//对每个字符进行判断
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
1.3 String类型中的常用方法
1.3.1 valueOf()
valueOf 将传入的参数转成字符串
String.valueOf('1');
String.valueOf("");
String.valueOf(null);
String.valueOf(new Object());
//String.valueOf(); ❌ 不能没有参数
重载的方法一个参数为String,一个参数为Object的情况下,参数的输入值为null,则优先调使用String的方法
public class EasyString {
public static void main(String[] args) {
//String和Object重载的情况下,优先调使用String的方法
test(null); //输出结果为 String
}
public static void test(String str){
System.out.println("String");
};
public static void test(Object obj){};
}
1.3.2 indexOf() lastIndexOf() charAt()
indexOf()和lastIndexOf()是用来查找字符串的子串出现的下标的方法
int index = "123456".indexOf("456");
System.out.println(index); //返回子串的下标
int index1 = "123456".indexOf("46");
System.out.println(index1); //没有子串,返回-1
index = "123123123".indexOf("1"); // index值为0
index = "123123123".lastIndexOf("1");// 查找最后一个子串所在第一个字符的下标,index值为6
charAt()用来获取指定位置的字符
//获取指定位置的字符
char item = "123456".charAt(4); //获取下标为4的字符 item为"5"
//char item1 = "123456".charAt(100); String index out of range 字符串下标超出范围
1.3.3 subString()
subString()用于截取字符串的一段
str = "123456".substring(1); //从开始下标截取到最后
System.out.println(str); // 23456
//包含开始下标,不包含结束下标
str = "123456".substring(1, 3); //从开始下标截取到结束下标的前一个
System.out.println(str); // 23
1.3.4 replace() replaceAll()
replace()和replaceAll()用来将字符串中的子串替换为指定的字符串
str = "123456".replace("34","aaaa");
System.out.println(str); //12aaaa56
//replace替换所有子串
str = "12345634".replace("34","aaaa");
System.out.println(str); //12aaaa56aaaa
//正则表达式替换,使用正则表达式匹配子串并替换
str = "12.31.23".replaceAll(".","1");
System.out.println(str); //111111111
1.3.5 split()
使用split()来根据输入的分隔符来分割字符串
String[] strArr = "123123123".split("1");
//如果分隔符是第一个元素,则会分割一个空串
System.out.println(Arrays.toString(strArr)); // [, 23, 23, 23]
String[] strArr1 = "23123121".split("1");
//如果分隔符是最后一个元素,那就不会有空串
System.out.println(Arrays.toString(strArr1)); // [23, 23, 2]
String[] strArr2 = "12312312".split("2");
System.out.println(Arrays.toString(strArr2)); // [1, 31, 31]
1.3.6 其他
String的方法都可以见名知义,不需要特别记忆
//字符串长度 length()
int l = "123123".length();
for (int i = 0 ; i < l; i++){};
//trim 去除前后空白位 空格 \n \r \t \f
//只去除前后的空白位
str = " 123 456 \n\r\t\f ".trim();
System.out.println(str); // 输出结果为123 456
// 大写 小写 (针对字母)
str = "123abc".toUpperCase(); //转成大写 123ABC
str = "123ABC".toLowerCase(); //转成小写 123abc
//判断是否是空串
bool = "123".isEmpty(); // false
bool = "".isEmpty(); // true
//结束开始判断
//判断是否以某字符串开始
bool = "123456".startsWith("123"); //true
//判断是否以某字符串结束
bool = "123456".endsWith("456"); //true
1.4 字符串常量池
池这个词在java里并不罕见,比如运行时常量池,字符串常量池,线程池,数据库连接池等,所谓“池”就是为了资源复用,减少空间的占用,提高性能。
JVM为了提高性能,减少内存开销,维护的一个存放字符串常量的内存区域,里面的字符串不允许重复,有长度限制,最大为65535字节
使用量方式声明的字符串,就会加入到常量池中
// 量的形式声明
// 直接赋予值,就是量的形式声明
int a = 12;
String str = "abc";
程序中第一次使用量的形式定义"123",会将这个字符串存入常量池中
之后再使用量的形式使用该对象,就执行使用常量池中的对象
String strA = "123";
String strB = "123";
//strA strB是同一对象
System.out.println(strA == strB); // true
new即创建新对象,创建新对象的时不会使用常量池的对象
String strA = "123";
String strC = new String("123");
String strD = new String("123");
//对象不同
System.out.println(strC == strD); //false
System.out.println(strC == strA); //false
是否使用了常量池中的对象判断
String strE = "12" + "3"; // "123"
String strF = "1" + "2" + "3"; // "123"
String item = "12";
String strG = item + "3";
String strGG = item + 3;
// itme不明确,在计算之前,先创建对象存储 item + ? 再计算值
// 所以strG和strGG不是同一对象
System.out.println(strG == strGG); //false
String strH = "12" + 3;
System.out.println(strA == strE); //true
System.out.println(strA == strF); //true
System.out.println(strA == strG); //false
System.out.println(strA == strH); //true
final String item1 = "12";
//item1是常量,下面item1 + "3"在解析之前就明确为"123"
String strI = item1 + "3";
System.out.println(strI == strA); //true
//新对象
final String strJ = new String("123");
System.out.println(strA == strJ); // false
也开始使用intern()方法来将字符串加入到常量池中
//intern
str.intern();
intern()将对象加入常量池的方式
//返回str对象在字符串常量池中的副本对象 没有副本对象就将str复制一份到常量池中再返回副本对象
//所以如果两个字符串 equals为true
//那么两个字符串的intern()方法是相等的(都是同一副本对象)
strA = new String("123123");
strB = new String("123123");
System.out.println(strA.equals(strB));//true
System.out.println(strA == strB); // false
System.out.println(strA.intern() == strB.intern()); // true
面试题
//new String("abc") 创建了几个对象
//一个或两个
//当字符串常量池中有abc的副本对象,就创建了一个对象
//如果没有abc副本对象,就创建abc副本对象,和此对象,即两个对象
1.5 StringBuilder() StringBuffer()
String 字符串定义后就不可改变 存在常量池中
在拼接字符串的过程中会产生中间量
public class EasyStringbuilder {
public static void main(String[] args) {
//String 字符串定义后就不可改变 存在常量池中
String str = "";
for (int i = 0; i < 10; i++) {
str = str + i;
}
//在拼接字符串的过程中会产生中间量 0 01 012 0123 .... 0123456789
}
}
在拼接字符串时想要不产生中间量就需要StringBuilder StringBuffer
使用StringBuilder的append方法进行拼接
StringBuilder strB = new StringBuilder();
strB.append("123");
strB.append("abc");
strB.append("456");
//调用append 往StringBuilder数组中追加字符
//其中没有再产生字符串对象
System.out.println(strB);
//输出为 123abc456
StringBuilder的append方法中直接调用了其父类AbstractStringBuilder的append方法
public AbstractStringBuilder append(String str) {
if (str == null)
return appendNull();
int len = str.length();
//ensureCapacityInternal对存放字符串的数组进行扩容
//count为字符串数组的容量,len为要拼接的字符串的长度
ensureCapacityInternal(count + len);
//对字符串进行拼接
str.getChars(0, len, value, count);
count += len;
return this;
}
ensureCapacityInternal()方法用来确认AbstractStringBuilder中存储字符串的数组是否需要扩容
private void ensureCapacityInternal(int minimumCapacity) {
// overflow-conscious code
if (minimumCapacity - value.length > 0) {
//定义扩容大小
value = Arrays.copyOf(value,
newCapacity(minimumCapacity));
}
}
如果需要扩容则通过newCapacity()方法确认需要扩容至多大
private int newCapacity(int minCapacity) {
// overflow-conscious code
//将数组的容量左移一位再加2,即*2+2
int newCapacity = (value.length << 1) + 2;
//如果扩容后仍小于需要的大小,则将扩容大小变为需要的大小
if (newCapacity - minCapacity < 0) {
newCapacity = minCapacity;
}
//容量足够则返回新容量,不足则用hugeCapacity()方法继续扩容
return (newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0)
? hugeCapacity(minCapacity)
: newCapacity;
}
再回去看ensureCapacityInternal()方法中的Arrays.copyOf()方法,在确定需要扩容至多大后就用Arrays.copyOf()方法来扩容
public static char[] copyOf(char[] original, int newLength) {
//声明更大的数组
char[] copy = new char[newLength];
//将小数组的数据拷贝到大数组
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
return copy;
}
System.array()是原生方法,下面是System.array()的使用方法
import java.util.Arrays;
int[] source = { 1, 2, 3, 4, 5, 6, 7 };
int[] destination = new int[5];
//System.arraycopy(源数组,源数组开始拷贝下标,目标数组,目标数组拷贝下标,拷贝的元素个数)
System.arraycopy(source, 3, destination, 2, 3); // 要复制的源数组中的元素为 4,5,6。 这些将从索引2开始复制到目标数组。
System.out.println(Arrays.toString(destination)); // [0, 0, 4, 5, 6]
再看append()调用的getChars()方法
public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) {
if (srcBegin < 0) {
throw new StringIndexOutOfBoundsException(srcBegin);
}
if (srcEnd > value.length) {
throw new StringIndexOutOfBoundsException(srcEnd);
}
if (srcBegin > srcEnd) {
throw new StringIndexOutOfBoundsException(srcEnd - srcBegin);
}
//使用System.arraycopy()对数组进行拼接
System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
}
由StringBuilder类的构造方法可知AbstractStringBuilder类中存储字符串的数组大小初始值设为16
public final class StringBuilder extends AbstractStringBuilder
implements java.io.Serializable, CharSequence{
public StringBuilder() {
super(16);
}
}
abstract class AbstractStringBuilder implements Appendable, CharSequence {
char[] value;
int count;
AbstractStringBuilder() {
}
AbstractStringBuilder(int capacity) {
//数组初始大小设为16
value = new char[capacity];
}
}
总结
StringBuiLder 默认容量是16
StringBuilder 扩容
追加字符时容量不够,就需要扩容(value) 默认原容量*2+2
StringBuilder 是线程不安全的 异步
StringBuffer 是线程安全的 synchronized
StringBuilder使用arraycopy来拼接
2. 关于日期的方法
获取当前时间可以使用java.util.Date类
import java.util.Date;
public class EasyDate {
public static void main(String[] args) {
//时间类型 Date
//获取当前时间 --执行时
//Date date = new Date(100000);
Date date = new Date();
System.out.println(date);
}
}
获取当前时间的时间戳,可以用Date类的getTime方法
import java.util.Date;
public class EasyDate {
public static void main(String[] args) {
//获取从1970-1-1 00:00:00.000过了多少毫秒
//获取时间戳
Date date = new Date();
long time = date.getTime();
System.out.println(time);
}
}
在实例化对象时填入的数值,就是从1970-1-1 00:00:00.000过了多少毫秒的日期
import java.util.Date;
public class EasyDate {
public static void main(String[] args) {
Date date = new Date(100000); //过了100秒
System.out.println(date);
//输出为 Thu Jan 01 08:01:40 CST 1970
//因为在东八区,所以显示8点
}
}
天数计算
import java.util.Date;
public class EasyDate {
public static void main(String[] args) {
Date date = new Date();
System.out.println(date);
//获取从1970-1-1 00:00:00.000过了多少毫秒
//获取时间戳
long time = date.getTime();
System.out.println(time);
//输出两天后的时间
time = time + 2*24*60*60*1000;
date = new Date(time);
System.out.println(date); //输出的日期为两天后
}
}
时间格式化
使用SimpleDateFormat对象的format()方法来格式化Date对象的输出格式
import java.text.SimpleDateFormat;
public class EasyDate {
public static void main(String[] args) {
Date date = new Date();
System.out.println(date);
//时间格式化
//将日期输出的形式重新定义
//HH为24小时制 年-月-日 小时:分钟:秒 毫秒
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
//hh为12小时制度
System.out.println(sdf.format(date));
SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss SSS");
System.out.println(sdf1.format(date));
}
}
其他的时间类
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZonedDateTime;
public class EasyDate {
public static void main(String[] args) {
LocalDate ld = LocalDate.now(); //本地日期
System.out.println(ld); //2024-07-18
LocalDateTime ldt = LocalDateTime.now(); //本地时间
System.out.println(ldt);// 2024-07-18T17:29:52.532
ZonedDateTime zdt = ZonedDateTime.now(); //本地时间 + 时区和地区
System.out.println(zdt);
//2024-07-18T17:29:52.533+08:00[Asia/Shanghai]
Object obj = zdt.getZone(); //哪个地区时间制度
System.out.println(obj); //Asia/Shanghai
}
}
3. 随机数
可以用Math.random方法来返回一个double类型的随机数
//随机数
double ran = Math.random(); //[0,1)
//如果想得到取值范围为[8,90)的随机数
//[8,90) -----> [0,82) + 8 -----> [0,1)*82 + 8
double ran = Math.random()*82 + 8;
Random类也可以实现随机数,不同于Math.random()的是Random类实现的随机数是假随机,而Math.random是真随机
Random randomObj = new Random(12); //参数是种子数
Random randomObjA = new Random(12);
//种子数相同,返回的随机数也相同 假随机
int a = randomObj.nextInt();
int b = randomObjA.nextInt();
System.out.println(a == b); //true
//种子数相同,取值范围相同,返回值也相同
a = randomObj.nextInt(200);
b = randomObjA.nextInt(200);
System.out.println(a == b); //true
虽然是假随机,但Random类使用方便,想要哪个类型的随机数直接调用方法返回即可
Random randomObj = new Random(12);
int numInt = randomObj.nextInt();
long numLong = randomObj.nextLong();
double numDouble = randomObj.nextDouble();
float numFloat = randomObj.nextFloat();
boolean numBoolean = randomObj.nextBoolean();
4. 取整
Math类中还有一些取整方法
//取整
//四舍五入
ran = 12.5;
//Math.round方法传入double返回long 传入float返回int
long roundNum = Math.round(ran); //13
float aa = 12.4f;
int roundNum1 = Math.round(aa); //12
System.out.println(roundNum);
//向上取整 有小数部分舍去进一 double
double ceilNum = Math.ceil(ran); //13
System.out.println(ceilNum);
//向下取整 double
double floorNum = Math.floor(ran);//12
System.out.println(floorNum);
负数的取整要注意
roundNum = Math.round(-0.5); // 0
roundNum = Math.round(-0.51); // -1
roundNum = Math.round(-0.49); // 1
roundNum = Math.round(0.5); // 1