Java面试题归纳
-
java与javac命令
javac index.java
java index
-
int i = 10000000000000;会报错吗?为什么?
会报错,int变量只有4个字节,超过了最大变量 -
整数有哪几种类型?int几个字节?
byte(1),short(2),int(4),long(8) -
float f = 3.14;报错吗?为什么?
会报错,3.14默认为double型,需要进行强转float f = 3.14f ;
-
B/S架构跟C/S架构的区别是什么?
C/S架构通过客户端来使用服务器,常用于开发软件,如微信APP
B/S架构通过浏览器来使用服务器,常用于网页,如www.taobao.com -
Java中有哪些基本数据类型?
整型: byte(1),short(2),int(4),long(8)
浮点型:float,double
布尔型:boolean
字符型:char -
int t = 5;
int r = ++t + t++ + ++t + ++t;
6+6+8+9=29 -
short s = 1; s += 1;
会报错吗?short s = 1; s = s+1;
会报错吗?为什么?
前面不会,s+=1自动转换为short类型
后面会,s=s+1中,1为int型,大转小需要强转 -
8*2最快的的运算方式是什么?
左移一位,8的二进制数为0000 1000,左移一位为:0001 0000为16System.out.println(8<<1);
-
System.out.println(5>3?9:3.1);
结果为9.0
9:3.1先进行类型转换,9转换为9.0,结果为true则输出9.0,否则输出3.0 -
switch可以跟哪些数据类型?
整型:byte,short,int
字符型:char,String
枚举:enum -
char c = ‘72’
正确吗?
不正确,char是字符型变量,单位长度为1
char c = 72;
输出为ASCⅡ表中对应的字符,一个字符
char c = ‘2’;
一个字符 -
写出5个异常类(报错)
ArrayIndexOutOfBoundsException 数组下标越界错误
NullPointerException 空指针异常
ClassCastException 指定的类异常
IllegalArgumentException 方法的参数错误
ArithmeticException 数学运算异常 -
使用冒泡排序,排序一组数据
public static void main(String[] args) {
int numArr[] = {5,11,9,4};
int a;
for(int i=0;i<numArr.length;i++){
for(int j=0;j<numArr.length-1;j++){
if(numArr[j]>numArr[j+1]){
a = numArr[j];
numArr[j] = numArr[j+1];
numArr[j+1] = a;
}
}
}
System.out.println(Arrays.toString(numArr));
}
-
Java三大特性
封装,继承,多态 -
重写与重载的区别
重写:方法名相同,参数列表相同。返回的数据类型,若为基本数据类型则保持一致,若为引用数据类型,父类>=子类。
重载:方法名相同,参数列表必须不同 -
this与super关键字
在重写的方法中,super.方法名为调用父类的方法,this.方法名为调用子类的方法
如果不涉及重写,this与super没有区别,都调用父类的方法 -
final的用法
final修饰属性:属性的内容必须初始化(构造器赋值、定义属性直接赋值)
final修饰类:表示这个类不能被继承
final修饰方法:表示这个方法不能被重写 -
堆与栈的区别
堆:存储实例对象,存取速度慢
栈:存储对象、变量的引用,存取速度快 -
抽象类与接口的区别
默认方法实现:抽象类可以有默认的方法实现;接口不能有默认的方法实现。
实现:抽象类的子类使用 extends 来继承;接口必须使用 implements 来实现接口。
构造函数:抽象类可以有构造函数;接口不能有。
main 方法:抽象类可以有 main 方法,并且我们能运行它;接口不能有 main 方法。
实现数量:类可以实现很多个接口;但是只能继承一个抽象类。
访问修饰符:接口中的方法默认使用 public 修饰;抽象类中的方法可以是任意访问修饰符。 -
访问修饰符及作用范围
-
父类跟子类静态代码块、代码块、构造方法的执行顺序
父类静态代码块–>子类静态代码块–>父类成员代码块–>父类构造方法–>子类成员方法–>子类构造方法 -
请写出5个类、5个包
类:Date,System,Class,List,HashMap
包:java.lang包,java.io包,java.util包,java.sql包,javax.servlet包 -
final、finally、finalize的区别
final:是修饰符。声明类时,这个类不能再派生出新的子类,一个类不能同时被abstract和final声明。声明属性时属性必须初始化。声明方法时表示这个方法不能被重写。
finally:在异常处理时提供finally块来执行清楚操作。如果抛出一个异常,相匹配的catch语句执行进入finally块(如果存在finally块的情况下)。
finalize:是方法名。finalize()方法在垃圾收集器将对象从内存中清除之前作必要的清理工作。这个方法在垃圾收集器确定对象没有被引用的情况之后才能被调用。此方法在Object中被定义,因此所有类都可以调用它,也可以重写该方法。 -
== 与 equals的区别
==用于基本数据类型的对象时比较的是值,用于引用数据类型的对象时比较的是地址。equals只能用于引用数据类型,比较两个对象是否相等。equals 默认情况下是引用比较,只是很多类重写了 equals 方法,
比如 String、Integer 等把它变成了值比较,所以一般情况下 equals 比较的是值是否相等。 -
创建对象有几种方式?
new、clone、反射、反序列化 -
System.out.println(Math.round(-3.5));
结果为-3,
-3.5+0.5的结果向下取整 -
常量属性的赋值方式:
常量的定义可以在定义时直接赋值 可以在构造方法中赋值
final关键字修饰 命名规则: 都为大写 字母中间用下划线隔开 例如 PASS_WORD; -
Java 中操作字符串都有哪些类?它们之间有什么区别?
操作字符串的类有:String、StringBuffer、StringBuilder。
String 和 StringBuffer、StringBuilder 的区别在于 String 声明的是不可变的对象,每次操作都会生成新的 String 对象,然后将指针指向新的 String 对象,而 StringBuffer、StringBuilder 可以在原有对象的基础上进行操作,所以在经常改变字符串内容的情况下最好不要使用 String。StringBuffer 和 StringBuilder 最大的区别在于:StringBuffer 是线程安全的,而 StringBuilder 是非线程安全的,但 StringBuilder 的性能却高于 StringBuffer,所以在单线程环境下推荐使用 StringBuilder,多线程环境下推荐使用 StringBuffer。
-
下面的代码将创建几个字符串对象?
String s1 = new String(“Hello”); 2 堆里面一个 常量池里一个
String s2 = new String(“Hello”);1 堆里面一个 常量池已经存在 无需重新创建
3个,堆里2个,常量池1个 -
在java中,String s=new String(“xyz”);创建了几个对象?(B)
A 1个 B 1个或2个 C 2个 D 以上都不对
堆里面一个 常量池可能存在 所有选择1个或者2个 -
下面的代码输出什么?
String s1 = new String(“abc”);
String s2 = new String(“abc”);
System.out.println(s1 == s2); false,地址不同
System.out.println(s1.equals(s2));true,值相同 -
下面的代码输出什么?
String s1= “abc”;
String s2= “abc”;
String s3 = new String(“abc”);
String s4 = new String(“abc”);
System.out.println("s3 == s4 : "+( s3 == s4)); false 在堆里 地址不同
System.out.println("s3.equals(s4) : "+(s3.equals(s4))); true
System.out.println("s1 == s3 : "+(s1 == s3)); false 一个在堆 一个在常量池 地址不同
System.out.println("s1.equals(s3) : "+(s1.equals(s3))); true
System.out.println(s1==s2); true 指向常量池的同一个地址 -
下面的代码输入什么?
String s1 = “abc”;
String s2 = new String(“abc”);
s2.intern();
System.out.println(s1 ==s2); false
如果s2=s2.intern();为true,编译时合为abcd -
下面的代码输出什么?
String str2 = “ab”;
String str3 = “cd”;
String str4 = str2+str3;
String str5 = “abcd”;
System.out.println("str4 = str5 : " + (str4==str5));
false,str2和str3都指向堆里,无法识别为常量 -
下面的代码输出什么?
final String str2 = “ab”;
final String str3 = “cd”;
String str4 = str2+str3;
String str5 = “abcd”;
System.out.println("str4 = str5 : " + (str4==str5));
true,final定义为常量属性,str2和str2指向常量池,因此str4也为常量 -
下面的代码输入什么?
String str6 = “b”;
String str7 = “a” + str6;
String str67 = “ab”;
System.out.println("str7 = str67 : "+ (str7 == str67));false -
下面的代码输入什么?
final String str8 = “b”;
String str9 = “a” + str8;
String str89 = “ab”;
System.out.println("str9 = str89 : "+ (str9 == str89));true -
String s1=”Hello”;
String s2=”hello”;
String s3=s1.toLowerCase();
String s4=s2.toLowerCase();
下面选项结果为true的是:C
A.S1 == s3
B.S2 == s3
C.S2 == s4
D.S3 == s4
s2都是小写的 所有直接返回常量池里的hello -
Math.round(5.5)、Math.round(-5.5)分别输出多少?
Integer i1 = 1 ;
Integer i2 = 1 ;
Integer i3 = 128 ;
Integer i4 = 128 ;
System.out.println(i1 == i2); //true
System.out.println(i3 == i4);//false
Integer存储范围(-128,127),超出则开辟新的空间 -
ArrayList元素用循环全部删除。
List<String> list = new ArrayList<String>();
list.add("zhangsan");
list.add("lisi");
for(int i=list.size()-1;i>=0;i--){
list.remove(list.size()-1);
}
-
说下java中的集合?
java集合有两大类:List和Set,他们都实现了Collection接口。List包括ArrayList、Vector和LinkedList;Set包括HashSet和TreeSet。 -
ArrayList跟Vector的区别?(自学Vector,用法跟Arraylist类似)
ArrayList :
默认初始容量0 加载因子1 扩容1.5倍
底层数据结构是数组结构
线程不安全,效率高
若有设置的初始容量使用设置的初始容量。
Vector :
默认初始容量10 加载因子1 扩容2倍
底层数据结构是数组结构
线程安全,效率低
若有设置的初始容量使用设置的初始容量。 -
List Set Map的区别?
List接口:List是有序的Collection,使用此接口能够精确的控制每个元素插入的位置。用户能够使用索引(元素在List中的位置,类似于数组下标)来访问List中的元素,允许有相同的元素。
Set接口:Set是一种不包含重复的元素的Collection,存放的元素是无序的。
Map接口:Map用来存两个字段:键和值。Map的key是不能重复的。如果重复后面覆盖前面的。 -
说下HashTable HashMap的区别?
存储: HashMAP允许key和value为null,而Hashtable不允许
线程安全: HashMAP是非线程安全的 Hashtable是线程安全的 -
HashMap(HashSet)用来装学生,人数在37-43人左右,请问如何初始化HashMap数量达到性能最优?
HashMap默认容量是2的幂次方。故初始容量为64性能最优。 -
重写equals是否需要重写hashcode? 重写hashcode是否需要重写equals?
前者不需要,后者需要 -
说下数组跟链表的区别?
从增删改查操作的效率上来谈
增删 链表的效率更高 , 改查 数组的效率更高 -
throw跟throws区别?
throw 是在方法体中主动抛出一个异常
throws 把异常往上抛 谁调用谁处理 -
堆和栈的区别:
栈内存:栈内存首先是一片内存区域,存储的都是局部变量,凡是定义在方法中的都是局部变量(方法外的是全局变量),for循环内部定义的也是局部变量,是先加载函数才能进行局部变量的定义,所以方法先进栈,然后再定义变量,变量有自己的作用域,一旦离开作用域,变量就会被释放。栈内存的更新速度很快,因为局部变量的生命周期都很短。堆内存:存储的是数组和对象(其实数组就是对象), 凡是new建立的都是在堆中,堆中存放的都是实体(对象),实体用于封装数据,而且是封装多个(实体的多个属性),如果一个数据消失,这个实体也没有消失,还可以用,所以堆是不会随时释放的,但是栈不一样,栈里存放的都是单个变量,变量被释放了,那就没有了。堆里的实体虽然不会被释放,但是会被当成垃圾,Java有垃圾回收机制不定时的收取。
-
java中可以多继吗?
不可以 java是单继承机制 -
重写和重载的区别
重载(Overload)是让类以统一的方式处理不同类型数据的一种手段,实质表现就是多个具有不同的参数个数或者类型的同名函数(返回值类型可随意,不能以返回类型作为重载函数的区分标准)同时存在于同一个类中,是一个类中多态性的一种表现(调用方法时通过传递不同参数个数和参数类型来决定具体使用哪个方法的多态性)。重写(Override)是父类与子类之间的多态性,实质是对父类的函数进行重新定义,如果在子类中定义某方法与其父类有相同的名称和参数则该方法被重写,不过子类函数的访问修饰权限不能小于父类的;若子类中的方法与父类中的某一方法具有相同的方法名、返回类型和参数表,则新方法将覆盖原有的方法,如需父类中原有的方法则可使用 super 关键字。
-
成员内部类中可以定义静态的属性和方法吗?
不能 要定义成员内部类需要先定义外部类 -
String str="i"与 String str=new String(“i”)一样吗?
不一样,因为内存的分配方式不一样。String str="i"java虚拟机会将其分配到常量池中;
而String str=new String(“i”)则会被分配到堆内存中 -
如何将字符串反转?
使用StringBuilder或者Stringbuffer的 reverse方法:
StringBuilder stringBuilder = new StringBuilder();
stringBuilder. append(“abcdefg”);
System. out. println(stringBuilder. reverse()); // gfedcba -
String类的常用方法有哪些?
indexOf() 返回指定字符的索引
charOf() 返回指定索引处的字符
replace() 字符串替换
trim() 取出字符串两边留白
aplit() 分割字符串,返回一个分割后的字符串数组
getBytes() 返回字符串的byte类型数组
length() 返回字符串长度
toLowerCase() 将字符串转成小写字母
toUpperCase() 将字符串转成大写字母
subString() 截取字符串
equals() 字符串比较 -
普通类和抽象类有什么区别?
普通类不能包含抽象方法,抽象类可以包含抽象方法
抽象类不能直接实例化, 普通类可以直接实例化 -
抽象类可以使用final修饰吗?
不能,定义抽象类就是要让其他类来继承,如果用final修饰 该类就不能被继承
这样彼此会产生矛盾,所以final不能修饰抽象类 -
java中IO流分为几种?
按功能分 输入流(input) 输出流(output)
按类型分 字节流 字符流
字节流和字符流的区别是:字节流是按8位传输以字节为单位输出输入数据
字符流是按16位传输以字符为单位输出输入数 -
Files的常用方法有哪些?
Files. exists():检测文件路径是否存在。
Files. createFile():创建文件。
Files. createDirectory():创建文件夹。
Files. delete():删除一个文件或目录。
Files. copy():复制文件。
Files. move():移动文件。
Files. size():查看文件个数。
Files. read():读取文件。
Files. write():写入文件。 -
java容器有哪些?
java容器分为Collection和Map两大类 其下又分为很多子类
Collection
List:ArrayList,LinkedList,Vector,Stack
Set:HashSet,LinkedHashSet,TreeSet
Map
HashMap,LinkedHashMap,TreeMap,ConcurrentHashMap,Hashtable
Collection 是一个集合接口,它提供了对集合对象进行基本操作的通用接口方法,
所有集合都是它的子类,比如 List、Set 等。 -
如何决定使用HashMap还是treeMap?
对于在map在插入,删除,定位一个元素这类操作。HashMap是最好的选择,因为相对而言 HashMAP的插入会更快。但如果你要对一个Key集合进行有序的遍历,那treeSet是更好的选择 -
说一下HashMap的实现原理
HashMAP是基于Hash算法实现的,我们通过put(key,value)存储,get(key)来获取。当传入key时,HashMap会key.Hashcode()计算出hash值,根据hash值将value存在bucket里。当计算出hash值相同的我们称为Hash冲突,HashMap 的做法是用链表和红黑树存储相同 hash 值的 value。当 hash 冲突的个数比较少时,使用链表否则使用红黑树。 -
说一下HashSet的实现原理?
HashSet是基于HashMap实现的,HashSet底层使用HashMap来保存所有元素,因此HashSet的实现比较简单,相关HashSet的操作,基本上都是直接调用底层HashMap的相关方法来完成的,HashSet不允许重复的值 -
ArrayList和LinkedList的区别是什么?
数据结构实现:ArrayList是动态数组结构实现,而LinkedList是双向链表的数据结构实现。
随机访问效率:ArrayList比LinkedList在随机方访问的时机效率要高, 因为LinkedList是线程的数据存储方式,所以需要移动指针从前往后以次找
增加和删除效率:在非首尾的增加和删除操作,LinkedList 要比 ArrayList 效率要高,因为 ArrayList 增删操作要影响数组内的其他数据的下标
综合来说:在需要频繁读取集合中的元素时,更推荐ArrayList,而在需要插入和删除操作较多的时候,更推荐LinkedList -
如何实现数组和List之间的互换?
数组转List:使用Array.asList进行转换。
List转数组:使用List自带的toArray()方法
// list to array
List list = new ArrayList();
list. add(“王磊”);
list. add(“的博客”);
list. toArray();
// array to list
String[] array = new String[]{“王磊”,“的博客”};
Arrays. asList(array);
-
ArrayList和Vector的区别是什么?
线程安全:Vector 使用了 Synchronized 来实现线程同步,是线程安全的,而 ArrayList 是非线程安全的。
性能:ArrayList 在性能方面要优于 Vector。
扩容:ArrayList 和 Vector 都会根据实际的需要动态的调整容量,只不过在 Vector 扩容每次会增加 1 倍,而 ArrayList 只会增加 50%。 -
Array和ArrayList 有何区别?
Array 可以存储基本数据类型和对象,ArrayList只能存储对象。
Array 大小是固定的, 而ArrayList大小是自动扩展的。 -
在 Queue 中 poll()和 remove()有什么区别?
相同点:都是返回第一个元素,并在队列中删除返回的对象。
不同点:如果没有元素 poll()会返回 null,而 remove()会直接抛出 NoSuchElementException 异常。
代码示例:
Queue queue = new LinkedList();
queue. offer(“string”); // add
System. out. println(queue. poll());
System. out. println(queue. remove());
System. out. println(queue. size()); -
哪些集合类是线程安全的?
Vector、Hashtable、Stack 都是线程安全的,而像 HashMap 则是非线程安全的,不过在 JDK 1.5 之后随着 Java. util. concurrent 并发包的出现,它们也有了自己对应的线程安全类,比如 HashMap 对应的线程安全类就是 ConcurrentHashMap。 -
迭代器Iterator是什么?
Iterator 接口提供遍历任何Collection的接口。
我们可以从一个 Collection 中使用迭代器方法来获取迭代器实例。
迭代器取代了 Java 集合框架中的 Enumeration,迭代器允许调用者在迭代过程中移除元素。 -
Iterator怎么使用? 有什么特点?
Iterator 使用代码如下:
List list = new ArrayList<>();
Iterator it = list. iterator();
while(it. hasNext()){
String obj = it. next();
System. out. println(obj);
}
特点是更加安全,因为它可以确保,在当前遍历的集合元素被更改的时候,就会抛出
ConcurrentModificationException 异常。 -
Iterator 和 ListIterator 有什么区别?
Iterator 可以遍历 Set 和 List 集合,而 ListIterator 只能遍历 List。
Iterator 只能单向遍历,而 ListIterator 可以双向遍历(向前/后遍历)。
ListIterator 从 Iterator 接口继承,然后添加了一些额外的功能,比如添加一个元素、替换一个元素、获取前面或后面元素的索引位置。 -
int a = 5 ; 5 7 3 7
int b = a++ + ++a +3 + a++ ;
System.out.println(a);8
System.out.println(b); 22 -
js中“ == ” 与 “===”的区别?
== 表示相等 (值相等)
===表示恒等(类型和值都要相等) -
xml 与json的区别?
区别:
1、传输同样格式的数据,xml需要使用更多的字符进行描述,
2、 流行的是基于json的数据传输。
3、xml的层次结构比json更清晰。 -
请说下3次握手跟4次挥手?
三次握手:
第一次握手:客户端发送第一个包,其中SYN标志位为1, ACK=0,发送顺序号sequence=X(随机int)。客户端进入SYN发送状态,等待服务器确认。
第二次握手:服务器收到这个包后发送第二个包,其中包SYN、ACK标志位为1,发送顺序号seq=Y(随机int),接收顺序号ACK=X+1,此时服务器进入SYN接收状态。
第三次握手:客户端收到服务器传来的包后,向服务器发送第三个包,SYN=0, ACK=1,接收顺序号ACK = Y+1,发送顺序号seq=X+1。此包发送完毕,客户端和服务器进入ESTABLISHED建立成功状态,完成三次握手。四次挥手:
第一次挥手:主动关闭方发送第一个包,其中FIN标志位为1,发送顺序号seq为X。
第二次挥手:被动关闭方收到FIN包后发送第二个包,其中发送顺序号seq为Z,接收顺序号ack为X+1。
第三次挥手:被动关闭方再发送第三个包,其中FIN标志位为1,发送顺序号seq为Y,接收顺序号ack为X。
第四次挥手:主动关闭方发送第四个包,其中发送顺序号为X,接收顺序号为Y。至此,完成四次挥手。 -
为什么建立连接协议是三次握手,而关闭连接却是四次握手呢?
建立连接时,ACK和SYN可以放在一个报文里来发送。而关闭连接时,被动关闭方可能还需要发送一些数据后,再发送FIN报文表示同意现在可以关闭连接了,所以它这里的ACK报文和FIN报文多数情况下都是分开发送的。 -
为什么TIME_WAIT状态还需要等2MSL后才能返回到CLOSED状态?
两个存在的理由:1、无法保证最后发送的ACK报文会一定被对方收到,所以需要重发可能丢失的ACK报文。
2、关闭链接一段时间后可能会在相同的IP地址和端口建立新的连接,为了防止旧连接的重复分组在新连接已经终止后再现。2MSL足以让分组最多存活msl秒被丢弃。 -
断开连接的时候客户端为什么需要等待2MSL时间?
保证客户端发送的最后一个ACK报文能够到达服务器,因为这个ACK报文可能丢失,站在服务器的角度看来,我已经发送了FIN+ACK报文请求断开了,客户端还没有给我回应,应该是我发送的请求断开报文它没有收到,于是服务器又会重新发送一次,而客户端就能在这个2MSL时间段内收到这个重传的报文,接着给出回应报文,并且会重启2MSL计时器。 -
servlet是单线程还是多线程?
Servlet是单实例多线程的。 -
Servlet的生命周期是怎样?
Servlet 通过调用构造方法进行实例化,构造方法必须为无参构造方法。默认是在第一次被访问的时候构造对象。也可以在web.xml中通过N配置Servlet被创建的时机。(N为一个整数表示优先级,>=0表示在web服务器启动的时候创建,<0表示第一次访问时创建)- Servlet 实例创建完毕后立马通过调用 init()方法进行初始化,如果初始化失败抛出ServletException,Servlet对象将会被垃圾回收器回收。
init初始化操作只会执行一次。初始化时机紧跟Servlet创建。 - Servlet 调用service()方法来处理客户端的请求。
- Servlet 通过调用 destroy()方法终止(结束)。
- 最后,Servlet 是由 JVM 的垃圾回收器进行垃圾回收的。
- Servlet 实例创建完毕后立马通过调用 init()方法进行初始化,如果初始化失败抛出ServletException,Servlet对象将会被垃圾回收器回收。
-
get请求与post的请求的区别是什么?
get:
1,参数拼接在url,不安全
2,只能是字符串类型
3,大小有限制2kb,速度快
post:
1,参数封装到请求体,安全
2,还可以是非字符串类型比如文件
3,理论上大小没限制,速度慢 -
转发与重定向的区别?
转发:一次请求,返回200,地址栏没变化
重定向:二次请求,第一返回302,
第二次请求会使用第一次请求响应中的Location头信息地址进行访问。
第二次请求返回200或者304,地址栏会改变。 -
静态包含(指令)跟动态包含(动作)有什么区别?
-
原理不同:
静态包含:先合并再翻译
动态包含:先翻译在包含 -
最终编译成java文件的数目不同。
静态包含在转换成为java文件的时候将包含文件的内容“复制”到主体文件,然后作为一个整体编译。最终编译为一个java文件。 -
静态包含在两个文件中不能有相同的变量,动态包含允许
-
语法不同:
静态包含语法: <%@inclue file=“被包含的页面”%>
动态包含语法: <jsp:include page=“被包含的页面”> -
参数传递不同 :
静态包含不能向被包含页面传递参数
动态包含可以使用jsp:param标签向被包含页面传递参数
- JSP的内置对象有哪些?
jsp有九大隐式对象(内置对象)
1.request:HttpServletRequest对象,代表请求
2.response:HttpServletResponse对象,代表响应
3.out:JspWriter对象,代表输出
4.session:HttpSession对象,代表每个会话
5.application:ServletContext应用对象,代表整个应用程序
6.config:ServletConfig配置对象,代表配置文件中的信息
7.pageContext:PageContext对象页面上下文对象,包含页面信息
8.page:当前页面对象(this)(基本不使用)
9.exception:Throwable异常对象,包含jsp异常信息