java字符串以及常见API

String字符串

String是一个类,属于数据类型中的引用类型。

Java中一切使用""引起来的内容,都是这个类的实例,称为字符串对象。

字符串在定义后,值不可改变,是一个常量,实际是一个字符数组

//这句话执行时,创建一个"Tom"字符串对象,将其地址保存在变量name中
String name = "Tom";
//这句话执行看似在改变字符串的值,实际是创建了一个新的"Jerry"字符串对象,将其地址保存到变量name中
name = "Jerry";
//以上两句,在内存中,会有两个字符串对象"Tom"和"Jerry",没有任何字符串发生了改变,只是name引用了不同的字符串地址


//字符串可以当做数组使用
String str1 = "hello";

//字符串对象实际是一个字符数组对象"包装"而来
char[] list = {'h', 'e', 'l', 'l', 'o'};
String str2=new String(list);

System.out.println(str1);
System.out.println(str2);

String类使用时注意

右上方案例可见,如果频繁地将一个String类型变量的值进行更改时,会创建很多字符串对象。效率低,浪费内存空间。

所以在频繁更改字符串时,不要使用String类变量。

System.out.println("开始执行");
String str = "";
//5万次的循环,就会创建5万个字符串对象,但最终只会有一个字符串对象被str引用
for (int i = 0; i < 50000; i++) {
    str += i;
}
System.out.println("执行结束");

如果要频繁更改字符串,使用StringBuilder类或StringBuffer类

如何创建字符串对象

1.使用""赋值创建

String str="abc";

2.通过构造方法创建

常用构造方法说明
String()创建一个空白字符串对象。
String(String str)创建一个指定字符串的字符串对象。
String(char[] list)创建一个指定字符数组的字符串对象。
String(byte[] list,String charsetName)按指定的编码格式创建一个指定字节数组的字符串对象。

不同方式创建字符串的过程

使用""赋值的形式创建

//这句话执行时,先判断字符串常量池(缓冲区)中是否存在"ab",不存在则创建,将其地址保存到str1变量中
String str1 = "ab";
//这句话执行时,先判断字符串常量池(缓冲区)中是否存在"ab",已存在,不用创建,将其地址保存到str2变量中
String str2 = "ab";
//这句话执行时,+两端如果都是""定义的字符串,拼接后再判断字符串常量池(缓冲区)中是否存在
//拼接后的"ab"依然存在,将其地址保存到str3变量中
String str3 = "a" + "b";

//以上三句话,只会在内存中的字符串常量池(缓冲区)创建一个字符串对象"ab",分别引用给3个变量
System.out.println(str1==str2);//true
System.out.println(str1==str3);//true

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3R5BuMsG-1670158334420)(F:\221001\笔记\JavaAdv01.assets\image-20221123100225789.png)]

可以使用Jdk中自带的反编译工具javap对class文件进行反编译

在class文件所在目录下(项目的Out目录中),进入控制台,输入javap -c 字节码文件名.class

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DihiHrTw-1670158334420)(F:\221001\笔记\JavaAdv01.assets\image-20221123100558302.png)]

使用构造方法String(String str)创建

//这句话执行时的流程
//1.在字符串常量池中寻找"ab",不存在,创建
//2.在堆中new String(),将字符串常量池中的"ab"保存到new出来的区域
//3.将堆中new出来的地址保存到栈中变量str1中
String str1 = new String("ab");

//这句话执行时的流程
//1.在字符串常量池中寻找"ab",存在,直接引用
//2.在堆中new String(),将字符串常量池中的"ab"保存到new出来的区域
//3.将堆中new出来的地址保存到栈中变量str2中
String str2 = new String("ab");

//由于str1和str2是堆中的两个区域,所以结果为false
System.out.println(str1 == str2);//false

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9U6OXWYP-1670158334421)(F:\221001\笔记\JavaAdv01.assets\image-20221123102449165.png)]

使用+拼接""和new出来的字符串对象创建

