部分API的使用(数学、日期、字符串工具、数组工具、系统相关),正则表达式,可变字符串(十七)

282 阅读24分钟

API(部分)

​ 基础API是指JRE核心类库中已经定义好的一些类、方法等,Java程序员可以直接使用。

基础API有:

(1)数学相关的类和方法

(2)日期时间相关的类和方法

(3)字符串相关的类和方法

(4)数组工具类相关的类和方法

(5)系统相关的类和方法

数学相关

1、java.lang.Math类

​ Math 类包含用于执行基本数学运算的方法,如初等指数、对数、平方根和三角函数。

​ (1)常量E和PI

​ (2)求绝对值 abs

​ (3)三角函数

​ (4)static double ceil(double a):向上取整

​ static double floor(double a):向下取整

​ static long round(double a):四舍五入的效果

​ (5)平方根:sqrt

​ (6)求幂次方:pow

​ (7)求最大值,最小值:max,min

​ (8)求对数

​ ...

java.math包

(1)BigInteger:大整数

(2)BigDecimal:任意精度的小数

​ BigInteger和BigDecimal引用数据类型,和包装类又不同,不支持自动装箱与拆箱。

计算: (1)加 (2)减 (3)乘 (4)除 (5)模 ...

public class TestMath {
    @Test
    public void test05(){
        //之前表示小数,double类型最大
        System.out.println(Double.MAX_VALUE);
        //double类型的精度范围:科学计数法小数点后大概15-16位

        //如果想要表示比  Double.MAX_VALUE 还大的小数,或者小数点16位以上 怎么办?
        //使用BigDecimal
        BigDecimal big1 = new BigDecimal("9.22337205878558368547758075648995");
        BigDecimal big2 = new BigDecimal("5.8522233720368547758075648995");
        //计算
        System.out.println("和:" + big1.add(big2)) ;
        System.out.println("差:" + big1.subtract(big2)) ;
        System.out.println("积:" + big1.multiply(big2)) ;
//        System.out.println("商:" + big1.divide(big2)) ;//这个方法,仅适用于可以除尽的
        //java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.
        //上面求商的代码报错了,算术异常, 无法得到确定的结果,因为是无限循环小数
        //改用divide(BigDecimal divisor, int scale, int roundingMode)
        //divisor是除数,scale表示商的结果最终保留到小数点后几位
        //roundingMode表示舍入模式,如何处理第51位,可以选择直接舍去,也可以选择进位等方案
        //舍入模式的选择可以用RoundingMode枚举类的常量对象,也可以用BigDecimal自己的常量对象
        System.out.println("商:" + big1.divide(big2,47, RoundingMode.CEILING)) ;
        System.out.println("商:" + big1.divide(big2,52, RoundingMode.CEILING)) ;

        System.out.println("余:" + big1.remainder(big2)) ;
    }

    @Test
    public void test04(){
        //之前表示整数,long类型最大
        System.out.println(Long.MAX_VALUE);//9223372036854775807

        //如果想要表示比  9223372036854775807 还大的整数怎么办?
        //使用BigInteger
        BigInteger big1 = new BigInteger("92233720368547758075648995");
        BigInteger big2 = new BigInteger("58522233720368547758075648995");
        //计算
//        System.out.println("和:" +( big1 + big2)) ;//错误
        System.out.println("和:" + big1.add(big2)) ;
        System.out.println("差:" + big1.subtract(big2)) ;
        System.out.println("积:" + big1.multiply(big2)) ;
        System.out.println("商:" + big1.divide(big2)) ;
        System.out.println("余:" + big1.remainder(big2)) ;
    }

    @Test
    public void test03(){
        System.out.println(Math.max(4,7));
        System.out.println(Math.min(4,7));
    }
    @Test
    public void test02(){
        System.out.println(Math.sqrt(16));
        System.out.println(Math.pow(2,5));
    }

    @Test
    public void test01(){
        System.out.println(Math.ceil(2.7));//3.0
        System.out.println(Math.floor(2.7));//2.0
        System.out.println(Math.round(2.7));//3

        System.out.println("---------------------------");
        System.out.println(Math.ceil(2.3));//3.0
        System.out.println(Math.floor(2.3));//2.0
        System.out.println(Math.round(2.3));//2
    }
}

随机数

java.util.Random:用来获取随机值

之前:java.lang.Math类中random():产生的随机数范围[0,1)

现在:java.util.Random,获取各种类型的随机数

public class TestRandom {
    public static void main(String[] args) {
//        Math.random() 产生的随机数范围[0,1)
        System.out.println(Math.random());

        Random random = new Random();
        System.out.println(random.nextDouble());//[0,1)
        System.out.println(random.nextInt());//任意int的值
        System.out.println(random.nextInt(100));//[0,100)
        System.out.println(random.nextBoolean());//false/true
    }
}

日期时间API

JDK1.8之前

1、第一代日期时间API

​ java.util.Date类

​ java.text.DateFormat:因为Date类的格式显示不符合中国等非美国的其他国家和地区的阅读习惯,所以Java提供了DateFormat类来共Java程序员自己设定格式。

​ 通常不直接针对DateFormat使用,而是使用它的直接子类SimpleDateFormat 。

​ SimpleDateFormat 是一个以与语言环境有关的方式来格式化和解析日期的具体类。

​ (1)parse:解析 把字符串转为日期时间对象

​ (2)format:格式化 把日期时间对象转为字符串

2、第二代日期时间API

java.util.Calendar:

​ 它是抽象类,有一个直接子类 GregorianCalendar。

