常用类

包装类

定义

1.针对八种基本数据类型相应的引用类型--包装类
2.有了类的特点就可以调用类中的方法

基本数据类型 包装类
boolean Boolean
char Charater
byte Byte
short Short
int Integer
long Long
float Float
double Double

其中,除了布尔类型和字符类型,其余的父类都是Number

包装类和基本数据类型的转换

基本类型-->包装类型 装箱
包装类型-->基本类型 拆箱

package javaSEStudy.wrapper;

//演示 int <--> Integer 的装箱和拆箱
public class Integer_ {
    public static void main(String[] args) {
        //jdk5之前 手动拆箱与装箱
        int num1 = 100;

//        Integer integer = new Integer(num1);
//        ps:该写法在jdk9已被废弃
//        或
        Integer in = Integer.valueOf(num1);

        //手动拆箱
        int i1 = in.intValue();

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

        //在jdk5以后,就可以自动装箱和自动拆箱
        int num2 = 200;

        //自动装箱
        //这种写法底层调用的是 Integer.valueOf(num2)
        Integer i2 =num2;

        //自动拆箱
        //这总写法底层调用intValue()
        int num3 = i2;

    }
}

常见面试题
其他的数据类型基本类似,这里就不一一举例
注意:三元运算符是是一个整体

Object obj ? new Integer(1):new Double(2.0);
sout(obj);

上面会输出1.0
因为三元运算符是一个整体,其中double精度最高,所以输出double格式

包装类型和String类型的相互转换

package javaSEStudy.wrapper;

// 包装类型和String类型的相互转换
public class String_ {
    public static void main(String[] args) {
        /**
         * 将Integer转换为String
         */
        Integer num1 = 100;
        // 方式一 ,注意该方法只是把一个新的对象交给了str1,而num1是没有变的
        // 此例子中得到的是"100",而原来的num1没变,他们是互不相干的两个类型
        String str1 = num1 + "";

        // 方式二
        String str2 = num1.toString();

        // 方式三
        String str3 = String.valueOf(num1);


        /**
         * 将String转换为Integer
         */
        String str = "这是一个测试用例";

        // 方式一 使用自动装箱
        // 此方法前面会输出int类型,因为parseInt源码返回类型就是int,手动改为Integer,他会自动装箱,该写法没有问题
        Integer i1 = Integer.parseInt(str);

        // 方式二 使用构造器
        // 该写法已被废弃,这里只做了解
//        Integer i2 = new Integer(str);

    }
}

Integer和Charater常用方法

Integer.MIN_VALUE  // 返回最小值
Integer.MAX_VALUE  // 返回最大值

Charater.isDigit('a')  // 判断是不是数字
Charater.isLetter('a')  // 判断是不是字母
Charater.isUpperCase('a')  // 判断是不是大写
Charater.isLowerCase('a')  // 判断是不是小写

Charater.isWhitespace('a')  // 判断是不是空格
Charater.toUpperCase('a')  // 转成大写
Charater.toLowerCase('A')  // 转成小写

常见面试题

//This method will always cache values in the range -128 to 127,

public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }

讲解:
顺序输出 False True False

第一个输出假,是因为这里用的是new,分别new 了一个对象,这里是==.不同对象的hashCode不同,这里也就为False
第二个输出为真,需要看源码,源码如图所示,这里使用的是自动装箱,也就是采用的Integer.valuesOf,而在源码中,如果在-128~127这个范围内,则会返回这个值,否则返回一个new的对象
第三个则由源码可知,不在该范围内,会new 一个新的对象,所以他们不等,输出假

但是在这里要注意

这里两个都输出的是True
因为只要有基本数据类型参与比较的,则他们比较的就是值的大小

String类

String 类的理解和创建对象

  • String 对象用于保存字符串,也就是一组字符序列
  • 字符串常量对象是用双括号括起的字符序列
  • 字符串的字符使用的是Unicode字符编码,一个字符(不区分字母还是汉字)占两个字节
  • String类常用构造方法
    • String s1 = new String();
    • String s2 = new String(String original);
    • String s3 = new String(char[] a);
    • String s4 = new String(char[] a,int startIndex,int count)


这里String分别实现了三个接口
Serializable --> 如果实现了该接口,代表我们的对象可以串行化,一旦可以串行化,就代表String对象可以网络传输
Comparable --> 如果实现该接口,就说明String对象可以进行比较大小

ps:
5.String 是final类,不能被其他类继承
6.private final char[] value;这是String的源码,在底层被转换为字符数组存储
7.一定要注意:value是一个final类型,赋值后就 不可修改(内容可以修改,但是value指向的地址不可以修改)

package javaSEStudy.wrapper.String_;

public class String1 {
    public static void main(String[] args) {
        // 1.String 对象用于保存字符串,也就是一组字符序列
        // 2.在这里"蔡徐坤"就是字符串常量 双括号引起来的字符序列
        // 3.String类有很多的构造器,构造器的重载
        // 4.两个重要接口Serializable(对象串行化,可进行网络传输) Comparable(字符串比较大小)
        // 5.String 是final类,不能被其他类继承
        // 6.private final byte[] value;这是String的源码,在底层被转换为字节数组存储
        // 7.一定要注意:value是一个final类型,赋值后就 不可修改(内容可以修改,但是value指向的地址不可以修改)

        String name = "蔡徐坤";

        // 下面这种写法并没有报错,证明内容是可以修改的
        final char[] value = {'a', 'b', 'c', 'd', 'e', 'f'};
        value[0] = 'H';

        // 下面这种写法就会报错,因为上面为final,无法赋值
//        char[] a = {'a','c','e'};
//        value = a;  不可以修改value的地址
    }
}

创建String对象的两种方式

一 直接赋值: String str = "蔡徐坤";
二 调用构造器: String str = new String("蔡徐坤");

两种创建String对象的区别
一:先从常量池查看是否有"蔡徐坤"的数据空间,如果有则直接指向,如果没有则直接创建,然后指向;str最终指向的是常量池的空间地址;
二:先在堆中创建空间,里面维护了value属性,指向了常量池的蔡徐坤空间.如果常量池没有"蔡徐坤",直接创建,如果有,通过value指向,最终指向的是堆中的空间地址

练习题
第一问


所以最终输出结果为:T F T F

第二问

最终输出结果为:T T T F

第一个创建了两个对象p1和p2,然后他们指向的常量池是同一个,所以第一个sout使用equals比较字符串的内容是否相等,输出T
第二个使用的是p1.name和p2.name,比较的是常量池中的对象,他们指向的是同一个常量,所以相等
第三个这里的"hspedu"就是常量池中的"hspedu",所以他们的相等
第四个比较的是s1 和 s2,他们分别是堆中的两个对象,所以这里不等

字符串的特性

String是一个final类,代表不可变的字符序列
字符串是不可变的,一个字符串对象一旦被分配,其内容是不可变的

第一问

这里它先创建了一个对象s1,然后指向了常量池"hello",然后s1这个对象去查找常量池是否有"haha",没有它创建了一个"haha",然后指向了"haha",原来 s1 指向的 "hello" 对象依然存在于字符串常量池中(没有被销毁)

第二问

这里只创建了一个对象,编译器会在编译时对其优化,会将这两个字符串进行拼接为helloabc,然后去常量池寻找是否有该字符串.

第三问

总共创建了三个对象

package javaSEStudy.wrapper.String_;

