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

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

相关阅读


【小家java】java5新特性(简述十大新特性) 重要一跃

【小家java】java6新特性(简述十大新特性) 鸡肋升级

【小家java】java7新特性(简述八大新特性) 不温不火

【小家java】java8新特性(简述十大新特性) 饱受赞誉

【小家java】java9新特性(简述十大新特性) 褒贬不一

【小家java】java10新特性(简述十大新特性) 小步迭代

【小家java】java11新特性(简述八大新特性) 首个重磅LTS版本


每篇一句

理解一门技术的原理,最好最扎实的方式就是自己尝试去实现一遍


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(); //java.lang.NullPointerException
    }
    private 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");
    }
}


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");

   }

}

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


所引用对象是否为null无关紧要,因为访问静态方法不需要实例对象。

如果引用不为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中**nullnull将返回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
    }
}


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

还有有的时候,并不需要返回值的抽像性设计,比如我设计的分布式锁: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到了吗?


相关文章
|
5月前
|
IDE Java 编译器
java编程最基础学习
Java入门需掌握:环境搭建、基础语法、面向对象、数组集合与异常处理。通过实践编写简单程序,逐步深入学习,打牢编程基础。
323 1
|
6月前
|
Java API 容器
Java基础学习day08-2
本节讲解Java方法引用与常用API,包括静态、实例、特定类型方法及构造器引用的格式与使用场景,并结合代码示例深入解析。同时介绍String和ArrayList的核心方法及其实际应用。
189 1
|
5月前
|
存储 Oracle Java
java零基础学习者入门课程
本课程为Java零基础入门教程,涵盖环境搭建、变量、运算符、条件循环、数组及面向对象基础,每讲配示例代码与实践建议,助你循序渐进掌握核心知识,轻松迈入Java编程世界。
487 0
|
5月前
|
负载均衡 Java API
grpc-java 架构学习指南
本指南系统解析 grpc-java 架构,涵盖分层设计、核心流程与源码结构,结合实战路径与调试技巧,助你从入门到精通,掌握高性能 RPC 开发精髓。
561 8
|
6月前
|
Java
Java基础学习day08-作业
本作业涵盖Java中Lambda表达式的应用,包括Runnable与Comparator接口的简化实现、自定义函数式接口NumberProcessor进行加减乘及最大值操作,以及通过IntProcessor处理整数数组,实现遍历、平方和奇偶判断等功能,强化函数式编程实践。
106 5
|
6月前
|
Java 程序员
Java基础学习day08
本节讲解Java中的代码块(静态与实例)及其作用,深入介绍内部类(成员、静态、局部及匿名)的定义与使用,并引入函数式编程思想,重点阐述Lambda表达式及其在简化匿名内部类中的应用。
180 5
|
6月前
|
Java
Java基础学习day07-作业
本作业包含六个Java编程案例:1)动物类继承与多态;2)加油卡支付系统;3)员工管理类设计;4)学生信息统计接口;5)USB设备控制;6)家电智能控制。综合运用抽象类、接口、继承、多态等面向对象技术,强化Java基础编程能力。
242 3
|
6月前
|
Java
Java基础学习day06-作业
本内容为Java基础学习作业,涵盖两个案例:一是通过Card类及其子类GoldenCard、SilverCard实现加油卡系统,体现封装与继承;二是通过Shape类及子类Circle、Rectangle演示多态与方法重写,强化面向对象编程理解。
120 1
|
6月前
|
设计模式 存储 Java
Java基础学习day07
本节讲解Java中的final关键字、单例设计模式、枚举类、抽象类与接口。涵盖常量定义、单例写法(饿汉式/懒汉式)、枚举特点及应用场景,以及抽象类与接口的使用与区别,助力掌握核心面向对象编程思想。
224 1
|
6月前
|
算法 Java
Java基础学习day03-作业
本内容包含多个Java编程案例,涵盖条件判断、循环、数组、随机数生成、素数判断等基础算法练习,适用于巩固Java语法与逻辑思维训练。
202 6

热门文章

最新文章