一 常用类之String类

1 String类概述

1.1 String类简介

​String类代表字符串类,java程序中所有的字符串文字都作为该类的实例,字符串不变,他们的值在创建之后不可被更改。

特点:

  • 类被final修饰,表明不可被继承。
  • 类实现了Serializable接口,表明该类可以序列化。
  • 类实现了Comparable接口,表明该类可以比较大小。
  • 类内部定义了final char[] value,用于存储字符串数据。

1.2 创建字符串对象的方式

​ 1)字面量的方式

​ String string=“abc”; //生成的对象在方法区的常量池中

​ 2)调用构造器的方式来创建String对象

​ String string=new String(“abc”); //创建了两个对象

​ 初始化新创建的String对象,使其表示与参数相同的字符序列; 换句话说,新创建的字符串是参数字符串的副本。

1.3 内存分析

​ 1)创建的对象的个数

​ String s1=“123”;
​ s1=s1+“45”;

​ 对于这两行代码来说,创建了3个字符串对象,即值为“123”,“45”,“122345”。

​ 2)创建的对象的个数

​ String string=new String(“abc”);

​ 创建了两个对象,一个对象为“abc”存在于常量池中,另一个为String对象,存在于堆中。

​ 3)jvm内存解析

​ 在jvm的规范中,方法区(又称永久区)逻辑上属于堆的一部分,但是实际落地的时候,方法区和堆是分开的。

常量池的位置也随着JDK版本的迭代发生着变化。

​ JDK1.6及以前:常量池分配在永久代,在方法区中。

​ JDK1.7:依旧存在,但是已经逐步去永久代的路上,1.7在堆中(heap space)。

​ JDK1.8及以后:字符串常量池依旧在方法区,此时不叫永久代,叫metadata,元数据。

1.4 String类常见的构造器的使用

  • String() 初始化新创建的 String对象,使其表示空字符序列。
    String(byte[] bytes) 通过使用平台的默认字符集解码指定的字节数组来构造新的 String
    String(byte[] bytes, int offset, int length) 通过使用平台的默认字符集解码指定的字节子阵列来构造新的 String
    String(byte[] bytes, String charsetName) 构造一个新的String由指定用指定的字节的数组解码[charset]。
    String(char[] value, int offset, int count) 分配一个新的 String ,其中包含字符数组参数的子阵列中的字符。
    String(String original) 初始化新创建的String对象,使其表示与参数相同的字符序列; 换句话说,新创建的字符串是参数字符串的副本。
    String(StringBuffer buffer) 分配一个新的字符串,其中包含当前包含在字符串缓冲区参数中的字符序列。
    String(StringBuilder builder) 分配一个新的字符串,其中包含当前包含在字符串构建器参数中的字符序列。

    常见的编码集:UTF-8,GBK,GB2312,ISO-8859-1,ascII

1.5 StringAPI

1.5.1 常见API

  • int length():返回字符串的长度。
  • char charAt(Int index):返回指定索引处的字符。
  • boolean isEmpty():判断数组是否是空的。
  • String toLowerCase():全部字符转换为小写的。
  • String toUpperCase():全部的字符转换为大写。
  • String trim():忽略前后空白,字符串中间的空白不会发生变化。
  • boolean equals(String str):比较两个字符串的内容是否相同。
  • boolean equalsIgnoreCase(String str):忽略大小写的对比。
  • String concat(String str):字符串的连接。
  • int compareTo(string str):比较两个字符串的大小,涉及到字符串的排序,关注的是内容。
  • String substring(int beginIndex):返回索引以后的字符串。
  • String substring(int beginIndex,int endIndex):返会索引区间内的字符串.[begin,end)

1.5.2 查找相关的API

  • boolean endsWith(String suffix):测试此字符串是不是以指定的字符串结束的。
  • boolean startsWith(String prefix):测试字符串是不是以指定字符串结尾的。
  • boolean startsWith(String prefix,int offset):此字符串从指定的索引处的子字符串是不是以指定字符串开头的。

1.5.3 判断是否包含某字符串

  • boolean contains(CharSequence s):判断当前字符串是否包含指定的字符串

  • int indexOf(String str):判断指定字符是否存在,存在的话返回第一次出现的索引位置,未找到返回-1.

  • int indexOf(String str,int fromIndex):从指定索引处,查找是否存在指定的字符串,存在返回指定索引,不存在就返回-1.

  • int lastIndexOf(String str):返回最后一次出现该字符串的索引位置。

  • int lastIndexOf(String str,int fromIndex):从指定的索引位置开始反向搜索,从右往左,返回最后一次出现该字符串的索引位置。

    注意:什么情况下,indexOf和lastIndexOf返回值相同?==>存在唯一的指定字符串,或者不存在该字符串。

1.5.4 替换

  • String replace(char oldChar,char new Char):返回一个新的字符串,将原有字符串中某个字符进行替换。
  • String replace(charSequence target,charSequence replacement):返回一个新的字符串,将原有字符串中某个字符串使用新的字符串进行替换。
  • String replaceAll(String regex,String replacement):使用给定的字符串替换符合正则表达式的部分。
  • String replaceFirst(String regex,String replacement):使用给定的字符串替换第一次符合正则表达式的部分。

1.5.5 匹配

  • boolean matches(String regex)::告知该字符串是否匹配正则表达式。

1.5.6 切分

  • String[] split(String regex):根据给定的正则表达式的匹配拆分此字符串。
  • String[] split(String regex,int limit):根据匹配给定的正则表达式拆分此字符串,最多不超过指定个数。

1.6字符串和其他类型转换问题

1)String和基本数据类型,包装类之间的转换