public class String2 {
    public static void main(String[] args) {
        String a = "hello";
        String b = "abc";

        // 1.先创建一个 StringBuilder sb = StringBuilder();
        // 2.执行 sb.append("hello");
        // 3.再执行 sb.append("abc");
        // 4.String c = sb.toString()
        // 最后,其实c指向的是堆中的对象 value[],它是String类型,然后value指向池中的"helloabc"

        String c = a + b;
        String d = "helloabc";
        System.out.println(c == d); // 输出F
    }
}

第四问

package javaSEStudy.wrapper.String_;

public class String3 {
    public static void main(String[] args) {
        Test1 ex = new Test1();
        ex.change(ex.str, ex.ch);
        System.out.print(ex.str + " and ");
        System.out.println(ex.ch);
    }
}

class Test1 {
    String str = new String("hsp");
    final char[] ch = {'j', 'a', 'v', 'a'};

    public void change(String str, char[] ch) {
        str = "java";
        ch[0] = 'h';
    }
}

输出结果

分析:
我们先画出内存分析图(有点丑)

这里需要注意char数组存储在堆中,并且我们调用了一个方法会产生一个新栈
  首先是堆中的一个空间会产生str和ch两个对象,str通过堆中产生一个value空间,在通过value指向池中的hsp,ch则直接指向堆中的空间,并创建一个数组存储java
  然后我们main线程产生了一个对象ex,去指向堆中的两个对象
  然后我们调用了一个方法,就产生了一个新的栈,他这里的两个对象是方法中的String str和char[] ch,注意这里str直接指向了堆中的value,再指向hsp,ch则直接指向了堆中的java数组,调用方法的时候,str发现常量池中没有java,它就创建了一个常量java,并且直接指向了常量池,不会再通过堆中value去指向该常量,而ch则直接修改了数组中的值
  注意:此时方法结束,他的生命周期也到此为止,该栈会直接被销毁,回到main方法,在这里的str指向并未被修改,还是通过value指向的hsp,而ch的指向已被修改
  最终,它输出的就是 hsp and hava

String类的常用方法

String类是保存字符串常量的,每次更新都需要重新开辟空间,效率较低,因此java设计者还提供了StringBuilderStringBuffer来增强String功能
常见方法一:

  • equals :区分大小写判断内容是否相等
  • equalsIgnoreCase :忽略大小写的判断内容是否相等
  • length :获取字符的个数,字符串的长度
  • indexOf :获取字符在字符串中第一次出现的索引,索引从0开始,如果找不到,则返回-1
  • lastIndexOf :获取字符在字符串中最后一次出现的索引,索引从0开始,如果找不到则返回-1
  • substring :截取指定范围的子串
  • trim :去除前后空格
  • charAt :获取某索引的处的字符,注意不能使用Str[index]这种方式

substring解释:

String name = "hello,张三";
sout(name.substring(6)); //该写法是指将字符串从索引6的位置,把后面所有的内容截取
sout(name.substring(0,5)); //表示从索引0开始截取,截取到第5个字符,左闭右开区间

charAt解释:

String str = "hello";
//这里我们想取出h
str[0] //该写法报错,String不是数组,他用的value存储,无法取出
str.charAt(0) //该写法才最终输出h

常见方法二:

  • toUpperCase :将字符串全部转换为大写
  • toLowerCase :将字符串全部转换为小写
  • concat :拼接
  • replace :替换字符串中的字符
  • split :分割字符串,对于某些分割字符,我们需要转义,比如| \ 等
  • compareTo :比较两个字符串大小,如果前者大,返回正数,如果后者大,返回负数,相等则返回零
  • toCharArray :转换成字符数组
  • startsWith :检查字符串是否以指定前缀开头
  • formart :格式字符串,%s字符串,%c字符,%d整型,%.2f浮点型 这里%.2f保留小数点后两位,并且进行四舍五入操作

concat解释:

String s1 = "贾宝玉";
s1 = s1.concat("林黛玉").concat("薛宝钗").concat("together");
sout(s1);

//最终输出结果 贾宝玉林黛玉薛宝钗together

replace解释:
注意此处,只是s1.replace()返回的结果将其替换,s1本身是不会发生改变,如果下面不用s1接收,改为s11接收返回结果,输出s1则会将原句输出

String s1 = "贾宝玉 and 林黛玉 林黛玉 林黛玉";
//将所有的林黛玉替换为薛宝钗
s1 = s1.replace("林黛玉","薛宝钗");
sout(s1);

//最终输出结果为 贾宝玉 and 薛宝钗 薛宝钗 薛宝钗

split解释:

String[] poem = "锄禾日当午,汗滴禾下土,谁知盘中餐,粒粒皆辛苦";
// 以逗号为标准,对 poem 进行分割,返回一个数组
String[] split = poem.split(",");
for(i<0;i<splint.length;i++){
  sout(split[i]);
}

// 在对字符串进行分割时,如果有特殊字符,需要加入转义符 \
poem = "E:\\aaa\\bbb";
split = poem.split("\\\\");
for(i<0;i<splint.length;i++){
  sout(split[i]);
}
//最终输出
/*
E:
aaa
bbb
*/

toCharArray解释:

s = "happy";
char[] chars = s.toCharArray();
for(i<0;i<chars.length;i++){
  sout(chars [i]);
}
/*
最终输出:
  h
  a
  p
  p
  y
*/

format解释:

//占位符有 :%s %c %d %.2f
String name = "jack";
int age = 10;
double score = 98.3/3;
char gender = '男';

// 该写法特别麻烦
String info = "名字:"+name+"年龄"+age+"成绩是:"+score+"性别是:"+gender;
sout(info);

//格式字符串
String info2 = String.format("名字是%s,年龄是%d,成绩是%.2f,性别是%c",name,age,score,gender);
sout(info2);
//除了这样,还有另外写法
String formatStr = "名字是%s,年龄是%d,成绩是%.2f,性别是%c";
String info2 = String.format(formartStr,name,age,score,gender);
sout(info2);

StringBuffer类

java.lang.StringBuffer代表可变的字符序列,可以对字符串内容进行增删
很多方法与String相同,但是StringBuffer是可变长度的
StringBuffer是一个容器

特点:
StringBuffer是final类,不能被继承
实现了Serializable接口,可以保存到文件或者网络传输(串行化)
继承了抽象类AbstractStringBuffer
AbstractStringBuffer中的属性char[] value,存放字符序列,他不是final,因此存储数据存放在堆中

String VS StringBuffer

  • String保存的是字符串常量,里面的值不能更改,每次String类的更新实际上就是更改地址效率较低
    private final char value[]
  • StringBuffer保存的是字符串变量,里面的值可以更改,每次的StringBuffer的更新实际上可以更新内容,不用每次更新地址,效率较高
    char[] value 存放在堆

简单理解:StringBuffer的字符内容是存放在 char[] value (堆)中,每次变化(增加/删除)都不用更新地址(创建新的对象),效率高于String

StringBuffer的构造器

StringBuffer() :构造一个其中不带字符的字符串缓冲区,其初始容量为16个字符
StringBuffer(CharSequence seq) :public java.lang.StringBuffer(CharSequence seq)构造一个字符串缓冲区,它包含与指定的CharSequence相同的字符(了解)
StringBuffer(int capacity) // capacity[容量] :构造一个不带字符但是具有指定初始容量的字符串缓冲区,即堆char[]大小进行指定
StringBuffer(String str) :构造一个字符串缓冲区,并将其内容指定为字符串内容

package javaSEStudy.wrapper.StringBuffer_;

// StringBuffer 构造器的使用
public class StringBuffer_Constructor {
    public static void main(String[] args) {

        // 1.创建一个大小为16的char[] 数组,用于存放字符内容
        StringBuffer stringBuffer1 = new StringBuffer();

        // 2.通过构造器指定char[] 大小
        StringBuffer stringBuffer2 = new StringBuffer(100);

        // 3.通过给一个String 创建StringBuffer ,char[] 大小就是str.length+16
        StringBuffer stringBuffer3 = new StringBuffer("java");

    }
}

String和StringBuffer转换

package javaSEStudy.wrapper.StringBuffer_;

// String 和 StringBuffer 转换
public class StringAndStringBuffer {
    public static void main(String[] args) {
        String str = "hello world";

        //Sting ---> StringBuffer
        // 1.使用构造器,ps:这里只有接收返回的数据buffer才是StringBuffer,str本身依旧是String类型
        StringBuffer buffer1 = new StringBuffer(str);

        // 2.使用append方法
        StringBuffer buffer2 = new StringBuffer();
        buffer2 = buffer2.append(str);


        //StringBuffer ---> String
        StringBuffer buffer3 = new StringBuffer("基尼太美");
        // 1.使用StringBuffer提供的toString方法
        String str1 = buffer3.toString();

        // 2.使用构造器
        String str2 = new String(buffer3);
    }
}

StringBuffer常见方法

  • append :增
  • delete(start,end) :删
  • replace(start,end,string) :改,将Start -- end 间的内容替换掉,不含end,左闭右开区间
  • indexOf :查,查找子串在字符串第一次出现的索引,如果找不到就返回-1
  • insert :插
  • length :获取长度
  • reverse() :字符序列反转
package javaSEStudy.wrapper.StringBuffer_;

// StringBuffer 方法
public class StringBuffer_Method {
    public static void main(String[] args) {
        StringBuffer stringBuffer = new StringBuffer("java");
        // 增 append:追加
        stringBuffer.append(',');
        stringBuffer.append("张三丰");
        stringBuffer.append("赵敏").append(100).append(true).append(10.5);
        System.out.println(stringBuffer);
        // 输出 java,张三丰赵敏100true10.5

        // 删
        /**
         * 删除索引为 >=start && < end 这区间内的字符
         */
        stringBuffer.delete(10,13);
        System.out.println(stringBuffer);
        // 输出 java,张三丰赵敏true10.5

        // 改 也就是替换 在这里是替换8 - 10区间内的内容
        stringBuffer.replace(8,10,"周芷若");
        System.out.println(stringBuffer);
        // 输出 java,张三丰周芷若true10.5

        // 查
        int indexOf = stringBuffer.indexOf("张三丰");
        System.out.println(indexOf);
        // 输出 5

        // 插 在索引下标为8的位置插入内容,原来该位置的内容后移
        stringBuffer.insert(8,"赵敏");
        System.out.println(stringBuffer);
        // 输出 java,张三丰赵敏周芷若true10.5

        // 长度 将字符串的总长度输出
        System.out.println(stringBuffer.length());
        
    }
}

小题
第一问

第一个sout会输出四,因为子啊append方法中会将null当做一个字符串数组输出,因此输出4
第二个sout输出null
第三个直接报空指针异常,因为创了一个新的对象,但是传入的str为null,它调用的是super.length() +16 ,这里为空,所以直接报空指针异常

第二问

package javaSEStudy.wrapper.StringBuffer_;

import java.util.Scanner;

public class Sell {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入商品名和价格");

        String name = scanner.nextLine();
        Double price = scanner.nextDouble();

        StringBuffer priceBuffer = new StringBuffer(String.format("%.2f", price));
        int index = priceBuffer.lastIndexOf(".");

        for (int i = index-3; i >0 ; i -= 3) {
            priceBuffer.insert(i,",");
        }
        System.out.println("商品名     商品价格");
        System.out.print(name+"\t\t");
        System.out.println(priceBuffer);
    }
}

StringBuilder类

基本介绍:

  • 一个可变的字符序列,此类提供一个与StringBuffer兼容的API接口但是不保证同步(StringBuilder线程不安全),此类被设计用作StringBuffer的一个简易替换,用在字符串缓冲区被单个线程使用的时候,如果可能,建议优先使用该类,因为在大多数实现中,他比StringBuffer快
  • 在StringBuilder上的主要操作是append方法和insert方法,可重载这些方法,以接受任意类型数据

StringBuilder继承了AbstractStringBuilder 类
它实现了 Serializable ,说明StringBuilder对象是可以串行化的
StringBuilder是final ,不可被继承
StringBuilder 对象字符序列仍然是存放在其父类AbstractStringBuilder的 char[] value 中,放在堆中
StringBuilder 的所有方法没有做互斥处理(Synchronized),多线程不安全,单线程使用

String StringBuffer StringBuilder 比较

  • StringBuilder和StringBuffer非常类似,均代表可变字符序列,而且方法也一样
  • String:不可变字符序列,效率低,但是复用率高
  • StringBuffer:可变字符序列,效率较高(增删),线程安全
  • StringBuilder:可变字符序列,效率最高,线程不安全

String使用说明:

String s  = "a"; //创建了个字符串
s += "b"; //实际上原来的"a"已经被丢弃,现在又产生了一个字符串s+"b"(也就是"ab"),如果多次执行改变这些串内容的操作,会导致大量副本字符串对象存留在内存中,降低效率,如果这些操作放到循环中会极大影响程序性能

//结论:如果我们需要对String做大量修改,不要使用String 

String StringBuffer Stringbuilder的选择
1.如果字符串存在大量修改操作,一般使用StringBuffer或StringBuilder
2.如果字符串存在大量修改操作,并且存在单线程,使用StringBuilder
3.如果字符串存在大量修改操作,并且存在多线程,使用StringBuilder
4.如果字符串很少修改,被多个对象引用,使用String,比如配置信息等

Math类

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

  • abs :绝对值
  • pow :求幂
  • ceil :向上取整
  • floor :向下取整
  • round :四舍五入
  • sqrt :求开方
  • random :求随机数
  • max :求两个数最大值
  • min :求两个数最小值

获取a~b之间的一个随机整数
公式: (int)(a + Math.random() * (b-a + 1))

package javaSEStudy.conection.math;

