
凡是双引号括起来的,都在字符串常量池中有一份
new对象的时候一定在堆内存当中开辟空间
String s1 = "abcdef";
//实际在底层创建了三个字符串对象,都在字符串常量池中
//这就是Java中的字符串一旦创建就不可变,即不可在abcdef后直接补xy
String s2 = "abcdef"+"xy";
String s3 = new String("xy");

class User{int id;String name;
}
....
User user = new User(110,"张三");

注意:name中存的是指向字符串常量池中的字符串对象的内存地址
String s1 = "hello";
String s2 = "hello";
System.out.println(s1 == s2);//true

String x = new String("xyz");
String y = new String("xyz");
System.out.println(x == y); //false

综上:String类的比较直接使用equals()方法:
String k = new String("testString");
//这么写更优,可以避免空指针异常
System.out.println("testString".equals(k));//这么写k为null时空指针异常
System.out.println(k.equals("testString"));
String的特殊性:
int i = 100; i中存的是100这个值
String s = “abc”; s中存的是"abc"这个字符串对象在字符串常量池中的内存地址!!!
构造方法中传入数组
//97即a,98是b,99是c
byte[] bytes = {97,98,99};
//传入一个数组
String s3 = new String(bytes);
//输出abc字符串
System.out.println(s3);
构造方法中传入数组和偏移量
//String(字节数组,数组元素起始下标。长度)
String s4 = new String(bytes,1,2);
//输出bc,即只将byte数组中的一部分转化为字符串
System.out.println(s4);
对于char数组char[] charArray以上构造方法同样可以用
总结String类的构造方法:
1)charAt方法—char
用法:
字符串对象 . charAt(int下标);
示例:
//返回一个char
char c = "中国人".charAt(1);
System.out.println(c); //国
2)compareTo方法—int
作用:
按字典顺序比较两个字符串,靠后的字母是大的
示例:
//0 ,代表前后一致
int result1 = "abc".compareTo("abc");
//-1;代表前小后大,类比8-9
int result2 = "abcd".compareTo("abce");
//1, 代表前大后小,类比9-8
int result3 = "abce".compareTo("abcd");//第一位的比较已经得出结论,x < y,-1
System.out.println("xyz".compareTo("yxz");
可以看到:equals只能看到是否相等,但compareTo方法还能看到谁打谁小:
//随便写个静态的自己编的equals方法协助理解public static boolean equals(byte[] value,byte[] other){if(value.length == other.length) {for(int i=0;iif(value[i] != other[i]){return false;}}return true;}return false;
}
3)contains方法—boolean
作用:
判断前面的字符串是否包含后面的字符串
示例:
//true
System.out.println("HelloWorld.java".contains(".java"));
4)endsWith方法—boolean
作用:
判断当前字符串是否以某个字符串结尾
示例:
//false
System.out.println("text.txt".endWith(".java"));
类似的,也有startWith方法,用法与endWith方法相同
5)equalsIgnoreCase方法—boolean
作用:
判断两个字符串是否相等,且忽略大小写
示例:
System.out.println("ABC".equalsIgnoreCase("abc"); //true
6)getBytes方法—byte[]
作用:
将字符串对象转换成字节数组,即返回一个byte数组
示例:
byte[] byteArray = "abcdef".getBytes();
for(int i=0;iSystem.out.println(byteArray[i]);
}
运行:

7)indexOf()方法–int
作用:
判断某子字符串在当前字符串中第一次出现的索引下标
示例:
//6
System.out.println("oraclejava++java#".indexOf("java"));//-1
System.out.println("abc".indexOf("qwe"));
判断最后一次出现的索引可以用lastIndexOf()
8)isEmpty()方法–boolean
作用:
判断某个字符串是否为空
示例:
String e = "";
System.out.println(e.isEmpty()); //true
//源码
public boolean isEmpty(){return value.length == 0;
}
//底层调用了字符串对象的length()方法
//System.out.println("abc".length()); //3
判断数组长度和字符串长度不一样,判断数组长度是length属性,判断字符串长度是length()方法
9)replace()方法—String
作用:
完成字符串对象某部分的替换
示例:
String newString = "http://code-9527.com".replace("http://","https://");
System.out.println(newString);
System.out.println("name=9527&age=22".replace("=",":"));
10)split()方法----String[]
作用:
拆分字符串
示例:
String[] ymd = "2022-11-16".split("-");
for(int i=0;iSystem.out.println(ymd[i]);
}
//输出结果:2022 11 16
11)subString()方法—String
作用:
截取字符串
示例:
//传入要截取的起始下标
String a = "http://www.baidu.com".subString(7);//重载后可以:
subString(int beginIndex,int endIndex)
//注意是左闭右开
12)toCharArray()方法—char[]
作用:
将字符串转换成char数组
示例:
char[] chars = "中国人".toCharArray();
for(int i=o;iSystem.out.println(char[i]);
}//输出: 中 国 人
13)toLowerCase()方法—String
作用:
将字符串中的大写字母转化为小写
示例:
String str ="AbcDEF".toLowerCase();
//即abcdef//相反的:
toUpperCase()方法,将小写转化为大写
14)trim()方法—String
作用:
去除字符串前后的空白
示例:
String s = " hello World ".trim();//输出hello world ,注意去除的只是前后的空格
15)valueOf()方法—String
作用:
将非字符串转化成字符串。
“String类中只有一个方法是静态的,不用new对象—valueOf()方法”
示例:
String s1 = String.valueOf(true);
String s2 = String.valueOf(100);
当静态方法valueOf传入参数是一个对象的时候,会自动调用该对象的toString()方法
class Customer{
//未重写toString方法
}
String s1 = String.valueOf(new Customer());
//输出s1结果是Customer@10f7689
为什么println输出一个引用时,默认调用引用的toString()方法?
println源码中:String s = String.valueOf(x); 而valueOf方法又调用了toString()
在实际的开发中,进行字符串频繁的拼接,又什么影响?
😉
Java中,字符串是不可变的,每一次拼接都会产生新的字符串,从而占用大量的方法区内存,造成空间浪费
Strings = "abc";
s += "hello";
//以上,在方法区字符串常量池中共创建了三个对象
当进行大量的字符串拼接时,用JDK中自带的java.lang.StringBuffer和java.lang.StringBuilder(Buffer即缓冲)
构造一个其中不带字符的字符串缓冲区,初始容量为16字符//源码
public StringBuffer(){super(16); //调用了父类的构造方法
}//父类
AbstractStringBuilder(int capacity) {if (COMPACT_STRINGS) {value = new byte[capacity];coder = LATIN1;} else {value = StringUTF16.newBytesFor(capacity);coder = UTF16;}}
StringBuffer底层实际是一个byte[]数组,往StringBuffer中放字符串,实际上是放到byte数组中了
//创建一个初始化容量为16的byte[]数组(字符串缓冲区对象)
StringBuffer sb = new StringBuffer();
//append方法拼接字符串
sb.append("a");
sb.append("b");
//append方法和println相似,对各类型数据有重载
sb.append(3.14);
sb.append(true);
System.out.println(sb); //结果:ab3.14true
注意:
append方法中调用了arrayCopy()方法,在进行字符串追加的时候,若byte数组满了,会自动扩容
图示:

String和StringBuffer的底层虽然都是一个byte[]数组,但String的byte[]用了final修饰,而数组一旦创建长度不可变,且final修饰的引用一旦指向某个对象,就不可再指向其他对象,故String不可变

如何优化StringBuffer的性能?
在创建StringBuffer的时候,尽可能给定一个初始化容量(即使用有参构造),以减少底层数组扩容的次数。(预估后,给一个大点的初始化容量,默认16)
StringBuffer sb = new StringBuffer(100);
初始化容量太小,追加(拼接)没几次数组就满了,底层就得调用arrayCopy扩容,从而影响效率。
StringBulider sb = new StringBulider();
sb.append("code");
sb.append(9527);
……
不同之处:
StringBuffer中的方法都有synchronized关键字修饰,表示StringBuffer在多线程环境下运行是安全的,相反,StringBuffer是非线程安全的
Java中为8种基本数据类型对应准备了8种包装类型,8种包装类属于引用数据类型,父类是Object。包装类存在的意义如图:

//写段代码帮助理解包装类的实现public class MyInt{int value;public MyInt(){}public MyInt(int value){this.value = value;}
}
--------
//通过构造方法把100包装成了对象
MyInt mi = new MyInt(100);
//此时图中的doSome()方法可传参了
doSome(mi);

其中,前六种的父类的Number类。Boolean和Character的父类是Object。Number类是一个抽象类,不能直接new对象。
Integer类:
//Integer的两个构造方法:
//Integer(int)
//Integer("String")
Integer x = new Integer(100);
Integer y = new Integer("123");
//Integer类已经重写了toString()方法
Double类和Integer类相似:
Double d1 = new Double(1.23);
Double d2 = new Double("1.23");
通过访问包装类常量,可以获取最大值和最小值
System.out.println("int的最大值:" + Integer.MAX_VALUE);
//Integer.MIN_VALUE
//Byte.MIN_VALUE
了解自动装箱、拆箱前先看一下装箱、拆箱:
装箱
//通过构造方法,将基本数据类型转化为引用数据类型
Integer i = new Integer(123);------------
拆箱
//通过xxValue方法,将引用数据类型转化为基本数据类型
int retValue = i.intValue();
float f = i.floatValue();

自动装箱与自动拆箱:
//自动装箱
Integer x = 100;
//自动拆箱
int y = x;
Integer z = 1000;
//+两边要求基本数据类型,故z自动拆箱
System.out.println(z+1);
Integer a = 1000;
Integer b = 1000;
System.out.println(a == b);//,两对象的内存地址不同,false//虽然是自动装箱,但本质还是Integer a = new Integer(1000);
比较坑爹的一个点:
//这里是false
Integer a = 128;
Integer b = 128;
System.out.println(a == b);
---------
//这里是true
Integer a = 127;
Integer b = 127;
System.out.println(a == b);
Java中,为了提高程序效率,将[-128,127]之间所有的包装对象提前创建好,放到了方法区的整数型常量池中,这个区间的数据不再需要new,直接从整数型常量池中取用,所哟后者是true
示意图:
Integer类加载的时候,会初始化整数型常量池,256个对象
Integer x = new Integer("123"); //123
Integer y = new Integer("string"); //error
Integer z = new Integer("中文"); //error
不是一个数字的字符串时,不能包装成Integer类型,编译不报错(因为构造方法中允许传入一个字符串)但运行出错。



import java.util.Date;
//调用Date类的无参构造,精确到毫秒Date nowTime = new Date();//Date类的toString方法已经被重写,英文格式的日期
System.out.println(nowTime);
//整理到这会儿突然有些emo,记录下此刻的时间吧。