​ String==>基本数据类型,包装类:调用包装类的静态方法:parseXxx();

​ 基本数据类型,包装类==>String :调用String重载的valueOf()方法.

2)String和char[]之间的转换

​ String==>char[] :string里面的方法toCharArray()。

​ char==>String:String的构造器。

​ 面试题:字符串的反转,变成char数组,然后进行反转

4)String和byte[]之间的转换

​ String==>byte[]:String方法里面的getBytes()==>编码

​ byte[]>String:String的构造器>解码

2 字符串缓冲区

2.1 StringBuffer,StringBuilder和String三者的异同

​ String:JDK1.0,不可变字符序列,底层使用char[]来进行存储。

​ StringBuffer:JDK1.0,可变字符序列,线程安全的(效率低),底层使用char[]来进行存储。

​ StringBuilder:JDK1.5新增,可变字符序列,线程不安全的(效率高),底层使用char[]来进行存储。

2.2 简单分析:

​ 1)String

​ String str=new String(); //char[] value=new char[0];

​ String str1=new String(“abc”); //char[] value=new char[]{‘a’,‘b’,‘c’};

​ 2)StringBuffer和StringBuilder

​ StringBuffer sb1=new StringBuffer();//new char[16]相当于创建了一个char类型数组长度为16。

​ sb1.append(‘a’); value[0]=‘a’;

​ sb1.append(‘b’); value[1]=‘a’;

​ StringBuffer sb1=new StringBuffer(“abc”);//new char[16+abc.length]相当于创建了一个数组长度为16+abc.length。sb.length=3

​ sb1.append(‘a’); value[abc.length]=‘a’;

​ sb1.append(‘b’); value[abca.length]=‘a’;

注意:输出数组的长度为实际字符串的长度。

2.3 扩容机制

​ 因为StringBuilder底层使用char[ ] value来进行存储数据,当我们进行追加的时候需要对数组的长度进行判断。接下来对源码进行分析。

