从“四大名著”上重新认识Java(一)
手足的茧疤,是越磨越厚;沾了泥土尘埃的煤炭,是越洗越黑。人的面皮很薄,慢慢的磨练,就渐渐地加厚了;人的心,生来是黑的,遇着讲因果的人,讲理学的人,拿些道德仁义蒙在上面,才不会黑,假如把他洗去了,黑的本体自然出现。
《厚黑学》
从大学起才正式接触编程,学的第一门也不是Java,而是C。虽说两者一个是面向对象,一个是面向过程,但对于我们初学者来说,这些定义也只是死记硬背,并且在大学做了几个简单Demo后也没感觉出区别。
《Thinking in Java》这本书用了两个章节才把“对象”理论讲完。但读了两三遍的我也只是---知其大意,不知所以然。
那么当面试官问你面向对象的时候,该如何回答(就算是大厂也会问)。在之前的笔记中,我是从面向对象说到三大特征,这其实参考了这本书的思路。我个人感觉,这题更像是在问你的表达和对计算机的理解。下面是我的答案。
OOP即面向对象编程,是一种编程思想,也是对计算机问题的一种解决方案。用木匠打一个比方,一个“面向对象的”木匠始终关注的是所制作的椅子,第二位才是所使用的工具;一个”非面向对象的“木匠首先考虑的是所用的工具。(来自于Java核心技术卷 卷Ⅰ)。
吐槽:在书上读这句话有点像现在的一个小部分的现状,为了学计算机(挣大钱),第一门语言去学习被大多数培训机构吹嘘的Python,以为这样能进入大数据、AI领域。又或者因为区块链大火,第一门去学Go。其实,语言只是工具,我们需要解决什么样的问题就用什么样的工具。(听说这是阿里的规矩,只管解决问题,无论用什么办法)。
如果想多说一些,也可以加上三大特征。
从面向对象角度来说,三大特性是封装、继承、多态。而在《Thinking in Java》这本书的描述来说,是封装,复用类,多态。
提到封装,第一个想法就是属性私有化,get、set方法公开。那么为什么这样做呢?(公司技术总监的一个灵魂拷问)
第一反应,是为了安全。但是这样怎么就保证安全了呢?后来在一些文章上看到说,是因为,如果不这样做,数据是直接赋值,就没有保证数据成为“对象”的思想(赋值与函数都是方法形式),而且直接赋值不会被记录,安全性也缺少保证。
或许是因为要从设计角度讲,书上将继承放在了复用里面,而复用也是对第一章的详细展开。我们说,万物皆对象。那么难免对象与对象之间有关联。比如“父与子”,就是“继承”。“继承”在书上是这样描述的 is-a 。比如说“一个圆形就是一个几何形状”(《Thinking in Java》)。这样来判断抽象的“继承”,就变得更有含义。“兄弟”之间,他们用 is-like-a来表示。还有一些依赖的关系。比如说“我有一部手机”,用has-a来表示。复用类这里面除了常见的组合,继承外,还有代理。
第三种关系称为代理,Java并没有提供对他的直接支持。这是继承与组合之间的中庸之道,因为我们将一个成员对象置于所要构造的类中(就像组合),但与此同时,我们在新类中暴漏了该成员对象的所有方法(就像继承) 《Thinking in Java》
书上的例子是用“太空飞船”继承“控制模块”,虽然启动和飞行都是使用“控制模块”的方法,但是他俩的关系并不是is-a。所以这里的解决办法是先将他俩组合,然后在“太空飞船”里面写具体的方法,每个方法都是调用“控制模块”的实体类进而使用“控制模块”写好的方法。
此外,书上没有再过多描述代理。其实在中软培训的时候,使用SpringMVC时就用到了“代理”,当时写出来又有接口,又有mapper,然后一环套一环。大家都是仿写,并不很理解,直到恢复线下,换了老师我才明白,这是一种设计模式。
对于多态的认知,往往就是类的向上转型,向下转型。那么这种叫法来自哪里呢?在软考里,有一个叫“初级程序员”的考试,虽然说是程序员,但考题几乎不涉及语言。此中便有一项技能,叫UML图,如下
用不同的线,箭头表示类与类之间的关系。图上代表的就是继承。所谓向上转型和向下转型即从UML来的称呼。
转型的意义
代码都会写,但是单独写出来似乎并没有意义。我在学“策略模式”的时候才知道含义。
比如我们写一个简单的排序(比大小)方法,这里参数类型为double的话,那么int,byte类型的也都可以比较。如果我们想比较对象呢?这里模仿了一下Collections的sort
package Strategy;
public class Sorter {
public static void sort(Comparable[] arr) {
for (int i = 0; i < arr.length - 1; i++) {
int minPos = i;
for (int j = i + 1; j < arr.length; j++) {
minPos = arr[j].compareTo(arr[minPos])==-1 ? j : minPos;
}
swap(arr,i,minPos);
}
}
static void swap(Comparable[] arr, int i, int j) {
// TODO Auto-generated method stub
Comparable temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
光是这样还不够,还需要这个类重写下compareTo方法
public int compareTo(Cat c) {
if(this.weight < c.weight ) return -1;
else if(this.weight > c.weight ) return 1;
else return 0;
}
这样无论传进来普通数组还是对象数组都可以排序,这里只是用到了向上转型。减少了代码冗余问题。
Object o = new Object();
在传统意义上来讲,新建一个对象就是这样一个过程。我们唯一可以改变的就是自己创建一个有参构造,在声明的时候,追加属性。在JDBC之后我们认识到“反射”,知道反射也可以生成对象。最后在设计模式里,我们通过工厂来帮我们生成和加工对象,这里便有了更多选择。同时在《Effective Java》中的第一章就是让我们考虑使用静态工厂来创建对象。
同时也指出了他的优点和弊端。
作为一个Java程序员,如果是从事web开发,那么难免接触Spring,甚至被称为Spring程序员。在对其原理解读后,发现Spring有更自由的对一个对象的创建。
前置加强,后置加强,切片,以及处理循环依赖等。
所以站在巨人肩膀上的我们,即使只是学会使用,也可以完成很多工作。而不是从研发角度去构建或优化项目。这样也导致的结果就是在很多情况下,只是因为业务不同,写不同的CRUD罢了。
垃圾回收在Java中已经变成了自动处理,但他们的回收只针对普遍情况。所以在阿里、IBM、还有几个大厂,他们都有自己定制的JVM(阿里的据说只有十个人左右写这个),其中垃圾回收机制也是独家定制的。
这个问题是个很大的课题,等以后学会了,再来分享。
先来看下两段代码
if(条件){
代码段;
}
if(条件)
代码段;
在日常使用中,这二者并无差别。但从安全角度来说,最好使用第一种。因为第二种代码是不利于维护的,苹果曾经也因此犯下过错误。当时就引来很多嘲讽。
除了这种 { } 问题外,还有就是多个if判断问题。我们知道switch有时能够代替多个else if,但是在有一部分情况下,还是需要使用这种多层判断。但是很多个else if看起来总是令人头大,所以这种分支我们除了尽量避免外,可以将他们拆开来写。保证除了if条件只是其他情况的分支。
下图为反例