`

day25 注解技术 动态代理 servlet3.0 线程池

阅读更多

Java基础加强第一天 : JDJava基础加强第一天 : JDK5.0新特性(泛型、枚举、for/in、可变参数、自动装箱拆箱、静态导入) + 反射 API(Class、Construtctor 、Field、Method )
字符串格式化 StringFormat、System.out.printf
注解技术
线程并发库

Java基础加强第二天 :上午:注解技术使用 @override ----- Servlet3.0 新特性 (之前课程中2.5 特性) 、动态代理技术(方法增强三种方式)
下午:复习线程基础知识、多线程编写案例 、Java5 提供线程并发库 线程池 、Socket网络编程(自定义服务器案例 tomcat)

Java Annotation 注解技术 是java5.0 新特性 (如果编译环境是java1.4 之前版本 无法对注解进行编译)

JDK官方提供了三个注解
@Override: 限定重写父类方法, 该注解只能用于方法 ------ 编译时检查,不构成覆盖 报错
* JDK5.0 override注解只能用于方法覆盖 JDK6.0 该注解可以用于接口的方法实现
@Deprecated: 用于表示某个程序元素(类, 方法等)已过时 ----- 在编译器进行编译时 报出一个警告
* 为什么会有过时方法: 提供了新的方法取代 之前方法、发现某个方法存在安全隐患,不建议用户再使用
@SuppressWarnings: 抑制编译器警告.  ---- 通知编译器在编译时 不要报出警告信息
* 使用 all 忽略所有警告信息
@SuppressWarnings("all")
    // 抑制警告信息 类型
    public static void main(String[] args) {
        int a;

        List list = new ArrayList();

        Thread t = new Thread();
        t.start();
        t.stop();// 删除线,因为stop 方法已经过时

        show();
    }

    // 通过deprecated 使 show 方法过时
    @Deprecated
    public static void show() {

    }
}

interface I {
    void p();
}

class A implements I {
    @Override
    // 这是JDK6 特性
    public void p() {
    }
}

class B extends A {
    @Override
    // 使用override 通知编译器,该方法是一个方法覆盖
    // 如果该方法 不构成 方法覆盖,编译器就会报错
    public void p() {
    }
}
Annotation 其实就是代码里的特殊标记, 它用于替代配置文件!!
* 注解不但可以通知编译器,在运行时通知信息给 JVM虚拟机

注解典型应用:在一个类上面使用注解(配置信息),在程序中通过反射技术 获得配置注解信息 ---- 充当配置文件

已经有配置文件技术 xml、properties ,为什么还需要注解 ??
随着企业端软件应用越来越复杂,配置文件内容越来越多,导致上万行配置文件(配置文件过大,可读性会变得很差) ---- 反射案例:办晚会
* 引入注解目的:解决程序配置可读性问题 ,注解相当于代码中配置文件

注解程序开发流程
1、编写注解类
使用@interface 定义
所有注解类都默认继承 java.lang.annotation.Annotation 接口
注解支持类型:八种基本数据类型和String、Enum、Class、Annotation以及以上类型的数组)
如果注解属性提供默认值 ,使用注解是 不设置新的值
如果注解只有一个value 属性,使用注解 省略value=
public class AnnotationTest2 {

}

// 使用元注解修改 自定义注解
@interface MyAnnotation2 {
    String[] value();// 只有一个value 属性
}

// 这就是一个注解
@interface MyAnnotation {
    // 定义注解属性,和定义接口方法类似
    // 格式:返回值、属性名() 默认值
    int id(); // 这里id 是注解的 属性

    String name(); // 默认 required

    Class c() default MyAnnotation.class;

    // 这里Date 不属于 8种基本类型 String enum Class Annotation --- 报错
    // Date date();
}

@MyAnnotation(id = 0, name = "")
// 因为只有一个value 属性 省略 value=
@MyAnnotation2( { "aaa", "bbb" })
class MyAnnotationBean {

}

2、在一个类 应用注解类
3、通过反射技术获得注解信息
通过java.lang.reflect.AnnotatedElement 在程序运行时 获得注解信息

元注解:修饰注解的注解
@Retention 声明注解存活生命周期 --Source Class Runtime --- 开发中主要使用Runtime
@Target 声明注解可以修饰对象类型 ---- FIELD 、TYPE、METHOD
@Documented 注解可以被 生成API文档
@Inherited 注解在使用时,可以被子类继承
/**
 * 元注解的使用
 *
 * @author seawind
 *
 */
@Annotation1(name = "abc")
public class AnnotationTest3 {
    @Annotation1(name = "abc")
    int a;

    @Annotation1(name = "abc")
    public void p() {

    }
}

// 因为父类使用@Inherited 注解,所以子类自动继承该注解
// @Annotation1(name = "abc")
class AnnotationTest3_2 extends AnnotationTest3 {

}

// 使用@Retention 声明注解声明周期
@Retention(RetentionPolicy.RUNTIME)
// RetentionPolicy.RUNTIME 运行时可见
// RetentionPolicy.CLASS 在字节码阶段仍然可见 --- 运行时丢失
// RetentionPolicy.SOURCE 在源码阶段可见 ---- 给编译器使用
@Target(value = { ElementType.TYPE, ElementType.FIELD, ElementType.METHOD })
// Target修饰注解可以应用目标类型
// Type 该注解可以用于修饰一个类 、接口、枚举
// FIELD 该注解可以修饰成员变量
@Documented
// 该注解的信息 在AnnotationTest3 生成API文档 时 存在
@Inherited
// 如果A类使用该注解,B类继承A类,B类自动继承该注解
@interface Annotation1 {
    String name();
}


注解案例 : 在运行阶段,通过注解充当配置文件,利用反射技术获得注解中信息
银行转账 : 最大一次只能汇款XXX钱 (转账金额大小限制)

读取注解信息
步骤一:获得注解修饰反射对象 Class Field Method ---- 这些所有反射对象 都实现了 AnnotatedElement接口
步骤二:通过 AnnotatedElement 接口 boolean isAnnotationPresent(Class<? extends Annotation> annotationClass)  判断目标注解是否存在
步骤三:通过 AnnotatedElement 接口 getAnnotation(Class<T> annotationClass)  获得目标注解
@Retention(RetentionPolicy.RUNTIME)
// 运行可见
@Target(ElementType.METHOD)//必须得写这句
// 修饰类
public @interface BankProperties {
    double max();
}
/**
 * 银行账户
 *
 * @author seawind
 *
 */
public class BankAccount {

    // 账户余额
    private double amount;

    public BankAccount(double amount) {
        this.amount = amount;
    }

    @BankProperties(max = 5000)
    public void transfer(double money) throws SecurityException,
            NoSuchMethodException {
        if (money > amount) {
            throw new RuntimeException("余额不足!");
        }
        // 限制转账金额
        // 方式一 读取配置文件
        // String maxStr = ResourceBundle.getBundle("bank").getString("max");
        // double max = Double.parseDouble(maxStr);
        // if (money > max) {
        // throw new RuntimeException("一次转账最大允许 " + max + "金额,您的转账额度超出了最大额度!");
        // }

        // 方式二 使用注解充当配置文件
        // 读取注解中信息 AnnotatedElement
        // 获得注解修饰反射对象
        Class c = BankAccount.class;
        Method m = c.getMethod("transfer", double.class);
        // 判断目标注解是否存在
        boolean isExist = m.isAnnotationPresent(BankProperties.class);
        if (isExist) {// 注解存在
            // 获得目标注解 --- 返回值可以强制转换为目标注解
            BankProperties bankProperties = (BankProperties) m
                    .getAnnotation(BankProperties.class);
            double max = bankProperties.max();// 获得max 属性
            if (money > max) {
                throw new RuntimeException("一次转账最大允许 " + max
                        + "金额,您的转账额度超出了最大额度!");
            }
        }

        System.out.println("该账户转出金额 :" + money);
        amount -= money;
        System.out.println("余额:" + amount);
    }
}

Servlet3.0 新特性 ----- 对于web 开发很有用的 ---- JavaEE6 最新开发工具
1、web.xml 关于 Servlet 、Filter、Listener 通过注解进行配置
2、服务器异步处理机制
3、集成文件上传API

安装eclipse3.7 和 tomcat7.0
启动Eclipse 版本必须和 JDK版本 位数匹配
启动错误 eclipse.ini -Xmx512m 尝试 修改256 或者 128

在Eclipse集成tomcat 编写web工程
1、创建Dynamic web project --- 配置target runtime 运行环境 tomcat
* eclipse目录默认 没有WebRoot 目录 --- 有WebContent
2、eclipse和my eclipse发布方式不同
* myeclipse 将目录复制tomcat/webapps
* eclipse 内部有tomcat插件,将web目录复制插件目录/webapps

* 自动生成Servers工程目录  --- 保存tomcat启动需要配置文件

1、3.0 web 工程没有web.xml
@WebServlet("/hello")
@WebFilter("/hello")
@WebListener
* 当你配置欢迎页面、错误页面 编写web.xml
* metadata-complete  web-app元素的属性 设置为true 将不支持注解技
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
    metadata-complete="false"
    version="3.0"
    xmlns="http://java.sun.com/xml/ns/javaee"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" >
</web-app>
2、异步处理支持
运行服务器端对Response 用多个线程统计信息响应生成
* 作用 改善用户体验
@WebServlet(value="/hello2",asyncSupported=true)//必须写上asyncSupported=true否则报异常
public class HelloServlet2 extends HttpServlet {
    private static final long serialVersionUID = 1L;
      
    /**
     * @see HttpServlet#HttpServlet()
     */
    public HelloServlet2() {
        super();
        // TODO Auto-generated constructor stub
    }

    /**
     * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setContentType("text/html;charset=utf-8");
       
        response.getWriter().println("start servlet ...<br/>");
        // 模拟用户注册:将数据保存到数据库,发送激活邮件
        response.getWriter().println("regist success... <br/>");
        // 因为将用户数据保存到数据库后,发邮件是不是很复杂,很浪费时间
        // 启动一个单独线程发送邮件,先将响应会送给客户端,等发送邮件成功后,再提升用户激活邮件已经发送
        AsyncContext asyncContext = request.startAsync();
        asyncContext.addListener(new AsyncListener() {
           
            @Override
            public void onTimeout(AsyncEvent arg0) throws IOException {
                // TODO Auto-generated method stub
               
            }
           
            @Override
            public void onStartAsync(AsyncEvent arg0) throws IOException {
                // TODO Auto-generated method stub
               
            }
           
            @Override
            public void onError(AsyncEvent arg0) throws IOException {
                // TODO Auto-generated method stub
               
            }
           
            @Override
            public void onComplete(AsyncEvent arg0) throws IOException {
                System.out.println("servlet 完成");
            }
        });
        new Thread(new Exccutor(asyncContext)).start();
       
        response.getWriter().println("end servlet...<br/>");
        response.getWriter().flush();
    }

    /**
     * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // TODO Auto-generated method stub
    }

    // 异步发送邮件程序
public class Exccutor implements Runnable {

    private AsyncContext asyncContext;

    public Exccutor(AsyncContext asyncContext) {
        this.asyncContext = asyncContext;
    }

    @Override
    public void run() {
        // 模拟发送邮件
        try {
            Thread.sleep(5000);// 发邮件需要时间
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        try {
            asyncContext.getResponse().getWriter().print("active mail has been send!");
            asyncContext.getResponse().getWriter().flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
       
    }

}
3、内部提供一套API 完成文件上传
编写文件上传form 表单
在服务器端Servlet 中 @MultipartConfig
* getParameter位于getPart操作之前 --- 必须先处理普通form域,再处理上传域
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;


/**
 * Servlet implementation class UploadServlet
 */
@WebServlet("/upload")
@MultipartConfig//必须写
// 写注解 通知服务器form 是一个文件上传表单
public class UploadServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
      
    /**
     * @see HttpServlet#HttpServlet()
     */
    public UploadServlet() {
        super();
        // TODO Auto-generated constructor stub
    }

    /**
     * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        request.setCharacterEncoding("utf-8");
        // 文件上传逻辑
        // 获得用户名
        String username = request.getParameter("username");
        System.out.println(username);
        // 获得上传文件内容
        Part part = request.getPart("upload");
        // 获得上传文件名 解析请求头中 Content-Dispostion
        String filename = getFileName(part);
       
        // 将文件写入c盘
        part.write("c:\\"+filename);
       
       
    }
   
    public String getFileName(Part part) {
        String filename = part.getHeader("content-disposition");
        filename = filename.substring(filename.indexOf("filename=")+10,filename.length()-1);
        int index = filename.lastIndexOf("\\");
        if(index != -1){
            filename = filename.substring(index+1);
        }
        return filename;
    }
    /**
     * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

}


动态代理价值: 当.class文件 被类加载器加载 到内存 形成Class对象,所有程序访问都是针对Class对象 ,动态代理技术可以根据Class对象的实现接口,在内存中虚拟构造一个对象,该对象成为代理对象,访问真实对象的所有API的过程中 都将通过代理对象去访问 。
* 拦截对真实对象的访问 修改访问参数、拦截访问请求
* Java Spring 内部拦截器技术 -- 使用动态代理

动态代理案例
1、编写真实业务对象
2、使用动态代理,必须为真实对象提供一个接口
3、使用Proxy的newInstance 根据真实业务对象,创建代理对象
4. 根据代理对象取间接访问真实对象
5、拦截真实访问后,阻止对目标访问、修改参数、修改返回值
public class Liudehua implements Singable {
    public void sing(double money) {
        System.out.println("出场费:" + money);
        System.out.println("德华演唱歌曲");
    }

    @Override
    public void dance(double money) {
        System.out.println("出场费:" + money);
        System.out.println("德华跳舞了!");
    }
}
public class MainApp {
    public static void main(String[] args) {
        final Liudehua liudehua = new Liudehua();
        // 根据真实业务对象创建代理对象
        Singable proxy = (Singable) Proxy.newProxyInstance(liudehua.getClass()
                .getClassLoader(), liudehua.getClass().getInterfaces(),
                new InvocationHandler() {
                    @Override
                    /*
                     * proxy 代理对象本身,method 当前执行方法,args 方法真实参数
                     */
                    public Object invoke(Object proxy, Method method,
                            Object[] args) throws Throwable {
                        System.out.println("被代理了");
                        // 拦截后,修改参数 阻止
                        if (method.getName().equals("sing")) {
                            double money = (Double) args[0];
                            if (money < 5000) {
                                throw new RuntimeException("免谈");
                            }
                            money -= 20000; // 取走20000
                            method.invoke(liudehua, money);
                        }
                        if (method.getName().equals("dance")) {
                            double money = (Double) args[0];
                            if (money < 100000) {
                                throw new RuntimeException("免谈");
                            }
                            money -= 30000; // 取走30000
                            method.invoke(liudehua, money);
                        }
                        return null;
                    }
                });

        // 通过代理对象 访问 真实业务对象 --- 无论执行什么方法 invoke都将执行
        proxy.sing(50000); // invoke 执行 method --- sing args --- 50000
        proxy.dance(100000); // invoke 执行 method --- dance args ---- 100000
    }
}

银行取钱案例 ATM : 取款手续费用
真实业务对象如果想动态代理,生成代理对象 -------- 实现接口
* 在实际企业开发中,动态代理经常用于加强方法原来逻辑功能  ---- 无需修改原来程序逻辑,就可以实现方法增强
public interface Account {
    // 存钱
    public void saveMoney(double money);

    // 取钱
    public double getMoney(double money);

    // 查询账户余额
    public double queryRestMoney();
}
public class MyAccount implements Account {
    private double amount;

    @Override
    public double getMoney(double money) {
        if (money > amount) {
            // 余额不足
            throw new RuntimeException("余额不足!");
        }
        amount = amount - money;
        return money;
    }

    @Override
    public double queryRestMoney() {
        return amount;
    }

    @Override
    public void saveMoney(double money) {
        amount = amount + money;
    }

}
public class ATM {
    public static void main(String[] args) {
        final Account account = new MyAccount();// 账户对象

        // 使用动态代理,根据真实账户 生成代理对象
        // 代理对象返回类型 必须转换 接口类型
        Account accountProxy = (Account) Proxy.newProxyInstance(account
                .getClass().getClassLoader(), account.getClass()
                .getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args)
                    throws Throwable {
                // 取款时 收取1%手续费用
                // 需要加强的方法
                // 使用if 判断 该方法 逻辑需要加强
                if (method.getName().equals("getMoney")) {// 用户在取款
                    // 从账户中取出 101% 金额
                    // 获得要取款金额
                    double want_money = (Double) args[0];
                    // 从账户中 取出101% 金额
                    double real_money = want_money * 1.01;
                    // 进行取款
                    double getMoney = (Double) method.invoke(account,
                            real_money);
                    System.out.println("取款:" + want_money + ",收取手续费用 "
                            + (real_money - want_money));

                    return want_money;// 返回要取款金额

                }

                // 不需要加强的方法
                return method.invoke(account, args);
            }
        });

        accountProxy.saveMoney(10000);
        System.out.println("存款 10000");
        double money = accountProxy.getMoney(5000);
        System.out.println("取款 5000");
        double restMoney = accountProxy.queryRestMoney();
        System.out.println("查询余额:" + restMoney);
    }
}

线程和进程区别 ?一个进程是多个线程组成的,进程是操作系统管理内存最小单位,线程使用内存,从进程申请
线程的创建有两种方式:extends Thread、implements Runnable接口
* 获得当前线程的名字 Thread.currentThread().getName();
* 优先使用Runnable 接口 ---- 因为java单继承,如果继承Thread 就不能继承其他类

线程的四种状态:创建(new Thread().start())、执行状态 (线程run方法正在运行)、阻塞状态(run方法在执行一段时间后暂停执行)、线程死亡(run方法执行结束、发生异常)
* 新建 --- 运行 (获得cpu的使用权)
* 运行 --- 阻塞 (Thread.sleep join 使用同步锁 wait IO读写、网络传输)
* 阻塞 --- 运行  结束阻塞,重新获得cpu使用权

sleep 使当前线程睡眠一段时间,睡眠过程中,不释放锁资源
join 等待目标线程执行结束后,其他线程才能继续执行
同步锁,当运行一个代码块,使用同步锁机制,一个线程已经将代码块锁定,另一个线程无法进入代码块 ---- 执行等待锁
wait 当你获得一个同步锁后,选择在锁上面监视器进行等待,等待必须由别人进行 唤醒 notify  notifyAll

锁:保证一段程序同一时间只能由一个线程进行执行 ---- 阻止两个线程同时执行一段代码
* java中每个对象都可以作为锁 ----- 锁的本质,锁定一块内存地址
public class MyThread2 {
    public static void main(String[] args) {
        Object lock = new Object();

        ThreadA a = new ThreadA(lock);
        new Thread(a).start();

        ThreadB b = new ThreadB(lock);
        new Thread(b).start();

        // 保证顺序不乱 --- 加锁 锁定同一个内存
    }
}

class ThreadA implements Runnable {

    private Object lock;

    public ThreadA(Object lock) {
        this.lock = lock;
    }

    @Override
    public void run() {
        synchronized (lock) {
            for (int i = 0; i < 10; i++) {
                System.out.println(Thread.currentThread().getName() + ":" + i);
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

}

class ThreadB implements Runnable {

    private Object lock;

    public ThreadB(Object lock) {
        this.lock = lock;
    }

    @Override
    public void run() {
        synchronized (lock) {
            for (int i = 0; i < 20; i++) {
                System.out.println(Thread.currentThread().getName() + ":" + i);
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

}

* 特殊锁 锁定方法
public static synchronized void a() {} ---- 锁定 类的Class对象
public synchronized void b() {}  --- 锁定this 对象
同步锁案例 : 售票系统 模拟火车站多个售票窗口同时售票  ---- 确保同一个时间 只能有一个窗口打票
public class SaleTicketSystem {
    public static void main(String[] args) {
        TicketSystem system = new TicketSystem();// 票源唯一
        for (int i = 0; i < 5; i++) {// 模拟5个窗口同时卖票
            SaleTicket saleTicket = new SaleTicket(system);
            new Thread(saleTicket).start();
        }
    }
}

// 票源
class TicketSystem {
    private int totalTickets = 100; // 总票数
    private int hasSaleTickets = 0; // 已经售票编号

    // 添加同步后,同一时间只能有一个窗口卖票
    public synchronized void saleTicket() {
        if (totalTickets <= 0) {
            throw new RuntimeException("票已经卖完");
        }
        // 卖掉一张票
        hasSaleTickets++;
        System.out.println("打印一张票!" + hasSaleTickets);
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 总票数 -1
        totalTickets--;
        System.out.println("剩余票数:" + totalTickets);
    }

}
//售票
class SaleTicket implements Runnable {

    private TicketSystem ticketSystem;

    public SaleTicket(TicketSystem ticketSystem) {
        this.ticketSystem = ticketSystem;
    }

    @Override
    public void run() {
        while (true) {
            try {
                ticketSystem.saleTicket();
            } catch (RuntimeException e) {
                break;
            }
        }
    }



死锁原因:互相等待 ------ 同步代码块嵌套
* 尽量同步代码块不要嵌套
public class DeadLockTest {
    public static void main(String[] args) {
        Object pen = new Object();// 笔
        Object note = new Object(); // 本

        new Thread(new AThread(pen, note)).start();
        new Thread(new BThread(pen, note)).start();
    }
}

class AThread implements Runnable {

    private Object pen;
    private Object note;

    public AThread(Object pen, Object note) {
        this.pen = pen;
        this.note = note;
    }

    @Override
    public void run() {
        // 先获得笔 --- 再获得本
        synchronized (pen) {
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (note) {
                System.out.println("A 写字");
            }
        }
    }
}

class BThread implements Runnable {

    private Object pen;
    private Object note;

    public BThread(Object pen, Object note) {
        this.pen = pen;
        this.note = note;
    }

    @Override
    public void run() {
        // 先获得 本 --- 再获得笔
        synchronized (note) {
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (pen) {
                System.out.println("B 写字");
            }
        }
    }
}

锁监视器操作 ----- java中所有对象都可以作为锁,java 中 Object类 提供 wait notify notifyAll
wait :当前线程 在锁对象的监视器上进行等待
notify : 唤醒 一个 在 指定锁对象的监视器上等待的线程
notifyAll : 唤醒所有在指定 锁对象的监视器上等待的线程
* 线程通信案例 (当两个线程协同完成某个任务 )---- 生产者消费者模型

生产者消费者场景 :一个生产者、一个消费者,生产者生产一个面包,通知消费者来吃,消费者吃完了,通知生产者生产
* 生产者发现面包还没吃 需要等待
* 消费者发现面包已经吃了 需要等待
/**
 * 生产者 消费者案例
 *
 * @author seawind
 *
 */
public class ProducerConsumerTest {
    public static void main(String[] args) {
        CakeHouse cakeHouse = new CakeHouse();
        new Thread(new Producer(cakeHouse)).start();
        new Thread(new Consumer(cakeHouse)).start();
    }
}

// 生产者
class Producer implements Runnable {

    private CakeHouse cakeHouse;

    public Producer(CakeHouse cakeHouse) {
        this.cakeHouse = cakeHouse;
    }

    @Override
    public void run() {
        // 生产100个蛋糕
        for (int i = 0; i < 100; i++) {
            cakeHouse.put();
        }
    }

}

// 消费者
class Consumer implements Runnable {
    private CakeHouse cakeHouse;

    public Consumer(CakeHouse cakeHouse) {
        this.cakeHouse = cakeHouse;
    }

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            cakeHouse.take();
        }
    }

}

class CakeHouse {
    // 蛋糕房 蛋糕窗口
    private List<Object> list = new LinkedList<Object>();
    private int index;

    // 必须先同步,才能使用锁监视器
    public synchronized void put() {
        while (!list.isEmpty()) {
            // 已经有蛋糕
            try {
                this.wait(); // 调用wait的对象 就是锁定对象
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        // 生产蛋糕
        list.add(new Object());
        index++;
        System.out.println("生产者 生产蛋糕 " + index);

        // 通知消费者 快去吃
        this.notify();
    }

    public synchronized void take() {
        while (list.isEmpty()) {
            // 没蛋糕
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        // 吃掉蛋糕
        list.remove(0);
        System.out.println("消费者消费蛋糕 " + index);

        // 通知生产者生产
        this.notify();
    }
}

JDK5.0 之后提供两个接口 Lock 和 Condition ,简化同步和锁编程
java.util.concurrent.locks.Lock  ---- 取代 synchronized
java.util.concurrent.locks.Condition  ---- 取代 wait notify
* 在同一个锁上面 建立多个监视器
public class SaleTicketSystem {
    public static void main(String[] args) {
        TicketSystem system = new TicketSystem();// 票源唯一
        for (int i = 0; i < 5; i++) {// 模拟5个窗口同时卖票
            SaleTicket saleTicket = new SaleTicket(system);
            new Thread(saleTicket).start();
        }
    }
}

// 票源
class TicketSystem {
    private int totalTickets = 100; // 总票数
    private int hasSaleTickets = 0; // 已经售票编号
    private Lock lock = new ReentrantLock();

    // 添加同步后,同一时间只能有一个窗口卖票
    public void saleTicket() {
        // 加锁
        lock.lock();

        if (totalTickets <= 0) {
            throw new RuntimeException("票已经卖完");
        }
        // 卖掉一张票
        hasSaleTickets++;
        System.out.println("打印一张票!" + hasSaleTickets);
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 总票数 -1
        totalTickets--;
        System.out.println("剩余票数:" + totalTickets);

        // 解锁
        lock.unlock();
    }

}

class SaleTicket implements Runnable {

    private TicketSystem ticketSystem;

    public SaleTicket(TicketSystem ticketSystem) {
        this.ticketSystem = ticketSystem;
    }

    @Override
    public void run() {
        while (true) {
            try {
                ticketSystem.saleTicket();
            } catch (RuntimeException e) {
                break;
            }
        }
    }

}
/**
 * 生产者 消费者案例
 *
 * @author seawind
 *
 */
public class ProducerConsumerTest {
    public static void main(String[] args) {
        CakeHouse cakeHouse = new CakeHouse();
        new Thread(new Producer(cakeHouse)).start();
        new Thread(new Consumer(cakeHouse)).start();
    }
}

// 生产者
class Producer implements Runnable {

    private CakeHouse cakeHouse;

    public Producer(CakeHouse cakeHouse) {
        this.cakeHouse = cakeHouse;
    }

    @Override
    public void run() {
        // 生产100个蛋糕
        for (int i = 0; i < 100; i++) {
            cakeHouse.put();
        }
    }

}

// 消费者
class Consumer implements Runnable {
    private CakeHouse cakeHouse;

    public Consumer(CakeHouse cakeHouse) {
        this.cakeHouse = cakeHouse;
    }

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            cakeHouse.take();
        }
    }

}

class CakeHouse {
    // 蛋糕房 蛋糕窗口
    private List<Object> list = new LinkedList<Object>();
    private int index;
    private Lock lock = new ReentrantLock();
    // 生产者专用监视器
    private Condition producerCondition = lock.newCondition();
    // 消费者专用监视器
    private Condition consumerCondition = lock.newCondition();

    public void put() {
        lock.lock();
        while (!list.isEmpty()) {
            // 已经有蛋糕
            try {
                producerCondition.await(); // 在生产者监视器上等
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        // 生产蛋糕
        list.add(new Object());
        index++;
        System.out.println("生产者 生产蛋糕 " + index);

        // 通知消费者 快去吃
        consumerCondition.signal();

        lock.unlock();
    }

    public void take() {
        lock.lock();
        while (list.isEmpty()) {
            // 没蛋糕
            try {
                consumerCondition.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        // 吃掉蛋糕
        list.remove(0);
        System.out.println("消费者消费蛋糕 " + index);

        // 通知生产者生产
        producerCondition.signal();

        lock.unlock();
    }
}
java.util.concurrent 包 和 子包 ---- ArrayBlockingQueue<E> CopyOnWriteArrayList<E> LinkedBlockingQueue<E> 

线程池技术 Excutors
为什么用线程池,线程创建、关闭释放资源 ---- 消耗程序性能
一次创建多个线程,做任务,随机获得一个线程,完成任务,将线程归还线程池

newFixedThreadPool(int nThreads) 固定线程数量线程池
newCachedThreadPool()  返回根据程序需要自动扩充大小 线程池 ----- 最常用
newSingleThreadExecutor()  返回单线程处理程序

shutdown与shutdownNow的比较
shutdown 完成当前线程池中所有任务 再关闭
shutdownNow 会对当前线程池中所有线程,调用interrupt 尝试打断当前线程,如果无法打断,会执行结束
public class ExcutorsTest {
    // java中编写程序测试多线程,一定不能用 junit --- System.exit
    public static void main(String[] args) {
        // 创建线程池
        // 创建固定数量为3 线程池
        // ExecutorService service = Executors.newFixedThreadPool(5);
        // 创建 自增扩充大小 线程池
        ExecutorService service = Executors.newCachedThreadPool();

        // 单一线程对象
        // ExecutorService service = Executors.newSingleThreadExecutor();

        // 开启十个线程 分别输出
        for (int i = 0; i < 10; i++) {
            service.execute(new MyExcutor());
        }

        // 关闭连接池
        service.shutdown();// 当之前任务都结束后,尝试关闭连接池
        // service.shutdownNow();// 尝试马上关闭连接池,但是不一定能关闭
        // interrupt
    }
}

class MyExcutor implements Runnable {

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName() + ":" + i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

}
------------------------------------------------------------------------------------------------------------------
OSI 七层体系结构: 应用层、表示层、会话层、传输层、网络层、数据链路层、物理层 
*协议层次越高,越容易让人理解, 层次越低,数据越接近0 1
TCP/IP 四层 : 应用层、传输层、网际层、网络接口层

应用层、传输层
应用层:HTTP SMTP POP3 FTP TELNET
传输层:TCP(不允许丢包 三次握手) UDP(广播 允许丢包)

三次握手:
A 向 B 发送一个消息:能听到我说话吗
B 回复 A 消息:我能听到,你能听到我说话吗
A 回复 B :我能听到

TCP发送数据没有收到对方回应,选择重新发送 ---- 时间限制 超时

Socket 两台计算机之间一个连接 ---- 两台计算机可以通信

使用socket建立双方连接 -----传输协议需要自己编写 面向底层协议

socket 编写程序 可以模拟服务器,可以模拟客户端
服务器如何编写

* 使用socket进行通信过程中,如果调用in.readLine 但是对方没有写 ---- 程序一直等待 、out.print向对方发送信息 没有阻塞
/**
 * 模拟服务器
 *
 * @author seawind
 *
 */
public class Server {
    public static void main(String[] args) throws IOException {
        // 步骤一 创建ServerSocket 对象
        ServerSocket serverSocket = new ServerSocket(9000);
        // 连接服务器需要 ip 和 端口

        // 步骤二 等待客户端来连接
        Socket socket = serverSocket.accept(); // socket代表一个连接

        // 步骤三 需要获得流
        PrintWriter out = new PrintWriter(socket.getOutputStream());
        BufferedReader in = new BufferedReader(new InputStreamReader(socket
                .getInputStream()));

        // 收取对方信息 用 in
        // 发送给对方信息 用 out

        out.println("Hello !");// 输出信息 因为用字符流 ,所以flush
        out.flush();

        System.out.println(in.readLine());
    }
}
/**
 * 客户端
 *
 * @author seawind
 *
 */
public class Client {
    public static void main(String[] args) throws UnknownHostException,
            IOException {
        // 步骤一 连接服务器
        Socket socket = new Socket("localhost", 9000);

        // 步骤二 获得流
        PrintWriter out = new PrintWriter(socket.getOutputStream());
        BufferedReader in = new BufferedReader(new InputStreamReader(socket
                .getInputStream()));

        // 通过in 获得信息
        System.out.println(in.readLine());

        out.println("你好服务器!");
        out.flush();
    }
}


socket 案例 ----- 模拟tomcat服务器

/**
 * 模拟tomcat server
 *
 * @author seawind
 *
 */
public class TomcatServer {
    public static void main(String[] args) throws IOException {
        // 步骤一
        ServerSocket serverSocket = new ServerSocket(9000);

        // 创建线程池
        ExecutorService executorService = Executors.newCachedThreadPool();

        while (true) {
            // 步骤二 获得与客户端连接
            Socket socket = serverSocket.accept();
            DealResponse dealResponse = new DealResponse(socket);
            // 将处理任务 加入线程池
            executorService.execute(dealResponse);

        }
    }

    static class DealResponse implements Runnable {
        private Socket socket;

        public DealResponse(Socket socket) {
            this.socket = socket;
        }

        @Override
        public void run() {
            try {
                // 步骤三 流
                OutputStream bout = new BufferedOutputStream(socket
                        .getOutputStream());
                // PrintWriter out = new PrintWriter(socket.getOutputStream());
                BufferedReader in = new BufferedReader(new InputStreamReader(
                        socket.getInputStream()));

                // 浏览器连接服务器 发送HTTP请求
                String requestline = in.readLine();// 获得请求行

                // 解析资源路径
                String path = parse(requestline);
                System.out.println(path);

                // 判断目标文件是否存在
                File file = new File("webroot" + path);
                // 判断服务器上面 有没有客户访问文件
                if (file.exists()) {
                    // 回写HTTP协议格式
                    bout.write("HTTP/1.1 200 OK\r\n".getBytes());

                    // 根据文件扩展名得到文件格式

                    bout
                            .write(("Content-Type:" + getType(file.getName()) + "\r\n")
                                    .getBytes()); // 不同文件类型是不同的
                    bout.write(("Content-Length:" + file.length() + "\r\n\r\n")
                            .getBytes());

                    // 存在
                    InputStream fis = new BufferedInputStream(
                            new FileInputStream(file));
                    int temp;
                    while ((temp = fis.read()) != -1) {
                        bout.write(temp);
                    }
                    bout.flush();
                    bout.close();
                    fis.close();
                    System.out.println("文件找到了");
                } else {
                    // 不存在
                    bout.write("file not found".getBytes());
                    bout.flush();
                    System.out.println("文件找不到!");
                    bout.close();
                }

                // 断开socket连接
                in.close();
                socket.close();
            } catch (FileNotFoundException e) {
                // e.printStackTrace();
            } catch (IOException e) {
                // e.printStackTrace();
            }
        }
    }

    private static String getType(String name) {
        // 获得扩展名
        String ext = name.substring(name.lastIndexOf(".") + 1);
        String type = ResourceBundle.getBundle("mime").getString(ext);
        return type;
    }

    private static String parse(String requestline) {
        return requestline.split(" ")[1];
    }

}

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

注解的使用 ---- 定义注解,利用反射解析注解内容 (案例:银行转账最大金额)
Servlet3.0 注解开发Servlet、异步技术、文件上传
动态代理 (案例:德华案例、银行取款 收取手续费用)
多线程:两种线程创建方式、四种状态、同步和锁、死锁、wait和notify (案例:卖票、生产蛋糕) ---- 使用JDK5 Lock和Condition 重写

socket编程 服务器怎么写 客户端怎么写
* 实际socket案例 按照协议发送内容 模拟tomcat 服务器 遵循HTTP协议








































































































K5.0新特性(泛型、枚举、for/in、可变参数、自动装箱拆箱、静态导入) + 反射 API(Class、Construtctor 、Field、Method )
字符串格式化 StringFormat、System.out.printf
注解技术
线程并发库

Java基础加强第二天 :上午:注解技术使用 @override ----- Servlet3.0 新特性 (之前课程中2.5 特性) 、动态代理技术(方法增强三种方式)
下午:复习线程基础知识、多线程编写案例 、Java5 提供线程并发库 线程池 、Socket网络编程(自定义服务器案例 tomcat)

Java Annotation 注解技术 是java5.0 新特性 (如果编译环境是java1.4 之前版本 无法对注解进行编译)

JDK官方提供了三个注解
@Override: 限定重写父类方法, 该注解只能用于方法 ------ 编译时检查,不构成覆盖 报错
* JDK5.0 override注解只能用于方法覆盖 JDK6.0 该注解可以用于接口的方法实现
@Deprecated: 用于表示某个程序元素(类, 方法等)已过时 ----- 在编译器进行编译时 报出一个警告
* 为什么会有过时方法: 提供了新的方法取代 之前方法、发现某个方法存在安全隐患,不建议用户再使用
@SuppressWarnings: 抑制编译器警告.  ---- 通知编译器在编译时 不要报出警告信息
* 使用 all 忽略所有警告信息
@SuppressWarnings("all")
    // 抑制警告信息 类型
    public static void main(String[] args) {
        int a;

        List list = new ArrayList();

        Thread t = new Thread();
        t.start();
        t.stop();// 删除线,因为stop 方法已经过时

        show();
    }

    // 通过deprecated 使 show 方法过时
    @Deprecated
    public static void show() {

    }
}

interface I {
    void p();
}

class A implements I {
    @Override
    // 这是JDK6 特性
    public void p() {
    }
}

class B extends A {
    @Override
    // 使用override 通知编译器,该方法是一个方法覆盖
    // 如果该方法 不构成 方法覆盖,编译器就会报错
    public void p() {
    }
}
Annotation 其实就是代码里的特殊标记, 它用于替代配置文件!!
* 注解不但可以通知编译器,在运行时通知信息给 JVM虚拟机

注解典型应用:在一个类上面使用注解(配置信息),在程序中通过反射技术 获得配置注解信息 ---- 充当配置文件

已经有配置文件技术 xml、properties ,为什么还需要注解 ??
随着企业端软件应用越来越复杂,配置文件内容越来越多,导致上万行配置文件(配置文件过大,可读性会变得很差) ---- 反射案例:办晚会
* 引入注解目的:解决程序配置可读性问题 ,注解相当于代码中配置文件

注解程序开发流程
1、编写注解类
使用@interface 定义
所有注解类都默认继承 java.lang.annotation.Annotation 接口
注解支持类型:八种基本数据类型和String、Enum、Class、Annotation以及以上类型的数组)
如果注解属性提供默认值 ,使用注解是 不设置新的值
如果注解只有一个value 属性,使用注解 省略value=
public class AnnotationTest2 {

}

// 使用元注解修改 自定义注解
@interface MyAnnotation2 {
    String[] value();// 只有一个value 属性
}

// 这就是一个注解
@interface MyAnnotation {
    // 定义注解属性,和定义接口方法类似
    // 格式:返回值、属性名() 默认值
    int id(); // 这里id 是注解的 属性

    String name(); // 默认 required

    Class c() default MyAnnotation.class;

    // 这里Date 不属于 8种基本类型 String enum Class Annotation --- 报错
    // Date date();
}

@MyAnnotation(id = 0, name = "")
// 因为只有一个value 属性 省略 value=
@MyAnnotation2( { "aaa", "bbb" })
class MyAnnotationBean {

}

2、在一个类 应用注解类
3、通过反射技术获得注解信息
通过java.lang.reflect.AnnotatedElement 在程序运行时 获得注解信息

元注解:修饰注解的注解
@Retention 声明注解存活生命周期 --Source Class Runtime --- 开发中主要使用Runtime
@Target 声明注解可以修饰对象类型 ---- FIELD 、TYPE、METHOD
@Documented 注解可以被 生成API文档
@Inherited 注解在使用时,可以被子类继承
/**
 * 元注解的使用
 *
 * @author seawind
 *
 */
@Annotation1(name = "abc")
public class AnnotationTest3 {
    @Annotation1(name = "abc")
    int a;

    @Annotation1(name = "abc")
    public void p() {

    }
}

// 因为父类使用@Inherited 注解,所以子类自动继承该注解
// @Annotation1(name = "abc")
class AnnotationTest3_2 extends AnnotationTest3 {

}

// 使用@Retention 声明注解声明周期
@Retention(RetentionPolicy.RUNTIME)
// RetentionPolicy.RUNTIME 运行时可见
// RetentionPolicy.CLASS 在字节码阶段仍然可见 --- 运行时丢失
// RetentionPolicy.SOURCE 在源码阶段可见 ---- 给编译器使用
@Target(value = { ElementType.TYPE, ElementType.FIELD, ElementType.METHOD })
// Target修饰注解可以应用目标类型
// Type 该注解可以用于修饰一个类 、接口、枚举
// FIELD 该注解可以修饰成员变量
@Documented
// 该注解的信息 在AnnotationTest3 生成API文档 时 存在
@Inherited
// 如果A类使用该注解,B类继承A类,B类自动继承该注解
@interface Annotation1 {
    String name();
}


注解案例 : 在运行阶段,通过注解充当配置文件,利用反射技术获得注解中信息
银行转账 : 最大一次只能汇款XXX钱 (转账金额大小限制)

读取注解信息
步骤一:获得注解修饰反射对象 Class Field Method ---- 这些所有反射对象 都实现了 AnnotatedElement接口
步骤二:通过 AnnotatedElement 接口 boolean isAnnotationPresent(Class<? extends Annotation> annotationClass)  判断目标注解是否存在
步骤三:通过 AnnotatedElement 接口 getAnnotation(Class<T> annotationClass)  获得目标注解
@Retention(RetentionPolicy.RUNTIME)
// 运行可见
@Target(ElementType.METHOD)//必须得写这句
// 修饰类
public @interface BankProperties {
    double max();
}
/**
 * 银行账户
 *
 * @author seawind
 *
 */
public class BankAccount {

    // 账户余额
    private double amount;

    public BankAccount(double amount) {
        this.amount = amount;
    }

    @BankProperties(max = 5000)
    public void transfer(double money) throws SecurityException,
            NoSuchMethodException {
        if (money > amount) {
            throw new RuntimeException("余额不足!");
        }
        // 限制转账金额
        // 方式一 读取配置文件
        // String maxStr = ResourceBundle.getBundle("bank").getString("max");
        // double max = Double.parseDouble(maxStr);
        // if (money > max) {
        // throw new RuntimeException("一次转账最大允许 " + max + "金额,您的转账额度超出了最大额度!");
        // }

        // 方式二 使用注解充当配置文件
        // 读取注解中信息 AnnotatedElement
        // 获得注解修饰反射对象
        Class c = BankAccount.class;
        Method m = c.getMethod("transfer", double.class);
        // 判断目标注解是否存在
        boolean isExist = m.isAnnotationPresent(BankProperties.class);
        if (isExist) {// 注解存在
            // 获得目标注解 --- 返回值可以强制转换为目标注解
            BankProperties bankProperties = (BankProperties) m
                    .getAnnotation(BankProperties.class);
            double max = bankProperties.max();// 获得max 属性
            if (money > max) {
                throw new RuntimeException("一次转账最大允许 " + max
                        + "金额,您的转账额度超出了最大额度!");
            }
        }

        System.out.println("该账户转出金额 :" + money);
        amount -= money;
        System.out.println("余额:" + amount);
    }
}

Servlet3.0 新特性 ----- 对于web 开发很有用的 ---- JavaEE6 最新开发工具
1、web.xml 关于 Servlet 、Filter、Listener 通过注解进行配置
2、服务器异步处理机制
3、集成文件上传API

安装eclipse3.7 和 tomcat7.0
启动Eclipse 版本必须和 JDK版本 位数匹配
启动错误 eclipse.ini -Xmx512m 尝试 修改256 或者 128

在Eclipse集成tomcat 编写web工程
1、创建Dynamic web project --- 配置target runtime 运行环境 tomcat
* eclipse目录默认 没有WebRoot 目录 --- 有WebContent
2、eclipse和my eclipse发布方式不同
* myeclipse 将目录复制tomcat/webapps
* eclipse 内部有tomcat插件,将web目录复制插件目录/webapps

* 自动生成Servers工程目录  --- 保存tomcat启动需要配置文件

1、3.0 web 工程没有web.xml
@WebServlet("/hello")
@WebFilter("/hello")
@WebListener
* 当你配置欢迎页面、错误页面 编写web.xml
* metadata-complete  web-app元素的属性 设置为true 将不支持注解技
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
    metadata-complete="false"
    version="3.0"
    xmlns="http://java.sun.com/xml/ns/javaee"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" >
</web-app>
2、异步处理支持
运行服务器端对Response 用多个线程统计信息响应生成
* 作用 改善用户体验
@WebServlet(value="/hello2",asyncSupported=true)//必须写上asyncSupported=true否则报异常
public class HelloServlet2 extends HttpServlet {
    private static final long serialVersionUID = 1L;
      
    /**
     * @see HttpServlet#HttpServlet()
     */
    public HelloServlet2() {
        super();
        // TODO Auto-generated constructor stub
    }

    /**
     * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setContentType("text/html;charset=utf-8");
       
        response.getWriter().println("start servlet ...<br/>");
        // 模拟用户注册:将数据保存到数据库,发送激活邮件
        response.getWriter().println("regist success... <br/>");
        // 因为将用户数据保存到数据库后,发邮件是不是很复杂,很浪费时间
        // 启动一个单独线程发送邮件,先将响应会送给客户端,等发送邮件成功后,再提升用户激活邮件已经发送
        AsyncContext asyncContext = request.startAsync();
        asyncContext.addListener(new AsyncListener() {
           
            @Override
            public void onTimeout(AsyncEvent arg0) throws IOException {
                // TODO Auto-generated method stub
               
            }
           
            @Override
            public void onStartAsync(AsyncEvent arg0) throws IOException {
                // TODO Auto-generated method stub
               
            }
           
            @Override
            public void onError(AsyncEvent arg0) throws IOException {
                // TODO Auto-generated method stub
               
            }
           
            @Override
            public void onComplete(AsyncEvent arg0) throws IOException {
                System.out.println("servlet 完成");
            }
        });
        new Thread(new Exccutor(asyncContext)).start();
       
        response.getWriter().println("end servlet...<br/>");
        response.getWriter().flush();
    }

    /**
     * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // TODO Auto-generated method stub
    }

    // 异步发送邮件程序
public class Exccutor implements Runnable {

    private AsyncContext asyncContext;

    public Exccutor(AsyncContext asyncContext) {
        this.asyncContext = asyncContext;
    }

    @Override
    public void run() {
        // 模拟发送邮件
        try {
            Thread.sleep(5000);// 发邮件需要时间
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        try {
            asyncContext.getResponse().getWriter().print("active mail has been send!");
            asyncContext.getResponse().getWriter().flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
       
    }

}
3、内部提供一套API 完成文件上传
编写文件上传form 表单
在服务器端Servlet 中 @MultipartConfig
* getParameter位于getPart操作之前 --- 必须先处理普通form域,再处理上传域
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;


/**
 * Servlet implementation class UploadServlet
 */
@WebServlet("/upload")
@MultipartConfig//必须写
// 写注解 通知服务器form 是一个文件上传表单
public class UploadServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
      
    /**
     * @see HttpServlet#HttpServlet()
     */
    public UploadServlet() {
        super();
        // TODO Auto-generated constructor stub
    }

    /**
     * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        request.setCharacterEncoding("utf-8");
        // 文件上传逻辑
        // 获得用户名
        String username = request.getParameter("username");
        System.out.println(username);
        // 获得上传文件内容
        Part part = request.getPart("upload");
        // 获得上传文件名 解析请求头中 Content-Dispostion
        String filename = getFileName(part);
       
        // 将文件写入c盘
        part.write("c:\\"+filename);
       
       
    }
   
    public String getFileName(Part part) {
        String filename = part.getHeader("content-disposition");
        filename = filename.substring(filename.indexOf("filename=")+10,filename.length()-1);
        int index = filename.lastIndexOf("\\");
        if(index != -1){
            filename = filename.substring(index+1);
        }
        return filename;
    }
    /**
     * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

}


动态代理价值: 当.class文件 被类加载器加载 到内存 形成Class对象,所有程序访问都是针对Class对象 ,动态代理技术可以根据Class对象的实现接口,在内存中虚拟构造一个对象,该对象成为代理对象,访问真实对象的所有API的过程中 都将通过代理对象去访问 。
* 拦截对真实对象的访问 修改访问参数、拦截访问请求
* Java Spring 内部拦截器技术 -- 使用动态代理

动态代理案例
1、编写真实业务对象
2、使用动态代理,必须为真实对象提供一个接口
3、使用Proxy的newInstance 根据真实业务对象,创建代理对象
4. 根据代理对象取间接访问真实对象
5、拦截真实访问后,阻止对目标访问、修改参数、修改返回值
public class Liudehua implements Singable {
    public void sing(double money) {
        System.out.println("出场费:" + money);
        System.out.println("德华演唱歌曲");
    }

    @Override
    public void dance(double money) {
        System.out.println("出场费:" + money);
        System.out.println("德华跳舞了!");
    }
}
public class MainApp {
    public static void main(String[] args) {
        final Liudehua liudehua = new Liudehua();
        // 根据真实业务对象创建代理对象
        Singable proxy = (Singable) Proxy.newProxyInstance(liudehua.getClass()
                .getClassLoader(), liudehua.getClass().getInterfaces(),
                new InvocationHandler() {
                    @Override
                    /*
                     * proxy 代理对象本身,method 当前执行方法,args 方法真实参数
                     */
                    public Object invoke(Object proxy, Method method,
                            Object[] args) throws Throwable {
                        System.out.println("被代理了");
                        // 拦截后,修改参数 阻止
                        if (method.getName().equals("sing")) {
                            double money = (Double) args[0];
                            if (money < 5000) {
                                throw new RuntimeException("免谈");
                            }
                            money -= 20000; // 取走20000
                            method.invoke(liudehua, money);
                        }
                        if (method.getName().equals("dance")) {
                            double money = (Double) args[0];
                            if (money < 100000) {
                                throw new RuntimeException("免谈");
                            }
                            money -= 30000; // 取走30000
                            method.invoke(liudehua, money);
                        }
                        return null;
                    }
                });

        // 通过代理对象 访问 真实业务对象 --- 无论执行什么方法 invoke都将执行
        proxy.sing(50000); // invoke 执行 method --- sing args --- 50000
        proxy.dance(100000); // invoke 执行 method --- dance args ---- 100000
    }
}

银行取钱案例 ATM : 取款手续费用
真实业务对象如果想动态代理,生成代理对象 -------- 实现接口
* 在实际企业开发中,动态代理经常用于加强方法原来逻辑功能  ---- 无需修改原来程序逻辑,就可以实现方法增强
public interface Account {
    // 存钱
    public void saveMoney(double money);

    // 取钱
    public double getMoney(double money);

    // 查询账户余额
    public double queryRestMoney();
}
public class MyAccount implements Account {
    private double amount;

    @Override
    public double getMoney(double money) {
        if (money > amount) {
            // 余额不足
            throw new RuntimeException("余额不足!");
        }
        amount = amount - money;
        return money;
    }

    @Override
    public double queryRestMoney() {
        return amount;
    }

    @Override
    public void saveMoney(double money) {
        amount = amount + money;
    }

}
public class ATM {
    public static void main(String[] args) {
        final Account account = new MyAccount();// 账户对象

        // 使用动态代理,根据真实账户 生成代理对象
        // 代理对象返回类型 必须转换 接口类型
        Account accountProxy = (Account) Proxy.newProxyInstance(account
                .getClass().getClassLoader(), account.getClass()
                .getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args)
                    throws Throwable {
                // 取款时 收取1%手续费用
                // 需要加强的方法
                // 使用if 判断 该方法 逻辑需要加强
                if (method.getName().equals("getMoney")) {// 用户在取款
                    // 从账户中取出 101% 金额
                    // 获得要取款金额
                    double want_money = (Double) args[0];
                    // 从账户中 取出101% 金额
                    double real_money = want_money * 1.01;
                    // 进行取款
                    double getMoney = (Double) method.invoke(account,
                            real_money);
                    System.out.println("取款:" + want_money + ",收取手续费用 "
                            + (real_money - want_money));

                    return want_money;// 返回要取款金额

                }

                // 不需要加强的方法
                return method.invoke(account, args);
            }
        });

        accountProxy.saveMoney(10000);
        System.out.println("存款 10000");
        double money = accountProxy.getMoney(5000);
        System.out.println("取款 5000");
        double restMoney = accountProxy.queryRestMoney();
        System.out.println("查询余额:" + restMoney);
    }
}

线程和进程区别 ?一个进程是多个线程组成的,进程是操作系统管理内存最小单位,线程使用内存,从进程申请
线程的创建有两种方式:extends Thread、implements Runnable接口
* 获得当前线程的名字 Thread.currentThread().getName();
* 优先使用Runnable 接口 ---- 因为java单继承,如果继承Thread 就不能继承其他类

线程的四种状态:创建(new Thread().start())、执行状态 (线程run方法正在运行)、阻塞状态(run方法在执行一段时间后暂停执行)、线程死亡(run方法执行结束、发生异常)
* 新建 --- 运行 (获得cpu的使用权)
* 运行 --- 阻塞 (Thread.sleep join 使用同步锁 wait IO读写、网络传输)
* 阻塞 --- 运行  结束阻塞,重新获得cpu使用权

sleep 使当前线程睡眠一段时间,睡眠过程中,不释放锁资源
join 等待目标线程执行结束后,其他线程才能继续执行
同步锁,当运行一个代码块,使用同步锁机制,一个线程已经将代码块锁定,另一个线程无法进入代码块 ---- 执行等待锁
wait 当你获得一个同步锁后,选择在锁上面监视器进行等待,等待必须由别人进行 唤醒 notify  notifyAll

锁:保证一段程序同一时间只能由一个线程进行执行 ---- 阻止两个线程同时执行一段代码
* java中每个对象都可以作为锁 ----- 锁的本质,锁定一块内存地址
public class MyThread2 {
    public static void main(String[] args) {
        Object lock = new Object();

        ThreadA a = new ThreadA(lock);
        new Thread(a).start();

        ThreadB b = new ThreadB(lock);
        new Thread(b).start();

        // 保证顺序不乱 --- 加锁 锁定同一个内存
    }
}

class ThreadA implements Runnable {

    private Object lock;

    public ThreadA(Object lock) {
        this.lock = lock;
    }

    @Override
    public void run() {
        synchronized (lock) {
            for (int i = 0; i < 10; i++) {
                System.out.println(Thread.currentThread().getName() + ":" + i);
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

}

class ThreadB implements Runnable {

    private Object lock;

    public ThreadB(Object lock) {
        this.lock = lock;
    }

    @Override
    public void run() {
        synchronized (lock) {
            for (int i = 0; i < 20; i++) {
                System.out.println(Thread.currentThread().getName() + ":" + i);
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

}

* 特殊锁 锁定方法
public static synchronized void a() {} ---- 锁定 类的Class对象
public synchronized void b() {}  --- 锁定this 对象
同步锁案例 : 售票系统 模拟火车站多个售票窗口同时售票  ---- 确保同一个时间 只能有一个窗口打票
public class SaleTicketSystem {
    public static void main(String[] args) {
        TicketSystem system = new TicketSystem();// 票源唯一
        for (int i = 0; i < 5; i++) {// 模拟5个窗口同时卖票
            SaleTicket saleTicket = new SaleTicket(system);
            new Thread(saleTicket).start();
        }
    }
}

// 票源
class TicketSystem {
    private int totalTickets = 100; // 总票数
    private int hasSaleTickets = 0; // 已经售票编号

    // 添加同步后,同一时间只能有一个窗口卖票
    public synchronized void saleTicket() {
        if (totalTickets <= 0) {
            throw new RuntimeException("票已经卖完");
        }
        // 卖掉一张票
        hasSaleTickets++;
        System.out.println("打印一张票!" + hasSaleTickets);
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 总票数 -1
        totalTickets--;
        System.out.println("剩余票数:" + totalTickets);
    }

}
//售票
class SaleTicket implements Runnable {

    private TicketSystem ticketSystem;

    public SaleTicket(TicketSystem ticketSystem) {
        this.ticketSystem = ticketSystem;
    }

    @Override
    public void run() {
        while (true) {
            try {
                ticketSystem.saleTicket();
            } catch (RuntimeException e) {
                break;
            }
        }
    }



死锁原因:互相等待 ------ 同步代码块嵌套
* 尽量同步代码块不要嵌套
public class DeadLockTest {
    public static void main(String[] args) {
        Object pen = new Object();// 笔
        Object note = new Object(); // 本

        new Thread(new AThread(pen, note)).start();
        new Thread(new BThread(pen, note)).start();
    }
}

class AThread implements Runnable {

    private Object pen;
    private Object note;

    public AThread(Object pen, Object note) {
        this.pen = pen;
        this.note = note;
    }

    @Override
    public void run() {
        // 先获得笔 --- 再获得本
        synchronized (pen) {
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (note) {
                System.out.println("A 写字");
            }
        }
    }
}

class BThread implements Runnable {

    private Object pen;
    private Object note;

    public BThread(Object pen, Object note) {
        this.pen = pen;
        this.note = note;
    }

    @Override
    public void run() {
        // 先获得 本 --- 再获得笔
        synchronized (note) {
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (pen) {
                System.out.println("B 写字");
            }
        }
    }
}

锁监视器操作 ----- java中所有对象都可以作为锁,java 中 Object类 提供 wait notify notifyAll
wait :当前线程 在锁对象的监视器上进行等待
notify : 唤醒 一个 在 指定锁对象的监视器上等待的线程
notifyAll : 唤醒所有在指定 锁对象的监视器上等待的线程
* 线程通信案例 (当两个线程协同完成某个任务 )---- 生产者消费者模型

生产者消费者场景 :一个生产者、一个消费者,生产者生产一个面包,通知消费者来吃,消费者吃完了,通知生产者生产
* 生产者发现面包还没吃 需要等待
* 消费者发现面包已经吃了 需要等待
/**
 * 生产者 消费者案例
 *
 * @author seawind
 *
 */
public class ProducerConsumerTest {
    public static void main(String[] args) {
        CakeHouse cakeHouse = new CakeHouse();
        new Thread(new Producer(cakeHouse)).start();
        new Thread(new Consumer(cakeHouse)).start();
    }
}

// 生产者
class Producer implements Runnable {

    private CakeHouse cakeHouse;

    public Producer(CakeHouse cakeHouse) {
        this.cakeHouse = cakeHouse;
    }

    @Override
    public void run() {
        // 生产100个蛋糕
        for (int i = 0; i < 100; i++) {
            cakeHouse.put();
        }
    }

}

// 消费者
class Consumer implements Runnable {
    private CakeHouse cakeHouse;

    public Consumer(CakeHouse cakeHouse) {
        this.cakeHouse = cakeHouse;
    }

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            cakeHouse.take();
        }
    }

}

class CakeHouse {
    // 蛋糕房 蛋糕窗口
    private List<Object> list = new LinkedList<Object>();
    private int index;

    // 必须先同步,才能使用锁监视器
    public synchronized void put() {
        while (!list.isEmpty()) {
            // 已经有蛋糕
            try {
                this.wait(); // 调用wait的对象 就是锁定对象
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        // 生产蛋糕
        list.add(new Object());
        index++;
        System.out.println("生产者 生产蛋糕 " + index);

        // 通知消费者 快去吃
        this.notify();
    }

    public synchronized void take() {
        while (list.isEmpty()) {
            // 没蛋糕
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        // 吃掉蛋糕
        list.remove(0);
        System.out.println("消费者消费蛋糕 " + index);

        // 通知生产者生产
        this.notify();
    }
}

JDK5.0 之后提供两个接口 Lock 和 Condition ,简化同步和锁编程
java.util.concurrent.locks.Lock  ---- 取代 synchronized
java.util.concurrent.locks.Condition  ---- 取代 wait notify
* 在同一个锁上面 建立多个监视器
public class SaleTicketSystem {
    public static void main(String[] args) {
        TicketSystem system = new TicketSystem();// 票源唯一
        for (int i = 0; i < 5; i++) {// 模拟5个窗口同时卖票
            SaleTicket saleTicket = new SaleTicket(system);
            new Thread(saleTicket).start();
        }
    }
}

// 票源
class TicketSystem {
    private int totalTickets = 100; // 总票数
    private int hasSaleTickets = 0; // 已经售票编号
    private Lock lock = new ReentrantLock();

    // 添加同步后,同一时间只能有一个窗口卖票
    public void saleTicket() {
        // 加锁
        lock.lock();

        if (totalTickets <= 0) {
            throw new RuntimeException("票已经卖完");
        }
        // 卖掉一张票
        hasSaleTickets++;
        System.out.println("打印一张票!" + hasSaleTickets);
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 总票数 -1
        totalTickets--;
        System.out.println("剩余票数:" + totalTickets);

        // 解锁
        lock.unlock();
    }

}

class SaleTicket implements Runnable {

    private TicketSystem ticketSystem;

    public SaleTicket(TicketSystem ticketSystem) {
        this.ticketSystem = ticketSystem;
    }

    @Override
    public void run() {
        while (true) {
            try {
                ticketSystem.saleTicket();
            } catch (RuntimeException e) {
                break;
            }
        }
    }

}
/**
 * 生产者 消费者案例
 *
 * @author seawind
 *
 */
public class ProducerConsumerTest {
    public static void main(String[] args) {
        CakeHouse cakeHouse = new CakeHouse();
        new Thread(new Producer(cakeHouse)).start();
        new Thread(new Consumer(cakeHouse)).start();
    }
}

// 生产者
class Producer implements Runnable {

    private CakeHouse cakeHouse;

    public Producer(CakeHouse cakeHouse) {
        this.cakeHouse = cakeHouse;
    }

    @Override
    public void run() {
        // 生产100个蛋糕
        for (int i = 0; i < 100; i++) {
            cakeHouse.put();
        }
    }

}

// 消费者
class Consumer implements Runnable {
    private CakeHouse cakeHouse;

    public Consumer(CakeHouse cakeHouse) {
        this.cakeHouse = cakeHouse;
    }

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            cakeHouse.take();
        }
    }

}

class CakeHouse {
    // 蛋糕房 蛋糕窗口
    private List<Object> list = new LinkedList<Object>();
    private int index;
    private Lock lock = new ReentrantLock();
    // 生产者专用监视器
    private Condition producerCondition = lock.newCondition();
    // 消费者专用监视器
    private Condition consumerCondition = lock.newCondition();

    public void put() {
        lock.lock();
        while (!list.isEmpty()) {
            // 已经有蛋糕
            try {
                producerCondition.await(); // 在生产者监视器上等
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        // 生产蛋糕
        list.add(new Object());
        index++;
        System.out.println("生产者 生产蛋糕 " + index);

        // 通知消费者 快去吃
        consumerCondition.signal();

        lock.unlock();
    }

    public void take() {
        lock.lock();
        while (list.isEmpty()) {
            // 没蛋糕
            try {
                consumerCondition.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        // 吃掉蛋糕
        list.remove(0);
        System.out.println("消费者消费蛋糕 " + index);

        // 通知生产者生产
        producerCondition.signal();

        lock.unlock();
    }
}
java.util.concurrent 包 和 子包 ---- ArrayBlockingQueue<E> CopyOnWriteArrayList<E> LinkedBlockingQueue<E> 

线程池技术 Excutors
为什么用线程池,线程创建、关闭释放资源 ---- 消耗程序性能
一次创建多个线程,做任务,随机获得一个线程,完成任务,将线程归还线程池

newFixedThreadPool(int nThreads) 固定线程数量线程池
newCachedThreadPool()  返回根据程序需要自动扩充大小 线程池 ----- 最常用
newSingleThreadExecutor()  返回单线程处理程序

shutdown与shutdownNow的比较
shutdown 完成当前线程池中所有任务 再关闭
shutdownNow 会对当前线程池中所有线程,调用interrupt 尝试打断当前线程,如果无法打断,会执行结束
public class ExcutorsTest {
    // java中编写程序测试多线程,一定不能用 junit --- System.exit
    public static void main(String[] args) {
        // 创建线程池
        // 创建固定数量为3 线程池
        // ExecutorService service = Executors.newFixedThreadPool(5);
        // 创建 自增扩充大小 线程池
        ExecutorService service = Executors.newCachedThreadPool();

        // 单一线程对象
        // ExecutorService service = Executors.newSingleThreadExecutor();

        // 开启十个线程 分别输出
        for (int i = 0; i < 10; i++) {
            service.execute(new MyExcutor());
        }

        // 关闭连接池
        service.shutdown();// 当之前任务都结束后,尝试关闭连接池
        // service.shutdownNow();// 尝试马上关闭连接池,但是不一定能关闭
        // interrupt
    }
}

class MyExcutor implements Runnable {

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName() + ":" + i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

}
------------------------------------------------------------------------------------------------------------------
OSI 七层体系结构: 应用层、表示层、会话层、传输层、网络层、数据链路层、物理层 
*协议层次越高,越容易让人理解, 层次越低,数据越接近0 1
TCP/IP 四层 : 应用层、传输层、网际层、网络接口层

应用层、传输层
应用层:HTTP SMTP POP3 FTP TELNET
传输层:TCP(不允许丢包 三次握手) UDP(广播 允许丢包)

三次握手:
A 向 B 发送一个消息:能听到我说话吗
B 回复 A 消息:我能听到,你能听到我说话吗
A 回复 B :我能听到

TCP发送数据没有收到对方回应,选择重新发送 ---- 时间限制 超时

Socket 两台计算机之间一个连接 ---- 两台计算机可以通信

使用socket建立双方连接 -----传输协议需要自己编写 面向底层协议

socket 编写程序 可以模拟服务器,可以模拟客户端
服务器如何编写

* 使用socket进行通信过程中,如果调用in.readLine 但是对方没有写 ---- 程序一直等待 、out.print向对方发送信息 没有阻塞
/**
 * 模拟服务器
 *
 * @author seawind
 *
 */
public class Server {
    public static void main(String[] args) throws IOException {
        // 步骤一 创建ServerSocket 对象
        ServerSocket serverSocket = new ServerSocket(9000);
        // 连接服务器需要 ip 和 端口

        // 步骤二 等待客户端来连接
        Socket socket = serverSocket.accept(); // socket代表一个连接

        // 步骤三 需要获得流
        PrintWriter out = new PrintWriter(socket.getOutputStream());
        BufferedReader in = new BufferedReader(new InputStreamReader(socket
                .getInputStream()));

        // 收取对方信息 用 in
        // 发送给对方信息 用 out

        out.println("Hello !");// 输出信息 因为用字符流 ,所以flush
        out.flush();

        System.out.println(in.readLine());
    }
}
/**
 * 客户端
 *
 * @author seawind
 *
 */
public class Client {
    public static void main(String[] args) throws UnknownHostException,
            IOException {
        // 步骤一 连接服务器
        Socket socket = new Socket("localhost", 9000);

        // 步骤二 获得流
        PrintWriter out = new PrintWriter(socket.getOutputStream());
        BufferedReader in = new BufferedReader(new InputStreamReader(socket
                .getInputStream()));

        // 通过in 获得信息
        System.out.println(in.readLine());

        out.println("你好服务器!");
        out.flush();
    }
}


socket 案例 ----- 模拟tomcat服务器

/**
 * 模拟tomcat server
 *
 * @author seawind
 *
 */
public class TomcatServer {
    public static void main(String[] args) throws IOException {
        // 步骤一
        ServerSocket serverSocket = new ServerSocket(9000);

        // 创建线程池
        ExecutorService executorService = Executors.newCachedThreadPool();

        while (true) {
            // 步骤二 获得与客户端连接
            Socket socket = serverSocket.accept();
            DealResponse dealResponse = new DealResponse(socket);
            // 将处理任务 加入线程池
            executorService.execute(dealResponse);

        }
    }

    static class DealResponse implements Runnable {
        private Socket socket;

        public DealResponse(Socket socket) {
            this.socket = socket;
        }

        @Override
        public void run() {
            try {
                // 步骤三 流
                OutputStream bout = new BufferedOutputStream(socket
                        .getOutputStream());
                // PrintWriter out = new PrintWriter(socket.getOutputStream());
                BufferedReader in = new BufferedReader(new InputStreamReader(
                        socket.getInputStream()));

                // 浏览器连接服务器 发送HTTP请求
                String requestline = in.readLine();// 获得请求行

                // 解析资源路径
                String path = parse(requestline);
                System.out.println(path);

                // 判断目标文件是否存在
                File file = new File("webroot" + path);
                // 判断服务器上面 有没有客户访问文件
                if (file.exists()) {
                    // 回写HTTP协议格式
                    bout.write("HTTP/1.1 200 OK\r\n".getBytes());

                    // 根据文件扩展名得到文件格式

                    bout
                            .write(("Content-Type:" + getType(file.getName()) + "\r\n")
                                    .getBytes()); // 不同文件类型是不同的
                    bout.write(("Content-Length:" + file.length() + "\r\n\r\n")
                            .getBytes());

                    // 存在
                    InputStream fis = new BufferedInputStream(
                            new FileInputStream(file));
                    int temp;
                    while ((temp = fis.read()) != -1) {
                        bout.write(temp);
                    }
                    bout.flush();
                    bout.close();
                    fis.close();
                    System.out.println("文件找到了");
                } else {
                    // 不存在
                    bout.write("file not found".getBytes());
                    bout.flush();
                    System.out.println("文件找不到!");
                    bout.close();
                }

                // 断开socket连接
                in.close();
                socket.close();
            } catch (FileNotFoundException e) {
                // e.printStackTrace();
            } catch (IOException e) {
                // e.printStackTrace();
            }
        }
    }

    private static String getType(String name) {
        // 获得扩展名
        String ext = name.substring(name.lastIndexOf(".") + 1);
        String type = ResourceBundle.getBundle("mime").getString(ext);
        return type;
    }

    private static String parse(String requestline) {
        return requestline.split(" ")[1];
    }

}

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

注解的使用 ---- 定义注解,利用反射解析注解内容 (案例:银行转账最大金额)
Servlet3.0 注解开发Servlet、异步技术、文件上传
动态代理 (案例:德华案例、银行取款 收取手续费用)
多线程:两种线程创建方式、四种状态、同步和锁、死锁、wait和notify (案例:卖票、生产蛋糕) ---- 使用JDK5 Lock和Condition 重写

socket编程 服务器怎么写 客户端怎么写
* 实际socket案例 按照协议发送内容 模拟tomcat 服务器 遵循HTTP协议








































































































0
0
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics