专业书P17~37读后提问

1 什么是“内嵌”,内嵌如何消除函数调用

 即内联,“java也有类似c++中inline函数的机制,即在函数调用时直接将被调用函数的代码拷贝副本过来运行,而不进行普通的压栈当前运行地址,压栈参数,跳转执行然后再返回的过程。在早期版本中,使用final关键字可以建议编译器以内联函数的方式处理这个函数。但是在后来的几个版本中,编译器进行了优化,会自动将适合内联的“小函数”以内联的方式处理,而只有在真正需要禁止override的时候才使用final关键字以避免混乱。


2 什么是即时编译器

引用:

简单的回答一下,就以java为例。在jdk 1.0时代,java虚拟完全是解释执行的。那什么是解释执行呢?解释器(你可以理解为翻译器)每次读一代码,就将字节码起转换(翻译)为JVM可执行的指令,一直到最后,说白了边听边译。这样的结果显易见,效率低下,更重要的是同样的代码每次都需要重新翻译。这怎么能忍,必须要解决啊。随着后面的发展,现在大多数的主流的JVM都包含即时编译器。那什么是即时编译器呢?所谓的即时编译器说白了就是将源代码直接生成符合本地物理机可识别的机器语言。还是拿java举例呗,JVM在运行期间会根据代码热度来选择是否代码转换为本地机器语言。当然,这个代码热度的判断相对复杂,不仅仅是某个方法调用的次数达到指定阀值,不同的场景有不同的策略,具体就不说了。即时编译器的好处在于可以对代码进行深度优化,同时提高效率(只编译了一次,以后每次都会调用执行的速度大大提高)

作者:sbbic
链接:https://www.zhihu.com/question/36746487/answer/69645836
来源:知乎
著作权归作者所有,转载请联系作者获得授权。


解释器与编译器的区别。引用图:http://rednaxelafx.iteye.com/blog/492667


引用“实际上很多解释器内部是以“编译器+虚拟机”的方式来实现的,先通过编译器将源码转换为AST或者字节码,然后由虚拟机去完成实际的执行。所谓“解释型语言”并不是不用编译,而只是不需要用户显式去使用编译器得到可执行代码而已。 


3 Java没有头文件,如何实现头文件的功能?

#import确保引用的文件只会被引用一次,这样就不会陷入递归包含的问题中。


你把#include理解成将要include的代码复制粘贴到指定位置就可以了~~~
import 是引入了一个类或包,为了让你使用这个类的小名,不必在任何地方都使用它的大名


说的比较清楚的:http://zhua547.blog.163.com/blog/static/309625220091184578260/(import避免了交叉引用)

引用:http://www.aichengxu.com/java/18973.htm

ava的import与C++的#include浅析,有需要的朋友可以参考下。

相信很多人对这两者的到底有什么不同都不了解,而我搜了一下相关文章,单独介绍的很多,但把这两者做对比的不多。我就根据自己的理解写一下,希望大家指正我的不足。

首先,要说java的import就需要说一下package,package就相当于我们的姓,class就像是我们的名字。package名字很长的时候就类似复姓。例如:java.lang.String,可以理解为复姓为java.lang,名字叫做String。而且,按照Sun的规范取名字,理论上每个人创建的类名字都不会相同,也就不会发生命名冲突。

但是这个时候就出现问题了,复杂的程序和继承使得很多类的名字非常长,这样在写程序的时候就会变得麻烦,于是乎,import就来了。import就是在程序开始的时候说明会用到哪些类,写的时候就可以直接写它的名字,而不用写它的姓了。还是看java.lang.String。如果没有 import java.lang.String 。那么我们在每次创建String对象时都得写java.lang.String string = new blah blah。而现在只需String string=new blah

blah。是不是一下子简单了很多。所以,这就是import的作用,简化代码,编程方便,就这么简单,编译器也不会把import所导入的类写到.class中。具体的分为单类型导入和按需类型导入,本文侧重比较import和#include,不再做具体的介绍了。网上的讲解很多。

