自学Java心得

时间:2024.3.31

要学好java找一份工作,需要很大的毅力,具体学多长时间就可以找工作,要看个人的悟性和学习能力,以我们宿舍6个人来说,计算机专业大专学历,我比较笨在大学里自学了一年左右的时间,毕业后在北京一家做移动增值的公司上班了。同宿舍的其它5个人,其中一个比较强,自学半年就去深圳华为上班了,2个找了个培训班学了4个月,现在都干上开发了,还有2个家里有关系,工作和计算机没关系。

总的来说,经济不困难的话,找个培训班比较好,学得比较全面,有老师指导学起来也不困难,就业比较顺利。如果经济条件不好,自己又喜欢这行,自学能坚持的话那就自学,老大不小了,总不能老伸手找家里拿钱不是。选择自学的话,基本上要分以下几个阶段:

1、要学java,肯定首先得把Java语言本身给掌握了,万事开头难,学java最难的也是java的入门,学习顺序是先掌握java的语法(多练就行),再掌握java面向对象的思想(封装、继承、多态、接口这些东西是重点,要多理解),接下来就是熟悉java常用的API(集合、IO等,主要是跟着书多写多练),这些东西掌握后,Java语言就算入门了。入门java推荐大家下载张孝祥的《java就业培训教程》视频教程,学java的没有不知道这套视频的,我们宿舍当时都是学这套视频入门的。这套视频最大的好处简单易懂,并引用了很多实用案例,并且还有配套书(书名好像也是java就业培训教程,新华书店有),省得忘了的地方,动不动对着电脑翻视频了。还有一些视频也比较好,例如孙鑫的java视频,马士兵的java视频等。不过对初学者而言,选中一套视频了就跟着好好学,不要下很多视频,东抓一把、西抓一把,最后啥也没学到。

2、有java基础了想马上从事实际开发还是不够的,接下来就需要掌握1-2门数据库技术了,对java来说数据库当然是oracle和mysql。oracle数据库现在还没发现好的视频,不过现在大学都学了数据库,有了数据库基础后,找本好书学oracle也就不难了。这里推荐何明的《从实践中学习Oracle/SQL》,作者来头很大,有非常深的oracle功底,书写得由浅入深,相当不错。对程序员来讲,学数据库关键是把增删改查的sql语句学好了就OK了,不要去抠什么数据库管理方面的东西。学了oracle后,要把数据库和java程序联系起来,这时候就要学jdbc了,jdbc不难,网上很多资料,大概一周时间就可以掌握了。

3、有了上面的基础后,恭喜你已经具备用java开发企业级应用的基础了。这时候你可以学习javaweb试着做点小项目玩了。不过学javaweb需要很多杂七杂八的知识点,比如xml、html、javascript、tomcat服务器等,学了这些后,大家才可以学servlet、jsp。学javaweb推荐大家买《深入体验Javaweb开发内幕-核心基础》这本书,还是张孝祥写的,这本书写的书很有特点,很细很深入,书上还带大量的实验案例,一步步讲得非常清晰,非常适合初学者。学javaweb这本书绝对是首选,并且这本书也有配套视频,在网上有下,不过视频只有前4章,比较可惜,希望张孝祥老师能早日录制全套视频。还有些视频和书也不错,比如李兴华的JSP视频教程、孙鑫的JAVA WEB 开发详解等,这些也不错,在网上都有免费下的。

4、接下来为找工作就要学struts\hibernate\spring\ajax这些流行技术了。这些技术我学习的时候没有视频,买的孙卫琴的书学,花了我大半年时间才学明白。现在幸福多了,网上有很多视频,个人推荐大家下传智播客的视频看。这个机构的老师很牛课程很有深度,真正能学到技术,不会误了大家。不过他们的视频里老打广告,很烦。书籍推荐struts看孙卫琴的,hibernate看夏昕的,spring看林信良的,具体书名叫什么忘了,大家google一下就清楚了。

学完上面这些就可以试着找工作了,我找了40天找着了。顺便再说一句,自学不可能不碰到问题,CSDN的论坛很不错,有问题上去问,一般都会有人回答你。要找培训班的话北京的传智播客还不错,我宿舍的2个就在那里,教学质量高,学完后很轻松都上班了,这家机构就是在外地没有分部,想培训要去北京,生活费用高,花费大。北京还有一些培训也不错,达内什么的,可以看看。


第二篇:如何自学java迅速成为java高手


如何自学java迅速成为java高手

如何自学java迅速成为java高手

很多网友咨询学习Java有没有什么捷径,我说“无他,唯手熟尔”。但是尚学堂愿意将一些经验写出来,以便后来者少走弯路,帮助别人是最大的快乐嘛!

要想学好Java,首先要知道Java的大致分类。我们知道,自从Sun推出Java以来,就力图使之无所不包,所以Java发展到现在,按应用来分主要分为三大块:J2SE,J2ME和J2EE,这也就是SunONE(Open NetEnvironment)体系。J2SE就是Java2的标准版,主要用于桌面应用软件的编程;J2ME主要应用于嵌入式系统开发,如手机和PDA的编程;J2EE是Java2的企业版,主要用于分布式的网络程序的开发,如电子商务网站,OA系统和ERP系统。J2SE开发桌面应用软件比起VC,VB,DEPHI这些传统开发语言来说,优势好象并不明显。J2ME对于初学者来说,好象又有点深奥,而且一般开发者很难有开发环境。所以现在应用最广泛又最好学的就是J2EE了。J2EE又包括许多组件,如Jsp,Servlet,JavaBean,EJB,JDBC,JavaMail等。要学习起来可不是一两天的事。

那么又该如何学习J2EE呢?当然Java语法得先看一看的,I/O包,Util包,Lang包你都熟悉了吗?然后再从JSP学起。如果你学过HTML,那么事情要好办的多,如果没有,那你快去补一补HTML基础吧。其实JSP中的Java语法也不多,它更象一个脚本语言,有点象ASP。然后你就该学一学Servlet了。Servlet就是服务器端小程序,他负责生成发送给客户端的HTML文件。JSP在执行时,也是先转换成Servlet再运行的。虽说JSP理论上可以完全取代Servlet,这也是SUN推出JSP的本意,可是Servlet用来控制流程跳转还是挺方便的,也令程序更清晰。接下来你应该学习一下Javabean了,可能你早就看不管JSP在HTML中嵌Java代码的混乱方式了,这种方式跟ASP又有什么区别呢?还好,SUN提供了Javabean可以把你的JSP中的Java代码封装起来,便于调用也便于重用。接着就是EJB了,EJB就是Enterprise JavaBean,看名字好象它是Javabean,可是它和Javabean还是有区别的。它是一个体系结构,你可以搭建更安全、更稳定的企业应用。它的大量代码已由中间件(也就是我们常听到的Weblogic,Websphere这些J2EE服务器)完成了,所以我们要做的程序代码量很少,大部分工作都在设计和配置中间件上。至于JDBC,就不用我多说了,你如果用java编过存取数据库的程序,就应该很熟悉。最后,Javascript也是大家重点要掌握的,现在所有公司面试几乎没有不问Javascript的

好了,对Java和J2EE有了一些基本概念之后,你就应该编一些程序了,千万不要纸上谈兵哦。最好从开始找一些小的程序来写,大家可以到上下载一个项目的视频程序及源码参考着写一下!

那么我书也看了,程序也做了,别人问我的问题我都能解决了,是不是就成为高手了呢?当然没那么简单,这只是万里长征走完了第一步。不信?那你出去接一个项目,你知道怎么下手吗,你知道怎么设计吗,你知道怎么组织人员进行开发吗?你现在脑子里除了一些散乱的代码之外,可能再没有别的东西了吧!这说明你掌所的只是一些基础的知识,并没有完全理解!你现在最缺的是实际的工作经验和编程思想的培养,而不是书本上那些凭空想出来的程序。你应该发现你有一点,书本上的内容你都明白了,但是自已写东西还是不会,还是要依

赖书本!

记得尚学堂也曾碰到过一个来培训的学员,号称Java都会了,框架也都理解了。其实他现在只是学到了Java的骨架,却还没有学到Java的精髓。接下来你得研究设计模式了。设计模式的书大家可以到尚学堂的网站找到资料下载就可以了!设计模式是高级程序员真正掌握面向对象核心思想的必修课。设计模式并不是一种具体"技术",它讲述的是思想,它不仅仅展示了接口或抽象类在实际案例中的灵活应用和智慧,让你能够真正掌握接口或抽象类的应用,从而在原来的Java语言基础上跃进一步,更重要的是,设计模式反复向你强调一个宗旨:要让你的程序尽可能的可重用,MVC结构(把Model-View-Control分离开的设计思想)是必须要研究的,现在很流行的Structs就是它的一种实现方式,不过Structs用起来实在是很繁,相比来说还是webwork2.0好一些。然后你再研究一下hibernate和Spring框架,相信你又会上一个台阶。 做完这些,接下来应该做一些实际的项目,不要写一些贪蛇食啊,坦克大战这种不实用的项目,要去写一些有实际应用价值的项目。

Java语言学习六大要点

如何自学java迅速成为java高手

Java的学习是比较复杂的,主要表现在相关的一系列平台、规范和协议上。有经验的Java程序员都知道,只掌握了Java语言本身很难开发应用程序。本文不讨论这些复杂的概念,而是我是在北京参加的培训,现在从初学者的角度,对于Java语言本身的学习提出自己的见解和建议。本文的讨论基于以下假设:

1. 学习的目的是为了将来进行应用程序的开发,而不是进行语言理论研究

2. 将来的应用开发是在成熟的平台上展开,而不是自己从底层开发平台

一、掌握静态方法和属性

静态方法和属性用于描述某一类对象群体的特征,而不是单个对象的特征。java中大量应用了静态方法和属性,这是一个通常的技巧。但是这种技巧在很多语言中不被频繁地使用。理解静态方法和属性对于理解类与对象的关系是十分有帮助的,在大量的java规范中,静态方法和属性被频繁使用。因此学习者应该理解静态方法和属性。Java在方法和属性的调用上是一致的,区别只表现在声明的时候,这和c++是不同的。

二、重视接口

在面向对象早期的应用中大量使用了类继承。随着软件工程理论的不断发展,人们开始意识到了继承的众多缺点,开始努力用聚合代替继承。软件工程解决扩展性的重要原则就是抽象描述,直接使用的工具就是接口。接口近年来逐渐成为java编程方法的核心。另一方面,就应用而言,大部分开发是建立在规范基础之上的,不需要自己建立复杂的继承关系和庞大的类。因此读懂规范和用好规范已经成为应用程序开发人员的首要任务,Java各项规范的主要描述手段就是接口。

三、学好集合框架

java描述复杂数据结构的主要方式是集合框架。Java没有指针,而是通过强大的集合框架描述数组、对象数组等复杂的数据结构。学好这些数据结构的描述方法对于应用程序编写,特别是涉及到服务器方、3层结构编程至关重要。程序员在这个时候不能再用诸如数据库结果集之类的结构描述数据了。

由于很多语言没有这么强大的集合框架体系,很多初学者不知所措,更不知道拿来做什么用,因此应该引起足够的重视。

四、例外捕捉

java对例外捕捉的强调是空前的,它强迫程序员用显著的与逻辑方法完全不同的方式描述例外捕捉,对于程序描述的完整性和严谨性有很大的意义。c++也有类似的机制,但是我们看到很多c++程序员并不习惯使用这些机制。

Java的初学者应该充分学习好这种例外捕捉机制,养成良好的编程习惯。

五、多线程需要理解机理

很多java程序员热衷于多线程程序编写,认为是对逻辑能力的挑战。其实在大量应用中根本就不需要编写多线程程序,或者说大多数编写应用程序的程序员不会去写多线程程序。这是因为多线程机制都内置到基础平台当中了。

程序员应该了解的是多线程原理和多线程安全,这对于今后准确地把握程序是至关重要的。

例如JSP中编写到不同的位置对于多个用户环境的安全影响完全不同,又如著名的Super Servlet是每一个访问作为一个进程,但是每一个页面是一个线程,和Servlet正好相反,对程序的性能和安全的影响有天壤之别。

六、了解网络编程

java号称是最强的网络编程语言,但是大多数应用程序开发人员是从来不会自己开发什么底层的网络程序的。需要做只是了解原理就够了。网络机制的实现是靠平台实现的,除非自己开发平台,否则是不需要知道socket怎么实现,怎么监听访问的。因此在这方面花太多的功夫就偏离了“将来的应用开发是在成熟的平台上展开,而不是自己从底层开发平台”这一假设。

Java三种常见异常及解决

如何自学java迅速成为java高手

异常跟普通的警告等有一定的区别。当应用程序发生异常时,会中断正在执行的程序的正常指令流。也就是说,发生异常后面的代码将得不到正确的执行。甚至还会触发数据库的回退操作。

在Java的常见异常中,异常包括预定义异常与自定义异常。这两种异常的类型互为补充。作为一个合格的程序开发人员,要善于在应用程序中使用异常。这可以提高应用程序的交互性。同时,也是保证应用程序正常运行的前提。故异常的处理对于开发一个优秀的应用程序来说非常的重要。为此笔者认为程序开发人员应该对Java应用程序的常见异常有一个深入的了解。只有在了解这些常见异常的情况下,才能够做好自定义异常的处理。

一、Java常见异常的类型与原因。

对于Java应用程序的常见异常,笔者认为程序开发人员要从两个方面去了解。一是要知道有哪些常见的Java应用程序异常,二是需要知道哪些原因可能会造成这个异常。这不仅需要程序管理人员在日常工作中要注意积累,在必要的情况下还需要去从其它渠道收集资料。笔者对此就进行一个分析,希望能够对各位程序开发人员有一定的帮助。

1、 SQLException:操作数据库异常类。

现在的Java应用程序大部分都是依赖于数据库运行的。当Java应用程序与数据库进行沟通时如果产生了错误,就会触发这个类。同时会将数据库的错误信息通过这个类显示给用户。也就是说,这个操作数据库异常类是数据库与用户之间异常信息传递的桥梁。如现在用户往系统中插入数据,而在数据库中规定某个字段必须唯一。当用户插入数据的时候,如果这个字段的值跟现有的纪录重复了,违反了数据库的唯一性约束,此时数据库就会跑出一个异常信息。这个信息一般用户可能看不到,因为其发生在数据库层面的。此时这个操作数据库异常类就会捕捉到数据库的这个异常信息,并将这个异常信息传递到前台。如此的话,前台用户就可以根据这个异常信息来分析发生错误的原因。这就是这个操作数据库异常类的主要用途。在Java应用程序中,所有数据库操作发生异常时,都会触发这一个类。所有此时Java应用程序本身的提示信息往往过于笼统,只是说与数据库交互出现错误,没有多大的参考价值。此时反而是数据库的提示信息更加有使用价值。

2、 ClassCastException:数据类型转换异常。

在Java应用程序中,有时候需要对数据类型进行转换。这个转换包括显示的转换与隐式的转换。不过无论怎么转换,都必须要符合一个前提的条件,即数据类型的兼容性。如果在数据转换的过程中,违反了这个原则,那么就会触发数据类型转换异常。如现在在应用程序中,开发人员需要将一个字符型的日期数据转换为数据库所能够接受的日期型数据,此时只需要在前台应用程序中进行控制,一般不会有问题。但是,如果前台应用程序缺乏相关的控制,如用户在输入日期的时候只输入月、日信息,而没有年份的信息。此时应用程序在进行数据类型转换的时候,就会出现异常。根据笔者的经验,数据类型转换异常在应用程序开发中使一个出现的比较多的异常,也是一个比较低级的异常。因为大部分情况下,都可以在应用程序窗口中对数据类型进行一些强制的控制。即在数据类型进行转换之前,就保证数据类型的兼容性。如此的话,就不容易造成数据类型的转换异常。如在只允许数值类型的字段中,可以设置不允许用户输入数值以外的字符。虽然说有了异常处理机制,可以保证应用程序不会被错误的运行。但是在实际开发中,还是要尽可能多的预见错误发生的原因,尽量避免异常的发生。

3、NumberFormatException:字符串转换为数字类型时抛出的异常。

在数据类型转换过程中,如果是字符型转换为数字型过程中出现的问题,对于这个异常在Java程序中采用了一个独立的异常,即NumberFormatException.如现在讲字符型的数据“123456”转换为数值型数据时,是允许的。但是如果字符型数据中包含了非数字型的字符,如123#56,此时转换为数值型时就会出现异常。系统就会捕捉到这个异常,并进行处理。

Java常见的异常类还有很多。如未找到相应类异常、不允许访问某些类异常、文件已经结束异常、文件未找到异常、字段未找到异常等等。一般系统开发人员都可以根据这个异常

名来判断当前异常的类型。虽然不错,但是好记性不如烂笔头。程序开发人员在必要的时候(特别是存在自定义异常的时候),最后手头有一份异常明细表。如此的话,无论是应用程序在调试过程中发现问题,还是运行过程中接到用户的投诉,都可以及时的根据异常名字来找到异常发生的原因。从而可以在最短时间内解决异常,恢复应用程序的正常运行。这个措施笔者用了很多年,非常的有效。

二、异常管理的实用建议

对于操作数据库异常来说,Java应用程序只提供了一个异常类。故光凭Java应用程序的错误信息,往往不能够帮助应用程序人员排除错误的原因。只能够指名是应用程序错误还是数据库错误导致的这个异常。为了更进一步指明问题的原因,在数据库层面定义异常的时候,最好能够说明具体的原因。如前台应用程序可能会调用数据库的函数或者过程。此时在数据库的函数或者过程中做好能够说明某个异常的具体原因。如根据某个基础表生成另一张表的时候,某个字段不能够为空等等。将这些异常信息说明清楚后,如果真的遇到类似的异常时,操作数据库异常类就会将数据库的异常信息反会给前台用户。从而有利于用户寻找问题的原因,并在最短时间内改正。当然,这需要Java程序员与数据库设计人员进行协调。

其次需要注意的是,异常并不是常态。也就是说,大部分异常可以通过前提的合理预见与预防,来消除。如设计到四则运算,可以在前台应用程序窗口中限制在除数字段内输入0值等手段来消除应用程序运行中可能产生的异常。不过这往往要求应用程序开发人员有比较丰富的工作经验以及由比较严密的思维逻辑。虽然这有一定的难度,但是笔者认为程序开发人员还是应该往这方面努力,而不要老是让用户作为你的试验品,让用户来发现应用程序中的设计Bug.笔者认为,只有一些实在是程序人员无法控制的因素才允许抛出异常。如果应用程序开发人员能够意识到这种错误、但是仍然没有引起重视或者采取有效的措施防止出现这种异常,那么笔者是不允许的。

用java多线程断点续传实践

如何自学java迅速成为java高手

/**

* author:annegu

* date:2009-07-16

*/

annegu做了一个简单的Http多线程的下载程序,来讨论一下多线程并发下载以及断点续传的问题。

这个程序的功能,就是可以分多个线程从目标地址上下载数据,每个线程负责下载一部分,并可以支持断点续传和超时重连。

下载的方法是download(),它接收两个参数,分别是要下载的页面的url和编码方式。在这个负责下载的方法中,主要分了三个步骤。第一步是用来设置断点续传时候的一些信息的,第二步就是主要的分多线程来下载了,最后是数据的合并。

1、多线程下载: /** *//** */

public String download(String urlStr, String charset) {

this.charset = charset;

long contentLength = 0;

CountDownLatch latch = new CountDownLatch(threadNum);

long[] startPos = new long[threadNum];

long endPos = 0;

try {

// 从url中获得下载的文件格式与名字

this.fileName = urlStr.substring(urlStr.lastIndexOf("/") + 1);

this.url = new URL(urlStr);

URLConnection con = url.openConnection();

setHeader(con);

// 得到content的长度

contentLength = con.getContentLength();

// 把context分为threadNum段的话,每段的长度。

this.threadLength = contentLength / threadNum;

// 第一步,分析已下载的临时文件,设置断点,如果是新的下载任务,则建立目标文件。在第4点中说明。

startPos = setThreadBreakpoint(fileDir, fileName, contentLength, startPos);

//第二步,分多个线程下载文件

ExecutorService exec = Executors.newCachedThreadPool();

for (int i = 0; i < threadNum; i++) {

// 创建子线程来负责下载数据,每段数据的起始位置为(threadLength * i + 已下载长度)

startPos[i] += threadLength * i;

/**//*设置子线程的终止位置,非最后一个线程即为(threadLength * (i + 1) - 1) 最后一个线程的终止位置即为下载内容的长度*/

if (i == threadNum - 1) {

endPos = contentLength;

} else {

endPos = threadLength * (i + 1) - 1;

}

// 开启子线程,并执行。

ChildThread thread = new ChildThread(this, latch, i, startPos[i], endPos); childThreads[i] = thread;

exec.execute(thread);

}

try {

// 等待CountdownLatch信号为0,表示所有子线程都结束。

latch.await();

exec.shutdown();

// 第三步,把分段下载下来的临时文件中的内容写入目标文件中。在第3点中说明。 tempFileToTargetFile(childThreads);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

首先来看最主要的步骤:多线程下载。

首先从url中提取目标文件的名称,并在对应的目录创建文件。然后取得要下载的文件大小,根据分成的下载线程数量平均分配每个线程需要下载的数据量,就是threadLength。然后就可以分多个线程来进行下载任务了。

在这个例子中,并没有直接显示的创建Thread对象,而是用Executor来管理Thread对象,并且用CachedThreadPool来创建的线程池,当然也可以用FixedThreadPool。CachedThreadPool在程序执行的过程中会创建与所需数量相同的线程,当程序回收旧线程的时候就停止创建新线程。FixedThreadPool可以预先新建参数给定个数的线程,这样就不用在创建任务的时候再来创建线程了,可以直接从线程池中取出已准备好的线程。下载线程的数量是通过一个全局变量threadNum来控制的,默认为5。

好了,这5个子线程已经通过Executor来创建了,下面它们就会各自为政,互不干涉的执行了。线程有两种实现方式:实现Runnable接口;继承Thread类。

ChildThread就是子线程,它作为DownloadTask的内部类,继承了Thread,它的构造方法需要5个参数,依次是一个对DownloadTask的引用,一个CountDownLatch,id(标识线程的id号),startPosition(下载内容的开始位置),endPosition(下载内容的结束位置)。

这个CountDownLatch是做什么用的呢?

现在我们整理一下思路,要实现分多个线程来下载数据的话,我们肯定还要把这多个线程下载下来的数据进行合。主线程必须等待所有的子线程都执行结束之后,才能把所有子线程的下载数据按照各自的id顺序进行合并。CountDownLatch就是来做这个工作的。

CountDownLatch用来同步主线程,强制主线程等待所有的子线程执行的下载操作完成。在主线程中,CountDownLatch对象被设置了一个初始计数器,就是子线程的个数5个,代码①处。在新建了5个子线程并开始执行之后,主线程用CountDownLatch的await()方法来阻塞主线程,直到这个计数器的值到达0,才会进行下面的操作,代码②处。 对每个子线程来说,在执行完下载指定区间与长度的数据之后,必须通过调用CountDownLatch的countDown()方法来把这个计数器减1。

2、在全面开启下载任务之后,主线程就开始阻塞,等待子线程执行完毕,所以下面我们来看一下具体的下载线程ChildThread。

/** *//**

*author by

*/

public class ChildThread extends Thread {

public static final int STATUS_HASNOT_FINISHED = 0;

public static final int STATUS_HAS_FINISHED = 1;

public static final int STATUS_HTTPSTATUS_ERROR = 2;

private DownloadTask task;

private int id;

private long startPosition;

private long endPosition;

private final CountDownLatch latch;

private File tempFile = null;

//线程状态码

private int status = ChildThread.STATUS_HASNOT_FINISHED;

public ChildThread(DownloadTask task, CountDownLatch latch, int id, long startPos, long endPos) {

super();

this.task = task;

this.id = id;

this.startPosition = startPos;

this.endPosition = endPos;

this.latch = latch;

try {

tempFile = new File(this.task.fileDir + this.task.fileName + "_" + id); if(!tempFile.exists()){

tempFile.createNewFile();

}

} catch (IOException e) {

e.printStackTrace();

}

}

public void run() {

System.out.println("Thread " + id + " run ");

HttpURLConnection con = null;

InputStream inputStream = null;

BufferedOutputStream outputStream = null;

int count = 0;

long threadDownloadLength = endPosition - startPosition;

try {

outputStream = new BufferedOutputStream(new FileOutputStream(tempFile.getPath(), true));

} catch (FileNotFoundException e2) {

e2.printStackTrace();

}

③ for(; ; ){

④ startPosition += count;

try {

//打开URLConnection

con = (HttpURLConnection) task.url.openConnection();

setHeader(con);

con.setAllowUserInteraction(true);

//设置连接超时时间为10000ms

⑤ con.setConnectTimeout(10000);

//设置读取数据超时时间为10000ms

con.setReadTimeout(10000);

if(startPosition < endPosition){

//设置下载数据的起止区间

con.setRequestProperty("Range", "bytes=" + startPosition + "-"

+ endPosition);

System.out.println("Thread " + id + " startPosition is " + startPosition); System.out.println("Thread " + id + " endPosition is " + endPosition);

//判断http status是否为HTTP/1.1 206 Partial Content或者200 OK

//如果不是以上两种状态,把status改为STATUS_HTTPSTATUS_ERROR ⑥ if (con.getResponseCode() != HttpURLConnection.HTTP_OK

&& con.getResponseCode() != HttpURLConnection.HTTP_PARTIAL) { System.out.println("Thread " + id + ": code = "

+ con.getResponseCode() + ", status = "

+ con.getResponseMessage());

status = ChildThread.STATUS_HTTPSTATUS_ERROR;

this.task.statusError = true;

outputStream.close();

con.disconnect();

System.out.println("Thread " + id + " finished.");

latch.countDown();

break;

}

inputStream = con.getInputStream();

int len = 0;

byte[] b = new byte[1024];

while ((len = inputStream.read(b)) != -1) {

outputStream.write(b, 0, len);

count += len;

//每读满5000个byte,往磁盘上flush一下

if(count % 5000 == 0){

⑦ outputStream.flush();

}

}

System.out.println("count is " + count);

if(count >= threadDownloadLength){

hasFinished = true;

}

⑧ outputStream.flush();

outputStream.close();

inputStream.close();

con.disconnect();

}

System.out.println("Thread " + id + " finished.");

latch.countDown();

break;

} catch (IOException e) {

try {

⑨ outputStream.flush();

⑩ TimeUnit.SECONDS.sleep(getSleepSeconds());

} catch (InterruptedException e1) {

e1.printStackTrace();

} catch (IOException e2) {

e2.printStackTrace();

}

continue;

}

}

}

}

在ChildThread的构造方法中,除了设置一些从主线程中带来的id, 起始位置之外,就是新建了一个临时文件用来存放当前线程的下载数据。临时文件的命名规则是这样的:下载的目标文件名+”_”+线程编号。

现在让我们来看看从网络中读数据是怎么读的。我们通过URLConnection来获得一个http的连接。有些网站为了安全起见,会对请求的http连接进行过滤,因此为了伪装这个http的连接请求,我们给httpHeader穿一件伪装服。下面的setHeader方法展示了一些非常常用的典型的httpHeader的伪装方法。比较重要的有:Uer-Agent模拟从Ubuntu的firefox浏览器发出的请求;Referer模拟浏览器请求的前一个触发页面,例如从skycn站点来下载软件的话,Referer设置成skycn的首页域名就可以了;Range就是这个连接获取的流文件的起始区间。

private void setHeader(URLConnection con) {

con.setRequestProperty("User-Agent", "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.3) Gecko/2008092510 Ubuntu/8.04 (hardy) Firefox/3.0.3"); con.setRequestProperty("Accept-Language", "en-us,en; q=0.7,zh-cn; q=0.3");

con.setRequestProperty("Accept-Encoding", "aa");

con.setRequestProperty("Accept-Charset", "ISO-8859-1,utf-8; q=0.7,*; q=0.7");

con.setRequestProperty("Keep-Alive", "300");

con.setRequestProperty("Connection", "keep-alive");

con.setRequestProperty("If-Modified-Since", "Fri, 02 Jan 2009 17:00:05 GMT");

con.setRequestProperty("If-None-Match", "\"1261d8-4290-df64d224\""); con.setRequestProperty("Cache-Control", "max-age=0");

con.setRequestProperty("Referer", "http://");

}另外,为了避免线程因为网络原因而阻塞,设置了ConnectTimeout和ReadTimeout,代码⑤、⑥处。setConnectTimeout设置的连接的超时时间,而setReadTimeout设置的是读取数据的超时时间,发生超时的话,就会抛出socketTimeout异常,两个方法的参数都是超时的毫秒数。

这里对超时的发生,采用的是等候一段时间重新连接的方法。整个获取网络连接并读取下载数据的过程都包含在一个循环之中(代码③处),如果发生了连接或者读取数据的超时,在抛出的异常里面就会sleep一定的时间(代码⑩处),然后continue,再次尝试获取连接并读取数据,这个时间可以通过setSleepSeconds()方法来设置。我们在迅雷等下载工具的使用中,经常可以看到状态栏会输出类似“连接超时,等待*秒后重试”的话,这个就是通过ConnectTimeout,ReadTimeout来实现的。

连接建立好之后,我们要检查一下返回响应的状态码。常见的Http Response Code有以下几种:

a) 200 OK 一切正常,对GET和POST请求的应答文档跟在后面。

b) 206 Partial Content 客户发送了一个带有Range头的GET请求,服务器完成。 c) 404 Not Found 无法找到指定位置的资源。这也是一个常用的应答。

d) 414 Request URI Too Long URI太长。

e) 416 Requested Range Not Satisfiable 服务器不能满足客户在请求中指定的Range头。

f) 500 Internal Server Error 服务器遇到了意料不到的情况,不能完成客户的请求。 g) 503 Service Unavailable 服务器由于维护或者负载过重未能应答。例如,Servlet

可能在数据库连接池已满的情况下返回503。

在这些状态里面,只有200与206才是我们需要的正确的状态。所以在代码⑥处,进行了状态码的判断,如果返回不符合要求的状态码,则结束线程,返回主线程并提示报错。

假设一切正常,下面我们就要考虑从网络中读数据了。正如我之前在分析mysql的数据库驱动中看的一样,网络中发送数据都是以数据包的形式来发送的,也就是说不管是客户端向服务器发出的请求数据,还是从服务器返回给客户端的响应数据,都会被拆分成若干个小型数据包在网络中传递,等数据包到达了目的地,网络接口会依据数据包的编号来组装它们,成为完整的比特数据。因此,我们可以想到在这里也是一样的,我们用inputStream的read方法来通过网卡从网络中读取数据,并不一定一次就能把所有的数据包都读完,所以我们要不断的循环来从inputStream中读取数据。Read方法有一个int型的返回值,表示每次从inputStream中读取的字节数,如果把这个inputStream中的数据读完了,那么就返回-1。

Read方法最多可以有三个参数,byte b[]是读取数据之后存放的目标数组,off标识了目标数组中存储的开始位置,len是想要读取的数据长度,这个长度必定不能大于b[]的长度。

public synchronized int read(byte b[], int off, int len);

我们的目标是要把目标地址的内容下载下来,现在分了5个线程来分段下载,那么这些分段下载的数据保存在哪里呢?如果把它们都保存在内存中是非常糟糕的做法,如果文件相当之大,例如是一个视频的话,难道把这么大的数据都放在内存中吗,这样的话,万一连接中断,那前面下载的东西就都没有了?我们当然要想办法及时的把下载的数据刷到磁盘上保存下来。当用bt下载视频的时候,通常都会有个临时文件,当视频完全下载结束之后,这个临时文件就会被删除,那么下次继续下载的时候,就会接着上次下载的点继续下载。所以我们的outputStream就是往这个临时文件来输出了。

OutputStream的write方法和上面InputStream的read方法有类似的参数,byte b[]是输出数据的来源,off标识了开始位置,len是数据长度。

public synchronized void write(byte b[], int off, int len) throws IOException;

在往临时文件的outputStream中写数据的时候,我会加上一个计数器,每满5000个比特就往文件中flush一下(代码⑦处)。

对于输出流的flush,有些要注意的地方,在程序中有三个地方调用了outputStream.flush()。第一个是在循环的读取网络数据并往outputStream中写入的时候,每满5000个byte就flush一下(代码⑦处);第二个是循环之后(代码⑧处),这时候正常的读取写入操作已经完成,但是outputStream中还有没有刷入磁盘的数据,所以要flush一下才能关闭连接;第三个就是在异常中的flush(代码⑨处),因为如果发生了连接超时或者读取数据超时的话,就会直接跑到catch的exception中去,这个时候outputStream中的数据如果不flush的话,重新连接的时候这部分数据就会丢失了。另外,当抛出异常,重新连接的时候,下载的起始位置也要重新设置(代码④处),count就是用来标识已经下载的字节数的,把count+startPosition就是新一次连接需要的下载起始位置了。

3、现在每个分段的下载线程都顺利结束了,也都创建了相应的临时文件,接下来在主线程中会对临时文件进行合并,并写入目标文件,最后删除临时文件。这部分很简单,就是一个

对所有下载线程进行遍历的过程。这里outputStream也有两次flush,与上面类似,不再赘述。

/** *//**author by */ private void tempFileToTargetFile(ChildThread[] childThreads) {

try {

BufferedOutputStream outputStream = new BufferedOutputStream( new FileOutputStream(fileDir + fileName));

// 遍历所有子线程创建的临时文件,按顺序把下载内容写入目标文件中

for (int i = 0; i < threadNum; i++) {

if (statusError) {

for (int k = 0; k < threadNum; k++) {

if (childThreads[k].tempFile.length() == 0)

childThreads[k].tempFile.delete();

}

System.out.println("本次下载任务不成功,请重新设置线程数。");

break;

}

BufferedInputStream inputStream = new BufferedInputStream(

new FileInputStream(childThreads[i].tempFile));

System.out.println("Now is file " + childThreads[i].id);

int len = 0;

int count = 0;

byte[] b = new byte[1024];

while ((len = inputStream.read(b)) != -1) {

count += len;

outputStream.write(b, 0, len);

if ((count % 5000) == 0) {

outputStream.flush();

}

// b = new byte[1024];

}

inputStream.close();

// 删除临时文件

if (childThreads[i].status == ChildThread.STATUS_HAS_FINISHED) { childThreads[i].tempFile.delete();

}

}

outputStream.flush();

outputStream.close();

} catch (FileNotFoundException e) {

e.printStackTrace();

} catch (IOException e) {

e.printStackTrace();

}

}

4、最后,说说断点续传,前面为了实现断点续传,在每个下载线程中都创建了一个临时文件,现在我们就要利用这个临时文件来设置断点的位置。由于临时文件的命名方式都是固定的,所以我们就专门找对应下载的目标文件的临时文件,临时文件中已经下载的字节数就是我们需要的断点位置。startPos是一个数组,存放了每个线程的已下载的字节数。 //第一步,分析已下载的临时文件,设置断点,如果是新的下载任务,则建立目标文件。

