欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Java基础类库

程序员文章站 2022-05-22 12:37:57
...

内容学习于:edu.aliyun.com


1. StringBuffer类

  String类是在所有项目开发之中一定会使用到的一个功能类,并且这个类拥有如下的特点:

  • 每一个字符串的常量都属于一个String类的匿名对象,并且不可更改;
  • String有两个常量池:静态常量池、运行时常量池;
  • String 类对象实例化建议使用直接赋值的形式完成,这样可以直接将对象保存在对象池之中以方便下次重用:
      虽然String类很好使用,但是如果认真去思考也会发现其最大的弊端:内容不允许修改,虽然大部分的情况下都不会涉及到字符串内容的频繁修改,但是依然可能会存在有这样的情况,所以为了解决此问题,专门提供有一个StringBuffer类可以实现字符串内容的修改处理。
      StringBuffer并不像String类那样拥有两种对象实例化方式,StringBuffer必须像普通类对象那样首先进行对象实例化,而后才可以调用方法执行处理,而这个时候可以考虑使用StringBuffer类中的如下方法:
  • 构造方法:public StringBuffer()
  • 构造方法:public StringBuffer(String str),可以接收字符串进行初始化
  • 追加方法:public StringBuffer append(数据类型 变量),相当于字符串的+操作

String类引用传递代码:

public class JavaAPIDemo {
    public static void main(String[] args) {
        String str = "Hello";
        change(str);
        System.out.println(str);
    }

    public static void change(String str) { 
        str += "World!!"; //内容并没有发生改变
    }
}

结果:

Hello

StringBuffer的引用传递代码:

public class JavaAPIDemo {
    public static void main(String[] args) {
        StringBuffer buf = new StringBuffer("Hello ");
        change(buf);
        System.out.println(buf);
    }

    public static void change(StringBuffer str) {
        str.append("World!!");
    }
}

结果:

Hello World!!

  实际上大部分的情况下,很少会出现有字符串内容的改变,这种改变指的并不是针对于静态常量池的改变。

分析下列代码:

public class JavaAPIDemo {
    public static void main(String[] args) {
        String strA = "www.mldn.cn";
        String strB = "www" + ".mldn" + ".cn";
        System.out.println(strA == strB);
    }
}

结果:

true

  这个时候的strB对象的内容并不算是改变,或者更加严格的意义上来讲,对于现在的strB当程序编译之后会变为如下的形式:

  • StringBuffer strB = new StringBuffer();→strB.append(“www”).append(".mldn").append(".cn");

  所有的“+”在编译之后都变为了StringBuffer 中的append()方法,并且在程序之中StringBuffer 与 String类之间本来就可以直接互相转换:

  • String类对象变为StringBuffer可以依靠StringBuffer类的构造方法或者使用append()方法;
    所有类对象都可以通过toString()方法将其变为String类型。

观察下列代码:

public class JavaAPIDemo {
    public static void main(String[] args) {
        String strA = "www.mldn.cn";
        StringBuffer strB = new StringBuffer();
        strB.append("www").append(".mldn").append(".cn");
        System.out.println(strA.equals(strB.toString()));
        System.out.println(strA == strB.toString());
    }
}

结果:

true
false

  Stringbuffer是动态字符串数组,append( )是往动态字符串数组添加,跟“xxxx”+“yyyy”相当‘+’号。
  跟String不同的是Stringbuffer是放一起的,String1+String2和Stringbuffer1.append(“yyyy”)虽然打印效果一样,但在内存中表示却不一样,String1+String2 存在于不同的两个地址内存,Stringbuffer1.append(Stringbuffer2)放在一起。

  String类中不具备的方法:

  • a.插入数据:public StringBuffer insert(int offset, 数据类型 变量)

插入操作代码:

public class JavaAPIDemo {
    public static void main(String[] args) {
        StringBuffer strB = new StringBuffer();
        strB.append(".cn").insert(0, "www.").insert(4, "mldn");
        System.out.println(strB);
    }
}

结果:

www.mldn.cn

  • b.删除指定范围内的数据:public StringBuffer delete(int start, int end)

删除指定范围代码:

public class JavaAPIDemo {
    public static void main(String[] args) {
        StringBuffer strB = new StringBuffer();
        strB.append("Hello World !").delete(6,12);
        System.out.println(strB);
    }
}

结果:

Hello !

  • c.字符串翻转:public StringBuffer reverse()

翻转代码:

public class JavaAPIDemo {
    public static void main(String[] args) {
        StringBuffer strB = new StringBuffer();
        strB.append("Hello World !").reverse();
        System.out.println(strB);
    }
}

结果:

! dlroW olleH

  实际上与StringBuffer类还有一个类似的功能类: StringBuilder 类,这个类是在JDK 1.5 的时候提供的,该类中提供的方法与StringBuffer功能相同,最大的区别在于StringBuffer 类中的方法属于线程安全的,全部使用了synchronized 关键字进行标注,而StringBuilder类属于非线程安全的。

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

String类是字符串的首选类型,其最大的特点是内容不允许修改;
StringBuffer与StringBuilder 类的内容允许修改;
StringBuffer 是在JDK 1.0 的时候提供的,属于线程安全的操作,而StringBuilder是在JDK 1.5 之后提供的,属于非线程安全的操作。

2. CharSequence接口

  CharSequence是一个描述字符串结构的接口,在这个接口里面一般发现有三种常用子类:

String类 StringBuffer类 StringBuilder类
public final class Stringextends Object implements Serializable, Comparable, CharSequence public final class StringBuffer extends Object implements Serializable, CharSequence public final class StringBuilder extends Object implements Serializable, CharSequence

  如下图所示:Java基础类库

  现在只要有字符串就可以为CharSequence接口实例化。

代码:

public class JavaAPIDemo {
    public static void main(String[] args) {
        CharSequence sequence = "www.mldn.cn";//子类实例向父接口转型
        System.out.println(sequence);
    }
}

结果:

www.mldn.cn

  CharSequence本身是一个接口,在该接口之中也定义有如下操作方法:

  • 获取指定字符:char charAt(int index)
  • 获取字符串的长度:int length()
  • 截取部分字符串:CharSequence subSequence(int start,int end)

字符串截取代码:

public class JavaAPIDemo {
    public static void main(String[] args) {
        CharSequence sequence = "www.mldn.cn";
        CharSequence sub = sequence.subSequence(4, 8);
        System.out.println(sub);
    }
}

结果:

mldn

  以后只要看见了CharSequence描述的就是一个字符串。

3. AutoCloseable接口

  AutoCloseable主要是用于日后进行资源开发的处理上,以实现资源的自动关闭(释放资源),例如:在以后进行文件、网络、数据库开发过程之中由于服务器的资源有限,所以使用之后一定要关闭资源,这样才可以被更多的使用者所使用。
  下面为了更好的说明资源的问题,将通过一个消息的发送处理来完成。

手工实现资源处理:

interface IMessage {
    void send();//消息发送
}

class NetMessage implements IMessage {//实现消息处理的机制
    private String msg;

    public NetMessage(String msg) {
        this.msg = msg;
    }

    public boolean open() {//获取连接资源
        System.out.println("【open】获取消息发送的资源");
        return true;
    }


    public void close() {
        System.out.println("【close】关闭消息发送的资源");
    }

    @Override
    public void send() {
        if (this.open())//是否打开了连接
            System.out.println("【发送消息】" + this.msg);
    }
}

public class JavaAPIDemo {
    public static void main(String[] args) {
        NetMessage nm = new NetMessage("www.mldn.cn");//定义要发送的消息
        nm.send();//消息发送
        nm.close();//关闭连接
    }
}

  此时有位设计师说了,既然所有的资源完成处理之后都必须进行关闭操作,那么能否实现一种自动关闭的功能呢?在这样的要求下,推出了AutoCloseable 访问接口,这个接口是在JDK 1.7的时候提供的,并且该接口只提供有一个方法: .

  • 关闭方法:void close() throws Exception

  如下图所示:Java基础类库

实现自动关闭操作代码:

interface IMessage extends AutoCloseable {//继承AutoCloseable类
    void send();//消息发送
}

class NetMessage implements IMessage {//实现消息处理的机制
    private String msg;

    public NetMessage(String msg) {
        this.msg = msg;
    }

    public boolean open() {//获取连接资源
        System.out.println("【open】获取消息发送的资源");
        return true;
    }

    @Override
    public void close() throws Exception {
        System.out.println("【close】关闭消息发送的资源");
    }

    @Override
    public void send() {
        if (this.open())//是否打开了连接
            System.out.println("【发送消息】" + this.msg);
    }
}

public class JavaAPIDemo {
    public static void main(String[] args) {
        try (IMessage nm = new NetMessage("www.mldn.cn")) {//需要通过try...catch实现自动关闭
            nm.send();//消息发送
        } catch (Exception e) {

        }
    }
}

结果:

【open】获取消息发送的资源
【发送消息】www.mldn.cn
【close】关闭消息发送的资源

4. Runtime类

  Runtime描述的是运行时的状态,也就是说在整个的JVM之中,Runtime 类是唯一个与JVM运行状态有关的类,并且都会默认提供有一个该类的实例化对象。
  由于在最每一个JVM进程里面只允许提供有一个Runtime类的对象,所以这个类的构造方法被默认私有化了,那么就证明该类使用的是单例设计模式,并且单例设计模式一定会提供有一个static方法获取本类实例。

  • 获取实例化对象:public static Runtime getRuntime()

  如下图所示:Java基础类库

  通过这个类中的availableProcessors()方法可以获取本机的CPU内核数: public int availableProcessors();

获取Runtime类对象代码:

public class JavaAPIDemo {
    public static void main(String[] args) {
        Runtime run = Runtime.getRuntime();//获取实例化对象
        System.out.println(run.availableProcessors());//获取CPU的内核数
    }
}

结果:

8

  但是除了以上的方法之外,在Runtime类里面还提供有以下四个重要的操作方法:

  • 获取最大可用内存空间:public long maxMemory(),默认配置为系统内存的四分之一
  • 获取可用内存空间:public long totalMemory(),默认配置为系统内存的64分之一
  • 获取空闲内存空间:public long freeMemory()
  • 手动进行GC处理:public void gc()

代码:

public class JavaAPIDemo {
    public static void main(String[] args) throws InterruptedException {
        Runtime run = Runtime.getRuntime();//获取实例化对象
        System.out.println("【1】MAX_MEMORY:" + run.maxMemory());
        System.out.println("【1】TOTAL_MEMORY:" + run.totalMemory());
        System.out.println("【1】FREE_MEMORY:" + run.freeMemory());
        String str = "";
        for (int i = 0; i < 30000; i++) {
            str += i;
        }
        System.out.println("【2】MAX_MEMORY:" + run.maxMemory());
        System.out.println("【2】TOTAL_MEMORY:" + run.totalMemory());
        System.out.println("【2】FREE_MEMORY:" + run.freeMemory());
        Thread.sleep(200);
        run.gc();

        System.out.println("【3】MAX_MEMORY:" + run.maxMemory());
        System.out.println("【3】TOTAL_MEMORY:" + run.totalMemory());
        System.out.println("【3】FREE_MEMORY:" + run.freeMemory());
    }
}

结果:

【1】MAX_MEMORY:2116026368
【1】TOTAL_MEMORY:134217728
【1】FREE_MEMORY:132158872
【2】MAX_MEMORY:2116026368
【2】TOTAL_MEMORY:398458880
【2】FREE_MEMORY:387975832
【3】MAX_MEMORY:2116026368
【3】TOTAL_MEMORY:10485760
【3】FREE_MEMORY:9182664

  面试题:请问什么是GC?如何处理?

GC (Garbage Collector)垃圾收集器,是可以由系统自动调用的垃圾释放功能,或者使用Runtime类中的gc0手工调用。

5. System类

  System类是一直陪伴着我们学习的程序类,之前使用的系统输出采用的就是System类中的方法,而后在System类 里面也定义有一些其它的处理方法: .

  • 数组拷贝:public static void arraycopy(Object src,int srcPos,Object dest,int destPos,int length)
  • 获取当前日期的数值:public static long currentTimeMillis()
  • 进行垃圾回收:public static void gc()

计算程序耗时时间代码:

public class JavaAPIDemo {
    public static void main(String[] args) throws InterruptedException {
        Runtime run = Runtime.getRuntime();//获取实例化对象
        long start = System.currentTimeMillis();
        String str = "";
        for (int i = 0; i < 30000; i++) {
            str += i;
        }
        long end = System.currentTimeMillis();
        System.out.println("该程序耗时:" + (end - start));//计算系统耗时
    }
}

结果:

该程序耗时:456

  在System类里面会发现也提供有一个gc()方法,但是这个gc()方法并不是重新定义的新方法,而是继续调用了Runtime类中的gc()操作( Runtime.getRuntime().gc())

6. Cleaner类(1.9之后)

  Cleaner是在JDK 1.9 之后提供的一个对象清理操作,其主要的功能是进行finialize()方法的替代。在c++语言里面有两种特殊的函数:构造函数、析构函数(对象手工回收),在Java里面所有的垃圾空间都是通过GC自动回收的,所以很多情况下是不需要使用这类析构函数的,也正是因为如此,所以 Java并没有提供这方面支持。
  但是Java本身依然提供了给用户收尾的操作,每一个实例化对象在回收之前至少给它一个喘息的机会,最初实现对象收尾处理的方法是Object类中所提供的finalize()方法,这个方法的定义如下:

  对象回收方法:

  • @Deprecated(since=“9”)
    protected void finalize()throws Throwable

  该替换指的是不建议继续使用这个方法了,但是说子类可以继续使用这个方法名称。这个方法上最大的特点是抛出了一个Throwable异常类型,而这个异常类型分为两个子类型: Error、 Exception, 平常所处理的都是Exception。

