我是靠谱客的博主 细心板凳,最近开发中收集的这篇文章主要介绍经典面试题Hibernate、Spring和Struts工作原理及使用理由,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

public class ThreadMethod {
	public static boolean f =false;
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		ThreadMethod t = new ThreadMethod();
		A1 a = t.new A1();
		A2 b = t.new A2();
		for(int i=0;i<100;i++){
		Thread t1 = new Thread(a);
		t1.start();
		Thread t2 = new Thread(b);
		t2.start();
		System.out.println("sadff:"+i);
		}
	}
	

	public static void a1(){
		if(!f){
			System.out.println(Thread.currentThread().getName()+"a1:1");
		}
		
		System.out.println(Thread.currentThread().getName()+"a1:2");
	}
	
	public static void a2(){
		System.out.println(Thread.currentThread().getName()+"a2:3");
		f = true;
	}
	
	class A1 implements Runnable{
		public void run(){
			a1();
		}
	}
	
	class A2 implements Runnable{
		public void run(){
			a2();
		}
	}
}

以上程序可能出现的结果为:

任意可能结果都有,因为两个方法都不是sysnchronized.

部分结果为:

写道
Thread-0a1:1
Thread-0a1:2
sadff:0
Thread-1a2:3
Thread-2a1:2
Thread-3a2:3
sadff:1
Thread-4a1:2
sadff:2
Thread-5a2:3
Thread-6a1:2
sadff:3
Thread-7a2:3
sadff:4
Thread-9a2:3
sadff:5
Thread-8a1:2
Thread-10a1:2
Thread-12a1:2
sadff:6
Thread-11a2:3
Thread-14a1:2
sadff:7

 若对方法都加synchronized方法时的代码如下:

public class ThreadSynchornizedMethod {

	public static boolean f = false;

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		ThreadSynchornizedMethod t = new ThreadSynchornizedMethod();
		A1 a = t.new A1();
		A2 b = t.new A2();
		for (int i = 0; i < 100; i++) {
			Thread tt = new Thread(a);
			tt.start();
			tt = new Thread(b);
			tt.start();
			System.out.println("run times:" + i);
		}
	}

	public synchronized static  void a1() {
		if (!f) {
			System.out.println(Thread.currentThread().getName() + " a1:1");
		}

		System.out.println(Thread.currentThread().getName() + " a1:2");
	}

	public synchronized static void a2() {
		System.out.println(Thread.currentThread().getName() + " a2:3");
		f = true;
	}

	class A1 implements Runnable {
		public void run() {
			a1();
		}
	}

	class A2 implements Runnable {
		public void run() {
			a2();
		}
	}
}

 

 运行结果也是任意的情况,如下一些例子:

写道
run times:0
Thread-1 a2:3
run times:1
Thread-2 a1:2
run times:2
run times:3
Thread-3 a2:3
run times:4
run times:5
Thread-7 a2:3
run times:6
Thread-11 a2:3
Thread-10 a1:2
run times:7
Thread-6 a1:2
run times:8
Thread-14 a1:2
run times:9
run times:10
Thread-19 a2:3
run times:11
Thread-23 a2:3
run times:12
run times:13
Thread-5 a2:3
Thread-13 a2:3
Thread-26 a1:2
Thread-27 a2:3
Thread-9 a2:3
run times:14
Thread-17 a2:3
run times:15
Thread-22 a1:2
Thread-31 a2:3
Thread-18 a1:2
Thread-15 a2:3
Thread-30 a1:2
run times:16

 因为synchronized只是对同一对象,同一方法时才有作用,若不同对象对同一方法没有任何作用。

 

下列代码执行的结果:

public class ThreadMethod extends Thread {
	public static boolean f = false;

	public static void main(String[] args) {
		// TODO Auto-generated method stub
			ThreadMethod t = new ThreadMethod();
			t.start();
			//t.a2();
			ThreadMethod t2 = new ThreadMethod();
			t2.a2();
	}

	public void run(){
		a1();
	}
	public  static void a1() {
		if (!f) {
			System.out.println(Thread.currentThread().getName() + " a1:1");
		}

		System.out.println(Thread.currentThread().getName() + " a1:2");
		System.out.println("------------------------");
	}

	public static void a2() {
		System.out.println(Thread.currentThread().getName() + " a2:3");
		f = true;
	}
}

 执行的可能结果:

 

Thread-0 a1:1
main a2:3
Thread-0 a1:2
------------------------

main a2:3
Thread-0 a1:1
Thread-0 a1:2
------------------------

 若a1前加上synchronized,也会得到同上结果。

 

若用同一thread进行调用:

public class ThreadMethod extends Thread {
	public static boolean f = false;

	public static void main(String[] args) {
		// TODO Auto-generated method stub
			ThreadMethod t = new ThreadMethod();
			t.start();
			t.a2();
			//ThreadMethod t2 = new ThreadMethod();
			//t2.a2();
	}

	public void run(){
		a1();
	}
	public   static void a1() {
		if (!f) {
			System.out.println(Thread.currentThread().getName() + " a1:1");
		}

		System.out.println(Thread.currentThread().getName() + " a1:2");
		System.out.println("------------------------");
	}

	public static void a2() {
		System.out.println(Thread.currentThread().getName() + " a2:3");
		f = true;
	}
}

 运行结果都是:

写道
main a2:3
Thread-0 a1:1
Thread-0 a1:2
------------------------

 以上例子都是执行一次 可能出现的结果,若是多次,结果不同。因为a1是synchronized或不是synchronized的,如果a1()和a2()都是synchronized的,因为main线程的优先级高,运行结果只有

写道
main a2:3
Thread-0 a1:2
------------------------
 

 

JSP和Servlet的异同?

JSP Servlet 技术的扩展,本质上是 Servlet 的简易方式 ,更强调应用的外表表达。 JSP 编译后是 " servlet" Servlet JSP 最主要的不同点在于, Servlet 的应用逻辑是在 Java 文件中 ,并且完全从表示层中的 HTML 里分离开来 。而 JSP 的情况是 Java HTML 可以组合成一个扩展名为 .jsp 的文件。 JSP 侧重于视图, Servlet 主要用于控制逻辑。

 

Servlet中没有内置对象,原来Jsp中的内置对象都是必须通过HttpServletRequest对象,或由
HttpServletResponse对象生成。
* 对于静态的HTML标签,Servlet都必须使用页面输出流诼行输出。
总之,Jsp是Servlet的一种简化,使用Jsp只需要完成程序员需要输出到客户端的内容,至于Jsp中的
Java脚本如何镶嵌到一个类中,由Jsp容器完成。而Servlet则是个完整的Java类,这个类的Service
方法用于生成对客户端的响应。

Servlet 以及 Jsp 的生命周期

Servlet init() 方法初始化, service() 方法进行 Web 服务, destroy() 方法进行销毁,从生到灭都由容器来掌握。 Servlet 生命周期方法都是回调方法。回调这个概念简单来说就是把自己注入另外一个类中,由它来调用你的方法,所谓的另外一个类就是 Web 容器,它只认识接口和接口的方法,注入进来的是怎样的对象不管,它只会根据所需调用这个对象在接口定义存在的那些方法。由容器来调用的 Servlet 对象的初始化、服务和销毁方法,所以叫做回调。

那么 Jsp 呢?本事上是 Servlet ,还是有些区别的,它的生命周期是这样的: a) 一个客户端的 Request 到达服务器; b) 判断是否第一次调用 ,是的话编译 Jsp Servlet c) 否的话再判断此 Jsp 是否有改变,是的话也重新编译 Jsp Servlet d) 已经编译最近版本的 Servlet 装载所需的其他 Class e ) 发布 Servlet ,即调用它的 Service() 方法。

所以 Jsp 号称的是第一次 Load 缓慢,以后都会很快的运行。这里稍微解释一下 Http 的无状态性,因为发现很多人误解, Http 的无状态性是指每次一张页面显示出来了,与服务器的连接其实就已经断开了,当再次有提交动作的时候,才会再次与服务器进行连接请求提供服务。

 

Servlet 的生命周期

  servlet 有良好的生存期的定义,包括加载和实例化、初始化、处理请求以及服务结束 。这个生存期由 javax.servlet.Servlet 接口的 init,service 和 destroy 方法表达。 Servlet 被服务器实例化 后,容器 运行其 init 方法,请求到达时运行其 service 方法 , service 方法自动派遣运行 与请求对应的 doXXX 方法( doGet , doPost )等,无论哪种方法,都会调用 process 方法,用于根据用户访问的上下文路径来选择处理的应用(选择处理器来处理用户需求)。当服务器决定将实例销毁的时候调用其 destroy 方法。
与 cgi 的区别在于 servlet 处于服务器进程中,它通过 多线程方式 运行其 service 方法 ,一个实例可以服务于多个请求,并且其实例一般不会销毁 ,而 CGI 对每个请求都产生新的进程服务完成后就销毁 ,所以效率上低于 servlet

 

内存泄露:

内存泄露是指程序 中间动态分配了内存,但是在程序结束时没有释放这部分内存,从而造成那一部分内存不可用的情况,重起计算机 可以解决,但是也有可能再次发生内存泄露,内存泄露和硬件 没有关系,它是由软件 引起的。

 

 java会造成内存泄漏吗?并简单说明原因?如何解决?

会。 java 导致内存泄露的原因很明确:长生命周期的对象持有短生命周期对象的引用就很可能发生内存泄露,尽管短生命周期对象已经不再需要,但是因为长生命周期对象持有它的引用而导致不能被回收, 这就是 java 中内存泄露的发生场景。

1. 集合类,集合类仅仅有添加元素的方法,而没有相应的删除机制,导致内存被占用。这一点其实也不明确,这个集合类如果仅仅是局部变量,根本不会造成内存泄露,在方法栈退出后就没有引用了会被 jvm 正常回收。而如果这个集合类是全局性的变量(比如类中的静态属性,全局性的 map 等即有静态引用或 final 一直指向它),那么没有相应的删除机制,很可能导致集合所占用的内存只增不减,因此提供这样的删除机制或者定期清除策略非常必要。

2. 单例模式。不正确使用单例模式是引起内存泄露的一个常见问题,单例对象在被初始化后将在 JVM 的整个生命周期中存在(以静态变量的方式),如果单例对象持有外部对象的引用,那么这个外部对象将不能被 jvm 正常回收,导致内存泄露 ,考虑下面的例子:

   class A{

   public A(){

   B.getInstance().setA(this);

   }

   ....

   }

   //B 类采用单例模式

   class B{

   private A a;

   private static B instance=new B();

   public B(){}

   public static B getInstance(){

   return instance;

   }

   public void setA(A a){

   this.a=a;

   }

   //getter...

   }

  显然 B 采用 singleton 模式,他持有一个 A 对象的引用,而这个 A 类的对象将不能被回收。

上面所讲的这些也启发我们如何去查找内存泄露问题,在代码复审的时候关注长生命周期对象:全局性的集合、单例模式的使用、类的 static 变量等等 。 Java 的实现过程中,也要考虑其对象释放,最好的方法是在不使用某对象时,显式地将此对象赋空。最好遵循谁创建谁释放的原则。

 

HashMap里的key如何获得:

Map.Entry 的 Entry 内部类,它包含了hash,value,key 和next 这四个属性,很重要。put的源码如下 for (Entry e = table[i]; e != null; e = e.next) {

  public Object put(Object key, Object value) {

  Object k = maskNull(key);

  这个就是判断键值是否为空,并不很深奥,其实如果为空,它会返回一个static Object 作为键值,这就是为什么HashMap允许空键值的原因。

  int hash = hash(k);

  int i = indexFor(hash, table.length);

  这连续的两步就是 HashMap 最牛的地方!其中 hash 就是通过 key 这个Object的 hashcode 进行 hash,然后通过 indexFor 获得在Object table的索引值。

put 其实是一个有返回的方法,它会把相同键值的 put 覆盖掉并返回旧的值!如下方法彻底说明了 HashMap 的结构,其实就是一个表加上在相应位置的Entry的链表:

 

  if (e.hash == hash && eq(k, e.key)) {

  Object oldvalue = e.value;

  e.value = value; //把新的值赋予给对应键值。

  e.recordAccess(this); //空方法,留待实现

  return oldvalue; //返回相同键值的对应的旧的值。

  }

  }

  modCount++; //结构性更改的次数

  addEntry(hash, k, value, i); //添加新元素,关键所在!

  return null; //没有相同的键值返回

  } 

我们把关键的方法拿出来分析:

  void addEntry(int hash, Object key, Object value, int bucketIndex) {

  table[bucketIndex] = new Entry(hash, key, value, table[bucketIndex]);

  因为 hash 的算法有可能令不同的键值有相同的hash码并有相同的table索引,如:key=“33”和key=Object g的hash都是-8901334,那它经过indexfor之后的索引一定都为i,这样在new的时候这个Entry的next就会指向这个原本的table[i],再有下一个也如此,形成一个链表,和put的循环对定e.next获得旧的值。到这里,HashMap的结构,大家也十分明白了吧?

  if (size++ >= threshold) //这个threshold就是能实际容纳的量

  resize(2 * table.length); //超出这个容量就会将Object table重构 

 

Map里有个方法:entrySet() 是用来返回Map里面所有键的Set视图的

Map map = new HashMap(); for (Iterator iter = map.entrySet().iterator(); iter.hasNext();) { Map.Entry entry = (Map.Entry)iter.next(); String key = (String)entry.getKey(); String value = (String)map.get(key); }

 

 

 谈一下你对ajax的理解?

ajax全称是Asynchonous Javascript and XML,由以下技术来组成:

1.CSS和XHTML来进行展示

2.使用DOM模型来交互和动态显示

3.使用XMLHttpRequest来和服务器进行异步通信

4.使用javascript来绑定和调用

 

AJAX的核心原理是异步载入,通过javascript的XMLHTTPRequest对象来向服务器发送异步请求来获得数据,然后通过javascript来操作DOM来更新页面。

中心实现方式就是通过JavaScript的xmlhttprequest对象向服务器提交请求取得数据不刷新页面的情况下,完成数据交互。

AJAX,从底层来讲,无外乎两种实现机制:XMLHTTP以及IFRAME
xmlHttpRequest对象属性。

属性
描述
onreadystatechange
每次状态改变所触发事件的事件处理程序

readyState
对象状态值:

0 = 未初始化(uninitialized) 对象已建立,但尚未初始化(未调用open()方法)
1 = 正在加载(loading) (已初始化)对象已建立,但未调用send()方法
2 = 加载完毕(loaded) (发送数据)send方法已调用,但当前的状态和http头未知
3 = 交互(interactive) (数据发送中)已接收部分数据,因为响应及HTTP头不全,这时通过responseText 和

                                      responseXML 进行取得部分数据会出现错误
4 = 完成(complete)    完成,接收数据完毕,此时可通过responseText 和responseXML接收完整的回应数据


responseText
从服务器进程返回的数据的字符串形式

responseXML
从服务器进程返回的DOM兼容的文档数据对象

status
从服务器返回的数字代码,比如404(未找到)或200(就绪)

statusText
伴随状态码的字符串信息

现在processVoteResponse()函数开始显示出其意义了。它首先检查XmlHttpRequest的整体状态以保证它已经完成(readyStatus == 4),然后根据服务器的设定询问请求状态。如果一切正常(status == 200),就使用innerHTML属性重写DOM的“votes”节点的内容。

 

 

但是各浏览器之间存在差异,所以创建一个XMLHttpRequest对象需要不同的方法,这个主要体现的IE和其它浏览器之间,下面是一个比较标准的创建XMLHttpRequest对象。

var xmlhttp=false;

function CreateXmlHttp(){

 //非IE浏览器创建XMLHttpRequest对象

   if(window.XmlHttpRequest){
    xmlhttp = new XmlHttpRequest();
   }
    //IE浏览器创建XMLHttpRequest对象
   if(window.ActiveXObject){
    try{
     xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
    }catche(e){
     try{
      xmlhttp = new ActiveXObject("msxml2.XMLHTTP");
     }catch(ex){}
    }
   }
}


function Ustbwuyi(){
 var data = document.getElementById("username").value;
 CreateXmlHttp();
 if(!xmlhttp){
  alert("create xmlhttp object exception!");
  return false;
 }
 xmlhttp.open("POST",url,false);//false is synchronized, true is ansyschronized
 xmlhttp.onreadystatechange=function(){
  if(xmlhttp.readyState==4){
   document.getElementById("user1").innerHTML="loading data....";
   if(xmlhttp.status==200){
    document.write(xmlhttp.responseText);
   }
  }
 };
 xmlhttp.send();
}

AJAX的优点:

1.最大一点是无页面刷新,在页面内与服务器进行通信,给用户体验非常好

2.使用异步方式与服务器通信,不需要打断用户的操作,具有更加迅速的响应能力。

3.可以把以前一些服务器负担的工作转嫁到客户端,利用客户端闲置的能力来处理,减轻服务器和带宽的负担,节约空间和宽带租用成本,并且减轻服务器的负担,ajax的原则是“按需取数据”,可以最大程度减少冗余请求,和响应对服务器造成的负担。

4.基于标准化的并被广泛支持的技术,不需要下载插件或者小程序

 

AJAX的缺点:

1.AJAX取消了back按钮,即对浏览器后退机制的破坏。后退按钮是一个标准的web站点的重要功能,但是它没法和js进行很好的合作。gmail实现了后退的功能,但并不是改变ajax的机制,只是采用一个比较笨而有效的方法,即用户单击后退按钮历史记录,通过创建或使用一个隐藏的IFRAME来重现页面上的变更。

2.安全问题,AJAX技术就如同对企业数据建立了一个直接通道。使得开发者在不经意间会暴露比以前更多的数据和服务器逻辑。ajax的逻辑可以对客户端的安全扫描技术隐藏起来,允许黑客从无端服务器上建立新的攻击。还有ajax也难以避免一些已知的安全弱点,诸如跨站点脚本攻击,SQL注入攻击和基于credentials的安全漏洞。

3.对搜索引擎的支持比较弱

4.破坏了程序的异常机制。至少从目前来看,像ajax.dll,ajaxpro.dll,这些ajax框架是会破坏程序的异常机制的。

5.违背了url和资源定位的初衷。如给一个url地址,如果采用了ajax技术,也许你在该url地址下面看到的和在这个url地址下看到的内容是不同的。这个和资源定位的初衷是背离的。

6.一些手机设备如手机,如手机PDA等还不很好的支持ajax

 


浏览器兼容性以及解决办法?

 

 

二、 名词解释

  1.UED (用户体验设计) 2.UI(用户界面) 3.PV(页面浏览量) 4.CSS(层叠样式表) 5.POI(兴趣点)

UED = user experience design,用户体验设计 :是一种纯主观的在用户使用一个产品(服务)的过程中建立起来的心理感受.

UI即User Interface(用户界面)的简称。UI设计则是指对软件的人机交互 、操作逻辑、界面美观的整体设计。好的UI设计不仅是让软件变得有个性有品味,还要让软件的操作变得舒适、简单、自由、充分体现软件的定位和特点

PV(page view),即页面浏览量,或点击量;用户每1次对网站中的每个网页访问均被记录1次。用户对同一页面的多次访问,访问量累计。

uv(unique visitor),指访问某个站点或点击某条新闻的不同IP地址的人数。

.CSS(层叠样式表) :层叠样式表 (英语 :Cascading Style Sheets ,简写CSS ),又称串样式列表 ,由W3C 定义和维护的标准,一种用来为结构化文档(如HTML 文档或XML 应用)添加样式(字体、间距和颜色等)的计算机语言 .

CSS reference:http://zh.wikipedia.org/wiki/%E5%B1%82%E5%8F%A0%E6%A0%B7%E5%BC%8F%E8%A1%A8#.E5.9F.BA.E6.9C.AC.E9.81.B8.E6.93.87.E5.99.A8

 

POI(兴趣点):POI是“Point of Interest”的缩写,每个POI包含四方面信息,名称、类别、经度、纬度。

 

几道微软编程面试题

 

(1)
一个整数数列,元素取值可能是0~65535中的任意一个数,相同数值不会重复出现。0是例外,可以反复出现。
请设计一个算法,当你从该数列中随意选取5个数值,判断这5个数值是否连续相邻。
注意:
- 5个数值允许是乱序的。比如: 8 7 5 0 6
- 0可以通配任意数值。比如:8 7 5 0 6 中的0可以通配成9或者4
- 0可以多次出现。
- 复杂度如果是O(n2)则不得分。
分析:

功能 :判断随意取的数值是否连续相邻,相邻返回1,否则返回0
参数array :存储随意取的数
参数size :随意取的数的个数

 
public int is_progression(int[] array, int size) {
		/* 最大值的索引和最小值的索引 */
		int maxIndex = 0;
		int minIndex = 0;

		int i = 0;
		/* 试图找到第一个不为0的元素,初始化 maxIndex和minIndex */
		for (; i < size; ++i) {
			if (array[i] != 0) {
				maxIndex = minIndex = i;
				break;
			}
		}

		/* 试图确定随意取的数中的最大值和最小值 */
		for (; i < size; ++i) {
			if (array[i] > array[maxIndex]) {
				maxIndex = i;
			} else if (array[i] < array[minIndex]) {
				minIndex = i;
			}
		}

		/* 如果最大值和最小值的差小于总个数,则可以判定它们是连续相邻的 */
		return (array[maxIndex] - array[minIndex]) < size ? 1 : 0;
	}
 


(2)
设计一个算法,找出二叉树上任意两个结点的最近共同父结点。
复杂度如果是 O(n2)则不得分。

分析:

typedef int datatype typedef struct tree_node{ datatype data; struct tree_node *lchild; struct teee_node *rchild; }treenode, *lptreenode; treenode* find_parentnode(tree, node1, node2) { initstack(s); push(s, tree); finded = 0; parentnode = tree; while ( !stackempty(s) ) { while ( gettop(s, p) && p ) { if ( p == node1 ) { finded += 1; } if ( p == node2 ) { finded += 1; } if ( finded == 0 ) { parentnode = p; break; } else if ( finded == 2 ) { break; } push(s, p->lchild); } if ( finded == 3 ) { break; } else if ( parentnode != NULL ) { while ( pop(s, p) && p == parentnode ) { break; } push(s, p->rchild); } } return finded == 3 ? parentnode : NULL; }

 

 



(3)
一棵排序二叉树,令 f=(最大值+最小值)/2,设计一个算法,找出距离f值最近、大于f值的结点。
复杂度如果是O(n2)则不得分。

treenode* find_parentnode(tree, f) { tempnode = tree; while ( tempnode ) { if ( tempnode->data > f ) { if ( NULL == tree->lchild || tree->lchild->data <= f ) { break; } tempnode = tree->lchild; } else { if ( NULL == (tempnode = tempnode->right) || tempnode->data > f ) { break; } } } return tempnode; }

 

 


(4)
一个整数数列,元素取值可能是1~N(N是一个较大的正整数)中的任意一个数,相同数值不会重复出现。设计一个算法,找出数列中符合条件的数对的个数,满足数对中两数的和等于N+1。
复杂度最好是O(n),如果是O(n2)则不得分。

分析:

建一个大小为(N+1)/2的哈希表,哈希表初始化为0.
遍历数组:
(1).当arr[i] <=(N+1)/2时,将哈希表中第i位置1.
(2).当arr[i]>(N+1)/2时,将arr[i]散列到第(N+1-i) 位,如果哈希表中此位为1,则打印i与N+1-i,并将哈希表此位值置0.
复杂度O(n).

public class PairTest {
	public static void main(String[] args) {
		int[] array = { 0, 1, 3, 2, 5, 7 };
		int n = findPair(array, 6, 7);
		System.out.println(n);
	}

	private static int findPair(int[] array, int size, int N) {
		int temp_array_size = N / 2 + 1;
		int temp_index = 0;
		int count = 0;
		int[] temp_array = new int[temp_array_size];
		for (int i = 0; i < size; ++i) {
			temp_index = array[i] < temp_array_size ? array[i] : (N + 1 - array[i]);
			if (temp_array[temp_index] == 1) {
				++count;
			} else {
				temp_array[temp_index] = 1;
			}
		}
		return count;
	}
}
 
 

 

Struts的处理机制:

struts利用控制反转的机制,

struts1.x

1.ActionServlet核心控制器会拦截所有*.do的请求
2.从struts-config.xml中找到用户请求的Action
3. 通过struts-config.xml中的配置再去找这个Action对应的ActionForm,并实例化
4.把用户填写的数据自动填充到 ActionForm中(调用ActionForm中的setXX()方法填充)
5.同时把ActionForm放入到指定的范围中(request,session)
6.然后把请求转发给Action

7.Action获取ActionForm中的值然后调用业务逻辑层实现功能
8.在通过ActionMapping查找Actionforward实现转发;

 

struts2的工作原理

1、struts2核心技术是拦截器

2、核心控制器--FilterDispatcher,该控制器运行在WEB容器中负责拦截用户请求,如果用户请求以*.action形式出现,那么核心控制器就将用户请求拦截并处理,这其中显然运用了控制反转的技术,真正执行操作的还是用户写的业务逻辑层代码。

struts2 主要是前台接对象 通过struts2的Action类跳转 把对象处理传给DAO类 struts2的Action类再把传回来的对象或对相集合再传回前台页

 

struts1用actinServlet中央控制器拦截请求,Struts2用中央过滤器拦截请求;Struts2是从Webwork框架继承过来的.

struts2工作流程:

1 客户端初始化一个指向Servlet容器(例如Tomcat)的请求
2 这个请求经过一系列的过滤器(Filter)(这些过滤器中有一个叫做ActionContextCleanUp的可选过滤器,这个过滤器对于 Struts2和其他框架的集成很有帮助,例如:SiteMesh Plugin)
3 接着FilterDispatcher被调用,FilterDispatcher询问ActionMapper来决定这个请是否需要调用某个Action
4 如果ActionMapper决定需要调用某个Action,FilterDispatcher把请求的处理交给ActionProxy
5 ActionProxy通过Configuration Manager询问框架的配置文件,找到需要调用的Action类
6 ActionProxy创建一个ActionInvocation的实例。
7 ActionInvocation实例使用命名模式来调用,在调用Action的过程前后,涉及到相关拦截器(Intercepter)的调用。
8 一旦Action执行完毕,ActionInvocation负责根据struts.xml中的配置找到对应的返回结果。返回结果通常是(但不总是,也可能是另外的一个Action链)一个需要被表示的JSP或者FreeMarker的模版。在表示的过程中可以使用Struts2 框架中继承的标签。在这个过程中需要涉及到ActionMapper

 

 

Hibernate、Spring和Struts工作原理及使用理由

 1.读取并解析配置文件

  2.读取并解析映射信息,创建SessionFactory

  3.打开Sesssion

  4.创建事务Transation

  5.持久化操作

  6.提交事务

  7.关闭Session

  8.关闭SesstionFactory

  为什么要用:

  1. 对JDBC访问数据库的代码做了封装,大大简化了数据访问层繁琐的重复性代码。

  2. Hibernate是一个基于JDBC的主流持久化框架,是一个优秀的ORM实现。他很大程度的简化DAO层的编码工作

  3. hibernate使用Java反射机制,而不是字节码增强程序来实现透明性。

  4.hibernate的性能非常好,因为它是个轻量级框架。映射的灵活性很出色。它支持各种关系数据库,从一对一到多对多的各种复杂关系。

  2. Hibernate是如何延迟加载?

  1. Hibernate2延迟加载实现:a)实体对象 b)集合(Collection)

  2. Hibernate3 提供了属性的延迟加载功能

  当Hibernate在查询数据的时候,数据并没有存在与内存中,当程序真正对数据的操作时,对象才存在与内存中,就实现了延迟加载,他节省了服务器的内存开销,从而提高了服务器的性能。

  3.Hibernate中怎样实现类之间的关系?(如:一对多、多对多的关系)

  类与类之间的关系主要体现在表与表之间的关系进行操作,它们都市对对象进行操作,我们程序中把所有的表与类都映射在一起,它们通过配置文件中的 many-to-one、one-to-many、many-to-many、

  4. 说下Hibernate的缓存机制

  1. 内部缓存存在Hibernate中又叫一级缓存,属于应用事物级缓存

  2. 二级缓存:

  a) 应用及缓存

  b) 分布式缓存

  条件:数据不会被第三方修改、数据大小在可接受范围、数据更新频率低、同一数据被系统频繁使用、非 关键数据

  c) 第三方缓存的实现

  5. Hibernate的查询方式

  Sql、Criteria,t comptosition

  Hql:

  1、 属性查询

  2、 参数查询、命名参数查询

  3、 关联查询

  4、 分页查询

   5、 统计函数

  6. 如何优化Hibernate?

  1.使用双向一对多关联,不使用单向一对多

  2.灵活使用单向一对多关联

  3.不用一对一,用多对一取代

  4.配置对象缓存,不使用集合缓存

  5.一对多集合使用Bag,多对多集合使用Set

  6. 继承类使用显式多态

  7. 表字段要少,表关联不要怕多,有二级缓存撑腰

  7. Struts工作机制?为什么要使用Struts?

  工作机制:

  Struts的工作流程:

  在web应用启动时就会加载初始化 ActionServlet,ActionServlet从

  struts-config.xml文件中读取配置信息,把它们存放到各种配置对象

  当ActionServlet接收到一个客户请求时,将执行如下流程.

  -(1)检索和用户请求匹配的 ActionMapping实例,如果不存在,就返回请求路径无效信息;

  -(2)如果ActionForm实例不存在,就创建一个 ActionForm对象,把客户提交的表单数据保存到ActionForm对象中;

  -(3)根据配置信息决定是否需要表单验证.如果需要验证,就调用ActionForm的validate()方法;

  -(4)如果ActionForm的validate()方法返回 null或返回一个不包含ActionMessage的ActuibErrors对象,就表示表单验证成功;

   -(5)ActionServlet根据ActionMapping所包含的映射信息决定将请求转发给哪个Action,如果相应的Action实例不存在,就先创建这个实例,然后调用Action的execute()方法;

  -(6)Action的execute()方法返回一个 ActionForward对象,ActionServlet在把客户请求转发给ActionForward对象指向的JSP组件;

   -(7)ActionForward对象指向JSP组件生成动态网页,返回给客户;

  为什么要用:

  JSP、 Servlet、JavaBean技术的出现给我们构建强大的企业应用系统提供了可能。但用这些技术构建的系统非常的繁乱,所以在此之上,我们需要一个规则、一个把这些技术组织起来的规则,这就是框架,Struts便应运而生。

  基于Struts开发的应用由3类组件构成:控制器组件、模型组件、视图组件

  8. Struts的validate框架是如何验证的?

  在 struts配置文件中配置具体的错误提示,再在FormBean中的validate()方法具体调用。

  9. 说下Struts的设计模式

  MVC模式:web应用程序启动时就会加载并初始化ActionServler。用户提交表单时,一个配置好的ActionForm对象被创建,并被填入表单相应的数据,ActionServler根据Struts-config.xml文件配置好的设置决定是否需要表单验证,如果需要就调用ActionForm的Validate()验证后选择将请求发送到哪个Action,如果 Action不存在,ActionServlet会先创建这个对象,然后调用Action的execute()方法。Execute()从 ActionForm对象中获取数据,完成业务逻辑,返回一个ActionForward对象,ActionServlet再把客户请求转发给 ActionForward对象指定的jsp组件,ActionForward对象指定的jsp生成动态的网页,返回给客户。

  10. spring工作机制及为什么要用?

  1.springmvc请所有的请求都提交给 DispatcherServlet,它会委托应用系统的其他模块负责负责对请求进行真正的处理工作。

   2.DispatcherServlet查询一个或多个HandlerMapping,找到处理请求的Controller.

   3.DispatcherServlet请请求提交到目标Controller

  4.Controller进行业务逻辑处理后,会返回一个ModelAndView

  5.Dispathcher查询一个或多个ViewResolver视图解析器,找到 ModelAndView对象指定的视图对象

  6.视图对象负责渲染返回给客户端。

  为什么用:

   AOP 让开发人员可以创建非行为性的关注点,称为横切关注点,并将它们插入到应用程序代码中。使用 AOP后,公共服务(比如日志、持久性、事务等)就可以分解成方面并应用到域对象上,同时不会增加域对象的对象模型的复杂性。

  IOC 允许创建一个可以构造对象的应用环境,然后向这些对象传递它们的协作对象。正如单词 倒置 所表明的,IOC 就像反过来的JNDI。没有使用一堆抽象工厂、服务定位器、单元素(singleton)和直接构造(straightconstruction),每一个对象都是用其协作对象构造的。因此是由容器管理协作对象(collaborator)。

  Spring即使一个AOP框架,也是一 IOC容器。 Spring 最好的地方是它有助于您替换对象。有了Spring,只要用JavaBean属性和配置文件加入依赖性(协作对象)。然后可以很容易地在需要时替换具有类似接口的协作对象。

  Spring 框架是一个分层架构,由 7 个定义良好的模块组成。Spring模块构建在核心容器之上,核心容器定义了创建、配置和管理bean 的方式,如图 1 所示。

  组成 Spring 框架的每个模块(或组件)都可以单独存在,或者与其他一个或多个模块联合实现。每个模块的功能如下:

  核心容器:核心容器提供 Spring框架的基本功能。核心容器的主要组件是BeanFactory,它是工厂模式的实现。BeanFactory使用控制反转(IOC)模式将应用程序的配置和依赖性规范与实际的应用程序代码分开。

  Spring 上下文:Spring 上下文是一个配置文件,向 Spring框架提供上下文信息。Spring上下文包括企业服务,例如 JNDI、EJB、电子邮件、国际化、校验和调度功能。

   Spring AOP:通过配置管理特性,Spring AOP 模块直接将面向方面的编程功能集成到了Spring框架中。所以,可以很容易地使 Spring 框架管理的任何对象支持 AOP。Spring AOP 模块为基于Spring的应用程序中的对象提供了事务管理服务。通过使用 Spring AOP,不用依赖EJB组件,就可以将声明性事务管理集成到应用程序中。

  Spring DAO:JDBCDAO抽象层提供了有意义的异常层次结构,可用该结构来管理异常处理和不同数据库供应商抛出的错误消息。异常层次结构简化了错误处理,并且极大地降低了需要编写的异常代码数量(例如打开和关闭连接)。SpringDAO的面向 JDBC 的异常遵从通用的 DAO 异常层次结构。

  Spring ORM:Spring 框架插入了若干个 ORM 框架,从而提供了 ORM的对象关系工具,其中包括JDO、Hibernate 和 iBatis SQL Map。所有这些都遵从 Spring 的通用事务和DAO异常层次结构。

  Spring Web 模块:Web 上下文模块建立在应用程序上下文模块之上,为基于Web的应用程序提供了上下文。所以,Spring 框架支持与 Jakarta Struts的集成。Web模块还简化了处理多部分请求以及将请求参数绑定到域对象的工作。

  Spring MVC 框架:MVC 框架是一个全功能的构建 Web 应用程序的 MVC实现。通过策略接口,MVC框架变成为高度可配置的,MVC 容纳了大量视图技术,其中包括JSP、Velocity、Tiles、iText 和 POI。

  Spring 框架的功能可以用在任何 J2EE服务器中,大多数功能也适用于不受管理的环境。Spring的核心要点是:支持不绑定到特定 J2EE服务的可重用业务和数据访问对象。毫无疑问,这样的对象可以在不同 J2EE 环境 (Web或EJB)、独立应用程序、测试环境之间重用。

  共2页: 1 [2]

  内容导航

  第 1 页:Hibernate工作原理及用的理由(1) 第 2 页:Hibernate工作原理及用的理由(2)

  <!-- page--> IOC 和 AOP

  控制反转模式(也称作依赖性介入)的基本概念是:不创建对象,但是描述创建它们的方式。在代码中不直接与对象和服务连接,但在配置文件中描述哪一个组件需要哪一项服务。容器(在Spring框架中是 IOC 容器) 负责将这些联系在一起。

  在典型的 IOC 场景中,容器创建了所有对象,并设置必要的属性将它们连接在一起,决定什么时间调用方法。下表列出了IOC的一个实现模式。

   Spring 框架的 IOC 容器采用类型 2 和类型3 实现。

  面向方面的编程

  面向方面的编程

  ,即AOP,是一种编程技术,它允许程序员对横切关注点或横切典型的职责分界线的行为(例如日志和事务管理)进行模块化。AOP的核心构造是方面,它将那些影响多个类的行为封装到可重用的模块中。

  AOP 和IOC是补充性的技术,它们都运用模块化方式解决企业应用程序开发中的复杂问题。在典型的面向对象开发方式中,可能要将日志记录语句放在所有方法和 Java类中才能实现日志功能。在 AOP方式中,可以反过来将日志服务模块化,并以声明的方式将它们应用到需要日志的组件上。当然,优势就是Java类不需要知道日志服务的存在,也不需要考虑相关的代码。所以,用 Spring AOP 编写的应用程序代码是松散耦合的。

  AOP 的功能完全集成到了 Spring 事务管理、日志和其他各种特性的上下文中。

  IOC 容器

  Spring 设计的核心是 org.springframework.beans 包,它的设计目标是与JavaBean组件一起使用。这个包通常不是由用户直接使用,而是由服务器将其用作其他多数功能的底层中介。下一个最高级抽象是 BeanFactory接口,它是工厂设计模式的实现,允许通过名称创建和检索对象。BeanFactory也可以管理对象之间的关系。

  BeanFactory 支持两个对象模型。

  单态模型提供了具有特定名称的对象的共享实例,可以在查询时对其进行检索。 Singleton是默认的也是最常用的对象模型。对于无状态服务对象很理想。

  原型 模型确保每次检索都会创建单独的对象。在每个用户都需要自己的对象时,原型模型最适合。

  bean 工厂的概念是 Spring 作为 IOC容器的基础。IOC将处理事情的责任从应用程序代码转移到框架。正如我将在下一个示例中演示的那样,Spring 框架使用JavaBean属性和配置数据来指出必须设置的依赖关系。

  BeanFactory 接口

  因为org.springframework.beans.factory.BeanFactory是一个简单接口,所以可以针对各种底层存储方法实现。最常用的BeanFactory 定义是 XmlBeanFactory,它根据XML 文件中的定义装入 bean,如清单 1 所示。

  清单 1. XmlBeanFactory

  BeanFactory factory = newXMLBeanFactory(newFileInputSteam("mybean.xml"));

  在 XML 文件中定义的 Bean 是被消极加载的,这意味在需要 bean 之前,bean本身不会被初始化。要从BeanFactory 检索 bean,只需调用 getBean() 方法,传入将要检索的 bean的名称即可,如清单 2所示。

  清单 2. getBean()

   MyBean mybean = (MyBean) factory.getBean("mybean");

  每个 bean 的定义都可以是 POJO (用类名和 JavaBean 初始化属性定义)或FactoryBean。FactoryBean 接口为使用 Spring 框架构建的应用程序添加了一个间接的级别。

  IOC 示例

  理解控制反转最简单的方式就是看它的实际应用。在对由三部分组成的 Spring 系列 的第1部分进行总结时,我使用了一个示例,演示了如何通过 Spring IOC 容器注入应用程序的依赖关系(而不是将它们构建进来)。

  我用开启在线信用帐户的用例作为起点。对于该实现,开启信用帐户要求用户与以下服务进行交互:

  信用级别评定服务,查询用户的信用历史信息。

  远程信息链接服务,插入客户信息,将客户信息与信用卡和银行信息连接起来,以进行自动借记(如果需要的话)。

  电子邮件服务,向用户发送有关信用卡状态的电子邮件。

  三个接口

  对于这个示例,我假设服务已经存在,理想的情况是用松散耦合的方式把它们集成在一起。以下清单显示了三个服务的应用程序接口。

  清单 3. CreditRatingInterface

  public interface CreditRatingInterface publicbooleangetUserCreditHistoryInformation(ICustomer iCustomer);

  清单 3 所示的信用级别评定接口提供了信用历史信息。它需要一个包含客户信息的 Customer对象。该接口的实现是由CreditRating 类提供的。

  清单 4. CreditLinkingInterface

  public interface CreditLinkingInterface

  public String getUrl(); public void setUrl(String url);publicvoid linkCreditBankAccount() throws Exception ;

  信用链接接口将信用历史信息与银行信息(如果需要的话)连接在一起,并插入用户的信用卡信息。信用链接接口是一个远程服务,它的查询是通过 getUrl()方法进行的。URL 由 Spring 框架的 bean 配置机制设置,我稍后会讨论它。该接口的实现是由CreditLinking类提供的。

  清单 5. EmailInterface

  public interface EmailInterface

  public void sendEmail(ICustomer iCustomer); publicStringgetFromEmail(); public void setFromEmail(String fromEmail) ;publicString getPassword(); public void setPassword(Stringpassword) ;public String getSmtpHost() ; public voidsetSmtpHost(StringsmtpHost); public String getUserId() ; publicvoid setUserId(StringuserId);

 

 

Spring IOC的原理:

控制权由应用代码中转到了外部容器,控制权的转移是所谓反转。IoC还有另外一个名字——“依赖注入(Dependency Injection)”。从名字上理解,所谓依赖注入,即组件之间的依赖关系由容器在运行期决定,形象地说,即由容器动态地将某种依赖关系注入到组件之中。

 

JVM的工作原理和特点:

JVM在整个jdk中处于最底层,负责于操作系统的交互,用来屏蔽操作系统环境,提供一个完整的Java运行环境,因此也就虚拟计算机. 操作系统装入JVM是通过jdk中Java.exe来完成,通过下面4步来完成JVM环境.

1.创建JVM装载环境和配置

2.装载JVM.dll

3.初始化JVM.dll并挂界到JNIENV(JNI调用接口)实例

4.调用JNIEnv实例装载并处理class类。

一.JVM装入环境,JVM提供的方式是操作系统的动态连接文件. 既然是文件那就一个装入路径的问题,Java是怎么找这个路径的呢?当你在调用Java test的时候,操作系统会在path下在你的Java.exe程序,Java.exe就通过下面一个过程来确定JVM的路径和相关的参数配置。

下面基于Windows的实现的分析.

首先查找jre路径,Java是通过GetApplicationHome api来获得当前的Java.exe绝对路径,c:j2sdk1.4.2_09binJava.exe,那么它会截取到绝对路径c: j2sdk1.4.2_09,判断c:j2sdk1.4.2_09binJava.dll文件是否存在,如果存在就把c: j2sdk1.4.2_09作为jre路径,如果不存在则判断c:j2sdk1.4.2_09jrebinJava.dll是否存在,如果存在这c:j2sdk1.4.2_09jre作为jre路径.如果不存在调用GetPublicJREHome查HKEY_LOCAL_MACHINE SoftwareJavaSoftJava Runtime Environment“当前JRE版本号”JavaHome的路径为jre路径。