// Math类的方法
public class MathMethod {
    public static void main(String[] args) {
        // 基本都是静态方法,可以直接用
        // 1.abs 绝对值
        int abs = Math.abs(-10);
        System.out.println(abs); // 返回10

        // 2.pow 幂
        double pow = Math.pow(2 ,4); // 2的四次方
        System.out.println(pow); // 返回16.0

        // 3.ceil 向上取整,返回 >=该参数的最小整数(转成double类型)
        double ceil = Math.ceil(10.1); // ceil 上限,天花板
        System.out.println(ceil); // 返回11.0,如果参数是10,返回10.0

        // 4.floor 向下取整,返回 <=该参数的最大整数(转成double)
        double floor = Math.floor(-4.00001);  // floor 地板,地面
        System.out.println(floor); // 返回-5.0

        // 5.round 四舍五入
        long round = Math.round(-5.0001);
        System.out.println(round); // 返回-5

        // 6.sqrt 开方,负数没有平方根
        double sqrt = Math.sqrt(-9);
        System.out.println(sqrt); // 输出 NaN not a number

        // 7.random 随机数
        /**
         *  random 返回的是0~1之间的一个随机小数 [0,1)
         *  当我们想输出两个整数之间的数时
         *  公式: (int)(a + Math.random() * (b-a + 1))
         */
        System.out.println("标准随机数输出样式:");
        for (int i = 0; i < 10; i++) {
            System.out.print(Math.random()+" ");
        }
        // 那获取 a ~ b 之间的随机整数,a b 均为整数 [a ,b]
        // 可理解为 (int)(a) <= x <= (int)(a + Math.random() * (b-a + 1))
        // Math.random() * (b-a+1) ---> 返回的值为 0 <= x <= b-a+1

        // 带入具体值为: a =2,b=7
        // (int)(2 + Math.random() * (5 + 1))
        // 所以上述表达式为 2 <= x < 8
        // 前面可以取到2,但是后面因为小于8,可以取值7.16等,取整,最终输出 2 <= x <= 7
        System.out.println("\n取2~7间的随机整数");
        for (int i = 0; i < 10; i++) {
            System.out.print((int)(2 + Math.random()*(7-2+1))+" ");
        }
        System.out.println("\n");

        // 8.max min 返回两个数之间的最大值和最小值
        System.out.println(Math.max(1,9));
        System.out.println(Math.min(1,9));
    }
}

Arrays类

Arrays里面包含了一系列静态方法,用于管理或操作数组(比如排序和搜索)
常见方法一 :

  • toString :返回数组的字符串形式
  • sort :自然排序和定制排序
  • binarySearch 通过二分搜索法进行查找,要求必须排好序,返回值为索引
package javaSEStudy.conection.arrays_;

import java.util.Arrays;
import java.util.Comparator;

// Arrays类方法
public class ArraysMethod1 {
    public static void main(String[] args) {

        Integer[] array1 = {1, 2, 3, 4, 5};
        // 遍历数组
        for (Integer i : array1) {
            System.out.println(i);
        }
        // Arrays类提供toString方法直接显示数组信息
        System.out.println(Arrays.toString(array1));

        // sort 方法的使用
        Integer[] array2 = {1, -2, 3, 8, 10, -4, 5};
        // 1.可以使用冒泡排序 如下
        // 2.Arrays类中有sort排序方法
        //  注意: 因为数组是引用类型,所以通过sort排序后,会影响到实参arrays2
        Arrays.sort(array2);
        System.out.println(Arrays.toString(array2));
        // 3.sort 是重载的,也可以通过传入接口 Comparator(比较器) 实现定制排序
        //   调用定制排序时,传入两个参数,第一个参数为排序数组,第二个为实现了Comparator接口的匿名内部类
        //   该匿名内部类要求实现 compare 方法
        Arrays.sort(array2,new Comparator<Integer>(){

            @Override
            public int compare(Integer o1, Integer o2) {
                // 这里 o2 - o1 就可以实现从大到小排序
                return o2 - o1;
            }
        });
        System.out.println(Arrays.toString(array2));
        // 这里体现了接口编程+动态绑定+匿名内部类综合使用的好处


        // 4.binarySearch 通过二分搜索法(二叉查找)进行查找,要求必须排好序,查找数组中有没有某个数
        //   注意,该方法返回值的值是索引,索引从0开始
        //   如果数组中不存在该元素则返回 -(low + 1),意思是如果找不到该数,就返回这个数应该在的位置,但是为负,因为该数不存在
        //   +1是因为让索引从1开始,符合人们排序习惯
        System.out.println(Arrays.binarySearch(array1,1));
    }

    // 冒泡排序
    public static Integer[] sort1(Integer[] array2) {
        int temp = 0;
        boolean flag = false;

        for (int i = 0; i < array2.length-1;  i++) {
            for (int j = 0; j < array2.length-1-i;  j++) {
                if (array2[j+1] < array2[j]) {
                    temp = array2[j];
                    array2[j] = array2[j+1];
                    array2[j+1] = temp;
                    flag = true;
                }
            }
            if (!flag) {
                break;
            }
        }
        return array2;
    }
}


这是定制排序的位置,调用了Comparator接口,然后重写compare方法,这里的return返回值为正还是为负,直接影响到binarySearch方法中的while,也就是下图中的方法


为正数走if方法,为负数走else方法,就实现将其倒置,while也是调用了compare方法,所以这里的compare的正负值直接影响到排序

如果理解不深刻,我们自己写一个排序方法

package javaSEStudy.conection.arrays_;

import java.util.Arrays;
import java.util.Comparator;

// 自己写一个定制的数组排序
public class ArraysSortCustom {
    public static void main(String[] args) {
        int[] array = {1, 21, 13, 45, 35, 0, 17};

//        bubble_sort1(array);
        bubble_sort2(array, new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                Integer i1 = (Integer)o1;
                Integer i2 = (Integer)o2;
                // 这里返回的值的正负决定下面是否大于0
                return i2 - i1;
            }
        });

        System.out.println(Arrays.toString(array));
    }

    // 使用冒泡进行排序
    public static void bubble_sort1(int[] array) {
        int temp = 0;
        for (int i = 0; i < array.length - 1; i++) {
            for (int j = 0; j < array.length - 1 - i; j++) {
                // 从小到大
                if (array[j] > array[j + 1]) {
                    temp = array[j];
                    array[j] = array[j + 1];
                    array[j + 1] = temp;
                }
            }
        }
    }

    // 结合冒泡排序 + 定制
    public static void bubble_sort2(int[] array, Comparator c) {
        int temp = 0;
        for (int i = 0; i < array.length - 1; i++) {
            for (int j = 0; j < array.length - 1 - i; j++) {
                // 数组排序由 c.compare(array[j], array[j + 1]) 返回的值决定
                if (c.compare(array[j], array[j + 1]) > 0) {
                    temp = array[j];
                    array[j] = array[j + 1];
                    array[j + 1] = temp;
                }
            }
        }
    }
}

常见方法二 :

  • copyOf :数组元素复制
  • fill :数组元素填充
  • equals :比较两个数组元素内容是否完全一致
  • asList :将一组值转换为list
package javaSEStudy.conection.arrays_;

import java.util.Arrays;
import java.util.List;

// Arrays类方法
public class ArraysMethod2 {
    public static void main(String[] args) {
        Integer[] array1 = {1, 21, 13, 45, 35, 0, 17};

        /**
         *  copyOf 数组元素复制
         *      表示从array数组中拷贝 array1.length 个数据到newArray中
         *      如果拷贝长度大于原始数组,就在copy的数组后加null
         *      如果拷贝0个,就是一个空数组
         *      如果拷贝长度 < 0 ,就会抛出异常 NegativeArraySizeException 负数组大小异常
         *      该方法的底层调用的是 System.arraycopy
         */
        Integer[] newArray1 = Arrays.copyOf(array1, array1.length);
        Integer[] newArray2 = Arrays.copyOf(array1, array1.length + 1);
        Integer[] newArray3 = Arrays.copyOf(array1, array1.length - 1);
        Integer[] newArray4 = Arrays.copyOf(array1, 3);
        System.out.println("数组复制 "+Arrays.toString(newArray4));

        /**
         * fill 数组填充
         *  使用99去将num中所有的元素替换为99
         */
        Integer[] num = new Integer[]{1, 2, 5, 0, 8, 30, 23};
        Arrays.fill(num, 99);
        System.out.println("数组填充 "+Arrays.toString(num));

        /**
         * equ 比较两个数组中的元素是否相等
         *  如果两个数组的元素是一样的,则返回true
         *  注意:如果只是数组顺序不一样,但是内容一样,会返回false
         */
        Integer[] array2 = {21, 1, 13, 45, 35, 0, 17};
        boolean equ = Arrays.equals(array1, array2);
        System.out.println("数组比较 "+ equ);

        /**
         * asList 将一组值转换为list
         *  会将array1数据转成一个List集合
         *  返回的 list1 编译类型 List(接口)
         *  list1 的运行类型 java.util.Arrays$ArrayList(在Arrays类里的 ArrayList静态内部类)
         */
        List<Integer> list1 = Arrays.asList(array1);
        System.out.println("数组转换为list "+list1);
        System.out.println("运行类型 "+list1.getClass());

    }
}

