String类代表字符串类,java程序中所有的字符串文字都作为该类的实例,字符串不可改变,他们的值在创建之后不可被更改,从其类的定义我们也可以看出。
特点:
1)字面量的方式
String string=“abc”;
2)调用构造器的方式来创建String对象
String string=new String(“abc”); //创建了两个对象
初始化新创建的String
对象,使其表示为与参数相同的字符序列;换句话说,新创建的字符串是参数字符串的副本拷贝。
1)创建的对象的个数
String s1=“123”;
s1=s1+“45”;
对于这两行代码来说,如果字符串常量池中不存在"123"和"45"的情况下创建了3个字符串对象,即“123”,“45”,“12345”。
2)创建的对象的个数
String string=new String(“abc”);
创建了两个对象,一个对象为“abc”存在于字符串常量池中,另一个为String类的对象,存在于堆中。
3)jvm内存解析
在jvm的规范中,方法区(又称永久区)逻辑上属于堆的一部分,但是实际落地的时候,方法区和堆是分开的。
常量池的位置也随着JDK版本的迭代发生着变化。
JDK1.6及以前:常量池分配在永久代,在方法区中。
JDK1.7:依旧存在,但是已经逐步去永久代的路上,1.7在堆中(heap space)。
JDK1.8及以后:字符串常量池依旧在方法区,此时方法区不叫永久代,叫metadata,元数据。
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) 分配一个新的字符串,其中包含当前包含在字符串缓冲区参数中的字符序列。 |
常见的编码集:UTF-8,GBK,GB2312,ISO-8859-1,ascII
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)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的构造器>解码
String:JDK1.0,不可变字符序列,底层使用char[]来进行存储。
StringBuffer:JDK1.0,可变字符序列,线程安全的(效率低),底层使用char[]来进行存储。
StringBuilder:JDK1.5新增,可变字符序列,线程不安全的(效率高),底层使用char[]来进行存储。
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’;
注意:输出数组的长度为实际字符串的长度,而非底层char型数组的长度。
因为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
int 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) { // overflow
throw new OutOfMemoryError();
}
//Integer.MAX_VALUE - 8==0x7fffffff-8
return (minCapacity > MAX_ARRAY_SIZE)
? minCapacity : MAX_ARRAY_SIZE;
}
分析:如果要添加的数据底层数组盛不下,那就需要扩容底层数组,默认情况下扩容为原来容量的的2倍加2。如果还不够用,则新的长度为添加的字符串的长度与原字符串的长度和。然后将这个将数组中的数据复制到新的数组中去。
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):修改指定位置的字符
@Test
public void test02(){
StringBuffer sb1=new StringBuffer("abcd123");
sb1.append("abc");
//abcd123abc
System.out.println(sb1);
//[start,end)
sb1.delete(3,7);
//abcabc:
System.out.println(sb1);
sb1.replace(0,3,"hello");
//helloabc
System.out.println(sb1);
sb1.insert(5,",");
//hello,abc
System.out.println(sb1);
sb1.reverse();
//cba,olleh
System.out.println(sb1);
String substring = sb1.substring(0, 3);
//cba
System.out.println(substring);
sb1.setCharAt(7,'o');
sb1.setCharAt(8,'。');
System.out.println(sb1);
}
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();
}
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;
}
.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;
}