一、String类
1、String类概述
- String表示字符串类型,属于引用数据类型
- 在Java中,随便使用双引号括起来的都是String对象,如”abc”、”Hello world!”就是两个String对象
- Java中,字符串是不可变的
- JDK中,双引号括起来的字符串,如”abc”都是直接存储在方法区的字符串常量池中(因为字符串在实际开发中使用频繁,为了执行效率)
2、String字符串的存储原理
凡是双引号括起来的,都在字符串常量池中有一份
new对象的时候一定在堆内存当中开辟空间
String s1 = "abcdef";
//实际在底层创建了三个字符串对象,都在字符串常量池中
//这就是Java中的字符串一旦创建就不可变,即不可在abcdef后直接补xy
String s2 = "abcdef"+"xy";
String s3 = new String("xy");
3、有String型属性的对象
class User{
int id;
String name;
}
....
User user = new User(110,"张三");
注意:name中存的是指向字符串常量池中的字符串对象的内存地址
4、两种字符串对象创建方式的区别
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 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”这个字符串对象在字符串常量池中的内存地址!!!
5、String类的特殊构造方法
构造方法中传入数组
//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类的构造方法:
- String s = new String(“xxx”);
- String s = “xxx”;
- String s = new String(char数组);
- String s = new String(char数组,起始下标,长度);
- String s = new String(byte数组);
- String s = new String(byte数组,起始下标,长度);
6、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;i<value.length;i++){
if(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;i<byteArray.length;i++){
System.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;i<ymd.length;i++){
System.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;i<chars.length;i++){
System.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()
二、StringBuffer类
在实际的开发中,进行字符串频繁的拼接,又什么影响?
😉
Java中,字符串是不可变的,每一次拼接都会产生新的字符串,从而占用大量的方法区内存,造成空间浪费
Strings = "abc";
s += "hello";
//以上,在方法区字符串常量池中共创建了三个对象
当进行大量的字符串拼接时,用JDK中自带的java.lang.StringBuffer和java.lang.StringBuilder(Buffer即缓冲)
1、StringBuffer类的构造方法
构造一个其中不带字符的字符串缓冲区,初始容量为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数组满了,会自动扩容
2、String类和StringBuffer类的区别
- String类底层是一个private final byte[] value
- StringBuffer类底层是一个byte[] value
图示:
String和StringBuffer的底层虽然都是一个byte[]数组,但String的byte[]用了final修饰,而数组一旦创建长度不可变,且final修饰的引用一旦指向某个对象,就不可再指向其他对象,故String不可变
如何优化StringBuffer的性能?
在创建StringBuffer的时候,尽可能给定一个初始化容量(即使用有参构造),以减少底层数组扩容的次数。(预估后,给一个大点的初始化容量,默认16)
StringBuffer sb = new StringBuffer(100);
初始化容量太小,追加(拼接)没几次数组就满了,底层就得调用arrayCopy扩容,从而影响效率。
3、StringBuffer和StringBuilder的区别
StringBulider sb = new StringBulider();
sb.append("code");
sb.append(9527);
……
不同之处:
StringBuffer中的方法都有synchronized关键字修饰,表示StringBuffer在多线程环境下运行是安全的,相反,StringBuffer是非线程安全的
三、基础类型对应的8个包装类
1、包装类出现的背景
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);
2、八种包装类
其中,前六种的父类的Number类。Boolean和Character的父类是Object。Number类是一个抽象类,不能直接new对象。
3、包装类的构造方法和常量
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
4、自动装箱和自动拆箱
了解自动装箱、拆箱前先看一下装箱、拆箱:
装箱
//通过构造方法,将基本数据类型转化为引用数据类型
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个对象
5、Integer类的常用方法(拆箱)
Integer x = new Integer("123"); //123
Integer y = new Integer("string"); //error
Integer z = new Integer("中文"); //error
不是一个数字的字符串时,不能包装成Integer类型,编译不报错(因为构造方法中允许传入一个字符串)但运行出错。
四、日期相关类
import java.util.Date;
1、获取系统当前时间
//调用Date类的无参构造,精确到毫秒
Date nowTime = new Date();
//Date类的toString方法已经被重写,英文格式的日期
System.out.println(nowTime);
//整理到这会儿突然有些emo,记录下此刻的时间吧。
2、日期格式化
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
3、currentTimeMillis方法
获取自1970年1月1日00:00:00起,到当前系统时间的毫秒数。
long nowTimeMillis = System.currentTimeMillis();
该方法可用于统计某方法执行所需时长:
long beginTime = System.currentTimeMillis();
xxx方法执行
long endTime = System.currentTimeMillis();
//endTime-beginTime
除了这个方法,System类中的方法还有:
- System.g() 建议启动垃圾回收器
- System.exit(0)退出JVM
4、Date类的有参构造方法
Date time = new Date(1); //参数单位是毫秒,即1970-01-01 00:00:00 01
//获取昨天此时的时间
Date time2 = new Date(System.currentTimeMillis()-1000*24*60*60);
五、Random类
1、数字格式化
import java.text.DecimalFormat;
//#代表任意数字,逗号代表4分位, 点.代表小数点,0代表不够时补0
DecimalFormat df = new DecimalFormat("###,###.##");
String s2 = df.format(1234.56789); //1,234.57
2、BigDecimal
发音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);
3、产生随机数
//创建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;i<array.length;i++){
array[i] = -1;
}
int index = 0;
Random random = new Random();
while(index<array.length){
int element = random.nextInt(10001);
//这里的if条件,一开始可以用汉字先占位表达逻辑
//if(元素不包含于数组)
if(!contains2(array,element)){
array[index] = element;
index++;
}
}
//遍历输出即为5个不重复的随机数
for(int i=0;i<array.length;i++){
System.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;i<array.length;i++){
if(array[i] == num){
return true;
}
}
return false;
}
}
//第一个使用排序会对每次循环中原数组下标发生重新洗牌,赋值出现错位,存在bug,使用contains2方法遍历
六、枚举
就上上面例子中的contains方法,只有两种返回结果,这时使用boolean返回类型很合适,但当返回结果有两种以上,且都是一枚一枚的可以列举出来的时候,boolean就不再满足了=====>枚举诞生。
1、枚举
一枚一枚可以列举出来的,建议使用枚举类型,枚举类型属于引用数据类型。枚举编译之后也是生成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;
}
}
}
2、枚举类型的定义
enum 枚举类型名{ //引用数据类型
枚举值1,枚举值2,枚举值3 //常量
}
举例:
public enum Color{
RED,BLUE,YELLOW,BLACK
}
3、switch+枚举
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;
}
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之家整理,本文链接:https://www.bmabk.com/index.php/post/146102.html