2024-12-28 16:03

Java开发成长之路第一年

听见冬天离开

职场

(34)

(0)

收藏

blog

本文总结了要走Java开发这条路的成长轨迹。欢迎各位看客留下脚印。

正文开始之前,先申明几点:本文说的“Java开发”指的是以Java后端为主、少量的前端开发,如果要了解更多前端开发的内容请移步;另外,本文假设您在学习Java之前没有其他语言的基础。

这是一个系列的文章,而且会长期更新,欢迎各位看客关注!

  • 前言

这时候,作为刚刚加入“码农”行列的您,还是有几点是必须要搞定的:Java语法、行业代码规范(类、方法、变量等的命名规范)、计算机基本的运行原理、Java程序运行的基本原理。我这里说的“搞定”的更加准确的含义指的是:要很掌握的很熟练。因为这就像是楼梯的第一个台阶、高楼大厦的地基一样,它直接影响你后续阶段的发展。

您别小瞧这几点,以为在学校里就早早地对Java语法很了解了;不止您在成长,Java本身也在发展中,所以Java的不同版本之间可能也会有或大或小、或多或少的差异,忽略这一点的童鞋可能会被“绊脚石”磕碰的很惨。

程序是运行在计算机上的,为了以后的发展,可以说对计算机的原理研究的不管多么深入都是很有必要的。但现在,你至少要对CPU、内存、硬盘、网卡等大部件的性能参数有所了解。

而Java呢,是需要编译为class文件(而不是机器码),然后在虚拟机(JVM)上运行的。这一点,决定了Java是跨平台(Linux、Windows等)的,也决定了它可能会比C语言要运行的慢一点。它的内存管理机制,又决定了Java是比较耗内存的——虽然所占用的内存并不是时时刻刻都需要的。

以上三点相对来说都是比较范范的东西,下面我来说一下对于初出茅庐的Java程序员们有哪些技能是必须掌握的,换句话说说,掌握了下面的内容你就可以轻松应对一些面试了:

  • 方法的重写的重载

方法的重写(override),指的是子类重写(覆盖)父类的方法。要求方法签名要保持一致,不能抛出更多的异常(封装),不能有更加严格的访问权限(多态)。

方法的重载(overload),指的是在同一个类或者子类中来重载一个方法,两个方法之间要求参数列表不能相同,而其他的要相同。这是为了能够有多个过程类似的方法实现的。

  • Java接口、类(抽象)的概念

接口可以理解为是一种规范、标准、协议,而并不关心具体实现过程。接口中的方法只能使用public、abstract来修饰,而属性只能是用public、static、final来修饰。换句话说,在接口中即使不添加任何修饰符也可以,因为它只能添加那些修饰符——方法必然是public abstract的,而属性一定是public static final的。根据接口中属性的特点,我们可以推断,在申明属性变量时必须给赋初始值,而且不能被修改。

如果有人要问,在接口的方法上添加abstract或者不添加有没有区别的话,从上文就可以推断出——是一样的。对应的属性也是这个道理。

普通类中,所有的方法必须要有方法体——也就是说必须要有实现,同样的方法不可以使用abstract来修饰。类只允许继承一个类,但是可以实现多个接口。类A实现类B,可以理解为类A也是一种B;而类A实现接口B、C,则可以理解为类A有B和C的特性(特点)。

而抽象类,就像是接口和普通类的结合体,方法既可以有抽象的(abstract)的,也可以没有。对属性则没有任何限制。另外,由于抽象类中有可能有抽象方法,所以是无法直接进行实例化的。

  • Java的集合(Collection)体系

这是必须要弄明白的,在日常工作中跟它打交道的次数太多了。首先是java.util.Collection这个接口,它是集合的顶层接口,另外要留心它所在的包(package)是java.util。它的子接口包括List、Set,List的实现类包括:ArrayList、LinkedList、Vector。Set的实现类包括:HashSet、LinkedHashSet、TreeSet。

ArrayList的内部是用动态数组(普通数组的容量必须在初始化时给定)实现的,根据数组下标来对元素进行操作,具有访问快速、修改较慢的特点,这些是由于ArrayList的存储结构导致的。如果调用无参构造函数的话,会把内部数组elementData赋值一个空数组;如果调用的是带有一个int类型参数(容量capacity)的构造函数的话,会创建一个指定大小的数组。在调用add方法时,会先检查当前数组是否可以在容纳一个元素(这时候,保证至少会有10的容量),如果不够的话就准备扩容(最大容量为Integer.MAX_VALUE)——先是创建一个新容量的数组,然后把之前的元素拷贝过去,最后把要添加的元素加入新的数组中。

