Java面试宝典

参见:http://fjding.iteye.com/blog/2319515

http://www.360doc.com/content/16/0216/00/26211242_534899333.shtml

J2SE基础 


1. 九种基本数据类型的大小,以及他们的封装类。 
Long(64),Integer(32),Short(16),Double(64),Float(32),Character(16),Byte(8), 
Boolean(1),void 
为什么java能做到基本类型大小与平台无关:因为java是运行在jvm虚拟机上的,基本类型占用的类型大小是按占用的字节的个数多少来决定的,像64位就占用8个字节,因此能做到与平台无关性。 
扩展:java类型分为基本类型和引用类型:引用类型:class type,interface type, 
Array type,null type;
 

2. Switch能否用string做参数?  
在java7之前是不可以的,只能用char,int,byte,short,和enum类型 
在java7之后就可以使用string类型作为参数。
 

3. equals与==的区别。 
equal比较的是对象的是否相等,而==号是比较地址。
 

4. Object有哪些公用方法? 
Wat(),notify(),notifyAll(),hascode(),toString(),equals(),getClass(),clone(),getClass(), 
finalize() 
各个方法关键内容: 
1:wait(),notify(),notifyall():在并发编程中,他们都是针对同一个对象的,也就是同一个object调用notify()或者是调用notifyAll()方法唤醒的是同一个object()中wait()的线程,而不是别的线程。Wat()和sleep()的区别区别是wait()会放弃当前持有的锁,而sleep()不会放弃当前持有的锁。 
2:hashCode()与equals()方法: 
1:判读两个对象是否相等的规则:equals()方法是先比较一个对象的hashCode是否相等,然后在判读对象是否相等(一般是成员变量比较)。 
2:为什么要重写equals()方法:Object默认的equals()方法是比较两个对象默认的引用是否相等(public boolean equals(Object obj) {return (this == obj);}),如过你要判读两个对象的值是否相等,则需要重写equals()方法。 
3:一般地方不要求重写hashCode(),但是当对象要存入hashMap,hashTable或者hashSet(value本身就是key)中作为key的时候,如过重写equal方法,则要求重写hashCode()方法。在hashMap中,会采用hashCode()来计算该对象的hash值,然后决定存入哪一个槽位,当用get方法获取该对象时,则会通过该对象(key)的hashCode()计算其存储的槽位,然后通过equal()方法来确定该槽位中对应的entry的key是否与寻找的的key值是否相等 (get方法中源代if(e.hash==hash&&((k=e.key)== key||key.equals(k)))。equal相等,在要求hashCode不相等(则在hash中找不到对应的值),如过put进hash表的和get的hash表的key的hashcode不一致,始终是找不到key值的,所以要求两个hashCode要相等(所以说一般要求重写equals方法),不想等会造成内存泄漏。Hashcode相等,则不要求equals相等。自己可以举例子 
4:在使用hashMap的时候,一般用不可变对象作为key值,因为他们中都复写了equal方法和hashCode()方法,自己写对象作为key则要重写equals和hashCode,实践中曾用身份证作为对象来当作key(不要将,问的时候再说,了解下应用场景) 
3.toString()方法没啥好说的。复写很简单。 
4.Clone()方法使用: 
1:clone()方法用来复制一个对象,自己创建一个对象,然后调用对象clone()方法,就会在堆中在重新分一个区域,来存放该对象的备份,并建立一个新的引用指向该内存区域。同时,要是一个对象要被复制,必须要实现Clonesabel接口(只是一个标志接口,跟serializable一样,内部用obj instance of Conesabel来验证), 
2:同时两个内存里面的东西是复制,也就是一样的,因此obj.clone().getClass()==obj.getClass()。也就是对象实例相等,但是引用是不相等的。 
3:同时有一点就是要要注意:clone()方法只是简单的复制一个对象,因此如果一个对象中有一个引用作为变量,那么该应用也直接复制,这样就导致两个两个内存区域的引用指向同一个对象,因此这叫浅引用。 
4:Class.forName(),getClass()和类名.class,都已可以获得一个Class对象,但是class是静态属性,不需要创建对象,而getClass()需要创建一个对象才可以用。此外,Class.forName()和getClass()是在运行时加载;Class.class是在编译器加载,即.class是静态加载,.getClass()是动态加载。但是最终功能没什么区别,主要用于反射。 
5:finalize()方法:这个java虚拟机垃圾回收有关,两次标记(finailize()方法):即使经过可达性分析以后该对象不可到达,不代表该对象非死不可,是否死亡要经历两次标记,这还有由之后的动作决定:1:如果对象在进行可达性分析以后和gc roots没有直接或者间接的相连,此时会进行第一次标记,筛选的条件:当finalize()方法没有被覆盖或者finalize()方法被调用过,则这次标记的对象被回收。 
第二次标记:如果一个对象覆盖了finalize()方法,那么该对象将被放在一个叫F-queue的队列中,之后虚拟机会自动触发该队列中的finalize()方法的执行(虚拟机只是触发,并不会等到其执行完,如果等待容易造成阻塞),如果在执行finalize()方法的时候,如果该对象没有重新与gc roots建立连接(将this赋值给某个静态变量就和gc ro ots建立联系了),他就会被垃圾回收器回收。所以收对象可以在finalize()方法中拯救自己
 

5.Java的四种引用,强弱软虚,用到的场景。  
1:Java中四种引用是强引用,弱应用,软引用,虚引用。强引用:用new关键字创建的对象,只要引用还存在,gc就不会回收 
软引用:就是一些还有用但是非必需的对象;在系统即将发生内存溢出之前,才会把这些对象进行回收 
弱引用:比软引用的强度要若的对象,他能生成到下一次垃圾回收器回收之前,下一次垃圾回收器就会把他收回去,不管发不发生内存溢出。 
虚引用:为对象设置一个虚引用的目的就是为了在垃圾回收时收到一条系统通知,不能通过虚引用获得一个对象。 

2:四种引用为什么存在的原因:如过我们使用hashMap来存储对象的时候,当里面装入大量数据的时候,如过我们不手动clear(将所有value设置为null),那么就会一直存在,知道发生内存溢出。因此引入弱引用和软引用的目的就是将回收的问题交给java虚拟机,我们自己不直接管理,如过HashMap中数据过多,则垃圾回收器自行回收。 
3:应用场景:像弱引用和软引用的引用场景就是cache啦,因为cache是不能保证绝对的可靠,因此我们可以用WeakHashMap来当缓存,这样垃圾回收器会自动进行回收。如果一些重要的数据,则不能用weakHashMap。
 

6. Hashcode的作用。  
1:HashCode的作用主要是用来配合基于散列的集合的,如HashMap,hashTable,hashSet之类的。用于计算散列值,然后决定存储的槽位。 
2:为什么需要hashCode,只用equals()方法不就可以吗?假如在hash表中(非链式hash表)如过将每一个对象都进行equals的话,如过是比较一个对象,里面有很多字段,我们要逐一比较,消耗是比较大的,有了hashcode,我们就能大大减轻的比较的消耗,(hashcode默认是地址,但是我们重写的时候一般是地址和字段进行配合得出), 
3:hashcode和equal有一个规则: 就是hashCode相等,则equals可能不相等,(因为hashCode可能不是一个地址生成,即使默认用地址生成,他指向的对象可能也会发生改变)。如过equals相等,则hashCode一定相等。所以如果只用hashCode的话,它可以用来判断一个对象不相等,而不能用来判断一个对象相等,因此它可以提高查找的速率。
 

7. ArrayList、LinkedList、Vector的区别。 
1:ArrayList,Vector采用的数组的形势来存储数据,两个都继承List,因此数组在查找的时候是很快的,但是在删除和插入的时候效率,比较低,因为要改变插入元素或删除后面所有序号,但是Vector是线程安全的,在很多方法中都加了一个synchronized,将方法锁起来,因此效率比较低。(细节的话还有就是他们扩容因子不一样,Vector默认扩容是原来的2倍,而ArrayList是原理的1.5倍) 
2:LinkedList是采用双向链表来实现的,他继承(List和Queue两个接口)因此查找的时候速度会稍微低一些,但是在插入或者删除的时候速度会很快。由于LinkedList继承了Queue接口,他还可以当队列来使用(add方法相当于addLast()) 
3:set和List都继承了Collection接口,但是List都是有序的,一般用链表或者数组实现,而set一般都是无序且唯一的,一般采用hash表接口(HashMap采用的链式hash表,hash表有很多种) 
4:而且arraylist和vector采用数组,就存在resize的问题(扩容),重写细算数据大小,将原来数组拷贝到新数据中,因此比较耗费时间,而linkedList采用链表形式,没有resize,因此对于增长较快的,应该采用linkedList;
 

8. String、StringBuffer与StringBuilder的区别。 
1:String类型是不可变的,体现在两个方面:1.该类的实现实际上是采用char[]实现的,其中char[]数组变量是不可变,所以说该类是不可变的,而不是说该类用final修饰,然后不可变,final修饰只能说是不可覆盖。2。同时String类是不可变的,也就是每一次操作的都会返回一个新数组对象(return new String(buf))。 
2:而StringBuffer和StringBuilder是可变的,1:他们的操作过后每一返回的都是该字符串本身(return this)。2:而StringBuilder和StringBuffer都继承了AbstractStringBuilder,在AbstractStringBuilder中,char[]类型的数组变量没有用final修饰,这一体现出了他们的可变特性。 
3:StringBuffer是线程安全的,而StringBuilder是线程不安全的,也就是StringBuffer的很多方法中都加了synchronized对方法进行加锁。 
4:包装了Integer,Character之类也是不可变的,因为他们除了用final修饰类本身,他们也用final修饰了内部的int和char基本类型的变量 
5:由于String在进行连接操作时,会用String字符串创建一个StringBuilder对象,然后调用StringBuilder的append()方法来连接两个字符串,因此在进行大量的连接操作的时候,要使用StringBuilder或者StringBuffer,节省资源损耗  


9. Map、Set、List、Queue、Stack的特点与用法。 
Map特点:以键值对形式存入数据,而且键可以是null值,但是只能为一个null值,后面的null值会覆盖前面的。 
Set特点:继承Collection接口,放入数据没有顺序,像HashSet其实就是采用HashMap的key值来来存放Set的value;不能放入重复值,可以存入null值 
List,继承Collection接口,可以放入重复值,可以存入null值,放入的是有顺序的 
Stack:是栈,先进后出,LinkedList也是可以实现栈,但是在java中并没哟让LinkedList继承Stack类,而是有一默认的Stack类。Java.util包中的栈继承了Vector,因此Stack类是用数组实现的,而且是线程安全的。 
Queue:队列,先进后出,LinkedList及继承了接口,因此可以用LinkedList创建queue;
 

10. HashMap和HashTable的区别。 
1:HashMap是不是线程安全的,HashTable是线程安全的,HashMap允许key或者value为null,而hashTable不允许key或者value为null,同时HashTable继承Dictionary类,而hashMap,继承AbstractMap类,但是都实现类Map接口 
2:原因:hashMap的put()方法一开始就有一个if(key==null){return putForNullKey(value)},因此可知key可以为null。而HashTable中put()方法是if(value==null){throw new NullpointerException()},而且下面要调用key.hashCode(),因此如过key或者value为null,则会发生异常。 

3:如过HashMap要实现同步,则可以采用Collection.sychronbizedMap(HashMap),这样就能够达到HashTable的效果 

11. HashMap和ConcurrentHashMap的区别,HashMap的底层源码。 
1:hashMap与是采用链式hash表实现。也就是HashMap表里面有一个Entry[]数组,当一个键值对加入该数据的时候,会通过key的hashcode去计算存在那一个数组位置,当通过不同的hashCode计算出出现相同的entry数组位置的时候,则会在该位置以链表的形式存储。 
2:hashMap存在一个问题,就是会当hash表的容量超过设定的容量的时候,就会出现rehash,此时会创建一个新的entry数组,然后将原来的entry[]数组中的数据复制到新的数组中,但是复制的时候就会重新根据key的hashCode来计算存入位置,数据量大的时候比较耗费资源,所以我们在能预估hashMap大小的是时候尽量在初始化的时候给适当的值。(hashMap每次扩容为原来的两倍) 
3:hash表解决冲突的做法:1:保证每一扩容都为2的n次方(int capacity = 1;  while (capacity < initialCapacity)  capacity <<= 1;  ),从这儿我们可以看出,我们不是我们给的初值是多少,map的容量就是多少,而已2的n次方,然后跟我们给的初值很相近(具体原因可以根据数学上的计算的来的)。2:负载因子取一个平衡值,负载因子也是影响冲突的一个因素,当负载因子越大,则数组利用率越高,但是对应查询的时间复杂度增加(因为变密集了吗),当负载在因子越小,则越稀疏,则越不容易冲突。 
4:hashMap初始容量为16,负载因子默认为0.75(平衡值);扩容阈值=原容量*负载因子。16*0.75=12,也就是存入了12个就扩容,扩容以后是2*16=32; 
5:ConcurrentHashMap采用分段锁的概念,将一个数组分成一个个小的HashTable,因此就是提高类并发编程类Map的效率。线程只是对一个段里面HashTable加锁,而不是整个map对象加锁。 
6:具体的做法就是有一个segmetn[]数组,该数组中segment对象就相当于一个HashMap,当一个键值对put进来的时候,首先通过key值计算该对象存在哪一个segment中,当定到哪一个段以后,再在段中计算hash值,然后存入hashEntry[]数组中的对应位置,接下来就和hashMap差不多了。这样的话我们只要锁定一个段就可以了,而不用锁定整个segment数组,如过一个线程操作其中的一个段,另个一线操作的另一个段,那么他们就不存在竞争的现象。
 

12. TreeMap、HashMap、LindedHashMap的区别。
 
1:TreeMap采用的是红黑树实现的,他的查询时间复杂度是o(logn),而LinkedHashMap遍历采用的哈希表加双向循环列表实现的,通过key查询value时间复杂度是o(1),LinkedHashMap就是解决TreeMap的不足的; 
2:LinkedHashMap可以通过设置accessOrder来确定是否使用LRU(Least Recently Used)算法,如果accessOrder=true,则表示使用lru,如果是false,则不用,遍历循序和插入循序一致。 
3:LinkedHashMap实现原理:具体做法是在存入第一entry的时候,将其赋值给head,当第二来entry来的时候,先和head建立起双向循环链表的关系,然后在存入entry[]数组结构中。以后没来一个就先和之前的已经建立好的循环链表中加入自己,然后按照hashMap的规则存到数组当中。这样查找和遍历就相互分开了。
 


13. Collection包结构,与Collections的区别。
 
1:Collection叫类集,他的包含了4个结构,List接口,Set接口,Queue接口,BeanContext接口。对于List接口的标准实现有abstractList类,Vector类,LinkedList类,ArrayList类(同时继承abstractList),copyOnwriteArrayLIst类,对于Set的被一个AbstractSet实现,其他标准继承abstractSet,实现由hashSet类,copyonwriteArraySet类,TreeSet类(跟hashSet类似,也是用TreeMap实现的),ConcurrentSkipArraySet; 
2:使用类集的总用统一管理单列集合容器,对各个容器实现了标准化。易于我们扩展自己的类。 
3:collections是集合类的一个帮助类(工具类),提供了一系列静态方法对集合进行排序,搜索线程安全等操作(Collections.sort(),Collections.Copy(),Colections。sysnchronizedList()Collections.sysnchronizedMap()等) 
14. try catch finally,try里有return,finally还执行么? 
是先执行finally,然后在返回去执行try块或者catch块里面的return。
 

15. Excption与Error包结构。OOM你遇到过哪些情况,SOF你遇到过哪些情况。 
[img][/img]
 
1:java异常结构: 
2:Thorwable类所有异常和错误的超类,有两个子类Error和Exception,分别表示错误和异常。其中异常类Exception又分为运行时异常(RuntimeException)和非运行时异常,这两种异常有很大的区别,也称之为不检查异常(Unchecked Exception)和检查异常(Checked Exception)。 
3:运行时异常也就是在代码中不用try块或者throws捕获或者抛出异常,而是由系统在运行时出现时自动抛出,如果在代码中用try块捕获,会出现一个问题就是抛异常的时候不会打印,也就是不会显示出来,对于查找有一定困难。 
4:Error一般是程序无法处理的错误,如OutofMemoryError,StackOverFlowError,ThreadDeath错误等。这类一般java虚拟机直接选择线程终止。Exception异常中运行时异常包括NullPointerException,IndexOutOfBoundsException等,其他的异常则为检查异常,如IoException,SqlException或者自己写的异常。 
5:OOM(outOfMemoryError)(统一将Error也叫异常,因为他是异常体系的一部分),如果往一个List列表里面不断的加入对象,则最终会出现OOM异常主要是针对java堆的。 
6:Sof(stackOverflowError)对于栈溢出这种情况,我们可以通过设置-Xss参数来控制每一线程栈的大小,当减小每个线程栈大小,同时在函数方法内存放比较大或者多的局部变量,就会发生栈溢出。当然sof只放生在虚拟机栈和本地方法栈,不会发生在java堆和方法区中。如果要在方法区发生sof,可以设置MaxPermSize参数,然后调用String.inter()方法不断的往方法区中加入字符串。 
7:java运行时常量池和普通常量池区别:如果在编译时的常量,则存放入普通常量池,当运行时时如采用String.intern()动态加入的常量或者从远端调用产生的常量,则存入运行时常量池中。当然,运行时常量池和静态常量池都是属于方法区的一部分,受maxpermsize参数的影响。
 


16. Java面向对象的三个特征与含义。
 
1:封装:也就是将一个实物抽象化为一个类,然后通过一个类去创建一个对象;我们常用的进程就是有一个进程类,然后创建进程对象产生的。 
2:继承:也就是对父类方法的继承,包括对接口的实现也属于继承。易于扩展,降低耦合。 
3:多态:当子类对象的引用赋给父类引用的时候就会发生多态(简单说就是向上转型。向上转型是多态机制的一种表现形式)多态的实现依赖于多态机制,即动态绑定:也就是在编译的时候编译器只通过父类引用只知道调用方法的签名,无法知道调用哪一个方法体,要确定这个调用的方法体,只有通过new 创建一个对象以后,并将该方法对应的栈帧加入虚拟机栈中才能知道实际的方法体,也就是方法体的后期绑定,因此就不能像使用final修饰方法一样,编译器自己可以优化方法。Final修饰的方法不可覆盖,也就关闭了动态绑定。由此可知覆盖和重载都是多态的一种表现形式
 

17. Override和Overload的含义去区别。 
1:override是覆盖,覆盖是子类和父类之间多态性的表象形式 
2:overload是重载。重载的原则是方法的签名不同,方法签名不包括返回值哦,重载是一个类中多态的表现形式
 

18. Interface与abstract类的区别。 
语法上区分: 
1:interface是abstract的一种极端。 
2:interface和abstract都不可以创建实例,interface中所有方法默认都是public,所有属性都是public static final修饰的,因此必须初始化(但是一般不再接口中定义属性),(final修饰的可以在构造函数中初始化)因为interface和abstract都不可以创建实例,所以没有构造函数。 
3:abstract 类中可以定义方法体,而interface中不可以定义方法体。 

使用理念上区分: 
1:并不是所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类。 
2:要想使得继承关系合理,父类和派生类之间必须存在"is a"关系,即父类和派生类在概念本质上应该是相同的,因此抽象类要满足这种情况。对于interface 来说则不然,并不要求interface的实现者和interface定义在概念本质上是一致的,仅仅是实现了interface定义的契约而已。interface表示的是"like a"关系。
 

19. Static class 与non static class的区别。 
1:在java中静态类只有静态内部类这一种定义,其他的都是非静态类。 
2:java中静态内部类和非静态内部类的区别:静态内部类相当于外部类的一个静态属性,因此他不能使用外部类的非静态属性和非静态方法。而非静态内部类相当于外部类非静态属性,因此可以使用静态字段和非静态字段或者方法。 
3:静态内部类存在的价值在于:静态内部类可以创建静态属性,而非静态内部类不可以创建静态属性。(内部类只有在需要的时候才会加载),在这里在每个类中用静态main()方法做测试就派上用场了啊 
4:静态内部类在平时很少用,但是有一种是用来测试使用的时候特别好用,但是现在都用单元测试也很方便:在电子所的时候这用方法就被使用了。http://blog.sina.com.cn/s/blog_605f5b4f0100zbps.html 
5:静态类我的理解是所有的属性或者方法都是静态的,然后这儿非静态类没有本质的区别,有区别的是静态方法和静态属性的区别。
 
20. java多态的实现原理。 
1:多态简单的讲就是一个类有多种状态,他的具体表现为方法的重载和方法覆盖。 
2:多态的实现依赖于编译阶段和运行时阶段:在编译阶段主要表现在静态分派,静态分派就是通过静态类型和方法参数个数来选择哪一个方法版本,这就是主要体现了方法的重载; 
3:在运行时阶段体现在动态分派(动态绑定),也就是当一个父类引用指向子类对象,通过该父类引用去调用一个该方法,由于在编译阶段生产的调用函数巷道代码的字节码指向的是父类(静态类型)被调用方法,并不知道具体要去调用哪一个实际类型的方法,因此会发生这样一个过程,虚拟机找到操作数栈中位于栈顶获取该操作数的指所指向的类,然后到常量池中去搜索与被调用的方法匹配的方法名和描述符,如果找到,就进行权限校验(校验失败就抛出异常),如果可以访问,则返回该方法的符号引用,并转换成直接引用,调用该执行,如果找不到就到父类中去找,然后重复上面动作,最后找不到就抛出异常。 
4:对动态绑定的优化:由于要去常量池中搜索每一类的方法名和描述符,因此效率比较低,所以最后进行了优化,就是在方法区为每一类维护一张虚方法表或者接口方法表(虚表中存放了该方法的实际入口地址),让该类的所有方法都维护进去(包括父类的方法),因此要查找方法名的时候,直接去该虚表中去搜索到该方法名对应的直接地址然后执行。对于没有被重写的方法,子类中直接存放父类的入口地址,如果该方法被重写,在存放子类的方法入口地址。
 

21. 实现多线程的两种方法:Thread与Runable。 
1:用Thread类或者Runnable接口创建的线程本质上没什么区别,因为Thread类也是实现了Runnable接口的。只是他们创建的方式不一样; 
2:还有一个区别就是java中只能继承一类,而可以实现很多方法,因此我们实现创建一个线程的时候尽量用Runnable创建。 
3:创建方法方式:Thread thread=new Thread(“线程1”),thread.start();MyTread thread=new MyTread(线程1);new Thread(thread).start();
 

22. 线程同步的方法:sychronized、lock、reentrantLock等。
 
1:synchronized是一个同步锁关键字;他修饰对象有如下几种:修饰一个代码块(作用于调用这个代码块的对象),修饰一个方法(作用于调用该方法的对象),修饰一个静态方法(作用于调用方法该类的所有对象),修饰一个类(主要体现在synchronized(类名.class))(作用于调用方法该类的所有对象)。 
2:上面中1中所说的对象还是所有对象,则可以有这样一个假设:一个类中有一个用syschronized修饰的方法,然后创建一个对象,然后将该对象放入两个线程中去,在线程中调用同步方法,则会发生互斥同步的现象,如果是两个对象分别放入两个线程中,则这两个对象中该方法不会发生互斥同步的现象,各做个的。但是如果是对该类的所有对象。则不管是几个多上个对象放入该线程中,都会发生互斥同步的现象。 
3:发生上面情况的主要原因:每个线程有自己的栈,栈中存放着对应方法的栈帧(局部变量,操作数栈,返回地址,动态链接信息等),当创建两个对象,而且分别付给了两个线程,则在在栈帧中的对该对象的引用指向了两个对象中的资源,不存在竞争的情况;但是两个线程中栈帧中的引用指向了同一个对象,则会对资源形成竞争的情况,因此会发生互斥。对于对静态方法(静态方法是要载入各个线程的栈中执行的,因此静态方法是线程安全的,也就是在各个线程的栈中执行该静态方法,当碰到synchronized关键字的时候就上锁执行,别的线程就不能执行,别的线程为什么不能执行呢,就是涉及到锁的问题了。上锁的目的是针对资源)用synchronized,此时锁定的资源肯定是静态资源,静态资源存放在方法区,方法区是贡献的资源,因此会出现共享资源发生互斥同步现象。 
4:有一个问题就是当一个对象被synchronized(this)上锁以后,该对象的非synchronized方法等是可以被别的线程调用的。synchronized的参数代表被上锁的对象,当代码要执行monitorenter(synchronized编译后的指令),会预先检查被上锁的对象的锁是否被别的线程持有,所以同一个对象如果多个synchronized块只能被一个线程当前拥有同步块的线程随意执行,而别的线程是不能执行该被锁对象的任何synchronized块,但是可以执行其他非同步块方法块方法,因为执行其他的方法会不会扫面当前对象是否被上锁。 
Lock锁: 
1:Lock存存在的原因:如果用synchronized修饰一个资源,当一个线程占有该锁事件比较长(如读取io或者sleep()的时候),别的线程想要获取该资源,机会干巴巴的在哪儿等待,造成效率底下。因此Lock出现时必然的。(还有一种情况就是对同一个文件操作,两个线程同时读是不互斥的,而两个一个写,一个读,或者两个都写是要发生互斥的,如果用sychronized,则两个线程读的时候也会发生互斥); 
2:Lock与Sychronized关键字的区别:1:synchronized关键字jvm内置的关键字,而Lock是一个java.util.concurrent.locks包下的一个接口。2:sychronized的锁不用手动释放,而Lock的锁需要手动释放。synchronized在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生;而Lock在发生异常时,如果没有主动通过unLock()去释放锁,则很可能造成死锁现象,因此使用Lock时需要在finally块中释放锁; 
3:  由于在前面讲到如果采用Lock,必须主动去释放锁,并且在发生异常时,不会自动释放锁。因此一般来说,使用Lock必须在try{}catch{}块中进行,并且将释放锁的操作放在finally块中进行,以保证锁一定被被释放,防止死锁的发生。 
4:Lock接口中的几个方法:lock()、tryLock()(尝试获取锁,获取则返回true,否则false)、tryLock(long time, TimeUnit unit)(在规定时间内去获取)和lockInterruptibly()(如果一个线程正处于等待状态,则调用该方法可以中断等待,但是如果一个现在正则运行,他是不能中断的,只能中断处于阻塞的线程)是用来获取锁的。unLock()方法是用来释放锁的; 
5:Lock对象自己就是一个锁,相当与synchronized()括号中的对象。reentrantLock类: 
1:ReentrantLock是唯一实现了Lock接口的类,并且ReentrantLock提供了更多的方法。 
2:ReadWriteLock接口,就是用来获取读锁和写锁的,ReentrantReadWriteLock类实现了readwriteLock接口。private ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();rwl.readLock().lock();尝试获取读锁,如果一个线程占用了读锁,别的线程来申请读锁的时候,还是会给该锁,如果领个线程中是rwl.writeLock().lock()则代笔是申请写锁,此时会失败,synchronized并不具备这样的功能。
 

23. 锁的等级:方法锁、对象锁、类锁。 
1:可重入锁:只得是如果一个线程获取了一个对象锁以后,如果该线程获取该对象锁锁定的方法块,则会直接进入,而不用重新申请锁;Lock和syschronized都是可重入锁。(可重入就设计到进入一次,计数器+1,没出来一次,计数器-1) 
2:可中断锁:如果某一线程A正在执行锁中的代码,另一线程B正在等待获取该锁,可能由于等待时间过长,线程B不想等待了,想先处理其他事情,我们可以让它中断自己或者在别的线程中中断它,这种就是可中断锁。Lock就是可中断锁,而sysnchronized是不可中断的。 
3:公平锁:公平锁即尽量以请求锁的顺序来获取锁。比如同是有多个线程在等待一个锁,当这个锁被释放时,等待时间最久的线程(最先请求的线程)会获得该所,这种就是公平锁。非公平锁则可能是随机的;有的线程永远也不能获得该锁。synchronized就是非公平锁,ReentrantLock和ReentrantReadWriteLock,它默认情况下是非公平锁,但是可以设置为公平锁。 
4:读写锁:读写锁将对一个资源(比如文件)的访问分成了2个锁,一个读锁和一个写锁。ReadWriteLock就是读写锁,它是一个接口,ReentrantReadWriteLock实现了这个接口。 
---------------------------------------------------------------------------- 
5:方法锁:也就是对方法加锁,这个比较简单:注意锁的对象是this本身; 
6:对象锁:也就是以一个对象做为锁,也就是synchronized()关键字中的对象;此时该锁只是针对一个对象的,因为对象在内存中可以有多个,也可以有一个,注意; 
7:类锁:就是以一个静态变量(或者class对象(类名.class))作为锁对象,或者以sychronized关键字修饰一个静态方法;此时锁住对象是一个类,而不是一个对象。
 

24. 写出生产者消费者模式。 
1:有两种写法,用synchronized关键字和wait()/notifyAll()实现,另一种是用ReentrantLock锁和Condition变量来实现 
2:使用Conditon变量对时候,记住这两种中文含义(中文描述很重要):notFull.wait()等待没有满对条件(此时应该是满了),notEmpty.signalAll()满足了不为空的条件,通知等待不为空的线程
 

25. ThreadLocal的设计理念与作用。 
1:ThreadLocal理解:TheadLocal的创建方式ThreadLocal<Integer> local = new ThreadLocal<Integer>();同时local.set(100);那么就相当于Integer local=100;不要把本地线程变量看出一个容器,因为你多次调用local.set()方法以后总是存放的是最新的值,所以他不是容器,如果要在线程中放另一变量,则重新new一个线程变量,同local.get()方法获得的就相当于获得local的值,ThreadLocal只是包装了一下而已。 
2:设计理念:ThreadLocal中有一个静态内部类ThreadLocalMap(该map就理解为hashMap吧,只是改map中的enty实体继承了弱引用,也就是说,如果该Map中的对象太多的时候,即使我们没有主动放弃,可能垃圾回收器回收);同时在Thread类中有一ThreadLocalMap的组合,为ThreadLocal.ThreadLocalMap<ThreadLocal,Object> threadLocals;从这里可以看出,当在一个线程中调用local.set(100)方法的时候,则在该线程对象的ThreadLocalMap中存入<local,100>;当通过local.get()方法的时候,则先获取当前线程,通过当前线程获取ThreadLocalMap,然后,通过该local变量为key值去获取对应的value(100); 
3:TheadLocal变量不是用来实现资源共享的,而是用来实现资源的在各个线程中的隔离。各个线程单独占有。虽然也可用局部变量,但是有些场景,如果说资源只有一个的时候,就得使用ThreadLcoal变量。还有要注意是对一个对象,如ThreadLocal<Person>这种状况,则map中存的是指向person的引用。要特别小心。
 

26. ThreadPool用法与优势。
 
1: Java通过Executors提供四种线程池,分别为: 
newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。 
newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行(设置多长时间后该线程执行;每隔几秒执行一次)。 
newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行(保证只有一个线程执行任务,与newFixedThreadPool(1)等价)。 
2: 线程池用法:Executors中有很多静态方法可以用来创建线程池;Executor是一个工厂方法类;里面可以创建很多线程;所有的ThreadPool都实现了Executor这个接口;因此只要返回Executor(该接口只有一个方法,那就是executor(Runnable thread))就可以了。ExecutorService cachedThreadPool = Executors.newCachedThreadPool();cachedThreadPool.execute(new Runnable()   public void run() {  System.out.println(index); }  也执行一次execute方法,就产生一个新的线程; 
3:线程池的优势:因为创建一个线程和杀掉一个线程花费的资源是比较大的,因此采用线程池,则将线程先创建好放在一个队列中,当需要的时候就从线程池中获取一个线程来执行任务;如果用完,则将线程放入该队列中。避免了多次的创建和销毁线程。
 

27. Concurrent包里的其他东西:ArrayBlockingQueue、CountDownLatch等等。
 
1:本例介绍一个特殊的队列:BlockingQueue,如果BlockingQueue是空的,从BlockingQueue取东西的操作将会被阻断进入等待状态,直到BlockingQueue进了东西才会被唤醒,同样,如果BlockingQueue是满的,任何试图往里存东西的操作也会被阻断进入等待状态,直到BlockingQueue里有空间时才会被唤醒继续操作。 
2:BlockingQueue有四个具体的实现类, 
ArrayBlockingQueue:规定大小的BlockingQueue,其构造函数必须带一个int参数来指明其大小。其所含的对象是以FIFO(先入先出)顺序排序的。 
LinkedBlockingQueue:大小不定的BlockingQueue,若其构造函数带一个规定大小的参数,生成的BlockingQueue有大小限制,若不带大小参数,所生成的BlockingQueue的大小由Integer.MAX_VALUE来决定。其所含的对象是以FIFO顺序排序的。 
PriorityBlockingQueue:类似于LinkedBlockingQueue,但其所含对象的排序不是FIFO,而是依据对象的自然排序顺序或者是构造函数所带的Comparator决定的顺序。 
SynchronousQueue:特殊的BlockingQueue,对其的操作必须是放和取交替完成的。 
3:semaphore:Java 并发库 的Semaphore 可以很轻松完成信号量控制,Semaphore可以控制某个资源可被同时访问的个数,acquire()获取一个许可,如果没有就等待,而release()释放一个许可。比如在Windows下可以设置共享文件的最大客户端访问个数。 
4:countdownLatch:CountDownLatch的一个非常典型的应用场景是:有一个任务想要往下执行,但必须要等到其他的任务执行完毕后才可以继续往下执行。假如我们这个想要继续往下执行的任务调用一个CountDownLatch对象的await()方法,其他的任务执行完自己的任务后调用同一个CountDownLatch对象上的countDown()方法,这个调用await()方法的任务将一直阻塞等待,直到这个CountDownLatch对象的计数值减到0为止。(当每个线程调用countdown方法直到将countdownlatch方法创建时数减为0时,那么之前调用await()方法的线程才会继续执行。有一点注意,那就是只执行一次,不能到0以后重新执行) 
5:CyclicBarrier:类有一个整数初始值,此值表示将在同一点同步的线程数量。当其中一个线程到达确定点,它会调用await() 方法来等待其他线程。当线程调用这个方法,CyclicBarrier阻塞线程进入休眠直到其他线程到达。当最后一个线程调用CyclicBarrier 类的await() 方法,它唤醒所有等待的线程并继续执行它们的任务。(当等待的线程数量达到CyclicBarrier线程指定的数量以后(调用await方法的线程数),才一起往下执行,否则大家都在等待,注意:如果达到指定的线程数量的时候:则可以重新计数,上面的过程可以循环) 
6:CountDownLatch是减计数方式,计数==0时释放所有等待的线程;CyclicBarrier是加计数方式,计数达到构造方法中参数指定的值时释放所有等待的线程。
 

28. wait()和sleep()的区别。
 
1:wait()是Object类中,与notify和notifyAll搭配使用,sleep()属于Thread类中的;1.CountDownLatch的作用是允许1或N个线程等待其他线程完成执行;而CyclicBarrier则是允许N个线程相互等待。 

2.CountDownLatch的计数器无法被重置;CyclicBarrier的计数器可以被重置后使用,因此它被称为是循环的barrier。 
2:wait()的时候放弃锁,而sleep的时候不放弃锁,但是在sleep的时候,可以调用interrupt()函数来中断该线程; 
3:suspend将当前线程挂起,要等到线程调用resume()方法,该线程才能重新执行。
 

29. foreach与正常for循环效率对比。
 
1:foreach在使用的过程中,会创建额外的iterator方法,而每次调用哪个hasNext,和next()方法,增加了更多的操作,对于访问数组的话for的效率要高于foreach;而且在foreach中,还要检查mod_count,避免导致快速失败,所以效率上会降低,但是由此可以看出foreach是线程安全的,而for不是线程安全的 
2:什么时候使用:在多线程中,则使用foreach,如果是在方法中读局部变量的操作,则使用for。
 

30. Java IO与NIO。 
1:javaIO与javaNIO区别:IO是基于流的,而NIO是基于缓存的。JavaIO每次从流中读取一个或者多个字节,知道读取所有的字节,不能前后移动流中的数据。而javaIO通过通道读取到缓存区中,需要可在缓存区中前后移动, 
2:javaIO是阻塞的,而NIO是非阻塞的;IO阻塞当一个线程调用read()方法或者write()方法的时候,该线程会阻塞直到数据读取或者写完毕,中间线程不能干别的事情;而在NIO中,以个线程可以通过通道去向缓存中发送一个读取数据请求,当没有数据时就会去做别的事,而不会一直等待直到读取到数据为止(io就是如果read了没有读取到想要的就会一直等待,直到读取到),NIO向文件缓存器写数据也一样,他可以只向其中写一部分,而不是全部写完(在写的时候线程当然不能做别的事情了)。 
3:选择器:NIO允许一个线程来监视多个输入通道,可以注册一个选择器,然后用一个单独的线程来管理选择监控那一个通道; 
4:运用场景:如果需要管理同时打开的成千上万个连接,这些连接每次只是发送少量的数据,例如聊天服务器,实现NIO的服务器可能是一个优势。同样,如果你需要维持许多打开的连接到其他计算机上,如P2P网络中,使用一个单独的线程来管理你所有出站连接,可能是一个优势。 
5:NIO的都流程:先向通道发出读取数据请求,然后通道向buffer中写入数据,如果buffer中满或者不满,你都可以向其中读取数据。 
6::同步异步:同步和异步是消息通信机制:同步:发出一个请求,然后一定要等待其返回,在返回期间,如果该线程去干别的事情,那就是不阻塞了(由于是同步,操作系统并不会通知你消息准备好了,所以你要过一会就去询问一次);组合起来就叫同步非阻塞;如果在等待期间,当前线程一直在等待,而不干别的事情就是同步阻塞; 
7:异步:发送一个请求,我(应用程序)就不用去管了,等待操作系统来完成以后通知我,如果在发送了请求以后,在操作系统没有通知我这段时间,线程什么都不干(所以异步阻塞基本上不实用),一直在哪儿等着,那么就是异步阻塞,如果在操作系统告诉应用程序之前,线程去干别的,也就是异步非阻塞; 
8:同步阻塞的BIO、同步非阻塞的NIO(要去轮询,单独效率低比BIO稍低,但是在实际运用中效率就高了)、异步非阻塞的AIO; 
11:NIO的最重要的地方是当一个连接创建后,不需要对应一个线程,这个连接会被注册到多路复用器上面,所以所有的连接只需要一个线程就可以搞定,当这个线程中的多路复用器进行轮询的时候,发现连接上有请求的话,才开启一个线程进行处理,也就是一个请求一个线程模式。(回答的核心,服务器中应用) 
在NIO的处理方式中,当一个请求来的话,开启线程进行处理,可能会等待后端应用的资源(JDBC连接等),其实这个线程就被阻塞了,当并发上来的话,还是会有BIO一样的问题。 
12:HTTP/1.1出现后,有了Http长连接,这样除了超时和指明特定关闭的http header外,这个链接是一直打开的状态的,这样在NIO处理中可以进一步的进化,在后端资源中可以实现资源池或者队列,当请求来的话,开启的线程把请求和请求数据传送给后端资源池或者队列里面就返回,并且在全局的地方保持住这个现场(哪个连接的哪个请求等),这样前面的线程还是可以去接受其他的请求,而后端的应用的处理只需要执行队列里面的就可以了,这样请求处理和后端应用是异步的.当后端处理完,到全局地方得到现场,产生响应,这个就实现了异步处理。(异步IO原理) 

31. 反射的作用与原理。 
1:反射的作用:反射可以增加代码的灵活性,通过反射可以使系统和插件解耦,如Spring就是使用反射来实现控制反转的(IOC)。 
2:什么是反射: java运行时阶段的程度对自身的一种审查的能力; 
3:反射机制的原理:反射就是java语言在运行时可以通过获取类的class对象,并通过class对象获取到当前对象的方法(包括私有方法),属性,以及构造方法,运行时class对象相当于外部程序访问内存中该类的一道门,通过该到门能获取到运行时该类的各种东西。
 

32. 泛型常用特点,List<String>能否转为List<Object>。 
1:泛型的种类:泛型类、泛型接口、泛型方法。Java泛型被引入的好处是安全简单。增加代码的复用性。(泛型解决了方法的重载问题:public void write(Integer i, Integer[] ia);public void write(Double  d, Double[] da); 的范型版本为public <T> void write(T t, T[] ta)) 
2:为什么要引用泛型:在java1.5之前,如果一个方法的参数中要支持任意类型,则一般用Object类来作为参数的类型,因此在运行的时候就要进行类型转换。这种自动转换类型容易出现当编译的时候不会报错,而运行的时候回报错,这是一个安全隐患(我还没有遇到过,可能是这种情况,就是返回对象是Object,此时要将 Object转换成具体的类型时这是容易出现错误的)。注意:在java中父类是可以转换成子类的而且不报错,调用的时候出错运行时才能检查到(亲测Sunzi sun=(Sunzi)new Child();可以,编译通过) 
3:泛型的好处是在编译的时候检查类型安全(类型检查是保证类型转换是可以正确转换的(object编译的时候就没有检查到父类转子类)),并且所有的强制转换都是自动和隐式的,提高代码的重用率。 
4:泛型信息在编译的时候会被自动擦除的,也就是在字节码中没有泛型的信息,并使用其限定类型替换(无限定类型用Object),如public T fun(T t)替换成public Object fun(Object t),限定类型只的是T extend Father,则用Father替换T,List<T> 则变成List。 
5:泛型主要的体现还是在类型安全的检查与限定,如List<String>,那我们就只能放入String类型的,在这儿就体现了类型检查了。 
6:List<String>不能List<Object>,因为泛型并不具有继承性。只是一个限定的作用。
 

33. 解析XML的几种方式的原理与特点:DOM、SAX、PULL。
 
1:DOM是基于树的解析,DOM是把XML全部加载到内存中建立一棵树之后再进行处理。所以DOM不适合处理大型的XML【会产生内存的急剧膨胀】。这样可以随时修改xml文件内容。 
2:SAX基于事件的解析,sax解析一行一行的读取xml文件开始标记的时候执行startElement方法,然后执行对应解析方法character方法,当遇到结束表示符就调用endElement方法,所以所是基于事件型解析, SAX不必把全部的xml都加载到内存中。但是SAX的缺点也很明显,它只能对文件顺序解析一遍,不支持对文件的随意存取。SAX也仅仅能够读取文件的内容,并不能修改内容。DOM可以随意修改文件树。(主要用于读取xml文件) 
3:SAX 和 DOM 不是相互排斥的,记住这点很重要。您可以使用 DOM 来创建 SAX 事件流,也可以使用 SAX 来创建 DOM 树。事实上,用于创建 DOM 树的大多数解析器实际上都使用 SAX 来完成这个任务!
 

34. Java与C++对比。 
(1)Java比C++程序可靠性更高。 
(2)Java语言中没有指针的概念, 
(3)Java用接口(Interface)技术取代C++程序中的多继承性; 
(4)Java语言不需要程序对内存进行分配和回收。Java中自动回收内存,而c++中要调用new和delete来申请和释放内存,如果对内存分配与申请掌握不好就容易报错 
(5)异常:JAVA中的异常机制用于捕获例外事件,增强系统容错能力 try{//可能产生例外的代码 }catch(exceptionType name){ //处理 } 其中exceptionType表示异常类型。而C++则没有如此方便的机制 
(6)“可怕”的goto语句是c和c++的“遗物”,它是该语言技术上的合法部分,引用goto语句引起了程序结构的混乱,不易理解,goto语句子要用于无条件转移子程序和多结构分支技术。鉴于以广理由,Java不提供goto语句,它虽然指定goto作为关键字,但不支持它的使用,使程序简洁易读。
 

35. Java1.7与1.8新特性。
 
1:1.7新特性: 
1:switch中可以使用字串了运用 
2:List<String> tempList = new ArrayList<>(); 即泛型实例化类型自动推断 
3.语法上支持集合,而不一定是数组final List<Integer> piDigits = [ 1,2,3,4,5,8 ]; 
4:try块可以不用finally手动关闭,直接try块中就可以关闭 
5:很长的数字可读性不好,在Java 7中可以使用下划线分隔长int以及long了,如1_1 * 10 = 110,120 – 1_0 = 110 
Java1.8新特性 
1: Java 8允许我们给接口添加一个非抽象的方法实现,只需要使用 default关键字即可,default double sqrt(int a) {return Math.sqrt(a);},改方法不用实现,可以自己直接通过子类调用。 
2:增加了lamda表达式 
3:Java 8 在包java.time下包含了一组全新的时间日期API
 

36. 设计模式:单例、工厂、适配器、责任链、观察者等等。 

37. JNI的使用。 
1:jni就是通过java本地方法可以调用其他语言的代码,能与其它语言(如C、C++)的动态库进行交互 
2:jni的使用场景 
1:java是一门半解释性的语言,代码很容易被别人反编译,因此可以采用jni通过c语言或者c++来实现其核心,这样还能提高一定的性能; 
2:一些性能要求特别高时,可以采用汇编语言实现,通过jni调用; 
3:别的语言已经实现了的功能,通过jni可以直接调用,而不用重新编写。 
3:jni使用步骤:用native关键字定义本地方法,然后编译该类为class文件,用javah工具生成.h的头文件,然后用c/c++语言实现其中的.h头文件中的方法,并生成动态库(以.so结尾的文件,大多数动态库是以.dll文件结尾的)),然后提供给java使用
 

38:java的动态代理与静态代理 
1:静态代理,这比较简单,相当于一个静态代理模式,比如要在一方法前输入一个输出helloworld方法的前面输出一句welcome,则可以用一个代理类,在输出welcome的时候回调输出helloworld的方法。缺点是当一个借口中有很多个方法需前输出welcome,则代理类中就要取实现借口中的每一个方法,然后回调,这样比较麻烦;http://kuangbaoxu.iteye.com/blog/192804 
2:动态代理就是将给一个需要代理的对象关联一个调用处理器(InvokeHandler),然后通过Prox.newPoxyInstance对象创建代理对象,通过该代理对象将要代理的真实对象和InvokeHandler的对象关联起来,并将代理对象强制转换成真实对象所对应的类,然后通过该代理对象的的引用去调用方法,则会出现动态代理的情况;真实的原理就是在当通过代理对象的引用都调用的时候,会调用invokehander对象的invoke方法(个人理解:因为通过Proxy类创建代理对象的时候,会传递一个真实代理对象实现的类的接口进去,动态代理通过内部实现了该接口的所有方法,然后在每个方法中回调InvokeHander类对象的invoke方法,并不是在编译的时候采用inline的那种方法直接编译进去,因为要用到反射,所以不可能是采用内联的方法实现的)。http://www.cnblogs.com/xiaoluo501395377/p/3383130.html
 

Java里有很多很杂的东西,有时候需要你阅读源码,大多数可能书里面讲的不是太清楚,需要你在网上寻找答案。 

推荐书籍:《java核心技术卷I》《Thinking in java》《java并发编程》《effictive java》《大话设计模式》
 

JVM

 

1. 内存模型以及分区,需要详细到每个区放什么。

1:主内存,工作内存,主内存存储对象的变量,各个线程修改变量都在工作内存中实现。线程之间不能跨过主内存去操作另一个线程的工作内存。必须要通过主内存。

2:运行时数据区域:虚拟机栈,本地方法栈,程序计数器,堆,方法区

① 程序计数器:线程私有的,他是一块较小的内存空间,他相当字节码于解释器中的指针,也就是该内存存放下一条即将执行指令的地址。字节码解释器就是通过改变 这个计数器的值来选择下一条即将执行的指令。每一个线程都有一个程序计数器(内存),这样线程切换的时候就能找到自己各个线程各自即将执行的下一条指令。 所以说是线程私有的。

②java虚拟机栈:线程私有的,每一个方法在执行的时候就会创建一个栈帧来存放方法的局部变量,操作数栈,返回地址等,当方法执行完成的时候就释放该栈帧。

栈帧:虚拟机栈中是一栈帧为单位存储的,所以一个虚拟机栈中有很多栈帧,每一个栈帧中分为:局部变量区(存放方法的参数和局部变量),操作数栈,方法的返回地址,动态链接(一般解析解阶段是将部分符号引用转换成直接应用(类加载),而动态链接是另外一部分的符号引用转换成直接引用(运行时))

③本地方法栈:线程私有,本地方法指的是那种不是用java语言写的方法,java虚拟机栈只针java方法,而不是本地方法。hotspot虚拟机支持别的语言写的方法在虚拟机上运行,本法方法栈和java虚拟机栈一样。只是他们服务的对象不一样而已,一个为java方法服务,一个为native方法服务。

④java堆:线程共享的,不过也可能为多个线程分配私有的buffer,也就是每个线程有自己的缓存器,java堆可以是物理上连续的,也可以是不连续的。java堆是垃圾回收器管理的主要区域,所以也叫gc堆。java堆可以分为:新生代和老年代

⑤方法区:线程共享的,可以理解为gcc中所所说的静态区,不过也不是确切的准确,因为在hotspot虚拟机中他存放的是类中静态变量和常量(注意是常量哦)。因为他能存储常量,所以还有存储常量的区域有一个特别的名称,叫做常量池(包括引用和基本数据类型的常量),方法区并不是堆,这一点和静态区很相似。所以别名叫non-heap,java堆中可以选择不实现gc回收,但是实际上呢还是会的,只能说垃圾回收器在这个区域不活跃而已,但是回收都是回收常量池中的常量,而不是静态变量。可以称为永久代

⑥运行时常量池:他是方法区的一部分,但是和方法区的常量池有区别,他存放的常量是在运行时产生的,而不是编译时产生的。注意与普通方法区的区别

 

2. 堆里面的分区:Eden,survival from to,老年代,各自的特点。

1:Eden区的对象都是朝生夕死,发生minor gc的时候会清除eden区和survival区的,把存活的对象移到另一个Survival区,该survial区由老年代保证。当在年轻代中对象经过多次minor gc以后还存活,达到老年代的年纪,就会移动到老年代,还有就是大对象在年轻代无法存储,直接转到老年代,还有可能因为担保而进入老年代的

 

3. 对象创建方法,对象的内存分配,对象的访问定位。

1对象的创建包括三步骤:①当遇到new命令的时候,会在常量池中检查该对象的符号引用是否存在,不存在则进行类的加载,否则执行下一步②分配内存,将将要分配的内存都清零。③虚拟机进行必要的设置,如设置hashcode,gc的分代年龄等,此时会执行<init>命令在执行之前所有的字段都为0,执行<init>指令以后,安装程序的意愿进行初始化字段。

2:对象的内存分配:包括对象头,实例数据,对齐填充

①对象头:包括对象的hascode,gc分代年龄,锁状态标等。

②实例数据:也就是初始化以后的对象的字段的内容,包括父类中的字段等

③对齐填充:对象的地址是8字节,虚拟机要求对象的大小是对象的整数倍(1倍或者两倍)。因此就会有空白区。

3:对象的访问:hotspan中 是采用对象直接指向对象地址的方式(这样的方式访问比较快)(还有一种方式就是句柄,也就是建一张表维护各个指向各个地址的指针,然后给指针设置一个句柄 (别名),然后引用直接指向这个别名,就可以获得该对象,这种的优势就是,实例对象地址改变了,只要修改句柄池中的指针就可以了,而不用引用本身不会发生 改变)。

 

4. GC的两种判定方法:引用计数与引用链。

1:引用计数:给一个对象设置一个计数器,当被引用一次就加1,当引用失效的时候就减1,如果该对象长时间保持为0值,则该对象将被标记为回收。优点:算法简单,效率高,缺点:很难解决对象之间的相互循环引用问题。

2:引用链(可达性分析):现在主流的gc都采用可达性分析算法来判断对象是否已经死亡。可达性分析:通过一系列成为GC Roots的对象作为起点,从这些起点向下搜索,搜索所走过的路径成为引用链,当一个对象到引用链没有相连时,则判断该对象已经死亡。

3:可作为gc roots的对象:虚拟机栈(本地方法表)中引用的对象(因为在栈内,被线程引用),方法区中类静态属性引用的对象,方法区中常量引用的(常量存放在常量池中,常量池是方法区的一部分)对象,native方法引用的对象

4:引用计数和引用链是只是用来标记,判断一个对象是否失效,而不是用来清除。

 

5. GC的三种收集方法:标记清除、标记整理、复制算法的原理与特点,分别用在什么地方,如果让你优化收集方法,有什么思路?

1:标记清除:直接将要回收的对象标记,发送gc的时候直接回收:特点回收特别快,但是回收以后会造成很多不连续的内存空间,因此适合在老年代进行回收,CMS(current mark-sweep),就是采用这种方法来会后老年代的。

2:标记整理:就是将要回收的对象移动到一端,然后再进行回收,特点:回收以后的空间连续,缺点:整理要花一定的时间,适合老年代进行会后,parallel Old(针对parallel scanvange gc的) gc和Serial old就是采用该算法进行回收的。

3:复制算法:将内存划分成原始的是相等的两部分,每次只使用一部分,这部分用完了,就将还存活的对象复制到另一块内存,将要回收的内存全部清除。这样只要进行少量的赋值就能够完成收集。比较适合很多对象的回收,同时还有老年代对其进行担保。(serial new和parallel new和parallel scanvage)

优化收集方法:对复制算法的优化:并不是将两块内存分配同等大小,可以将存活率低的区域大一些,而让回收后存活的对象所占的区域小一些,不够的内存由老年代的内存来保证,这样复制算法的空闲的空间减少了。

两个survival区域的是为了减少风险率,有一个survivor区要参与回收,也要参与存储,只要只有10%的空间浪费,同时也减少对老年代的依赖。

 

6. GC收集器有哪些?CMS收集器与G1收集器的特点。

1:串行的,也就是采用单线程(比较老了),分类:serial new(收集年轻代,复制算法)和serial old(收集老年代,标记整理),缺点:单线程,进行垃圾回收时暂时所有的用户线程。优点:实现简单。

2:并行的,采用多线程,对于年轻代有两个: parallel new(简称ParNew)(参考serial new的多线程版本)和parallel scavenge;parallel scavenge是一个针对年轻代的垃圾回收器,采用复制算法,主要的优点是进行垃圾回收时不会停止用户线程(不会发生stop all world)

老年代回收器也有两种:Parallel old是parallel scavenge的我老年代设计的。CMS(并发标记清除),他采用标记清除算法,采用这种的优点就是快咯,因此会尽快的进行回收,减少停顿时间。

3:高级杀手:G1收集器,年轻代和老年代通吃,最新一代的技术。面向服务器端的垃圾收集器(并行+并发的垃圾收集器)。

7. Minor GC与Full GC分别在什么时候发生?

1.Minor GC发生:当jvm无法为新的对象分配空间的时候就会发生minor gc,所以分配对象的频率越高,也就越容易发生minor gc。

2.Full GC:发生GC有两种情况,①当老年代无法分配内存的时候,会导致MinorGC,②当发生Minor GC的时候可能触发Full GC,由于老年代要对年轻代进行担保,由于进行一次垃圾回收之前是无法确定有多少对象存活,因此老年代并不能清除自己要担保多少空间,因此采取采用动态估算的方法:也就是上一次回收发送时晋升到老年代的对象容量的平均值作为经验值,这样就会有一个问题,当发生一次Minor GC以后,存活的对象剧增(假设小对象),此时老年代并没有满,但是此时平均值增加了,会造成发生Full GC

8. 几种常用的内存调试工具:jmap、jstack、jconsole。

 

9. 类加载的五个过程:加载、验证、准备、解析、初始化。

1:加载:加载有两种情况,①当遇到new关键字,或者static关键字的时候就会发生(他们对应着对应的指令)如果在常量池中找不到对应符号引用时,就会发生加载 ,②动态加载,当用反射方法(如class.forName(“类名”)),如果发现没有初始化,则要进行初始化。(注:加载的时候发现父类没有被加载,则要先加载父类)

2:验证:这一阶段的目的是确保class文件的字节流中包含的信息符合当前虚拟机的要求,并不会危害虚拟机自身的安全(虽然编译器会严格的检查java代码并生成class文件,但是class文件不一定都是通过编译器编译,然后加载进来的,因为虚拟机获取class文件字节流的方式有可能是从网络上来的,者难免不会存在有人恶意修改而造成系统崩溃的问题,class文件其实也可以手写16进制,因此这是必要的)

3:准备:该阶段就是为对象分派内存空间,然后初始化类中的属性变量,但是该初始化只是按照系统的意愿进行初始化,也就是初始化时都为0或者为null。因此该阶段的初始化和我们常说初始化阶段的初始化时不一样的

4:解析:解析就是虚拟机将常量池中的符号引用替换成直接引用的过程。符号引用其实就是class文件常量池中的各种引用,他们按照一定规律指向了对应的类名,或者字段,但是并没有在内存中分配空间,因此符号因此就理解为一个标示,而在直接引用直接指向内存中的地址。

5:初始化:简单讲就是执行对象的构造函数,给类的静态字段按照程序的意愿进行初始化,注意初始化的顺序。(此处的初始化由两个函数完成,一个是<clinit>,初始化所有的类变量(静态变量),该函数不会初始化父类变量,还有一个是实例初始化函数<init>,对类中实例对象进行初始化,此时要如果有需要,是要初始化父类的)

 

10. 双亲委派模型:Bootstrap ClassLoader、Extension ClassLoader、ApplicationClassLoader。

类 加载器的工作过程:如果一个类加载器收到类类加载的请求,他首先不会自己去加载这个类,而是把类委派个父类加载器去完成,因此所有的请求最终都会传达到顶 层的启动类加载器中,只有父类反馈无法加载该类的请求(在自己的搜索范围类没有找到要加载的类)时候,子类才会试图去加载该类。

 

11. 分派:静态分派与动态分派。

1:静态分派和动态分派都是多态的内容,多态的实现依赖于编译阶段和运行时阶段:在编译阶段主要表现在静态分派,

2:静态分派就是通过静态类型和方法参数个数来选择哪一个方法版本,这就是主要体现了方法的重载;因为他在编译的时候就能确定调用哪一个函数,所以叫静态分派。

3:在运行时阶段体现在动态分派(动态绑定),也就是当一个父类引用指向子类对象,通过该父类引用去调用一个该方法,由于在编译阶段生产的调用函数代码的字节码指向的是父类(静态类型)被调用方法,并不知道具体要去调用哪一个实际类型的方法,因 此会发生这样一个过程,虚拟机找到操作数栈中位于栈顶获取该操作数的指所指向的类,然后到常量池中去搜索与被调用的方法匹配的方法名和描述符,如果找到, 就进行权限校验(校验失败就抛出异常),如果可以访问,则返回该方法的符号引用,并转换成直接引用,调用该执行,如果找不到就到父类中去找,然后重复上面 动作,最后找不到就抛出异常。

4: 对动态绑定的优化:由于要去常量池中搜索每一类的方法名和描述符,因此效率比较低,所以最后进行了优化,就是在方法区为每一类维护一张虚方法表或者接口方 法表(虚表中存放了该方法的实际入口地址),让该类的所有方法都维护进去(包括父类的方法),因此要查找方法名的时候,直接去该虚表中去搜索到该方法名对 应的直接地址然后执行。对于没有被重写的方法,直接存放父类的入口地址,如果该方法被重写,在存放子类的方法入口地址。

 

JVM过去过来就问了这么些问题,没怎么变,内存模型和GC算法这块问得比较多,可以在网上多找几篇博客来看看。


1.进程与线程的区别

1:进程是系统进行资源分配和调度的基本单位,而线程是进程的一个实体,是系统调度和分派的基本单位;

2:进程中各个线程可以共享进程的资源,线程之间可以相互的控制(并发编程),而进程之间不能共享资源(特殊情况除外,操作系统中有两个方法就就可以让一个进程直接去读取另外一个进程的内存,但是是高度保密的);进程之间共享资源有相互的通信模式。

3:进程只是一个概念,他真是存在的实体是进程控制块(PCB),他存储进程的相关信息,创建一个进程就是创建一个进程控制块,销毁一个进程就是销毁该进程的进程控制块;进程控制块包含的信息:如进程的程序和数据在内存中中的起始地址等,进程的名字,进程的父亲,进程的儿子,进程调度信息(就绪,阻塞,执行等信息);

4:线程中也有TCP,线程控制块,线程的实体,线程中的tcb所保存的线程的状态比pcb的状态要多,因为tcp设计到各个线程之间的相互协调等。

5:进程间通信的三种方式:共享存储系统,消息传送系统,和管道系统;

2. 死锁的必要条件,怎么处理死锁。

1:进程死锁的必要条件:

1,互斥(也就是一个资源多个进程共享,如打印机)

2是资源的不可抢占(一个资源只能被拥有它的进程释放,不能被别的进程抢占,如打印机就是不可以,但是cpu可以抢占)

3是请求和保持(进程已经占有一个资源,但是又去申请新的资源,而该资源被别的进程占有)

4是环路等待;(也就是一个进程占用了一个资源,然后去申请别进程占有的资源,而别的进程占有该资源,然后又来申请之前线程占有的资源,形成一个环路)

2:只要发生死锁,这四个条件一定同时成立,同时成立则不一定造成死锁。

3:解决线程死锁的方法:

1死锁的预防(让一个线程占有所有的资源,使用完以后才可以给别的线程使用):去破环产生死锁的四个条件(效率比较低,破环了多道程序的并发执行,破坏资源共享原则)

2死锁的避免(银行家算法:在分配一个资源个进程的时候,先检查资源是否处于安全状态。如此次分配不会导致系统从安全状态转变为不安全状态,便可以将该资源分配给进程。):在进程运行时采取措施,而不是破环必要条件,在进程申请资源时避免系统进入不安全状态(不安全状态的检查);

3死锁的检查和解除:系统检查到死锁,然后采用杀死进程的方法解除死锁

4:发生死锁采用人工重启(个人电脑可以采用这种方式,比较重要的就不可以)

 

3. Window内存管理方式:段存储,页存储,段页存储。

1:分页存储:将程序的逻辑地址空间划分为固定大小的页(page),而物理内存划分为同样大小的页框(page frame)。程序加载时,可将任意一页放人内存中任意一个页框,这些页框不必连续。

页式存储优点:没有外碎片,但会有内碎片,可以不连续存储,缺点: 要求程序全部装入内存,没有足够的内存,程序就不能执行。

页表:页号(一个进程中的逻辑页号,从0开始)和块号(物理内存中的实际块号,整个内存空间的块号是唯一的,是从0开始的)

逻辑地址到物理地址的转换:先获取逻辑地址,并按照页的划分将逻辑地址划分为页号和页内偏移地址,进程物理映射的时候,先通过页表获取对应物理地址的块号,然后将将偏移量直接映射到物理块的偏移量中。如何计算实际地址:如果一页大小为1k,对应的物理地址的块号是5,则对应的地址就是5*1024(物理地址是从0开始计算块号的)+页内偏移地址就是逻辑对应的实际地址。

如何保证各个进程的块不冲突呢?整个系统有一张页面表,记录了物理内存的拿一些块被占用,拿一些块没有被占用,只要在分配内存的时候去查找一下这张页面表,也就不会去造成内存分配冲突。

对页表的优化:一般来说,页表都存贮在主存当中,这样,处理器要进行读写数据的时候,要进行两次操作,第一次用来查找页表将操作数的逻辑地址变换为物理地址;第二次完成真正的读写操作。这样写一次内存需要两次操作内存,耗费资源和时间,因此就出现了块表。

快表:将页表读入cpu的一个寄存器(cpu的高速缓冲器)中,由于高速缓存器容量有限,只能存储一部分页表(可以采用LRU算法存储常用的页表映射(块表)),当cpu计算出逻辑地址以后,直接去快表中读取对应的块号,而不到注册中获取,如果没有对应的映射,就到主存中去存取。

 

 

2:段式存储:(相对于页存储来说,是为了满足用户的需求而出现的一种内存分配方式)用户将自己的应用程序按照逻辑将内存地址分成若干个段,每个段都有一个自己的名字,而且每个段都是从0开始编址的,然后程序加载的时候,直接将各个段映射到内存中去,各个段在内存之间可以是不连续的,但是在段内是连续的。

段式存储的优点:各个段是一个完整的逻辑地址,因此可以对各个段施加不同的安全保护。还有就是没有内碎片,形成的外碎片也可以通过内存压缩来解决。缺点:就是要将各个段全部加载到内存,不能使用虚拟内存。

段表物理地址的计算:段地址由两部分组成,段号和段内地址。通过段号在段表中找到对应的段号,然后通过段表段号对应的段的基地址+段内偏移地址就可以计算出物理地址。

段页式存储:将内存安装程序逻辑关系分割成多个段,段内采用页式划分;

优点:可以充分提高内存利用率,也可以采用分段的方式按逻辑划分内存地址。

4. 进程的几种状态。

就绪态,阻塞态,运行态,对进程的状态可以进行细化(挂起,激活,主要是方便用户在进程运行的时候中间过程是可控的,可以在运行期间暂停某些进程,减轻系统负荷或者暂停进程故障排查(我们eclipse中断点应该就是这么实现的))。

细分以后就成了:活动就绪态,静止就绪态,活动阻塞态,静止阻塞态,运行态,(为什么叫静止时因为挂起以后如果不激活,他会一直挂起)

五种状态的转换,阻塞态必须先转换成就绪态才能到运行态,运行态可以直接到阻塞态,或者就绪态(或者直接被挂起,进入静止阻塞态,静止阻塞必须要转换成阻塞就绪,然后转换成活动就绪态)。

 

5. IPC几种通信方式。

1:共享内存模式:共享同一内存区间(我们的复制粘贴就是采用共享内存实现的)

2:消息队列模式:一个进程将消息发送到一个公共的消息队列缓存区中,然后另一个线程自己去该公共消息队列缓存区去读取是否有自己的消息(间接通信),还有一种是直接通信,通过一个进程通过内核直接将消息发送到一个进程自己的消息缓存队列缓存区(而不是公共消息队列缓冲区),然后目标线程有空就去读取(缺点,只能实现P2P通信,优点:就是比较快)。

3:管道模式:主要就是借助一个中间临时文件,一个进程向该文件中写数据,另一个进程从该管道中获取数据。(该中间文件与我们一般使用的文件的区别就是,先进先出,而且具有数据不可再现性(被读取以后就没有了),无名管道关机后自动删除该文件,该文件并不存放到硬盘中)

4:File文件通信:通过文件流的形式进行通信,然后一个从通过文件流从该文件中读取消息。(特点就是慢)

5RPC远程进程间通信,也就是Socket通信。主要是用于远程通信

6LPC本地进程通信,对RPC的改进。同一台主机中两个进程通过端口进行通信。

 

6. 什么是虚拟内存。

虚拟内存就是将硬盘中划分出一部分区域来当内存使用,是将部分内存的持久化到硬盘,当需要的时候就从内存中去读取。并将暂时不需要的内存信息存储到硬盘中去。

请求分页系统(重点),请求分段系统,请求段页式)就是针对虚拟内存的。通过请求实现内存与外存的信息置换。

置换算法:

①  FIFO先进先出算法,该操作在操作系统中经常被用到,比如作业调度(主要实现简单,很容易想到)。

②  LRULeast recently use)最近最少使用算法。根据使用时间到现在的长短来判断。

③  LFULeast frequently use)最少使用次数算法。根据使用次数来判断。

④  OPTOptimal replacement)最优置换算法,理论的最优,理论;就是要保证置换出去的是不再被使用的页,或者是在实际内存中最晚使用的算法。

7. 虚拟地址、逻辑地址、线性地址、物理地址的区别。

虚拟地址:程序自身定义的地址,需要转换成物理地址才能使用

逻辑地址:段内偏移地址,也是虚拟地址。

物理地址:内存中的实际地址。

线性地址:cpu可寻址的地址,段内偏移地址+段基址。然后该地址就是实际的了地址。

因为是做android的这一块问得比较少一点,还有可能上我简历上没有写操作系统的原因。

 

推荐书籍:《深入理解现代操作系统》


TCP/IP

 

1. OSITCP/IP各层的结构与功能,都有哪些协议。

1:应用层:HTTPFTPDHCPSMTP(推协议),POP3(拉取协议),DNS

(表示层:数据格式的转换和加密,如FTP协议可以采用二进制形式传输还是ASCII传输,都可以在这儿进行转换,加密算法MD5SHA1MD5SHA1都是Hash算法,MD5输出是128位的,SHA1输出是160位的,MD5SHA1快,SHA1MD5强度高。))

(会话层:控制通信是全双工还是半双工,管理用户登录和注销,和session很像)

2:运输层:TCPUDP

3:网络层:IP协议,ICMP协议,ARP协议,RARP

4:链路层:以太网,令牌环。

 

2. TCPUDP的区别。

TCP协议面向连接,UDP协议面向非连接(三次握手,四次挥手);

TCP协议传输速度慢,UDP协议传输速度快

TCP协议可靠的,UDP协议不可靠的;

TCP有丢包重传机制,UDP没有(与无连接无关);

TCP有拥塞控制,UDP没有(与无连接无关)

 

3. TCP报文结构。

1TCPTransmission Control Protocol)传输控制协议是一种面向连接的、可靠的、基于字节流的传输层协议

2TCP报文分为报头和数据,报头中主要的字段:源地址和端口号,目的地址和端口号,确认号(syn报文),滑动窗口大小。检验和(目的,计算发送端的报头和数据,然后得出一个数字,报文接受段采用同样的校验方式来计算得出校验和值,如果两个相同,则接受,不相同,则可能才传输过程中发送数据被人修改,不接受,MD5是常被用来校验一个文件是否被改变)。

 

4. TCP的三次握手与四次挥手过程,各个状态名称与含义,TIMEWAIT的作用。

1. 三次握手建立连接:第一次握手:建立连接时,客户端发送syn(syn=j)到服务器,并进入SYN_SEND状态,等待服务器确认;第二次握手:服务器收到syn包,必须确认客户的SYNack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;第三次握手:客户端收到服务器的SYNACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。

 

 

2:对于四次挥手,客户端想主动断开,则发送一个FIN指令以后,自己进入FIN_WAIT_1状态,服务端此时知道客户端想要断开,自己进入  CLOSE_WAIT状态,但是此时两端都是还是可以相互通信的,如果此时服务端有数据,则会发送给客户端一个ACK报文(告诉客户端我知道了),客户端接收到ACK则进入FIN_WAIT_2阶段(暂时阶段),然后继续传输数据,数据传输完成以后,然后服务端发送FIN指令,此时客户端接受到FIN指令,自己进入LAST_ACK状态,然后发送一个ACK他本可以马上关闭了,但是客户端并没有马上关闭而是进入TIME_WAIT状态,处在该状态以后保持2RTT(大概四分钟吧)就客户端的Socket就自动关闭了,这是因为可能是最后的那个FIN先收到,而在FIN之前的包还为到达客户端(网络延迟),所以要等一等。

 

3: 为什么有FIN_Wait_1FIN_wait_2?

这是因为第一个发送了fin命令之后,服务端有两种发送情况一种是没有数据发送了。直接发送ACK+FIN,所以FIN_WAI_1理解我第等待fin也就可以了,还有就是服务器还有数据发送,那么他先发给服务端ack,等数据传输完成以后再发送fin,所以这儿理解为等待fin也是可以理解的。

