读书笔记--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只能添加X类及其子类对象元素(因为? super X表示:X及其父类,存在一定的不确定性)

        List list  = new ArrayList();
        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:【修饰注解,表明让子类继承该注解】表明注解是否会被子类继承,默认情况是不继承的。
  • 自定义注解
    1. 类型是@interface
    2. 注解参数的类型只能是:所有基本类型(int、float、double)、String、Class、enum、Annotation 以及以上类型的数组。
    3. 快捷赋值参数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【响应描述】+换行+消息头+换行+消息体

五、多线程

  1. 线程操作相关基础:
  • Thread.sleep():【休眠】当前线程休眠;不占CPU时间;休眠结束后,线程继续运行;线程休眠时间精确性与系统时钟有关。
  • Thread.interrupt():【中断】设置线程的中断状态为true;不直接强制线程的执行或中止,线程自己决定是否运行;
    • 注意点一:线程处于阻塞状态,线程没有执行,所以没机会检查中断状态。(线程阻塞状态:由于某种原因线程暂停运行的状态,包括可中断阻塞和不可中断阻塞)
      1. 可中断阻塞:线程上调用了sleep()、wait()、join()导致。
      2. 不可中断阻塞:获取对象锁 导致的阻塞。
    • 注意点二:如果线程处于可中断阻塞状态,另一个线程对它提出中断请求,线程将抛出InterruptedException异常或ClosedByInterruptException异常,并跳出阻塞状态;如果线程处于不可中断状态,则对中断请求不会做出响应。
  • Object.wait():导致本线程等待,并释放本线程拥有的该对象的锁;只有本线程被别的线程调用nofity()或notifyAll()并抢到对象锁时,才重新回到运行态。
  • Object.notify() 和 Object.notifyAll():前者只通知本线程以外的其他等待此对象锁的线程中的一个,后者是则通知其他所有的等待线程并让随机一个线程获取对象锁。
  • Condition.await():【类似于:Object.wait()】让本线程等待。
  • Condition.signal()和signalAll():【类似于:Object.notify()】通知等待的线程,并结束等待线程的等待状态。
  1. 例子:线程的run()方法中对中断做出响应的正确写法:
while(还有工作没完成){
  if(Thread.currentThread().isInterrupted())
  {///如果中断状态被设为true(别人叫我中断了)
    ///响应中断请求。首先决定是否终止线程,如果要终止,需要完成必须完成的结束工作
    ///例如关闭资源占用等,然后退出run()方法
  }
  ///处理未完成工作
}
  1. 未捕获异常,用Thread.UncaughtExceptionHandler来解决。
  • 出现未捕获异常的情况:子线程的run()方法执行过程中抛出异常并终止,但是主线程无法获取到这个异常。【因为主线程与子线程是完全不同的两个指令序列】
  • 解决方法:
    Thread.setDefaultUncaughtExceptionHandler(new 自定义UncaughtExceptionHandler实现类);
    
  1. Lock的用法
  • Lock是接口,常用实现类是ReentrantLock。
  • 正确写法实例:
    class X{
      private ReentrantLock lock = new ReentrantLock();
      public void readOrWriteSharedData(){
        if(lock.tryLock()){///尝试获取对象锁
          try{
             ///对共享资源进行处理
          } finally{
             lock.unlock();
          }
        }
      }
    }
    
  • 如果一个锁被另一个线程保持,那么,处于线程调度目的, 阻塞当前线程,当前线程处于休眠状态,直到:
    1. 锁由当前线程获得
    2. 其他某个线程中断当前线程
    3. 已超过指定的等待时间
  1. 线程的六个状态
  2. 新生:new出了Thread对象,还没执行start()。
  3. 可运行:调用start()后的状态。【注意:此状态下,线程不一定被线程调度器加载到CPU上执行】
  4. 阻塞:受阻塞并正在等待锁的线程状态,没有CPU时间片。两种方法进入阻塞态:①线程进入synchronized方法或代码块(或调用Lock对象的lock()或tryLock()等方法),并试图获取其他线程已经占用的锁时;②退出等待状态、并试图重新获得在等待状态时拥有的锁但此锁已被其他线程占用时。
  5. 等待:此状态下的线程正在等待另一个线程,以执行特定操作。不占CPU时间片。
  6. 计时等待:具有指定等待时间的某一等待线程的线程状态。与等待状态相似,区别是:过了等待时间,也会退出等待状态。
  7. 终止:线程结束执行。进入此状态的两种可能:① run()方法执行完毕并返回; ② 在执行run()过程中抛出未处理异常。
  8. 示例: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

以上代码的功能于一下使用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();//转账完毕,可以唤醒其他需要转账的线程了
  }
  1. 同步器之---信号量(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;
    }
}
  1. 同步器之---倒计时门栓(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();
        }
    }
}
  1. 同步器之---障栅(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();
            }
        }
    }
}
  1. 同步器之--交换器(Exchanger)
     顾名思义, 就是进行线程间数据交换的。实现方法为:Exchanger.exchange(Object obj):Object

六、序列化

  • 注意点:
    1. 静态变量和所有方法都不必序列化
    2. 使用transient关键字,可以修饰不想被序列化的属性。
    3. 要实现序列化接口的类,都必须声明一个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接口
    • 优点:灵活,可部分序列化,可提高序列化效率
    • 缺点:较为复杂,需要重写方法
    • 重写方法:
      1. readExternal():自定义哪些属性需要反序列化
      2. 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:
      1. 采用建立树形接口的方式访问XML文档,直接解析整个文档,生成一棵树并对树遍历。
      2. 优点:JDK自带,编程容易,容易添加或修改树中的元素。
      3. 缺点:需要处理整个XML文档,对性能和内存要求比较高。
      4. DOM解析器常用于XML文档需要频繁改变的服务中。
    • SAX:
      1. 采用事件模型,找到对应的tag时,激活回调方法。
      2. 优点:JDK自带,对内容要求低,不需要整个文档处理,效率提高。
      3. 缺点:编码较复杂,难以同时访问同一个文档中的多处不同数据。
    • JDOM:
      1. 与DOM的区别:JDOM使用具体类,而不是接口,限制了灵活性。
      2. 需要导入jdom.jar包。
    • DOM4J:
      1. 特点:API更复杂,但提供更大的灵活性。
      2. 需要导入dom4.jar包。
  • 对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 freeCons = new ArrayList<>();
private List 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开发规范等,将后续更新,谢谢阅读~

你可能感兴趣的:(读书笔记--Java高级程序设计 之 Java后端入门准备)