关于Java中的null值探究

第一个问题-关于引用类型的null探讨

最近在补习数据结构-树的时候,发现了一个问题,发现一个null引用的有趣问题,先上代码:
1.Node.java

public class Node {
    public int data;

    public Node leftChild;

    public Node rightChild;


    public Node(int data) {
        this.data = data;
        leftChild = null;
        rightChild = null;
    }
}

2.MyTree

public class MyTree {
    private Node root;


    public void insert(int value) {
        Node node = new Node(value);
        if (root == null) {
            root = node;//根节点
        } else {
            Node current = root;
            while (true) {
                //左小右大

                if (current.data > node.data) {
                    //先找左边
                    current = current.leftChild;
                    if (current == null) {
                        current = node;
                        System.out.println("左边插入之后current==node" + (current == node));
                        System.out.println("左边插入之后current==node" + (root.leftChild == node));

                        return;
                    }
                } else {
                    //再找右
                    current = current.rightChild;
                    if (current == null) {
                        current = node;
                        System.out.println("右边边插入之后current==node" + (current == node));
                        System.out.println("右边插入之后current==node" + (root.leftChild == node));
                        return;
                    }
                }
            }

        }

    }


    public Node getRoot() {
        return root;
    }
}

3.测试代码:

        MyTree mt = new MyTree();
        mt.insert(3);
        mt.insert(2);
        mt.insert(1);
        mt.insert(4);

        Node root = mt.getRoot();
        System.out.println(root.data);//3
        System.out.println(root.leftChild);//null
        System.out.println(root.rightChild);//null

这里写图片描述

这样就尴尬了,为什么会这样的呢?明明就是current有引用的,但root的左右子树都是null,因此我做了下面这个实验:

        Node node = new Node(1);
        Node left = node.leftChild;

        Node newNode = new Node(111);
        left = newNode;
        System.out.println(node.leftChild);//null

这里写图片描述

总结:
如下面的代码所示,其实上面的代码就是把一个null给了另外一个引用对象,null在java中代码没有引用任何对象(即没任何地址),所以就造成了上面的尴尬局面:

Apple a=null;
APPle b=null;
b=new APPle();//此时b有引用对象,a还是null


第二个问题-关于string类型null的探讨

        String b=null+"111";
        System.out.println(b);//null111

        String bNull=null;
        String b1=bNull+"111111";
        System.out.println(b1);//null111

        String b2=b+b1;
        System.out.println(b2);//null111null111111

在很多时候,我们都学到一个空值+任何非null都会出现NullPointerException,当你运行上面的代码时候,会发现string类型的这种null居然不报,为什么呢?

个人理解:在c/c++中,+/-运算符都是可以被重载的,在Java核心1书本中提及了Java中的String类型的+是唯一被重载的,我认为是这里做了一定的处理(建议往下看)

进阶(先看下面代码):

1.Grandpa类

class Grandpa {

    public double salary;


    public int age;
    public String name;

    public double getSalary() {
        return salary;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }

    public String getName() {
        return name;
    }
}



2.Father类

class Father extends Grandpa {

    @Override
    public String toString() {
        return "Father{" +
                "salary=" + salary +
                ", age=" + age +
                ", name='" + name + '\'' +
                '}';
    }
}

3.测试代码:


        Father father = new Father();
        Grandpa grandpa = new Grandpa();
        Father fatherNull=null;
        //com.test.Grandpa@74a14482ccccccccccc
        System.out.println(grandpa + "ccccccccccc");

        //Father{salary=0.0, age=0, name='null'}ccccccccccc
        System.out.println(father + "ccccccccccc");

        //nullccccccccccc
        System.out.println(fatherNull+ "ccccccccccc");

             try {
             //java.lang.NullPointerException
            System.out.println(fatherNull.toString() + "ccccccccccc");
        } catch (Exception e) {
            System.out.println(e);
        }

这里写图片描述

在这里亲友们可能发现了问题了吧???特别是最后两行的代码有木有感觉很意外呢?个人想解释的就是,String重载的+号作用是先把式子A+B+C中的A,B,C都变为字符串再进行相加的(非null的引用类型自动调用toString()方法,null引用类型的就直接给null表示),但最后一行很遗憾就是null.toString()方法是null指针调用方法了导致出现异常的


以上解释还是不合理的,继续探讨

        StringBuffer stringBuffer = new StringBuffer();
        stringBuffer.append(fatherNull);
        stringBuffer.append(father);

        //String.valueOf(father):     Father{salary=0.0, age=0, name='null'}
        System.out.println("String.valueOf(father):     "+String.valueOf(father));

        //stringBuffer:    nullFather{salary=0.0, age=0, name='null'}
        System.out.println("stringBuffer:    "+stringBuffer.toString());

这里写图片描述

JDK中文文档叙述的:

Java 语言提供对字符串串联符号(”+”)以及将其他对象转换为字符串的特殊支持。字符串串联是通过 StringBuilder(或 StringBuffer)类及其 append 方法实现的。字符串转换是通过 toString 方法实现的,该方法由 Object 类定义,并可被 Java 中的所有类继承。有关字符串串联和转换的更多信息,请参阅 Gosling、Joy 和 Steele 合著的 The Java Language Specification。

这里写图片描述

就是说String类型在运行+的时候其实就是调用 StringBuilder(或 StringBuffer)类及其 append 方法实现的,然后我们就需要看看源码的实现了;
1.首先来到了StringBuffer的Append方法

    @Override
    public synchronized StringBuffer append(Object obj) {
        toStringCache = null;//这里不需要理会
        super.append(String.valueOf(obj));
        return this;
    }

   public AbstractStringBuilder append(String str) {
        if (str == null)
            return appendNull();//返回null
        int len = str.length();
        ensureCapacityInternal(count + len);//扩容
        str.getChars(0, len, value, count);//使用下面代码复制
        // System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
        count += len;
        return this;
    }

    //其实就是返回null
    private AbstractStringBuilder appendNull() {
        int c = count;
        ensureCapacityInternal(c + 4);//扩容,扩展成为c+4长度的数组
        final char[] value = this.value;
        value[c++] = 'n';
        value[c++] = 'u';
        value[c++] = 'l';
        value[c++] = 'l';
        count = c;
        return this;
    }

2.String.valueOf(obj)源码

    public static String valueOf(Object obj) {
        return (obj == null) ? "null" : obj.toString();
    }

看完之后,你就会发现为什么Null会返回null了,看str参数的太多了,使用Object作为形参的来解释:是因为String.valueOf(obj)做了处理,obj为null就返回null要不然就调用obj.toString()
核心:是null的话就返回null,不为null就另外处理
PS:无论是在常量池字符串相加还是堆内存字符串相加,都是调用StringBuilder(或 StringBuffer)类及其 append 方法实现的

猜你喜欢

转载自blog.csdn.net/simplebam/article/details/75808681