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协议