javaweb笔记(方立勋)

1 Day01 02-eclipse使用和程序的断点调试
1.1 Eclipse的使用
工作空间目录是纯英文不带空格的路径
在eclipse下Java程序的编写和运行,及java运行环境的配置。
新建java工程day01,在弹出窗口中可配置jre
工程右键属性可配置编辑器的版本
1.2 调试程序
1.3 Debug窗口
Resume(F8)到下一个断点
Step into(F5)进入到函数等里面
Step over(F6)到下一行代码
Step return(F7)返回到调用的下一行
Drop to Frame返回到当前方法的第一行,
Terminate (F12)终止虚拟机,程序就结束了。(调试完后用)
右键watch观察变量的值
1.4 Breakpoints窗口
移除所以断点
1.5 断点注意问题
1. 调完后,移除所以断点
2. 调完后,一定要结束断点的JVM。
2 03-eclipse常用快捷键
MyEclipse设置工作空间默认编码utf-8等,使新建工程使用默认编码
菜单栏——Window / Preferences / General / Workspace 。
内容提示:Alt + / Content Assist
选中多行代码,按Tab键是整块向右推进,按Shift+Tab是整块向左缩进
快速修复:Ctrl + 1
导包:Ctrl + shift + O
格式化代码块:ctrl + shift + F
向前向后:Alt + 方向键(left right arrow)查看源代码时
添加注释 Ctrl+Shift+/
除去注释 Ctrl+Shift+\
查看源代码 Ctrl+单击 ctrl+shift+t
查看方法说明:F2
重置透视图 Window menu下
更改为大写 Ctrl+Shift+X
更改为小写 Ctrl+Shift+Y
复制行 Ctrl+Alt+向下键(有些不能用)
查看类的继承关系Ctrl+T
查看快捷键Ctrl+shift+L
3 04-junit测试框架
在Outline窗口方法上右键Run As /JUnit Test 测试某一个方法,类上右键run as /JUnit Test 测试这个类的所有方法
1、用junit进行单元测试的时候,在每个被测试的方法中必须加上@Test注解
2、用@Before注解是在每个被测试的方法前执行。
3、用@After注解是在每个被测试的方法后执行。
4、用@BeforeClass 注解的静态方法是在所有方法被测试之前执行的方法,就像类里面的构造方法一样。用来初始化一些要用到的变量等资源。
5、用@AterClass注解的静态方法是在所有被测试的方法之后执行。相当于c++中析构函数。用来释放一些资源。
6、使用断言类Assert可以判断被测试的方法的返回值是否跟你预期的相同。
//person.java
package cn.itcast.elclipse;

public class Person {
public void eat()
{
System.out.println("eating.");
}
public String run()
{
System.out.println("runing.");
return "1";
}
} //demo4.java
package cn.itcast.elclipse;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

public class Demo4 {
@Before
public void before(){
System.out.println("before");
}

@Test
public void testRun()
{
Person p=new Person();
p.run();
}
@Test
public void testEat(){
Person p=new Person();
p.eat();
}
@After
public void after(){
System.out.println("after");
}
}
//Demo5.java
package cn.itcast.elclipse;

import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;

public class Demo5 {
@BeforeClass
public void beforeClass(){
System.out.println("beforeclass");
}

@Test
public void testRun()
{
Person p=new Person();
p.run();
}
@Test
public void testEat(){
Person p=new Person();
p.eat();
}
@AfterClass
public void afterClass(){
System.out.println("afterclass");
}
} //Demo6.java
package cn.itcast.elclipse;

import junit.framework.Assert;

import org.junit.Test;

public class Demo6 {

@Test
public void testRun()
{
Person p=new Person();
Assert.assertEquals("2", p.run());
}
}
4 java5的静态导入和自动装箱拆箱.avi
JDK5中新增了很多新的java特性,利用这些新语法可以帮助开发人员编写出更加高效、清晰,安全的代码。
静态导入 自动装箱/拆箱 增强for循环 可变参数 枚举反射内省 泛型 元数据
4.1 静态导入
JDK 1.5 增加的静态导入语法用于导入类的某个静态属性或方法。使用静态导入可以简化程序对类静态属性和方法的调用。
语法:
Import static 包名.类名.静态属性|静态方法|*
例如:
4.2 自动装箱/拆箱
JDK5.0的语法允许开发人员把一个基本数据类型直接赋给对应的包装类变量, 或者赋给 Object 类型的变量,这个过程称之为自动装箱。
自动拆箱与自动装箱与之相反,即把包装类对象直接赋给一个对应的基本类型变量。
典型应用:
静态导入 自动装箱/拆箱
import static java.lang.System.out
import static java.lang.Math.*
//Demo1
package cn.itcast.demo;
import static java.lang.System.out;
import static java.util.Arrays.*;

public class Demo1 {
public static void main(String[] args) {
out.print("main");
int []a=new int[]{6,5,3};
sort(a);
for(int i:a)
out.print(i);
}
}
// main356 package cn.itcast.demo;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class Demo2 {
public static void main(String[] args) {
Integer i=1;//装箱
int j=i;//拆箱
//典型应用
List list = new ArrayList();
list.add(1);
int k = (Integer)list.get(0);
Iterator it=list.iterator();
while(it.hasNext())
{
int m=(Integer)it.next();//拆箱
}
}
}

5 06-增强for循环
5.1 增强for循环
引入增强for循环的原因:在JDK5以前的版本中,遍历数组或集合中的元素,需先获得数组的长度或集合的迭代器,比较麻烦!
因此JDK5中定义了一种新的语法——增强for循环,以简化此类操作。增强for循环只能用在数组、或实现Iterator接口的集合类上
语法格式:
for(变量类型变量 :需迭代的数组或集合){

}
Map map=new HashMap();
//Map map2=new LinkedHashMap();
map.put("1", "aaa");
map.put("2", "bbb");
map.put("3", "ccc");
//传统方式1
Set set=map.keySet();
Iterator it=set.iterator();
while(it.hasNext()){
String key=(String)it.next();
String value=(String) map.get(key); System.out.println("key="+key+",value="+value);
} //增强for循环的1种方式
for(Object obj:map.keySet()){
String key2=(String)obj;
String value2=(String)map.get(key2); System.out.println("key2="+key2+",value2="+value2);
}
//传统方式2
Set set2=map.entrySet();
Iterator it2=set2.iterator();
while(it2.hasNext()){
Map.Entry entry=(Entry)it2.next(); System.out.println("key="+entry.getKey()+",value="+entry.getValue());
} //增强for循环的2种方式
for(Object obj:map.entrySet()){
Map.Entry entry3=(Entry) obj;
String key3=(String) entry3.getKey();
String value3=(String) entry3.getValue();
System.out.println("key3="+key3+",value3="+value3);
}
//增强for循环需要注意的问题:只适合取数据
int arr[]={1,2,3};
for(int i: arr){
i=10;
}
System.out.println(arr[0]); // 1
List li=new ArrayList();
li.add("1");
for(Object obj : li){
obj="888";
}
System.out.println(li.get(0));// 1

6 可变参数
测试JDK中具有可变参数的类Arrays.asList()方法。分别传多个参、传数组,传数组又传参的情况。
注意:传入基本数据类型数组的问题。
从JDK 5开始, Java 允许为方法定义长度可变的参数。语法:
public void foo(int … args){
}
注意事项:
调用可变参数的方法时, 编译器将自动创建一个数组保存传递给方法的可变参数,因此,程序员可以在方法体中以数组的形式访问可变参数
可变参数只能处于参数列表的最后, 所以一个方法最多只能有一个长度可变的参数
public void testSum(){
sum(1,2,3,4);
int arr[]={5,6,7};
sum(arr);
}
public void sum(int ...nums){
//可变参数当成数组
int sum=0;
for(int i:nums){
sum+=i;
}
System.out.println(sum);
} public void bb(){
//public static ListasList(T... a)
List list=Arrays.asList("1","2","3");
System.out.println(list);//[1, 2, 3]

String arr[]={"1","2","3","4"};
list=Arrays.asList(arr);
System.out.println(list);//[1, 2, 3, 4]

int nums[]={1,2,3,4,5};
list=Arrays.asList(nums);
System.out.println(list);//[[I@120d62b]

Integer nums2[]={1,2,3,4,5};
list=Arrays.asList(nums2);
System.out.println(list);//[1, 2, 3, 4, 5]

}
//可变参数注意的问题
//public void aa(int ...nums,int s){}//不可以
//public void bb(int s ,int ...nums)//可以

7 枚举类
为什么需要枚举?
一些方法在运行时,它需要的数据不能是任意的,而必须是一定范围内的值,此类问题在JDK5以前采用自定义带有枚举功能的类解决,Java5以后可以直接使用枚举予以解决。
JDK 5新增的 enum 关键字用于定义一个枚举类。
7.1 枚举类
枚举类具有如下特性:
枚举类也是一种特殊形式的Java类。
枚举类中声明的每一个枚举值代表枚举类的一个实例对象。
与java中的普通类一样,在声明枚举类时,也可以声明属性、方法和构造函数,但枚举类的构造函数必须为私有的(这点不难理解)。
枚举类也可以实现接口、或继承抽象类。
JDK5中扩展了swith语句,它除了可以接收int, byte, char, short外,还可以接收一个枚举类型。
若枚举类只有一个枚举值,则可以当作单态设计模式使用。
练习:请编写一个关于星期几的枚举WeekDay,要求:
枚举值:Mon、Tue 、Wed 、Thu 、Fri 、Sat 、Sun
该枚举要有一个方法,调用该方法返回中文格式的星期。
Java中声明的枚举类,均是java.lang.Enum类的孩子,它继承了Enum类的所有方法。常用方法:
name()
ordinal()//返回枚举常量的序数(它在枚举声明中的位置,其中初始常量序数为零)。
valueof(Class enumClass, String name) //返回带指定名称的指定枚举类型的枚举常量。
//String str=”B”;Grade g=Grade.valueOf(Grade.class,str);
values() 此方法虽然在JDK文档中查找不到,但每个枚举类都具有该方法,它遍历枚举类的所有枚举值非常方便。
package cn.itcast.enumeration;
import org.junit.Test;
public class Demo1 {
@Test
public void test() {
print(Grade.B);
}

public void print(Grade g) // A B C D E
{
String value=g.getValue();
System.out.println(value);
}
}

/*
* class Grade{ private Grade(){ }
* public static final Grade A=new Grade();
* public static final Grade B=new Grade();
* public static final Grade C=new Grade();
* public static final Grade D=new Grade();
* public static final Grade E=new Grade();
* }
*/
//如何定义枚举的构造函数、方法、字段
enum Grade {// class A 100-90 B 89-80 C 79-70 D 69-60 E 59-0
A("100-90"), B("89-80"), C("79-70"), D("69-60"), E("59-0");// object
private String value;
private Grade(String value){
this.value=value;
}
public String getValue(){
return this.value;
}
} package cn.itcast.enumeration2;
import org.junit.Test;
public class Demo1 {
@Test
public void test() {
print(Grade.B); //89-80,良
}

public void print(Grade g) // A B C D E
{
String value=g.getValue();
String value2=g.localeValue();

System.out.println(value+","+value2);
}
}

//带抽象方法的枚举
enum Grade {// class A 100-90优 B 89-80良 C 79-70 一般D 69-60差 E 59-0不及格
A("100-90"){
public String localeValue(){
return "优";
}
},
B("89-80"){
public String localeValue(){
return "良";
}
},
C("79-70"){
public String localeValue(){
return "一般";
}
},
D("69-60"){
public String localeValue(){
return "差";
}
},
E("59-0"){
public String localeValue(){
return "不及格";
}
};// object

private String value;
private Grade(String value){
this.value=value;
}
public String getValue(){
return this.value;
}
public abstract String localeValue();
}
7.2 反射什么—Class类
反射就是把Java类中的各种成分映射成一个个的java对象。例如,一个类有:成员变量,方法,构造方法,包等等信息,利用反射技术可以对一个类进行解剖,把各个组成部分映射成一个个对象。
Class类用于表示.class文件,画图演示一个对象的创建过程。
如何得到某个class文件对应的class对象。
类名.class, 对象.getClass() Class.forName(“类名”)
以上三种方式,JVM都会把相应class文件装载到一个class对象中(只装载一次)
创建类的实例:Class.newInstance方法
数组类型的Class实例对象
Class.isArray()
总之,只要是在源程序中出现的类型,都有各自的Class实例对象,例如,int,void…
反射就是把Java类中的各种成分映射成一个个的java对象。例如,一个类有:成员变量,方法,构造方法,包等等信息,利用反射技术可以对一个类进行解剖,把各个组成部分映射成一个个对象。
掌握反射技术的要点在于:如何从一个class中反射出各个组成部分。反射出来的对象怎么用。
7.3 Constructor类
Constructor类代表某个类中的一个构造方法
得到某个类所有的构造方法:
例子:Constructor [] constructors= Class.forName("java.lang.String").getConstructors();
得到某一个构造方法:
例子: //获得方法时要用到类型
Constructor constructor = Class.forName(“java.lang.String”).getConstructor(StringBuffer.class);
创建实例对象:
通常方式:String str = new String(new StringBuffer("abc"));
反射方式: String str = (String)constructor.newInstance(new StringBuffer("abc"));
Class.newInstance()方法:例子:String obj = (String)Class.forName("java.lang.String").newInstance();
该方法内部先得到默认的构造方法,然后用该构造方法创建实例对象。
7.4 Field类
Field类代表某个类中的一个成员变量
问题:得到的Field对象是对应到类上面的成员变量,还是对应到对象上的成员变量?类只有一个,而该类的实例对象有多个,如果是与对象关联,哪关联的是哪个对象呢?所以字段fieldX 代表的是x的定义,而不是具体的x变量。(注意访问权限的问题)
示例代码:
ReflectPoint point = new ReflectPoint(1,7);
Field y = Class.forName("cn.itcast.corejava.ReflectPoint").getField("y");
System.out.println(y.get(point));
//Field x = Class.forName("cn.itcast.corejava.ReflectPoint").getField("x");
Field x = Class.forName("cn.itcast.corejava.ReflectPoint").getDeclaredField("x");
x.setAccessible(true);
System.out.println(x.get(point));
7.5 Method类
Method类代表某个类中的一个成员方法
得到类中的某一个方法:
例子: Method charAt = Class.forName("java.lang.String").getMethod("charAt", int.class);
调用方法:
通常方式:System.out.println(str.charAt(1));
反射方式:System.out.println(charAt.invoke(str, 1));
如果传递给Method对象的invoke()方法的第一个参数为null,这有着什么样的意义呢?说明该Method对象对应的是一个静态方法!
jdk1.4和jdk1.5的invoke方法的区别:
Jdk1.5:public Object invoke(Object obj,Object... args)
Jdk1.4:public Object invoke(Object obj,Object[] args),即按jdk1.4的语法,需要将一个数组作为参数传递给invoke方法时,数组中的每个元素分别对应被调用方法中的一个参数,所以,调用charAt方法的代码也可以用Jdk1.4改写为 charAt.invoke(“str”, new Object[]{1})形式。
7.6 用反射方式执行某个类中的main方法
目标:写一个程序,这个程序能够根据用户提供的类名,去执行该类中的main方法。用普通方式调完后,大家要明白为什么要用反射方式去调啊?
问题:
启动Java程序的main方法的参数是一个字符串数组,即public static void main(String[] args),通过反射方式来调用这个main方法时,如何为invoke方法传递参数呢?按jdk1.5的语法,整个数组是一个参数,而按jdk1.4的语法,数组中的每个元素对应一个参数,当把一个字符串数组作为参数传递给invoke方法时,javac会到底按照哪种语法进行处理呢?jdk1.5肯定要兼容jdk1.4的语法,会按jdk1.4的语法进行处理,即把数组打散成为若干个单独的参数。所以,在给main方法传递参数时,不能使用代码mainMethod.invoke(null,new String[]{“xxx”}),javac只把它当作jdk1.4的语法进行理解,而不把它当作jdk1.5的语法解释,因此会出现参数类型不对的问题。
解决办法:
mainMethod.invoke(null,new Object[]{new String[]{"xxx"}});
mainMethod.invoke(null,(Object)new String[]{"xxx"}); ,编译器会作特殊处理,编译时不把参数当作数组看待,也就不会数组打散成若干个参数了
package cn.itcast.reflect;
import java.io.InputStream;
import java.util.List;
public class Person {

public String name="aaaa";
private int password;
private static int age=23;

public Person(){
System.out.println("person");
}
public Person(String name){
this.name=name;
System.out.println("person name");
}
public Person(String name,int password){
this.name=name;
System.out.println("person name password");
}
private Person(List list){
System.out.println("list");
}

public void aa1(){
System.out.println("aa1");
}
public void aa1(String name,int password){
System.out.println("name= "+name+" password="+password);
}
public Class[] aa1(String name,int[] password){
return new Class[]{String.class};
}
private void aa1(InputStream in){
System.out.println(in);
}
public static void aa1(int num){
System.out.println(num);
}
public static void main(String []args){
System.out.println("main");
}

} package cn.itcast.reflect;
import java.lang.reflect.Field;
import org.junit.Test;

