精华内容
下载资源
问答
  • 虽说有c++基础,但是对Java不甚了解。小虾再次请教各位大神,Java前端跟后端的区别在哪里?还有若同是架构,Java和c++区别在哪呢? 跪求大神们回答简单明了些哈,小妹很笨的(*^__^*) 嘻嘻……
  • java前端代码封装问题

    2017-01-16 02:45:21
    求助,我现在接了一个java前端的项目,有些代码看不到,好像是封装了,应该怎么样才能看到被封装的代码?哪位大神帮帮我
  • java 前端乱码问题

    千次阅读 2017-03-16 17:10:51
    java 前端开发,常常会遇到后台数据编码 UTF8,传递到前端却乱码(前端也使用 utf8 编码解码)。可以尝试下面的步骤去解决。 1.确认 Controller 中的 response 设置响应的数据格式和编码,如 text/html;charset=...

    做 java 前端开发,常常会遇到后台数据编码 UTF8,传递到前端却乱码(前端也使用 utf8 编码解码)。可以尝试下面的步骤去解决。

    1.确认 Controller 中的 response 设置响应的数据格式和编码,如 text/html;charset=UTF-8; 或  application/json;charset=UTF-8

    2.若以注解方式实现 Controller,则加入注解: RequestMapping(Value="view.html"; produces={"text/html;charset=UTF-8"})

    3.若以上两种方法不凑效,只能自己对返回数据进行 URLEncode ,传到前端再进行 URLDecode。

    展开全文
  • Java前端面试题总结

    万次阅读 多人点赞 2018-02-27 10:43:54
    Java前端面试题总结简单说一下HTML,CSS,javaScript在网页开发中的定位?HTML:超文本标记语言,定义网页的结构CSS:层叠样式表,用来美化页面JavaScript:主要用来验证表单,做动态交互(其中AJAX) CSS面试题...

    Java前端面试题总结

    简单说一下HTML,CSS,javaScript在网页开发中的定位?

    HTML:超文本标记语言,定义网页的结构

    CSS:层叠样式表,用来美化页面

    JavaScript:主要用来验证表单,做动态交互(其中AJAX)

     

    CSS面试题

    bootstrap是什么

    bootstrap是一个移动设备优先的UI框架,我们可以不用写任何cssjs代码就能实现比较漂亮的有交互性的页面,我们程序员对页面的编写是有硬伤的,所有要自己写页面的话就要使用类似于bootstrap这样的UI框架

    平时用的很多:

    1)模态框

    <div class="modal fade" tabindex="-1" role="dialog" aria-labelledby="myLargeModalLabel" aria-hidden="true"  id="addModal">
           <div class="modal-dialog modal-lg">
          <div class="modal-content">
             <div class="modal-header">
             标题

    </div>
             <div class="modal-body">
                内容体
             </div>
             <div class="modal-footer">
                <a href="#" class="btn btn-default" data-dismiss="modal">取消</a>
                <a href="javascript:add();" class="btn btn-primary">保存</a>
             </div>
          </div>
       </div>

    $("#updateModal").modal("show");

    $("#updateModal").modal("hide");

     

     

    2)表单、表单项

    <form role="form">

      <div>

        <label for="name">名称</label>

        <input type="text" id="name" placeholder="请输入名称">

      </div>

      <button type="submit" class="btn btn-default">提交</button>

    </form>

     

    3)布局容器(container类用于固定宽度并支持响应式布局的容器container-fluid类用于 100% 宽度,占据全部视口(viewport)的容器

    4)删格系统(系统会自动分为最多12)

    简要说一下CSS的元素分类

    · 块级元素:div,p,h1,form,ul,li;

    · 行内元素 : span>,a,label,input,img,strong,em;

    CSS隐藏元素的几种方法(至少说出三种)

    · Opacity:元素本身依然占据它自己的位置并对网页的布局起作用。它也将响应用户交互;

    · Visibility:与 opacity 唯一不同的是它不会响应任何用户交互。此外,元素在读屏软件中也会被隐藏;

    · Display:display 设为 none 任何对该元素直接用户交互操作都不可能生效。此外,读屏软件也不会读到元素的内容。这种方式产生的效果就像元素完全不存在;

    CSS清除浮动的几种方法(至少两种)

    · 使用带clear属性的空元素

    · 使用CSS的overflow属性;

    · 使用CSS的:after伪元素;

    · 使用邻接元素处理;

    CSS居中(包括水平居中和垂直居中)

    内联元素居中方案

    水平居中设置:
    1.行内元素

    · 设置 text-align:center;

    2.Flex布局

    · 设置display:flex;justify-content:center;(灵活运用,支持Chroime,Firefox,IE9+)

    垂直居中设置:
    1.父元素高度确定的单行文本(内联元素)

    · 设置 height = line-height;

    2.父元素高度确定的多行文本(内联元素)

    · a:插入 table (插入方法和水平居中一样),然后设置 vertical-align:middle;

    · b:先设置 display:table-cell 再设置 vertical-align:middle;

    块级元素居中方案

    · 

    水平居中设置:
    1.定宽块状元素

    · 

    · 设置 左右 margin 值为 auto;

    2.不定宽块状元素

    · a:在元素外加入 table 标签(完整的,包括 table、tbody、tr、td),该元素写在 td 内,然后设置 margin 的值为 auto;

    · b:给该元素设置 display:inine 方法(转换为内联元素)

    · c:父元素设置 position:relative 和 left:50%,子元素设置 position:relative 和 left:50%;

    垂直居中设置:

    · 使用position:absolute(fixed),设置left、top、margin-left、margin-top的属性;

    · 利用position:fixed(absolute)属性,margin:auto这个必须不要忘记了;

    · 利用display:table-cell属性使内容垂直居中;

    · 使用css3的新属性transform:translate(x,y)属性;

    · 使用:before元素;

    页面导入样式时,使用link和@import有什么区别?

    · link属于XHTML标签,除了加载CSS外,还能用于定义RSS, 定义rel连接属性等作用;而@import是CSS提供的,只能用于加载CSS;

    · 页面被加载的时,link会同时被加载,而@import引用的CSS会等到页面被加载完再加载;

    · import是CSS2.1 提出的,只在IE5以上才能被识别,而link是XHTML标签,无兼容问题;

    CSS 选择符有哪些?哪些属性可以继承?优先级算法如何计算? CSS3新增伪类有那些?

    · id选择器( # myid)

    · 类选择器(.myclassname)

    · 标签选择器(div, h1, p)

    · 相邻选择器(h1 + p)

    · 子选择器(ul > li)

    · 后代选择器(li a)

    · 通配符选择器( * )

    · 属性选择器(a[rel = “external”])

    · 伪类选择器(a: hover, li: nth – child)

    · 可继承的样式: font-size font-family color, UL LI DL DD DT;

    · 不可继承的样式:border padding margin width height ;

    · 优先级就近原则,同权重情况下样式定义最近者为准;

    优先级为:

    1

    2

    !important >  id > class > tag

    important  内联优先级高

    CSS3有哪些新特性?

    · CSS3实现圆角(border-radius:8px),阴影(box-shadow:10px),
    对文字加特效(text-shadow、),线性渐变(gradient),旋转(transform)

    · transform:rotate(9deg) scale(0.85,0.90) translate(0px,-30px) skew(-9deg,0deg);//旋转,缩放,定位,倾斜
    增加了更多的CSS选择器 多背景 rgba

    JavaScript面试题

    简单介绍一下AJAX

    什么是AJAX

    --》异步的JavaScriptXML

    作用是什么?

    --》通过AJAX与服务器进行数据交换,AJAX可以使用网页实现局部更新,这意味着可以在不刷新整个网页的情况下,对网页的某部分进行更新。

    怎么来实现AJAX

    --》使用XmlHttpRequest这个对象可以异步向服务器发送请求,获取响应,完成局部更新,

    Open send responseText/responseXML局部响应。

    使用场景?

    --》登录失败时不跳转页面,注册时提示用户名是否存在,二级联动等等使用场景

     

    JS和JQuery的关系

    JQuery是一个JS框架,封装了JS的属性和方法,并且增强了JS的功能,让用户使用起来更加方便,

    原来使用js是要处理很多兼容性的问题(注册事件),由于Jquery封装了底层,就不用处理兼容性问题(注册事件等)

    原生的jsdom和事件绑定Ajax等操作非常麻烦,JQuery等装以后非常方便。

     

    JQuery的常用选择器

    ID选择器:通过ID获取一个元素

    Class选择器:通过类获取元素

    标签选择器:通过标签获取元素

    通用选择器(*):获取所有元素

    层次选择器:

    儿子选择器> 获取下面的子元素

    后代选择器 空格 获取下面的后代,包括儿子、孙子等后代

     

    属性选择器:

    tag[arrName=test] 获取属性名为xxx并且属性的值为test的所有标签

    <input type=checkboxname=body/> 吃饭<br/>

    <input type=checkboxname=body/> 睡觉<br/>

    $(input[name='body']),表示获取属性名为name并且name属性值body的所有input标签。

     

    Jquery的页面加载完毕事件

    很多时候我们需要获取元素,必须等到该元素被加载完成后才能获取,我们可以把js代码放到该元素的后面,但是这样就会造成js在我们的body中存在不好管理,所有页面加载完毕后所有元素当然已经加载完毕,一般获取元素做操作都要在页面加载完毕后操作。

     

    1)第一种:

    $(document).ready(function(){

    });

    $(document)把原生的document这个dom对象转换为JQuery对象,转换完成后才能调用ready方法。

    ready(fn)表示的是页面结构被加载完毕后执行传入函数fn

     

    2)第二种:

    $(function(){

    });

    当页面加载完毕后执行里面的函数。这一种相对简单,用的最多。

     

    3window.onload的区别

    JQuery中的页面加载完毕事件,表示页面结构被加载完毕;

    window.onload表示的是页面被加载完毕;必须等页面中的图片、声音、图像等远程资源被加载完毕后才调用而JQuery中只需要页面架构加载完毕

    $(window).load(function(){

    });

     

    JQuery的AJAX和原生js实现有什么关系

    JQuery中的AJAX也是通过原生的js封装的,封装完成后让我们使用更加便利,不用考虑底层实现和兼容性等处理。

    如果采用原生js实现AJAX是非常麻烦的,并且每次都是一样的,如果我们不使用JQuery,我们也要封装ajax对象的方法和属性,有像jquery这些已经封装完成,并且经过很多企业实际的框架,比较可靠并且开源,我们就不需要等装,直接使用成熟的框架(jquery)即可;

     

    简单说一下html5?你对哪些现在哪些新技术有了解

    html5是最新版本的html,是在原来html4的基础上增强类一些标签。

    html5增加了一些像画板、声音、视频、web存储方面等高级功能,但是html5有一个不好的地方,那就是html5太强调语义了,导致开发者都不知道要选择哪个标签。在页面布局时,无论头部、主体、导航等模块都使用div来表示,但是html5的规范,需要使用不同不同的标签。(header,footer)

     

    你对新技术有哪些了解:html5 css3

     

    简单说一下css3

    css3是最新版本的css,是对原来的css2的功能增强

    css3中提供一些css2中实现起来比较困难或者不能实现的功能。

    1)盒子圆角边框

    2)盒子和文字的阴影

    3)渐变

    4)装换、移动、缩放、旋转等

    5)过渡、动画都可以使用动画

    6)可以使用媒体查询实现响应式网站

    css3最大的缺点就是要根据不同的浏览器处理兼容性,对应有一些处理兼容性的工具,不用担心

     

    javascript的typeof返回哪些数据类型

    · bjectnumberfunctionbooleanunderfind;

    例举3种强制类型转换和2种隐式类型转换?

    · 强制(parseInt,parseFloat,number)隐式(== – ===);

    数组方法pop() push() unshift() shift()

    · push()尾部添加 pop()尾部删除

    · unshift()头部添加 shift()头部删除

    ajax请求的时候get 和post方式的区别?

    · 一个在url后面 一个放在虚拟载体里面
    有大小限制

    · 安全问题
    应用不同 一个是论坛等只需要请求的,一个是类似修改密码的;

    两种在客户端和服务器端进行请求-响应的常用方法是:GET 和 POST。

    · GET - 从指定的资源请求数据

    · POST - 向指定的资源提交要处理的数据

    GET 基本上用于从服务器获得(取回)数据。注释:GET 方法可能返回缓存数据。

    POST 也可用于从服务器获取数据。不过,POST 方法不会缓存数据,并且常用于连同请求一起发送数据。

     

    ajax请求时,如何解释json数据

    · 使用eval parse,鉴于安全性考虑 使用parse更靠谱;

    添加 删除 替换 插入到某个接点的方法

    · obj.appendChidl()
    obj.innersetBefore
    obj.replaceChild
    obj.removeChild

    编写一个b继承a的方法;

    JavaScript

    spacer.gif

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    function A(name){

        this.name = name;

        this.sayHello = function(){alert(this.name+ say Hello!);};

    }

    function B(name,id){

        this.temp = A;

        this.temp(name);        //相当于new A();

        delete this.temp;      

         this.id = id;  

        this.checkId = function(ID){alert(this.id==ID)};

    }

    如何阻止事件冒泡和默认事件

    JavaScript

    spacer.gif

    1

    2

    3

    4

    5

    6

    7

    8

    function stopBubble(e)

    {

        if (e && e.stopPropagation)

            e.stopPropagation()

        else

            window.event.cancelBubble=true

    }

    return false

    下面程序执行后弹出什么样的结果?

    JavaScript

    spacer.gif

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    function fn() {

        this.a = 0;

        this.b = function() {

            alert(this.a)

        }

    }

    fn.prototype = {

        b: function() {

            this.a = 20;

            alert(this.a);

        },

        c: function() {

            this.a = 30;

            alert(this.a);

        }

    }

    var myfn = new fn();

    myfn.b();

    myfn.c();

    谈谈This对象的理解。

    this是js的一个关键字,随着函数使用场合不同,this的值会发生变化。
    但是有一个总原则,那就是this指的是调用函数的那个对象。
    this一般情况下:是全局对象Global。 作为方法调用,那么this就是指这个对象

    下面程序的结果

    JavaScript

    spacer.gif

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    function fun(n,o) {

      console.log(o)

      return {

        fun:function(m){

          return fun(m,n);

        }

      };

    }

    var a = fun(0);  a.fun(1);  a.fun(2);  a.fun(3);

    var b = fun(0).fun(1).fun(2).fun(3);

    var c = fun(0).fun(1);  c.fun(2);  c.fun(3);

    //答案:

    //a: undefined,0,0,0
    //b: undefined,0,1,2
    //c: undefined,0,1,1

    下面程序的输出结果

    JavaScript

    spacer.gif

    1

    2

    3

    4

    5

    6

    7

    8

    9

    var name = 'World!';

    (function () {

        if (typeof name === 'undefined') {

            var name = 'Jack';

            console.log('Goodbye ' + name);

        } else {

            console.log('Hello ' + name);

        }

    })();

    介绍下你最常用的一款框架

    · Jquery,Bootstrap,juqeryDataTable,BootstrapTable,EasyUI;

    其它

    你有哪些性能优化的方法?

    1) 减少http请求次数:CSS, JS、CSS源码压缩、图片大小控制合适;网页Gzip,CDN托管,data缓存 ,图片服务器。
    2)前端模板 JS+数据,减少由于HTML标签导致的带宽浪费,前端用变量保存AJAX请求结果,每次操作本地变量,不用请求,减少请求次数
    3) 用innerHTML代替DOM操作,减少DOM操作次数,优化javascript性能。
    4) 当需要设置的样式很多时设置className而不是直接操作style。
    5) 少用全局变量、缓存DOM节点查找的结果。减少IO读取操作。
    6) 避免使用CSS Expression(css表达式)又称Dynamic properties(动态属性)。
    7) 图片预加载,将样式表放在顶部,将脚本放在底部 加上时间戳。
    8) 避免在页面的主体布局中使用table,table要等其中的内容完全下载之后才会显示出来,显示div+css布局慢。对普通的网站有一个统一的思路,就是尽量向前端优化、减少数据库操作、减少磁盘IO。向前端优化指的是,在不影响功能和体验的情况下,能在浏览器执行的不要在服务端执行,能在缓存服务器上直接返回的不要到应用服务器,程序能直接取得的结果不要到外部取得,本机内能取得的数据不要到远程取,内存能取到的不要到磁盘取,缓存中有的不要去数据库查询。减少数据库操作指减少更新次数、缓存结果减少查询次数、将数据库执行的操作尽可能的让你的程序完成(例如join查询),减少磁盘IO指尽量不使用文件系统作为缓存、减少读写文件次数等。程序优化永远要优化慢的部分,换语言是无法“优化”的。

    3.http状态码有那些?分别代表是什么意思?
    100-199 用于指定客户端应相应的某些动作。
    200-299 用于表示请求成功。
    300-399 用于已经移动的文件并且常被包含在定位头信息中指定新的地址信息。
    400-499 用于指出客户端的错误。400 1、语义有误,当前请求无法被服务器理解。401 当前请求需要用户验证 403 服务器已经理解请求,但是拒绝执行它。
    500-599 用于支持服务器错误。 503 – 服务不可用
    4.一个页面从输入 URL 到页面加载显示完成,这个过程中都发生了什么?(流程说的越详细越好)

    · 查找浏览器缓存

    · DNS解析、查找该域名对应的IP地址、重定向(301)、发出第二个GET请求

    · 进行HTTP协议会话

    · 客户端发送报头(请求报头)

    · 文档开始下载

    · 文档树建立,根据标记请求所需指定MIME类型的文件

    · 文件显示

    浏览器这边做的工作大致分为以下几步:

    · 加载:根据请求的URL进行域名解析,向服务器发起请求,接收文件(HTML、JS、CSS、图象等)。

    · 解析:对加载到的资源(HTML、JS、CSS等)进行语法解析,建议相应的内部数据结构(比如HTML的DOM树,JS的(对象)属性表,CSS的样式规则等等)

    浏览器的同源策略

     提到跨域不能不先说一下”同源策略”。 
    何为同源?只有当协议、端口、和域名都相同的页面,则两个页面具有相同的源。只要网站的 协议名protocol、 主机host、 端口号port 这三个中的任意一个不同,网站间的数据请求与传输便构成了跨域调用,会受到同源策略的限制。 
     同源策略限制从一个源加载的文档或脚本如何与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的关键的安全机制。浏览器的同源策略,出于防范跨站脚本的攻击,禁止客户端脚本(如 JavaScript)对不同域的服务进行跨站调用(通常指使用XMLHttpRequest请求)。

    跨域请求方式

    解决跨域问题,最简单的莫过于通过nginx反向代理进行实现,但是其需要在运维层面修改,且有可能请求的资源并不再我们控制范围内(第三方),所以该方式不能作为通用的解决方案,下面阐述了经常用到几种跨域方式:

     JSONP(JSON with Padding)是数据格式JSON的一种“使用模式”,可以让网页从别的网域要数据。根据 XmlHttpRequest 对象受到同源策略的影响,而利用 <script>元素的这个开放策略,网页可以得到从其他来源动态产生的JSON数据,而这种使用模式就是所谓的 JSONP。用JSONP抓到的数据并不是JSON,而是任意的JavaScript,用 JavaScript解释器运行而不是用JSON解析器解析。所有,通过Chrome查看所有JSONP发送的Get请求都是js类型,而非XHR。 
    spacer.gif
    缺点:

    只能使用Get请求

    不能注册success、error等事件监听函数,不能很容易的确定JSONP请求是否失败

    JSONP是从其他域中加载代码执行,容易受到跨站请求伪造的攻击,其安全性无法确保

     

     

     

     

     




    查看原文:http://www.coder306.cn/?p=128
    展开全文
  • 在上篇文章中了解到了Java前端编译 JIT编译 AOT编译各有什么优点和缺点,下面详细了解Java前端编译:Java源代码编译成Class文件的过程;我们从官方JDK提供的前端编译器javac入手,用javac编译一些测试程序,调试跟踪...

    Java编译(二)Java前端编译:

    Java源代码编译成Class文件的过程

          

          在上篇文章《Java三种编译方式:前端编译 JIT编译 AOT编译》中了解到了它们各有什么优点和缺点,以及前端编译+JIT编译方式的运作过程。

           下面我们详细了解Java前端编译:Java源代码编译成Class文件的过程;我们从官方JDK提供的前端编译器javac入手,用javac编译一些测试程序,调试跟踪javac源码,看看javac整个编译过程是如何实现的。

    1、javac编译器

    1-1、javac源码与调试

           javac编译器是官方JDK中提供的前端编译器,JDK/bin目录下的javac只是一个与平台相关的调用入口,具体实现在JDK/lib目录下的tools.jar。此外,JDK6开始提供在运行时进行前端编译,默认也是调用到javac,如图:

           javac是由Java语言编写的,而HotSpot虚拟机则是由C++语言编写;标准JDK中并没有提供javac的源码,而在OpenJDK中的提供;我们需要在Eclipse中调试跟踪javac源码,看整个编译过程是如何实现的。

           javac编译器源码下载(JDK8):http://hg.openjdk.java.net/jdk8u/jdk8u-dev/langtools/archive/tip.tar.bz2

           javac编译器源码目录:**\src\share\classes\com\sun\tools\javac

           在Eclipse新建工程导入后,可以看到javac源码的目录结构如下:

           javac编译器程序入口:com.sun.tools.javac.Main类中的main()方法;

           运行javac程序,先是解析命令行参数,由com.sun.tools.javac.main.Main.compile()方法处理,代码片段如下:

           因为没有给参数,可看到输出的是javac用法,如下:

           这就是平时我们用JDK/bin/javac的用法,更多javac选项用法请参考:http://docs.oracle.com/javase/8/docs/technotes/tools/unix/javac.html

           调试编译文件,需要右键工程 -> Debug As -> Debug Configurations ->切换到Arguments选项卡,在Program arguments中输入我们要用javac编译的Java程序文件的路径即可;然后就可以打断点Debug运行调试了,如图:

    1-2、javac编译过程

           JVM规范定义了Class文件结构格式,但没有定义如何从java程序文件转化为Class文件,所以不同编译器可以有不同实现。

           从javac编译器源码来看,其编译过程可以分为3个子过程:

           1、解析与填充符号表过程:解析主要包括词法分析和语法分析两个过程;

           2、插入式注解处理器的注解处理过程;

           3、语义分析与字节码的生成过程;

           如图所示(来自参考4):

           javac编译动作入口: com.sun.tools.javac.main.JavaCompiler类;

           3个编译过程逻辑集中在这个类的compile()和compile2()方法;

           如图所示:

    1-3、javac中的访问者模式

           访问者模式可以将数据结构和对数据结构的操作解耦,使得增加对数据结构的操作不需要修改数据结构,也不必修改原有的操作,而执行时再定义新的Visitor实现者就行了。

           Javac经过第一步解析(词法分析和语法分析),会生成用来一棵描述程序代码语法结构的抽象语法树,每个节点都代表程序代码中的一个语法结构,包括:包、类型、修饰符、运算符、接口、返回值、甚至注释等;而后的不同编译阶段都定义了不同的访问者去处理该语法树(节点)。

           了解这些更容易理解javac的编译过程实现,而后面分析过程中会再对访问者模式的实现作相关说明。

    2、解析与填充符号表

    2-1、解析:词法、语法分析

          解析包括:词法分析和语法分析两个过程;

    2-1-1、词法分析

    1、概念解理

          词法分析是将源代码的字符流转变为标记(Token)集合;

          标记:

        标记是编译过程的最小元素;

        包括关键字、变量名、字面量、运算符(甚至一个".")等;

    2、源码分析:                                

           由com.sun.tools.javac.parser.Scanner类实现对外部提供服务;

           由com.sun.tools.javac.parser.JavaTokenizer类实现具体的Token分析动作(JavaTokenizer.readToken()方法);

           Scanner.nextToken()调用JavaTokenizer.readToken()方法读取下一个Token;    

           返回com.sun.tools.javac.parser.Tokens.Token类实例表示的一个Token;

     

           Scanner.nextToken()方法如下:

              

          注意,下面语法分析时才会不断调用Scanner.nextToken()读取一个个Token进来解析。

    2-1-2、语法分析

    1、概念解理

          语法分析是根据Token序列构造抽象语法树的过程;

          抽象语法树(Abstract Syntax Tree,AST):

        是一种用来描述程序代码语法结构的树形表示方式;

        每个节点都代表程序代码中的一个语法结构;

        语法结构(Construct)包括:包、类型、修饰符、运算符、接口、返回值、甚至注释等;

    2、源码分析:

           由com.sun.tools.javac.parser.JavacParser类完成整个过程,该类实现com.sun.tools.javac.parser.Parser接口;

           一个类文件解析产生的抽象语法树的所有内容保存在JCCompilationUnit类实例里,JCCompilationUnit类是由com.sun.tools.javac.tree.JCTree类扩展;

           JCTree是个抽象类,实现了Tree接口,Tree接口里有一个"<R,D> R accept(TreeVisitor<R,D> visitor, D data)"方法用来接收访问者,所以Tree接口是访问者模式中的抽象节点元素

           JCTree类中有一个Visitor内部类,同时也是一个抽象类,作为访问者模式中的抽象访问者

           一个JCTree类实例相当于抽象语法树的一个节点,它会扩展许多类型,对应不同语法结构类型的树节点,如JCStatement,JCClassDecl,JCMethodDecl,JCBlock等等,这些类是访问者模式中的具体节点元素

           JCTree扩展的JCMethodDecl方法类型节点结构如下:

           

           代码执行的解析过程,如下:    

    1)、由JavaCompiler.compile()方法调用JavaCompiler.parseFiles()方法完成参数输入的所有文件的编译;

    2)、JavaCompiler.parseFiles()方法中又调用本类中的parse()方法对其中一个文件进行编译;

           该方法中生成JavacParser类实例,然后调用该实例的parseCompilationUnit()方法开始进行整个文件的解析(包括"package"包名),如下:

    Parser parser = parserFactory.newParser(content, keepComments(), genEndPos, lineDebugInfo);
    tree = parser.parseCompilationUnit();          

           返回的tree是JCCompilationUnit类型实例,保存了一个类文件解析产生的抽象语法树的所有内容,也可以说是抽象语法树的根节点;                

    3)、JavacParser.parseCompilationUnit()方法中调用JavacParser.typeDeclaration()进行文件中所有类型定义的解析;

           JavacParser.typeDeclaration()又调用JavacParser.classOrInterfaceOrEnumDeclaration()进行类或接口的解析;

           如果是类又调用classDeclaration()对该类进行解析....              

    JCTree def = typeDeclaration(mods, docComment);

           返回一个JCTree类实例表示文件中所有类型定义定义的语法树(不包括"package"包名);                

          这期间会不断调用Scanner.nextToken()读取一个个Token进来解析;        

    3、编译测试:

          下面我们用javac编译JavacTest.java文件来跟踪整个解析过程,测试文件代码如下:

          package com.jvmtest;
    
          public class JavacTest {
             private int i;
    
             public int getI() {
                 return i;
             }
    
             public void setI(int i) {
                 this.i = i;
             }    
    
          }

          对于解析JavacTest.java文件生成的抽象语法树,由返回的JCCompilationUnit类实例表示,如下图所示:


           最外层节点为"com.jvmtest"包名的定义,同时它也是语法树的根节点;

           再里一层是"public class JavacTest"类的定义;

           再里面可以看到一个字段变量"i"的结构节点,以及两个方法"getI"和"setI"节点;                

    4、类实例构造函数重名为<init>()

           先在再上面的测试程序中加入类实例构造函数:

          Public JavacTest() {
    
           }

           需要注意的是,在classOrInterfaceBodyDeclaration()解析类时,如果遇到添加的类构造函数,会重名为<init>(),如下:    

          如测试程序中加入类构造函数,可以看到被重命名<init>(),但在生成的树结构上名称还是表现为"JavacTest",如下

      

          经过上面解析,后续所有操作都建立在抽象语法树之上,下面不会再对源码文件操作;

    2-2、填充符号表

    1、概念解理

            符号表(Symbol Table)是由一组符号地址和符号信息构成的表格,可以想象成哈希表中K-V值的形式;                

            符号表登记的信息在编译的不同阶段都要用到,如:

                1)、用于语义检查和产生中间代码;

                2)、在目标代码生成阶段,符号表是对符号名进行地址分配的依据;

    2、源码分析:

          根据上一步生成的抽象语法树列表,由JavaCompiler.enterTrees()方法完成填充符号表;

          由com.sun.tools.javac.comp.Enter类实现填充符号表动作,Enter类继承JCTree.Visitor内部抽象类,重写了一些visit**()方法来处理抽象语法树,作为访问者模式中的具体访问者;


           符号由com.sun.tools.javac.code.Symbol抽象类表示, 实现了Element接口,Element接口里有一个accept()方法用来接收访问者,所以Element接口是访问者模式中的抽象节点元素;

          Symbol类扩展成多种类型的符号,如ClassSymbol表示类的符号、MethodSymbol表示方法的符号等等,这些类是访问者模式中的具体节点元素

          Symbol类和MethodSymbol类定义如下:

              public abstract class Symbol extends AnnoConstruct implements Element {
    
                 /** The kind of this symbol.
                  * @see Kinds
                  */
                  public int kind;
                       
                  /** The flags of this symbol.
                  */
                  public long flags_field;
    
                  /** An accessor method for the flags of this symbol.
                  * Flags of class symbols should be accessed through the accessor
                  * method to make sure that the class symbol is loaded.
                  */
                  public long flags() { return flags_field; }
                        
                 /** The name of this symbol in Utf8 representation.
                  */
                  public Name name;
          
                  /** The type of this symbol.
                  */
                  public Type type;
      
                  /** The owner of this symbol.
    
                  */
                  public Symbol owner;
                 
                  /** The completer of this symbol.
    
                  */
                  public Completer completer;
                        
                 /** A cache for the type erasure of this symbol.
                  */
                  public Type erasure_field;
                       
                  // <editor-fold defaultstate="collapsed" desc="annotations">
                   
                  /** The attributes of this symbol are contained in this
                  * SymbolMetadata. The SymbolMetadata instance is NOT immutable.
                  */
                  protected SymbolMetadata metadata;
          
                     ......
    
                  }

             /** A class for method symbols.
             */
    
             public static class MethodSymbol extends Symbol implements ExecutableElement {
       
                 /** The code of the method. */
                 public Code code = null;
         
                 /** The extra (synthetic/mandated) parameters of the method. */
                 public List<VarSymbol> extraParams = List.nil();
                 
                 /** The captured local variables in an anonymous class */
                 public List<VarSymbol> capturedLocals = List.nil();
                 
                 /** The parameters of the method. */
                 public List<VarSymbol> params = null;
                 
                 /** The names of the parameters */
                 public List<Name> savedParameterNames;
        
                 /** For an attribute field accessor, its default value if any.
                 * The value is null if none appeared in the method
                 * declaration.
                 */
                 public Attribute defaultValue = null;
    
                     ......
    
                 }

          从上面可以看到它们包含了哪些信息;

          代码执行的填充过程,如下:    

            1)、JavaCompiler.enterTrees()方法调用Enter.main()方法;

                  根据上一步生成的抽象语法树列表完成填充符号表,返回填充了类中所有符号的抽象语法树列表;

            2)、Enter.main()方法调用中本类的complete()方法;

                   complete()方法先调用Enter.classEnter()方法完成填充包符号、类符号以及导入信息等;

            3)、接着complete()方法还会不断调用前面生成的每个类的类符号实例的ClassSymbol.complete()方法;

                   ClassSymbol.complete()方法会调用到MemberEnter.complete(),以完成整个类的填充符号表;

            4、MemberEnter.complete()中会添加类的默认构造函数(如果没有任何的);

                   还会调用 MemberEnter.finish()方法完成对类中字段和方法符号的填充;

                   等等(其实先处理注解信息)...

          注意,EnterTrees()方法最终完成返回一个待处理列表("todo" list),其实该列表还是抽象语法树列表,符号只是填充到上一步生成的抽象语法树列表中;可以从上面语法分析给出的JCMethodDecl类中看到有一个MethodSymbol类的成员变量;

    3、编译测试

          还用上面的JavacTest.java文件测试,其中getI()方法的符号如下(显示符号名称):

          测试JavacTest.java文件填充符号表的前后,抽象语法树列表变化(红色)如下:

    4、计算方法的特征签名

          其实MethodSymbol方法符号中的MethodType类型的type成员就是其特征签名;

          在.MemberEnter.visitMethodDef(JCMethodDecl tree)中填充方法符号的时候计算特征签名,如下:

             public void visitMethodDef(JCMethodDecl tree) {
                     ......
    
                     MethodSymbol m = new MethodSymbol(0, tree.name, null, enclScope.owner);
                     ......
                     // Compute the method type
                     m.type = signature(m, tree.typarams, tree.params,
                                               tree.restype, tree.recvparam,
                                               tree.thrown,
                                               localEnv);
                      ......
              }

          MethodType如下:

             public static class MethodType extends Type implements ExecutableType {
                 
                 public List<Type> argtypes;
                 public Type restype;
                 public List<Type> thrown;
                 
                 /** The type annotations on the method receiver.
                 */
                 public Type recvtype;
          
                 public MethodType(List<Type> argtypes,
                    Type restype,
                    List<Type> thrown,
                    TypeSymbol methodClass) {
    
                      super(methodClass);
    
                      this.argtypes = argtypes;
                      this.restype = restype;
                      this.thrown = thrown;
    
                 }
    
                 ......
    
         }

          可以看到特征签名包含了返回值类型,其实方法特征签名在Java语言层面和JVM层面是不同的:

          Java语言层面特征签名:

          方法名、参数类型和参数顺序;                            

          JVM层面特征签名:

          方法名、参数类型、参数顺序和返回值类型;

          这个在后面文章介绍Class文件格式再详细说明;

    5、添加默认类实例构造函数、"this"类变量符号、"super"父类变量

          这个阶段,编译器自动添加默认类实例构造函数、"this"类变量符号、"super"父类变量符号:

          (a)、如果类中没有定义任何实例构造函数,编译器会自动添加默认的类实例构造函数;

          在完成一个类的填充符号时调用:

           MemberEnter.complete(Symbol sym){
                 ......
                 // Add default constructor if needed.
                 if ((c.flags() & INTERFACE) == 0 &&
                 !TreeInfo.hasConstructors(tree.defs)) {
                      ......
                      if (addConstructor) {
    
                         MethodSymbol basedConstructor = nc != null ?
                            (MethodSymbol)nc.constructor : null;
                                 JCTree constrDef = DefaultConstructor(make.at(tree.pos), 
                                                                      c, basedConstructor, typarams,
                                                                      argtypes, thrown, ctorFlags, based);
    
                                 tree.defs = tree.defs.prepend(constrDef);
                       }
                        ......
                }
                ......
           }

          测试JavacTest.java文件添加的实例构造函数如下:

          可以看到添加的类实例构造名称为<init>(),虽然树结构上名称还是表现为"JavacTest";

          还有添加的时候会判断当前类的类型如果不是Object类型,都会在构造函数里添加"super();",表示调用父类的构造函数,如下:

          (b)、添加"this"类变量

          在类实例作用域添加"this"符号,表示当前类实例,如下:

          (c)、"super"父类变量符号

          接着,在类实例作用域添加"super"符号,表示类父,如下:

    3、插入式注解处理器的注解处理过程

          JDK1.5后,Java语言提供了对注解(Annotation)的支持,注解和Java代码一样,可以在运行期间发挥作用;

          JDK1.6中提供一组插件式注解处理器的标准API(JSR 269: Pluggable AnnotationProcessing API),取代 APT(JEP 117: Remove the Annotation-ProcessingTool),可以实现API自定义注解处理器,干涉编译器的行为;

          注解处理器可以看作编译器的插件,在编译期间对注解进行处理,可以对语法树进行读取、修改、添加任意元素;但如果有注解处理器修改了语法树,编译器将返回解析及填充符号表的过程,重新处理,直到没有注解处理器修改为止,每一次重新处理循环称为一个Round。

          如Hibernate Validator Annotation Process:用于校验Hibernate标签。

    1、源码分析

          注解处理器的初始化过程在JavaCompiler.initProcessAnnotations()方法中完成;

          执行过程则是JavaCompiler.processAnnotations()方法;

          如果有多个注解处理器,在JavacProcessingEnvironment.doProcessing()继续处理;

    2、注解处理器实现与运行

          代码实现:继承抽象类javax.annotation.processing.AbstractProcess,并覆盖abstract方法:"process()";

          运行/测试:通过javac -processor参数附带编译时的注解处理器;

     

          这里我们没有实现注解处理器,运行javac编译JavacTest.java不会处理语法树;

    4、语义分析与字节码生成

          上面我们获得了填充了符号表的抽象语法树列表;

          它能表示程序的结构,但无法保证程序的符合逻辑。

    4-1、语义分析

          主要任务是对结构上正确的源程序进行上下文有关性质的审查(如类型审查);

          语义分析过程分为标注检查、数据及控制流分析两个步骤;

    4-1-1、标注检查

    1、概念解理

          标注检查步骤检查的内容包括变量使用前是否已被声明、变量与赋值的数据类型是否能匹配等;        

          还有比较重要的动作称为常量折叠;

          如前面测试程序"int i;"改为"int i=1+2;",会被折叠成字面量"3",与"int i=3"一样,如图:

    2、源码分析

          主要由com.sun.tools.javac.comp.Attr类和com.sun.tools.javac.comp.Check类完成,调用关系如下图:

          由JavaCompiler.attribute()入口分析整个类的语法树的标注;        

          到Attr.attribClassBody()分析类的主体部分,如进行所有定义的检查:

          comp.Check类的实例在Attr.attribClassBody()分析中进行定义、类型等检查;

          如"boolean k = 1",最终是通过类型检查赋值数据"1"的类型"int"不是接收者"k"的类型"Boolean"的父类来确定错误,如下:

    3、自动添加super():

          方法检查时,如果发现(自己定义的)类实例构造函数没有显式调用super()或this(),会添加super()的父类构造函数调用,如下:

          但是,前面说过如果没有自定定义任何构造函数,前面填充符号表时,就已经添加含有super()的默认构造函数了;

    4、标注检查结果            

          标注检查中已经使用Env<AttrContext>类实例作为类编译信息的存储形式,它包含了一些访问上下文环境。

          还是前面的测试程序,标注检查前后变化(红色)如下:

    4-1-2、数据及控制分析

    1、概念解理    

          数据及控制分析是对程序上下方逻辑更进一步的验证;        

          如检查变量的初始化、方法每个执行分支是否都有返回值、是否所有的异常都被正确处理等;

          注意这阶段并不会对变量赋值;

     

          这个时期与类加载时的数据及控制分析的目的一致,但校验范围不同;

          如final修饰的局部变量:

          final修饰的局部变量是在这个编译阶段处理的;

          有没有final修饰符,编译出来的Class文件都一样,在常量池没有CONSTANT_Fiedref_info称号引用;

          即在运行期没有影响,参数不变性由编译器在编译期保障;

    2、源码分析

          主要由 com.sun.tools.javac.comp.Flow类实现;

          调用关系如下:


          主要在其analyzeTree()方法中完成分析,如下: 

     public void analyzeTree(Env<AttrContext> env, TreeMaker make) {
    
             //1、活性分析:检查每个语句是否可访问;
    
            new AliveAnalyzer().analyzeTree(env, make);
    
            //2、(i)、赋值分析:检查确保每个变量在使用前已被初始化;
            //   (ii)、未赋值分析:检查确保final修饰变量的不变性(不会被第二次赋值);
            //    还用于标记"effectively-final"局部变量/参数;
            //    使用活性分析的结果;
    
            new AssignAnalyzer().analyzeTree(env);
    
            //3、异常分析:检查确保每个异常被抛出、声明或捕获;
            //    需要使用活性分析设置的一些信息;
    
            new FlowAnalyzer().analyzeTree(env, make);
    
            //4、"effectively-final"分析:这检查每个来自lambda body/local内部类的局部变量引用是"final or effectively";
            //    由于effectively final变量在DA/DU期间被标记,所以该步骤必须在AssignAnalyzer之后运行;
    
            new CaptureAnalyzer().analyzeTree(env, make);
    
    }

    1)、活性分析

          new AliveAnalyzer().analyzeTree(env, make);

          检查每个语句是否可访问;

                         

          它里面有一个方法makeDead(),它的调用关系如下:

          可以看到访问到return/break/continue以及thorw关键字,就会调用标记后面的语句不能再访问;

          如果还有就会发现编译错误:

    (A)、如程序中,方法return后,还有逻辑,就会发生错误,如下:

    public void setI(int i) {        
         return ;
         this.i = i;
    }

          会发生:错误:无法访问的语句(unreachable stmt),如图:

    (B)、还有throw的情况,如在类普通块中直接抛出异常:

    {
          throw new RuntimeException();        
    } 
    
          会发生:错误: 初始化程序必须能够正常完成(error: initializer must be able to complete normally),如图


    2)、赋值分析

          new AssignAnalyzer().analyzeTree(env);    

          检查确保每个变量在使用前已被初始化;

          检查确保final修饰变量的不变性(不会被第二次赋值);

          注意,如果实例成员方法中为final成员变量赋值,会在标注检查阶段分析出错误;

                                                 

          这里的检查主要是对象final修饰的变量,下面我们用另一个程序编译测试,如下:

             public class JavacTest {
    
                public static int s_uinit;
                public static int s = 1;                              
    
                public final int f_uinit; //错误: 变量f_uinit未在默认构造器中初始化
                public final int f = 2;
                                        
                public static final int sf_uinit; //错误: 变量sf_uinit未在默认构造器中初始化
                public static final int sf = 3;
                                    
                private int i_uinit;
                private int i = 4;
                                       
                public void test(final int methodParam_f) {    
    
                    final int method_f_uinit;
                    final int method_f = methodParam_f;
                                            
                    this.i = method_f_uinit;   //错误: 可能尚未初始化变量method_f_uinit                                  
                    this.i = method_f;
                                            
                    method_f_uinit = 1;    
                    method_f_uinit = 2;    //错误: 可能已分配变量method_f_uinit
    
                    //f_uinit = 12;   //错误(属于标注检查错误)
                }                
            }

           这个程序编译会出现四个错误,我们看下是怎么检查的:      

          AssignAnalyzer里面有一个trackable()方法,说明这里应该关注什么样的字段/变量符号的初始化;

           从它实现中可以看出检查主要是对象final修饰的字段/变量,如下:

           还有一个newVar()方法,当然发现应该关注检查的字段/变量后,newVar()方法会把这个符号记录下来;

           它在三个地方调用,在visitClassDef()里检查static类字段和非static类实例字段时,以及在visitVarDef()检查方法中的变量及参数,调用如下:

    A)、检查static类字段和非static类实例字段

           从上图可以看到,先是检查static类字段和非static类实例字段,把关注的未进行初始化的final字段记录下来;

           而后再检查方法,先是检查类实例构造方法;

           这时会把前面记录的字段,通过checkInit()再次检查确认;

           如果的确定是未进行初始化的final字段,报告相关错误,如下:

          错误: 变量 sf_uinit 未在默认构造器中初始化(var not initialized in default constructor)

          错误: 变量 f_uinit 未在默认构造器中初始化

    (B)、检查方法的传入参数

           接着还是visitMethodDef()检查类中的方法(访问者模式);

           先检查方法参数,如下:

           虽然scan()中检查并记录了测试程序test()方法methodParam_f参数,但是下面立刻调用initParam()删除了记录;

           所以方法的final参数未初始化并不影响下面的使用;

           可以认为运行时传入的final参数都是赋值初始化了的;

    (C)、检查方法中的变量定义

           接着检查方法体中定义的变量;

           其中method_f_uinit变量未初始化,被记录下来;

                                        

           而method_f变量虽然开始被记录下来,

           但它初始化为methodParam_f参数值,所以立即调用letInit()删除了相关记录,如下图:

           所以下面它也可以被使用(this.i = method_f);

    (D)、检查方法运行中的变量使用

           注意,上面检查方法中的参数和变量,只是记录下来定义时未初始化final变量,这里才是检查使用前已被初始化;

           可以看到method_f_uinit变量在上面被记录下来,使用时作为"Ident"检查;

           在visitIdent()中调用checkInit()确定其未初始化,然后打印错误,如下:

          错误: 可能尚未初始化变量method_f_uinit(var might not have been initialized);

                                   

           而method_f变量初始化为methodParam_f,未被记录,所以checkInit()检查通过,正常使用;

    (E)、检查final修饰变量不会被二次赋值

           注意,如果实例成员方法中为final成员变量赋值(方法中f_uinit = 12),会在标注检查阶段分析出错误;

           但在类块{}中为未初始化的final成员变量赋值(相当于在构造函数赋值),也会发生检查二次赋值的情况;

                                        

           方法中两次为method_f_uinit变量赋值;

           检查赋值操作是visitAssign()方法,里面会为左值method_f_uinit变量调用letInit();

           第一次因为定义时没有初始化,所以letInit()中调用uninit()把前面定义时未初始化的记录删除;

           第二次因为没有了记录,所以letInit()中打印出错误,如图:

           错误:可能已分配变量method_f_uinit(var might already be assigned);

           正如前面说的:

          final修饰的局部变量是在这个编译阶段处理的;

          有没有final修饰符,编译出来的Class文件都一样,在常量池没有CONSTANT_Fiedref_info称号引用;

          即在运行期没有影响,参数不变性由编译器在编译期保障;

    4-2、解语法糖

    1、概念解理    

           语法糖(Syntactic Sugar)也称粮衣语法;

           对象语言功能没有影响,只是简化程序,提高效率,增可读性,减少出错;

           但使得程序员难以看清程序的运行过程;

                    

           Java最常用的有:

           泛型、变长参数、自动装箱/拆箱、遍历循环、内部类、断言等;                

           JVM不支持这些语法;

           在编译阶段还原回简单的基础语法结构,称为解语法糖

    2、源码分析                

           入口调用com.sun.tools.javac.main.JavaCompiler.desugar()完成;

           主要由com.sun.tools.javac.comp.TransTypes类和com.sun.tools.javac.comp.Lower类实现;

        

    3、泛型与类型擦除

           拿泛型来说,泛型是JDK1.5的新增特性;

           本质是参数化类型(Parametersized Type)的应用;

           即所操作的数据类型被指定为一个参数;

                

           可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口和泛型方法;   

    (A)、Java泛型与C#泛型

           C#的泛型在程序、编译后、运行期都是存在的;

           对于List<int>和List<String>是两种不现的类型,在运行期有自己的虚方法表和类型数据;

           这种实现方法称为类型膨胀,基于这种方法实现的泛型称为真实泛型

                    

           Java语言泛型在编译后的字节码文件中,就被替换为原来的原生类型(Raw Type);

           并在相应地方插入强制转型代码;

           对于ArrayList<int>和ArrayList<String>,在运行期是同一种类型;

           这种实现方法称为类型擦除,基于这种方法实现的泛型称为伪泛型

    (B)、运行时识别(反射)泛型参数类型

           对于泛型类型擦除后,需要在运行时识别(反射)泛型参数类型的问题:

           JVM规范引入了Signature、LocalVariableTypeTable等Class属性;

           Signature存储一个方法在字节码层面的特征签名,保存了参数化类型的信息;

          这也是能通过反射手段取得参数化类型的根本依据;

           相关Class属性会在后面文章介绍Class文件格式时再说明;

    (C)、编译测试

           测试程序JvmTest10_2.java,如下:

    package com.jvmtest;
    
    import java.util.HashMap;
    import java.util.Map;
    
    public class JvmTest10_2 {
    
        public static void main(String[] args) {
            Map<String, String> map = new HashMap<String, String>();
            map.put("hello", "java");
            System.out.println(map.get("hello"));
        }    
    }

          泛型擦除调用关系及关键代码,如下:

          测试程序中泛型经过编译类型被擦除,如下:

    4-3、字节码生成

    1、概念理解

          把前面生成的语法树、符号表等信息转化成字节码,然后写到磁盘Class文件中;

    2、源码分析

          由com.sun.tools.javac.jvm.Gen类实现添加代码和转换字节码;

          入口调用com.sun.tools.javac.jvm.Gen.genClass(),调用关系如下:

          完成转换后,由com.sun.tools.javac.main.JavaCompiler的writer()方法写到磁盘Class文件,如下:

    3、类构造器<clinit>()与实例构造器<init>()

          另外,还进行了少量代码添加,如类构造器<clinit>()到语法树中;

          注意,通过前面的分析可以知道,对于实例构造器<init>(),如果程序代码中定义有构造函数,它在解析的语法分析阶段被重命名为<init>();如果没有定义构造函数,则实例构造器<init>()是在填充符号表时添加的。

          并把需要初始化的变量以及需要执行的语句块添加到相应的构造器中;

                    

          Gen.genClass()中会调用Gen.normalizeDefs()方法,进行添加实例构造器<init>()和类构造器<clinit>()到语法树;

           用下面的程序测试Parent.java和Child.java,看添加了什么内容到两个构造器中,Parent.java如下:

        package com.jvmtest;
    
        public class Parent {
    
            static int i = 1;                      
    
            {
                System.out.println("父类实例块1:" + i++);
            }
                             
            static {
                System.out.println("父类静态块1:" + i++);
            }
                                    
            private String mStr1="父类实例变量1:" + i++;
            private static String mStaticStr1="父类静态类变量1:" + i++;
                           
            {
                System.out.println("父类实例块2:" + i++);
            }
                              
            static {
                System.out.println("父类静态块2:" + i++);
            }
                               
            private String mStr2="父类实例变量2:" + i++;
            private static String mStaticStr2="父类静态类变量2:" + i++;
                                    
            public Parent() {
                System.out.println("父类构造器:" + i++);
            }
                           
            public void print1() {
                String str="父类局部变量:" + i++;
                              
                System.out.println("父类方法print1():\n " +
                        mStaticStr1 + "\n " + mStaticStr2 + "\n " +
                        mStr1 + "\n " + mStr2 + "\n " +
                        str);
            }
        }

          Child.java如下:

        package com.jvmtest;
      
        public class Child extends Parent{
                     
            {
                System.out.println("子类实例块1:" + i++);
            }
                               
            static {
                System.out.println("子类静态块1:" + i++);
            }
                                  
            private String mStr1="子类实例变量1:" + i++;
            private static String mStaticStr1="子类静态类变量1:" + i++;
                            
            {
                System.out.println("子类实例块2:" + i++);
            }
                               
            static {
                System.out.println("子类静态块2:" + i++);
            }
                              
            private String mStr2="子类实例变量2:" + i++;
            private static String mStaticStr2="子类静态类变量2:" + i++;
                                                         
            public Child() {
                System.out.println("子类构造器:" + i++);
            }
                            
            public void print2() {
    
                String str="子类局部变量:" + i++;
                                     
                System.out.println("子类方法print2():\n " +
                         mStaticStr1 + "\n " + mStaticStr2 + "\n " +
                         mStr1 + "\n " + mStr2 + "\n " +
                            str);
            }
                          
            public static void main(String[] args){
                 Child child = new Child();
                 child.print1();
                 child.print2();                            
            }
       }
    

    1)、它先把一个类的定义声明符号分为三类保存

          A、initCode:保存需要初始化执行的实例变量和块(非static);

          B、clinitCode:保存需要初始化执行的类变量和块(static);

          C、methodDefs:保存方法定义符号;

          程序代码分类后的如下:

    2)、把initCode中的定义插入到实例构造器<init>()中

          注意,对于实例构造器<init>(),如果程序代码中定义有构造函数,它在解析的语法分析阶段被重命名为<init>();

          如果没有定义构造函数,则实例构造器<init>()是作为默认构造函数,是在填充符号表时添加的;

                            

          另外<init>()中的super()调用父类<init>(),

          在语义分析的标注检查在方法检查时,如果发现自己定义的类构造函数没有显式调用super()或this(),会添加super()的父类构造函数调用;            

          如果没有自定定义任何构造函数,在前面填充符号表时添加的默认构造函数就已经含有super()了;

              

          initCode插入<init>()原有代码的前面;

          添加后的<init>()如下:

    3)、把clinitCode中的定义插入到类构造器<clinit>()中

          类构造器<clinit>()是在这时候创建的;

          然后clinitCode插入到<clinit>(),再把<clinit>()放到方法定义methodDefs的后面,如下:

          可以看到,<clinit>()并不调用父类的<clinit>(),这是由JVM保证的其执行;                        

          我们运行上面的程序,可以看到输出(后面的数字表明自执行顺序):

    测试表明执行顺序如下:

           先执行类构造器<clinit>():

                  父类静态成员变量初始化、静态语句块(static{})执行;

                  子类静态成员变量初始化、静态语句块(static{})执行;

                                

                  静态成员变量与静态语句块不区分,按照在代码中的位置顺序执行;

                                

                  不调用父类的类构造器,由JVM保证其执行;

            而后执行实例构造器<init>():

                  父类实例成员变量初始化、实例语句块({})执行;

                  父类实例构造器调用;

                  实例成员变量初始化、实例语句块({})执行;

                                

                  实例成员变量、实例语句块不区分,按照在代码中的位置顺序执行;

                                

                   <init>()无论如何(自定义或编译器添加)都有父类<init>()(super())调用;

                   而由于initCode插入<init>()原有代码的前面,所以实例成员变量初始化、实例语句块({})执行输出要先于构造器原来的代码执行输出;

                                

             即按照先父类,后子类;先静态、后实例的原则;

             另外,<clinit>()是在Class文件被类加载器加载的时候(初始化阶段)执行,并且只执行一次(加锁 );而<init>()在每次实例化对象时都会执行。

     


          到这里,我们大体了解javac把Java源代码编译成Class文件的过程,可以用JDK提供的javap工具查看反编译后的文件,如查看JavacTest.class文件:"javap -verbose JavacTest > JavacTest.txt"输出到文件、

          后面我们将分别去了解: 前端编译生成的Class文件结构、以及JIT编译--在运行时把Class文件字节码编译成本地机器码的过程……

     

    【参考资料】

    1、javac源码

    2、《编译原理》第二版

    3、《深入分析Java Web技术内幕》修订版 第4章

    4、《深入理解Java虚拟机:JVM高级特性与最佳实践》第二版 第10章

    5、《The Java Virtual Machine Specification》Java SE 8 Edition:https://docs.oracle.com/javase/specs/jvms/se8/html/index.html

    6、实时Java,第2部分: 比较编译技术--本地 Java 代码的静态编译和动态编译中的问题:www.ibm.com/developerworks/cn/java/j-rtj2/

    7、很多文章都提到JVM对class文件的编译,那么编译后的文件是在内存里还是在哪?怎么查看?:https://www.zhihu.com/question/52487484/answer/130785455

       

       

    
    
    
    
    展开全文
  • java前端传到数据库显示问号?

    前端中文传到数据库显示问号

    在这里插入图片描述

    解决方法:

    1. 数据库连接时的编码问题

    在数据库连接语句后边加入如下代码:

    ?useUnicode=true&characterEncoding=UTF8
    

    在这里插入图片描述

    2.数据库本身编码问题

    修改mysql数据库编码设置,修改mysql的配置文件my.ini

    default-character-set=utf8
    

    在这里插入图片描述

    展开全文
  • Java编译(二)Java前端编译:Java源代码编译成Class文件的过程 在上篇文章《Java三种编译方式:前端编译 JIT编译 AOT编译》中了解到了它们各有什么优点和缺点,以及前端编译+JIT编译方式的运作过程。 下面我们...
  • java前端向后端传送实体类数据 使用post向后端传送数据时, 当在params中填写参数,将会以get形式将参数写在url中,无法传过去,需要在body中填写json参数 后端使用@requestbody注解接收 时间2020.10.13 ...
  • 自学java 前端

    千次阅读 2018-07-14 14:32:11
    一个对自学java前端的小哥哥和小姐姐很有帮助的教程网站,里面除了知识点的讲解,还有各种框架的讲解。每一个知识点讲解完都会有对应的阶段性练习,而且还有各种实践项目。 不懂的地方还可以在站内向站长提问,也...
  • java 前端及后台轮询方法总结

    千次阅读 2016-05-11 17:07:48
    java 前端及后台轮询方法总结 前端实现轮询 代码块//定时器开始 $scope.start = function () { timer = function clock() { findFundNetValueByProId(function (result) { $scope.listEntitys=result; $scop
  • 资料整理:java 前端页面AES加密数据 后端AES解密
  • 我刚入这行不久,小学生一枚,现在有个Java的问题如下: http://zhidao.baidu.com/question/1242054694549391659.html 我挂在百度问答上了,会的大神们可以大体上先看一下,我的问题主要一个是: 1.那个用js写的大致...
  • Eclipse JAVA 前端/后端页面跳转乱码问题排查(一)前端页面传值到后端,后端接收数据产生乱码 (1)检查tomcat:server.xml (2)可能存在编码与解码,成对进行编解码(二)后端传值回前端页面,页面出现乱码...
  • 现在突然之间要我转做前端,我心里没底,不知道现在做前端和后端哪个发展更好些。如果我现在转做前端对我以后跳槽好吗?因为我现在在的是外包公司,我不可能在外包公司一直呆下去.... 求大家给点建议
  • Java前端模版介绍——FreeMarker

    千次阅读 2016-11-25 17:07:21
    模版对于前后端开发的分离十分重要,通俗的说,模版就是一种语法规则,前端在开发时只需要关注自己的部分,如...FreeMarker就是这样的一个模版,它针对的后台是Java。其工作流程为,前端通过HTML文件改造的FreeMarke
  • java 前端 需要引入js文件 引入网址和下载下来然后直接引入本地js文件 哪个好 哪个对项目性能好
  • java前端开发JD

    千次阅读 2018-02-10 08:23:17
    6.熟悉并掌握至少一款主流的前端开发框架(Vue、AngularJs、React)等,了解MVC开发模式; 7.熟悉Tomcat、Nginx中任一种应用服务器的开发和相关配置; 8.熟练使用svn,git,tfs 等任意一种管理工具 9.有良好的编码...
  • Java前端和后端的区别

    万次阅读 2018-08-12 15:39:01
    关于前端技术 前端的话我想主要是指jsp,页面显示给用户的, 前端技术的话比如ajax,javascript,EL这些技术 关于后端技术 后端的话主要是对于业务的控制和处理,比如访问数据库,进行数据更新查看修改等等. ...
  • java前端用的bootstrap,有没有好的办法实现视频上传视频上传和播放功能????
  • java前端左右移动数据

    2016-07-05 09:01:00
    ![图片说明]...哪位大神,会做这个左右移动数据,然后把右边的数据保存到数据库里,并且再次打开 这个页面,就会显示已经选择的元数在右边,求一个类似的后台的controller实现方法
  • Java 前端请求下载文件

    千次阅读 2019-01-09 17:16:04
  • Java前端请求后台跨域问题解决

    千次阅读 2018-03-31 12:29:24
    当前端页面与后台运行在不同的服务器时,就必定会出现跨域这一问题,本篇简单介绍解决跨域的一种常用方案1、错误如图,前端请求后台报错2、Java后台添加Filter解决,代码如下import java.io.IOException; import ...
  • Java中的面试题 [6] --- Java前端知识、模板、css、jsp、JavaScript、vue.js等 1、写出一个JQuery的插件代码结构。 2、简单描述一下ajax通信过程及ajax限制。 3、简述一次http请求过程及浏览器和后台交互大概报文。
  • java前端和后端的区别

    千次阅读 2016-12-28 14:55:01
    关于前端技术: 前端技术的话比如ajax,javascript,EL这些技术。 关于后端技术: 后端的话主要是对于业务的控制和处理,比如访问数据库,进行数据更新查看修改等等..
  • 1、首先从后台接到一个list<对象>,然后通过c:forEach遍历成一个table,其中需要每一条信息都有个复选框。 2、通过勾选复选框组成一个list<对象>提交到后台。 1我已经实现了,请问各位大神2该如何实现
  • Java前端到后端教学大集合

    千次阅读 2018-07-09 11:24:21
    Java项目实战: 链接:https://pan.baidu.com/s/1Te6vOnbKtl6LXjRiwnTpQg 密码:x8lplinux系统学习: 链接:https://pan.baidu.com/s/1qiTKWz_gN78t1ABEuPPqIg 密码:8355java开发工具:链接:......
  • Java前端上传图片到后台

    万次阅读 2016-12-14 16:17:42
    简单记录一下前端文件上传到后台的过程,免得到处找; spring-mvc.xml加入配置,这里还可以加入文件的编码格式defaultEncoding属性配置; class="org.springframework.web.multipart.commons.CommonsMultipartR
  • JAVA前端与后端参数传递方法小结

    万次阅读 2019-04-19 14:08:47
    一.Servlet后端传值给前端 使用一些Servlet API进行值的存取操作:HttpServletRequest、HttpSession和ServletContext。Struts2对这个三个对象用Map进行了封装,我们就可以使用Map对象来存取数据了。   1)在Action...
  • java前端上传图片保存到本地文件夹

    千次阅读 2019-06-04 15:04:29
    后端java代码 import org.springframework.context.annotation.Configuration; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springf...
  • Java前端传值后台接收为Date类型

    千次阅读 2019-09-15 08:40:20
    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd", timezone = "GMT+8") 在Date类型属性上加这个注解即可 ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 42,842
精华内容 17,136
关键字:

java前端

java 订阅