我想知道程序为什么会这样,要怎么改,编译程序与解释程序的区别的时候0报错,程序内容位实现傅立叶变换

里面的反射功能可以实现运行时獲得函数的额外信息参数和返回类型,从这一点来说他们更加“高级”)

在C语言中,一个模块就是一个目标文件

库其实就是目标文件的集合。我们经常听到运行时库C标准库,C++标准库动态库静态库,这些名词都是从不同的维度定义的

运行时库(runtime Library)支持程序运行基本函数集合

标准库是实现了语言标准的诸如输入/输出处理、字符串处理、内存管理等的内容标准库中的函数很多都是对系统调鼡的封装,比如C语言的标准库中有printf函数(也有不是的比如strlen())——printf在Linux下封装的是一个write系统调用(系统调用 是程序与操作系统内核交互的中介),在Windows下是一个WriteConsole系统api也就是说程序通过标准库进行一些系统调用,有了标准库程序就可以忽略系统调用,直接使用标准库中的函数即可

每个平台都有其自己的标准库实现。\Framework64\ 命令解释器将可执行文件装入内存中

command设置cpu的cs:ip指向程序的第一条命令,将cpu的控制权交给他然後程序退出以后,控制器交回给command

debug可以将程序加载入内存但是不放弃对cpu的控制,这样就可以利用debug的相关命令来单步执行程序

}

让自己稳拿铁饭碗 ;-)

永远不要(把洎己遇到的问题)归因于(他人的)恶意这恰恰说明了(你自己的)无能。 

为了造福大众在Java编程领域创造就业机会,兄弟我在此传授夶师们的秘籍这些大师写的代码极其难以维护,后继者就是想对它做最简单的修改都需要花上数年时间而且,如果你能对照秘籍潜心修炼你甚至可以给自己弄个铁饭碗,因为除了你之外没人能维护你写的代码。再而且如果你能练就秘籍中的全部招式,那么连你自巳都无法维护你的代码了!

你不想练功过度走火入魔吧那就不要让你的代码一眼看去就完全无法维护,只要它实质上是那样就行了否則,你的代码就有被重写或重构的风险!

想挫败维护代码的程序员你必须先明白他的思维方式。他接手了你的庞大程序没有时间把它铨部读一遍,更别说理解它了他无非是想快速找到修改代码的位置、改代码、编译程序与解释程序的区别,然后就能交差并希望他的修改不会出现意外的副作用。

他查看你的代码不过是管中窥豹一次只能看到一小段而已。你要确保他永远看不到全貌要尽量让他难以找到他想找的代码。但更重要的是要让他不能有把握忽略任何东西。

程序员都被编程惯例洗脑了还为此自鸣得意。每一次你处心积虑哋违背编程惯例都会迫使他必须用放大镜去仔细阅读你的每一行代码。

你可能会觉得每个语言特性都可以用来让代码难以维护其实不嘫。你必须精心地误用它们才行

“当我使用一个单词的时候” Humpty Dumpty 曾经用一种轻蔑的口气说, “它就是我想表达的意思,不多也不少“

编写無法维护代码的技巧的重中之重是变量和方法命名的艺术。如何命名是和编译程序与解释程序的区别器无关的这就让你有巨大的自由度詓利用它们迷惑维护代码的程序员。

妙用 宝宝起名大全

买本宝宝起名大全你就永远不缺变量名了。比如 Fred 就是个好名字而且键盘输入它吔省事。如果你就想找一些容易输入的变量名可以试试 adsf 或者 aoeu之类。

如果你给变量起名为a,b,c用简单的文本编辑器就没法搜索它们的引用。洏且没人能猜到它们的含义。

如果你必须使用描述性的变量和函数名那就把它们都拼错。还可以把某些函数和变量名拼错再把其他嘚拼对(例如 SetPintleOpening 和 SetPintalClosing) ,我们就能有效地将grep或IDE搜索技术玩弄于股掌之上这招超级管用。还可以混淆不同语言(比如colour — 英国英语和 color — 美国英语)。

鼡首字母大写缩写(比如GNU 代表 GNU’s Not Unix) 使代码简洁难懂真正的汉子(无论男女)从来不说明这种缩写的含义,他们生下来就懂

为了打破沉闷的编程气氛,你可以用一本辞典来查找尽量多的同义词例如 display, show, present。在注释里含糊其辞地暗示这些命名之间有细微的差别其实根本没有。不过洳果有两个命名相似的函数真的有重大差别,那倒是一定要确保它们用相同的单词来命名(例如对于 “写入文件”, “在纸上书写” 和 “屏幕显示” 都用 print 来命名)。 在任何情况下都不要屈服于编写明确的项目词汇表这种无理要求你可以辩解说,这种要求是一种不专业的行为咜违反了结构化设计的信息隐藏原则。

随机地把单词中间某个音节的首字母大写例如 ComputeReSult()。

在语言规则允许的地方尽量把类、构造器、方法、成员变量、参数和局部变量都命名成一样。更高级的技巧是在{}块中重用局部变量这样做的目的是迫使维护代码的程序员认真检查每個实例的作用域。特别是在Java代码中可以把普通方法伪装成构造器。

在命名中偷偷使用不易察觉的非英语字母例如

看上去没啥不对是吧?嘿嘿嘿…这里的第二个 ínt 的 í 实际上是东北欧字母并不是英语中的 i 。在简单的文本编辑器里想看出这一点点区别几乎是不可能的。

巧妙利用编译程序与解释程序的区别器对于命名长度的限制

如果编译程序与解释程序的区别器只区分命名的前几位比如前8位,那么就把後面的字母写得不一样比如,其实是同一个变量有时候写成 var_unit_update() ,有时候又写成 var_unit_setup()看起来是两个不同的函数调用。而在编译程序与解释程序的区别的时候它们其实是同一个变量 var_unit。

可以拿 _ 和 __ 作为标示符

随机地混用两种语言(人类语言或计算机语言都行)。如果老板要求使鼡他指定的语言你就告诉他你用自己的语言更有利于组织你的思路,万一这招不管用就去控诉这是语言歧视,并威胁起诉老板要求巨額精神损失赔偿

扩展 ASCII 字符用于变量命名是完全合法的,包括 ?, ?, 和 ?  等在简单的文本编辑器里,除了拷贝/粘贴基本上没法输入。

使鼡外语字典作为变量名的来源例如,可以用德语单词 punkt 代替 point除非维护代码的程序员也像你一样熟练掌握了德语. 不然他就只能尽情地在代碼中享受异域风情了。

用数学操作符的单词来命名变量例如:

用带有完全不相关的感情色彩的单词来命名变量。例如:

(欢乐满人间 = (超人 + 煋河战队)/上帝;)

这一招可以让阅读代码的人陷入迷惑之中因为他们在试图想清楚这些命名的逻辑时,会不自觉地联系到不同的感情场景里洏无法自拔

永远不要把 i 用作最内层的循环变量。 用什么命名都行就是别用i。把 i 用在其他地方就随便了用作非整数变量尤其好。

惯例 — 明修栈道暗度陈仓

忽视 Java 编码惯例,Sun 自己就是这样做的幸运的是,你违反了它编译程序与解释程序的区别器也不会打小报告这一招嘚目的是搞出一些在某些特殊情况下有细微差别的名字来。如果你被强迫遵循驼峰法命名你还是可以在某些模棱两可的情况下颠覆它。唎如inputFilename 和 inputfileName 两个命名都可以合法使用。在此基础上自己发明一套复杂到变态的命名惯例然后就可以对其他人反咬一口,说他们违反了惯例

小写的 l 看上去很像数字 1

在A 模块里声明一个全局数组,然后在B 模块的头文件里再声明一个同名的私有数组这样看起来你在B 模块里引用的昰那个全局数组,其实却不是不要在注释里提到这个重复的情况。

让每个方法都和它的名字蕴含的功能有一些差异例如,一个叫 isValid(x)的方法在判断完参数x的合法性之后还顺带着把它转换成二进制并保存到数据库里。

当一个bug需要越长的时间才会暴露它就越难被发现。

编写無法维护代码的另一大秘诀就是伪装的艺术即隐藏它或者让它看起来像其他东西。很多招式有赖于这样一个事实:编译程序与解释程序嘚区别器比肉眼或文本编辑器更有分辨能力下面是一些伪装的最佳招式。

把代码伪装成注释反之亦然

下面包括了一些被注释掉的代码,但是一眼看去却像是正常代码

如果不是用单绿色标出来,你能注意到这三行代码被注释掉了么

可以把 “xy_z” 打散到两行里:

这样全局搜索 xy_z 的操作在这个文件里就一无所获了。 对于 C 预处理器来说第一行最后的 “\” 表示继续拼接下一行的内容。

任何傻瓜都能说真话而要紦谎编圆则需要相当的智慧。

不正确的文档往往比没有文档还糟糕

既然计算机是忽略注释和文档的,你就可以在里边堂而皇之地编织弥忝大谎让可怜的维护代码的程序员彻底迷失。

实际上你不需要主动地撒谎只要没有及时保持注释和代码更新的一致性就可以了。

这样嘚注释但是永远不要记录包或者方法的整体设计这样的干货。

只解释一个程序功能的细节而不是它要完成的任务是什么。这样的话洳果出现了一个bug,修复者就搞不清这里的代码应有的功能

比如你在开发一套航班预定系统,那就要精心设计让它在增加另一个航空公司的时候至少有25处代码需要修改。永远不要在文档里说明要修改的位置后来的开发人员要想修改你的代码?门都没有除非他们能把每┅行代码都读懂。

永远不要在文档中说明任何变量、输入、输出或参数的计量单位如英尺、米、加仑等。计量单位对数豆子不是太重要但在工程领域就相当重要了。同理永远不要说明任何转换常量的计量单位,或者是它的取值如何获得要想让代码更乱的话,你还可鉯在注释里写上错误的计量单位这是赤裸裸的欺骗,但是非常有效如果你想做一个恶贯满盈的人,不妨自己发明一套计量单位用自巳或某个小人物的名字命名这套计量单位,但不要给出定义万一有人挑刺儿,你就告诉他们你这么做是为了把浮点数运算凑成整数运算而进行的转换。

永远不要记录代码中的坑如果你怀疑某个类里可能有bug,天知地知你知就好如果你想到了重构或重写代码的思路,看茬老天爷的份上千万别写出来。切记电影《小鹿斑比》里那句台词 “如果你不能说好听的话那就什么也不要说。”万一这段代码的原作者看到你的注释怎么办?万一老板看到了怎么办万一客户看到了怎么办?搞不好最后你自己被解雇了一句”这里需要修改“的匿洺注释就好多了,尤其是当看不清这句注释指的是哪里需要修改的情况下切记“难得糊涂”四个字,这样大家都不会感觉受到了批评

詠远不要对变量声明加注释。有关变量使用的方式、边界值、合法值、小数点后的位数、计量单位、显示格式、数据录入规则等等后继鍺完全可以自己从程序代码中去理解和整理嘛。如果老板强迫你写注释就在方法体里胡乱多写点,但绝对不要对变量声明写注释即使昰临时变量!

为了阻挠任何雇佣外部维护承包商的倾向,可以在代码中散布针对其他同行软件公司的攻击和抹黑特别是可能接替你工作嘚其中任何一家。例如:

      /* 优化后的内层循环
这套技巧对于SSI软件服务公司的那帮蠢材来说太高深了他们只会
用 <math.h> 里的笨例程,消耗50倍的内存囷处理时间
*/

class clever_SSInc{
.. .
}

可能的话,除了注释之外这些攻击抹黑的内容也要掺到代码里的重要语义部分,这样如果管理层想清理掉这些攻击性的言論然后发给外部承包商去维护就会破坏代码结构。

编写无法维护代码的基本规则就是:在尽可能多的地方以尽可能多的方式表述每一個事实。

编写可维护代码的关键因素是只在一个地方表述应用里的一个事实如果你的想法变了,你也只在一个地方修改这样就能保证整个程序正常工作。所以编写无法维护代码的关键因素就是反复地表述同一个事实,在尽可能多的地方以尽可能多的方式进行。令人高兴的是像Java这样的语言让编写这种无法维护代码变得非常容易。例如改变一个被引用很多的变量的类型几乎是不可能的,因为所有造型和转换功能都会出错而且关联的临时变量的类型也不合适了。而且如果变量值要在屏幕上显示,那么所有相关的显示和数据录入代碼都必须一一找到并手工进行修改类似的还有很多,比如由C和Java组成的Algol语言系列Abundance甚至Smalltalk对于数组等结构的处理,都是大有可为的

Java的造型機制是上帝的礼物。你可以问心无愧地使用它因为Java语言本身就需要它。每次你从一个Collection 里获取一个对象你都必须把它造型为原始类型。這样这个变量的类型就必须在无数地方表述如果后来类型变了,所有的造型都要修改才能匹配如果倒霉的维护代码的程序员没有找全(或者修改太多),编译程序与解释程序的区别器能不能检测到也不好说类似的,如果变量类型从short

Java要求你给每个变量的类型写两次表述 Java 程序员已经习惯了这种冗余,他们不会注意到你的两次表述有细微的差别例如

不幸的是 ++ 操作符的盛行让下面这种伪冗余代码得手的难喥变大了:

永远不要对输入数据做任何的正确性或差异性检查。这样能表现你对公司设备的绝对信任以及你是一位信任所有项目伙伴和系统管理员的团队合作者。总是返回合理的值即使数据输入有问题或者错误。

避免使用 assert() 机制因为它可能把三天的debug盛宴变成10分钟的快餐。

为了提高效率不要使用封装。方法的调用者需要所有能得到的外部信息以便了解方法的内部是如何工作的。

以效率的名义使用 复淛+粘贴+修改。这样比写成小型可复用模块效率高得多在用代码行数衡量你的进度的小作坊里,这招尤其管用

如果一个库里的模块需要┅个数组来存放图片,就定义一个静态数组没人会有比512 X 512 更大的图片,所以固定大小的数组就可以了为了最佳精度,就把它定义成 double 类型嘚数组

编写一个名为 “WrittenByMe” 之类的空接口,然后让你的所有类都实现它然后给所有你用到的Java 内置类编写包装类。这里的思想是确保你程序里的每个对象都实现这个接口最后,编写所有的方法让它们的参数和返回类型都是这个 WrittenByMe。这样就几乎不可能搞清楚某个方法的功能昰什么并且所有类型都需要好玩的造型方法。更出格的玩法是让每个团队成员编写它们自己的接口(例如 WrittenByJoe),程序员用到的任何类都要实現他自己的接口这样你就可以在大量无意义接口中随便找一个来引用对象了。

永远不要为每个组件创建分开的监听器对所有按钮总是鼡同一个监听器,只要用大量的if…else 来判断是哪一个按钮被点击就行了

狂野地使用封装和OO思想。例如

这段很可能看起来不怎么好笑别担惢,只是时候未到而已

在C++ 里尽量多使用friend声明。再把创建类的指针传递给已创建类现在你不用浪费时间去考虑接口了。另外你应该用仩关键字private 和 protected 来表明你的类封装得很好。

大量使用它们用扭曲的方式在数组之间移动数据,比如用arrayA里的行去填充arrayB的列。这么做的时候鈈管三七二十一再加上1的偏移值,这样很灵让维护代码的程序员抓狂去吧。

存取方法和公共变量神马的都要给他用上这样的话,你无需调用存取器的开销就可以修改一个对象的变量还能宣称这个类是个”Java Bean”。对于那些试图添加日志函数来找出改变值的源头的维护代码嘚程序员用这一招来迷惑他尤其有效。

把每个方法和变量都声明为 public毕竟某个人某天可能会需要用到它。一旦方法被声明为public 了就很难縮回去。对不这样任何它覆盖到的代码都很难修改了。它还有个令人愉快的副作用就是让你看不清类的作用是什么。如果老板质问你昰不是疯了你就告诉他你遵循的是经典的透明接口原则。

把你所有的没用的和过时的方法和变量都留在代码里毕竟说起来,既然你在1976姩用过一次谁知道你啥时候会需要再用到呢?当然程序是改了但它也可能会改回来嘛,你”不想要重新发明轮子”(领导们都会喜欢這样的口气)如果你还原封不动地留着这些方法和变量的注释,而且注释写得又高深莫测甭管维护代码的是谁,恐怕都不敢对它轻举妄动

把你所有的叶子类都声明为 final。毕竟说起来你在项目里的活儿都干完了,显然不会有其他人会通过扩展你的类来改进你的代码这種情况甚至可能有安全漏洞。 java.lang.String 被定义成 final 也许就是这个原因吧如果项目组其他程序员有意见,告诉他们这样做能够提高运行速度

永远不偠用到布局。当维护代码的程序员想增加一个字段他必须手工调整屏幕上显示所有内容的绝对坐标值。如果老板强迫你使用布局那就寫一个巨型的 GridBagLayout 并在里面用绝对坐标进行硬编码。

全局变量怎么强调都不过分

如果上帝不愿意我们使用全局变量,他就不会发明出这个东覀不要让上帝失望,尽量多使用全局变量每个函数最起码都要使用和设置其中的两个,即使没有理由也要这么做毕竟,任何优秀的維护代码的程序员都会很快搞清楚这是一种侦探工作测试有利于让他们从笨蛋中脱颖而出。

全局变量让你可以省去在函数里描述参数的麻烦充分利用这一点。在全局变量中选那么几个来表示对其他全局变量进行操作的类型

永远不要用局部变量。在你感觉想要用的时候把它改成一个实例或者静态变量,并无私地和其他方法分享它这样做的好处是,你以后在其他方法里写类似声明的时候会节省时间C++程序员可以百尺竿头更进一步,把所有变量都弄成全局的

配置文件通常是以 关键字 = 值 的形式出现。在加载时这些值被放入 Java 变量中最明顯的迷惑技术就是把有细微差别的名字用于关键字和Java 变量.甚至可以在配置文件里定义运行时根本不会改变的常量。参数文件变量和简单变量比维护它的代码量起码是后者的5倍。

对于编写无法维护代码的任务来说面向对象编程的思想简直是天赐之宝。如果你有一个类里邊有10个属性(成员/方法),可以考虑写一个基类里面只有一个属性,然后产生9层的子类每层增加一个属性。等你访问到最终的子类时你才能得到全部10个属性。如果可能把每个类的声明都放在不同的文件里。

从互联网上的各种混乱C 语言竞赛中学习追随大师们的脚步。

总是追求用最迷惑的方式来做普通的任务例如,要用数组来把整数转换为相应的字符串可以这么做:

当你需要一个字符常量的时候,可以用多种不同格式: ‘ ‘, 32, 0x20, 040在C或Java里10和010是不同的数(0开头的表示8进制),你也可以充分利用这个特性

把所有数据都以 void * 形式传递,然后洅造型为合适的结构不用结构而是通过位移字节数来造型也很好玩。

Switch 里边还有 Switch这种嵌套方式是人类大脑难以破解的。

牢记编程语言中所有的隐式转化细节充分利用它们。数组的索引要用浮点变量循环计数器用字符,对数字执行字符串函数调用不管怎么说,所有这些操作都是合法的它们无非是让源代码更简洁而已。任何尝试理解它们的维护者都会对你感激不尽因为他们必须阅读和学习整个关于隱式数据类型转化的章节,而这个章节很可能是他们来维护你的代码之前完全忽略了的

在所有语法允许的地方都加上分号,例如:

把八進制数混到十进制数列表里就像这样:

尽可能深地嵌套。优秀的程序员能在一行代码里写10层()在一个方法里写20层{}。

遗憾的是这一招只能在本地C类里用,Java 还不行

一行代码里堆的东西越多越好。这样可以省下临时变量的开销去掉换行和空格还可以缩短源文件大小。记住要去掉运算符两边的空格。优秀的程序员总是能突破某些编辑器对于255个字符行宽的限制

在这里我要向你传授一个编程领域里鲜为人知嘚秘诀。异常是个讨厌的东西良好的代码永远不会出错,所以异常实际上是不必要的不要把时间浪费在这上面。子类异常是给那些知噵自己代码会出错的低能儿用的在整个应用里,你只用在main()里放一个try/catch里边直接调用 System.exit()就行了。在每个方法头要贴上标准的抛出集合定义臸于会不会抛出异常你就甭管了。

在非异常条件下才要使用异常比如终止循环就可以用 ArrayIndexOutOfBoundsException。还可以从异常里的方法返回标准的结果

在程序里留些bug,让后继的维护代码的程序员能做点有意思的事精心设计的bug是无迹可寻的,而且谁也不知道它啥时候会冒出来要做到这一点,最简单的办法的就是不要测试代码

永远不要测试负责处理错误、当机或操作系统故障的任何代码。反正这些代码永远也不会执行只會拖累你的测试。还有你怎么可能测试处理磁盘错误、文件读取错误、操作系统崩溃这些类型的事件呢?为啥你要用特别不稳定的计算機或者用测试脚手架来模拟这样的环境现代化的硬件永远不会崩溃,谁还愿意写一些仅仅用于测试的代码这一点也不好玩。万一将来絀了事用户抱怨你就怪到操作系统或者硬件头上。他们永远不会知道真相的

嘿,如果软件运行不够快只要告诉客户买个更快的机器僦行了。如果你真的做了性能测试你可能会发现一个瓶颈,这会导致修改算法然后导致整个产品要重新设计。谁想要这种结果而且,在客户那边发现性能问题意味着你可以免费到外地旅游你只要备好护照和最新照片就行了。

永远不要写任何测试用例

永远不要做代码覆盖率或路径覆盖率测试自动化测试是给那些窝囊废用的。搞清楚哪些特性占到你的例程使用率的90%然后把90%的测试用在这些路径上。毕竟说起来这种方法可能只测试到了大约你代码的60%,这样你就节省了40%的测试工作这能帮助你赶上项目后端的进度。等到有人发现所有这些漂亮的“市场特性”不能正常工作的时候你早就跑路了。一些有名的大软件公司就是这样测试代码的所以你也应该这样做。如果因為某种原因你还没走那就接着看下一节。

勇敢的程序员会跳过这个步骤太多程序员害怕他们的老板,害怕丢掉工作害怕客户的投诉郵件,害怕遭到起诉这种恐惧心理麻痹了行动,降低了生产率有科学研究成果表明,取消测试阶段意味着经理有把握能提前确定交付時间这对于规划流程显然是有利的。消除了恐惧心理创新和实验之花就随之绽放。程序员的角色是生产代码调试工作完全可以由技術支持和遗留代码维护组通力合作来进行。

如果我们对自己的编程能力有充分信心那么测试就没有必要了。如果我们逻辑地看待这个问題随便一个傻瓜都能认识到测试根本都不是为了解决技术问题,相反它是一种感性的信心问题。针对这种缺乏信心的问题更有效的解决办法就是完全取消测试,送我们的程序员去参加自信心培训课程毕竟说起来,如果我们选择做测试那么我们就要测试每个程序的變更,但其实我们只需要送程序员去一次建立自信的培训课就行了很显然这么做的成本收益是相当可观的。

计算机语言正在逐步进化變得更加傻瓜化。使用最新的语言算什么好汉尽可能坚持使用你会用的最老的语言,先考虑用穿孔纸带不行就用汇编,再不行用FORTRAN 或者 COBOL再不行就用C 还有 BASIC,实在不行再用 C++

用 FORTRAN 写所有的代码。如果老板问你为啥你可以回答说它有很多非常有用的库,你用它可以节约时间鈈过,用 FORTRAN 写出可维护代码的概率是 0所以,要达到不可维护代码编程指南里的要求就容易多了

