Java面试大全(2020年版)301-400

目录

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

所谓内存泄露就是指一个不再被程序使用的对象或变量一直被占据在内存中。java中有垃圾回收机制,它可以保证一对象不再被引用的时候,即对象编程了孤儿的时候,对象将自动被垃圾回收器从内存中清除掉。由于Java 使用有向图的方式进行垃圾回收管理,可以消除引用循环的问题,例如有两个对象,相互引用,只要它们和根进程不可达的,那么GC也是可以回收它们的,例如下面的代码可以看到这种情况的内存回收:

package com.huawei.interview;

import java.io.IOException;

public class GarbageTest {

	/**
	 * @param args
	 * @throws IOException 
	 */
	public static void main(String[] args) throws IOException {
		// TODO Auto-generated method stub
		try {
			gcTest();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println("has exited gcTest!");
		System.in.read();
		System.in.read();		
		System.out.println("out begin gc!");		
		for(int i=0;i<100;i++)
		{
			System.gc();
			System.in.read();	
			System.in.read();	
		}
	}

	private static void gcTest() throws IOException {
		System.in.read();
		System.in.read();		
		Person p1 = new Person();
		System.in.read();
		System.in.read();		
		Person p2 = new Person();
		p1.setMate(p2);
		p2.setMate(p1);
		System.out.println("before exit gctest!");
		System.in.read();
		System.in.read();		
		System.gc();
		System.out.println("exit gctest!");
	}

	private static class Person
	{
		byte[] data = new byte[20000000];
		Person mate = null;
		public void setMate(Person other)
		{
			mate = other;
		}
	}
}

java中的内存泄露的情况:长生命周期的对象持有短生命周期对象的引用就很可能发生内存泄露,尽管短生命周期对象已经不再需要,但是因为长生命周期对象持有它的引用而导致不能被回收,这就是java中内存泄露的发生场景,通俗地说,就是程序员可能创建了一个对象,以后一直不再使用这个对象,这个对象却一直被引用,即这个对象无用但是却无法被垃圾回收器回收的,这就是java中可能出现内存泄露的情况,例如,缓存系统,我们加载了一个对象放在缓存中(例如放在一个全局map对象中),然后一直不再使用它,这个对象一直被缓存引用,但却不再被使用。
检查java中的内存泄露,一定要让程序将各种分支情况都完整执行到程序结束,然后看某个对象是否被使用过,如果没有,则才能判定这个对象属于内存泄露。
如果一个外部类的实例对象的方法返回了一个内部类的实例对象,这个内部类对象被长期引用了,即使那个外部类实例对象不再被使用,但由于内部类持久外部类的实例对象,这个外部类对象将不会被垃圾回收,这也会造成内存泄露。
下面内容来自于网上(主要特点就是清空堆栈中的某个元素,并不是彻底把它从数组中拿掉,而是把存储的总数减少,本人写得可以比这个好,在拿掉某个元素时,顺便也让它从数组中消失,将那个元素所在的位置的值设置为null即可):
我实在想不到比那个堆栈更经典的例子了,以致于我还要引用别人的例子,下面的例子不是我想到的,是书上看到的,当然如果没有在书上看到,可能过一段时间我自己也想的到,可是那时我说是我自己想到的也没有人相信的。

public class Stack {
private Object[] elements=new Object[10];
private int size = 0;
public void push(Object e){
ensureCapacity();
elements[size++] = e;
}
public Object pop(){
if( size == 0)
throw new EmptyStackException();
return elements[--size];
}
private void ensureCapacity(){
if(elements.length == size){
Object[] oldElements = elements;
elements = new Object[2 * elements.length+1];
System.arraycopy(oldElements,0, elements, 0, size);
}
}
}
上面的原理应该很简单,假如堆栈加了10个元素,然后全部弹出来,虽然堆栈是空的,没有我们要的东西,但是这是个对象是无法回收的,这个才符合了内存泄露的两个条件:无用,无法回收。
但是就是存在这样的东西也不一定会导致什么样的后果,如果这个堆栈用的比较少,也就浪费了几个K内存而已,反正我们的内存都上G了,哪里会有什么影响,再说这个东西很快就会被回收的,有什么关系。下面看两个例子。
例子1
public class Bad{
public static Stack s=Stack();
static{
s.push(new Object());
s.pop(); //这里有一个对象发生内存泄露
s.push(new Object()); //上面的对象可以被回收了,等于是自愈了
}
}
因为是static,就一直存在到程序退出,但是我们也可以看到它有自愈功能,就是说如果你的Stack最多有100个对象,那么最多也就只有100个对象无法被回收其实这个应该很容易理解,Stack内部持有100个引用,最坏的情况就是他们都是无用的,因为我们一旦放新的进取,以前的引用自然消失!

内存泄露的另外一种情况:当一个对象被存储进HashSet集合中以后,就不能修改这个对象中的那些参与计算哈希值的字段了,否则,对象修改后的哈希值与最初存储进HashSet集合中时的哈希值就不同了,在这种情况下,即使在contains方法使用该对象的当前引用作为的参数去HashSet集合中检索对象,也将返回找不到对象的结果,这也会导致无法从HashSet集合中单独删除当前对象,造成内存泄露。

302. 能不能自己写个类,也叫java.lang.String?

可以,但在应用的时候,需要用自己的类加载器去加载,否则,系统的类加载器永远只是去加载jre.jar包中的那个java.lang.String。由于在tomcat的web应用程序中,都是由webapp自己的类加载器先自己加载WEB-INF/classess目录中的类,然后才委托上级的类加载器加载,如果我们在tomcat的web应用程序中写一个java.lang.String,这时候Servlet程序加载的就是我们自己写的java.lang.String,但是这么干就会出很多潜在的问题,原来所有用了java.lang.String类的都将出现问题。
虽然java提供了endorsed技术,可以覆盖jdk中的某些类,具体做法是….。但是,能够被覆盖的类是有限制范围,反正不包括java.lang这样的包中的类。
(下面的例如主要是便于大家学习理解只用,不要作为答案的一部分,否则,人家怀疑是题目泄露了)例如,运行下面的程序:

package java.lang;

public class String {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		System.out.println("string");
	}

}

报告的错误如下:

java.lang.NoSuchMethodError: main
Exception in thread "main"

这是因为加载了jre自带的java.lang.String,而该类中没有main方法。

303. Java代码查错

1.
abstract class Name {
   private String name;
   public abstract boolean isStupidName(String name) {}
}
大侠们,这有何错误?
答案: 错。abstract method必须以分号结尾,且不带花括号。
2.
public class Something {
   void doSomething () {
       private String s = "";
       int l = s.length();
   }
}
有错吗?
答案: 错。局部变量前不能放置任何访问修饰符 (private,public,和protected)。final可以用来修饰局部变量
(final如同abstract和strictfp,都是非访问修饰符,strictfp只能修饰class和method而非variable)。
3.
abstract class Something {
   private abstract String doSomething ();
}
这好像没什么错吧?
答案: 错。abstract的methods不能以private修饰。abstract的methods就是让子类implement(实现)具体细节的,怎么可以用private把abstract
method封锁起来呢? (同理,abstract method前不能加final)。
4.
public class Something {
   public int addOne(final int x) {
       return ++x;
   }
}
这个比较明显。
答案: 错。int x被修饰成final,意味着x不能在addOne method中被修改。
5.
public class Something {
   public static void main(String[] args) {
       Other o = new Other();
       new Something().addOne(o);
   }
   public void addOne(final Other o) {
       o.i++;
   }
}
class Other {
   public int i;
}
和上面的很相似,都是关于final的问题,这有错吗?
答案: 正确。在addOne method中,参数o被修饰成final。如果在addOne method里我们修改了o的reference
(比如: o = new Other();),那么如同上例这题也是错的。但这里修改的是o的member vairable
(成员变量),而o的reference并没有改变。
6.
class Something {
    int i;
    public void doSomething() {
        System.out.println("i = " + i);
    }
} 
有什么错呢? 看不出来啊。
答案: 正确。输出的是"i = 0"。int i属於instant variable (实例变量,或叫成员变量)。instant variable有default value。int的default value是0。
7.
class Something {
    final int i;
    public void doSomething() {
        System.out.println("i = " + i);
    }
}
和上面一题只有一个地方不同,就是多了一个final。这难道就错了吗?
答案: 错。final int i是个final的instant variable (实例变量,或叫成员变量)。final的instant variable没有default value,必须在constructor (构造器)结束之前被赋予一个明确的值。可以修改为"final int i = 0;"。
8.
public class Something {
     public static void main(String[] args) {
        Something s = new Something();
        System.out.println("s.doSomething() returns " + doSomething());
    }
    public String doSomething() {
        return "Do something ...";
    }
}
 看上去很完美。
答案: 错。看上去在main里call doSomething没有什么问题,毕竟两个methods都在同一个class里。但仔细看,main是static的。static method不能直接call non-static methods。可改成"System.out.println("s.doSomething() returns " + s.doSomething());"。同理,static method不能访问non-static instant variable。
9.
此处,Something类的文件名叫OtherThing.java
class Something {
    private static void main(String[] something_to_do) {        
        System.out.println("Do something ...");
    }
}
 这个好像很明显。
答案: 正确。从来没有人说过Java的Class名字必须和其文件名相同。但public class的名字必须和文件名相同。
10.
interface  A{
   int x = 0;
}
class B{
   int x =1;
}
class C extends B implements A {
   public void pX(){
      System.out.println(x);
   }
   public static void main(String[] args) {
      new C().pX();
   }
}
答案:错误。在编译时会发生错误(错误描述不同的JVM有不同的信息,意思就是未明确的x调用,两个x都匹配(就象在同时import java.util和java.sql两个包时直接声明Date一样)。对于父类的变量,可以用super.x来明确,而接口的属性默认隐含为 public static final.所以可以通过A.x来明确。
11.
interface Playable {
    void play();
}
interface Bounceable {
    void play();
}
interface Rollable extends Playable, Bounceable {
    Ball ball = new Ball("PingPang");
}
class Ball implements Rollable {
    private String name;
    public String getName() {
        return name;
    }
    public Ball(String name) {
        this.name = name;        
    }
   public void play() {
        ball = new Ball("Football");
        System.out.println(ball.getName());
    }
}
这个错误不容易发现。
答案: 错。"interface Rollable extends Playable, Bounceable"没有问题。interface可继承多个interfaces,所以这里没错。问题出在interface Rollable里的"Ball ball = new Ball("PingPang");"。任何在interface里声明的interface variable (接口变量,也可称成员变量),默认为public static final。也就是说"Ball ball = new Ball("PingPang");"实际上是"public static final Ball ball = new Ball("PingPang");"。在Ball类的Play()方法中,"ball = new Ball("Football");"改变了ball的reference,而这里的ball来自Rollable interface,Rollable interface里的ball是public static final的,final的object是不能被改变reference的。因此编译器将在"ball = new Ball("Football");"这里显示有错。

304. 编写一个程序,将a.txt文件中的单词与b.txt文件中的单词交替合并到c.txt文件中,a.txt文件中的单词用回车符分隔,b.txt文件中用回车或空格进行分隔。

package cn.xzy;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;

public class MainClass{
	public static void main(String[] args) throws Exception{
		FileManager a = new FileManager("a.txt",new char[]{'\n'});
		FileManager b = new FileManager("b.txt",new char[]{'\n',' '});		
		FileWriter c = new FileWriter("c.txt");
		String aWord = null;
		String bWord = null;
		while((aWord = a.nextWord()) !=null ){
			c.write(aWord + "\n");
			bWord = b.nextWord();
			if(bWord != null)
				c.write(bWord + "\n");
		}
		
		while((bWord = b.nextWord()) != null){
			c.write(bWord + "\n");
		}	
		c.close();
	}
	
}


class FileManager{

	String[] words = null;
	int pos = 0;
	public FileManager(String filename,char[] seperators) throws Exception{
		File f = new File(filename);
		FileReader reader = new FileReader(f);
		char[] buf = new char[(int)f.length()];
		int len = reader.read(buf);
		String results = new String(buf,0,len);
		String regex = null;
		if(seperators.length >1 ){
			regex = "" + seperators[0] + "|" + seperators[1];
		}else{
			regex = "" + seperators[0];
		}
		words = results.split(regex);
	}
	
	public String nextWord(){
		if(pos == words.length)
			return null;
		return words[pos++];
	}

}

305. 编写一个程序,将d:\java目录下的所有.java文件复制到d:\jad目录下,并将原来文件的扩展名从.java改为.jad。

listFiles方法接受一个FileFilter对象,这个FileFilter对象就是过虑的策略对象,不同的人提供不同的FileFilter实现,即提供了不同的过滤策略。
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

public class Jad2Java {

	public static void main(String[] args) throws Exception {
		File srcDir = new File("java");
		if(!(srcDir.exists() && srcDir.isDirectory()))
				throw new Exception("目录不存在");
		File[] files = srcDir.listFiles(
			new FilenameFilter(){

					public boolean accept(File dir, String name) {
						return name.endsWith(".java");
					}
					
				}
		);
		
		System.out.println(files.length);
		File destDir = new File("jad");
		if(!destDir.exists()) destDir.mkdir();
		for(File f :files){
			FileInputStream  fis = new FileInputStream(f);
			String destFileName = f.getName().replaceAll("\\.java$", ".jad");
			FileOutputStream fos = new FileOutputStream(new File(destDir,destFileName));
			copy(fis,fos);
			fis.close();
			fos.close();
		}
	}
	
	private static void copy(InputStream ips,OutputStream ops) throws Exception{
		int len = 0;
		byte[] buf = new byte[1024];
		while((len = ips.read(buf)) != -1){
			ops.write(buf,0,len);
		}

	}
}
由本题总结的思想及策略模式的解析:
1.
class jad2java{
	1. 得到某个目录下的所有的java文件集合
		1.1 得到目录 File srcDir = new File("d:\\java");
		1.2 得到目录下的所有java文件:File[] files = srcDir.listFiles(new MyFileFilter());
		1.3 只想得到.java的文件: class MyFileFilter implememyts FileFilter{
			public boolean accept(File pathname){
				return pathname.getName().endsWith(".java")
			}
		}
		
	2.将每个文件复制到另外一个目录,并改扩展名
		2.1 得到目标目录,如果目标目录不存在,则创建之
		2.2 根据源文件名得到目标文件名,注意要用正则表达式,注意.的转义。
		2.3 根据表示目录的File和目标文件名的字符串,得到表示目标文件的File。
			//要在硬盘中准确地创建出一个文件,需要知道文件名和文件的目录。 
		2.4 将源文件的流拷贝成目标文件流,拷贝方法独立成为一个方法,方法的参数采用抽象流的形式。
			//方法接受的参数类型尽量面向父类,越抽象越好,这样适应面更宽广。	
}

分析listFiles方法内部的策略模式实现原理
File[] listFiles(FileFilter filter){
	File[] files = listFiles();
	//Arraylist acceptedFilesList = new ArrayList();
	File[] acceptedFiles = new File[files.length];
	int pos = 0;
	for(File file: files){
		boolean accepted = filter.accept(file);
		if(accepted){
			//acceptedFilesList.add(file);
			acceptedFiles[pos++] = file;
		}		
	}
	
	Arrays.copyOf(acceptedFiles,pos);
	//return (File[])accpetedFilesList.toArray();
	
}

306. 编写一个截取字符串的函数,输入为一个字符串和字节数,输出为按字节截取的字符串,但要保证汉字不被截取半个,如“我ABC”,4,应该截取“我AB”,输入“我ABC汉DEF”,6,应该输出“我ABC”,而不是“我ABC+汉的半个”。

首先要了解中文字符有多种编码及各种编码的特征。
假设n为要截取的字节数。
public static void main(String[] args) throws Exception{
	String str = "我a爱中华abc我爱传智def';
	String str = "我ABC汉";
	int num = trimGBK(str.getBytes("GBK"),5);
	System.out.println(str.substring(0,num) );
}

public static int  trimGBK(byte[] buf,int n){
	int num = 0;
	boolean bChineseFirstHalf = false;
	for(int i=0;i<n;i++)
	{
		if(buf[i]<0 && !bChineseFirstHalf){
			bChineseFirstHalf = true;
		}else{
			num++;
			bChineseFirstHalf = false;				
		}
	}
	return num;
}

307. 有一个字符串,其中包含中文字符、英文字符和数字字符,请统计和打印出各个字符的个数。

哈哈,其实包含中文字符、英文字符、数字字符原来是出题者放的烟雾弹。
String content = “中国aadf的111萨bbb菲的zz萨菲”;
HashMap map = new HashMap();
for(int i=0;i<content.length;i++)
{
	char c = content.charAt(i);
	Integer num = map.get(c);
	if(num == null)
		num = 1;
	else
		num = num + 1;
	map.put(c,num);
} 
for(Map.EntrySet entry : map)
{
	system.out.println(entry.getkey() + “:” + entry.getValue());
}
估计是当初面试的那个学员表述不清楚,问题很可能是:
如果一串字符如"aaaabbc中国1512"要分别统计英文字符的数量,中文字符的数量,和数字字符的数量,假设字符中没有中文字符、英文字符、数字字符之外的其他特殊字符。
int engishCount;
int chineseCount;
int digitCount;
for(int i=0;i<str.length;i++)
{
	char ch = str.charAt(i);
	if(ch>=’0’ && ch<=’9’)
	{
		digitCount++
	}
	else if((ch>=’a’ && ch<=’z’) || (ch>=’A’ && ch<=’Z’))
	{
		engishCount++;
	}
	else
	{
		chineseCount++;
	}
}
System.out.println(……………);

308. 说明生活中遇到的二叉树,用java实现二叉树

这是组合设计模式。
我有很多个(假设10万个)数据要保存起来,以后还需要从保存的这些数据中检索是否存在某个数据,(我想说出二叉树的好处,该怎么说呢?那就是说别人的缺点),假如存在数组中,那么,碰巧要找的数字位于99999那个地方,那查找的速度将很慢,因为要从第1个依次往后取,取出来后进行比较。平衡二叉树(构建平衡二叉树需要先排序,我们这里就不作考虑了)可以很好地解决这个问题,但二叉树的遍历(前序,中序,后序)效率要比数组低很多。

代码如下:
package com.huawei.interview;

public class Node {
	public int value;
	public Node left;
	public Node right;
	
	public void store(int value)
	{
		if(value<this.value)
		{
			if(left == null)
			{
				left = new Node();
				left.value=value;
			}
			else
			{
				left.store(value);
			}
		}
		else if(value>this.value)
		{
			if(right == null)
			{
				right = new Node();
				right.value=value;
			}
			else
			{
				right.store(value);
			}			
		}
	}
	
	public boolean find(int value)
	{	
		System.out.println("happen " + this.value);
		if(value == this.value)
		{
			return true;
		}
		else if(value>this.value)
		{
			if(right == null) return false;
			return right.find(value);
		}else
		{
			if(left == null) return false;
			return left.find(value);
		}

	}
	
	public  void preList()
	{
		System.out.print(this.value + ",");
		if(left!=null) left.preList();
		if(right!=null) right.preList();
	}
	
	public void middleList()
	{
		if(left!=null) left.preList();
		System.out.print(this.value + ",");
		if(right!=null) right.preList();		
	}
	public void afterList()
	{
		if(left!=null) left.preList();
		if(right!=null) right.preList();
		System.out.print(this.value + ",");		
	}	
	public static void main(String [] args)
	{
		int [] data = new int[20];
		for(int i=0;i<data.length;i++)
		{
			data[i] = (int)(Math.random()*100) + 1;
			System.out.print(data[i] + ",");
		}
		System.out.println();
		
		Node root = new Node();
		root.value = data[0];
		for(int i=1;i<data.length;i++)
		{
			root.store(data[i]);
		}
		
		root.find(data[19]);
		
		root.preList();
		System.out.println();
		root.middleList();
		System.out.println();		
		root.afterList();
	}
}
-----------------又一次临场写的代码---------------------------
import java.util.Arrays;
import java.util.Iterator;

public class Node {
	private Node left;
	private Node right;
	private int value;
	//private int num;
	
	public Node(int value){
		this.value = value;
	}
	public void add(int value){
		
		if(value > this.value)
		{
			if(right != null)
				right.add(value);
			else
			{
				Node node = new Node(value);				
				right = node;
			}
		}
		else{
			if(left != null)
				left.add(value);
			else
			{
				Node node = new Node(value);				
				left = node;
			}			
		}
	}
	
	public boolean find(int value){
		if(value == this.value) return true;
		else if(value > this.value){
			if(right == null) return false;
			else return right.find(value);
		}else{
			if(left == null) return false;
			else return left.find(value);			
		}

	}
	
	public void display(){
		System.out.println(value);
		if(left != null) left.display();
		if(right != null) right.display();
		
	}
	
	/*public Iterator iterator(){
		
	}*/
	
	public static void main(String[] args){
		int[] values = new int[8];
		for(int i=0;i<8;i++){
			int num = (int)(Math.random() * 15);
			//System.out.println(num);
			//if(Arrays.binarySearch(values, num)<0)
			if(!contains(values,num))
				values[i] = num;
			else
				i--;
		}
		
		System.out.println(Arrays.toString(values));
		
		Node root  = new Node(values[0]);
		for(int i=1;i<values.length;i++){
			root.add(values[i]);
		}
		
		System.out.println(root.find(13));
		
		root.display();
		
	}
	
	public static boolean contains(int [] arr, int value){
		int i = 0;
		for(;i<arr.length;i++){
			if(arr[i] == value) return true;
			
		}
		return false;
	}
	
}

309. 从类似如下的文本文件中读取出所有的姓名,并打印出重复的姓名和重复的次数,并按重复次数排序:

1,张三,28
2,李四,35
3,张三,28
4,王五,35
5,张三,28
6,李四,35
7,赵六,28
8,田七,35

程序代码如下(答题要博得用人单位的喜欢,包名用该公司,面试前就提前查好该公司的网址,如果查不到,现场问也是可以的。还要加上实现思路的注释):
package com.huawei.interview;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.TreeSet;


public class GetNameTest {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		//InputStream ips = GetNameTest.class.getResourceAsStream("/com/huawei/interview/info.txt");
		//用上一行注释的代码和下一行的代码都可以,因为info.txt与GetNameTest类在同一包下面,所以,可以用下面的相对路径形式
		
		Map results = new HashMap();
		InputStream ips = GetNameTest.class.getResourceAsStream("info.txt");
		BufferedReader in = new BufferedReader(new InputStreamReader(ips));
		String line = null;
		try {
			while((line=in.readLine())!=null)
			{
				dealLine(line,results);
			}
			sortResults(results);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	static class User
	{
		public  String name;
		public Integer value;
		public User(String name,Integer value)
		{
			this.name = name;
			this.value = value;
		}

		@Override
		public boolean equals(Object obj) {
			// TODO Auto-generated method stub
				
			//下面的代码没有执行,说明往treeset中增加数据时,不会使用到equals方法。
			boolean result = super.equals(obj);
			System.out.println(result);
			return result;
		}
	}
	
	private static void sortResults(Map results) {
		// TODO Auto-generated method stub
		TreeSet sortedResults = new TreeSet(
				new Comparator(){
					public int compare(Object o1, Object o2) {
						// TODO Auto-generated method stub
						User user1 = (User)o1;
						User user2 = (User)o2;
						/*如果compareTo返回结果0,则认为两个对象相等,新的对象不会增加到集合中去
						 * 所以,不能直接用下面的代码,否则,那些个数相同的其他姓名就打印不出来。
						 * */
						
						//return user1.value-user2.value;
						//return user1.value<user2.value?-1:user1.value==user2.value?0:1;
						if(user1.value<user2.value)
						{
							return -1;
						}else if(user1.value>user2.value)
						{
							return 1;
						}else
						{
							return user1.name.compareTo(user2.name);
						}
					}
					
				}
		);
		Iterator iterator = results.keySet().iterator();
		while(iterator.hasNext())
		{
			String name = (String)iterator.next();
			Integer value = (Integer)results.get(name);
			if(value > 1)
			{				
				sortedResults.add(new User(name,value));				
			}
		}
		
		printResults(sortedResults);
	}
	private static void printResults(TreeSet sortedResults) 
	{
		Iterator iterator  = sortedResults.iterator();
		while(iterator.hasNext())
		{
			User user = (User)iterator.next();
			System.out.println(user.name + ":" + user.value);
		}	
	}
	public static void dealLine(String line,Map map)
	{
		if(!"".equals(line.trim()))
		{
			String [] results = line.split(",");
			if(results.length == 3)
			{
				String name = results[1];
				Integer value = (Integer)map.get(name);
				if(value == null) value = 0;
				map.put(name,value + 1);
			}
		}
	}

}

310. 写一个Singleton出来。

第一种:饱汉模式
public class SingleTon {
	private SingleTon(){
		}

	//实例化放在静态代码块里可提高程序的执行效率,但也可能更占用空间	
	private final static SingleTon instance = new SingleTon();
	public static SingleTon getInstance(){
		return instance;
	}
}

第二种:饥汉模式
public class SingleTon {
	private SingleTon(){}
	
	private static instance = null;//new SingleTon();
	
	public static synchronized SingleTon getInstance(){
		if(instance == null)
			instance = new SingleTon();
		return instance;
	}
}

第三种:用枚举
	public enum SingleTon{
		ONE;
	
	}

第三:更实际的应用(在什么情况用单例)
public class SequenceGenerator{
	//下面是该类自身的业务功能代码
	private int count = 0;

	public synchronized int getSequence(){
		++count;
	}
	
	//下面是把该类变成单例的代码
	private SequenceGenerator(){}
	private final static instance = new SequenceGenerator();
	public static SingleTon getInstance(){
		return instance;
	}	
	
}

第四:
	public class MemoryDao
	{
    private HashMap map = new HashMap();
    
   	public void add(Student stu1){ 
    		map.put(SequenceGenerator.getInstance().getSequence(),stu1);
    }
   
   //把MemoryDao变成单例 
  }






Singleton模式主要作用是保证在Java应用程序中,一个类Class只有一个实例存在。 
一般Singleton模式通常有几种种形式: 
第一种形式: 定义一个类,它的构造函数为private的,它有一个static的private的该类变量,在类初始化时实例话,通过一个public的getInstance方法获取对它的引用,继而调用其中的方法。 
public class Singleton { 
private Singleton(){} 
      //在自己内部定义自己一个实例,是不是很奇怪? 
      //注意这是private 只供内部调用 
      private static Singleton instance = new Singleton(); 
      //这里提供了一个供外部访问本class的静态方法,可以直接访问   
      public static Singleton getInstance() { 
        return instance;    
      } 
   } 
   第二种形式: 
public class Singleton { 
  private static Singleton instance = null; 
  public static synchronized Singleton getInstance() { 
  //这个方法比上面有所改进,不用每次都进行生成对象,只是第一次      
  //使用时生成实例,提高了效率! 
  if (instance==null) 
    instance=new Singleton(); 
			return instance;   
	} 
} 
其他形式: 
定义一个类,它的构造函数为private的,所有方法为static的。 
一般认为第一种形式要更加安全些

311. 递归算法题1

一个整数,大于0,不用循环和本地变量,按照n,2n,4n,8n的顺序递增,当值大于5000时,把值按照指定顺序输出来。
例:n=1237
则输出为:
1237,
2474,
4948,
9896,
9896,
4948,
2474,
1237,
提示:写程序时,先致谢按递增方式的代码,写好递增的以后,再增加考虑递减部分。
	public static void doubleNum(int n)
	{
		System.out.println(n);
		if(n<=5000)
			doubleNum(n*2);
		System.out.println(n);		
	}

312. 递归算法题2

第1个人10,第2个比第1个人大2岁,依次递推,请用递归方式计算出第8个人多大?
package cn.xzy;

import java.util.Date;

public class A1 {

	public static void main(String [] args)
	{
		System.out.println(computeAge(8));
	}
	
	public static int computeAge(int n)
	{
		if(n==1) return 10;
		return computeAge(n-1) + 2;
	}
}

	public static void toBinary(int n,StringBuffer result)
	{

		if(n/2 != 0)
			toBinary(n/2,result);
		result.append(n%2);		
	}

313. 排序都有哪几种方法?请列举。用JAVA实现一个快速排序。

本人只研究过冒泡排序、选择排序和快速排序,下面是快速排序的代码:
public class QuickSort {
/**
* 快速排序
* @param strDate
* @param left
* @param right
*/
public void quickSort(String[] strDate,int left,int right){
String middle,tempDate;
int i,j;
i=left;
j=right;
middle=strDate[(i+j)/2];
do{
while(strDate[i].compareTo(middle)<0&& i<right)
i++; //找出左边比中间值大的数
while(strDate[j].compareTo(middle)>0&& j>left)
j--; //找出右边比中间值小的数
if(i<=j){ //将左边大的数和右边小的数进行替换 
tempDate=strDate[i];
strDate[i]=strDate[j];
strDate[j]=tempDate;
i++;
j--;
}
}while(i<=j); //当两者交错时停止

if(i<right){
quickSort(strDate,i,right);//从
}
if(j>left){
quickSort(strDate,left,j);
}
}
/**
  * @param args
  */
public static void main(String[] args){
String[] strVoid=new String[]{"11","66","22","0","55","22","0","32"};
QuickSort sort=new QuickSort();
sort.quickSort(strVoid,0,strVoid.length-1);
for(int i=0;i<strVoid.length;i++){
System.out.println(strVoid[i]+" ");
}
}


}

314. 有数组a[n],用java代码将数组元素顺序颠倒

//用下面的也可以
//for(int i=0,int j=a.length-1;i<j;i++,j--) 是否等效于 for(int i=0;i<a.length/2;i++)呢?

import java.util.Arrays;

public class SwapDemo{

	public static void main(String[] args){
		int [] a = new int[]{
						(int)(Math.random() * 1000),
						(int)(Math.random() * 1000),
						(int)(Math.random() * 1000),
						(int)(Math.random() * 1000),						
						(int)(Math.random() * 1000)																		
		};	
		
		System.out.println(a);
		System.out.println(Arrays.toString(a));
		swap(a);
		System.out.println(Arrays.toString(a));		
	}
	
	public static void swap(int a[]){
		int len = a.length;
		for(int i=0;i<len/2;i++){
			int tmp = a[i];
			a[i] = a[len-1-i];
			a[len-1-i] = tmp;
		}
	}
}

315. 金额转换,阿拉伯数字的金额转换成中国传统的形式如:(¥1011)->(一千零一拾一元整)输出。

去零的代码:
	return sb.reverse().toString().replaceAll("零[拾佰仟]","零").replaceAll("零+万","万").replaceAll("零+元","元").replaceAll("零+","零");

public class RenMingBi {

	/**
	 * @param args add by zxx ,Nov 29, 2008
	 */
	private static final char[] data = new char[]{
			'零','壹','贰','叁','肆','伍','陆','柒','捌','玖'
		}; 
	private static final char[] units = new char[]{
		'元','拾','佰','仟','万','拾','佰','仟','亿'
	};
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		System.out.println(
				convert(135689123));
	}

	public static String convert(int money)
	{
		StringBuffer sbf = new StringBuffer();
		int unit = 0;
		while(money!=0)
		{
			sbf.insert(0,units[unit++]);
			int number = money%10;
			sbf.insert(0, data[number]);
			money /= 10;
		}

		return sbf.toString();
	}
}

316. 判断第二个日期比第一个日期大

	如何用脚本判断用户输入的的字符串是下面的时间格式2004-11-21 必须要保证用户的输入是此格式,并且是时间,比如说月份不大于12等等,另外我需要用户输入两个,并且后一个要比前一个晚,只允许用JAVASCRIPT,请详细帮助作答,, 
//这里可用正则表达式判断提前判断一下格式,然后按下提取各时间字段内容
<script type="text/javascript">
	window.onload = function()
	{
		//这么写是为了实现js代码与html代码的分离,当我修改js时,不能影响html代码。
		document.getElementById("frm1").onsubmit = 
			function(){
				var d1 = this.d1.value;
				var d2 = this.d2.value;
				if(!verifyDate (d1)) {alert("第一个日期格式不对");return false;}
				if(!verifyDate (d2)) {alert("第二个日期格式不对");return false;}
				if(!compareDate(d1,d2)) {alert("第二个日期比第一日期小");return false;}			
			};
		}
		
		function compareDate(d1,d2)
		{
			var arrayD1 = d1.split("-");
			var date1 = new Date(arrayD1[0],arrayD1[1],arrayD1[2]);
			var arrayD2 = d2.split("-");
			var date2 = new Date(arrayD2[0],arrayD2[1],arrayD2[2]);	
			if(date1 > date2) return false;		
			return true;
		} 
		
		function verifyDate(d)
		{
			var datePattern = /^\d{4}-(0?[1-9]|1[0-2])-(0?[1-9]|[1-2]\d|3[0-1])$/;
			return datePattern.test(d);
		}
</script>

<form id="frm1" action="xxx.html">
<input type="text" name="d1" />
<input type="text" name="d2" />
<input type="submit"/>
</form>

317. 用table显示n条记录,每3行换一次颜色,即1,2,3用红色字体,4,5,6用绿色字体,7,8,9用红颜色字体。

<body>
<table id="tbl">
	<tr><td>1</td></tr>
	<tr><td>2</td></tr>
	<tr><td>3</td></tr>
	<tr><td>4</td></tr>
	<tr><td>5</td></tr>
	<tr><td>6</td></tr>
	<tr><td>7</td></tr>
	<tr><td>8</td></tr>
	<tr><td>9</td></tr>
	<tr><td>10</td></tr>
</table>
</body>
<script type="text/javascript">
	window.onload=function()
		{
			var tbl = document.getElementById("tbl");
			rows = tbl.getElementsByTagName("tr");
			for(i=0;i<rows.length;i++)
			{
				var j = parseInt(i/3);
				if(j%2==0) rows[i].style.backgroundColor="#f00";
				else  rows[i].style.backgroundColor="#0f0";
			}
		}
</script>

318. HTML 的 form 提交之前如何验证数值文本框的内容全部为数字? 否则的话提示用户并终止提交?

<form onsubmit=’return chkForm(this)’>
<input type="text" name="d1"/>
<input type="submit"/>
</form>
<script type=”text/javascript” />	
function chkForm(this)
	{ 
		var value = thist.d1.value;
		var len = value.length;
		for(var i=0;i<len;i++)
		{
			if(value.charAt(i)>"9" || value.charAt(i)<"0")
			{
				alert("含有非数字字符"); 				return false;
			}
		}
		return true;
	}
</script>

319. 请写出用于校验HTML文本框中输入的内容全部为数字的javascript代码

<input type="text" id="d1" onblur=" chkNumber (this)"/>
<script type=”text/javascript” />	
function chkNumber(eleText)  
	{ 
		var value = eleText.value;
		var len = value.length;
		for(var i=0;i<len;i++)
		{
			if(value.charAt(i)>"9" || value.charAt(i)<"0")
			{
				alert("含有非数字字符"); 
				eleText.focus();
				break; 
			}
		}
	}
</script>
除了写完代码,还应该在网页上写出实验步骤和在代码中加入实现思路,让面试官一看就明白你的意图和检查你的结果。

320. 说说你用过那些ajax技术和框架,说说它们的区别

[Ajax的常用框架有哪些?]
AJAX(Asynchronous JavaScript and XML,异步JavaScript和XML),是创建交互式Web应用的主要开发技术。互联网中也有大量的关于AJAX的框架,本文汇总了最常用的11个框架。
1. jQueryjQuery是一个轻量级的Javascript库,兼容CSS3,还兼容各种浏览器。jQuery使用户能更方便地处理HTML documents、events、实现动画效果,并且方便地为网站提供AJAX交互。
2. MooToolsMooTools是一个简洁、模块化、面向对象的JavaScript库。它能够帮助你更快、更简单地编写可扩展和兼容性强的JavaScript代码。Mootools跟prototypejs相类似,语法几乎一样。但它提供的功能要比prototypejs多,而且更强大。比如增加了动画特效、拖放操作等等。
3. PrototypePrototype是Sam Stephenson写的一个非常优雅的JavaScript基础类库,对JavaScript做了大量的扩展,旨在简化动态Web应用程序的开发。Prototype很好的支持AJAX,国内外有多个基于此类库实现的效果库,也做得很棒。
4. ASP.NET AJAXASP.NET AJAX是一个完整的开发框架,容易与现有的ASP.NET程序相结合,通常实现复杂的功能只需要在页面中拖几个控件,而不必了解深层次的工作原理,除此之外服务器端编程的ASP.NET AJAX Control Toolkit含有大量的独立AJAX控件和对ASP.NET原有服务器控件的AJAX功能扩展,实现起来也非常简单。
5. Apache WicketApache Wicket是一个针对Java的Web开发框架,与Struts、WebWork、Tapestry类似。其特点在于对HTML和代码进行了有效的分离(有利于程序员和美工的合作),基于规则的配置(减少了XML 等配置文件的使用),学习曲线较低(开发方式与C/S相似),更加易于调试(错误类型比较少,而且容易定位)。
6. Dojo TookitDojo是一个强大的面向对象的JavaScript框架。主要由三大模块组成:Core、Dijit、DojoX。Core提供AJAX、events、packaging、CSS-based querying、animations、JSON等相关操作API;Dijit是一个可更换皮肤、基于模板的WEB UI控件库;DojoX包括一些创新/新颖的代码和控件:DateGrid、charts、离线应用、跨浏览器矢量绘图等。
7. DWR(Direct Web Remoting)DWR是一个Java库,可以帮助开发者轻松实现服务器端的Java和客户端的JavaScript相互操作、彼此调用。
8. Spry Framework Adobe Spry是一个面向Web设计人员而不是开发人员的AJAX框架,它使得设计人员不需要了解复杂的AJAX技巧也能在一个HTML页面中创建丰富体验成为了可能。
9. YUI (Yahoo User Interface) LibraryYUI(Yahoo User Interface),是由雅虎开发的一个开源的JavaScript函数库,它采用了AJAX、 DHTML和DOM等诸多技术。YUI包含多种程序工具、函数库以及网页操作界面,能够更快速地开发互动性高且丰富的网站应用程序。
10. Google Web ToolkitGoogle Web Toolkit (GWT) 是一个开源的Java开发框架,可以使不会使用第二种浏览器语言的开发人员编写Google 地图和 Gmail 等 AJAX 应用程序时更加轻松。
11. ZK FrameworkZK是一套开源、兼容XUL/HTML标准、使用Java编写的AJAX框架,使用该框架,你无需编写JavaScript 代码就可以创建一个支持Web 2.0的富互联网应用程序(RIA)。其最大的好处是,在设计AJAX网络应用程序时,轻松简便的操作就像设计桌面程序一样。ZK包含了一个以AJAX为基础、事件驱动(event-driven)、高互动性的引擎,同时还提供了多样丰富、可重复使用的XUL与HTML组件,以及以XML为基础的使用接口设计语言ZK User-interfaces Markup Language(ZUML)。

321. Tomcat的优化经验

  • 去掉对web.xml的监视,把jsp提前编辑成Servlet。
  • 有富余物理内存的情况,加大tomcat使用的jvm的内存

322. HTTP请求的GET与POST方式的区别

servlet有良好的生存期的定义,包括加载和实例化、初始化、处理请求以及服务结束。这个生存期由javax.servlet.Servlet接口的init,service和destroy方法表达。

323. 解释一下什么是servlet

Servlet是一种服务器端的Java应用程序,具有独立于平台和协议的特性,可以生成动态的Web页面。 它担当客户请求(Web浏览器或其他HTTP客户程序)与服务器响应(HTTP服务器上的数据库或应用程序)的中间层

324. 说一说Servlet的生命周期?

servlet有良好的生存期的定义,包括加载和实例化、初始化、处理请求以及服务结束。这个生存期由javax.servlet.Servlet接口的init,service和destroy方法表达。
Servlet被服务器实例化后,容器运行其init方法,请求到达时运行其service方法,service方法自动派遣运行与请求对应的doXXX方法(doGet,doPost)等,当服务器决定将实例销毁的时候调用其destroy方法。
web容器加载servlet,生命周期开始。通过调用servlet的init()方法进行servlet的初始化。通过调用service()方法实现,根据请求的不同调用不同的do***()方法。结束服务,web容器调用servlet的destroy()方法。

325. Servlet的基本架构

public class ServletName extends HttpServlet { 
public void doPost(HttpServletRequest request, HttpServletResponse response) throws 
ServletException, IOException { 
} 
public void doGet(HttpServletRequest request, HttpServletResponse response) throws 
ServletException, IOException { 
} 
} 

326. SERVLET API中forward()与redirect()的区别?

前者仅是容器中控制权的转向,在客户端浏览器地址栏中不会显示出转向后的地址;后者则是完全的跳转,浏览器将会得到跳转的地址,并重新发送请求链接。这样,从浏览器的地址栏中可以看到跳转后的链接地址。所以,前者更加高效,在前者可以满足需要时,尽量使用forward()方法,并且,这样也有助于隐藏实际的链接。在有些情况下,比如,需要跳转到一个其它服务器上的资源,则必须使用
sendRedirect()方法。

327. 什么情况下调用doGet()和doPost()?

Jsp页面中的FORM标签里的method属性为get时调用doGet(),为post时调用doPost()。

328. 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):删除请求中的一个属性