​ Calendar类中提供了静态方法,来获取 Calendar的这个子类对象。

​ Calendar中有很多常量对象,分别对应各个日期的值

JDK1.8之后

3、第三代日期时间API

本地化日期时间:LocalDate、LocalTime、LocalDateTime

和时区有关:ZonedDateTime

日期时间间隔:

​ Period:日期间隔

​ Duration:时间间隔

日期时间格式:DateTimeFormatter

​ (1)预定义标准:DateTimeFormatter类中有几个常量值,例如:ISO_DATE_TIME

​ (2)本地化相关的格式:

​ (3)自定义

public class TestDate {
    @Test
    public void test15() {
        LocalDateTime now = LocalDateTime.now();

        DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH时mm分ss秒  SSS毫秒  E 是这一年的D天");
        String str = df.format(now);
        System.out.println(str);
    }
    @Test
    public void test14() {
        LocalDateTime now = LocalDateTime.now();
        DateTimeFormatter df = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT);
        System.out.println(df.format(now));//21-7-15 上午11:02

        DateTimeFormatter df2 = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG);
        System.out.println(df2.format(now));//2021年7月15日 上午11时03分06秒

        DateTimeFormatter df3 = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM);
        System.out.println(df3.format(now));//2021-7-15 11:03:31
    }
    @Test
    public void test13(){
        LocalDateTime now = LocalDateTime.now();
        DateTimeFormatter df = DateTimeFormatter.ISO_DATE_TIME;//2021-07-15T11:01:31.226
        String str = df.format(now);
        System.out.println(str);
    }

    @Test
    public void test12() {
        LocalDate today = LocalDate.now();
        LocalDate kai = LocalDate.of(2021,6,22);
        Period p = Period.between(today, kai);
        System.out.println(p);//P-23D


        System.out.println("------------------------");
        LocalDate old = LocalDate.of(2020,10,1);
        Period p2 = Period.between(today, old);
        System.out.println(p2);//P-9M-14D
    }

    @Test
    public void test11() {
        ZonedDateTime t = ZonedDateTime.now();//默认本地
        System.out.println(t);

        ZonedDateTime t1 = ZonedDateTime.now(ZoneId.of("America/New_York"));//美国纽约
        System.out.println(t1);
    }
    @Test
    public void test10() {
        LocalDate today = LocalDate.now();
        System.out.println(today.isLeapYear());
    }

    @Test
    public void test09() {
        LocalDate today = LocalDate.now();
        LocalDate newDate = today.withMonth(5);//直接修改月份值
        System.out.println(today);
        System.out.println(newDate);

        LocalDate after5Month = today.plusMonths(5);
        System.out.println(after5Month);

        LocalDate before5Month = today.minusMonths(5);
        System.out.println(before5Month);

        LocalDate future = today.plusDays(1345);
        System.out.println(future);//2025-03-21
    }
    @Test
    public void test08() {
        LocalTime offLesson = LocalTime.of(12, 0, 0);
        System.out.println(offLesson);
    }

    @Test
    public void test07() {
        LocalDate birthday = LocalDate.of(1998, 5, 1);
        System.out.println(birthday);

        System.out.println(birthday.getYear());
        System.out.println(birthday.getMonth());
        System.out.println(birthday.getMonth().getValue());
        System.out.println(birthday.getDayOfMonth());
        System.out.println(birthday.getDayOfWeek());
        System.out.println(birthday.getDayOfYear());
    }

    @Test
    public void test06() {
        LocalDate today = LocalDate.now();
        LocalTime now = LocalTime.now();
        LocalDateTime todayNow = LocalDateTime.now();

        System.out.println(today);
        System.out.println(now);
        System.out.println(todayNow);
    }

    @Test
    public void test05() {
         //public static Calendar getInstance(TimeZone zone,   Locale aLocale)
        Calendar instance = Calendar.getInstance(TimeZone.getTimeZone("America/Los_Angeles"), Locale.US);
        System.out.println("年:" + instance.get(Calendar.YEAR));
        System.out.println("月:" + (instance.get(Calendar.MONTH) + 1));//月份是从0开始
        System.out.println("日:" + instance.get(Calendar.DAY_OF_MONTH));
        System.out.println("时:" + instance.get(Calendar.HOUR));
        System.out.println("分:" + instance.get(Calendar.MINUTE));
        System.out.println("秒:" + instance.get(Calendar.SECOND));

        //获取时区的id的方式
        /*String[] availableIDs = TimeZone.getAvailableIDs();
        for (int i = 0; i < availableIDs.length; i++) {
            System.out.println(availableIDs[i]);
        }*/
    }
    @Test
    public void test04(){
        Calendar instance = Calendar.getInstance();
        System.out.println(instance);

        System.out.println("年:" + instance.get(Calendar.YEAR));
        System.out.println("月:" + (instance.get(Calendar.MONTH) + 1));//月份是从0开始
        System.out.println("日:" + instance.get(Calendar.DAY_OF_MONTH));
    }

    @Test
    public void test03(){
//        Date(int year, int month, int date)
        Date d = new Date(2021,7,15);
        System.out.println(d);//Mon Aug 15 00:00:00 CST 3921
        //year参数是 减 1900 的年份。
//        month参数 0-11 的月份
    }

    @Test
    public void test02(){
        Date date = new Date();
        SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS D");
        //yyyy:四位数字的年份
        //MM:月份
        //dd:日期
        //HH:24小时制的小时值
        //mm:分钟
        //ss:秒
        //SSS:毫秒
        //D:是一年中的第几天
        System.out.println(sf.format(date));

        try {
            String str = "1990-1-25 23:25:21 785 25";
            Date d = sf.parse(str);
            System.out.println(d);
        } catch (ParseException e) {
            e.printStackTrace();
        }
    }

    @Test
    public void test01(){
        Date date = new Date();
        System.out.println(date);//Thu Jul 15 10:13:47 CST 2021
        //星期四,7月15日  上午10:13:47,公历2021年
        //精确到毫秒
        System.out.println(date.getTime());  //date这个日期时间对象,对应 的毫秒值
        //1626315338711  距离1970年1月1日的0:0:0 0毫秒
    }
}

