Java Web实例学习的总结内容
一、如何使Servlet在服务器启动时就被加载并读取初始化参数:
*****************************************************************************************
1.要使到Servlet在服务器启动时就被加载,需要在Web应用中配置web.xml文件:
整型数值
该参数指定当Web应用启动时,装载Servlet的次序。当这个值为正数或零,Servlet容器先加载数值小的Servlet,再依次加载其他数值大的Servlet。如果这个值为负数或者没有设定,那么Servlet容器将在Web客户首次访问这个Servlet时加载它
2.要使到Servlet在服务器启动时就读取初始化参数,需要在Web应用中配置web.xml文件:
参数名
参数值
该参数定义Servlet的初始化参数,一个元素可以有多个,当Servlet被加载后,可以通过Servlet的init()方法的参数ServletConfig对象,调用getInitParameter(参数名)获取初始化参数的值
*****************************************************************************************
二、如何在Servlet中读取XML文件:
*****************************************************************************************
要在Servlet中读取XML文件,必须使用JDOM包,必须的包结构如下:
A.import org.jdom.input.SAXBuilder
B.import org.jdom.Document
C.import org.jdom.Element
D.import org.jdom.JDOMException
SAXBuilder类使用了第三方的工具SAX PASER来从各种各样的文件、stream、reader、url或者SAX InputSouce实例中创建JDOM 文档对象。Document类代表了一个XML文档。Element类代表了一个XML文档中的节点,提供了许多方法用于访问节点及其子节点、属性、文本内容。
读取XML文件的步骤如下:
A.创建一个SAXBuilder对象
B.利用该对象构架一个文档对象
C.从文档对象中获取根节点
D.从根节点中获取各个子节点
E.从各个子节点中提取属性内容
例如:
try{
//创建一个SAXBuilder对象
SAXBuilder builder = new SAXBuilder(false);
//读取参数configURI指定的路径下的XML文件
Document doc = builder.build(configURI);
//获取XML文件的根节点
Element root = doc.getRootElement();
//获取根节点下的"database"节点的子节点"url"的值
root.getChild("database").getChild("url").getTextTrim();
}
*****************************************************************************************
三、如何把字符串转换成GBK、ASCII、UTF-8编码格式:
*****************************************************************************************
1.将字符串转换成GBK编码:
public String toGBK(String str){
try
{
str=new String(str.getBytes("ISO-8859-1"),"GBK");
}catch (Exception e) {}
return str;
}
2.将字符串转换成ASCII编码:
public String toGBK(String str){
try
{
str=new String(str.getBytes("GBK"),"ISO-8859-1");
}catch (Exception e) {}
return str;
}
3.将字符串转换成UTF-8编码:
A.将String转换成byte数组(8bit)
B.定义一个与byte数组等长的char数组(16bit)
C.使用循环每次取出byte数组中一个byte与十六进制数0x00FF做逻辑与,再转换成char类型
D.循环结束后将char数组作为参数创建一个字符串
public String toUtf8String(String src){
byte b[] = src.getBytes();//使用本地平台编码,默认为8位编码
char c[] = new char[b.length];//采用unicode编码表示(16位)
//与0x00FF相与得到其本身(8bit),再转换成16bit的char类型数值
for(int i=0;i
c[i] = (char)b[i]&0x00FF;
}
//将char数值作为String构造函数的参数新建一个String
return new String(c);
}
*****************************************************************************************
四、如何根据特定的分隔符分割字符串:
*****************************************************************************************
1.使用字符串类提供的方法:
A.在原字符串之后连接上分隔符(分割后的子串的数目=分隔符出现的次数+1)
B.使用循环判断原字符串应该被分割成多少个子串
C.使用循环截取从当前字符开始到分割符之间的子串
D.将分隔符之后的子串作为下一次循环开始的原字符串
public String[] splitStr(String str,char c){
//原字符串之后连接上分隔符,确保子串的数目=分隔符的数目+1
str+=c;
int n = 0;
for(int i=0;i
//如果出现分隔符则子串的数目加1
if(charAt(i).equalsIgnoreCase(c)) n=n+1;
}
String out[] = new String[n];
for(int j=0;j
int index = str.indexOf(c);//分隔符的位置
out[j] = str.substring(0,index);//截取子串
str = str.substring(index+1,str.lengt()); //下一次循环的str
}
return out;
}
2.使用java.util.StringTokenizer类对字符串进行分割
public String[] splitStr(String str,char c){
//创建一个StringTokenizer对象
StringTokenizer st = new StringTokenizer(new Character(c).toString());
//获取token的数目,必须在第一次调用nextToken()之前调用
int num = st.countTokens();
//新建一个String数组用于存放各个子串
String out[] = new String[num];
//取出每个token
int i = 0;
while(st.hasMoreTokens()){
out[i] = st.nextToken();
i = i+1;
}
return out;
}
*****************************************************************************************
五、如何获取结果集的元数据信息:
*****************************************************************************************
ResultSetMetaData是一种用于从ResultSet中获取列的类型和属性的对象,常用的API有:
A.getColumnCount
()
:
获取ResultSet对象中的列数
B.getColumnName(int column
): 获取 ResultSet对象中的列名
C.getColumnType(int column
): 获取ResultSet对象中的列的类型(整型)
D.getColumnTypeName(int column
): 获取ResultSet对象中的列的类型的名称(S)
E.getTableName(int column
): 获取列所在的表的表名
F.isNullable(int column
): 判断该列是否可以为空
*****************************************************************************************
六、如何获取数据库的结构信息:
*****************************************************************************************
1. 使用这个接口的用户在大多数情况下都是为了解决一些和DBMS有关的底层问题,例如:如果要使用CREATE TABLE语句来创建一个表结构,那么可能需要使用getTypeInfo方法来找出这个数据库支持的数据类型,或者用户可能通过调用supportsCorrrelatedSubqueries方法来判断是否可以在数据库使用嵌套的子查询、通过调用supportsBatchUpdates方法来判断是否可以在数据库中执行批处理的更新操作
2. DatabaseMetaData类的一些方法会以ResultSet的形式返回关于数据库的信息列表,ResultSet类的很多方法,类似getString、getInt都可以用在这里以获取数据,如果给出的metadata的格式不是有效的,则ResultSet的那些getter方法会抛出一个SQLException
3. DatabaseMetaData类的一些方法都带有代表字符串格式的参数,在这些格式字符串中,"%"表示匹配任何零个或多个的字符子串,而"_"表示匹配一个字符,当且仅当匹配的元数据被返回,如果搜索的格式字符串被设置为空,则搜索全部的元数据
4.常用的API:
A. String getCatalogSeparator():返回数据库的目录和表名之间的分隔符
B. ResultSet getColumns(String catalog, //目录
String schemaPattern, //数据库的模式,通常以用户作为一个模式
String tableNamePattern, //数据库的数据表名称
String columnNamePattern) //数据表的列名称
throws SQLException
C. String getDatabaseProductName():获取数据库的产品名称
D. String getDriverName():获取数据库驱动程序的名称
E. int getMaxConnections():获取当前数据库允许的最大连接
F. ResultSet getPrimaryKeys(String catalog, //目录
String schema, //数据库的模式,通常以用户作为一个模式
String table) //数据库的数据表名称
throws SQLException
G. ResultSet getTables(String catalog, //目录
String schemaPattern, //数据库的模式,通常以用户作为一个模式
String tableNamePattern, //数据库的数据表名称
String[] types) //数据表的类型,诸如TABLE、VIEW等
throws SQLException
H.ResultSet getTypeInfo():获取该数据库中所有被支持的数据类型
I. String getUserName():获取该数据库中所有已知的用户名
*****************************************************************************************
七、如何动态地拼装插入SQL语句:
*****************************************************************************************
1.思路:要动态地拼装INSERT语句,关键在于获取每一个字段的名称、值、类型,所以只要将每一个字段的名称、值、类型都封装在一个对应的Vector中,利用循环每次取出一个字段的Vecotr,就可以根据字段名称动态地拼装字段列表,根据值和类型做出相应的转化后动态地拼装值列表了
2.参数:
[0] [1] [2]
3.步骤:
A.从Vector中取出第一个元素:tableName
B.从Vector中取出第二个元素:columnName/columnValue/columnType
C.如果此时字段列表变量为空,则为字段列表变量赋值”(”
D.如果此时字段列表变量不为空,则在字段列表变量后添加”,”
E.在字段列表变量后添加cloumnName
F.如果此时值列表变量为空,则为值列表变量赋值”(”
G.如果此时值列表变量不为空,则在值列表变量后添加“,”
H.根据columnType的值,对columnValue的值进行相应的转换
I.在值列表变量后添加转换后的columnValue
J.重复步骤B直至所有的字段被添加完毕
4.主要代码:
//取出每个字段中的field、value、type进行拼装
Vector v_t = (Vector)vect.get(i);
field = (String)v_t.get(0);
value = (String)v_t.get(1);
type = (String)v_t.get(2);
//组合字段SQL
if(sqlField.equals(""))
sqlField = " (";
else
sqlField = sqlField + ",";
sqlField = sqlField + field;
//组合值SQL
if(sqlValue.equals(""))
sqlValue = "(";
else
sqlValue = sqlValue + ",";
if(value.equals("")){ //为空时
sqlValue = sqlValue + "null";
}
else if(type.equals("CHAR")){ //字符串
sqlValue = sqlValue + "'" + value + "'";
}
else if(type.equals("NUM")){ //数值
sqlValue = sqlValue + value;
}
else if(type.equals("TIME")){ //日期
sqlValue = sqlValue + "to_date('yyyy-MM-dd HH:mm:ss','" + value + "')";
}
//拼装结束
sqlField = sqlField + ")";
sqlValue = sqlValue + ")";
String sql = "insert into " + (String)vect.get(0) + sqlField + " values" + sqlValue;
*****************************************************************************************
八、如何动态地拼装更新的SQL语句:
*****************************************************************************************
1.思路:和动态地拼装插入的SQL语句相同,不同的只是参数的结构不同,增加了更新的条件部分
2.参数:
3.步骤:
基本步骤和动态地拼装插入的SQL语句相同,只是在取更新的字段列表时是从Vector的第二个元素开始直至倒数第二个结束,最后再加上condition字段作为更新的条件
4.代码:
//获取更新SQL语句的赋值部分
String sql = "update " + (String)vect.get(0) + " set " + sqlSet;
//获取更新SQL语句的条件部分
String sqlWhere = (String)vect.get(vect.size()-1);
if(!sqlWhere.equals("")){ //拼装结束
sql = sql + " where " + sqlWhere;
}
*****************************************************************************************
九、如何对数据库的查询结果进行分页显示:
*****************************************************************************************
1.思路:要获取当前页的数据,最主要的就是要获取三个变量的值:总记录数,每一页的记录数,当前页的页码,由此可以确定分页的页数,定位到当前页的第一条记录
2.参数:要查询的当前页页码,每页的记录数
3.步骤:A.执行SQL查询语句,获取由所有符合条件的记录所组成的记录集
B.统计记录集中的记录总数
C.根据记录总数、每一页的记录数确定分页的页数
D.根据分页的页数对当前页参数做出相应的调整
E.根据当前页的页码定位到当前页的首条记录
F.将每一条记录的字段名/值以key/value对的形式存储到Hashtable中
G.将代表当前记录的Hashtable添加到Vector中
H.重复步骤B直至记录集中的所有数据被全部添加到Vector中
4.输出:
Vector
5.代码:
rs = pstm.executeQuery();
//获取记录集中的记录总数
while (rs.next()){
rows++;
}
//获取分页的总数:如果不能整除则页数再增加1页
int sum = rows / records;
if(rows % records != 0 || rows == 0){
sum++;
}
//根据分页的总数对当前页数做出相应的调整
if (page==0){
page = 1;
}
else if (page>sum)
{
page = sum;
}
//定位到当前记录
index = (page - 1) * records;
rs.absolute(index+1);
//从当前页的第一条记录开始将当前页的所有记录存储到Vector中
int j = 0;
do
{ // 当结果集中没有数据、数据取满则退出查询
if(rs==null||j == records||rs.getRow()==0){
break;
}
j++;
ResultSetMetaData rsmd = rs.getMetaData();
int cols = rsmd.getColumnCount();
Hashtable hash = new Hashtable();
//将当前记录的所有字段以名/值对的形式保存到Hashtable中
for(int i = 1; i <= cols; i++){
String field = ds.toString(rsmd.getColumnName(i));
String value = ds.toString(rs.getString(i));
hash.put(field, value);
}
//将当前记录添加到Vector的末尾
vect.add(hash);
}while(rs.next());
*****************************************************************************************
十、如何同时保存查询的字段和值:
*****************************************************************************************
1.要获取字段的信息,可以使用java.sql.ResultSetMetaData接口,在这个接口中定义了一系列的关于字段信息的获取、设置的API
2.要同时保存查询的字段和值,可以使用java.uti.Hashtable类,Hashtable是哈希表对象,存储在其中的元素都以key/value对的形式表示,Hashtable中具有put、get方法,可以将任意类型的数据以id为标识存放到其中,也可以从中取出key为id的对象
ResultSetMetaData rsmd = rs.getMetaData();
Hashtable hash = new Hashtable();
int cols = rsmd.getColumnCount();
for (int i=1;i<=cols;i++){
String colName = rsmd.getColumnName(i);
String colValue= rs.getString(i);
hash.put(colName,colValue);
}
*****************************************************************************************
十一、如何将HTML格式的文件保存为普通格式的文本文件:
*****************************************************************************************
1.思路:要将HTML格式的文件保存为普通格式的文本文件,要注意需要将其中的一些特殊的HTML标记转换成相应的文本形式,例如”&”、”<”、”>”、”"”
2.步骤:A.获取包含绝对路径的文件名和文件内容
B.对文件内容中的某些HTML标记进行适当的转换
C.判断文件名对应的文件是否已经存在
D.如果不存在则将文件以字符的形式写到本地磁盘上
3.代码:
//获取文件名和文件内容
String file = (String)hashtable.get(“file”);
String content = (String)hashtable.get(“content”);
//对HTML的部分标记进行替换
content = content.replaceAll(“&”,”&”);
content = content.replaceAll(“<”,”<”);
content = content.replaceAll(“>”,”>”);
content = content.replaceAll(“"”,”\””);
//判断文件名对应的文件是否以及存在
String f = file.substring(file.lastIndexOf(“/”)+1,file.length());
String sql = “select * from art where file = ‘”+f+”’”;
if (getResultSetData(selectRecord(sql)).size()!=0) {
return 1;
}
else{
//建立Unicode字符流
OutputStreamWriter osw = new OutputStreamWriter(os);
PrintWriter bw = new PrintWriter(osw);
//写Unicode字符串
bw.print(str,0,str.length());;
bw.newLine();
}
*****************************************************************************************
十二、如何从文本文件中读取内容并转换成HTML格式:
*****************************************************************************************
//建立Unicode字符流
InputStreamReader isw = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isw);
//读Unicode字符串
String s = "";
while((s=br.readLine())!=null){
if(!str.equals(""))str = str + "\r\n" + s;
else str = str + s;
}
*****************************************************************************************
十三、File类的常用方法:
*****************************************************************************************
1. public boolean canRead():能否读
2.public boolean canWrite():能否写
3.public int compareTo(File pathname):比较两个File类对象是否为同一个
4.public boolean createNewFile() throws IOException:创建新的文件
5.public boolean delete():删除文件
6.public String getAbsolutePath():获取File类对象的抽象路径所对应的绝对路径
7.public String getName():获取File类对象的文件名(不包含路径)
8.public String getParent():获取File类对象所代表的路径的上一级目录
9.public File getParentFile():获取File类对象的父目录对象
10. public long length():
11.public boolean isAbsolute():判断该路径是否为绝对路径
12.public boolean isDirectory():判断该File类对象是否为目录
13.public boolean isFile():判断该File类对象是否为文件
14.public long lastModified():获取该File类对象的最后修改时间
15.public String[] list(FilenameFilter filter):列出该File类对象所在目录下的目录、文件名
16.public boolean mkdir():创建新目录
17.public boolean renameTo(File dest):对File类对象重命名
18.public boolean setLastModified(long time):设置该File类对象的最后修改时间
19.public boolean setReadOnly():设置该File类对象的属性为只读
20. public URL toURL()throws MalformedURLException:将File对象的路径转换为URL
*****************************************************************************************
十四、如何使用I/0流拷贝文件:
*****************************************************************************************
1.思路:要使用I/O流拷贝文件,最重要的就是要获取一个输入文件对象,输出文件对象,然后通过 InputStream或Reader从输入文件中按字节或字符读取内容,再通过OutputStream或Writer 写到输出文件就可以了
2.代码:
FileInputStream fis = new FileInputStream(infile);
FileOutputStream fos = new FileOutputStream(outfile);
1./**拷贝文件内容fis->fos*/
public void movefile_FileStream(){
try{
File f = new File(infile); //创建一个文件对象
byte b[]=new byte[(int)(f.length())]; //创建一个长度为文件长度的byte数组
fis.read(b); //读取byte数组长度的字节到数组中
fos.write(b); //将byte数组中的字节全部输出到文件中
}catch(IOException ioe){
System.out.println("调用DealFile.movefile_FileStream()函数错误:\r\n"+ioe);}
}
2./**拷贝文件内容fis->fos*/
public void movefile_BufferedByteStream(){
try{
BufferedInputStream in = new BufferedInputStream(fis);
BufferedOutputStream out = new BufferedOutputStream(fos);
int c;
//每次从文件中读取一个字节的数据,返回值为读取的有效字节数,文件结束则返回-1
while((c=in.read())!=-1){
out.write(c);
}
in.close();
out.close();
}catch(IOException ioe){
System.out.println("调用DealFile.movefile_BufferedByteStream()函数错 误:\r\n"+ioe);
}
}
3./**拷贝文件内容infile->outfile*/
public void movefile_BufferedCharStream(){
try{
BufferedReader in = new BufferedReader(new FileReader(infile));
PrintWriter out = new PrintWriter(new FileWriter(outfile));
int c;
while((c=in.read())!=-1){//每次读入一个字符,文件结束则返回-1
out.print(c); //每次写一个字符
}
in.close();
out.close();
}catch(IOException ioe){
System.out.println("调用DealFile.movefile_BufferedCharStream()函数错 误:\r\n"+ioe);
}
}
*****************************************************************************************
十四、如何使用RandomAccessFile类读/写数据:
*****************************************************************************************
1.RandomAccessFile类简介:
RandomAccessFile类的实例支持从一个随机访问文件中读、写文件的操作,可以把一个随机访问文
件看成是存储在文件系统中的一个巨大的字节数组,在这个数组中有一个类似于游标或者下标的东
西,我们称之为文件指针;
读取文件时从当前文件指针的位置开始,文件指针不断地传递要读取的字节,如果随机访问文件是
以读/写的模式创建的,对文件的输出操作也是允许的。写文件是从当前文件指针的位置开始,文件
指针不断的传递要写的字节。文件指针可以通过getFilePointer方法获取,也可以通过seek方法
进行设置
2.RandomAccessFile类的常用API:
1.public RandomAccessFile(String name, String mode):用指定的模式创建随机访问文件
2.public long getFilePointer():获取该随机访问文件的文件指针
3.public long length():获取该随机访问文件的实际长度
4.public int read():从随机访问文件中读取一个字节的内容
5.public final char readChar():从随机访问文件中读取一个字符
6.public final int readInt():从随机访问文件中读取一个整数
7.public final String readLine():从随机访问文件中读取一行文本
8.public final String readUTF():从随机访问文件中读取一个UTF-8的字符串
9.public void seek(long pos):设置该随机访问文件的文件指针的位置
10.public void setLength(long newLength):设置该随机访问文件的长度
11.public int skipBytes(int n):跳过由参数指定的字节数的数据
12.public void write(byte[] b):向文件中写长度为b的长度的数据
13.public final void writeBytes(String s):将字符串s以字节流的形式输出
14.public final void writeChars(String s):将字符串s以字符的形式输出
3.使用RadndomAccessFile向文件的末尾追加数据:
public void appendCHStr(String outfile,String str) {
try {
RandomAccessFile rf = new RandomAccessFile(outfile,"rw");
rf.seek(rf.length()); //定位到文件末尾
rf.writeBytes(str); //从当前文件指针的位置开始追加数据
rf.close();
}catch(IOException ioe){
System.out.println("调用DealFile.appendCHStr()函数错误:\r\n"+ioe);
}
}
*****************************************************************************************
十五、如何在文件中进行全文搜索:
*****************************************************************************************
1.思路:全文搜索主要包含下面几个类型:
A.从文章的开始搜索字符串第一次出现的位置
B.从指定的位置搜索字符串第一次出现的位置
C.从指定的字符串开始搜索另一字符串第一次出现的位置
要实现全文搜索,必须支持在文章中随机地访问文件中的内容,普通的像stream、reader、 writer等都不能实现随机访问的功能,只能向前读/写,而不能向后读/写,更不能像访问数 组一样按位置随机访问文件,这个时候只能使用RandomAccessFile类实现随机访问。A、C两 种情况可以通过调用B函数来完成
2.步骤:
A.判断搜索的位置是否比文件的长度大,如果是则不执行搜索操作
B.否则使用RandomAccessFile直接定位到搜索的开始位置处,判断剩余长度是否小于字符串长度
C.如果是则不执行搜索操作,否则从当前位置开始读取和搜索字符串等长的内容进行比较
D.如果比较结果相同则退出,返回字符串的位置,否则返回步骤C继续下一次比较直至文件结束
3.代码:
/**从位置cur开始检索第一个str的位置*/
public long seekStrPos(long cur,String str) {
long fcur = cur;
try {
RandomAccessFile file = new RandomAccessFile(new File(infile),"r");
long flen = file.length(); //文件的长度
int slen = str.length(); //比较字符串的长度
byte []b = new byte[slen]; //临时变量,用以存放读取的字符串
for(;fcur
file.seek(fcur); //使用文件指针直接定位到搜索的开始位置
//文件尾剩余长度不再够时
if((flen-fcur)
fcur = -1;
break;
}
//判断读取的字符串和比较字符串是否相同,不是则继续搜索
file.read(b,0,slen); //读取和比较字符串等长的字符串到数组中
String bstr = new String(b);
if(str.equals(bstr)) break;
}
file.close(); //注意操作结束要关闭文件
}catch(IOException ioe){
System.out.println("调用DealFile.seekStrPos()函数错误:\r\n"+ioe);
}
return fcur;
}
*****************************************************************************************
十六、如何在Java中使用正则表达式:
*****************************************************************************************
*****************************************************************************************
十七、如何在JSP中显示”首页 上一页 下一页 末页”的连接:
*****************************************************************************************
1.思路:要在JSP中显示“首页 上一页 下一页 末页”的连接,关键是要知道几个参数:
A.执行SQL查询后的总记录数
B.执行SQL查询后的分页总数
C.当前请求的查询页码
当请求到达时,首先判断是否有页码的参数,如果没有则为首次查询,默认为查询第一页,否 则的话调用分页模块获取上面的3个参数,将当前请求的页码和前两个参数进行比较就可以确 定是否要显示这几个链接了
2.步骤:
A.获取当前请求的查询页码参数
B.如果参数为空,则为第一次查询,默认查询第一页,否则将参数转换为整型数据
C.将要查询的页码和每页显示记录数作为参数调用查询分页的模块,获取一个Vector
D.从Vector中获取查询结果的总记录数、总分页数、ResultSet
E.如果当前页码比1大,则显示上一页
F.如果当前页码比总分页数小,则显示下一页
3.代码:
//取得待显示页码
strPage = request.getParameter("page");
if(strPage==null){
//表明第一次显示该页面,此时显示第一页数据
intPage = 1;
}
else{
//将字符串转换成整型
intPage = java.lang.Integer.parseInt(strPage);
if(intPage<1) intPage = 1;
}
//定位到查询页面的第一条记录:如果是首次访问则定位到第1条记录
int recno=(intPage-1)*intPageSize+1;
//取得当前页的数据:总的记录数+总的页数+各条记录的Hashtable结构
Vector vect = myBean.getCurPage(intPage,intPageSize);
//获取记录总数:rows
intRowCount = Integer.parseInt((String)vect.get(0));
//记算总页数:sums
intPageCount = Integer.parseInt((String)vect.get(1));
//调整待显示的页码
if(intPage>intPageCount) intPage = intPageCount;
<!--显示当前页码和总共的页数、记录数-->
第<%=intPage%>页 共<%=intPageCount%>页(<%=intRowCount%>条)
<!--显示首页链接-->
<a href="user.jsp?">首页</a>
<!--显示上一页的链接-->
<%if(intPage>1){%>
<a href="user.jsp?&page=<%=intPage-1%>">上一页</a>
<%}%>
<!--显示下一页的链接-->
<%if(intPage<intPageCount){%>
<a href="user.jsp?&page=<%=intPage+1%>">下一页</a>
<%}%>
<!--显示未页的链接-->
<a href="user.jsp?&page=<%=intPageCount%>">尾页</a>
*****************************************************************************************
十八、如何根据数据表中的级联关系得出文件的绝对路径:
*****************************************************************************************
1.思路:在数据库中,经常采用级联的方式表示一个文件的路径,在子目录的表中保存了与之对应的父 目录的id和name、每次根据文件的层次从相应的表中获取上一层目录的名称,再用路径分隔 符连接上当前的目录名称就可以得到文件的绝对路径了,循环直至最顶层的目录结束
2.步骤:
3.代码:
if(menufloor.equals("2")){
//从menu2表中获取menu2id等于news表中menu2id的记录的menu2Name
menuname = myBean.toName("menu2","id","name",menuid);
//从menu2表中获取menu2id等于news表中menu2id的记录的menu1id
menuid = myBean.toID("menu2","id","id1",menuid) + "";
//从menu1表中获取menu1id等于menu2表中menu1id记录的menu1Name
menuname = myBean.toName("menu1","id","name",menuid)+ "\\" + menuname;
}
*****************************************************************************************
十九、如何动态地显示…的值:
*****************************************************************************************
<% for(int i = 0;i ><%=city[i]%> <% } %>
*****************************************************************************************
二十、如何在JSP中实现两个下拉列表框的联动:
*****************************************************************************************
1.思路:要实现两个下拉列表框的联动,必须使用下拉列表框的onChange事件,当用户选中了第一个下拉列表框的某个选项时候,就会触发onchange事件,此时要先将原来第二个列表框中的所 有选项先清空,在将第一个列表框被选中项所对应的子项目添加到第二个列表框中
2.代码;
*****************************************************************************************
二十一、如何在页面中动态地设置图片热点区域的链接:
*****************************************************************************************
1.思路:要实现在图片上动态设定热点区域的链接,需要开发人员预先订好图片的热点坐标,以数据库或者配置文件的形式读入,在加载页码的时候从数据库中或者配置文件中读取热点区域的坐标 使用(其中shape表示热点区域的 形状,coords表示热点区域的坐标,href表示热点区域的链接)
2.步骤:
A.加载页码时先读入图片
B.从数据库中读取热点区域的坐标值
C.设置热点区域
3.代码:
<!--插入一级菜单的图片,并且使用图片映射-->
<img src="../images/left.gif" usemap="#Map" width="161" height="401" border="0">
//获取一级菜单的所有菜单项的id、热点链接的区域
public Vector getMenu1List()
{
String sql = "select id,rect from menu1 where isuse='1' order by id";
return getDataBySql(sql);
}
<!--一级菜单的图像映射-->
<map name="Map">
<%
Vector v1 = myBean.getMenu1List();
for(int i=0;i<v1.size();i++){
Hashtable h = (Hashtable)v1.get(i);
%>
<!--获取一级菜单图片的每一个热点区域链接-->
<area shape="rect" coords="<%=h.get("rect")%>" //获取该热点区域的坐标值
href="javascript:gomenu1('<%=h.get("id")%>');"> //获取点击该区域后的动作
<%}%>
</map>
*****************************************************************************************
二十二、如何在页面中动态地设置菜单栏目的图片、背景色、字体的颜色:
*****************************************************************************************
1.思路:可以预先设置N种图标图片,如果菜单栏目的个数小于N,则直接依次显示,如果多于N个, 则需要循环提取图片。同理,菜单栏目背景颜色的设置也有N个,如果少于N个,则直接按顺 序设置颜色,如果多于N个,则要进行循环设置
2.步骤:
A.取出该菜单下的所有栏目存储到Vector中
B.利用循环每次取出一个栏目
C.根据该栏目的序号和N之间的关系确定要设置的图片、背景色、字体颜色
3.代码:
<%
//取出menu2表中所有可用的二级菜单栏目
Vector v2 = myBean.getMenu2List();
if (v2.size()!=0){
%>
<table width="100%" border=0 cellspacing=0>
<tr>
<%
for(int i=0;i<v2.size();i++){
Hashtable h = (Hashtable)v2.get(i);
//根据该栏目的序号和基数N的关系确定背景图片和背景色
String img = "../upload/t"+(i+1)%4+".gif";
String bg = "../upload/t_"+(i+1)%4+".gif";
//根据该栏目的序号和基数N的关系确定字体的颜色
String color = "#936b09"; //第一张图片的字体颜色
if ((i+1)%4==2) color = "#0C4699"; //第二张图片的字体颜色
if ((i+1)%4==3) color = "#58B8DA"; //第三张图片的字体颜色
if ((i+1)%4==0) color = "972B9A"; //第四张图片的字体颜色
//该栏目的序号刚好是N的倍数
if (img.equals("../upload/t0.gif")){
img = "../upload/t4.gif";
bg = "../upload/t_4.gif";
}
%>
<td background="<%=img%>" height=24 width=21></td>
<td background="<%=bg%>" height=24 clor:<%=color%>><%=h.get("name")%></td>
<%}%>
</tr>
</table>
<%}%>
*****************************************************************************************
二十三、如何在多层级联菜单中显示文章:
*****************************************************************************************
1.思路:假设有N层的菜单,用户要查看相关的文章,可以通过逐级的选择下层菜单直至最后第N层菜
单,为了使到用户每次点击第i层的菜单时都有相应的文章显示,所以在每一层的菜单都有一 篇文章与之相对应,当用户点击菜单时,先显示该篇对应的文章,如果用户选择该菜单中相应 的子菜单项则再显示相应的文章
此外,用户还可以通过其他页面的超链接链接到当前页面,此时传入的参数只有文章的id,而没有菜单的层次和id,则必须通过从数据库中查找该文章对应的菜单id、层次确定要显示 的页面
2.步骤:
A.用户点击某菜单时触发JS函授,对当前菜单id进行赋值并提交表单
B.获取用户请求的菜单的id
C.根据菜单的id获取与该菜单对应的文章名称
D.将该文章的内容包含到当前页面中
E.如果是从其它页面链接而来,则根据文章的id获取对应的菜单id、层次再显示相应页面
3.代码:
DealString ds = new DealString();
String menu1id = ds.toString(request.getParameter("menu1id"));
String menu2id = ds.toString(request.getParameter("menu2id"));
String menu3id = ds.toString(request.getParameter("menu3id"));
String child = ds.toString(request.getParameter("child"));
//首次浏览默认为1级菜单,直接显示一级菜单下的文章
if(menu1id.equals(""))menu1id="1";
if(!child.equals("")) { //通过自页面内部的链接定位:如search.jsp
//从其他页面链接传递而来的参数只有child,要根据文章的名称获取其对应的目录、级别
String menufloor = myBean.toName("art","file","menufloor",child);
String menuid = myBean.toName("art","file","menuid",child);
//一级
if(menufloor.equals("1")){
menu1id = menuid;
menu2id = "";
menu3id = "";
}
//二级
if(menufloor.equals("2")){
menu2id = menuid;
menu1id = myBean.toName("menu2","id","id1",menu2id);
menu3id = "";
}
//三级
if(menufloor.equals("3")){
menu3id = menuid;
menu2id = myBean.toName("menu3","id","id2",menu3id);
menu1id = myBean.toName("menu2","id","id1",menu2id);
}
}
else
{ //通过菜单定位:如单击一级、二级、三级菜单产生的事件
//直接显示挂靠在相应菜单下的文章
if(!menu1id.equals("")&&menu2id.equals("")){ //只有一级
child = myBean.toName("menu1","id","href",menu1id);
}
if(!menu2id.equals("")&&menu3id.equals("")){ //只有二级
child = myBean.toName("menu2","id","href",menu2id);
}
if(!menu3id.equals("")){ //只有三级
child = myBean.toName("menu3","id","href",menu3id);
}
}
%>
*****************************************************************************************
二十四、集合类的Collection、List、Set、Map的框架和简介:
*****************************************************************************************
1.框架:
Collection
|-List
| |-LinkedList
| |-ArrayList
| |-Vector
| |-Stack
|-Set
Map
|-Hashtable
|-HashMap
2.简介:
A.Collection接口:
一个Collection接口代表一组Object,JDK不提供直接继承自Collection的类,JDK提供的 类都是继承自Collection的“子接口”诸如:List和Set的类。它支持一个iterator()的方 法,该方法返回一个迭代子,使用该迭代子即可逐一访问Collection中的每一个元素。
B.List接口:
List是有序的Collection,使用这个接口能够精确的控制每个元素插入的位置。用户能够使 用索引来访问List中的元素,除了具有Collection接口必备的iterator()方法外,List还 提供了一个listIterator()方法,返回一个ListIterator接口,它多了一些add()之类的方 法,允许添加,删除,设定元素,还能向前或向后遍历。
C.Set接口:
Set接口是一种不包含重复元素的Collection,即任意的两个元素e1和e2都有e1.equals(e2) 为假,Set最多有一个null元素。很明显,Set的构造函授有一个约束条件,传入的Collection 参数不能包含重复的元素,每个具体的Set实现类都依赖添加对象的equals()方法来检查一 致性
D.Map接口:
Map提供了Key到Value的映射,一个Map中不可能包 含相同的Key,每个Key只能映射一个 Value。
*****************************************************************************************
二十五、LinkedList类、ArrayList类的区别和适用场合:
*****************************************************************************************
1.LinkedList类:
LinkedList类实现了List接口,允许null元素,可以被用作堆栈,队列或者双向队列,非同步
2.ArrayList类:
ArrayList实现了可变大小的数组。它允许所有元素,包括null。ArrayList是非同步的,每个 ArrayList实例都有一个容量(Capacity),即用于存储元素的数组的大小。这个容量可以随着不断 添加新元素而自动增加。
3.区别和适用场合:
A.当你的操作是在一列数据的后面添加数据而不是在前面或中间,并且需要随机的访问其中的元素 时,使用ArrayList会提供比较好的性能
B.当你的操作是在一列数据的前面或中间添加或删除数据,并且按照顺序访问其中的元素时,就 应该使用LinkedList
C.如果编程中,A,B两种情形交替出现,这是,可以考虑使用List这样的通用接口,而不用关心 具体的实现,在具体的情形下,它的性能由具体的实现来保证
*****************************************************************************************
二十六、Vector类、Stack类的使用:
*****************************************************************************************
1.Vector类:
Vector非常类似于ArrayList,但是Vector是同步的。由Vector创建的Iterator,虽然和ArrayList 创建的Iterator是同一接口,但是因为Vector是同步的,当一个Iterator被创建而且正在被使 用,另一个线程改变了Vector的状态(例如添加或删除了一些元素),这时调用Iterator的方法将 抛出ConcurrentModificationException,因此必须捕获该异常。
2.Stack类:
Stack继承自Vector,实现了一个后进先出的堆栈,Stack提供了5个额外的方法使得Vector得意 被当作堆栈使用。基本的push和pop方法,还有peek方法得到栈顶的元素,empty方法检测堆栈 是否为空,search方法检测一个元素在堆栈中的位置。Stack刚创建后是空栈
*****************************************************************************************
二十七、Hashtable类、HashMap类的区别和适用场合:
*****************************************************************************************
1.Hashtable类:
Hashtable继承Map接口,实现一个key-value映射的哈希表。任何非空的对象都可以作为key或 者value。添加数据用put(key,value),取出数据用get(key)。Hashtable是同步的
2.HashMap类:
HashMap和Hashtbale类似,不同之处在于HashMap是非同步的,并且允许null的key,value
3.区别和适用场合:
A.Hashtable是同步的,且不允许null元素
B.HashMap是非同步的,且允许null元素
*****************************************************************************************
二十八、Vector和ArrayList的区别:
*****************************************************************************************
1. Vector的方法是同步的(Synchronized)和线程安全的(thread-safe)。而ArrayList的方法不是,由于线程的同步必然要影响性能,因此ArrayList的性能比Vector好。
2. 当Vector或ArrayList中的元素超过它的初始大小时,Vector会将它的容量翻倍,而ArrayList 只增加50%的大小,这样ArrayList就有利于节约内存空间
*****************************************************************************************
二十九、什么时候需要使用多线程的同步:
*****************************************************************************************
只要在几个线程之间共享非 final 变量,就必须使用 synchronized(或 volatile)以确保一个线程可以看见另一个线程做的更改。可见性同步的基本规则是在以下情况中必须同步:
· 读取上一次可能是由另一个线程写入的变量
· 写入下一次可能由另一个线程读取的变量
同样,final 字段对于线程也更友好。因为 final 字段在初始化之后,它们的值就不能更改,所以
当在线程之间共享 final 字段时,不需要担心同步访
*****************************************************************************************
三十、什么时候不需要使用多线程的同步:
*****************************************************************************************
在某些情况中,您不必用同步来将数据从一个线程传递到另一个,因为 JVM 已经隐含地为您执行同
步。这些情况包括:
· 由静态初始化器(在静态字段上或 static{} 块中的初始化器)初始化数据时
· 访问 final 字段时
· 在创建线程之前创建对象时
· 线程可以看见它将要处理的对象时
*****************************************************************************************
三十一、使用Synchronized的原则:
*****************************************************************************************
· 使代码块保持简短。Synchronized 块应该简短 — 在保证相关数据操作的完整性的同时,尽量简 短。把不随线程变化的预处理和后处理移出 synchronized 块。
· 不要阻塞。不要在 synchronized 块或方法中调用可能引起阻塞的方法,如InputStream.read()。
· 在持有锁的时候,不要对其它对象调用方法。这听起来可能有些极端,但它消除了最常见的死锁。
*****************************************************************************************
三十二、Java多线程的常用API:
*****************************************************************************************
1. sleep() 方法:
sleep() 允许指定以毫秒为单位的一段时间作为参数,它使得线程在指定的时间内进入阻塞状态,不能得到CPU 时间,指定的时间一过,线程重新进入可执行状态。典型地,sleep() 被用在等待某个资源就绪的情形:测试发现条件不满足后,让线程阻塞一段时间后重新测试,直到条件满足为止。
2. suspend() 和 resume() 方法:
两个方法配套使用,suspend()使得线程进入阻塞状态,并且不会自动恢复,必须其对应的resume() 被调用,才能使得线程重新进入可执行状态。典型地,suspend() 和 resume() 被用在等待另一个线程产生的结果的情形:测试发现结果还没有产生后,让线程阻塞,另一个线程产生了结果后,调用 resume() 使其恢复。
3. yield() 方法:
yield() 使得线程放弃当前分得的 CPU 时间,但是不使线程阻塞,即线程仍处于可执行状态,随时可能再次分得 CPU 时间。调用 yield() 的效果等价于调度程序认为该线程已执行了足够的时间从而转到另一个线程。
4. wait() 和 notify() 方法:
两个方法配套使用,wait() 使得线程进入阻塞状态,它有两种形式,一种允许指定以毫秒为单位的一段时间作为参数,另一种没有参数,前者当对应的 notify() 被调用或者超出指定时间时线程重新进入可执行状态,后者则必须对应的 notify() 被调用。
*****************************************************************************************
三十三、wait()、notify()与suspend()、resume()方法的区别:
*****************************************************************************************
初看起来wait()、notify()与 suspend() 和 resume() 方法对没有什么分别,但是事实上它们是截然不同的。区别的核心在于,前面叙述的所有方法,阻塞时都不会释放占用的锁(如果占用了的话),而这一对方法则相反。
上述的核心区别导致了一系列的细节上的区别。
首先,前面叙述的所有方法都隶属于 Thread 类,但是这一对却直接隶属于 Object 类,也就是说,所有对象都拥有这一对方法。初看起来这十分不可思议,但是实际上却是很自然的,因为这一对方法阻塞时要释放占用的锁,而锁是任何对象都具有的,调用任意对象的 wait() 方法导致线程阻塞,并且该对象上的锁被释放。而调用任意对象的notify()方法则导致因调用该对象的 wait() 方法而阻塞的线程中随机选择的一个解除阻塞(但要等到获得锁后才真正可执行)。
其次,前面叙述的所有方法都可在任何位置调用,但是这一对方法却必须在 synchronized 方法或块中调用,理由也很简单,只有在synchronized 方法或块中当前线程才占有锁,才有锁可以释放。同样的道理,调用这一对方法的对象上的锁必须为当前线程所拥有,这样才有锁可以释放。因此,这一对方法调用必须放置在这样的 synchronized 方法或块中,该方法或块的上锁对象就是调用这一对方法的对象。若不满足这一条件,则程序虽然仍能编译,但在运行时会出现IllegalMonitorStateException 异常。
wait() 和 notify() 方法的上述特性决定了它们经常和synchronized 方法或块一起使用,将它们和操作系统的进程间通信机制作一个比较就会发现它们的相似性:synchronized方法或块提供了类似于操作系统原语的功能,它们的执行不会受到多线程机制的干扰,而这一对方法则相当于 block 和
wakeup 原语(这一对方法均声明为 synchronized)。它们的结合使得我们可以实现操作系统上一系列精妙的进程间通信的算法(如信号量算法),并用于解决各种复杂的线程间通信问题。
关于 wait() 和 notify() 方法最后再说明两点:
第一:调用 notify() 方法导致解除阻塞的线程是从因调用该对象的 wait() 方法而阻塞的线程中随机选取的,我们无法预料哪一个线程将会被选择,所以编程时要特别小心,避免因这种不确定性而产生问题。
第二:除了 notify(),还有一个方法 notifyAll() 也可起到类似作用,唯一的区别在于,调用 notifyAll() 方法将把因调用该对象的 wait() 方法而阻塞的所有线程一次性全部解除阻塞。当然,只有获得锁的那一个线程才能进入可执行状态。
谈到阻塞,就不能不谈一谈死锁,略一分析就能发现,suspend() 方法和不指定超时期限的 wait() 方法的调用都可能产生死锁。遗憾的是,Java 并不在语言级别上支持死锁的避免,我们在编程中必须小心地避免死锁。
*****************************************************************************************