根据以上ArrayList类的特点,我们可以总结出一些ArrayList的使用技巧:如果您估计ArrayList中要添加的元素个数超过10个的话,最好调用有int参数的构造函数给定一个初始容量,这样能有效地避免频繁扩容导致的频繁申请内存(堆),甚至能避免一些不必要的gc事件;ArrayList适合读取和修改操作较多的场合,而不适合于添加或者删除较多的场合;ArrayList并没有实现了多线程之间的同步,所以是线程不安全的。

可以通过工具类java.util.Collections的sort方法给List排序。

通过Collections的binarySearch来快速地查找List中的元素。

LinkedList<E>实现的是双向链表(Doubly-linked),另外还实现了接口java.util.Deque<E>。它有add、addFirst、addLast、addAll等方法,add等同于addLast,set方法可以添加到指定位置。调用get方法时,它会从所有的元素中遍历查找。所以,LinkedList和ArrayList相反,插入、新增速度快,查找速度慢。另外,它也是线程不安全的。

Vector<E>除了实现了List接口外还实现了java.util.RandomAccess接口,默认的容量为10,内部也是用数组的形式实现的。它的add以及其他操作元素的方法使用了关键字synchronized,因此是线程安全的,也可以认为Vector是线程安全版本的ArrayList。

TreeSet<E>内部是利用TreeMap实现的,调用add方法时其实就是向map中添加一条key,value的值统一都是Object类。

HashSet<E>内部是利用HashMap实现的。add方法实现过程与TreeSet类似。

Java集合体系中的java.util.Map比较特殊,和Collection不是一类,它本身就是顶层接口。它的实现类包括:HashMap、Hashtable。

HashMap<K,V>默认的构造函数会初始化一个容量(应该是2的倍数,最大容量为Integer.MAX_VALUE / 2 + 1,或者为1 << 30)为10、因子(load factor)为0.75的集合,之后调用init方法(默认为空实现,是让子类来扩展的)。

HashMap第一次调用put方法时,内部会调用方法inflateTable来初始化,所以说如果只是申明了一个Map对象后并不会占用任何空间。从这里可以看到HashMap内部也是通过数组实现的,数组的元素类型为HashMap.Entry;而Entry类包含了key、value、下一个Entry的引用、hash值。而当key为null时,则内部会调用方法putForNullKey,把key为null的Entry会放到数组的第一个。这时候会通过hash方法来计算key的hash值,hash方法会先调用key对象的hashCode,然后再进行一些hash运算。之后利用hash值和数组的长度计算出来一个数组下标值,如果数组中的该下标(index)中已经有了Entry对象,则从Entry链中通过hash查找是否已经存在了相同的key,有的话更新;没有的话,则新增一个Entry放到数组的该下标中,然后该Entry指向之前的Entry,形成一个Entry链。

从上面对HashMap的分析,可以得出一些结论。它内部是由多个元素类型为HashMap.Entry的数组实现,因此占用的内存空间还是比较大的。key可以为null。它从Java1.6开始出现,是非线程安全的。

Hashtable实现了抽象类Dictionary<K,V>。它从Java1.0开始出现。它的初始化过程和HashMap类似,但初始容量(capacity)为11,因子(load factor)为0.75f。

Hashtable的put方法使用关键字(synchronized)保证了线程间的同步,当key或者value为空(null)时会抛出空指针异常。

TreeMap<K,V>实现了接口NavigableMap<K,V>。该集合和HashMap实现的过程类似,只是在调用put方法时就会先对key进行排序。这个排序就要求key实现接口Comparator<? super K>,或者是在实例化TreeMap时给定一个Comparator。

NavigableMap<K,V>该接口继承了接口SortedMap<K,V>,从Java1.6开始出现。

  • Java流(Stream)体系

Java中的“流”相关内容都在包java.io中,想要系统的了解Java流就要知道有哪些分类。

按照大的类型分为:输入流(InputStream)和输出流(OutputStream)。这两个顶层的流都是用抽象类来表示的,它们规定了输入、输出流的基本方法,它们是基于字节(byte)流。

java.lang.Readable和java.lang.Appendable是另一对Java“流”接口。注意这两接口在java.io中对应的顶层抽象类分别为:Reader、Writer,它们是针对字符类型的流。

DataInput和DataOutput是Java“流”中的面向数据类型的输入、输出流。

所有类型的输入、输出流都实现了接口Closeable,它只有一个close方法,那这个close方法应该由谁来调用呢,我这里有个建议——谁打开的“流”谁来关闭。


0条评论

点击登录参与评论