一,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);
	}