读书笔记--Java高级程序设计 之 Java后端入门准备
主要是对一个Java开发的注意点和易忘点做个小总结,不少地方没有详细分析,读者见谅。另外,对一些特殊的示例程序做一些笔记。
一、Integer 内部的IntegerCache类对象,缓存了从-128~127的整数(因为这些小的整数可能较为常用,所以提前缓存在内存中),也是常量池的一种运用。
Class cache = Integer.class.getDeclaredClasses()[0];//IntegerCache.java 这个类的类类型
Field myCache = cache.getDeclaredField("cache");//通过这个类类型反射获取到类对象
myCache.setAccessible(true);///将该类对象的所有属性和方法的访问权限都变为public
Integer[] newCache = (Integer[]) myCache.get(cache);///调用这个反射获得者的方法
System.out.printf("看一下这个newCache 缓存数组的首元素,是不是 -128? newCache[0]=%d\n",newCache[0]);
System.out.printf("所以,newCache[128]=%d\n",newCache[(0+128)]);
newCache[132] = newCache[133];// 这个数组的第133个元素, 值为: 5
int a =2;
int b =a+a;//实际指向了 cache数组的
System.out.printf("%d + %d = %d\n" , a,a,b);
System.out.println(newCache[128+3]);
////看看大整数和小整数的区别
Integer big1 = 1000;
Integer big2 = 1000;
Integer sm1 = 127;
Integer sm2 = 127;
System.out.println(big1==big2);///这里可以看出,比127大的数,会在堆中新建一个Integer类对象,所以,两个Integer对象引用指向两个不同位置的Integer类对象
System.out.println(sm1==sm2);///而在Integer类中,[-128,127]区间里的整数,都会先在IntegerCache类中的常量池里查找,并索引之,所以,sm1和sm2指向同一个位置
///这里,由于上述的代码"newCache[132]==newCache[133]"的缘故,即newCache[sm1+128]== newCache[sm2+128],而newCache[sm1+128]=newCache[sm2+128]= -128+128+5 = 5
///所以,sm1 和 sm2 的指向的常量池下标对应的值都是5,于是,sm1 == sm2
sm1 = 4;
sm2 = 5;
System.out.printf("sm1=%d,sm2=%d,sm1=sm2吗?%b\n",sm1,sm2,sm1==sm2);
//而这里,就是正常的情况
sm2 = 6;
System.out.printf("sm1=%d,sm2=%d,sm1=sm2吗?%b\n",sm1,sm2,sm1==sm2);
运行结果如下:
看一下这个newCache 缓存数组的首元素,是不是 -128? newCache[0]=-128
所以,newCache[128]=0
2 + 2 = 5
3
false
true
sm1=5,sm2=5,sm1=sm2吗?true
sm1=5,sm2=6,sm1=sm2吗?false
二、泛型注意点:通配符+边界,List<? super X>
只能添加X
类及其子类对象元素(因为? super X
表示:X及其父类,存在一定的不确定性)
List<? super Mother> list = new ArrayList<Mother>();
Me me = new Me();///Me extends Mother
Sister sister = new Sister();//Sister extends Mother
Mother mother = new Mother();//Mother extends Grandpa
Grandpa grandpa = new Grandpa();
Son son = new Son();
list.add(me);
list.add(son);
list.add(sister);
list.add(mother);
// list.add(grandpa); //不合法
三、Java常用注解与自定义注解
- 常用注解:
@Deprecated///表示这个Person类是过时的类
class Person{
@SuppressWarnings(value = "unused")///无须抛出 未使用警告
private String name;
@Deprecated///表示过时的方法
public void speak(){
}
@Override///表示重载的方法
public String toString() {
return super.toString();
}
@SuppressWarnings("unused")
public void working(){
@SuppressWarnings({"unchecked", "unused"})///无须抛出检测警告和未使用警告
List list=new ArrayList();
}
}
- 自定义注解的基础--元注解(@Target,@Retention,@Document,@Inherited)
- @Target:表明自定义注解的作用域(枚举类型ElementType中找)
-
ElementType.ANNOTATION_TYPE
:作用在注解类型上的注解 -
ElementType.CONSTRUCTOR
:作用在构造方法上 -
ElementType.FIELD
:作用在属性上 -
ElementType.LOCAL_VARIABLE
:作用在本地变量上 -
ElementType.METHOD
:方法上 -
ElementType.PACKAGE
:包上 -
ElementType.PARAMETER
:参数上 -
ElementType.Type
:类、接口、枚举上
-
- @Retention:用于声明注解方法的保留策略(枚举类型RetentionPolicy中找)
-
RetentionPolicy.SOURCE
:【源代码】注解信息仅保留在源文件中,编译时将丢弃注解信息。 -
RetentionPolicy.CLASS
:【源代码-->编译后】注解信息保留到编译后的class文件中。 -
RetentionPolicy.RUNTIME
:【源代码-->编译后-->运行时】注解信息保留到运行时,可通过反射来读取该注解信息。
-
- @Documented:【注解信息添加到文档】表明制作Javadoc时,是否将注解信息加入文档。
- @Inherited:【修饰注解,表明让子类继承该注解】表明注解是否会被子类继承,默认情况是不继承的。
- @Target:表明自定义注解的作用域(枚举类型ElementType中找)
- 自定义注解
- 类型是
@interface
。 - 注解参数的类型只能是:所有基本类型(int、float、double)、String、Class、enum、Annotation 以及以上类型的数组。
- 快捷赋值参数
value
,详见以下例子。
- 类型是
@Target(ElementType.FIELD)///注解@JP用于修饰方法
@Retention(RetentionPolicy.RUNTIME)//注解@JP能够保留到运行时
public @interface ID {
public String value();///快捷参数value,可用于快捷赋值
public String description() default "";
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@SuppressWarnings("unused")
public @interface Entity {
String name() default "用户";
ID id() default @ID("007");///①Entity内部可拥有@ID注解参数 ②快捷赋值给value为007:@ID("007")
}
- 注解处理器【需自定义】
- 相关接口:
AnnotationProcessor
- 接口的实现类:
ExtractProcessor
- 处理步骤:① 获取@ExtractInterface 注解【获取失败后结束】② 构建StringBuilder对象作为临时代码存储器 ③ 按顺序添加:包信息、接口信息、方法信息 ④ 生成接口文件
- 相关接口:
- Apt工具
- Sun公司开发的一款用于处理注解的工具。
- 能快速构建复杂的注解处理器。
- 深度优先检查新产生的类,并对新生的文件进行编译【Apt一般包含javac功能】。
- 容易将我们编写的多个注解处理器(都必须实现了AnnotationProcessor接口)组合起来,以处理一或多个注解。
- Apt处理源代码的Annotation时,会调用特定的AnnotationProcessor(开发人员自己定义的),一个Processor处理一个或多个Annotation。Apt是通过AnnotationProcessorFactory来获取处理具体Annotation的Processor的。
- 使用Apt的条件:①实现AnnotationProcessor接口 ②实现AnnotationProcessorFactory接口
- Apt的实现在
com.sun.tools.apt
及其子包中【此包 包含:用于处理Java代码文件的com.sun.mirror
包,内部是Visitor模式进行代码维护的,每个Declaration接口的子类都都有一个accept方法,用于接受一个DeclarationVisitor接口实现类,并调用该接口的相应方法(设计模式中的访问者模式的思想)】
- 注解示例demo【实现.class文件到sql建表语句的转换过程】
四、Java网络编程
- 网络协议:
- OSI(开放系统互联模型)【7层】:物理层、数据链路层、网络层、运输层、会话层、表示层、应用层
- TCP/IP模型【4层】:网络接口层、网际层、运输层、应用层
- 五层协议体系【5层】参考文章:物理层、数据链路层、网络层、传输层、应用层
- IP地址:
- IPv4:32位二进制数表示(8x4)
- IPv6:128位二进制数表示(8x16)
- InetAddress以及实现类是Java对IP地址的封装。作用:IP与主机地址(网址)互转。实现原理是:使用本地及其配置或DNS(域名系统)、NIS(网络信息服务)来对域名进行解析。提高效率的方法:对于DNS来说,通过本地缓存一些主机名与IP地址的映射,避免重复发送DNS请求。
- TCP通信:
- 主要两个类:ServerSocket 和 Socket
- ServerSocket.accept() throws IOException 此方法一直处于阻塞状态,知道有新的连接接入,建立连接后,此方法返回一个套接字,用于操作读写。
- UDP通信:
- 特点:不像TCP每次通信都建立一条特定的连接通道,进行传输控制,UDP本身数据就自带传输控制信息,所以UDP传输能节省系统开销,数据传输效率高于TCP。
- 主要类:DatagramPacket(数据包,'邮件'),DatagramSocket(数据包收发套接字,‘邮箱’)
- 工作过程:接收方会一直监听是否有数据包到达,当有数据包到达时,会创建一个DatagramPacket对象,来接收存储这个报文,接收的报文中存储了发送者的地址和端口。
- HTTP通信:
- HTTP作用:用于从WWW服务器传输超文本到本地浏览器的传送协议,可以使浏览器更高效,是网络传输减少。
- HTTP是基于TCP的应用层协议,有时承载着TLS或SSL协议层【TLS:安全传输层协议,用于两个通信应用之间提供保密性和数据完整性;SSL:Netscape研发的用于保障Internet上数据传输的安全,利用数据加密(Encryption)技术,确保数据在网络上的传输过程中不被截取、窃听】
- HTTP永远是:客户端发请求,服务端响应。
- HTTP格式:
- 请求消息格式:
Method【GET/POST等】
+空格+Request-URI【请求访问的地址(相对地址,"/"开头)】
+空格+HTTP/Version【HTTP版本】
+换行+消息头【客户端运行环境信息+消息体信息等】
+换行+消息体
- 响应消息格式:
HTTP/Version
+空格+Status【响应状态码】
+空格+Description【响应描述】
+换行+消息头
+换行+消息体
- 请求消息格式:
五、多线程
- 线程操作相关基础:
- Thread.sleep():【休眠】当前线程休眠;不占CPU时间;休眠结束后,线程继续运行;线程休眠时间精确性与系统时钟有关。
- Thread.interrupt():【中断】设置线程的中断状态为true;不直接强制线程的执行或中止,线程自己决定是否运行;
- 注意点一:线程处于阻塞状态,线程没有执行,所以没机会检查中断状态。(线程阻塞状态:由于某种原因线程暂停运行的状态,包括可中断阻塞和不可中断阻塞)
- 可中断阻塞:线程上调用了sleep()、wait()、join()导致。
- 不可中断阻塞:获取对象锁 导致的阻塞。
- 注意点二:如果线程处于可中断阻塞状态,另一个线程对它提出中断请求,线程将抛出InterruptedException异常或ClosedByInterruptException异常,并跳出阻塞状态;如果线程处于不可中断状态,则对中断请求不会做出响应。
- 注意点一:线程处于阻塞状态,线程没有执行,所以没机会检查中断状态。(线程阻塞状态:由于某种原因线程暂停运行的状态,包括可中断阻塞和不可中断阻塞)
- Object.wait():导致本线程等待,并释放本线程拥有的该对象的锁;只有本线程被别的线程调用nofity()或notifyAll()并抢到对象锁时,才重新回到运行态。
- Object.notify() 和 Object.notifyAll():前者只通知本线程以外的其他等待此对象锁的线程中的一个,后者是则通知其他所有的等待线程并让随机一个线程获取对象锁。
- Condition.await():【类似于:Object.wait()】让本线程等待。
- Condition.signal()和signalAll():【类似于:Object.notify()】通知等待的线程,并结束等待线程的等待状态。
- 例子:线程的run()方法中对中断做出响应的正确写法:
while(还有工作没完成){
if(Thread.currentThread().isInterrupted())
{///如果中断状态被设为true(别人叫我中断了)
///响应中断请求。首先决定是否终止线程,如果要终止,需要完成必须完成的结束工作
///例如关闭资源占用等,然后退出run()方法
}
///处理未完成工作
}
- 未捕获异常,用
Thread.UncaughtExceptionHandler
来解决。
- 出现未捕获异常的情况:子线程的run()方法执行过程中抛出异常并终止,但是主线程无法获取到这个异常。【因为主线程与子线程是完全不同的两个指令序列】
- 解决方法:
Thread.setDefaultUncaughtExceptionHandler(new 自定义UncaughtExceptionHandler实现类);
- Lock的用法
- Lock是接口,常用实现类是ReentrantLock。
- 正确写法实例:
class X{ private ReentrantLock lock = new ReentrantLock(); public void readOrWriteSharedData(){ if(lock.tryLock()){///尝试获取对象锁 try{ ///对共享资源进行处理 } finally{ lock.unlock(); } } } }
- 如果一个锁被另一个线程保持,那么,处于线程调度目的, 阻塞当前线程,当前线程处于休眠状态,直到:
- 锁由当前线程获得
- 其他某个线程中断当前线程
- 已超过指定的等待时间
- 线程的六个状态
- 新生:new出了Thread对象,还没执行start()。
- 可运行:调用start()后的状态。【注意:此状态下,线程不一定被线程调度器加载到CPU上执行】
- 阻塞:受阻塞并正在等待锁的线程状态,没有CPU时间片。两种方法进入阻塞态:①线程进入synchronized方法或代码块(或调用Lock对象的lock()或tryLock()等方法),并试图获取其他线程已经占用的锁时;②退出等待状态、并试图重新获得在等待状态时拥有的锁但此锁已被其他线程占用时。
- 等待:此状态下的线程正在等待另一个线程,以执行特定操作。不占CPU时间片。
- 计时等待:具有指定等待时间的某一等待线程的线程状态。与等待状态相似,区别是:过了等待时间,也会退出等待状态。
- 终止:线程结束执行。进入此状态的两种可能:① run()方法执行完毕并返回; ② 在执行run()过程中抛出未处理异常。
- 示例:Condition.await()和Condition.signal()的应用:多个转账交易可以被多个线程调用,而遇到转账金额过大的情况,就选择等待其他交易完成。
Condition相对于对象锁的优势:能够在一个锁对象上创建多个Condition对象,每个Condition对象代表一种不同的等待类型。
private Account[] accounts;
private Lock dealLock = new ReentrantLock();
private Condition moneyCondition = dealLock.newCondition();///资金不足的情况
public void deal(int fromAccount,int toAccount,Long money){
if (this.dealLock.tryLock()){
try {
while(accounts[fromAccount].money<money){
///如果存款不足,就不能转这么多帐了
try {
moneyCondition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
///到有了存款了,那么,就可以继续执行转账
accounts[fromAccount].money -= money;
accounts[toAccount].money += money;
/////转账完毕,可以唤醒其他需要转账的线程了
moneyCondition.signalAll();
} finally {
this.dealLock.unlock();
}
}
}
以上代码的功能于一下使用synchronized同步关键字的一样:
public synchronized void deal(int fromAccount, int toAccount, Long money) {
while (accounts[fromAccount].money < money) {
///如果存款不足,就不能转这么多帐了
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
///到有了存款了,那么,就可以继续执行转账
accounts[fromAccount].money -= money;
accounts[toAccount].money += money;
this.notifyAll();//转账完毕,可以唤醒其他需要转账的线程了
}
- 同步器之---信号量(Semaphore)
信号量的作用是限制对共享资源读写的线程的最大并发数。它一般配合具有同步能力的资源接口方法一起使用。而信号量本身的阻塞过多线程的能力与共享数据的同步操作之间,是不同的处理,也就是说:能够被信号量允许的线程,也还得需要经过同步方法的同步过程,才能真的达到共享资源的同步读写。
///例子:10个线程 ,同时对共享数据进行读取,通过信号量限制每次最多5个线程异步运行。
///,剩下的线程被阻塞,而运行的5个线程,如果要做同步操作,也必须遵循先后的原则。
public class PoolSemaphoreDemo {
private static final int max =5;
private final Semaphore semaphore = new Semaphore(max,true);
public static void main(String[] args){
final PoolSemaphoreDemo pool = new PoolSemaphoreDemo();
Runnable runnable = new Runnable() {
@Override
public void run() {
try {
Object obj;
obj = pool.getItem();///读写数据,设置数据状态为"被占用"
if (obj!=null)
System.out.println("线程:"+Thread.currentThread().getName()+"读取了数据:"+obj.toString());
else
System.out.println("线程:"+Thread.currentThread().getName()+"没有数据");
Thread.sleep(1000);
pool.putItem(obj);//读写数据完毕,设置数据为"空闲"状态,并容许后续的等待线程调用
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
for (int i=0;i<10;i++){
Thread t = new Thread(runnable,"thread"+i);
t.start();
}
}
public Object getItem() throws InterruptedException {
System.out.println(Thread.currentThread().getName()+"正在被信号量检查。。。");
semaphore.acquire();
System.out.println(Thread.currentThread().getName()+"被允许执行同步方法");
return getNextAvailableItem();
}
public void putItem(Object x){
if (markAsUnused(x)){
semaphore.release();
System.out.println("线程:"+Thread.currentThread().getName()+"释放了资源:"+x.toString());
}
}
protected Object[] items = {"11","22","33"};
protected boolean[] useds = new boolean[3];
private synchronized Object getNextAvailableItem() {
for (int i=0;i<3;i++){
if (!useds[i]){
useds[i] = true;
return items[i];
}
}
return null;
}
private synchronized boolean markAsUnused(Object item){
for (int i=0;i<3;i++){
if (item == items[i]){
if (useds[i]) {///正在被使用
useds[i] = false;
return true;
}else///没有被使用
return false;
}
}
return false;
}
}
- 同步器之---倒计时门栓(CountDownLatch)
是一个同步辅助器。下面通过一个例子,说明他的用法:
public class LatchDriverDemo {
public static void main(String[] args) throws InterruptedException {
CountDownLatch startSignal = new CountDownLatch(1);
CountDownLatch doneSignal = new CountDownLatch(5);
//主线程运行
for (int i=0;i<5;i++){
new Thread(new Worker(startSignal,doneSignal),"t"+i).start();
}
//此时,5个子线程都执行了CountDownLatch.await()从而等待主线程开门,主线程继续运行
///此时,5个子线程处于等待状态,主线程休眠4s
Thread.sleep(4000);
// 此时,主线程执行CountDownLatch.countDown(),使得由于startSignal的值已经减为0从而解放5个子线程,子线程开始继续往下执行,主线程继续执行
startSignal.countDown();///startSignal变成0,所有线程开始工作
///此时,子线程继续执行中,主线程调用了await(),所以主线程阻塞了,等待doneSignal的值变为0,才被允许继续执行
doneSignal.await();
////然后,子线程开始做。。。。。。。
System.out.println("所有任务完成!");
}
}
class Worker implements Runnable{
private final CountDownLatch startSignal;
private final CountDownLatch doneSignal;
public Worker(CountDownLatch startSignal, CountDownLatch doneSignal) {
this.startSignal = startSignal;
this.doneSignal = doneSignal;
}
@Override
public void run() {
try {
//上门栓,等待主线程的开门(startSignal减为0)
startSignal.await();
/*
do sth
*/
//完成任务,doneSignal减一(减到0,主线程才会被唤醒)
doneSignal.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
- 同步器之---障栅(CyclicBarrier)
允许一组线程相互等待,直到到达某个公共屏障点。优点:能够循环多次地进行(等待--运行)的状态切换,从而实现线程间的复杂协作过程。
例子说明一切:快的线程,会等待慢的线程执行完,每次达到同一个进度,就再次解放所有线程,继续往下执行。
public class CyclicBarrierTest {
public static void main(String[] args){
CyclicBarrier barrier = new CyclicBarrier(2, new Runnable() {
@Override
public void run() {
System.out.println("人齐了!!");
}
});
new Thread(new Tour(barrier,"小明走路",1)).start();
new Thread(new Tour(barrier,"小花开车",2)).start();
}
static class Tour implements Runnable{
///走路到:广州、深圳、珠海的时间
private int walkTime[] = {5,5,5};
//开车到:广州、深圳、珠海的时间
private int driveTime[] = {3,3,3};
private CyclicBarrier barrier;
private String name;
private int way;
public Tour(CyclicBarrier barrier,String name,int way) {
this.barrier = barrier;
this.name = name;
this.way = way;
}
@Override
public void run() {
try{
Thread.sleep(1000*((way==1)?walkTime[0]:driveTime[0]));
System.out.println(this.name+"到广州了");
barrier.await();
Thread.sleep(1000*((way==1)?walkTime[1]:driveTime[1]));
System.out.println(this.name+"到深圳了");
barrier.await();
Thread.sleep(1000*((way==1)?walkTime[2]:driveTime[2]));
System.out.println(this.name+"到珠海了");
barrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
}
}
- 同步器之--交换器(Exchanger)
顾名思义, 就是进行线程间数据交换的。实现方法为:Exchanger.exchange(Object obj):Object
六、序列化
- 注意点:
- 静态变量和所有方法都不必序列化
- 使用
transient
关键字,可以修饰不想被序列化的属性。 - 要实现序列化接口的类,都必须声明一个
serialVersionUID
静态Long属性,默认JVM也会自动声明该属性并为之赋值。只有类的序列化标识完全相同,Java才会进行反序列化工作。
- Serializable 接口的扩展:
这里面的两个方法:public class Person implements Serializable{ private static final Long serialVersionUID= 1L; private String name; ///getter and setter 方法 private void writeObject(ObjectOutputStream out) throws IOException{ out.defaultWriteObject(); Date date = new Date(); } private void readObject(ObjectInputStream in) throws IOException,ClassNotFoundException{ in.defaultReadObject(); Date date = (Date)in.readObject();///序列化时的时刻 Date now = new Date();///当前时刻 long offset = now.getTime() - date.getTime();///求得时间差 ///后续的自定义操作等。。。 } }
writeObject()
和readObject()
,分别能够给到我们在序列化和反序列化过程时添加自定义的逻辑,例如添加想要序列化的数据,如上述代码。 - 解决单例模式的类在序列化后,被多次反序列出不同的类对象的情况:
主要加入两个方法:public class Earth implements Serializable{ private static Earth sInstance; private Earth(){ } public static Earth getInstance(){ if(instance==null) instance=new Earth(); return instance; } private Object readResolve(){ return getInstance(); } private Object writeReplace(){ return getInstance(); } }
readResolve()
和writeResolve()
即可。 - Externalizable接口
- 优点:灵活,可部分序列化,可提高序列化效率
- 缺点:较为复杂,需要重写方法
- 重写方法:
-
readExternal()
:自定义哪些属性需要反序列化 -
writeExternal()
:自定义哪些属性需要序列化
-
- 例子:
class Person implements Externalizable{ public String name; public int age; @Override public void writeExternal(ObjectOutput out) throws IOException { out.write(name.getBytes());///只序列化name } @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { this.name = in.readLine();///反序列化name }
}
```
- 对XML的解析
- DOM:
- 采用建立树形接口的方式访问XML文档,直接解析整个文档,生成一棵树并对树遍历。
- 优点:JDK自带,编程容易,容易添加或修改树中的元素。
- 缺点:需要处理整个XML文档,对性能和内存要求比较高。
- DOM解析器常用于XML文档需要频繁改变的服务中。
- SAX:
- 采用事件模型,找到对应的tag时,激活回调方法。
- 优点:JDK自带,对内容要求低,不需要整个文档处理,效率提高。
- 缺点:编码较复杂,难以同时访问同一个文档中的多处不同数据。
- JDOM:
- 与DOM的区别:JDOM使用具体类,而不是接口,限制了灵活性。
- 需要导入jdom.jar包。
- DOM4J:
- 特点:API更复杂,但提供更大的灵活性。
- 需要导入dom4.jar包。
- DOM:
- 对JSON的解析
可以参考本人的这篇文章《JSON数据通信--org.json和Gson基本用法》
七、JDBC
-
JDBC生命周期:
【开始】 --> 加载数据库驱动 --> 注册数据库驱动 --> 获取连接会话 --> 进行数据库操作 --> 关闭并释放连接 --> 【结束】 -
三个重要接口:
-
java.sql.Connection
:负责维护Java开发者与数据库之间的会话 -
java.sql.Driver
:数据库提供商提供的驱动类必须实现的接口 -
java.sql.DriverManager
:用户需要将数据库驱动(Driver实现类)注册到DriverManagerService中,才能访问数据库。
-
-
数据库操作类相关:
-
java.sql.Statement
:会话类,用于执行sql语句。 -
java.sql.PreparedStatement
:会话类,相对于Statement
,能够识别sql语句中的?
问号,并进行动态赋值等操作。 -
java.sql.ResultSet
:执行结果类,用于读取query返回的数据库查询结果。
-
-
MySql的Connection驱动包下载:http://dev.mysql.com/downloads/connector/j/3.1.html
-
例子:自定义的简易数据库连接池
具体思路:MyCon封装由MySqlDAO工具类中获取的新建Connection对象,而ConPool则管理所有的MyCon连接封装对象。- MyCon.java【Connection数据库连接对象封装类】
public class MyCon {
public static final int FREE = 100;///空闲
public static final int BUZY = 101;///繁忙
public static final int CLOSED = 102;///连接关闭
private Connection connection;//持有Connection对象的引用
private int state= FREE;///当前的连接状态
public MyCon(Connection connection) {
this.connection = connection;
}
/**
* @return 返回数据库连接,用于操作
/
public Connection getConnection() {
return connection;
}
/*
* @return 当前状态
*/
public int getState() {
return state;
}
/**
* @param state 设置当前状态
*/
public void setState(int state) {
this.state = state;
}
}
* ConPool.java 【数据库连接池】
public class ConPool {
private List<MyCon> freeCons = new ArrayList<>();
private List<MyCon> buzyCons = new ArrayList<>();
private int max = 10;
private int min = 2;
private int current = 0;
private static ConPool sInstance;
private ConPool(){
while(this.min > this.current){
this.freeCons.add(this.createCon());
}
}
public static ConPool getInstance(){
if (sInstance==null){
synchronized (ConPool.class){
if (sInstance==null)
sInstance = new ConPool();
}
}
return sInstance;
}
/**
* 获取数据库连接
* @return 数据库连接
*/
public MyCon getCon() {
MyCon myCon = this.getFreeCon();
if (myCon!=null)
return myCon;
return this.getNewCon();
}
/**
* 设置连接为空闲状态
* @param con 连接
*/
public void setFree(MyCon con){
this.buzyCons.remove(con);
con.setState(MyCon.FREE);
this.freeCons.add(con);
}
/**
* @return 返回当前连接池的连接状态
*/
public String toString(){
return "当前连接数:"+ this.current + ",空闲连接数:"+this.freeCons.size()+",繁忙连接数:"+this.buzyCons.size();
}
//=================================================================================
private MyCon getNewCon() {
if (this.current>=this.max)
return null;
MyCon myCon = this.createCon();
assert myCon != null;
myCon.setState(MyCon.BUZY);
this.buzyCons.add(myCon);
return myCon;
}
private MyCon createCon() {
try{
Connection connection = MySqlDAO.getConnection();
MyCon myCon = new MyCon(connection);
this.current++;
return myCon;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
private MyCon getFreeCon() {
if (freeCons.size()>0){
MyCon con =freeCons.remove(0);
con.setState(MyCon.BUZY);
this.buzyCons.add(con);
return con;
}
return null;
}
}
* MySqlDAO.java【MySql的连接驱动操作工具类】
public class MySqlDAO {
public static String database = null;
public static Connection getConnection() throws Exception{
String driverName="com.mysql.jdbc.Driver";
String url = "jdbc:mysql://localhost:3306/"+(database==null?"":database);///连接协议 + 数据库地址+ 数据库名称
String user = "root";
String password="110120130140";
Class.forName(driverName);///加载数据库驱动,此过程会自动调用DriverManager中的registerDriver(Driver driver)方法,注册到管理器中
Connection con = DriverManager.getConnection(url,user,password);////创建并获取连接
return con;//等待并最终返回连接信息
}
//。。。。。。。。。。
}
#### 八、IntelliJ IDEA上第一个后端项目的创建过程。
关于eclipse或MyEclipse上新建Web工程,网上比较全,这里不多介绍,而IDEA上创建web项目的过程,可以参考[这篇文章](http://blog.csdn.net/u012532559/article/details/51013400),亲测成功。
由于篇幅问题,关于Servlet注意点与Java Web开发规范等,将后续更新,谢谢阅读~