//反射类的字段
public class Demo4 {
//public String name="aaaa";
@Test
public void test1() throws Exception{
Class clazz=Class.forName("cn.itcast.reflect.Person");
Field f=clazz.getField("name");
Person p=new Person();
Object value= f.get(p);//获取字段值
Class type=f.getType();//字段类型
System.out.println(type);//class java.lang.String
if(type.equals(String.class)){
String S_value=(String)value;
System.out.println(S_value);//aaaa
}

//设置值
f.set(p, "ppppp");
System.out.println(f.get(p));//ppppp
System.out.println(String.class);//class java.lang.String

}
//private int password;
@Test
public void test2() throws Exception{
Class clazz=Class.forName("cn.itcast.reflect.Person");
Field f=clazz.getDeclaredField("password");
f.setAccessible(true);
Person p=new Person();
f.set(p, 123);
System.out.println(f.get(p));//123
}
//private static int age=23;
@Test
public void test3() throws Exception{
Class clazz=Class.forName("cn.itcast.reflect.Person");
Field f=clazz.getDeclaredField("age");
f.setAccessible(true);
System.out.println(f.get(null));//23
}
}

package cn.itcast.reflect;

import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.List;

import org.junit.Test;

//反射类的构造函数,创建类的对象
public class Demo2 {

//public Person()
@Test
public void test1() throws Exception{
Class clazz=Class.forName("cn.itcast.reflect.Person");
Constructor c=clazz.getConstructor(null);
Person p=(Person) c.newInstance(null);//person
System.out.println(p.name);//aaaa
}
//public Person(String name)
@Test
public void test2() throws Exception{
Class clazz=Class.forName("cn.itcast.reflect.Person");
Constructor c=clazz.getConstructor(String.class);
Person p=(Person) c.newInstance("abc");//person name
System.out.println(p.name);//abc
}

//public Person(String name,int password)
@Test
public void test3() throws Exception{
Class clazz=Class.forName("cn.itcast.reflect.Person");
Constructor c=clazz.getConstructor(String.class,int.class);
Person p=(Person) c.newInstance("abc",999);//person name password
System.out.println(p.name);//abc
}
//private Person(List list)
@Test
public void test4() throws Exception{
Class clazz=Class.forName("cn.itcast.reflect.Person");
Constructor c=clazz.getDeclaredConstructor(List.class);
c.setAccessible(true);//暴力反射,统统打开访问权限
Person p=(Person) c.newInstance(new ArrayList());//list
System.out.println(p.name);//aaaa
}
//创建对象的另外一种途径,无参构造方法,等效test1
public void test5() throws Exception{
Class clazz=Class.forName("cn.itcast.reflect.Person");
Person p=(Person) clazz.newInstance();
System.out.println(p.name);//aaaa
}
} package cn.itcast.reflect;

import java.io.FileInputStream;
import java.io.InputStream;
import java.lang.reflect.Method;

import org.junit.Test;
//反射方法
public class Demo3 {
// public void aa1()
@Test
public void test1() throws Exception{
Class clazz=Class.forName("cn.itcast.reflect.Person");
Method method=clazz.getMethod("aa1", null);
Person p=new Person();//person
method.invoke(p, null);//aa1
}
//public void aa1(String name,int password)
@Test
public void test2() throws Exception{
Class clazz=Class.forName("cn.itcast.reflect.Person");
Method method=clazz.getMethod("aa1", String.class,int.class);
Person p=new Person();//person
method.invoke(p, "xxxx",99);//name= xxxx password=99
}
//public Class[] aa1(String name,int[] password)
@Test
public void test3() throws Exception{
Class clazz=Class.forName("cn.itcast.reflect.Person");
Method method=clazz.getMethod("aa1", String.class,int[].class);
Person p=new Person();//person
Class cs[]=(Class[]) method.invoke(p, "xxxx",new int[]{1,2,3});//name= xxxx password=99
System.out.println(cs[0]);//class java.lang.String
}
//private void aa1(InputStream in)
public void test4() throws Exception{
Class clazz=Class.forName("cn.itcast.reflect.Person");
Method method=clazz.getDeclaredMethod("aa1",InputStream.class);
method.setAccessible(true);
Person p=new Person();//person
method.invoke(p,new FileInputStream("C:\\1.txt"));//

}
//public static void aa1(int num)
public void test5() throws Exception{
Class clazz=Class.forName("cn.itcast.reflect.Person");
Method method=clazz.getMethod("aa1",int.class);
method.invoke(null,777);//777 静态方法调用不需要对象,给对象也行
}

// public static void main(String []args){
public void test6() throws Exception{
Class clazz=Class.forName("cn.itcast.reflect.Person");
Method method=clazz.getMethod("main",String [].class);
//method.invoke(null,new String[]{"a","c"});//Wrong
//method.invoke(null,"a","c");//Wrong
String []str={"x","y","z"};
method.invoke(null, (Object)str);
method.invoke(null, new Object[]{new String[]{"a","c"} });
method.invoke(null, (Object)new String[]{"a","c"});
}
}

7.7 用反射技术实现一个简单的web服务器
内省(Introspector) — JavaBean
Introspector 类为通过工具学习有关受目标 Java Bean 支持的属性、事件和方法的知识提供了一个标准方法。
什么是JavaBean和属性的读写方法?
有get或set方法就是一个属性,另外所有类继承了Object类的getClass()方法,所以还有一个属性class。
访问JavaBean属性的两种方式:
直接调用bean的setXXX或getXXX方法。
通过内省技术访问(java.beans包提供了内省的API),内省技术访问也提供了两种方式。
通过PropertyDescriptor类操作Bean的属性
通过Introspector类获得Bean对象的 BeanInfo,然后通过 BeanInfo 来获取属性的描述器( PropertyDescriptor ),通过这个属性描述器就可以获取某个属性对应的 getter/setter 方法,然后通过反射机制来调用这些方法。
什么情况下用内省?
package cn.itcast.introspector;

//该类共有5个属性
public class Person {
private String name;
private String password;
private int age;
public void setAb(int a){

}
public String getName() {
return name;
}
public String getPassword() {
return password;
}
public int getAge() {
return age;
}
public void setName(String name) {
this.name = name;
}
public void setPassword(String password) {
this.password = password;
}
public void setAge(int age) {
this.age = age;
}

} package cn.itcast.introspector;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import org.junit.Test;

public class Demo1 {
//得到bean所有属性
public void test1() throws IntrospectionException{
BeanInfo info=Introspector.getBeanInfo(Person.class);
//去掉Object里的属性
BeanInfo info2=Introspector.getBeanInfo(Person.class,Object.class);
PropertyDescriptor[] pds=info.getPropertyDescriptors();
for(PropertyDescriptor pd:pds){
System.out.println(pd.getName());
//ab age class name password
}
}
//操纵bean的指定属性:age
@Test
public void test2() throws Exception{
Person p=new Person();
PropertyDescriptor pd=new PropertyDescriptor("age", Person.class);
//得到属性的写方法,为属性赋值
Method method=pd.getWriteMethod();
method.invoke(p, 45);
System.out.println(p.getAge());//45

//获取属性的值
method=pd.getReadMethod();
System.out.println(method.invoke(p, null));//45
}
//高级内容,获取当前操作的属性的类型
@Test
public void test3() throws Exception{
Person p=new Person();
PropertyDescriptor pd=new PropertyDescriptor("age", Person.class);

//得到属性的写方法,为属性赋值
Method method=pd.getWriteMethod();
System.out.println(pd.getPropertyType());//int
method.invoke(p, 45);
System.out.println(p.getAge());//45

//获取属性的值
method=pd.getReadMethod();
System.out.println(method.invoke(p, null));//45
}
}

7.8 内省—beanutils工具包
Apache组织开发了一套用于操作JavaBean的API,这套API考虑到了很多实际开发中的应用场景,因此在实际开发中很多程序员使用这套API操作JavaBean,以简化程序代码的编写。
在工程下新建lib目录,导入commons-beanutils-1.8.3.jar 和支持包commons-logging-1.1.1.jar
选中两个包,右键build path/add to build path
Beanutils工具包的常用类:
BeanUtils
PropertyUtils
ConvertUtils.regsiter(Converter convert, Class clazz)
自定义转换器
package cn.itcast.beanutils;

import java.util.Date;

public class Person {
private String name;
private String password;
private int age;
private Date birthday;
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public String getName() {
return name;
}
public String getPassword() {
return password;
}
public int getAge() {
return age;
}
public void setName(String name) {
this.name = name;
}
public void setPassword(String password) {
this.password = password;
}
public void setAge(int age) {
this.age = age;
}

} package cn.itcast.beanutils;

import java.lang.reflect.InvocationTargetException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.ConversionException;
import org.apache.commons.beanutils.ConvertUtils;
import org.apache.commons.beanutils.Converter;
import org.apache.commons.beanutils.locale.converters.DateLocaleConverter;
import org.junit.Test;

//使用beanUtils操纵bean的属性 ( 第三方)
public class Demo1 {
@Test
public void test1() throws Exception{
Person p=new Person();
BeanUtils.setProperty(p, "age", 456);
System.out.println(p.getAge());//456
}
@Test
public void test2() throws Exception{
String name="aaaa";
String age="123";
String password="pw";

Person p=new Person();
//支持8种基本类型自动转换
BeanUtils.setProperty(p, "name", name);
BeanUtils.setProperty(p, "age", age);
BeanUtils.setProperty(p, "password", password);

System.out.println(p.getName());//aaaa
System.out.println(p.getAge());//123
System.out.println(p.getPassword());//pw
}
@Test
public void test3() throws Exception{
String birthday="1983-12-1";
//为了让日期赋值到bean的birthday属性上,给beanUtils注册一个日期转换器
//ConvertUtils.register(converter, clazz);
ConvertUtils.register(new Converter(){
public Object convert(Class type, Object value) {
if(value==null) return null;
if(!(value instanceof String)){
throw new ConversionException("只支持String类型的转换");
}
String str=(String)value;
if(str.trim().equals("")) return null;
SimpleDateFormat df=new SimpleDateFormat("yyyy-MM-dd",Locale.US);
try {
return df.parse(str);
} catch (ParseException e) {
throw new RuntimeException(e);
}
}
}, Date.class);
Person p=new Person();
BeanUtils.setProperty(p, "birthday", birthday);
System.out.println(p.getBirthday());//pw
System.out.println("___"+BeanUtils.getProperty(p, "birthday"));
}
public void test5() throws Exception {
Map map=new HashMap();
map.put("name", "aaa");
map.put("password", "123");
map.put("brithday", "1980-09-09");
ConvertUtils.register(new DateLocaleConverter(), Date.class);
Person p=new Person();
//用map集合填充bean属性,map关键字和bean属性要一致
BeanUtils.populate(p, map);
}
}

7.9 泛型(Generic)—泛形的作用
JDK5以前,对象保存到集合中就会失去其特性,取出时通常要程序员手工进行类型的强制
转换,这样不可避免就会引发程序的一些安全性问题。例如:
ArrayList list = new ArrayList();
list.add("abc");
Integer num = (Integer) list.get(0); //运行时会出错,但编码时发现不了