然后装载JVM.cfg文件JRE路径+lib+ARCH(CPU构架)+JVM.cfgARCH(CPU构架)的判断是通过 Java_md.c中GetArch函数判断的,该函数中windows平台只有两种情况:WIN64的‘ia64’,其他情况都为‘i386’。以我的为例:C:j2sdk1.4.2_09jrelibi386JVM.cfg.主要的内容如下:

  1. client KNOWN  
  2. -server KNOWN
-hotspot ALIASED_TO -client -classic WARN -native ERROR -green ERROR

在我们的jdk目录中jrebinserver和jrebinclient都有JVM.dll文件存在,而Java正是通过JVM.cfg 配置文件来管理这些不同版本的JVM.dll的。

二:装载JVM.dll

通过第一步已经找到了JVM的路径,Java通过LoadJavaVM来装入JVM.dll文件.装入工作很简单就是调用Windows API函数:

LoadLibrary装载JVM.dll动态连接库.然后把JVM.dll中的导出函数JNI_CreateJavaVM和 JNI_GetDefaultJavaVMInitArgs挂接到InvocationFunctions变量的CreateJavaVM和 GetDefaultJavaVMInitArgs函数指针变量上。JVM.dll的装载工作宣告完成。

三:初始化JVM,获得本地调用接口, 这样就可以在Java中调用JVM的函数了.调用 InvocationFunctions->CreateJavaVM也就是JVM中JNI_CreateJavaVM方法获得JNIEnv结构的实例.