系统相关类

1 java.lang.System类:

​ (1)常量对象:out,in,err

​ out:PrintStream类型,后面IO章节还会介绍PrintStream,用于输出,默认情况下输出到控制台

​ in:InputStream类型,后面IO章节还会介绍InputStream,用于输入,默认情况下用于从键盘输入

​ err:PrintStream类型,后面IO章节还会介绍PrintStream,用于输出,默认情况下输出到控制台。

它与out的区别,就是一个是打印普通信息,一个是打印错误信息。

out和err是分别由两个线程打印的。谁先出来不一定。

(2)long System.currentTimeMillis();

(3)static Properties getProperties() :获取系统属性

(4)public static void arraycopy(Object src, int srcPos, Object dest,int destPos,int length):用于数组复制或移动效果的方法

(5)static void gc() :调用gc方法,表示通知垃圾回收器来收拾垃圾。但是它不是立刻就能响应。

(6)static void exit(int status) :用于结束Java程序,退出JVM。根据惯例,非 0 的状态码表示异常终止。

2 java.lang.Runtime类:

​ 这个类是一个单例类。

​ 用于表示JVM虚拟机的运行环境,可以获取内存大小等信息

public class TestSystem {
    public static void main(String[] args) {
        System.out.println("hello");
        System.err.println("error");

        System.out.println("----------------");
        long time = System.currentTimeMillis();
        System.out.println(time);//1626319255384,距离1970年1月1日凌晨的毫秒值

        System.out.println("----------------");
        Properties properties = System.getProperties();
        System.out.println(properties);

        System.out.println("-------------------------");
        /*
        public static void arraycopy(
                            Object src, 源数组
                             int srcPos,  源数组起始位置
                             Object dest,   目标数组
                             int destPos,   模板数组的起始位置
                             int length)    要复制或移动的元素的个数
          奇怪:src和dest不是Object[]或int[]
             因为如果写Object[],只能支持对象数组
             如果写int[],只能支持int[]数组
             如果写Object类型,可以接收任意类型的数组
         */
        String[] arr = {"hello","java","world","atguigu"};
        System.arraycopy(arr, 0, arr, 1, 2);
        for (int i = 0; i < arr.length; i++) {
            System.out.println(arr[i]);
        }
        // {"hello","hello","java","atguigu"};
        System.out.println("----------------------");
        String[] arr2 = {"hello","java","world","atguigu"};
        System.arraycopy(arr2, 2, arr2, 1, 2);
        for (int i = 0; i < arr2.length; i++) {
            System.out.println(arr2[i]);
        }
        // {"hello","world","atguigu","atguigu"};

        System.out.println("----------------");
        Runtime runtime = Runtime.getRuntime();
        System.out.println(runtime.maxMemory());//最大内存
        System.out.println(runtime.freeMemory());//空闲内存
    }
}

数组工具类

java.util.Arrays工具类

(1)二分查找

public static int binarySearch(byte[] a,byte key)

public static int binarySearch(int[] a,int key)

public static int binarySearch(...[] a,... key) 要求数组是有序的,如果无序,结果是不正确的。返回值是负数表示不存在;返回值是0或正数,表示存在

(2)public static int[] copyOf(int[] original,int newLength)

public static int[] copyOfRange(int[] original,int from, int to)

​ 复制数组,下标范围左闭右开 [from, to) to可以超过数组的最大下标,from不能越界,from<=to

(3)public static String toString(int[] a)

(4)public static boolean equals(int[] a, int[] a2)

(5)public static void fill(int[] a,int val)

(6)public static void sort(int[] a) 各种基本数据类型的数组排序

public static void sort(Object[] a):给对象数组排序,要求元素实现Comparable接口

public static void sort(T[] a, Comparator<? super T> c) :给对象数组排序,必须要求单独有个类实现Comparator接口