把所有的通用工具函数都转成汇编程序。

所有重要的库函数都要用 QBASIC 写然后再写个汇编的封包程序来处理 large 到 medium 的内存模型映射。

在你的代码里混杂一些内联的汇编程序这样很好玩。这年头几乎没人懂汇编程序了只要放几行汇编代码就能让维护代码的程序员望而却步。

如果你有个汇编模块被C调用那就尽可能经常從汇编模块再去调用C,即使只是出于微不足道的用途另外要充分利用 goto, bcc 和其他炫目的汇编秘籍。

如果你的老板认为他20年的 FORTRAN 编程经验对于现玳软件开发具有很高的指导价值你务必严格采纳他的所有建议。投桃报李你的老板也会信任你。这会对你的职业发展有利你还会从怹那里学到很多搞乱程序代码的新方法。

确保代码中到处是bug的有效方法是永远不要让维护代码的程序员知道它们这需要颠覆技术支持工莋。永远不接电话使用自动语音答复“感谢拨打技术支持热线。需要人工服务请按1或在嘀声后留言。”请求帮助的电子邮件必须忽畧,不要给它分配服务追踪号对任何问题的标准答复是“我估计你的账户被锁定了,有权限帮你恢复的人现在不在”

永远不要对下一個危机保持警觉。如果你预见到某个问题可能会在一个固定时间爆发摧毁西半球的全部生命,不要公开讨论它不要告诉朋友、同事或其他你认识的有本事的人。在任何情况下都不要发表任何可能暗示到这种新的威胁的内容只发送一篇正常优先级的、语焉不详的备忘录給管理层,保护自己免遭秋后算账如果可能的话,把这篇稀里糊涂的信息作为另外一个更紧急的业务问题的附件这样就可以心安理得哋休息了,你知道将来你被强制提前退休之后一段时间他们又会求着你回来,并给你对数级增长的时薪!

加入一个计算机每月一书俱乐蔀选择那些看上去忙着写书不可能有时间真的去写代码的作者。去书店里找一些有很多图表但是没有代码例子的书浏览一下这些书,從中学会一些迂腐拗口的术语用它们就能唬住那些自以为是的维护代码的程序员。你的代码肯定会给他留下深刻印象如果人们连你写嘚术语都理解不了,他们一定会认为你非常聪明你的算法非常深奥。不要在你的算法说明里作任何朴素的类比

你一直想写系统级的代碼。现在机会来了忽略标准库, 编写你自己的标准这将会是你简历中的一大亮点。

推出你自己的 BNF 范式

总是用你自创的、独一无二的、無文档的BNF范式记录你的命令语法永远不要提供一套带注解的例子(合法命令和非法命令之类)来解释你的语法体系。那样会显得完全缺乏学术严谨性确保没有明显的方式来区分终结符和中间符号。永远不要用字体、颜色、大小写和其他任何视觉提示帮助读者分辨它们茬你的 BNF 范式用和命令语言本身完全一样的标点符号,这样读者就永远无法分清一段 (…), […], {…} 或 “…” 到底是你在命令行里真正输入的还是想提示在你的BNF 范式里哪个语法元素是必需的、可重复的、或可选的。不管怎么样如果他们太笨,搞不清你的BNF 范式的变化就没资格使用伱的程序。

地球人儿都知道调试动态存储是复杂和费时的。与其逐个类去确认它没有内存溢出还不如自创一套存储分配机制呢。其实咜无非是从一大片内存中 malloc 一块空间而已用不着释放内存,让用户定期重启动系统这样不就清除了堆么。重启之后系统需要追踪的就那麼一点东西比起解决所有的内存泄露简单得不知道到哪里去了!而且,只要用户记得定期重启系统他们也永远不会遇到堆空间不足的問题。一旦系统被部署你很难想象他们还能改变这个策略。

如果你给某人一段程序你会让他困惑一天;如果你教他们如何编程,你会讓他困惑一辈子 

让我们从一条可能是有史以来最友好的技巧开始:把代码编译程序与解释程序的区别成可执行文件。如果它能用就在源代码里做一两个微小的改动 — 每个模块都照此办理。但是不要费劲巴拉地再编译程序与解释程序的区别一次了 你可以留着等以后有空洏且需要调试的时候再说。多年以后等可怜的维护代码的程序员更改了代码之后发现出错了,他会有一种错觉觉得这些肯定是他自己朂近修改的。这样你就能让他毫无头绪地忙碌很长时间

对于试图用行调试工具追踪来看懂你的代码的人,简单的一招就能让他狼狈不堪那就是把每一行代码都写得很长。特别要把 then 语句 和 if 语句放在同一行里他们无法设置断点。他们也无法分清在看的分支是哪个 if 里的

在笁程方面有两种编码方式。一种是把所有输入都转换为公制(米制)计量单位然后在输出的时候自己换算回各种民用计量单位。另一种昰从头到尾都保持各种计量单位混合在一起总是选择第二种方式,这就是美国之道!

要持续不懈地改进要常常对你的代码做出“改进”,并强迫用户经常升级 — 毕竟没人愿意用一个过时的版本嘛即便他们觉得他们对现有的程序满意了,想想看如果他们看到你又“完善“了它,他们会多么开心啊!不要告诉任何人版本之间的差别除非你被逼无奈 — 毕竟,为什么要告诉他们本来永远也不会注意到的一些bug呢

”关于“一栏应该只包含程序名、程序员姓名和一份用法律用语写的版权声明。理想情况下它还应该链接到几 MB 的代码,产生有趣嘚动画效果但是,里边永远不要包含程序用途的描述、它的版本号、或最新代码修改日期、或获取更新的网站地址、或作者的email地址等這样,所有的用户很快就会运行在各种不同的版本上在安装N+1版之前就试图安装N+2版。

在两个版本之间你能做的变更自然是多多益善。你鈈会希望用户年复一年地面对同一套老的接口或用户界面这样会很无聊。最后如果你能在用户不注意的情况下做出这些变更,那就更恏了 — 这会让他们保持警惕戒骄戒躁。

写无法维护代码不需要多高的技术水平喊破嗓子不如甩开膀子,不管三七二十一开始写代码就荇了记住,管理层还在按代码行数考核生产率即使以后这些代码里的大部分都得删掉。

一招鲜吃遍天会干什么就吆喝什么,轻装前進如果你手头只有一把锤子,那么所有的问题都是钉子

有可能的话,忽略当前你的项目所用语言和环境中被普罗大众所接受的编程规范比如,编写基于MFC 的应用时就坚持使用STL 编码风格。

把常用的 true 和 false 的定义反过来用这一招听起来平淡无奇,但是往往收获奇效你可以先藏好下面的定义:

把这个定义深深地藏在代码中某个没人会再去看的文件里不易被发现的地方,然后让程序做下面这样的比较

某些人肯萣会迫不及待地跳出来“修正”这种明显的冗余并且在其他地方照着常规去使用变量var:

还有一招是为 TRUE 和 FALSE赋予相同的值,虽然大部分人可能会看穿这种骗局给它们分别赋值 1 和 2 或者 -1 和 0 是让他们瞎忙乎的方式里更精巧的,而且这样做看起来也不失对他们的尊重你在Java 里也可以鼡这一招,定义一个叫 TRUE 的静态常量在这种情况下,其他程序员更有可能怀疑你干的不是好事因为Java里已经有了内建的标识符

在你的项目裏引入功能强大的第三方库,然后不要用它们潜规则就是这样,虽然你对这些工具仍然一无所知却可以在你简历的“其他工具”一节Φ写上这些没用过的库。

假装不知道有些库已经直接在你的开发工具中引入了如果你用VC++编程,忽略MFC 或 STL 的存在手工编写所有字符串和数組的实现;这样有助于保持你玩指针技术的高水平,并自动阻止任何扩展代码功能的企图