import java.text.SimpleDateFormat;Date nowTime = new Date();
//创建日期格式化对象
//yMd HmsS字符不可变,中间的格式连接符随意
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
//Date转成了String
String nowTimeStr = sdf.format(nowTime);
System.out.println(nowTimeStr);
以上格式化日期的同时,也将Date类型转化成了String类型,那String类型如何转成Date类型
String time = "2022-11-17 22:36:26 666";//此处的格式要和上面字符串中的格式一致,否则后面会java.text.ParseException异常
SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");Date dateTime = sdf2.parse(time);System.out.println(dateTime); //Thu Nov 17 22:36:26 CST 2022
获取自1970年1月1日00:00:00起,到当前系统时间的毫秒数。
long nowTimeMillis = System.currentTimeMillis();
该方法可用于统计某方法执行所需时长:
long beginTime = System.currentTimeMillis();xxx方法执行long endTime = System.currentTimeMillis();
//endTime-beginTime
除了这个方法,System类中的方法还有:
Date time = new Date(1); //参数单位是毫秒,即1970-01-01 00:00:00 01
//获取昨天此时的时间Date time2 = new Date(System.currentTimeMillis()-1000*24*60*60);
import java.text.DecimalFormat;//#代表任意数字,逗号代表4分位, 点.代表小数点,0代表不够时补0
DecimalFormat df = new DecimalFormat("###,###.##");String s2 = df.format(1234.56789); //1,234.57
发音big dai sei mao,属于大数据,精度极高,不属于基本数据类型,属于引用数据类型,常用于财务软件当中。
BigDecimal v1 = new BigDecimal(100);
BigDecimal v2 = new BigDecimal(200);//别使用v1+v2
//v1和v2是引用,别用+
BigDecimal v3 = v1.add(v2);//除
BigDecimal v4 = v2.divide(v1);
//创建random对象
Random random = new Random();//产生一个随机数
int num1 = random.nextInt();//netInt(101),即下一个是100
//所以产生一个[0~100]之间的随机数
int num2 = random.nexInt(101);
练习:生成五个不重复的随机数
/*** 先理清思路,梳理逻辑,再提取方法,最后填充代码实现* 可先写个长度为5的初始数组,再产生一个个随机数,放入数组* 为了保证不重复,进入数组前需要比较(此处需要一个比较是否相同,或元素是否包含于数组的方法)*/import java.util.Arrays;
import java.util.Random;
public class RandomTest {public static void main(String[] args) {int[] array = new int[5];/*** 改一下数组中元素的初始值* 赋初值-1*/for(int i=0;iarray[i] = -1;}int index = 0;Random random = new Random();while(indexint element = random.nextInt(10001);//这里的if条件,一开始可以用汉字先占位表达逻辑//if(元素不包含于数组)if(!contains2(array,element)){array[index] = element;index++;}}//遍历输出即为5个不重复的随机数for(int i=0;iSystem.out.println(array[i]);}}/*** 判断元素是否包含于数组* “是否”即boolean返回类型* @param array* @param num* @return*/public static boolean contains(int[] array,int num){Arrays.sort(array);//返回>=0的索引即找到了,也就是包含,反之不包含return Arrays.binarySearch(array,num) >=0;}/*** 不用Arrays类提供的排序和二分查找,我自己用遍历查找* @param array* @param num* @return*/public static boolean contains2(int[] array, int num){for(int i=0;iif(array[i] == num){return true;}}return false;}
}//第一个使用排序会对每次循环中原数组下标发生重新洗牌,赋值出现错位,存在bug,使用contains2方法遍历
就上上面例子中的contains方法,只有两种返回结果,这时使用boolean返回类型很合适,但当返回结果有两种以上,且都是一枚一枚的可以列举出来的时候,boolean就不再满足了=====>枚举诞生。
一枚一枚可以列举出来的,建议使用枚举类型,枚举类型属于引用数据类型。枚举编译之后也是生成class文件,枚举中的每一个值可以看作是常量。
//举个例子,先写两个结果的来举例public enum Result {SUCCESS,FAIL
}
class TestEnum{public static void main(String[] args) {Result r = divide(10,0);System.out.println(r==Result.SUCCESS ? "计算成功" : "计算失败");}//注意这里返回类型是枚举类型public static Result divide(int a,int b){try{int c = a/b;return Result.SUCCESS;}catch(Exception e){return Result.FAIL;}}
}
enum 枚举类型名{ //引用数据类型枚举值1,枚举值2,枚举值3 //常量
}
举例:
public enum Color{RED,BLUE,YELLOW,BLACK
}
public enum Season {SPRING,SUMMER,AUTUMN,WINTER
}
switch(){case Season.SPRING: //case中的枚举名必须省略System.out.println("春天");break;case Season.SUMMER:System.out.println("夏天");break;case Season.AUTUMN:System.out.println("秋天");break;case Season.WINTER:System.out.println("冬天");break;}