public class TestArray {
    @Test
    public void test08(){
        Circle[] arr = new Circle[5];
        arr[0] = new Circle(2.3);
        arr[1] = new Circle(3.3);
        arr[2] = new Circle(1.3);
        arr[3] = new Circle(2.6);
        arr[4] = new Circle(3.3);

        Arrays.sort(arr, new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                return Double.compare(((Circle)o1).getRadius(), ((Circle)o2).getRadius());
            }
        });
        System.out.println(Arrays.toString(arr));
    }

    @Test
    public void test07(){
        Circle[] arr = new Circle[5];
        arr[0] = new Circle(2.3);
        arr[1] = new Circle(3.3);
        arr[2] = new Circle(1.3);
        arr[3] = new Circle(2.6);
        arr[4] = new Circle(3.3);

        Arrays.sort(arr);
        System.out.println(Arrays.toString(arr));
    }

    @Test
    public void test06(){
        int[] arr = {5,6,7,2,1};
        Arrays.sort(arr);
        System.out.println(Arrays.toString(arr));
    }

    @Test
    public void test05(){
        int[] arr = new int[5];
        Arrays.fill(arr, 10);
        System.out.println(Arrays.toString(arr));
    }
    @Test
    public void test04(){
        int[] arr1 = {1,2,3};
        int[] arr2 = {1,2,3,4};
        System.out.println(Arrays.equals(arr1,arr2));
        /*
        怎么比较?
        (1)先比较地址
        (2)考虑null
        (3)考虑长度
        (4)考虑元素
         */
    }

    @Test
    public void test03(){
        int[] arr = {4,6,8,16,17};
        int[] arr1 = Arrays.copyOfRange(arr, 2,4);
        System.out.println(Arrays.toString(arr));
        System.out.println(Arrays.toString(arr1));

        int[] arr2 = Arrays.copyOfRange(arr, 2, 2);
        System.out.println(Arrays.toString(arr2));
    }

    @Test
    public void test02(){
        int[] arr = {4,6,8,16,17};
        int[] arr1 = Arrays.copyOf(arr, 3);
        System.out.println(Arrays.toString(arr));
        System.out.println(Arrays.toString(arr1));

        int[] arr2 = Arrays.copyOf(arr, 10);
        System.out.println(Arrays.toString(arr2));
    }



    @Test
    public void test01(){
        int[] arr = {4,6,8,16,17};
        int value = 4;
        System.out.println(Arrays.binarySearch(arr, value));
    }


    //自己模拟写二分查找
    //数组是从小到大
    public int binaryFind(int[] arr, int value){
        int left = 0;
        int right = arr.length-1;
        while(left <= right){
            //int mid = (left + right)/2;
            int mid = left + (right-left)/2;

            if(arr[mid] == value){
                return mid;
            }else if(arr[mid] >value){//value在左
                right = mid - 1;
            }else{
                left = mid + 1;
            }
        }
        return -1;
    }
}

class Circle {//implements Comparable{
    private double radius;

    public Circle(double radius) {
        this.radius = radius;
    }

    public double getRadius() {
        return radius;
    }

    public void setRadius(double radius) {
        this.radius = radius;
    }

    @Override
    public String toString() {
        return "Circle{" +
                "radius=" + radius +
                '}';
    }

/*    @Override
    public int compareTo(Object o) {
        return Double.compare(radius, ((Circle)o).radius);
    }*/
}

数组的一些补充算法

1 反转

/*
数组的算法补充:
(1)数组的反转
A:整个数组全部反转
B:数组的其中一段反转

合起来:public static void reverse(int[] arr, int from ,int to)
反转[from, to)范围的元素
*/
public class TestReverse {
 public static void main(String[] args) {
/*        int[] arr = {1,2,3,4,5,6};
     reverse(arr, 0, arr.length);
     System.out.println(Arrays.toString(arr));

     System.out.println("------------------------");
     reverse(arr, 1, 4);
     System.out.println(Arrays.toString(arr));*/


     System.out.println("------------------------");
     int[] arr = {1,2,3,4,5,6};
     arr = reverse2(arr, 0, arr.length);
     System.out.println(Arrays.toString(arr));

     System.out.println("------------------------");
     arr = reverse2(arr, 1, 4);
     System.out.println(Arrays.toString(arr));
 }


 //反转整个数组
 public static void reverse(int[] arr){
     //思路:首尾对应位置交换
     for(int i=0; i<arr.length/2; i++){//循环的次数就是交换的次数
         int temp = arr[i];
         arr[i] = arr[arr.length-1-i];
         arr[arr.length-1-i] = temp;
     }
 }

 /*
 假设arr的长度是10,arr.length=10
 交换的范围 [2, 7)
         arr[2] ~  arr[6]
         arr[3] ~  arr[5]
  */
 public static void reverse(int[] arr, int from ,int to){
     //思路:反转部分  首尾对应位置交换
     for(int i=from,j=to-1; i<j; i++,j--){
         int temp = arr[i];
         arr[i] = arr[j];
         arr[j] = temp;
     }
 }


 public static int[] reverse2(int[] arr, int from ,int to){
     //思路:反转部分  借助一个新数组
     //(1)先创建一个新数组
     int[] newArr = new int[arr.length];

     //(2)把原来的数组的元素依照新的要求放到新数组中
     //[0, from)之前的照搬
     for (int i=0; i<from; i++){
         newArr[i] = arr[i];
     }

     //处理[from,to)如何搬到新数组
     for(int i=from,j=to-1; i<to; i++,j--){
         newArr[j] = arr[i];
     }


     //[to, arr.length)之后的照搬
     for (int i=to; i<arr.length; i++){
         newArr[i] = arr[i];
     }
     return newArr;
 }
}

2 扩容

/*
数组的扩容:
 数组的特点:(1)访问效率很高,根据[下标]可以直接定位元素
             (2)数组的长度是固定的

  如果数组的长度一开始设计的不够,需要存储更多的元素,那么怎么办?
  就是需要进行数组的扩容。

 思路:再创建一个更大的数组,把原来的数据搬过去

*/
public class TestArrayGrow {
 public static void main(String[] args) {
     int[] arr = {1,2,3,4,5};

     //有更多的数据了,需要对arr进行扩容
     int value = 6;//假设这是新数据

     //(1)创建一个新数组
     //新数组的长度:(1)可以在原来的长度上+1 (2)是原来的2倍 (3)原来的1.5倍(4)....
     //如何选择?  长度扩的大,可能造成浪费,但是扩容次数少
     //          长度扩的小,不会浪费,但是可能需要再次扩容
     //假设这里选择1.5倍
//        int[] newArr = new int[(int)(arr.length * 1.5)];
//        int[] newArr = new int[arr.length * 3 / 2];
//        int[] newArr = new int[arr.length + arr.length / 2];
     int[] newArr = new int[arr.length + (arr.length >> 1)];

     //(2)搬家
     for (int i=0; i<arr.length; i++){
         newArr[i] = arr[i];
     }

     //(3)让新数据住到新数组中
     newArr[arr.length] = value;

     //(4)让arr指向新数组
     arr = newArr;

     //(5)遍历arr
     System.out.println(Arrays.toString(arr));
 }
}