4:默认是客户端想主动断开,所以发送FIN以后自己就不会主动传输数据了,也就是不会调用write(),但是会调用read()进行读取,所以服务端关闭socket不需要停留2RTT

5:为什么要三次连接,最简单解释,两个人爬山,山顶的人向山脚的人喊了一声,而山脚的人听到了,就回答了一句:知道了,但是他并不知道山顶的人是否听到,这样就会发生很多问题。

两次连接的问题

1:服务端有可能发送一次无用连接,占用服务器资源。

当客户端发送一个syn进行连接,由于网络延迟,此时客户端没有收到ack,服务端也没有收到ack,此时客户端再次发送syn进行连接,这次和服务端建立了连接,然后发送完数据,通信完成,此时第一个syn包来了,服务端以为客户端又来了,因为是两次,所以开放资源和客户端建立连接,然后该连接是无效的。

2:发生死循环(建立连接以后服务端主动发送数据的情况);

当服务端接受到客户端的连接请求syn包时,发送ack包,然后建立连接,此时就开始向客户端发送数据,但是由于网络不稳定,服务端的ack并没有被客户端收到,但是客户端却收到了服务端发送的数据,但是此时不是客户端想要的ack,丢弃,然后继续发送syn包,服务端不管该syn包,然后继续发送数据,客户端发现该数据还是不是需要的ack,继续发送syn包。一直持续下去。

 

 