private long[] setThreadBreakpoint(String fileDir2, String fileName2, long contentLength, long[] startPos) {

File file = new File(fileDir + fileName);

long localFileSize = file.length();

if (file.exists()) {

System.out.println("file " + fileName + " has exists!");

// 下载的目标文件已存在,判断目标文件是否完整

if (localFileSize < contentLength) {

System.out.println("Now download continue ");

// 遍历目标文件的所有临时文件,设置断点的位置,即每个临时文件的长度

File tempFileDir = new File(fileDir);

File[] files = tempFileDir.listFiles();

for (int k = 0; k < files.length; k++) {

String tempFileName = files[k].getName();

// 临时文件的命名方式为:目标文件名+"_"+编号

if (tempFileName != null && files[k].length() > 0

&& tempFileName.startsWith(fileName + "_")) {

int fileLongNum = Integer.parseInt(tempFileName

.substring(tempFileName.lastIndexOf("_") + 1,

tempFileName.lastIndexOf("_") + 2));

// 为每个线程设置已下载的位置

startPos[fileLongNum] = files[k].length();

}

}

}

} else {

// 如果下载的目标文件不存在,则创建新文件

try {

file.createNewFile();

} catch (IOException e) {

e.printStackTrace();

}

}

return startPos;

}

5、测试

public class DownloadStartup {

private static final String encoding = "utf-8";

public static void main(String[] args) {

DownloadTask downloadManager = new DownloadTask();

String urlStr = "/velocity/tools/1.4/velocity-tools-1.4.zip";

downloadManager.setSleepSeconds(5);

downloadManager.download(urlStr, encoding);

}

}

浅谈JDBC的概念理解与学习

如何自学java迅速成为java高手

JDBC的概念:工厂一般使用单例模式

工厂-->生产产品(Dao这个产品)

类加载器,不光可以load.class,还可以load其他的文件

事务概念与处理

1.原子性:不能拆分,组成事事务处理的语句形成一个逻辑处理单元,不能只执行其中的一部分

2.一致性:事务做完之后,保定数据的完整性约束

3.隔离性:一个事务处理对另一个事务处理的影响

4.持续性:事务处理的结果能被永久保存下来

步骤:打开事务connection.setAutoCommit(false);

提交事务:connection.commit();

回滚事务:connection.rollback();

jdbc缺省是自动提交的,客户端连接也是自动提交的,要想打开事务,要把自动提交关掉

保存点:savePoint

Savepointsp=null

sp=conn.setSavepoint();

conn.rollback(sp);

-----------------------------------

JDBC的概念:跨多个数据库的事务JTA

JTA容器weblogic,websphere

分成两阶段提交。

用到JNDI服务器

javax.transaction.UserTransactiontx=

(UserTransaction)ctx.lookup("jndiName");

tx.begin();

//connection1connection2(可能来自不同的数据库)

tx.commit(); //tx.rollback();

---------------------------------

事务的隔离级别:

connction.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);

隔离级别

读未提交(Readuncommitted)可能出现脏读,不可重复读,幻读

读已提交(Readcommitted)可能出现不可重复读,幻读

可重复读(Repeatableread)可能出现幻读(幻影数据,执行插入一条数据,在另一个客户端有可能看到)

可串行化(Serializable)

隔离级别各个数据库的实现是不一样的。

--------------------------------------------------

关于存储过程(两层架构的时候用的很多)

三层架构的系统很少使用了。略过不看了先。

---------------------------------------------------

其他几个有用的

jdbc3.0规范提供的方法

插一条记录,它能把主键返回来

PreparedStatement.getGeneratedKeys();

PreparedStatementps=connection.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS);

ps.executeUpdate();

ResultSetrs=st.getGeneratedKeys();

rs.getInt(1);

批量处理:可以大幅提升大量增删改的速度(并非绝对),与具体数据库有关,也不一定能提高性能

PreparedStatement.addBatch();

PreparedStatement.executeBatch();

-------------------------------------------------------------

可滚动结果集与分页特性

Statementst=conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_UPDATABLE);

ResultSet.TYPE_SCROLL_SENSITIVE--对数据库敏感的,就是说,数据库中记录发生了变化,结果集会觉察到(但某些数据库不一定遵循这一规范,mysql就不遵循)

ResultSet.CONCUR_UPDATABLE--可更新的,就是说,改变结果集中的内容可以影响到数据库里

ResultSet.CONCUR_READ_ONLY--只读的,

可滚动的结果集

Statementst=connection.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_UPDATABLE);

ResultSetrs=st.executeQuery(sql);

rs.beforeFirst();

rs.afterLast();

rs.first();

rs.isFirst();

rs.last();

rs.isLast();

rs.absolute(9);

rs.moveToInsertRow();

conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_UPDATABLE);

rs.updateString("colname","newvalue");

rs.updateRow();

mysql分页例如:

selectid,name,money,birthdayfromuserlimit150,10;

从user表中第150条记录开始,取10条记录

---------------------------------------------------------

jdbc元数据信息数据库元数据信息、参数元数据信息

DatabaseMetaDatameta=connection.getMetaData();

通过DatabaseMetaData可以获得数据库相关的信息,如:

数据库版本,数据库名,数据库厂商信息,是否支持事务,是否支持某种事务隔离级别,

是否支持滚动结果集等。

ParameterMetaDatapmd=preparedStatement.getParameterMetaData();

通过ParameterMetaData可以获得参数信息.

ParameterMetaDatapmd=ps.getParameterMetaData();

intcount=pmd.getParameterCount();

System.out.println("count="+count);

for(inti=1; i<=count; i++)

{

ps.setObject(i,params[i-1]);

}

ResultSetrs=ps.executeQuery();

--------------------------------------------------

dataSource代理模式:

组合优先于继承

拦截Connection.close()方法

动态代理

调用处理器

----------------------------------------------------

数据源和连接池

DataSource用来取代DriverManger来获取Connection

通过DataSource获得Connection速度很快

通过DataSource获得的Connection都是已经被包裹过的(不是驱动原来的连接),

他的close方法已经被修改

一般DataSource内部会用一个连接池来缓存Connection,这样可以大幅度提高

数据库的访问速度;

连接池可以理解成一个能够存放Connection的Connection

我们的程序只和DataSource打交道,不会直接访问连接池。

------------------------------------------

DBCP的应用

1.加载三个.jar

2.写dbcpconfig.properties配置文件

3.创建DataSource数据源

使用DBCP必须用的三个包

commons-dbcp-1.2.2\commons-dbcp-1.2.1.jar,

spring-framework-2.5.6\lib\jakarta-commons\commons-pool.jar,commons-collection-3.1.jar

dbcpconfig.properties

driverClassName=com.mysql.jdbc.Driver

url=jdbc:mysql://localhost:3306/jdbc

username=root

password=root

#初始化连接

initialSize=10

#最大连接数量

maxActive=50

#最大空闲连接

maxIdle=20

#最小空闲连接

minIdle=5

#超时等待时间以毫秒为单位

maxWait=60000

#JDBC驱动建立连接时附带的连接属性格式必须为这样:属性名=property

#注注意user与password两个属性会被明确地传递。因此这里不需要包含他们

connectionProperties=useUnicode=true; characterEncoding=utf-8

#指定由连接池所创建的连接的自动提交auto-commit状态

defaultAutoCommit=true

#driverdefault指定由连接池所创建的连接的只读read-only状态

#如果没有设置该值,则setReadOnly方法不被调用。

defaultReadOnly=

#driverdefault指定由连接池所创建的连接的事务级别TransactionIsolation

#可用值为下列之一:这依赖于数据库

#NONE,READ_UNCOMMITED,READ_COMMITTED

defaultTransactionIsolation=READ_COMMITTED

----------------------------------------------------

创建数据源

Propertiesprop=newProperties();

prop.setProperty("driverClassName","com.mysql.jdbc.Driver");

prop.setProperty("url","jdbc:mysql://localhost:3306/jdbc");

prop.setProperty("username","root");

prop.setProperty("password","root");

#dbcp使用这个方法创建数据源,需要一个Property参数,参数中的键值对就为.properties文件中的配置

#可以直接load进来,省的写了

Propertiesprop=newProperties();

InputStreamis=JdbcUtil.class.getClassLoader().getResourceAsStream("dbcpconfig.properties");

prop.load(is);

DataSourcemyDataSource=BasicDataSourceFactory.createDataSource(prop);

-------------------------------------------------------------------------

模板模式对查询抽象,采用继承的方式(对查询来说,不够灵活,查询的列一变化,就得修改超类)

策略模式(采用接口的方式传递一个接口的参数,通过匿名类为各种需求实现)

-----------------------------------------------------------------------

使用JdbcTemplate工具类,在spring框架中提供了一个JdbcTemplate工具类,这个类对JDBCAPI进行了很好的封装

这个类就像我们自己对JDBC进行封装一样。只是代码列健壮,功能更强大而已。

我们以后在实际项目中可以使用JdbcTemplate类来完全替代直接使用JDBCAPI这与直接使用jdbcapi没有太大性能区别

使用JdbcTemplate工具类需要额外从spring开发包中导入spring.jar和commons-logging.jar

关于jdbcTemplate查询,更新相关使用方法见练习