四:运行Java程序.

Java程序有两种方式一种是jar包,一种是class. 运行jar,Java -jar XXX.jar运行的时候,Java.exe调用GetMainClassName函数,该函数先获得JNIEnv实例然后调用Java类 Java.util.jar.JarFileJNIEnv中方法getManifest()并从返回的Manifest对象中取 getAttributes("Main-Class")的值即jar包中文件:META-INF/MANIFEST.MF指定的Main-Class的主类名作为运行的主类。之后main函数会调用Java.c中LoadClass方法装载该主类(使用JNIEnv实例的FindClass)。main 函数直接调用Java.c中LoadClass方法装载该类。如果是执行class方法。main函数直接调用Java.c中LoadClass方法装载该类。

然后main函数调用JNIEnv实例的GetStaticMethodID方法查找装载的class主类中

“public static void main(String[] args)”方法,并判断该方法是否为public方法,然后调用JNIEnv实例的

CallStaticVoidMethod方法调用该Java类的main方法。 

 

groovy 闭包:

闭包是可以用作函数参数和方法参数的代码块”。其实Groovy的闭包更象是一个“代码块”或者方法指针,代码在某处被定义然后在其后的调用处执行。

闭包类似Java的内类,区别是闭包只有单一的方法可以调用,但可以有任意的参数,闭包用“{}”括起,“->”前面是参数,后面是处理语句,可以直接调用,也可以使用call调用。不管那种调用,最后groovy编译器都会把编译成对doCall方法的调用,这是groovy对闭包的一个隐藏方法。