//在字符串常量池中创建"ab"
String str1 = "ab";
//1.创建StringBuilder对象
//2.在字符串常量池中创建"a"
//3.在字符串常量池中创建"b"
//4.创建String对象
//5.调用StringBuilder的append方法,将"a"和new String("b")拼接
String str2 = "a" + new String("b");//一共创建了"a","b",String,StringBuilder这四个对象


//两个不同的地址
System.out.println(str1==str2);//false

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DjRRK7b0-1670158334421)(F:\221001\笔记\JavaAdv01.assets\image-20221123104555028.png)]

总结

在使用字符串时,如果要比较其值是否相同,不要使用判断,因为判断的是内存地址。

所以在比较字符串是否相同时,要使用String类重写的equals方法进行判断。

该方法判断的原理大致为:将两个字符串用字符数组保存,逐个判断字符数组中的每个字符,全部一致时返回true,

所以比较的是字面值。在使用equals方法时,通常将已知的非空字符串作为调用者。

username.equals("admin");//这样写,username变量可能为空,会抛出空指针异常
"admin".equals(username);//这样写能避免空指针异常

字符串相关面试题

//题目一
String str1 = "ab";//常量池中创建"ab"
String str2 = new String("ab");//堆中new String()保存常量池中已有的"ab"
String str3 = "a" + "b";//用常量池已有的"ab"
String str4 = "a" + new String("b");//常量池中创建"a"和"b",堆中new String()和new StringBuilder()
String str5 = "ab";//用常量池已有的"ab"

System.out.println(str1 == str2);//false
System.out.println(str1 == str3);//true
System.out.println(str1 == str4);//false
System.out.println(str1 == str5);//true


//题目二
//这两句话执行后,会创建几个对象
String s1 = "abc";
String s2 = "a" + "b" + "c";
//在字符串常量池中创建一个对象"abc"

//题目三
//这两句话执行后,会创建几个对象
String s3 = new String("你好");//常量池:"你好",堆中:new String()
String s4 = new String("你好");//堆中:new String()
//3个对象:堆中两个new String(),常量池中"你好"


//题目四
//这两句话执行后,会创建几个对象
String s5 = "hello";//常量池:"hello"
String s6 = "hel" + new String("lo");//常量池:"hel"和"lo"  堆:new String()和new StringBuilder
//5个对象:常量池:"hello"、"hel"和"lo",堆:new String()和new StringBuilder


//题目五
String s7 = new String("wor");//常量池:"wor",堆:new String()
String s8 = s7 + "ld";//常量池:"ld" 堆:new StringBuilder()
//4个对象:常量池:”wor"和"ld",堆:new String()和new StringBuilder

字符串String类中的常用方法

方法名返回值作用
length()int得到字符串的长度
toLowerCase()String转换为小写
toUpperCase()String转换为大写
trim()String去除字符串首尾的所有空格
isEmpty()boolean判断字符串是否为空白字符串""
getBytes()byte[]将字符串转换为字节数组
toCharArray()char[]将字符串转换为字符数组
equalsIgnoreCase(String str)boolean忽略大小写判断两个字符串是否相同
equals(String str)boolean判断两个字符串是否相同
charAt(int index)char得到字符串指定索引上的字符
indexOf(String str)int得到字符串中某个子字符串第一次出现的索引,如果不存在,返回-1
lastIndexOf(String str)int得到字符串中某个子字符串最后一次出现的索引,如果不存在,返回-1
contains(字符序列)boolean判断某个子字符串是否在原字符串中出现
concat(String str)String将参数字符串拼接到原字符串末尾
startsWith(String str)boolean判断是否以指定字符串开头
endsWith(String str)boolean判断是否以指定字符串结尾
substring(int begin)String从指定索引开始截取字符串至末尾
substring(int being,int end)String截取[begin,end)区间内的字符串
split(String regex)String[]按执行字符串或正则表达式切分原字符串。如果指定内容不再末尾,n个指定字符能得到n+1个子串;如果指定内容在末尾,n个指定字符能得到n个子串(不包含末尾的无效字符)
replace(char oldChar,char newChar)String将原字符串中的所有指定字符替换为新字符
String.valueOf(参数)String将任意参数转换为字符串。通常用于原始类型转换为字符串。
String.formart(String 格式,Object… obj)String根据指定格式转换参数。常用于将浮点数保留小数。如String.format(“%4.2f”,10.0/3)表示将计算的结果四舍五入保留2位小数转换为字符串;如果最终数据所占位置小于4,原样输出,大于4在最前补充空格。

可变字符串

String字符串对象是一个常量,在定义后,值不可改变。

如果使用String类的对象,对其频繁更新时,就会不停地创建新的对象,不停引用给同一个变量。

如要执行10000次循环重新赋值的过程,就要创建10000个字符串对象,执行效率很低,这时就需要使用可变字符串对象。

package com.hqyj.StringBuilderTest;

/*
 * 可变字符串StringBuilder
 * */
public class Test1 {
    public static void main(String[] args) {

        System.out.println("程序开始执行");
        //System.currentTimeMillis();用于获取当前时间对应的毫秒数
        //从1970 1 1 0:0:0这一刻开始,到这句话执行时间隔的毫秒数
        long startTime = System.currentTimeMillis();
        /*
        //循环"更新"字符串,实际是在不停创建新的字符串
        String str = "";
        for (int i = 0; i < 50000; i++) {
            str += i;
        }*/
        //使用可变字符串StringBuilder对象,真正更新字符串
        //因为全程只有一个对象StringBuilder,每次循环只是在不停操作该对象,不会创建新对象,所以效率很高
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < 500000; i++) {
            sb.append(i);
        }
        long endTime = System.currentTimeMillis();

        System.out.println("程序执行结束,用时" + (endTime - startTime) + "毫秒");

    }
}

StringBuilder类

用于表示可变字符串的一个类,是非线程安全的,建议在单线程环境下使用。

StringBuffer类

用于表示可变字符串的一个类,是线程安全的,建议在多线程环境下使用。

StringBuilder和StringBuffer中的方法都一致,只不过StringBuffer中的方法使用了synchoronized关键字修饰,表示是一个同步方法,在多线程环境下不会出现问题。

这里以StringBuilder为例

构造方法

常用构造方法作用
StringBuilder()创建一个大小为16的字符串数组,表示一个空白字符。类似于String str=“”;
StringBuilder(String str)创建一个str长度+16的字符数组后,将str添加到其中。类似于String str=“初始值”;

普通方法

常用方法作用
append(Object obj)将任意类型的参数添加到原可变字符串末尾
delete(int start,int end)删除[start,end)区间内的字符
deleteCharAt(int index)删除index索引上的字符
insert(int index,Object obj)在索引index上插入obj
replace(int start,int end,String str)将[start,end)区间内的字符替换为str
reverse()反转字符串

注意

  • 以上表格中的方法都是在直接操作同一个字符串对象,每次调用方法后,原字符串都会发生变化
  • StringBuffer和StringBuilder并没有重写equals方法,所以可变字符串的值是否相同时,调用的是equals中原始的==判断。如果要判断两个可变字符串的值是否相同时,需要将其转换为String后调用equals判断

可变字符串与String之间的转换

String转换为可变字符串

String str="hello";
//通过构造方法将String"包装"为可变字符串对象
StringBuilder sb = new StringBuilder(str);

可变字符串转换为String(任意类型对象转换为String)

方法一:String.valueOf(Object obj)方法

StringBuilder sb = new StringBuilder("你好");
//调用静态方法
String str = String.valueOf(sb);   

方法二:对象.toString();

StringBuilder sb = new StringBuilder("你好");
//调用toString()
String str = sb.toString();

方法三:

StringBuilder sb = new StringBuilder("你好");
//拼接一个空字符串
String str = sb + "";

可变字符串面试题

比较String、StringBuilder和StringBuffer的区别

相同点:

  • 这三个类都可以表示字符串。都提供了一些操作字符串的方法。
  • 这三个类中有相同的方法,如charAt()、indexOf()等
  • 这三个类都是被final修饰的类,不能被继承