如果想获取connection,自由进行操作,可以使用

jdbc.execute(newConnectionCallback()

{

publicObjectdoInConnection(Connectionconn)throwsSQLException,

DataAccessException

{}

}); //拿到了Connction随你怎么用了

-------------------------------------------------------------------

Spring的NameParameterJdbcTemplate

NameParameterJdbcTemplate内部包含了一个JdbcTemplate,所以JdbcTemplate能做的事情它都能做

NameParameterJdbcTemplate相对于JdbcTemplate主要增加了参数可以命名的功能

publicObjectqueryForObject(Stringsql,MapparamMap,RowMapperrowMapper) publicObjectqueryForObject(Stringsql,SqlParameterSourceparamSource,RowMapperrowMapper

SqlParameterSource的两个主要实现MapSqlParameterSource和BeanPropertyParameterSource

SimpleJdbcTemplate内部包含了一个NamedParameterJdbcTemplate,所以NamedParameterJdbcTemplate

能做的事情SimpleJdbcTemplate都能干,SimpleJdbcTemplate相对于NamedParameterJdbcTemplate主要

增加了JDK5.0的泛型技术和可变长度参数支持

simpleJdbcTemplate中有些方法不全,比如拿到主键的方法就没有,可以获取到NamedParameterJdbcTemplate

调用它里面的方法

getNamedParameterJdbcOperations()返回的是NamedParameterJdbcOperations其实现就是NamedParameterJdbcTemplate这个类

getJdbcOperations()返回的是JdbcOperation其实现就是JdbcTemplate

++++++++++++++++++++++++++++++++++++++++++++++++++

Map参数源:采用键值对方式传参(参数别名可任意,如:m,:n)

staticUserfindUser(Useruser)

{

NamedParameterJdbcTemplatenamed=newNamedParameterJdbcTemplate(dataSource);

Stringsql="selectid,name,money,birthdayfromuserwheremoney>:mandid<:n";

Mapmap=newHashMap();

map.put("m",user.getMoney());

map.put("n",user.getId());

Objectu=named.queryForObject(sql,map,newBeanPropertyRowMapper(User.class)); //这个方法当查询对象多于一个或为null时会抛异常

return(User)u;

}

Bean属性参数源使用方法(参数别名要与bean类的属性对应,如:money,:id)(通过&号也可以命名别名,不建议使用)

staticUserfindUser(Useruser)

{

NamedParameterJdbcTemplatenamed=newNamedParameterJdbcTemplate(dataSource);

Stringsql="selectid,name,money,birthdayfromuserwheremoney>:moneyandid<:id";

SqlParameterSourceps=newBeanPropertySqlParameterSource(user);

Objectu=named.queryForObject(sql,ps,newBeanPropertyRowMapper(User.class)); //这个方法当查询对象多于一个或为null时会抛异常

return(User)u;

} //保存数据获得主键

publicintupdate(Stringsql,SqlParameterSourceparamSource,KeyHoldergeneratedKeyHolder)staticvoidaddUser(Useruser)

{

NameParameterJdbcTemplatenamed=newNameParameterJdbcTemplate(dataSource);

Stringsql="insertintouser(name,birthday,money)values(:name,:birthday,:money";

SqlParameterSourceps=newBeanPropertySqlParameterSource(user);

KeyHolderkeyHolder=newGeneratedKeyHolder();

named.update(sql,ps,keyHolder);

intid=keyHolder.getKey().intValue(); user.setId(id); //如果不是整型的主键呢,使用Mapmap=keyHolder.getKeys(); }

JDBC的概念:使用泛型技术与可变长度的参数(JDK1.5版本以上)

//泛型技术(JDK1.5以上版本可使用)

//使用泛型技术与可变长度的参数(JDK1.5版本以上)

staticUserfindUserSimple(Stringname)

{

SimpleJdbcTemplatesimple=newSimpleJdbcTemplate(JdbcUtil.getDataSource

());

Stringsql="selectid,name,money,birthdayfromuserwheremoney>:moneyandid<:id";

//用这个泛型,返回结果就不用向下转型了

//publicTqueryForObject(Stringsql,ParameterizedRowMapperrm,Object...args)throwsDataAccessException

Useru=simple.queryForObject(sql,

ParameterizedBeanPropertyRowMapper.newInstance(

User.class),name); //这个行映射器的参数是可变长的Object...

returnu;

}

-----------------------------------------------------------------------------------------------------------------------

//代码

importjava.util.HashMap;

importjava.util.Map;

importorg.springframework.jdbc.core.BeanPropertyRowMapper;

importorg.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource;

importorg.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;

importorg.springframework.jdbc.core.namedparam.SqlParameterSource;

importcom.feihu.domain.User;

importcom.feihu.jdbc.JdbcUtil;

publicclassSimpleJdbcTemplate

{

privatestaticNamedParameterJdbcTemplatenamed=newNamedParameterJdbcTemplate(

JdbcUtil.getDataSource());

///参数命名方法查询NamedParameterJdbcTemplate

//两种参数传递方法BeanPropertySqlParameterSource(user)、MapSqlParameterSource

//bean属性参数源

staticUserfindUser(Useruser)

{

//Bean属性参数源使用方法(参数别名要与bean类的属性对应,如:money,:id)(通过&号也可以命名别名,不建议使用)

Stringsql="selectid,name,money,birthdayfromuserwheremoney>:moneyandid<:id";

SqlParameterSourceps=newBeanPropertySqlParameterSource(user);

Objectu=named.queryForObject(sql,ps,newBeanPropertyRowMapper( User.class)); //这个方法当查询对象多于一个或为null时会抛异常

return(User)u;

}

//Map参数源

staticUserfindUser2(Useruser)

{

Stringsql="selectid,name,money,birthdayfromuserwheremoney>:mandid<:n";

Mapmap=newHashMap();

map.put("m",user.getMoney());

map.put("n",user.getId());

Objectu=named.queryForObject(sql,map,newBeanPropertyRowMapper( User.class)); //这个方法当查询对象多于一个或为null时会抛异常

return(User)u;

}

//泛型技术(JDK1.5以上版本可使用)

//使用泛型技术与可变长度的参数(JDK1.5版本以上)

staticUserfindUserSimple(Stringname)

{

SimpleJdbcTemplatesimple=newSimpleJdbcTemplate(JdbcUtil.getDataSource

());

Stringsql="selectid,name,money,birthdayfromuserwheremoney>:moneyandid<:id";

//用这个泛型,返回结果就不用向下转型了

//publicTqueryForObject(Stringsql,ParameterizedRowMapperrm,Object...args)throwsDataAccessException

Useru=simple.queryForObject(sql,

ParameterizedBeanPropertyRowMapper.newInstance(

User.class),name); //这个行映射器的参数是可变长的Object...

returnu;

} }

JDBC连接MySQL数据库关键的四个步骤

1、查找驱动程序

MySQL目前提供的Java驱动程序为Connection/J,可以从MySQL官方网站下载,并找到mysql-connector-java-3.0.15-ga-bin.jar文件,此驱动程序为纯Java驱动程序,JDBC链接MySQL不需做其他配置。

2、动态指定classpath

如果需要执行时动态指定classpath,就在执行时采用-cp方式。否则将上面的.jar文件加入到classpath环境变量中。

3、加载JDBC 连接MySQL

try{

Class.forName(com.mysql.jdbc.Driver);

System.out.println(Success loading Mysql Driver!);

}catch(Exception e)

{ System.out.println(Error loading Mysql Driver!); e.printStackTrace(); }

4、设置JDBC连接MySQL的URL

jdbc:mysql://localhost/databasename[?pa=va][&pa=va]

Java初学者习题20道

如何自学java迅速成为java高手

1. 源文件中最多只能有一个()类,其他类的个数不限。

public类,而且这个类的类名要和文件名一致.

2.面向对象程序设计的特征(封装性)(继承)(多态性)

3.接口是由(抽象方法)和(常量)组成的特殊类

4.用static修饰的方法是(静态方法,或类方法)

5.在产生异常的方法声明中声明会产生的异常,向更高一级方法抛出异常的关键字是:throws

6.子类通过(super)关键字调用父类的构造方法。

7.Java的标准输出流是(System.out)。

8.实现继承的关键字(extends)。

9.JVM是(Java虚拟机--java virtual machine)

JDK是(java开发工具包--Java Developer's Kit )

appletviewer是(小应用程序阅览器) 。

10.用户不能直接调用构造方法,只能通过(new )关键字自动调用。

11.在 Java 中,由Java编译器自动导入,而无需在程序中用import导入的包是(java.lang.*)

12.在 Java 中,能实现多重继承效果的方式是(实现多个接口)

13.创建包的语句应该放在程序的位置为(非注释的第一行)。

14.用(abstract)关键字修饰的类只能被继承,不能生成其对象。

15.通过调用Thread类的(start)方法来启动一个线程。

16.定义包的关键字(package)

17.Java不支持多重继承,但可通过(接口interface)实现多继承。

18.Java源程序经编译生成的字节码文件的扩展名为(class)

19.Java的核心包中,提供编程应用的基本类的包是(自己查一下)

20.每个Java应用程序可以包括许多方法,但必须有且只能有一个(main)方法。

21.当实现Runnable接口时,要实现的方法是(public void run())

22.使用(throw)关键字可以明确地抛出一个异常

浅谈JDBC的概念理解与学习

如何自学java迅速成为java高手

:工厂一般使用单例模式

工厂-->生产产品(Dao这个产品)

类加载器,不光可以load.class,还可以load其他的文件

事务概念与处理

1.原子性:不能拆分,组成事事务处理的语句形成一个逻辑处理单元,不能只执行其中的一部分

2.一致性:事务做完之后,保定数据的完整性约束

3.隔离性:一个事务处理对另一个事务处理的影响

4.持续性:事务处理的结果能被永久保存下来

步骤:打开事务connection.setAutoCommit(false);

提交事务:connection.commit();

回滚事务:connection.rollback();

jdbc缺省是自动提交的,客户端连接也是自动提交的,要想打开事务,要把自动提交关掉

保存点:savePoint

Savepointsp=null

sp=conn.setSavepoint();

conn.rollback(sp);

-----------------------------------

JDBC的概念:跨多个数据库的事务JTA

JTA容器weblogic,websphere

分成两阶段提交。

用到JNDI服务器

javax.transaction.UserTransactiontx=

(UserTransaction)ctx.lookup("jndiName");

tx.begin();

//connection1connection2(可能来自不同的数据库)

tx.commit(); //tx.rollback();

---------------------------------

事务的隔离级别:

connction.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);

隔离级别

读未提交(Readuncommitted)可能出现脏读,不可重复读,幻读

读已提交(Readcommitted)可能出现不可重复读,幻读

可重复读(Repeatableread)可能出现幻读(幻影数据,执行插入一条数据,在另一个客户端有可能看到)

可串行化(Serializable)

隔离级别各个数据库的实现是不一样的。

--------------------------------------------------

关于存储过程(两层架构的时候用的很多)

三层架构的系统很少使用了。略过不看了先。

---------------------------------------------------

其他几个有用的API

jdbc3.0规范提供的方法

插一条记录,它能把主键返回来

PreparedStatement.getGeneratedKeys();

PreparedStatementps=connection.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS);

ps.executeUpdate();

ResultSetrs=st.getGeneratedKeys();

rs.getInt(1);

批量处理:可以大幅提升大量增删改的速度(并非绝对),与具体数据库有关,也不一定能提高性能

PreparedStatement.addBatch();

PreparedStatement.executeBatch();

-------------------------------------------------------------

可滚动结果集与分页特性

Statementst=conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_UPDATABLE);

