JavaSE(11)_常用类 String类、时间API、Java比较器comparable\comparator、System类、Math类、BigInteger\BigDecimal

本文深入探讨了Java中的String类,包括其不可变性、实例化方式、拼接操作及其陷阱。同时,介绍了JVM中字符串的内存结构和Heap堆。接着,对比了JDK8之前的日期API与新引入的java.time API,强调了新API的改进和优势。此外,文章还讨论了自定义比较器和System、Math、BigInteger与BigDecimal的使用。
摘要由CSDN通过智能技术生成

目录 

 

字符串相关类

String类

String不可变性代码演示

String不同实例化方式的对比 

 String不同拼接操作的对比(String不可变性补充)

String使用陷阱及String类的关于浅复制的面试题

JVM中涉及字符串的内存结构 

JVM运行时各数据区

 三种JVM虚拟机介绍

 Heap堆

String常用方法

普通方法

替换:

匹配:

分割:

(回顾)String与基本数据类型包装类的转换:

String与char[]之间的转换

String与byte[]之间的转换

面试中的String算法

String、StringBuffer和StringBuilder的区别

源码分析

StringBuffer/StringBuilder常用方法

String、StringBuffer、StringBuilder效率字符拼接效率对比

JDK8之前的时间API

 System类中获取时间戳的方法

Java中两个Date类的使用

SimpleDateFormat的使用

Calendar日历类的使用

 JDK8中日期时间API的介绍

诞生背景

引入java.time API解决以上方法

java.time

LocalDate、LocalTime、LocalDateTime的使用

Instant(瞬时)类的使用

java.time.format

DateTimeFormatter的使用

其它日期时间相关API的使用

ZoneId类(时区)

Duration类(时间间隔)/Period(日期间隔)

(其他类)

(参考)与传统日期处理的转换

Java比较器

自然排序:java.lang.Comparable

自定义类实现Comparable自然排序

定制排序:java.util.Comparator

自然排序Comparable与Comparator排序的对比

System类、Math类、BigInteger与BigDecimal

System类

Math类

BigInteger与BigDecimal

BigInteger

BigDecimal


字符串相关类

String类

  • String类的声明与初始化
    • string s;该语句表示只是声明了一个引用变量,但是并没有初始化引用,所以对变量s的任何操作(除了初始化赋值外) 都将引发异常。
    • string s=null;该语句表示声明了一个引用变量并初始化引用,但是该引用没有指向任何对象.但可以把它作为参数传递或其它使用,但是不能调用它作为对象的方法,如toString,getHashCode等。
    • string s="";该语句表示声明并引用到一个对象,只不过这个对象为0个字节.所以既然有了对象,就可以调用对象的方法。
  • String字符串,用""引起字符串来表示
  • String类声明为final的,不可被继承
  • String实现了Serializable接口:表示字符串是支持序列化的。
  • String实现了Comparable接口:表示String可以比较大小
  • String内部定义了final char[] value用于存储字符串数据.
  • 被final修饰,也意味着String:代表不可变的字符序列,简称:"不可变性"
    • 当对字符串重新赋值时,需要重写指定内存区域赋值,不能使用原有的value进行赋值。
    • 当对现有的字符串进行连接操作时,也需要重新指定内存区域赋值(这个地址不是常量池,后面"String拼接"会说),不能使用原有的value进行赋值
    • 当调用String的replace()方法修改指定字符或字符串时,也需要重新指定内存区域赋值,不能使用原有的value进行赋值。
  • 通过字面量的方式给一个字符串赋值,此时的字符串值声明在字符串常量池中。而new String(String s)这种构造方式创建的对象是在堆内存中,像每个对象一样拥有自己独立的内存空间
  • 字符串常量池中是不会存储相同内容的字符串的。因为他底层是HahsTable(到时候将集合会提到)

String不可变性代码演示

  • 注意"replace()"与"变量拼接"的字符不是存放在常量池!!!!!!!!!!!!!!!!!!!!
package javase11;

import org.junit.Test;