list.add(new Random());
list.add(new ArrayList());
for(int i=0;i为例:念着typeof
ArrayList中的E称为类型参数变量
ArrayList中的Integer称为实际类型参数
整个称为ArrayList泛型类型
整个ArrayList称为参数化的类型ParameterizedType
7.10 泛型典型应用
使用迭代器迭代泛形集合中的元素。
使用增强for循环迭代泛形集合中的元素。
存取HashMap中的元素。
使用泛形时的几个常见问题:
使用泛形时,泛形类型须为引用类型,不能是基本数据类型
//使用泛型时,如果两边都使用到泛型,两边必须一样
ArrayList list = new ArrayList(); //bad
ArrayList list = new ArrayList(); //bad
ArrayList list = new ArrayList ();//ok
ArrayList list = new ArrayList();//ok
package cn.itcast.generic;

import java.util.ArrayList;
import java.util.List;

import org.junit.Test;

public class Demo1 {
@Test
public void test1(){
List list=new ArrayList();
list.add("111");
list.add("222");
list.add("333");
//传统方式手工转换
String i=(String) list.get(0);
//下面注释代码手工转换编辑不报错,运行错误
//Integer ii=(Integer) list.get(0);
System.out.println(i);
}
@Test
public void test2(){
List list=new ArrayList();
list.add("111");
list.add("222");
list.add("333");
//现在不需要强制转换
String i=list.get(0);
//下面注释代码编译通不过
//Integer ii=(Integer) list.get(0);
System.out.println(i);
}
} package cn.itcast.generic;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.junit.Test;

public class Demo2 {
@Test
public void test1(){
List list=new ArrayList();
list.add("111");
list.add("222");
list.add("333");
//传统
Iterator it=list.iterator();
while(it.hasNext()){
String value=it.next();
System.out.println(value);
}
//增强for
for(String s:list)
System.out.println(s);
}
@Test
public void test2(){
Map map=new HashMap();
map.put(1, "aaa");
map.put(2, "bbb");
map.put(3, "ccc");
//传统 keyset entryset
Set > set=map.entrySet();
Iterator >it=set.iterator();
while(it.hasNext()){
Map.Entryentry=it.next();
int key=entry.getKey();
String value=entry.getValue();
System.out.println(key+"="+value);
}
//增强for(重点)
for(Map.Entry entry:map.entrySet()){
int key=entry.getKey();
String value=entry.getValue();
}
}
}

7.11 自定义泛形——泛型方法
Java程序中的普通方法、构造方法和静态方法中都可以使用泛型。方法使用泛形前,必须对泛形进行声明,语法:,T可以是任意字母,但通常必须要大写。通常需放在方法的返回值声明之前。例如: public static void doxx(T t);
练习:
编写一个泛形方法,实现数组指定位置上元素的交换。
编写一个泛形方法,接收一个任意数组,并颠倒数组中的所有元素。
注意:
只有对象类型才能作为泛型方法的实际参数。
在泛型中可以同时有多个类型,例如:
public static V getValue(K key) { return map.get(key);}
7.12 自定义泛形——泛型类
如果一个类多处都要用到同一个泛型,可以把泛形定义在类上(即类级别的泛型),语法格式如下:
public class GenericDao {
private T field1;
public void save(T obj){}
public T getId(int id){}
}
注意,静态方法不能使用类定义的泛形,而应单独定义泛形。
package cn.itcast.generic;
//自定义带泛型的方法
public class Demo5 {
public void testa(){
a("aaaa");
}
public void a(T t){

}
public void b(T t,E e,K k){

}
} package cn.itcast.generic;
//自定义类上的泛型
public class Demo6 {
public void testa(){
//a("aaaa");
}
public void a(T t){

}
public void b(T t,E e,K k){

}
//类上的泛型不能作用于静态方法
public static void c(T t){
}
}

7.13 泛型的高级应用——通配符
定义一个方法,接收一个集合,并打印出集合中的所有元素,如下所示:
void print (Collection c) {
for (String e : c) {
System.out.println(e);
}
}
问题:该方法只能打印保存了Object对象的集合,不能打印其它集合。通配符用于解决此类问题,方法的定义可改写为如下形式:
void print (Collection> c) {   //Collection>(发音为:"collection of unknown")
for (Object e : c) {
System.out.println(e);
}
}
此种形式下需要注意的是:由于print方法c参数的类型为Collection>,即表示一种不确定的类型,因此在方法体内不能调用与类型相关的方法,例如add()方法。
总结:使用?通配符主要用于引用对象,使用了?通配符,就只能调对象与类型无关的方法,不能调用对象与类型有关的方法。
7.14 泛型的高级应用——有限制的通配符
限定通配符的上边界:
正确:Vector x = new Vector();
错误:Vector x = new Vector();
限定通配符的下边界:
正确:Vector x = new Vector();
错误:Vector x = new Vector();
问题:以下代码行不行?
public void add(List extends String> list){
list.add("abc");
}
7.15 Annotation(注解) 概述
从 JDK 5.0 开始, Java 增加了对元数据(MetaData) 的支持, 也就是 Annotation(注解)。
什么是Annotation,以及注解的作用?三个基本的 Annotation:
@Override: 限定重写父类方法, 该注解只能用于方法
@Deprecated: 用于表示某个程序元素(类, 方法等)已过时
@SuppressWarnings: 抑制编译器警告.
Annotation 其实就是代码里的特殊标记, 在Java技术里注解的典型应用是:可以通过反射技术去得到类里面的注解,以决定怎么去运行类。
掌握注解技术的要点:
如何定义注解
如何反射注解
7.16 自定义Annotation
定义新的 Annotation 类型使用 @interface 关键字
声明注解的属性:
Annotation 的属性声明方式:String name();
Annotation 属性默认值声明方式:
String name() default “xxx”;
特殊属性value:如果注解中有一个名称为value的属性,那么使用注解时,可以省略value=部分,例如:@MyAnnotation(“xxx")。
7.17 JDK 的元Annotation
元 Annotation指修饰Annotation的Annotation。JDK中定义了如下元Annotation:
@Retention: 只能用于修饰一个 Annotation 定义, 用于指定该 Annotation 可以保留的域, @Rentention 包含一个 RetentionPolicy 类型的成员变量, 通过这个变量指定域。
RetentionPolicy.CLASS: 编译器将把注释记录在 class 文件中. 当运行 Java 程序时, JVM 不会保留注释. 这是默认值
RetentionPolicy.RUNTIME:编译器将把注释记录在 class 文件中. 当运行 Java 程序时, JVM 会保留注释. 程序可以通过反射获取该注释
RetentionPolicy.SOURCE: 编译器直接丢弃这种策略的注释
@Target: 指定注解用于修饰类的哪个成员. @Target 包含了一个名为 value 的成员变量.
@Documented: 用于指定被该元 Annotation 修饰的 Annotation 类将被 javadoc 工具提取成文档.
@Inherited: 被它修饰的 Annotation 将具有继承性.如果某个类使用了被 @Inherited 修饰的 Annotation, 则其子类将自动具有该注解
7.18 提取Annotation 信息
JDK 5.0 在 java.lang.reflect 包下新增了 AnnotationElement 接口, 该接口代表程序中可以接受注释的程序元素
当一个 Annotation 类型被定义为运行时 Annotation 后, 该注释才是运行时可见, 当 class 文件被载入时保存在 class 文件中的 Annotation 才会被虚拟机读取
程序可以调用 AnnotationElement 对象的如下方法来访问 Annotation 信息

7.19 Tip:动态代理
在java里,每个对象都有一个类与之对应。
现在要生成某一个对象的代理对象,这个代理对象也要通过一个类来生成,所以首先要编写用于生成代理对象的类。
如何编写生成代理对象的类,两个要素:
代理谁
如何生成代理对象
代理谁?
设计一个类变量,以及一个构造函数,记住代理类代理哪个对象。
如何生成代理对象?
设计一个方法生成代理对象(在方法内编写代码生成代理对象是此处编程的难点)
Java提供了一个Proxy类,调用它的newInstance方法可以生成某个对象的代理对象,使用该方法生成代理对象时,需要三个参数:
1.生成代理对象使用哪个类装载器
2.生成哪个对象的代理对象,通过接口指定
3.生成的代理对象的方法里干什么事,由开发人员编写handler接口的实现来指定。
初学者必须理解,或不理解必须记住的2件事情:
Proxy类负责创建代理对象时,如果指定了handler(处理器),那么不管用户调用代理对象的什么方法,该方法都是调用处理器的invoke方法。
由于invoke方法被调用需要三个参数:代理对象、方法、方法的参数,因此不管代理对象哪个方法调用处理器的invoke方法,都必须把自己所在的对象、自己(调用invoke方法的方法)、方法的参数传递进来。


7.20 Tip:动态代理应用
在动态代理技术里,由于不管用户调用代理对象的什么方法,都是调用开发人员编写的处理器的invoke方法(这相当于invoke方法拦截到了代理对象的方法调用)。
并且,开发人员通过invoke方法的参数,还可以在拦截的同时,知道用户调用的是什么方法,因此利用这两个特性,就可以实现一些特殊需求,例如:拦截用户的访问请求,以检查用户是否有访问权限、动态为某个对象添加额外的功能。
7.21 类加载器
类加载器负责将 .class 文件(可能在磁盘上, 也可能在网络上) 加载到内存中, 并为之生成对应的 java.lang.Class 对象
当 JVM 启动时,会形成由三个类加载器组成的初始类加载器层次结构:

7.22 bootstrap classloader
bootstrap classloader:引导(也称为原始)类加载器,它负责加载Java的核心类。这个加载器的是非常特殊的,它实际上不是 java.lang.ClassLoader的子类,而是由JVM自身实现的。可以通过执行以下代码来获得bootstrap classloader加载了那些核心类库:
URL[] urls=sun.misc.Launcher.getBootstrapClassPath().getURLs();
for (int i = 0; i
元素类型可以是元素内容、或类型
如为元素内容:则需要使用()括起来,如


如为元素类型,则直接书写,DTD规范定义了如下几种类型:
EMPTY:用于定义空元素,例如


ANY:表示元素内容为任意类型。
元素内容中可以使用如下方式,描述内容的组成关系
元素内容使用空白符分隔,表示出现顺序没有要求: ×
用逗号分隔,表示内容的出现顺序必须与声明时一致。
用|分隔,表示任选其一,即多个只能出现一个

在元素内容中也可以使用+、*、?等符号表示元素出现的次数:
+: 一次或多次 (书+)
?: 0次或一次 (书?)
*: 0次或多次 (书*)
也可使用圆括号( )批量设置,例

7.27 Tip:属性定义
xml文档中的标签属性需通过ATTLIST为其设置属性
语法格式:

属性声明举例:

对应XML文件:
…商品>
…商品>
设置说明:
#REQUIRED:必须设置该属性
#IMPLIED:可以设置也可以不设置
#FIXED:说明该属性的取值固定为一个值,在 XML 文件中不能为该属性设置其它值。但需要为该属性提供这个值
直接使用默认值:在 XML 中可以设置该值也可以不设置该属性值。若没设置则使用默认值。
举例:

7.28 Tip:常用属性值类型
CDATA:表示属性值为普通文本字符串。
ENUMERATED
ID
ENTITY(实体)
7.29 Tip:属性值类型ENUMERATED
属性的类型可以是一组取值的列表,在 XML 文件中设置的属性值只能是这个列表中的某个值(枚举)

]>




购物篮>
7.30 Tip:属性值类型 ID
表示属性的设置值为一个唯一值。
ID 属性的值只能由字母,下划线开始,不能出现空白字符
7.31 Tip:实体定义
实体用于为一段内容创建一个别名,以后在XML文档中就可以使用别名引用这段内容了。
在DTD定义中,一条语句用于定义一个实体。
实体可分为两种类型:引用实体和参数实体。
7.32 Tip:实体定义引用实体
引用实体主要在 XML 文档中被应用
语法格式:
:直接转变成实体内容
引用方式:
&实体名称;
举例:

……
&copyright;
7.33 Tip:实体定义参数实体
参数实体被 DTD 文件自身使用
语法格式:

引用方式:
%实体名称;
举例1:




举例2:

...


7.34 Tip:XML解析技术概述
XML解析方式分为两种:dom和sax
dom:(Document Object Model, 即文档对象模型) 是 W3C 组织推荐的处理 XML 的一种方式。
sax: (Simple API for XML) 不是官方标准,但它是 XML 社区事实上的标准,几乎所有的 XML 解析器都支持它。
XML解析器
Crimson、Xerces 、Aelfred2
XML解析开发包
Jaxp、Jdom、dom4j
7.35 Tip:JAXP
JAXP 开发包是J2SE的一部分,它由javax.xml、org.w3c.dom 、org.xml.sax 包及其子包组成
在 javax.xml.parsers 包中,定义了几个工厂类,程序员调用这些工厂类,可以得到对xml文档进行解析的 DOM 或 SAX 的解析器对象。
7.36 Tip:使用JAXP进行DOM解析
javax.xml.parsers 包中的DocumentBuilderFactory用于创建DOM模式的解析器对象 , DocumentBuilderFactory是一个抽象工厂类,它不能直接实例化,但该类提供了一个newInstance方法 ,这个方法会根据本地平台默认安装的解析器,自动创建一个工厂的对象并返回。
7.37 Tip:获得JAXP中的DOM解析器
调用 DocumentBuilderFactory.newInstance() 方法得到创建 DOM 解析器的工厂。

调用工厂对象的 newDocumentBuilder方法得到 DOM 解析器对象。

调用 DOM 解析器对象的 parse() 方法解析 XML 文档,得到代表整个文档的 Document 对象,进行可以利用DOM特性对整个XML文档进行操作了。
package cn.itcast.xml;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.junit.Test;
import org.w3c.dom.Document;

public class Demo2 {
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
//1.创建工厂
DocumentBuilderFactory factory=DocumentBuilderFactory.newInstance();
//2。得到dom解析器
DocumentBuilder builder=factory.newDocumentBuilder();
//3。解析xml文档,得到代表文档的document
Document document=builder.parse("src/book.xml");
}
}
java web就业书名>
张孝祥作者>
40售价>
书>

C++教程书名>
自己作者>
50售价>
书>
书架>
package cn.itcast.xml;

import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.junit.Test;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

//使用dom方式对xml文档进行CRUD
public class Demo3 {
//读取C++教程书名>
public void read1() throws Exception
{
DocumentBuilderFactory factory=DocumentBuilderFactory.newInstance();
DocumentBuilder builder=factory.newDocumentBuilder();
Document document=builder.parse("src/book.xml");
NodeList list=document.getElementsByTagName("书名");
Node node=list.item(1);
String content=node.getTextContent();
System.out.println(content);//C++教程
}
//得到文档中所有标签
public void read2() throws Exception
{
DocumentBuilderFactory factory=DocumentBuilderFactory.newInstance();
DocumentBuilder builder=factory.newDocumentBuilder();
Document document=builder.parse("src/book.xml");
NodeList list=document.getElementsByTagName("书名");
Node root=document.getElementsByTagName("书架").item(0);
list(root);
}
private void list(Node node) {
Node child;
if (node instanceof Element)
System.out.println(node.getNodeName());
NodeList nodelist=node.getChildNodes();
for (int i=0;ijava web就业书名>
public void read3() throws Exception
{
DocumentBuilderFactory factory=DocumentBuilderFactory.newInstance();
DocumentBuilder builder=factory.newDocumentBuilder();
Document document=builder.parse("src/book.xml");


NodeList list=document.getElementsByTagName("书名");
Node node=list.item(0);
if(node.hasAttributes()){
NamedNodeMap nodemap=node.getAttributes();
for(int i=0;i10售价>
public void add() throws Exception{
DocumentBuilderFactory factory=DocumentBuilderFactory.newInstance();
DocumentBuilder builder=factory.newDocumentBuilder();
Document document=builder.parse("src/book.xml");
//创建节点
Element price=document.createElement("售价");
price.setTextContent("59.0元");
//把创建的节点放到第一本书上
document.getElementsByTagName("书").item(0).appendChild(price);
//把跟新后的内容写回文档
Transformer transformer=TransformerFactory.newInstance().newTransformer();
DOMSource source=new DOMSource(document);
FileOutputStream outstream =new FileOutputStream(new File("src/outbook.xml"));
StreamResult reslut=new StreamResult(outstream);
transformer.transform(source, reslut);
outstream.close();
}

//向文档中指定位置上添加节点 10售价>
public void add2() throws Exception{
DocumentBuilderFactory factory=DocumentBuilderFactory.newInstance();
DocumentBuilder builder=factory.newDocumentBuilder();
Document document=builder.parse("src/book.xml");
//创建节点
Element price=document.createElement("售价");
price.setTextContent("59.0元");
//得到参考节点
Element refNode=(Element)document.getElementsByTagName("售价").item(0);
//得到挂崽的节点
Element book=(Element)document.getElementsByTagName("书").item(0);
//把创建的节点放到第一本书上
document.getElementsByTagName("书").item(0).appendChild(price);
// 往book节点指定位置插值
book.insertBefore(price, refNode);
Transformer transformer=TransformerFactory.newInstance().newTransformer();
DOMSource source=new DOMSource(document);
FileOutputStream outstream =new FileOutputStream(new File("src/outbook2.xml"));
StreamResult reslut=new StreamResult(outstream);
transformer.transform(source, reslut);
outstream.close();
}
//向文档节点 添加属性 10售价>
public void addAtt() throws Exception{
DocumentBuilderFactory factory=DocumentBuilderFactory.newInstance();
DocumentBuilder builder=factory.newDocumentBuilder();
Document document=builder.parse("src/book.xml");

//得到参考节点
Element refNode=(Element)document.getElementsByTagName("售价").item(0);
refNode.setAttribute("addAtrr","new cha ru value");

Transformer transformer=TransformerFactory.newInstance().newTransformer();
DOMSource source=new DOMSource(document);
FileOutputStream outstream =new FileOutputStream(new File("src/outbook3.xml"));
StreamResult reslut=new StreamResult(outstream);
transformer.transform(source, reslut);
outstream.close();
}
//删除 10售价>
public void delete() throws Exception{
DocumentBuilderFactory factory=DocumentBuilderFactory.newInstance();
DocumentBuilder builder=factory.newDocumentBuilder();
Document document=builder.parse("src/book.xml");

//得到要删除的节点
Element refNode=(Element)document.getElementsByTagName("售价").item(0);
refNode.getParentNode().removeChild(refNode);

Transformer transformer=TransformerFactory.newInstance().newTransformer();
DOMSource source=new DOMSource(document);
FileOutputStream outstream =new FileOutputStream(new File("src/outbook3.xml"));
StreamResult reslut=new StreamResult(outstream);
transformer.transform(source, reslut);
outstream.close();
}
//更新 售价
public void update() throws Exception{
DocumentBuilderFactory factory=DocumentBuilderFactory.newInstance();
DocumentBuilder builder=factory.newDocumentBuilder();
Document document=builder.parse("src/book.xml");

//得到要更新的节点
Element refNode=(Element)document.getElementsByTagName("售价").item(0);
refNode.setTextContent("1000");

Transformer transformer=TransformerFactory.newInstance().newTransformer();
DOMSource source=new DOMSource(document);
FileOutputStream outstream =new FileOutputStream(new File("src/outbook3.xml"));
StreamResult reslut=new StreamResult(outstream);
transformer.transform(source, reslut);
outstream.close();
}
}

7.38 调虚拟机内存大小
-Xmx83886080 C:\Program Files\Java\jdk1.7.0\docs\technotes\tools\windows\java.html
-Xmx81920k
-Xmx80m
7.39 Tip:DOM编程
DOM模型(document object model)
DOM解析器在解析XML文档时,会把文档中的所有元素,按照其出现的层次关系,解析成一个个Node对象(节点)。
在dom中,节点之间关系如下:
位于一个节点之上的节点是该节点的父节点(parent)
一个节点之下的节点是该节点的子节点(children)
同一层次,具有相同父节点的节点是兄弟节点(sibling)
一个节点的下一个层次的节点集合是节点后代(descendant)
父、祖父节点及所有位于节点上面的,都是节点的祖先(ancestor)
节点类型(下页ppt)
Node对象提供了一系列常量来代表结点的类型,当开发人员获得某个Node类型后,就可以把Node节点转换成相应的节点对象(Node的子类对象),以便于调用其特有的方法。(查看API文档)
Node对象提供了相应的方法去获得它的父结点或子结点。编程人员通过这些方法就可以读取整个XML文档的内容、或添加、修改、删除XML文档的内容了。
7.40 Tip:DOM方式解析XML文件
DOM解析编程
遍历所有节点
查找某一个节点
删除结点
更新结点
添加节点
7.41 Tip:更新XML文档
javax.xml.transform包中的Transformer类用于把代表XML文件的Document对象转换为某种格式后进行输出,例如把xml文件应用样式表后转成一个html文档。利用这个对象,当然也可以把Document对象又重新写入到一个XML文件中。
Transformer类通过transform方法完成转换操作,该方法接收一个源和一个目的地。我们可以通过:
javax.xml.transform.dom.DOMSource类来关联要转换的document对象,
用javax.xml.transform.stream.StreamResult 对象来表示数据的目的地。
Transformer对象通过TransformerFactory获得。

7.42 Tip:SAX解析
在使用 DOM 解析 XML 文档时,需要读取整个 XML 文档,在内存中构架代表整个 DOM 树的Doucment对象,从而再对XML文档进行操作。此种情况下,如果 XML 文档特别大,就会消耗计算机的大量内存,并且容易导致内存溢出。

SAX解析允许在读取文档的时候,即对文档进行处理,而不必等到整个文档装载完才会文档进行操作。
SAX采用事件处理的方式解析XML文件,利用 SAX 解析 XML 文档,涉及两个部分:解析器和事件处理器:
解析器可以使用JAXP的API创建,创建出SAX解析器后,就可以指定解析器去解析某个XML文档。
解析器采用SAX方式在解析某个XML文档时,它只要解析到XML文档的一个组成部分,都会去调用事件处理器的一个方法,解析器在调用事件处理器的方法时,会把当前解析到的xml文件内容作为方法的参数传递给事件处理器。
事件处理器由程序员编写,程序员通过事件处理器中方法的参数,就可以很轻松地得到sax解析器解析到的数据,从而可以决定如何对数据进行处理。

阅读ContentHandler API文档,常用方法:startElement、endElement、characters
7.43 Tip:SAX方式解析XML文档
使用SAXParserFactory创建SAX解析工厂
SAXParserFactory spf = SAXParserFactory.newInstance();
通过SAX解析工厂得到解析器对象
SAXParser sp = spf.newSAXParser();
通过解析器对象得到一个XML的读取器
XMLReader xmlReader = sp.getXMLReader();
设置读取器的事件处理器
xmlReader.setContentHandler(new BookParserHandler());
解析xml文件
xmlReader.parse("book.xml");
package cn.itcast.sax;

import java.io.IOException;
import javax.xml.parsers.*;
import org.xml.sax.*;
public class Demo1 {
/**
* sax解析xml文档
*/
public static void main(String[] args) throws Exception {
//1.创建解析工厂
SAXParserFactory factory=SAXParserFactory.newInstance();
//2.得到解析器
SAXParser sp=factory.newSAXParser();
//3.得到读取器
XMLReader reader=sp.getXMLReader();
//4.设置内容处理器
reader.setContentHandler(new ListHandler());
//5.读取xml文档内容
reader.parse("src/book.xml");
}
}
//得到xml文档所有内容
class ListHandler implements ContentHandler{

@Override
public void characters(char[] ch, int start, int length)
throws SAXException {
System.out.println(new String(ch,start,length));
}
@Override
public void endDocument() throws SAXException {
// TODO Auto-generated method stub

}
@Override
public void endElement(String uri, String localName, String qName)
throws SAXException {
System.out.println(""+qName+">");
}
@Override
public void endPrefixMapping(String prefix) throws SAXException {
}
@Override
public void ignorableWhitespace(char[] ch, int start, int length)
throws SAXException {

}
@Override
public void processingInstruction(String target, String data)
throws SAXException {

}
@Override
public void setDocumentLocator(Locator locator) {

}
@Override
public void skippedEntity(String name) throws SAXException {

}
@Override
public void startDocument() throws SAXException {

}
@Override
public void startElement(String uri, String localName, String qName,
Attributes atts) throws SAXException {
System.out.println("");
for (int i=0;atts!=null && i list=handle.getList();
list.iterator();

}
}
//把每一本书封装到一个book对象,并把book对象
class BeanListHandler extends DefaultHandler{
private List list=new ArrayList();
public List getList() {
return list;
}

private String currentTag;
private Book book;
@Override
public void startElement(String uri, String localName, String qName,
Attributes attributes) throws SAXException {
currentTag=qName;
if("书".equals(currentTag)){
book=new Book();
}
}
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
if("书名".equals(currentTag)){
book.setName(new String(ch,start,length));
}
if("作者".equals(currentTag)){
book.setAuthor(new String(ch,start,length));
}
if("售价".equals(currentTag)){
book.setPrice(new String(ch,start,length));
}
}

@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
if(qName.equals("书")){
list.add(book);
book=null;
}
currentTag=null; //这句有必要
}
}

7.44 Tip:DOM4J解析XML文档
Dom4j是一个简单、灵活的开放源代码的库。Dom4j是由早期开发JDOM的人分离出来而后独立开发的。与JDOM不同的是,dom4j使用接口和抽象基类,虽然Dom4j的API相对要复杂一些,但它提供了比JDOM更好的灵活性。
Dom4j是一个非常优秀的Java XML API,具有性能优异、功能强大和极易使用的特点。现在很多软件采用的Dom4j,例如Hibernate,包括sun公司自己的JAXM也用了Dom4j。
使用Dom4j开发,需下载dom4j相应的jar文件。
//读第 2 本书的信息 C++教程书名>
private static void read() throws DocumentException {
SAXReader reader=new SAXReader();
Document document=reader.read(new File("src/book.xml"));
Element root=document.getRootElement();
Element book=(Element) root.elements("书").get(1);
String value=book.element("书名").getText();
String value2=book.element("书名").attributeValue("name");
System.out.println(value +";"+value2);
}
//在第一本上添加一个新的售价
public void add() throws Exception{
SAXReader reader=new SAXReader();
Document document=reader.read(new File("src/book.xml"));
Element book=document.getRootElement().element("书");
book.addElement("售价").setText("111");
//XMLWriter writer=new XMLWriter(new FileWriter("src/book.xml"));
//XMLWriter writer=new XMLWriter(new OutputStreamWriter(new FileOutputStream("src/book.xml"), "UTF-8"));

OutputFormat format = OutputFormat.createPrettyPrint();
format.setEncoding("gb2312");
XMLWriter writer=new XMLWriter(new OutputStreamWriter(new FileOutputStream("src/book.xml"), "gb2312"),format);

writer.write(document);
writer.close();
}
//在第一本书指定位置上添加一个新的售价,更改List集合
public void add2() throws Exception{
SAXReader reader=new SAXReader();
Document document=reader.read(new File("src/book.xml"));

Element book=document.getRootElement().element("书");
List list=book.elements();//[书名,作者,售价]

Element price=DocumentHelper.createElement("售价");
price.setText("309元");
list.add(2,price);

OutputFormat format = OutputFormat.createPrettyPrint();
format.setEncoding("gb2312");
XMLWriter writer=new XMLWriter(new OutputStreamWriter(new FileOutputStream("src/book.xml"), "gb2312"),format);

writer.write(document);
writer.close();
}
//删除上面的节点
public void delete() throws Exception {
SAXReader reader=new SAXReader();
Document document=reader.read(new File("src/book.xml"));

Element price=document.getRootElement().element("书").element("售价");
price.getParent().remove(price);
}
//更新节点
SAXReader reader=new SAXReader();
Document document=reader.read(new File("src/book.xml"));

Element book=(Element) document.getRootElement().elements("书").get(1);
book.element("作者").setText("张三");
OutputFormat format = OutputFormat.createPrettyPrint();
format.setEncoding("gb2312");
XMLWriter writer=new XMLWriter(new OutputStreamWriter(new FileOutputStream("src/book.xml"), "gb2312"),format);

writer.write(document);
writer.close();
//应用xpath提取xml文档的数据,需要包jaxen-1.1-beta-6.jar
SAXReader reader=new SAXReader();
Document document=reader.read(new File("src/book.xml"));
String value=document.selectSingleNode("//作者").getText();//第一个值
System.out.println(value);

7.45 Tip:Document对象
DOM4j中,获得Document对象的方式有三种:
1.读取XML文件,获得document对象
SAXReader reader = new SAXReader();
Document document = reader.read(new File("input.xml"));
2.解析XML形式的文本,得到document对象.
String text = "";
Document document = DocumentHelper.parseText(text);
3.主动创建document对象.
Document document = DocumentHelper.createDocument();
//创建根节点
Element root = document.addElement("members");
7.46 Tip:节点对象
1.获取文档的根节点.
Element root = document.getRootElement();
2.取得某个节点的子节点.
Element element=node.element(“书名");
3.取得节点的文字
String text=node.getText();
4.取得某节点下所有名为“member”的子节点,并进行遍历.
List nodes = rootElm.elements("member");
for (Iterator it = nodes.iterator(); it.hasNext();) {
Element elm = (Element) it.next();
// do something
}

5.对某节点下的所有子节点进行遍历.
for(Iterator it=root.elementIterator();it.hasNext();){
Element element = (Element) it.next();
// do something
}

6.在某节点下添加子节点.
Element ageElm = newMemberElm.addElement("age");
7.设置节点文字.
element.setText("29");

8.删除某节点.
//childElm是待删除的节点,parentElm是其父节点
parentElm.remove(childElm);
9.添加一个CDATA节点.
Element contentElm = infoElm.addElement("content");
contentElm.addCDATA(diary.getContent());
7.47 Tip:节点对象属性 
1.取得某节点下的某属性
Element root=document.getRootElement();
//属性名name
Attribute attribute=root.attribute("size");
2.取得属性的文字
String text=attribute.getText();
3.删除某属性
Attribute attribute=root.attribute("size");
root.remove(attribute);
3.遍历某节点的所有属性
Element root=document.getRootElement();
for(Iterator it=root.attributeIterator();it.hasNext();){
Attribute attribute = (Attribute) it.next();
String text=attribute.getText();
System.out.println(text);
}

4.设置某节点的属性和文字.
newMemberElm.addAttribute("name", "sitinspring");
5.设置属性的文字
Attribute attribute=root.attribute("name");
attribute.setText("sitinspring");
7.48 Tip:将文档写入XML文件
1.文档中全为英文,不设置编码,直接写入的形式.
XMLWriter writer = new XMLWriter(new FileWriter("output.xml"));
writer.write(document);
writer.close();

2.文档中含有中文,设置编码格式写入的形式.
OutputFormat format = OutputFormat.createPrettyPrint();
// 指定XML编码
format.setEncoding("GBK");
XMLWriter writer = new XMLWriter(newFileWriter("output.xml"),format);
writer.write(document);
writer.close();
7.49 Tip:Dom4j在指定位置插入节点
1.得到插入位置的节点列表(list)
2.调用list.add(index,elemnent),由index决定element的插入位置。
Element元素可以通过DocumentHelper对象得到。示例代码:

Element aaa = DocumentHelper.createElement("aaa");
aaa.setText("aaa");

List list = root.element("书").elements();
list.add(1, aaa);

//更新document
7.50 Tip:字符串与XML的转换
1.将字符串转化为XML

String text = "sitinspring";
Document document = DocumentHelper.parseText(text);

2.将文档或节点的XML转化为字符串.

SAXReader reader = new SAXReader();
Document document = reader.read(new File("input.xml"));
Element root=document.getRootElement();

String docXmlText=document.asXML();

String rootXmlText=root.asXML();
Element memberElm=root.element("member");
String memberXmlText=memberElm.asXML();
7.51 XML Schema
XML Schema 也是一种用于定义和描述 XML 文档结构与内容的模式语言,其出现是为了克服 DTD 的局限性
XML Schema VS DTD:
XML Schema符合XML语法结构。
DOM、SAX等XML API很容易解析出XML Schema文档中的内容。
XML Schema对名称空间支持得非常好。
XML Schema比XML DTD支持更多的数据类型,并支持用户自定义新的数据类型。
XML Schema定义约束的能力非常强大,可以对XML实例文档作出细致的语义限制。
XML Schema不能像DTD一样定义实体,比DTD更复杂,但Xml Schema现在已是w3c组织的标准,它正逐步取代DTD。
7.52 Schema约束快速入门
XML Schema 文件自身就是一个XML文件,但它的扩展名通常为.xsd。
一个XML Schema文档通常称之为模式文档(约束文档),遵循这个文档书写的xml文件称之为实例文档。
和XML文件一样,一个XML Schema文档也必须有一个根结点,但这个根结点的名称为Schema。
编写了一个XML Schema约束文档后,通常需要把这个文件中声明的元素绑定到一个URI地址上,在XML Schema技术中有一个专业术语来描述这个过程,即把XML Schema文档声明的元素绑定到一个名称空间上,以后XML文件就可以通过这个URI(即名称空间)来告诉解析引擎,xml文档中编写的元素来自哪里,被谁约束。
7.53 Schema入门案例
JavaScript网页开发张孝祥28.00元
7.54 名称空间的概念
在XML Schema中,每个约束模式文档都可以被赋以一个唯一的名称空间,名称空间用一个唯一的URI(Uniform Resource Identifier,统一资源标识符)表示。 在Xml文件中书写标签时,可以通过名称空间声明(xmlns),来声明当前编写的标签来自哪个Schema约束文档。如:
……
此处使用itcast来指向声明的名称,以便于后面对名称空间的引用。
注意:名称空间的名字语法容易让人混淆,尽管以 http:// 开始,那个 URL 并不指向一个包含模式定义的文件。事实上,这个 URL:http://www.itcast.cn根本没有指向任何文件,只是一个分配的名字。
7.55 使用名称空间引入Schema
为了在一个XML文档中声明它所遵循的Schema文件的具体位置,通常需要在Xml文档中的根结点中使用schemaLocation属性来指定,例如:

schemaLocation此属性有两个值。第一个值是需要使用的命名空间。第二个值是供命名空间使用的 XML schema 的位置,两者之间用空格分隔。
注意,在使用schemaLocation属性时,也需要指定该属性来自哪里。
7.56 使用默认名称空间
基本格式:
xmlns="URI"
举例:


JavaScript网页开发书名>
张孝祥作者>
28.00元售价>
书>

7.57 使用名称空间引入多个XML Schema文档
文件清单:xmlbook.xml
JavaScript网页开发书名>
张孝祥作者>
28.00元售价>
书>
书架>
7.58 不使用名称空间引入XML Schema文档
文件清单:xmlbook.xml
JavaScript网页开发书名>
张孝祥作者>
28.00元售价>
书>
书架>
7.59 在XML Schema文档中声明名称空间


targetNamespace元素用于指定schema文档中声明的元素属于哪个名称空间。
elementFormDefault元素用于指定,该schema文档中声明的根元素及其所有子元素都属于targetNamespace所指定的名称空间。


8 HTTP协议
8.1 什么是HTTP协议
客户端连上web服务器后,若想获得web服务器中的某个web资源,需遵守一定的通讯格式,HTTP协议用于定义客户端与web服务器通迅的格式。

使用telnet程序连上web服务器,并使用HTTP协议获取某个页面,以快速了解 HTTP协议的作用。

安装IE浏览器插件HttpWatch,查看IE浏览器通过HTTP协议获取某个页面。
import java.io.*;
import java.net.*;

public class Server {
public static void main(String[] args) throws Exception {
ServerSocket server=new ServerSocket(9999);
Socket sock=server.accept();
FileInputStream in=new FileInputStream("d:\\qq\\1.html");
OutputStream out=sock.getOutputStream();
int len=0;
byte buffer[]=new byte[1024];
while((len=in.read(buffer))>0){
out.write(buffer,0,len);
}
in.close();
out.close();
sock.close();
server.close();
}
}

Weblogic tomcat WebSphere
Fport工具查看端口占用的程序

Java_home环境变量的设置问题
只要在setclasspath.bat批处理文件第一次使用JAVA_HOME环境变量之前的任何地方,将JAVA_HOME环境变量设置为JDK的主目录,就可以使用startup.bat文件启动Tomcat了。
端口占用问题
Catalina_home环境变量的设置问题 指明Tomcat在哪里,当硬盘上有多个Tomcat时,点任何一个startup.bat时,都是运行同一个。(一般不配)
8.2 Tip:配置虚拟目录
Web应用开发好后,若想供外界访问,需要把web应用交给web服务器管理,这个过程称之为配置虚拟目录。
tomcat服务器会自动管理webapps目录下的所有web应用,并把它映射成虚似目录。换句话说,只要把web项目放置到tomcat服务器的webapps目录中,不需要做其它设置,这个web应用就可以直接被外界访问了。
对计算机中的任意位置的WEB应用,若想被外界访问,就需要手工通知web服务器去管理,即通知web服务器把其映射成虚似目录,这样才能供外界访问。

8.3 Tip2:HTTP协议简介
HTTP是hypertext transfer protocol(超文本传输协议)的简写,它是TCP/IP协议的一个应用层协议,用于定义WEB浏览器与WEB服务器之间交换数据的过程。
HTTP协议是学习JavaWEB开发的基石,不深入了解HTTP协议,就不能说掌握了WEB开发,更无法管理和维护一些复杂的WEB站点。
HTTP协议的版本:HTTP/1.0、HTTP/1.1
8.4 Tip3:HTTP1.0和HTTP1.1的区别
在HTTP1.0协议中,客户端与web服务器建立连接后,只能获得一个web资源。
HTTP1.1协议,允许客户端与web服务器建立连接后,在一个连接上获取多个web资源。
使用telnet举例说明。
一个好多同学搞不清楚的问题:
一个web页面中,使用img标签引用了三幅图片,当客户端访问服务器中的这个web页面时,客户端总共会访问几次服务器,即向服务器发送了几次HTTP请求。
8.5 Tip4:HTTP请求
客户端连上服务器后,向服务器请求某个web资源,称之为客户端向服务器发送了一个HTTP请求。一个完整的HTTP请求包括如下内容:
一个请求行、若干消息头、以及实体内容,其中的一些消息头和实体内容都是可选的,消息头和实体内容之间要用空行隔开。如下所示:
GET /books/java.html HTTP/1.1
Accept: */*
Accept-Language: en-us
Connection: Keep-Alive
Host: localhost
Referer: http://localhost/links.asp
User-Agent: Mozilla/4.0
Accept-Encoding: gzip, deflate 请求行


多个消息头


一个空行

8.6 Tip5:HTTP请求的细节——请求行
请求行中的GET称之为请求方式,请求方式有:
POST、GET、HEAD、OPTIONS、DELETE、TRACE、PUT
常用的有:POST、GET
不管POST或GET,都用于向服务器请求某个WEB资源,这两种方式的区别主要表现在数据传递上,客户端通过这两种方式都可以带一些数据给服务器:
如请求方式为GET方式,则可以在请求的URL地址后以?的形式带上交给服务器的数据,多个数据之间以&进行分隔,例如:
GET /mail/1.html?name=abc&password=xyz HTTP/1.1
GET方式的特点:在URL地址后附带的参数是有限制的,其数据容量不能超过1K。
如请求方式为POST方式,则可以在请求的实体内容中向服务器发送数据,例如:
POST /servlet/ParamsServlet HTTP/1.1
Host:
Content-Type: application/x-www-form-urlencoded
Content-Length: 28

name=abc&password=xyz
Post方式的特点:传送的数据量无限制。
Tip6:HTTP请求的细节——消息头
用于HTTP请求中的常用头
Accept: text/html,image/*
Accept-Charset: ISO-8859-1
Accept-Encoding: gzip,compress
Accept-Language: en-us,zh-cn
Host: www.it315.org:80
If-Modified-Since: Tue, 11 Jul 2000 18:23:51 GMT
Referer: http://www.it315.org/index.jsp
User-Agent: Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 5.0)
Cookie
Connection: close/Keep-Alive
Date: Tue, 11 Jul 2000 18:23:51 GMT
一个HTTP响应代表服务器向客户端回送的数据,它包括:
一个状态行、若干消息头、以及实体内容,其中的一些消息头和实体内容都是可选的,消息头和实体内容之间要用空行隔开。
HTTP/1.1 200 OK
Server: Microsoft-IIS/5.0
Date: Thu, 13 Jul 2000 05:46:53 GMT
Content-Length: 2291
Content-Type: text/html
Cache-control: private


状态行


多个消息头


一个空行
实体内容

状态行
格式: HTTP版本号 状态码 原因叙述
举例:HTTP/1.1 200 OK
状态码用于表示服务器对请求的处理结果,它是一个三位的十进制数。响应状态码分为5类,如下所示:
状态码 含义
100~199 表示成功接收请求,要求客户端继续提交下一次请求才能完成整个处理过程
200~299 表示成功接收请求并已完成整个处理过程,常用200
300~399 为完成请求,客户需进一步细化请求。例如,请求的资源已经移动一个新地址,常用302、307和304
400~499 客户端的请求有错误,常用404
500~599 服务器端出现错误,常用500

8.7 Tip9:HTTP响应细节——常用响应头
HTTP请求中的常用响应头
Location: http://www.it315.org/index.jsp
Server:apache tomcat
Content-Encoding: gzip
Content-Length: 80
Content-Language: zh-cn
Content-Type: text/html; charset=GB2312
Last-Modified: Tue, 11 Jul 2000 18:23:51 GMT
Refresh: 1;url=http://www.it315.org
Content-Disposition: attachment; filename=aaa.zip
Transfer-Encoding: chunked
Set-Cookie:SS=Q0=5Lb_nQ; path=/search
Expires: -1
Cache-Control: no-cache
Pragma: no-cache
Connection: close/Keep-Alive
Date: Tue, 11 Jul 2000 18:23:51 GMT
8.8 Tip10:HTTP请求的细节—通用信息头
通用信息头指既能用于请求,又能用于响应的一些消息头。
Cache-Control: no-cache
Pragma: no-cache
Connection: close/Keep-Alive
Date: Tue, 11 Jul 2000 18:23:51 GMT
8.9 Tip11:作业
请写一篇关于HTTP协议的笔记,要求:
描述清楚HTTP请求头、响应头的格式
请求头和响应头中各个头字段的含义
如果浏览器传递给WEB服务器的参数内容超过1K,应该使用那种方式发送请求消息?
请描述200、302、304、404和500等响应状态码所表示的意义。
请列举三种禁止浏览器缓存的头字段,并写出相应的设置值。
请求
1.From: 请求发送者的email地址,由一些特殊的Web客户程序使用,浏览器不会用到它。
2.Accept: 浏览器可接受的MIME类型。
3.Accept-Encoding: 浏览器能够进行解码的数据编码方式,比如gzip。
4.Accept-Language: 浏览器所希望的语言种类,当服务器能够提供一种以上的语言版本时要用到。
5.User-Agent:
6.Referer: 包含一个URL,用户从该URL代表的页面出发访问当前请求的页面。
7.Authorization:
8.ChargeTo:
9.Pragma:
Accept-Charset: 浏览器可接受的字符集。
If-Modified-Since: 只有当所请求的内容在指定的日期之后又经过修改才返回它,否则返回304“Not Modified”应答。
Cookie: 设置cookie,这是最重要的请求头信息之一
Content-Length: 表示请求消息正文的长度。
Host: 初始URL中的主机和端口。
User-Agent 浏览器类型,如果Servlet返回的内容与浏览器类型有关则该值非常有用。
Connection 表示是否需要持久连接。如果Servlet看到这里的值为“Keep-Alive”,或者看到请求使用的是HTTP 1.1(HTTP 1.1默认进行持久连接),它就可以利用持久连接的优点,当页面包含多个元素时(例如Applet,图片),显著地减少下载所需要的时间。要实现这一点, Servlet需要在应答中发送一个Content-Length头,最简单的实现方法是:先把内容写入ByteArrayOutputStream,然后在正式写出内容之前计算它的大小。
UA-Pixels,UA-Color,UA-OS,UA-CPU: 由某些版本的IE浏览器所发送的非标准的请求头,表示屏幕大小、颜色深度、操作系统和CPU类型。
Range Range头域可以请求实体的一个或者多个子范围。例如,
表示头500个字节:bytes=0-499
表示第二个500字节:bytes=500-999
表示最后500个字节:bytes=-500
表示500字节以后的范围:bytes=500-
第一个和最后一个字节:bytes=0-0,-1
同时指定几个范围:bytes=500-600,601-999
但是服务器可以忽略此请求头,如果无条件GET包含Range请求头,响应会以状态码206(PartialContent)返回而不是以200(OK)。

响应头字段仅用于响应消息
Accept-Ranges  
Age
ETag 缓存相关的头,能做到实时,其他的头只做到秒一级。
Last-Modified    文档的最后改动时间。客户可以通过If-Modified-Since请求头提供一个日期,该请求将被视为一个条件GET,只有改动时间迟于指定时间的文档才会返回,否则返回一个304(Not Modified)状态。
Location 这个头配合302使用,用于告诉浏览器去找哪个资源。Location通常不是直接设置的,而是通过HttpServletResponse的sendRedirect方法,该方法同时设置状态代码为302。
Proxy-Authenticate
Retry-After     
Vary
WWW-Authenticate
Content-Type 表示后面的文档属于什么MIME类型。Servlet默认为text/plain,但通常需要显式地指定为text/html。
Content-Encoding 文档的编码(Encode)方法。只有在解码之后才可以得到Content-Type头指定的内容类型。利用gzip压缩文档能够显著地减少HTML文档的下载时间。Java的GZIPOutputStream可以很方便地进行gzip压缩,但只有Unix上的Netscape和Windows上的IE 4、IE 5才支持它。
Cache-Control no-cache
Pragma no-cache
Expires 应该在什么时候认为文档已经过期,从而不再缓存它?-1 或0 不缓存
Refresh 表示浏览器应该在多少时间之后刷新文档,以秒计。除了刷新当前文档之外,你还可以通过setHeader("Refresh", "5; URL=http://host/path")让浏览器读取指定的页面。
Content-Disposition 以下载方式打开
Transfer-Encoding 数据的传送方式
Accept-Range 源服务器如果接受字节范围请求(byte-range request)那么可以发送 Accept-Ranges: bytes 但是没有必要这样做。客户端在没有接收此头域时也可以产生字节范围请求(byte-range request)。服务器如果不能接受任何类型的范围请求(range request),将会发送Accept-Ranges:none去告诉客户不要尝试范围请求(range request)。
Content-Range 指定了返回的Web资源的字节范围。格式是:Content-Range:1000-3000/5000

数据压缩数据
String data="aaaa";
ByteArrayOutputStream bout=new ByteArrayOutputStream();
GZIPOutputStream gout=new GZIPOutputStream(bout);
gout.write(data.getBytes());//包装流一般有缓冲
gout.close();//为确保有数据
byte gzip[]=bout.toByteArray();//得到压缩后的数据
response.setHeader("Content-Encoding", "gzip");
response.setHeader("Content-Length", gzip.length+"");

response.getOutputStream().write(gzip);

显示图片
response.setHeader("content-type", "image/bmp");
InputStream in=this.getServletContext().getResourceAsStream("/1.bmp");
int len=0;
byte buffer[]=new byte[1024];
OutputStream out=response.getOutputStream();
while((len=in.read(buffer))>0){
out.write(buffer,0,len);
}

以下载方式打开
response.setHeader("content-disposition", "attachment;filename=3.jpg");
InputStream in=this.getServletContext().getResourceAsStream("/1.bmp");
int len=0;
byte buffer[]=new byte[1024];
OutputStream out=response.getOutputStream();
while((len=in.read(buffer))>0){
out.write(buffer,0,len);
}

9 Servlet开发
9.1 Tip:Servlet简介
Servlet是sun公司提供的一门用于开发动态web资源的技术。
Sun公司在其API中提供了一个servlet接口,用户若想用发一个动态web资源(即开发一个Java程序向浏览器输出数据),需要完成以下2个步骤:
编写一个Java类,实现servlet接口。
把开发好的Java类部署到web服务器中。
编写一个Java程序,向浏览器输出“hello servlet”。
编写这个程序需要解决的2个问题:
在Java程序中,如何才能向IE浏览器输出数据?
输出hello servlet的java代码应该写在程序的哪个方法内?
阅读Servlet API,解决以上两个问题。
9.2 Servlet在web应用中的位置

9.3 Tip:Servlet的运行过程(课后看)
Servlet程序是由WEB服务器调用,web服务器收到客户端的Servlet访问请求后:
①Web服务器首先检查是否已经装载并创建了该Servlet的实例对象。如果是,则直接执行第④步,否则,执行第②步。
②装载并创建该Servlet的一个实例对象。
③调用Servlet实例对象的init()方法。
④创建一个用于封装HTTP请求消息的HttpServletRequest对象和一个代表HTTP响应消息的HttpServletResponse对象,然后调用Servlet的service()方法并将请求和响应对象作为参数传递进去。
⑤WEB应用程序被停止或重新启动之前,Servlet引擎将卸载Servlet,并在卸载之前调用Servlet的destroy()方法。

9.4 Tip:在Eclipse中开发Servlet

新建一个j2ee 1.4的工程,完成后说Incompatible java Compliance Level 点No,用新的(高版本)编译器,

添加一个tomcat服务器 window/Preference/MyEclipse/Servers/Timcat/Tomcat 7.x选择目录和Enable

更改模板刚换上Myeclipse9.0,结果要修改servlet模板的时候不像Myeclpse6.5一样能搜索的到servlet.java了.
网上搜了下也没有搜到,还好求助了下老师,方法如下.
在x:\Program Files\MyEclipse\Common\plugins下找到com.genuitec.eclipse.wizards_9.0.0.me201012172208.jar,然后用winrar打开,找到templates打开后就能找到Servlet.java了.
要是您的是8.5的话,搜:com.genuitec.eclipse.wizard*.jar 这个关键词就行了.
拷贝一个工程后,更改发布路径,工程/属性/MyEclipse/Web改发布路径
9.5 Tip:Servlet接口实现类
Servlet接口SUN公司定义了两个默认实现类,分别为:GenericServlet、HttpServlet。

HttpServlet指能够处理HTTP请求的servlet,它在原有Servlet接口上添加了一些与HTTP协议处理方法,它比Servlet接口的功能更为强大。因此开发人员在编写Servlet时,通常应继承这个类,而避免直接去实现Servlet接口。

HttpServlet在实现Servlet接口时,覆写了service方法,该方法体内的代码会自动判断用户的请求方式,如为GET请求,则调用HttpServlet的doGet方法,如为Post请求,则调用doPost方法。因此,开发人员在编写Servlet时,通常只需要覆写doGet或doPost方法,而不要去覆写service方法。

阅读HttpServlet API文档
9.6 Tip:Servlet的一些细节(1)
由于客户端是通过URL地址访问web服务器中的资源,所以Servlet程序若想被外界访问,必须把servlet程序映射到一个URL地址上,这个工作在web.xml文件中使用元素和元素完成。
元素用于注册Servlet,它包含有两个主要的子元素:,分别用于设置Servlet的注册名称和Servlet的完整类名。
一个元素用于映射一个已注册的Servlet的一个对外访问路径,它包含有两个子元素:,分别用于指定Servlet的注册名称和Servlet的对外访问路径。例如:
AnyNameHelloServletAnyName/demo/hello.html
同一个Servlet可以被映射到多个URL上,即多个元素的子元素的设置值可以是同一个Servlet的注册名。
在Servlet映射到的URL中也可以使用*通配符,但是只能有两种固定的格式:一种格式是“*.扩展名”,另一种格式是以正斜杠(/)开头并以“/*”结尾。

AnyName

*.do

AnyName

/action/*


对于如下的一些映射关系:
Servlet1 映射到 /abc/*
Servlet2 映射到 /*
Servlet3 映射到 /abc
Servlet4 映射到 *.do
问题:
URL 都匹配 哪个servlet响应
/abc/a.html “/abc/*”和“/*” Servlet1
/abc “/abc/*”和“/abc” Servlet3
/abc/a.do “/abc/*”和“*.do” Servlet1
/a.do “/*”和“*.do” Servlet2
/xxx/yyy/a.do “/*”和“*.do” Servlet2

Servlet是一个供其他Java程序(Servlet引擎)调用的Java类,它不能独立运行,它的运行完全由Servlet引擎来控制和调度。

针对客户端的多次Servlet请求,通常情况下,服务器只会创建一个Servlet实例对象,也就是说Servlet实例对象一旦创建,它就会驻留在内存中,为后续的其它请求服务,直至web容器退出,servlet实例对象才会销毁。

在Servlet的整个生命周期内,Servlet的init方法只被调用一次。而对一个Servlet的每次访问请求都导致Servlet引擎调用一次servlet的service方法。对于每次访问请求,Servlet引擎都会创建一个新的HttpServletRequest请求对象和一个新的HttpServletResponse响应对象,然后将这两个对象作为参数传递给它调用的Servlet的service()方法,service方法再根据请求方式分别调用doXXX方法。
如果在元素中配置了一个元素,那么WEB应用程序在启动时,就会装载并创建Servlet的实例对象、以及调用Servlet实例对象的init()方法。
举例:
invoker
org.apache.catalina.servlets.InvokerServlet
2

是一个正整数,数字越小,先启动。
用途:为web应用写一个InitServlet,这个servlet配置为启动时装载,为整个web应用创建必要的数据库表和数据。
如果某个Servlet的映射路径仅仅为一个正斜杠(/),那么这个Servlet就成为当前Web应用程序的缺省Servlet。
凡是在web.xml文件中找不到匹配的元素的URL,它们的访问请求都将交给缺省Servlet处理,也就是说,缺省Servlet用于处理所有其他Servlet都不处理的访问请求。
\conf\web.xml文件中,注册了一个名称为org.apache.catalina.servlets.DefaultServlet的Servlet,并将这个Servlet设置为了缺省Servlet。
当访问Tomcat服务器中的某个静态HTML文件和图片时,实际上是在访问这个缺省Servlet。
9.7 Tip:Servlet的一些细节(7)—线程安全
当多个客户端并发访问同一个Servlet时,web服务器会为每一个客户端的访问请求创建一个线程,并在这个线程上调用Servlet的service方法,因此service方法内如果访问了同一个资源的话,就有可能引发线程安全问题。
如果某个Servlet实现了SingleThreadModel接口,那么Servlet引擎将以单线程模式来调用其service方法。
SingleThreadModel接口中没有定义任何方法,只要在Servlet类的定义中增加实现SingleThreadModel接口的声明即可。
对于实现了SingleThreadModel接口的Servlet,Servlet引擎仍然支持对该Servlet的多线程并发访问,其采用的方式是产生多个Servlet实例对象,并发的每个线程分别调用一个独立的Servlet实例对象。
实现SingleThreadModel接口并不能真正解决Servlet的线程安全问题,因为Servlet引擎会创建多个Servlet实例对象,而真正意义上解决多线程安全问题是指一个Servlet实例对象被多个线程同时调用的问题。事实上,在Servlet API 2.4中,已经将SingleThreadModel标记为Deprecated(过时的)。
//子类在覆盖福雷的方法是,不能抛出比父类更多的异常
9.8 Tip:ServletConfig对象
在Servlet的配置文件中,可以使用一个或多个标签为servlet配置一些初始化参数。
当servlet配置了初始化参数后,web容器在创建servlet实例对象时,会自动将这些初始化参数封装到ServletConfig对象中,并在调用servlet的init方法时,将ServletConfig对象传递给servlet。进而,程序员通过ServletConfig对象就可以得到当前servlet的初始化参数信息。
阅读ServletConfig API,并举例说明该对象的作用:
获得字符集编码
获得数据库连接信息
获得配置文件,查看struts案例的web.xml文件


9.9 Tip:ServletContext
WEB容器在启动时,它会为每个WEB应用程序都创建一个对应的ServletContext对象,它代表当前web应用。

ServletContext对象被包含在ServletConfig对象中,开发人员在编写servlet时,可以通过ServletConfig.getServletContext方法获得对ServletContext对象的引用。

由于一个WEB应用中的所有Servlet共享同一个ServletContext对象,因此Servlet对象之间可以通过ServletContext对象来实现通讯。ServletContext对象通常也被称之为context域对象。

查看ServletContext API文档,了解ServletContext对象的功能。
package cn.itcast;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
//ServletConfig用于封装servlet的配置
public class ServletDemo5 extends HttpServlet {
private ServletConfig config;
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String value=this.getServletConfig().getInitParameter("data");
//通常用value方法,因为父类已经写了
String value2=config.getInitParameter("data");
System.out.println(value+","+value2);
response.getOutputStream().write("ServeltDemo2!!".getBytes());
//得到所有的
Enumeration e= this.getInitParameterNames();
while(e.hasMoreElements()){
String a=(String) e.nextElement();
String v=this.getServletConfig().getInitParameter(a);
System.out.println(a+"="+v);
}

}
@Override
public void init(ServletConfig config) throws ServletException {
this.config=config;
super.init(config);
}
}

9.10 Tip:ServletContext应用
多个Servlet通过ServletContext对象实现数据共享。
package cn.itcast;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class ServletDemo6 extends HttpServlet {
//servletContext示例
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//得到servletContext
ServletContext context=this.getServletConfig().getServletContext();
ServletContext context2=this.getServletContext();
String data="aaaaa";
context.setAttribute("data", data);
}
} package cn.itcast;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/*
servletContext域:1,是一个容器 2。作用范围是应用程序范围
*/
public class ServletDemo8 extends HttpServlet {

public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String value = (String) this.getServletContext().getAttribute("data");
response.getOutputStream().write(value.getBytes());
}
}

获取WEB应用的初始化参数。

datazzzz this.getServletContext().getAttribute("data");

实现Servlet的转发。
RequestDispatcher rd=this.getServletContext().getRequestDispatcher("/1.jsp");
rd.forward(request, response);

利用ServletContext对象读取资源文件。
得到文件路径
读取资源文件的三种方式
.properties文件(属性文件)
url=jdbc:mysql://localhost:3306/test
username=root
password=root
db.properties放在 src目录下,因为该目录下的资源文件发布时会发到/WEB-INF/classes目录下

InputStreamin=this.getServletContext().getResourceAsStream("/WEB-INF/classes/db.properties");
Properties props=new Properties();
props.load(in);
System.out.println(props.getProperty("url"));
//in2的相对路径是tomcat的bin目录,所以这种方法要在该目录下建立文件夹classes,并把文件放在这里
File file=new File("");
response.getOutputStream().write(file.getAbsolutePath().getBytes());
//以上两行代码得到当前路径C:\apache-tomcat-7.0.22\bin
FileInputStream in2=new FileInputStream("classes/db.properties");
Properties props=new Properties();
props.load(in2);
//得到绝对路径
String path=this.getServletContext().getRealPath("/WEB-INF/classes/db.properties");
String filename=path.substring(path.lastIndexOf("\\"));
FileInputStream in2=new FileInputStream(path);
Properties props=new Properties();
props.load(in2);
package cn.itcast;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import cn.itcast.dao.UserDao;

//servlet调用其它程序,在其它程序中如何去读取配置文件
//通过类装载器
public class ServletDemo12 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
UserDao user=new UserDao();
user.update();
}
} package cn.itcast.dao;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Properties;

//如果读取资源文件的程序不是servlet的话,
//就只能通过类转载器去读了,文件不能太大
//用传递参数方法不好,耦合性高
public class UserDao {

private static Properties dbconfig=new Properties();
static {
InputStream in=UserDao.class.getClassLoader().getResourceAsStream("db.properties");
try {
dbconfig.load(in);
} catch (IOException e) {
throw new ExceptionInInitializerError(e);
}
//上面代码类装载器只装载一次,下面代码用类装载方式得到文件位置
URL url=UserDao.class.getClassLoader().getResource("db.properties");
String str=url.getPath();
//file:/C:/apache-tomcat-7.0.22/webapps/day05/WEB-INF/classes/db.properties
try {
InputStream in2=new FileInputStream(str);
try {
dbconfig.load(in2);
} catch (IOException e) {
throw new ExceptionInInitializerError(e);
}
} catch (FileNotFoundException e1) {
throw new ExceptionInInitializerError(e1);
}
}
public void update() {
System.out.println(dbconfig.get("url"));
}
}

9.11 Tip:Servlet高级应用—Servlet与缓存
设置缓存的两种场景:
场景一:对于不经常变化的数据,在servlet中可以为其设置合理的缓存时间值,以避免浏览器频繁向服务器发送请求,提升服务器的性能。
场景二:如果要实现一种高级功能,即客户端请求动态web资源时,动态web资源发现发给客户端的数据更新了,就给客户端发送最新的数据,如果发现数据没有更新,则动态web资源就要客户端就去访问它自己缓存的数据。此种情况可以通过覆写动态web资源(即servlet)的getLastModify方法予以实现。
getLastModified方法由service方法调用,默认情况下,getLastModified方法返回一个负数,开发人员在编写servlet时,如果不覆盖getLastModified方法,则每次访问servlet时,service方法发现getLastModified方法返回负数,它就会调用doXXX方法向客户端返回最新的数据。此种情况下,服务器在向客户端返回doXXX方法返回的数据时,不会在数据上加Last-Modified头字段。

如果编写servlet时,覆盖了getLastModified方法,并返回某一个时间值,则客户端访问Servlet时,service方法首先会检查客户端是否通过If-Modified-Since头字段带一个时间值过来。如果没有的话,则service方法会调用doXXX方法向客户端返回最新的数据。在返回数据时,service方法还会调用getLastModified方法得到一个时间值,并以这个时间值在数据上加上一个Last-Modified头字段。(即通知客户端缓存数据)

客户端在访问servlet时,如果通过If-Modified-Since头字段带了一个时间值过来,则service方法在调用doXXX方法之前,它会先调用getLastModified方法,得到一个时间值,并与客户端带过来的时间值进行比较,如果比客户端的时间值要新,则service方法调用doXXX方法向客户端返回最新的数据。如果要旧,则service方法而不会调用doXXX方法向客户端返回数据,而是返回一个304的状态码给客户端,通知客户端在拿它缓存中的数据。
9.12 Tip:getLastModified方法与缓存

9.13 Tip:缓存的应用
一个网站有很多静态资源,例如css文件、html页面、gif图片等等,这些文件一旦创建,有可能永远不会更新。当客户端第一次访问这些文件时,服务器在把文件数据交给客户端的同时,就应该通知客户端缓存这些文件,以后客户端每次访问,服务器如果发现文件没更新,则应要客户端去拿它缓存中的文件,以减轻服务器的压力。

编程:使用一个servlet读取一个文件数据给客户端,当文件数据未更新时,通知客户端去访问它缓存中的数据,如果文件数据更新了,则向客户端返回最新数据。

Tomcat服务器中的所有静态web资源,都是由一个缺省servlet负责读取回送给客户端的,它就是以上方式来提升服务器的性能。

9.14 response、request对象
Web服务器收到客户端的http请求,会针对每一次请求,分别创建一个用于代表请求的request对象、和代表响应的response对象。
request和response对象即然代表请求和响应,那我们要获取客户机提交过来的数据,只需要找request对象就行了。要向客户机输出数据,只需要找response对象就行了。
HttpServletResponse对象服务器的响应。这个对象中封装了向客户端发送数据、发送响应头,发送响应状态码的方法。
setHeader(java.lang.String name, java.lang.String value)
Sets a response header with the given name and value.
setStatus(int sc) Sets the status code for this response.
getWriter() Returns a PrintWriter object that can send character text to the client.
getOutputStream() Returns a ServletOutputStream suitable for writing binary data in the response.


Tip:response常见应用
向客户端输出中文数据


分别以OutputStream和PrintWriter输出
String data="中国 ";
//程序以什么码表输出,一定要控制浏览器以什么码表打开
response.setHeader("Content-type", "text/html;charset=UTF-8");
OutputStream out=response.getOutputStream();
//out.write(data.getBytes());
out.write(data.getBytes("UTF-8"));
String data="中国2 ";
//html: 标签模拟一个http响应头
OutputStream out=response.getOutputStream();
//out.write(data.getBytes());
out.write("".getBytes());
out.write(data.getBytes("UTF-8"));

String data="中国q";
//设置response使用的码表,以控制response以什么码表向浏览器输入数据,默认为ISO-8859-1
response.setCharacterEncoding("GB2312");
//指定浏览器以什么码表打开服务器发送的数据
response.setHeader("Content-type", "text/html;charset=GB2312");
//response.setHeader("Content-type", "text/html;charset=ISO-8859-1");
PrintWriter out=response.getWriter();
out.write(data);//
String data="中国q";
//该句代码等价于其后面两句
response.setContentType("text/html;charset=GB2312");
//response.setCharacterEncoding("GB2312");
//response.setHeader("Content-type", "text/html;charset=ISO-8859-1");
PrintWriter out=response.getWriter();
out.write(data);//

用OutputStream输出1,客户端看到的是什么?
9.15 文件下载
String path=this.getServletContext().getRealPath("/download/new.jpg");
//C:\apache-tomcat-7.0.22\webapps\day06\download\new.jsp
String filename=path.substring(path.lastIndexOf("\\")+1);

InputStream in=null;
OutputStream out=null;
response.setHeader("content-disposition", "attachment;filename="+filename);
//如果下载文件是中文文件,则文件名需要经过url编码
response.setHeader("content-disposition", "attachment;filename="+URLEncoder.encode(filename,"UTF-8"));

try {
in=new FileInputStream(path);
int len=0;
byte buffer[]=new byte[1024];
out=response.getOutputStream();
while((len=in.read(buffer))>0){
out.write(buffer,0,len);
}
}finally{
if(in!=null){
try{
in.close();
}catch(Exception e){
e.printStackTrace();
}
}
}


生成随机图片

9.16 发送http头,控制浏览器定时刷新网页(REFRESH)
response.setHeader("refresh", "3");
String data = new Random().nextInt(100000)+"";
response.getWriter().write(data);
response.setHeader("refresh", "3;url='/day06/index.jsp'");
response.setContentType("text/html;charset=GB2312");
String data = new Random().nextInt(100000)+"";
response.getWriter().write("登录成功,将在3秒后跳转,如果没有,请点超链接");
String message="登录成功,将在3秒后跳转,如果没有,请点超链接";
request.setAttribute("message",message);
this.getServletContext().getRequestDispatcher("/message.jsp").forward(request, response);

9.17 发送http头,控制浏览器缓存当前文档内容
String data="aaaaa";
response.setDateHeader("expires", System.currentTimeMillis()+3600*1000);
response.getWriter().write(data);

多学一招:使用HTML语言里面的标签来控制浏览器行为
9.18 通过response实现请求重定向。
response.setStatus(302);// 302状态码和location头即可实现重定向
response.setHeader("location", "/day06/index.jsp");
response.sendRedirect("/day06/index.jsp");
请求重定向指:一个web资源收到客户端请求后,通知客户端去访问另外一个web资源,这称之为请求重定向。
应用场景:用户注册,让用户知道注册成功,重定向到首页。
购物网站购完后,转到购物车显示页面,用转发按刷新又买一个,所以用重定向。
9.19 Tip: response细节
getOutputStream和getWriter方法分别用于得到输出二进制数据、输出文本数据的ServletOuputStream、Printwriter对象。
getOutputStream和getWriter这两个方法互相排斥,调用了其中的任何一个方法后,就不能再调用另一方法,包括转发时。
Servlet程序向ServletOutputStream或PrintWriter对象中写入的数据将被Servlet引擎从response里面获取,Servlet引擎将这些数据当作响应消息的正文,然后再与响应状态行和各响应头组合后输出到客户端。
Serlvet的service方法结束后,Servlet引擎将检查getWriter或getOutputStream方法返回的输出流对象是否已经调用过close方法,如果没有,Servlet引擎将调用close方法关闭该输出流对象。
9.20 HttpServletRequest
HttpServletRequest对象代表客户端的请求,当客户端通过HTTP协议访问服务器时,HTTP请求头中的所有信息都封装在这个对象中,开发人员通过这个对象的方法,可以获得客户这些信息。
getMethod()
Returns the name of the HTTP method with which this request was made, for example, GET, POST, or PUT.
getParameter(java.lang.String name)
Returns the value of a request parameter as a String, or null if the parameter does not exist.
getRequestURI()
Returns the part of this request's URL from the protocol name up to the query string in the first line of the HTTP request.
getRequestURL()
Reconstructs the URL the client used to make the request.
Http://www.sina.com/news/1.html URL 子
/news/1.html URI 父

获得客户机信息

getRequestURL方法返回客户端发出请求时的完整URL。
getRequestURI方法返回请求行中的资源名部分。
getQueryString 方法返回请求行中的参数部分。
getPathInfo方法返回请求URL中的额外路径信息。额外路径信息是请求URL中的位于Servlet的路径之后和查询参数之前的内容,它以“/”开头。
getRemoteAddr方法返回发出请求的客户机的IP地址
getRemoteHost方法返回发出请求的客户机的完整主机名
getRemotePort方法返回客户机所使用的网络端口号
getLocalAddr方法返回WEB服务器的IP地址。
getLocalName方法返回WEB服务器的主机名
获得客户机请求头
getHeader方法
getHeaders方法
getHeaderNames方法
获得客户机请求参数(客户端提交的数据)
getParameter方法
getParameterValues(String name)方法
getParameterNames方法
getParameterMap方法
Tip: request常见应用1
防盗链
9.21 //获取头相关数据
String headValue=request.getHeader("Accept-Encoding");
Enumeration e=request.getHeaderNames();
while(e.hasMoreElements()){
String value=(String) e.nextElement();
System.out.println(value);
}

e=request.getHeaderNames();
while(e.hasMoreElements()){
String name=(String) e.nextElement();
String value=request.getHeader(name);
System.out.println("name="+name+",value="+value);
}
}

String value=request.getParameter("username");
System.out.println(value);

Enumeration e=request.getParameterNames();
while(e.hasMoreElements()){
String name=(String) e.nextElement();
value=request.getParameter(name);
System.out.println(name+"="+value);
}

String value2[]=request.getParameterValues("username");
for(String v:value2){
System.out.println("u="+v);
}

Map map=request.getParameterMap();
User user=new User();
User formbean=new User();
try {
BeanUtils.populate(user, map);//用map集合填充bean
BeanUtils.copyProperties(user, formbean);//bean的拷贝
} catch (Exception e1) {
e1.printStackTrace();
}
System.out.println(user);

InputStream in=request.getInputStream();
byte buffer[]=new byte[1024];
int len=0;
while((len=in.read(buffer))>0){
System.out.println(new String(buffer,0,len));
}

9.22 各种表单输入项数据的获取
text、password、radio、checkbox、file、select、textarea、 hidden、image、button给js编程用



带数据给requestDemo3



用户名1
:

密码:


性别:




所在地:

爱好:
唱歌
跳舞
篮球
足球


备注:

大头照



request.getParameter("username");
request.getParameter("password");
request.getParameter("gender");
request.getParameter("city");
request.getParameter("city");
String[] likes=request.getParameterValues("likes");
for(int i=0;likes!=null && i
useBodyEncodingForURI
This specifies if the encoding specified in contentType should be used for URI query parameters, instead of using the URIEncoding. This setting is present for compatibility with Tomcat 4.1.x, where the encoding specified in the contentType, or explicitly set using Request.setCharacterEncoding method was also used for the parameters from the URL. The default value is false.
//无乱码
request.setCharacterEncoding("UTF-8");
String username=request.getParameter("username");
response.setCharacterEncoding("gb2312");
response.setContentType("text/htm;charset=gb2312");
response.getWriter().write(username);

Javascript防止表单重复提交
URL地址的编码
request对象实现请求转发:请求转发指一个web资源收到客户端请求后,通知服务器去调用另外一个web资源进行处理。
请求转发的应用场景:MVC设计模式
request对象提供了一个getRequestDispatcher方法,该方法返回一个RequestDispatcher对象,调用这个对象的forward方法可以实现请求转发。
request对象同时也是一个域对象,开发人员通过request对象在实现转发时,把数据通过request对象带给其它web资源处理。
setAttribute方法
getAttribute方法
removeAttribute方法
getAttributeNames方法
//servlet中
String message="aaaaaa";
request.setAttribute("data", message);
request.getRequestDispatcher("/mesage.jsp").forward(request, response);
Jsp中


9.24 Tip: 请求转发的细节
forward方法用于将请求转发到RequestDispatcher对象封装的资源。
如果在调用forward方法之前,在Servlet程序中写入的部分内容已经被真正地传送到了客户端,forward方法将抛出IllegalStateException异常。
如果在调用forward方法之前向Servlet引擎的缓冲区中写入了内容,只要写入到缓冲区中的内容还没有被真正输出到客户端,forward方法就可以被正常执行,原来写入到输出缓冲区中的内容将被清空,但是,已写入到HttpServletResponse对象中的响应头字段信息保持有效。
9.25 Tip:请求重定向和请求转发的区别
一个web资源收到客户端请求后,通知服务器去调用另外一个web资源进行处理,称之为请求转发。
一个web资源收到客户端请求后,通知浏览器去访问另外一个web资源,称之为请求重定向。
RequestDispatcher.forward方法只能将请求转发给同一个WEB应用中的组件;而HttpServletResponse.sendRedirect 方法还可以重定向到同一个站点上的其他应用程序中的资源,甚至是使用绝对URL重定向到其他站点的资源。
如果传递给HttpServletResponse.sendRedirect 方法的相对URL以“/”开头,它是相对于整个WEB站点的根目录;如果创建RequestDispatcher对象时指定的相对URL以“/”开头,它是相对于当前WEB应用程序的根目录。
调用HttpServletResponse.sendRedirect方法重定向的访问过程结束后,浏览器地址栏中显示的URL会发生改变,由初始的URL地址变成重定向的目标URL;调用RequestDispatcher.forward 方法的请求转发过程结束后,浏览器地址栏保持初始的URL地址不变。
HttpServletResponse.sendRedirect方法对浏览器的请求直接作出响应,响应的结果就是告诉浏览器去重新发出对另外一个URL的访问请求;RequestDispatcher.forward方法在服务器端内部将请求转发给另外一个资源,浏览器只知道发出了请求并得到了响应结果,并不知道在服务器程序内部发生了转发行为。
RequestDispatcher.forward方法的调用者与被调用者之间共享相同的request对象和response对象,它们属于同一个访问请求和响应过程;而HttpServletResponse.sendRedirect方法调用者与被调用者使用各自的request对象和response对象,它们属于两个独立的访问请求和响应过程。
9.26 Tip: RequestDispatcher
include方法:
RequestDispatcher.include方法用于将RequestDispatcher对象封装的资源内容作为当前响应内容的一部分包含进来,从而实现可编程的服务器端包含功能。
被包含的Servlet程序不能改变响应消息的状态码和响应头,如果它里面存在这样的语句,这些语句的执行结果将被忽略。
request.getRequestDispatcher("/public/head.jsp").include(request, response);
response.getWriter().write("hahaha");
request.getRequestDispatcher("/public/foot.jsp").include(request, response);

//给服务器用 /代表当前应用
//给浏览器用 /代表网站,网站下有多个应用
request.getRequestDispatcher("/form1.html").include(request, response);
response.sendRedirect("/day06/form1.html");
this.getServletContext().getRealPath("/form1.html");
this.getServletContext().getResourceAsStream("/fomr1.html");
/**
*
xx



*/

String referer=request.getHeader("referer");//利用referer防盗链
if(referer==null && !referer.startsWith("http://localhost"))
{
response.sendRedirect("/day06/index.jsp");
return;
}
response.getOutputStream().write("bbb".getBytes());


9.27 会话管理
什么是会话?
会话可简单理解为:用户开一个浏览器,点击多个超链接,访问服务器多个web资源,然后关闭浏览器,整个过程称之为一个会话。
会话过程中要解决的一些问题?
每个用户在使用浏览器与服务器进行会话的过程中,不可避免各自会产生一些数据,服务器要想办法为每个用户保存这些数据。
例如:多个用户点击超链接通过一个servlet各自购买了一个商品,服务器应该想办法把每一个用户购买的商品保存在各自的地方,以便于这些用户点结帐servlet时,结帐servlet可以得到用户各自购买的商品为用户结帐。
提问:这些数据保存在request或servletContext中行不行?

9.28 Tip:保存会话数据的两种技术:
Cookie
Cookie是客户端技术,服务器把每个用户的数据以cookie的形式写给用户各自的浏览器。当用户使用浏览器再去访问服务器中的web资源时,就会带着各自的数据去。这样,web资源处理的就是用户各自的数据了。
Session
Session是服务器端技术,利用这个技术,服务器在运行时可以为每一个用户的浏览器创建一个其独享的session对象,由于session为用户浏览器独享,所以用户在访问服务器的web资源时,可以把各自的数据放在各自的session中,当用户再去访问服务器中的其它web资源时,其它web资源再从用户各自的session中取出数据为用户服务。
9.29 Tip:Cookie技术


9.30 Tip:session


9.31 Tip:Cookie API
javax.servlet.http.Cookie类用于创建一个Cookie,response接口也中定义了一个addCookie方法,它用于在其响应头中增加一个相应的Set-Cookie头字段。 同样,request接口中也定义了一个getCookies方法,它用于获取客户端提交的Cookie。Cookie类的方法:
public Cookie(String name,String value)
setValue与getValue方法
setMaxAge与getMaxAge方法 //不调用此方法,是浏览器进程,关闭浏览器cookie就消失
setPath与getPath方法 //day06,如果没用此方法,是谁发的cooki,他所在的目录就带cookie过来
setDomain与getDomain方法 .sina.com
getName方法
9.32 Tip:Cookie应用
显示用户上次访问时间
response.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
out.print("上次访问时间:");
Cookie cookies[]=request.getCookies();
for(int i=0;cookies!=null && i");
Mapmap=Db.getAll();
for(Map.Entry entry:map.entrySet()){
Book book=entry.getValue();
out.print(""+book.getName()+"
");

}
//显示用户看过的商品
out.print("
你曾经看过的商品
");
Cookie cookies[]=request.getCookies();
for(int i=0;cookies!=null && i"+book.getName()+"
");
}
}
}
}
}

class Db{
private static Map map=new LinkedHashMap();
static {
map.put("1", new Book("1","JavaWeb开发","老k","一本好书"));
map.put("2", new Book("2","jdbc开发","老张","一本好书"));
map.put("3", new Book("3","spring开发","老li","一本好书"));
map.put("4", new Book("4","struts开发","老张","一本好书"));
map.put("5", new Book("5","android开发","老bi","一本好书"));
}
public static Map getAll(){
return map;
}
}

class Book{
private String id;
private String name;
private String author;
private String description;
public Book() {
super();
}
public Book(String id, String name, String author, String description) {
this.id = id;
this.name = name;
this.author = author;
this.description = description;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
package cn.itcast.cookie;

import java.io.*;
import java.util*;

import javax.servlet.*;
import javax.servlet.http.* ;
//显示详细信息的servlet

public class CookieDemo4 extends HttpServlet {

public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//根据用户带过来的id,显示相应的详细信息
response.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
String id=request.getParameter("id");
Book book=(Book)Db.getAll().get(id);
out.write(book.getId()+"
");
out.write(book.getName()+"
");
out.write(book.getAuthor()+"
");
out.write(book.getDescription()+"
");
//2.构建cookie,回写给浏览器;
String cookieValue=buildCookie(id,request);
Cookie cookie=new Cookie("bookHistory",cookieValue);
cookie.setMaxAge(1*30*24*3600);//1 个月
cookie.setPath("/day07");
response.addCookie(cookie);
}

private String buildCookie(String id, HttpServletRequest request) {
//bookHistory =null 1 1
//bookHistory=2,5,1 1 1,2,5
//bookHistory=2,5,4 1 1,2,5
//bookHistroy=2,5 1 1,2,5 // 假如列表最多3个
String bookHistroy=null;
Cookie cookies[]=request.getCookies();
for(int i=0;cookies!=null && i list=Arrays.asList(bookHistroy.split("\\,"));
LinkedList linkedlist=new LinkedList(list);
if(list.contains(id)){
linkedlist.remove(id);
linkedlist.addFirst(id);
}else{
if(list.size()>=3){
linkedlist.removeLast();
linkedlist.addFirst(id);
}else
linkedlist.addFirst(id);
}
StringBuffer sb=new StringBuffer();
for(String bid : linkedlist){
sb.append(bid+",");
}
return sb.deleteCharAt(sb.length()-1).toString();
}
}

9.35 Tip:显示上次浏览商品的实现过程

9.36 Tip:session
在WEB开发中,服务器可以为每个用户浏览器创建一个会话对象(session对象),注意:一个浏览器独占一个session对象(默认情况下)。因此,在需要保存用户数据时,服务器程序可以把用户数据写到用户浏览器独占的session中,当用户使用浏览器访问其它程序时,其它程序可以从用户的session中取出该用户的数据,为用户服务。
Session和Cookie的主要区别在于:
Cookie是把用户的数据写给用户的浏览器。
Session技术把用户的数据写到用户独占的session中。
Session对象由服务器创建,开发人员可以调用request对象的getSession方法得到session对象。
不是访问一个页面就创建session,而是遇到语句request..getSession();就创建了一个session。
注意,虽然代码相同,但不同浏览器得到的各自的数据

Session是发呆时间,而不是累计时间
60
HttpSession session=request.getSession();
// request.getSession(false);只获取,而不创建,通常用在查看购物车上,有些人没买商品就点查看
session.setAttribute("name", "洗衣机");
session.setMaxInactiveInterval(3600);//1小时
session.invalidate();//摧毁

Session小实验:使用IE访问某一个servlet,其它IE可以取到这个servlet存的数据吗?
9.37 Tip:session案例
使用Session完成简单的购物功能
//ListBookServlet
response.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
out.print("本站有如下商品
");
Map map=Db.getAll();
for(Map.Entry entry:map.entrySet()){
Book book=entry.getValue();
String str=""+book.getName()+"购买"+"
";
out.print(str);
}
System.out.println("a"+request.getContextPath()+"b");

//BuyServlet
response.setContentType("UTF-8");
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
String id=request.getParameter("id");
Book book=(Book)Db.getAll().get(id);
HttpSession session=request.getSession();
//用session中得到用户购买的商品集合
List list=(List)session.getAttribute("list");
if(list==null){
list=new LinkedList();
session.setAttribute("list", list);
}
list.add(book);
response.sendRedirect(request.getContextPath()+"/servlet/listCartServlet");

//ListCartServlet
response.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
HttpSession session=request.getSession(false);
if(session==null){
out.write("您没有购买任何商品");return;
}
List list=(List) session.getAttribute("list");
out.write("你购买了如下商品");
for(Book book:list){
out.write(book.getName()+"
");
}

9.38 Tip:session实现原理
疑问:服务器是如何实现一个session为一个用户浏览器服务的?



问题:如何实现多个IE浏览器共享同一session?(应用:关掉IE后,再开IE,上次购买的商品还在。)
HttpSession session=request.getSession();
session.setAttribute("name", "洗衣机");
String id=session.getId();
Cookie cookie=new Cookie("JSESSIONID",id);
cookie.setPath("/day07");
cookie.setMaxAge(30*60);//30 minutes
response.addCookie(cookie);

9.39 Tip:IE禁用Cookie后的session处理
实验演示禁用Cookie后servlet共享数据导致的问题。
解决方案:URL重写
response. encodeRedirectURL(java.lang.String url)
用于对sendRedirect方法后的url地址进行重写。
response. encodeURL(java.lang.String url)
用于对表单action和超链接的url地址进行重写

target="_blank">购买
target="_blank">结账


附加:
Session的失效
Web.xml文件配置session失效时间
9.40 Tip:session案例
使用Session完成用户登陆
利用Session实现一次性验证码
利用Session防止表单重复提交

不足:但用户单击”刷新”,或单击”后退”再次提交表单,将导致表单重复提交
9.41 Tip:session案例-防止表单重复提交
表单页面由servlet程序生成,servlet为每次产生的表单页面分配一个唯一的随机标识号,并在FORM表单的一个隐藏字段中设置这个标识号,同时在当前用户的Session域中保存这个标识号。
当用户提交FORM表单时,负责处理表单提交的serlvet得到表单提交的标识号,并与session中存储的标识号比较,如果相同则处理表单提交,处理完后清除当前用户的Session域中存储的标识号。
在下列情况下,服务器程序将拒绝用户提交的表单请求:
存储Session域中的表单标识号与表单提交的标识号不同
当前用户的Session中不存在表单标识号
用户提交的表单数据中没有标识号字段
编写工具类生成表单标识号:TokenProcessor
//首页

public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//产生随机数(表单号)
TokenProcessor tp=TokenProcessor.getInstance();
String token=tp.generateToken();
request.getSession().setAttribute("token", token);
request.getRequestDispatcher("/form.jsp").forward(request, response);
}
class TokenProcessor{//令牌
/*
* 1。构造 方法私有
* 2。自己创建一个
* 3。对外暴露一个方法,允许获取上面创建的对象 *
*/
private TokenProcessor(){}
private static final TokenProcessor instance=new TokenProcessor();
public static TokenProcessor getInstance(){
return instance;

}
public String generateToken(){
String token=System.currentTimeMillis()+new Random().nextInt()+"";
try {
MessageDigest md=MessageDigest.getInstance("md5");
byte []md5=md.digest(token.getBytes());
//base64编码
BASE64Encoder encoder=new BASE64Encoder();//文档没有正式发布
return encoder.encode(md5);
//return new String(md5);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
//return null;
}
}
//form.jsp



My JSP 'form.jsp' starting page




用户名:



//处理提交的servlet
request.setCharacterEncoding("UTF-8");
/*
* String username=request.getParameter("username"); try {
* Thread.sleep(1000*3); } catch (InterruptedException e) { // TODO
* Auto-generated catch block e.printStackTrace(); }
* System.out.println("向数据库写用户名");
*/
String r_token = request.getParameter("token");
HttpSession session = request.getSession(false);
if (r_token != null
&& session != null
&& r_token.equalsIgnoreCase((String) session.getAttribute("token"))) {
request.getSession().removeAttribute("token");
System.out.println("向数据库写用户名");
} else {
System.out.println("重复提交");
}

9.42 Tip:session案例一次性校验码
一次性验证码的主要目的就是为了限制人们利用工具软件来暴力猜测密码。
服务器程序接收到表单数据后,首先判断用户是否填写了正确的验证码,只有该验证码与服务器端保存的验证码匹配时,服务器程序才开始正常的表单处理流程。
密码猜测工具要逐一尝试每个密码的前题条件是先输入正确的验证码,而验证码是一次性有效的,这样基本上就阻断了密码猜测工具的自动地处理过程。
Request 显示完就不用了
session 显示完等下还要用,用户登录
context 显示完等下还要用,还要给别人用,如聊天室

9.43 应用Session+Cookie技术完成用户自动登陆功能

10 Java Server Pages
JSP全称是Java Server Pages,它和servle技术一样,都是SUN公司定义的一种用于开发动态web资源的技术。

JSP这门技术的最大的特点在于,写jsp就像在写html,但:
它相比html而言,html只能为用户提供静态数据,而Jsp技术允许在页面中嵌套java代码,为用户提供动态数据。
相比servlet而言,servlet很难对数据进行排版,而jsp除了可以用java代码产生动态数据的同时,也很容易对数据进行排版。

Jsp快速入门:在jsp页面中输出当前时间。
把英文版myeclipse 8.6中jsp默认编码格式改为pageEncoding="utf-8"
windows -- > Preferences -- > MyEclipse -- > Files and Editors -- > JSP
然后右边选择Encoding为 ISO 10646/Unicode(UTF-8)

10.1 Tip:JSP最佳实践
不管是JSP还是Servlet,虽然都可以用于开发动态web资源。但由于这2门技术各自的特点,在长期的软件实践中,人们逐渐把servlet作为web应用中的控制器组件来使用,而把JSP技术作为数据显示模板来使用。

其原因为,程序的数据通常要美化后再输出:
让jsp既用java代码产生动态数据,又做美化会导致页面难以维护。
让servlet既产生数据,又在里面嵌套html代码美化数据,同样也会导致程序可读性差,难以维护。
因此最好的办法就是根据这两门技术的特点,让它们各自负责各的,servlet只负责响应请求产生数据,并把数据通过转发技术带给jsp,数据的显示jsp来做。
10.2 Tip:JSP原理
目标:
Web服务器是如何调用并执行一个jsp页面的?
Jsp页面中的html排版标签是如何被发送到客户端的?
Jsp页面中的java代码服务器是如何执行的?
Web服务器在调用jsp时,会给jsp提供一些什么java对象?

思考:JSP为什么可以像servlet一样,也可以叫做动态web资源的开发技术?
10.3 Tip:JSP语法
JSP模版元素
JSP表达式
JSP脚本片断
JSP注释
JSP指令
JSP标签
JSP内置对象
如何查找JSP页面中的错误
10.4 Tip: JSP模版元素
JSP页面中的HTML内容称之为JSP模版元素。
JSP模版元素定义了网页的基本骨架,即定义了页面的结构和外观。
10.5 Tip: JSP脚本表达式
JSP脚本表达式(expression)用于将程序数据输出到客户端
语法:
举例:当前时间:
JSP引擎在翻译脚本表达式时,会将程序数据转成字符串,然后在相应位置用out.print(…) 将数据输给客户端。
JSP脚本表达式中的变量或表达式后面不能有分号(;)。
10.6 Tip: JSP脚本片断(1)
JSP脚本片断(scriptlet)用于在JSP页面中编写多行Java代码。语法:

注意:JSP脚本片断中只能出现java代码,不能出现其它模板元素, JSP引擎在翻译JSP页面中,会将JSP脚本片断中的Java代码将被原封不动地放到Servlet的_jspService方法中。
JSP脚本片断中的Java代码必须严格遵循Java语法,例如,每执行语句后面必须用分号(;)结束。
在一个JSP页面中可以有多个脚本片断,在两个或多个脚本片断之间可以嵌入文本、HTML标记和其他JSP元素。
举例:

这是JSP页面文本



多个脚本片断中的代码可以相互访问,犹如将所有的代码放在一对之中的情况。如:out.println(x);
单个脚本片断中的Java语句可以是不完整的,但是,多个脚本片断组合后的结果必须是完整的Java语句,例如:


www.it315.org




10.7 Tip: JSP声明
JSP页面中编写的所有代码,默认会翻译到servlet的service方法中, 而Jsp声明中的java代码被翻译到_jspService方法的外面。语法:

所以,JSP声明可用于定义JSP页面转换成的Servlet程序的静态代码块、成员变量和方法 。

多个静态代码块、变量和函数可以定义在一个JSP声明中,也可以分别单独定义在多个JSP声明中。

JSP隐式对象的作用范围仅限于Servlet的_jspService方法,所以在JSP声明中不能使用这些隐式对象。
10.8 Tip: JSP声明案例


10.9 Tip: JSP注释
JSP注释的格式:

JSP引擎在将JSP页面翻译成Servlet程序时,忽略JSP页面中被注释的内容。
10.10 Tip: JSP指令
JSP指令(directive)是为JSP引擎而设计的,它们并不直接产生任何可见输出,而只是告诉引擎如何处理JSP页面中的其余部分。在JSP 2.0规范中共定义了三个指令:
page指令
Include指令
taglib指令
10.11 Tip: JSP指令简介
JSP指令的基本语法格式:

举例:
如果一个指令有多个属性,这多个属性可以写在一个指令中,也可以分开写。
例如:


也可以写作:

10.12 Tip: Page指令
page指令用于定义JSP页面的各种属性,无论page指令出现在JSP页面中的什么地方,它作用的都是整个JSP页面,为了保持程序的可读性和遵循良好的编程习惯,page指令最好是放在整个JSP页面的起始位置。
JSP 2.0规范中定义的page指令的完整语法:

JSP 引擎自动导入下面的包:
java.lang.*
javax.servlet.*
javax.servlet.jsp.*
javax.servlet.http.*
errorPage属性的设置值必须使用相对路径,如果以“/”开头,表示相对于当前WEB应用程序的根目录(注意不是站点根目录),否则,表示相对于当前页面。
可以在web.xml文件中使用元素为整个WEB应用程序设置错误处理页面,其中的子元素指定异常类的完全限定名,元素指定以“/”开头的错误处理页面的路径。
如果设置了某个JSP页面的errorPage属性,那么在web.xml文件中设置的错误处理将不对该页面起作用。

JSP引擎会根据page指令的contentType属性生成相应的调用ServletResponse.setContentType方法的语句。
page指令的contentType属性还具有说明JSP源文件的字符编码的作用。

10.13 Tip:使用page指令解决JSP中文乱码
JSP程序存在有与Servlet程序完全相同的中文乱码问题
输出响应正文时出现的中文乱码问题
读取浏览器传递的参数信息时出现的中文乱码问题
JSP引擎将JSP页面翻译成Servlet源文件时也可能导致中文乱码问题
JSP引擎将JSP源文件翻译成的Servlet源文件默认采用UTF-8编码,而JSP开发人员可以采用各种字符集编码来编写JSP源文件,因此,JSP引擎将JSP源文件翻译成Servlet源文件时,需要进行字符编码转换。
如果JSP文件中没有说明它采用的字符集编码,JSP引擎将把它当作默认的ISO8859-1字符集编码处理。
如何解决JSP引擎翻译JSP页面时的中文乱码问题
通过page指令的contentType属性说明JSP源文件的字符集编码
page指令的pageEncoding属性说明JSP源文件的字符集编码
10.14 Tip: include指令
include指令用于引入其它JSP页面,如果使用include指令引入了其它JSP页面,那么JSP引擎将把这两个JSP翻译成一个servlet。所以include指令引入通常也称之为静态引入。在编译时包含
语法:

其中的file属性用于指定被引入文件的相对路径。 file属性的设置值必须使用相对路径,如果以“/”开头,表示相对于当前WEB应用程序的根目录(注意不是站点根目录),否则,表示相对于当前文件。
细节:
被引入的文件必须遵循JSP语法。
被引入的文件可以使用任意的扩展名,即使其扩展名是html,JSP引擎也会按照处理jsp页面的方式处理它里面的内容,为了见明知意,JSP规范建议使用.jspf(JSP fragments)作为静态引入文件的扩展名。
由于使用include指令将会涉及到2个JSP页面,并会把2个JSP翻译成一个servlet,所以这2个JSP页面的指令不能冲突(除了pageEncoding和导包除外)。
request.getRequestDispatcher("/a.jsp").forward(request,response);//动态包含,翻译成多个servlet,运行时包含
10.15 Tip: taglib指令
Taglib指令用于在JSP页面中导入标签库,讲自定义标签技术时讲。
10.16 Tip:JSP运行原理和九大隐式对象
每个JSP 页面在第一次被访问时,WEB容器都会把请求交给JSP引擎(即一个Java程序)去处理。JSP引擎先将JSP翻译成一个_jspServlet(实质上也是一个servlet) ,然后按照servlet的调用方式进行调用。
由于JSP第一次访问时会翻译成servlet,所以第一次访问通常会比较慢,但第二次访问,JSP引擎如果发现JSP没有变化,就不再翻译,而是直接调用,所以程序的执行效率不会受到影响。
JSP引擎在调用JSP对应的_jspServlet时,会传递或创建9个与web开发相关的对象供_jspServlet使用。JSP技术的设计者为便于开发人员在编写JSP页面时获得这些web对象的引用,特意定义了9个相应的变量,开发人员在JSP页面中通过这些变量就可以快速获得这9大对象的引用。
这9个对象分别是哪些,以及作用也是笔试经常考察的知识点。
request
response
config
application
exception
Session
page
out
pageContext
10.17 Tip:JSP九大隐式对象对应关系
request HttpServletRequest
response HttpServletResponse
session HttpSession
application servletContext
config servletConfig
out JspWriter -----> PrintWriter
exception
page this
pageContext
10.18 Tip: out隐式对象
out隐式对象用于向客户端发送文本数据。
out对象是通过调用pageContext对象的getOut方法返回的,其作用和用法与ServletResponse.getWriter方法返回的PrintWriter对象非常相似。
JSP页面中的out隐式对象的类型为JspWriter,JspWriter相当于一种带缓存功能的PrintWriter,设置JSP页面的page指令的buffer属性可以调整它的缓存大小,甚至关闭它的缓存。
只有向out对象中写入了内容,且满足如下任何一个条件时,out对象才去调用ServletResponse.getWriter方法,并通过该方法返回的PrintWriter对象将out对象的缓冲区中的内容真正写入到Servlet引擎提供的缓冲区中:
设置page指令的buffer属性关闭了out对象的缓存功能
out对象的缓冲区已满
整个JSP页面结束
10.19 Tip: out隐式对象的工作原理图

10.20 Tip: out隐式对象的注意事项
同时使用out和response.getwriter()输出数据。
用JSP实现文件下载。
10.21 Tip: pageContext对象
pageContext对象是JSP技术中最重要的一个对象,它代表JSP页面的运行环境,这个对象不仅封装了对其它8大隐式对象的引用,它自身还是一个域对象,可以用来保存数据。并且,这个对象还封装了web开发中经常涉及到的一些常用操作,例如引入和跳转其它资源、检索其它域对象中的属性等。
10.22 Tip:通过pageContext获得其他对象
getException方法返回exception隐式对象
getPage方法返回page隐式对象
getRequest方法返回request隐式对象
getResponse方法返回response隐式对象
getServletConfig方法返回config隐式对象
getServletContext方法返回application隐式对象
getSession方法返回session隐式对象
getOut方法返回out隐式对象
pageContext封装其它8大内置对象的意义,思考:如果在编程过程中,把pageContext对象传递给一个普通java对象,那么这个java对象将具有什么功能? //用在 自定义标签上
10.23 Tip:pageContext作为域对象
pageContext对象的方法
public void setAttribute(java.lang.String name,java.lang.Object value)
public java.lang.Object getAttribute(java.lang.String name)
public void removeAttribute(java.lang.String name)
pageContext对象中还封装了访问其它域的方法
public java.lang.Object getAttribute(java.lang.String name,int scope)
public void setAttribute(java.lang.String name, java.lang.Object value,int scope)
public void removeAttribute(java.lang.String name,int scope)
代表各个域的常量
PageContext.APPLICATION_SCOPE
PageContext.SESSION_SCOPE
PageContext.REQUEST_SCOPE
PageContext.PAGE_SCOPE
findAttribute方法 (*重点,查找各个域中的属性)
10.24 Tip:重点
到此为止,web开发接触到了4个域对象:
pageContext(称之为page域)
request(称之为request域)
session(称之为session域)
servletContext(称之为application域)
这4个域对象是学习web的重点,也是笔试经常考察的知识点。
明确如下问题:
这4个对象的生命周期?
什么是域?为什么把这4个对象叫做域对象呢?
哪种情况下用哪种域对象。
10.25 Tip:引入和跳转到其他资源
PageContext类中定义了一个forward方法和两个include方法来分别简化和替代RequestDispatcher.forward方法和include方法

传递给这些方法的资源路径都只能是相对路径,如果路径以“/”开头,表示相对于当前WEB应用程序的根目录,否则,表示相对于当前JSP所映射到的访问路径。
10.26 Tip: JSP标签
虽然我们希望JSP页面仅用作数据显示模块,不要嵌套任何java代码引入任何业务逻辑,但在实际开发中不引入一点业务逻辑是不可能的,但引入业务逻辑会导致页面出现难看java代码,怎么办?
Sun公司允许用户开发自定义标签封装页面的java代码,以便jsp页面不出现一行java代码。当然sun公司在jsp页面中也内置了一些标签(这些标签叫做jsp标签),开发人员使用这些标签可以完成页面的一些常用业务逻辑。
JSP标签也称之为Jsp Action(JSP动作)元素,它用于在JSP页面中提供业务逻辑功能。
10.27 Tip: JSP常用标签
标签
标签

你可能感兴趣的:(JavaBasic)