练习题

package javaSEStudy.conection.arrays_.test;

import java.util.Arrays;
import java.util.Comparator;

// 练习 自定义Book类
class Application {
    public static void main(String[] args) {
        Book[] books = new Book[4];
        books[0] = new Book("红楼梦", 100);
        books[1] = new Book("新金平梅", 90);
        books[2] = new Book("新青年文选", 5);
        books[3] = new Book("java从入门到放弃", 300);

        /**
         * 按照价格从小到大
         */
//        Arrays.sort(books, new Comparator<Book>() {
//
//            @Override
//            public int compare(Book b1, Book b2) {
//                return Double.compare(b1.getPrice(), b2.getPrice()); // 升序
//                return Double.compare(b2.getPrice(), b1.getPrice()); // 降序
//            }
//        });

        /**
         * 按照价格从大到小
         */
//        Arrays.sort(books,new Comparator<Book>() {
//
//            @Override
//            public int compare(Book b1, Book b2) {
//                double d1 = b1.getPrice();
//                double d2 = b2.getPrice();
//                if (d1 > d2){
//                    return -1;
//                }else if (d1 < d2){
//                    return 1;
//                }else {
//                    return 0;
//                }
//            }
//        });

        /**
         * 书名长度从大到小
         */
        Arrays.sort(books, new Comparator<Book>() {

            @Override
            public int compare(Book b1, Book b2) {
//                String s1 = b1.getName();
//                String s2 = b2.getName();
//                return s2.length() - s1.length();
                return Integer.compare(b2.getName().length(), b1.getName().length());
            }
        });

        for (Book book : books) {
            System.out.println(book);
        }
    }
}

public class Book {
    private String name;
    private double price;

    public Book(String name, double price) {
        this.name = name;
        this.price = price;
    }

    public String getName() {
        return name;
    }

    public double getPrice() {
        return price;
    }

    @Override
    public String toString() {
        return "name='" + name + '\'' + ", price=" + price;
    }
}

System类

常见方法:

  • exit :退出当前程序
  • arraycopy :复制数组元素,比较适合底层调用,一般使用Arrays.copyOf完成复制数组
    System.arraycopy(src,0,dest,0,3);
  • currentTimeMillens :返回当前时间距离1970-1-1 的毫秒数
  • gc :运行垃圾回收机制
    System.gc();
package javaSEStudy.common.system_;

import java.util.Arrays;

// System类方法
public class SystemMethod {
    public static void main(String[] args) {

        // exit 退出当前程序
        System.out.println("111111111");
        // 0 代表正常退出
//        System.exit(0);
        System.out.println("222222222");

        // arraycopy :复制数组元素,比较适合底层调用
        int[] src = {1,2,3};
        // dest 当前状态为{0,0,0}
        int[] dest = new int[3];
        /**
         * src      – 源数组
         * srcPos   – 从源数组的哪个索引开始拷贝
         * dest     – 目标数组
         * destPos  – 把源数组的数据拷贝到目标数组的哪个索引
         * length   – 从源数组拷贝多少个元素到目标数组
         */
        // 倘若length长度大于源数组,则会抛出数组下标越界异常
        System.arraycopy(src, 0, dest, 0, src.length-1);
        System.out.println(Arrays.toString(dest));

        // currentTimeMillens :返回当前时间距离1970-1-1 的毫秒数
        System.out.println(System.currentTimeMillis());

        // gc :运行垃圾回收机制
        System.gc();

    }
}

gc垃圾回收机制详解

垃圾回收(Garbage Collection, GC)是现代编程语言内存管理的核心机制,它自动回收不再使用的内存空间,减轻了开发者的负担。

基本定义:GC是自动内存管理系统,负责在程序运行时识别并回收不再被引用的对象所占用的内存空间。

核心功能:

  • 分配内存给新对象
  • 识别哪些内存块仍在被使用
  • 回收不再被使用的内存块

关键优势:

  • 防止内存泄漏
  • 避免悬垂指针问题
  • 减少手动内存管理错误
  • 提高开发效率

复制算法(Copying)
原理:将内存分为两个半区,只使用其中一个,GC时将存活对象复制到另一半区
特点:

  • 适合存活对象少的场景
  • 无内存碎片
  • 空间利用率仅50%

分代收集算法(Generational)
理论基础:弱分代假说(大多数对象很快死亡)和强分代假说(老对象很少引用新对象)
分代划分:

  • 新生代(Young Generation):新创建对象
  • 老年代(Old Generation):长期存活对象
  • 永久代(Perm Generation,Java8前)/元空间(Metaspace,Java8+)

收集策略:

  • 新生代:通常使用复制算法
  • 老年代:通常使用标记-清除或标记-整理

复制算法与分代收集算法详解

一、复制算法(Copying Algorithm)

  1. 基本原理
  • 内存划分:将可用内存分为大小相等的两个半区(From空间和To空间)
  • 运行机制:
    1. 新对象始终分配在From空间
    2. 当From空间满时,暂停应用线程(STW)
    3. 将From空间的存活对象复制到To空间
    4. 清空整个From空间
    5. 交换From和To空间的角色
  1. 核心特点
  • 空间效率:始终只有一半内存可用(空间利用率50%)
  • 时间效率:仅需遍历存活对象,时间复杂度O(存活对象数)
  • 无碎片化:对象在To空间连续排列
  • 适用场景:对象存活率低的区域(如新生代)
  1. 优化变种
  • Appel式回收:将新生代划分为Eden+2个Survivor区(8:1:1比例)
  • 多空间复制:某些JVM实现采用3个空间轮转

二、分代收集算法(Generational Collection)

  1. 理论基础
  • 弱分代假说:绝大多数对象"朝生夕死"(IBM研究显示Java应用中98%对象在Young GC时死亡)
  • 强分代假说:老年代对象很少引用新生代对象(跨代引用<1%)
  1. 代际划分
代名称 占比 对象特征 回收频率 算法选择
新生代 1/3堆 新创建对象,存活时间短 复制算法
老年代 2/3堆 经历多次GC仍存活的对象 标记-清除/整理
永久代 固定 类元数据、常量等(JVM内部使用) 极低 特定回收策略
  1. 跨代引用处理
  • 记忆集(Remembered Set):记录老年代到新生代的引用
  • 卡表(Card Table):将堆划分为512字节的卡,脏卡标记跨代引用
  • 写屏障(Write Barrier):在对象引用修改时维护记忆集
  1. 典型工作流程

  2. 新生代回收(Minor GC):

    • 触发条件:Eden区满
    • 过程:存活对象从Eden+From Survivor复制到To Survivor
    • 年龄计数:对象每存活一次GC年龄+1,达到阈值(默认15)晋升老年代
  3. 老年代回收(Full GC):

    • 触发条件:老年代空间不足或元空间不足
    • 过程:通常采用并发标记-清除算法