不同点:

  • String定义的字符串是一个常量。可变字符串定义的字符串是一个变量
  • String类中的方法,调用后,不会改变原本字符串的值;可变字符串类中的方法,调用后,会改变原本字符串的值
  • StringBuilder是非线程安全的可变字符串类,StringBuffer是线程安全的可变字符串类,其中的方法被synchronized修饰

总结

在频繁操作同一个字符串时,一定要使用可变字符串StringBuidler或StringBuffer类的对象,不能使用String类的对象。

System类

这个类中包含了一些系统相关的信息和一些方法。其中的属性和方法都是静态的。

该类不能创建对象,不是因为它是一个抽象类,而是因为它的构造方法是私有的。

常用属性和方法
System.out获取打印输出流PrintStream对象,用于控制台打印信息。
System.in获取输入流InputStream对象,用于获取输入的信息
System.err获取打印输出流PrintStream对象,用于控制台打印异常信息。
System.exit(int statues)终止虚拟机运行,参数0表示正常终止。
System.currentTimeMillis()获取从1970.1.1 0:0:0至今进过了多少毫秒。中国是UTC(+8),所以是从1970.1.1 8:0:0至今经过了多少毫秒。返回long类型。
System.arraycopy(原数组,原数组起始位置,目标数组,目标数组起始位置,原数组要复制的元素数量)将原数组中指定长度的元素复制到新数组中

RunTime类

Runtime类的对象,表示程序运行时对象(程序运行环境对象)。

包含了程序运行环境相关的信息。常用于获取运行环境信息(如虚拟机内存)或执行某个命令。

特点

这个类不是一个抽象类,但不能创建对象,因为它的构造方法是私有的。

这个类提供了一个静态方法getRuntime(),通过这个方法,可以获取一个Runtime类的对象。

这是Java中的一种设计模式–单例模式(一个类只能有一个创建对象)。

public class Runtime {
    //定义了私有的一个静态成员:当前类的对象
    //由于静态成员只在类加载时执行一次,所以这里只会创建唯一一个当前类的对象
    private static Runtime currentRuntime = new Runtime();

    //定义了一个公共的静态方法,用于获取创建的唯一的当前类的对象
    public static Runtime getRuntime() {
        return currentRuntime;
    }

    //构造方法是私有的,不能在当前类之外创建对象
    private Runtime() {}
}

使用

package com.hqyj.test;

import java.io.IOException;

public class RuntimeTest {
    public static void main(String[] args) throws IOException, InterruptedException {
        //通过Runtime类的静态方法getRuntime()获取唯一的Runtime类的实例
        Runtime runtime = Runtime.getRuntime();

        System.out.println("当前虚拟机空闲内存" + runtime.freeMemory() / 1024 / 1024 + "MB");
        System.out.println("当前虚拟机实际最大内存" + runtime.totalMemory() / 1024 / 1024 + "MB");
        System.out.println("当前虚拟机支持的最大内存" + runtime.maxMemory() / 1024 / 1024 + "MB");

        //exec(String 指令名)运行某个指令,返回运行的进程对象
        //在指定秒后关机
        // Process process = runtime.exec("shutdown -s -t 300");
        //取消关机任务
        //  Process process = runtime.exec("shutdown -a");
        //mspaint画图 calc计算器 notepad记事本
        Process process = runtime.exec("mspaint");
        Thread.sleep(2000);   
        //通过进程对象调用销毁功能,从而关闭
        process.destroy();
    }
}

方法调用时传值问题

package com.hqyj.test2;

public class Test {
    /*
     *  当方法的参数为原始类型,方法中对该参数做修改,不会影响实际参数
     * */
    public static void fun1(int i) {
        i = 123;
        System.out.println(i);
    }

    /*
     *  当方法的参数为字符串时,方法中对字符串"重新赋值",实际是创建了一个新的字符串对象,不会影响实际参数
     * */
    public static void fun2(String str) {
        str = "new";
        System.out.println(str);
    }

    /*
     *  如果参数为引用类型,方法中直接操作该参数,操作的就是实际参数的内存地址,会影响实际参数
     * */
    public static void fun3(Person p) {
        p.setName("吴彦祖");
        System.out.println(p.getName());
    }

