java中为啥要把main方法定义为一个static方法

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java中为啥要把main方法定义为一个static方法相关的知识,希望对你有一定的参考价值。

java中为什么要把main方法定义为一个static方法?
在讲这个之前,我先讲一些关于Java关键字static的吧。

(1)在类中,变量的前面有修饰符static称为静态变量(类变量),方法的前面有修饰符static称为静态方法(类方法)。静态方法和静态变量是属于某一个类,而不属于类的对象。
(2)静态方法和静态变量的引用直接通过类名引用。

例如:类Point中有个 static int x;类变量,我们要引用它:Point.x=89;
(3)在静态方法中不能调用非静态方法和引用非静态的成员变量。反之,则可以。

原因:静态变量和静态方法在类被加载的时候就分配了内存空间,当非静态的方法调用他们的时候,他们已经有了内存空间,当然是可以调用的咯!
(4)可以用类的对象去调用静态的方法。

我们知道,在C/C++当中,这个main方法并不是属于某一个类的,它是一个全局的方法,所以当我们执行的时候,c++编译器很容易的就能找到这个main方法,然而当我们执行一个java程序的时候,因为java都是以类作为程序的组织单元,当我们要执行的时候,我们并不知道这个main方法会放到哪个类当中,也不知道是否是要产生类的一个对象,为了解决程序的运行问题,我们将这个main方法定义为static,这样的话,当我们在执行一个java代码的时候,我们在命令提示符中写:java Point(Point为一个类),解释器就会在Point这个类当中,去调用这个静态的main方法,而不需要产生Point这个类的对象,当我们加载Point这个类的时候,那么main方法也被加载了,作为我们java程序的一个入口。
参考技术A 1、首先介绍一下static这个修饰符
在类中,变量的前面有修饰符static称为静态变量(类变量),方法的前面有修饰符static称为静态方法(类方法)。静态方法和静态变量是属于某一个类,而不属于类的对象。
2、如果一个方法没有用static来修饰的话就说明这是一个成员方法,只能通过对象来调用这个方法
3、在Java程序运行时,是没有实例化任何对象的,只能通过调用类方法来运行main方法,
所以会用static来修饰本回答被提问者采纳

Java8:为啥禁止为 java.lang.Object 中的方法定义默认方法

【中文标题】Java8:为啥禁止为 java.lang.Object 中的方法定义默认方法【英文标题】:Java8: Why is it forbidden to define a default method for a method from java.lang.ObjectJava8:为什么禁止为 java.lang.Object 中的方法定义默认方法 【发布时间】:2014-07-23 21:28:34 【问题描述】:

默认方法是我们 Java 工具箱中一个不错的新工具。但是,我尝试编写一个接口来定义default 版本的toString 方法。 Java 告诉我这是被禁止的,因为在java.lang.Object 中声明的方法可能不是defaulted。为什么会这样?

我知道有“基类总是获胜”的规则,所以默认情况下(双关语;),Object 方法的任何default 实现都会被Object 中的方法覆盖。但是,我认为规范中 Object 中的方法不应该有例外。特别是对于toString,拥有一个默认实现可能非常有用。

那么,Java 设计人员决定不允许 default 方法覆盖来自 Object 的方法的原因是什么?

【问题讨论】:

我现在对自己感觉很好,为此投票了 100 次,因此获得了金牌。好问题! 【参考方案1】:

这是另一个看起来“显然是个好主意”的语言设计问题,直到您开始挖掘并意识到它实际上是个坏主意。

This mail 有很多关于这个主题(以及其他主题)。有几种设计力量汇聚在一起,将我们带到了当前的设计:

希望保持继承模型简单; 事实上,一旦您查看了明显的示例(例如,将AbstractList 转换为接口),您就会意识到继承 equals/hashCode/toString 与单一继承和状态密切相关,并且接口是多重继承和无状态的; 它可能为一些令人惊讶的行为打开了大门。

您已经触及“保持简单”的目标;继承和冲突解决规则被设计得非常简单(类胜过接口,派生接口胜过超接口,任何其他冲突都由实现类解决。)当然,这些规则可以调整为例外,但是我想当你开始拉弦时你会发现,增量的复杂性并不像你想象的那么小。

当然,有一定程度的好处可以证明更复杂是合理的,但在这种情况下它不存在。我们这里讨论的方法是equals、hashCode和toString。这些方法本质上都是关于对象状态的,拥有状态的是类,而不是接口,谁最有资格确定平等对那个类意味着什么(尤其是平等的契约非常强;参见有效Java 带来了一些令人惊讶的后果);界面编写者离得太远了。

很容易拉出AbstractList的例子;如果我们能摆脱AbstractList 并将行为放入List 界面,那就太好了。但是一旦你超越了这个明显的例子,就找不到很多其他好的例子了。在根上,AbstractList 是为单继承而设计的。但是接口必须为多重继承而设计。

进一步,假设你正在编写这个类:

class Foo implements com.libraryA.Bar, com.libraryB.Moo  
    // Implementation of Foo, that does NOT override equals

Foo 作者查看超类型,没有看到 equals 的实现,并得出结论,要获得引用相等,他需要做的就是从 Object 继承 equals。然后,下周,Bar 的库维护者“有帮助地”添加了一个默认的 equals 实现。哎呀!现在Foo 的语义已被另一个维护域中的接口破坏,“有用地”为常用方法添加了默认值。

默认值应该是默认值。向没有的接口(层次结构中的任何位置)添加默认值不应影响具体实现类的语义。但是,如果默认值可以“覆盖” Object 方法,那将是不正确的。

因此,虽然它看起来像是一个无害的功能,但实际上却是相当有害的:它增加了很多复杂性,只需要很少的增量表现力,而且对于出于善意的、看似无害的更改单独编译来说太容易了接口破坏实现类的预期语义。

