网站外包的恶意刷流量,能上网但是浏览器打不开网页,云浮市做网站的公司,辽宁建设银行企业银行官方网站String 类代表字符串。Java 程序中的所有字符串字面值#xff08;如 “abc” #xff09;都作为此类的实例实现。 字符串是常量#xff1b;它们的值在创建之后不能更改。字符串缓冲区支持可变的字符串。因为 String 对象是不可变的#xff0c;所以可以共享。 一、类定义
p… String 类代表字符串。Java 程序中的所有字符串字面值如 “abc” 都作为此类的实例实现。 字符串是常量它们的值在创建之后不能更改。字符串缓冲区支持可变的字符串。因为 String 对象是不可变的所以可以共享。 一、类定义
public final class Stringimplements java.io.Serializable, ComparableString, CharSequence{...}实现的三个接口
java.io.Serializable可被序列化的标志接口Comparable强行对实现它的每个类的对象进行整体排序CharSequence用来表示一个有序字符的集合
String类是一个被final修饰的常量类常量类的特性为不可被任何类所继承一旦String对象被创建该对象是无法被改变的直至该对象被销毁特殊情况除外如暴力反射。
二、成员变量
//存储字段串
private final char value[];//缓存哈希值
private int hash; // Default to 0//用于序列化和反序列化之间的ID比对
private static final long serialVersionUID -6849794470754667710L;//Class String is special cased within the Serialization Stream Protocol.
private static final ObjectStreamField[] serialPersistentFields new ObjectStreamField[0];从源码看出String底层使用一个字符数组来维护的。通过成员变量可以知道 String类 的值是 final 类型的不能被改变的所以只要一个值改变就会生成一个新的 String 类型对象存储 String 数据也不一定从数组的第0个元素开始的而是从 offset 所指的元素开始。
三、构造方法
//初始化一个新创建的 String 对象使其表示一个空字符序列。
public String() {
}//初始化一个新创建的 String 对象使其表示一个与参数相同的字符序列换句话说新创建的字符串是该参数字符串的副本。
public String(String original) {
}//分配一个新的 String使其表示字符数组参数中当前包含的字符序列。
public String(char value[]) {
}//分配一个新的 String它包含取自字符数组参数一个子数组的字符。
public String(char value[], int offset, int count) {
}//分配一个新的 String它包含 Unicode 代码点数组参数一个子数组的字符。
public String(int[] codePoints, int offset, int count) {
}Deprecated
public String(byte ascii[], int hibyte, int offset, int count) {
}Deprecated
public String(byte ascii[], int hibyte) {
}//通过使用指定的字符集解码指定的 byte 子数组构造一个新的 String。
public String(byte bytes[], int offset, int length, String charsetName)throws UnsupportedEncodingException {
}//通过使用指定的 charset 解码指定的 byte 子数组构造一个新的 String。
public String(byte bytes[], int offset, int length, Charset charset) {
}//通过使用指定的 charset 解码指定的 byte 数组构造一个新的 String。
public String(byte bytes[], String charsetName)throws UnsupportedEncodingException {
}//通过使用指定的 charset 解码指定的 byte 数组构造一个新的 String。
public String(byte bytes[], Charset charset) {
}//通过使用平台的默认字符集解码指定的 byte 子数组构造一个新的 String。
public String(byte bytes[], int offset, int length) {
}//通过使用平台的默认字符集解码指定的 byte 数组构造一个新的 String。
public String(byte bytes[]) {
}//分配一个新的字符串它包含字符串缓冲区参数中当前包含的字符序列。
public String(StringBuffer buffer) {
}//分配一个新的字符串它包含字符串生成器参数中当前包含的字符序列。
public String(StringBuilder builder) {
}/*
* Package private constructor which shares value array for speed.
* this constructor is always expected to be called with sharetrue.
* a separate constructor is needed because we already have a public
* String(char[]) constructor that makes a copy of the given char[].
*/
String(char[] value, boolean share) {
}四、普通方法
1、equals(Object anObject)方法
比较字符串的内容是否相同 public boolean equals(Object anObject) {if (this anObject) {return true;}if (anObject instanceof String) {String anotherString (String)anObject;int n value.length;if (n anotherString.value.length) {char v1[] value;char v2[] anotherString.value;int i 0;while (n-- ! 0) {if (v1[i] ! v2[i])return false;i;}return true;}}return false;}String类里重写了Object里的equals方法首先比较对象地址判断是否是两个相等的对象若不相等再通过instanceof关键字比对传入对象是否是String的实例若是则一一比对字符串的每一个字符;
2、hashCode()方法
String类的hashCode算法很简单使用数字31作为乘数; public int hashCode() {int h hash;if (h 0 value.length 0) {char val[] value;for (int i 0; i value.length; i) {h 31 * h val[i];}hash h;}return h;
}3、charAt(int index) 方法
该方法的作用是得到字符串的指定索引位置的字符元素;
public char charAt(int index) {if ((index 0) || (index value.length)) {throw new StringIndexOutOfBoundsException(index);}return value[index];
}4、compareTo(String anotherString)方法
该方法是按字母顺序比较两个字符串中每个字符的 Unicode 值 public int compareTo(String anotherString) {int len1 value.length;int len2 anotherString.value.length;int lim Math.min(len1, len2);char v1[] value;char v2[] anotherString.value;int k 0;while (k lim) {char c1 v1[k];char c2 v2[k];if (c1 ! c2) {return c1 - c2;}k;}return len1 - len2;
}当两个字符串某个位置的字符不同时返回的是这一位置的字符 Unicode 值之差当两个字符串都相同时返回两个字符串长度之差。此外还有个compareToIgnoreCase()方法该方法是在 compareTo() 方法的基础上忽略大小写。
5、concat(String str)方法
该方法的作用的将指定字符串拼接到原字符串末尾
public String concat(String str) {int otherLen str.length();if (otherLen 0) {return this;}int len value.length;char buf[] Arrays.copyOf(value, len otherLen);str.getChars(buf, len);return new String(buf, true);}首先判断要拼接字符串长度若长度为0则返回原字符串不为0则利用工具类Arrays中的静态方法copyOf来构建一个长度为原字符串和要拼接字符串的之和的字符数组 并将原字符串填充到字符数组前面后面为空再利用getChars方法将要拼接字符串放入字符数组后面为空的位置最后返回一个拼接后的新字符串。
6、indexOf(int ch, int fromIndex) 方法
该方法的作用是从指定索引位置开始查找指定字符第一次出现的位置
public int indexOf(int ch, int fromIndex) {final int max value.length;// 指定索引值小于0默认从0开始搜索if (fromIndex 0) {fromIndex 0;} else if (fromIndex max) {// 指定索引值大于等于字符串长度直接返回-1return -1;}// 一个char占用两个字节如果ch小于2的16次方65536绝大多数字符都在此范围内if (ch Character.MIN_SUPPLEMENTARY_CODE_POINT) {final char[] value this.value;for (int i fromIndex; i max; i) {if (value[i] ch) {return i;}}return -1;} else {//当字符大于 65536时处理的少数情况该方法会首先判断是否是有效字符然后依次进行比较return indexOfSupplementary(ch, fromIndex);}}首先进行一系列的逻辑判断最后for循环逐一判断对比相等返回下标索引值循环结束没有相等的就返回-1。
7、split(String regex, int limit) 方法
该方法的作用是将字符串分隔成指定正则表达式匹配后的字符串数组 public String[] split(String regex, int limit) {/* 1、单个字符且不是.$|()[{^?*\\其中一个* 2、两个字符第一个是\第二个大小写字母或者数字*/char ch 0;if (((regex.value.length 1 .$|()[{^?*\\.indexOf(ch regex.charAt(0)) -1) ||(regex.length() 2 regex.charAt(0) \\ (((ch regex.charAt(1))-0)|(9-ch)) 0 ((ch-a)|(z-ch)) 0 ((ch-A)|(Z-ch)) 0)) (ch Character.MIN_HIGH_SURROGATE ||ch Character.MAX_LOW_SURROGATE)){int off 0;int next 0;boolean limited limit 0;//大于0limitedtrue,反之limitedfalseArrayListString list new ArrayList();while ((next indexOf(ch, off)) ! -1) {//当参数limit0 或者 集合list的长度小于 limit-1if (!limited || list.size() limit - 1) {list.add(substring(off, next));off next 1;} else {//判断最后一个list.size() limit - 1list.add(substring(off, value.length));off value.length;break;}}//如果没有一个能匹配的返回一个新的字符串内容和原来的一样if (off 0)return new String[]{this}; // 当 limit0 时limitedfalse,或者集合的长度 小于 limit是截取添加剩下的字符串if (!limited || list.size() limit)list.add(substring(off, value.length));// 当 limit 0 时如果末尾添加的元素为空长度为0则集合长度不断减1直到末尾不为空int resultSize list.size();if (limit 0) {while (resultSize 0 list.get(resultSize - 1).length() 0) {resultSize--;}}String[] result new String[resultSize];return list.subList(0, resultSize).toArray(result);
}
return Pattern.compile(regex).split(this, limit);
}limit的取值存在三种情况
limit0拆分limit-1次
String str a,b,c;
String[] c1 str.split(,, 2);
System.out.println(c1.length);//2
System.out.println(Arrays.toString(c1));//{a,b,c}
1234limit0,拆分无限次且忽略原字符串后面的空白部分
String str a,b,c,,;
String[] c1 str.split(,, 0);
System.out.println(c1.length);//3
System.out.println(Arrays.toString(c1));//{a,b,c}
1234limit0,拆分无限次
String str a,b,c,,;
String[] c1 str.split(,, 0);
System.out.println(c1.length);//5
System.out.println(Arrays.toString(c1));//{a,b,c,,}8、String replaceAll(String regex, String replacement) 方法
该方法的作用是将原字符串中符合正则表达式的都替换成指定的字符串 public String replaceAll(String regex, String replacement) {return Pattern.compile(regex).matcher(this).replaceAll(replacement);}此外还有个replace(char oldChar, char newChar)方法作用是将所有olfChar都替换成newCher。
9、substring(int beginIndex, int endIndex) 方法
该方法的作用是返回从索引 beginIndex 到 endIndex 的子字符串
public String substring(int beginIndex, int endIndex) {if (beginIndex 0) {//起始索引小于0抛出异常throw new StringIndexOutOfBoundsException(beginIndex);}if (endIndex value.length) {//起始索引大于字符串长度抛出异常throw new StringIndexOutOfBoundsException(endIndex);}int subLen endIndex - beginIndex;//起始索引大于截止索引抛出异常if (subLen 0) {throw new StringIndexOutOfBoundsException(subLen);}return ((beginIndex 0) (endIndex value.length)) ? this: new String(value, beginIndex, subLen);}
123456789101112131415此外还有个重载方法substring(int beginIndex)作用是返回从索引 beginIndex 开始一直到结尾的子字符串。
五、拓展
1、常量池
JVM里有一块区域叫做常量池常量池中的数据是那些在编译期间被确定并被保存在已编译的.class文件中的一些数据。除了包含所有的8种基本数据类型char、byte、short、int、long、float、double、boolean外还有String及其数组的常量值另外还有一些以文本形式出现的符号引用。
我们声明字符串对象有两种常用的方式
通过字面值的形式直接赋值
String strabc;通过构造函数构建对象
String strnew String(abc);那么这两种方式有什么区别呢我们来测试一下
String str1 hello;
String str2 hello;
String str3 new String(hello);
String str4 new String(hello);
System.out.println(str1str2);//true
System.out.println(str1str3);//fasle
System.out.println(str3str4);//fasle通过上面这个例子充分说明了以下规律
①、字面量创建字符串会先在字符串池中找看是否有相等的对象没有的话就在字符串池创建该对象有的话则直接用池中的引用避免重复创建对象。 ②、new关键字创建时直接在堆中创建一个新对象变量所引用的都是这个新对象的地址。
实际在日常我们也很经常用表达式来拼接字符串这些的字符串对象又是怎么得到的呢
String str1 hello;
String str2 helloworld;
String str3 str1world;//编译器不能确定为常量(会在堆区创建一个String对象)
String str4 helloworld;//编译器确定为常量直接到常量池中引用
System.out.println(str2str3);//fasle
System.out.println(str2str4);//true
System.out.println(str3str4);//fasle从开头我们可以知道常量池保存的是在编译期间被确定一些数据这些数据绝对不能是变量因此我们可以很清楚的知道上面的这些例子为什么是这样的结果了。
2、关于String不可变
从开始我们就知道String类被final修饰因此我们把它当做是不可变对象它的值是同样被final修饰的字符数组
private final char value[];在java 中使用字符串最重要的一个规则必须记住一个字符串对象一旦被创建它的内容就是固定不变的 public static String str abc;这个声明会创建一个长度为3内容为abc的字符串对象您无法改变这个字符串对象的内容。
str 1111;不要以为这样就改变了字符串对象的内容,事实上。上面那段代码中产生了两个字符串对象一个是abc字符串对象长度为3一个是1111字符串对象长度为4两个不同的字符串对象。您不不是在abc 字符串改为1111字符串而是让str 引用名称从新引用1111字符串而不在引用abc 字符串但abc字符串在内存中还是存在的只是现在没有被引用。
String类为什么要被设置成不可变呢
安全 引发安全问题譬如数据库的用户名、密码都是以字符串的形式传入来获得数据库的连接或者在socket编程中主机名和端口都是以字符串的形式传入。因为字符串是不可变的所以它的值是不可改变的否则黑客们可以钻到空子改变字符串指向的对象的值造成安全漏洞。保证线程安全在并发场景下多个线程同时读写资源时会引竞态条件由于 String 是不可变的不会引发线程的问题而保证了线程。HashCode当 String 被创建出来的时候hashcode也会随之被缓存hashcode的计算与value有关若 String 可变那么 hashcode 也会随之变化针对于 Map、Set 等容器他们的键值需要保证唯一性和一致性因此String 的不可变性使其比其他对象更适合当容器的键值。 性能 当字符串是不可变时字符串常量池才有意义。字符串常量池的出现可以减少创建相同字面量的字符串让不同的引用指向池中同一个字符串为运行时节约很多的堆内存。若字符串可变字符串常量池失去意义基于常量池的String.intern()方法也失效每次创建新的 String 将在堆内开辟出新的空间占据更多的内存。