一、数组
数组初始化
java声明数组的时候不能指定其长度(元素的个数),一旦初始化完成,就不能改变了。
public class Test {
public static void main(String[] args) throws Throwable{
int[] a = {1,2,3};//静态
System.out.println(a[0]);// 1
int[] b = new int [4];//动态
b[3]= 20;
System.out.println(b[3]);// 20
}
}
二、String类
2.1 认识hashCode
hashCode()是一个对象在堆中的内存地址通过hash算法进行了进制的转换得到int返回值。
如果两个对象的hashCode()值一样则他们是同一个对象。
hashCode()的作用:
1. 一个对象是否存在。
2. 两个对象是否是同一个对象。
3. 两个对象的hashCode值比较相当于引用之间使用"==",只比较地址,不比较值。
2.2 认识hash
翻看String中源码:
初始为0
private int hash; // Default to 0
public String(String original) {
this.value = original.value;
this.coder = original.coder;
this.hash = original.hash;
}
调用hashcode后,才会给对象的hash属性赋值:
public int hashCode() {
int h = hash;
if (h == 0 && !hashIsZero) {
h = isLatin1() ? StringLatin1.hashCode(value)
: StringUTF16.hashCode(value);
if (h == 0) {
hashIsZero = true;
} else {
hash = h;
}
}
return h;
}
2.3 关于 String str = “abc”;(摘)
String str = “abc”;
编译的时候,编译器只会检查"="两边是否是同一类型(或者是多态),如果是,编的过程就结束了,译的过程中发现,有"abc"这样的字面量,它会把它看成是常量,放入到常量表中。
运行的时候,首先进行类加载,将源程序的字节码文件中的内容加载到方法区内存中,这时候JVM就会去检查有没有特殊的要处理的内容,如果有static修饰的变量,static代码块,常量等等,就会在类加载的时候先执行,执行完以后存入方法区内存,常量存入常量池中。
在类加载完以后,程序开始运行,执行到 String str = “abc”;的时候,将常量池中的"abc"的内存地址赋值给引用类型String str。这个String str是在栈内存空间中分配空间,用来存储"abc"的内存地址。从此,str所代表的内存空间中存放的是"abc"在方法区内存中的常量池中的内存地址。
2.4 关于 new String(String original)
在new的时候,字符串类型的实参在堆里面有,常量池里面也有。
String类的new String(String original)构造方法的实现:
public String(String original) {
this.value = original.value;
this.hash = original.hash;
}
String类有两个成员属性:
private final char value[];
private int hash; //这里是默认为0
// 老师讲解的正常写法应该是这样的:
public String(char[] value, int hash) {
this.value = value;
this.hash = hash;
}
public String(String original) {
this.value = original.value;
this.hash = original.hash;
}
这里的this.value = original.value;
original.value是original对象中数组的地址。
String str = new String("hello");
构造方法中的形式参数:String original
调用时传入的实际参数:"hello"
String original = "hello";
1、字面量"hello"是一个常量,在方法区内存中的字符串常量池中。
2、字符串常量池中存放的字符串是“对象”形式存放的。
3、String是引用类型,它表示字符串这一类对象。
2.5 字节数组和String的相互转换问题
public class Test {
public static void main(String[] args) throws Throwable{
String str1 = "2021牛年!";
byte[] strBytes = str1.getBytes("GBK");//字符串打成GBK形式的字节流
for (int i = 0; i < strtoBytes.length; i++) {
System.out.println("0x" + Integer.toHexString(strtoBytes[i]));
}
System.out.println();
/*0x32
0x30
0x32
0x31
0xffffffc5
0xffffffa3
0xffffffc4
0xffffffea
0xffffffa3
0xffffffa1
*/
String string = new String(strtoBytes, "GBK");//又变回字符串
System.out.println(string);// 2021牛年!
byte[] bytes = new byte[5];
for (int i = 0; i < bytes.length ; i++) {
bytes[i] = (byte)(96+2);
}
String ter = new String(bytes, "utf8"); //字节流变回字符串
System.out.println(ter);// bbbbb
}
}
2.6 StringBuilder 和 StringBuffer
2.6.1 String VS StringBuilder
1、String是不可变类,一旦String对象被创建,包含在对象中的字符序列是不可变的,直到对象被销毁;
StringBuffer 与 StringBuilder对象则是可变的。
2、String和StringBuilder的底层都是char数组,String的char[] 是final修饰的,只能被赋值一次
StringBuilder的char[]数组不被final修饰。
2.6.2 StringBuilder的构造方法
1. StringBuilder源码:
public final class StringBuilder
extends AbstractStringBuilder //父类是AbstractStringBuilder
implements java.io.Serializable, Comparable<StringBuilder>, CharSequence
{xxx}
// 无参构造
public StringBuilder() {
super(16);
}
char[] value数组的初始容量是:16
2. 有参构造直接调用了父类的
public StringBuilder(int capacity) {
super(capacity);
}
父类的有参构造:
public AbstractStringBuilder(int capacity) {
if (COMPACT_STRINGS) {
value = new byte[capacity];
coder = LATIN1;
} else {
value = StringUTF16.newBytesFor(capacity);
coder = UTF16;
}
}
3. 字符串的拼接
// String类型的对象作为参数
public StringBuilder append(String str) {
super.append(str);
return this;
}
public StringBuilder(String str) {
super(str.length() + 16);
append(str); //字符串长度应该是传入字符串长度+16
}
最后使用StringBuilder中的toString方法打印出结果
@Override
public String toString() {
return new String(value, 0, count);// value.length = 16
}
// 这个方法被调用以后,方法区内存中的字符串常量池里才会有一个String常量。
输出“abc niu”:
public static void main(String[] args){
StringBuilder strb = new StringBuilder("abc");
strb.append(" ");
strb.append("n");
strb.append("i");
strb.append("u");
// abc, ,n, i, u, abc niu
System.out.println(strb);//abc niu
String str = "abc";
str = str + 'n';
str = str + 'i';
str = str + 'u';
// abc, abc , abc n,abc ni,abc niu
System.out.println(strb);//abc niu
}
可以看见,采用string浪费空间。
而相比之下,StringBuilder可以节省空间
因为StringBuilder和StringBuffer底层有一个字符缓存数组
追加的字符串,不会直接放入字符串常量池中,会先放入堆中的缓存字符数组中。
直到调用toString方法才将最后的结果放入常量池中,这也是StringBuilder为什么优于String,不会随便在常量池中占位置。
注意:只有String类型的才存放到常量池中。
StringBuilder和StringBuffer,不是String类型的。
2.6.3 StringBuffer
StringBuffer: 是线程安全的,效率低,字符序列是可变的(通过append等方法操作);
StringBuilder: 是线程不安全的,效率高点;
1、StringBuffer 和 String之间的转换;
String toString() 返回此序列中数据的字符串表示形式。
public static void main(String[] args){
StringBuffer strbf = new StringBuffer("abc");
System.out.println(strbf.toString());
}
StringBuffer(String str):以指定的字符串创建StringBuffer对象。
2、StringBuffer方法:
StringBuffer insert(int offset, Object o) :将任意类型参数的字符串表示形式插入此序列中。
StringBuffer delete(int start, int end) :移除此序列的子字符串中的字符。
StringBuffer deleteCharAt(int index): 移除此序列指定位置的 char。
总结:String完结。。。。。。