5. TCP拥塞控制。

拥塞控制算法:“慢启动”(Slow Start)、“拥塞避免”(Congestion voidance)、“快速重传 ”(Fast Retransmit)、“快速恢复”(Fast Recovery

1. 慢启动:早期开发的TCP应用在启动一个连接时会向网络中发送大量的数据包,这样很容易导致路由器缓存空间耗尽,网络发生拥塞,使得TCP连接的吞吐量急剧下降,一看是先发送一个segment到服务器,服务器正常返回ack以后就发送两个segment,如果服务器正确返回,则继续增减一个发送的segment的数据,也就是逐渐增大拥塞窗口的大小,(2^n

2. 拥塞避免: 如果按上述慢启动的逻辑继续下去而不加任何控制的话,必然会发生拥塞,因此设置一个动态阈值来控制拥塞窗口的大小,当拥塞窗口的大小小于阈值的大小,就属于慢启动阶段,随着拥塞窗口越来越大,甚至超过了阈值,则网络会逐渐发生拥堵状况,这时就会将发送阈值降为原来的一般,并把拥塞窗口的大小设置为初始值(发生第一次拥塞以后变成线性增长的发送)

3. 快速重传,(当接收到冗余ack和长时间接受不到对应的ack的时候就,就会发生重新发送对应序列号的分组),当快速重传的时候则将阈值设置为原来的一半,并发送丢失序号的分组。

4. 快速恢复(是对快速重传的补充): 那么,什么是快速恢复呢?我们通常认为client接收到3个重复的ack后,就会开始快速重传,但是,如果还有更多的重复ack呢,如何处理?这就是快速恢复要做的,事实上,我们可以把快速恢复看作是快速重传的后续处理,它不是一种单独存在的形态。快速重传之后吧马上增大下一个发送分组的数量,也就是阈值+3segment的大小。为什么要增加3segment呢,这时因为一个发送端收到了3ack,就确认有三个分组被正确接收,为了最大利用网络,保证在网络中的分组数量一致,就在增加了3segment

 

6. TCP滑动窗口与回退N针协议。

1.TCP的滑动窗口只在发送端和接收端才有,滑动窗口是用于流量控制的,注意滑动窗口与拥塞窗口的区分,拥塞窗口除了在发送端和接收端有,路由器也是有的。拥塞窗口是用于拥塞控制的(该窗口是试探性窗口,为了最大利用网络,该窗口一直在变动)。

2. 流量控制:用于窗口时接收方的缓存器的大小的窗口才叫滑动窗口。

公式:可以接收窗口大小=存器窗口大小-【刚接收到的窗口大小-刚被读取的窗口的大小】,可以接收窗口的大小是要发送到tcp的发送端的。发送方通过接收到大窗口的大小来判断是否可以发送多少数据到接收端才比较合适。

同时在接收端也是有算法的:接收方保证:接收端可接收窗口大小>刚发送的的分组数-刚接受到的分组数的ack,也就是未被确认的分组的大小小于接收方可接受窗口的大小。接收方缓存器窗口大小是固定的。

3. 流控制是一种局部控制机制,其参与者仅仅是发送方和接收方,它只考虑了接收端的接收能力,而没有考虑到网络的传输能力;而拥塞控制则注重于整体,其考虑的是整个网络的传输能力,是一种全局控制机制。正因为流控制的这种局限性,从而导致了拥塞崩溃现象的发生。

4:回退N步和选择重传都是解决流水线的差错恢复的两中基本方法;

1. 回退N步:就是将各个分组编号,假如滑动窗口的大小是4,那么一次传输过去四个分组,如果对方接受到一个分组,且分组序号的大小比上一个提交分组的序号大1,则发送一个ack给发送端并提交分组,如果发送端接收到ackackbase序号相同,则滑动窗口向后移动。如果接受到的分组不是比提交的分组大1,则会丢弃该分组,这样发送端如果在定时器超时的时间之内没有接受到ack,则回重新发送分组;

缺点:就是如果有一个分组没有被接收而被丢弃,那么可能同一个窗口发送过来的好多分组都会被丢弃。这样就造成了网络的浪费,同时滑动窗口越大,虽然网络的利用率越大,但是一点一次传输过去的是base序号的分组失序或者丢失,则之后整个窗口分组都会被重新发送。(GBN协议只需要一个定时器)

2. 选择重传:由于GBN协议可能会将失序的分组丢掉,其实失序的分组有时候是没有必要被重新传输的,只需要用一个buffer存存好就可以了。选择重传有在发送方每一个分组都有一个定时器(重点,也是缺点),假如滑动窗口大小是4,如果接受方接收到的分组不比上一个提交的分组序号大1,则会将该分组收分组存入buffer中,等下一个正确的分组到来以后和他一起提交,并将刚接手到;而对于接受方来说:如果一个分组未收到确认,如果超过该分组定时器的时间,则将该分组重新发送,如果ack接受到一个base序号的分组,则滑动窗口向后移动,如果滑动窗口接受到的序号不是base序号的ack,则会将base后面序号的ack先确认,但是此时发送方的滑动窗口并不会向后滑动,只有base序号的ack回来以后滑动窗口才会向后移动。(SR选择重传在没有收到想要的序号的时候返回的是ack中包含的是想要的序号的ack,而不是刚刚接收到的分组的序号

3TCP用于解决流水线的差错恢复方法:使用的协议是GNB协议和SR选择重传的综合:GBN协议是只有一个超时定时器,而SR选择重传每一分组有一个定时器,TCP采用的是设置一个定时器(所有的分组的超时时间就是一样的)。但是在ack确认和接收方序号乱的时候采用的SR协议来实现的。

面试中一定要准备的7种排序算法


1.冒泡排序 

	public int[] bubbleSort(int[] arr) {
		for (int i = 0; i < arr.length; i++) {
			for (int j = arr.length - 1; j > i; j--) {
				if (arr[j - 1] > arr[j]) {
					int temp = arr[j - 1];
					arr[j - 1] = arr[j];
					arr[j] = temp;
				}
			}
		}
		return arr;
	}


冒泡排序的改进 

// 稍微优化一下,对于排序好的如123456789,会一直交换多次
	public void bubbleSortBetter(int[] arr) {
		// 一次循环中如果没有交换过元素,就说明已经排好了
		boolean isSorted = true;
		for (int i = 0; i < arr.length; i++) {
			for (int j = arr.length - 1; j > i && isSorted; j--) {
				isSorted = false;
				if (arr[j - 1] > arr[j]) {
					int temp = arr[j - 1];
					arr[j - 1] = arr[j];
					arr[j] = temp;
					isSorted = true;
				}
			}
		}
	}
2.选择排序 

	public void chooseSort(int[] arr) {
		for (int i = 0; i < arr.length; i++) {
			int min = i;
			for (int j = i + 1; j < arr.length; j++) {
				if (arr[min] > arr[j]) {
					min = j;
				}
			}
			if (min != i) {
				int temp = arr[min];
				arr[min] = arr[i];
				arr[i] = temp;
			}
		}
	}

3.插入排序 

public void insertSort(int[] arr) {
		int j;

		for (int i = 1; i < arr.length; i++) {
			// 优化条件
			if (arr[i] < arr[i - 1]) {
				int temp = arr[i];
				// 是从最左边开始,比较的同时还要移动
				for (j = i - 1; j >= 0 && arr[j] > temp; j--) {
					arr[j + 1] = arr[j];
				}
				arr[j + 1] = temp;
			}
		}
	}
4.希尔排序 
	// 排序是不稳定的,最坏时间复杂度O(n^1.5),平均时间复杂度O(nlogn);
	// 步长一般都用2,但是最后只要保证gap=1就可以。
	public void shellSort(int[] arr) {
		int j;
		int gap = arr.length / 2;
		while (gap >= 1) {
			// 下面就是一个插入排序过程,只是每个过程都是有间隔
			for (int i = gap; i < arr.length; i++) {
				int temp = arr[i];
				for (j = i - gap; j >= 0 && temp < arr[j]; j = j - gap) {
					arr[j + gap] = arr[j];
				}
				arr[j + gap] = temp;
			}
			gap = gap / 2;
		}
	}
5.堆排序 
// 堆的删除,只能删除A【0】的节点,将交换后的节点和儿子比较,选最小的一个作为头节点。
	// 从i节点开始调整,n为节点总数 从0开始计算 i节点的子节点为 2*i+1, 2*i+2
	public void heapAdjust(int[] arr, int i, int n) {
		// i表示当前节点开始调整,主要是通用性
		int temp = arr[i];
		int index = 2 * i + 1;
		while (index < n) {
			// 找到左右儿子最小值的索引
			if (arr[index] < arr[index + 1]) {
				index++;
			}
			if (temp < arr[index]) {
				break;
			}
			// 将最小儿子上移动
			arr[i] = arr[index];
			i = index;
			index = 2 * i + 1;
		}
		// 以为temp一直没有变,而且用来作为比较的参考值,
		// 因此不交换而用这种方式就可以。
		arr[index] = temp;
	}


// 堆化数组
	public void makeArrayToMinHeap(int[] arr, int n) {
		// 因为叶子节点不用参与重组,相当于是已经建好的堆
		// 这就用上了之前的堆的删除的那个函数,删除那个函数因为n
		// n不变时就用来当调整使用了n / 2 - 1最后一个非叶子节点
		for (int i = n / 2 - 1; i >= 0; i--)
			heapAdjust(arr, i, n);
	}

public void heapSort(int[] arr) {
		// 第一次将A[0]与A[n - 1]交换,再对A[0…n-2]重新恢复堆。
		// 第二次将A[0]与A[n – 2]交换,再对A[0…n - 3]重新恢复堆,
		// 重复这样的操作直到A[0]与A[1]交换。
		for (int i = arr.length - 1; i >= 1; i--) {
			int temp = arr[i];
			arr[i] = arr[0];
			arr[0] = temp;
			heapAdjust(arr, 0, i);
		}

	}

6.快速排序 
public void quickSort(int[] arr, int low, int high) {
		if (low >= high) {
			return;
		}
		int left = low;
		int right = high;
		int mid = arr[low];
		
		while (low < high) {
			// 必须先从高位开始
			while (low < high && arr[high] >= mid) {
				high--;
			}
			arr[low] = arr[high];
			while (low < high && arr[low] <= mid) {
				low++;
			}
			arr[high] = arr[low];
		}
		arr[low] = mid;

		quickSort(arr, left, low - 1);
		quickSort(arr, low + 1, right);

	}

7.归并排序 
public void mergeSort(int[] arr) {
		// 必须要从外面创建temp传进去
		int[] temp = new int[arr.length];
		mergeArray(arr, 0, arr.length - 1, temp);
	}

	public void mergeArray(int[] arr, int low, int high, int[] temp) {

		// 注意递归出口
		if (low < high) {
			int mid = (low + high) / 2;
			mergeArray(arr, low, mid, temp);
			mergeArray(arr, mid + 1, high, temp);
			merge(arr, temp, low, mid, high);
		}
	}

	public void merge(int[] arr, int[] temp, int low, int mid, int high) {
		int left1 = low;
		int left2 = mid + 1;
		int k = 0;
		while (left1 <= mid && left2 <= high) {
			if (arr[left1] > arr[left2]) {
				temp[k++] = arr[left2++];
			} else {
				temp[k++] = arr[left1++];
			}
		}
		while (left2 < high) {
			// 不会发生越界,因为left2也是先赋值,再++;
			temp[k++] = arr[left2++];
		}
		while (left1 < mid) {
			temp[k++] = arr[left1++];
		}
		System.arraycopy(arr, low, temp, 0, temp.length);
	}

1、Jsp与Servlet技术

 

 1.jsp有哪些内置对象作用分别是什么
答:JSP共有以下9种基本内置组件(可与ASP的6种内部组件相对应):
1:request 用户端请求,此请求会包含来自GET/POST请求的参数 (javax.servlet.ServletRequest类的对象,可以获取包含http请求中的所有数据)
2:response 网页传回用户端的回应 (javax.servlet.ServletResponse的对象,包含http响应中的所有数据)
3:application  (javax.servlet.ServletContext的对象,封装了web.xml中的全局变量的初始化参数,整个web应用程序就只有一个application对象,因此可以通过他来在各个jsp页面之间传递参数,当然也可以通过request对象来传递参数)
4:config (javax.servlet.ServletConfig类的实例,ServletConfig对象封装了web.xml的中的当前servlet或者jsp的初始化参数,通过getInitParameter(String name)就可以获得,该局部变量主要用于初始化)

5:out 用来传送回应的输出(javax.servlet.jsp.JspWriter类的对象)

6:pageContext (javax.servlet.jsp.PageContext类的对象,代表当前jsp页面编辑后的内容,通过pageContext可以获取到jsp中的各个内置对象,以及页面的一些属性,可以在jsp页面中查找各个变量,可获得当前范围内对象的scope,比如当前范围内一个pojo的的scope)
7:page JSP网页本身(javax.servlet.jsp.HttpJspPage对象) (当前的jsp页面)

8:session (javax.servlet.http.HttpSession类的对象,包含session中的所有信息。如session创建时间,最后访问时间,存活时间,sessionid等)
9: exception 针对错误网页,未捕捉的例外(java.lang.Excetion类的对象)

 

2.jsp有哪些动作作用分别是什么
答:JSP共有以下6种基本动作(也叫行为)
   1:jsp:include:在页面被请求的时候引入一个文件。

(注意与指令<@page include=”usl”>的区别:jsp:include是在运行的时候包含进来,如果要包含进来的文件有程序,先执行程序,然后返回结果,而include指令只是在编译的时候将文件全部包含进来)(polo就是只有私有属性和getter,setter方法的类)
   2:jsp:useBean:寻找或者实例化一个JavaBean。

(具体用法:<form action=”userBean.jsp” method=“post“>,然后定义一个userBean.jsp页面,在jsp页面中用上如下语句<jsp:useBeam id=”person(随便取)” class=”com……Person类” scope=”page(表示该bean的可以作用的范围)“>,之后就可以用setProperty标签和getProperty标签获取bean的各个属性了)在这个域中

Scope的值可以是page,request,session,application;如果scope设置为session,就可以统计某个用户访问某个界面的次数,当scope设置为applocation的时候,就可以统计所有用户的用户次数;(可能的做法:在内存中只存在一个pojo对象,然后如果scope是session,则通过sessionid判断是否同一个人,如果是就count++;如果scope是application,则不用判断sessionid,直接调用该pojo,然后count++)
   3:jsp:setProperty:设置JavaBean的属性。 
   4:jsp:getProperty:输出某个JavaBean的属性。 
   5;jsp:forward:把请求转到一个新的页面。 对servlet中,requeat.getRequestdispather(“servlet名字“).forward()方法的封装,可以实现对request,reponse对象的转发。
   6:jsp:plugin:根据浏览器类型为Java插件生成OBJECT或EMBED标记

 

3、JSP中动态INCLUDE与静态INCLUDE的区别?

答:动态INCLUDE用jsp:include动作实现
   它总是会检查所含文件中的变化,适合用于包含动态页面,并且可以带参数,如果jsp页面有程序,先执行程序直接返回结果。
   静态INCLUDE用include伪码实现,定不会检查所含文件的变化,适用于包含静态页面(用<%@ include file=”文件名”>命令实现;include指令一般用在有同一个header.jsp,或者foot.jsp;会全部引入进来。当然,最上面的<%@page language=”java”,contentType=”text/html”;character=utf-8 %>这一行是不会引进来的。)

4、两种跳转方式分别是什么有什么区别

答:跳转有两种方法:RequestDispatcher.forward()方法和HttpServletResponse.sendRedirect()方法.

区 别是:

转发:仅是容器中控制权(从一个servlet对应Wrapper容器转让给另一个Wrapper容器对应的servlet)的转向,在客户端浏览器地址栏中不会显示出转向后的地址,他是不会改变Request的值,如果你需要在下一个页面中能从中获 取新的信息的话,你可以Request.setAttribute()来放置一些标志,这样从下一个页面中获取;

重定向:(如果发生重定向,服务端向客户端发送http状态吗301(永久重定向)或者302(临时重定向),并把新的url传给浏览器,浏览器再去访问服务端返回的url)后者则是完全的跳转,浏览器将会得到跳转的地址,并重新发送请求链接。这样,从浏览器的地址栏中可以看到跳转后的链接地址。所以,前者更加高效,在前者可以满足需要时,尽量使用Request Dispatcher.forward()方法,并且,这样也有助于隐藏实际的链接。

对于用户:

(301,302对用户来说没有区别,他们看到效果只是一个跳转,浏览器中旧的URL变成了新的URL。页面跳到了这个新的url指向的地方。)

对于引擎及站长

(采用302临时重定向可能造成网址劫持的可能:一个在在自己的网站中使用了302重定向,就代表重定向的目标可能被更换成别的网站;网站劫持就是当网站A重定向到网站B,在浏览器的url栏框中显示的是原网站A的地址,但是显示的页面的内容却是网站B的地址,这样就网站劫持,你幸幸苦苦写的网页就成别人的了。造成网站被劫持的情况:比如说,有的时候A网址很短,但是它做了一个302重定向到B网址,而B网址是一个很长的乱七八糟的URL网址,甚至还有可能包含一些问号之类的参数。很自然的,A网址更加用户友好,而B网址既难看,又不用户友好。这时Google很有可能会仍然显示网址A。)

 

5. 描述JSP和Servlet的区别、共同点、各自应用的范围

1.jsp经编译后就变成了Servlet.(JSP的本质就是Servlet,JVM只能识别java的类,不能识别JSP的代码,Web容器将JSP的代码编译成JVM能够识别的java类)

2.jsp更擅长表现于页面显示,servlet更擅长于逻辑控制.

3.Servlet中没有内置对象,Jsp中的内置对象都是必须通过HttpServletRequest对象,HttpServletResponse对象以及HttpServlet对象

得到.

4:因此在实际应用中采用Servlet来控制业务流程,而采用JSP来生成动态网页.在struts框架中,JSP位于MVC设计模式的视图层,而Servlet位于控制层.

 

6. 在Web开发中需要处理HTML标记时,应做什么样的处理,要筛选那些字符

(< > &“”)(可以用正则表达式提取)

 

7. 在JSP中如何读取客户端的请求,如何访问CGI变量,如何确定某个Jsp文件的真实路径。

request里面有很多方法与cgi(common gateway interface)变量对应,getParameter(), getHeader(), getCookies()等等

真实路径通过getServletContext().getRealPath()来取到


8. 描述Cookie和Session的作用,区别和各自的应用范围,Session工作原理。

1:区别:1.cookie 是一种服务器发送到客户浏览器的文本串句柄,并保存在客户机硬盘上,可以用来在某个WEB站点会话间持久的保持数据。(常用cookie来记住用户/密码,还有sessionid,访问某个网站的时候直接通过读取访问网站对应cookie,然后发送到服务端,下次不用手动输入)

2:session实在服务器端创建了的一个对象,来保存该客户端和服务端之间的一个链接,该对象其实就是一个hashmap,它可以存储key,value形式的数值,有一个专门sessionMangage来管理该session的存活时间,最后访问时间,过期时间等信息。

3.cookie和session的共同之处在于:cookie和session都是用来跟踪浏览器用户身份的会话方式。

4.cookie 和session的区别是:cookie数据保存在客户端,session数据保存在服务器端。

5:cookie使用的场景是保存每一个用户名/密码,或者作为sessionid方式的一种,session主要是用来解决http协议的无状态的问题的,如果不用session,服务器在浏览器访问结束以后是不存储关于客户端的任何信息的,同时session也是可以持久化的。同时session存储在服务器端,可以保证用户在一段时间内可以不用用户名/密码登录;用户过期以后,则需要的。

6:cookie不是很安全,别人可以分析存放在本地的COOKIE并进行COOKIE欺骗,如果主要考虑到安全应当使用session。

7:session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能,如果主要考虑到减轻服务器性能方面,应当使用COOKIE

8:单个cookie在客户端的限制是3K,就是说一个站点在客户端存放的COOKIE不能3K。

9:session跟踪的三种方法:重点说Cookie、隐藏表单和URL重写:

隐藏表单:<input type="hidden" name="jsessionid" value="ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764">也主要是用来存储sessionid;也就是服务器发送到客户端浏览其中的隐藏了一个hidden;他用来存储sessionid,因为session只在一段时间内才会有用,他不想用户名密码一样一直保存,因此用隐藏表单是可行的;

URL重新:也就是服务器在每一个url请求的后面都加入sessionid=xxxx;然后就可以识别该sessionid了,因为sessionid放在url后面是一个参数,所以可以通过request.getParameter获取得到sessionid的值的。(就是在所有的页面跳转上加上 encodeURL() 或者 encodeRedirectURL():如:<a href="<%=response.encodeRedirectURL("test.jsp")%>">totest.jsp</a>)

Jsp与Servlet技术

 

收集的网上一些关于HttpSession常见问题:

1.一个常见的误解是以为session在有客户端访问时就被创建,然而事实是直到某server端程序调用 HttpServletRequest.getSession(true)这样的语句时才被创建,注意如果JSP没有显示的使用 <% @page session="false"%> 关闭session,则JSP文件在编译成Servlet时将会自动加上这样一条语句 HttpSession session = HttpServletRequest.getSession(true);这也是JSP中隐含的session对象的来历。由于session会消耗内存资源,因此,如果不打算使用session,应该在所有的JSP中关闭它。

 

2.Session删除的时间是:

1)Session超时:超时指的是连续一定时间服务器没有收到该Session所对应客户端的请求,并且这个时间超过了服务器设置的Session超时的最大时间。

2)程序调用HttpSession.invalidate()

3)服务器关闭或服务停止

