文章目录
- 一,关于main方法的问题
- 1,main方法传参形式?
- 2,mian方法为啥是静态的?
- 3,main方法可以重载吗?
- 4,main方法可以被覆盖吗?
- 5,main方法的返回类型可以修改吗?
- 6,main 方法的作用域可以 修改吗?
- 7,main方法可以同步吗?
- 8,main方法可以总结吗?
- 9,举例说明什么情况下会更倾向于使用抽象类而不是接口?
- 10,使用Final修饰符修饰的类、对象、方法和变量各有什么特点
- 1,集合接口有哪些?
- 2,enumeration和Iterator接口的区别
- 3, 为何Iterator没有add()方法?
- 4,Iterator和ListIterator的区别
- 5,Iterator的fail-fast属性有啥用?
- 6,在迭代一个集合的时候,如何避免ConcurrentModificationException?
- 7,hashmap与hashtable的区别
- 8.如何决定使用HashMAP还是TreeMap?
- 9. ArraylIST和vector的异同点?
- 10. array 和arraylist的区别
- 11,Arraylist和LinkedList的 区别
- 12,哪些集合提供了对元素的随机访问?
- 13,哪些集合类是线程安全的?
- 14,并发集合类是什么?
- 15,队列Queen和栈stack的区别?
- 16,comparable 和comparator接口的区别
- 17,如何对一组对象进行排序
- 18,如何确保一个集合作为参数传递时,确保函数不能修改它?
- 19,为什么集合类没有实现Cloneable和Serializable接口?
- 20,怎么选择是使用有序数组还是使用无须数组,以及相应的时间复杂度?
- 21,hashSet和TreeSet的区别,即相应的时间复杂度
- 22 ArrayList和Linkedlist的底层数据结构是什么?可以放null元素吗,可以重复吗?
- 23 ArrayList 默认容量大小
- 24 List和array之间如何相互转换
- 三,HashMap存储原理
- 四,java基础知识
- 1,接口和抽象类的区别
- 2 什么是值传递,什么是引用传递
- 3 JAVA 的两种异常类型是什么?
- 4 java Exception和Error的区别?
- 5 throw 和throws的区别
- 6 异常处理完后,Exception 对象会发生什么?
- 7 fianlly代码块和finalize()方法的区别
- 8,关于java 的序列化的应用serialization
- 9,什么是servlet?
- 10 servlet的生命周期
- 11 doget和dopost方法有什么区别?
- 12 什么是servlet链?
- 13 怎么知道哪一个客户端机器正在请求你的servlet?
- 14 http响应的结构是怎样的?
- 15 cookie和session的区别?
- 16 sendRedirect和forward的区别
- 17,什么是url编码,url解码
- 18 jsp请求是如何被处理的?
- 19,什么是JSP动作?
- 20 JSP中的隐含对象有哪些,jsp九大内置对象?
- 21,String 不是基本数据类型
- 21 String是final类型的,不可变
- 22 怎么比较两个字符串是否一样?
- 23 Switch 可以用String吗?
- 24 String str=new String("abc") 创建了几个对象?
- 25 String StringBuffer StringBuilder 有什么区别?
- 26 String.trim()方法是去掉收尾空白字符
- 27 可以自定义java.lang.String类并使用吗?
- 28 String和byte[]两者是如何相互转换的?
- 29 java 基本数据类型 及各自所占字节大小
- 30 关于java中byte类型数据的取值范围为-128~127的说明
- 31 public private protected 和默认的作用域区别
- 32 匿名内部类是否可以继承其他类?是否可以实现其他接口
- 33 & 和&& 的区别
- 34 Math.round(11.5)等于多少?Math.round(-11.5)等于多少?
- 35 short s1=1;s1=s1+1;有什么错?short s1=1;s1+=1;对不对?
- 36 数组有length方法,没有length()方法。string有length()方法
- 37 抽象类是否可以 继承实体类?
- 38 构造器可以被重载,但不能被重写
- 39 fianlly{}里面的代码会在return之前执行还是后
- 40 用最有效的方法算出2*8等于几?
- 41 抽象方法可否同时是static,native,synchronize?
- 42 对象中的boolean属性,默认是true还是false?
- 43 两个对象值相同(x.equal(y)==true),但却有可能有不同的hashcode,这句话对不对?及hashcode与equlas深入讲解
- 44 float f=3.4 是否正确?
- 45 throw throw finally的区别
- 46 .java 源文件是否可以包含多个类?
- 47 java会存在内存泄漏吗?
- 48 Java中多态的机制是什么?
- 49 自定义异常类案例
- 50 三次握手和四次挥手
- 51 为什么关闭连接要四次握手?
- 52 处理海量数据文件的办法,如给定a,b两个文件,各放50亿条url,找到两个文件中相同的url?
- 53 处理海量数据并排序输出,如有10个文件,每个文件1g,每个文件的每一行存放的都说用户的query,每个query都有可能重复,请按照query的品读排序。
- 54 java 反射的知识和通过反射创建类对象的3种方式
- 55 创建对象的几种方式
- 56 try catch 和finally的执行顺序
- 57 面向切面执行切面方法的执行顺序
- 58 sql注入的办法
- 59 instanceof 用法
- 60 接口中的属性的默认是public static final 、方法是public abstract
- 61 深克隆浅克隆的区别
- 62 java回调函数
- 五 tomcat知识
- 六 笔试题
一,关于main方法的问题
1,main方法传参形式?
String 数组,且不能改变
2,mian方法为啥是静态的?
main方法一定要是静态的,如果不是jvm就要先实例化它的类。如果是非静态的,就只能作为普通方法使用,虽然可以编译成功,但是运行会失败。
3,main方法可以重载吗?
可以,
4,main方法可以被覆盖吗?
因为其实静态的,所以在jvm编译时静态方法会编译在一起,为防止歧义,是不允许覆盖的。
5,main方法的返回类型可以修改吗?
void,不能修改
6,main 方法的作用域可以 修改吗?
public ,不能修改。这是入口,如果私有了还怎么让外部调用
7,main方法可以同步吗?
可以,运行synchronize做修饰,
8,main方法可以总结吗?
可以在java中终结main方法。
9,举例说明什么情况下会更倾向于使用抽象类而不是接口?
首先二者共同点:都遵循,面向接口而不是实现编码的原则,即都不要在二者中写逻辑代码。
不同点,类只能继承一个,但可以实现多个接口,
再者 在相应时间要求比较高的系统中,抽象类会比接口快一点。
最后如果有共同的默认行为可以写在抽象类中,精简代码。其实更为灵活的是二者协调使用,接口规范行为,抽象定义默认行为。
10,使用Final修饰符修饰的类、对象、方法和变量各有什么特点
一、使用Final修饰符修饰的类的特点:该类不能有子类;
二、使用Final修饰符修饰的对象的特点:该对象的引用地址不能改变;
三、使用Final修饰符修饰的方法的特点:该方法不能被重写;
四、使用Final修饰符修饰的变量的特点:该变量会变成常亮,值不能被改变。
二 java集合面试题
1,集合接口有哪些?
collection是顶级接口,其下有set 接口(不能包含重复原型,实现它的有hashset,treeset)和list接口(可以重复元素,根据索引查询,查询快,实现它的常见有arraylist,linkedlist)
Map是独立于collection的接口,键值对存储,不能有重复元素,实现它的有hashmap,hashtable。
其他的接口还有queen,sortedSet,sortedMap,listIterator等。
2,enumeration和Iterator接口的区别
enumeration速度是iterator的两倍,但是iterator加锁了,更安全,也可以做删除操作。
3, 为何Iterator没有add()方法?
iterator协议不能保证迭代的次序
4,Iterator和ListIterator的区别
1,后者继承自前者,因此功能上有所扩大,如可以双向遍历,前者只能向前遍历。后者还可以添加元素,替换元素,获取前后索引位置。
2,Iterator可以遍历list和set,LISTIterator只能遍历list。所以这里要明白set和list存储数据的区别。set是hash散列储存,不能有重复值,list添加删除元素时有顺序的,也是有索引的,可以有重复数据。
5,Iterator的fail-fast属性有啥用?
当我们每次尝试获取下一个元素的时候,Iterator的fail-fast属性会检查当前集合结构里的数据有没有任何改动。如果有改动就会报出ConcurrentModificationException异常。当然了这里的改动可能是别的线程操作的,也能是当前线程,总而言之,只要不是当前线程的Iterator提供的方法修改的都抛异常。如下案例,执行iterator自己的remove不会报错,但执行map的remove就会报错:
Map<Integer, String> map = new HashMap<>();
map.put(1, "a");
map.put(2, "a");
Iterator<Integer> keys = map.keySet().iterator();
while (keys.hasNext()) {
Integer i = 0;
try {
i = keys.next();
System.out.println("before remove: "+map.size());
//keys.remove();//正常
map.remove(i);//java.util.ConcurrentModificationException
System.out.println("remove running: "+map.size());
} catch (ConcurrentModificationException e) {
System.out.println(e.toString());
System.exit(0);
}
}
6,在迭代一个集合的时候,如何避免ConcurrentModificationException?
可以使用并发结合类,如CopyAndWriteArrayList,currentHashMap,而不是arrayList
7,hashmap与hashtable的区别
1,hashmap允许key和value是null,而hashTable不允许
2,后者是同步的,线程安全,前者不是,
3,hashmap提供对key的Iterator遍历,支持fail-fast;hashtable提供对key的enumeration进行遍历,所以不支持fail-fast。
8.如何决定使用HashMAP还是TreeMap?
二者的区别,前者是hash散列存储,后者是树状存储,虽然都很优秀,但散列存储还是更胜一筹,所以查询,添加删除这类操作还是用hashmap吧。如果要遍历出一个有序的key,那就可以用tereemap。再者java也提供了将map转treemap的方法,很方便。灵活使用。
TreeMap treeMap=new TreeMap<>();
treeMap.put("a", 1);
treeMap.put("c", 3);
treeMap.put("b", 2);
Iterator iterator=(Iterator) treeMap.entrySet().iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
a=1
b=2
c=3
9. ArraylIST和vector的异同点?
1,二者都是基于索引的,数组的储存结构,是有序的。
2,二者的迭代器都实现了fail-fast。
3,都允许空值,
4,vector是同步的,线程安全的,ArrayList不是
5,因为两个都有fail-fast属性所以,要在遍历时修改要使用copyAndWriteArrayList.
10. array 和arraylist的区别
1,Array可以容纳基本类型和对象,而ArrayList只能是对象,即使是基本类型,也是其装箱类(如:Integer)
2,Array 创建时要声明容量大小,后者不用
3,Array提供的功能少,
4,array的优势在于,在基本类型上更好,更适合多维数组,适合列表大小已经固定的,且不用删除修改的。
11,Arraylist和LinkedList的 区别
1,前者是基于数组的。后者是基于链表的,每个节点都与前一个和下一个节点相连接。所以前者更适合查询,后者更适合添加删除。后者会消耗更多的内存,因为其每个节点存储了前后节点的引用。
2,前者支持随机访问,即根据下标精准查询,时间复杂度是O(1)。后者查询的内部实现还是从起点查询开始查询。时间复杂度是O(n)
3,arraylist提供了add(插入位置,值)方法,可以插入到指定位置,但是后面的数据都要往后移,耗费性能。
4,linkedlist 也提供了 linkedList.add(index, element);方法,但是由于是链表的结构,元素只跟前后元素关联,所以插入耗费性能小
5,当然了二者的默认新增数据都是按新增顺序放的。
12,哪些集合提供了对元素的随机访问?
随机访问即根据下标或key可以直接找到数据存放的位置,精准查询.
ArrayList ,hashmap,TreeMap,HashTable提供了随机访问
13,哪些集合类是线程安全的?
Vector,HashTable,Properties,Stack是同步类。
14,并发集合类是什么?
是java1.5后提供的运行在遍历集合的过程中修改数据的一些集合。有copyOnWriteArrayList,CopyOnWriteArraySet,ConcurrentHashMap.
15,队列Queen和栈stack的区别?
1,二者都可以用来储存数据。
2,java.util.Queen是一个借口。stack是继承自Vactor的类。
3,队列大多是先进先出,栈是先进后出
16,comparable 和comparator接口的区别
二者功能主要用来给对象集合做排序。前者是由自己默认自然排序算法。后者提供的了多种排序算法供我们选择。
17,如何对一组对象进行排序
1,如果是数组可以使用Array.sort()方法。如果是列表可以用Collections.sort()排序。
2,更复杂就用16中的comparable和comparator实现吧
18,如何确保一个集合作为参数传递时,确保函数不能修改它?
可以使用Collections.unmodifiableCollection(Collection c)方法创建一个只读集合。
19,为什么集合类没有实现Cloneable和Serializable接口?
因为没必要。克隆和序列化的语义和含义跟具体的实现相关。因此应该由集合类的具体实现来决定如何被克隆和序列化。
20,怎么选择是使用有序数组还是使用无须数组,以及相应的时间复杂度?
首先要弄清楚,这里说的是数组,hash的数据存放也是无须的,但不是数组,所以不在讨论范围内。所以就细分为两种,一个是根据下表获取的有序队列,一个是基于链表的无序队列(如 linkedlist).两种优劣我们就不赘述了,这里说下时间复杂度,有序的查找是O(logn),无须的是O(N)。插入操作,有序是O(n),无序是O(1)。
21,hashSet和TreeSet的区别,即相应的时间复杂度
都是set说明元素时不可重复的,
hashset是根据hashcode计算位置的,也是无序的,提供了add,remove,contains等方法。时间复杂度是O(1)
treeset是树结构存储,有序的,add。remove,contains等操作的时间复杂度是O(logn).
总之还是hash快。
22 ArrayList和Linkedlist的底层数据结构是什么?可以放null元素吗,可以重复吗?
前者是数组,后者是链表
可以是null,
可以重复。
ArrayList<Integer> list11=new ArrayList<>();
list11.add(null);
list11.add(null);
System.out.println(list11.size());
LinkedList<Integer> linkedList=new LinkedList<>();
linkedList.add(null);
linkedList.add(null);
linkedList.add(1);
System.out.println(linkedList.size());
2
3
23 ArrayList 默认容量大小
jdk1.7之前默认大小是10,之后是0,每次扩容按1.5倍扩大
24 List和array之间如何相互转换
List到array 用toArray方法,array到list用arrays.asList(array)方法,但此方法得到的list是fianl的,不固定可是使用 new ArrayList(Arrays.asList(array))
三,HashMap存储原理
叙述阶段:
hashmap是一种键值对集合,主要使用的方法有put,get方法。对于put方法是于存放Entry键值对。存放原理是,对放入的值做hashCode运算,得出数组下标,放入对应的位置。当计算出来的下标已经存在就出现了所谓的hash冲突,会启用链表来解决,采用头插法或尾插法将新的值通过next指针指向下一个entry对象。
get方法也是对key做同样的hash运算,找到对应的下标取值,如果算出来的下标位置有多个Entry,那么久以此对比key值是否一样,再取值。简而言之,就是利用了hashcode和equal两个方法精确定位取值。
可能的复杂提问阶段:
1,hashMap的默认长度是16,增长方式是按2幂次增长。
2,那么如何根据key计算出键值对存放的桶(就一个entry占一个坑)的位置呢?
hashcode运算为了使数据在容器总存放的更均匀,通常使用取模运算,即取余。举个例子:如果key=“apple”的hashcode值是3029737,根据初始hashmap的长度16取余,即3029737%16=9,那么久放在下标是9的位置处。但是hashmap实际利用的是位运算。即3029737对应的二进制是10111000111101011101001,然后对hashmap的长度做减一运算,得到15,15的二进制是1111,所以
10111000111101011101001
&
0000000000000000001111
=1001,对应的十进制是9,效果是一样的,但是效率比作余运算快很多。因为hashmap的长度是2的次幂,所以扩容后,无论哪个长度减一后,长度对应的二进制都是11111的形式,很神奇。
3,hashmap死锁,是发生在当entry的个数占据数据量的0.75时,就会扩容。如果数据中有链表数据扩容过程中就会颠倒链表数据的位置,在多线程情况下就有可能出现死循环。这个过程很复杂,百度吧!java8后解决了这个问题
4,对于java8之后, hashmap为数组+链表+红黑树。采用尾插法插入链表数据,在此之前是头插法,因为研究人员认为,后来的数据最有可能先用到。
5,对于null值,是放置下标为例的table中。也是hash计算的结果吧!
6,最后说明下,hashcode计算的速度很快,因此查询速度很快。像Hbase这样的Nosql数据库就是用hash散列存储,能够达到准实时的效果。
1 解决hash冲突的几种办法
如果说到hash,并不代表人家就问hashmap,因为hash是一种算法,在heap表中,hbase中等都有用到。
1,开放定址法:出现冲突后,以计算出来的地址为key,计算其hashcode,得出的新的地址,如果还冲突就一次类推
2.再哈希法 :换个hash函数处理
3.链地址法(Java hashmap就是这么做的)
4.建立一个公共溢出区:把冲突的数据放到另一个集中的地方,不放表里面。
2 为什么会有hash冲突
不同的key会计算出不同的hashcode。但是在计算下标的时候,会出现冲突,因为hashcode对容量做位运算的时候只对尾数几个起作用,不同的hashcode有相同的尾数,就出现了hash冲突了。比如:
3 hash 扩容的过程
1,jdk1.8之前是重新计算各键的hashcode跟(容量-1)的位运算后的值,没什么好说的,比较耗性能
2,jdk1.8是先拿到扩容后的容量-1的值的二进制,然后对比该二进制的最高位跟每个hashcode值的对于位做位运行,如果是0,该值还在原位置,如果不是就重新计算下标,放到新的位置。如5(101),37(100101),53(110101)这三个数据,在容量时16的hashmap中,对于的下标都是5:
1111& 101=0101 即5
1111& 100101=000101 即5
1111& 110101=000101 即5
这三个位置都一样,就用链表连接。
扩容后:
11111& 000101=0101 即5 拿11111的最高为比较后是0
11111& 100101=000101 即5 拿11111的最高为比较后是0
11111& 110101=010101 即16+4+1=21=16+5(刚好等于原位置加16) 拿11111的最高为比较后是1
可以看到扩容后前两个位置不变,后一个位置变了。
4 hashmap 链表怎么转红黑树的?
但是当链表长度很长的时候,查找的性能会很低,JDK1.8中对此进行了优化,当链表的长度超过TREEIFY_THRESHOLD(8)的时候,就理解成链表长度达到8的时候,链表会树化,即通过treeifyBin()方法转换成红黑树。
四,java基础知识
1,接口和抽象类的区别
1,接口所有方法都是隐含抽象的,实现类都必须实现。抽象类可以同时包含抽象和非抽象方法
2,如果实现类也是抽象的,那么可以不用必须实现所有方法。
interface AA{
public void aa();
}
abstract class AAA implements AA{
private int a=1;
}
3,类可以实现多个接口,但只能继承一个抽象类
4,接口中的变量默认是用final和public修饰的。抽象类可以有不用final和public修饰的变量,但如果是抽象的,还是要用final和public修饰
5,接口和抽象类都不能被实例化。但可以执行抽象类中的main方法,因为其实静态的。
2 什么是值传递,什么是引用传递
前者是传递真实的值,后者是传递的是值得地址。
值传递是针对基本型变量而言的,比如说int a=3,同时又有一个int b=3.那么a,b指向的是3的同一块内存地址,所以3既然已经存在了,b=3这个3也就不用重新建一个块新的地址了。如果把b=4,内存中没有4就会开辟一块新的地址,b再指向这个地址,而不是改变原来的值。
引用传递一般是针对对象型变量而言的,传递的是该对象的存储地址,如果有多个变量指向这个地址,那么这些变量都会对这个对象做修改。
总之,大家都认为java内的传递时值传递。我觉着还是模糊着回答吧!像上面分开举例说。
3 JAVA 的两种异常类型是什么?
受检查的异常和不受检查的异常,更为通用的讲法是非运行时异常和运行时异常,运行时异常是java会自动抛出,不需要强制程序员处理的异常,常见的运行时异常如NullPointerException,classCastException,IndexOutOfBoundsException,ArrayStoreException。非运行时异常就反之了,比如做io时,强迫我们抛出或处理IOexception。
4 java Exception和Error的区别?
二者都是Throwable的子类,
exception用于程序可以捕获的异常,通过捕获处理,我们的程序还可以继续运行。但是如果error了系统就不能继续运行了,比如说OOM,栈溢出stackoverflowError。
5 throw 和throws的区别
1、throw代表动作,表示抛出一个异常的动作;throws代表一种状态,代表方法可能有异常知抛出
2、throw用在方法实现中,而throws用在方法声明中
3、throw只能用于道抛出一种异常,而throws可以抛出多个异常
6 异常处理完后,Exception 对象会发生什么?
Exception 对象会在下一个垃圾回收过程中被回收掉。
7 fianlly代码块和finalize()方法的区别
无论是否抛出异常,finally代码块都会执行,它主要用来释放因公占用的资源。finalize方法是在对象被垃圾回收前由JVM调用的,用来回收特殊渠道申请的内存。
8,关于java 的序列化的应用serialization
1,java提供了一中叫做对象序列化的机制,他把对象表示成一连串的字节,里面包含了对象的数据,类信息等全部内容。序列化可以看成是为了把对象存储在磁盘上或是从磁盘上读出来并重建对象扁平化的处理方式。反序列化(deserialization)就是把扁平化状态转成活动对象的步骤。
2,是么情况下使用?要把对象存放到磁盘中,或在网络上传输对象
3,java提供了Serializable序列化接口,实现即可。也提供了ObjectOutputStream和ObjectinputStream对象流写入和读取对象。并且某类如果实现了序列化,类里面的对象变量也会自动序列化,不用一一实现序列化接口。
4,案例:
import java.io.Serializable;
public class Person implements Serializable {
private static final long serialVersionUID = 8428798474641047929L;
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
public class SerializationUtil {
//.bin后缀代表二进制文件
private static String FILE_NAME = "D:/obj.bin";
/**
* 序列化
*/
public static void writeObject(Serializable s) {
try {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(FILE_NAME));
oos.writeObject(s);
oos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 反序列化
*/
public static Object readObject() {
Object obj = null;
try {
ObjectInput input = new ObjectInputStream(new FileInputStream(FILE_NAME));
obj = input.readObject();
input.close();
} catch (Exception e) {
e.printStackTrace();
}
return obj;
}
}
public class Main {
public static void main(String[] args) {
//序列化
Person person = new Person();
person.setName("混世魔王");
SerializationUtil.writeObject(person);
Person p = (Person) SerializationUtil.readObject();
System.out.println("name = " + p.getName());
}
}
9,什么是servlet?
是用来处理客户端请求并产生动态网页内容的java类。它主要是用来处理存储html表单提交的数据并产生动态内容,在无http协议下管理状态信息。
10 servlet的生命周期
接受客户端请求,载入servlet引擎,调用init初始化方法。然后servlet对象通过调用service方法来处理所有随后来自客户端的请求,最后,调用servlet调用destory方法把servlet删除掉。
11 doget和dopost方法有什么区别?
doget:处理get请求
dopost:处理post请求
12 什么是servlet链?
servlet链是把一个servlet的输出结果发送给另一个servlet的方法。第二个servlet的输出结果发送给第三个。以此类推,链条上最后一个servlet复制把响应发送给客户端。
13 怎么知道哪一个客户端机器正在请求你的servlet?
servletrequest类可以找出客户端的ip地址或主机名等内容。getremoteAddr()方法获取客户端主机名的ip地址,getRemotehost可以获主机名。
14 http响应的结构是怎样的?
1,状态码(startecode):表示响应状态,200是正常响应,像300,404 等就是不正常的,通常数字越大错误程度越严重。
2,头信息(header): 包含了更多响应信息,如编码格式,日期等
3,报文体(body):包含了响应内容,我们要传输的用户数据都在里面。他可以包含,图片,html代码,等。由紧跟在header后面的数据字节组成。
15 cookie和session的区别?
cookie是web服务器发送给浏览器的一块信息。浏览器会在本地文件中给每一个Web服务器存储cookie。所以cookie信息是保留在客户端的。session是保存在服务器端。客户端可以选择禁用cookie,并且session仍然可以正常工作。sessin可以存储任何java数据类型。而cookie只能储存String类型。
16 sendRedirect和forward的区别
前者是重定向:创建一个新的请求,之前的作用域不能访问了。
后者是转发,把请求转发到新的目标上,之前的作用域还能访问。
一般认为重定向要比转发慢。
17,什么是url编码,url解码
编码(encode):把url中的空格和特殊字符转成十六进制表示。解码(decode)反之。
18 jsp请求是如何被处理的?
浏览器首先要请求一个以.jsp为扩展名结尾的页面,发起jsp请求,然后web服务器读取请求,使用jsp编译器把jsp页面转换成servlet类,然后服务器调用servlet类,处理浏览器请求,一旦执行结束,servlet把响应发送给客户端。
19,什么是JSP动作?
就是在jsp文件中动态处理来自后台或返回后台的代码部分,通常被包裹在尖括号里面<>。
常见的动作有:
jsp:include:jsp页面被请求时包含的一个文件。
jsp:useBean 初始化javabean的属性
jsp:setProperty 设置Javabean的属性
jsp:forward把请求转发到新的页面
jsp:plugin产生特定浏览器的代码
20 JSP中的隐含对象有哪些,jsp九大内置对象?
就是页面中一个写java对象,开发者可以不用声明就直接使用他们。常见的有
application,page,request,reponse.session,exception,out,config,pageContxt,也称为九大内置对象
21,String 不是基本数据类型
21 String是final类型的,不可变
22 怎么比较两个字符串是否一样?
比较字符串值用equals方法,比较字符串对象用==
23 Switch 可以用String吗?
jdk7+以后可以了
24 String str=new String(“abc”) 创建了几个对象?
两个,abc本身创建在常量池中,又通过new常见在堆中。
25 String StringBuffer StringBuilder 有什么区别?
三者最大的不同是String 不可变,后面两个可变。Stringbuffer是线程安全的,Stringbuilder是线程不安去的,速度较快。
26 String.trim()方法是去掉收尾空白字符
27 可以自定义java.lang.String类并使用吗?
可以,也可以编译成功,但是不能被加载使用,因为类加载机制会优先加载java提供的基础类。
28 String和byte[]两者是如何相互转换的?
String>byte[] 通过String类的getBytes方法,
byte[]>String ,通过new String(byte[])构造器
29 java 基本数据类型 及各自所占字节大小
计算机的基本单位:bit . 一个bit代表一个0或1
byte:1byte = 8bit 1个字节是8个bit
short:2byte
int:4byte
long:8byte
float:4byte
double:8byte
boolean:1byte
char:2byte
30 关于java中byte类型数据的取值范围为-128~127的说明
既然找到了1byte=8bit, 既然是bit,那就肯定要看二进制的了,那么8bit的二进制最大表现形式是多少呢?首先要说明的是二进制最高为表示符号,0表示正,1表示负。那么最大表现形式就是01111111,它的十进制就是127了。
那么-128是怎么来的呢,怎么不是-127呢? 这个问题还是很复杂的,牵扯到原码,反码,补码的问题,我也不太清楚,先记住把,对于127的11111111反转成负的就是首位1不变,其他变0,就是10000000,这个值恰好就是-128,同时也满足首位1表示负的要求。
此外,在面试中常见到如下问题,字节类型127加1是多少?看了上面就改知道是-128了。
System.out.println((byte)(127+1));
结果:-128
31 public private protected 和默认的作用域区别
默认是friendly ,
public 可被当前类,继承类,其他包访问
protected 可被当前类,同一包,继承类访问
friendly 可被当前类,同一包访问
private 可被当前类访问
32 匿名内部类是否可以继承其他类?是否可以实现其他接口
匿名内部类是因为其类的名字不会被我们明写出来,如下面案例中的 Chouxiang c=new Chouxiang() {这个大括号里面的内容就是一个类的内容但是它没有类名,只知道它继承了chouxiang()这个抽象类。}
不能继承其他类,因为它是没有名字的内部类;但可以作为接口被另一个内部类实现。
内部类有四五种,我常用到的是匿名内部类,如Runnable接口的实现。
它有两种继承方式,如下面例子中的接口和抽象类
举个例子:
package test;
public class innerclass {
public static void main(String[] args) {
System.out.println("下面是是内部类的程序展示");
//创建外部类和内部类的方法有点不相同
A4 a=new A4();
A4.B b=new A4().new B();
a.say2();
b.sayit();
System.out.println("现在开始匿名内部类程序的编写\n");
Chouxiang c=new Chouxiang() {
String name="Geek Song too";
public void say3()
{
System.out.println("这是匿名内部类当中的方法,重写了抽象类的方法");
System.out.println(name);
}
};//在使用匿名内部类的时候,当这个类在陈述完之后,是需要打分号的。
c.say3();
System.out.println("我们来看看这个name到底是抽象类当中的name还是匿名内部类当中的"+c.name);//结果果然是父类当中的属性,和我们多态的性质相重合了
//这就是所谓的向上转型。现在我们再来试试接口的匿名内部类实现
Jiekou yui=new Jiekou() {
@Override//由于必须实现接口当中的方法,因此计算机就自动为我们写上了override的标识符了
public void say5() {
System.out.println("这是继承的接口当中的方法");
}
};
yui.say5();
}
}
class A4{
int waibu=12;
public void say2()
{
System.out.println("这是外部类当中的方法");
}
class B
{
int neibu=13;
public void sayit()
{
System.out.println("这是内部类里面的方法");
System.out.println("使用内部类和外部类当中的数值进行想加的结果是"+(neibu+waibu));
//之所以内部类可以使用外部类的属性是因为在创建对象的时候,已经给内部类的对象附加了一个外部类的对象,内部类的对象是建立在外部类对象的基础上的。
}
}
}
//虽然内部类的程序已经成功了,但是匿名内部类的程序还没有成功,现在我们来创建一个匿名内部类(在主方法当中,首先应该创建一个抽象类或者接口)
abstract class Chouxiang
{
String name="Geek Song";//抽象类的属性是不会被调用的,除了方法
public void say3()
{
System.out.println("这是抽象类当中的方法,抽象类当中是允许有具体方法来进行实现的,接口不行");
}
}
interface Jiekou
{
public void say5();
}
下面是是内部类的程序展示
这是外部类当中的方法
这是内部类里面的方法
使用内部类和外部类当中的数值进行想加的结果是25
现在开始匿名内部类程序的编写
这是匿名内部类当中的方法,重写了抽象类的方法
Geek Song too
我们来看看这个name到底是抽象类当中的name还是匿名内部类当中的Geek Song
这是继承的接口当中的方法
此外像我们写线程用到的runnable内部类就常有用到:
threads[i]=new Thread(new Runnable() {
@Override
public void run() {
for(int i=0;i<1000;i++){
increase();
}
}
});
33 & 和&& 的区别
前者是位运行,按位与运算
后者是逻辑运算符,表示且
34 Math.round(11.5)等于多少?Math.round(-11.5)等于多少?
12,-11 round方法是返回最接近的长整数
35 short s1=1;s1=s1+1;有什么错?short s1=1;s1+=1;对不对?
s1+1的结果是整型的,等于short型要做强转
第二个正确
36 数组有length方法,没有length()方法。string有length()方法
37 抽象类是否可以 继承实体类?
我们平时都是见实体类继承抽象类,还真没见过反着来的。其实抽象类也可以继承实体类,但是前提是要有明确的构造函数
38 构造器可以被重载,但不能被重写
39 fianlly{}里面的代码会在return之前执行还是后
在return之前
可以debug试试
40 用最有效的方法算出2*8等于几?
System.out.println(2<<3);
41 抽象方法可否同时是static,native,synchronize?
都不行,因为这些关键字修饰的方法都是需要有方法体的,与abtract矛盾。
42 对象中的boolean属性,默认是true还是false?
false
43 两个对象值相同(x.equal(y)==true),但却有可能有不同的hashcode,这句话对不对?及hashcode与equlas深入讲解
先看案例:
1,不重写equal和hashcode方法
public class People2 {
public String name;
public int age;
public People2(String name, int age) {
super();
this.name = name;
this.age = age;
}
}
class TestPeole{
public static void main(String[] args) {
People2 people2=new People2("张三", 19);
People2 people3=new People2("张三", 19);
System.out.println(people2.equals(people3));
System.out.println(people2.hashCode()==people3.hashCode());
}
}
结果:
false
false
2,重写equal和hashcode方法
public class People2 {
public String name;
public int age;
public People2(String name, int age) {
super();
this.name = name;
this.age = age;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
People2 other = (People2) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
}
class TestPeole{
public static void main(String[] args) {
People2 people2=new People2("张三", 19);
People2 people3=new People2("张三", 19);
System.out.println(people2.equals(people3));
System.out.println(people2.hashCode()==people3.hashCode());
System.out.println(people2==people3);//与equal 和hashcode是否重写无关,比较的是地址
People2 people22=people2;
people22.name="李四";
System.out.println(people2.equals(people22));//true
System.out.println(people2.hashCode()==people22.hashCode());//true
System.out.println(people2==people22);//true
String a="a";
String b="a";
System.out.println(a==b);//true
System.out.println(a.hashCode()==b.hashCode());//true
System.out.println(a.equals(b));//true
String c=new String("a");
String d=new String("a");
System.out.println(c==d);//false
System.out.println(c.hashCode()==d.hashCode());//true
System.out.println(c.equals(d));//true
}
}
结果:
true
true
false
true
true
true
true
true
true
false
true
true
总结,1,对于相同的类,new出来的对象,在属性值都一样的情况下,equals和hashcode方法重写哪个,哪个就相等,二者互不关联。
2,对于 == 比较的是地址,跟hashcode无关,我们new出来两个对象,那在堆中的内存地址肯定不一样啊!重写hashcode和equals方法也没用。
3,明确equals和hashcode的作用定位,二者只是判断变量是否相等的工具,并不能影响对象在堆中的存在状况
4,对于基本类型的各种装箱类和String,他们都已经实现重写了hashcode和equals方法,通过new的方式创建就当在正常的对象看待就行了。通过直接=的方式赋值,那么不同的变量相同的值,指向的是常量池中该值的地址。池中一个确定的值只有唯一的一个。
5,equals和hashcode在hashmap中的应用。根据前面的关于hashmap的讲解我们知道hashmap元素的位置是根据key的hashcode得出桶的位置的,如果key相同,那hashcode也相同,桶的位置也相同。那就要比较key的equlas方法了,也相同的话那就要更新键值了。如果key不同,那其实也有可能会造成hashcode相同,为什么?就单一的一个字符而言,自然不会相同,但是因为key可能是好多字符的组合,就有可能出现key不同,但是hashcode相同的状况了;就像1+4=5,而 2+3也可以等于5啊。总之这时呢,也要比较equals方法了,经对比不同,就要启用链表,使用尾插法,插入新的数据了。
6,equals和hashcode在set中的应用。一般使用set的对象都要重写equals和hashcode方法的,不然像上面的例子中,你不重写,那相同的对象值也比较不出来啊!所以set也是根据hashcode和equals对对象去重的。也可以测试试试,只有都重写了两个方法,set才能对同一类的不同对象去重。
7,想了那么多,发现只用比较equlas方法就可以判断两个对象值是否相同了,那还要hashcode方法干嘛!这也没毛病。确实可以判断。但是二者的功能不同,就像hashmap中,hashcode返回int可以找对象的放的位置,equlas方法行吗?对不对。
8,原生hashcode和equlas方法和重写后的还是很大不同的,有人说对象在堆中的内存地址是关联hashcode的,我也没细研究,但猜测这个关联是关联的原生hashcode,原生的hashcode是不随对象属性值的改变的,始终保持创建时得到的值。重写后的hashcode是关联属性值的,是随时都可能发生改变的,但堆的地址并没有改变啊,所以这时候就没啥关系了。
People2 people2=new People2("张三", 19);
System.out.println(people2.hashCode());
people2.name="王二";
System.out.println(people2.hashCode());
776439
938607
9,观察equals方法可以看出,它比较对象是先getclass对比类信息的,再依次对比各属性。这样就确保比较的是同一个类的对象了。
10,言归正传,回归题目,网上给的答案都是不对,有相同的hashcode。这是因为equals和hashcode方法已经被大家认为是一体的了。首先两个对像值相同,说明他们重写了equals方法,那hashcode方法肯定也重写了。所以就相等了。
11,为什么equals和hashcode方法要一起存在呢?这是个原则问题,二者在一定程度上包含了对象的信息,对象属性值发生了改变,你重写后也能更好地体现嘛,这点在解决hash冲突上体现的尤为明显。
44 float f=3.4 是否正确?
不正确,需强转 float f=(float)3.4
45 throw throw finally的区别
throw语句用来明确抛出一个异常
throws用来标记一个成员函数可能抛出的各种异常
finally为保证一段代码不管发生什么异常都被执行的一段代码
46 .java 源文件是否可以包含多个类?
可以,但必须只有一个与文件名相同。
47 java会存在内存泄漏吗?
内存泄漏:对象已经没有被应用程序使用,但是垃圾回收器没办法移除它们,因为还在被引用着。
它的特点是对象是GC ROOT可达的,但是实际并没有用到。常见情况如下:
1,静态集合类中的元素。如hashmap vector,即使给里面的元素设置空的,GC也不能回收,因为他还被集合引用着
2,各种链接或io流没有显示close(),如数据库的,文件读写的
还有其他的各种情况,不一一说了。
48 Java中多态的机制是什么?
重写和重载
49 自定义异常类案例
public class MyException extends Exception{
/**
* 自定义异常类,需要继承Exception
*/
private static final long serialVersionUID = 1L;
public MyException(){
super();
}
public MyException(String msg){
super(msg);
}
}
```java
public class ThrowTest {
void worryMethod() throws MyException{
try{
int a = 1/0;
System.out.println(a);
}catch (Exception e) {
/**
* throw 扔出异常,那么调用此方法的函数就要处理此方法抛出来的异常。
* eclipse也会自动提示开发人员调用此方法时要通过try catch 或exception两种方式处理,
* 这样 调用此方法的函数就必须处理
*/
throw new MyException("抛出我自己定义的异常");//最后一行
// System.out.println("------上面一步跑出异常后就不在往下走,也就是不会打印这一句话----"); 报错了
}
}
public class ExceptionTest {
/**
*测试类,需要捕获异常
*/
public static void main(String[] args) {
ThrowTest throwTest=new ThrowTest();
try {
throwTest.worryMethod();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
com.liuxin.file.MyException: 抛出我自己定义的异常
at com.liuxin.file.ThrowTest.worryMethod(ThrowTest.java:14)
at com.liuxin.file.ExceptionTest.main(ExceptionTest.java:27)
50 三次握手和四次挥手
三次握手:1,客户端发送联机请求,生成请求码是1和请求序列号,如123456, 然后发送到服务器,服务器知道请求码是1知道客户端要建立连接请求。2,主机B确认联机信息,然后生成确认码是1和确认序列号,把请求序列号赋值给确认请求序列号并加1,成123457,请求序列号再随机生成一个,如234567,发送至客户端;3,客户端检查确认序号是不是加1后的和确认码是不是1,若正确,把请求序列号加1赋值给确认序列号,确认序列号再加1成234568,然后和确认码一起发送到服务器,服务器检查确认序列号是不是加1后的,确认成功则成功建立连接。
四次挥手:1,客户端发送结束请求到服务器,生成结束请求序列号;2,服务器收到结束请求后,生成确认码和确认序列号,并把确认序列号加1,并重新生成新的序列号,发送到客户端。3,服务器主动向客户端发送结束请求,服务器进入最后等待确认关闭状态。4,客户端收到结束请求后,同样先检查确认序列号是不是加1后的,然后再把步骤2中新生成的序列号赋值给确认序列号,再加1,发送到服务器。服务器收到并确认后,后彻底关闭连接,完成四次挥手。最后客户端等待2msl主动关闭连接。
51 为什么关闭连接要四次握手?
因为客户端发送关闭请求后,说明没有数据要服务器处理了,而服务器可能还有正在处理的数据还没有发回,所以服务器可能要先把正在处理的链接发完后,在发送结束请求。
52 处理海量数据文件的办法,如给定a,b两个文件,各放50亿条url,找到两个文件中相同的url?
思考:50亿是个很大的数据量,假设每个url大概由30个英文字符串组成,每个英文字符是栈一个字节,那就是30个字节。那50亿就是50亿*30/1024/1024/1024=139G,远大于我们常见的内存4g或8g。所以不能将其完全加载到内存中,所以要对其进行分割。
处理:遍历a文件,对每个url做hash(url)运行,然后再对hashcode值取1000的模运行,结果余几就放到编号是几的文件中,总共1000个文件,这样每个文件大概14M。然后最b文件做同样的处理,也得出1000个文件。因为是对url做hash取模运行,所以相同的url肯定会在相同的变化文件中。最后把a的一个小文件的url都放到hashset中,然后遍历对应b的小文件,看是否在hashset中,是的话就是相同的url。
53 处理海量数据并排序输出,如有10个文件,每个文件1g,每个文件的每一行存放的都说用户的query,每个query都有可能重复,请按照query的品读排序。
顺序读取10个文件,按照hash(query)%10对query做hashcode取模,这样相同的query会被放到相同的文件中,总共10个文件。每个文件大概会是1g大小。(记住hash函数的结果是随机的,无序的)。然后依次对10个文件用hashmap做计数操作,然后hash转list,再用collections.sort对list排序。简单案例如下:
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
public class HashMapTest {
public static void main(String[] args) {
//这个数值类比文件及文件内容
String[] a1={"a","b","c","d","a","d"};
//putmap方法做计数操作
Map<String, Integer> map = putMap(a1);
//余下代码是做排序操作
List<Map.Entry<String,Integer>> list = new ArrayList<Map.Entry<String,Integer>>(map.entrySet());
Collections.sort(list,new Comparator<Map.Entry<String,Integer>>() {
//升序排序
public int compare(Entry<String, Integer> o1,
Entry<String, Integer> o2) {
return o1.getValue().compareTo(o2.getValue());
}
});
for(Map.Entry<String,Integer> mapping:list){
System.out.println(mapping.getKey()+":"+mapping.getValue());
}
}
private static Map<String, Integer> putMap(String[] a1) {
// TODO Auto-generated method stub
Map<String, Integer> map = new HashMap<String, Integer>();
for(int i=0;i<a1.length;i++){
if(map.get(a1[i])!=null){
map.put(a1[i],map.get(a1[i])+1);
}else{
map.put(a1[i],1);
}
}
return map;
}
}
结果:
b:1
c:1
a:2
d:2
54 java 反射的知识和通过反射创建类对象的3种方式
1,反射的作用是可以通过给出.class类文件得到类的属性和方法,还可以用来创建对象。很灵活,业务代码中的主体架构都是用到反射动态的给出类名创建不同的对象或执行不同的方法。但是反射根据类名找类存在个遍历的过程,能还是不如直接new来的快。
2,获取class对象的3种方法:
package test;
public class ClassTest {
public static void main(String[] args) throws ClassNotFoundException {
// TODO Auto-generated method stub
//1,通过对象获取
A a=new A();
Class class1=a.getClass();
System.out.println(class1.getName());
//2,直接赋值class
Class class2=A.class;
System.out.println(class2.getName());
System.out.println(class1==class2);
//3,forname方法获取class全路径,
Class class3=Class.forName("test.A");
System.out.println(class3.getName());
}
}
这三种常用第三种,灵活性最高。
55 创建对象的几种方式
1,直接new
2,通过加载类路径得到类对象,然后强转成对象
package test;
public class ClassTest {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
A a=new A();
Class class3=Class.forName("test.A");
System.out.println(class3.getName());
A a2=(A) class3.newInstance();
System.out.println(a==a2);
}
}
3,使用clone方法,这个前面讲过,需要克隆的类实现cloneable接口,然后就能通过clone方法复制前一对象的全部内容
4,反序列化,也讲过,类要实现serializable接口,然后把对象通过对象流写入到文件中,再读出来还是个对象。
56 try catch 和finally的执行顺序
不管有无异常都会最后执行finally,像文件流,如果出错,fianlly会帮你关闭的。
如果有return 就先执行fianlly,再retrun
57 面向切面执行切面方法的执行顺序
使用@Order注解进行指定值,值越大越先执行。
58 sql注入的办法
SQL注入即是指web应用程序对用户输入数据的合法性没有判断或过滤不严,攻击者可以在web应用程序中事先定义好的查询语句的结尾上添加额外的SQL语句,在管理员不知情的情况下实现非法操作,以此来实现欺骗数据库服务器执行非授权的任意查询,从而进一步得到相应的数据信息。
办法:
1,权限管理,如果可以的话只有管理员才能做增删改,就是降低普通用户的权限
2,避免直接向用户显示数据库错误,因为攻击者可以根据错误获取数据库信息
3,严格匹配参数输入格式,如用正则表达式
59 instanceof 用法
一个二元操作符(运算符),和==,>,<是同一类东西。
它的作用是判断其左边对象是否为其右边类的实例,返回boolean类型的数据。
如:
System.out.println("abcd" instanceof Object);
true
Object是所以对象的父类。
60 接口中的属性的默认是public static final 、方法是public abstract
61 深克隆浅克隆的区别
深克隆是把对象和对象的属性,以及属性的值都原封不动的克隆过去了。
浅克隆是只复制对象及属性。
如果对象实现Cloneable并重写clone方法不进行任何操作时,调用clone是进行的浅克隆。而使用对象流将对象写入流然后再读出是进行的深克隆。
62 java回调函数
回调分为同步回调和异步回调
1,同步回调,就是告诉被调用者调用者的地址,可以使用接口实现。调用者实现接口,被调用者装载接口。调用者通过接口把自己的信息传递到被调用者。
简单说就是被调用者提供一个方法接口给调用者,调用者在里面写自己的实现,再由被调用者在合适的时候执行。
public class Client implements MyCallBack{
Server server = new Server();
/**如果契约,必须定义一个规定的函数,以实现被调
* 这个方法,本对象一般不执行,只用于回调
*/
public void execute(){
System.out.println("我被回调了");
}
/**将本对象以接口的方式传递过去,server处理完后执行接口中的方法,实现回调*/
public void callServer(){
server.testTime(this);
}
public static void main(String[] args) {
Client client = new Client();
client.callServer();
}
}
class Server {
/**如果要执行这个方法,必须提供回调接口*/
public void testTime(MyCallBack callBack) {
callBack.execute(); ///进行回调操作
}
}
interface MyCallBack {
//执行回调操作的方法
void execute();
}
2,异步回调
回调函数适合多线程的情况,当前线程在调用另一个线程的方法而不用等其返回,继续向下执行,另一个线程处理完了,自动返回。
多线程里futuretask与callable的合作方式就是一种回调方式,主线程在运行过程中,使用callable多线程执行其他的任务,而主线程也能继续往下执行,当主线程需要获得callable的执行结果时,使用futuretask.get()获得,如果还没处理完,就让主线程阻塞,直到拿到。
五 tomcat知识
1,tomcat运行流程
以javaweb为例:
1,假设来自客户的请求为:http://localhost:8080/test/index.jsp.请求被发送到本机端口8080;
2,请求被connector连接器获取并把它转交给engine(在server.xml中配置)处理器处理,connector等待engine处理结果。
3,engine获得请求,根据url匹配所有主机host(在server.xml中配置)。这里是匹配到名称为localhost的主机;
4,下面就交给web包处理了,
5,web处理完了之后将返回信息返回到engine。
6,engine再把返回数据交给connector,
7,connector把数据返回到客户端。
2,Connector的配置介绍和 它的几种运行模式的介绍
对Connector的配置位于conf/server.xml文件中,内嵌在Service元素中,可以有多个Connector元素。
运行模式:
1,BIO (blocking I/O),顾名思义,即同步阻塞式I/O操作
一个典型的配置如下:
2,NIO(New I/O),是一个基于缓冲区、并能提供非阻塞I/O操作的Java API
3,NIO2:NIO的升级,JDK7后开始支持,是异步非阻塞IO
这两个NIO的配置可设置如下:
//NIO
protocol="org.apache.coyote.http11.Http11NioProtocol"
//NIO2
protocol="org.apache.coyote.http11.Http11Nio2Protocol"
对于这里的非阻塞原理比较复杂,就不解说了,我也不太清楚。
对于异步可以简单说说,客户端请求来到后,通过管道进入一个wrapper容器管道,调用servlet实例的service后,创建一个异步上下文,将耗时的逻辑操作封装起来,交给用户自定义的线程池,而tomcat的线程就回到executor线程池继续接受其他的请求。当然了之前的线程处理完了后要返回到客户端,所以在wrapper容器中一个存方法request和response对象的区域,以便输出响应报文
4,apr:Apache Portable Runtime可移植运行库,Tomcat将以JNI的形式调用Apache HTTP服务器的核心动态链接库来处理文件读取或网络传输操作,从而大大地提高Tomcat对静态文件的处理性能。 Tomcat apr也是在Tomcat上运行高并发应用的首选模式。简而言之就是:tomcat直接调用操作系统语言处理程序,省去了很多中间转码环节当然很快了。
apr server.xml 配置
<Connector port="8080" protocol="org.apache.coyote.http11.Http11AprProtocol"
connectionTimeout="20000"
redirectPort="8443" />
3 tomcat jvm内存分配设置
修改bin/catalina.bat文件设置参数:
set JAVA_OPTS=-Dfile.encoding=UTF-8
-server
-Xms1024m
-Xmx2048m
-XX:NewSize=512m
-XX:MaxNewSize=1024m
-XX:PermSize=256m
-XX:MaxPerPermSize=356m
-XX:NewRatio=2
-XX:MaxTenuringThreshold=50
-XX:+DisableExplicitGC
4 Connector连接器线程常见配置和线程存活时间配置
connectionTimeout - 网络连接超时,单位:毫秒。设置为0表示永不超时,这样设置有隐患的。通常可设置为30000毫秒。
keepAliveTimeout - 长连接最大保持时间(毫秒)。默认是60s且长连接默认是打开的。
maxKeepAliveRequests - 最大长连接个数(1表示禁用,-1表示不限制个数,默认100个。一般设置在100~200之间)
maxHttpHeaderSize - http请求头信息的最大程度,超过此长度的部分不予处理。一般8K。
URIEncoding - 指定Tomcat容器的URL编码格式。
acceptCount - 指定当所有可以使用的处理请求的线程数都被使用时,可以放到处理队列中的请求数,超过这个数的请求将不予处理,默认为10个。
maxSpareThreads - 最大空闲连接数,一旦创建的线程超过这个值,Tomcat就会关闭不再需要的socket线程 The default value is 50.
maxThreads - 最多同时处理的连接数,Tomcat使用线程来处理接收的每个请求。这个值表示Tomcat可创建的最大的线程数。 minSpareThreads - 最小空闲线程数,Tomcat初始化时创建的线程数 .
minProcessors - 最小空闲连接线程数,用于提高系统处理性能,默认值为10。
maxProcessors - 最大连接线程数,即:并发处理的最大请求数,默认值为75。
总结:可见tomcat本身就是一个线程池,如果你的程序内部没有自己编写的线程池和队列,就要熟悉上面的配置了。像我工作中的系统有自己的队列和线程池,这里tomcat起的作用就是一个连接的转发,上面的配置重点是keepAliveTimeout 线程的存活时间,可以的话把线程的存活时间降低一点进而增大访问量。再者就是最大线程数了,也要调高些。至于其他的都不重要了。
5,简单说下tomcat容器是如何创建servlet类实例的
容器启动时,会读取在webapps目录下的所有web应用中的web.xml文件,然后对xml文件解析,读取其中的servlet注册信息,然后将每个应用注册的servlet类进行加载,并通过反射的方式实例化。
6 tomcat 同步阻塞和异步阻塞的区别
六 笔试题
1: ++
int k = 0;
int ret = ++k + k++ + ++k + k;
System.out.println(ret);
8
2:for
static boolean foo(char c) {
System.out.print(c);
return true;
}
public static void main(String[] argv) {
int i = 0;
//for(65;88&&(i<2);67)
for (foo('A'); foo('B') && (i < 2); foo('C')) {
i++;
foo('D');
}
}
ABDCBDCB