美文网首页JAVA学习首页投稿(暂停使用,暂停投稿)Java学习笔记
读书笔记--Java高级程序设计 之 Java后端入门准备

读书笔记--Java高级程序设计 之 Java后端入门准备

作者: androidjp | 来源:发表于2016-11-19 10:41 被阅读448次

    主要是对一个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:【修饰注解,表明让子类继承该注解】表明注解是否会被子类继承,默认情况是不继承的。
    • 自定义注解
      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<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();//转账完毕,可以唤醒其他需要转账的线程了
      }
    
    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<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开发规范等,将后续更新,谢谢阅读~

    相关文章

      网友评论

        本文标题:读书笔记--Java高级程序设计 之 Java后端入门准备

        本文链接:https://www.haomeiwen.com/subject/agqfpttx.html