java基础之Object类解析(二)

java基础之Object类解析(二)

1. 导读

    接上一篇的分享, 我们一起看一下Object类中剩余的6个方法:
    1.1 toString();
    1.2 notify();
    1.3 notifyAll();
    1.4 wait();
    1.5 finalize();
    1.6 registerNatives();
复制代码

2. toString方法

    toString方法是我们比较常用的方法, 在Object中的默认实现返回一个
    类名+'@'+hasCode的16进制拼接的字符串;
复制代码
    public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }
复制代码
    注意到toString方法是没有被final修饰的, 证明他可以被子类重写;
    
    划重点:
    .1 我们可以根据自身需求重写toString方法,
    默认实现是返回类名+'@'+hashCode的16进制拼接而成的字符串;
    .2 我们调用System.out.println(obj), 默认调用的就是obj的toString方法;
复制代码

3. notify, notifyAll 和 wait方法

    把这三个方法放一起是因为这三个方法是配套的, 用来实现JAVA多线程的协作;
    既然是多线程相关的方法, 为什么会在Object这个类中呢? 
    这个是因为调用这三个方法前提都是需要在synchronized修饰的同步代码块中, 
    而synchronized锁的实现是基于对象(Object)监视器的;
    那么这三个方法之间是如何协作的? 每个方法又具体干了什么呢?
    3.1 wait: 在同步代码块中调用该方法时, 当前线程立即释放锁并等待,
    直到有其他线程调用notify/notifyAll或超时等待时, 才会去再次竞争锁, 
    成功后继续执行下面的逻辑;
复制代码
    public final native void wait(long timeout) throws InterruptedException;
复制代码
    wait方法被final, native修饰, 证明他是不可被重写的原生方法; 该方法在等待的时候,
    有其他线程打断了他的等待, 那么他会抛出InterruptedException并退出等待;
    Object类中还有wait(),  wait(long timeout, int nanos)这两种wait的实现, 
    但其实都是调用的wait(long timeout);
    
    3.2 notify: 线程A在同步代码块中调用该方法时,
    会随机地唤醒一个等待在该对象锁上的线程B, 注意这时候唤醒的线程B还没有持有锁, 
    必须要等到线程A释放锁后才能持有该把锁;
    当线程在A对象的同步代码块中执行B对象的notify时, 
    会抛出IllegalMonitorStateException;如果没有这个限制, 我们想想会发生什么情况;
    拿最常用的生产者消费者举例:
    1. 消费者消费(notify生产者);
    2. 当货物不存在时等待生产者生产(wait);
    3. 生产者生产货物(notify消费者);
    4. 当货物没被消费时等待(wait);
    我们期望的是 1-->2-->3-->4-->1-->2...这样的顺序;
    但是如果消费者和生产者持有的是两把不同的对象锁, 那么当消费者notify时, 
    因为生产者等待在另一把锁上, 导致无法唤醒生产者, 
    那么就会导致:1-->2-->4,生产者和消费者会同时阻塞;
    所以为了消除这种竞态条件, 在A对象的同步代码块中, 只能调用A对象的notify方法, 
    否则就会抛出IllegalMonitorStateException;
    
    3.3 notifyAll: 线程A在同步代码块中调用该方法时, 会唤醒所有等待在该锁上的线程, 
    同样的, 这些唤醒的线程只有在线程A释放锁以后, 才能再次竞争该把锁, 
    竞争到锁的线程继续执行, 其他的线程继续等待;
    如果调用的线程不是该把锁的持有者, 那么也会抛出IllegalMonitorStateException;
复制代码
    public final native void notify();
    public final native void notifyAll();
复制代码
    nofity 和 notifyAll都是不可重写的原生方法, 
    虽然这两个方法没有显式的抛出IllegalMonitorStateException这个异常, 
    但是当竞态条件产生时, IllegalMonitorStateException这个异常自然就出现了;
    
    划重点:
    .1 执行notify | notifyAll时, 唤醒的线程并不会立即持有锁, 故而会形成假唤醒的情况, 
    那么在写wait方法的时, 推荐使用以下方式:
复制代码
     synchronized(lock) {
        while(!condition) {
            lock.wait();
        }
        //doSomething;
     }
复制代码
    当条件不满足时, 该线程还需继续等待;
    
    .2 执行wait | notify | notifyAll的对象, 必须与synchronized锁住的对象是同一个, 
    否则会形成竞态条件导致IllegalMonitorStateException异常的产生;
    .3 java多线程这块内容较多, 这期只是简略的介绍下这三个方法, 
    后面会有一系列的文章专门分享多线程相关的内容;
复制代码

4. finalize方法

     protected void finalize() throws Throwable { }
复制代码
    java的内存管理依赖于JVM实现的GC(Garbage Collection)机制来实现内存的回收, 
    GC相关的内容后面再展开; JVM在进行GC时, 如果这个对象需要被回收, 
    会先判断该方法是否有被重写, 若未重写, 则直接回收该对象内存空间; 
    反之则判断该对象的finalize是否被执行过, 如果没有执行过, 会先放入一个队列中, 
    由低优先级的线程去执行该对象的finalize方法, 执行完毕后再判断该对象是否需要回收;
    如果该对象已经执行过一遍finalize方法了, 直接回收对象的内存空间;
