[TOC]

一. StringBuffer类

1. StringBuffer

[回顾]String类的特点:

  • Stirng类对象有两种实例化方式:
    • 直接赋值: 只开辟一块堆内存空间,可以自动入池
    • 构造方法: 开辟两块堆内存空间,不会自动入池,使用intern()手工入池;
  • 任何一个字符串都是String类的匿名对象;
  • 字符串一旦声明则不可改变,可以改变的只是String类对象的引用。

String类的内容不可改变,为此Java里面提供了另外一个类————StringBuffer类(里面的内容可以修改)
String类可以通过"+"进行字符串的连接操作,但是在StringBuffer类里面必须使用append()方法进行追加。

1
2
3
4
5
6
7
8
9
10
11
12
13
package cn.ngp.demo;
public class TestDemo { //主类
public static void main(String[] args) throws Exception {
//String类可以直接赋值实例化,但是StringBuffer类不行
StringBuffer buf = new StringBuffer();
buf.append("Hello").append("World").append("!!");
change(buf); //引用传递
System.out.println(buf);
}
public static void change(StringBuffer temp) {
temp.append("\n").append("Hello NGP");
}
}

发现StringBuffer类的内容是可以进行修改的,而String的内容是不可以修改的。

2. CharSequence接口

String类 和 StringBuffer类 都是CharSequence接口的子类。而以后的开发之中,如果你看见某些方法的操作上出现的
是CharSequence接口,那么应该立刻清楚,只需要传递字符串即可。

1
2
3
4
5
6
7
package cn.ngp.demo;
public class TestDemo { //主类
public static void main(String[] args) throws Exception {
CharSequence seq = "Hello" ;
System.out.println(seq);
}
}

3. String和StringBuffer类, 不能够直接转换

虽然String和StringBuffer类有着共同的接口,但是这两个类对象之间如果要转换不能够直接转换。

(1) 将 String变为StringBuffer类对象?

方式一: 利用StringBuffer类的构造方法(public StringBuffer(String str))

1
2
3
4
5
6
7
package cn.ngp.demo;
public class TestDemo { //主类
public static void main(String[] args) throws Exception {
StringBuffer buf = new StringBuffer("Hello"); //String变为StringBuffer
System.out.println(buf);
}
}

方式二: 利用append()方法(public StringBuffer append(String str))

1
2
3
4
5
6
7
8
package cn.ngp.demo;
public class TestDemo { //主类
public static void main(String[] args) throws Exception {
StringBuffer buf= new StringBuffer();
buf.append("Hello"); //String变为StringBuffer
System.out.println(buf);
}
}
(2) 将StringBuffer变为String

方式一: 利用toString()方法可以将StringBuffer转化为String

1
2
3
4
5
6
7
8
package cn.ngp.demo;
public class TestDemo { //主类
public static void main(String[] args) throws Exception {
StringBuffer buf= new StringBuffer("Hello");
String str = buf.toString(); //StringBuffer变为String
System.out.println(str);
}
}

也可以利用String类的构造方法(public String(StringBuffer buffer))实现StringBuffer变为String类

(3) String类 与 StringBuffer比较的方法

String类里面有提供和StringBuffer比较的方法:public boolean contentEquals(StringBuffer sb)(大小写敏感)

1
2
3
4
5
6
7
package cn.ngp.demo;
public class TestDemo { //主类
public static void main(String[] args) throws Exception {
StringBuffer buf = new StringBuffer("Hello");
System.out.println("Hello".contentEquals(buf));
}
}

4. StringBuffer类的方法(与String类互补)

4.1. 字符串反转:public StringBuffer reverse();
1
2
3
4
5
6
7
   package cn.ngp.demo;
public class TestDemo { //主类
public static void main(String[] args) throws Exception {
StringBuffer buf = new StringBuffer("12345");
System.out.println(buf.reverse());
}
}
4.2. 指定位置添加数据:public StringBuffer insert(int offset,数据类型 变量);
1
2
3
4
5
6
7
8
package cn.ngp.demo;
public class TestDemo { //主类
public static void main(String[] args) throws Exception {
StringBuffer buf = new StringBuffer("Hello");
buf.insert(5, " Java ").insert(0, " NGP ");
System.out.println(buf);
}
}
4.3. 删除部分数据:public StringBuffer delete(int start ,int end)
1
2
3
4
5
6
7
package cn.ngp.demo;
public class TestDemo { //主类
public static void main(String[] args) throws Exception {
StringBuffer buf = new StringBuffer("Hello World");
System.out.println(buf.delete(0, 5));
}
}

5. StringBuffer总结

从JDK1.5之后增加了一个新的字符串操作类: StringBuilder类,这个类定义结构如下

1
2
3
public final class StringBuilder
extends Object
implements Serializable, CharSequence

发现StringBuffer类与StringBuilder类在定义上非常的相似,几乎连方法也一样。
文档找不到区别,于是,找源代码

面试题: 请解释String、StringBuffer、StringBuilder的区别?

  • String的内容一旦声明不可改变,而StringBuffer和StringBuilder声明的内容可以改变。
  • StringBuffer类中提供的方法都是同步方法,属于安全的线程操作,二StringBuilder类提供的方法属于非线程安全的操作。

日后在开发之中,如果见到了字符串的应用,不需要思考95%使用的都是String类,只有在需要频繁修改的时候才会考虑到StringBuffer或者是StringBuilder类操作。

总结:

  • String类依然是最常用的字符串描述类,而 StringBuffer类由于出现时间较长,
    所以使用要比StringBuilder多。(习惯问题)