    /*
     * 如果参数为引用类型,方法中创建了一个新对象对其赋值,操作的是创建的新对象,不会影响实际参数
     * */
    public static void fun4(Person p) {
        p = new Person();
        p.setName("易烊千玺");
        System.out.println(p.getName());
    }

    /*
    * 如果参数为数组,也属于引用类型,方法中直接操作数组,操作的是实参数组,会影响实际参数
    * */
    public static void fun5(int[] list) {
        list[0] = 123;
        System.out.println(list[0]);
    }


    public static void fun(char[] list,Person p){
        list[0]='m';//这里在直接操作实际参数,会影响实参
        p = new Person();//这里创建了一个新的对象,操作的是方法中的对象,不会影响实参
        p.setName("刘鑫");
    }

    public static void main(String[] args) {
        //方法参数为原始类型,方法中对参数做修改,不会改变实际参数
        int i = 0;
        fun1(i);//123
        System.out.println(i);//0

        //方法参数为字符串,方法中对字符串重新赋值,不会改变实际参数
        String str = "old";
        fun2(str);//new
        System.out.println(str);//old

        //方法参数为引用类型,方法中对参数直接修改,会改变实际参数
        Person p = new Person();
        p.setName("王海");
        fun3(p);
        System.out.println(p.getName());

        //方法参数为引用类型,方法中创建新对象后赋值给实际参数,操作的是方法中的对象,不会改变实际参数
        Person p1 = new Person();
        p1.setName("赵敏");
        fun4(p1);
        System.out.println(p1.getName());

        //方法参数为数组,属于引用类型,方法中对参数直接修改,会改变实际参数
        int[] list = {0,1,2};
        fun5(list);
        System.out.println(list[0]);

        //练习
        char[] list2={'a','b','c'};
        Person p2 = new Person();
        fun(list2,p2);
        System.out.println(list2[0]);//m
        System.out.println(p2.getName());//null
    }
}

总结

参数只有是引用类型(类、数组、接口),并且方法中在直接操作该参数时,才会对实际参数造成影响。

fun3(Person p)参数为Person对象,方法中直接调用参数p的xxx方法,是在操作实际参数。

fun5(int[] list)参数为数组,方法中直接操作数组某个索引对应的元素,是在操作实际参数。

fun2(String str)和fun4(Person p)都在方法中创建了一个新的对象,是在操作方法中的参数,不影响实际参数。

public static void fun(char[] list,Person p){
    list[0]='m';//这里在直接操作实际参数,会影响实参
    p = new Person();//这里创建了一个新的对象,操作的是方法中的对象,不会影响实参
    p.setName("刘鑫");
}
public static void main(String[] args){
    char[] list={'a','b','c'};
    Person p = new Person();
    fun(list,p);
    System.out.println(list[0]);//方法内部直接操作数组,会影响实际参数,输出m
    System.out.println(p.getName());//方法内部创建了新对象,不会影响实际参数,输出null
}

Date类

用于表示日期时间的类,位于java.util包下

构造方法

常用构造方法说明
Date()创建当前瞬间对应的日期对象
Date(long l)创建指定瞬间对应的日期对象
Date(int year,int month,int day)该构造方法已过时。创建指定年月日的日期对象(年是1900年起经过的年数,月用0-11表示1到12月)

常用方法

常用方法作用
getTime()得到对应Date对象表示的毫秒数
setTime(long l)设置Date对象的毫秒数
after(Date when)判断调用日期对象是否在when之后
before(Date when)判断调用日期对象是否在when之前

SimpleDateFormat类

用于格式化日期的类。

构造方法

常用构造方法作用
SimpleDateFormat(String pattern);创建一个指定日期模板的格式化日期对象

日期模板

特殊字符作用
yyyy年份
MM月份
dd日期
HH小时
mm分钟
ss
E星期
以上两个字母都可以写成一个,如月份5M:5,MM:05
yyyy/MM/dd HH:mm:ss E2022/11/24 16:24:09 星期四

常用方法

常用方法返回值作用
format(Date date)String将Date对象按日期模板转换为字符串
parse(String str)Date将满足日期模板的字符串转换为Date对象
package com.hqyj.test3;

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