329. forward 和redirect的区别

  • forward是服务器请求资源,服务器直接访问目标地址的URL,把那个URL的响应内容读取过来,然后把这些内容再发给浏览器,浏览器根本不知道服务器发送的内容是从哪儿来的,所以它的地址栏中还是原来的地址。
  • redirect就是服务端根据逻辑,发送一个状态码,告诉浏览器重新去请求那个地址,一般来说浏览器会用刚才请求的所有参数重新请求,所以session,request参数都可以获取。

330. request.getAttribute()和request.getParameter()有何区别?

request.getAttribute()方法返回request范围内存在的对象,而request.getParameter()方法是获取http提交过来的数据。

331. jsp有哪些内置对象?作用分别是什么? 分别有什么方法?

JSP共有以下9个内置的对象:

  • request 用户端请求,此请求会包含来自GET/POST请求的参数
  • response 网页传回用户端的回应
  • pageContext 网页的属性是在这里管理
  • session 与请求有关的会话期
  • application servlet 正在执行的内容
  • out 用来传送回应的输出
  • config servlet的构架部件
  • page JSP网页本身
  • exception 针对错误网页,未捕捉的例外
  • request表示HttpServletRequest对象。它包含了有关浏览器请求的信息,并且提供了几个用于获取cookie, header, 和session数据的有用的方法。
  • response表示HttpServletResponse对象,并提供了几个用于设置送回 浏览器的响应的方法(如cookies,头信息等)
  • out对象是javax.jsp.JspWriter的一个实例,并提供了几个方法使你能用于向浏览器回送输出结果。
  • pageContext表示一个javax.servlet.jsp.PageContext对象。它是用于方便存取各种范围的名字空间、servlet相关的对象的API,并且包装了通用的servlet相关功能的方法。
  • session表示一个请求的javax.servlet.http.HttpSession对象。Session可以存贮用户的状态信息
  • applicaton 表示一个javax.servle.ServletContext对象。这有助于查找有关servlet引擎和servlet环境的信息
  • config表示一个javax.servlet.ServletConfig对象。该对象用于存取servlet实例的初始化参数。
  • page表示从该页面产生的一个servlet实例

332. jsp有哪些动作?作用分别是什么?

JSP共有以下6种基本动作

  • jsp:include:在页面被请求的时候引入一个文件。
  • jsp:useBean:寻找或者实例化一个JavaBean。
  • jsp:setProperty:设置JavaBean的属性。
  • jsp:getProperty:输出某个JavaBean的属性。
  • jsp:forward:把请求转到一个新的页面。
  • jsp:plugin:根据浏览器类型为Java插件生成OBJECT或EMBED标记

333. JSP的常用指令

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

334. JSP中动态INCLUDE与静态INCLUDE的区别?

动态INCLUDE用jsp:include动作实现
<jsp:include page=included.jsp flush=true />它总是会检查所含文件中的变化,适合用于包含动态页面,并且可以带参数 静态INCLUDE用include伪码实现,定不会检查所含文件的变化,适用于包含静态页面 <%@ include file=included.htm %>

335. 两种跳转方式分别是什么?有什么区别?

有两种,分别为:

  • <jsp:include page=included.jsp flush=true>
  • <jsp:forward page= nextpage.jsp/>
    前者页面不会转向include所指的页面,只是显示该页的结果,主页面还是原来的页面。执行完后还会回来,相当于函数调用。并且可以带参数.后者完全转向新页面,不会再回来。相当于go to 语句。

336. 页面间对象传递的方法

request,session,application,cookie等

337. JSP和Servlet有哪些相同点和不同点,他们之间的联系是什么?

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

338. MVC的各个部分都有那些技术来实现?如何实现?

MVC是Model-View-Controller的简写。Model 代表的是应用的业务逻辑(通过JavaBean,EJB组件实现), View 是应用的表示面(由JSP页面产生),Controller 是提供应用的处理过程控制(一般是一个Servlet),通过这种设计模型把应用逻辑,处理过程和显示逻辑分成不同的组件实现。这些组件可以进行交互和重用。

339. 我们在web应用开发过程中经常遇到输出某种编码的字符,如iso8859-1等,如何输出一个某种编码的字符串?

Public String translate (String str) { 
  String tempStr = ""; 
  try { 
    tempStr = new String(str.getBytes("ISO-8859-1"), "GBK"); 
    tempStr = tempStr.trim(); 
  } 
  catch (Exception e) { 
    System.err.println(e.getMessage()); 
  } 
  return tempStr; 
} 

340. 用两种方式根据部门号从高到低,工资从低到高列出每个员工的信息。

employee:
     eid,ename,salary,deptid;
select * from employee order by deptid desc,salary

341. 列出各个部门中工资高于本部门的平均工资的员工数和部门号,并按部门号排序

创建表:
       mysql> create table employee921(id int primary key auto_increment,name varchar(5
0),salary bigint,deptid int);

插入实验数据:
mysql> insert into employee921 values(null,'zs',1000,1),(null,'ls',1100,1),(null
,'ww',1100,1),(null,'zl',900,1) ,(null,'zl',1000,2), (null,'zl',900,2) ,(null,'z
l',1000,2) , (null,'zl',1100,2);

编写sql语句:

()select avg(salary) from employee921 group by deptid;
()mysql> select employee921.id,employee921.name,employee921.salary,employee921.dep
tid tid from  employee921 where salary > (select avg(salary) from employee921 where  deptid = tid);
   效率低的一个语句,仅供学习参考使用(在group by之后不能使用where,只能使用having,在group by之前可以使用where,即表示对过滤后的结果分组):
mysql> select employee921.id,employee921.name,employee921.salary,employee921.dep
tid tid from  employee921 where salary > (select avg(salary) from employee921 group by deptid having deptid = tid);
()select count(*) ,tid 
	from (
		select employee921.id,employee921.name,employee921.salary,employee921.deptid tid 
		from  	employee921 
		where salary >
	 		(select avg(salary) from employee921 where  deptid = tid)
	) as t 
	group by tid ;

另外一种方式:关联查询
select a.ename,a.salary,a.deptid 
 from emp a,
    (select deptd,avg(salary) avgsal from emp group by deptid ) b 
 where a.deptid=b.deptid and a.salary>b.avgsal;

342. 存储过程与触发器必须讲,经常被面试到?

create procedure insert_Student (_name varchar(50),_age int ,out _id int)
begin
	insert into student value(null,_name,_age);
	select max(stuId) into _id from student;
end;

call insert_Student('wfz',23,@id);
select @id;

mysql> create trigger update_Student BEFORE update on student FOR EACH ROW
-> select * from student;
触发器不允许返回结果

create trigger update_Student BEFORE update on student FOR EACH ROW  
insert into  student value(null,'zxx',28);
mysql的触发器目前不能对当前表进行操作

create trigger update_Student BEFORE update on student FOR EACH ROW  
delete from articles  where id=8;
这个例子不是很好,最好是用删除一个用户时,顺带删除该用户的所有帖子
这里要注意使用OLD.id

触发器用处还是很多的,比如校内网、开心网、Facebook,你发一个日志,自动通知好友,其实就是在增加日志时做一个后触发,再向通知表中写入条目。因为触发器效率高。而UCH没有用触发器,效率和数据处理能力都很低。
存储过程的实验步骤:
mysql> delimiter |
mysql> create procedure insertArticle_Procedure (pTitle varchar(50),pBid int,out
 pId int)
    -> begin
    -> insert into article1 value(null,pTitle,pBid);
    -> select max(id) into pId from article1;
    -> end;
    -> |
Query OK, 0 rows affected (0.05 sec)

mysql> call insertArticle_Procedure('传智播客',1,@pid);
    -> |
Query OK, 0 rows affected (0.00 sec)

mysql> delimiter ;
mysql> select @pid;
+------+
| @pid |
+------+
| 3    |
+------+
1 row in set (0.00 sec)

mysql> select * from article1;
+----+--------------+------+
| id | title        | bid  |
+----+--------------+------+
| 1  | test         | 1    |
| 2  | chuanzhiboke | 1    |
| 3  | 传智播客     | 1    |
+----+--------------+------+
3 rows in set (0.00 sec)

触发器的实验步骤:
create table board1(id int primary key auto_increment,name varchar(50),ar
ticleCount int);

create table article1(id int primary key auto_increment,title varchar(50)
,bid int references board1(id));

delimiter |

create trigger insertArticle_Trigger after insert on article1 for each ro
w begin
    -> update board1 set articleCount=articleCount+1 where id= NEW.bid;
    -> end;
    -> |

delimiter ;

insert into board1 value (null,'test',0);

insert into article1 value(null,'test',1);
还有,每插入一个帖子,都希望将版面表中的最后发帖时间,帖子总数字段进行同步更新,用触发器做效率就很高。下次课设计这样一个案例,写触发器时,对于最后发帖时间可能需要用declare方式声明一个变量,或者是用NEW.posttime来生成。

343. 数据库三范式是什么?

第一范式(1NF):字段具有原子性,不可再分。所有关系型数据库系统都满足第一范式)
	数据库表中的字段都是单一属性的,不可再分。例如,姓名字段,其中的姓和名必须作为一个整体,无法区分哪部分是姓,哪部分是名,如果要区分出姓和名,必须设计成两个独立的字段。

  第二范式(2NF):
第二范式(2NF)是在第一范式(1NF)的基础上建立起来的,即满足第二范式(2NF)必须先满足第一范式(1NF)。
要求数据库表中的每个实例或行必须可以被惟一地区分。通常需要为表加上一个列,以存储各个实例的惟一标识。这个惟一属性列被称为主关键字或主键。

第二范式(2NF)要求实体的属性完全依赖于主关键字。所谓完全依赖是指不能存在仅依赖主关键字一部分的属性,如果存在,那么这个属性和主关键字的这一部分应该分离出来形成一个新的实体,新实体与原实体之间是一对多的关系。为实现区分通常需要为表加上一个列,以存储各个实例的惟一标识。简而言之,第二范式就是非主属性非部分依赖于主关键字。
  
 第三范式的要求如下: 
满足第三范式(3NF)必须先满足第二范式(2NF)。简而言之,第三范式(3NF)要求一个数据库表中不包含已在其它表中已包含的非主关键字信息。
所以第三范式具有如下特征:
         1,每一列只有一个值 
         2,每一行都能区分。 
         3,每一个表都不包含其他表已经包含的非主关键字信息。
例如,帖子表中只能出现发帖人的id,而不能出现发帖人的id,还同时出现发帖人姓名,否则,只要出现同一发帖人id的所有记录,它们中的姓名部分都必须严格保持一致,这就是数据冗余。

344. 说出一些数据库优化方面的经验?

用PreparedStatement 一般来说比Statement性能高:一个sql 发给服务器去执行,涉及步骤:语法检查、语义分析, 编译,缓存
“inert into user values(1,1,1)”-二进制
“inert into user values(2,2,2)”-二进制
“inert into user values(?,?,?)”-二进制



有外键约束会影响插入和删除性能,如果程序能够保证数据的完整性,那在设计数据库时就去掉外键。(比喻:就好比免检产品,就是为了提高效率,充分相信产品的制造商)
(对于hibernate来说,就应该有一个变化:empleyee->Deptment对象,现在设计时就成了employeedeptid)

看mysql帮助文档子查询章节的最后部分,例如,根据扫描的原理,下面的子查询语句要比第二条关联查询的效率高:
1.  select e.name,e.salary where e.managerid=(select id from employee where name='zxx');

2.   select e.name,e.salary,m.name,m.salary from employees e,employees m where
 e.managerid = m.id and m.name='zxx';

表中允许适当冗余,譬如,主题帖的回复数量和最后回复时间等
将姓名和密码单独从用户表中独立出来。这可以是非常好的一对一的案例哟!

sql语句全部大写,特别是列名和表名都大写。特别是sql命令的缓存功能,更加需要统一大小写,sql语句发给oracle服务器语法检查和编译成为内部指令缓存和执行指令。根据缓存的特点,不要拼凑条件,而是用?和PreparedStatment

还有索引对查询性能的改进也是值得关注的。

备注:下面是关于性能的讨论举例

4航班 3个城市

m*n

select * from flight,city where flight.startcityid=city.cityid and city.name='beijing';

m + n


select * from flight where startcityid = (select cityid from city where cityname='beijing');

select flight.id,'beijing',flight.flightTime from flight where startcityid = (select cityid from city where cityname='beijing')

345. union和union all有什么不同?

假设我们有一个表Student,包括以下字段与数据:
drop table student;
create table student
(
id int primary key,
name nvarchar2(50) not null,
score number not null
);
insert into student values(1,'Aaron',78);
insert into student values(2,'Bill',76);
insert into student values(3,'Cindy',89);
insert into student values(4,'Damon',90);
insert into student values(5,'Ella',73);
insert into student values(6,'Frado',61);
insert into student values(7,'Gill',99);
insert into student values(8,'Hellen',56);
insert into student values(9,'Ivan',93);
insert into student values(10,'Jay',90);
commit;
Union和Union All的区别。 
select *
from student
where id < 4
union
select *
from student
where id > 2 and id < 6
结果将是
1    Aaron    78
2    Bill    76
3    Cindy    89
4    Damon    90
5    Ella    73
如果换成Union All连接两个结果集,则返回结果是:
1    Aaron    78
2    Bill    76
3    Cindy    89
3    Cindy    89
4    Damon    90
5    Ella    73
可以看到,Union和Union All的区别之一在于对重复结果的处理。

  UNION在进行表链接后会筛选掉重复的记录,所以在表链接后会对所产生的结果集进行排序运算,删除重复的记录再返回结果。实际大部分应用中是不会产生重复的记录,最常见的是过程表与历史表UNION。如:
select * from gc_dfys
union
select * from ls_jg_dfys
  这个SQL在运行时先取出两个表的结果,再用排序空间进行排序删除重复的记录,最后返回结果集,如果表数据量大的话可能会导致用磁盘进行排序。
 而UNION ALL只是简单的将两个结果合并后就返回。这样,如果返回的两个结果集中有重复的数据,那么返回的结果集就会包含重复的数据了。
 从效率上说,UNION ALL 要比UNION快很多,所以,如果可以确认合并的两个结果集中不包含重复的数据的话,那么就使用UNION ALL,