二. Runtime类(了解)

    在每一个JVM进程里面都会存在有一个Runtime类的对象,这个类的主要功能是取得一些与运行时有关的环境属性或者创建新的进程操作。
    在Runtime类定义的时候它的构造方法已经被私有化了,这就属于单例设计模式的应用,因为要保证在整个进程里面只有唯一的一个Runtime类的对象,所以在Runtime类里面提供有一个static的方法,这个方法可以取得Runtime类的实例化对象: public static Runtime getRuntime()。
    Runtime类是直接与本地运行有关的所有相关属性的集合,所以在Runtime类里面定义有如下方法:

  • 返回所有可用内存空间: public long totalMemory();
  • 返回最大可用内存空间: public long maxMemory();
  • 返回空余内存空间: public long freeMemory();

范例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package cn.ngp.demo;
public class TestDemo {
public static void main(String[] args) {
Runtime run = Runtime.getRuntime();// 取得Runtime类实例化对象;
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 i = 0; i < 2000; i++) {
str += i;// 制造垃圾
}
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)垃圾收集器,指的是释放无用的内存空间。
  • GC 会由系统不定期进行自动的回收,或者调用Runtime类中的gc()方法手工回收.
    实际上Runtime类还有一个更加有意思的功能,就是说它可以调用本机的可执行程序,冰且创建进程

范例:

1
2
3
4
5
6
7
8
9
package cn.ngp.demo;
public class TestDemo {
public static void main(String[] args) throws Exception{
Runtime run=Runtime.getRuntime();
Process pro=run.exec("mspaint.exe");//调用本机画图程序
Thread.sleep(2000);
pro.destroy();//销毁进程
}
}

总结:

  1. Runtime类使用了单例设计模式,必须通过内部的getRuntime()方法才可以取得Runtime类的对象;
  2. Runtime类提供了gc()方法,可以用于手工释放内存。

三. System类(了解)

    之前一直使用的"System.out.println()"就属于System类的操作功能,只不过这个功能由于牵扯到 IO 部分,所以留到以后继续讲解。

    在System类里面之前也使用过一个System.arraycopy()方法实现数组拷贝,而这个方法的真是定义如下:

  • 数组拷贝: public static void arraycopy(Object src,int srcPos,Object dest,int destPos,int length);

    在System类里面定义有一个重要的方法:

  • 取得当前系统的时间: public static long currentTimeMillis();
1
2
3
4
5
6
7
8
9
10
11
12
package cn.ngp.demo;
public class TestDemo {
public static void main(String[] args) {
long start = System.currentTimeMillis();
String str = "";
for (int i = 0; i < 2000; i++) {
str += i;
}
long end = System.currentTimeMillis();
System.out.println("本次操作消耗的时间是:" + (end - start));
}
}

    如果想要统计出所花费的毫秒时间,就用long型数据进行数学计算后得来。
    在System类里面定义了一个操作方法: public static void gc(),这个gc方法并不是一个新定义的方法,而是连一个收尾的机会都没有,如果需要给对象一个收尾的机会,那么就可以考虑覆写Object类中的finalize()方法完成。

  • finalize()方法: protected void finalize() throws Throwable
    • 在对象回收是就算抛出了任何的异常,也不会影响到整个程序的正常执行。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package cn.ngp.demo;
class Message {
public Message() {
System.out.println("灰太狼来啦");
}

@Override
protected void finalize() throws Throwable {
System.out.println("灰太狼被赶走啦");
throw new Exception("我还会回来的!");
}
}
public class TestDemo {
public static void main(String[] args) {
Message msg = new Message(); //会出现一些出生时的辅助操作
msg = null; //会产生垃圾
System.gc(); //手工处理垃圾
}
}

    构造方法是留给对象初始化时使用的,而finalize()方法是留给对象回收前使用的。
面试题: 请解释 final、finally、finalize 的区别?

  • final : 关键字,定义不能被继承的类、不能被覆写的方法、常量;
  • finally : 关键字,异常的统一出口;
  • finalize : 方法,Object类提供的方法(protected void finalize() throws Throwable),指的是对象回收前的收尾方法,技术出现了异常也不会导致程序中断执行。

总结:

  1. System类可以使用currentTimeMillies()方法取得当前的系统时间;
  2. System类中的gc()方法就直接调用了 “Runtime.getRuntime().gc()” 方法。

四. 对象克隆

    对象克隆指的就是对象的复制操作,在Object类里面提供有一个专门的克隆方法:

  • 对象克隆: protected Object clone() throws CloneNotSupportedException
        此方法上抛出一个 “CloneNotSupportedException” 异常,如果要使用对象克隆的类没有实现Cloneable接口,那么就会抛出此异常。但是Cloneale接口看不见方法,此为标识接口,表示一种操作能力。
    范例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package cn.ngp.demo;
class Book implements Cloneable{ //此类的对象可以被克隆
private String title;
private double price;

public Book(String title, double price) {
this.title = title;
this.price = price;
}
public void setTitle(String title) {
this.title = title;
}
@Override
public String toString() {
return "书名《" + this.title + "》价格:" + this.price + "元。";
}
//由于此类需要对象克隆操作,所以才需要进行对象的覆写
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone(); //调用父类的克隆方法
}
}
public class TestDemo {
public static void main(String[] args) throws Exception {
Book booka = new Book("Java开发", 88.6);
Book bookb = (Book) booka.clone();
bookb.setTitle("C++开发");
System.out.println(booka);
System.out.println(bookb);
}
}

    对象克隆的操作,理论价值大于实际价值,因为在实际的工作里面很少会用到对象克隆操作。重点在于标识接口上,以后依然会见到没有方法的接口,这样的接口就好比通行证一样,表示的是能力。
总结:
    标识接口: 没有任何方法定义,只是一个空接口的声明。

参考

来自: 阿里云大学(笔记) → 零基础学Java10系列三:Java高级编程