public class SimpleDateFormatTest {
    public static void main(String[] args) throws ParseException {
        //定义格式化日期类所需的时间模板
        /*
        * yyyy  年
        * MM    月份
        * dd    日期
        * HH    24小时制
        * hh    12小时制
        * mm    分钟
        * ss    秒
        * E     星期
        *
        * 两个字母都可以写成一个,如月份MM和M
        * MM       5月实际为05
        * M       5月实际为5
        * */
        String patten = "yyyy/MM/dd HH:mm:ss E";//年/月/日 时:分:秒 星期
        //创建格式化日期类对象,参数为日期模板
        SimpleDateFormat sdf = new SimpleDateFormat(patten);
        //创建当前日期对象
        Date now = new Date();
        //调用格式化日期对象的format(Date date),将Date对象转换为指定日期格式的字符串
        String format = sdf.format(now);
        //输出
        System.out.println(format);
        //parse(String str)将指定日期模板的字符串转换为Date对象
        Date date = sdf.parse("2000/5/3 2:1:3 星期一");

        System.out.println(date);


    }
}

Calendar类

表示日历的类,包含了很多日历相关的信息。

是一个抽象类,无法创建对象。可以通过静态方法getInstance()获取该类的一个实例。

//获取Calendar类的对象
Calendar cal = Calendar.getInstance();

日历字段

在Calendar类中,定义了很多被final和static修饰的常量,称为日历字段,实际一个数字,用于获取指定信息

作用
Calendar.YEAR年份
Calendar.MONTH月份(0-11表示1-12月)
Calendar.DATE日期
Calendar.DAY_OF_WEEK星期(1-7表示周天到周六)
Calendar.HOUR12进制小时
Calendar.HOUR_OF_DAY24进制小时
Calendar.MINUTE分钟
Calendar.SECOND
Calendar.DAY_OF_MONTH本月第几天
Calendar.DAY_OF_YEAR本年第几天
Calendar.WEEK_OF_MONTH本月第几周
Calendar.WEEK_OF_YEAR本年第几周

常用方法

常用方法作用
get(int field)根据日历字段获取对应的值
getTime()获取对应的Date对象(Calendar对象转换为Date对象)
getMaximum(int field)获取指定日历字段支持的最大值,如Calendar.DATE最大31
getActualMaximum(int field)获取指定日历字段在当前日期下的实际最大值,如11月,Calendar.DATE最大30
set(int field,int value)将指定的日历字段设置为指定值
set(int year,int month,int date)同时设置日历对象的年月日
setTime(Date date)将Date对象作为参数设置日历对象的信息

实现万年历

package com.hqyj.homework;

import java.util.Calendar;
import java.util.Scanner;

public class Test2 {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入年月");
        Calendar cal = Calendar.getInstance();
        int year = sc.nextInt();
        int month = sc.nextInt();
        //设置指定年月,默认为1号
        cal.set(year, month - 1, 1);
        //用于换行的计数,每行7个,包括空格
        int count = 0;

        System.out.println("========"+year+"年"+month+"月========");
        System.out.println("一\t二\t三\t四\t五\t六\t日");

        //打印1号前的空格
        //DAY_OF_WEEK    星期     空格数量
        //2              一          0
        //3              二          1
        //4              三          2
        //5              四          3
        //6              五          4
        //7              六          5
        //1              日          6
        //获取本月1号是所在周的第几天
        int week = cal.get(Calendar.DAY_OF_WEEK);
        //根据规律判断,打印空格的同时也要计数
        if (week == 1) {
            System.out.print("\t\t\t\t\t\t");
            count += 6;
        } else {
            for (int i = 1; i <= week - 2; i++) {
                System.out.print("\t");
                count++;
            }
        }

        //遍历当月每一天
        for (int i = 1; i <= cal.getActualMaximum(Calendar.DATE); i++) {
            System.out.print(i + "\t");
            count++;
            //隔7换行
            if (count % 7 == 0) {
                System.out.println();
            }
        }

    }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值