C++中,include是预处理器的一部分,所谓预处理,顾名思义就是在编译器之前运行。一个源文件,.c .cpp和它所包含的文件一起,在经过预处理之后就形成一个源码文件,标准的叫法是translation unit(编译单元),它包括一系列的声明和定义。一个程序由若干(>=1)编译单元组成。编译器将各个编译单元编译为目标代码(.obj),通过linker(连接器)将这些目标代码连接成完整的指令序列(可执行文件、静态库、动态库等)。所以,#include后的头文件名,预处理过后是写到了编译单元里的,这和java有着本质的不同


4  为什么要设置执行路径

引用:http://www.cnblogs.com/wangchenyang/archive/2011/08/17/2143620.html

1. PATH环境变量。作用是指定命令搜索路径,在shell下面执行命令时,它会到PATH变量所指定的路径中查找看是否能找到相应的命令程序。我们需要把 jdk安装目录下的bin目录增加到现有的PATH变量中,bin目录中包含经常要用到的可执行文件如javac/java/javadoc等待,设置好 PATH变量后,就可以在任何目录下执行javac/java等工具了。 

  2. CLASSPATH环境变量。作用是指定类搜索路径,要使用已经编写好的类,前提当然是能够找到它们了,JVM就是通过CLASSPTH来寻找类的。我们 需要把jdk安装目录下的lib子目录中的dt.jar和tools.jar设置到CLASSPATH中,当然,当前目录“.”也必须加入到该变量中。 

  3. JAVA_HOME环境变量。它指向jdk的安装目录,Eclipse/NetBeans/Tomcat等软件就是通过搜索JAVA_HOME变量来找到并使用安装好的jdk。 


5 如何理解Java的“接口”概念


6 何为字节码

引用:http://tec.5lulu.com/detail/110k8n1h9n13j8y26.html

Java 字节码(英语:Java bytecode)是Java虚拟机执行的一种指令格式。大多数操作码都是一个字节长,而有些操作需要参数,导致了有一些多字节的操作码。而且并不是所有可能的256个操作码都被使用;其中有51个操作码被保留做将来使用。除此之外,原始Java平台开发商,升阳微系统,额外保留了3个代码永久不使用。


Java字节码的计算模型是面向堆栈结构计算机的。例如,一个x86处理器的汇编代码如下 

 
 
  1. mov eax, byte [ebp-4]
  2. mov edx, byte [ebp-8]
  3. add eax, edx
  4. mov ecx, eax

这段代码将两个数值相加,并存入另一个地址。相似的反汇编字节码如下 

 
 
  1. 0 iload_1
  2. 1 iload_2
  3. 2 iadd
  4. 3 istore_3

在这里,需要相加的两个操作数被放入堆栈,而相加操作就在栈中进行,其结果也被放入堆栈。存储指令之后把栈顶的数据放入一个变量地址。在每条指令前面的数字仅仅是表示这条指令到方法开始处的偏移值。这种堆栈结构也可以推广到面向对象模型上。



7 何为Java解释器,何为“增量式链接”

如果一种语言的主流实现是解释器,其内部是编译器+虚拟机,而虚拟机又是采用解释方式实现的,或者内部实现是编译器+树遍历解释器,那它就是名副其实的“解释型语言”。如果内部用的虚拟机是用编译方式实现的,其实跟普遍印象中的“解释器”还是挺不同的…


引用:http://www.cnblogs.com/nbsofer/archive/2013/06/26/3157619.html

 增量链接(Incremental Linking)这个词语在使用Visual C++时经常会遇到(其实
不只是VS系列,其它链接器也有这个特性), 就比如经常遇到的:上一个增量链接没有
生成它, 正在执行完全链接.  但是它是什么意思呢? 很多人只是经常看到, 但是不
明白这是什么. 首先说明一下, 这个特性是链接器为了<提高链接速度>而增加的功
能. 说是这样, 可能不理解, 下面就说说其原理:

    有一个源代码文件, 你写了许多许多行, 你正在调试它, 你突然发现某个地方
