30

Java 解惑系列(六):main 方法可以重载或者继承么

 4 years ago
source link: https://mp.weixin.qq.com/s/E_HJWSyCJpi1v9TB7kmxEA
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

问题一:包装类的缓存还记得不?

我们来看一下包装类相关的比较,看下下面的代码,最终将打印什么呢?

public static void main(String[] args) {
    Boolean bool1 = true, bool2 = true;
    System.out.println("bool1==bool2 ? " + (bool1 == bool2) );

    Character c1 = 127, c2 = 127;
    Character c3 = 128, c4 = 128;
    System.out.println("c1==c2 ? " + (c1 == c2));
    System.out.println("c3==c4 ? " + (c3 == c4) );

    Integer i1 = 10, i2 = 10;
    Integer i3 = 300, i4 = 300;
    System.out.println("i1==i2 ? " + (i1 == i2));
    System.out.println("i3==i4 ? " + (i3 == i4) );

    Long long1 = 10L, long2 = 10L;
    Long long3 = 300L, long4 = 300L;
    System.out.println("long1==long2 ? " + (long1 == long2));
    System.out.println("long3==long4 ? " + (long3 == long4));

    Float float1 = 10f, float2 = 10f;
    Float float3 = 300f, float4 = 300f;
    System.out.println("float1==float2 ? " + (float1 == float2));
    System.out.println("float3==float4 ? " + (float3 == float4));
}

代码很简单,就是对各个包装类的几个值进行比较,可以猜测下这段代码的打印结果。这里我们直接将打印结果贴出来:

bool1==bool2 ? true
c1==c2 ? true
c3==c4 ? false
i1==i2 ? true
i3==i4 ? false
long1==long2 ? true
long3==long4 ? false
float1==float2 ? false
float3==float4 ? false

如果和你的预期结果是一致的,那么说明你这里掌握的很好,而如果和你的预期结果有稍稍不同的话,那么或许你可以再接着往下看。

解惑1:

首先,我们学习String的时候,都知道String类是不可变的,因此编译阶段会将String常量放入字符串常量池中,当下次使用时就可以直接从字符串常量池中提取。

而对于包装类来说,其对象同样也是不可变的。所以对于某些频繁使用的值,系统也提供了包装类的缓存,当需要时直接从缓存中取值,而不是再创建一个新的包装类对象。

这些包装类缓存的范围如下:

  • boolean的所有值(true和false);

  • char值的 0~127

  • byte,short,int,long的 -128~127

以Long为例,我们来简单看一下源码:

private static class LongCache {
    private LongCache(){}

    static final Long cache[] = new Long[-(-128) + 127 + 1];

    static {
        for(int i = 0; i < cache.length; i++)
            cache[i] = new Long(i - 128);
    }
}

这个 LongCache 就是Long中缓存的实现,其他的也是类似,如 IntegerCacheCharacterCache 等,这里比较有意思的是:

static final Long cache[] = new Long[-(-128) + 127 + 1];

这里数组的容量,使用了 -(-128) + 127 + 1 ,这个我觉得写的挺有意思的,相当于是间接标识出了数组元素对应的范围:

-(-128)表示负数的元素是128个; 127 则表示正数的元素是127个, 1 则表示元素0的个数;

这里我们在写代码的时候可以参考下,而其他的Cache的实现则是类似的,大家有兴趣的可以扒下代码看看。

对于浮点类型Float和Double,包装类没有缓存。

问题二:main方法有什么特殊的呢

我们一开始学习Java程序的时候,最先跑的一段代码肯定是main方法,main方法的格式如下:

public static void main(String[] args) 

那么main方法有什么特殊的地方呢?我们来简单看一下。

解惑2:

首先针对main方法的格式定义:

  • public:main方法是启动的时候由JVM进行加载的,public的可访问权限是最高的,所以需要声明为public;

  • static:方法的调用要么是通过对象,要么是通过类,而main方法的话因为是由虚拟机调用的,所以无需生成对象,那么声明为static即可;

  • main:至于为什么方法名称叫main,我想应该是参考的是C语言的方法名吧;

  • void:main方法退出时,并没有需要有相关返回值需要返回,所以是void;

  • String[]:此字符串数组用来运行时接受用户输入的参数;因为字符串在Java中是具有通用普遍性的,所以使用字符串是最优选择;数组的话,因为我们的参数不止一个,所以数组肯定是合适的;

不过自动JDK1.5引入动态参数后, String[] 数组也可以使用 String... args 来实现:

public static void main(String... args)

除了上面JVM规定的这个main方法比较特殊外,其他的main方法与普通的静态方法是没有什么不同的。

1. main方法能重载么?

这个是可以的,比如说我们给它重载一个方法:

public class Main {
    public static void main(String args) {
        System.out.println("hello world:" + args);
    }

    public static void main(String[] args) {
        main("test");
    }
}

编译运行,很显然没啥问题,除了JVM规定的作为应用程序入口的main方法之外,其他的main方法都是比较普通的方法。

2. main方法能被其他方法调用么?

public class Main {
    private static int times = 3;

    public static void main2(String[] args) {
        times--;
        main(args);
    }

    public static void main(String[] args) {
        System.out.println("main方法执行:" + times);
        if (times <= 0) {
            System.exit(0);
        }
        main2(args);
    }
}

运行一下代码,可以发现代码能正常执行:

main方法执行:3
main方法执行:2
main方法执行:1
main方法执行:0

所以说即使是作为应用程序入口的main方法,也是可以被其他方法调用的,但要注意程序的关闭方式,别陷入死循环了。

3. main方法可以继承么?

我们以前了解过,当类继承时,子类可以继承父类的方法和变量,那么当父类定义了main方法,而子类没有main方法时,能继承父类的main方法,从而正常的运行程序么?

public class Main {
    public static void main(String[] args) {
        System.out.println("hello world");
    }
}

定义子类:

public class Main2 extends Main {
}

这时候我们运行子类Main2,可以发现,同样打印了 hello world ,这说明main方法也是可以继承的。那么还有一种隐藏的情况也很显然了,子类定义自己的main方法,隐藏掉父类中的实现,那么这也是可以的。

public class Main2 extends Main {
    public static void main(String[] args) {
        System.out.println("hello world Main2");
    }
}

这时候就会打印子类自己的内容了: hello world Main2

这么来看,除了main方法作为应用程序的入口比较特殊外,其他情况下与正常的静态方法是没什么区别的。

本文主要参考自《细说Java》这本书。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK