一、StringBuffer类
在讲解StringBuffer类之前先来回顾一下String类特点:
- 一个字符串常量就是String类的匿名对象;
- String类对象有两种实例化方式:
|- 方式一:直接赋值,可以自动入池,只开辟一块内存空间;
|- 方式二:构造方法实例化,会开辟两块空间,其中一块将成为垃圾,不会自动入池,可以使用intern()方法手工入池;
- 字符串内容一旦声明则不可改变。
正是因为字符串内容一旦声明不可改变,所以如果在频繁修改字符串内容的程序上是不能够使用String的,那么为此在Java之中又提供有一个StringBuffer类,而String和StringBuffer类最大的特征在于:String不可改变内容,而StringBuffer可以改变。在String类对象之中使用“+”可以实现字符串连接操作,但是在StringBuffer类之中只能够使用append()方法实现字符串连接:public StringBuffer append(数据类型 b)。既然返回的是StringBuffer就可以一直使用append()连接。
范例:观察程序
public class TestDemo { public static void main(String[] args) throws Exception { // 方便处理 StringBuffer buf = new StringBuffer(); buf.append("Hello ").append("World ").append("!!!"); fun(buf); System.out.println(buf); } public static void fun(StringBuffer temp) { temp.append("\n").append("Hello MLDN ."); } }
此时在fun()方法之中对StringBuffer类对象所做的修改可以影响原始对象,所以就可以得出结论:StringBuffer的内容可以进行修改。
到此已经学习过了两个表示字符串的类:String、StringBuffer,那么这两个类有什么联系呢?
String类定义: |
StringBuffer类定义: |
public final class String extends Object implements Serializable, Comparable, CharSequence |
public final class StringBuffer extends Object implements Serializable, CharSequence |
发现String和StringBuffer两个类都是CharSequence接口的子类。那么就表示,如果见到某些方法上的参数或返回值类型是CharSequence,就应该立刻想到是字符串。
范例:利用CharSequence接收
public class TestDemo { public static void main(String[] args) throws Exception { // 方便处理 StringBuffer buf = new StringBuffer(); buf.append("Hello ").append("World ").append("!!!"); CharSequence csA = buf; // StringBuffer实现CharSequence CharSequence csB = "HELLO"; // String实现了CharSequence System.out.println(csA); System.out.println(csB); } }
但是虽然String和StringBuffer都是CharSequence接口的子类,可是这两个类的对象并不可能直接进行转型。但是在实际的工作之中毕竟直接使用String和StringBuffer这两子类居多。那么这两个类对象之间也可以进行转型。
1、将String变为StringBuffer;
· 利用StringBuffer类的构造方法:public StringBuffer(String str)。
利用StringBuffer类的append()方法:public StringBuffer append(String str)。
public class TestDemo { public static void main(String[] args) throws Exception { // 方便处理 StringBuffer bufA = new StringBuffer("Hello"); // String变为StringBuffer StringBuffer bufB = new StringBuffer(); bufB.append("World"); System.out.println(bufA); System.out.println(bufB); } }
2、将StringBuffer变为String
· 所有类对象都支持变为String方法:public String toString();
public class TestDemo { public static void main(String[] args) throws Exception { // 方便处理 StringBuffer bufA = new StringBuffer("Hello"); // String变为StringBuffer String str = bufA.toString(); // StringBuffer变为String System.out.println(str); } }
除了以上的基本特征之外,在StringBuffer类里面还提供有一些String类所没有的方法。
范例:执行数据插入
- 方法:public StringBuffer insert(int offset, 数据类型 l)。
public class TestDemo { public static void main(String[] args) throws Exception { // 方便处理 StringBuffer buf = new StringBuffer("Hello"); // String变为StringBuffer buf.insert(0, "你好 "); System.out.println(buf); } }
范例:删除部分数据
- 方法:public StringBuffer delete(int start, int end);
public class TestDemo { public static void main(String[] args) throws Exception { // 方便处理 StringBuffer buf = new StringBuffer("Hello World"); buf.delete(5, 11); System.out.println(buf); } }
范例:字符串反转
- 方法:public StringBuffer reverse()。
public class TestDemo { public static void main(String[] args) throws Exception { // 方便处理 StringBuffer buf = new StringBuffer("Hello World"); System.out.println(buf.reverse()); } }
扩展题目:请解释String和StringBuffer的区别?
- String和StringBuffer都是CharSequence接口的子类,其中String的内容不可以修改,而StringBuffer的内容可以修改;
- StringBuffer类提供了与String类互补的操作方法,例如:append()、insert()、reverse()。
工作之中使用最多的一定是String类,只有在频繁修改的时候才使用StringBuffer类。
在JDK 1.5之后Java增加了一个StringBuilder类,此类定义如下:
public final class StringBuilder
extends Object
implements Serializable, CharSequence
但是通过方法组成可以发现,StringBuilder和StringBuffer类的操作方法可以说是完全一样的。
范例:使用StringBuilder
public class TestDemo { public static void main(String[] args) throws Exception { // 方便处理 StringBuilder buf = new StringBuilder("Hello World"); System.out.println(buf.reverse()); } }
范例:比较一下String、StringBuffer、StringBuilder的区别
public class TestDemo { public static void main(String[] args) throws Exception { testString(); testStringBuffer(); testStringBuilder(); } public static void testString() { // 方便处理 long start = System.currentTimeMillis(); String str = ""; for (int x = 0; x < 20000; x++) { str += x; } long end = System.currentTimeMillis(); System.out.println("1、String花费:" + (end - start)); } public static void testStringBuffer() { // 方便处理 long start = System.currentTimeMillis(); StringBuffer str = new StringBuffer(); for (int x = 0; x < 200000; x++) { str.append(x); } long end = System.currentTimeMillis(); System.out.println("2、StringBuffer花费:" + (end - start)); } public static void testStringBuilder() { // 方便处理 long start = System.currentTimeMillis(); StringBuilder str = new StringBuilder(); for (int x = 0; x < 200000; x++) { str.append(x); } long end = System.currentTimeMillis(); System.out.println("2、StringBuffer花费:" + (end - start)); } }
可以发现,如果在频繁修改的情况下,String的性能是明显不足的,而StringBuffer和StringBuilder明显是高效的,但是以上的代码是在单线程情况下,如果是多线程共同操作,那么StringBuilder会更快,因为StringBuilder采用的是异步处理,而StringBuffer采用的是同步处理的,但是StringBuffer数据安全。
扩展题目:请解释String、StringBuffer、StringBuilder的区别?
- String不适合于字符串的频繁修改,而StringBuffer和StringBuilder适合于频繁修改,而且速度要远远高于String;
- StringBuffer是在JDK 1.0的时候引入的,而StringBuilder是在JDK 1.5的时候引入的,而且StringBuffer和StringBuilder继承结构相同,方法的组成也相同;
- StringBuffer中的所有方法都使用synchronized进行标记,都是同步方法,性能相对较低,而StringBuilder采用的异步处理,性能相对较高,但是StringBuffer在多线程操作时的数据安全性要高于StringBuilder。
二、Runtime类
在每一个JVM进程之中都会存在有一个Runtime类的实例化对象,此类对象将由JVM为用户提供好。但是为了保持只有一个Runtime类的实例化对象,所以Runtime类所采用的是一个单例设计模式,构造方法被私有化了。
既然此处是单例设计模式,那么一定会存在有一个static方法可以取得本类的实例化对象:
- 取得Runtime类对象:public static Runtime getRuntime();
当取得了Runtime类对象之后就可以通过如下的三个方法取得一些内存信息:
- 最大可用内存量:public long maxMemory();
- 可用内存量:public long totalMemory();
- 空闲内存量:public long freeMemory()。
- 清理垃圾空间:public void gc()。
范例:观察内存信息
public class TestDemo { public static void main(String[] args) { Runtime run = Runtime.getRuntime(); // 单例设计 System.out.println("1、MAX = " + run.maxMemory()); System.out.println("1、TOTAL = " + run.totalMemory()); System.out.println("1、FREE = " + run.freeMemory()); String str = ""; for (int x = 0; x < 30000; x++) { str += x; // 垃圾产生 } System.out.println("2、MAX = " + run.maxMemory()); System.out.println("2、TOTAL = " + run.totalMemory()); System.out.println("2、FREE = " + run.freeMemory()); run.gc(); System.out.println("3、MAX = " + run.maxMemory()); System.out.println("3、TOTAL = " + run.totalMemory()); System.out.println("3、FREE = " + run.freeMemory()); } }
扩展题目:请问什么是GC?如何操作?
- GC是垃圾收集器(Garbage Collector),是负责进行垃圾空间清理的线程;
- 在Java之中可以由JVM自动调用GC,或者是由用户调用Runtime类中的gc()方法手工清理垃圾。
三、System类
对于System类应该是很熟悉的,至少已经接触过两类方法:输出、数组排序,但是对于之前给出的数组排序方法名称并不标准:public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length)。此方法基本上不用。
在System类里面有一个方法是取得当前的日期时间:public static long currentTimeMillis()。此时取得的数据只是一个数值,并不是传统意义的年、月、日。
范例:观察currentTimeMillis()方法
public class TestDemo { public static void main(String[] args) throws Exception { // 方便处理 long start = System.currentTimeMillis(); String str = ""; for (int x = 0; x < 30000; x++) { str += x; // 垃圾产生 } long end = System.currentTimeMillis(); System.out.println("本操作所花费的时间:" + (end - start)); } }
在System类之中存在如下的一个方法:public static void gc()。但是此操作并不是一个新的操作,通过System类调用gc()就等价于调用了Runtime类的gc()方法,这两个gc()没有任何的区别。
思考?所谓的垃圾指的就是没有任何的栈内存所指向堆内存空间,但是在堆内存变为垃圾之前,一定会经历创建、使用的两个过程,最终的过程才成为垃圾。那么现在的疑问:一个对象产生的时候发现可以通过构造方法做一些对象产生的处理,但是一旦对象没用了,连个说最后一句话的机会都没有。
那么如果希望在对象被回收前可以进行一些处理操作,则可以覆写Object类之中的finliaze()方法:
- 方法:protected void finalize() throws Throwable;
虽然此时抛出了异常信息,但是不管抛出什么样的信息,最终都不会影响程序的执行。
class Person {
public Person() {
System.out.println("啊从出生了。");
}
@Override
protected void finalize() throws Throwable {
System.out.println("啊从因为得罪李老师,结果自杀了。");
throw new Exception("老子死的好嗨皮啊!");
}
}
public class TestDemo {
public static void main(String[] args) throws Exception { // 方便处理
Person per = new Person();
per = null;
System.gc();
}
}
面试题:请解释final、finally、finalize的区别?
- final:关键字,可以定义不能被继承的父类、定义不能被覆写的方法、常量;
- finally:关键字,异常处理的统一出口,不管是否有异常都执行;
- finalize:方法(protected void finalize() throws Throwable),Object类方法,当对象被清理前执行的收尾操作。
四、对象克隆
克隆指的就是对象的复制操作,在Object类里面提供有一个clone()方法:
- clone()方法:protected Object clone() throws CloneNotSupportedException;
要被克隆的对象所在的类需要去实现一个Cloneable接口。但是此接口里面没有包含任何的方法,所以此接口属于一个标识接口,表示一种能力。
class Person implements Cloneable {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone(); // 调用父类的对象克隆方法
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "姓名:" + this.name + ",年龄:" + this.age;
}
}
public class TestDemo {
public static void main(String[] args) throws Exception {
Person perA = new Person("张三", 20);
Person perB = (Person) perA.clone(); // 克隆
perB.setAge(90);
System.out.println(perA);
System.out.println(perB);
}
}
一定要知道这使用的是一个标识接口,表示能力。