346. 分页语句

取出sql表中第31到40的记录(以自动增长ID为主键)
sql server方案1:
	select top 10 * from t where id not in (select top 30 id from t order by id ) orde by id
sql server方案2:
	select top 10 * from t where id in (select top 40 id from t order by id) order by id desc

mysql方案:select * from t order by id limit 30,10

oracle方案:select * from (select rownum r,* from t where r<=40) where r>30

--------------------待整理进去的内容-------------------------------------
pageSize=20;
pageNo = 5;

1.分页技术1(直接利用sql语句进行分页,效率最高和最推荐的)

mysql:sql = "select * from articles limit " + (pageNo-1)*pageSize + "," + pageSize;
oracle: sql = "select * from " +
								"(select rownum r,* from " +
									"(select * from articles order by postime desc)" +
								"where rownum<= " + pageNo*pageSize +") tmp " +
							"where r>" + (pageNo-1)*pageSize;
注释:第7行保证rownum的顺序是确定的,因为oracle的索引会造成rownum返回不同的值
简洋提示:没有order by时,rownum按顺序输出,一旦有了order by,rownum不按顺序输出了,这说明rownum是排序前的编号。如果对order by从句中的字段建立了索引,那么,rownum也是按顺序输出的,因为这时候生成原始的查询结果集时会参照索引表的顺序来构建。

sqlserver:sql = "select top 10 * from id not id(select top " + (pageNo-1)*pageSize + "id from articles)"

DataSource ds = new InitialContext().lookup(jndiurl);
Connection cn = ds.getConnection();
//"select * from user where id=?"  --->binary directive
PreparedStatement pstmt = cn.prepareSatement(sql);
ResultSet rs = pstmt.executeQuery()
while(rs.next())
{
	out.println(rs.getString(1));
}