finalize的调用代码:

class Member {
    public Member() {
        System.out.println("在一个风雨交加的夜晚,出生了一个婴儿");
    }

    @Override
    protected void finalize() throws Throwable {
        System.out.println("人古有一死");
        throw new Exception("我不想死!!!!");//即使抛出了异常,也无法避免该对象的消亡
    }
}

public class JavaAPIDemo {
    public static void main(String[] args) throws InterruptedException {
        Member member = new Member();//对象诞生
        member = null;//该对象挂掉
        System.gc();//手动调用垃圾处理
        System.out.println("太阳照常升起");
    }
}

结果:

在一个风雨交加的夜晚,出生了一个婴儿
太阳照常升起
人古有一死

  但是从JDK 1.9开始,这一操作已经不建议使用了,而对于对象回收释放。从JDK 1.9开始建议开发者使用AutoCloseable或者使用java.lang.ref.Cleaner类进行回收处理(Cleaner 也支持有AutoCloseable 处理)。

新版的对象清除代码:

class Member implements Runnable {
    public Member() {
        System.out.println("【构造】在一个风雨交加的夜晚,出生了一个婴儿");
    }

    @Override
    public void run() {//执行清除的时候执行的是此操作
        System.out.println("【回收】人古有一死");
    }
}

class MemberCleaning implements AutoCloseable {
    private static final Cleaner CLEANER = Cleaner.create();//创建清除处理
    private Member member;
    private Cleaner.Cleanable cleanable;

    public MemberCleaning() {
        this.member = new Member();//创建新对象
        this.cleanable = this.CLEANER.register(this, this.member);//注册使用的对象
    }

    @Override
    public void close() throws Exception {
        cleanable.clean();//启动多线程
    }
}

public class JavaAPIDemo {
    public static void main(String[] args) throws InterruptedException {
        try (MemberCleaning mc = new MemberCleaning()) {
            //在这里可以执行一些代码操作
        } catch (Exception e) {

        }
    }
}

结果:

【构造】在一个风雨交加的夜晚,出生了一个婴儿
【回收】人古有一死

  在新一代的清除回收处理的过程之中,更多的情况下考虑的是多线程的使用,即:为了防止有可能造成的延迟处理,所以许多对象回收前的处理都是单独通过一个线程完成的。

  面试题:请解释final、finally、 finalize 的区别? (局限于 JDK 1.9 以前)

final是一个关键字主要定义不能够被继承的父类、不能够被覆写的方法、常量;
finally是一个关键字主要用于异常处理上,不管是否处理异常都要执行finally中的代码;
finliaze()是 Object类中所提供的一个方法,主要是在对象回收前进行收尾处理(一般不考虑),并且在JDK 1.9之后该方法已经默认被废除了,可以使用Cleaner使用多线程的模式来进行回收处理。

finally代码:

public class JavaAPIDemo {
    public static void main(String[] args) throws ParseException {
        System.out.println(get());
    }

    public static int get() {
        try {
            return 100;
        } finally {
            System.out.println("我还是执行了");
        }
    }
}

结果:

我还是执行了
100

  那怕是在方法返回处理的过程之中,finally 也一定要在返回前执行。

7. 对象克隆

  所谓的对象克隆指的就是对象的复制,而且属于全新的复制。即:使用已有对象内容创建一个新的对象,如果要想进行对象克隆需要使用到Object类中提供的clone()方法: protected Object clone() throws CloneNotSupportedException;
  所有的类都会继承Object父类,所以所有的类都一定会有clone()方法,但是并不是所有的类都希望被克隆。所以如果要想实现对象克隆,那么对象所在的类需要实现一个Cloneable接口,此接口并没有任何的方法提供,是因为它描述的是一种能力。

实现对象克隆代码:

class Member implements Cloneable {
    private String name;
    private int age;

    public Member(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "【" + super.toString() + "】" + "、name=" + this.name + "、age=" + this.age;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();//想要实现克隆,一定要覆写clone方法
    }
}


public class JavaAPIDemo {
    public static void main(String[] args) throws CloneNotSupportedException {
        Member memberA = new Member("团子", 30);
        Member memberB = (Member) memberA.clone();
        System.out.println(memberA);
        System.out.println(memberB);
    }
}

结果:

【aaa@qq.com】、name=团子、age=30
【aaa@qq.com】、name=团子、age=30

  如果在开发之中不是非常特别的需求下,很少会出现有对象克隆的需求。