3 插入

/*
数组元素的插入:
 在arr数组中,插入一个元素,位置是[index]。

 (1)分为两种情况
 原来数组是满的,要先扩容再插入
 原来数组是未满的,不需要扩容,直接插入

 (2)如何插入
 先把[index]以及往后的元素先右移,然后把新元素放到[index]
*/
public class TestArrayInsert {

 public static void main(String[] args) {
     int[] arr = {1,2,3,4,5};//假设是满的

     int index = 1;//下标
     int value = 6; //把6插入到[1]位置,最后数组{1,6,2,3,4,5...}
     int total = arr.length;//实际的元素的个数

     //(1)先扩容+搬家
     arr = Arrays.copyOf(arr, arr.length *2);

     //(2)移动元素
     /*
     arraycopy方法的几个参数:
     (1)原数组
     (2)原数组起始下标
     (3)目标数组
     (4)目标起始位置
     (5)移动元素的个数

     index = 1;
     int[] arr = {1,2,3,4,5}
     移动 arr[1],arr[2],arr[3],arr[4]分别是2,3,4,5
      */
     System.arraycopy(arr, index, arr, index+1, total-index);

     //(3)把新数据放到[index]
     arr[index] = value;

     //(4)显示结果
     System.out.println(Arrays.toString(arr));
 }
}

4 删除

/*
假设删除[index]位置的元素。

数组元素的删除:
 (1)先搬家,把[index]后面的元素往前移动
 (2)然后把刚才最后一个元素置为空

*/
public class TestArrayDelete {
 public static void main(String[] args) {
     int[] arr = {1,2,3,4,5};

     int index = 1;//被删除元素的下标
     int total = arr.length; //实际元素的个数

     //移动元素
     System.arraycopy(arr, 2, arr ,1, total-index-1);

     //把刚才最后一个元素置为空
     arr[total-1] = 0;//默认值是0、

     System.out.println(Arrays.toString(arr));

 }
}

5 直接选择排序

/*
排序的方法:
(1)冒泡
(2)直接选择排序
 思路:每一轮从当前未排序的数据中,找到“最小”值,把“它”放到它应该在的位置。
*/
public class TestSelectSort {
 public static void main(String[] args) {
     int[] arr = {3,5,4,1,7};

     /*
     第一轮:所有元素都未排序,找它们的最小值1,1应该在[0]位置,现在1不在[0],就把1与[0]位置的元素直接交换
         {1,5,4,3,7}
     第二轮:除了1,剩下的元素未排序,找它们的最小值3,3应该在[1]位置,现在3不在[1]位置,就把3与[1]位置的元素直接交换
         {1,3,4,5,7}
      第三轮:除了1和3,剩下的元素未排序,找它们的最小值4,4应该在[2]位置,现在4在[2]位置不动
      第四轮:除了1和3,4,剩下的元素未排序,找它们的最小值5,5应该在[3]位置,现在5在[3]位置不动
      */
     for(int i=0; i<arr.length-1; i++){//外循环控制轮数 =  arr.length-1
         //(1)找本轮未排序元素中最小值及其下标
         /*
         第1轮:范围[0, arr.length-1]
         第2轮:范围[1, arr.length-1]
         第3轮:范围[2, arr.length-1]
         第4轮:范围[3, arr.length-1]

         如何找最小值?
          用一个min来存储最小值,一开始假设本轮的第一个元素最小,用剩下的元素与min比较
          */
         int min = arr[i];
         int minIndex = i;
         for(int j=i+1; j<arr.length; j++){
             if(arr[j] < min){
                 min = arr[j];
                 minIndex = j;
             }
         }

         //(2)判断本轮最小值是否在它应该在的位置,如果不在,就交换
         /*
         第1轮:范围[0, arr.length-1],min应该在[0]
         第2轮:范围[1, arr.length-1],min应该在[1]
         第3轮:范围[2, arr.length-1],min应该在[2]
         第4轮:范围[3, arr.length-1],min应该在[3]
         */
         if(minIndex != i){//min不在[i]的位置
             int temp = arr[i];
             arr[i] = arr[minIndex];
             arr[minIndex] = temp;
         }
     }

     //显示结果
     System.out.println(Arrays.toString(arr));
 }
}

字符串-String

java.lang.String类型

1、这个类,这个类的对象有什么特点?

(1)String类是final修饰的

(2)所有程序中,""引起来的都是String类的对象

(3)字符串对象不可变

考题类型有两种:

​ A:String类型的形参, 在方法里面对String类型形参的修改,如果没有返回的话,是和实参无关

​ B:String类型的对象修改后,没有接收

(4)因为 String 对象是不可变的,所以可以共享。

​ 字符串常量对象是可以共享的

​ 注意:非常量对象是不可以共享的

(5)String对象底层是用什么存储的?