public class CommonClass1 {
    @Test
    public void Test1() {
        //字面量的定义方式
        String s1 = "abc";//在字符串常量池中创建了一个字面量为"abc"的字符串。赋予给s1
        String s2 = "abc";//从常量池中获取abc字符
        System.out.println(s1 == s2);//true
        s1 = "hello";//在字符串常量池中创建了一个字面量为"hello"的字符串。当对字符串重新赋值时,需要重写指定内存区域赋值,不能使用原有的value进行赋值。
        System.out.println(s1 == s2);//false,因为指向常量池的地址不同

        System.out.println("*********************");

        String s3 = "abc";
        String s4 = "abc";
        s3 += "def";//当对现有的字符串进行连接操作时,也需要重新指定内存区域赋值(这个地址不是常量池,后面会说),不能使用原有的value进行赋值
        System.out.println(s3);//abcdef
        String s5 = "abcdef";
        System.out.println(s3 == s4);//false
        System.out.println(s3 == s5);//false

        System.out.println("*********************");

        String s6 = "abc";
        String s7 = s6.replace('a', 'm');//把a字符全部改成m字符,也需要重新指定内存区域赋值(这个地址不是常量池),不能使用原有的value进行赋值。
        System.out.println(s7);//mbc
        String s8 = "mbc";
        System.out.println(s6 == s7);//false
        System.out.println(s7 == s8);//false


        String s9 = "abc" + "def";//当对字符串重新赋值时,需要重写指定内存区域赋值,不能使用原有的value进行赋值。
        System.out.println(s5 == s9);//true

    }
}

String不同实例化方式的对比 

  • 字符串常量存储在字符串常量池,目的是共享
  • 字符串非常量对象存储在堆中。
  • 面试题:String s = new String("abc");方式创建对象,在内存中创建了几个对象?
    • 两个,一个是String类型的对象,存放在堆内存中,这个对象里面有个value值,是一个char数组类型变量,因此需要new一个新的char数组让value值存放该char数组的内存地址.
package javase11;

import org.junit.Test;

import java.io.ObjectStreamField;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

public class CommonClass2 {
    @Test
    public void Test1() {
        //通过字面量定义的方式:此时的s1和s2的数据javaEE声明在方法区中的字符串常量池中,s1、s2存的都是指向常量池的地址值。
        String s1 = "javaEE";
        String s2 = "javaEE";

        //new String()内存地址执行的是堆内存
    /*本质上this.value = new char[0];
    这个String构造器会给String类型对象的private final char value[]变量赋予一个长度为0的char[]数组,没有任何意义*/
        String s3 = new String();
//        s3.charAt(0);//下标越界异常

        /*底层是this.value = s.value;
        这个String构造器会把String类型对象s的value值赋予String类型对象s2的private final char value[]变量,
        或者把String类型对象s存的常量池地址值赋予String类型对象s2的private final char value[]变量*/
//        String  s4 = new String(String s);

        /*底层是this.value = Arrays.copyOf(value, value.length);
         * 这个String构造器会把传入的char[]数组拷贝成一份新的数组赋予给String类型对象s3的private final char value[]变量*/
//        String  s5 = new String(char[] a);

        /*这个String构造器会把传入的char[]数组指定下标,从下标开始挑多少个字符创建一个数组,
        赋予给String类型对象s4的private final char value[]变量*/
//        String  s6 = new String(char[] a,int startIndex,int count);


        //通过new + 构造器的方式:此时的s3和s4保存的地址值,是数据在堆空间中开辟空间以后对应的地址值。
        String s7 = new String("javaEE");
        String s8 = new String("javaEE");

        System.out.println(s1 == s2);//true
        System.out.println(s1 == s7);//false
        System.out.println(s1 == s7);//false
        System.out.println(s7 == s8);//false

        System.out.println("***********************");
        Person p1 = new Person("Tom", 12);
        Person p2 = new Person("Tom", 12);

        System.out.println(p1.name.equals(p2.name));//true
        System.out.println(p1.name == p2.name);//true,证明他们引用的都是常量池的"Tom"字符串
    }
}

class Person {
    public String name;
    public int age;

    public Person(String name, int age) {
        this.age = age;
        this.name = name;
    }
}

 

 

 

 String不同拼接操作的对比(String不可变性补充)

  • 常量与常量的拼接结果在常量池。且常量池中不会存在相同内容的常量。
  • 只要其中有一个是变量,结果就在堆中
  • 如果拼接的结果调用intern()方法,返回值就在常量池中
package javase11;

import org.junit.Test;

