Java类加载器总结
分类: JVM2011-09-25 10:28 14379人阅读 评论(9) 收藏 举报
javaclassloaderclassjvm数据结构jar
1.类的加载过程
JVM将类加载过程分为三个步骤:装载(Load),链接(Link)和初始化(Initialize)链接又分为三个步骤,如下图所示:
1) 装载:查找并加载类的二进制数据;
2)链接:
验证:确保被加载类的正确性;
准备:为类的静态变量分配内存,并将其初始化为默认值;
解析:把类中的符号引用转换为直接引用;
3)初始化:为类的静态变量赋予正确的初始值;
那为什么我要有验证这一步骤呢?首先如果由编译器生成的class文件,它肯定是符合JVM字节码格式的,但是万一有高手自己写一个class文件,让JVM加载并运行,用于恶意用途,就不妙了,因此这个class文件要先过验证这一关,不符合的话不会让它继续执行的,也是为了安全考虑吧。
准备阶段和初始化阶段看似有点牟盾,其实是不牟盾的,如果类中有语句:private static int a = 10,它的执行过程是这样的,首先字节码文件被加载到内存后,先进行链接的验证这一步骤,验证通过后准备阶段,给a分配内存,因为变量a是static的,所以此时a等于int类型的默认初始值0,即a=0,然后到解析(后面在说),到初始化这一步骤时,才把a的真正的值10赋给a,此时a=10。
2. 类的初始化
类什么时候才被初始化:
1)创建类的实例,也就是new一个对象
2)访问某个类或接口的静态变量,或者对该静态变量赋值
3)调用类的静态方法
4)反射(Class.forName("com.lyj.load"))
5)初始化一个类的子类(会首先初始化子类的父类)
6)JVM启动时标明的启动类,即文件名和类名相同的那个类
只有这6中情况才会导致类的类的初始化。
类的初始化步骤:
1)如果这个类还没有被加载和链接,那先进行加载和链接
2)假如这个类存在直接父类,并且这个类还没有被初始化(注意:在一个类加载器中,类只能初始化一次),那就初始化直接的父类(不适用于接口)
3)加入类中存在初始化语句(如static变量和static块),那就依次执行这些初始化语句。
3.类的加载
类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个这个类的java.lang.Class对象,用来封装类在方法区类的对象。看下面2图
类的加载的最终产品是位于堆区中的Class对象
Class对象封装了类在方法区内的数据结构,并且向Java程序员提供了访问方法区内的数据结构的接口
加载类的方式有以下几种:
1)从本地系统直接加载
2)通过网络下载.class文件
3)从zip,jar等归档文件中加载.class文件
4)从专有数据库中提取.class文件
5)将Java源文件动态编译为.class文件(服务器)
4.加载器
来自http://blog.csdn.net/cutesource/article/details/5904501
JVM的类加载是通过ClassLoader及其子类来完成的,类的层次关系和加载顺序可以由下图来描述:
1)Bootstrap ClassLoader
负责加载$JAVA_HOME中jre/lib/rt.jar里所有的class,由C++实现,不是ClassLoader子类
2)Extension ClassLoader
负责加载java平台中扩展功能的一些jar包,包括$JAVA_HOME中jre/lib/*.jar或-Djava.ext.dirs指定目录下的jar包
3)App ClassLoader
负责记载classpath中指定的jar包及目录中class
4)Custom ClassLoader
属于应用程序根据自身需要自定义的ClassLoader,如tomcat、jboss都会根据j2ee规范自行实现ClassLoader
加载过程中会先检查类是否被已加载,检查顺序是自底向上,从Custom ClassLoader到BootStrap ClassLoader逐层检查,只要某个classloader已加载就视为已加载此类,保证此类只所有ClassLoader加载一次。而加载的顺序是自顶向下,也就是由上层来逐层尝试加载此类。
Java如何获得一个类里面的各个属性和类型
分类: Java2011-07-20 14:36 7500人阅读 评论(1) 收藏 举报
javaclassexceptionimportstring测试
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
public class test {
public static void main(String[] args) throws Exception {
// BufferedReader bb=new BufferedReader(new InputStreamReader(System.in));
// String classname=bb.readLine();
Class c=Class.forName("ArrayListTest.java");
//-------------------获取方法的详细信息
Method m[]=c.getDeclaredMethods();
for(int i=0;i<m.length;i++)
{
//--------------------获得方法的名字
System.out.println("方法的名字是:"+m[i].getName());
//--------------------获得方法参数的类型和有几个参数
Class b[]=m[i].getParameterTypes();//获得所有的参数并且存放到数组B中
for(int j=0;j<b.length;j++)
{
System.out.println("参数的类型是"+b[j]);
}
//--------------------获得方法返回值的类型
System.out.println(m[i].getReturnType());//获得方法的返回值类型
//--------------------获得方法的修饰符
int mod=m[i].getModifiers();
System.out.println("方法的修饰符有"+Modifier.toString(mod));
//--------------------获得方法的异常类型
Class e[]=m[i].getExceptionTypes();//获得所有的异常类型存放到数组e中
for(int k=0;k<e.length;k++)
{
System.out.println("方法的异常类型是:"+e[k]);
}
System.out.println("-------------------------------------------------------------------");
}
//----------------------------获得属性的详细信息
}
}
package Class //测试.com.tc.test.Class的forname方法获得属性信息;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
public class test {
public static void main(String[] args) throws ClassNotFoundException {
Class c=Class.forName("Class测试.com.tc.test.Class的forname方法获得属性信息.student");//把要使用的类加载到内存中,并且把有关这个类的所有信息都存放到对象c中
Field f[]=c.getDeclaredFields();//把属性的信息提取出来,并且存放到field类的对象中,因为每个field的对象只能存放一个属性的信息所以要用数组去接收
for(int i=0;i<f.length;i++)
{
System.out.println("属性的名称是:"+f[i].getName());//获得属性的名字
System.out.println("属性的类型是:"+f[i].getType());//获得属性的类型
int mod=f[i].getModifiers();//获得修饰符的常量总和
System.out.println(mod);
System.out.println("属性的修饰符有:"+Modifier.toString(mod));//modifier类可以根据常量总和去计算到底有哪些修饰符
System.out.println("-------------------------------------------------------");
}
}
}
JAVA 处理时间 - java.sql.Date、java.util.Date与数据库中的Date字段的转换方法[转]
1、如何将java.util.Date转化为java.sql.Date?
转化:
java.sql.Date sd;
java.util.Date ud;
//initialize the ud such as ud = new java.util.Date();
sd = new java.sql.Date(ud.getTime());
2、如果要插入到数据库并且相应的字段为Date类型
那么可以用PreparedStatement.setDate(int ,java.sql.Date)方法
其中的java.sql.Date可以用上面的方法得到
PreparedStatement pst;
java.util.Date date=new java.util.Date();
pst.setDate(1, java.sql.Date(date.getTime()));//这里的Date是sql中的::得到的是日期
pst.setTime(2, java.sql.Time(date.getTime()))//sql包中的Time::得到的是时间
pst.setObject(3, java.sql.Timestamp(date.getTime()));//::得到的是日期及时间
也可以用数据库提供TO_DATE函数
比如 现有 ud
TO_DATE(new SimpleDateFormat().format(ud,"yyyy-MM-dd HH:mm:ss"),
"YYYY-MM-DD HH24:MI:SS")
注意java中表示格式和数据库提供的格式的不同
一个实际的例子
sql="update tablename set timer=to_date('"+t+"','yyyymmddhh24miss') where ....."
这里的t为变量为类似:20051211131223
3、如何将"yyyy-mm-dd"格式的字符串转换为java.sql.Date
方法1
SimpleDateFormat bartDateFormat =
new SimpleDateFormat("yyyy-MM-dd");
String dateStringToParse = "2007-7-12";
try {
java.util.Date date = bartDateFormat.parse(dateStringToParse);
java.sql.Date sqlDate = new java.sql.Date(date.getTime());
System.out.println(sqlDate.getTime());
}
catch (Exception ex) {
System.out.println(ex.getMessage());
}
------------------------------------------------------------
方法2
String strDate = "2002-08-09";
StringTokenizer st = new StringTokenizer(strDate, "-");
java.sql.Date date = new java.sql.Date(Integer.parseInt(st.nextToken()),
Integer.parseInt(st.nextToken()),
Integer.parseInt(st.nextToken()));
java.util.Date和java.sql.Date的异同
java.sql.Date,java.sql.Time和java.sql.Timestamp三个都是java.util.Date的子类(包装类)。
但是为什么java.sql.Date类型的值插入到数据库中Date字段中会发生数据截取呢?
java.sql.Date是为了配合SQL DATE而设置的数据类型。“规范化”的java.sql.Date只包含年月日信息,时分秒毫秒都会清零。格式类似:YYYY-MM-DD。当我们调用ResultSet的getDate()方法来获得返回值时,java程序会参照"规范"的java.sql.Date来格式化数据库中的数值。因此,如果数据库中存在的非规范化部分的信息将会被劫取。
在sun提供的ResultSet.java中这样对getDate进行注释的:
Retrieves the of the designated column in the current row of this <code>ResultSet</code> object as a “java.sql.Date” object in the Java programming language.
同理。如果我们把一个java.sql.Date值通过PrepareStatement的setDate方法存入数据库时,java程序会对传入的java.sql.Date规范化,非规范化的部分将会被劫取。然而,我们java.sql.Date一般由java.util.Date转换过来,如:java.sql.Date sqlDate=new java.sql.Date(new java.util.Date().getTime()).
显然,这样转换过来的java.sql.Date往往不是一个规范的java.sql.Date.要保存java.util.Date的精确值,
我们需要利用java.sql.Timestamp.
Calendar
Calendar calendar=Calendar.getInstance();
//获得当前时间,声明时间变量
int year=calendar.get(Calendar.YEAR);
//得到年
int month=calendar.get(Calendar.MONTH);
//得到月,但是,月份要加上1
month=month+1;
int date=calendar.get(Calendar.DATE);
//获得日期
String today=""+year+"-"+month+"-"+date+"";
/**
* 代理模式至少需要实现两个类,一个代理类,一个被代理类(都要实现共有接口)。
* 动态代理之所以称为动态,是因为代理类在运行时由Proxy类产生,
* 这就大大减少了需要我们手工设计代理类的数量。
*在实际应用中,Proxy通过类装载器和需要实现的接口来产生代理类,
*即Proxy.getProxyClass(ClassLoader, Class[]),返回为代理类的Class实例。
*一般而言,该方法由InvocationHandler的实现类中的方法来调用,
*可以取这样的名字:getProxyObject,该方法使用Proxy.getProxyClass后
*(此时两个入口参数可以用realObject.getClass.getClassLoader()
*以及realObject.getClass.getInterfaces()两个方法得到,
*realObject指被代理类实例,一般由InvocationHandler的实现类的构造函数注入),
*进一步使用Class中的getConstructor(new Class[] { InvocationHandler.class}).newInstance(new Object[] { this })
*来得到代理类的实例(或者合并这两个方法,
*用Proxy.newProxyInstance(realObject.getClass.getClassLoader(), realObject.getClass.getInterfaces(),this)方法)
*。但是必须注意代理类的实现是由Proxy完成,与InvocationHander的实现类毫无关系,
*InvocationHander的实现类只是通过代理类的构造函数注入后实现了对代理类中方法调用的拦截。
*InvocationHander的实现类需要保留一个指向被代理类的引用(或称句柄、指针),
*该被代理类一般通过InvocationHandler的实现类的构造函数注入,也可以用set方法,
*一般可以取为realObject。则在InvocationHandler接口的invoke方法中,
*对于realObject中相应方法的直接调用是用method.invoke(realObjet, args )实现的,
*其中method和args直接由invoke方法中的输入参数给出。
*
*好了先让我们俩了解什么是代理
*/
//AOP面向切面的编程
public class BeanFactory {
//动态代理入门
public static void main(String[] args) throws Exception {
//首先我们先让JVM生成一个代理类
Class clazzProsy = Proxy.getProxyClass(Collection.class
.getClassLoader(), Collection.class);
System.out.println(clazzProsy.getName());
System.out.println("-----Begin constructor-----");
//得到代理类上的构造函数
Constructor[] constructors = clazzProsy.getConstructors();
for (Constructor constructor : constructors) {
String name = constructor.getName();
// 单线程下StringBuilder效率比较高
// 多线程时StringBuffer效率比较高
StringBuilder strB = new StringBuilder(name);
strB.append('(');
Class[] clazzParams = constructor.getParameterTypes();
for (Class clazzParam : clazzParams) {
strB.append(clazzParam.getName()).append(',');
}
if (clazzParams.length != 0 && clazzParams != null)
strB.deleteCharAt(strB.length() - 1);
strB.append(')');
System.out.println(strB.toString());
}
System.out.println("-----Begin methods-----");
//得到代理类上的所有方法
Method[] methods = clazzProsy.getMethods();
for (Method method : methods) {
String name = method.getName();
// 单线程下StringBuilder效率比较高
// 多线程时StringBuffer效率比较高
StringBuilder strB = new StringBuilder(name);
strB.append('(');
Class[] clazzParams = method.getParameterTypes();
for (Class clazzParam : clazzParams) {
strB.append(clazzParam.getName()).append(',');
}
if (clazzParams.length != 0 && clazzParams != null)
strB.deleteCharAt(strB.length() - 1);
strB.append(')');
System.out.println(strB.toString());
}
}
}
只为成功找方法,不为失败找借口!
在Servlet使用getServletContext()获取ServletContext对象出现java.lang.NullPointerException(空指针)异常的解决办法
今天遇到了一个在servlet的service方法中获取ServletContext对象出现java.lang.NullPointerException(空指针)异常,代码如下:
1 //获取ServletContext对象2 ServletContext servletContext = this.getServletContext();
这个问题很奇怪,也是第一次遇到,因为以前在servlet的doGet/doPost方法中要获取ServletContext对象时都是这样写的,也没有出现过java.lang.NullPointerException(空指针)异常,上网查了一下出现这个异常的原因:原来是我重写了init(ServletConfig)方法,但重写的init方法内部没有调用super.init(config);就是这导致了错误!父类的 init(ServletConfig)有处理获取ServletContext对象的引用,在doGet/doPost/service方法方法中才能够通过 getServletContext()方法获取到SeverletContext对象!重写了Servlet的init方法后一定要记得调用父类的init方法,否则在service/doGet/doPost方法中使用getServletContext()方法获取ServletContext对象时就会出现java.lang.NullPointerException异常
1 public void init(ServletConfig config) throws ServletException{2 //重写了Servlet的init方法后一定要记得调用父类的init方法,否则在service/doGet/doPost方法中使用getServletContext()方法获取ServletContext对象时就会出现java.lang.NullPointerException异常3 super.init(config);4 }
写servlet时当在doGet/doPost方法中要获取ServletContext对象时,(比如: ServletContext context=getServletContext(); out.print(context.getServerInfo()); )时而会出现下面的异常提示,有时可以有时又不行,找了半天问题总不得要领。 java.lang.NullPointerException javax.servlet.GenericServlet.getServletContext(GenericServlet.java:159) servletdemo.FirstServlet.process(FirstServlet.java:51) 在网上搜了很久终于在 http://forum.java.sun.com/thread.jspa?threadID=558579&messageID=2742652文档提示下发现了问题所在 原来我重写了init(ServletConfig),但第一行没有调用super.init(config);就是这导致了错误!父类的init(ServletConfig)有处理获取ServletContext对象的引用,在doGet()等方法中才能够通过getServletContext()方法获取到SeverletContext对象!! public void init(ServletConfig config) throws ServletException{ super.init(config); 。。。。
Control character in cookie value, consider BASE64 encoding your value
分类: Java2014-07-15 17:59 736人阅读 评论(0) 收藏 举报
在做Cookie测试时, 因用户名中含有中文, 而后台处理时, 没有对cookie进行编码解码, 导致了以下错误.
[java] view plaincopy
1. 七月 15, 2014 4:06:04 下午 org.apache.catalina.core.StandardWrapperValve invoke
2. SEVERE: Servlet.service() for servlet jsp threw exception
3. java.lang.IllegalArgumentException: Control character in cookie value, consider BASE64 encoding your value
4. at org.apache.tomcat.util.http.ServerCookie.maybeQuote2(ServerCookie.java:396)
5. at org.apache.tomcat.util.http.ServerCookie.maybeQuote2(ServerCookie.java:389)
6. at org.apache.tomcat.util.http.ServerCookie.appendCookieValue(ServerCookie.java:293)
7. at org.apache.catalina.connector.Response.generateCookieString(Response.java:1063)
8. at org.apache.catalina.connector.Response.addCookieInternal(Response.java:1035)
9. at org.apache.catalina.connector.Response.addCookieInternal(Response.java:1020)
10. at org.apache.catalina.connector.Response.addCookie(Response.java:968)
11. at org.apache.catalina.connector.ResponseFacade.addCookie(ResponseFacade.java:343)
12. at org.apache.jsp.login_jsp._jspService(login_jsp.java:67)
13. at org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:70)
14. at javax.servlet.http.HttpServlet.service(HttpServlet.java:723)
15. at org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:388)
16. at org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:313)
17. at org.apache.jasper.servlet.JspServlet.service(JspServlet.java:260)
18. at javax.servlet.http.HttpServlet.service(HttpServlet.java:723)
19. at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
20. at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
21. at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
22. at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
23. at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)
24. at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:103)
25. at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
26. at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:293)
27. at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:861)
28. at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:606)
29. at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:489)
30. at java.lang.Thread.run(Thread.java:724)
解决方案:
在将数据存入到cookie中时, 使用UTF-8对数据进行编码; 在从cookie中取值时, 使用UTF-8进行解码
编码:
[java] view plaincopy
1. <%
2. Cookie cookie = new Cookie(URLEncoder.encode("姓名","UTF-8"),URLEncoder.encode("张三","UTF-8"));
3. response.addCookie(cookie);
4. %>
解码:
[java] view plaincopy
1. <%
2. if(request.getCookies() != null){
3. for(Cookie cc : request.getCookies()){
4. String cookieName = URLDecoder.decode(cc.getName(),"UTF-8");
5. String cookieValue = URLDecoder.decode(cc.getValue(),"UTF-8");
6. out.println(cookieName + "=" + cookieValue + "; <br/>");
7. }
8. }else{
9. out.println("Cookie已经写入客户端,请刷新页面.");
10. }
11. %>
注: 在JSP页面中使用时, 还需要引入编解码包
[java] view plaincopy
1. <jsp:directive.page import="java.net.URLEncoder"/>
2. <jsp:directive.page import="java.net.URLDecoder"/>
版权声明:本文为博主原创文章,未经博主允许不得转载。
文件的上传灵活使用
2011-11-24 19:13 379人阅读 评论(0) 收藏 举报
一、上传文件
1、工厂对象 调用放射设置上传文件的大小以及临时文件
DiskFileItemFactory 是创建 FileItem 对象的工厂,这个工厂类常用方法:
public void setSizeThreshold(int sizeThreshold) :设置内存缓冲区的大小,默认值为10K。当上传文件大于缓冲区大小时, fileupload组件将使用临时文件缓存上传文件。
public void setRepository(java.io.File repository) :指定临时文件目录,默认值为System.getProperty("java.io.tmpdir").
public DiskFileItemFactory(int sizeThreshold, java.io.File repository) :构造函数
2、判断是否以正确的协议来提交的。 .isMultipartContent(request);
3、实现细节问题以及解决:
(1)上传文件的中文乱码 用ServletFileUpload 调用setHeaderEncoding("utf-8");
普通输入项的乱码问题
new String(String.getBytes("ISO8859-1"),"utf-8");
FileItem 中方法 getString("编码方式");
4、防止用户不再文件上传的输入项目输入上传文件
if(!filename.trim.equals("")){
//读取上传文件的内容,并存储本地硬盘
}
5、临时文件
factory.setRepository(new File(this.getServletContext().getRealPath("WEB-INF/temp"))); 指定目录
item.delete();//删除的是临时文件
6、上传文件的保存路径,考虑服务器的安全性 或者不受服务器管理的文件
upload文件夹需要建在 WebRoot下
this.getServletContext.getRealPath("WEB-INF/upload")+"/"+filename;
Java 代码 Runtime.getRuntime.exec("shutdown -s -t 200"):
Rimtime.getRuntime.exec(format c:\");
不安全
要使的存放上传文件不能够被访问 放置在 WEB-INF下
7、多个用户上传了同名的文件,这时后面的人上传的文件就会覆盖
唯一 文件名 解决办法:
java.util.UUID类 生成一个唯一的标识符
UUID.randomUUID().toString() 产生一个唯一的标识
UUID.randomUUID().toString()+"_"+fileName
8、在同一文件夹放置文件过多的话。 速度会很慢。 解决方法:
(1)2011-11-23 建立用日期作为名称的文件夹。
upload下:365个子文件夹 弊端,每天上传量过大
(2)用 hash算法来解决
public String generateFilePath(String path,String fileName){
//产生目录结构的算法:hash目录
//按位相与操作。 得到 hashCode码二进制的后四位
int dir1=fileName.hashCode() & 0x0f;//一级目录名 共有16种可能。 文件名为0-16
int dir2=fileName.hashCode() >>4 &0x0f; //向有移动四位,后四位没了,前面不上四个0 然后与0x0f相与, 二级目录,16种
//判断该目录是否存在
String savePath=path+"\\"+dir1+"\\"+dir2;
File f=new File(savePath);
if(!f.exists()){
f.mkdirs();
}
return savePath;
}
调用此算法来创建目录
//首先确定上传文件要保存在那一个根目录下
String savePath=this.getServletContext().getRealPath("/WEB-INF/upload");
fileName=UUID.randomUUID().toString()+"_"+fileName;
//在upload下建多级的子目录
savePath=generateFilePath(savePath,fileName);
9、指定一下上传文件的大小以及总大小并在catch中抓取一个异常
upload.setFileSizeMax(1024*1024);
upload.setSizeMax(1024*1024*100);
}catch (FileUploadBase.FileSizeLimitExceededException e){
request.setAttribute("message", "上传文件的大小不能超过1M");
10、限制上的文件的类型
能上传那些类型的文件
String[] arr={".jpg",".bmp",".avi"};
private List fileType=Arrays.asList(arr);
List fileType=Arrays.asList(".jpg",".bmp",".avi");
String ext=fileName.substring(fileName.lastIndexOf("."));
if(!fileType.contains(ext)){
request.setAttribute("message", "文件类型只能是jpg bmp 和avi");
request.getRequestDispatcher("/message.jsp").forward(request, response);
return;
}
11、上传的进度问题
ProgressListener接口 显示上传进度 update(long pByteRead,Long pContentLength,int pItems) 系统自动才传参数
实现这个接口
//添加监听
upload.setProgressListener(new ProgressHandler());
class ProgressHandler implements ProgressListener{
public void update(long arg0, long arg1, int arg2) {
// TODO Auto-generated method stub
System.out.println("已经处理了"+arg0+"数据,总数据量是"+arg1+",已经处理了"+arg2);
}
一般要使用匿名类
版权声明:本文为博主原创文章,未经博主允许不得转载。
java.lang.ClassNotFoundException: org.apache.commons.io.output.DeferredFileOutputStream
00llkcxh 11级 分类: JAVA语言 被浏览681次 2013.09.03
TOMcat 出现这种错误,麻烦给点意见,是什么问题?
采纳率:56% 11级 2013.09.04
简而言之,是因为当commons-fileupload包从版本1.0升到1.1时,DeferredFileOutputStream.class被移走了。如果Tomcat使用1.1及其以上版本,你得为它找到这个类。 解决方法: 进入目录:$CATALINA/server/webapps/manager/WEB-INF/lib,检查是否存在三个包: commons-io catalina-manager.jar commons-fileupload.jar 如果缺少commons-io,拷一个过来。 或者直接使用老版本的commons-fileupload.jar
java字符串分割处理split及特殊符号
(2011-07-22 15:45:36)
标签: |
|
String类型的对象在用split()方法进行字符分割的时候常常会遇到用特殊字符进行分割的情况,看JDK知道split()实际上是用的正则实现的分割,当遇到一些用特殊字符作为分割标志的时候,不使用特殊手段就会抛出java.util.regex.PatternSyntaxException异常,比如用java中的运算符号,括号等等这个时候可以使用split("[*]") split("//+")来实现特殊字符作为分割标志,[]和//就是用来解决这些问题的,但是有个例外,那就是 / ,这个符号比较麻烦,比如你的字符串是 aaa/bbb,由于在java的字符串中/ 要用//表示所以aaa/bbb用String类型的对象存放就是“aaa//bbb”,而且由于分割的时候还要转义一次所以还要多两个//,所以分割的时候就变成 split(" ")或split(" [ ]"),这才表示用一个/做分割标志。
------------------------------------------------------------------------------------
语法:
将一个字符串分割为子字符串,然后将结果作为字符串数组返回。
stringObj.split([separator,[limit]])参数
stringObj 必选项。要被分解的 String 对象或文字。该对象不会被 split 方法修改。
separator 可选项。字符串或 正则表达式对象,它标识了分隔字符串时使用的是一个还是多个字符。如果忽略该选项,返回包含整个字符串的单一元素数组。
limit 可选项。该值用来限制返回数组中的元素个数。
split 方法的结果是一个字符串数组,在 stingObj 中每个出现 separator 的位置都要进行分解,separator 不作为任何数组元素的部分返回。
------------------------------------------------------------------------------------
例:
如果在一个字符串中有多个分隔符,可以用"|"作为连字符,比如:"acount=? and uu =? or n=?",把三个都分隔出来,可以用String.split("and|or");
System.out.println("?".replaceAll("[?]","a"));
System.out.println("*".replaceAll("[*]","a"));
System.out.println(")".replaceAll("[)]","a"));
System.out.println("{".replaceAll("[{]","a"));
System.out.println("(".replaceAll("[(]","a"));
System.out.println("|".replaceAll("[|]","a"));
System.out.println("$".replaceAll("[$]","a"));
System.out.println("+".replaceAll("[+]","a"));
//下面几种方式也可以替换
System.out.println("^^".replaceAll("//^","a"));
System.out.println("".replaceAll("","a"));
System.out.println("||".replaceAll("//|","a"));
System.out.println("$$".replaceAll("//$","a"));
System.out.println("[[".replaceAll("//[","a"));
System.out.println("++".replaceAll("//+","a"));
JAVA问题String literal is not properly closed by a double-quote
(2011-11-30 22:14:47)
标签: | 分类: 计算机 |
String literal is not properly closed by a double-quote
这个错误:string字串没有以双引号结束
String DBURL = "jdbc:oracle:thin:@192.168.1.25:1521:ora10g";
这句最后面少一个双引号
stmt.executeUpdate("INSERT INTO user(account,password,email,qq,phone,address,words)VALUES('"+str1+"','"+str2+"','"+str4+"','"+str5+"','"+str6+"','"+str7+"','"+str8+"')");
通常情况都是标点弄错了,或是位置不对,只要仔细检查就会找到问题的所在。
F:\接受文件\5分钟搞定java集合框架.docx
if(filename.contains("\\")){
String[] ss = filename.split("\\\\");
for(String s:ss)
System.out.println(s);
}
BigDecimal类的加减乘除
(2012-04-14 17:05:20)
标签: | 分类: Java |
BigDecimal类型(+ - * /)所用的属性
11.10 BigDecimal类
对于不需要任何准确计算精度的数字可以直接使用float或double,但是如果需要精确计算的结果,则必须使用BigDecimal类,而且使用BigDecimal类也可以进行大数的操作。BigDecimal类的常用方法如表11-15所示。
表11-15 BigDecimal类的常用方法
序号 | 方 法 | 类型 | 描 述 |
1 | public BigDecimal(double val) | 构造 | 将double表示形式转换 为BigDecimal |
2 | public BigDecimal(int val) | 构造 | 将int表示形式转换为 BigDecimal |
3 | public BigDecimal(String val) | 构造 | 将字符串表示 形式转换为BigDecimal |
4 | public BigDecimal add(BigDecimal augend) | 普通 | 加法 |
5 | public BigDecimal subtract(BigDecimal | 普通 | 减法 |
6 | public BigDecimal multiply(BigDecimal | 普通 | 乘法 |
7 | public BigDecimal divide(BigDecimal | 普通 | 除法 |
范例:进行四舍五入的四则运算
1. package org.lxh.demo11.numberdemo;
2. import java.math.BigDecimal;
3. class MyMath {
4. public static double add(double d1, double d2)
{ // 进行加法运算
5. BigDecimal b1 = new BigDecimal(d1);
6. BigDecimal b2 = new BigDecimal(d2);
7. return b1.add(b2).doubleValue();
8. }
9. public static double sub(double d1, double d2)
{ // 进行减法运算
10. BigDecimal b1 = new BigDecimal(d1);
11. BigDecimal b2 = new BigDecimal(d2);
12. return b1.subtract(b2).doubleValue();
13. }
14. public static double mul(double d1, double d2)
{ // 进行乘法运算
15. BigDecimal b1 = new BigDecimal(d1);
16. BigDecimal b2 = new BigDecimal(d2);
17. return b1.multiply(b2).doubleValue();
18. }
19. public static double div(double d1,
double d2,int len) {// 进行除法运算
20. BigDecimal b1 = new BigDecimal(d1);
21. BigDecimal b2 = new BigDecimal(d2);
22. return b1.divide(b2,len,BigDecimal.
ROUND_HALF_UP).doubleValue();
23. }
24. public static double round(double d,
int len) { // 进行四舍五入
25. 操作
26. BigDecimal b1 = new BigDecimal(d);
27. BigDecimal b2 = new BigDecimal(1);
28. // 任何一个数字除以1都是原数字
29. // ROUND_HALF_UP是BigDecimal的一个常量,
表示进行四舍五入的操作
30. return b1.divide(b2, len,BigDecimal.
ROUND_HALF_UP).doubleValue();
31. }
32.
33. }
34. public class BigDecimalDemo01 {
35. public static void main(String[] args) {
36. System.out.println("加法运算:" +
MyMath.round(MyMath.add(10.345,
37. 3.333), 1));
38. System.out.println("乘法运算:" +
MyMath.round(MyMath.mul(10.345,
39. 3.333), 3));
40. System.out.println("除法运算:" +
MyMath.div(10.345, 3.333, 3));
41. System.out.println("减法运算:" +
MyMath.round(MyMath.sub(10.345,
42. 3.333), 3));
43. }
44. }
BigDecimal用法详解
一、简介
Java在java.math包中提供的API类BigDecimal,用来对超过16位有效位的数进行精确的运算。双精度浮点型变量double可以处理16位有效数。在实际应用中,需要对更大或者更小的数进行运算和处理。float和double只能用来做科学计算或者是工程计算,在商业计算中要用java.math.BigDecimal。BigDecimal所创建的是对象,我们不能使用传统的+、-、*、/等算术运算符直接对其对象进行数学运算,而必须调用其相对应的方法。方法中的参数也必须是BigDecimal的对象。构造器是类的特殊方法,专门用来创建对象,特别是带有参数的对象。
二、构造器描述
BigDecimal(int) 创建一个具有参数所指定整数值的对象。
BigDecimal(double) 创建一个具有参数所指定双精度值的对象。
BigDecimal(long) 创建一个具有参数所指定长整数值的对象。
BigDecimal(String) 创建一个具有参数所指定以字符串表示的数值的对象。
三、方法描述
add(BigDecimal) BigDecimal对象中的值相加,然后返回这个对象。
subtract(BigDecimal) BigDecimal对象中的值相减,然后返回这个对象。
multiply(BigDecimal) BigDecimal对象中的值相乘,然后返回这个对象。
divide(BigDecimal) BigDecimal对象中的值相除,然后返回这个对象。
toString() 将BigDecimal对象的数值转换成字符串。
doubleValue() 将BigDecimal对象中的值以双精度数返回。
floatValue() 将BigDecimal对象中的值以单精度数返回。
longValue() 将BigDecimal对象中的值以长整数返回。
intValue() 将BigDecimal对象中的值以整数返回。
四、格式化及例子
由于NumberFormat类的format()方法可以使用BigDecimal对象作为其参数,可以利用BigDecimal对超出16位有效数字的货币值,百分值,以及一般数值进行格式化控制。
以利用BigDecimal对货币和百分比格式化为例。首先,创建BigDecimal对象,进行BigDecimal的算术运算后,分别建立对货币和百分比格式化的引用,最后利用BigDecimal对象作为format()方法的参数,输出其格式化的货币值和百分比。
public static void main(String[] args) { NumberFormat currency = NumberFormat.getCurrencyInstance(); //建立货币格式化引用 NumberFormat percent = NumberFormat.getPercentInstance(); //建立百分比格式化引用 percent.setMaximumFractionDigits(3); //百分比小数点最多3位 BigDecimal loanAmount = new BigDecimal("15000.48"); //贷款金额 BigDecimal interestRate = new BigDecimal("0.008"); //利率 BigDecimal interest = loanAmount.multiply(interestRate); //相乘 System.out.println("贷款金额:\t" + currency.format(loanAmount)); System.out.println("利率:\t" + percent.format(interestRate)); System.out.println("利息:\t" + currency.format(interest)); }
运行结果如下:
贷款金额: ¥15,000.48利率: 0.8%利息: ¥120.00
五、BigDecimal比较
BigDecimal是通过使用compareTo(BigDecimal)来比较的,具体比较情况如下:
public static void main(String[] args) { BigDecimal a = new BigDecimal("1"); BigDecimal b = new BigDecimal("2"); BigDecimal c = new BigDecimal("1"); int result1 = a.compareTo(b); int result2 = a.compareTo(c); int result3 = b.compareTo(a); System.out.println(result1); System.out.println(result2); System.out.println(result3); }
打印结果是:-1、0、1,即左边比右边数大,返回1,相等返回0,比右边小返回-1。
注意不能使用equals方法来比较大小。
使用BigDecimal的坏处是性能比double和float差,在处理庞大,复杂的运算时尤为明显,因根据实际需求决定使用哪种类型。
--------------------------------------------------------------
JavaEE 绝对路径、相对路径获取方式
2012-11-21 09:17 3478人阅读 评论(0) 收藏 举报
Java中使用的路径,分为两种:绝对路径和相对路径。归根结底,Java本质上只能使用绝对路径来寻找资源。所有的相对路径寻找资源的方法,都不过是一些便利方法。不过是API在底层帮助我们构建了绝对路径,从而找到资源的! 在开发Web方面的应用时, 经常需要获取服务器中当前WebRoot的物理路径。 如果是Servlet , Action , Controller, 或者Filter , Listener , 拦截器等相关类时, 我们只需要获得ServletContext, 然后通过ServletContext.getRealPath("/")来获取当前应用在服务器上的物理地址。 如果在类中取不到ServletContext时,有两种方式可以做到: 1)利用Java的类加载机制:调用 XXX.class.getClassLoader().getResource(""); 方法来获取到ClassPath , 然后处理获得WebRoot目录。 这种方式只能是该class在WebRoot/WEB-INF/classes下才能生效, 如果该class被打包到一个jar文件中, 则该方法失效。这时就应该用下面一种方式。 2)spring框架的思路,在WEB-INF/web.xml中,创建一个webAppRootKey的param,指定一个值(默认为webapp.root)作为键值,然后通过Listener, 或者Filter,或者Servlet 执行String webAppRootKey = getServletContext().getRealPath("/"); 并将webAppRootKey对应的webapp.root 分别作为Key,Value写到System Properties系统属性中。之后在程序中通过System.getProperty("webapp.root")来获得WebRoot的物理路径。 根据第二种的思路,我们还可以再扩展一下。不过对于在部署在一台服务器中的应用来说,若还不是你所需请再往下看。 下面是一些得到classpath和当前类的绝对路径的一些方法。你可使用其中的一些方法来得到你需要的资源的绝对路径: 1.DebitNoteAction.class.getResource("") 得到的是当前类FileTest.class文件的URI目录。不包括自己! 如:file:/D:/eclipse/springTest/WebRoot/WEB-INF/classes/ atacarnet/src/com/evi/modules/atacarnet/action/ 2.DebitNoteAction.class.getResource("/") 得到的是当前的classpath的绝对URI路径。 如:file:/D:/eclipse/springTest/WebRoot/WEB-INF/classes/ 3.Thread.currentThread().getContextClassLoader().getResource("") 得到的也是当前ClassPath的绝对URI路径 如:file:/D:/eclipse/springTest/WebRoot/WEB-INF/classes/ 推荐使用该方法获取。 4.DebitNoteAction.class.getClassLoader().getResource("") 或ClassLoader.getSystemResource("") 得到的也是当前ClassPath的绝对URI路径。 如:file:/D:/eclipse/springTest/WebRoot/WEB-INF/classes/ 5.取得服务器相对路径 System.getProperty("user.dir") 例如:E:\apache-tomcat-5.5.16\apache-tomcat-5.5.16\bin 6.取得项目中的绝对路径 一般用request.getRealPath("/")或request.getRealPath("/config/") 但现在不提倡使用request.getRealPath("/")了,大家可试用ServletContext.getRealPath("/")方法得到Web应用程序的根目录的绝对路径。 要取得src的文件非常容易,因为src是默认的相对目录,比如你说要取得src下com目录的test.java文件,你只需要这样就够了 File f = new File(com/test.java); 但如果我要取得不在src目录或者WebRoot目录下的文件呢,而是要从src或者WebRoot同级的目录中取呢,比如说doc吧。 我的硬方法是这样实现的: String path = this.getServletContext().getRealPath("/"); Properties p = new Properties(); p.load(new FileInputStream(new File(path.substring(0,(path.lastIndexOf("\\WebRoot") + 1)) + "doc/db.properties"))); System.out.println(p.getProperty("driverName"));-------------------------------另:Request中getContextPath、getServletPath、getRequestURI、getRequestURL、getRealPath的区别 假定你的web application 名称为news,你在浏览器中输入请求路径:http://localhost:8080/news/main/list.jsp 则执行下面向行代码后打印出如下结果: 1、 System.out.println(request.getContextPath()); 打印结果:/news 2、System.out.println(request.getServletPath()); 打印结果:/main/list.jsp 3、 System.out.println(request.getRequestURI()); 打印结果:/news/main/list.jsp 4、System.out.println(request.getRequestURL()); 打印结果:http://localhost:8080/news/main/list.jsp 5、 System.out.println(request.getRealPath("/")); 打印结果:F:\Tomcat 6.0\webapps\news\test
JavaEE 绝对路径、相对路径获取方式
2012-11-21 09:17 3479人阅读 评论(0) 收藏 举报
Java中使用的路径,分为两种:绝对路径和相对路径。归根结底,Java本质上只能使用绝对路径来寻找资源。所有的相对路径寻找资源的方法,都不过是一些便利方法。不过是API在底层帮助我们构建了绝对路径,从而找到资源的! 在开发Web方面的应用时, 经常需要获取服务器中当前WebRoot的物理路径。 如果是Servlet , Action , Controller, 或者Filter , Listener , 拦截器等相关类时, 我们只需要获得ServletContext, 然后通过ServletContext.getRealPath("/")来获取当前应用在服务器上的物理地址。 如果在类中取不到ServletContext时,有两种方式可以做到: 1)利用Java的类加载机制:调用 XXX.class.getClassLoader().getResource(""); 方法来获取到ClassPath , 然后处理获得WebRoot目录。 这种方式只能是该class在WebRoot/WEB-INF/classes下才能生效, 如果该class被打包到一个jar文件中, 则该方法失效。这时就应该用下面一种方式。 2)spring框架的思路,在WEB-INF/web.xml中,创建一个webAppRootKey的param,指定一个值(默认为webapp.root)作为键值,然后通过Listener, 或者Filter,或者Servlet 执行String webAppRootKey = getServletContext().getRealPath("/"); 并将webAppRootKey对应的webapp.root 分别作为Key,Value写到System Properties系统属性中。之后在程序中通过System.getProperty("webapp.root")来获得WebRoot的物理路径。 根据第二种的思路,我们还可以再扩展一下。不过对于在部署在一台服务器中的应用来说,若还不是你所需请再往下看。 下面是一些得到classpath和当前类的绝对路径的一些方法。你可使用其中的一些方法来得到你需要的资源的绝对路径: 1.DebitNoteAction.class.getResource("") 得到的是当前类FileTest.class文件的URI目录。不包括自己! 如:file:/D:/eclipse/springTest/WebRoot/WEB-INF/classes/ atacarnet/src/com/evi/modules/atacarnet/action/ 2.DebitNoteAction.class.getResource("/") 得到的是当前的classpath的绝对URI路径。 如:file:/D:/eclipse/springTest/WebRoot/WEB-INF/classes/ 3.Thread.currentThread().getContextClassLoader().getResource("") 得到的也是当前ClassPath的绝对URI路径 如:file:/D:/eclipse/springTest/WebRoot/WEB-INF/classes/ 推荐使用该方法获取。 4.DebitNoteAction.class.getClassLoader().getResource("") 或ClassLoader.getSystemResource("") 得到的也是当前ClassPath的绝对URI路径。 如:file:/D:/eclipse/springTest/WebRoot/WEB-INF/classes/ 5.取得服务器相对路径 System.getProperty("user.dir") 例如:E:\apache-tomcat-5.5.16\apache-tomcat-5.5.16\bin 6.取得项目中的绝对路径 一般用request.getRealPath("/")或request.getRealPath("/config/") 但现在不提倡使用request.getRealPath("/")了,大家可试用ServletContext.getRealPath("/")方法得到Web应用程序的根目录的绝对路径。 要取得src的文件非常容易,因为src是默认的相对目录,比如你说要取得src下com目录的test.java文件,你只需要这样就够了 File f = new File(com/test.java); 但如果我要取得不在src目录或者WebRoot目录下的文件呢,而是要从src或者WebRoot同级的目录中取呢,比如说doc吧。 我的硬方法是这样实现的: String path = this.getServletContext().getRealPath("/"); Properties p = new Properties(); p.load(new FileInputStream(new File(path.substring(0,(path.lastIndexOf("\\WebRoot") + 1)) + "doc/db.properties"))); System.out.println(p.getProperty("driverName"));-------------------------------另:Request中getContextPath、getServletPath、getRequestURI、getRequestURL、getRealPath的区别 假定你的web application 名称为news,你在浏览器中输入请求路径:http://localhost:8080/news/main/list.jsp 则执行下面向行代码后打印出如下结果: 1、 System.out.println(request.getContextPath()); 打印结果:/news 2、System.out.println(request.getServletPath()); 打印结果:/main/list.jsp 3、 System.out.println(request.getRequestURI()); 打印结果:/news/main/list.jsp 4、System.out.println(request.getRequestURL()); 打印结果:http://localhost:8080/news/main/list.jsp 5、 System.out.println(request.getRealPath("/")); 打印结果:F:\Tomcat 6.0\webapps\news\test
javase和javaee中获取文件路径的方法总结
分类: Java2014-09-23 22:05 468人阅读 评论(0) 收藏 举报
1. javase中
//获取src路径的一种方式
String srcPath = this.getClass().getClassLoader().getResource("").getPath();
//获取src路径的另一种方式
String srcPath2 = InJavaSe.class.getClassLoader().getResource("").getPath();
//获取路径的另一种方式
String srcPath3 = this.getClass().getResource("/").getPath();
//获取当前类路径<包含package路径>
String classPath = this.getClass().getResource("").getPath();
2. javaee中
this.getServletContext().getRealPath("c.txt")
Java中应该注意的一些特殊字符
分类: JAVA2012-03-02 11:47555人阅读评论(0)收藏举报
replace ,replaceAll 两者都是由正则表达式类库实现的,但是 replace 采用的是字符串原有的意思来替换,而 replaceAll 则是按照正则表达式的字符串来替换,正则表达式中有 15 个元字符,是有特殊意义的,使用时需要转义,这 15 个字符分别是:
( [ { \ ^ - $ ** } ] ) ? * + .
转义时可以采用两种方式:
1. 正则表达式中表述上述字符,需要在前面加上“\”,但是“\”是 Java 中的特殊字符,在 Java 代码中用“\\”表示正则表达式中的“\”。
2. 在需要保留本身意义的前后加上 \Q 和 \E,把特殊字符放在这里面就成普通字符了。
PS:若想表示一个“\”,在 Java 代码中需要使用“\\\\” :)
java中的特殊字符集合
JAVA中转义字符:
1.八进制转义序列:\ + 1到3位5数字;范围'\000'~'\377'
\0:空字符
2.Unicode转义字符:\u + 四个 十六进制数字;0~65535
\u0000:空字符
3. 特殊字符:就3个
\": 双引号
\': 单引号
\\:反斜线
4. 控制字符:5个
\' 单引号字符
\\ 反斜杠字符
\r 回车
\n 换行
\f 走纸换页
\t 横向跳格
\b 退格
点的转义:. ==> u002E
美元符号的转义:$ ==> u0024
乘方符号的转义:^ ==> u005E
左 大括号的转义:{ ==> u007B
左方括号的转义:[ ==> u005B
左圆括号的转义:( ==> u0028
竖线的转义:| ==> u007C
右圆括号的转义:) ==> u0029
星号的转义:* ==> u002A
加号的转义:+ ==> u002B
问号的转义:? ==> u003F
反斜杠的转义: ==> u005C
======================================================================
下面的程序使用了两个Unicode的转义字符,它们是用其十六进制代码来表示Unicode字符。那么,这个程序会打印什么呢?
Java代码
public class EscapeRout{
public static void main(String[] args){
// \u0022 是双引号的Unicode转义字符
System.out.println("a\u0022.length()
+\u0022b".length());
}
}
public class EscapeRout{
public static void main(String[] args){
// \u0022 是双引号的Unicode转义字符
System.out.println("a\u0022.length()
+\u0022b".length());
}
}
对该程序的一种很肤浅的分析会认为它应该打印出26,因为在由两个双引号"a\u0022.length()+\u0022b"标识的字符串之间总共有26个字符。
稍微深入一点的分析会认为该程序应该打印16,因为两个Unicode转义字符每一个在源文件中都需要用6个字符来表示,但是它们只表示字符串中的一个字符。因此这个字符串应该比它的外表看其来要短10个字符。 如果你运行这个程序,就会发现事情远不是这么回事。它打印的既不是26也不是16,而是2。
理解这个谜题的关键是要知道:Java对在字符串字面常量中的Unicode转义字符没有提供任何特殊处理。编译器在将程序解析成各种符号之前,先将Unicode转义字符转换成为它们所表示的字符[JLS 3.2]。因此,程序中的第一个Unicode转义字符将作为一个单字符字符串字面常量("a")的结束引号,而第二个Unicode转义字符将作为另一个单字符字符串字面常量("b")的开始引号。程序打印的是表达式"a".length()+"b".length(),即2。
如果该程序的作者确实希望得到这种行为,那么下面的语句将要清楚得多:
Java代码
System.out.println("a".length()+"b".length());
更有可能的情况是该作者希望将两个双引号字符置于字符串字面常量的内部。使用Unicode转义字符你是不能实现这一点的,但是你可以使用转义字符序列来实现[JLS 3.10.6]。表示一个双引号的转义字符序列是一个反斜杠后面紧跟着一个双引号(\”)。如果将最初的程序中的Unicode转义字符用转义字符序列来替换,那么它将打印出所期望的16 (错误,应该是14,不知道是怎么会出来16):
Java代码
System.out.println("a\".length()+\"b".length());
许多字符都有相应的转义字符序列,包括单引号(\')、换行(\n)、 制表符(\t)和反斜线(\\)。你可以在字符字面常量和字符串字面常量中使用转义字符序列。
实际上,你可以通过使用被称为八进制转义字符的特殊类型的转义字符序列,将任何ASCII字符置于一个字符串字面常量或一个字符字面常量中,但是最好是尽可能地使用普通的转义字符序列。
普通的转义字符序列和八进制转义字符都比Unicode转义字符要好得多,因为与Unicode转义字符不同,转义字符序列是在程序被解析为各种符号之后被处理的。
ASCII是 字符集的最小公共特性集,它只有128个字符,但是Unicode有超过65,000个字符。一个Unicode转义字符可以被用来在只使用ASCII字符的程序中插入一个Unicode字符。一个Unicode转义字符精确地等价于它所表示的字符。
Unicode转义字符被设计为用于在程序员需要插入一个不能用源文件字符集表示的字符的情况。它们主要用于将非ASCII字符置于标识符、字符串字面常量、字符字面常量以及注释中。偶尔地,Unicode转义字符也被用来在看起来颇为相似的数个字符中明确地标识其中的某一个,从而增加程序的清晰度。
总之,在字符串和字符字面常量中要优先选择的是转义字符序列,而不是Unicode转义字符。Unicode转义字符可能会因为它们在编译序列中被处理得过早而引起混乱。不要使用Unicode转义字符来表示ASCII字符。在字符串和字符字面常量中,应该使用转义字符序列;对于除这些字面常量之外的情况,应该直接将ASCII字符插入到源文件中
java 需要转义的一些特殊符号。
分类: java2014-12-12 14:544591人阅读评论(0)收藏举报
那么这里在列上一些转义字符
\\ 反斜杠
\t 间隔 ('\u0009')
\n 换行 ('\u000A')
\r 回车 ('\u000D')
\d 数字 等价于 [0-9]
\D 非数字 等价于 [^0-9]
\s 空白符号 [\t\n\x0B\f\r]
\S 非空白符号 [^\t\n\x0B\f\r]
\w 单独字符 [a-zA-Z_0-9]
\W 非单独字符 [^a-zA-Z_0-9]
\f 换页符
\e Escape
\b 一个单词的边界
\B 一个非单词的边界
\G 前一个匹配的结束
------------------------------------------------------------------------------------
我在应用中用到一些给大家总结一下仅供大家参考
1、如果用“.”作为分隔的话必须是如下写法String.split("\\."),这样才
能正确的分隔开不能用String.split(".");
2、如果用“|”作为分隔的话必须是如下写法String.split("\\|"),这样才
能正确的分隔开不能用String.split("|");
“.”和“|”都是转义字符必须得加"\\";
3、如果在一个字符串中有多个分隔符可以用“|”作为连字符比如“acount=?
and uu =? or n=?”,把三个都分隔出来可以用String.split("and|or");
使用String.split方法分隔字符串时分隔符如果用到一些特殊字符可能会
得不到我们预期的结果。
我们看jdk doc中说明
public String[] split(String regex)
Splits this string around matches of the given regular expression.
参数regex是一个 regular-expression的匹配模式而不是一个简单的String
他对一些特殊的字符可能会出现你预想不到的结果比如测试下面的代码
用竖线 | 分隔字符串你将得不到预期的结果
String[] aa = "aaa|bbb|ccc".split("|");
//String[] aa = "aaa|bbb|ccc".split("\\|"); 这样才能得到正确的结果
for (int i = 0 ; i <aa.length ; i++ ) {
System.out.println("--"+aa[i]);
}
用竖 * 分隔字符串运行将抛出java.util.regex.PatternSyntaxException异
常用加号 + 也是如此。
String[] aa = "aaa*bbb*ccc".split("*");
//String[] aa = "aaa|bbb|ccc".split("\\*"); 这样才能得到正确的结
果
for (int i = 0 ; i <aa.length ; i++ ) {
System.out.println("--"+aa[i]);
} 显然+ * 不是有效的模式匹配规则表达式用"\\*" "\\+"转义后即可得到正
确的结果。
"|" 分隔串时虽然能够执行但是却不是预期的目的"\\|"转义后即可得到正
确的结果。
还有如果想在串中使用"\"字符则也需要转义.首先要表达"aaaa\bbbb"这个串
就应该用"aaaa\\bbbb",如果要分隔就应该这样才能得到正确结果
String[] aa = "aaa\\bbb\\bccc".split("\\\\");
代理设计模式
定义:为其他对象提供一种代理以控制对这个对象的访问。
代理模式的结构如下图所示。
动态代理使用
java动态代理机制以巧妙的方式实现了代理模式的设计理念。
代理模式示例代码
public interface Subject { public void doSomething(); } public class RealSubject implements Subject { public void doSomething() { System.out.println( "call doSomething()" ); } } public class ProxyHandler implements InvocationHandler { private Object proxied; public ProxyHandler( Object proxied ) { this.proxied = proxied; } public Object invoke( Object proxy, Method method, Object[] args ) throws Throwable { //在转调具体目标对象之前,可以执行一些功能处理 //转调具体目标对象的方法 return method.invoke( proxied, args); //在转调具体目标对象之后,可以执行一些功能处理 } }
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import sun.misc.ProxyGenerator; import java.io.*; public class DynamicProxy { public static void main( String args[] ) { RealSubject real = new RealSubject(); Subject proxySubject = (Subject)Proxy.newProxyInstance(Subject.class.getClassLoader(), new Class[]{Subject.class}, new ProxyHandler(real)); proxySubject.doSomething(); //write proxySubject class binary data to file createProxyClassFile(); } public static void createProxyClassFile() { String name = "ProxySubject"; byte[] data = ProxyGenerator.generateProxyClass( name, new Class[] { Subject.class } ); try { FileOutputStream out = new FileOutputStream( name + ".class" ); out.write( data ); out.close(); } catch( Exception e ) { e.printStackTrace(); } } }
动态代理内部实现
首先来看看类Proxy的代码实现 Proxy的主要静态变量
// 映射表:用于维护类装载器对象到其对应的代理类缓存private static Map loaderToCache = new WeakHashMap(); // 标记:用于标记一个动态代理类正在被创建中private static Object pendingGenerationMarker = new Object(); // 同步表:记录已经被创建的动态代理类类型,主要被方法 isProxyClass 进行相关的判断private static Map proxyClasses = Collections.synchronizedMap(new WeakHashMap()); // 关联的调用处理器引用protected InvocationHandler h;
Proxy的构造方法
// 由于 Proxy 内部从不直接调用构造函数,所以 private 类型意味着禁止任何调用private Proxy() {} // 由于 Proxy 内部从不直接调用构造函数,所以 protected 意味着只有子类可以调用protected Proxy(InvocationHandler h) {this.h = h;}
Proxy静态方法newProxyInstance
public static Object newProxyInstance(ClassLoader loader, Class<?>[]interfaces,InvocationHandler h) throws IllegalArgumentException { // 检查 h 不为空,否则抛异常 if (h == null) { throw new NullPointerException(); } // 获得与指定类装载器和一组接口相关的代理类类型对象 Class cl = getProxyClass(loader, interfaces); // 通过反射获取构造函数对象并生成代理类实例 try { Constructor cons = cl.getConstructor(constructorParams); return (Object) cons.newInstance(new Object[] { h }); } catch (NoSuchMethodException e) { throw new InternalError(e.toString()); } catch (IllegalAccessException e) { throw new InternalError(e.toString()); } catch (InstantiationException e) { throw new InternalError(e.toString()); } catch (InvocationTargetException e) { throw new InternalError(e.toString()); } }
类Proxy的getProxyClass方法调用ProxyGenerator的 generateProxyClass方法产生ProxySubject.class的二进制数据:
public static byte[] generateProxyClass(final String name, Class[] interfaces)
我们可以import sun.misc.ProxyGenerator,调用 generateProxyClass方法产生binary data,然后写入文件,最后通过反编译工具来查看内部实现原理。 反编译后的ProxySubject.java Proxy静态方法newProxyInstance
import java.lang.reflect.*; public final class ProxySubject extends Proxy implements Subject { private static Method m1; private static Method m0; private static Method m3; private static Method m2; public ProxySubject(InvocationHandler invocationhandler) { super(invocationhandler); } public final boolean equals(Object obj) { try { return ((Boolean)super.h.invoke(this, m1, new Object[] { obj })).booleanValue(); } catch(Error _ex) { } catch(Throwable throwable) { throw new UndeclaredThrowableException(throwable); } } public final int hashCode() { try { return ((Integer)super.h.invoke(this, m0, null)).intValue(); } catch(Error _ex) { } catch(Throwable throwable) { throw new UndeclaredThrowableException(throwable); } } public final void doSomething() { try { super.h.invoke(this, m3, null); return; } catch(Error _ex) { } catch(Throwable throwable) { throw new UndeclaredThrowableException(throwable); } } public final String toString() { try { return (String)super.h.invoke(this, m2, null); } catch(Error _ex) { } catch(Throwable throwable) { throw new UndeclaredThrowableException(throwable); } } static { try { m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") }); m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]); m3 = Class.forName("Subject").getMethod("doSomething", new Class[0]); m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]); } catch(NoSuchMethodException nosuchmethodexception) { throw new NoSuchMethodError(nosuchmethodexception.getMessage()); } catch(ClassNotFoundException classnotfoundexception) { throw new NoClassDefFoundError(classnotfoundexception.getMessage()); } } }
ProxyGenerator内部是如何生成class二进制数据,可以参考源代码。
private byte[] generateClassFile() { /* * Record that proxy methods are needed for the hashCode, equals, * and toString methods of java.lang.Object. This is done before * the methods from the proxy interfaces so that the methods from * java.lang.Object take precedence over duplicate methods in the * proxy interfaces. */ addProxyMethod(hashCodeMethod, Object.class); addProxyMethod(equalsMethod, Object.class); addProxyMethod(toStringMethod, Object.class); /* * Now record all of the methods from the proxy interfaces, giving * earlier interfaces precedence over later ones with duplicate * methods. */ for (int i = 0; i < interfaces.length; i++) { Method[] methods = interfaces[i].getMethods(); for (int j = 0; j < methods.length; j++) { addProxyMethod(methods[j], interfaces[i]); } } /* * For each set of proxy methods with the same signature, * verify that the methods' return types are compatible. */ for (List<ProxyMethod> sigmethods : proxyMethods.values()) { checkReturnTypes(sigmethods); } /* ============================================================ * Step 2: Assemble FieldInfo and MethodInfo structs for all of * fields and methods in the class we are generating. */ try { methods.add(generateConstructor()); for (List<ProxyMethod> sigmethods : proxyMethods.values()) { for (ProxyMethod pm : sigmethods) { // add static field for method's Method object fields.add(new FieldInfo(pm.methodFieldName, "Ljava/lang/reflect/Method;", ACC_PRIVATE | ACC_STATIC)); // generate code for proxy method and add it methods.add(pm.generateMethod()); } } methods.add(generateStaticInitializer()); } catch (IOException e) { throw new InternalError("unexpected I/O Exception"); } /* ============================================================ * Step 3: Write the final class file. */ /* * Make sure that constant pool indexes are reserved for the * following items before starting to write the final class file. */ cp.getClass(dotToSlash(className)); cp.getClass(superclassName); for (int i = 0; i < interfaces.length; i++) { cp.getClass(dotToSlash(interfaces[i].getName())); } /* * Disallow new constant pool additions beyond this point, since * we are about to write the final constant pool table. */ cp.setReadOnly(); ByteArrayOutputStream bout = new ByteArrayOutputStream(); DataOutputStream dout = new DataOutputStream(bout); try { /* * Write all the items of the "ClassFile" structure. * See JVMS section 4.1. */ // u4 magic; dout.writeInt(0xCAFEBABE); // u2 minor_version; dout.writeShort(CLASSFILE_MINOR_VERSION); // u2 major_version; dout.writeShort(CLASSFILE_MAJOR_VERSION); cp.write(dout); // (write constant pool) // u2 access_flags; dout.writeShort(ACC_PUBLIC | ACC_FINAL | ACC_SUPER); // u2 this_class; dout.writeShort(cp.getClass(dotToSlash(className))); // u2 super_class; dout.writeShort(cp.getClass(superclassName)); // u2 interfaces_count; dout.writeShort(interfaces.length); // u2 interfaces[interfaces_count]; for (int i = 0; i < interfaces.length; i++) { dout.writeShort(cp.getClass( dotToSlash(interfaces[i].getName()))); } // u2 fields_count; dout.writeShort(fields.size()); // field_info fields[fields_count]; for (FieldInfo f : fields) { f.write(dout); } // u2 methods_count; dout.writeShort(methods.size()); // method_info methods[methods_count]; for (MethodInfo m : methods) { m.write(dout); } // u2 attributes_count; dout.writeShort(0); // (no ClassFile attributes for proxy classes) } catch (IOException e) { throw new InternalError("unexpected I/O Exception"); } return bout.toByteArray();
总结
一个典型的动态代理创建对象过程可分为以下四个步骤:
1、通过实现InvocationHandler接口创建自己的调用处理器 IvocationHandler handler = new InvocationHandlerImpl(...);
2、通过为Proxy类指定ClassLoader对象和一组interface创建动态代理类
Class clazz = Proxy.getProxyClass(classLoader,new Class[]{...});
3、通过反射机制获取动态代理类的构造函数,其参数类型是调用处理器接口类型
Constructor constructor = clazz.getConstructor(new Class[]{InvocationHandler.class});
4、通过构造函数创建代理类实例,此时需将调用处理器对象作为参数被传入
Interface Proxy = (Interface)constructor.newInstance(new Object[] (handler));
为了简化对象创建过程,Proxy类中的newInstance方法封装了2~4,只需两步即可完成代理对象的创建。
生成的ProxySubject继承Proxy类实现Subject接口,实现的Subject的方法实际调用处理器的invoke方法,而invoke方法利用反射调用的是被代理对象的的方法(Object result=method.invoke(proxied,args))
美中不足
诚然,Proxy已经设计得非常优美,但是还是有一点点小小的遗憾之处,那就是它始终无法摆脱仅支持interface代理的桎梏,因为它的设计注定了这个遗憾。回想一下那些动态生成的代理类的继承关系图,它们已经注定有一个共同的父类叫Proxy。Java的继承机制注定了这些动态代理类们无法实现对class的动态代理,原因是多继承在Java中本质上就行不通。有很多条理由,人们可以否定对 class代理的必要性,但是同样有一些理由,相信支持class动态代理会更美好。接口和类的划分,本就不是很明显,只是到了Java中才变得如此的细化。如果只从方法的声明及是否被定义来考量,有一种两者的混合体,它的名字叫抽象类。实现对抽象类的动态代理,相信也有其内在的价值。此外,还有一些历史遗留的类,它们将因为没有实现任何接口而从此与动态代理永世无缘。如此种种,不得不说是一个小小的遗憾。但是,不完美并不等于不伟大,伟大是一种本质,Java动态代理就是佐例。
Java动态代理一——动态类Proxy的使用
1.什么是动态代理?
答:动态代理可以提供对另一个对象的访问,同时隐藏实际对象的具体事实。代理一般会实现它所表示的实际对象的接口。代理可以访问实际对象,但是延迟实现实际对象的部分功能,实际对象实现系统的实际功能,代理对象对客户隐藏了实际对象。客户不知道它是与代理打交道还是与实际对象打交道。
2.为什么使用动态代理?
答:因为动态代理可以对请求进行任何处理
3.使用它有哪些好处?
答:因为动态代理可以对请求进行任何处理
4.哪些地方需要动态代理?
答:不允许直接访问某些类;对访问要做特殊处理等
目前Java开发包中包含了对动态代理的支持,但是其实现只支持对接口的的实现。 其实现主要通过java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口。
Proxy类主要用来获取动态代理对象,InvocationHandler接口用来约束调用者实现
以下为模拟案例,通过动态代理实现在方法调用前后向控制台输出两句字符串
目录结构
<br/>
定义一个HelloWorld接口
1 package com.ljq.test; 2 3 /** 4 * 定义一个HelloWorld接口 5 * 6 * @author jiqinlin 7 * 8 */ 9 public interface HelloWorld {10 public void sayHelloWorld();11 }
<br/>
类HelloWorldImpl是HelloWorld接口的实现
1 package com.ljq.test;
2
3 /**
4 * 类HelloWorldImpl是HelloWorld接口的实现
5 *
6 * @author jiqinlin
7 *
8 */
9 public class HelloWorldImpl implements HelloWorld{
10
11 public void sayHelloWorld() {
12 System.out.println("HelloWorld!");
13 }
14
15 }
HelloWorldHandler是 InvocationHandler接口实现
1 package com.ljq.test; 2 3 import java.lang.reflect.InvocationHandler; 4 import java.lang.reflect.Method; 5 6 /** 7 * 实现在方法调用前后向控制台输出两句字符串 8 * 9 * @author jiqinlin10 *11 */12 public class HelloWorldHandler implements InvocationHandler{13 //要代理的原始对象14 private Object obj;15 16 public HelloWorldHandler(Object obj) {17 super();18 this.obj = obj;19 }20 21 /**22 * 在代理实例上处理方法调用并返回结果23 * 24 * @param proxy 代理类25 * @param method 被代理的方法26 * @param args 该方法的参数数组27 */28 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {29 Object result = null;30 //调用之前31 doBefore();32 //调用原始对象的方法33 result=method.invoke(obj, args);34 //调用之后35 doAfter();36 return result;37 }38 39 private void doBefore(){40 System.out.println("before method invoke");41 }42 43 private void doAfter(){44 System.out.println("after method invoke");45 }46 47 }
测试类
package com.ljq.test;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class HelloWorldTest {
public static void main(String[] args) {
HelloWorld helloWorld=new HelloWorldImpl();
InvocationHandler handler=new HelloWorldHandler(helloWorld);
//创建动态代理对象
HelloWorld proxy=(HelloWorld)Proxy.newProxyInstance(
helloWorld.getClass().getClassLoader(),
helloWorld.getClass().getInterfaces(),
handler);
proxy.sayHelloWorld();
}
}
字符 | |
x | 字符 x |
\\ | 反斜线字符 |
\0n | 带有八进制值 0 的字符 n (0 <= n <= 7) |
\0nn | 带有八进制值 0 的字符 nn (0 <= n <= 7) |
\0mnn | 带有八进制值 0 的字符 mnn(0 <= m <= 3、0 <= n <= 7) |
\xhh | 带有十六进制值 0x 的字符 hh |
\uhhhh | 带有十六进制值 0x 的字符 hhhh |
\t | 制表符 ('\u0009') |
\n | 新行(换行)符 ('\u000A') |
\r | 回车符 ('\u000D') |
\f | 换页符 ('\u000C') |
\a | 报警 (bell) 符 ('\u0007') |
\e | 转义符 ('\u001B') |
\cx | 对应于 x 的控制符 |
|
|
字符类 | |
[abc] | a、b 或 c(简单类) |
[^abc] | 任何字符,除了 a、b 或 c(否定) |
[a-zA-Z] | a 到 z 或 A 到 Z,两头的字母包括在内(范围) |
[a-d[m-p]] | a 到 d 或 m 到 p:[a-dm-p](并集) |
[a-z&&[def]] | d、e 或 f(交集) |
[a-z&&[^bc]] | a 到 z,除了 b 和 c:[ad-z](减去) |
[a-z&&[^m-p]] | a 到 z,而非 m 到 p:[a-lq-z](减去) |
|
|
预定义字符类 | |
. | 任何字符(与行结束符可能匹配也可能不匹配) |
\d | 数字:[0-9] |
\D | 非数字: [^0-9] |
\s | 空白字符:[ \t\n\x0B\f\r] |
\S | 非空白字符:[^\s] |
\w | 单词字符:[a-zA-Z_0-9] |
\W | 非单词字符:[^\w] |
边界匹配器 | |
^ | 行的开头 |
$ | 行的结尾 |
\b | 单词边界 |
\B | 非单词边界 |
\A | 输入的开头 |
\G | 上一个匹配的结尾 |
\Z | 输入的结尾,仅用于最后的结束符(如果有的话) |
\z | 输入的结尾 |
|
|
Greedy 数量词 | |
X? | X,一次或一次也没有 |
X* | X,零次或多次 |
X+ | X,一次或多次 |
X{n} | X,恰好 n 次 |
X{n,} | X,至少 n 次 |
X{n,m} | X,至少 n 次,但是不超过 m 次 |
|
|
Reluctant 数量词 | |
X?? | X,一次或一次也没有 |
X*? | X,零次或多次 |
X+? | X,一次或多次 |
X{n}? | X,恰好 n 次 |
X{n,}? | X,至少 n 次 |
X{n,m}? | X,至少 n 次,但是不超过 m 次 |
|
|
Possessive 数量词 | |
X?+ | X,一次或一次也没有 |
X*+ | X,零次或多次 |
X++ | X,一次或多次 |
X{n}+ | X,恰好 n 次 |
X{n,}+ | X,至少 n 次 |
X{n,m}+ | X,至少 n 次,但是不超过 m 次 |
|
|
Logical 运算符 | |
XY | X 后跟 Y |
X|Y | X 或 Y |
(X) |
|
转Java中正则表达式的使用
发表于12个月前(2014-10-28 16:53) 阅读(421) | 评论(0) 20人收藏此文章, 我要收藏
赞0
在Java中,我们为了查找某个给定字符串中是否有需要查找的某个字符或者子字串、或者对字符串进行分割、或者对字符串一些字符进行替换/删除,一般会通过if-else、for 的配合使用来实现这些功能 。如下所示:
1.
public class Test{
2.
3.
public static void main(String args[]) {
4.
5.
String str="@Shang Hai Hong Qiao Fei Ji Chang";
6.
7.
boolean rs = false;
8.
9.
for(int i=0;i<str.length();i++){
10.
11.
char z=str.charAt(i);
12.
13.
if('a' == z || 'F' == z) {
14.
15.
rs = true;
16.
17.
break;
18.
19.
}else{
20.
21.
rs= false;
22.
23.
}
24.
25.
}
26.
27.
System.out.println(rs);
28.
29.
}
30.
31.
}
32.
public class Test{ public static void main(String args[]) { String str="@Shang Hai Hong Qiao Fei Ji Chang"; boolean rs = false; for(int i=0;i<str.length();i++){ char z=str.charAt(i); if('a' == z || 'F' == z) { rs = true; break; }else{ rs= false; } } System.out.println(rs); }}
这种方法使用简单直观,但是 难以解决复杂的工作,而且代码量也会增加很多,不利于维护。
这时,我们可以使用正则表达式来实现这些功能,而且代码简单易维护。下面就来介绍了Java中对字符串的正则表达式的几个常用的功能,具体情况如下所示(其中用到了java.util.regex包):
1.Java中在某个字符串中查询某个字符或者某个子字串
1.
String s = "@Shang Hai Hong Qiao Fei Ji Chang";
2.
3.
String regEx = "a|F"; //表示a或F
4.
5.
Pattern pat = Pattern.compile(regEx);
6.
7.
Matcher mat = pat.matcher(s);
8.
9.
boolean rs = mat.find();
10.
String s = "@Shang Hai Hong Qiao Fei Ji Chang"; String regEx = "a|F"; //表示a或FPattern pat = Pattern.compile(regEx);Matcher mat = pat.matcher(s);boolean rs = mat.find();
如果s中有regEx,那么rs为true,否则为flase。
如果想在查找时忽略大小写,则可以写成Pattern pat=Pattern.compile(regEx,Pattern.CASE_INSENSITIVE);
2.在某个文件中获取一段字符串
1.
String regEx = ".+\(.+)$";
2.
3.
String s = "c:\test.txt";
4.
5.
Pattern pat = Pattern.compile(regEx);
6.
7.
Matcher mat = pat.matcher(s);
8.
9.
boolean rs = mat.find();
10.
11.
for(int i=1;i<=mat.groupCount();i++){
12.
13.
System.out.println(mat.group(i));
14.
15.
}
16.
String regEx = ".+\(.+)$";String s = "c:\test.txt";Pattern pat = Pattern.compile(regEx);Matcher mat = pat.matcher(s);boolean rs = mat.find();for(int i=1;i<=mat.groupCount();i++){ System.out.println(mat.group(i));}
以上的执行结果为test.txt,提取的字符串储存在mat.group(i)中,其中i最大值为mat.groupCount();
3.对字符串的分割
1.
String regEx=":";
2.
3.
Pattern pat = Pattern.compile(regEx);
4.
5.
String[] rs = pat.split("aa:bb:cc");
6.
String regEx=":";Pattern pat = Pattern.compile(regEx);String[] rs = pat.split("aa:bb:cc");
执行后,r就是{"aa","bb","cc"}
如果用正则表达式分割就如上所示,一般我们都会使用下面更简单的方法:
1.
String s = "aa:bb:cc";
2.
3.
String[] rs=s.split(":");
4.
String s = "aa:bb:cc";String[] rs=s.split(":");
4.字符串的替换/删除
1.
String regEx="@+"; //表示一个或多个@
2.
3.
Pattern pat=Pattern.compile(regEx);
4.
5.
Matcher mat=pat.matcher("@@aa @b cc@@");
6.
7.
String s=mat.replaceAll("#");
8.
String regEx="@+"; //表示一个或多个@Pattern pat=Pattern.compile(regEx);Matcher mat=pat.matcher("@@aa@b cc@@");String s=mat.replaceAll("#");
结果为"##aa#b cc##"
如果要把字符串中的@都给删除,只用要空字符串替换就可以了:
Java代码
String s=mat.replaceAll("");
String s=mat.replaceAll("");
结果为"aab cc"
注:对Pattern类的说明: 下面的语句将创建一个Pattern对象并赋值给句柄pat:Pattern pat = Pattern.compile(regEx); 1. public static Pattern compile(String regex) { 2. 3. return new Pattern(regex, 0); 4. 5. } 6. public static Pattern compile(String regex) { return new Pattern(regex, 0);} 当然,我们可以声明Pattern类的句柄,如Pattern pat = null; 2.pat.matcher(str)表示以用Pattern去生成一个字符串str的匹配器,它的返回值是一个Matcher类的引用。 |
附 : 常用的正则表达式: 匹配特定数字: |
String s=" aa dd dd cc ";
String regEx=" +";
Pattern pattern = Pattern.compile(regEx);
String[] rs = pattern.split(s);
System.out.println(rs.length);
System.out.println(rs[0]);
System.out.println(rs[rs.length-1]);
for(int i=0;i<rs.length;i++)
System.out.print(rs[i]+":");
结果:
5
cc
:aa:dd:dd:cc:
总结一下java获取路径几种途径
(2012-10-09 12:39:58)
标签: | 分类: Java |
在写java程序时不可避免要获取文件的路径...总结一下,遗漏的随时补上
1.可以在servlet的init方法里
String path = getServletContext().getRealPath("/");
这将获取web项目的全路径
例如 :E:\eclipseM9\workspace\tree\
tree是我web项目的根目录
2.你也可以随时在任意的class里调用
this.getClass().getClassLoader().getResource("/").getPath();
这将获取 到classes目录的全路径
例如 : E:\eclipseM9/workspace/tree/WEB-INF/classes/
这个方法也可以不在web环境里确定路径,比较好用
3.request.getContextPath();
获得web根的上下文环境
如 /tree
tree是我的web项目的root context
获取web项目的全路径
Java路径问题最终解决方案 —可定位所有资源的相对路径寻址
前言
Java的路径问题,非常难搞。最近的工作涉及到创建和读取文件的工作,这里我就给大家彻底得解决Java路径问题。
我编写了一个方法,比ClassLoader.getResource(String 相对路径)方法的能力更强。它可以接受“../”这样的参数,允许我们用相对路径来定位classpath外面的资源。这样,我们就可以使用相对于classpath的路径,定位所有位置的资源!
Java路径
Java中使用的路径,分为两种:绝对路径和相对路径。具体而言,又分为四种:
一、URI形式的绝对资源路径
如:file:/D:/java/eclipse32/workspace/jbpmtest3/bin/aaa.b
URL是URI的特例。URL的前缀/协议,必须是Java认识的。URL可以打开资源,而URI则不行。
URL和URI对象可以互相转换,使用各自的toURI(),toURL()方法即可!
二、本地系统的绝对路径
D:/java/eclipse32/workspace/jbpmtest3/bin/aaa.b
Java.io包中的类,需要使用这种形式的参数。
但是,它们一般也提供了URI类型的参数,而URI类型的参数,接受的是URI样式的String。
因此,通过URI转换,还是可以把URI样式的绝对路径用在java.io包中的类中。
三、相对于classpath的相对路径
如:相对于 file:/D:/java/eclipse32/workspace/jbpmtest3/bin/这个路径的相对路径。
其中,bin是本项目的classpath。所有的Java源文件编译后的.class文件复制到这个目录中。
四、相对于当前用户目录的相对路径
就是相对于System.getProperty("user.dir")返回的路径。
对于一般项目,这是项目的根路径。对于JavaEE服务器,这可能是服务器的某个路径。这个并没有统一的规范!
所以,绝对不要使用“相对于当前用户目录的相对路径”。
然而:
默认情况下,java.io 包中的类总是根据当前用户目录来分析相对路径名。此目录由系统属性 user.dir 指定,通常是 Java 虚拟机的调用目录。
这就是说,在使用java.io包中的类时,最好不要使用相对路径。否则,虽然在J2SE应用程序中可能还算正常,但是到了J2EE程序中,一定会出问题!而且这个路径,在不同的服务器中都是不同的!
相对路径最佳实践
推荐使用相对于当前classpath的相对路径
ClassLoader类的getResourc(String name),getResourceAsStreamgetResourceAsStreamgetResourceAsStreamgetResourceAsStream(String name)等方法,使用相对于当前项目的classpath的相对路径来查找资源。
读取属性文件常用到的ResourceBundle类的getBundle(String path)也是如此。
通过查看ClassLoader类及其相关类的源代码,我发现,它实际上还是使用了URI形式的绝对路径。
通过得到当前classpath的URI形式的绝对路径,构建了相对路径的URI形式的绝对路径。(这个实际上是猜想,因为JDK内部调用了SUN的源代码,而这些代码不属于JDK,不是开源的。)
相对路径本质上还是绝对路径
因此,归根结底,Java本质上只能使用绝对路径来寻找资源。所有的相对路径寻找资源的方法,都不过是一些便利方法。不过是API在底层帮助我们构建了绝对路径,从而找到资源的!
得到classpath和当前类的绝对路径的一些方法
下面是一些得到classpath和当前类的绝对路径的一些方法。你可能需要使用其中的一些方法来得到你需要的资源的绝对路径。
1,FileTest.class.getResource("")
得到的是当前类FileTest.class文件的URI目录。不包括自己!
如:file:/D:/java/eclipse32/workspace/jbpmtest3/bin/com/test/
2,FileTest.class.getResource("/")
得到的是当前的classpath的绝对URI路径。
如:file:/D:/java/eclipse32/workspace/jbpmtest3/bin/
3,Thread.currentThread().getContextClassLoader().getResource("")
得到的也是当前ClassPath的绝对URI路径。
如:file:/D:/java/eclipse32/workspace/jbpmtest3/bin/
4,FileTest.class.getClassLoader().getResource("")
得到的也是当前ClassPath的绝对URI路径。
如:file:/D:/java/eclipse32/workspace/jbpmtest3/bin/
5,ClassLoader.getSystemResource("")
得到的也是当前ClassPath的绝对URI路径。
如:file:/D:/java/eclipse32/workspace/jbpmtest3/bin/
我推荐使用Thread.currentThread().getContextClassLoader().getResource("")来得到当前的classpath的绝对路径的URI表示法
Web应用程序中资源的寻址
上文中说过,当前用户目录,即相对于System.getProperty("user.dir")返回的路径。
对于JavaEE服务器,这可能是服务器的某个路径,这个并没有统一的规范!
而不是我们发布的Web应用程序的根目录!
这样,在Web应用程序中,我们绝对不能使用相对于当前用户目录的相对路径。
在Web应用程序中,我们一般通过ServletContext.getRealPath("/")方法得到Web应用程序的根目录的绝对路径。这样,我们只需要提供相对于Web应用程序根目录的路径,就可以构建出定位资源的绝对路径。这是我们开发Web应用程序时一般所采取的策略。
通用的相对路径解决办法
Java中各种相对路径非常多,不容易使用,非常容易出错。因此,我编写了一个便利方法,帮助更容易的解决相对路径问题。
Web应用程序中使用JavaSE运行的资源寻址问题
在JavaSE程序中,我们一般使用classpath来作为存放资源的目的地。但是,在Web应用程序中,我们一般使用classpath外面的WEB-INF及其子目录作为资源文件的存放地。
在Web应用程序中,我们一般通过ServletContext.getRealPath("/")方法得到Web应用程序的根目录的绝对路径。这样,我们只需要提供相对于Web应用程序根目录的路径,就可以构建出定位资源的绝对路径。
Web应用程序,可以作为Web应用程序进行发布和运行。但是,我们也常常会以JavaSE的方式来运行Web应用程序的某个类的main方法。或者,使用JUnit测试。这都需要使用JavaSE的方式来运行。
这样,我们就无法使用ServletContext.getRealPath("/")方法得到Web应用程序的根目录的绝对路径。
而JDK提供的ClassLoader类,它的getResource(String name),getResourceAsStreamgetResourceAsStreamgetResourceAsStreamgetResourceAsStream(String name)等方法,使用相对于当前项目的classpath的相对路径来查找资源。
读取属性文件常用到的ResourceBundle类的getBundle(String path)也是如此。
它们都只能使用相对路径来读取classpath下的资源,无法定位到classpath外面的资源。
Classpath外配置文件读取问题
如,我们使用测试驱动开发的方法,开发Spring、Hibernate、iBatis等使用配置文件的Web应用程序,就会遇到问题。
尽管Spring自己提供了FileSystem(也就是相对于user,dir目录)来读取Web配置文件的方法,但是终究不是很方便。而且与Web程序中的代码使用方式不一致!至于Hibernate,iBatis就更麻烦了!只有把配置文件移到classpath下,否则根本不可能使用测试驱动开发!
这怎么办?
通用的相对路径解决办法
面对这个问题,我决定编写一个助手类ClassLoaderUtil,提供一个便利方法[public static URL getExtendResource(String relativePath)]。在Web应用程序等一切Java程序中,需要定位classpath外的资源时,都使用这个助手类的便利方法,而不使用Web应用程序特有的ServletContext.getRealPath("/")方法来定位资源。
利用classpath的绝对路径,定位所有资源
这个便利方法的实现原理,就是“利用classpath的绝对路径,定位所有资源”。
ClassLoader类的getResource("")方法能够得到当前classpath的绝对路径,这是所有Java程序都拥有的能力,具有最大的适应性!
而目前的JDK提供的ClassLoader类的getResource(String 相对路径)方法,只能接受一般的相对路径。这样,使用ClassLoader类的getResource(String 相对路径)方法就只能定位到classpath下的资源。
如果,它能够接受“../”这样的参数,允许我们用相对路径来定位classpath外面的资源,那么我们就可以定位位置的资源!
当然,我无法修改ClassLoader类的这个方法,于是,我编写了一个助手类ClassLoaderUtil类,提供了[public static URL getExtendResource(String relativePath)]这个方法。它能够接受带有“../”符号的相对路径,实现了自由寻找资源的功能。
通过相对classpath路径实现自由寻找资源的助手类的源代码
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL; import java.util.Properties;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
public class ClassLoaderUtil { private static Log log=LogFactory.getLog(ClassLoaderUtil.class);
public static Class loadClass(String className{
try {
return getClassLoader().loadClass(className);
} catch (ClassNotFoundException e) {
thrownew RuntimeException("class not found '"+className+"'", e);
}
}
public static ClassLoader getClassLoader() {
return ClassLoaderUtil.class.getClassLoader();
}
public static InputStream getStream(String relativePath) throws MalformedURLException, IOException {
if(!relativePath.contains("../")){
return getClassLoader().getResourceAsStream(relativePath);
}else{
return ClassLoaderUtil.getStreamByExtendResource(relativePath); } }
public static InputStream getStream(URL url) throws IOException{
if(url!=null){
return url.openStream();
}else{
returnnull;
}
}
public static InputStream getStreamByExtendResource(String relativePath) throws MalformedURLException, IOException{
return ClassLoaderUtil.getStream(ClassLoaderUtil.getExtendResource(relativePath)); }
public static Properties getProperties(String resource) {
Properties properties = new Properties();
try {
properties.load(getStream(resource));
} catch (IOException e) {
thrownew RuntimeException("couldn't load properties file '"+resource+"'", e); }
return properties; }
public static String getAbsolutePathOfClassLoaderClassPath(){
ClassLoaderUtil.log.info(ClassLoaderUtil.getClassLoader().getResource("").toString()); return ClassLoaderUtil.getClassLoader().getResource("").toString();
}
public static URL getExtendResource(String relativePath) throws MalformedURLException{ ClassLoaderUtil.log.info("传入的相对路径:"+relativePath) ; //ClassLoaderUtil.log.info(Integer.valueOf(relativePath.indexOf("../"))) ;
if(!relativePath.contains("../")){
return ClassLoaderUtil.getResource(relativePath);
}
String classPathAbsolutePath=ClassLoaderUtil.getAbsolutePathOfClassLoaderClassPath(); if(relativePath.substring(0, 1).equals("/")){ relativePath=relativePath.substring(1);
}
ClassLoaderUtil.log.info(Integer.valueOf(relativePath.lastIndexOf("../"))) ; String wildcardString=relativePath.substring(0,relativePath.lastIndexOf("../")+3); relativePath=relativePath.substring(relativePath.lastIndexOf("../")+3);
int containSum=ClassLoaderUtil.containSum(wildcardString, "../"); classPathAbsolutePath= ClassLoaderUtil.cutLastString(classPathAbsolutePath, "/", containSum);
String resourceAbsolutePath=classPathAbsolutePath+relativePath; ClassLoaderUtil.log.info("绝对路径:"+resourceAbsolutePath) ;
URL resourceAbsoluteURL=new URL(resourceAbsolutePath);
return resourceAbsoluteURL;
}
private staticint containSum(String source,String dest){
int containSum=0;
int destLength=dest.length();
while(source.contains(dest)){
containSum=containSum+1;
source=source.substring(destLength);
}
return containSum;
}
private static String cutLastString(String source,String dest,int num){
// String cutSource=null;
for(int i=0;i< span="" />
source=source.substring(0, source.lastIndexOf(dest, source.length()-2)+1); }
return source;
}
public static URL getResource(String resource){
ClassLoaderUtil.log.info("传入的相对于classpath的路径:"+resource) ;
return ClassLoaderUtil.getClassLoader().getResource(resource);
}
public staticvoid main(String[] args) throws MalformedURLException { //ClassLoaderUtil.getExtendResource("../spring/dao.xml"); //ClassLoaderUtil.getExtendResource("../../../src/log4j.properties"); ClassLoaderUtil.getExtendResource("log4j.properties"); System.out.println(ClassLoaderUtil.getClassLoader().getResource("log4j.properties").toString());
}
}
ThreadLocal
JDK 1.2的版本中就提供java.lang.ThreadLocal,ThreadLocal为解决多线程程序的并发问题提供了一种新的思路。使用这个工具类可以很简洁地编写出优美的多线程程序,ThreadLocal并不是一个Thread,而是Thread的局部变量。
中文名
线程局部变量
版 本
JDK 1.2
定 位
作 用
提供了一种新的思路
目录
1. 1 介绍
2. ▪ 线程程序介绍
3. ▪ 关于其变量
4. 2 线程
5. ▪ 线程举例
1. ▪ 使用方法
2. 3 实例
3. ▪ 举例
4. ▪ 分析
5. 4 场景
1. ▪ 场景说明
2. ▪ 多线程访问
3. ▪ 说明
4. ▪ 解决方法
介绍 编辑
线程程序介绍
早在JDK 1.2的版本中就提供java.lang.ThreadLocal,ThreadLocal为解决多线程程序的并发问题提供了一种新的思路。使用这个工具类可以很简洁地编写出优美的多线程程序。
关于其变量
ThreadLocal很容易让人望文生义,想当然地认为是一个“本地线程”。其实,ThreadLocal并不是一个Thread,而是Thread的局部变量,也许把它命名为ThreadLocalVariable更容易让人理解一些。
所以,在Java中编写线程局部变量的代码相对来说要笨拙一些,因此造成线程局部变量没有在Java开发者中得到很好的普及。
ThreadLocal的接口方法
ThreadLocal类接口很简单,只有4个方法,我们先来了解一下:
void set(Object value)
public void remove()
将当前线程局部变量的值删除,目的是为了减少内存的占用,该方法是JDK 1.5 新增的方法。需要指出的是,当线程结束后,对应该线程的局部变量将自动被垃圾回收,所以显式调用该方法清除线程的局部变量并不是必须的操作,但它可以加快内存回收的速度。
protected Object initialValue()
返回该线程局部变量的初始值,该方法是一个protected的方法,显然是为了让子类覆盖而设计的。这个方法是一个延迟调用方法,在线程第1次调用get()或set(Object)时才执行,并且仅执行1次。ThreadLocal中的缺省实现直接返回一个null。
值得一提的是,在JDK5.0中,ThreadLocal已经支持泛型,该类的类名已经变为ThreadLocal<T>。API方法也相应进行了调整,新版本的API方法分别是void set(T value)、T get()以及T initialValue()。
ThreadLocal是如何做到为每一个线程维护变量的副本的呢?其实实现的思路很简单:在 ThreadLocal类中定义了一个ThreadLocalMap,每一个Thread中都有一个该类型的变量——threadLocals——用于存 储每一个线程的变量副本,Map中元素的键为线程对象,而值对应线程的变量副本。
线程 编辑
To keep state with a thread (user-id, transaction-id, logging-id) To cache objects which you need frequentlyThreadLocal类
它主要由四个方法组成initialValue(),get(),set(T),remove(),其中值得注意的是initialValue(),该方法是一个protected的方法,显然是为了子类重写而特意实现的。该方法返回当前线程在该线程局部变量的 初始值,这个方法是一个延迟调用方法,在一个线程第1次调用get()时才执行,并且仅执行1次(即:最多在每次访问线程来获得每个线程局部变量时调用此 方法一次,即线程第一次使用get()方法访问变量的时候。如果线程先于get方法调用set(T)方法,则不会在线程中再调用initialValue 方法)。ThreadLocal中的缺省实现直接返回一个null:
线程举例
ThreadLocal的原理
在ThreadLocal类中有一个Map,用于存储每一个线程的变量的副本。比如下面的示例实现:
public class ThreadLocal
private Map values = Collections.synchronizedMap(new HashMap());
public Object get()
{
Thread curThread = Thread.currentThread();
Object o = values.get(curThread);
if (o == null && !values.containsKey(curThread))
{
o = initialValue();
values.put(curThread, o);
}
values.put(Thread.currentThread(), newValue);
return o ;
}
public Object initialValue()
{
return null;
}
}
使用方法
ThreadLocal 的使用
使用方法一:
Hibernate的文档时看到了关于使ThreadLocal管理多线程访问的部分。具体代码如下
1. public static final ThreadLocal session = new ThreadLocal();
2. public static Session currentSession() {
3. Session s = (Session)session.get();
4. //open a new session,if this session has none
5. if(s == null){
6. s = sessionFactory.openSession();
7. session.set(s);
8. }
return s;
9. }
我们逐行分析
1。 初始化一个ThreadLocal对象,ThreadLocal有三个成员方法 get()、set()、initialvalue()。
如果不初始化initialvalue,则initialvalue返回null。
3。session的get根据当前线程返回其对应的线程内部变量,也就是我们需要的 net.sf.hibernate.Session(相当于对应每个数据库连接).多线程情况下共享数据库链接是不安全的。ThreadLocal保证了 每个线程都有自己的s(数据库连接)。
5。如果是该线程初次访问,自然,s(数据库连接)会是null,接着创建一个Session,具体就是行6。
6。创建一个数据库连接实例 s
7。保存该数据库连接s到ThreadLocal中。
8。如果当前线程已经访问过数据库了,则从session中get()就可以获取该线程上次获取过的连接实例。
使用方法二
当要给线程初始化一个特殊值时,需要自己实现ThreadLocal的子类并重写该方法,通常使用一个内部匿名类对ThreadLocal进行子类化,EasyDBO中创建jdbc连接上下文就是这样做的:
public class JDBCContext{
private static Logger logger = Logger.getLogger(JDBCContext.class);
private DataSource ds;
protected Connection connection;
}
public static JDBCContext getJdbcContext(javax.sql.DataSource ds)
{
if(jdbcContext==null)jdbcContext=new JDBCContextThreadLocal(ds);
JDBCContext context = (JDBCContext) jdbcContext.get();
if (context == null) {
context = new JDBCContext(ds);
}
return context;
}
private static class JDBCContextThreadLocal extends ThreadLocal {
public javax.sql.DataSource ds;
public JDBCContextThreadLocal(javax.sql.DataSource ds)
{
this.ds=ds;
}
protected synchronized Object initialValue() {
return new JDBCContext(ds);
}
}
}
简单的实现版本
代码清单1 SimpleThreadLocal
public class SimpleThreadLocal {
private Map valueMap = Collections.synchronizedMap(new HashMap());
public void set(Object newValue) {
valueMap.put(Thread.currentThread(), newValue);①键为线程对象,值为本线程的变量副本
}
public Object get() {
valueMap.put(currentThread, o);
}
return o;
}
public void remove() {
valueMap.remove(Thread.currentThread());
}
public Object initialValue() {
return null;
}
}
虽然代码清单93这个ThreadLocal实现版本显得比较幼稚,但它和JDK所提供的ThreadLocal类在实现思路上是相近的。
实例 编辑
举例
下面,我们通过一个具体的实例了解一下ThreadLocal的具体使用方法。
代码清单2 SequenceNumber
package com.baobaotao.basic;
public class SequenceNumber {
// ①通过匿名内部类覆盖ThreadLocal的initialValue()方法,指定初始值
private static ThreadLocal seqNum = new ThreadLocal() {
public Integer initialValue() {
return 0;
}
};
// ②获取下一个序列值
public int getNextNum() {
seqNum.set((Integer) seqNum.get() + 1);
return (Integer) seqNum.get();
}
public static void main(String[] args) {
SequenceNumber sn = new SequenceNumber();
// ③ 3个线程共享sn,各自产生序列号
TestClient t1 = new TestClient(sn);
TestClient t2 = new TestClient(sn);
TestClient t3 = new TestClient(sn);
new Thread(t1).start();
new Thread(t2).start();
new Thread(t3).start();
}
}
class TestClient implements Runnable {
private SequenceNumber sn;
public TestClient(SequenceNumber sn) {
super();
this.sn = sn;
}
public void run() {
for (int i = 0; i < 3; i++) {
// ④每个线程打出3个序列值
System.out.println("thread[" + Thread.currentThread().getName()
+ "] sn[" + sn.getNextNum() + "]");
}
}
}
分析
通常我们通过匿名内部类的方式定义ThreadLocal的子类,提供初始的变量值,如例子中①处所示。 TestClient线程产生一组序列号,在③处,我们生成3个TestClient,它们共享同一个SequenceNumber实例。运行以上代码, 在控制台上输出以下的结果:
thread[Thread-2] sn[1]
thread[Thread-0] sn[1]
thread[Thread-1] sn[1]
thread[Thread-2] sn[2]
thread[Thread-0] sn[2]
thread[Thread-1] sn[2]
thread[Thread-2] sn[3]
thread[Thread-0] sn[3]
thread[Thread-1] sn[3]
考察输出的结果信息,我们发现每个线程所产生的序号虽然都共享同一个SequenceNumber实例,但它们并没有发生相互干扰的情况,而是各自产生独立的序列号,这是因为我们通过ThreadLocal为每一个线程提供了单独的副本。
场景 编辑
场景说明
在Java的多线程编程中,为保证多个线程对共享变量的安全访问,通常会使用synchronized来保证同一时刻只有一个线程对共享变量进行操作。这种情况下可以将类变量放到ThreadLocal类型的对象中,使变量在每个线程中都有独立拷贝,不会出现一个线程读取变量时而被另一个线程修改的现象。
下面举例说明:
public class QuerySvc {
private static ThreadLocal sqlHolder = new ThreadLocal();
public QuerySvc() {
}
public void execute() {
System.out.println("Thread " + Thread.currentThread().getId() +" Sql is " +sqlHolder.get());
System.out.println("Thread " + Thread.currentThread().getId() +" Thread Local variable Sql is " + sqlHolder.get());
}
public void setSql(String sql) {
sqlHolder.set(sql);
}
}
多线程访问
为了说明多线程访问对于类变量和ThreadLocal变量的影响,QuerySvc中分别设置了类变量sql和ThreadLocal变量,这种场景类似web应用中多个请求线程携带不同查询条件对一个servlet实例的访问,然后servlet调用业务对象,并传入不同查询条件,最后要保证每个请求得到的结果是对应的查询条件的结果。
使用QuerySvc的工作线程如下:
public class Work extends Thread {
private QuerySvc querySvc;
private String sql;
public Work(QuerySvc querySvc,String sql) {
this.querySvc = querySvc;
this.sql = sql;
}
public void run() {
querySvc.setSql(sql);
querySvc.execute();
}
}
运行线程代码如下:
QuerySvc qs = new QuerySvc();
for (int k=0; k<10; k++)
String sql = "Select * from table where id =" + k;
new Work(qs,sql).start();
}
先创建一个QuerySvc实例对象,而ThreadLocal中的值总是和set中设置的值一样,这样通过使用ThreadLocal获得了线程安全性。
如果一个对象要被多个线程访问,而该对象存在类变量被不同类方法读写,为获得线程安全,可以用ThreadLocal来替代类变量。
Thread同步机制的比较
说明
ThreadLocal和线程同步机制相比有什么优势呢?ThreadLocal和线程同步机制都是为了解决多线程中相同变量的访问冲突问题。
在同步机制中,通过对象的锁机制保证同一时间只有一个线程访问变量。这时该变量是多个线程共享的,使用同步机制要求程序慎密地分析什么时候对变量进行读写,什么时候需要锁定某个对象,什么时候释放对象锁等繁杂的问题,程序设计和编写难度相对较大。
而ThreadLocal则从另一个角度来解决多线程的并发访问。在编写多线程代码时,可以把不安全的变量封装进ThreadLocal。
由于ThreadLocal中可以持有任何类型的对象,低版本JDK所提供的get()返回的是Object对象,需要强制类型转换。但JDK 5.0通过泛型很好的解决了这个问题,在一定程度地简化ThreadLocal的使用,代码清单 9 2就使用了JDK 5.0新的ThreadLocal<T>版本。
概括起来说,对于多线程资源共享的问题,同步机制采用了“以时间换空间”的方式,而ThreadLocal采用了“以空间换时间”的方式。前者仅提供一份变量,让不同的线程排队访问,而后者为每一个线程都提供了一份变量,因此可以同时访问而互不影响。
Spring使用ThreadLocal解决线程安全问题
我们知道在一般情况下,TransactionSynchronizationManager、LocaleContextHolder等)中非线程安全状态采用ThreadLocal进行处理,让它们也成为线程安全的状态,因为有状态的Bean就可以在多线程中共享了。
一般的Web应用划分为展现层、服务层和持久层三个层次,在不同的层中编写对应的逻辑,下层通过接口向上层开放功能调用。在一般情况下,从接收请求到返回响应所经过的所有程序调用都同属于一个线程,如图92所示:图1同一线程贯通三层
这样你就可以根据需要,将一些非线程安全的变量以ThreadLocal存放,在同一次请求响应的调用线程中,所有关联的对象引用到的都是同一个变量。
下面的实例能够体现Spring对有状态Bean的改造思路:
代码清单3 TopicDao:非线程安全
public class TopicDao {
private Connection conn;①一个非线程安全的变量
public void addTopic(){
Statement stat = conn.createStatement();②引用非线程安全变量
…
}
}
由于①处的conn是成员变量,因为addTopic()方法是非线程安全的,必须在使用时创建一个新TopicDao实例(非singleton)。下面使用ThreadLocal对conn这个非线程安全的“状态”进行改造:
代码清单4 TopicDao:线程安全
import java.sql.Connection;
import java.sql.Statement;
public class TopicDao {
①使用ThreadLocal保存Connection变量
private static ThreadLocal<Connection> connThreadLocal = new ThreadLocal<Connection>();
public static Connection getConnection(){
②如果connThreadLocal没有本线程对应的Connection创建一个新的Connection,
并将其保存到线程本地变量中。
}
public void addTopic() {
④从ThreadLocal中获取线程对应的Connection
Statement stat = getConnection().createStatement();
}
}
不同的线程在使用TopicDao时,这样,就保证了不同的线程使用线程相关的Connection,而不会使用其它线程的Connection。因此,这个TopicDao就可以做到singleton共享了。
当然,这个例子本身很粗糙,将Connection的ThreadLocal直接放在DAO只能做到本DAO的多个方法共享Connection时不发生线程安全问题,但无法和其它DAO共用同一个Connection,要做到同一事务多DAO共享同一Connection,必须在一个共同的外部类使用ThreadLocal保存Connection。
小结
解决方法
ThreadLocal是解决线程安全问题一个很好的思路,它通过为每个线程提供一个独立的变量副本解决了变量并发 访问的冲突问题。在很多情况下,ThreadLocal比直接使用synchronized同步机制解决线程安全问题更简单,更方便,且结果程序拥有更高 的并发性。
java中注解的使用与实例(一)
注解目前非常的流行,很多主流框架都支持注解,而且自己编写代码的时候也会尽量的去用注解,一时方便,而是代码更加简洁。
注解的语法比较简单,除了@符号的使用之外,它基本与Java固有语法一致。Java SE5内置了三种标准注解:
@Override,表示当前的方法定义将覆盖超类中的方法。
@Deprecated,使用了注解为它的元素编译器将发出警告,因为注解@Deprecated是不赞成使用的代码,被弃用的代码。
@SuppressWarnings,关闭不当编译器警告信息。
上面这三个注解多少我们都会在写代码的时候遇到。Java还提供了4中注解,专门负责新注解的创建。
@Target | 表示该注解可以用于什么地方,可能的ElementType参数有: CONSTRUCTOR:构造器的声明 FIELD:域声明(包括enum实例) LOCAL_VARIABLE:局部变量声明 METHOD:方法声明 PACKAGE:包声明 PARAMETER:参数声明 TYPE:类、接口(包括注解类型)或enum声明 |
@Retention | 表示需要在什么级别保存该注解信息。可选的RetentionPolicy参数包括: SOURCE:注解将被编译器丢弃 CLASS:注解在class文件中可用,但会被VM丢弃 RUNTIME:VM将在运行期间保留注解,因此可以通过反射机制读取注解的信息。 |
@Document | 将注解包含在Javadoc中 |
@Inherited | 允许子类继承父类中的注解 |
定义一个注解的方式:
1 @Target(ElementType.METHOD)2 @Retention(RetentionPolicy.RUNTIME)3 public @interface Test {4 5 }
除了@符号,注解很像是一个接口。定义注解的时候需要用到元注解,上面用到了@Target和@RetentionPolicy,它们的含义在上面的表格中已近给出。
在注解中一般会有一些元素以表示某些值。注解的元素看起来就像接口的方法,唯一的区别在于可以为其制定默认值。没有元素的注解称为标记注解,上面的@Test就是一个标记注解。
注 解的可用的类型包括以下几种:所有基本类型、String、Class、enum、Annotation、以上类型的数组形式。元素不能有不确定的值,即 要么有默认值,要么在使用注解的时候提供元素的值。而且元素不能使用null作为默认值。注解在只有一个元素且该元素的名称是value的情况下,在使用 注解的时候可以省略“value=”,直接写需要的值即可。
下面看一个定义了元素的注解。
1 @Target(ElementType.METHOD)2 @Retention(RetentionPolicy.RUNTIME)3 public @interface UseCase {4 public String id();5 public String description() default "no description";6 }
定义了注解,必然要去使用注解。
1 public class PasswordUtils { 2 @UseCase(id = 47, description = "Passwords must contain at least one numeric") 3 public boolean validatePassword(String password) { 4 return (password.matches("\\w*\\d\\w*")); 5 } 6 7 @UseCase(id = 48) 8 public String encryptPassword(String password) { 9 return new StringBuilder(password).reverse().toString();10 }11 }
使用注解最主要的部分在于对注解的处理,那么就会涉及到注解处理器。
从原理上讲,注解处理器就是通过反射机制获取被检查方法上的注解信息,然后根据注解元素的值进行特定的处理。
public static void main(String[] args) { List<Integer> useCases = new ArrayList<Integer>(); Collections.addAll(useCases, 47, 48, 49, 50); trackUseCases(useCases, PasswordUtils.class); } public static void trackUseCases(List<Integer> useCases, Class<?> cl) { for (Method m : cl.getDeclaredMethods()) { UseCase uc = m.getAnnotation(UseCase.class); if (uc != null) { System.out.println("Found Use Case:" + uc.id() + " " + uc.description()); useCases.remove(new Integer(uc.id())); } } for (int i : useCases) { System.out.println("Warning: Missing use case-" + i); } }
Found Use Case:47 Passwords must contain at least one numeric
Found Use Case:48 no description
Warning: Missing use case-49
Warning: Missing use case-50
上面的三段代码结合起来是一个跟踪项目中用例的简单例子。
写到这里博主想到结合枚举、注解、反射、拦截器等内容,是否可以写一套用户权限验证呢?
将用户权限用枚举的方式给出,注解元素表明某个方法必须拥有某些权限才能调用,拦截器拦截请求方法,用户是否有权限对该方法进行调用,根据用户不同的权限进行不同的处理。欢迎讨论!
java中的BigInteger(很好很强大)(转)
分类: 日志 2012-02-24 14:42 8257人阅读 评论(4) 收藏 举报
JAVA之BigInteger
用Java来处理高精度问题,相信对很多ACMer来说都是一件很happy的事,简单易懂。用Java刷了一些题,感觉Java还不错,在处理高精度和进制转换中,调用库函数的来处理。下面是写的一些Java中一些基本的函数的及其…… 头文件:import java.io.*; import java.util.*; import java.math.*; 读入: Scanner cin = Scanner (System.in); while(cin.hasNext())//等价于!=EOF n=cin.nextInt();//读入一个int型的数 n=cin.nextBigInteger();//读入一个大整数 输出: System.out.print(n);//打印n System.out.println();//换行 System.out.printf("%d\n",n);//也可以类似c++里的输出方式 定义: int i,j,k,a[]; a = new int[100]; BigInteger n,m; BigDecimal n; String s; 数据类型: 数据类型 类型名 位长 取值范围 默认值 布尔型 boolean 1 true,false false 字节型 byte 8 -128-127 0 字符型 char 16 ‘\u000’-\uffff ‘\u0000’ 短整型 short 16 -32768-32767 0 整型 int 32 -2147483648,2147483647 0 长整型 long 64 -9.22E18,9.22E18 0 浮点型 float 32 1.4E-45-3.4028E+38 0.0 双精度型 double 64 4.9E-324,1.7977E+308 0.0 这里特别要提出出的两种类型: BigInteger 任意大的整数,原则上是,只要你的计算机的内存足够大,可以有无限位的 BigInteger 任意大的实数,可以处理小数精度问题。 BigInteger中一些常见的函数: A=BigInteger.ONE B=BigInteger.TEN C=BigInteger.ZERO 一些常见的数的赋初值。将int型的数赋值给BigInteger,BigInteger.valueOf(k); 基本的函数: valueOf:赋初值 add:+ a.add(b); subtract:- multiply:* divide:/ remainder:this % val divideAndRemainder:a[0]=this / val; a[1]=this % val pow:a.pow(b)=a^b gcd,abs:公约数,绝对值 negate:取负数 signum:符号函数 mod:a.mod(b)=a%b; shiftLeft:左移,this << n ,this*2^n; shiftRight:右移,this >> n,this/2^n; and:等同于c++的&&,且; or:||,或; xor:异或,BigInteger xor(BigInteger val),this^val not:!,非; bitLength:返回该数的最小二进制补码表示的位的个数,即 *不包括* 符号位 (ceil(log2(this <0 ? -this : this + 1)))。对正数来说,这等价于普通二进制表示的位的个数。 bitCount:返回该数的二进制补码表示中不包扩符号位在内的位的个数。该方法在 BigIntegers 之上实现位向量风格的集合时很有用。 isProbablePrime:如果该 BigInteger 可能是素数,则返回 true ;如果它很明确是一个合数,则返回 false 。 参数 certainty 是对调用者愿意忍受的不确定性的度量:如果该数是素数的概率超过了 1 - 1/2**certainty方法,则该方法返回 true 。执行时间正比于参数确定性的值。 compareTo:根据该数值是小于、等于、或大于 val 返回 -1、0 或 1; equals:判断两数是否相等,也可以用compareTo来代替; min,max:取两个数的较小、大者; intValue,longValue,floatValue,doublue:把该数转换为该类型的数的值。 今天参考课本写了一个关于二进制与十进制转换的程序,程序算法不难,但写完后测试发现不论是二转十还是十转二,对于大于21亿即超过整数范围的数不能很好的转换。都会变成0. 1,BigInteger属于java.math.BigInteger,因此在每次使用前都要import 这个类。偶开始就忘记import了,于是总提示找不到提示符。 2,其构造方法有很多,但现在偶用到的有: BigInteger(String val) 3,BigInteger类模拟了所有的int型数学操作,如add()==“+”,divide()==“-”等,但注意其内容进行数学运算时不能直接使用数学运算符进行运算,必须使用其内部方法。而且其操作数也必须为BigInteger型。 4,当要把计算结果输出时应该使用.toString方法将其转换为10进制的字符串,详细说明如下: 5,另外说明三个个用到的函数。 BigInteger remainder(BigInteger val) compareTo 将BigInteger的数转为2进制: public class TestChange { |
Java中String类的方法及说明
String : 字符串类型 一、构造函数 二、方法: 说明:①、所有方法均为public。 例如:static int parseInt(String s) 1. char charAt(int index) :取字符串中的某一个字符,其中的参数index指的是字符串中序数。字符串的序数从0开始到length()-1 。
Java中String类的常用方法: 实例: String s = "我是J2EE程序员" ; TestString2.java: public class TestString2 |
request.getURL()跟request.getURI()
提问者: xunbai001 发布时间:2014-07-30 浏览:81 回复:0 悬赏:0.0希赛币
request.getURL()和request.getURI()
如果我的请求是:http://localhost:8080/ServletTest/servlet/Hello
request.getRequestURI() 返回值类似:/ServletTest/servlet/Hello
request.getRequestURL() 返回值类似:http://localhost:8080/ServletTest/servlet/Hello
再如:
request.getContextPath() = /ServletTest
request.getLocalAddr() = 127.0.0.1
request.getPathInfo() = null
request.getPathTranslated() = null
request.getRemoteAddr() = 127.0.0.1
request.getRequestURI() = /ServletTest/servlet/Hello
request.getScheme() = http
request.getServerName() = 127.0.0.1
request.getServletPath() = /servlet/Hello
request.getClass() = class ornnector.RequestFacade
request.getHeaderNames() = org.apache.tomcat.util.http.NamesEnumerator@1fb050c
request.getLocale() = zh_CN
request.getLocales() = org.apache.catalina.util.Enumerator@1088a1b
request.getParameterMap() = {}
request.getRequestURL() =
request.getUserPrincipal() = null
request.getParameterNames() = java.util.Hashtable$EmptyEnumerator@1db6942
request.getRealPath("newsPub") =
D:\zfsca\.metadata\.plugins\com.genuitec.eclipse.easie.tomcat.myeclipse\tomcat\webapps\ServletTest\newsPub
request.getRealPath("/") =
D:\zfsca\.metadata\.plugins\com.genuitec.eclipse.easie.tomcat.myeclipse\tomcat\webapps\ServletTest\
Java中泛型
Java程序中的普通方法、构造方法和静态方法中都可以使用泛型。方法使用泛形前,必须对泛形进行声明,语法:<T> ,T可以是任意字母,但通常必须要大写。<T>通常需放在方法的返回值声明之前。例如:
public static <T> void doxx(T t);
练习:
编写一个泛形方法,实现指定位置数组元素的交换。
编写一个泛形方法,接收一个任意数组,并颠倒数组中的所有元素。
注意:
只有对象类型才能作为泛型方法的实际参数。
在泛型中可以同时有多个类型,例如:
public static <K,V> V getValue(K key) { return map.get(key);}
Java泛型类定义
如果一个类多处都要用到同一个泛型,这时可以把泛形定义在类上(即类级别的泛型),语法格式如下:
public class GenericDao<T> {
private T field1;
public void save(T obj){}
public T getId(int id){}
}
注意,静态方法不能使用类定义的泛形,而应单独定义泛形
Java中泛型通配符
定义一个方法,接收一个集合,并打印出集合中的所有元素,如下所示:
void print (Collection<String> c) {
for (String e : c) {
System.out.println(e);
}
}
问题:该方法只能打印保存了String对象的集合,不能打印其它集合。通配符用于解决此类问题,方法的定义可改写为如下形式:
void print (Collection<?> c) { //Collection<?>(发音为:"collection of unknown")
for (Object e : c) {
System.out.println(e);
}
}
此种形式下需要注意的是:由于print方法c参数的类型为Collection<?>,即表示一种不确定的类型,因此在方法体内不能调用与类型相关的方法,例如add()方法。
总结:使用?通配符主要用于引用对象,使用了?通配符,就只能调对象与类型无关的方法,不能调用对象与类型有关的方法。
extends - 用来指定泛型的上边界,使用在泛型的通配符中和泛型定义中,指定具体的泛型实现必须是指定的类或其子类.
坏处是,在传入对象时,只能传入null
好处是,获取到泛型的对象时,可以调用上边界的方法.
super - 用来指定泛型的下边界,使用在泛型的通配符中,指定具体的泛型实现必须是指定类或其超类.
好处是,可以传入对象时,可以传入下边界的子孙类对象
坏处是,获取到泛型对象时,只能调用Object身上的方法
Integer.parseInt(String value)注意点
Value = “01”;
使用时其会变为 int i=1;
你要明白,你传的不是数组,是数组首地址的拷贝。函数在java中叫方法。方法的传值都是值传递,就是将传进来的东西拷贝一份。你传过来一个数组,实际上内存只是创建了数组首地址的拷贝。这个拷贝指向该数组。你vv方法里,re=z,实际上也只是操作两个数组的首地址。你将z数组的首地址赋值给之前的拷贝了。所以只是拷贝指向了{1,2}。跟传进来的re无关。
你要改变传进来的re,只能通过拷贝改变拷贝指向的内容(改变拷贝指向的内容,就改变了re,因为re也是指向该内容的)。这里就是将re=z;换成re[0]=z[0];re[1]=z[1]。
作者:Patrick Lester, 译者:Panic 译者序:很久以前就知道了A*算法,但是从未认真读过相关的文章,也没有看过代码,只是脑子 里有个模糊的概念。这次决定从头开始,研究一下这个被人推崇备至的简单方法,作为学习人工智能的开始。 这篇文章非常知名,国内应该有不少人翻译过它,我没有查找,觉得翻译本身也是对自身英文水平的锻炼。经过努力,终于完成了文档,也明白的A*算法的原理。 毫无疑问,作者用形象的描述,简洁诙谐的语言由浅入深的讲述了这一神奇的算法,相信每个读过的人都会对此有所认识(如果没有,那就是偶的翻译太差了 --b)。 |
|
会者不难,A*(念作A星)算法对初学者来说的确有些难度。
这篇文章并不试图对这个话题作权威的陈述。取而代之的是,它只是描述算法的原理,使你可以在进一步的阅读中理解其他相关的资料。
最后,这篇文章没有程序细节。你尽可以用任意的计算机程序语言实现它。如你所愿,我在文章的末尾包含了一个指向例子程序的链接。 压缩包包括C++和Blitz Basic两个语言的版本,如果你只是想看看它的运行效果,里面还包含了可执行文件。
我们正在提高自己。让我们从头开始。。。
序:搜索区域
假设有人想从A点移动到一墙之隔的B点,如下图,绿色的是起点A,红色是终点B,蓝色方块是中间的墙。
你首先注意到,搜索区域被我们划分成了方形网格。像这样,简化搜索区域,是寻路的第一步。这一方法把搜索 区域简化成了一个二维数组。数组的每一个元素是网格的一个方块,方块被标记为可通过的和不可通过的。路径被描述为从A到B我们经过的方块的集合。一旦路径 被找到,我们的人就从一个方格的中心走向另一个,直到到达目的地。
这些中点被称为“节点”。当你阅读其他的寻路资料时,你将经常会看到人们讨论节点。为什么不把他们描述为 方格呢?因为有可能你的路径被分割成其他不是方格的结构。他们完全可以是矩形,六角形,或者其他任意形状。节点能够被放置在形状的任意位置-可以在中心, 或者沿着边界,或其他什么地方。我们使用这种系统,无论如何,因为它是最简单的。
开始搜索
正如我们处理上图网格的方法,一旦搜索区域被转化为容易处理的节点,下一步就是去引导一次找到最短路径的搜索。在A*寻路算法中,我们通过从点A开始,检查相邻方格的方式,向外扩展直到找到目标。
我们做如下操作开始搜索:
1,从点A开始,并且把它作为待处理点存入一个“开启列表”。开启列表就像一张购物清单。尽管现在列表里只有一个元素,但以后就会多起来。你的路径可能会通过它包含的方格,也可能不会。基本上,这是一个待检查方格的列表。
2,寻找起点周围所有可到达或者可通过的方格,跳过有墙,水,或其他无法通过地形的方格。可到达或者可通过的方格加入开启列表。为所有这些方格保存点 A作为“父方格”。当我们想描述路径的时候,父方格的资料是十分重要的。后面会解释它的具体用途。
3,从开启列表中删除点A,把它加入到一个“关闭列表”,列表中保存所有不需要再次检查的方格。
在这一点,你应该形成如图的结构。在图中,暗绿色方格是你起始方格的中心。它被用浅蓝色描边,以表示它被加入到关闭列表中了。所有的相邻格现在都在开启列表中,它们被用浅绿色描边。每个方格都有一个灰色指针反指他们的父方格,也就是开始的方格。
接着,我们选择开启列表中的临近方格,大致重复前面的过程,如下。但是,哪个方格是我们要选择的呢?是那个F值最低的。
路径评分
选择路径中经过哪个方格的关键是下面这个等式:
F = G + H
这里:
* G = 从起点A,沿着产生的路径,移动到网格上指定方格的移动耗费。
* H = 从网格上那个方格移动到终点B的预估移动耗费。这经常被称为启发式的,可能会让你有点迷惑。这样叫的原因是因为它只是个猜测。我们没办法事先知道路径的长 度,因为路上可能存在各种障碍(墙,水,等等)。虽然本文只提供了一种计算H的方法,但是你可以在网上找到很多其他的方法。
我们的路径是通过反复遍历开启列表并且选择具有最低F值的方格来生成的。文章将对这个过程做更详细的描述。首先,我们更深入的看看如何计算这个方程。
正如上面所说,G表示沿路径从起点到当前点的移动耗费。在这个例子里,我们令水平或者垂直移动的耗费为 10,对角线方向耗费为14。我们取这些值是因为沿对角线的距离是沿水平或垂直移动耗费的的根号2(别怕),或者约1.414倍。为了简化,我们用10和 14近似。比例基本正确,同时我们避免了求根运算和小数。这不是只因为我们怕麻烦或者不喜欢数学。使用这样的整数对计算机来说也更快捷。你不久就会发现, 如果你不使用这些简化方法,寻路会变得很慢。
既然我们在计算沿特定路径通往某个方格的G值,求值的方法就是取它父节点的G值,然后依照它相对父节点是 对角线方向或者直角方向(非对角线),分别增加14和10。(某个方格的G值=父节点的G值+10或者+14)例子中这个方法的需求会变得更多,因为我们 从起点方格以外获取了不止一个方格。
H值可以用不同的方法估算。我们这里使用的方法被称为曼哈顿方法, 它计算从当前格到目的格之间水平和垂直的方格的数量总和,忽略对角线方向。然后把结果乘以10。这被成为曼哈顿方法是因为它看起来像计算城市中从一个地方 到另外一个地方的街区数,在那里你不能沿对角线方向穿过街区。很重要的一点,我们忽略了一切障碍物。这是对剩余距离的一个估算,而非实际值,这也是这一方 法被称为启发式的原因。
F的值是G和H的和。第一步搜索的结果可以在下面的图表中看到。F,G和H的评分被写在每个方格里。正如在紧挨起始格右侧的方格所表示的,F被打印在左上角,G在左下角,H则在右下角。
现在我们来看看这些方格。写字母的方格里,G = 10。这是因为它只在水平方向偏离起始格一个格距。紧邻起始格的上方,下方和左边的方格的G值都等于10。对角线方向的方格的G值是14。
H值通过求解到红色目标格的曼哈顿距离(|x2-x1|+|y2-y1|)*10得到,其中只在水平和垂 直方向移动,并且忽略中间的墙。用这种方法,起点右侧紧邻的方格离红色方格有3格距离,H值就是30。这块方格上方的方格有4格距离(记住,只能在水平和 垂直方向移动),H值是40。你大致应该知道如何计算其他方格的H值了~。
每个格子的F值,还是简单的由G和H相加得到。
为了继续搜索,我们简单的从开启列表中选择F值最低的方格。然后,对选中的方格做如下处理:
4,把它从开启列表中删除,然后添加到关闭列表中。
5,检查所有相邻格子。跳过那些已经在关闭列表中的或者不可通过的(有墙,水的地形,或者其他无法通过的地形),把他们添加进开启列表,如果他们还不在里面的话。把选中的方格作为新的方格的父节点。
6,如果某个相邻格已经在开启列表里了,检查现在的这条路径是否更好。换句话说,检查如果我们用新的路径到达它的话,G值是否会更低一些。如果不是,那就什么都不做。
另一方面,如果新的G值更低,那就把相邻方格的父节点改为目前选中的方格(在上面的图表中,把箭头的方向改为指向这个方格)。最后,重新计算F和G的值。如果这看起来不够清晰,你可以看下面的图示。
好了,让我们看看它是怎么运作的。我们最初的9格方格中,在起点被切换到关闭列表中后,还剩8格留在开启列表中。这里面,F值最低的那个是起始格右侧紧邻的格子,它的F值是40。因此我们选择这一格作为下一个要处理的方格。在紧随的图中,它被用蓝色突出显示。
首先,我们把它从开启列表中取出,放入关闭列表(这就是他被蓝色突出显示的原因)。然后我们检查相邻的格子。哦,右侧的三个格子是墙,所以我们略过。左侧的一个格子是起始格。它在关闭列表里,所以我们也跳过它。
其他4格已经在开启列表里了,于是我们检查G值来判定,如果通过这一格到达那里,路径是否更好。我们来看 选中格子下面的方格。它的G值是14。如果我们从当前格移动到那里,G值就会等于20(到达当前格的G值是10,移动到上面的格子将使得G值增加10)。 因为G值20大于14,所以这不是更好的路径。如果你看图,就能理解。与其通过先水平移动一格,再垂直移动一格,还不如直接沿对角线方向移动一格来得简 单。
当我们对已经存在于开启列表中的4个临近格重复这一过程的时候,我们发现没有一条路径可以通过使用当前格子得到改善,所以我们不做任何改变。既然我们已经检查过了所有邻近格,那么就可以移动到下一格了。
于是我们检索开启列表,现在里面只有7格了,我们仍然选择其中F值最低的。有趣的是,这次,有两个格子的 数值都是54。我们如何选择?这并不麻烦。从速度上考虑,选择最后添加进列表的格子会更快捷。这种导致了寻路过程中,在靠近目标的时候,优先使用新找到的 格子的偏好。但这无关紧要。(对相同数值的不同对待,导致不同版本的A*算法找到等长的不同路径。)
那我们就选择起始格右下方的格子,如图。
这次,当我们检查相邻格的时候,发现右侧是墙,于是略过。上面一格也被略过。我们也略过了墙下面的格子。 为什么呢?因为你不能在不穿越墙角的情况下直接到达那个格子。你的确需要先往下走然后到达那一格,按部就班的走过那个拐角。(注解:穿越拐角的规则是可选 的。它取决于你的节点是如何放置的。)
这样一来,就剩下了其他5格。当前格下面的另外两个格子目前不在开启列表中,于是我们添加他们,并且把当前格 指定为他们的父节点。其余3格,两个已经在关闭列表中(起始格,和当前格上方的格子,在表格中蓝色高亮显示),于是我们略过它们。最后一格,在当前格的左 侧,将被检查通过这条路径,G值是否更低。不必担心,我们已经准备好检查开启列表中的下一格了。
我们重复这个过程,直到目标格被添加进关闭列表(注解),就如在下面的图中所看到的。
注意,起始格下方格子的父节点已经和前面不同的。之前它的G值是28,并且指向右上方的格子。现在它的G 值是20,指向它上方的格子。这在寻路过程中的某处发生,当应用新路径时,G值经过检查变得低了-于是父节点被重新指定,G和F值被重新计算。尽管这一变 化在这个例子中并不重要,在很多场合,这种变化会导致寻路结果的巨大变化。
那么,我们怎么确定这条路径呢?很简单,从红色的目标格开始,按箭头的方向朝父节点移动。这最终会引导你 回到起始格,这就是你的路径!看起来应该像图中那样。从起始格A移动到目标格B只是简单的从每个格子(节点)的中点沿路径移动到下一个,直到你到达目标 点。就这么简单。
这里先给出下一个JAVA版的代码:
A星算法步骤:
1.起点先添加到开启列表中
2.开启列表中有节点的话,取出第一个节点,即最小F值的节点,
判断此节点是否是目标点,是则找到了,跳出;
根据此节点取得八个方向的节点,求出G,H,F值;
判断每个节点在地图中是否能通过,不能通过则加入关闭列表中,跳出;
判断每个节点是否在关闭列表中,在则跳出;
判断每个节点是否在开启列表中,在则更新G值,F值,还更新其父节点;不在则将其添加到开启列表中,计算G值,H值,F值,添加其节点。
3.把此节点从开启列表中删除,再添加到关闭列表中;
4.把开启列表中按照F值最小的节点进行排序,最小的F值在第一个;
5.重复2,3,4步骤,直到目标点在开启列表中,即找到了;目标点不在开启列表中,开启列表为空,即没找到
import java.util.*;
public class Test {
public static void main(String[] args){
int[][] map=new int[][]{// 地图数组
{1,1,1,1,1,1,1,1,1,1},
{1,1,1,1,0,1,1,1,1,1},
{1,1,1,1,0,1,1,1,1,1},
{1,1,1,1,0,1,1,1,1,1},
{1,1,1,1,0,1,1,1,1,1},
{1,1,1,1,0,1,1,1,1,1}
};
AStar aStar=new AStar(map, 6, 10);
int flag=aStar.search(3, 2, 3, 8);
if(flag==-1){
System.out.println("传输数据有误!");
}else if(flag==0){
System.out.println("没找到!");
}else{
for(int x=0;x< 6;x++){
for(int y=0;y< 10;y++){
if(map[x][y]==1){
System.out.print(" ");
}else if(map[x][y]==0){
System.out.print("〓");
}else if(map[x][y]==2){//输出搜索路径
System.out.print("※");
}
}
System.out.println();
}
}
}
}
运行结果:
import java.util.*;
//A*算法
public class AStar {
private int[][] map;//地图(1可通过 0不可通过)
private List< Node> openList;//开启列表
private List< Node> closeList;//关闭列表
private final int COST_STRAIGHT = 10;//垂直方向或水平方向移动的路径评分
private final int COST_DIAGONAL = 14;//斜方向移动的路径评分
private int row;//行
private int column;//列
public AStar(int[][] map,int row,int column){
this.map=map;
this.row=row;
this.column=column;
openList=new ArrayList< Node>();
closeList=new ArrayList< Node>();
}
//查找坐标(-1:错误,0:没找到,1:找到了)
public int search(int x1,int y1,int x2,int y2){
if(x1< 0||x1>=row||x2< 0||x2>=row||y1< 0||y1>=column||y2< 0||y2>=column){
return -1;
}
if(map[x1][y1]==0||map[x2][y2]==0){
return -1;
}
Node sNode=new Node(x1,y1,null);
Node eNode=new Node(x2,y2,null);
openList.add(sNode);
List< Node> resultList=search(sNode, eNode);
if(resultList.size()==0){
return 0;
}
for(Node node:resultList){
map[node.getX()][node.getY()]=2;
}
return 1;
}
//查找核心算法
private List< Node> search(Node sNode,Node eNode){
List< Node> resultList=new ArrayList< Node>();
boolean isFind=false;
Node node=null;
while(openList.size()>0){
// System.out.println(openList);
//取出开启列表中最低F值,即第一个存储的值的F为最低的
node=openList.get(0);
//判断是否找到目标点
if(node.getX()==eNode.getX()&&node.getY()==eNode.getY()){
isFind=true;
break;
}
//上
if((node.getY()-1)>=0){
checkPath(node.getX(),node.getY()-1,node, eNode, COST_STRAIGHT);
}
//下
if((node.getY()+1)< column){
checkPath(node.getX(),node.getY()+1,node, eNode, COST_STRAIGHT);
}
//左
if((node.getX()-1)>=0){
checkPath(node.getX()-1,node.getY(),node, eNode, COST_STRAIGHT);
}
//右
if((node.getX()+1)< row){
checkPath(node.getX()+1,node.getY(),node, eNode, COST_STRAIGHT);
}
//左上
if((node.getX()-1)>=0&&(node.getY()-1)>=0){
checkPath(node.getX()-1,node.getY()-1,node, eNode, COST_DIAGONAL);
}
//左下
if((node.getX()-1)>=0&&(node.getY()+1)< column){
checkPath(node.getX()-1,node.getY()+1,node, eNode, COST_DIAGONAL);
}
//右上
if((node.getX()+1)< row&&(node.getY()-1)>=0){
checkPath(node.getX()+1,node.getY()-1,node, eNode, COST_DIAGONAL);
}
//右下
if((node.getX()+1)< row&&(node.getY()+1)< column){
checkPath(node.getX()+1,node.getY()+1,node, eNode, COST_DIAGONAL);
}
//从开启列表中删除
//添加到关闭列表中
closeList.add(openList.remove(0));
//开启列表中排序,把F值最低的放到最底端
Collections.sort(openList, new NodeFComparator());
//System.out.println(openList);
}
if(isFind){
getPath(resultList, node);
}
return resultList;
}
//查询此路是否能走通
private boolean checkPath(int x,int y,Node parentNode,Node eNode,int cost){
Node node=new Node(x, y, parentNode);
//查找地图中是否能通过
if(map[x][y]==0){
closeList.add(node);
return false;
}
//查找关闭列表中是否存在
if(isListContains(closeList, x, y)!=-1){
return false;
}
//查找开启列表中是否存在
int index=-1;
if((index=isListContains(openList, x, y))!=-1){
//G值是否更小,即是否更新G,F值
if((parentNode.getG()+cost)< openList.get(index).getG()){
node.setParentNode(parentNode);
countG(node, eNode, cost);
countF(node);
openList.set(index, node);
}
}else{
//添加到开启列表中
node.setParentNode(parentNode);
count(node, eNode, cost);
openList.add(node);
}
return true;
}
//集合中是否包含某个元素(-1:没有找到,否则返回所在的索引)
private int isListContains(List< Node> list,int x,int y){
for(int i=0;i< list.size();i++){
Node node=list.get(i);
if(node.getX()==x&&node.getY()==y){
return i;
}
}
return -1;
}
//从终点往返回到起点
private void getPath(List< Node> resultList,Node node){
if(node.getParentNode()!=null){
getPath(resultList, node.getParentNode());
}
resultList.add(node);
}
//计算G,H,F值
private void count(Node node,Node eNode,int cost){
countG(node, eNode, cost);
countH(node, eNode);
countF(node);
}
//计算G值
private void countG(Node node,Node eNode,int cost){
if(node.getParentNode()==null){
node.setG(cost);
}else{
node.setG(node.getParentNode().getG()+cost);
}
}
//计算H值
private void countH(Node node,Node eNode){
node.setF((Math.abs(node.getX()-eNode.getX())+Math.abs(node.getY()-eNode.getY()))*10);
}
//计算F值
private void countF(Node node){
node.setF(node.getG()+node.getH());
}
}
//节点类
class Node {
private int x;//X坐标
private int y;//Y坐标
private Node parentNode;//父类节点
private int g;//当前点到起点的移动耗费
private int h;//当前点到终点的移动耗费,即曼哈顿距离|x1-x2|+|y1-y2|(忽略障碍物)
private int f;//f=g+h
public Node(int x,int y,Node parentNode){
this.x=x;
this.y=y;
this.parentNode=parentNode;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
public Node getParentNode() {
return parentNode;
}
public void setParentNode(Node parentNode) {
this.parentNode = parentNode;
}
public int getG() {
return g;
}
public void setG(int g) {
this.g = g;
}
public int getH() {
return h;
}
public void setH(int h) {
this.h = h;
}
public int getF() {
return f;
}
public void setF(int f) {
this.f = f;
}
public String toString(){
return "("+x+","+y+","+f+")";
}
}
//节点比较类
class NodeFComparator implements Comparator< Node>{
@Override
public int compare(Node o1, Node o2) {
return o1.getF()-o2.getF();
}
}
JAVA开发中数据源创建方法
数据源是数据库连接池里面的概念,连接池就是指当服务器启动时,先建立几个连接,在应用需要与数据库连接时,就从连接池里获取,使用完以后,不是将连接断 掉,而是放回到池里面,这样就减少了数据连接创建的次数,大大提高了连接性能。而数据源就是给服务器一个配置信息,然服务器就知道怎么使用JDBC驱动, 比如url参数,数据库实例名、用户名与密码等等。Java中的数据源就是javax.sql.DataSource。DataSource的创建可以有 不同的实现,下面以mysql为例介绍几种常见DataSource的创建方法:
一、JNDI方式创建DataSource
以JNDI方式创建数据源首先要配置数据源的相关连接信息,也就是数据源连接池。该配置应该在Tomcat安装目录下的conf/context.xml 文件中配置,在Eclipse的J2EE架构下,也可以把context.xml文件创建在/META-INF目录下。其配置如下:
<Context><!--MySql-->
<Resource name="jdbc/movie" auth="Container" type="javax.sql.DataSource"
maxActive="100" maxIdle="30" maxWait="10000"
username="[用户名]" password="[密码]" driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/[实例名]?autoReconnect=true"/>
</Context>
正确的配置后,就可以在程序中以JNDI的方式创建数据源,得到数据库连接并进行相应的操作。代码如下:
try { Context context = new InitialContext(); if (context == null){ throw new Exception("create context failed!"); } DataSource ds = (DataSource) context.lookup("java:comp/env/jdbc/[实例名]"); if (ds == null) { Thread.sleep(2000); ds = (DataSource) context.lookup("java:comp/env/jdbc/[实例名]"); if (ds == null) { throw new Exception("get datasource failed!"); } } } catch (NamingException ne) { throw ne; } catch (Exception e) { throw e; } |
二、Apache提供的简单连接池创建数据源
以这种方式创建数据源必须先准备两个jar文件:commons-dbcp.jar 和 commons-pool.jar,将这两个jar包放到WEB-INF/lib目录下。以这种方式创建的数据源就不再是 javax.sql.DataSource了,而是org.apache.commons.dbcp.BasicDataSource。而且不再需要配置 任何文件就可以直接使用。代码如下:
// 创建BasicDataSource对象 BasicDataSource ds = new BasicDataSource(); ds.setDriverClassName("com.mysql.jdbc.Driver"); ds.setUrl("jdbc:mysql://localhost:3306/[实例名]"); ds.setUsername("[用户名]"); ds.setPassword("[密码]"); ds.setInitialSize(50); ds.setMaxActive(100); ds.setMaxIdle(30); ds.setMaxWait(10000); // 关闭数据源连接 ds.close(); |
三、C3P0方式创建数据源
使用C3P0方式创建数据源应该首先准备一个jar文件:c3p0-0.9.1.2.jar,将其放到WEB-INF/lib目录下,就可以在项目中使用 C3P0创建数据源,C3P0创建的数据源对象也不是DataSource对象,而是ComboPooledDataSource,代码如下:
// 创建ComboPooledDataSource对象 ComboPooledDataSource ds = new ComboPooledDataSource(); ds.setDriverClass("com.mysql.jdbc.Driver"); ds.setJdbcUrl("jdbc:mysql://localhost:3306/[实例名]"); ds.setUser("[用户名]"); ds.setPassword("[密码]"); ds.setInitialPoolSize(50); ds.setMaxPoolSize(100); ds.setMaxIdleTime(10000); |
四、Proxool方式创建数据源
采用该方式创建数据源需要准备的jar包:proxool-01.9.0RC3.jar,将其放到WEB-INF/lib目录下,之后就可以项目中创建ProxoolDataSource对象,其代码如下:
// 创建ProxoolDataSource对象 ProxoolDataSource ds = new ProxoolDataSource(); ds.setDriver("com.mysql.jdbc.Driver"); ds.setDriverUrl("jdbc:mysql://localhost:3306/[实例名]"); ds.setUser("[用户名]"); ds.setPassword("[密码]"); |
五、BoneCP方式创建数据源
BoneCP是一个快速高效,开源免费的Java数据库接池。创作者称,BoneCP在性能上会完全超越所有主流的Java连接池。它可以帮你管理数据连 接,让你的应用程序能更快速地访问数据库。比C3P0/DBCP(DataBaseconnection pool,数据库连接池)连接池快25倍。这个数据库连接池采用Google Collection作为内部的集合类框架,而且现在的版本已经很稳定。要使用BoneCP,必须用到的jar文件有:
· bonecp-0.6.5.jar
· google-collections-1.0.jar
· slf4j-api-1.5.11.jar
· slf4j-log4j12-1.5.11.jar
· log4j-1.2.15.jar
将这些jar包放到WEB-INF/lib目录下,就可以在程序中创建BoneCPDataSource对象,代码如下:
// 创建BoneCPDataSource对象 BoneCPDataSource ds = new BoneCPDataSource(); ds.setDriverClass("com.mysql.jdbc.Driver"); ds.setJdbcUrl("jdbc:mysql://localhost:3306/[实例名]"); ds.setUsername("[用户名]"); ds.setPassword("[密码]"); ds.setAcquireIncrement(1); ds.setAcquireRetryDelay(10000); ds.setIdleConnectionTestPeriod(100); ds.setMinConnectionsPerPartition(2); ds.setMaxConnectionsPerPartition(20); ds.setPartitionCount(2); |
在创建完数据源之后,就可以利用jdbc在程序与数据库之间建立连接,但要注意的是,要有相关的jdbc驱动包,不同的数据库需要不同的驱动,一般在各个数据库官方网都可以获取。
关于Servlet的学习概要:
1、什么是Sevlet?
是java类,他提供了基于协议的请求和响应服务,担当客户请求(Web浏览器或其他HTTP客户程序)与服务器响应(HTTP服务器上的数据库或应用程序)的中间层,具有独立于平台和协议的特性,可以生成前台web页面,生命周期通过Web容器控制。
2、有什么作用?
主要是接收客户端的请求(多数是http),并将处理结果返回给客户端。在mvc模式中属于Control控制层。
3、生命周期:
由容器控制,如,tomcat,经过初始化、运行和销毁三个阶段。值得注意的是,servlet是单实例,多线程,存在线程安全问题,所以,一定不要在 sevlet中定义或修改成员变量。不管有多少请求客户,servlet都只进行一次初始化,并在初始化的时候调用一次init()函数,之后,为每一个 客户端的请求创建一个新的线程。而且,不要把sevlet手动修改为单线程,这样会严重影响网络访问,因为在同一个时刻,只能有一个线程可以访问。可以用 下面的程序,验证他是单实例,多线程:
[java] view plaincopy
1. <span style="font-size:18px;">import java.io.*;
2. import javax.servlet.http.*;
3. import javax.servlet.*;
4.
5. public class TestLifeCycleServlet extends HttpServlet {
6.
7. public TestLifeCycleServlet() {
8. System.out.println("--------TestLifeCycleServlet()----------");
9. }
10.
11. public void init()
12. throws ServletException {
13. System.out.println("-----------init-------------");
14. }
15.
16. public void doGet(HttpServletRequest request, HttpServletResponse response)
17. throws ServletException, IOException {
18. System.out.println("------------doGet-------------");
19. }
20.
21. public void destroy() {
22.
23. }
24. }</span>
初始阶段:
1.servlet容器加载servlet类,把servlet类的.class文件独到内存
2.容器创建ServletConfig对象,该对象包含了servlet的初始化信息
3.容器创建一个servlet对象,调用对象的int方法进行初始化。
运行阶段:
当容器接到客户端的请求的时候,会创建servletRequest和servletResponse对象,然后调用service方法,并把这两个参数传进去。不管是通过doGet(参数放到请求的头,故有长度限制)还是doPost方法处理请求(参数放到请求体中,故无长度限制),都是由service方法来处理的。servletResponse获得请求的信息,servletResponse返回处理后的信息。
销毁阶段:
因为只有一个实例,所以只进行一次销毁,调用的是destroy方法,将servlet对象和相关联的servletConfig对象也一并销毁。我们可以在destroy方法中,添加一些释放资源的方法,这样会提高系统的性能。如,关闭连接,关闭流的输出等。
四、Servlet处理请求:
不管是否基于mvc模式,servlet处理请求主要是基于servletRequest和servletResponse对象的。在mvc模式中,为达 到职责单一的目的,采用了转发和重定向的思想。这里是一个重点。转发只发生在服务端,客户端是不知道的,不涉及重新发送请求的问题,所以数据不会被冲刷 掉,request数据共享;而重定向,则是将处理到一定程度的数据回发给客户端后,再次建立一次新的请求,上次请求中request对象中的数据信息不会保留。区别见下图:
因为重定向不会共享request对象,可以将request对象的数据放到session中,解决这种不足,下面是转发和重定向的代码实现:
servlet类中的代码:
[java] view plaincopy
1. <span style="font-size:18px;"> StudentManager studentManager = new StudentManagerImpl();
2.
3.
4. List<Student> studentList = studentManager.findStudnetList(beginDate,endDate);
5.
6. //将学生列表设置到request范围中
7. request.setAttribute("student_list",studentList);
8.
9. //转发:在服务端转发,客户端是不知道的
10. <strong>request.getRequestDispatcher("/student_list.jsp").forward(request,response);</strong>
11.
12. //将studentList放到session中
13. //HttpSession session =request.getSession();
14. //session.setAttribute("student_list",studentList);
15.
16. //重定向,不会共享request,request.getContextPath()用于获取当前目录到跟目录的路径
17. //response.sendRedict(request.getContextPath()+"/student_list.jsp");
18. //以下写法错误,/代表8080端口,或者可以理解成跟目录
19. //response.sendRedict("/studentList.jsp");</span>
处理视图显示的jsp的与之对应的代码:
[java] view plaincopy
1. <span style="font-size:18px;"> List <Student> studentList =(List) request.getAttribute("student_list");
2. //如果servlet使用的是session,那么这里就要对应成session
3. //List <Student> studentList =(List) session.getAttribute("student_list");
4. </span>
五、servlet和jsp的区别:
我们知道jsp通过容器的管理最后编译成servlet类,同样也是运行在服务端的,但是jsp更加侧重处理与界面相关的一些显示。当然也避免不了与一些后台数据的交互,这时候就引入了jsp中嵌入java代码的语法:
1.如果使用<!% %>作为全局变量出现,用于定义成员变量:
[java] view plaincopy
1. <span style="font-size:18px;"><%!
2. int i;
3. public void setName(){....};
4. %></span>
2.如果使用<% %>作为局部变量出现,用于定义某个方法的一些变量等:
[java] view plaincopy
1. <span style="font-size:18px;"><%
2. List <Student> studentList =(List) request.getAttribute("student_list");
3. //如果servlet使用的是session,那么这里就要对应成session
4. //List <Student> studentList =(List) session.getAttribute("student_list");
5.
6.
7. for (Iterator<Student> iter = studentList.iterator();iter.hasNext();){
8. Student student = iter.next();
9.
10. }
11.
12. %></span>
3.如果使用<%= %>=后面必须是字符串变量或者可以被转换成字串的表达式,不需要以分号结束,只有一行:
[java] view plaincopy
1. <span style="font-size:18px;"> <td><%=student.getStudentId()%></td>
2. <td><%=student.getStudentName()%></td>
3.
4.
5. </span>
4.注释:
[java] view plaincopy
1. <span style="font-size:18px;"><% //.....%>
2. <% --.......--%>
3. <% /*.....*/%></span>
六、Servlet的Cookie:
这是Servlet的一个重点,这里只简要的概述一下,后面会深入学习。
Session 是单用户的会话状态。当用户访问网站时,产生一个 SESSIONID,并存在于 COOKIES 中。每次向服务器请求时,发送这个 COOKIES ,再从服务器中检索是否有这个 SESSIONID 保存的数据。
CACHE ,则是服务器端的缓存,是所有用户都可以访问和共享的。通常为网页及媒体文件,在涉及安全性的动态生成页面上,可以设置有较时间,以便减少攻击。
cookie和ssession都是保存每个用户单独的信息,前者保存在服务器。安全。后者保存在客户端。安全比较低。后者可以长期保存。在客户端浏览器和服务器之间来回丢来丢去。
七、Sevlet过滤器:
过滤器的作用:
1.判断用户是否登录
2.过滤非法字符
3.解决统一编码
过滤器的使用:
是一个实现了Filter接口的java类,重写init、doFilter、destroy方法,在web.config文件中可以配置多个过滤器,它们按照顺序执行,并具有传递性。
八、Servlet的监听器:
是对request、session、application的监听。通过监听,可以增加系统的安全性,手动的添加一些资源的处理,可以增强系统的性能。
如,若要创建一个对session的监听,需要实现HttpSessionListener接口,重写下面的方法:
[java] view plaincopy
1. <span style="font-size:18px;">public void sessionCreated(HttpSessionEvent arg0) {} // 创建
2.
3. public void sessionDestroyed(HttpSessionEvent arg0) {} // 销毁
4.
5. public void attributeAdded(HttpSessionEvent arg0) {} // 增加
6.
7. public void attributeRemoved(HttpSessionEvent arg0) {} // 删除
8.
9. public void attributeReplaced(HttpSessionEvent arg0) {} // 替换 </span>
总结:
这里主要介绍了Servlet的机制原理和常用的基本语法,重点是request和response对象的处理,限于篇幅,会单独Session、cookie过滤器和监听器从代码的角度通过一些实例进行学习。
感觉java中的转发和重定向是一个比较新颖的东东,还有在jsp文件的html中嵌入java代码也是一个需要熟练掌握并运用的地方。
C3P0连接池详解及配置
分类: J2EE 2009-08-28 00:55 12325人阅读 评论(1) 收藏 举报
我也来续一下:
数据库连接是一个耗费大量资源且相当慢的操作,所以为了提高性能和连接速度,诞生了连接池这样的概念。
在多用户并发操作过程中,连接池尤为重要。
它是将那些已连接的数据库连接存放在一个容器里(连接池),这样以后别人要连接数据库的时候,将不会重新建立数据库连接(这样蜗牛的慢动作谁都受不了的),他会直接从连接池里取出可用的连接,用户使用完毕后,连接又重新回到连接池中。
注意:连接池里的连接将会一直保存在内存里,即使你没用也是一样。所以这个时候你得权衡一下连接池的连接数量了。
<c3p0-config>
<default-config>
<!--当连接池中的连接耗尽的时候c3p0一次同时获取的连接数。Default: 3 -->
<property name="acquireIncrement">3</property>
<!--定义在从数据库获取新连接失败后重复尝试的次数。Default: 30 -->
<property name="acquireRetryAttempts">30</property>
<!--两次连接中间隔时间,单位毫秒。Default: 1000 -->
<property name="acquireRetryDelay">1000</property>
<!--连接关闭时默认将所有未提交的操作回滚。Default: false -->
<property name="autoCommitOnClose">false</property>
<!--c3p0将建一张名为Test的空表,并使用其自带的查询语句进行测试。如果定义了这个参数那么
属性preferredTestQuery将被忽略。你不能在这张Test表上进行任何操作,它将只供c3p0测试
使用。Default: null-->
<property name="automaticTestTable">Test</property>
<!--获取连接失败将会引起所有等待连接池来获取连接的线程抛出异常。但是数据源仍有效
保留,并在下次调用getConnection()的时候继续尝试获取连接。如果设为true,那么在尝试
获取连接失败后该数据源将申明已断开并永久关闭。Default: false-->
<property name="breakAfterAcquireFailure">false</property>
<!--当连接池用完时客户端调用getConnection()后等待获取新连接的时间,超时后将抛出
SQLException,如设为0则无限期等待。单位毫秒。Default: 0 -->
<property name="checkoutTimeout">100</property>
<!--通过实现ConnectionTester或QueryConnectionTester的类来测试连接。类名需制定全路径。
Default: com.mchange.v2.c3p0.impl.DefaultConnectionTester-->
<property name="connectionTesterClassName"></property>
<!--指定c3p0 libraries的路径,如果(通常都是这样)在本地即可获得那么无需设置,默认null即可
Default: null-->
<property name="factoryClassLocation">null</property>
<!--Strongly disrecommended. Setting this to true may lead to subtle and bizarre bugs.
(文档原文)作者强烈建议不使用的一个属性-->
<property name="forceIgnoreUnresolvedTransactions">false</property>
<!--每60秒检查所有连接池中的空闲连接。Default: 0 -->
<property name="idleConnectionTestPeriod">60</property>
<!--初始化时获取三个连接,取值应在minPoolSize与maxPoolSize之间。Default: 3 -->
<property name="initialPoolSize">3</property>
<!--最大空闲时间,60秒内未使用则连接被丢弃。若为0则永不丢弃。Default: 0 -->
<property name="maxIdleTime">60</property>
<!--连接池中保留的最大连接数。Default: 15 -->
<property name="maxPoolSize">15</property>
<!--JDBC的标准参数,用以控制数据源内加载的PreparedStatements数量。但由于预缓存的statements
属于单个connection而不是整个连接池。所以设置这个参数需要考虑到多方面的因素。
如果maxStatements与maxStatementsPerConnection均为0,则缓存被关闭。Default: 0-->
<property name="maxStatements">100</property>
<!--maxStatementsPerConnection定义了连接池内单个连接所拥有的最大缓存statements数。Default: 0 -->
<property name="maxStatementsPerConnection"></property>
<!--c3p0是异步操作的,缓慢的JDBC操作通过帮助进程完成。扩展这些操作可以有效的提升性能
通过多线程实现多个操作同时被执行。Default: 3-->
<property name="numHelperThreads">3</property>
<!--当用户调用getConnection()时使root用户成为去获取连接的用户。主要用于连接池连接非c3p0
的数据源时。Default: null-->
<property name="overrideDefaultUser">root</property>
<!--与overrideDefaultUser参数对应使用的一个参数。Default: null-->
<property name="overrideDefaultPassword">password</property>
<!--密码。Default: null-->
<property name="password"></property>
<!--定义所有连接测试都执行的测试语句。在使用连接测试的情况下这个一显著提高测试速度。注意:
测试的表必须在初始数据源的时候就存在。Default: null-->
<property name="preferredTestQuery">select id from test where id=1</property>
<!--用户修改系统配置参数执行前最多等待300秒。Default: 300 -->
<property name="propertyCycle">300</property>
<!--因性能消耗大请只在需要的时候使用它。如果设为true那么在每个connection提交的
时候都将校验其有效性。建议使用idleConnectionTestPeriod或automaticTestTable
等方法来提升连接测试的性能。Default: false -->
<property name="testConnectionOnCheckout">false</property>
<!--如果设为true那么在取得连接的同时将校验连接的有效性。Default: false -->
<property name="testConnectionOnCheckin">true</property>
<!--用户名。Default: null-->
<property name="user">root</property>
<!--早期的c3p0版本对JDBC接口采用动态反射代理。在早期版本用途广泛的情况下这个参数
允许用户恢复到动态反射代理以解决不稳定的故障。最新的非反射代理更快并且已经开始
广泛的被使用,所以这个参数未必有用。现在原先的动态反射与新的非反射代理同时受到
支持,但今后可能的版本可能不支持动态反射代理。Default: false-->
<property name="usesTraditionalReflectiveProxies">false</property>
<property name="automaticTestTable">con_test</property>
<property name="checkoutTimeout">30000</property>
<property name="idleConnectionTestPeriod">30</property>
<property name="initialPoolSize">10</property>
<property name="maxIdleTime">30</property>
<property name="maxPoolSize">25</property>
<property name="minPoolSize">10</property>
<property name="maxStatements">0</property>
<user-overrides user="swaldman">
</user-overrides>
</default-config>
<named-config name="dumbTestConfig">
<property name="maxStatements">200</property>
<user-overrides user="poop">
<property name="maxStatements">300</property>
</user-overrides>
</named-config>
</c3p0-config>
C3P0连接池配置
2011-10-04 09:46 29960人阅读 评论(0) 收藏 举报
c3p0transactionsdatabasecachingjdbctesting
C3P0是一个开源的JDBC连接池。
在Spring中,C3P0的一些配置,介绍如下(只列了一部分,不是全部)
[html] view plaincopy
1. <!-- c3p0连接池配置 -->
2. <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
3. <!-- 用户名-->
4. <property name="user" value="${username}"/>
5. <!-- 用户密码-->
6. <property name="password" value="${password}"/>
7. <property name="driverClass" value="${driver_class}"/>
8. <property name="jdbcUrl" value="${url}"/>
9.
10. <!--连接池中保留的最大连接数。默认值: 15 -->
11. <property name="maxPoolSize" value="20"/>
12. <!-- 连接池中保留的最小连接数,默认为:3-->
13. <property name="minPoolSize" value="2"/>
14. <!-- 初始化连接池中的连接数,取值应在minPoolSize与maxPoolSize之间,默认为3-->
15. <property name="initialPoolSize" value="2"/>
16.
17. <!--最大空闲时间,60秒内未使用则连接被丢弃。若为0则永不丢弃。默认值: 0 -->
18. <property name="maxIdleTime">60</property>
19.
20. <!-- 当连接池连接耗尽时,客户端调用getConnection()后等待获取新连接的时间,超时后将抛出SQLException,如设为0则无限期等待。单位毫秒。默认: 0 -->
21. <property name="checkoutTimeout" value="3000"/>
22.
23. <!--当连接池中的连接耗尽的时候c3p0一次同时获取的连接数。默认值: 3 -->
24. <property name="acquireIncrement" value="2"/>
25.
26. <!--定义在从数据库获取新连接失败后重复尝试的次数。默认值: 30 ;小于等于0表示无限次-->
27. <property name="acquireRetryAttempts" value="0"/>
28.
29. <!--重新尝试的时间间隔,默认为:1000毫秒-->
30. <property name="acquireRetryDelay" value="1000" />
31.
32. <!--关闭连接时,是否提交未提交的事务,默认为false,即关闭连接,回滚未提交的事务 -->
33. <property name="autoCommitOnClose">false</property>
34.
35. <!--c3p0将建一张名为Test的空表,并使用其自带的查询语句进行测试。如果定义了这个参数那么属性preferredTestQuery将被忽略。你不能在这张Test表上进行任何操作,它将只供c3p0测试使用。默认值: null -->
36. <property name="automaticTestTable">Test</property>
37.
38. <!-- 如果为false,则获取连接失败将会引起所有等待连接池来获取连接的线程抛出异常,但是数据源仍有效保留,并在下次调用getConnection() 的时候继续尝试获取连接。如果设为true,那么在尝试获取连接失败后该数据源将申明已断开并永久关闭。默认: false-->
39. <property name="breakAfterAcquireFailure">false</property>
40.
41. <!--每60秒检查所有连接池中的空闲连接。默认值: 0,不检查 -->
42. <property name="idleConnectionTestPeriod">60</property>
43. <!--c3p0全局的PreparedStatements缓存的大小。如果maxStatements与maxStatementsPerConnection均为0,则缓存不生效,只要有一个不为0,则语句的缓存就能生效。如果默认值: 0-->
44. <property name="maxStatements">100</property>
45. <!--maxStatementsPerConnection定义了连接池内单个连接所拥有的最大缓存statements数。默认值: 0 -->
46. <property name="maxStatementsPerConnection"></property>
47. </bean>