2.不可滚动的游标
pageSize=20;
pageNo = 5;
cn = null
stmt = null;
rs = null;
try
{
sqlserver:sql = "select  * from articles";

DataSource ds = new InitialContext().lookup(jndiurl);
Connection cn = ds.getConnection();
//"select * from user where id=?"  --->binary directive
PreparedStatement pstmt = cn.prepareSatement(sql);
ResultSet rs = pstmt.executeQuery()
for(int j=0;j<(pageNo-1)*pageSize;j++)
{
	rs.next();
}

int i=0;

while(rs.next() && i<10)
{
	i++;
	out.println(rs.getString(1));
}
}
cacth(){}
finnaly
{
	if(rs!=null)try{rs.close();}catch(Exception e){}
	if(stm.........
	if(cn............
}

3.可滚动的游标
pageSize=20;
pageNo = 5;
cn = null
stmt = null;
rs = null;
try
{
sqlserver:sql = "select  * from articles";

DataSource ds = new InitialContext().lookup(jndiurl);
Connection cn = ds.getConnection();
//"select * from user where id=?"  --->binary directive
PreparedStatement pstmt = cn.prepareSatement(sql,ResultSet.TYPE_SCROLL_INSENSITIVE,...);
//根据上面这行代码的异常SQLFeatureNotSupportedException,就可判断驱动是否支持可滚动游标

ResultSet rs = pstmt.executeQuery()
rs.absolute((pageNo-1)*pageSize)
int i=0;
while(rs.next() && i<10)
{
	i++;
	out.println(rs.getString(1));
}
}
cacth(){}
finnaly
{
	if(rs!=null)try{rs.close();}catch(Exception e){}
	if(stm.........
	if(cn............
}

347. 用一条SQL语句 查询出每门课都大于80分的学生姓名

name   kecheng   fenshu 
张三     语文       81
张三     数学       75
李四     语文       76
李四     数学       90
王五     语文       81
王五     数学       100
王五     英语       90

准备数据的sql代码:
create table score(id int primary key auto_increment,name varchar(20),subject varchar(20),score int);
insert into score values 
(null,'张三','语文',81),
(null,'张三','数学',75),
(null,'李四','语文',76),
(null,'李四','数学',90),
(null,'王五','语文',81),
(null,'王五','数学',100),
(null,'王五 ','英语',90);

提示:当百思不得其解时,请理想思维,把小变成大做,把大变成小做,

答案:
A: select distinct name from score  where  name not in (select distinct name from score where score<=80)

B:select distince name t1 from score where 80< all (select score from score where name=t1);

348. 所有部门之间的比赛组合

一个叫department的表,里面只有一个字段name,一共有4条纪录,分别是a,b,c,d,对应四个球对,现在四个球对进行比赛,用一条sql语句显示所有可能的比赛组合.

答:select a.name, b.name 
from team a, team b 
where a.name < b.name

349. 每个月份的发生额都比101科目多的科目

请用SQL语句实现:从TestDB数据表中查询出所有月份的发生额都比101科目相应月份的发生额高的科目。请注意:TestDB中有很多科目,都有1-12月份的发生额。
AccID:科目代码,Occmonth:发生额月份,DebitOccur:发生额。
数据库名:JcyAudit,数据集:Select * from TestDB
准备数据的sql代码:
drop table if exists TestDB;
create table TestDB(id int primary key auto_increment,AccID varchar(20), Occmonth date, DebitOccur bigint);
insert into TestDB values 
(null,'101','1988-1-1',100),
(null,'101','1988-2-1',110),
(null,'101','1988-3-1',120),
(null,'101','1988-4-1',100),
(null,'101','1988-5-1',100),
(null,'101','1988-6-1',100),
(null,'101','1988-7-1',100),
(null,'101','1988-8-1',100);
--复制上面的数据,故意把第一个月份的发生额数字改小一点
insert into TestDB values 
(null,'102','1988-1-1',90),
(null,'102','1988-2-1',110),
(null,'102','1988-3-1',120),
(null,'102','1988-4-1',100),
(null,'102','1988-5-1',100),
(null,'102','1988-6-1',100),
(null,'102','1988-7-1',100),
(null,'102','1988-8-1',100);
--复制最上面的数据,故意把所有发生额数字改大一点
insert into TestDB values 
(null,'103','1988-1-1',150),
(null,'103','1988-2-1',160),
(null,'103','1988-3-1',180),
(null,'103','1988-4-1',120),
(null,'103','1988-5-1',120),
(null,'103','1988-6-1',120),
(null,'103','1988-7-1',120),
(null,'103','1988-8-1',120);
--复制最上面的数据,故意把所有发生额数字改大一点
insert into TestDB values 
(null,'104','1988-1-1',130),
(null,'104','1988-2-1',130),
(null,'104','1988-3-1',140),
(null,'104','1988-4-1',150),
(null,'104','1988-5-1',160),
(null,'104','1988-6-1',170),
(null,'104','1988-7-1',180),
(null,'104','1988-8-1',140);
--复制最上面的数据,故意把第二个月份的发生额数字改小一点
insert into TestDB values 
(null,'105','1988-1-1',100),
(null,'105','1988-2-1',80),
(null,'105','1988-3-1',120),
(null,'105','1988-4-1',100),
(null,'105','1988-5-1',100),
(null,'105','1988-6-1',100),
(null,'105','1988-7-1',100),
(null,'105','1988-8-1',100);
答案:
select distinct AccID from TestDB 
where AccID not in 
	(select TestDB.AccIDfrom TestDB,
		 (select * from TestDB where AccID='101') as db101 
	where TestDB.Occmonth=db101.Occmonth and TestDB.DebitOccur<=db101.DebitOccur
	);

350. 统计每年每月的信息

year  month amount
1991   1     1.1
1991   2     1.2
1991   3     1.3
1991   4     1.4
1992   1     2.1
1992   2     2.2
1992   3     2.3
1992   4     2.4
查成这样一个结果
year m1  m2  m3  m4
1991 1.1 1.2 1.3 1.4
1992 2.1 2.2 2.3 2.4 

提示:这个与工资条非常类似,与学生的科目成绩也很相似。

准备sql语句:
drop table if exists sales;
create table sales(id int auto_increment primary key,year varchar(10), month varchar(10), amount float(2,1));
insert into sales values
(null,'1991','1',1.1),
(null,'1991','2',1.2),
(null,'1991','3',1.3),
(null,'1991','4',1.4),
(null,'1992','1',2.1),
(null,'1992','2',2.2),
(null,'1992','3',2.3),
(null,'1992','4',2.4);

答案一、
select sales.year ,
(select t.amount from sales t where t.month='1' and t.year= sales.year) '1',
(select t.amount from sales t where t.month='1' and t.year= sales.year) '2',
(select t.amount from sales t where t.month='1' and t.year= sales.year) '3',
(select t.amount from sales t where t.month='1' and t.year= sales.year) as '4' 
from sales  group by year;

351. 显示文章标题,发帖人、最后回复时间

表:id,title,postuser,postdate,parentid
准备sql语句:
drop table if exists articles;
create table articles(id int auto_increment primary key,title varchar(50), postuser varchar(10), postdate datetime,parentid int references articles(id));
insert into articles values
(null,'第一条','张三','1998-10-10 12:32:32',null),
(null,'第二条','张三','1998-10-10 12:34:32',null),
(null,'第一条回复1','李四','1998-10-10 12:35:32',1),
(null,'第二条回复1','李四','1998-10-10 12:36:32',2),
(null,'第一条回复2','王五','1998-10-10 12:37:32',1),
(null,'第一条回复3','李四','1998-10-10 12:38:32',1),
(null,'第二条回复2','李四','1998-10-10 12:39:32',2),
(null,'第一条回复4','王五','1998-10-10 12:39:40',1);

答案:
select a.title,a.postuser,
	(select max(postdate) from articles where parentid=a.id) reply 
from articles a where a.parentid is null;

注释:子查询可以用在选择列中,也可用于where的比较条件中,还可以用于from从句中。

352. 删除除了id号不同,其他都相同的学生冗余信息

2.学生表 如下:
id号   学号   姓名 课程编号 课程名称 分数
1        2005001  张三  0001      数学    69
2        2005002  李四  0001      数学    89
3        2005001  张三  0001      数学    69
A: delete from tablename where id号 not in(select min(id号) from tablename group by 学号,姓名,课程编号,课程名称,分数)
实验:
create table student2(id int auto_increment primary key,code varchar(20),name varchar(20));
insert into student2 values(null,'2005001','张三'),(null,'2005002','李四'),(null,'2005001','张三');

//如下语句,mysql报告错误,可能删除依赖后面统计语句,而删除又导致统计语句结果不一致。

delete from student2 where id not in(select min(id) from student2 group by name);
//但是,如下语句没有问题:
select *  from student2 where id not in(select min(id) from student2 group by name);
//于是,我想先把分组的结果做成虚表,然后从虚表中选出结果,最后再将结果作为删除的条件数据。
delete from student2 where id not in(select mid from (select min(id) mid
from student2 group by name) as t);
或者:
delete from student2 where id not in(select min(id) from (select * from s
tudent2) as t group by t.name);

353. 航空网的几个航班查询题:

表结构如下:
flight{flightID,StartCityID ,endCityID,StartTime}
city{cityID, CityName)
实验环境:
create table city(cityID int auto_increment primary key,cityName varchar(20));
create table flight (flightID int auto_increment primary key,
	StartCityID int references city(cityID),
	endCityID  int references city(cityID),
	StartTime timestamp); 
//航班本来应该没有日期部分才好,但是下面的题目当中涉及到了日期
insert into city values(null,'北京'),(null,'上海'),(null,'广州');
insert into flight values
	(null,1,2,'9:37:23'),(null,1,3,'9:37:23'),(null,1,2,'10:37:23'),(null,2,3,'10:37:23');


1、查询起飞城市是北京的所有航班,按到达城市的名字排序


参与运算的列是我起码能够显示出来的那些列,但最终我不一定把它们显示出来。各个表组合出来的中间结果字段中必须包含所有运算的字段。

  select  * from flight f,city c 
	where f.endcityid = c.cityid and startcityid = 
	(select c1.cityid from city c1 where c1.cityname = "北京")
	order by c.cityname asc;

mysql> select flight.flightid,'北京' startcity, e.cityname from flight,city e wh
ere flight.endcityid=e.cityid and flight.startcityid=(select cityid from city wh
ere cityname='北京');

mysql> select flight.flightid,s.cityname,e.cityname from flight,city s,city e wh
ere flight.startcityid=s.cityid and s.cityname='北京' and flight.endCityId=e.cit
yID order by e.cityName desc;


2、查询北京到上海的所有航班纪录(起飞城市,到达城市,起飞时间,航班号)
select c1.CityName,c2.CityName,f.StartTime,f.flightID
from city c1,city c2,flight f
where f.StartCityID=c1.cityID 
and f.endCityID=c2.cityID
and c1.cityName='北京'
and c2.cityName='上海'
3、查询具体某一天(2005-5-8)的北京到上海的的航班次数
select count(*) from 
(select c1.CityName,c2.CityName,f.StartTime,f.flightID
from city c1,city c2,flight f
where f.StartCityID=c1.cityID 
and f.endCityID=c2.cityID
and c1.cityName='北京'
and c2.cityName='上海'
and 查帮助获得的某个日期处理函数(startTime) like '2005-5-8%'

mysql中提取日期部分进行比较的示例代码如下:
select * from flight where date_format(starttime,'%Y-%m-%d')='1998-01-02'

354. 查出比经理薪水还高的员工信息:

Drop table if not exists employees;
create table employees(id int primary key auto_increment,name varchar(50)
,salary int,managerid int references employees(id));
insert into employees values (null,' lhm',10000,null), (null,' zxx',15000,1
),(null,'flx',9000,1),(null,'tg',10000,2),(null,'wzg',10000,3);

Wzg大于flx,lhm大于zxx

解题思路:
     根据sql语句的查询特点,是逐行进行运算,不可能两行同时参与运算。
涉及了员工薪水和经理薪水,所有,一行记录要同时包含两个薪水,所有想到要把这个表自关联组合一下。
     首先要组合出一个包含有各个员工及该员工的经理信息的长记录,譬如,左半部分是员工,右半部分是经理。而迪卡尔积会组合出很多垃圾信息,先去除这些垃圾信息。

select e.* from employees e,employees m where e.managerid=m.id and e.sala
ry>m.salary;

355. 求出小于45岁的各个老师所带的大于12岁的学生人数

数据库中有3个表 teacher 表,student表,tea_stu关系表。 
teacher 表 teaID name age 
student 表 stuID name age 
teacher_student表 teaID stuID 
要求用一条sql查询出这样的结果 
1.显示的字段要有老师name, age 每个老师所带的学生人数 
2 只列出老师age为40以下,学生age为12以上的记录
预备知识:
      1.sql语句是对每一条记录依次处理,条件为真则执行动作(select,insert,delete,update)
       2.只要是迪卡尔积,就会产生“垃圾”信息,所以,只要迪卡尔积了,我们首先就要想到清除“垃圾”信息
实验准备:
	drop table if exists tea_stu;
	drop table if exists teacher;
	drop table if exists student;
      create table teacher(teaID int primary key,name varchar(50),age int);
      create table student(stuID int primary key,name varchar(50),age int);
      create table tea_stu(teaID int references teacher(teaID),stuID int references student(stuID));
insert into teacher values(1,'zxx',45), (2,'lhm',25) , (3,'wzg',26) , (4,'tg',27);
insert into student values(1,'wy',11), (2,'dh',25) , (3,'ysq',26) , (4,'mxc',27);
insert into tea_stu values(1,1), (1,2), (1,3);
insert into tea_stu values(2,2), (2,3), (2,4);
 insert into tea_stu values(3,3), (3,4), (3,1);
insert into tea_stu values(4,4), (4,1), (4,2) , (4,3);

结果:23,32,43

解题思路:(真实面试答题时,也要写出每个分析步骤,如果纸张不够,就找别人要)
1要会统计分组信息,统计信息放在中间表中:
select teaid,count(*) from tea_stu group by teaid;

2接着其实应该是筛除掉小于12岁的学生,然后再进行统计,中间表必须与student关联才能得到12岁以下学生和把该学生记录从中间表中剔除,代码是:
select tea_stu.teaid,count(*) total from student,tea_stu 
where student.stuid=tea_stu.stuid and student.age>12 group by tea_stu.teaid

3.接着把上面的结果做成虚表与teacher进行关联,并筛除大于45的老师
select teacher.teaid,teacher.name,total from teacher ,(select tea_stu.tea
id,count(*) total from student,tea_stu where student.stuid=tea_stu.stuid and stu
dent.age>12 group by tea_stu.teaid) as tea_stu2 where teacher.teaid=tea_stu2.tea
id and teacher.age<45;

356. 求出发帖最多的人:

select authorid,count(*) total from articles 
group by authorid 
having total=
(select max(total2) from (select count(*) total2 from articles group by authorid) as t);

select t.authorid,max(t.total) from
(select authorid,count(*) total from articles )as t
这条语句不行,因为max只有一列,不能与其他列混淆。

select authorid,count(*) total from articles 
group by authorid having total=max(total)也不行。

357. 一个用户表中有一个积分字段,假如数据库中有100多万个用户,若要在每年第一天凌晨将积分清零,你将考虑什么,你将想什么办法解决?

alter table drop column score;
alter table add colunm score int;
可能会很快,但是需要试验,试验不能拿真实的环境来操刀,并且要注意,
这样的操作时无法回滚的,在我的印象中,只有inert update delete等DML语句才能回滚,
对于create table,drop table ,alter table等DDL语句是不能回滚。


解决方案一,update user set score=0; 
解决方案二,假设上面的代码要执行好长时间,超出我们的容忍范围,那我就alter table user drop column score;alter table user add column score int。

下面代码实现每年的那个凌晨时刻进行清零。
Runnable runnable = 
	new Runnable(){
		public void run(){
			clearDb();
			schedule(this,new Date(new Date().getYear()+1,0,0));
			}		
			};

schedule(runnable,
	new Date(new Date().getYear()+1,0,1));

358. 一个用户具有多个角色,请查询出该表中具有该用户的所有角色的其他用户。

select count(*) as num,tb.id 
from 
 tb,
 (select role from tb where id=xxx) as t1
where
 tb.role = t1.role and tb.id != t1.id
group by tb.id 
having 
	num = select count(role) from tb where id=xxx;

359. xxx公司的sql面试

Table EMPLOYEES Structure:
EMPLOYEE_ID      NUMBER        Primary Key,
FIRST_NAME       VARCHAR2(25),
LAST_NAME       VARCHAR2(25),
Salary number(8,2),
HiredDate DATE,
Departmentid number(2)
Table Departments Structure:
Departmentid number(2)        Primary Key,
DepartmentName  VARCHAR2(25).

 (2)基于上述EMPLOYEES表写出查询:写出雇用日期在今年的,或者工资在[1000,2000]之间的,或者员工姓名(last_name)以’Obama’打头的所有员工,列出这些员工的全部个人信息。(4分)
select * from employees 
where Year(hiredDate) = Year(date()) 
	or (salary between 1000 and 200)
	or left(last_name,3)='abc';

(3) 基于上述EMPLOYEES表写出查询:查出部门平均工资大于1800元的部门的所有员工,列出这些员工的全部个人信息。(4分)
mysql> select id,name,salary,deptid did from employee1 where (select avg(salary)
 from employee1 where deptid = did) > 1800;

(4) 基于上述EMPLOYEES表写出查询:查出个人工资高于其所在部门平均工资的员工,列出这些员工的全部个人信息及该员工工资高出部门平均工资百分比。(5分)
select employee1.*,(employee1.salary-t.avgSalary)*100/employee1.salary 
from employee1,
	(select deptid,avg(salary) avgSalary from employee1 group by deptid) as t
where employee1.deptid = t.deptid and employee1.salary>t.avgSalary;

360. 注册Jdbc驱动程序的三种方式

1、比较常用
try{
       Class.forName("com.mysql.jdbc.Driver");//加载数据库驱动
       String url="jdbc:mysql://localhost:3306/databasename";//数据库连接子协议
       Connection conn=DriverManager.getConnection(url,"username","password");
       Statement stmt=conn.createStatement();
       ResultSet rs=stmt.executeQuery("select * from tablename");
       while(rs.next()){//不断指向下一条记录
            System.out.println("DeptNo:"+rs.getInt(1));
            System.out.println("\tDeptName:"+rs.getString(2));
            System.out.println("\tLOC:"+rs.getString(3));
}         
    rs.close();
    stmt.close();
    conn.close();
}catch(ClassNotFoundException e){
   System.out.println("找不到指定的驱动程序类!");
}catch(SQLException e){
    e.printStackTrace();
}
 
 
2、通过系统的属性设置
try{
       System.setProperty("jdbc.driver","com.mysql.jdbc.Driver");//系统属性指定数据库驱动
       String url="jdbc:mysql://localhost:3306/databasename";//数据库连接子协议
       Connection conn=DriverManager.getConnection(url,"username","password");
       Statement stmt=conn.createStatement();
       ResultSet rs=stmt.executeQuery("select * from tablename");
       while(rs.next()){//不断指向下一条记录
            System.out.println("DeptNo:"+rs.getInt(1));
            System.out.println("\tDeptName:"+rs.getString(2));
            System.out.println("\tLOC:"+rs.getString(3));
}         
    rs.close();
    stmt.close();
    conn.close();
}catch(SQLException e){
    e.printStackTrace();
}

 
3、看起来比较直观的一种方式,注册相应的db的jdbc驱动,3在编译时需要导入对应的lib
try{
       new com.mysql.jdbc.Driver();//创建driver对象,加载数据库驱动
       String url="jdbc:mysql://localhost:3306/databasename";//数据库连接子协议
       Connection conn=DriverManager.getConnection(url,"username","password");
       Statement stmt=conn.createStatement();
       ResultSet rs=stmt.executeQuery("select * from tablename");
       while(rs.next()){//不断指向下一条记录
            System.out.println("DeptNo:"+rs.getInt(1));
            System.out.println("\tDeptName:"+rs.getString(2));
            System.out.println("\tLOC:"+rs.getString(3));
}         
    rs.close();
    stmt.close();
    conn.close();
}catch(SQLException e){
    e.printStackTrace();
}

361. 用JDBC如何调用存储过程

代码如下:
package com.huawei.interview.lym;

import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Types;

public class JdbcTest {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Connection cn = null;
		CallableStatement cstmt = null;		
		try {
			//这里最好不要这么干,因为驱动名写死在程序中了
			Class.forName("com.mysql.jdbc.Driver");
			//实际项目中,这里应用DataSource数据,如果用框架,
			//这个数据源不需要我们编码创建,我们只需Datasource ds = context.lookup()
			//cn = ds.getConnection();			
			cn = DriverManager.getConnection("jdbc:mysql:///test","root","root");
			cstmt = cn.prepareCall("{call insert_Student(?,?,?)}");
			cstmt.registerOutParameter(3,Types.INTEGER);
			cstmt.setString(1, "wangwu");
			cstmt.setInt(2, 25);
			cstmt.execute();
			//get第几个,不同的数据库不一样,建议不写
			System.out.println(cstmt.getString(3));
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		finally
		{

			/*try{cstmt.close();}catch(Exception e){}
			try{cn.close();}catch(Exception e){}*/
			try {
				if(cstmt != null)
					cstmt.close();
				if(cn != null)				
					cn.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}

362. JDBC中的PreparedStatement相比Statement的好处

答:一个sql命令发给服务器去执行的步骤为:语法检查,语义分析,编译成内部指令,缓存指令,执行指令等过程。
select * from student where id =3----缓存--xxxxx二进制命令
select * from student where id =3----直接取-xxxxx二进制命令
select * from student where id =4--- -会怎么干?
如果当初是select * from student where id =?--- -又会怎么干?
 上面说的是性能提高
可以防止sql注入。

363. 写一个用jdbc连接并访问oracle数据的程序代码

import java.sql.Connection;

import java.sql.DriverManager;

import java.sql.ResultSet;

import java.sql.SQLException;

import java.sql.Statement;

 

public class TestDemo

{  

    private static String url="jdbc:oracle:thin:@localhost:1521:orcl";     

    private static String user="system";    

    private static String password="manager";    

    private static Connection conn;    

    private Statement statement = null;

    private ResultSet rs = null;


    public void getConnection(){    

        try {    

            Class.forName("oracle.jdbc.driver.OracleDriver");    

            conn=DriverManager.getConnection(url, user, password); 

             

            statement = conn.createStatement();

                 

        } catch (Exception e) {    

            e.printStackTrace();    

        }    

    } 

    public ResultSet executeQuery(String sql){

        try{

            rs=statement.executeQuery(sql);

        }

        catch (SQLException e){

            e.printStackTrace();

        }

        return rs;

    }

    public void close(){

        try{

            rs.close();

            statement.close();

            conn.close();

        }

        catch (Exception e){

            e.printStackTrace();

        }

    }


    public static void main(String[] args){

        ResultSet rs;

        TestDemo testDemo = new TestDemo();

        testDemo.getConnection();

        rs=testDemo.executeQuery("select * from test");

        try{

          while (rs.next()){

              //do something

          }

      }catch(Exception e){

          e.printStackTrace();

      }

      testDemo.close();

   }

}

364. Class.forName的作用?为什么要用?

按参数中指定的字符串形式的类名去搜索并加载相应的类,如果该类字节码已经被加载过,则返回代表该字节码的Class实例对象,否则,按类加载器的委托机制去搜索和加载该类,如果所有的类加载器都无法加载到该类,则抛出ClassNotFoundException。加载完这个Class字节码后,接着就可以使用Class字节码的newInstance方法去创建该类的实例对象了。
有时候,我们程序中所有使用的具体类名在设计时(即开发时)无法确定,只有程序运行时才能确定,这时候就需要使用Class.forName去动态加载该类,这个类名通常是在配置文件中配置的,例如,spring的ioc中每次依赖注入的具体类就是这样配置的,jdbc的驱动类名通常也是通过配置文件来配置的,以便在产品交付使用后不用修改源程序就可以更换驱动类名。

365. 大数据量下的分页解决方法。

最好的办法是利用sql语句进行分页,这样每次查询出的结果集中就只包含某页的数据内容。再sql语句无法实现分页的情况下,可以考虑对大的结果集通过游标定位方式来获取某页的数据。
sql语句分页,不同的数据库下的分页方案各不一样,下面是主流的三种数据库的分页sql:
sql server:
	String sql = 
	"select top " + pageSize + " * from students where id not in" +

 "(select top " + pageSize * (pageNumber-1) + " id from students order by id)" + 
 
 "order by id";

mysql:
  
	String sql = 
	"select * from students order by id limit " + pageSize*(pageNumber-1) + "," + pageSize;
	
oracle:
 
	String sql = 
	 "select * from " +  
	 (select *,rownum rid from (select * from students order by postime desc) where rid<=" + pagesize*pagenumber + ") as t" + 
	 "where t>" + pageSize*(pageNumber-1);

366. 用 JDBC 查询学生成绩单, 把主要代码写出来(考试概率极大).

Connection cn = null;
PreparedStatement pstmt =null;
Resultset rs = null;
try
{
	Class.forname(driveClassName);
	cn =  DriverManager.getConnection(url,username,password);
	pstmt = cn.prepareStatement(“select  score.* from score ,student “ + 
		“where score.stuId = student.id and student.name = ?”);
	pstmt.setString(1,studentName);
	Resultset rs = pstmt.executeQuery();
	while(rs.next())
	{
		system.out.println(rs.getInt(“subject”)  +  “    ” + rs.getFloat(“score”) );
	}
}catch(Exception e){e.printStackTrace();}
finally
{
	if(rs != null) try{ rs.close() }catch(exception e){}
	if(pstmt != null) try{pstmt.close()}catch(exception e){}
	if(cn != null) try{ cn.close() }catch(exception e){}
}

367. 这段代码有什么不足之处?

try {
Connection conn = ...;
Statement stmt = ...;
ResultSet rs = stmt.executeQuery("select * from table1");
while(rs.next()) {
}
} catch(Exception ex) {
} 
答:没有finally语句来关闭各个对象,另外,使用finally之后,要把变量的定义放在try语句块的外面,以便在try语句块之外的finally块中仍可以访问这些变量。

368. 说出数据连接池的工作机制是什么?

J2EE服务器启动时会建立一定数量的池连接,并一直维持不少于此数目的池连接。客户端程序需要连接时,池驱动程序会返回一个未使用的池连接并将其表记为忙。如果当前没有空闲连接,池驱动程序就新建一定数量的连接,新建连接的数量有配置参数决定。当使用的池连接调用完成后,池驱动程序将此连接表记为空闲,其他调用就可以使用这个连接。 
实现方式,返回的Connection是原始Connection的代理,代理Connection的close方法不是真正关连接,而是把它代理的Connection对象还回到连接池中。

369. 为什么要用 ORM? 和 JDBC 有何不一样?

orm是一种思想,就是把object转变成数据库中的记录,或者把数据库中的记录转变成objecdt,我们可以用jdbc来实现这种思想,其实,如果我们的项目是严格按照oop方式编写的话,我们的jdbc程序不管是有意还是无意,就已经在实现orm的工作了。
现在有许多orm工具,它们底层调用jdbc来实现了orm工作,我们直接使用这些工具,就省去了直接使用jdbc的繁琐细节,提高了开发效率,现在用的较多的orm工具是hibernate。也听说一些其他orm工具,如toplink,ojb等。 

370. xml有哪些解析技术?区别是什么?

有DOM,SAX,STAX等 
DOM:处理大型文件时其性能下降的非常厉害。这个问题是由DOM的树结构所造成的,这种结构占用的内存较多,而且DOM必须在解析文件之前把整个文档装入内存,适合对XML的随机访问SAX:不现于DOM,SAX是事件驱动型的XML解析方式。它顺序读取XML文件,不需要一次全部装载整个文件。当遇到像文件开头,文档结束,或者标签开头与标签结束时,它会触发一个事件,用户通过在其回调事件中写入处理代码来处理XML文件,适合对XML的顺序访问 
STAX:Streaming API for XML (StAX) 
讲解这些区别是不需要特别去比较,就像说传智播客与其他培训机构的区别时,我们只需说清楚传智播客有什么特点和优点就行了,这就已经间接回答了彼此的区别。

371. 你在项目中用到了xml技术的哪些方面?如何实现的?

用到了数据存贮,信息配置两方面。在做数据交换平台时,将不能数据源的数据组装成XML文件,然后将XML文件压缩打包加密后通过网络传送给接收者,接收解密与解压缩后再同XML文件中还原相关信息进行处理。在做软件配置时,利用XML可以很方便的进行,软件的各种配置参数都存贮在XML文件中。 

372. 用jdom解析xml文件时如何解决中文问题?如何解析?

看如下代码,用编码方式加以解决 
package test; 
import java.io.*; 
public class DOMTest 
{ 
private String inFile = "c:\\people.xml" 
private String outFile = "c:\\people.xml" 
public static void main(String args[]) 
{ 
new DOMTest(); 
} 
public DOMTest() 
{ 
try 
{ 
javax.xml.parsers.DocumentBuilder builder = 
javax.xml.parsers.DocumentBuilderFactory.newInstance().newDocumentBuilder(); 
org.w3c.dom.Document doc = builder.newDocument(); 
org.w3c.dom.Element root = doc.createElement("老师"); 
org.w3c.dom.Element wang = doc.createElement("王"); 
org.w3c.dom.Element liu = doc.createElement("刘"); 
wang.appendChild(doc.createTextNode("我是王老师")); 
root.appendChild(wang); 
doc.appendChild(root); 
javax.xml.transform.Transformer transformer = 
javax.xml.transform.TransformerFactory.newInstance().newTransformer(); 
transformer.setOutputProperty(javax.xml.transform.OutputKeys.ENCODING, "gb2312"); 
transformer.setOutputProperty(javax.xml.transform.OutputKeys.INDENT, "yes"); 
transformer.transform(new javax.xml.transform.dom.DOMSource(doc), 
new 
javax.xml.transform.stream.StreamResult(outFile)); 
} 
catch (Exception e) 
{ 
System.out.println (e.getMessage()); 
} 
} 
} 

373. 编程用JAVA解析XML的方式

用SAX方式解析XML,XML文件如下: 
<?xml version=1.0 encoding=gb2312?> 
<person> 
<name>王小明</name> 
<college>信息学院</college> 
<telephone>6258113</telephone> 
<notes>男,1955年生,博士,95年调入海南大学</notes> 
</person> 
事件回调类SAXHandler.java 
import java.io.*; 
import java.util.Hashtable; 
import org.xml.sax.*; 
public class SAXHandler extends HandlerBase 
{ 
private Hashtable table = new Hashtable(); 
private String currentElement = null; 
private String currentValue = null; 
public void setTable(Hashtable table) 
{ 
this.table = table; 
} 
public Hashtable getTable() 
{ 
return table; 
} 
public void startElement(String tag, AttributeList attrs) 
throws SAXException 
{ 
currentElement = tag; 
} 
public void characters(char[] ch, int start, int length) 
throws SAXException 
{ 
currentValue = new String(ch, start, length); 
} 
public void endElement(String name) throws SAXException 
{ 
if (currentElement.equals(name)) 
table.put(currentElement, currentValue); 
} 

} 
JSP内容显示源码,SaxXml.jsp: 
<HTML> 
<HEAD> 
<TITLE>剖析XML文件people.xml</TITLE> 
</HEAD> 
<BODY> 
<%@ page errorPage=ErrPage.jsp 
contentType=text/html;charset=GB2312 %> 
<%@ page import=java.io.* %> 
<%@ page import=java.util.Hashtable %> 
<%@ page import=org.w3c.dom.* %> 
<%@ page import=org.xml.sax.* %> 
<%@ page import=javax.xml.parsers.SAXParserFactory %> 
<%@ page import=javax.xml.parsers.SAXParser %> 
<%@ page import=SAXHandler %> 
<% 
File file = new File(c:\people.xml); 
FileReader reader = new FileReader(file); 
Parser parser; 
SAXParserFactory spf = SAXParserFactory.newInstance(); 
SAXParser sp = spf.newSAXParser(); 
SAXHandler handler = new SAXHandler(); 
sp.parse(new InputSource(reader), handler); 
Hashtable hashTable = handler.getTable(); 
out.println(<TABLE BORDER=2><CAPTION>教师信息表</CAPTION>); 
out.println(<TR><TD>姓名</TD> + <TD> + 
(String)hashTable.get(new String(name)) + </TD></TR>); 
out.println(<TR><TD>学院</TD> + <TD> + 
(String)hashTable.get(new String(college))+</TD></TR>); 
out.println(<TR><TD>电话</TD> + <TD> + 
(String)hashTable.get(new String(telephone)) + </TD></TR>); 
out.println(<TR><TD>备注</TD> + <TD> + 
(String)hashTable.get(new String(notes)) + </TD></TR>); 
out.println(</TABLE>); 
%> 
</BODY> 
</HTML> 

374. XML文档定义有几种形式?它们之间有何本质区别?解析XML文档有哪几种方式?

a: 两种形式 dtd  schema,b: 本质区别:schema本身是xml的,可以被XML解析器解析(这也是从DTD上发展schema的根本目的),c:有DOM,SAX,STAX等 
   DOM:处理大型文件时其性能下降的非常厉害。这个问题是由DOM的树结构所造成的,这种结构占用的内存较多,而且DOM必须在解析文件之前把整个文档装入内存,适合对XML的随机访问 
SAX:不现于DOM,SAX是事件驱动型的XML解析方式。它顺序读取XML文件,不需要一次全部装载整个文件。当遇到像文件开头,文档结束,或者标签开头与标签结束时,它会触发一个事件,用户通过在其回调事件中写入处理代码来处理XML文件,适合对XML的顺序访问 
   STAX:Streaming API for XML (StAX) 

375. 你对Struts的理解

1. struts是一个按MVC模式设计的Web层框架,其实它就是一个大的servlet,这个Servlet名为ActionServlet,或是ActionServlet的子类。我们可以在web.xml文件中将符合某种特征的所有请求交给这个Servlet处理,这个Servlet再参照一个配置文件(通常为/WEB-INF/struts-config.xml)将各个请求分别分配给不同的action去处理。
一个扩展知识点:struts的配置文件可以有多个,可以按模块配置各自的配置文件,这样可以防止配置文件的过度膨胀;
2. ActionServlet把请求交给action去处理之前,会将请求参数封装成一个formbean对象(就是一个java类,这个类中的每个属性对应一个请求参数),封装成一个什么样的formbean对象呢?看配置文件。
3.要说明的是, ActionServlet把formbean对象传递给action的execute方法之前,可能会调用formbean的validate方法进行校验,只有校验通过后才将这个formbean对象传递给action的execute方法,否则,它将返回一个错误页面,这个错误页面由input属性指定,(看配置文件)作者为什么将这里命名为input属性,而不是error属性,我们后面结合实际的运行效果进行分析。
4.action执行完后要返回显示的结果视图,这个结果视图是用一个ActionForward对象来表示的,actionforward对象通过struts-config.xml配置文件中的配置关联到某个jsp页面,因为程序中使用的是在struts-config.xml配置文件为jsp页面设置的逻辑名,这样可以实现action程序代码与返回的jsp页面名称的解耦。

你对struts可能还有自己的应用方面的经验,那也要一并说出来。

376. 对Hibernate的理解

(持久化对象)使用Hibernate的基本流程是:配置Configuration对象、产生SessionFactory、创建session对象,启动事务,完成CRUD操作,提交事务,关闭session。
在应用Hibernate时,重点要了解Session的缓存原理,级联,延迟加载和hql查询。
1. 面向对象设计的软件内部运行过程可以理解成就是在不断创建各种新对象、建立对象之间的关系,调用对象的方法来改变各个对象的状态和对象消亡的过程,不管程序运行的过程和操作怎么样,本质上都是要得到一个结果,程序上一个时刻和下一个时刻的运行结果的差异就表现在内存中的对象状态发生了变化。
2.为了在关机和内存空间不够的状况下,保持程序的运行状态,需要将内存中的对象状态保存到持久化设备和从持久化设备中恢复出对象的状态,通常都是保存到关系数据库来保存大量对象信息。从Java程序的运行功能上来讲,保存对象状态的功能相比系统运行的其他功能来说,应该是一个很不起眼的附属功能,java采用jdbc来实现这个功能,这个不起眼的功能却要编写大量的代码,而做的事情仅仅是保存对象和恢复对象,并且那些大量的jdbc代码并没有什么技术含量,基本上是采用一套例行公事的标准代码模板来编写,是一种苦活和重复性的工作。
3.通过数据库保存java程序运行时产生的对象和恢复对象,其实就是实现了java对象与关系数据库记录的映射关系,称为ORM(即Object Relation Mapping),人们可以通过封装JDBC代码来实现了这种功能,封装出来的产品称之为ORM框架,Hibernate就是其中的一种流行ORM框架。使用Hibernate框架,不用写JDBC代码,仅仅是调用一个save方法,就可以将对象保存到关系数据库中,仅仅是调用一个get方法,就可以从数据库中加载出一个对象。
4.使用Hibernate的基本流程是:配置Configuration对象、产生SessionFactory、创建session对象,启动事务,完成CRUD操作,提交事务,关闭session。
5.使用Hibernate时,先要配置hibernate.cfg.xml文件,其中配置数据库连接信息和方言等,还要为每个实体配置相应的hbm.xml文件,hibernate.cfg.xml文件中需要登记每个hbm.xml文件。
6.在应用Hibernate时,重点要了解Session的缓存原理,级联,延迟加载和hql查询。

377. AOP的作用

许多 Java 开发人员已经接受了面向方面编程(AOP)的非强制性风格和灵活性,特别是在用于建立高度松散和可扩展的企业系统时
在将商业需求转换成软件功能的快速开发周期中,面向方面是一种可以利用的强有力的设计原理。通过将首要的设计重点从面向对象编程(OOP)的传统化特性转移出来,AOP 和设计原理允许软件架构师用一种和面向对象相当而又互补的方式来考虑设计。
AOP是OOP的延续,是Aspect Oriented Programming的缩写,意思是面向方面编程。AOP实际是GoF设计模式的延续,设计模式孜孜不倦追求的是调用者和被调用者之间的解耦,AOP可以说也是这种目标的一种实现。
面向方面编程 AOP(Aspect Oriented Programming)是一种“关注点(Concern)”分离技术,通过运用“方面(aspect)”这种程序设计单元,允许开发者使用结构化的设计和编码,反映出其对系统的认识方式。AOP使设计和编码更加模块化、更加具结构化,从而使关注点局部化而不是分散于整个系统中。同时,需要定义好关注点和系统其他部分的接口,从而真正达到“分离关注点,分而治之”的目的。

378. 对Spring的理解

1.Spring实现了工厂模式的工厂类(在这里有必要解释清楚什么是工厂模式(提供创建对象的接口. 为系统结构提供了非常灵活强大的动态扩展机制,只要我们更换一下具体的工厂方法,系统其他地方无需一点变换,就有可能将系统功能进行改头换面的变化。)),这个类名为BeanFactory(实际上是一个接口),在程序中通常BeanFactory的子类ApplicationContext。Spring相当于一个大的工厂类,在其配置文件中通过<bean>元素配置用于创建实例对象的类名和实例对象的属性。
2. Spring提供了对IOC良好支持,IOC是一种编程思想,是一种架构艺术,利用这种思想可以很好地实现模块之间的解耦。IOC也称为DI(Depency Injection),什么叫依赖注入呢?
譬如,Class Programmer
{1.Spring实现了工厂模式的工厂类(在这里有必要解释清楚什么是工厂模式),这个类名为BeanFactory(实际上是一个接口),在程序中通常BeanFactory的子类ApplicationContext。Spring相当于一个大的工厂类,在其配置文件中通过<bean>元素配置用于创建实例对象的类名和实例对象的属性。

	Computer computer = null;
	public void code()
	{
		//Computer computer = new IBMComputer();
		//Computer computer = beanfacotry.getComputer();
		computer.write();
	}
	public void setComputer(Computer computer)
	{
		this.computer = computer;
	}
}
另外两种方式都由依赖,第一个直接依赖于目标类,第二个把依赖转移到工厂上,第三个彻底与目标和工厂解耦了。在spring的配置文件中配置片段如下:
<bean id=”computer” class=”cn.itcast.interview.Computer”>
</bean>

<bean id=”programmer” class=”cn.itcast.interview.Programmer”>
	<property name=”computer”  ref=”computer”></property>
</bean>
3. Spring提供了对AOP技术的良好封装, AOP称为面向切面编程,就是系统中有很多各不相干的类的方法,在这些众多方法中要加入某种系统功能的代码,例如,加入日志,加入权限判断,加入异常处理,这种应用称为AOP。实现AOP功能采用的是代理技术,客户端程序不再调用目标,而调用代理类,代理类与目标类对外具有相同的方法声明,有两种方式可以实现相同的方法声明,一是实现相同的接口,二是作为目标的子类在,JDK中采用Proxy类产生动态代理的方式为某个接口生成实现类,如果要为某个类生成子类,则可以用CGLI B。在生成的代理类的方法中加入系统功能和调用目标类的相应方法,系统功能的代理以Advice对象进行提供,显然要创建出代理对象,至少需要目标类和Advice类。spring提供了这种支持,只需要在spring配置文件中配置这两个元素即可实现代理和aop功能,例如,
<bean id=”proxy” type=”org.spring.framework.aop.ProxyBeanFactory”>
	<property name=”target” ref=””></property>
	<property name=”advisor” ref=””></property>

</bean>

379. 谈谈Struts中的Action servlet。

Servlet处理客户端请求的步骤:

1、Web客户向Servlet容器(Tomcat)发出Http请求

2、Servlet容器分析客户的请求信息

3、Servlet容器创建一个HttpServletRequest对象,将客户请求的信息封装到这个对象中

4、Servlet容器创建一个HttpServletResponse对象 

5、Servlet容器根据请求消息中的URL找到对应的servlet,然后针对该请求创建一个单独的线程,并把HttpRequest对象与HttpResponse对象作为参数传给新创建的线程中

6、容器调用Servlet对象的service()方法,完成对用户请求的响应,同时把动态页面返回给容器。

(或者是HttpServlet调用HttpRequest对象的有关方法,获取Http请求信息;调用HttpResponse对象的有关方法,生成响应数据)

7、Servlet容器把响应结果组装成HTTP格式传给Web客户

Struts2也是在容器上执行的,实际上虽然没有用到Servlet,但是前四步是一样的,会创建HttpServletRequest对象和HttpServletResponse对象,所以,在struts2.0中,可以通过ServletActionContext.getRequest()获取request对象。
可以说Struts2本质还是一个Servlet(在S1中用于整体控制的是一个Servlet;S2中是一个Filter,其实也可把它看作是Servlet),只是对Servlet进一步的封装罢了。
    打个比方:做Web项目,好比建房子。Servlet好比木材,用一根根的木材搭,也可盖起房子,只是速度慢而已;
而Struts2好比是用木材做好的一个房子大概结构,要建房子时,只需把它拿过来,再往其里面添砖加瓦,修修补补,就能很快盖好房子,这要比直接使用Servlet要快得多。

优缺点:

1、Servlet更随意灵活,带来的就是规范性差、不易解读。

2、安全性,Servlet没有Struts2高。Servlet可能会带来session的乱用,造成信息泄露。

3、维护性,struts远胜于servlet。struts项目的代码分层很清晰,如果用servlet,代码结构不清晰,很容易引入新BUG。

380. Struts优缺点

优点:
 1. 实现MVC模式,结构清晰,使开发者只关注业务逻辑的实现.
2.有丰富的tag可以用 ,Struts的标记库(Taglib),如能灵活动用,则能大大提高开发效率
3. 页面导航
	使系统的脉络更加清晰。通过一个配置文件,即可把握整个系统各部分之间的联系,这对于后期的维护有着莫大的好处。尤其是当另一批开发者接手这个项目时,这种优势体现得更加明显。
4. 提供Exception处理机制 . 
5. 数据库链接池管理 
6. 支持I18N 
缺点
一、	转到展示层时,需要配置forward,如果有十个展示层的jsp,需要配置十次struts,而且还不包括有时候目录、文件变更,需要重新修改forward,注意,每次修改配置之后,要求重新部署整个项目,而tomcate这样的服务器,还必须重新启动服务器
二、	二、 Struts 的Action必需是thread-safe方式,它仅仅允许一个实例去处理所有的请求。所以action用到的所有的资源都必需统一同步,这个就引起了线程安全的问题。
三、	 测试不方便. Struts的每个Action都同Web层耦合在一起,这样它的测试依赖于Web容器,单元测试也很难实现。不过有一个Junit的扩展工具Struts TestCase可以实现它的单元测试。 
四、	 类型的转换. Struts的FormBean把所有的数据都作为String类型,它可以使用工具Commons-Beanutils进行类型转化。但它的转化都是在Class级别,而且转化的类型是不可配置的。类型转化时的错误信息返回给用户也是非常困难的。
五、	对Servlet的依赖性过强. Struts处理Action时必需要依赖ServletRequest 和ServletResponse,所有它摆脱不了Servlet容器。 
六、	 前端表达式语言方面.Struts集成了JSTL,所以它主要使用JSTL的表达式语言来获取数据。可是JSTL的表达式语言在Collection和索引属性方面处理显得很弱。 
七、	 对Action执行的控制困难. Struts创建一个Action,如果想控制它的执行顺序将会非常困难。甚至你要重新去写Servlet来实现你的这个功能需求。 
八、	 对Action 执行前和后的处理. Struts处理Action的时候是基于class的hierarchies,很难在action处理前和后进行操作。 
九、	 对事件支持不够. 在struts中,实际是一个表单Form对应一个Action类(或DispatchAction),换一句话说:在Struts中实际是一个表单只能 对应一个事件,struts这种事件方式称为application event,application event和component event相比是一种粗粒度的事件

381. STRUTS的应用(如STRUTS架构)

Struts是采用Java Servlet/JavaServer Pages技术,开发Web应用程序的开放源码的framework。 采用Struts能开发出基于MVC(Model-View-Controller)设计模式的应用构架。 Struts有如下的主要功能: 一.包含一个controller servlet,能将用户的请求发送到相应的Action对象。 二.JSP自由tag库,并且在controller servlet中提供关联支持,帮助开发员创建交互式表单应用。 三.提供了一系列实用对象:XML处理、通过Java reflection APIs自动处理JavaBeans属性、国际化的提示和消息。

382. 说说struts1与struts2的区别。

1.都是MVC的WEB框架,
2 struts1的老牌框架,应用很广泛,有很好的群众基础,使用它开发风险很小,成本更低!struts2虽然基于这个框架,但是应用群众并多,相对不成熟,未知的风险和变化很多,开发人员相对不好招,使用它开发项目的风险系数更大,用人成本更高!
3.struts2毕竟是站在前辈的基础设计出来,它会改善和完善struts1中的一些缺陷,struts1中一些悬而未决问题在struts2得到了解决。
4.struts1的前端控制器是一个Servlet,名称为ActionServlet,struts2的前端控制器是一个filter,在struts2.0中叫FilterDispatcher,在struts2.1中叫StrutsPrepareAndExecuteFilter。
5.struts1的action需要继承Action类,struts2的action可以不继承任何类;struts1对同一个路径的所有请求共享一个Action实例,struts2对同一个路径的每个请求分别使用一个独立Action实例对象,所有对于struts2的Action不用考虑线程安全问题。
6.在struts1中使用formbean封装请求参数,在struts2中直接使用action的属性来封装请求参数。
7.struts1中的多个业务方法放在一个Action中时(即继承DispatchAction时),要么都校验,要么都不校验;对于struts2,可以指定只对某个方法进行校验,当一个Action继承了ActionSupport且在这个类中只编写了validateXxx()方法,那么则只对Xxx()方法进行校验。    
(一个请求来了的执行流程进行分析,struts2是自动支持分模块开发,并可以不同模块设置不同的url前缀,这是通过package的namespace来实现的;struts2是支持多种类型的视图;struts2的视图地址可以是动态的,即视图的名称是支持变量方式的,举例,论坛发帖失败后回来还要传递boardid。视图内容显示方面:它的标签用ognl,要el强大很多,在国际化方面支持分模块管理,两个模块用到同样的key,对应不同的消息;)	
与Struts1不同,Struts2对用户的每一次请求都会创建一个Action,所以Struts2中的Action是线程安全的。
给我印象最深刻的是:struts配置文件中的redirect视图的url不能接受参数,而struts2配置文件中的redirect视图可以接受参数。

383. hibernate中的update()和saveOrUpdate()的区别,session的load()和get()的区别。

update是更新一个对象,针对的是已经存在的对象
saveOrupdate是根据实体判断,如果没有的话就保存实体,如果有实体的话才修改(对象的存在与否都不会有人格影响)
重点:最主要的区别就在于是否进行延迟加载 load不会立即访问数据库,当试图加载的数据不存在的时候,load方法返回未初始化的代理对象,而get方法会直接访问数据库,当试图加载的数据不存在的时候,直接返回null
(1) 如果未能发现符合条件的记录,get方法返回null,而load方法会抛出一个ObjectNotFoundException;
(2) load方法可返回实体的代理类实例,而get方法永远直接返回实体类;
(3) load方法可以充分利用内部缓存和二级缓存中的现有数据,而get方法则仅仅在内部缓存中进行数据查找,如没有发现对应数据,将越过二级缓存,直接调用SQL完成数据读取。 

384. 简述 Hibernate 和 JDBC 的优缺点? 如何书写一个 one to many 配置文件.

其实刚开始用hibernate时,也没觉得有多方便,感觉配置好复杂,什么状态、关联、缓存搞不清,而且复杂的查询很难实现。开发起来缩手缩脚,如履薄冰,觉得没有jdbc来的灵活,随心所欲。
hibernate用多了之后再使用jdbc,感觉jdbc好烦,老是要对应你数据库中的字段和程序中的实体。比如查询操作,hibernate中一句load(class,id) 就好了,而jdbc一直写rs.get(“你数据库字段名") ,你要一直对照数据库。hibernate的开发效率确实要高很多。
还有一点感触,jdbc和数据库耦合太高,要换数据库的话,代价大。

385. iBatis与Hibernate有什么不同?

相同点:屏蔽jdbc api的底层访问细节,使用我们不用与jdbc api打交道,就可以访问数据。
jdbc api编程流程固定,还将sql语句与java代码混杂在了一起,经常需要拼凑sql语句,细节很繁琐。
ibatis的好处:屏蔽jdbc api的底层访问细节;将sql语句与java代码进行分离;提供了将结果集自动封装称为实体对象和对象的集合的功能,queryForList返回对象集合,用queryForObject返回单个对象;提供了自动将实体对象的属性传递给sql语句的参数。
Hibernate是一个全自动的orm映射工具,它可以自动生成sql语句,ibatis需要我们自己在xml配置文件中写sql语句,hibernate要比ibatis功能负责和强大很多。因为hibernate自动生成sql语句,我们无法控制该语句,我们就无法去写特定的高效率的sql。对于一些不太复杂的sql查询,hibernate可以很好帮我们完成,但是,对于特别复杂的查询,hibernate就很难适应了,这时候用ibatis就是不错的选择,因为ibatis还是由我们自己写sql语句。

386. 写Hibernate的一对多和多对一双向关联的orm配置?

Annotations配置
@Entity
@Table(name="t_group")
publicclass Group {
    private Integer id;
    private String name;
    private Set<Person> persons=newHashSet<Person>();//set不允许重复,最适合数据库模型
    @Id
    @GeneratedValue
    public Integer getId() {
       returnid;
    }
    publicvoid setId(Integerid) {
       this.id = id;
    }
    @Column(name="t_name")
    public String getName() {
       returnname;
    }
    publicvoid setName(Stringname) {
       this.name = name;
    }
    @OneToMany(mappedBy="group")//意识是告诉Hibernate的关联关系应该设在Person,多的一方
//  只有OneToOne,OneToMany,ManyToMany上才有mappedBy属性,ManyToOne不存在该属性;
    public Set<Person>getPersons() {
       returnpersons;
    }
    publicvoidsetPersons(Set<Person> persons) {
       this.persons = persons;
    }
}


@Entity
@Table(name="t_person")
publicclass Person {
    private Integer id;
    private String name;
    private Integer age;
    private Group group;
     
    @ManyToOne
    public Group getGroup() {
       returngroup;
    }
    publicvoid setGroup(Groupgroup) {
       this.group = group;
    }
    @Id
    @GeneratedValue
    public Integer getId() {
       returnid;
    }
    publicvoid setId(Integerid) {
       this.id = id;
    }
    @Column(name="p_name")
    public String getName() {
       returnname;
    }
    publicvoid setName(Stringname) {
       this.name = name;
    }
    @Column(name="p_age")
    public Integer getAge() {
       returnage;
    }
    publicvoid setAge(Integerage) {
       this.age = age;
    }
}


XML配置
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC 
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="csg.hibernate.entity">
    <class name="Group" table="t_group">
       <id name="id">
           <column name="id"/>
           <generator class="native" />
       </id>
       <property name="name" />
       <set name="persons">
           <key column="groupId"/>
           <one-to-many class="csg.hibernate.entity.Person"/>
       </set>
    </class>
</hibernate-mapping>

387. hibernate的inverse属性的作用?

Inverse属性

Inverse属性,是在维护关联关系的时候起作用的。

      表示控制权是否转移。(在一的一方起作用)

 

Inverse , 控制反转。

Inverse = false  不反转;   当前方有控制权

                   True  控制反转; 当前方没有控制权

 

维护关联关系中,是否设置inverse属性:

         1.保存数据

                   有影响。

            如果设置控制反转,即inverse=true, 然后通过部门方维护关联关系。在保存部门的时候,同时保存员工,数据会保存,但关联关系不会维护。即外键字段为NULL

                  

         2.获取数据

                   无。

         3.解除关联关系?

                   有影响。

                  inverse=false,  可以解除关联

                  inverse=true,  当前方(部门)没有控制权,不能解除关联关系(不会生成update语句,也不会报错)

         4.删除数据对关联关系的影响?

                   有影响。

                   inverse=false,有控制权, 可以删除。先清空外键引用,再删除数据。

                   inverse=true,  没有控制权: 如果删除的记录有被外键引用,会报错,违反主外键引用约束!  如果删除的记录没有被引用,可以直接删除。

388. 在DAO中如何体现DAO设计模式?

1、DAO是个什么东西呢?
DAO(Data Access Object,数据访问对象)的主要功能是数据操作,在程序的标准开发架构中属于数据层的操作。
2、DAO的组成
五部分的说法是
- 数据库连接类: 连接数据库
- VO实体类: JavaBean,里面一堆的setter和getter
- DAO接口:  接口还能干啥,不就是让别人来实现吗,进行规范化管理。老板(接口)说了让你写报告,你要是不写就开除(报错)
- DAO实现类: 员工写报告的的具体过程(打开电脑,***,关闭电脑,汇报)
- DAO工厂类: 工厂不就是为了方便调用吗,要什么给什么。
六部分的说法是
- 数据库连接类: 连接数据库
- VO实体类: JavaBean,里面一堆的setter和getter
- DAO接口:  接口还能干啥,不就是让别人来实现吗,进行规范化管理。老板(接口)说了让你写报告,你要是不写就开除(报错)
- DAO实现真正类: 员工自己不写,嘿嘿,去找小明帮他写,所以小明成为了真正的实现类
- DAO的代理实现类: 员工这时候就是代理实现类
- DAO工厂类: 工厂不就是为了方便调用吗,要什么给什么。
3、Demo
    1、创建数据库(自己使用的是MySQL),咱们就随便找几个字段意思一下,比较重点不在这里
    create table books(
        bookid int auto_increment primary key,
        bookName varchar(50) not null,
        bookNumber int not null default 0,
        pubdate datetime not null,
        UNIQUE INDEX unique_id(bookid)
    )
    2、编写数据库的连接类 
    DatabaseConnection的代码如下
    package DB;
    
    import java.sql.Connection;
    public interface DatabaseConnection {
        //  取得数据库的连接
        public Connection getConnection();
        //  关闭数据库的连接
        public void close();
    }
    MySQLConnection的代码如下
    package DB;
    
    import java.sql.Connection;
    import java.sql.DriverManager;
    
    public class MySQLConnection implements DatabaseConnection{
        //数据库连接驱动
        private static final String DBDRIVER = "com.mysql.jdbc.Driver";
        //数据库连接地址,我这里直接将数据库名字web放在后面了
        private static final String DBULR = "jdbc:mysql://localhost:3306/web";
        // 数据库用户名   
        private static final String USERNAME = "root";
        // 数据库密码    
        private static final String PASSWORD = "root";
        private Connection conn;
        // 取得数据库连接
        public Connection getConnection() throws Exception{
            try {
                // 加载驱动程序
                Class.forName(DBDRIVER);
                // 取得数据库连接,获得资源句柄
                this.conn = DriverManager.getConnection(DBULR,USERNAME,PASSWORD);
            }catch(Exception e) {
                throw e;
            }
            return this.conn;
        }
        // 关闭数据库连接  
        public void close() throws Exception{
            if(this.conn != null) {
                try{
                    this.conn.close();
                }catch(Exception e) {
                    throw e;
                }
            }
        }
    }
    book类的代码如下
    package VO;
    
    import java.util.Date;
    
    public class Book {
        private int bookid;
        private String bookName;
        private int bookNumber;
        private Date pubdate;
        public int getBookid() {
            return bookid;
        }
        public void setBookid(int bookid) {
            this.bookid = bookid;
        }
        public String getBookName() {
            return bookName;
        }
        public void setBookName(String bookName) {
            this.bookName = bookName;
        }
        public int getBookNumber() {
            return bookNumber;
        }
        public void setBookNumber(int bookNumber) {
            this.bookNumber = bookNumber;
        }
        public Date getPubdate() {
            return pubdate;
        }
        public void setPubdate(Date pubdate) {
            this.pubdate = pubdate;
        }
    }
    bookDAO的代码如下
    package DAO;
    
    import java.sql.SQLException;
    import java.util.List;
    
    import VO.Book;
    
    public interface BookDAO {
        //  查找某一本书籍
        public Book findById(int bookid) throws Exception;
        // 显示所有书籍
        public List<Book> findAll() throws Exception;
        // 修改书籍信息
        public boolean doUpdate(Book book) throws Exception;
        // 删除书籍信息
        public boolean doDelete(Book book) throws Exception;
        // 新增书籍信息
        public boolean doCreate(Book book) throws Exception;
    }
    BookDAOImpl的代码如下
    package DAO;
    
    import java.sql.Connection;
    import java.sql.Date;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.util.ArrayList;
    import java.util.List;
    import VO.Book;
    
    public class BookDAOImpl implements BookDAO {
        private Connection conn;
        // 这里采用PreparedStatement进行预处理,主要是为了方式SQL注入
        // 关于网络安全,可以看我GitHub上面的一个文章
        // https://github.com/deng1234/webSecurity
        private PreparedStatement pstmt;
        // 在代理实现类中进行对象实例化的时候获得连接数据库的资源句柄    
        public BookDAOImpl(Connection conn) {
            this.conn = conn;
        }
        @Override
        // 根据id查找   
        public Book findById(int bookid) throws SQLException{
            Book book = new Book();
            String sql = "SELECT bookid, bookName, bookNumber, pubdate FROM books WHERE bookid = ?";
            try {
                this.pstmt = this.conn.prepareStatement(sql);
                this.pstmt.setInt(1, bookid);
                ResultSet rs = (ResultSet) this.pstmt.executeQuery();
                if(rs.next()) {
                    book.setBookid(rs.getInt(1));
                    book.setBookName(rs.getString(2));
                    book.setBookNumber(rs.getInt(3));
                    book.setPubdate(rs.getDate(4));
                }
            } catch (SQLException e) {
                throw e;
            }
            this.pstmt.close();
            return book;
        }
    
        @Override
        // 列出所有书籍
        public List<Book> findAll() throws SQLException {
            List<Book> books = new ArrayList<Book>();
            Book book;
            String sql = "SELECT bookid, bookName, bookNumber, pubdate FROM books";
            try {
                this.pstmt = this.conn.prepareStatement(sql);
                ResultSet rs = (ResultSet) this.pstmt.executeQuery();
                while(rs.next()) {
                    book = new Book();
                    book.setBookid(rs.getInt(1));
                    book.setBookName(rs.getString(2));
                    book.setBookNumber(rs.getInt(3));
                    book.setPubdate(rs.getDate(4));
                    books.add(book);
                }
            } catch (SQLException e) {
                throw e;
            }
            this.pstmt.close();
            return books;
        }
    
        @Override
        // 修改书籍信息
        public boolean doUpdate(Book book) throws SQLException {
            boolean flag = false;
            String sql = "UPDATE books SET bookName = ?, bookNumber = ?, pubdate = ? WHERE bookid = ?";
            try {
                this.pstmt = this.conn.prepareStatement(sql);
                this.pstmt.setString(1, book.getBookName());
                this.pstmt.setInt(2, book.getBookNumber());
                this.pstmt.setDate(3, new java.sql.Date(book.getPubdate().getTime()));
                this.pstmt.setInt(4, book.getBookid());
                if(this.pstmt.executeUpdate() > 0) {
                    flag = true;
                }
            } catch (SQLException e) {
                throw e;
            }
            this.pstmt.close();
            return flag;
        }
    
        @Override
        // 删除书籍信息
        public boolean doDelete(Book book) throws SQLException {
            boolean flag = false;
            String sql = "DELETE FROM books WHERE bookid = ?";
            try {
                this.pstmt = this.conn.prepareStatement(sql);
                this.pstmt.setInt(1, book.getBookid());
                if(this.pstmt.executeUpdate() > 0) {
                    flag = true;
                }
            } catch (SQLException e) {
                throw e;
            }
            this.pstmt.close();
            return flag;
        }
    
        @Override
        // 新增书籍
        public boolean doCreate(Book book) throws SQLException {
            boolean flag = false;
            String sql = "INSERT INTO books (bookName, bookNumber, pubdate) "
                    + "values(?, ?, ?)";
            try {
                this.pstmt = this.conn.prepareStatement(sql);
                this.pstmt.setString(1, book.getBookName());
                this.pstmt.setInt(2, book.getBookNumber());
                this.pstmt.setDate(3, new java.sql.Date(book.getPubdate().getTime()));
                if(this.pstmt.executeUpdate() > 0) {
                    flag = true;
                }
            } catch (SQLException e) {
                throw e;
            }
            this.pstmt.close();
            return flag;
        }
    
    }
    BookDAOProxy类的代码为
    package DAO;
    
    import java.sql.SQLException;
    import java.util.ArrayList;
    import java.util.List;
    
    import DB.MySQLConnection;
    import VO.Book;
    
    public class BookDAOProxy implements BookDAO{
        private MySQLConnection conn = null;
        private BookDAO dao;
        public BookDAOProxy() throws Exception {
            this.conn = new MySQLConnection();
            // 在这里对真正的DAO实现内进行实例化       
            this.dao = new BookDAOImpl(this.conn.getConnection());
        }
    
        @Override
        // 书籍查找
        public Book findById(int bookid) throws Exception {
            Book book = new Book();
            try {
                book = this.dao.findById(bookid);
            }catch(SQLException e) {
                throw e;
            }finally {
                // 关闭数据库
                this.conn.close();
            }
            return book;
        }
    
        @Override
        // 列出所有书籍
        public List<Book> findAll() throws Exception {
            List<Book> books = new ArrayList<Book>();
            try {
                books = this.dao.findAll();
            }catch(Exception e) {
                throw e;
            }finally {
                this.conn.close();
            }
            return books;
        }
    
        @Override
        public boolean doUpdate(Book book) throws Exception {
            boolean flag = false;
            try {
                if(this.dao.findById(book.getBookid()) != null) {
                    flag = this.dao.doUpdate(book);
                }
            }catch(Exception e) {
                throw e;
            }finally {
                this.conn.close();
            }
            return flag;
        }
    
        @Override
        public boolean doDelete(Book book) throws Exception {
            boolean flag = false;
            try {
                flag = this.dao.doDelete(book);
            }catch(Exception e) {
                throw e;
            }finally {
                this.conn.close();
            }
            return flag;
        }
    
        @Override
        public boolean doCreate(Book book) throws Exception {
            boolean flag = false;
            try {
                flag = this.dao.doCreate(book);
            }catch(Exception e) {
                throw e;
            }finally {
                this.conn.close();
            }
            return flag;
        }
    
    }
    DAOFactory的代码为
    package FACTORY;
    
    import DAO.BookDAO;
    import DAO.BookDAOProxy;
    
    public class DAOFactory {
        public static BookDAO getBookDAOInstance() throws Exception {
            return new BookDAOProxy();
        }
    }
    测试
    package Test;
    
    import FACTORY.DAOFactory;
    import VO.Book;
    
    public class doCreate {
        public static void main(String args[]) throws Exception {
            Book book = new Book();
            try {
                book.setBookName("Java特种兵");
                book.setBookNumber(1);
                book.setPubdate(new java.util.Date());
                boolean flag = DAOFactory.getBookDAOInstance().doCreate(book);
                System.out.println(flag);//true则是成功,false则是失败
            }catch(Exception e) {
                System.out.println(e);
            }
        }
    }

389. spring+Hibernate中委托方案怎么配置?

<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource" ref="oracleDataSource"/>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">
${hibernate.dialect}
</prop>
<prop key="hibernate.connection.release_mode">
after_transaction
</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.hbm2ddl.auto">${hibernate.hbm2ddl.auto}</prop>
<prop key="hibernate.temp.use_jdbc_metadata_defaults">false</prop>
</props>
</property>
<!e799bee5baa6e79fa5e98193e4b893e5b19e31333335313231-- Must references all OR mapping files. -->
<property name="defineMappingResources">
<list>
<value>classpath:*-*ql.xml</value>
</list>
</property>
</bean> 

390. hibernate进行多表查询每个表中各取几个字段,也就是说查询出来的结果集没有一个实体类与之对应如何解决;

返回的是一个List<Objcet[]>这样的类型,你可以另外创建一个DTO然后通过循环,把Object[]中的每一个值,与dto中的字段对应,组成一个List<dto>的对象,例如:

People , Card 一个人和身份证
People:id ,name, age; 
Card:id, num.
现在通过查询出People表中的id, name;
Card中的id, num; 
那么现在List<Object[]> 中的Object[]有四个值分别对应:
People表中的id, name;Card中的id, num。
那么我定义一个dto :InfoDTO.java

   Java code

public class InfoDTO{
    private int peopleId;
    private String name;
    private int cardId;
    private String num;
 
    ...
   //成员变量的set和get方法
    。。。
}
然后循环List<Object[]>, 取出Object[], obj, 数组是有序的,那么我们就可以把InfoDTO类型的dto引用;        

391. 介绍一下Hibernate的二级缓存

按照以下思路来回答:(1)首先说清楚什么是缓存,(2)再说有了hibernate的Session就是一级缓存,即有了一级缓存,为什么还要有二级缓存,(3)最后再说如何配置Hibernate的二级缓存。
(1)缓存就是把以前从数据库中查询出来和使用过的对象保存在内存中(一个数据结构中),这个数据结构通常是或类似Hashmap,当以后要使用某个对象时,先查询缓存中是否有这个对象,如果有则使用缓存中的对象,如果没有则去查询数据库,并将查询出来的对象保存在缓存中,以便下次使用。下面是缓存的伪代码:
引出hibernate的第二级缓存,用下面的伪代码分析了Cache的实现原理
Dao
{
	hashmap map = new map();
	User getUser(integer id)
	{
		User user = map.get(id)
		if(user == null)
		{
			user = session.get(id);
			map.put(id,user);
		}
		return user;
	}
}

Dao
{
	Cache cache = null
	setCache(Cache cache)
	{
		this.cache = cache
	}
	
	User getUser(int id)
	{
		if(cache!=null)
		{
			User user = cache.get(id);
			if(user ==null)
			{
				user = session.get(id);
				cache.put(id,user);
			}
			return user;
		}
		
		return session.get(id);
	}
}
(2)Hibernate的Session就是一种缓存,我们通常将之称为Hibernate的一级缓存,当想使用session从数据库中查询出一个对象时,Session也是先从自己内部查看是否存在这个对象,存在则直接返回,不存在才去访问数据库,并将查询的结果保存在自己内部。由于Session代表一次会话过程,一个Session与一个数据库连接相关连,所以Session最好不要长时间保持打开,通常仅用于一个事务当中,在事务结束时就应关闭。并且Session是线程不安全的,被多个线程共享时容易出现问题。通常只有那种全局意义上的缓存才是真正的缓存应用,才有较大的缓存价值,因此,Hibernate的Session这一级缓存的缓存作用并不明显,应用价值不大。Hibernate的二级缓存就是要为Hibernate配置一种全局缓存,让多个线程和多个事务都可以共享这个缓存。我们希望的是一个人使用过,其他人也可以使用,session没有这种效果。
(3)二级缓存是独立于Hibernate的软件部件,属于第三方的产品,多个厂商和组织都提供有缓存产品,例如,EHCache和OSCache等等。在Hibernate中使用二级缓存,首先就要在hibernate.cfg.xml配置文件中配置使用哪个厂家的缓存产品,接着需要配置该缓存产品自己的配置文件,最后要配置Hibernate中的哪些实体对象要纳入到二级缓存的管理中。明白了二级缓存原理和有了这个思路后,很容易配置起Hibernate的二级缓存。扩展知识:一个SessionFactory可以关联一个二级缓存,也即一个二级缓存只能负责缓存一个数据库中的数据,当使用Hibernate 的二级缓存后,注意不要有其他的应用或SessionFactory来更改当前数据库中的数据,这样缓存的数据就会与数据库中的实际数据不一致。

392. Spring 的依赖注入是什么意思? 给一个 Bean 的 message 属性, 字符串类型, 注入值为 “Hello” 的 XML 配置文件该怎么写?

 Spring 能有效地组织J2EE应用各层的对象。不管是控制层的Action对象,还是业务层的Service对象,还是持久层的DAO对象,都可在Spring的 管理下有机地协调、运行。Spring将各层的对象以松耦合的方式组织在一起,Action对象无须关心Service对象的具体实现,Service对 象无须关心持久层对象的具体实现,各层对象的调用完全面向接口。当系统需要重构时,代码的改写量将大大减少。

上面所说的一切都得宜于Spring的核心机制,依赖注入。依赖注入让bean与bean之间以配置文件组织在一起,而不是以硬编码的方式耦合在一起。理解依赖注入

依赖注入(Dependency Injection)和控制反转(Inversion of Control)是同一个概念。具体含义是:当某个角色(可能是一个Java实例,调用者)需要另一个角色(另一个Java实例,被调用者)的协助时,在 传统的程序设计过程中,通常由调用者来创建被调用者的实例。但在Spring里,创建被调用者的工作不再由调用者来完成,因此称为控制反转;创建被调用者 实例的工作通常由Spring容器来完成,然后注入调用者,因此也称为依赖注入。

不管是依赖注入,还是控制反转,都说明Spring采用动态、灵活的方式来管理各种对象。对象与对象之间的具体实现互相透明。在理解依赖注入之前,看如下这个问题在各种社会形态里如何解决:一个人(Java实例,调用者)需要一把斧子(Java实例,被调用者)。

(1)原始社会里,几乎没有社会分工。需要斧子的人(调用者)只能自己去磨一把斧子(被调用者)。对应的情形为:Java程序里的调用者自己创建被调用者。

(2)进入工业社会,工厂出现。斧子不再由普通人完成,而在工厂里被生产出来,此时需要斧子的人(调用者)找到工厂,购买斧子,无须关心斧子的制造过程。对应Java程序的简单工厂的设计模式。

(3)进入“按需分配”社会,需要斧子的人不需要找到工厂,坐在家里发出一个简单指令:需要斧子。斧子就自然出现在他面前。对应Spring的依赖注入。

第一种情况下,Java实例的调用者创建被调用的Java实例,必然要求被调用的Java类出现在调用者的代码里。无法实现二者之间的松耦合。

第二种情况下,调用者无须关心被调用者具体实现过程,只需要找到符合某种标准(接口)的实例,即可使用。此时调用的代码面向接口编程,可以让调用者和被调用者解耦,这也是工厂模式大量使用的原因。但调用者需要自己定位工厂,调用者与特定工厂耦合在一起。

第三种情况下,调用者无须自己定位工厂,程序运行到需要被调用者时,系统自动提供被调用者实例。事实上,调用者和被调用者都处于Spring的管理下,二者之间的依赖关系由Spring提供。

所谓依赖注入,是指程序运行过程中,如果需要调用另一个对象协助时,无须在代码中创建被调用者,而是依赖于外部的注入。Spring的依赖注入对调用者和被调用者几乎没有任何要求,完全支持对POJO之间依赖关系的管理。依赖注入通常有两种:

·设值注入。

·构造注入。
设值注入

  设值注入是指通过setter方法传入被调用者的实例。这种注入方式简单、直观,因而在Spring的依赖注入里大量使用。看下面代码,是Person的接口
//定义Person接口
public interface Person
{
//Person接口里定义一个使用斧子的方法
public void useAxe();
}

然后是Axe的接口
//定义Axe接口
public interface Axe
{
//Axe接口里有个砍的方法
public void chop();
}

Person的实现类
//Chinese实现Person接口

public class Chinese implements Person
{
//面向Axe接口编程,而不是具体的实现类
private Axe axe;
//默认的构造器
public Chinese()
{}
//设值注入所需的setter方法
public void setAxe(Axe axe)
{
this.axe = axe;
}
//实现Person接口的useAxe方法
public void useAxe()
{
System.out.println(axe.chop());
}
}

Axe的第一个实现类
//Axe的第一个实现类 StoneAxe

public class StoneAxe implements Axe
{
//默认构造器
public StoneAxe()
{}
//实现Axe接口的chop方法
public String chop()
{
return "石斧砍柴好慢";
}
}

下面采用Spring的配置文件将Person实例和Axe实例组织在一起。配置文件如下所示:
<!-- 下面是标准的XML文件头 -->
<?xml version="1.0" encoding="gb2312"?>
<!-- 下面一行定义Spring的XML配置文件的dtd -->
"http://www.springframework.org/dtd/spring-beans.dtd">
<!-- 以上三行对所有的Spring配置文件都是相同的 -->
<!-- Spring配置文件的根元素 -->
<BEANS>
<!—定义第一bean,该bean的id是chinese, class指定该bean实例的实现类 -->
<BEAN class=lee.Chinese id=chinese>
<!-- property元素用来指定需要容器注入的属性,axe属性需要容器注入此处是设值注入,因此Chinese类必须拥有setAxe方法 -->
<property name="axe">
<!-- 此处将另一个bean的引用注入给chinese bean -->
<REF local="”stoneAxe”/">
</property>
</BEAN>
<!-- 定义stoneAxe bean -->
<BEAN class=lee.StoneAxe id=stoneAxe />
</BEANS>

从配置文件中,可以看到Spring管理bean的灵巧性。bean与bean之间的依赖关系放在配置文件里组织,而不是写在代码里。通过配置文件的 指定,Spring能精确地为每个bean注入属性。因此,配置文件里的bean的class元素,不能仅仅是接口,而必须是真正的实现类。

Spring会自动接管每个bean定义里的property元素定义。Spring会在执行无参数的构造器后、创建默认的bean实例后,调用对应 的setter方法为程序注入属性值。property定义的属性值将不再由该bean来主动创建、管理,而改为被动接收Spring的注入。

每个bean的id属性是该bean的惟一标识,程序通过id属性访问bean,bean与bean的依赖关系也通过id属性完成。

下面看主程序部分:
public class BeanTest
{
//主方法,程序的入口
public static void main(String[] args)throws Exception
{
//因为是独立的应用程序,显式地实例化Spring的上下文。
ApplicationContext ctx = new FileSystemXmlApplicationContext("bean.xml");
//通过Person bean的id来获取bean实例,面向接口编程,因此
//此处强制类型转换为接口类型
Person p = (Person)ctx.getBean("chinese");
//直接执行Person的userAxe()方法。
p.useAxe();
}
}

程序的执行结果如下:

石斧砍柴好慢

主程序调用Person的useAxe()方法时,该方法的方法体内需要使用Axe的实例,但程序里没有任何地方将特定的Person实例和Axe实 例耦合在一起。或者说,程序里没有为Person实例传入Axe的实例,Axe实例由Spring在运行期间动态注入。

Person实例不仅不需要了解Axe实例的具体实现,甚至无须了解Axe的创建过程。程序在运行到需要Axe实例的时候,Spring创建了Axe 实例,然后注入给需要Axe实例的调用者。Person实例运行到需要Axe实例的地方,自然就产生了Axe实例,用来供Person实例使用。

调用者不仅无须关心被调用者的实现过程,连工厂定位都可以省略(真是按需分配啊!)。下面也给出使用Ant编译和运行该应用的简单脚本:
<?xml version="1.0"?>
<!-- 定义编译该项目的基本信息-->
<PROJECT name="spring" default="." basedir=".">
<!-- 定义编译和运行该项目时所需的库文件 -->
<PATH id=classpath>
<!-- 该路径下存放spring.jar和其他第三方类库 -->
<FILESET dir=../../lib>
<INCLUDE name="*.jar" />
</FILESET>
<!-- 同时还需要引用已经编译过的class文件-->
<PATHELEMENT path="." />
</PATH>
<!-- 编译全部的java文件-->
<TARGET description="Compile all source code" name="compile">
<!-- 指定编译后的class文件的存放位置 -->
<JAVAC debug="true" destdir=".">
deprecation="false" optimize="false" failοnerrοr="true">
<!-- 指定需要编译的源文件的存放位置 -->
<SRC path="." />
<!-- 指定编译这些java文件需要的类库位置-->
<CLASSPATH refid="classpath" />
</JAVAC>
</TARGET>
<!-- 运行特定的主程序 -->
<TARGET description="run the main class" name="run" depends="compile">
<!-- 指定运行的主程序:lee.BeanTest。-->
<JAVA failοnerrοr="true" fork="yes" classname="lee.BeanTest">
<!-- 指定运行这些java文件需要的类库位置-->
<CLASSPATH refid="classpath" />
</JAVA>
</TARGET>
</PROJECT>

如果需要改写Axe的实现类。或者说,提供另一个实现类给Person实例使用。Person接口、Chinese类都无须改变。只需提供另一个Axe的实现,然后对配置文件进行简单的修改即可。

Axe的另一个实现如下:
//Axe的另一个实现类 SteelAxe
public class SteelAxe implements Axe
{
//默认构造器
public SteelAxe()
{}
//实现Axe接口的chop方法
public String chop()
{
return "钢斧砍柴真快";
}
}

然后,修改原来的Spring配置文件,在其中增加如下一行:
<!-- 定义一个steelAxe bean-->
<BEAN class=lee.SteelAxe id=steelAxe />

该行重新定义了一个Axe的实现:SteelAxe。然后修改chinese bean的配置,将原来传入stoneAxe的地方改为传入steelAxe。也就是将
<REF local="”stoneAxe”/">

改成
<REF local="”steelAxe”/">

此时再次执行程序,将得到如下结果:

钢斧砍柴真快

Person与Axe之间没有任何代码耦合关系,bean与bean之间的依赖关系由Spring管理。采用setter方法为目标bean注入属性的方式,称为设值注入。

业务对象的更换变得相当简单,对象与对象之间的依赖关系从代码里分离出来,通过配置文件动态管理。
构造注入

  所谓构造注入,指通过构造函数来完成依赖关系的设定,而不是通过setter方法。对前面代码Chinese类做简单的修改,修改后的代码如下:
//Chinese实现Person接口
public class Chinese implements Person
{
//面向Axe接口编程,而不是具体的实现类
private Axe axe;
//默认的构造器
public Chinese()
{}
//构造注入所需的带参数的构造器
public Chinse(Axe axe)
{
this.axe = axe;
}
//实现Person接口的useAxe方法
public void useAxe()
{
System.out.println(axe.chop());
}
}

此时无须Chinese类里的setAxe方法,构造Person实例时,Spring为Person实例注入所依赖的Axe实例。构造注入的配置文件也需做简单的修改,修改后的配置文件如下:
<!-- 下面是标准的XML文件头 -->
<xml version="1.0" encoding="gb2312"?>
<!-- 下面一行定义Spring的XML配置文件的dtd -->
"http://www.springframework.org/dtd/spring-beans.dtd">
<!-- 以上三行对所有的Spring配置文件都是相同的 -->
<!-- Spring配置文件的根元素 -->
<BEANS>
<!—定义第一个bean,该bean的id是chinese, class指定该bean实例的实现类 -->
<BEAN class=lee.Chinese id=chinese>
</BEAN>
<!-- 定义stoneAxe bean -->
<BEAN class=lee.SteelAxe id=steelAxe />
</BEANS>

执行效果与使用steelAxe设值注入时的执行效果完全一样。区别在于:创建Person实例中Axe属性的时机不同——设值注入是现创建一个默认的bean实例,然后调用对应的构造方法注入依赖关系。而构造注入则在创建bean实例时,已经完成了依赖关系的

393. Jdo是什么?

JDO是Java对象持久化的新的规范,为java data object的简称,也是一个用于存取某种数据仓库中的对象的标准化API。JDO提供了透明的对象存储,因此对开发人员来说,存储数据对象完全不需要额外的代码(如JDBC API的使用)。这些繁琐的例行工作已经转移到JDO产品提供商身上,使开发人员解脱出来,从而集中时间和精力在业务逻辑上。另外,JDO很灵活,因为它可以在任何数据底层上运行。JDBC只是面向关系数据库(RDBMS)JDO更通用,提供到任何数据底层的存储功能,比如关系数据库、文件、XML以及对象数据库(ODBMS)等等,使得应用可移植性更强。 

394. 什么是spring的IOC AOP

在传统的程序设计中,当调用者需要被调用者的协助时,通常由调用者来创建被调用者的实例。但在spring里创建被调用者的工作不再由调用者来完成,因此控制反转(IoC);创建被调用者实例的工作通常由spring容器来完成,然后注入调用者,因此也被称为依赖注入(DI),依赖注入和控制反转是同一个概念。
面向方面编程(AOP)是以另一个角度来考虑程序结构,通过分析程序结构的关注点来完善面向对象编程(OOP)。OOP将应用程序分解成各个层次的对象,而AOP将程序分解成多个切面。spring AOP 只实现了方法级别的连接点,在J2EE应用中,AOP拦截到方法级别的操作就已经足够。在spring中,未来使IoC方便地使用健壮、灵活的企业服务,需要利用spring AOP实现为IoC和企业服务之间建立联系。 

395. STRUTS的工作流程!

(1)减缩和用户请求匹配的ActionMapping实例,如果不存在,就返回用户请求路径无效的信息。
 (2)如果ActionForm实例不存在,就创建一个ActionForm对象,把客户提交的表单数据保存到ActionForm对象中。
 (3)根据配置信息决定是否需要表单验证。如果需要验证,就调用ActionForm的validate()方法。
 (4)如果ActionForm的validate()方法返回null或返回一个不包含ActionMessage的ActionErrors对象,就表示表单验证成功。
 (5)ActionServlet根据ActionMapping实例包含的映射信息决定将请求转发给哪个Action。如果相应的Action实例不存在,就先创建这个实例,然后调用Action的execute()方法。
 (6)Action的execute()方法返回一个ActionForward对象,ActinoServlet再把客户请求转发给ActionForward对象只想的JSP组件。
 (7)ActionForward对象指向的JSP组件生成动态网页,返回给客户。

  对于以上流程的流程(4),如果ActionForm的validate()方法返回一个或多个ActionMessage的ActionErrors对 象,就表示表单验证失败,此时ActionServlet将直接把请求转发给包含用户提交表单的JSP组件。再这种情况下,不会再创建Action对象并 调用Action的execute()方法。

396. spring 与EJB的区别!!

1 spring可以作为中间层使用,就像ejb的sessionBean,但是spring的依赖注入很强大,而ejb只支持jndi方式的注入

2 ejb有持久层,比如jpa;spring可以无缝结合很多持久化框架,比如hibernate、jpa、ibatis,jdbc

3 ejb支持分布式(最大特点),spring可以继承webservice来实现,也可以结合一些分布式的框架实现,但是ejb的分布式比较成熟(虽然也有一些事务相关的,无法解释的问题)

4 spring有spring mvc提供web层,而ejb需要结合jsf或者自己去组装struts等框架实现

还有很多

总结spring的灵活性很大,什么都能结合,充分利用java多开源的特点

ejb都是自己家的东西,在分布式方面是最好的选择,但是灵活性很差 

397. UML方面

标准建模语言UML。用例图,静态图(包括类图、对象图和包图),行为图,交互图(顺序图,合作图),实现图。 

398. j2ee常用的设计模式?说明工厂模式。

总共23种,分为三大类:创建型,结构型,行为型
我只记得其中常用的6、7种,分别是:
创建型(工厂、工厂方法、抽象工厂、单例)
结构型(包装、适配器,组合,代理)
行为(观察者,模版,策略)
然后再针对你熟悉的模式谈谈你的理解即可。   

Java中的23种设计模式: 
Factory(工厂模式),      Builder(建造模式),       Factory Method(工厂方法模式), 
Prototype(原始模型模式),Singleton(单例模式),    Facade(门面模式), 
Adapter(适配器模式),    Bridge(桥梁模式),        Composite(合成模式), 
Decorator(装饰模式),    Flyweight(享元模式),     Proxy(代理模式), 
Command(命令模式),      Interpreter(解释器模式), Visitor(访问者模式), 
Iterator(迭代子模式),   Mediator(调停者模式),    Memento(备忘录模式), 
Observer(观察者模式),   State(状态模式),         Strategy(策略模式), 
Template Method(模板方法模式), Chain Of Responsibleity(责任链模式) 
工厂模式:工厂模式是一种经常被使用到的模式,根据工厂模式实现的类可以根据提供的数据生成一组类中某一个类的实例,通常这一组类有一个公共的抽象父类并且实现了相同的方法,但是这些方法针对不同的数据进行了不同的操作。首先需要定义一个基类,该类的子类通过不同的方法实现了基类中的方法。然后需要定义一个工厂类,工厂类可以根据条件生成不同的子类实例。当得到子类的实例后,开发人员可以调用基类中的方法而不必考虑到底返回的是哪一个子类的实例。

399. 开发中都用到了那些设计模式?用在什么场合?

每个模式都描述了一个在我们的环境中不断出现的问题,然后描述了该问题的解决方案的核心。通过这种方式,你可以无数次地使用那些已有的解决方案,无需在重复相同的工作。主要用到了MVC的设计模式。用来开发JSP/Servlet或者J2EE的相关应用。简单工厂模式等。 

400. BS与CS的联系与区别。

C/S是Client/Server的缩写。服务器通常采用高性能的PC、工作站或小型机,并采用大型数据库系统,如Oracle、Sybase、InFORMix或 SQL Server。客户端需要安装专用的客户端软件。 
B/S是Brower/Server的缩写,客户机上只要安装一个浏览器(Browser),如Netscape Navigator或Internet Explorer,服务器安装Oracle、Sybase、InFORMix或 SQL Server等数据库。在这种结构下,用户界面完全通过WWW浏览器实现,一部分事务逻辑在前端实现,但是主要事务逻辑在服务器端实现。浏览器通过Web Server 同数据库进行数据交互。 
C/S 与 B/S 区别: 
1.硬件环境不同: 
  C/S 一般建立在专用的网络上, 小范围里的网络环境, 局域网之间再通过专门服务器提供连接和数据交换服务. 
  B/S 建立在广域网之上的, 不必是专门的网络硬件环境,例与电话上网, 租用设备. 信息自己管理. 有比C/S更强的适应范围, 一般只要有操作系统和浏览器就行 
2.对安全要求不同 
  C/S 一般面向相对固定的用户群, 对信息安全的控制能力很强. 一般高度机密的信息系统采用C/S 结构适宜. 可以通过B/S发布部分可公开信息. 
  B/S 建立在广域网之上, 对安全的控制能力相对弱, 可能面向不可知的用户。 
3.对程序架构不同 
  C/S 程序可以更加注重流程, 可以对权限多层次校验, 对系统运行速度可以较少考虑. 
  B/S 对安全以及访问速度的多重的考虑, 建立在需要更加优化的基础之上. 比C/S有更高的要求 B/S结构的程序架构是发展的趋势, 从MS的.Net系列的BizTalk 2000 Exchange 2000等, 全面支持网络的构件搭建的系统. SUN 和IBM推的JavaBean 构件技术等,使 B/S更加成熟. 
4.软件重用不同 
  C/S 程序可以不可避免的整体性考虑, 构件的重用性不如在B/S要求下的构件的重用性好. 
  B/S 对的多重结构,要求构件相对独立的功能. 能够相对较好的重用.就入买来的餐桌可以再利用,而不是做在墙上的石头桌子 
5.系统维护不同   
  C/S 程序由于整体性, 必须整体考察, 处理出现的问题以及系统升级. 升级难. 可能是再做一个全新的系统 
  B/S 构件组成,方面构件个别的更换,实现系统的无缝升级. 系统维护开销减到最小.用户从网上自己下载安装就可以实现升级. 
6.处理问题不同 
  C/S 程序可以处理用户面固定, 并且在相同区域, 安全要求高需求, 与操作系统相关. 应该都是相同的系统 
  B/S 建立在广域网上, 面向不同的用户群, 分散地域, 这是C/S无法作到的. 与操作系统平台关系最小. 
7.用户接口不同 
  C/S 多是建立的Window平台上,表现方法有限,对程序员普遍要求较高 
  B/S 建立在浏览器上, 有更加丰富和生动的表现方式与用户交流. 并且大部分难度减低,减低开发成本. 
8.信息流不同 
  C/S 程序一般是典型的中央集权的机械式处理, 交互性相对低 
  B/S 信息流向可变化, B-B B-C B-G等信息、流向的变化, 更像交易中心。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值