有一个小BUG, 你打算改正, 好了, 错误改正了, 但是链接器该采用何种方式链接新代码?

    其一: 可以把obj删除, 然后重新生成. 
    其二: 为每个函数预留一部分空间, 编译链接时, 只是修改你修改过的函数
对应的代码, 其它二进制代码保持不变.

    可能你已经明白了, 为目标(函数)代码"预留一部分空间", 这就是"增量"的含
义. 当代码修改后, 只需要修改这一部分的目标代码即可快速完成编译与链接! 
这就为调试代码时快速修改编译提高了速度! 这也是为什么调试器
支持 "Edit and Continue" 特性!

    这不浪费程序空间么? 你可能会想, 不过确实如此. 正因为如此, Debug版本的
目标文件会远远大于Release版本的目标文件(不单因为此). 在Release编译时, 是
不能启用增量链接模式的!

    好了, 先就介绍这些, 其实我也只是知道部分原理, 本文的讲解并不深入!


8 什么是虚拟机

http://rednaxelafx.iteye.com/blog/492667

引用“一般认为输入是满足某种指令集架构(instruction set architecture,ISA)的指令序列,中间转换为目标ISA的指令序列并加以执行,输出为程序的执行结果的,就是VM。源与目标ISA可以是同一种,这是所谓same-ISA VM。 
前面提到解释器中的编译器的输出可能是AST,也可能是字节码之类的指令序列;一般会把执行后者的程序称为VM,而执行前者的还是笼统称为解释器或者树遍历式解释器(tree-walking interpreter)。这只是种习惯而已,并没有多少确凿的依据。只不过线性(相对于树形)的指令序列看起来更像一般真正机器会执行的指令序列而已。 
其实我觉得把执行AST的也叫VM也没啥大问题。


9 Java中包的概念

引用:http://blog.chinaunix.net/uid-21411227-id-4138629.html

1.包的概念

包(package)是Java提供的一种区别类的名字空间的机制,是类的组织方式,是一组相关类和接口的集合,它提供了访问权限和命名的管理机制。

Java中提供的包主要有以下3种用途:

  1) 将功能相近的类放在同一个包中,可以方便查找与使用。

  2) 由于在不同包中可以存在同名类,所以使用包在一定程度上可以避免命名冲突。

  3) 在Java中,某次访问权限是以包为单位的。

1.1创建包

创建包可以通过在类或接口的源文件中使用 package语句实现,package语句的语法格式如下:

package 包名;

包名:必选,用于指定包的名称,包的名称为合法的 Java标识符。当包中还有包时,可以使用“包1.包2.…….包n”进行指定,其中,包1为最外层的包,而包n则为最内层的包。

package 语句通常位于类或接口源文件的第一行。例如,定义一个类Circ,将其放入com.wgh包中的代码如下:

package com.wgh;

public class Circ {

final float PI=3.14159f;       //定义一个用于表示圆周率的常量PI

// 定义一个绘图的方法

public void draw(){

System.out.println("画一个圆形!");

}

}

说明:

在Java中提供的包,相当于系统中的文件夹。例如,上面代码中的Circ类如果保存到C盘根目录下,那么它的实际路径应该为C:\com \wgh\Circ.java。

使用包中的类

类可以访问其所在包中的所有类,还可以使用其他包中的所有public类。访问其他包中的public类可以有以下两种方法。

1) 使用长名引用包中的类

使用长名引用包中的类比较简单,只需要在每个类名前面加上完整的包名即可。例如,创建Circ类(保存在com.wgh包中)的对象并实例化该对象的代码如下:

com.wgh.Circ circ=new com.wgh.Circ();

2) 使用import语句引入包中的类

由于采用使用长名引用包中的类的方法比较繁琐,所以Java提供了import语句来引入包中的类。import语句的基本语法格式如下:

import 包名1[.包名2.……].类名|*;

当存在多个包名时,各个包名之间使用“.”分隔,同时包名与类名之间也使用“.”分隔。

*:表示包中所有的类。

例如,引入com.wgh包中的Circ类的代码如下:

import com.wgh.Circ;

如果 com.wgh包中包含多个类,也可以使用以下语句引入该包下的全部类。

import com.wgh.*;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值