public class CommonClass3 {
    @Test
    public void Test1() {
        String s1 = "javaEEhadoop";
        String s2 = "javaEE";
        String s3 = s2 + "hadoop";
        System.out.println(s1 == s3);//false,只要其中有一个是变量,结果就在堆中

        final String s4 = "javaEE";//被final修饰后的s4是常量
        String s5 = s4 + "hadoop";
        System.out.println(s1 == s5);//true,常量与常量的拼接结果在常量池。

    }

    @Test
    public void test2(){
        String s1 = "javaEE";
        String s2 = "hadoop";

        String s3 = "javaEEhadoop";
        String s4 = "javaEE" + "hadoop";//常量与常量的拼接结果在常量池。
        String s5 = s1 + "hadoop";//s1是变量,因此结果一定在堆
        String s6 = "javaEE" + s2;//s2是变量.因此结果一定在堆
        String s7 = s1 + s2;//s1,s2都是变量,因此结果一定在堆

        System.out.println(s3 == s4);//true,
        System.out.println(s3 == s5);//false
        System.out.println(s3 == s6);//false
        System.out.println(s5 == s6);//false
        System.out.println(s3 == s7);//false
        System.out.println(s5 == s6);//false
        System.out.println(s5 == s7);//false
        System.out.println(s6 == s7);//false

        String s8 = s5.intern();//.intern可以把里面的字符串存到常量池中返回一个常量池地址给s8
        System.out.println(s3 == s8);//true
        String s9 = (s2+s1).intern();//与上同理
        System.out.println(s9 == "hadoopjavaEE");//true
    }
}

String使用陷阱及String类的关于浅复制的面试题

package javase11;

import org.junit.Test;

public class CommonClass4 {
    @Test
    public void Test1() {
        /*在常量池声明一个字面量为"0"的字符串赋予给s*/
        String s = "0";

        /*实际上原来的“0”字符串对象已经丢弃了,现在在堆空间中产生了一个字符串s+“1”(也就是"01")。
        如果多次执行这些改变串内容的操作,会导致大量副本字符串对象存留在内存中,降低效率。
        如果这样的操作放到循环中,会极大影响程序的性能。*/
        s = s + "1";
        for (int i = 2; i <= 5; i++) {
            s = s + i;
            System.out.println(s);
        }

        /*因此可以直接在常量池中创建一个字面量为01的字符串*/
        String s1 = "01";

        /*也可以常量池两个常量拼接,后面自动创建一个"01"的字符串*/
        String s2 = "0" + "1";

        /*又或者堆空间的s3对象在调用intern()之后,会将常量池中已经存在的"012"字符串赋值给s4。*/
        String s3 = new String("3");
        String s4 = s1 + "2" + s3;
        String s5 = s4.intern();
        System.out.println(s5 == "0123");//true

        /*创建了一个String类型对象,里面的value存的是指向常量池的中的字面量"0",后续操作会在堆空间产生一个字符串s5+"1",也就是("01").
         * 如果多次执行这些改变串内容的操作,会导致大量副本字符串对象存留在内存中,降低效率。
         * 如果这样的操作放到循环中,会极大影响程序的性能。*/
        String s6 = new String("0");
        for (int i = 1; i <= 5; i++) {
            s6 += i;
            System.out.println(s6);
        }
    }
}

/*关于String类的值传递之浅复制的面试题*/
class StringTest {
    String str = new String("good");//1.1创建一个String类型对象,由变量str保存指向堆内存的地址
    char[] ch = { 't', 'e', 's', 't' };//1.2创建一个char[]数组,由变量ch保存指向堆内存的地址

    public void change(String str, char ch[]) {//2.1此时方法的形参列表的变量str和变量ch就会接受到传入指向堆内存地址值
        str = "test ok";//2.2这里只是改变形参str存储的堆内存地址值
        ch[0] = 'b';//这里是通过指向堆内存地址的下标改变其元素字符为"b"
//        ch = new char[5];//这才是改变形参ch存储的堆内存地址值,这里就不再做演示
    }
    public static void main(String[] args) {
        StringTest ex = new StringTest();//1.创建一个对象
        ex.change(ex.str, ex.ch);//2.将对象里的属性传入到change()方法种
        System.out.println(ex.str);//3.1,good
        System.out.println(ex.ch);//3.2,best
    }
}

JVM中涉及字符串的内存结构 

JVM运行时各数据区

  • 方法区(类资源,常量,静态资源)和堆内存都是线程共享的
  • 虚拟机栈、本地方法栈‘程序计数器都是线程私有的

 三种JVM虚拟机介绍

  • 随着jdk版本的更替Java虚拟机规范也会发生变化,会落实到具体的JVM虚拟机,虚拟机不止一个,还有不同公司的虚拟机
  • 下图三种是使用比较多的JVM虚拟机,通常都是HotSpot
  • 不同的虚拟机会对具体的问题有不同的优化处理,例如用Java写的代码在Android上运行,Dalvik是Google公司自己设计用于Android平台的Java虚拟机

 Heap堆

  • 一个JVM实例只存在一个堆内存,堆内存的大小是可以调节的。类加载器读取了类文件后,需要把类、方法、常变量放到堆内存中,保存所有引用类型的真实信息,以方便执行器执行,堆内存分为三部分:
    • Young Generation Space 新生区
    • Tenure Generation Space 养老区
    • Permanent Space 永久存储区   #这里灰色的原因:

String常用方法

  • 普通方法

    • int length():返回字符串的字节:return value.length
    • char charAt(int index):返回某索引处的字符return value[index]
    • boolean isEmpty():判断是否是空字符串,其源码就是return value.length == 0;
      但是不能判断null,如果String s=null,那么s.isEmpty()会出现空指针异常.原因:String s = null只是声明了s引用变量并初始化引用变量,但是却没有指向任何对象,因此不能调用他作为一个对象的任何方法
    • String toLowerCase():使用默认语言环境,将String中的所有字符转换为小写
    • String toUpperCase():使用默认语言环境,将String中的所有字符转换为大写
    • String trim():返回字符串的副本,去除字符串两端的空格。
    • boolean equals(Object obj):比较字符串的内容是否相同,String类重写了Object类的equal方法
    • boolean equals(Object obj):比较字符串的内容和类型是否相同
    • boolean equalsIgnoreCase(String anotherString):忽略大小写来比较字符串的内容是否相同
    • String concat(String str):将指定字符串连接到此字符串的结尾。等价于用“+”
    • int compareTo(String anotherString):比较两个字符串的大小,从左到右截取两个字符串中最小的长度,然后将截取的字符串的每个字符解析成Unicode码从左到右一一对照比较,如果完全相等则返回0;如果String从左往右有一个字符比对应anotherString的一个字符大,则返回值一定是一个正数,返回值=String字符-anthoerString字符,后面就不再做对比了;如果String从左往右有一个字符比对应anotherString的一个字符小,则返回值一定是一个负数,后面就不再做对比了,返回值=String字符-anthoerString字符;
    • int compareToIgnoreCase(String anotherString):在忽略大小写的情况下比较两个字符串的大小
    • String substring(int beginIndex):返回一个新的字符串,它是此字符串的从beginIndex开始截取到最后的一个子字符串。
    • String substring(int beginIndex,int endIndex):返回一个新字符串,它是此字符串从beginIndex开始截取到endIndex(不包含)的一个子字符串。java特性,含头不含尾
    • boolean endsWith(String suffix):测试此字符串是否以指定的后缀结束,一般用作检查文件类型
    • boolean startsWith(String prefix):测试此字符串是否以指定的前缀开始
    • boolean startsWith(String prefix, int toffset):测试此字符串从指定索引开始的子字符串是否以指定前缀开始
    • boolean contains(CharSequence s):此字符串包含指定的 char 值序列时,返回 true
    • int indexOf(String str):返回指定子字符串在此字符串中第一次出现处的索引
    • int indexOf(String str, int fromIndex):返回指定子字符串在此字符串中第一次出现处的索引,从指定的索引开始
    • int lastIndexOf(String str):返回指定子字符串在此字符串中最右边出现处的索引
    • int lastIndexOf(String str, int fromIndex):返回指定子字符串在此字符串中最后一次出现处的索引,从指定的索引开始反向搜索
    • 提示:indexOf和lastIndexOf方法如果未找到都是返回-1

  • 替换:

    • String replace(char oldChar, char newChar):返回一个新的字符串,新字符串是用新的字符替换掉旧字符串里的就字符得来的
    • String replace(CharSequence target, CharSequence replacement):使用指定的字符串replace
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值