def closure = { param -> println("hello ${param}") }
closure.call("world!")
 
def closure = { greeting, name -> println(greeting + name) }
closure.call("hello ", "world!")

第一个例子演示了在字符串内使用参数的形式:${param}
第二个例子演示了多参数形式:用“,”分隔参数

roovy闭包中几个隐含变量

it:默认的参数名,调用是如果没有传参数,it为null
this : 跟Java一样,是定义闭包所在类的一个引用,不管有多少层闭包嵌套,this指向的都是最上层的类。
owner : 封闭闭包的对象(如果只有一层闭包就是this,如果有多层闭包嵌套就是含有此闭包的上层闭包)
delegate :缺省值是owner,但是可以改变

看例子:

class Class1 {
  def closure = {
    println " ============================== "
    println "this = "+ this.class.name
    println "owner = " + owner.class.name
    println "delegate = " + delegate.class.name
    def nestedClos = {
        println " ============================== "
        println "this = "+ this.class.name
        println "owner = " + owner.class.name
        println "delegate = " + delegate.class.name
      def thirdClos = {
            println " ============================== "
            println "this = "+ this.class.name
            println "owner = " + owner.class.name
            println "delegate = " + delegate.class.name
      }
      thirdClos() 
    }
    nestedClos()
  }
}
 
def clos = new Class1().closure
//clos.delegate = this
clos()

执行结果:
 ==============================
this = Class1
owner = Class1
delegate = Class1
 ==============================
this = Class1
owner = Class1$_closure1
delegate = Class1$_closure1
 ==============================
this = Class1
owner = Class1$_closure1_closure2
delegate = Class1$_closure1_closure2

闭包实现接口

前面说了用闭包实现类,或继承其他类。现在看看怎么实现接口。


interface Test
{
 def test()
}
 
 
def test = {
 println'ok'
} as Test
    
test.test()
如果接口只有一个方法需要实现,比如Comparator,Runnable 等接口就可以直接实现方法后加上 as Runnable接口名就可以。

多方法接口,groovy用Map的方法实现。

interface MultiFuncTest
{
    def test1()
    def test2(str)
}
 
 
def impl = [test1:{println'test'},
        test2:{str -> println str}] as MultiFuncTest


impl.test1()
impl.test2('ok')


delegate委托的用法
delegate委托在是一种常用设计模式,但在java中实现相对比较繁琐,groovy直接在GroovyObject中已经实现了delegate模式,所以在groovy中应用delegate很方便。

class Dog{
    def play = {
      "wang wang!"
    }
    def childmind = {
        println       delegate.play();     
    }
}

class Cat {
    def play = {"mi mi !"}
}

def dog = new Dog()
def cat = new Cat()

dog.childmind()

dog.childmind.delegate  = cat;
dog.childmind()

上面的例子是狗爸爸让老猫帮忙照看他的狗儿子玩游戏。

闭包是Groovy一种核心技术,也是现在比较流行的动态语言的核心技术,所以全面稳定的掌握闭包是非常必要的。

 

 

设计4个线程,其中两个线程每次对j增加1,另外两个线程对j每次减少1。写出程序。

 

public class ThreadTest {
	int j = 0;
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		ThreadTest tt = new ThreadTest();
		Inc inc = tt.new Inc();
		Dec dec = tt.new Dec();
		for (int i = 0; i < 2; i++) {
			Thread t = new Thread(inc);
			t.start();
			t = new Thread(dec);
			t.start();
		}
	}

	public synchronized void inc() {
		j++;
		System.out.println(Thread.currentThread().getName() + "-inc:" + j);
	}

	public synchronized void dec() {
		j--;
		System.out.println(Thread.currentThread().getName() + "-dec:" + j);
	}

	class Inc implements Runnable {

		public void run() {
			// TODO Auto-generated method stub
			inc();
		}

	}

	class Dec implements Runnable {

		public void run() {
			// TODO Auto-generated method stub
			dec();
		}

	}
}
 

 

运行结果:

Thread-0-inc:1
Thread-1-dec:0
Thread-2-inc:1
Thread-3-dec:0

 

 

下列程序的运行结果:

 

 

 

java中会存在内存泄漏吗,请简单描述。

最后

以上就是细心板凳为你收集整理的经典面试题Hibernate、Spring和Struts工作原理及使用理由的全部内容,希望文章能够帮你解决经典面试题Hibernate、Spring和Struts工作原理及使用理由所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(41)

评论列表共有 0 条评论

立即
投稿
返回
顶部