当前位置: 首页 > 科技观察

如何写出同事看不懂的Java代码?_0

时间:2023-03-15 18:43:28 科技观察

大家好,我是Hydra因为没有更新所以在家忙着带娃。前几天正好赶上群里的codereview。下午下来后,感觉血压都满了。代码的五花八门让我感叹,代码规范的道路还任重而道远……所以今天就总结一波Java的代码杀码技巧。掌握了这些技巧,保证你能写出同事看不懂的代码~至于为什么写出同事看不懂的代码,通过这节课,我发现还是有很多好处的。我举几个例子:同事不能轻易修改你的代码,避免团队协作不畅引入bug。塑造个人能力的不可替代性,避免被辞退的风险。在代码审查期间,我帮助我的同事治疗了多年的低血压。好了,正经的废话到此结束……废话不多说,下面开始吧。没用的知识又会增加...1.我敢打赌你永远不会想到有人会在评论中下毒。看看下面的代码,很简单,main方法中只有一行注释。publicstaticvoidmain(String[]args){//\u000dSystem.out.println("coderHydra");}猜猜这个程序的结果是什么?执行完之后,居然会在控制台打印:coderHydra,看到这里你是不是一头雾水,为什么注释里的代码被执行了?其实原理就在于大家熟悉的unicode编码。上面的\u000d是unicode转义符,代表换行符。java中的编译器不仅会编译代码,还会解析unicode编码,替换成对应的字符。所以,上面的代码解析之后,其实是这样的:publicstaticvoidmain(String[]args){//System.out.println("coderHydra");}这样就可以解释为什么注释可以执行语句。当然,如果你觉得上面的代码还不够完善,想要更完美,你可以把代码写成如下。publicstaticvoidmain(String[]args){inta=1;//\u000d\u0061\u002b\u002b\u003bSystem.out.println(a);}执行结果会打印2,同理,因为下面unicode编码的转义就是a++;。至于这样写的好处,当然是用在一些不想让别人看懂的地方,用来骗人的。下面这个笑话估计大家都看过了。这样写,如果客户看得懂一些代码,会很容易看出来,但是这样写,大部分人会认为是一段乱码://\u000d\u0054\u0068\u0072\u0065\u0061\u0064\u002e\u0073\u006c\u0065\u0065\u0070\u0028\u0032\u0030\u0030\u0030\u0029\u003b恕我直言,没有几十年的技能,我真的看不出来sleep在这里执行,很完美。2.如果你想写出别人看不懂的代码,最重要的技巧之一就是把简单的事情复杂化。比如判断一个int类型数的正负时,可以这样写:publicvoidjudge(intx){if(x>0){//...}elseif(x<0){//...}}但是我不想,我不使用简单的代码,我只是玩,我必须这样写:publicvoidjudge2(intx){if(x>>>31==0){//...}elseif(x>>>31==1){//...}}怎么样,这样写的话,会被强制站向上!其他人看到这里,不得不琢磨一下,这块到底写的是什么东西。其实原理也很简单。这里使用的>>>是一个无符号右移操作。举个简单的例子,以-3为例,在移位之前先将其转化为它的补码:111111111111111111111111111101无符号右移一位,变成如下形式。这个数转换成十进制后就是2147483646。011111111111111111111111111110因此,当一个int类型的数无符号右移31位时,前31位高位全为0,剩下的最低位为原来的符号位,所以可以用来判断这个数是否是正面或负面。基于这点小知识,我们还是可以做很多工作的。例如,我们可以通过以下方式定义一个0,而不是使用好的0:intZERO=Integer.MAX_VALUE>>31>>1;通过上面的知识,相信大家都很容易理解了,因为当一个数无符号右移32位后,二进制所有位都是0,所以最后会是0。那么问题来了,为什么不我只是使用Integer.MAX_VALUE>>32一次右移32位?这是因为int类型的数字移位时,会对运算符右边的参数进行取模32取余运算,所以如果直接写32,相当于什么都不做,得到的是原来的值。3.颠倒黑白古代赵高指出鹿为马,现在却有码农颠倒道理。阻碍同事阅读你代码的有力武器之一就是让他在遇到条件判断时失去基本的判断能力,陷入迷雾之中,不知道下一步该走哪个分支。下面这段代码,我说它会打印fasle,不会有人相信吧?publicclassTrueTest{publicstaticvoidmain(String[]args){Booleanreality=true;如果(现实){System.out.println(“真”);}else{System.out.println("false");}}}没错,只要了解Boolean类型,就会知道这是不符合逻辑的,但是经过下面的改造,就可以变成现实了。首先,在类中找到一个隐藏的地方,插入如下代码:static{try{FieldtrueField=Boolean.class.getDeclaredField("TRUE");trueField.setAccessible(true);字段modifiersField=Field.class.getDeclaredField("modifiers");modifiersField.setAccessible(true);modifiersField.setInt(trueField,trueField.getModifiers()&~Modifier.FINAL);trueField.set(null,false);}catch(IllegalAccessException|NoSuchFieldExceptione){e.打印堆栈跟踪();}}然后运行上面的程序,你会发现神奇的打印出了false。其实原理也很简单。首先通过反射获取Boolean类中定义的TRUE变量:publicstaticfinalBooleanTRUE=newBoolean(true);然后用反射去掉它的final修饰符,最后设置它的值为false。在后面使用true定义布尔类型变量的过程中,会进行自动装箱,调用如下方法:publicstaticBooleanvalueOf(booleanb){return(b?TRUE:FALSE);}这时候,b为真,而TRUE实际上为假,所以第一个表达式不满足,最终会返回false。这样上面的打印结果就可以解释了,但是记住,这样写的时候,一定要找到代码中的隐藏角落,以免被发现,否则很容易被打得很惨……4.分成几部分然后要介绍的技术有点厉害。它可以将原来的一段串口逻辑重写到判断逻辑的不同分支中,保证最终能够正常执行。在我们开始之前,我想问一个问题。有没有办法同时执行if和else中的语句,如下例:publicstaticvoidjudge(Stringparam){if(/*判断条件*/){System.out.println("stepone");}else{System.out.println("第二步");}}如果我只调用一次这个方法,我就可以同时输出if和else打印语句,你肯定会说不可能,因为这违反了java中判断逻辑的基本常识。没错,在上面修饰符只调用“一次”方法的情况下,谁也做不到。但是如果在判断条件上玩点小技巧,就可以实现上面提到的功能。看看修改后的代码:publicclassIfTest{publicstaticvoidmain(String[]args){judge("Hydra");}publicstaticvoidjudge(Stringparam){if(param==null||newIfTest(){{IfTest.check(null);}}.equals("Hydra")){System.out.println("step一”);}else{System.out.println("第二步");}}}运行后,控制台打印:steponestep2Surprisedorunexpected?其实它执行的秘诀就在于if的判断条件。第一次调用judge()方法时,OR运算中第一个条件不满足,所以执行第二个条件,执行匿名内部类中的实例化初始块代码,judge()方法再次执行,当满足if条件时,执行第一条print语句。但是实例化的new对象在后面的equals()方法中不满足条件,所以不满足if中的任何一个条件,所以会执行else中的语句,执行第二条print语句.这样就实现了表面调用一次方法,同时执行if和else中的语句块的功能。不如这样把一个整体的逻辑拆成两块,把同事搞糊涂了。五、划清界限在程序员的世界里,不同语言之间一直存在着鄙视链。比如写c的就看不起写java的,因为直接操作内存就显得很高大上是不是?所以今天我们假装是一个c语言的程序员来操作java中的一把内存。具体怎么做,还是用java中的魔法类Unsafe。从这个名字也可以理解,如果这个东西使用不当,是不是很安全,所以获取Unsafe实例比较麻烦,需要通过反射获取:FieldunsafeField=Unsafe.class.getDeclaredField("theUnsafe");unsafeField.setAccessible(true);Unsafeunsafe=(Unsafe)unsafeField.get(null);得到这个对象后,我们就可以对内存做任何想做的事情了。比如我们在实现inta=1;这样简单的赋值的时候,可以把它复杂化,绕个弯,这样:voidtest(){longaddr=unsafe.allocateMemory(4);unsafe.putInt(addr,1);inta=unsafe.getInt(addr);System.out.println(a);unsafe.freeMemory(addr);}先通过allocateMemory方法申请4个字节的内存空间,然后通过putInt方法1写入一个内存空间,然后从这个地址读取一个int类型长度的变量,最后实现操作将1分配给a。当然,还有很多更高级的用法。这里有两个简单的例子。voidtest(){longaddr=unsafe.allocateMemory(4);unsafe.setMemory(addr,4,(byte)1);System.out.println(unsafe.getInt(addr));unsafe.freeMemory(地址);}上面的代码中,通过setMemory方法将字节类型1写入每个字节,最后调用getInt方法一次读取4个字节作为一个int类型变量的值。这段代码最终打印结果为16843009,对应的二进制如下:00000001000000010000000100000001至于c语言的内存拷贝,也好用Unsafe:voidtest2(){longaddr=unsafe.allocateMemory(4);longaddr2=unsafe.reallocateMemory(addr,4*2);unsafe.putInt(addr,1);对于(inti=0;i<2;i++){unsafe.copyMemory(addr,addr2+4*i,4);}System.out.println(unsafe.getInt(addr));System.out.println(unsafe.getLong(addr2));unsafe.freeMemory(地址);unsafe.freeMemory(addr2);}上面代码中,通过reallocateMemory方法重新分配了一块8字节的内存空间,分两步将addr开头的4字节内存空间复制到addr2的内存空间中。上面的代码会打印:14294967297这是因为新的8个字符存放在段内存空间addr2中的二进制数如下。转换成十进制long类型后对应的是4294967297。1000000000000000000000000000000001Unsafe不仅可以直接操作内存空间,还具有线程调度、对象操作、CAS操作等实用功能。如果想详细了解,可以看看这篇Java双刃剑Unsafe类详解,打开了新世界的大门。最后,无用的知识介绍环节到此结束。相信大家在掌握了这些技巧之后,都能自带代码混淆光环,写出不一样的拉取代码。最后,我建议大家在项目中这样写代码的时候配合红花油和青梅酒一起使用,可能会有更好的效果。