Iterable 和 Iterator
Suppose b is a String array, or an object of java.util.ArrayList<String> , or of java.util.Set<String> . Then, one can write a foreach loop that processes each of b like this:
1 2 3 for (String s : b){ Process s }
With each iteration of the loop, another element of b is stored in s and the “processed” – whatever that means.
The use of such a foreach loop is made possible by the use of two interfaces: Iterator and Iterable.
假设 b 是一个字符串数组,或者是 java.util.ArrayList<String> 对象,或者是 java.util.Set<String> 对象。那么,可以编写一个 foreach 循环来处理 b 的每个元素,如下所示:
1 2 3 for (String s : b){ Process s }
随着循环的每次迭代,b 的一个元素会被储存在 s 中并被 “Process” 处理 —— 无论这是什么意思。
这样的 foreach 循环的使用是通过两个接口 Iterator 和 Iterable 来实现。
Iterable jdk 1.5 之后,添加了 iterable 接口用于支持 foreach 循环。iterable 接口含有 iterator 方法,返回集合的 iterator 对象。所有实现了 iterable 接口的集合都可以使用 foreach 循环进行遍历。
1 2 3 public interface Iterable <T>{ Iterator<T> iterator () ; }
Iterator 迭代器(Iterator)主要用来操作集合对象(Collection)。迭代器提供了统一的语法对集合对象(Collection)进行遍历操作,它可以把访问逻辑从不同类型的集合类中抽象出来,从而避免向客户端暴露集合的内部结构。所有集合类都实现了 Collection 接口,而 Collection 继承了 Iterable 接口。Iterator 只能向前移动,不能回退。
1 2 3 4 5 public interface Iterator <E>{ boolean hasNext () ; E next () ; void remove () ; }
方法细节 hasNext
next
remove
两者区别 Iterable 接口是为了 foreach 循环设计的。Iterable 接口表示集合可以返回 Iterator 对象,最终还是使用 Iterator 进行遍历。
Iterator 接口提供了一种统一的遍历集合元素的方式,使用 Iterator 对象可以不用关心具体集合对象的具体类型和内部实行,统一使用 Iterator 对象的接口方法就可以遍历集合。
Iterator 接口的核心方法 next() 或 hasNext() 都依赖于迭代器的当前迭代位置。如果 Collection 直接实现 Iterator 接口,势必导致集合对象中包含当前迭代位置的数据。当集合在不同方法间传递时,由于当前迭代位置不可预知,那么 next() 方法的结果会变成不可预知。而 Iterable 每次调用都会返回一个从头开始计数的迭代器。多个迭代器之间互不干扰。
Java SE 8 API 参考文档说明
StringBuilder 和 StringBuffer 在学习 Java 中,String 是最常能碰到的数据类型,但是 Java 中还存在有 StringBuilder 和 StringBuffer 两个类,这三者之间的区别主要集中在两个方面,即运行速度和线程安全,了解这些不同对学习 Java 中的字符串十分重要。
String 1 2 3 public final class String extends Object implements Serializable , Comparable<String>, CharSequence{ ... }
String 为字符串常量,所以对象一旦被创建就不能被修改,从运行速度来说慢于 StringBuilder 和 StringBuffer。
String 类是 final 类,也就意味着 String 类不能被继承,并且它的成员方法都默认为 final 方法,并且 String 类其实是通过 char 属组来保存字符串。
String 是不可变的对象,因此在每次对 String 类型进行改变的时候都等同于生成了一个新的 String 对象,然后将指向新的 String 对象。因此经常改变内容的字符串最好不要用 String,因为每次生成对象都会对系统性能产生影响,特别当内存中无引用对象多了以后。
StringBuilder 1 2 3 public final class StringBuilder extends Object implements Serializable , CharSequence{ ... }
StringBuffer 1 2 3 public final class StringBuffer extends Object implements Serializable , CharSequence{ ... }
性能测试 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 public class PerformTest { private static int time = 50000 ; public static void testString () { String s = "" ; long begin = System.currentTimeMillis(); for (int i = 0 ; i < time; i++){ s += "java" ; } long over = System.currentTimeMillis(); System.out.println("操作" + s.getClass().getName() + "类型使用的时间为:" + (over - begin) + "毫秒" ); } public static void testStringBuffer () { StringBuffer sbf = new StringBuffer (); long begin = System.currentTimeMillis(); for (int i = 0 ; i < time; i++){ sbf.append("java" ); } long over = System.currentTimeMillis(); System.out.println("操作" + sbf.getClass().getName() + "类型使用的时间为:" + (over - begin) + "毫秒" ); } public static void testStringBuilder () { StringBuilder sbl = new StringBuilder (); long begin = System.currentTimeMillis(); for (int i = 0 ; i < time; i++){ sbl.append("java" ); } long over = System.currentTimeMillis(); System.out.println("操作" + sbl.getClass().getName() + "类型使用的时间为:" + (over - begin) + "毫秒" ); } public static void testAddString1 () { long begin = System.currentTimeMillis(); for (int i = 0 ; i < time; i++){ String s = "I" + "love" + "java" ; } long over = System.currentTimeMillis(); System.out.println("字符串直接相加操作:" + (over - begin) + "毫秒" ); } public static void testAddString2 () { String s1 = "I" ; String s2 = "love" ; String s3 = "java" ; long begin = System.currentTimeMillis(); for (int i = 0 ; i < time; i++){ String s = s1 + s2 + s3; } long over = System.currentTimeMillis(); System.out.println("字符串间接相加操作:" + (over - begin) + "毫秒" ); } public static void main (String[] args) { testString(); testStringBuffer(); testStringBuilder(); testAddString1(); testAddString2(); } }
1 2 3 4 5 6 7 操作java.lang.String类型使用的时间为:3930毫秒 操作java.lang.StringBuffer类型使用的时间为:2毫秒 操作java.lang.StringBuilder类型使用的时间为:1毫秒 字符串直接相加操作:1毫秒 字符串间接相加操作:2毫秒 Process finished with exit code 0
三者间的联系 接口
String, StringBuffer 和 StringBuilder 都实现了 CharSequence 接口,内部都是用一个 char 数组实现。
1 2 3 4 5 6 7 8 9 public interface CharSequence { int length () ; char charAt (int index) ; CharSequence subSequence (int start, int end) ; public String toString () ; }
字符串相加操作细节 1 2 3 4 5 6 7 8 9 10 11 String a = "ab" ;String b = "cd" ;String ab = a + b;String s = "abcd" ;System.out.println(ab == s); String c = "ab" + "cd" ;String d = "abcd" ;System.out.println(c == d);
代码 1 中,当执行 a + b 时,JVM 首先会在堆中创建一个 StringBuilder 类,同时用 a 指向的字符串对象完成初始化,然后调用 append 方法完成对 b 所指向的字符串的合并操作。接着调用 StringBuilder 的 toString() 方法在堆中创建一个 String 对象,最后将刚生成的 String 对象的堆地址存放在局部变量 ab 中。而局部变量 s 存储的是常量池中 "abcd" 所对应的字符串对象的地址,两者地址不同。
代码 2 中 "ab" + "cd" 会直接在编译期就合并成常量 "abcd" ,因此相同字面值常量 "abcd" 所对应的是同一个字符串对象,地址相同。
StringBuilder 和 StringBuffer 两者的区别 StringBuilder 和 StringBuffer 拥有的成员属性以及成员方法基本相同,区别是 StringBuffer 的成员方法前面多了一个关键字:synchronized 。这个关键字是多线程访问时起到安全保护作用,也就说 StringBuffer 是线程安全的。
1 2 3 4 pubilc StringBuilder insert (int index, char str[], int offset, int len) { super .insert(index, str, offset, len); return this ; }
1 2 3 4 pubilc synchronized StringBuffer insert (int index, char str[], int offset, int len) { super .insert(index, str, offset, len); return this ; }
总结
String 是字符串常量,StringBuffer 是线程安全的字符串变量,StringBuilder 是直到 jdk 1.5 才加入的线程不安全的字符串变量,之所以设计 StringBuilder 是为了单线程使用提高效率而考虑的。
相对而言,从运行速度来说,StringBuilder > StringBuffer > String
从效率来说,如果对于很少改变内容的字符串,使用 String 效率高。如果对于经常改变内容的字符串,使用 StringBuilder 效率高,但是它只适用于单线程下在字符缓冲区进行大量操作的情况,在多线程场景下,容易导致数据不一致的现象出现。多线程场景下,要使用 StringBuffer,适用于多线程下在字符缓冲区进行大量操作的情况。
其他 1 2 3 4 5 6 String a = "hello2" ;String b = "hello" + 2 ;... System.out.println(a == b);
String b = "hello" + 2 在编译期间已经被优化成 "hello2" ,因此在运行期间,变量 a 和变量 b 指向的是同一个对象。
1 2 3 4 5 6 7 String a = "hello2" ;String b = "hello" ;String c = b + 2 ;... System.out.println(a == c);
因为有符号引用的存在,所以 String c = b + 2; 不会在编译期被优化,不会把 b + 2 当作字面常量来处理,因此这种方式生成的对象事实上是保存在堆上的。因此 a 和 c 指向的并不是同一个对象。
1 2 3 4 5 6 7 String a = "hello2" ;final String b = "hello" ;String c = b + 2 ;... System.out.println(a == c);
对于被 final 修饰的变量,会在 class 文件常量池中保存一个副本,也就是说不会通过连接而进行访问,对 final 变量的访问在编译期间都会直接被替代为真实的值。那么 String c = b + 2; 在编译期间就会被优化成:String c = "hello" + 2;
1 2 3 4 5 6 7 8 9 10 11 12 public static void main (String[] args) { String a = "hello2" ; final String b = getHello(); String c = b + 2 ; System.out.println(a == c); } public static String getHello () { return "hello" ; }
虽然将 b 用 final 修饰,但是由于其赋值是通过方法调用返回的,那么它的值只能在运行期间确定,因此 a 和 c 指向的不是同一个对象。
Java SE 8 API 文档参考说明
Comparable 和 Comparator Comparable Comparable 是排序接口。若一个类实现了 Comparable 接口,就意味着 “该类支持排序”。 即然实现 Comparable 接口的类支持排序,假设现在存在 “实现 Comparable 接口的类的对象的 List 列表(或数组)”,则该 List 列表(或数组)可以通过 Collections.sort(或 Arrays.sort)进行排序。
此外,“实现 Comparable 接口的类的对象” 可以用作 “有序映射(如TreeMap)” 中的键或 “有序集合(TreeSet)” 中的元素,而不需要指定比较器。
Comparable 定义 1 2 3 public interface Comparable <T> { public int compareTo (T o) ; }
说明:
假设我们通过 x.compareTo(y) 来 “比较 x 和 y 的大小”。若返回 “负数”,意味着 “x 比 y 小”;返回 “零”,意味着 “x 等于 y”;返回 “正数”,意味着 “x 大于 y”。
Comparator Comparator 是比较器接口。我们若需要控制某个类的次序,而该类本身不支持排序(即没有实现 Comparable 接口),那么,我们可以建立一个 “该类的比较器” 来进行排序,这个 “比较器” 只需要实现 Comparator 接口即可。也就是说,我们可以通过 “实现 Comparator 类来新建一个比较器”,然后通过该比较器对类进行排序。
Comparator 定义 1 2 3 4 5 6 public interface Comparator <T> { int compare (T o1, T o2) ; boolean equals (Object obj) ; }
说明:
若一个类要实现 Comparator 接口,它一定要实现 compareTo(T o1, T o2)函数,但可以不实现 equals(Object obj) 函数。
为什么可以不实现 equals(Object obj) 函数呢?因为任何类,默认都是已经实现了 equals(Object obj) 的。Java 中的一切类都是继承于 java.lang.Object ,在 Object.java 中实现了 equals(Object obj) 函数,所以,其它所有的类也相当于都实现了该函数。
int compare(T o1, T o2) 是 “比较 o1 和 o2 的大小”。返回 “负数”,意味着 “o1 比 o2 小”;返回 “零”,意味着 “o1 等于 o2”;返回 “正数”,意味着 “ o1 大于 o2”。
Comparable 和 Comparator 比较 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 import java.util.*;import java.lang.Comparable;public class CompareComparatorAndComparableTest { public static void main (String[] args) { ArrayList<Person> list = new ArrayList <Person>(); list.add(new Person ("ccc" , 20 )); list.add(new Person ("AAA" , 30 )); list.add(new Person ("bbb" , 10 )); list.add(new Person ("ddd" , 40 )); System.out.printf("Original sort, list:%s\n" , list); Collections.sort(list); System.out.printf("Name sort, list:%s\n" , list); Collections.sort(list, new AscAgeComparator ()); System.out.printf("Asc(age) sort, list:%s\n" , list); Collections.sort(list, new DescAgeComparator ()); System.out.printf("Desc(age) sort, list:%s\n" , list); testEquals(); } private static void testEquals () { Person p1 = new Person ("eee" , 100 ); Person p2 = new Person ("eee" , 100 ); if (p1.equals(p2)) { System.out.printf("%s EQUAL %s\n" , p1, p2); } else { System.out.printf("%s NOT EQUAL %s\n" , p1, p2); } } private static class Person implements Comparable <Person>{ int age; String name; public Person (String name, int age) { this .name = name; this .age = age; } public String getName () { return name; } public int getAge () { return age; } public String toString () { return name + " - " +age; } boolean equals (Person person) { if (this .age == person.age && this .name == person.name) return true ; return false ; } @Override public int compareTo (Person person) { return name.compareTo(person.name); } } private static class AscAgeComparator implements Comparator <Person> { @Override public int compare (Person p1, Person p2) { return p1.getAge() - p2.getAge(); } } private static class DescAgeComparator implements Comparator <Person> { @Override public int compare (Person p1, Person p2) { return p2.getAge() - p1.getAge(); } } }
Java 8 中的 Comparator 范例一 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 import java.math.BigDecimal;public class Developer { String name; BigDecimal salary; int age; public Developer (String name, BigDecimal salary, int age) { this .name = name; this .salary = salary; this .age = age; } public String getName () { return name; } public void setName (String name) { this .name = name; } public BigDecimal getSalary () { return salary; } public void setSalary (BigDecimal salary) { this .salary = salary; } public int getAge () { return age; } public void setAge (int age) { this .age = age; } @Override public String toString () { return "Developer [" + "name='" + name + '\'' + ", salary=" + salary + ", age=" + age + ']' ; } }
经典 Comparator 示例 1 2 3 4 5 6 Comparator<Developer> byName = new Comparator <Developer>() { @Override public int compare (Developer developer, Developer compareDeveloper) { return developer.getName().compareTo(compareDeveloper.getName()); } };
对应的 Lambda 表达式示例 1 2 Comparator<Developer> byNameLambda = (Developer developer, Developer compareDeveloper)->developer.getName().compareTo(compareDeveloper.getName());
Java8 更简洁的一种写法 1 Comparator<Developer> byNameLambdaSimple = Comparator.comparing(Developer::getName);
范例二 比较 Developer 的对象的 age 的示例。通常使用 Collections.sort 并传递一个这样的匿名 Comparator 类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 import java.math.BigDecimal;import java.util.ArrayList;import java.util.Collections;import java.util.Comparator;import java.util.List;public class TestSorting { public static void main (String[] args) { List<Developer> listDevs = getDevelopers(); System.out.println("Before Sort" ); for (Developer developer : listDevs) { System.out.println(developer); } Collections.sort(listDevs, new Comparator <Developer>() { @Override public int compare (Developer o1, Developer o2) { return o1.getAge() - o2.getAge(); } }); System.out.println("After Sort" ); for (Developer developer : listDevs) { System.out.println(developer); } } private static List<Developer> getDevelopers () { List<Developer> result = new ArrayList <Developer>(); result.add(new Developer ("mkyong" , new BigDecimal ("70000" ), 33 )); result.add(new Developer ("alvin" , new BigDecimal ("80000" ), 20 )); result.add(new Developer ("jason" , new BigDecimal ("100000" ), 10 )); result.add(new Developer ("iris" , new BigDecimal ("170000" ), 55 )); return result; } }
当排序要求更改时,您只需传递另一个新的匿名 Comparator 类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 Collections.sort(listDevs, new Comparator <Developer>() { @Override public int compare (Developer o1, Developer o2) { return o1.getAge() - o2.getAge(); } }); Collections.sort(listDevs, new Comparator <Developer>() { @Override public int compare (Developer o1, Developer o2) { return o1.getName().compareTo(o2.getName()); } }); Collections.sort(listDevs, new Comparator <Developer>() { @Override public int compare (Developer o1, Developer o2) { return o1.getSalary().compareTo(o2.getSalary()); } });
它是有效的,但是你不认为仅仅因为要改变一行代码创建一个类是有点奇怪的么?
用 Lambda 排序 在 Java 8 中,List 接口支持直接使用 sort 该方法,不再需要使用 Collections.sort 了。
1 2 3 4 5 6 7 listDevs.sort(new Comparator <Developer>() { @Override public int compare (Developer o1, Developer o2) { return o2.getAge() - o1.getAge(); } });
Lambda 表达式示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 import java.math.BigDecimal;import java.util.ArrayList;import java.util.List;public class TestSorting { public static void main (String[] args) { List<Developer> listDevs = getDevelopers(); System.out.println("Before Sort" ); for (Developer developer : listDevs) { System.out.println(developer); } System.out.println("After Sort" ); listDevs.sort((Developer o1, Developer o2)->o1.getAge()-o2.getAge()); listDevs.forEach((developer)->System.out.println(developer)); } private static List<Developer> getDevelopers () { List<Developer> result = new ArrayList <Developer>(); result.add(new Developer ("mkyong" , new BigDecimal ("70000" ), 33 )); result.add(new Developer ("alvin" , new BigDecimal ("80000" ), 20 )); result.add(new Developer ("jason" , new BigDecimal ("100000" ), 10 )); result.add(new Developer ("iris" , new BigDecimal ("170000" ), 55 )); return result; } }
更多 Lambda 的例子 按年龄排序 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 Collections.sort(listDevs, new Comparator <Developer>() { @Override public int compare (Developer o1, Developer o2) { return o1.getAge() - o2.getAge(); } }); listDevs.sort((Developer o1, Developer o2)->o1.getAge()-o2.getAge()); listDevs.sort((o1, o2)->o1.getAge()-o2.getAge()); listDevs.sort(Comparator.comparing(Developer::getAge));
按名称排序 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 Collections.sort(listDevs, new Comparator <Developer>() { @Override public int compare (Developer o1, Developer o2) { return o1.getName().compareTo(o2.getName()); } }); listDevs.sort((Developer o1, Developer o2)->o1.getName().compareTo(o2.getName())); listDevs.sort((o1, o2)->o1.getName().compareTo(o2.getName())); listDevs.sort(Comparator.comparing(Developer::getName));
按薪水排序 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 Collections.sort(listDevs, new Comparator <Developer>() { @Override public int compare (Developer o1, Developer o2) { return o1.getSalary().compareTo(o2.getSalary()); } }); listDevs.sort((Developer o1, Developer o2)->o1.getSalary().compareTo(o2.getSalary())); listDevs.sort((o1, o2)->o1.getSalary().compareTo(o2.getSalary())); listDevs.sort(Comparator.comparing(Developer::getSalary));
反转排序 使用 Lambda 表达式对列表进行工资由少到多的排序
1 Comparator<Developer> salaryComparator = (o1, o2)->o1.getSalary().compareTo(o2.getSalary()); listDevs.sort(salaryComparator);
使用 Lambda 表达式对列表进行工资由多到少的排序
1 Comparator<Developer> salaryComparator = (o1, o2)->o1.getSalary().compareTo(o2.getSalary()); listDevs.sort(salaryComparator.reversed());
Java SE 8 API 文档参考
BigDecimal 方法 首先来看一个简单的加法:
1 System.out.println(1.01 +2.02 );
这个加法的结果是什么呢?如果我们自己计算的话应该是 3.03 ,但实际上计算机默认给出的结果是 3.0300000000000002 ,因为无论是 float 还是 double 都是浮点数,而计算机为二进制,这两者都会损失一点精确度。
Java 中提供了大数字(超过 16 位有效位)的操作类,即 java.math.BinInteger 类和 java.math.BigDecimal 类,用于高精度计算。
其中 BigInteger 类是针对大整数的处理类,而 BigDecimal 类则是针对大小数的处理类。
BigDecimal 类的实现用到了 BigInteger 类,不同的是 BigDecimal 加入了小数的概念。
在日常使用的过程中 float 和 double 已经足够,但当涉及到商业计算等对精度要求比较高的情况时,我们需要使用到 BigDecimal 类。
创建对象 BigDecimal 类创建的是对象,不能使用传统的 +,-,*,/ 等运算符对其直接进行数学运算,而必须调用其对应的方法,方法的参数也必须是 BigDecimal 类的对象。
创建 BigDecimal 对象主要有两种方法:
1 2 3 BigDecimal bd1 = new BigDecimal ("10.511" );BigDecimal bd2 = BigDecimal.valueOf(10.511 );
注意,写成 BigDecimal bd1 = new BigDecimal(10.511); 也是可以运行的,但输出结果会变成 10.510999999999999232613845379091799259185791015625 ,即精度会出现损失。
特殊情况:
1 2 3 BigDecimal zero = BigDecimal.ZERO;BigDecimal one = BigDecimal.ONE;BigDecimal ten = BigDecimal.TEN;
加减乘除运算 1 2 3 4 public BigDecimal add (BigDecimal value) ;public BigDecimal subtract (BigDecimal value) ;public BigDecimal multiply (BigDecimal value) ;public BigDecimal divide (BigDecimal value) ;
注意,BigDecimal 的运算都没有对原值进行操作,而是返回一个新的 BigDecimal 对象。
1 2 3 4 5 BigDecimal bd4 = new BigDecimal ("3.3" );BigDecimal bd5 = new BigDecimal ("4.4" );bd4.add(bd5); System.out.println(bd4);
比较 BigDecimal 的比较使用的是 compareTo 方法,将此 BigDecimal 对象和指定的 BigDecimal 对象比较。
当值相等但保留位数不同的两个对象比较时(如 2.0 和 2.00),两者被认为是相等的。
当此 BigDecimal 对象在数字上小于、等于或大于被比较对象时,返回 -1、0 或 1。
1 2 3 4 5 6 7 8 BigDecimal bd4 = new BigDecimal ("3.3" );BigDecimal bd5 = new BigDecimal ("4.4" );BigDecimal bd6 = new BigDecimal ("4.40" );int c1 = bd4.compareTo(bd5);int c2 = bd5.compareTo(bd5);int c3 = bd5.compareTo(bd6);int c4 = bd5.compareTo(bd4);
请求转发和请求重定向 当客户端向服务器发送请求时,服务器收到请求后,会将请求封装成一个 HttpServletRequest 对象请求,并且所有的请求参数都封装在 request 对象中,这个对象是 JSP 的内置对象,可以直接在 JSP 中使用。服务器收到请求后,还需要请求别的页面,这时就有两种方式:请求转发和请求重定向。
请求转发 1 request.getRequestDispatcher(URL).forward(request, response)
请求转发,是服务器的行为,请求由服务器转发给另外一个页面处理,如何转发,何时转发,转发几次,客户端是不知道的。请求转发时,从发送第一次到最后一次请求的过程中,web 容器只创建一次 request 和 response 对象,新的页面继续处理同一个请求。也可以理解为服务器将 request 对象在页面之间传递。
转发是在 Web 服务器内部进行的,不能跨域访问。
请求转发之后地址栏的信息并不会有任何的改变。
请求重定向 1 response.sendRedirect(URL)
请求重定向,是客户端的行为,每次请求重定向都是由客户端发起的,也就是说重定向一次,就刷新 request 对象的属性,之前的 request 对象的属性值就失效了。
重定向的目的是当 Web 应用升级后,如果请求路径发生了变化,可以将原来的路径重定向到新路径,从而避免浏览器请求原路径找不到资源。
请求重定向可以跨域访问。
请求重定向之后地址栏是会改变的,变为跳转之后的页面地址。
JSP 中四个域对象
域对象
作用范围
pageContext
page 域
当前 JSP 页面
request
request 域
同一个请求中
session
session 域
同一个会话中
application
context 域
同一个 web 应用中
常见包名解释
POJO(Plain Ordinary Java Object): 简单的 Java 对象,只有 private 属性和 public 属性中的 get 和 set 方法,只能装载数据,不能实现接口。
PO(Persistent Object): 持久化对象,是与数据库中表相对应的 Java 对象,PO 对象需要实现序列化接口。
VO(View Object): 显示层对象。
DTO(Data Transfer Object): 数据传输对象。
DAO(Data Access Object): 数据访问对象,用于访问数据库,包含了各种数据库的操作方法。
BO(Business Object): 业务对象。由 Service 层输出的封装业务逻辑的对象。
MyBatis 加载 Mapper 配置的方式 Mybatis 加载映射文件主要有三种方式,分别是 resource 、class 和 package name=... 。
依据 Mapper 类具体路径 这种情况下,如果是非注解模式的话 xml 配置文件必须和这个类在同一级目录,且与 Mapper 类同名。
1 2 3 4 5 6 7 <configuration > <mappers > <mapper class ="com.bestcxx.stu.springmvc.mapper.UserModelMapper" /> <mapper class ="com.bestcxx.stu.springmvc.mapper.UserModelTwoMapper" /> </mappers > </configuration >
在存在 xml 配置文件的情况下,文件结构如下:
1 2 3 4 5 * com.bestcxx.stu.springmvc.mapper * UserModelMapper.java * UserModelTwoMapper.java * UserModelMapper.xml * UserModelTwoMapper.xml
依据 Mapper 类所在的 package 包路径 这种情况下,如果是非注解模式的话 xml 配置文件必须也处于同一级 package 下,且与Mapper类同名。
1 2 3 4 5 <configuration > <mappers > <package name ="com.bestcxx.stu.springmvc.mapper" /> </mappers > </configuration >
文件结构:
1 2 3 4 5 * com.bestcxx.stu.springmvc.mapper * UserModelMapper.java * UserModelTwoMapper.java * UserModelMapper.xml * UserModelTwoMapper.xml
把 Mapper 的 XML 配置文件单独放在 sources 中 这种方式的好处是便于统一管理 xml 配置文件,不好的的地方是无法使用注解模式
1 2 3 4 5 6 7 <configuration > <mappers > <mapper resource ="mybatis/mappings/UserModelMapper.xml" /> <mapper resource ="mybatis/mappings/UserModelTwoMapper.xml" /> </mappers > </configuration >
文件结构:
1 2 3 * com.bestcxx.stu.springmvc.mapper * UserModelMapper.java * UserModelTwoMapper.java
1 2 3 4 5 * src/main/resources * mybatis * mappings * UserModelMapper.xml * UserModelTwoMapper.xml
参考