创建一套Build顺序
把这套顺序规则做得非常晦涩,讓维护者根本无法编译程序与解释程序的区别任何他的修改代码秘密保留 SmartJ ,它会让 make脚本形同废物类似地,偷偷地定义一个 javac 类让它和編译程序与解释程序的区别程序同名。说到大招那就是编写和维护一个定制的小程序,在程序里找到需要编译程序与解释程序的区别的攵件然后通过直接调用 sun.tools.javac.Main 编译程序与解释程序的区别类来进行编译程序与解释程序的区别。

用一个 makefile-generated-batch-file 批处理文件从多个目录复制源文件文件之间的覆盖规则在文档中是没有的。这样无需任何炫酷的源代码控制系统,就能实现代码分支并阻止你的后继者弄清哪个版本的 DoUsefulWork() 才昰他需要修改的那个。

尽可能搜集所有关于编写可维护代码的建议例如 SquareBox 的建议 ,然后明目张胆地违反它们

某些公司有严格的规定,不尣许使用数字标识符你必须使用预先命名的常量。要挫败这种规定背后的意图太容易了比如,一位聪明的 C++ 程序员是这么写的:

一定要保留一些编译程序与解释程序的区别器警告在 make 里使用 “-” 前缀强制执行,忽视任何编译程序与解释程序的区别器报告的错误这样,即使维护代码的程序员不小心在你的源代码里造成了一个语法错误make 工具还是会重新把整个包build 一遍,甚至可能会成功!而任何程序员要是手笁编译程序与解释程序的区别你的代码看到屏幕上冒出一堆其实无关紧要的警告,他们肯定会觉得是自己搞坏了代码同样,他们一定會感谢你让他们有找错的机会学有余力的同学可以做点手脚让编译程序与解释程序的区别器在打开编译程序与解释程序的区别错误诊断笁具时就没法编译程序与解释程序的区别你的程序。当然了编译程序与解释程序的区别器也许能做一些脚本边界检查,但是真正的程序員是不用这些特性的所以你也不该用。既然你用自己的宝贵时间就能找到这些精巧的bug何必还多此一举让编译程序与解释程序的区别器來检查错误呢?

把 bug 修复和升级混在一起
永远不要发布什么“bug 修复”版本一定要把 bug 修复和数据库结构变更、复杂的用户界面修改,还有管悝界面重写等混在一起那样的话,升级就变成一件非常困难的事情人们会慢慢习惯 bug 的存在并开始称他们为特性。那些真心希望改变这些”特性“的人们就会有动力升级到新版本这样从长期来说可以节省你的维护工作量,并从你的客户那里获得更多收入

在你的产品发咘每个新版本的时候都改变文件结构
没错,你的客户会要求向上兼容那就去做吧。不过一定要确保向下是不兼容的这样可以阻止客户從新版本回退,再配合一套合理的 bug 修复规则(见上一条)就可以确保每次新版本发布后,客户都会留在新版本学有余力的话,还可以想办法让旧版本压根无法识别新版本产生的文件那样的话,老版本系统不但无法读取新文件甚至会否认这些文件是自己的应用系统产苼的!温馨提示:PC 上的 Word 文字处理软件就典型地精于此道。

不用费劲去代码里找 bug 的根源只要在更高级的例程里加入一些抵销它的代码就行叻。这是一种很棒的智力测验类似于玩3D棋,而且能让将来的代码维护者忙乎很长时间都想不明白问题到底出在哪里:是产生数据的低层唎程还是莫名其妙改了一堆东西的高层代码。这一招对天生需要多回合执行的编译程序与解释程序的区别器也很好用你可以在较早的囙合完全避免修复问题,让较晚的回合变得更加复杂如果运气好,你永远都不用和编译程序与解释程序的区别器前端打交道学有余力嘚话,在后端做点手脚一旦前端产生的是正确的数据,就让后端报错

不要用真正的同步原语,多种多样的旋转锁更好 — 反复休眠然后測试一个(non-volatile的) 全局变量直到它符合你的条件为止。相比系统对象旋转锁使用简便,”通用“性强”灵活“多变,实为居家旅行必备

紦某些系统同步原语安插到一些用不着它们的地方。本人曾经在一段不可能会有第二个线程的代码中看到一个临界区(critical section)代码本人当时僦质问写这段代码的程序员,他居然理直气壮地说这么写是为了表明这段代码是很”关键“(单词也是critical)的!

如果你的系统包含了一套 NT 设備驱动就让应用程序负责给驱动分配 I/O 缓冲区,然后在任何事务过程中对内存中的驱动加锁并在事务完成后释放或解锁。这样一旦应用非正常终止I/O缓存又没有被解锁,NT服务器就会当机但是在客户现场不太可能会有人知道怎么弄好设备驱动,所以他们就没有选择(只能請你去免费旅游了)

在你的 C/S 应用里嵌入一个在运行时按字节编译程序与解释程序的区别的脚本命令语言。

如果你发现在你的编译程序与解释程序的区别器或解释器里有个bug一定要确保这个bug的存在对于你的代码正常工作是至关重要的。毕竟你又不会使用其他的编译程序与解釋程序的区别器其他任何人也不允许!

下面是一位大师编写的真实例子。让我们来瞻仰一下他在这样短短几行 C 函数里展示的高超技巧

  • 偅新发明了标准库里已有的简单函数。

  • Realocate 这个单词拼写错误所以说,永远不要低估创造性拼写的威力

  • 无缘无故地给输入缓冲区产生一个臨时的副本。

  • 无缘无故地造型 memcpy() 里有 (void*),这样即使我们的指针已经是 (void*) 了也要再造型一次另外,这样做可以传递任何东西作为参数加10分。

  • 詠远不必费力去释放临时内存空间这样会导致缓慢的内存泄露,一开始看不出来要程序运行一段时间才行。

  • 把用不着的东西也从缓冲區里拷贝出来以防万一。这样只会在Unix上产生core dumpWindows 就不会。

  • 给 buf 分配内存之后memset 初始化它为 0。不要使用 calloc()因为某些人会重写 ANSI 规范,这样将来保鈈齐 calloc() 往 buf 里填的就不是 0 了(虽然我们复制过去的数据量和 buf 的大小是一样的,不需要初始化不过这也无所谓啦)

如果你的编译程序与解释程序的区别器冒出了 “unused local variable” 警告,不要去掉那个变量相反,要找个聪明的办法把它用起来我最喜欢的方法是:

差点忘了说了,函数是越夶越好跳转和 GOTO 语句越多越好。那样的话想做任何修改都需要分析很多场景。这会让维护代码的程序员陷入千头万绪之中如果函数真嘚体型庞大的话,对于维护代码的程序员就是哥斯拉怪兽了它会在他搞清楚情况之前就残酷无情地将他踩翻在地。

一张图片顶1000句话一個函数就是1000行
把每个方法体写的尽可能的长 — 最好是你写的任何一个方法或函数都不会少于1000行代码,而且里边是深度嵌套这是必须的。

┅定要保证一个或多个关键文件无法找到利用includes 里边再 includes 就能做到这一点。例如在你的 main 模块里,你写上:

然后refcode.h 就没地方能找到了。

(【譯者-老码农-注】为啥找不到呢仔细看看,现在还有人知道 a:\ 是什么吗A盘!传说中的软盘…)

至少要把一个变量弄成这样:到处被设置,泹是几乎没有哪里用到它不幸的是,现代编译程序与解释程序的区别器通常会阻止你做相反的事:到处读没处写。不过你在C 或 C++ 里还是鈳以这样做的


【译者注】:原文在后面还有一些内容,翻译时略有删减删节的内容主要是:

2.我觉得不怎么好笑的部分(其实很可能是洇为没看懂所以找不到笑点);

3.不容易引起现代程序猿共鸣的老旧内容。

本人水平有限时间匆忙,难免有误请读者不吝指出。谢谢!

长按识别二维码,加关注
}

多线程、并发及线程的基础问题

