0 基础准备
0-1 基本类型
基本类型的值就是一个数字,一个字符或一个布尔值,八种基本数据类型boolean,char,byte,short,int,long,float,double都是基本类型。
0-2 引用类型
引用类型是一个对象类型,它的值是指向内存空间的引用,就是地址,所指向的地址保存着变量的值,主要有:类,接口,数组。
1 Java String
1-1 Java中String不是基本数据类型,而是一种特殊的类,String类内部成员有一个被final修饰的char[],数组类型是引用类型,被final修饰后指的的value指向的字符数组首地址不可变,但是字符数组的内容是可以更改的。
private final char value[];
因为value数组是被修饰成private final,所以我们在用String类的时候,是无法对其进行修改的。从String类的设计角度来讲,由于String类中没有提供对value本身的修改,这样来讲,String的内容是不可变的。但是我们可以反射出String对象的value属性,设置为可以访问,进而改变value所指向的数组的内容进行修改,但是一般不这样做,这样做就破坏了jdk的封装。
public class StringTest {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
String str1 = "hzw";
String str2 = "hzw";
String strObj1 = new String("hzw");
String strObj2 = new String("hzw");
System.out.println("****** Testing Object ******");
System.out.println("str1 == str2 ?" + (str1==str2));
System.out.println("strObj1 == strObj2 ?" + (strObj1 == strObj2));
System.out.println("str1 == strObj1 ?" + (str1 == strObj1));
String str3 = "hello world!";
System.out.println("str3 = " + str3);
Field valueFieldOfString = String.class.getDeclaredField("value");
//更改value属性的访问权限
valueFieldOfString.setAccessible(true);
//获取str3对象的value属性的值
char[] value = (char[])valueFieldOfString.get(str3);
value[5] = '_';
System.out.println("str3 reflect == " + str3);
}
}
上面代码块执行结果如下:
分析执行结果:
String类型对象直接赋值和new操作符产生的结果在JVM内存分配过程中是不同的:
String str1 = "hzw";//首先在JVM栈内存中创建一个String类的变量str1,然后通过引用去字符串常量池里去找有没有"hzw",如果有"hzw",则直接将引用str1指向"hello",没有"hello",则在常量池中创建"hello",并将引用指向该"hello" 产生一个引用和一个对象
String str2 = "hzw";//由于上面已经字符串常量池中创建了"hzw"对象,这里只在栈内存中创建引用str2,指向常量池的字符串对象
String strObj1 = new String("hzw");//首先会在栈内存中创建一个String的类型引用变量strObj1,new()操作会在jvm的heap内存中创建一个新的"hzw"对象,并将strObj1引用指向堆内存的对象,同时检查String pool常量池中是否有"hzw",如果没有也产生一个"hzw"对象,如果有则不产生。 只需要产生1个对象及1个引用
String strObj2 = new String("hzw");//因为new每次都会保证在heap堆内存中产生新的对象,并将栈中的引用指向对应的堆中的地址.
1-2 String的intern的方法
因此a.intern(),b.intern(),newA.intern(),newB.intern()隐含的各自在栈中分配了各自的内存区域,同时都将栈中的引用用全部指向了String pool常量池中的同一块区域"hello"
intern方法,如果池中包含等于(equal)此对象的字符串,会返回常量池中的字符串,如果不存在,则返回String对象的引用
1-3 length
public int length() {
return value.length;
}
返回字符数组的length
1-4 isEmpty
public boolean isEmpty() {
return value.length == 0;
}
判断字符数组的长度是否为0,为0方法返回true
1.5 charAt
/**
* 判断字符串长度是否为0或位序是否大于等于字符串长度,如果是,就会发生字符数组越界的异常
* @param index 位序
* @return 返回指定位序的字符
*/
public char charAt(int index) {
if ((index < 0) || (index >= value.length)) {
throw new StringIndexOutOfBoundsException(index);
}
return value[index];
}
1.6 codePointAt
/**
* codePointAt 返回指定位序字符的unicode code
* @param index 位序
* @return 指定位序上的字符的unicode码
*/
public int codePointAt(int index) {
if ((index < 0) || (index >= value.length)) {
throw new StringIndexOutOfBoundsException(index);
}
return Character.codePointAtImpl(value, index, value.length);
}
/**
* 执行字符数组拷贝,从dst数组的dstBegin位置,将当前字符串拷贝到词,且不执行范围检查,底层调用的是System.arraycopy方法
* @param dst
* @param dstBegin
*/
void getChars(char dst[], int dstBegin) {
System.arraycopy(value,0, dst, dstBegin, value.length);
}
1.8 getBytes
/**
* 获得String对象的指定编码的字节数组,底层使用的是StringCoding.encode方法
* @param charsetName java支持的字符编码
* @return 以charsetName指定的编码,编码字符串返回的字节数组
* @throws UnsupportedEncodingException 不是Java支持的编码格式抛出此异常
*/
public byte[] getBytes(String charsetName)
throws UnsupportedEncodingException {
if (charsetName == null) throw new NullPointerException();
return StringCoding.encode(charsetName, value, 0, value.length);
}
1.9 equals,equalsIgnoreCase是忽略大小写的String内容比较
/**
* equals方法用于比较两个字符串对象的值是否相同,底层是用的字符等于来实现
* @param anObject 比较对象
* @return 相同返回true
*/
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;
}
1.8 contentEquals
/**
* String对象和StringBuffer对象的内容比较
* @param sb
* @return 相同返回true
*/
public boolean contentEquals(StringBuffer sb) {
return contentEquals((CharSequence)sb);
}
1.9 caseInsensitiveComparator(忽略大小写的字符串比较器)
private static class CaseInsensitiveComparator
implements Comparator<String>, java.io.Serializable {
// use serialVersionUID from JDK 1.2.2 for interoperability
private static final long serialVersionUID = 8575799808933029326L;
public int compare(String s1, String s2) {
int n1 = s1.length();
int n2 = s2.length();
int min = Math.min(n1, n2);
for (int i = 0; i < min; i++) {
char c1 = s1.charAt(i);
char c2 = s2.charAt(i);
if (c1 != c2) {
c1 = Character.toUpperCase(c1);
c2 = Character.toUpperCase(c2);
if (c1 != c2) {
c1 = Character.toLowerCase(c1);
c2 = Character.toLowerCase(c2);
if (c1 != c2) {
// No overflow because of numeric promotion
return c1 - c2;
}
}
}
}
return n1 - n2;
}
/** Replaces the de-serialized object. */
private Object readResolve() { return CASE_INSENSITIVE_ORDER; }
}
1.10 startWith
/**
* 判断toffset位序开始的子字符串是否以prefix为前缀,startwith不传偏移量的实现是startWith(prefix,0);
* @param prefix 前缀
* @param toffset 偏移
* @return 是否以前缀开头
*/
public boolean startsWith(String prefix, int toffset) {
char ta[] = value;
int to = toffset;
char pa[] = prefix.value;
int po = 0;
int pc = prefix.value.length;
// Note: toffset might be near -1>>>1.
if ((toffset < 0) || (toffset > value.length - pc)) {
return false;
}
while (--pc >= 0) {
if (ta[to++] != pa[po++]) {
return false;
}
}
return true;
}
1.11 endsWith
/**
* endsWith的实现是startsWith
* @param suffix 后缀
* @return 从指定的位序判断是否已suffix结尾
*/
public boolean endsWith(String suffix) {
return startsWith(suffix, value.length - suffix.value.length);
}