Java基础类库

Java字符串与内存管理

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

一定要知道这使用的是一个标识接口,表示能力。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值