一,String
String类是final类,也即意味着String类不能被继承,并且它的成员方法都默认为final方法。
String对象一旦被创建就是固定不变的了,相关的任何change操作都会生成新的对象,不影响原对象。
每当我们创建字符串常量时,JVM会首先检查字符串常量池,如果该字符串已经存在常量池中,那么就直接返回常量池中的实例引用。如果字符串不存在常量池中,就会实例化该字符串并且将其放到常量池中,常量池中一定不存在两个相同的字符串。
Java中的常量池,实际上分为两种形态:静态常量池和运行时常量池。
静态常量池,即*.class文件中的常量池,class文件中的常量池不仅仅包含字符串(数字)字面量,还包含类、方法的信息,占用class文件绝大部分空间。
运行时常量池,则是jvm虚拟机在完成类装载操作后,将class文件中的常量池载入到内存中,并保存在方法区中,我们常说的常量池,就是指方法区中的运行时常量池。
String c = new String(“abc”);
new关键字一定会产生一个对象abc,同时这个对象是存储在堆中。所以上面应该产生了两个对象:保存在栈中的abc和保存堆中abc。但是在Java中根本就不存在两个完全一模一样的字符串对象。故堆中的abc应该是引用字符串常量池中abc。
String s0="helloworld";
String s1="helloworld";
String s2="hello"+"world";
System.out.println(s0==s1); //true
System.out.println(s0==s2); //true
s0和s1中的"helloworld”都是字符串常量,它们在编译期就被确定了;当一个字符串由多个字符串常量连接而成时,它自己肯定也是字符串常量,
String s0 = "ab";
String s1 = "b";
String s2 = "a" + s1;
System.out.println((s0 == s2)); // false
JVM对于字符串引用,由于在字符串的"+“连接中,有字符串引用存在,而引用的值在程序编译期是无法确定的,即"a” + s1无法被编译器优化,只有在程序运行期来动态分配并将连接后的新地址赋给s2。所以上面程序的结果也就为false。
String s0 = "ab";
final String s1 = "b";
String s2 = "a" + s1;
System.out.println((s0 == s2)); // true
对于final修饰的变量,它在编译时被解析为常量。所以此时的"a" + s1和"a" + "b"效果是一样的。
String s1 = "Hello world";
String s2 = s1.replace("world", "abc");
s1.replace()的调用将创建一个新的字符串"Hello abc",并返回该对象的引用。如果没有其他引用指向原有字符串"Hello world",原字符串对象将被垃圾回收。
当字符串相加操作或者改动较少的情况下,建议使用 String str="hello"这种形式;
当字符串相加操作较多的情况下,建议使用StringBuilder,如果采用了多线程,则使用StringBuffer。
intern()方法会首先从常量池中查找是否存在该常量值,如果常量池中不存在则在常量池中创建,如果已经存在则直接返回。
二,创建对象的几种方式
用new关键字创建
调用对象的clone方法
User u4 = (User) u3.clone();
利用反射,调用Class类的或者是Constructor类的newInstance()方法
User u = (User) Class.forName("com.yu.User").newInstance();
Constructor<User> c = User.class.getConstructor();
User u3 = c.newInstance();
用反序列化,调用ObjectInputStream类的readObject()方法
三,RandomAccessFile
RandomAccessFile是Java中输入,输出流体系中功能最丰富的文件内容访问类,与普通的IO流相比,它最大的特别之处就是支持任意访问的方式,程序可以直接跳到任意地方来读写数据。
如果我们只希望访问文件的部分内容,而不是把文件从头读到尾,使用RandomAccessFile将会带来更简洁的代码以及更好的性能。
RandomAccessFile raf = new RandomAccessFile("D:\\work\\test.txt", "r");
//getFilePointer() --返回文件记录指针的当前位置
System.out.println("指针的初始位置:" + raf.getFilePointer());
raf.seek(6); //移动文件指针位置
byte[] buff = new byte[1024];
int hasRead = 0;
while((hasRead = raf.read(buff))>0) {
System.out.println(new String(buff,0 ,hasRead));
}
raf.close();
写
RandomAccessFile raf = new RandomAccessFile("D:\\work\\test.txt", "rw");
//将记录指针移到文件最后
raf.seek(raf.length());
raf.write("\r\n新增的内容".getBytes());
raf.close();
任意位置插入数据
File tmp = File.createTempFile(“tmp”, null);
tmp.deleteOnExit(); //在JVM退出时删除
RandomAccessFile raf = new RandomAccessFile("D:\\work\\test.txt", "rw");
//创建一个临时文件来保存插入点后的数据
FileOutputStream fos = new FileOutputStream(tmp);
FileInputStream fis = new FileInputStream(tmp);
raf.seek(33);
//将插入点后的内容读入临时文件中
byte[] buff = new byte[1024];
//用于保存临时读取的字节数
int hasRead = 0;
while((hasRead = raf.read(buff))>0) {
//将读取的数据写入临时文件中
fos.write(buff,0,hasRead);
}
//插入需要指定添加的数据
raf.seek(33); //返回原来的插入处
//追加需要追加的内容
raf.write("谢谢你".getBytes());
//最后追加临时文件中的内容
while((hasRead = fis.read(buff))>0) {
raf.write(buff,0,hasRead);
}