三、算法对比与选型建议

比较维度 复制算法 分代收集算法
内存开销 50%利用率 整体约85%利用率
执行效率 O(存活对象) 新生代O(存活),老代O(全部)
停顿时间 较短 新生代短,老年代可能较长
适用场景 对象存活率<10% 符合分代假设的所有场景
碎片问题 老年代可能有碎片

选型建议:

  • 内存敏感场景慎用纯复制算法
  • 面向响应式系统优先选择分代+G1/ZGC
  • 物联网设备考虑分代+Serial收集器

总结
复制算法分了两个区,一个from区,一个to区,新的对象总是在from区,当from区满了,就暂停应用线程,将from区对象复制到to区,再清空from区,此时to区变成from区,from区变成to区
注意:实际应用中,约有98%对象在第一次GC死亡,可能只有2%对象需要复制到to区

// 典型内存布局
YoungGen = Eden + Survivor0 + Survivor1  // 比例通常8:1:1

这行代码展示了JVM虚拟空间比例,只有10%空间(Survivor)用于复制,90%(Eden)用于分配

空间溢出

空间溢出(Space Overflow)是指程序在运行时因内存分配不当或资源耗尽,导致无法继续执行正常逻辑的情况。常见于堆内存溢出(Heap Overflow)、栈溢出(Stack Overflow)、内存泄漏(Memory Leak)等场景。以下从定义、成因、影响、检测与修复四个维度详细解析。


  1. 空间溢出的类型与定义

(1) 堆内存溢出(Heap Overflow)

  • 定义:程序在堆(Heap)上申请的内存超过JVM/系统限制(如-Xmx设定的最大堆大小)。
  • 典型错误:
    • Java: java.lang.OutOfMemoryError: Java heap space
    • C/C++: 程序崩溃或返回NULL指针

(2) 栈溢出(Stack Overflow)

  • 定义:线程的调用栈深度超过限制(如递归无终止条件)。
  • 典型错误:
    • Java: java.lang.StackOverflowError
    • C/C++: 段错误(Segmentation Fault)

(3) 内存泄漏(Memory Leak)

  • 定义:程序持续分配内存但未释放,最终耗尽可用内存。
  • 典型表现:
    • 长时间运行后内存占用持续上升
    • 最终触发OutOfMemoryError

  1. 空间溢出的主要成因