复制代码

    上图就是对GC执行回收对象finaliz方法时对象状态变化的过程;
    
    划重点:
    .1 重写了finalize方法后, 在对象的整个生命周期中GC只会执行一次finalize方法;
复制代码

5. registerNatives方法

    最后来看一下registerNatives方法, 可能看过源码的同学都知道这个方法是Object类中的第一个方法, 我把他放到最后将的原因是和他的功能相关;
复制代码
    private static native void registerNatives();
    static {
        registerNatives();
    }
复制代码
    首先应该关注到的是static代码块, 静态代码块是在类加载时就会执行的, 
    这个代码块中只是调用了registerNatives方法;
    再看到registerNatives方法:
    5.1 static: 这是个静态方法, 因为静态只能调用静态, 
    也就是只有静态方法才能在静态代码块中直接调用;
    5.2 native: 原生方法, 他是由C实现的;
    5.3 看到方法名, 我们可以猜到他是注册Object类中的原生方法的, 实现java中声明的native方法与C实现函数的绑定;
复制代码
static JNINativeMethod methods[] = {
    {"hashCode",    "()I",                    (void *)&JVM_IHashCode},
    {"wait",        "(J)V",                   (void *)&JVM_MonitorWait},
    {"notify",      "()V",                    (void *)&JVM_MonitorNotify},
    {"notifyAll",   "()V",                    (void *)&JVM_MonitorNotifyAll},
    {"clone",       "()Ljava/lang/Object;",   (void *)&JVM_Clone},
};

JNIEXPORT void JNICALL
Java_java_lang_Object_registerNatives(JNIEnv *env, jclass cls)
{
    (*env)->RegisterNatives(env, cls,
                            methods, sizeof(methods)/sizeof(methods[0]));
}
复制代码
    这是OpenJDK6对Object中native方法的绑定, 
    Java_java_lang_Object_registerNatives这样函数可直接调用java中native函数, 
    通过上面的代码可以清晰的看到registerNatives实现了method中native方法的绑定;
    同时也可以看到methods中是没有getClass这个方法, 
    自然可以猜到他也是采用规定函数名称直接调用的方式实现绑定的(Java_java_lang_Object_get
    Class);
    JNI这里就不做展开, 感兴趣的可以阅读下
复制代码

Java? Native Interface: Programmer's Guide and Specification;

   至此, Object类中的所有方法的解析已经告一段落了, 如有错误之处, 欢迎指正;
复制代码

转载于:https://juejin.im/post/5c487ff1f265da6158777688

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
POI是Apache的一个开源项目,可以到Apache网站下载相应的jar包文件,及其源文件。 POI提供了提取一些非TXT文本中文本内容的API,比如提取Word,Excel等,使用起来非常方便。 为了说明POI提起Word文件的方便和简单,通过提取一个Word文件的文本来,来了解POI API的功能。 假设在本地磁盘中存在一个Word文件 E:\POI\word\JBoss3.0 下配置和部署EJB简介.doc文件是具有格式的,内容如图所示: 下面看看提取它的内容是多么简单。 首先从Apache网站上下载POI的相关jar包。 新建一个测试: package org.shirdrn.word; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import org.apache.poi.hwpf.extractor.WordExtractor; public class MyWordExtractor { public static void main(String[] args) { File file = new File("E:\\POI\\word\\JBoss3.0 下配置和部署EJB简介.doc"); try { FileInputStream fis = new FileInputStream(file); WordExtractor wordExtractor = new WordExtractor(fis); System.out.println("【 使用getText()方法提取的Word文件的内容如下所示:】"); System.out.println(wordExtractor.getText()); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } } 提取Word文件的文本内容,打印到控制台上,如下所示: 使用WordExtractor的getTextFromPieces()方法提取: wordExtractor.getTextFromPieces(); 结果和上面是一样的。 WordExtractor还有一个可以提取Word文件的各个段落的方法getParagraphText(),返回一个String[]数组,数组中每个元素为一个段的文本内容。 这里,对Word文件中换行也看成是一个段,测试如下: package org.shirdrn.word; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import org.apache.poi.hwpf.extractor.WordExtractor; public class MyWordExtractor { public static void main(String[] args) { File file = new File("E:\\POI\\word\\JBoss3.0 下配置和部署EJB简介.doc"); try { FileInputStream fis = new FileInputStream(file); WordExtractor wordExtractor = new WordExtractor(fis); System.out.println("【 使用getText()方法提取的Word文件的内容如下所示:】"); String[] paragraph = wordExtractor.getParagraphText(); System.out.println("该Word文件共有"+paragraph.length+"段。"); for(int i=0;i<paragraph.length;i++){ System.out.println("< 第 "+(i+1)+" 段的内容为 >"); System.out.println(paragraph[i]); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } } 提取Word文件的文本内容,打印到控制台上,如下所示: 从上面的Word文件可以看出,最后一行是Word文件的一个换行符,使用WordExtractor提取时,也把它默认成为一个段,因为一个段结束后应该有一个回车换行符。 如果有多个Word文件,而且放在不同的目录下,要提取它们的文本内容,可以实现一个递归的函数,通过深度遍历,为每一个Word文件进行提取。 如果需要,可以将提取到的Word文件的文本内容输出到本地磁盘中,比如以txt记事本的根式保存。 从上面可以看出,提取Word文件的文本内容,实际上是将Word文件的格式去掉了,获取到文本的内容。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值