天天看点

Java开发成长之路第一年介绍前言封装、继承、多态方法的重写的重载Java接口、类(抽象)的概念Java的集合(Collection)体系Java流(Stream)体系Java异常(Exception)体系Java多线程(Thread)设计模式“单例”反射Java用户界面编程SwingJava网络编程JSP原理jQuery常用方法JavaScript基本概念各种浏览器的调试技巧集成开发工具Eclipse的熟练使用应用服务器配置数据库Struts2框架Hibernate框架Spring框架本系列文章

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

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

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

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

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

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

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

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

学java或者其他面向对象的童鞋们,这三个特性应该都是记得很清楚。它很重要,会一直贯穿你的java开发生涯;但这六个字过于浓缩、抽象,是很难理解的。其实,我建议作为java开发人员的第一年,反而可以忘记这些特性,先是多多动手,积累一些项目经验,积累足够的业务场景,积累足够的常见问题后,再回过头来细细研究这三个特性。

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

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

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

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

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

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

这是必须要弄明白的,在日常工作中跟它打交道的次数太多了。首先是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中的“流”相关内容都在包java.io中,想要系统的了解java流就要知道有哪些分类。

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

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

datainput和dataoutput是java“流”中的面向数据类型的输入、输出流。

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

java的异常体系的根类是java.lang.throwable,它有两个子类java.lang.error和java.lang.exception。

error是比较严重的错误,基本会在程序无法继续执行的情况下抛出,我们无法捕获更无法处理。

exception是“编译时”异常的根类,它的子类java.lang.runtimeexception是“运行时”异常。所谓编译时指的是在编译java源文件时抛出的异常,必须在处理(捕获或者抛出)后源文件才可以正常编译;所谓运行时异常,指的是在java程序运行过程中有“可能”抛出的异常。

除了error之外,编译时异常也是比较严重的异常情况,所以必须显示地处理。而运行时异常通常不严重,即遇到了这种异常情况程序也还是可以继续的。

实现一个多线程类,可以实现接口java.lang.runnable或者类java.lang.thread。这里要注意的是类thread也实现了接口runnable。启动一个线程要调用start方法,如下例子所示:

线程间同步,可以在类或者方法上使用关键字synchronized。

另外,还有个线程相关的知识点。使用thread.sleep和object.wait相比较,前者会一直占用cpu,而后者会放弃cpu的控制权。

有关java多线程的更多内容请搜索“java多线程”。

好多人都在谈论“设计模式”,还有一些人可以说出很多种设计模式,例如:单例模式、工厂模式、观察者模式等等。但对于刚刚进入这个行业的人来说,我不认为记住这么多的名称有任何用处,而且在实际项目中也不会有实践的机会。学习设计模式这是一件长期的事情,而且在有一定项目编程经验以后再来学习会更有效果。

我在这里先介绍一个很容易理解的设计模式——单例模式。所谓单例,指的是在虚拟机(jvm)中某个类只有一份实例。通常情况下,单例的实现有几个需要注意的地方:构造函数私有化、提供一个静态方法用于获取实例对象、考虑多线程情况。

在java中所有的都是类,而所有java.lang.object类则是所有类的父类——即使没有“显示地”继承。而jdk中有些类是用来描述“类本身”信息的,它们大多数在包java.lang.reflect中,例如:method、field、constructor、member、type、modifier等。

而我这里提到的反射就是jdk提供的用于获取“类本身”信息的api,换句话说:通过反射技术可以动态第获取甚至修改“类本身”的信息。如果你对c语言有点了解的话,这一点可以和c语言的指针来做对比理解。

那么反射技术通常都用在什么场合呢?利用反射可以做到很多常规程序无法做到的事情,例如代理,而在框架设计中经常会用到反射技术。

jdk中提供的用于编程gui(用户界面)程序的是swing框架。这里的api都是在java.awt和javax.swing中,我们可以认为javax.swing包中的api是对java.awt中api的升级或者改进。例如,按钮的实现类button就有两份,分别是java.awt.button和javax.swing.jbutton。从包的分布和类名上,我们可以看到区别,在javax.swing包中的组件类基本都是大写字母j开头的。这里,我推荐使用javax.swing包中的api。

java是跨平台的,由此提供的swing框架也是在所有的平台上运行时可以保持一致的风格。但由此引发的一个问题是:利用swing编写的gui程序(c/s架构)相对比较丑陋一点,导致在企业开发中用的相对比较少。但是,利用swing来开发一些小的工具还是完全可以的。