(1) 堆内存溢出的常见原因

  • 大对象分配(如加载超大文件到内存)
  • 缓存未清理(如无限制的HashMap缓存)
  • 集合类无限增长(如未限制的ArrayListLinkedList

(2) 栈溢出的常见原因

  • 无限递归(如未设置终止条件的递归函数)
  • 过深的函数调用链(如复杂嵌套调用)
  • 线程栈空间过小(如JVM参数-Xss设置不合理)

(3) 内存泄漏的常见原因

  • 静态集合持有对象(如static Map缓存未清理)
  • 未关闭资源(如数据库连接、文件流未close()
  • 监听器未注销(如事件监听器未移除)

  1. 空间溢出的影响
类型 影响范围 典型后果
堆溢出 整个JVM/进程崩溃 服务不可用,需重启
栈溢出 当前线程终止 部分功能失效,可能影响整体稳定性
内存泄漏 渐进式内存耗尽 长时间运行后系统变慢或崩溃

  1. 检测与修复方法

(1) 检测工具

  • 堆Dump分析(MAT、VisualVM、JProfiler)
  • 栈跟踪(jstack、GDB调试)
  • 内存监控(Prometheus + Grafana监控堆/栈使用率)

(2) 修复策略

  • 堆溢出:
    • 优化大对象存储(如流式处理替代全量加载)
    • 调整JVM参数(-Xmx-Xms
  • 栈溢出:
    • 限制递归深度(改用迭代)
    • 增加栈空间(-Xss2m
  • 内存泄漏:
    • 使用弱引用(WeakHashMap
    • 确保资源释放(try-with-resources

BigInteger类和BigDecimal类

应用场景:

  • BigInteger :适合保存比较大的整型
  • BigDecimal :适合保存精度更高的浮点型(小数)

BigIntegerl类方法

  • add :加法
  • subtract :减法
  • multiply :乘法
  • divide :除法
package javaSEStudy.common.bignum;

import java.math.BigInteger;

public class BigInteger_ {
    public static void main(String[] args) {
        // 当我们实际应用中数据过大long无法保存,如下
//        long l = 1222222222222222244851;

        /**
         * BigInteger 可以保存超大数据,但是是以字符串形式保存,所以需要加上双引号
         * 处理逻辑:以字符串形式保存,然后计算或输出在转换成数字
         * 需要进行加减乘除,需要使用对应方法,不能直接 + - * /
         */
        BigInteger bigInteger1 = new BigInteger("299999999999999999999999999");
        BigInteger bigInteger2 = new BigInteger("299991554981654891515699999");
        System.out.println(bigInteger1);

        // 如下写法报错
//        System.out.println(bigInteger+1);

        // 加法
        System.out.println("加法 "+bigInteger1.add(bigInteger2));
        // 减法
        System.out.println("减法 "+bigInteger1.subtract(bigInteger2));
        // 乘法
        System.out.println("乘法 "+bigInteger1.multiply(bigInteger2));
        // 除法
        System.out.println("除法 "+bigInteger1.divide(bigInteger2));

    }
}

BigDecimal类方法

  • add :加法
  • subtract :减法
  • multiply :乘法
  • divide :除法
package javaSEStudy.common.bignum;

import java.math.BigDecimal;
import java.math.RoundingMode;

// BigDecimal 类用法
public class BigDecimal_ {
    public static void main(String[] args) {
        // 下面d输出的时候会输出1952.9521991111112
        // 无法达到想要的精度
        double d = 1952.952199111111111111111111111d;
        System.out.println(d);

        /**
         * 使用BigDecimal 存储高精度小数
         * 对BigDecimal进行加减乘除 需要使用特定的方法,也不能直接运算
         * 创建一个需要操作的BigDecimal,在调用相应方法
         */
        BigDecimal bigDecimal1 = new BigDecimal("1952.952199111111111111111111111");
        BigDecimal bigDecimal2 = new BigDecimal("1.2");
        System.out.println(bigDecimal1);

        // 加法
        System.out.println("加法 "+bigDecimal1.add(bigDecimal2));
        // 减法
        System.out.println("减法 "+bigDecimal1.subtract(bigDecimal2));
        // 乘法
        System.out.println("乘法 "+bigDecimal1.multiply(bigDecimal2));
        // 除法
        // 注意 如果该数除不尽,会造成死循环,抛出该异常:ArithmeticException: Non-terminating decimal expansion;
        // 解决:指定精度 BigDecimal.ROUND_CEILING ---> 如果有无限小数,就保留和分子一样的精度
        System.out.println("除法 "+bigDecimal1.divide(bigDecimal2,BigDecimal.ROUND_CEILING));
        // 注意: 上述解决方法在jdk9+已被废弃,新版本写法:BigDecimal.RoundingMode.CEILING
        System.out.println("除法 "+bigDecimal1.divide(bigDecimal2, RoundingMode.CEILING));
    }
}

日期类 (知道怎么查,怎么用即可)

第一代日期类

  • Date :精确到毫秒,代表特定瞬间
  • SimpleDateFormat :格式和解析日期的类
    它允许进行格式化(日期-->文本),解析(文本-->日期)和规范化

日期字母一览表

package javaSEStudy.common.date_;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.SimpleTimeZone;

// 第一代日期类 详解
public class Date_ {
    public static void main(String[] args) throws ParseException {
        // 获取当前系统时间
        // 这里的包是java.util.Date,不是java.sql.Date
        // 默认输出格式为国外方式,通常需要进行格式转换
        Date d1 = new Date();
//        System.out.println("当前系统时间"+d1);

        // 格式转换
        // 创建 SimpleDateFormat 对象,可以指定相应格式,E是星期
        // 这里使用的字母是规定好的,不可以乱写
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 hh:mm:ss E");
        String format1 = sdf.format(d1);
        System.out.println("当前系统时间: "+format1);

        // 通过指定毫秒数获取指定时间
        // 这里显示1970年的时间
        Date d2 = new Date(962512655);
        String format2 = new SimpleDateFormat("yyyy年MM月dd日 hh:mm:ss E").format(d2);
        System.out.println("指定获取时间: "+format2);

        // 可以把一个格式化的String 转成对应的 Date
        // 得到的Date仍然在输出时是国外的形式,如果希望得到指定格式仍然需要进行格式转换
        // 得到的String--> Date,使用sdf格式需要和给的String格式一样,否则会抛出转换异常
        String s = "2025年5月12日 17:57:58 周一";
        Date parse = sdf.parse(s);
        System.out.println("parse: "+sdf.format(parse));

    }
}

第二代日期类

第二代日期类,主要就是Calendar类(日历)

Calendae类主要是一个抽象类,他为特定瞬间与一组诸如YEAR,MONTH,DAY_OF_MONTH,HOUR 等日历字段之间转换提供了一些方法,并为操作日历字段(例如获得下星期的日期)提供了一些方法

package javaSEStudy.common.date_;

import java.util.Calendar;

// 第二代日期类Calendar详解
// Calendar:日历类
public class Calendar_ {
    public static void main(String[] args) {
        /**
         * Calendar是一个抽象类,并且构造器也是私有的
         * 可以通过getInstance()获取实例
         * 提供了大量的方法和字段供程序员使用
         * Calendar没有提供格式化方法使用
         */
        // 创建一个日历类对象
        Calendar cal = Calendar.getInstance();
//        System.out.println(cal);
        // 年
        System.out.println(cal.get(Calendar.YEAR));
        // 月 这里威慑+1? 因为Calendar返回月份的时候从0开始编号
        System.out.println(cal.get(Calendar.MONTH)+1);
        // 日(一个月中的第几号)
        System.out.println(cal.get(Calendar.DAY_OF_MONTH));
        // 小时
        System.out.println(cal.get(Calendar.HOUR));
        // 分钟
        System.out.println(cal.get(Calendar.MINUTE));
        // 秒
        System.out.println(cal.get(Calendar.SECOND));
        // Calendar没有专门的格式化方法,所以需要程序员自己组合
        System.out.println(cal.get(Calendar.YEAR)+"年"
                +(cal.get(Calendar.MONTH)+1)+"月"
                +cal.get(Calendar.DAY_OF_MONTH)+"日");
    }
}

第三代日期类

前面两代不足分析:
jdk1.0中包含了一个java.util.Date类,但是他的大多数方法已经在JDK1.1引入Calendar类后被弃用,而Calendar也存在问题

  • 可变性 :像日期和时间这样的类应该是不可变的
  • 偏移性 :Date中的年份是从1900开始的,而月份都是从0开始的
  • 格式化 :格式化只对Date有用,Calendar 则不行
  • 此外 :他们也不是线程安全的,不能处理闰秒等(每隔2天多1秒)

第三代日期常见方法(JDK8加入):

  • LocalDate(日期) :只包含日期,可以获取日期字段
  • LocalTime(时间) :只包含时间,可以获取时间字段
  • LocalDateTime(日期时间) :包含日期+时间,可以获取日期和时间字段
  • DateTimeFormatter :格式日期类
  • Instant :时间戳,类似于Dae,提供一系列和Date转换的方式
    Instant-->Date
    Date date = Date.from(instant)
    Date-->Instant
    Instant instant = date .toInstant()
  • MonthDay类 :检查重复事件
  • 增加日期的某个部分
  • 使用plus方法测试增加时间的某个部分
  • 使用minus方法测试查看一年前和一年后的日期
  • 还有很多时间,使用的时候查看api
package javaSEStudy.common.date_;

import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;

// 第三代日期类使用
public class LocalDate_ {
    public static void main(String[] args) {
        /**
         * 第三代日期
         * 使用now(),返回当前日期时间的 对象
         */
        // LocalDate.now()  LocalTime.now()
        LocalDateTime ldt = LocalDateTime.now();
        System.out.println(ldt);

        // 获取年份
        System.out.println(ldt.getYear());
        // 获取月份 这里是英文月份
        System.out.println(ldt.getMonth());
        // 获取月份,数字表示
        System.out.println(ldt.getMonthValue());
        // 获取日
        System.out.println(ldt.getDayOfMonth());
        // 获取小时
        System.out.println(ldt.getHour());
        // 获取分钟
        System.out.println(ldt.getMinute());
        // 获取秒
        System.out.println(ldt.getSecond());

        /**
         * 使用DateTimeFormatter对象来进行格式化
         * 首先创建一个DateTimeFormatter对象
         */
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        System.out.println(ldt.format(formatter));

        /**
         * Instant 时间戳
         * 通过静态方法now(),获取表示当前时间戳的对象
         * 通过from方法可以把Instance转成Date
         */
        Instant now = Instant.now();
        System.out.println(now);
        // from方法: Instant 转 Date
        Date date = Date.from(now);
        //toInstance方法: Date 转 Instant
        Instant instant = date.toInstant();

        /**
         * 提供plus 和 minus 方法可以对当前时间加减
         * 还有很多方法,使用的使用查看api
         */
        // 查看增加了980天后的时间
        LocalDateTime localDateTime1 = ldt.plusDays(980);
        System.out.println(formatter.format(localDateTime1));

        // 查看2456分钟前的时间
        LocalDateTime localDateTime2 = ldt.minusMinutes(2456);
        System.out.println(formatter.format(localDateTime2));

    }
}

常用类测试

第一问

package javaSEStudy.common.test;

// 将字符串指定部分翻转
public class Test01 {
    public static void main(String[] args) {
        String s = "abcdef";
        try {
            s = reverse(s, 1, 4);
        } catch (Exception e) {
            System.out.println(e.getMessage());
            return;
        }
        System.out.println(s);

    }

    /**
     * 思路:
     * 使用StringBuffer的reverse()翻转数组
     * 在将其拼接输出即可
     */
//    public static String reverse(String str, int start, int end) {
//        String str1 = str.substring(start, end+1);
//        StringBuffer reverse1 = new StringBuffer(str1).reverse();
//        return str.substring(0, start) + reverse1 + str.substring(end+1);
//    }

    /**
     * 老韩做法思路:
     * String 是final类,需要转换为char[]才可以交换元素位置
     * 两边各有数组元素开始交换,当左边交换的位置开始与右边重合,或者左边交换元素的位置大于右边
     * 就代表此时元素已经完全交换完毕,再交换位置会导致将交换好的元素再换回去
     */
    public static String reverse(String str, int start, int end) {
        // 对输入参数做验证
        // 重要编程思想 :
        // 写出正确情况 然后取反
        if(!(str != null && end > start && end < str.length())) {
            throw new RuntimeException("参数错误");
        }

        char temp = ' ';
        char[] str1 = str.toCharArray();
        for (int i = start, j = end; i < j; i++, j--) {
            temp = str1[i];
            str1[i] = str1[j];
            str1[j] = temp;
        }
        // 使用str1 重新构建一个String返回
        return new String(str1);
    }
}

第二问

自己写的版本

package javaSEStudy.common.test;

import java.util.Scanner;
import java.util.regex.Pattern;

// 注册用户 自己写的版本
public class Test02_PLUS {
    public static void main(String[] args) {

        /**
         * 思路:
         *      先创建data存储传入的数据
         *      整行传入想输入的数据,正则表达式空格分割
         *      输入数据分割为三个数组,分别取出保存然后传入data
         *      最后增加异常抛出和对输入内容进行验证
         */
        System.out.println("------用户注册------");
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入用户名(2-4位) 密码(仅支持数字) 邮箱,空格隔开");
        String in = scanner.nextLine();
        String[] input = in.split("\\s");

        try {
            if (input.length != 3) {
                throw new RuntimeException("输入错误");
            }
            String name = input[0];
            String passWord = input[1];
            String email = input[2];

            nameMethod(name);
            passWordMethod(passWord);
            emailMethod(email);

            Data user = new Data(name, passWord, email);
            System.out.println("注册成功!");
            System.out.println(user);

        } catch (NumberFormatException e) {
            System.out.println("注册错误" + e.getMessage());
        } finally {
            scanner.close();
        }
    }

    // 验证方法
    // 用户名验证
    public static void nameMethod(String name) {
        // 用户名2-4位不为空取反则为错误条件
        if (!(name != null && name.length() > 1 && name.length() < 5)) {
            throw new RuntimeException("输入用户名错误");
        }
    }

    // 密码验证
    public static void passWordMethod(String passWord) {
        // 正则表达式匹配只能为6位数字
        if (!passWord.matches("\\d{6}")) {
            throw new RuntimeException("密码必须为6位数字");
        }
    }

    // 邮箱验证
    public static void emailMethod(String email) {
        // 偷个懒,直接找的网络上邮箱匹配的正则表达式
        if (!Pattern.matches("^\\w+@[a-z0-9]+\\.[a-z]{2,3}$", email)) {
            throw new IllegalArgumentException("邮箱输入错误");
        }
    }

}

class Data {
    private String name;
    private String passWord;
    private String email;

    public Data(String name, String passWord, String email) {
        this.name = name;
        this.passWord = passWord;
        this.email = email;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPassWord() {
        return passWord;
    }

    public void setPassWord(String passWord) {
        this.passWord = passWord;
    }

    @Override
    public String toString() {
        return "用户名: '" + name + '\'' +
                ", 密码: " + passWord +
                ", 邮箱: '" + email + '\'';
    }
}

老韩版本

package javaSEStudy.common.test;

// 老韩版本
public class Test02 {
    public static void main(String[] args) {
        String name = "jack";
        String pwd = "123456";
        String email = "jack@gmail.com";

        try {
            userRegister(name, pwd, email);
            System.out.println("恭喜你!注册成功~");
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }

    }

    /**
     * 编写方法userRegister
     * 针对输入的内容进行校验,如果有问题就抛出异常
     * 单独写个方法判断密码是否全是数字
     */
    public static void userRegister(String name, String pwd, String email) {

        if(name == null && pwd == null && email == null) {
            throw new RuntimeException("输入错误");
        }

        // 首先验证用户名
        int userLength = name.length();
        if (!(userLength >= 2 && userLength <= 4)) {
            throw new RuntimeException("用户名长度2~4位");
        }
        // 然后验证密码
        if (!(pwd.length() == 6 && isDigital(pwd))) {
            throw new RuntimeException("密码为6位纯数字");
        }
        // 最后验证邮箱
        int i = email.indexOf('@');
        int j = email.indexOf('.');
        if (!(i > 0 && j > i)) {
            throw new RuntimeException("邮箱格式错误");
        }
    }

    // 验证全是数字方法
    public static Boolean isDigital(String str) {
        char[] chars = str.toCharArray();
        for (int i = 0; i < chars.length; i++) {
            // 在ASCII码中 只有0~9才是数字,超过这个范围则不为数字
            if (chars[i] < '0' && chars[i] > '9') {
                return false;
            }
        }
        return true;
    }
}

第三问

package javaSEStudy.common.test;

// 以英文形式输出人名
public class Test03 {
    public static void main(String[] args) {
        String name = "William Jefferson Clinton";
        System.out.println(conversion(name));
//        printName(name);
    }

    // 写一个将英文名转换的方法
    public static String conversion(String str){

        if(str == null){
            System.out.println("数据不能为空");
            return str;
        }

        String[] strs = str.split(" ");
        if(strs.length != 3){
            System.out.println("输入格式错误");
            return str;
        }

        String surname = strs[0];
        String name1 = strs[1];
        String name2 = strs[2];
        name1 = name1.substring(0, 1).toUpperCase() ;

        // 使用StringBuffer对字符串进行拼接
        StringBuffer result = new StringBuffer();
        result.append(name2).append(",").append(surname).append(".").append(name1);

        return result.toString();
    }

    /**
     * 老韩方法
     * 对字符串进行分割 split(" ")
     * 对分割出来的字符串进行格式化 foemat
     */
    public static void printName(String str){
        if(str == null){
            System.out.println("输入数据不能为空");
            return;
        }

        String[] names = str.split(" ");
        if(names.length != 3){
            System.out.println("输入名字格式不正确");
            return;
        }

        String format = String.format("%s,%s.%c", names[2], names[0], names[1].toUpperCase().charAt(0));
        System.out.println(format);
    }
}

第四问

package javaSEStudy.common.test;

// 判断字符串里有多少数字,大写字母,小写字母
public class Test04 {
    public static void main(String[] args) {
        String str1 = "T5hV9nP2qR7yK4wX8bS1";

        judgmentString(str1);
    }

    /**
     * @param str;
     * 思路:
     *     先创建三种字符计数的对象
     *     然后使用for循环对其遍历获取所有值
     *     使用charAt获取该位置字符
     *     分别进行匹配是哪种并输出
     */
    public static void judgmentString(String str) {
        int digital = 0;
        int uppercase = 0;
        int lowercase = 0;

        if(str == null) {
            System.out.println("字符串不能为空");
            return;
        }

        for (int i = 0; i < str.length(); i++) {
            char c = str.charAt(i);
            if (c >= 'A' && c <= 'Z') {
                uppercase++;
            } else if (c >= 'a' && c <= 'z') {
                lowercase++;
            }else if (c >= '0' && c <= '9') {
                digital++;
            }else {
                break;
            }
        }

        System.out.println("字符串长度:"+str.length());
        System.out.println("大写字母:" + uppercase);
        System.out.println("小写字母:" + lowercase);
        System.out.println("数字:" + digital);
    }
}

第五问

答案 : f f t f f t
解析 :
  有个动物类,里面有个构造器包含那么属性,s1在栈中,直接在常量池创建"hspedu",然后new了两个a和b对象 a和b 是new的两个对象,在堆中分别是两个空间,在指向常量池中的相同常量,1为f,第二个由于方法没有重写,本质也是计算a==b,所以f,第三个通过.name,指向的是常量池中同一个地址,f.后面简单不多解释

posted @ 2025-05-14 17:29  sprint077  阅读(19)  评论(0)    收藏  举报