能Java 中可以创建 volatile 类型数组,不过只是一个指向数组的引用而不是整个数组。我的意思是如果改变引用指向的数组,将会受到 volatile 的保护泹是如果多个线程同时改变数组的元素,volatile 标示符就不能起到之前的保护作用了

2.volatile 能使得一个非原子操作变成原子操作吗?

一个典型的例子昰在类中有一个 long 类型的成员变量如果你知道该成员变量会被多个线程访问,如计数器、价格等你最好是将其设置为 volatile。为什么因为 Java 中讀取 long 类型变量不是原子的,需要分成两步如果一个线程正在修改该 long 变量的值,另一个线程可能只能看到该值的一半(前 32 位)但是对一個 volatile 型的 long 或 double

一种实践是用 volatile 修饰 long 和 double 变量,使其能按原子类型来读写double 和 long 都是64位宽,因此对这两种类型的读是分为两部分的第一次读取第一个 32 位,然后再读剩下的 32 位这个过程不是原子的,但 Java 中 volatile 型的 long 或 double 变量的读写是原子的volatile 修复符的另一个作用是提供内存屏障(memory barrier),例如在分布式框架中的应用简单的说,就是当你写一个 volatile 变量之前Java 内存模型会插入一个写屏障(write barrier),读一个 volatile 变量之前会插入一个读屏障(read barrier)。意思就是说在你写一个 volatile 域时,能保证任何线程都能看到你写的值同时,在写之前也能保证任何数值的更新对所有线程是可见的,因为內存屏障会将其他所有写的值更新到缓存

volatile 变量提供顺序和可见性保证,例如JVM 或者 JIT为了获得更好的性能会对语句重排序,但是 volatile 类型变量即使在没有同步块的情况下赋值也不会与其他语句重排序 volatile 提供 happens-before 的保证,确保一个线程的修改能对其他线程是可见的某些情况下,volatile 还能提供原子性如读 64 位数据类型,像 long 和

5. 10 个线程和 2 个线程的同步代码哪个更容易写?

从写代码的角度来说两者的复杂度是相同的,因为同步代码与线程数量是相互独立的但是同步策略的选择依赖于线程的数量,因为越多的线程意味着更大的竞争所以你需要利用同步技术,如锁分离这要求更复杂的代码和专业知识。

6.你是如何调用 wait()方法的使用 if 块还是循环?为什么

wait() 方法应该在循环调用,因为当线程獲取到 CPU 开始执行的时候其他条件可能还没有满足,所以在处理前循环检测条件是否满足会更好。下面是一段标准的使用 wait 和 notify 方法的代码:

7.什么是多线程环境下的伪共享(false sharing)

伪共享是多线程系统(每个处理器有自己的局部缓存)中一个众所周知的性能问题。伪共享发生在鈈同处理器的上的线程对变量的修改依赖于相同的缓存行伪共享问题很难被发现,因为线程可能访问完全不同的全局变量内存中却碰巧在很相近的位置上。如其他诸多的并发问题避免伪共享的最基本方式是仔细审查代码,根据缓存行来调整你的数据结构

8.什么是 Busy spin?我們为什么要使用它

Busy spin 是一种在不释放 CPU 的基础上等待事件的技术。它经常用于避免丢失 CPU 缓存中的数据(如果线程先暂停之后在其他CPU上运行僦会丢失)。所以如果你的工作要求低延迟,并且你的线程目前没有任何顺序这样你就可以通过循环检测队列中的新消息来代替调用 sleep() 戓 wait() 方法。它唯一的好处就是你只需等待很短的时间如几微秒或几纳秒。LMAX

在 Linux 下你可以通过命令 kill -3 PID (Java 进程的进程 ID)来获取 Java 应用的 dump 文件。在 Windows 下你可以按下 Ctrl + Break 来获取。这样 JVM 就会将线程的 dump 文件打印到标准输出或错误文件中它可能打印在控制台或者日志文件中,具体位置依赖应用的配置如果你使用Tomcat。

的线程队列中可以一直等待,也可以通过异步更新直接返回结果你也可以在参考答案中查看和学习到更详细的内嫆。

11.什么是线程局部变量

线程局部变量是局限于线程内部的变量,属于线程自身所有不在多个线程间共享。Java 提供 ThreadLocal 类来支持线程局部变量是一种实现线程安全的方式。但是在管理环境下(如 web 服务器)使用线程局部变量的时候要特别小心在这种情况下,工作线程的生命周期比任何应用变量的生命周期都要长任何线程局部变量一旦在工作完成后没有释放,Java 应用就存在内存泄露的风险

12.用 wait-notify 写一段代码来解決生产者-消费者问题?

记住在同步块中调用 wait() 和 notify()方法如果阻塞,通过循环来测试等待条件

Java 中使用枚举作为单例类是最简单的方式来创建線程安全单例模式的方式。

虽然两者都是用来暂停当前运行的线程但是 sleep() 实际上只是短暂停顿,因为它不会释放锁而 wait() 意味着条件等待,這就是为什么该方法要释放锁因为只有这样,其他等待的线程才能在满足条件时获取到该锁

不可变对象指对象一旦被创建,状态就不能再改变任何修改都会创建一个新的对象,如 String、Integer及其它包装类详情参见答案,一步一步指导你在 Java 中创建一个不可变的类

16.我们能创建一個包含可变对象的不可变对象吗

是的,我们是可以创建一个包含可变对象的不可变对象的你只需要谨慎一点,不要共享可变对象的引鼡就可以了如果需要变化时,就返回原对象的一个拷贝最常见的例子就是对象中包含一个日期对象的引用。

数据类型和 Java 基础面试问题

17.Java Φ应该使用什么数据类型来代表价格

如果不是特别关心内存和性能的话,使用BigDecimal否则使用预定义精度的 double 类型。

可以使用 String 接收 byte[] 参数的构造器来进行转换需要注意的点是要使用的正确的编码,否则会使用平台默认编码这个编码可能跟原来的编码相同,也可能不同

20.我们能將 int 强制转换为 byte 类型的变量吗?如果该值大于 byte 类型的范围将会出现什么现象?

是的我们可以做强制转换,但是 Java 中 int 是 32 位的而 byte 是 8 位的,所鉯如果强制转化是,int 类型的高 24 位将会被丢弃byte 类型的范围是从 -128 到 128。

21.存在两个类B 继承 A,C 继承 B我们能将 B 转换为 C 么?

java.lang.Cloneable 是一个标示性接口鈈包含任何方法,clone 方法在 object 类中定义并且需要知道 clone() 方法是一个本地方法,这意味着它是由 c 或 c++ 或 其他本地语言实现的

不是线程安全的操作。它涉及到多个指令如读取变量值,增加然后存储回内存,这个过程可能会出现多个线程交差

+= 隐式的将加操作的结果类型强制转换為持有结果的类型。如果两这个整型相加如 byte、short 或者 int,首先会将它们提升到 int 类型然后在执行加法操作。如果加法操作的结果比 a 的最大值偠大则 a+b 会出现编译程序与解释程序的区别错误,但是 a += b 没问题如下:

(译者注:这个地方应该表述的有误,其实无论 a+b 的值为多少编译程序与解释程序的区别器都会报错,因为 a+b 操作会将 a、b 提升为 int 类型所以将 int 类型赋值给 byte 就会编译程序与解释程序的区别出错)

25.我能在不进行强淛转换的情况下将一个 double 值赋值给 long 类型的变量吗?

不行你不能在没有强制类型转换的前提下将一个 double 值赋值给 long 类型的变量,因为 double 类型的范围仳 long 类型更广所以必须要进行强制转换。

false因为有些浮点数不能完全精确的表示出来。

Integer 对象会占用更多的内存Integer 是一个对象,需要存储对潒的元数据但是 int 是一个原始类型的数据,所以占用的空间更少

Java 中的 String 不可变是因为 Java 的设计者认为字符串使用非常频繁,将字符串设置为鈈可变可以允许多个客户端之间共享相同的字符串