JDK1.9之前:char[]

​ "hello" 等价于 char[] value = {'h','e','l','l','o'}

JDK1.9之后:byte[]

​ 为什么要修改?

​ 因为char类型的数据,占2个字节。大部分程序中出现的String对象都是由ASCII表范围内的字符组成的,例如:hello。 而这个范围的字符编码都在[0,127]范围内,使用1个字节就可以表示了。这样的话,如果把String底层改为byte[],可以节约很多内存。 如果你的String对象中的字符是在1个byte表示以外,仍然用2个byte来表示。

(6)String类型的对象是唯一一种支持“+”的引用数据类型。 +:表示拼接,这是因为底层对+运算符进行了“重载”。

基本数据类型的包装类对象可以进行+的基本运算,是因为发生了自动拆箱,并不是包装类对象之间进行的运算。

2、面试题:

(1)String类型的对象为什么不可变?

因为String内部使用char[]或byte[]数组来存储字符串内容,而这个char[]或byte[]数组是final和private修饰。

A:private:这个value数组在String类外面无法直接操作(不可见)。

B:final:value不能变,即char[] value数组不能进行扩容等操作。因为如果要扩容等,会需要创建新数组,value指向新数组,现在无法进行这个操作。

C:按理说value数组的元素是可以改变,这其实是String类中所有涉及到修改value元素值的方法,例如:replace等,都会复制一个新数组,然后修改,然后返回新数组构成的新字符串对象,不会在原来value数组中直接修改,所以String类型的对象不可变。

(2)字符串常量对象存在哪里?

​ JDK1.7之前:方法区

​ JDK1.7:堆

​ JDK1.8:元空间

3、字符串对象的创建方式

方式一:

String类型的构造器:

(1)无参构造 new String();

(2)public String(String original)

(3)String(char[] value)

​ (4)String(byte[] bytes)

方式二:

String类型是唯一一种,可以直接=字符串常量值,而不用构造器创建对象的。

方式三:

String类型的对象与其他类型的值进行“+”结果也是一个新的 String对象。

方式四:

所有对象调用toString结果都是一个String。

public class TestStringCreate {
    @Test
    public void test05(){
        String str = "hello";

        Integer i = 1;//自动装箱
    }

    @Test
    public void test04(){
        byte[] arr = {97,98,48,50};
        String str = new String(arr);
        System.out.println(str);//ab02
    }

    @Test
    public void test03(){
        char[] value = {'h','e','l','l','o'};
        String str = new String(value);
        //问?String中的value数组与  现在test03方法中的value数组什么关系?
        //源码:String的this.value = Arrays.copyOf(value, value.length);
        //两个不同的数组,只是长度和元素一样
        //为什么呢? this.value=value;这样写就会导致String和test03方法中的value数组是同一个,就无法保证“不可变”

        value[0] = 'a';
        System.out.println(str);
    }

    @Test
    public void test02(){
        String str = new String("hello");
        //这是两个字符串
        //一个是常量池中的"hello"
        //一个是堆中的new String(xx)
        //这两个字符串对象有什么关系呢?
        //它俩的value是相等的,但是是属于两个不同的字符串对象

        String str2 = "hello";
        System.out.println(str == str2);//false
        System.out.println(str.equals(str2));//true
    }

    @Test
    public void test01(){
        String str = new String();//""
        //长度为0的字符串,底层char[]/byte[]  长度为0的数组{}

        char[] value = new char[0];//长度为0的char数组
    }
}

4、字符串的比较

结论:

​ 如果参与拼接的都是指向常量池中的常量 ,结果还是常量池中。

​ 如果有“堆”中的常量或变量参与,结果都在堆中。

​ 无论前面是什么,只要最后调了intern(),结果都在常量池中

public class TestStringCompare {
    @Test
    public void test06(){
        String str1 = "hello";
        String str2 = "world";
        String str3 = "helloworld";

        String str4 = "hello" + "world"; //常量池中的字符串常量 + 常量池中的字符串常量  结果 仍然是常量池中的字符串常量
        String str5 = (str1 + "world").intern();// 无论前面是什么,只要最后调了intern(),结果都在常量池中
        String str6 = (str1 + str2).intern(); //

        System.out.println(str3 == str4);//true
        System.out.println(str3 == str5);//true
        System.out.println(str3 == str6);//true
        System.out.println(str5 == str6);//true
    }

    @Test
    public void test05(){
        final String str1 = new String("hello");
        final String str2 = new String("world");
        String str3 = "helloworld";

        String str4 = "hello" + "world"; //常量池中的字符串常量 + 常量池中的字符串常量  结果 仍然是常量池中的字符串常量
        String str5 = str1 + "world";// str1本身是常量,但是它指向了堆中的字符串常量
                                    //指向堆中常量 + 常量池中常量  结果在堆中
        String str6 = str1 + str2; //指向堆中常量 + 指向堆中常量 结果在堆中

        System.out.println(str3 == str4);//true  str3和str4都是代表常量池中的字符串常量“helloworld"
        System.out.println(str3 == str5);//false
        System.out.println(str3 == str6);//false
        System.out.println(str5 == str6);//false

        System.out.println(str3);
        System.out.println(str4);
        System.out.println(str5);
        System.out.println(str6);
    }

    @Test
    public void test04(){
        final String str1 = "hello";
        final String str2 = "world";
        String str3 = "helloworld";

        String str4 = "hello" + "world"; //常量池中的字符串常量 + 常量池中的字符串常量  结果 仍然是常量池中的字符串常量
        String str5 = str1 + "world";// str1本身是常量,并且它指向了常量池中的字符串常量
                                    //指向常量池中常量 + 常量池中常量  结果在常量池中
        String str6 = str1 + str2; //指向常量池中常量常量 + 指向常量池中常量 结果在常量池中

        System.out.println(str3 == str4);//true  str3和str4都是代表常量池中的字符串常量“helloworld"
        System.out.println(str3 == str5);//true
        System.out.println(str3 == str6);//true
        System.out.println(str5 == str6);//true

        System.out.println(str3);
        System.out.println(str4);
        System.out.println(str5);
        System.out.println(str6);
    }

    @Test
    public void test03(){
        String str1 = "hello";
        String str2 = "world";
        String str3 = "helloworld";

        String str4 = "hello" + "world"; //常量池中的字符串常量 + 常量池中的字符串常量  结果 仍然是常量池中的字符串常量
        String str5 = str1 + "world";// str1本身是本量,只是它指向了常量池中的字符串而已
                                    //变量 + 常量  结果在堆中
        String str6 = str1 + str2; //变量 + 变量 结果在堆中

        System.out.println(str3 == str4);//true  str3和str4都是代表常量池中的字符串常量“helloworld"
        System.out.println(str3 == str5);//false
        System.out.println(str3 == str6);//false
        System.out.println(str5 == str6);//false

        System.out.println(str3);
        System.out.println(str4);
        System.out.println(str5);
        System.out.println(str6);
    }

    @Test
    public void test02(){
        String str1 = new String("hello");
        String str2 = "hello";
        System.out.println(str1 == str2);//false
        System.out.println(str1.equals(str2));//true
    }

    @Test
    public void test01(){
        String str1 = "hello";
        String str2 = "hello";
        System.out.println(str1 == str2);//true
        System.out.println(str1.equals(str2));//true
    }
}

5、字符串对象的个数

(1)字符串常量对象

String str1 = "hello";
//1个 在常量池里

(2)字符串的普通对象和常量对象一起

String str2 = new String("hello");
//str2首先指向堆中的一个字符串对象,然后堆中字符串的value数组指向常量池中的常量对象的value数组

6、字符串对象的内存分析

String s;

String s = null;

String s = "";
String s = new String();
String s = new String("");

String s = "abc";
String s = new String("abc");

char[] arr = {'a','b'};
String s = new String(arr);


char[] arr = {'a','b','c','d','e'};
String s = new String(arr,0,3);

![1562945799274](F:\尚硅谷\尚硅谷_210622Java_柴林燕_JavaSE\预习笔记(部分)\第10章 基础API与常见算法\imgs\1562945799274.png)

7、空字符串的比较

(1)哪些是空字符串

String str1 = "";String str2 = new String();String str3 = new String("");

(2)判断某个字符串是否为空

if("".equals(str))//为什么不使用str.equals("")?//str.equals("")容易报空指针异常if(str!=null  && str.isEmpty())if(str!=null && str.equals(""))if(str!=null && str.length()==0)

8、字符串的常用方法

常规

(1)boolean isEmpty():字符串是否为空

(2)int length():返回字符串的长度

(3)String concat(xx):拼接,等价于+

(4)boolean equals(Object obj):比较字符串是否相等,区分大小写

(5)boolean equalsIgnoreCase(Object obj):比较字符串是否相等,不区分大小写

(6)int compareTo(String other):比较字符串大小,区分大小写,按照Unicode编码值比较大小

​ 这个方法是重写实现Comparable接口的抽象方法,说明字符串支持比大小,可以直接用Arrays.sort(字符串数组)进行排序。

(7)int compareToIgnoreCase(String other):比较字符串大小,不区分大小写

(8)String toLowerCase():将字符串中大写字母转为小写

(9)String toUpperCase():将字符串中小写字母转为大写

(10)String trim():去掉字符串前后空白符

查找

(11)boolean contains(xx):是否包含xx

(12)int indexOf(xx):从前往后找当前字符串中xx,即如果有返回第一次出现的下标,要是没有返回-1

(13)int lastIndexOf(xx):从后往前找当前字符串中xx,即如果有返回最后一次出现的下标,要是没有返回-1

字符串截取

(14)String substring(int beginIndex) :返回一个新的字符串,它是此字符串的从beginIndex开始截取到最后的一个子字符串。

(15)String substring(int beginIndex, int endIndex) :返回一个新字符串,它是此字符串从beginIndex开始截取到endIndex(不包含)的一个子字符串。

和字符相关

(16)char charAt(index):返回[index]位置的字符

(17)char[] toCharArray(): 将此字符串转换为一个新的字符数组返回

(18)String(char[] value):返回指定数组中表示该字符序列的 String。

(19)String(char[] value, int offset, int count):返回指定数组中表示该字符序列的 String。

(20)static String copyValueOf(char[] data): 返回指定数组中表示该字符序列的 String

(21)static String copyValueOf(char[] data, int offset, int count):返回指定数组中表示该字符序列的 String

(22)static String valueOf(char[] data, int offset, int count) : 返回指定数组中表示该字符序列的 String

(23)static String valueOf(char[] data) :返回指定数组中表示该字符序列的 String

编码与解码

(24)byte[] getBytes():编码,把字符串变为字节数组,按照平台默认的字符编码进行编码

​ byte[] getBytes(字符编码方式):按照指定的编码方式进行编码

(25)new String(byte[] ) 或 new String(byte[], int, int):解码,按照平台默认的字符编码进行解码