ResultSet.TYPE_SCROLL_SENSITIVE--对数据库敏感的,就是说,数据库中记录发生了变化,结果集会觉察到(但某些数据库不一定遵循这一规范,mysql就不遵循)

ResultSet.CONCUR_UPDATABLE--可更新的,就是说,改变结果集中的内容可以影响到数据库里

ResultSet.CONCUR_READ_ONLY--只读的,

可滚动的结果集

Statementst=connection.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_UPDATABLE);

ResultSetrs=st.executeQuery(sql);

rs.beforeFirst();

rs.afterLast();

rs.first();

rs.isFirst();

rs.last();

rs.isLast();

rs.absolute(9);

rs.moveToInsertRow();

conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_UPDATABLE);

rs.updateString("colname","newvalue");

rs.updateRow();

mysql分页例如:

selectid,name,money,birthdayfromuserlimit150,10;

从user表中第150条记录开始,取10条记录

---------------------------------------------------------

jdbc元数据信息数据库元数据信息、参数元数据信息

DatabaseMetaDatameta=connection.getMetaData();

通过DatabaseMetaData可以获得数据库相关的信息,如:

数据库版本,数据库名,数据库厂商信息,是否支持事务,是否支持某种事务隔离级别,

是否支持滚动结果集等。

ParameterMetaDatapmd=preparedStatement.getParameterMetaData();

通过ParameterMetaData可以获得参数信息.

ParameterMetaDatapmd=ps.getParameterMetaData();

intcount=pmd.getParameterCount();

System.out.println("count="+count);

for(inti=1; i<=count; i++)

{

ps.setObject(i,params[i-1]);

}

ResultSetrs=ps.executeQuery();

--------------------------------------------------

dataSource代理模式:

组合优先于继承

拦截Connection.close()方法

动态代理

调用处理器

----------------------------------------------------

数据源和连接池

DataSource用来取代DriverManger来获取Connection

通过DataSource获得Connection速度很快

通过DataSource获得的Connection都是已经被包裹过的(不是驱动原来的连接),

他的close方法已经被修改

一般DataSource内部会用一个连接池来缓存Connection,这样可以大幅度提高

数据库的访问速度;

连接池可以理解成一个能够存放Connection的Connection

我们的程序只和DataSource打交道,不会直接访问连接池。

------------------------------------------

DBCP的应用

1.加载三个.jar

2.写dbcpconfig.properties配置文件

3.创建DataSource数据源

使用DBCP必须用的三个包

commons-dbcp-1.2.2\commons-dbcp-1.2.1.jar,

spring-framework-2.5.6\lib\jakarta-commons\commons-pool.jar,commons-collection-3.1.jar

dbcpconfig.properties

driverClassName=com.mysql.jdbc.Driver

url=jdbc:mysql://localhost:3306/jdbc

username=root

password=root

#初始化连接

initialSize=10

#最大连接数量

maxActive=50

#最大空闲连接

maxIdle=20

#最小空闲连接

minIdle=5

#超时等待时间以毫秒为单位

maxWait=60000

#JDBC驱动建立连接时附带的连接属性格式必须为这样:属性名=property

#注注意user与password两个属性会被明确地传递。因此这里不需要包含他们

connectionProperties=useUnicode=true; characterEncoding=utf-8

#指定由连接池所创建的连接的自动提交auto-commit状态

defaultAutoCommit=true

#driverdefault指定由连接池所创建的连接的只读read-only状态

#如果没有设置该值,则setReadOnly方法不被调用。

defaultReadOnly=

#driverdefault指定由连接池所创建的连接的事务级别TransactionIsolation

#可用值为下列之一:这依赖于数据库

#NONE,READ_UNCOMMITED,READ_COMMITTED

defaultTransactionIsolation=READ_COMMITTED

----------------------------------------------------

创建数据源

Propertiesprop=newProperties();

prop.setProperty("driverClassName","com.mysql.jdbc.Driver");

prop.setProperty("url","jdbc:mysql://localhost:3306/jdbc");

prop.setProperty("username","root");

prop.setProperty("password","root");

#dbcp使用这个方法创建数据源,需要一个Property参数,参数中的键值对就为.properties文件中的配置

#可以直接load进来,省的写了

Propertiesprop=newProperties();

InputStreamis=JdbcUtil.class.getClassLoader().getResourceAsStream("dbcpconfig.properties");

prop.load(is);

DataSourcemyDataSource=BasicDataSourceFactory.createDataSource(prop);

-------------------------------------------------------------------------

模板模式对查询抽象,采用继承的方式(对查询来说,不够灵活,查询的列一变化,就得修改超类)

策略模式(采用接口的方式传递一个接口的参数,通过匿名类为各种需求实现)

-----------------------------------------------------------------------

使用JdbcTemplate工具类,在spring框架中提供了一个JdbcTemplate工具类,这个类对JDBCAPI进行了很好的封装

这个类就像我们自己对JDBC进行封装一样。只是代码列健壮,功能更强大而已。

我们以后在实际项目中可以使用JdbcTemplate类来完全替代直接使用JDBCAPI这与直接使用jdbcapi没有太大性能区别

使用JdbcTemplate工具类需要额外从spring开发包中导入spring.jar和commons-logging.jar

关于jdbcTemplate查询,更新相关使用方法见练习

如果想获取connection,自由进行操作,可以使用

jdbc.execute(newConnectionCallback()

{

publicObjectdoInConnection(Connectionconn)throwsSQLException,

DataAccessException

{}

}); //拿到了Connction随你怎么用了

-------------------------------------------------------------------

Spring的NameParameterJdbcTemplate

NameParameterJdbcTemplate内部包含了一个JdbcTemplate,所以JdbcTemplate能做的事情它都能做

NameParameterJdbcTemplate相对于JdbcTemplate主要增加了参数可以命名的功能