//追加字符串的方法,调用父类的方法
@Override
public StringBuilder append(String str) {//调用父类中的append();super.append(str);return this;
}
//父类中的append方法如下
public AbstractStringBuilder append(String str) {//判断添加的字符串是否为空if (str == null)return appendNull();int len = str.length();//确定容量足够(已经使用的字符长度和新添加的字符串的长度)ensureCapacityInternal(count + len);str.getChars(0, len, value, count);//修改字符数count += len;return this;
}
//确定容量的方法(参数为已经使用的字符长度和新添加的字符串的长度)
private void ensureCapacityInternal(int minimumCapacity) {// overflow-conscious code//如果长度不够,进行扩容操作if (minimumCapacity - value.length > 0) {value = Arrays.copyOf(value,newCapacity(minimumCapacity));}
}
//扩容
private int newCapacity(int minCapacity) {// overflow-conscious code//容量增大一倍且+2int newCapacity = (value.length << 1) + 2;if (newCapacity - minCapacity < 0) {newCapacity = minCapacity;}return (newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0)? hugeCapacity(minCapacity): newCapacity;
}
private int hugeCapacity(int minCapacity) {if (Integer.MAX_VALUE - minCapacity < 0) { // overflowthrow new OutOfMemoryError();}//Integer.MAX_VALUE - 8==0x7fffffff-8return (minCapacity > MAX_ARRAY_SIZE)? minCapacity : MAX_ARRAY_SIZE;
}

总结:如果要添加的数据底层数组盛不下,那就需要扩容底层数组,默认情况下扩容为原来容量的的2倍加2。如果还不够用,则新的长度为添加的字符串的长度与原字符串的长度和。然后将这个将数组中的数据复制到新的数组中去。

2.4 常用方法

  • StringBuffer append(XXX) :追加指定的字符串到末尾。

  • StringBuffer delete(int start,int end) :删除指定位置的内容。[begin,end)

  • StringBuffer replace(int start,int end,String str) :替换指定位置的内容。

  • StringBuffer insert(int offset,xxx) :指定位置插入数据。

  • StringBuffer reverse() :把当前字符序列反转

  • public int indexOf(String str):返回指定字符串第一次出现的索引位置

  • public String substring(int start,int end):返回[start,end)的子字符串。

  • public int length():返回字符串的长度。

  • public char charAt(int n):返回指定位置的字符串

  • public void setCharAt(int n,char ch):修改指定位置的字符

2.5 简单使用

@Test
public void test02(){StringBuffer sb1=new StringBuffer("abcd123");sb1.append("abc");//abcd123abcSystem.out.println(sb1);//[start,end)sb1.delete(3,7);//abcabc:System.out.println(sb1);sb1.replace(0,3,"hello");//helloabcSystem.out.println(sb1);sb1.insert(5,",");//hello,abcSystem.out.println(sb1);sb1.reverse();//cba,ollehSystem.out.println(sb1);String substring = sb1.substring(0, 3);//cbaSystem.out.println(substring);sb1.setCharAt(7,'o');sb1.setCharAt(8,'。');System.out.println(sb1);
}

2.6 练习

1.字符串大写转化为小写,小写转化为大写,删除其中的数字

public static void main(String[] args) {System.out.println(changestr1("asDSSasd qhw 12131 awdq"));System.out.println(changestr2("asDSSasd qhw 12131 awdq"));}public static String changestr1(String str) {StringBuilder builder=new StringBuilder(str);for (int i = 0; i < builder.length(); i++) {if (builder.charAt(i)>='0'&&builder.charAt(i)<='9') {builder.deleteCharAt(i);i--;}else if (builder.charAt(i)>='a'&&builder.charAt(i)<='z') {builder.setCharAt(i, (char)(builder.charAt(i)-('a'-'A')));}else if (builder.charAt(i)>='A'&&builder.charAt(i)<='Z') {builder.setCharAt(i, (char)(builder.charAt(i)+('a'-'A')));}}return builder.toString();} public static String changestr2(String str) {StringBuilder builder=new StringBuilder(str);for (int i = 0; i < builder.length(); i++) {if (Character.isDigit(builder.charAt(i))) {builder.deleteCharAt(i);i--;}else if (Character.isLowerCase(builder.charAt(i))) {builder.setCharAt(i, (char)(builder.charAt(i)-('a'-'A')));}else if (Character.isUpperCase(builder.charAt(i))) {builder.setCharAt(i, (char)(builder.charAt(i)+('a'-'A')));}}return builder.toString();}

拓展:String类常见的练习题

1.模拟一个trim方法,去除字符串两端的空格。

1.1 trim源码

public String trim() {int len = value.length;int st = 0;char[] val = value;    /* avoid getfield opcode */while ((st < len) && (val[st] <= ' ')) {st++;}while ((st < len) && (val[len - 1] <= ' ')) {len--;}return ((st > 0) || (len < value.length)) ? substring(st, len) : this;
}

1.2自定义trim( )

public static String getTrim(String oldString){char[] chars=oldString.toCharArray();int beginIndex=0;int lastIndex=0;for (int i = 0; i < oldString.length(); i++) {if(chars[i]!=' '){beginIndex=i;break;}}for (int i = oldString.length()-1; i>=beginIndex; i--) {if(chars[i]!=' '){lastIndex=i;break;}}return lastIndex>beginIndex?oldString.substring(beginIndex,lastIndex+1):"";
}public static String myTrim2(String oldString) {char[] chars = oldString.toCharArray();int beginIndex = -1;int lastIndex = -1;// 获取第一个非空字符的下标for (int i = 0; i < oldString.length(); i++) {if (chars[i] != ' ' && beginIndex == -1) {beginIndex = i;}if (chars[oldString.length() - 1 - i] != ' ' && lastIndex == -1) {lastIndex = oldString.length() - 1 - i;}}if (beginIndex == -1) {return "";}return oldString.substring(beginIndex, lastIndex + 1);}

2.将字符串指定索引内的数据进行反转

public static void main(String[] args) {try {//此处要求[beginIndex,endIndex)System.out.println(StringReverse.getReverse("abcdefg",4,6)); //abcdfeg} catch (Exception e) {System.out.println(e.getMessage());}}/*** @param oldString :待查找的字符串* @param beginIndex :起始下标* @param endIndex :结束下标* @return :反转之后的字符串* @throws Exception :抛出异常*/
public static String getReverse(String oldString,int beginIndex,int endIndex) throws Exception {if(endIndex<0||beginIndex<0||beginIndex>=oldString.length()-1||endIndex>=oldString.length()){throw new Exception("输入的下标越界!!");}if (beginIndex>endIndex){throw new Exception("输入的下标不合理!!");}if (beginIndex==endIndex||beginIndex==endIndex+1){return oldString;}char[] chars = oldString.toCharArray();char temp=' ';for (int i = beginIndex,j=endIndex-1; i < j ; i++,j--) {temp=chars[i];chars[i]=chars[j];chars[j]=temp;}return new String(chars);
}//方式2
public static String reverse(String str) {String ss = "";for (int i = 0; i < str.length(); i++) {ss = str.charAt(i) + ss;}return ss;}

3.获取字符串在另一个字符串中出现的次数

public static void main(String[] args) {int count = NumOfString.getNumOfString("abcabcabc", "ca");if (count == -1 || count == 0) {System.out.println("该字符串不存在");} else {System.out.println(count);}}/*** @param str1 :字符串* @param str2 :待查找的字符串* @return :出现的次数*/
public static int getNumOfString(String str1, String str2) {int num = 0;int i1 = 0;for (int i = 0; i < str1.length(); i = (i1 + str2.length())) {i1 = str1.indexOf(str2, i);if (i1 == -1) {break;}num++;}return num;
}

4.获取两个字符串中最大的相同字串。比如:str1=“wefweufihellonqdcbhf”;str2=“qwdchello”;

4.1实现方式1

public static void main(String[] args) {String[] maxString = MaxStringSize.getMaxString("hellohello11", "hello123");System.out.println(Arrays.toString(maxString));
}public static String[] getMaxString(String str1, String str2) {String[] maxString = new String[10];int count = 0;String minString = "";//始终要求str1为两个字符串中比较长的串。if ((str1.length() < str2.length())) {minString = str2;str2 = str1;str1 = minString;}boolean flag = false;//控制拆分的字符串的长度,长度依次递减for (int i = str2.length(); i > 0; i--) {//对字符串进行遍历for (int j = 0; j < str2.length() - i; j++) {//判断长的字符串中是否含有中是否拆分的字符串String max = str2.substring(j, j + i + 1);boolean contains = str1.contains(max);if (contains) {flag = true;maxString[count++] = max;}}if (flag) {break;}}return maxString;
}

4.2实现方式2

/*** 获取str1和str2中最大长度的字符串。* 前提,只有一个最大相同字串。* 解决方式,使用StringBuffer将其进行追加,以,分隔,然后切分为String类型数组。* @param str1 :字符串1* @param str2 :字符串2* @return :最大相同字串*/
public String getMaxSameString(String str1,String str2){if (str1!=null&&str2!=null){String maxStr=(str1.length()>=str2.length())?str1:str2;String minStr=(str1.length()<str2.length())?str1:str2;int length=minStr.length();//公共存在几大轮for (int i = 0; i < length; i++) {for (int x = 0,y=length-i; y <=length; x++,y++) {//注意此处时前开后闭String subStr=minStr.substring(x,y);if (maxStr.contains(subStr)){return subStr;}}}}return null;}
@Test
public void testGetMaxSameString(){String str1="abcwerthello1yuiodefabcdef";String str2="cvhello1bnm";String maxSameString = getMaxSameString(str1, str2);System.out.println(maxSameString);
}

5.查找字符串作用指定字符的数量

/*** @Description:1.获取字符串s中出现字符c次数num* @author 龍 date 2020年6月18日下午2:18:03* @param s* @param c* @return :字符串中出现的指定字符的次数*/public static int getNumFromString(String s, char c) {int index = -1;int num = 0;int no = 0;for (int i = 0; i < s.length(); i++) {//进行查找指定的字符index = s.indexOf(c, no);//如果字符不存在,则跳出循环if (index == -1) {break;}//找到了指定的字符,字符数量以及寻找的下标++。num++;no = index + 1;}//返回查找到的数目return num;}

6.字符转换:对指定的字符串进行转换 大写转换为小写 小写转换为大写并 删除其中数字

public static String changeStr(String s) {char[] chars = s.toCharArray();for (int i = 0; i < chars.length; i++) {if (chars[i] >= 'A' && chars[i] <= 'Z') {chars[i] = (char) (chars[i] + 32);} else if (chars[i] >= 'a' && chars[i] <= 'z') {chars[i] = (char) (chars[i] - 32);}}// 利用正则表达式去除数字return new String(chars).replaceAll("\\d+", "");}public static String changeStr02(String s) {char[] chars = s.toCharArray();String ss = "";for (int i = 0; i < chars.length; i++) {if (chars[i] >= 'A' && chars[i] <= 'Z') {//如果是大写字母,则转化为小写字母ss += (char) (chars[i] + 32);} else if (chars[i] >= 'a' && chars[i] <= 'z') {ss += (char) (chars[i] - 32);} else if (chars[i] > '9' || chars[i] < '0') {ss += chars[i];}}return ss;
}

7.实现compareTo方法

public static int myCompara02(String str1, String str2) {char[] charArray1 = str1.toCharArray();char[] charArray2 = str2.toCharArray();int len = Math.min(charArray1.length, charArray2.length);int last = 0;for (int i = 0; i < len; i++) {last = charArray1[i] - charArray2[i];if (last != 0) {return last;}}return charArray1.length - charArray2.length;
}

8.传入的字符串格式为xxxx-xx-xx。将日期s对应的日期加10天后的日期。

public static String getDays02(String str) {int firstIndex = str.indexOf('-');String year = str.substring(0, firstIndex);int secondIndex = str.indexOf('-', firstIndex + 1);String month = str.substring(firstIndex + 1, secondIndex);String date = str.substring(secondIndex + 1);int intDate = parstInt(date);int intMonth = parstInt(month);int intYear = parstInt(year);System.out.println(intYear + "==" + intMonth + "==" + intDate);int days = getDayFromMonth(intYear, intMonth);intDate += 10;if (days < intDate) {intDate -= days;intMonth++;}if (intMonth == 13) {intYear++;intMonth = 1;}String newDate = "" + intYear + "-" + intMonth + "-" + intDate;return newDate;
}public static int parstInt(String time) {int n = 0;for (int i = time.length() - 1, k = 1; i >= 0; i--, k *= 10) {// 获取当前位置的字符char c = time.charAt(i);// 获取当前位置的数int cInt = c - '0';n += k * cInt;}return n;
}public static int getDayFromMonth(int year, int month) {switch (month) {case 4:case 6:case 9:case 11:return 30;case 2:return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0) ? 29 : 28;default:return 31;}
}

9.获取参数字符串s中所有数字字符组成的最大整数

public static int getInt(String s) {String ss = "";for (int i = 0; i < s.length(); i++) {if (s.charAt(i) >= '0' && s.charAt(i) <= '9') {ss += s.charAt(i);}}char[] chars = ss.toCharArray();// 注意循环前缀。for (int i = 0; i < chars.length - 1; i++) {for (int j = i + 1; j < chars.length; j++) {// 保证是从小到大的顺序,进行排序。if (chars[j] > chars[i]) {char tmp = chars[i];chars[i] = chars[j];chars[j] = tmp;}}}String newStr = new String(chars);return parstInt(newStr);}

10.写一个方法 合并参数字符串中的所有叠词。如:“1111abcaabbbcccddd”—>“1abcabcd”

public static String changeStr04(String s) {/*** 定义一个变量ss来保存不同的字符 如果是第一个字符,则直接添加到ss中 如果不是第一个字符串,和前一个字符进行比较*/String ss = "";for (int i = 0; i < s.length(); i++) {if (i == 0) {ss += s.charAt(i);} else {if (s.charAt(i) != s.charAt(i - 1)) {ss += s.charAt(i);}}}return ss;}

11.打印参数字符串中数字字符,字母字符和其他字符出现的次数

public static void showAscll(String str) {int numCount = 0;int enCount = 0;int otherCount = 0;for (int i = 0; i < str.length(); i++) {char c = str.charAt(i);if (c >= '0' && c <= '9') {numCount++;} else if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) {enCount++;} else {otherCount++;}}System.out.println("数字的个数为:" + numCount + ",字母的个数为:" + enCount + ",其他字符的个数为:" + otherCount);
}

12.提取数字中的数字到数组中去

 public static void showArryFromStr(String str) {String[] nums = new String[10];String ss = "";int index = 0;for (int i = 0; i < str.length(); i++) {// 如果是数字则进行保存if (str.charAt(i) >= '0' && str.charAt(i) <= '9') {ss += str.charAt(i);} else {if (!"".equals(ss)) {nums[index] = ss;index++;ss = "";}}}if (!"".equals(ss)) {nums[index] = ss;index++;ss = "";}System.out.println(Arrays.toString(nums));}public static void getArr(String str) {//遍历字符串把其中所有非数组字符替换为-for (int i = 0; i < str.length(); i++) {char c=str.charAt(i);if (c>'9'||c<'0') {str=str.replace(c, '-');}}String[] arrs=str.split("-");  //[123, , , 123, , , , 98, , , , , , 876]System.out.println(Arrays.toString(arrs));int len=0;for (int i = 0; i < arrs.length; i++) {len+=arrs[i].isEmpty()?0:1;}String[] strArr=new String[len];int index=0;for (int i = 0; i < arrs.length; i++) {if (!arrs[i].isEmpty()) {strArr[index]=arrs[i];index++;}}System.out.println(Arrays.toString(strArr));    }public static void getArr02(String str) {//遍历字符串把其中所有非数组字符替换为,String ss="";for (int i = 0; i < str.length(); i++) {char c=str.charAt(i);if (c<'9'&&c>'0') {ss+=c;}else {if (ss!=""&&!ss.endsWith(",")) {ss+=',';}}}String[] strArr=ss.split(",");System.out.println(Arrays.toString(strArr));
}

13.一个任意的四位正整数。将数字重新组合成一个最大的数和最小的数相减,重复这个过程,最多七步,必得6174。即:7641-1467=6174。将永远出不来。 求证:所有四位数数字(全相同的除外),均能得到6174。输出掉进黑洞的步数。

public static void darkPool() {for (int i = 1000; i < 10000; i++) {boolean flag = false;int temp = i;for (int j = 0; j < 7; j++) {temp = getMax(temp) - getMin(temp);if (temp == 6174) {System.out.println(i+"调入黑洞,运行了"+(j+1)+"次。");flag = true;break;}}if (!flag) {System.out.println(i+"未掉入黑洞");}}
}public static int getMax(int k) {int[] nums = getArray(k);// 注意循环前缀。for (int i = 0; i < nums.length - 1; i++) {for (int j = i + 1; j < nums.length; j++) {// 保证是从小到大的顺序,进行排序。if (nums[j] > nums[i]) {int tmp = nums[i];nums[i] = nums[j];nums[j] = tmp;}}}int max = nums[0] * 1000 + nums[1] * 100 + nums[2] * 10 + nums[3];return max;
}public static int getMin(int k) {int[] nums = getArray(k);// 注意循环前缀。for (int i = 0; i < nums.length - 1; i++) {for (int j = i + 1; j < nums.length; j++) {// 保证是从小到大的顺序,进行排序。if (nums[j] > nums[i]) {int tmp = nums[i];nums[i] = nums[j];nums[j] = tmp;}}}int min = nums[3] * 1000 + nums[2] * 100 + nums[1] * 10 + nums[0];return min;
}public static int[] getArray(int i) {int num1 = i % 10;int num2 = i / 10 % 10;int num3 = i / 100 % 10;int num4 = i / 1000;int[] nums = new int[] { num1, num2, num3, num4 };return nums;
}

二 常用类之时间相关类

1.日期类:java.util.Date

1.1Date概述

​ 很多方法都过时了,推荐使用日历类Calendar来进行代替。

1.2 简单使用

 public static void main(String[] args) {//1.1.空参构造器:默认获取当前时间Date date=new Date();System.out.println(date); //Thu Jun 18 17:19:44 CST 2020//1.2.根据传入毫秒值,创建dateDate date1=new Date(0l);System.out.println(date1);  //Thu Jan 01 08:00:00 CST 1970//2.1将时间的年设置为指定年date1.setYear(date.getYear()-1);System.out.println(date1);  //Tue Jan 01 08:00:00 CST 2019//2.2毫秒值和date之间的转换System.out.println(date.getTime()); //1592473246614date.setTime(0);System.out.println(date);  //Thu Jan 01 08:00:00 CST 1970//2.3获取当前系统对应的字符串形式String localeString = date.toLocaleString();System.out.println(localeString);  //1970-1-1 8:00:00System.out.println(dateToStr(new Date())); //2020年6月18日   星期四   17时5分37秒}/*** @Description :将时间转化为指定格式的字符串* @author 龍* date 2020年6月18日下午5:39:36* @param date* @return*/public static String dateToStr(Date date) {int year = date.getYear();int month =date.getMonth();int day=date.getDate();int week=date.getDay();int hour=date.getHours();int minute=date.getMinutes();int second=date.getSeconds();String weeks="一二三四五六日";return (1900+year)+"年"+(month+1)+"月"+day+"日"+"   星期"+weeks.charAt(week-1)+"\t"+hour+"时"+month+"分"+second+"秒";}

2 SimpleDateFormat

2.1 出现背景

​ Data类的API不易于国际化,该类是为了对日期Date类进行格式化和解析,位于java.text.SimpleDateFormat

星期
y M d E H m s

2.2 简单使用

@Test
public void TestSimpleDateFormat() throws ParseException {//1.实例化SimpleDateFormat类对象SimpleDateFormat simpleDateFormat = new SimpleDateFormat();//2.格式化:日期-->字符串//2.1创建Date对象Date date = new Date();System.out.println(date);//2.2对date进行格式化String format = simpleDateFormat.format(date);System.out.println(format);//3.解析-->通常我们不喜欢使用默认的格式进行解析,所以需要使用带参数SimpleDateFormat对象。SimpleDateFormat simpleDateFormat1 = new SimpleDateFormat("yyyy-MM-dd");String string="2019-08-09";//3.1将需要解析的字符串传入parse方法的参数中去//注意:解析的时候字符串格式要符合SimpleDateFormat的解析格式,否则会报异常//java.text.ParseException: Unparseable date: "2019-08-09"Date parse = simpleDateFormat1.parse(string);System.out.println(parse);System.out.println(simpleDateFormat1.parse(format));System.out.println(simpleDateFormat1.format(date));
}

2.3 常见场景

/*** 练习一:字符串“2020-8-9”转换为java.sql.Data*/
@Test
public void test01() throws ParseException {String string="2020-8-9";SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");Date parse = simpleDateFormat.parse(string);//数据库中date设置需要使用毫秒值来进行赋值java.sql.Date date=new java.sql.Date(parse.getTime());//2020-08-09System.out.println(date);//class java.sql.DateSystem.out.println(date.getClass());
}
/*** "三天打鱼,两天晒网"  1990-1-1开始  xxxx-xx-xx 打渔?or晒网?* 总天数计算,*/
public static void main(String[] args) {try {new SimpleDateFormatTest().test02();} catch (ParseException e) {System.out.println(e.getMessage());}
}
//@Test
public void test02() throws ParseException {String startDate="1990-1-1";SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");Date parse = simpleDateFormat.parse(startDate);long start=parse.getTime();Scanner scanner = new Scanner(System.in);while(true){System.out.println("请输入日期(xxxx-MM-dd):");String next = scanner.next();Date parse1 = simpleDateFormat.parse(next);long end=parse1.getTime();switch ((int)(((end-start)/1000/60/60/24)%5)){case 0:case 1:case 2:System.out.println("打渔!!!");break;default:System.out.println("晒网");break;}}}

3 日历类:Calendar

3.1 简介

​ 所述Calendar类是一个抽象类,可以为在某一特定时刻和一组之间的转换的方法[calendar fields]如YEAR , MONTH ,DAY_OF_MONTH ,HOUR 等等,以及用于操纵该日历字段,如获取的日期下个星期。 时间上的瞬间可以用毫秒值表示,该值是从1970年1月1日00:00 00:00.000 GMT(Gregorian)的Epoch的偏移量

3.2 对象的创建

  • ​ 创建其子类(GregorianCalendar)对象。
  • ​ 调用其静态方法getInstance():实际返回的依旧是其子类对象

3.3 常用方法

  • **get(int field):**填入Calendar中的静态属性。可以获取一些常用的属性
  • **set(int field,int value):**修改当前Calendar对象的值。
  • **add():**增加指定值到当前属性。
  • **getTime():**日历类–>Date
  • **setTime(Date date):**Date–>日期类

3.4 简单使用

@Test
public void testCalendar(){//4.1实例化//方式一:创建其子类(GregorianCalendar)对象。//方式二:调用其静态方法getInstance()Calendar instance = Calendar.getInstance();//class java.util.GregorianCalendar。System.out.println(instance.getClass());//4.1方法的测试//get(int field):获取指定的参数值int i = instance.get(Calendar.DAY_OF_MONTH);System.out.println(i);//set(int field,value):设置参数为指定的值instance.set(Calendar.DAY_OF_MONTH,22);int i1 = instance.get(Calendar.DAY_OF_MONTH);System.out.println(i1);//add(int field,amount):instance.add(Calendar.DAY_OF_MONTH,3);int i2 = instance.get(Calendar.DAY_OF_MONTH);System.out.println(i2);//getTime():日历类-->DateDate time = instance.getTime();//Wed Mar 25 11:45:00 CST 2020System.out.println(time);//setTime(Date date):Date-->日期类Date date = new Date();instance.setTime(date);int i3 = instance.get(Calendar.DAY_OF_MONTH);System.out.println(i3);Date date1 = new Date(2020, 10, 1); //设置的时候是从1900年开始算起的//Mon Nov 01 00:00:00 CST 3920System.out.println(date1);//void setTimeInMillis(long millis) :设置日历对象为参数毫秒值对应的时间//void getTimeInMillis():获取日历对象对应的毫秒值}
/**
public static void calendarTest1() {Calendar calendar=Calendar.getInstance();calendar.set(Calendar.YEAR, 2010);calendar.set(Calendar.MONTH, 0);calendar.set(Calendar.DAY_OF_MONTH, 1);Calendar now=Calendar.getInstance();Long time=now.getTimeInMillis()-calendar.getTimeInMillis();int date=(int)(time/1000/3600/24);System.out.println(date);switch (date%5) {case 0:case 1:case 2:System.out.println("打渔日!");break;default:System.out.println("晒网日");break;}}

4 日期时间:LocalDateTime

4.1 出现的背景

  • 可变性:像日期和时间这样的类应该是不可变的。

  • 偏移性:Date中的年份是从1900年开始的,月份是从0开始的。

    Date date1 = new Date(2020, 10, 1);

    System.out.println(date1); //Mon Nov 01 00:00:00 CST 3920

  • 格式化:格式化只对Date有用,Calendar则不行。

  • 也不是线程安全的,也不能处理闰秒等。

4.2 改进

java8吸收了joda-Time的精华,新的java.time中包括所有关于本地日期(LocalDate),本地时间(LocalTime),本地日期时间(LocalDateTime),时区(ZonedDateTime)和持续时间(Duration)的类。Date类新增了toInstant( )用于将Date转换成新的表示形式。这些新增的本地化时间日期API大大的简化了日期时间和本地化的管理。

4.3 包结构

  • java.time-包含值对象的基础包。
  • java.time.chrono-提供对不同的日历系统的访问
  • java.time.format-格式化和解析时间和日期
  • java.time.temporal-包括底层框架和扩展性
  • java.time.zone-包括时区支持的类。

4.4 简单使用

@Test
public void testTime(){//now():静态方法,获取当前的日期,时间,日期+时间。LocalDate now = LocalDate.now();LocalTime now1 = LocalTime.now();LocalDateTime now2 = LocalDateTime.now();//2020-03-15System.out.println(now);//12:54:58.043System.out.println(now1);//2020-03-15T12:54:58.043System.out.println(now2);//of():设置指定的年月日时分秒,LocalDateTime of = LocalDateTime.of(2020, 10, 1, 8, 10, 4);//2020-10-01T08:10:04(无偏移量)System.out.println(of);//getXxx():获取指定的属性//15System.out.println(now2.getDayOfMonth());//SUNDAYSystem.out.println(now2.getDayOfWeek());//MARCHSystem.out.println(now2.getMonth());//75System.out.println(now2.getDayOfYear());//withXxx():设置相关的属性。体现了不可变性,返回值是修改之后的对象,本来的对象不会发生改变。比起Calendar更加的靠谱。LocalDateTime localDateTime = now2.withDayOfMonth(21);//2020-03-21T13:08:09.420System.out.println(localDateTime);//2020-03-15T13:08:09.420System.out.println(now2);//plusXxx():给指定属性增加。仍然是不可变性,本身对象属性不发生变化LocalDateTime localDateTime1 = localDateTime.plusMonths(3);//2020-03-21T13:08:09.420System.out.println(localDateTime);//2020-06-21T13:08:09.420System.out.println(localDateTime1);//minusXxx():给指定属性减少LocalDateTime localDateTime2 = localDateTime.minusDays(1);//2020-03-21T13:12:33.693System.out.println(localDateTime);//2020-03-20T13:12:33.693System.out.println(localDateTime2);
}

4.5 练习

@Testpublic void test04(){//当前时间LocalDate today = LocalDate.now();System.out.println("Current Date="+today);//根据输入的参数获取日期LocalDate firstDay_2014 = LocalDate.of(2014, Month.JANUARY, 1);System.out.println("Specific Date="+firstDay_2014);//Try creating date by providing invalid inputs//LocalDate feb29_2014 = LocalDate.of(2014, Month.FEBRUARY, 29);//Exception in thread "main" java.time.DateTimeException://Invalid date 'February 29' as '2014' is not a leap year//获取韩国当前的日期LocalDate todayKolkata = LocalDate.now(ZoneId.of("Asia/Kolkata"));System.out.println("Current Date in IST="+todayKolkata);//java.time.zone.ZoneRulesException: Unknown time-zone ID: IST//LocalDate todayIST = LocalDate.now(ZoneId.of("IST"));//获取从基数01/01/1970开始偏移365天的日期LocalDate dateFromBase = LocalDate.ofEpochDay(365);System.out.println("365th day from base date= "+dateFromBase);//获取2014年第100天后的日期LocalDate hundredDay2014 = LocalDate.ofYearDay(2014, 100);System.out.println("100th day of 2014="+hundredDay2014);}

拓展:

1 瞬时

1.1 瞬时简介

​ instant:时间线上的一个瞬时点,可能被用来记录应用程序中的事件时间戳。对于人来说年月日时分秒,对于机器来说时间线中一个点就是一个很大的数,有利于计算的处理。UNIX中这个数从1970年开始(utc),以秒为单位,在java中以毫秒为单位。java.time包是基于纳秒计算的,所以instant可以精确到纳秒。

​ 常见的 时间标准:

  • UTC:世界标准时间
  • GMT:格林尼治时间==>英国
  • CST:北京时间。UTC+8
1.2 瞬时简单使用
@Test
public void testInstant(){//1.实例化Instant now = Instant.now();//2020-03-15T05:33:31.729Z;;返回的是UTC的时间,如果是我们的时间就需要+8个小时System.out.println(now);//atOffset(ZoneOffset.ofHours(8)):根据时区添加偏移量OffsetDateTime offsetDateTime = now.atOffset(ZoneOffset.ofHours(8));//2020-03-15T13:37:00.750+08:00System.out.println(offsetDateTime);//toEpochMilli():获取1970年1月1日到现在的毫秒数。long l = now.toEpochMilli();System.out.println(l);//ofEpochMilli(l):根据指定的毫秒数获取指定对象。Instant instant = Instant.ofEpochMilli(l);//2020-03-15T05:40:53.664ZSystem.out.println(instant);
}

2 DateTimeFormatter

2.1 简介

​ 对时间进行本土化设置

2.2 简单使用
@Test
public void testDateTimeFormat(){//方式一:预定义的标准格式。// 如:ISO_LOCAL_DATE_TIMEDateTimeFormatter isoLocalDateTime = DateTimeFormatter.ISO_LOCAL_DATE_TIME;//格式化:LocalDateTime localDateTime = LocalDateTime.now();String format = isoLocalDateTime.format(localDateTime);//2020-03-15T14:50:06.953System.out.println(format);//解析:TemporalAccessor parse = isoLocalDateTime.parse(format);//{},ISO resolved to 2020-03-15T14:53:14.206System.out.println(parse);//方式二:本地化相关的操作://ofLocalizedDateTime()DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT);//格式化://风格:FormatStyle.SHORT,FormatStyle.LONG,FormatStyle.MEDIUM(适用于LocalDateTime)String format1 = dateTimeFormatter.format(localDateTime);//20-3-15 下午3:03System.out.println(format1);DateTimeFormatter dateTimeFormatter2 = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG);String format2 = dateTimeFormatter2.format(localDateTime);//2020年3月15日 下午03时03分01秒System.out.println(format2);DateTimeFormatter dateTimeFormatter3 = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM);String format3 = dateTimeFormatter3.format(localDateTime);//2020-3-15 15:03:01System.out.println(format3);//风格:FormatStyle.FULL(适用于LocalDate以上三种风格也适用于LocalDate)LocalDate localDate = LocalDate.now();DateTimeFormatter dateTimeFormatter1 = DateTimeFormatter.ofLocalizedDate(FormatStyle.FULL);String format4 = dateTimeFormatter1.format(localDate);//2020年3月15日 星期日System.out.println(format4);//方式三:自定义格式:ofPattern("yyyy-MM-dd hh:mm:ss E")DateTimeFormatter dateTimeFormatter4 = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss E");String format5 = dateTimeFormatter4.format(localDateTime);//2020-03-15 03:17:25 星期日System.out.println(format5);//解析TemporalAccessor parse1 = dateTimeFormatter4.parse(format5);//{SecondOfMinute=15, HourOfAmPm=3, NanoOfSecond=0, MicroOfSecond=0, MinuteOfHour=21, MilliOfSecond=0},ISO resolved to 2020-03-15System.out.println(parse1);
}

三 常用类之比较器

1 出现的背景

​ 在java中涉及到数组对象的排序问题

​ 说明:java中的对象,正常情况下,只能进行比较:==或者!=,不能使用>或者<,但是在实际的开发场景中,我们需要对多个对象进行排序,言外之意,就需要比较对象的大小,如何实现?

2 实现对象排序的方式

  • 自然排序:java.lang.Comparable
  • 定制排序:java.util.Comparator

3 使用

3.1 Comparable:自然排序

​ String,包装类等实现了Comparable接口,重写compareTo()方法,给出了比较两个对象的大小。

重写compareTo(obj)的规则:

  • 如果当前对象this大于形参对象obj,则返回正整数。

  • 如果当前对象this小于形参对象obj,则返回负整数。

  • 如果当前对象this等于形参对现象obj,则返回0。

    说明:对于自定义类进行排序,自定义类需要实现Comparable接口,若不实现Comparable接口,会报异常:java.lang.ClassCastException: com.atguigu.comparejava.Goods cannot be cast to java.lang.Comparable

​ 自定义类代码compareTo方法实现

class Goods implements Comparable {private String name;private double price;public Goods() {}public Goods(String name, double price) {this.name = name;this.price = price;}public String getName() {return name;}public void setName(String name) {this.name = name;}public double getPrice() {return price;}public void setPrice(double price) {this.price = price;}/*** 指明该类比较大小的方式:先按照价格从低到高,然后按照产品名称从低到高。** @param o:待比较的对象* @return :结果。正数说明this大,负数说明obj大,o则相等*/@Overridepublic int compareTo(Object o) {if (o instanceof Goods) {Goods goods = (Goods) o;if (this.price > goods.price) {return 1;} else if (this.price < goods.price) {return -1;} else {//return 0;return this.name.compareTo(goods.name);}}throw new RuntimeException("你输入的商品类型不对!");}@Overridepublic String toString() {return "Goods{" +"name='" + name + '\'' +", price=" + price +'}';}
}@Testpublic void testCompareGoods() {Goods[] goods = new Goods[4];goods[0] = new Goods("lenovo", 34);goods[1] = new Goods("xiaomi", 50);goods[2] = new Goods("dell", 50);goods[3] = new Goods("huawei", 50);Arrays.sort(goods);System.out.println(Arrays.toString(goods));}

3.2 Comparator接口的使用:定制排序

​ 1)排序:当元素的类型没有实现java.lang.Comparable接口而又不方便修改代码,或者实现了java.lang.Comparable接口的排序规则不适合当前的操作,那么可以考虑使用Comparator的对象来排序。

​ 2)实现compare(Object o1,Object o2)方法,比较o1,o2的大小:如果返回值是正数则o1>o2;负数则o1<o2;0则相等。

简单使用

@Test
public void testCompareGoods02() {Goods[] goods = new Goods[5];goods[0] = new Goods("lenovo", 34);goods[1] = new Goods("xiaomi", 50);goods[2] = new Goods("dell", 50);goods[3] = new Goods("huawei", 50);goods[3] = new Goods("huawei", 70);//产品名称从低到高,价格从高到低Arrays.sort(goods, new Comparator() {@Overridepublic int compare(Object o1, Object o2) {if (o1 instanceof Goods && o2 instanceof Goods) {Goods g1 = (Goods) o1;Goods g2 = (Goods) o2;if (g1.getName().equals(g2.getName())) {return -Double.compare(g1.getPrice(), g2.getPrice());} else {return g1.getName().compareTo(g2.getName());}}return 0;}});System.out.println(Arrays.toString(goods));
}

四 常用类之其他类

1 System类

1.1System类的概述

​ System类代表系统,是系统级的属性和控制方法都放置到该类的内部,位于java.lang包。

特点:

  • 该类的构造器是private的,所以无法创建该类的对象,也无法实例化该类。
  • 其内部的成员变量和成员方法都是static的,所以也很方便的进行调用。

1.2成员变量及方法

1.2.1 成员变量

  • in:标准输入流(键盘输入)
  • out:标准输出流(显示器)
  • err:标准错误输出流(显示器)

1.2.2 成员方法