【讨论】:

我很高兴您花时间解释这一点,我感谢您考虑的所有因素。我同意这对hashCodeequals 来说是危险的,但我认为这对toString 非常有用。例如,某些Displayable 接口可能定义了一个String display() 方法,并且可以节省大量样板文件以便能够在Displayable 中定义default String toString() return display(); ,而不是要求每个Displayable 都实现toString()或扩展 DisplayableToString 基类。 @Brandon 你说得对,允许继承 toString() 不会像 equals() 和 hashCode() 那样危险。另一方面,现在这个特性会更加不规则——而且你仍然会在继承规则方面产生同样的额外复杂性,为了这个方法......似乎更好地在我们所做的地方划清界限. @gexicide 如果toString() 仅基于接口方法,您可以简单地将default String toStringImpl() 添加到接口中,并在每个子类中覆盖toString() 以调用接口实现-a有点难看,但有效,总比没有好。 :) 另一种方法是制作Objects.hash()Arrays.deepEquals()Arrays.deepToString() 之类的东西。为@BrianGoetz 的回答 +1! lambda 的 toString() 的默认行为确实令人讨厌。我知道 lambda 工厂的设计非常简单和快速,但是吐出派生的类名确实没有帮助。在函数式接口中有一个default toString() 覆盖将允许我们——至少——做一些事情,比如吐出函数的签名和实现者的父类。更好的是,如果我们可以使用一些递归的 toString 策略,我们可以遍历闭包以获得对 lambda 的非常好的描述,从而大大改善 lambda 学习曲线。 对任何类、子类、实例成员中的 toString 的任何更改都可能对实现类或类的用户产生影响。此外,对任何默认方法的任何更改也可能会影响所有实现类。那么当涉及到某人改变接口的行为时,toString、hashCode 有什么特别之处呢?如果一个类扩展了另一个类,他们也可以改变。或者,如果他们正在使用委托模式。使用 Java 8 接口的人必须通过升级来做到这一点。可以提供可以在子类上抑制的警告/错误。【参考方案2】:

禁止在接口中为java.lang.Object 中的方法定义默认方法,因为默认方法永远不会“可达”。

默认接口方法可以在实现该接口的类中被覆盖,并且该方法的类实现具有比接口实现更高的优先级,即使该方法是在超类中实现的。由于所有类都继承自java.lang.Object,因此java.lang.Object 中的方法将优先于接口中的默认方法并被调用。

来自 Oracle 的 Brian Goetz 在mailing list post 中提供了有关设计决策的更多详细信息。

【讨论】:

【参考方案3】:

给出一个很迂腐的回答,只禁止为java.lang.Objectpublic方法定义default方法。有 11 种方法可供考虑,可以分为三种方式来回答这个问题。

    Object 方法中有六个不能有default 方法,因为它们是final 并且根本不能被覆盖:getClass()notify()notifyAll()wait()wait(long)wait(long, int)。 三个Object 方法不能有default 方法,原因是Brian Goetz 给出的上述原因:equals(Object)hashCode()toString()

    Object 方法中的两个可以具有 default 方法,尽管这样的默认值充其量是值得怀疑的:clone()finalize()

    public class Main 
        public static void main(String... args) 
            new FOO().clone();
            new FOO().finalize();
        
    
        interface ClonerFinalizer 
            default Object clone() System.out.println("default clone"); return this;
            default void finalize() System.out.println("default finalize");
        
    
        static class FOO implements ClonerFinalizer 
            @Override
            public Object clone() 
                return ClonerFinalizer.super.clone();
            
            @Override
            public void finalize() 
                ClonerFinalizer.super.finalize();
            
        
    
    

【讨论】:

。有什么意义?您仍然没有回答 WHY 部分 - “那么,Java 设计人员决定不允许默认方法覆盖 Object 方法的原因是什么?” @pro_cheats 正如明确指出的那样,他的观点非常迂腐。答案为已被接受的优秀答案添加了一些信息,这已经涵盖了 WHY 部分。【参考方案4】:

我看不到 Java 语言作者的头脑,所以我们只能猜测。但是我看到了很多原因,并且在这个问题上完全同意他们。

引入默认方法的主要原因是能够在不破坏旧实现的向后兼容性的情况下向接口添加新方法。默认方法也可用于提供“方便”的方法,而无需在每个实现类中定义它们。

这些都不适用于 toString 和 Object 的其他方法。简而言之,默认方法旨在提供没有其他定义的默认行为。不提供与其他现有实现“竞争”的实现。

“基类总是赢”的规则也有其坚实的理由。假设类定义了真正的实现,而接口定义了默认的实现,这有点弱。

此外,在一般规则中引入任何例外都会导致不必要的复杂性并引发其他问题。对象(或多或少)和其他任何类一样,那么它为什么会有不同的行为呢?

总而言之,您提出的解决方案可能弊大于利。

【讨论】:

发布我的时,我没有注意到 gexicide 答案的第二段。它包含一个更详细地解释该问题的链接。【参考方案5】:

原因很简单,因为 Object 是所有 Java 类的基类。因此,即使我们在某些接口中将 Object 的方法定义为默认方法,它也是无用的,因为 Object 的方法将始终被使用。这就是为什么为了避免混淆,我们不能使用覆盖 Object 类方法的默认方法。

【讨论】:

以上是关于java中为啥要把main方法定义为一个static方法的主要内容,如果未能解决你的问题,请参考以下文章

JAVA里面的main函数为啥要定义为static的?

java中求数组最值方法的定义和前面为啥不一样,好心人帮忙看看

java14

java - 为啥main方法没有转换为java中的守护线程[重复]

为啥使用Junit Test而不用普通java main方法来完成测试?

为啥要把sql脚本放在java项目下?