publicObjectqueryForObject(Stringsql,MapparamMap,RowMapperrowMapper) publicObjectqueryForObject(Stringsql,SqlParameterSourceparamSource,RowMapperrowMapper

SqlParameterSource的两个主要实现MapSqlParameterSource和BeanPropertyParameterSource

SimpleJdbcTemplate内部包含了一个NamedParameterJdbcTemplate,所以NamedParameterJdbcTemplate

能做的事情SimpleJdbcTemplate都能干,SimpleJdbcTemplate相对于NamedParameterJdbcTemplate主要

增加了JDK5.0的泛型技术和可变长度参数支持

simpleJdbcTemplate中有些方法不全,比如拿到主键的方法就没有,可以获取到NamedParameterJdbcTemplate

调用它里面的方法

getNamedParameterJdbcOperations()返回的是NamedParameterJdbcOperations其实现就是NamedParameterJdbcTemplate这个类

getJdbcOperations()返回的是JdbcOperation其实现就是JdbcTemplate

++++++++++++++++++++++++++++++++++++++++++++++++++

Map参数源:采用键值对方式传参(参数别名可任意,如:m,:n)

staticUserfindUser(Useruser)

{

NamedParameterJdbcTemplatenamed=newNamedParameterJdbcTemplate(dataSource);

Stringsql="selectid,name,money,birthdayfromuserwheremoney>:mandid<:n";

Mapmap=newHashMap();

map.put("m",user.getMoney());

map.put("n",user.getId());

Objectu=named.queryForObject(sql,map,newBeanPropertyRowMapper(User.class)); //这个方法当查询对象多于一个或为null时会抛异常

return(User)u;

}

Bean属性参数源使用方法(参数别名要与bean类的属性对应,如:money,:id)(通过&号也可以命名别名,不建议使用)

staticUserfindUser(Useruser)

{

NamedParameterJdbcTemplatenamed=newNamedParameterJdbcTemplate(dataSource);

Stringsql="selectid,name,money,birthdayfromuserwheremoney>:moneyandid<:id";

SqlParameterSourceps=newBeanPropertySqlParameterSource(user);

Objectu=named.queryForObject(sql,ps,newBeanPropertyRowMapper(User.class)); //这个方法当查询对象多于一个或为null时会抛异常

return(User)u;

} //保存数据获得主键

publicintupdate(Stringsql,SqlParameterSourceparamSource,KeyHoldergeneratedKeyHolder)staticvoidaddUser(Useruser)

{

NameParameterJdbcTemplatenamed=newNameParameterJdbcTemplate(dataSource);

Stringsql="insertintouser(name,birthday,money)values(:name,:birthday,:money";

SqlParameterSourceps=newBeanPropertySqlParameterSource(user);

KeyHolderkeyHolder=newGeneratedKeyHolder();

named.update(sql,ps,keyHolder);

intid=keyHolder.getKey().intValue(); user.setId(id); //如果不是整型的主键呢,使用Mapmap=keyHolder.getKeys(); }

JDBC的概念:使用泛型技术与可变长度的参数(JDK1.5版本以上)

//泛型技术(JDK1.5以上版本可使用)

//使用泛型技术与可变长度的参数(JDK1.5版本以上)

staticUserfindUserSimple(Stringname)

{

SimpleJdbcTemplatesimple=newSimpleJdbcTemplate(JdbcUtil.getDataSource

());

Stringsql="selectid,name,money,birthdayfromuserwheremoney>:moneyandid<:id";

//用这个泛型,返回结果就不用向下转型了

//publicTqueryForObject(Stringsql,ParameterizedRowMapperrm,Object...args)throwsDataAccessException

Useru=simple.queryForObject(sql,

ParameterizedBeanPropertyRowMapper.newInstance(

User.class),name); //这个行映射器的参数是可变长的Object...

returnu;

}

-----------------------------------------------------------------------------------------------------------------------

//代码

importjava.util.HashMap;

importjava.util.Map;

importorg.springframework.jdbc.core.BeanPropertyRowMapper;

importorg.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource;

importorg.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;

importorg.springframework.jdbc.core.namedparam.SqlParameterSource;

importcom.feihu.domain.User;

importcom.feihu.jdbc.JdbcUtil;

publicclassSimpleJdbcTemplate

{

privatestaticNamedParameterJdbcTemplatenamed=newNamedParameterJdbcTemplate(

JdbcUtil.getDataSource());

///参数命名方法查询NamedParameterJdbcTemplate

//两种参数传递方法BeanPropertySqlParameterSource(user)、MapSqlParameterSource

//bean属性参数源

staticUserfindUser(Useruser)

{

//Bean属性参数源使用方法(参数别名要与bean类的属性对应,如:money,:id)(通过&号也可以命名别名,不建议使用)

Stringsql="selectid,name,money,birthdayfromuserwheremoney>:moneyandid<:id";

SqlParameterSourceps=newBeanPropertySqlParameterSource(user);

Objectu=named.queryForObject(sql,ps,newBeanPropertyRowMapper( User.class)); //这个方法当查询对象多于一个或为null时会抛异常

return(User)u;

}

//Map参数源

staticUserfindUser2(Useruser)

{

Stringsql="selectid,name,money,birthdayfromuserwheremoney>:mandid<:n";

Mapmap=newHashMap();

map.put("m",user.getMoney());

map.put("n",user.getId());

Objectu=named.queryForObject(sql,map,newBeanPropertyRowMapper( User.class)); //这个方法当查询对象多于一个或为null时会抛异常

return(User)u;

}

//泛型技术(JDK1.5以上版本可使用)

//使用泛型技术与可变长度的参数(JDK1.5版本以上)

staticUserfindUserSimple(Stringname)

{

SimpleJdbcTemplatesimple=newSimpleJdbcTemplate(JdbcUtil.getDataSource

());

Stringsql="selectid,name,money,birthdayfromuserwheremoney>:moneyandid<:id";

//用这个泛型,返回结果就不用向下转型了

//publicTqueryForObject(Stringsql,ParameterizedRowMapperrm,Object...args)throwsDataAccessException

Useru=simple.queryForObject(sql,

ParameterizedBeanPropertyRowMapper.newInstance(

User.class),name); //这个行映射器的参数是可变长的Object...

returnu;

}

}

更多相关推荐:
java初学者学习心得

学习Java心得体会学习了一学期的Java课程觉得是该总结自己的心得体会了开始学习任何一门课包括java兴趣最重要一直觉得自己在学计算机编程语言学习了很多但每门语言都停留在知识边缘地带都没深入到它们的精华部分对...

java学习心得

湖南软件职业学院HNSERJ20xx0101湖南软件职业学院毕业论文知识改变命运课题java学习心得院系软件工程系专业软件技术班级软件0615班姓名周专书指导老师郑利娇完成时间20xx512WWWHNSOFTE...

关于java学习的总结

关于java学习的总结Java是一门编程语言。想对java有所了解,必须清晰的认识到这一点。但是java又不仅仅是编程语言那么简单。从它的产生到发展壮大都离不开编程思想的指导。因此想深入学习java就必须深刻理…

Java学习心得个人体会

Java学习心得个人体会Java学习路线以下这两张图片很好的规划了Java学习路径你可以另存为图片缩放观看图01传智播客Java学习路线图02疯狂Java学习路线推荐一篇博文JAVA学习路线地址blog4038...

学习java的心得体会

学习java的心得体会我是今年x月份接触java的,其实上大学以前就买过java的书,但没进去,因为那个时候我在学习vb,对于java代码类c的编写方式不是很习惯,所以就没有认真的看。为什么最后好是学习java…

java学习总结

JAVA论文姓名:耿岩班级:信学号:09220xx314202Java学习总结Java有三个体系,JavaSE、JavaME、JavaEE。JavaSE是桌面应用;JavaME属于企业级应用,用于网络编程,是对…

java的学习心得体会

1、我感觉学习java应该是循环渐进,有始有终,按部就班,脚踏实地。java是一门有着阶梯性的一们语言,如果要学习它。我觉得最好还是按照java的学习体系,先学习什么,在学习什么,只有这样,我们在学习中才会遇到…

java经典学习经验

给学Java的大学生们分享一些经验想来学习Java也有两个年头了永远不敢说多么精通但也想谈谈自己的感受写给软件学院的同仁们帮助大家在技术的道路上少一点弯路说得伟大一点是希望大家为软件学院争气其实最主要的还是大家...

java学习心得

每个人的学习方法是不同的一个人的方法不见得适合另一个人我只能是谈自己的学习方法因为我学习Java是完全自学的从来没有问过别人所以学习的过程基本上完全是自己摸索出来的我也不知道这种方法是否是比较好的方法只能给大家...

java的学习经验

转Java学习之路不走弯路就是捷径20xx5101235阅读5转载自胡杨1如何学习程序设计赞转载2分享评论复制地址举报更多上一篇下一篇好书大全点击JAVA是一种平台也是一种程序设计语言如何学好程序设计不仅仅适用...

java论文 学习心得 图形界面

java图形化用户界面及Applet学习总结一javaAppletApplication和Applet是Java程序的两种基本形式Applet是特殊的Java程序它不需要专门的解释器来执行它相对于JavaApp...

Java个人学习心得

呵呵当年我也是靠着自学走上java程序员这条路的就给你写几点我的体会吧首先java完全可以在半年之内学通只要你确实用心学这点完全不必担心如果你智商属于普通人并且完全没有编程的基础例如cc或者其他编程语言我指的是...

java学习心得(26篇)