【小家java】对java中null、void、Void的理解学习

时间:2022-08-29 21:48:45

1、概述

本篇博文很简单啊,主要说说咱们平时最长看见的null、void和Void等。一般人可能不会留意,但此文通过一些简单的例子,希望可以加深同学们对他哥几个的了解

2、栗子

关于null,估计很多人可能恨透它了,因为它是NullPointerException的罪魁祸首。但是用好了它,我们还是能成为好朋友滴。null是所有引用类型的默认值,但是我要澄清一些误解,null既不是对象也不是一种类型,它仅是一种特殊的值,你可以将其赋予任何引用类型你也可以将null转化成任何类型(这个估计很多人没有用过)

public final class Main {
    public static void main(String[] args) {
        Main m = (Main) null; //这样子是不报错的 其实编译器底层会对null这样做处理
        System.out.println(m); //输出null
        m.doSomething(); //竟然输出了doSomething 意不意外
    }

    private static void doSomething() {
        System.out.println("doSomething");
    }
}

对上述结果进行分析:第一个对null的强转,其实是java编译器底层的实现,看.class文件可以看出来结果。第二个,我们想到m的值肯定为null啊,为什么调用doSomething没有空指针呢?但下面代码就空指针了:

public final class Main {
    public static void main(String[] args) {
        Main m = (Main) null; //这样子是不报错的 其实编译器底层会对null这样做处理
        System.out.println(m); //输出null
        m.doSomething(); //java.lang.NullPointerException
    }

    private void doSomething() {
        System.out.println("doSomething");
    }
}

各位同学从这两个例子的对比,应该也能看出来了吧。所以对静态方法调用这里奉送两句话,记住就行:

  1. 所引用对象是否为null无关紧要,因为访问静态方法不需要实例对象。
  2. 如果引用不为null,运行时对象类型也无关紧要,因为静态调用不会导致动态调用分派。而是与类相关。

因此,静态方法的访问,不建议用实例调用,反而让语意变得晦涩了。最后提一点,在java中的自动拆装箱的过程中,如果遇到null值,处理的时候需要当心:

任何含有null值的包装类在Java拆箱生成基本数据类型时候都会抛出一个空指针异常

在看下面例子,判断null值的类型

public final class Main {
    public static void main(String[] args) {
        Main m = (Main) null;
        System.out.println(m instanceof Main); //返回false哦 返回false哦 返回false哦
        System.out.println(m.getClass()); //空指针
    }
}

我们会发现:如果使用了带有null值的引用类型变量,instanceof操作将会返回false。
我们可以使用==或者!=操作来比较null值,但是不能使用其他算法或者逻辑操作,例如小于或者大于。在Java中null==null将返回true

接下来聊聊void和Void。可能很多人咋一看挺懵逼的,好像没啥区别啊。

void不是函数,是方法的修饰符,void的意思是该方法没有返回值,意思就是方法只会运行方法中的语句,但是不返回任何东西。 java.lang.Void是一种类型。例如给Void引用赋值null。通过Void类的源代码可以看到,Void类型不可以继承与实例化。

public final class Main {

    public void do1() {
        return; //返回void,return可写可不写
    }

    public Void do2() {
        return null; //此处必须返回null 返回其余类型都不好使
    }
}
public final class Main {

    public static void main(String[] args) {
        System.out.println(Void.class); //class java.lang.Void
        System.out.println(void.class); //void
        //类似于下面的
        System.out.println(Integer.class); //class java.lang.Integer
        System.out.println(int.class); //int
    }
}

如上,可以清晰的看到void和Void的一些区别,但有些人可能会问,Void我们到底有什么用呢?其实在泛型出现之前,Void一般用于反射之中。例如,下面的代码打印返回类型为void(这里必须借助Void类)的方法名:

public class Test {
  public void print(String v) {}

  public static void main(String args[]){
    for(Method method : Test.class.getMethods()) {
      //判断返回值类型 用到了Void类
      if(method.getReturnType().equals(Void.TYPE)) {
        System.out.println(method.getName());
      }
    }
  }
}

还有有的时候,并不需要返回值的抽像性设计,比如我设计的分布式锁:RedisLock

public static <T> T doInLock(Supplier<T> supplier, RedisTemplate<String, String> redisTemplate, String lockKey) {
        //这里我们使用函数式变成supplier来提供回调函数并且接收返回值
        return supplier.get();
    }

如上,如果使用者实现supplier方法并不需要返回值怎么办呢?这个时候最优雅的处理方式如下:

LockUtil.doInLock(() -> {
  ..... //处理一系列业务逻辑
  return Void.class; //返回这个就非常的优雅了,阅读起来也十分明白
}, stringRedisTemplate,"aaa");

另外,再介绍两种情况下Void的使用,虽然使用较少,但技多不压身嘛。

泛型出现后,某些场景下会用到Void类型。例如Future用来保存结果,但如果操作并没有返回值呢?这种情况下就可以用Future表示。当调用get后结果计算完毕则返回后将会返回null。(原理同上示例)

另外Void也用于无值的Map中(只需要key不需要值),例如Map<,Void>这样map将具Set有一样的功能。
因此当你使用泛型时函数并不需要返回结果或某个对象不需要值时候这是可以使用java.lang.Void类型表示

Void类可能本身作用就只是不起任何作用,但是本身只是一个占位符类。即Void类本身只是一个占位符类,不能被实例化,多用于泛型中作占位符使用

3、使用场景

颈部惊喜,意不意外,没想到平时毫不起眼的一个Void,竟然还是有这么多使用场景的。这里附上Void.class源码里的一句代码,就更加能辅助小伙伴们理解了

public static final Class<Void> TYPE = (Class<Void>) Class.getPrimitiveClass("void");

4、最后

本文的内容可能平时使用很少,但我建议,这个可以当作装逼神器,哈哈。so,这技能你get到了吗?

—-题后语—-

更多内容持续更新中,欢迎关注我的博客!
有任何问题,可以跟我留言讨论,欢迎指正,不胜感激。
有任何疑问,亦可扫码向我提我哟~