这里我介绍一些gui程序原理性的内容,这样你就算没写过也可以多少有点了解。所有的gui程序的底层都是在调动api在屏幕上“绘制图形”,因此呢,可以理解gui程序中会有一个单独的线程来做“绘制”工作,而且这个线程就是主线程。然后,我们继续思考,如果一个界面中的组件(元素)过多的话,就会导致“绘制”线程占用的时间比较多,使用起来显得比较“卡”。

既然上面我已经提到了线程,那么在程序中读取、处理数据时占用的时间比较长的话,就会导致界面更加“卡”。为了解决这个问题,我们通常都会选择新开一个线程来处理占用时间比较长的任务。到此为止呢,我们至少已经有了两个线程,那么在读取、处理数据的线程中要修改界面元素的话,就必然涉及到了线程间同步的问题——我们不可以在新的线程中修改界面元素,否则会发生数据不同步的问题。

java网络编程是很大的一块内容,现在呢,您只需要了解java网络编程中都有哪些接口(功能),以及简单了解网络协议即可。

tcp/ip协议栈分为应用层、传输层、链路层等。java提供了传输层协议的网络编程接口,也就是可以对tcp、udp协议数据包进行操作。而应用层的网络协议,例如:ftp、http、sftp、snap等都需要利用第三方的库了。httpclient即是apache的一个子项目,用于操作http协议。

tcp协议是可靠的协议,它能保证数据包完整地在网络中进行传输,而udp协议则不能保证这一点,但udp传输的速度相对比较快。据此特点,udp经常用在需要传输大量数据,但对数据完整性要求不是很高的场合,例如视频传输等。另外,tcp在发送数据包时,必须要求对方正在简体你指定的端口,否则无法发送;而udp协议则不需要对方一定是正在监听中,它只管发送,不关心对方是否已经接收了。

jsp(java server pages)作为常用的一种动态页面技术,有几个很关键的地方是必须理解清楚、正确的,这也是作为一个合格、称职、专业的程序员所应该了解的。

首先,从jsp的字面上来说,它一定是服务器端(后端)的技术,而不是前端技术,不可以和html混为一谈。和其他的动态页面技术不同,它是需要编译的——当它被访问时,将会由jsp引擎编译(class字节码文件);如果是在tomcat中运行的话,编译后的字节码文件会保存在目录work/catalina/localhost/${webroot}/org/apache/jsp中。

那jsp编译完的class文件是什么呢?从它所继承(实现)的类上看,就能一目了然了。它继承了类org.apache.jasper.runtime.httpjspbase,实现了接口org.apache.jasper.runtime.jspsourcedependent。

而httpjspbase是个抽象类,它继承了抽象类javax.servlet.http.httpservlet,实现了接口javax.servlet.jsp.httpjsppage。

接口jspsourcedependent是为了能够跟踪jsp源码文件的依赖关系,从而能够编译过期的jsp文件。

这里至少应该知道如何使用jquery定位元素、设置(获取)值、ajax请求。

jquery有类(class)选择器、名称(tagname)选择器、id选择器、属性(attribute)选择器、派生选择器等。

本站有一篇《javascript调试介绍》您可以搜索一下,介绍了各种主流浏览器的javascript调试方法和技巧。

本站有一篇《eclipse使用技巧》您可以搜索一下,介绍了eclipse的一些使用技巧。

本章有一篇《玩转tomcat》您可以搜索一下,介绍了tomcat所有常用的配置方法。

除了要会使用数据库的带有界面的客户端(例如:navicat等)连接,还需要知道如何通过命令行来连接,例如:mysql -uroot -proot。

创建数据库(database)、表(table)的sql应该会写,简单的查询语句更是必须的。

本站有几篇有关hibernate的专题介绍,大家可以搜索一下《hibernate注解类介绍》、《hibernate常见异常》。

<a href="http://surenpi.com/2016/06/20/java_dev_path_first_year/">java开发成长之路第一年</a>

<a href="http://surenpi.com/2016/07/07/java_dev_path_second_year/">java开发成长之路第二年</a>

<a href="http://surenpi.com/2016/07/07/java_dev_path_third_year/">java开发成长之路第三年</a>

<a href="http://surenpi.com/2016/07/07/java_dev_path_fourth_year/">java开发成长之路第四年</a>

<a href="http://surenpi.com/2016/07/07/java_dev_path_fifth_year/">java开发成长之路第五年</a>

<a href="http://surenpi.com/2016/07/11/java_dev_path_sisth_year/" target="_blank">java开发成长之路第六年</a>

<a href="http://surenpi.com/2017/02/16/java_dev_path_seventh_year/" target="_blank">java开发成长之路第七年</a>