栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

java和main方法

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

java和main方法

一开始写java,所有demo一定是写一个main方法。后面你发现这个main方法其实和普通方法也没什么区别,可以被其他类的方法调用。而且你可以在多个java文件里写main方法,只不过是需要你指定main类,java也可以根据你指定的类找到main方法执行。
基于上面的疑问,我们去虚拟机中找找答案。

虚拟机的实现

下面是直接从openjdk中获取的代码,你会发现,他的方式竟然是jni的调用。

	mainClass = LoadMainClass(env, mode, what);
    CHECK_EXCEPTION_NULL_LEAVE(mainClass);
    appClass = GetApplicationClass(env);
    NULL_CHECK_RETURN_VALUE(appClass, -1);
    PostJVMInit(env, appClass, vm);
    CHECK_EXCEPTION_LEAVE(1);
    mainID = (*env)->GetStaticMethodID(env, mainClass, "main",
 "([Ljava/lang/String;)V");
    CHECK_EXCEPTION_NULL_LEAVE(mainID);
    mainArgs = CreateApplicationArgs(env, argv, argc);
    CHECK_EXCEPTION_NULL_LEAVE(mainArgs);
    (*env)->CallStaticVoidMethod(env, mainClass, mainID, mainArgs);
    ret = (*env)->ExceptionOccurred(env) == NULL ? 0 : 1;

为什么java的main方式必须声明成如下的模式

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

jvm中写的比较明了,通过jni,获取一个名字是main,返回值是void,方法参数是String[](这里的部分都是从方法签名中解读([Ljava/lang/String;)V)。
可以理解这是jvm硬编码的结果,如果把openjdk中的代码改成查找main1。再编译出来的jdk,就只会去找class文件中的main1方法了。

硬编码的风险

大家虽然都这么使用,但是这么硬编码也是带来了风险,这里的public static void main(String[] args)相当于一个约定,如果出现了约定不满足使用条件的情况,给迁移就会带来很大的风险,而且还要保证新旧的兼容性,以及特定版本要求以上的硬条件。
在应用层,这个缺点更明显,例如用asm等字节码工具做bci注入,实现的任何细节改变都会导致bci的失败。例如java中的http的注入

 protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
 String method = req.getMethod();
 long lastModified;
 if (method.equals("GET")) {
     lastModified = this.getLastModified(req);
     if (lastModified == -1L) {
  this.doGet(req, resp);
     } else {
  long ifModifiedSince;
  try {
      ifModifiedSince = req.getDateHeader("If-Modified-Since");
  } catch (IllegalArgumentException var9) {
      ifModifiedSince = -1L;
  }

  if (ifModifiedSince < lastModified / 1000L * 1000L) {
      this.maybeSetLastModified(resp, lastModified);
      this.doGet(req, resp);
  } else {
      resp.setStatus(304);
  }
     }
 } else if (method.equals("HEAD")) {
     lastModified = this.getLastModified(req);
     this.maybeSetLastModified(resp, lastModified);
     this.doHead(req, resp);
 } else if (method.equals("POST")) {
     this.doPost(req, resp);
 } else if (method.equals("PUT")) {
     this.doPut(req, resp);
 } else if (method.equals("DELETE")) {
     this.doDelete(req, resp);
 } else if (method.equals("OPTIONS")) {
     this.doOptions(req, resp);
 } else if (method.equals("TRACE")) {
     this.doTrace(req, resp);
 } else {
     String errMsg = lStrings.getString("http.method_not_implemented");
     Object[] errArgs = new Object[]{method};
     errMsg = MessageFormat.format(errMsg, errArgs);
     resp.sendError(501, errMsg);
 }

    }

包括自己写servlet的时候都是继承这个类,去覆盖里面的doXX方法。如果有特殊情况改了方法。那么整体的都无法使用了。
我们在使用混淆工具的时候,会特别注意string常量,如果是用在反射等情况下,这个都是混淆失败的,得做特殊处理,我们常用的混淆工具,是可以处理class.forname这种的操作的,但是无法混淆classloader.loadClass。
以上的情况都是硬编码带来的麻烦。

硬编码的好处

硬编码在编程上似乎没什么好处,但是在使用中确实是一个好处。例如做加密,解密方式不可配置,硬编码成一种,这个带来的好处就是以前的版本他都可以不认可。例如做在密钥检测里,生成密钥的方式都变了,解密的方式也一起变,很快就能阻止有人破解的方式。如果不是硬编码的,在破解起来就很方便了。往往大家花时间反编译找出了校验之处,如果可以配置,直接改个配置就可以运行软件了。

小结
  • jvm为什么认可 public static void main(String[] args),主要是jvm中jni硬编码调用导致。只要修改掉这里的jni调用。就等于换了一个规范。
  • 硬编码的坏处就是不能完美衔接改变,如果是定义在协议上,改动就得做到新旧兼容,否则就出现强制升级的情况,影响范围变大。
  • 硬编码的好处就是稳固。使用在校验,编解码等上面可以防止破解和过期的接入。
转载请注明:文章转载自 www.mshxw.com
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号