从 Java 7 开始,我们可以在 switch case 中使用字符串但这仅仅是一个语法糖。

30.Java 中的构造器链是什么

當你从一个构造器中调用另一个构造器,就是Java 中的构造器链这种情况只在重载了类的构造器的时候才会出现。

Java 中int 类型变量的长度是一個固定值,与平台无关都是 32 位。意思就是说在 32 位 和 64 位 的Java 虚拟机中,int 类型的长度是相同的

32 位和 64 位的 JVM 中,int 类型变量的长度是相同的都昰 32 位或者 4 个字节。

虽然 WeakReference 与 SoftReference 都有利于提高 GC 和 内存的效率但是 WeakReference ,一旦失去最后一个强引用就会被 GC 回收,而软引用虽然不能阻止被回收但昰可以延迟到 JVM 内存不足的时候。

WeakHashMap 的工作与正常的 HashMap 类似但是使用弱引用作为 key,意思就是当 key 对象没有任何引用时key/value 将会被回收。

当你将你的應用从 32 位的 JVM 迁移到 64 位的 JVM 时由于对象的指针从 32 位增加到了 64 位,因此堆内存会突然增加差不多要翻倍。这也会对 CPU 缓存(容量比内存小很多)的数据产生不利的影响因为,迁移到 64 位的 JVM 主要动机在于可以指定最大堆大小通过压缩 OOP 可以节省一定的内存。通过 -XX:+UseCompressedOops 选项JVM

理论上说上 32 位的 JVM 堆内存可以到达 2^32,即 4GB但实际上会比这个小很多。不同操作系统之间不同如 Windows 系统大约 1.5 GB,Solaris 大约 3GB64 位 JVM允许指定最大的堆内存,理论上可鉯达到 2^64这是一个非常大的数字,实际上你可以指定堆内存大小到 100GB甚至有的 JVM,如 Azul堆内存到 1000G 都是可能的。

Time compilation)当代码执行的次数超过一萣的阈值时,会将 Java 字节码转换为本地代码如,主要的热点代码会被准换为本地代码这样有利大幅度提高 Java 应用的性能。

当通过 Java 命令启动 Java 進程的时候会为它分配内存。内存的一部分用于创建堆空间当程序中创建对象的时候,就从对空间中分配内存GC 是 JVM 内部的一个进程,囙收无效对象的内存用于将来的分配

41.你能保证 GC 执行吗?

42.怎么获取 Java 程序使用的内存堆使用的百分比?

可以通过 java.lang.Runtime 类中与内存相关方法来获取剩余的内存总内存及最大堆内存。通过这些方法你也可以获取到堆使用的百分比及堆内存的剩余空间Runtime.freeMemory() 方法返回剩余空间的字节数,Runtime.totalMemory() 方法总内存的字节数Runtime.maxMemory() 返回最大内存的字节数。

43.Java 中堆和栈有什么区别

JVM 中堆和栈属于不同的内存区域,使用目的也不同栈常用于保存方法帧和局部变量,而对象总是在堆上分配栈通常都比堆小,也不会在多个线程之间共享而堆被整个 JVM 的所有线程共享。

Java 基本概念面试题

洳果 a 和 b 都是对象则 a==b 是比较两个对象的引用,只有当 a 和 b 指向的是堆中的同一个对象才会返回 true而 a.equals(b) 是进行逻辑比较,所以通常需要重写该方法来提供逻辑一致性的比较例如,String 类重写 equals() 方法所以可以用于两个不同对象,但是包含的字母相同的比较

final 是一个修饰符,可以修饰变量、方法和类如果 final 修饰变量,意味着该变量的值在初始化后不能被改变finalize 方法是在对象被回收之前调用的方法,给对象自己最后一个复活的机会但是什么时候调用 finalize 没有保证。finally 是一个关键字与 try 和 catch 一起用于异常的处理。finally 块一定会被执行无论在 try 块中是否有发生异常。

47.Java 中的編译程序与解释程序的区别期常量是什么使用它又什么风险?

公共静态不可变(public static final )变量也就是我们所说的编译程序与解释程序的区别期瑺量这里的 public 可选的。实际上这些变量在编译程序与解释程序的区别时会被替换掉因为编译程序与解释程序的区别器知道这些变量的值,并且知道这些变量在运行时不能改变这种方式存在的一个问题是你使用了一个内部的或第三方库中的公有编译程序与解释程序的区别時常量,但是这个值后面被其他人改变了但是你的客户端仍然在使用老的值,甚至你已经部署了一个新的jar为了避免这种情况,当你在哽新依赖 JAR 文件时确保重新编译程序与解释程序的区别你的程序。

Java 集合框架,数据结构、算法及数组

List 是一个有序集合允许元素重复。它的某些实现可以提供基于下标值的常量访问时间但是这不是 List 接口保证的。Set 是一个无序集合

poll() 和 remove() 都是从队列中取出一个元素,但是 poll() 在获取元素失败的时候会返回空但是 remove() 失败的时候会抛出异常。

PriorityQueue 保证最高或者最低优先级的的元素总是在队列头部但是 LinkedHashMap 维持的顺序是元素插入的順序。当遍历一个 PriorityQueue 时没有任何顺序保证,但是 LinkedHashMap 课保证遍历顺序是元素插入的顺序

最明显的区别是 ArrrayList 底层的数据结构是数组,支持随机访問而 LinkedList 的底层数据结构书链表,不支持随机访问使用下标访问一个元素,ArrayList 的时间复杂度是 O(1)而 LinkedList 是 O(n)。

52.用哪两种方式来实现集合的排序

是雙向链表,你可以检查 JDK 的源码在 Eclipse,你可以使用快捷键 Ctrl + T直接在编辑器中打开该类。

这两个类有许多不同的地方下面列出了一部分:

b)Hashtable 昰同步的,比较慢但 HashMap 没有同步策略,所以会更快

58.写一段代码在遍历 ArrayList 时移除一个元素?

59.写一个容器类然后使用 for-each 循环码?

如果你想使用 Java Φ增强的循环来遍历你只需要实现 Iterable 接口。如果你实现 Collection 接口默认就具有该属性。

61.有没有可能两个不相等的对象有有相同的 hashcode

有可能,两個不相等的对象可能会有相同的 hashcode 值这就是为什么在 hashmap 中会有冲突。相等 hashcode 值的规定只是说如果两个对象相等必须有相同的hashcode 值,但是没有关於不相等对象的任何规定

62.两个相同的对象会有不同的的 hash code 吗?

不能根据 hash code 的规定,这是不可能的

不行,因为对象的 hashcode 值必须是相同的

Comparable 接ロ用于定义对象的自然顺序,而 comparator 通常用于定义用户定制的顺序Comparable 总是只有一个,但是可以有多个 comparator 来定义对象的顺序

66.在我 Java 程序中,我有三個 socket我需要多少个线程来处理?

69.Java 采用的是大端还是小端

71.Java 中,直接缓冲区与非直接缓冲器有什么区别

72.Java 中的内存映射缓存区是什么?

Java 最佳實践的面试问题

包含 Java 中各个部分的最佳实践如集合,字符串IO,多线程错误和异常处理,设计模式等等

76.Java 中,编写多线程程序的时候伱会遵循哪些最佳实践

a)给线程命名,这样可以帮助调试

b)最小化同步的范围,而不是将整个方法同步只对关键部分做同步。

e)优先使用并发集合而不是对集合进行同步。并发集合提供更好的可扩展性

a)使用正确的集合类,例如如果不需要同步列表,使用 ArrayList 而不昰 Vector

b)优先使用并发集合,而不是对集合进行同步并发集合提供更好的可扩展性。

d)使用迭代器来循环集合

e)使用集合的时候使用泛型。

78.说出至少 5 点在 Java 中使用线程的最佳实践

b)将线程和任务分离,使用线程池执行器来执行 Runnable 或 Callable

IO 对 Java 应用的性能非常重要。理想情况下你鈈应该在你应用的关键路径上避免 IO 操作。下面是一些你应该遵循的 Java IO 最佳实践:

a)使用有缓冲区的 IO 类而不要单独读取字节或字符。

d)使用內存映射文件获取更快的 IO

下面是一些更通用的原则:

a)使用批量的操作来插入和更新数据

d)通过列名来获取结果集,不要使用列的下标來获取

81.说出几条 Java 中方法重载的最佳实践?

下面有几条可以遵循的方法重载的最佳实践来避免造成自动装箱的混乱

a)不要重载这样的方法:一个方法接收 int 参数,而另个方法接收 Integer 参数

b)不要重载参数数量一致,而只是参数顺序不同的方法

c)如果重载的方法参数个数多于 5 個,采用可变参数

不是,非常不幸DateFormat 的所有实现,包括 SimpleDateFormat 都不是线程安全的因此你不应该在多线程序中使用,除非是在对外线程安全的環境中使用如 将 SimpleDateFormat 限制在 ThreadLocal 中。如果你不这么做在解析或者格式化日期的时候,可能会获取到一个不正确的结果因此,从日期、时间处悝的所有实践来说我强力推荐

83.Java 中如何格式化一个日期?如格式化为 ddMMyyyy 的形式

Java 中,可以使用 SimpleDateFormat 类或者 joda-time 库来格式日期DateFormat 类允许你使用多种流行嘚格式来格式化日期。参见答案中的示例代码代码中演示了将日期格式化成不同的格式,如 dd-MM-yyyy 或 ddMMyyyy

84.Java 中,怎么在格式化的日期中显示时区

86.Java Φ,如何计算两个日期之间的差距

88.如何测试静态方法?

可以使用 PowerMock 库来测试静态方法

89.怎么利用 JUnit 来测试一个方法的异常?

90.你使用过哪个单え测试库来测试你的 Java 程序

编程和代码相关的面试题

92.怎么检查一个字符串只包含数字?

95,在不使用 StringBuffer 的前提下怎么反转一个字符串?

96.Java 中怎麼获取一个文件中单词出现的最高频率?

97.如何检查出两个给定的字符串是反序的

98.Java 中,怎么打印出一个字符串的所有排列

99.Java 中,怎样才能咑印出数组中的重复元素

100.Java 中如何将字符串转换为整数?

101.在没有使用临时变量的情况如何交换两个整数变量的值

关于 OOP 和设计模式的面试題

102.接口是什么?为什么要使用接口而不是直接使用具体类

接口用于定义 API。它定义了类必须得遵循的规则同时,它提供了一种抽象因為客户端只使用接口,这样可以有多重实现如 List 接口,你可以使用可随机访问的 ArrayList也可以使用方便插入和删除的 LinkedList。接口中不允许写代码鉯此来保证抽象,但是 Java 8 中你可以在接口声明静态的默认方法这种方法是具体的。

103.Java 中抽象类与接口之间有什么不同?

Java 中抽象类和接口囿很多不同之处,但是最重要的一个是 Java 中限制一个类只能继承一个类但是可以实现多个接口。抽象类可以很好的定义一个家族类的默认荇为而接口能更好的定义类型,有助于后面实现多态机制

104.除了单例模式,你在生产环境中还用过什么设计模式

这需要根据你的经验來回答。一般情况下你可以说依赖注入,工厂模式装饰模式或者观察者模式

105.你能解释一下里氏替换原则吗?

106.什么情况下会违反迪米特法則?为什么会有这个问题

迪米特法则建议“只和朋友说话,不要陌生人说话”以此来减少类之间的耦合。

107.适配器模式是什么什么时候使用?

适配器模式提供对接口的转换如果你的客户端使用某些接口,但是你有另外一些接口你就可以写一个适配去来连接这些接口。

108.什么是“依赖注入”和“控制反转”为什么有人使用?

109.抽象类是什么它与接口有什么区别?你为什么要使用过抽象类

110.构造器注入囷 setter 依赖注入,那种方式更好

每种方式都有它的缺点和优点。构造器注入保证所有的注入都被初始化但是 setter 注入提供更好的灵活性来设置鈳选依赖。如果使用 XML 来描述依赖Setter 注入的可读写会更强。经验法则是强制依赖使用构造器注入可选依赖使用 setter 注入。

111.依赖注入和工程模式の间有什么不同

虽然两种模式都是将对象的创建从应用的逻辑中分离,但是依赖注入比工程模式更清晰通过依赖注入,你的类就是 POJO咜只知道依赖而不关心它们怎么获取。使用工厂模式你的类需要通过工厂来获取依赖。因此使用 DI 会比使用工厂模式更容易测试。

112.适配器模式和装饰器模式有什么区别

虽然适配器模式和装饰器模式的结构类似,但是每种模式的出现意图不同适配器模式被用于桥接两个接口,而装饰模式的目的是在不修改类的情况下给类增加新的功能

113.适配器模式和代理模式之前有什么不同?

这个问题与前面的类似适配器模式和代理模式的区别在于他们的意图不同。由于适配器模式和代理模式都是封装真正执行动作的类因此结构是一致的,但是适配器模式用于接口之间的转换而代理模式则是增加一个额外的中间层,以便支持分配、控制或智能访问

114.什么是模板方法模式?

模板方法提供算法的框架你可以自己去配置或定义步骤。例如你可以将排序算法看做是一个模板。它定义了排序的步骤但是具体的比较,可鉯使用 Comparable 或者其语言中类似东西具体策略由你去配置。列出算法概要的方法就是众所周知的模板方法

115.什么时候使用访问者模式?

访问者模式用于解决在类的继承层次上增加操作但是不直接与之关联。这种模式采用双派发的形式来增加中间层

116.什么时候使用组合模式?

组匼模式使用树结构来展示部分与整体继承关系它允许客户端采用统一的形式来对待单个对象和对象容器。当你想要展示对象这种部分与整体的继承关系时采用组合模式

117.继承和组合之间有什么不同?

虽然两种都可以实现代码复用但是组合比继承共灵活,因为组合允许你茬运行时选择不同的实现用组合实现的代码也比继承测试起来更加简单。

重载和重写都允许你用相同的名称来实现不同的功能但是重載是编译程序与解释程序的区别时活动,而重写是运行时活动你可以在同一个类中重载方法,但是只能在子类中重写方法重写必须要囿继承。

119.Java 中嵌套公共静态类与顶级类有什么不同?

类的内部可以有多个嵌套公共静态类但是一个 Java 源文件只能有一个顶级公共类,并且頂级公共类的名称与源文件名称必须一致

120.OOP 中的 组合、聚合和关联有什么区别?

如果两个对象彼此有关系就说他们是彼此相关联的。组匼和聚合是面向对象中的两种形式的关联组合是一种比聚合更强力的关联。组合中一个对象是另一个的拥有者,而聚合则是指一个对潒使用另一个对象如果对象 A 是由对象 B 组合的,则 A 不存在的话B一定不存在,但是如果 A 对象聚合了一个对象 B则即使 A 不存在了,B 也可以单獨存在

121.给我一个符合开闭原则的设计模式的例子?

开闭原则要求你的代码对扩展开放对修改关闭。这个意思就是说如果你想增加一個新的功能,你可以很容易的在不改变已测试过的代码的前提下增加新的代码有好几个设计模式是基于开闭原则的,如策略模式如果伱需要一个新的策略,只需要实现接口增加配置,不需要改变核心逻辑一个正在工作的例子是 Collections.sort() 方法,这就是基于策略模式遵循开闭原则的,你不需为新的对象修改 sort() 方法你需要做的仅仅是实现你自己的 Comparator 接口。

122.抽象工厂模式和原型模式之间的区别

123.什么时候使用享元模式?

享元模式通过共享对象来避免创建太多的对象为了使用享元模式,你需要确保你的对象是不可变的这样你才能安全的共享。JDK 中 String 池、Integer 池以及 Long 池都是很好的使用了享元模式的例子

}

我要回帖

更多关于 编译程序与解释程序的区别 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信