3.session的id是从哪里来的,sessionID是如何使用的:当客户端第一次请求session对象时候,服务器会为客户端创建一个session,并将通过特殊算法算出一个session的ID,用来标识该session对象,当浏览器下次(session继续有效时)请求别的资源的时候,浏览器会偷偷地将sessionID放置到请求头中,服务器接收到请求后就得到该请求的sessionID,服务器找到该id的session返还给请求者(Servlet)使用。一个会话只能有一个session对象,对session来说是只认id不认人

4.如何做到在浏览器关闭时删除session

严格的讲,做不到这一点。可以做一点努力的办法是在所有的客户端页面里使用javascript代码window.oncolose来监视浏览器的关闭动作,然后向服务器发送一个请求来删除session。但是对于浏览器崩溃或者强行杀死进程这些非常规手段仍然无能为力

5.跨应用程序的session共享

常常有这样的情况,一个大项目被分割成若干小项目开发,为了能够互不干扰,要求每个小项目作为一个单独的web应用程序开发,可是到了最后突然发现某几个小项目之间需要共享一些信息,或者想使用session来实现SSO(single sign on),在session中保存login的用户信息,最自然的要求是应用程序间能够访问彼此的session。

然而按照Servlet规范,session的作用范围应该仅仅限于当前应用程序下不同的应用程序之间是不能够互相访问对方的session的。各个应用服务器从实际效果上都遵守了这一规范,但是实现的细节却可能各有不同,因此解决跨应用程序session共享的方法也各不相同。

首先来看一下Tomcat是如何实现web应用程序之间session的隔离的,从 Tomcat设置的cookie路径来看,它对不同的应用程序设置的cookie路径是不同的,这样不同的应用程序所用的session id是不同的,因此即使在同一个浏览器窗口里访问不同的应用程序,发送给服务器的session id也可以是不同的。

6.访问*.html的静态资源因为不会被编译为Servlet,也就不涉及session的问题

7.有个HttpSessionListener是怎么回事

你可以创建这样的listener去监控session的创建和销毁事件,使得在发生这样的事件时你可以做一些相应的工作。注意是session的创建和销毁动作触发listener,而不是相反。类似的与HttpSession有关的listener还有 HttpSessionBindingListener,HttpSessionActivationListener和 HttpSessionAttributeListener

10.

3.J3.JSP的常用指令

isErrorPage(是否能使用Exception对象),isELIgnored(是否忽略表达式)

三个编译指令为:page、include、taglib。注意与jsp行为的区别,这三个指令值在编译期间的,动作或者行为是在运行期间。

1:page指令:(用来指明该jsp页面的一些属性)通常位于jsp页面的顶端,同一个页面可以有多个page指令。在page指令的属性中,IsErrorPage属性,设置本 JSP 页面是否为错误处理程序。如果该页面本身己是错误处理页面,则无须使用 errorPage 属性。errorPage属性表示出现错误以后跳转到哪一个网页。

2:include指令,使用 include 指令,可以将一个外部文件嵌入到当前 JSP 文件中,同时解析这个页面中的 JSP 语句(如果有的话)。这是个静态的 include 语旬,不会检查所包含 JSP 页面的变化。如果被嵌入的文件经常需要改变,建议使用<jsp:include>操作指令,因为它是动态的 include 语句。

3:taglib指令,该指令用来指明jsp页面内使用的jsp标签库,有两个属性uri(类库地址)和prefix;


4、什么情况下调用doGet()和doPost()?
Jsp页面中的form标签里的method属性为get时调用doGet(),为post时调用doPost()。

一般的运用都会在doget方法中调用doPost方法,那样不管是对于get请求方式还是post请求方式都可以用doPost()方法处理。

1、通过表单提交到的servlet,看form的method是get还是post

2、通过链接<a href....>访问的servlet,doGet

3、直接在IE地址栏中键入的servlet地址,doGet

1、get是吧参数数据队列加到提交表单的Action属性所指的url中,值和表单内各个字段--对应,在url中可以看到。

2、Get是不安全的,因为在传输过程,数据被放在请求的URL章,而如今现有的很多服务器,代理服务器或者用户代理都会将请求URL记录到日志文件中,然后放在某个地方,这样就可能会有一些隐私的信息被第三方看到

3、Get传输的数据量小,这主要是因为受URL长度限制;而Post方式提交,所以在上传文件只能使用Post


4、servlet的生命周期

(在继承HttpServlet几类的时候,其实可以说明什么都不用写,因为HttpServlet是一个抽象类,里面没有抽象方法要求我们必须实现,而是对对我们常用的doGet,doPost方法进行了简单的定义,我们要写自己的Servlet,就要继承HttpServlet类,然后显示的定义和httpServlet中类一直方法签名进行覆盖,在httpServlet类中的service方法会通过判断请求的是get还是post方法,然后调用doget方法还是dopost方法,因此我们也可以直接覆盖HttpServlet中的service方法,这要不管什么什么请求都可以用一个方法来解决掉,忽略了get和post方法。在HttpServlet类的父类是一个GenericServlet,该类就与servlet的初始化关系比较大了,该类中定义init()方法,service方法,destroy()方法,我们在自己的servlet中可以也可以显示覆盖这几个方法,这几个方法被上层调用的顺序是先调用init()方法,然后是调用servcie方法,最后调用destroy()方法,我们可以在这几个方法中写入自己的方法,以决定调用每一个servlet之前或者销毁之前都做什么样的动作)

web容器加载servlet(这里所说的servlet从GenericServlet方法开始说起,之后子类都属于该个servlet),生命周期开始。通过调用servlet的init()方法进行servlet的初始化。通过调用service()方法实现,根据请求的不同调用不同的do***()方法。结束服务,web容器调用servlet的destroy()方法。

Servlet生命周期分为三个阶段:

  1,初始化阶段  调用init()方法

  2,响应客户请求阶段  调用service()方法

3,终止阶段  调用destroy()方法(在这儿是销毁的时候调用destroy方法进行关闭资源等,而不是调用destroy()方法就可以销毁servlet实例,具体怎么销毁,我暂时不知道,有可能该servlet实例长时间不用的话容器直接交给虚拟机销毁)

一个servlet被载入内存以后,除非手动销毁或者关闭服务器,否则是不会被销毁的。


5、如何现实servlet的单线程模式

1:如果是通过jsp实现servlet单线程,则可以通过如下设置

<%@ page isThreadSafe=”false”%>,(记忆:之所以是false,是因为此时不用sychronized修饰每一个servlet对象的service方法了,所以就不是线程安全的)此时该servlet方法继承SimpleThreadModel接口,该接口是一个空接口,实现了该接口的servlet容器能保证只有一个线程调用该servlet的service方法,是保证servlet一次只能有一个请求,但是由于service方法都是线程私有,这样设计的目的是为了只有一个线程调用service方法,则在service中调用的成员属性就不会出现线程被两个线程同时更改的效果,此时达到加syschronized的目的(这样也就不用加锁了,节省了开支),但是在让一个servlet同一时刻只处理一个servlet,这样严重影响了容器的性能,所以会创建很多个继承了SimpleThreadModel的servlet的多个实例,然后存入servlet实例池,才能提高性能,所以这样的servlet并不是单例的。

2:Servlet容器默认是只创建一个servlet对象,:多个线程可以同时调用一个实例的service方法,所以在servlet中一般是不要用到成员属性,(要用的话就要加锁,这种事最安全的,继承SimpleThreadModel被认为是不安全的,因为他不加锁同步)。

3:servlet不是线程安全的;用到成员属性时需要加锁同步。

 

6、页面间对象传递的方法

request,session,application,cookie等

Request,session,application都是在服务器端传值,而cookie是在客户端和服务器端传值。

7、四种会话跟踪技术

1:(保存用户和服务器之间会话状态及信息技术叫会话跟踪技术),我们之前所说的只有三种,隐藏表单,url重定向,cookie,是因为我们用这三种来传输sessionid,他们的目的也是为了跟踪技术,如果没有session,他们也能一定程度上的实现跟踪;所以说跟踪技术其实是四种,cookie,url重定向,隐藏表单,session;


8、Request对象的主要方法:
setAttribute(String name,Object):设置名字为name的request的参数值
getAttribute(String name):返回由name指定的属性值
getAttributeNames():返回request对象所有属性的名字集合,结果是一个枚举的实例
getCookies():返回客户端的所有Cookie对象,结果是一个Cookie数组
getCharacterEncoding():返回请求中的字符编码方式
getContentLength():返回请求的Body的长度
getHeader(String name):获得HTTP协议定义的文件头信息
getHeaders(String name):返回指定名字的request Header的所有值,结果是一个枚举的实例
getHeaderNames():返回所以request Header的名字,结果是一个枚举的实例
getInputStream():返回请求的输入流,用于获得请求中的数据
getMethod():获得客户端向服务器端传送数据的方法
getParameter(String name):获得客户端传送给服务器端的有name指定的参数值
getParameterNames():获得客户端传送给服务器端的所有参数的名字,结果是一个枚举的实例
getParameterValues(String name):获得有name指定的参数的所有值
getProtocol():获取客户端向服务器端传送数据所依据的协议名称
getQueryString():获得查询字符串
getRequestURI():获取发出请求字符串的客户端地址
getRemoteAddr():获取客户端的IP地址
getRemoteHost():获取客户端的名字
getSession([Boolean create]):返回和请求相关Session
getServerName():获取服务器的名字
getServletPath():获取客户端所请求的脚本文件的路径
getServerPort():获取服务器的端口号
removeAttribute(String name):删除请求中的一个属性


9、Servlet执行时一般实现哪几个方法?

public void init(ServletConfig config)
public ServletConfig getServletConfig()
public String getServletInfo()
public void service(ServletRequest request,ServletResponse response)
public void destroy()JSP

 

10、跨域请求

  1:做过跨越多个网站的Ajax开发的朋友都知道,如果在A网站中,我们希望使用Ajax来获得B网站中的特定内容,如果A网站与B网站不在同一个域中,那么就出现了跨域访问问题。(也就是当前网页是www.baidu.com/hello.jsp,现在在hello.jsp文件中通过ajax的url访问www.google.com/ok.jsp文件下的内容,以为baidu和googel不再一个域下面,就会出现跨域的问题)

2:IE对于跨域访问的处理是,弹出警告框,提醒用户。如果用户将该网站纳入可信任网站,或者调低安全级别,那么这个问题IE就不会在提醒你。

FireFox等其它非微软的浏览器遇到跨域访问,则解决方案统一是拒绝访问。

3:跨域访问的解决方式3中

1,<iframe src="http://localhost:8080/CmsUI/2.html" id="i"> 

2.使用javascript的<script src>标签的src,我们最常用了,去下载别的库文件

3.使用jsonp实现跨域

上面类似于http://localhost:2701/home/somejsonp?callback=jsonpcallback告诉服务端回调函数名是上面,上面图中是callback函数处理返回来的json格式的字符串。

4.浏览器的“同源策略(SOP:Same Origin Policy)”。 简而言之,就是浏览器限制脚本程序只能和同协议、同域名、同端口的脚本进行交互,这包括共享和传递变量等。cookie的传递也是遵从同样策略。这就造成一些涉及到多个服务器的应用在整合时一些麻烦。跨域访问的问题造成A站点的Ajax代码无法访问B站点的数据。

5:跨域访问浏览器会阻止;

 

 

11.说出在JSP页面里是怎么分页的?

页面需要保存以下参数:总行数:根据sql语句得到总行数,每页显示行数:设定值,当前页数:请求参数,页面根据当前页数和每页行数计算出当前页第一行行数,定位结果集到此行,对结果集取出每页显示行数的行即可

21:filter的作用是什么?主要实现什么方法?doFilter()方法中参数request,response的全称是什么? 

1:filter使用户可以改变一个request和修改一个response,它并不是一个servlet,不能产生一个response。它能在一个request到达servlet之前预处理request,也可以在离开servlet时处理response.换句话说,filter其实是一个“servlet chaining”(servlet链)。

2:一个filter包括:在servlet调用之前截获在servlet调用之前检查servlet的request根据需要修改request头和request数据,根据需要修改response头和response数据,在servlet调用之后截获。

3:Filter接口定义了三个方法:1, void setFilterConfig(FilterConfig config); //设置filter的配置对象。

2, FilterConfig getFilterConfig();//返回filter的配置对象 

过滤器中的servlet的FilterConfig对象和servlet中ServletConfig对象的作用一样,就是在web配置文件中读取该servlet或者给filter的配置的一些信息,如局部变量名之类的。   

3, doFilter(ServletRequest req,ServletResponse res,FilterChain chain);//执行filer工作。  

request 全称:HttpServletRequest,response全称:HttpServletResponse

4:过滤器链:ApplicationFilterChain这个是不是觉得和流水线的valve的设计模式很像呢,将各个过滤器加入到该链中,然后将该链的对象传给doFilter方法(doFilter(ServletRaquest request, ServletResponse response, FilterChain chain))然后第一个Filter处理器处理完以后,调用过滤器链上的第二个Filter的doFilter方法,当doFilter是最后一个方法的时候,就调用servlet的service方法。因为servlet的init()方法是servlet在加载的时候就执行,而且只执行一次,因此就他肯定是在Filter被调用之前的。

22,我们没有写servlet的构造方法,那么容器是怎么创建servlet的实例呢? 

容器会自动为servlet写一个无参的构造方法,并通过Class.forName(className).newInstance()来创建servlet实例

 

12:单点登录怎么实现?

1:单点登录SSO(Single Sign On)说得简单点就是在一个多系统共存的环境下,用户在一处登录后,就不用在其他系统中登录,也就是用户的一次登录能得到其他所有系统的信任。单点登录在大型网站里使用得非常频繁,例如像阿里巴巴这样的网站,在网站的背后是成百上千的子系统,

方法1:将登录的session持久化到数据库,在访问另一的主机上的应用的时候遍历数据库中的session,通过sessionid找到对应的session来辨别。(我自己的思路)(持久化到缓存服务器中也可以,就是不可靠,这样就不用每访问一次就去数据库中找,)

方法2:使用最简单的方法就是cookie,在第一层登录成功以后,给客户端一个cookie,然后每次访问别的子系统的时候,都带上该cookie信息,就ok啦。(缺点,不安全,不能跨域登录)

方法3:普遍采用的方案,当第一次登录成功以后,服务器产生可以免登录的url传送到客户端,客户端用免登录的url去访问分系统的时候,分系统就验证该客户端的一些信息,验证通过就免登录,不通过就不允许登录,这样的问题也就有,服务端还是得存储相关的临时信任数据,有可能不是session,但是这里就可以用缓存了,一般临时信息不需要持久化。




基于STM32 F4的永磁同步电机无位置传感器控制策略研究内容概要:本文围绕基于STM32 F4的永磁同步电机(PMSM)无位置传感器控制策略展开研究,重点探讨在不依赖物理位置传感器的情况下,如何通过算法实现对电机转子位置和速度的精确估计与控制。文中结合嵌入式开发平台STM32 F4,采用如滑模观测器、扩展卡尔曼滤波或高频注入法等先进观测技术,实现对电机反电动势或磁链的估算,进而完成无传感器矢量控制(FOC)。同时,研究涵盖系统建模、控制算法设计、仿真验证(可能使用Simulink)以及在STM32硬件平台上的代码实现与调试,旨在提高电机控制系统的可靠性、降低成本并增强环境适应性。; 适合人群:具备一定电力电子、自动控制理论基础和嵌入式开发经验的电气工程、自动化及相关专业的研究生、科研人员及从事电机驱动开发的工程师。; 使用场景及目标:①掌握永磁同步电机无位置传感器控制的核心原理与实现方法;②学习如何在STM32平台上进行电机控制算法的移植与优化;③为开发高性能、低成本的电机驱动系统提供技术参考与实践指导。; 阅读建议:建议读者结合文中提到的控制理论、仿真模型与实际代码实现进行系统学习,有条件者应在实验平台上进行验证,重点关注观测器设计、参数整定及系统稳定性分析等关键环节。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值