​ new String(byte[],字符编码方式 ) 或 new String(byte[], int, int,字符编码方式):解码,按照指定的编码方式进行解码

常见的编码方式:

(1)ISO08859

(2)GBK或GB2312

(3)UTF-8

(1)绝对不支持中文,(2)(3)支持中文

开头与结尾

(26)boolean startsWith(xx):是否以xx开头

(27)boolean endsWith(xx):是否以xx结尾

正则匹配

(28)boolean matchs(正则表达式):判断当前字符串是否匹配某个正则表达式

常用正则表达式:
字符类

[abc]abc(简单类)

[^abc]:任何字符,除了 abc(否定)

[a-zA-Z]azAZ,两头的字母包括在内(范围)

预定义字符类

.:任何字符(与行结束符可能匹配也可能不匹配)

\d:数字:[0-9]

\D:非数字: [^0-9]

\s:空白字符:[ \t\n\x0B\f\r]

\S:非空白字符:[^\s]

\w:单词字符:[a-zA-Z_0-9]

\W:非单词字符:[^\w]

POSIX 字符类(仅 US-ASCII)

\p{Lower} 小写字母字符:[a-z]

\p{Upper} 大写字母字符:[A-Z]

\p{ASCII} 所有 ASCII:[\x00-\x7F]

\p{Alpha} 字母字符:[\p{Lower}\p{Upper}]

\p{Digit} 十进制数字:[0-9]

\p{Alnum} 字母数字字符:[\p{Alpha}\p{Digit}]

\p{Punct} 标点符号:!"#$%&'()*+,-./:;<=>?@[]^_`{|}~

\p{Blank} 空格或制表符:[ \t]

边界匹配器

^:行的开头

$:行的结尾

Greedy 数量词

X?X,一次或一次也没有

X*X,零次或多次

X+X,一次或多次

X{n}X,恰好 n

X{n,}X,至少 n

X{n,m}X,至少 n 次,但是不超过 m

Logical 运算符

XYX 后跟 Y

X|YXY

(X):X,作为捕获组

特殊构造(非捕获)

(?:X) X,作为非捕获组

(?=X) X,通过零宽度的正 lookahead

(?!X) X,通过零宽度的负 lookahead

(?<=X) X,通过零宽度的正 lookbehind

(?<!X) X,通过零宽度的负 lookbehind

(?>X) X,作为独立的非捕获组

1.验证用户名和密码,要求第一个字必须为字母,一共6~16位字母数字下划线组成:(^[a-zA-Z]\w{5,15}$)

2.验证电话号码:xxx/xxxx-xxxxxxx/xxxxxxxx:(^(\d{3,4}-)\d{7,8}$)

3.验证手机号码:( ^(13[0-9]|14[5|7]|15[0|1|2|3|5|6|7|8|9]|18[0|1|2|3|5|6|7|8|9])\d{8}$ )

4.验证身份证号: (^\d{15})(\d18)|(^\d{18})|(^\d{17}(\d|X|x)$)

5.验证Email地址:(^\w+([-+.]\w+)@\w+([-.]\w+).\w+([-.]\w+)*$)

6.只能输入由数字和26个英文字母组成的字符串:(^[A-Za-z0-9]+$)

7.整数或者小数:(^[0-9]+(.[0-9]+){0,1}$)

8.中文字符的正则表达式:([\u4e00-\u9fa5])

9.金额校验(非零开头的最多带两位小数的数字):(^([1-9][0-9]*)+(.[0-9]{1,2})?$)

10.IPV4地址:(((\d{1,2})|(1\d{1,2})|(2[0-4]\d)|(25[0-5]))\.){3}((\d{1,2})|(1\d{1,2})|(2[0-4]\d)|(25[0-5]))

替换

(29)String replace(xx,xx):不支持正则

(30)String replaceFirst(正则,value):替换第一个匹配部分

(31)String repalceAll(正则, value):替换所有匹配部分

拆分

(32)String[] split(正则):按照某种规则进行拆分

特殊情况(需要转义)

(1)"."

他是一个正则表达式,

\.//错误的

因为在java中\是转义字符,所以这个表示的意义也和我们想要表达的意义不同。

想要表示使用.进行分割需要

\\.

(2)"|"

同样的,正确的正则表达式

\\|

可变字符串-StringBuffer和StringBuilder

分为两种:

StringBuffer

StringBuilder

1 线程安全性

这两个API几乎一致,唯一的区别在于:

​ StringBuffer是线程安全的;

​ StringBuilder是线程不安全的。

和String之间的区别:

​ StringBuffer、StringBuilder是可变的,String是不可变的。

2 效率

​ 相同情况下效率:StringBuilder>StringBuffer>String

​ StringBuilder效率比StringBuffer效率高。String效率最低。

​ 相同情况下空间消耗:StringBuilder=StringBuffer>String

3 方法

(1)拼接方法

append(xx):支持各种数据类型

(2)插入

insert

(3)删除

delete(int start,int end);

deleteCharAt(int index);

(4)反转字符串

reverse

(5)其他

indexOf()

lastIndexOf()

...

4 StringBuffer和StringBuilder存储字符串内容的方式

底层也是char[]存储

5 char[]数组的初始化长度

默认情况下,char[]一开始长度为16,也可以手动指定char[]初始长度

6 char[]数组满了怎么解决?

会copy到一个新char[]数组,也就是自动扩容。

但是这个数组不会自动缩减。如果想要缩减,可以调用trimToSize()方法,这个方法用于尝试减少存储空间。