java java纯数字正则表达式式 总共13位前7位数字第8位BCWESN其中一个,后5位数字。

51CTO旗下网站
Java正则表达式工具类实例
本文向您展示一个Java正则表达式的工具类,这个工具类包含25中正则表达式,本文列出15中常用功能。
作者:dolphin_ygj来源:JavaEye| 13:36
以前写了一个Java的正规表达式的Java工具类,分享一下,有用到的欢迎下载使用。如果你有常用的定义好的,且测试通过的正规表达式,欢迎跟贴,也让我享用一下类中用到了 jakarta-oro-2.0.jar包,请大家自己在 apache网站下下载
在这是junit测试单元类我就不提交了,在main()方法中有几个小测试,有兴趣自己玩吧.
这个Java正则表达式工具类目前主要有25种正规表达式,有些不常用,这里只列出15种常用Java正则表达式功能。1.匹配图象;&&&&&&&&&&&&&&&&&&&&& 2 匹配email地址;&&&&&&&&&&&&&&&&&&& 3 匹配匹配并提取&&&&&&&&&&&&&&&&&&&&&&&& 4 匹配并提取5.匹配日期&&&&&&&&&&&&&&&&&&&&&& 6 匹配电话;&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& 7 匹配身份证&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& 8 匹配邮编代码9. 不包括特殊字符的匹配10 匹配非负整数(正整数 + 0)&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& 11 匹配不包括零的非负整数(正整数 & 0)12 匹配正整数&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& 13& 匹配非正整数(负整数 + 0)&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& 14 匹配负整数;&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& 15.匹配整数
package&com.ygj.&&&& &&&& &import&java.util.*;&&&& &&&& &import&org.apache.oro.text.regex.*;&&&& &&& &&&& &&&&&&&&&&&&&&&&&&&&&&& &public&final&class&Regexp&&&& &{&&&& &&&& &&&&&&&& &&&&&static&final&&Set&SEPARATOR_SET=new&TreeSet();&&&& &&&&&{&&&& &&&&&&&&&&&&&&&&SEPARATOR_SET.add("(");&&&& &&&&&&&&&&&&&&&&SEPARATOR_SET.add(")");&&&& &&&&&&&&&&&&&&&&SEPARATOR_SET.add("[");&&&& &&&&&&&&&&&&&&&&SEPARATOR_SET.add("]");&&&& &&&&&&&&&&&&&&&&SEPARATOR_SET.add("{");&&&& &&&&&&&&&&&&&&&&SEPARATOR_SET.add("}");&&&& &&&&&&&&&&&&&&&&SEPARATOR_SET.add("&");&&&& &&&&&&&&&&&&&&&&SEPARATOR_SET.add("&");&&&& &&&&&}&&&& &&&& &&&& &&&&&&&& &&&&&&public&static&HashMap&regexpHash&=&new&HashMap();&&&& &&&& &&&&&&&& &&&&&public&static&&List&matchingResultList&=&new&ArrayList();&&&& &&&& &&&&private&&&&&&&Regexp()&&&& &&&&&{&&&& &&&& &&&&&}&&&& &&&&&&&&&&& &&&&&public&static&Regexp&getInstance()&&&& &&&&&{&&&& &&&&&&&&&return&new&Regexp();&&&& &&&&&}&&&& &&&& &&&&&&&&&&&&&&&&&&& &&&&&public&static&final&String&icon_regexp&=&"^(/{0,1}\\w){1,}\\.(gif|dmp|png|jpg)$|^\\w{1,}\\.(gif|dmp|png|jpg)$";&&&& &&&& &&&&&&&&&&&&&&&&&&& &&&&&public&static&final&String&email_regexp&=&"(?:\\w[-._\\w]*\\w@\\w[-._\\w]*\\w\\.\\w{2,3}$)";&&&& &&&& &&&&&&&&&&&&&&&&&&&&&&&&&& &&&&&public&static&final&String&url_regexp&=&"(\\w+)://([^/:]+)(:\\d*)?([^#\\s]*)";&&&& &&&& &&&&&&&&&&&&&&&&&&&&&&&&&& &&&&&public&static&final&String&http_regexp&=&"(http|https|ftp)://([^/:]+)(:\\d*)?([^#\\s]*)";&&&& &&&& &&&&&&&&&&&&&&&&&&&&&&& &&&&&public&static&final&String&date_regexp&=&"^((((19){1}|(20){1})d{2})|d{2})[-\\s]{1}[01]{1}d{1}[-\\s]{1}[0-3]{1}d{1}$";&&&& &&&&&&&&&&&&&&&&&&&&&&&& &&&&&public&static&final&String&phone_regexp&=&"^(?:0[0-9]{2,3}[-\\s]{1}|\\(0[0-9]{2,4}\\))[0-9]{6,8}$|^[1-9]{1}[0-9]{5,7}$|^[1-9]{1}[0-9]{10}$";&&&& &&&& &&&&&&&&&&&&&&&&&&&&& &&&&&public&static&final&String&ID_card_regexp&=&"^\\d{10}|\\d{13}|\\d{15}|\\d{18}$";&&&& &&&& &&&&&&&&&&&&&&&&&&&& &&&&&public&static&final&String&ZIP_regexp&=&"^[0-9]{6}$";&&&& &&&& &&&&&&&&&&&&&&&&&&&& &&&&&public&static&final&String&non_special_char_regexp&=&"^[^'\"\\;,:-&&\\s].+$";&&&& &&&& &&&&&&&&&& &&&&&public&static&final&String&non_negative_integers_regexp&=&"^\\d+$";&&&& &&&& &&&&&&&&&& &&&&&public&static&final&String&non_zero_negative_integers_regexp&=&"^[1-9]+\\d*$";&&&& &&&& &&&&&&&&&&&& &&&&&public&static&final&String&positive_integer_regexp&=&"^[0-9]*[1-9][0-9]*$";&&&& &&&& &&&&&&&&&&&& &&&&&public&static&final&String&non_positive_integers_regexp&=&"^((-\\d+)|(0+))$";&&&& &&&& &&&&&&&&&&&& &&&&&public&static&final&String&negative_integers_regexp&=&"^-[0-9]*[1-9][0-9]*$";&&&& &&&& &&&&&&&&&&&& &&&&&public&static&final&String&integer_regexp&=&"^-?\\d+$";&&&& &&&& &&&&&&&&&&&& &&&&&public&static&final&String&non_negative_rational_numbers_regexp&=&"^\\d+(\\.\\d+)?$";&&&& &&&& &&&&&&&&&&&& &&&&&public&static&final&String&positive_rational_numbers_regexp&=&"^(([0-9]+\\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\\.[0-9]+)|([0-9]*[1-9][0-9]*))$";&&&& &&&& &&&&&&&&&&&& &&&&&public&static&final&String&non_positive_rational_numbers_regexp&=&"^((-\\d+(\\.\\d+)?)|(0+(\\.0+)?))$";&&&& &&&& &&&&&&&&&&&& &&&&&public&static&final&String&negative_rational_numbers_regexp&=&"^(-(([0-9]+\\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\\.[0-9]+)|([0-9]*[1-9][0-9]*)))$";&&&& &&&& &&&&&&&&&&&& &&&&&public&static&final&String&rational_numbers_regexp&=&"^(-?\\d+)(\\.\\d+)?$";&&&& &&&& &&&&&&&&&&&& &&&&&public&static&final&String&letter_regexp&=&"^[A-Za-z]+$";&&&& &&&& &&&&&&&&&&&& &&&&&public&static&final&String&upward_letter_regexp&=&"^[A-Z]+$";&&&& &&&& &&&&&&&&&&&& &&&&&public&static&final&String&lower_letter_regexp&=&"^[a-z]+$";&&&& &&&& &&&&&&&&&&&& &&&&&public&static&final&String&letter_number_regexp&=&"^[A-Za-z0-9]+$";&&&& &&&& &&&&&&&&&&&& &&&&&public&static&final&String&letter_number_underline_regexp&=&"^\\w+$";&&&& &&&& &&&&&&&&&&&&&&& &&&&&public&void&putRegexpHash(String&regexpName,&String&regexp)&&&& &&&&&{&&&& &&&&&&&&&regexpHash.put(regexpName,&regexp);&&&& &&&&&}&&&& &&&& &&&&&&&&&&&&&&& &&&&&public&String&getRegexpHash(String&regexpName)&&&& &&&&&{&&&& &&&&&&&&&if&(regexpHash.get(regexpName)&!=&null)&&&& &&&&&&&&&{&&&& &&&&&&&&&&&&&return&((String)&regexpHash.get(regexpName));&&&& &&&&&&&&&}&&&& &&&&&&&&&else&&& &&&&&&&&&{&&&& &&&&&&&&&&&&&System.out.println("在regexpHash中没有此正规表达式");&&&& &&&&&&&&&&&&&return&"";&&&& &&&&&&&&&}&&&& &&&&&}&&&& &&&& &&&&&&&&&& &&&&&public&void&clearRegexpHash()&&&& &&&&&{&&&& &&&&&&&&&regexpHash.clear();&&&& &&&&&&&&&return;&&&& &&&&&}&&&& &&&& &&&&&&&&&&&&&&&&&& &&&&&public&static&boolean&isHardRegexpValidate(String&source,&String&regexp)&&&& &&&&&{&&&& &&&& &&&&&&&&&try&&& &&&&&&&&&{&&&& &&&&&&&&&&&&&&&&&&&&&&&&&&PatternCompiler&compiler&=&new&Perl5Compiler();&&&& &&&& &&&&&&&&&&&&&&&&&&&&&&&&&&PatternMatcher&matcher&=&new&Perl5Matcher();&&&& &&&& &&&&&&&&&&&&&&&&&&&&&&&&&&Pattern&hardPattern&=&compiler.compile(regexp);&&&& &&&& &&&&&&&&&&&&&&&&&&&&&&&&&&return&matcher.contains(source,&hardPattern);&&&& &&&& &&&&&&&&&}&&&& &&&&&&&&&catch&(MalformedPatternException&e)&&&& &&&&&&&&&{&&&& &&&&&&&&&&&&&e.printStackTrace();&&&& &&&& &&&&&&&&&}&&&& &&&&&&&&&return&false;&&&& &&&&&}&&&& &
上面的工具类代码包含了常用Java正则表达式功能,稍加调试就可以满足实际项目中的需求。
【编辑推荐】
【责任编辑: TEL:(010)】
大家都在看猜你喜欢
热点头条热点关注头条
24H热文一周话题本月最赞
讲师:27599人学习过
讲师:218801人学习过
讲师:17686人学习过
精选博文论坛热帖下载排行
本书是由长期从事网络管理工作和网络工程人员培训工作的一线网管人员和教学人员精心编写,从现实的技术发展角度和实际应用的角度,通过大量...
订阅51CTO邮刊当前位置: >>
JRE 和 JDK1IO 流1 ByteArrayOutput/InputStreamByteArrayOutputStream 内 部 已 经 封 装 了 字 节 数 组 , 即 已 经 包 含 了 目 的 。 ByteArrayInputStream 在构造的时候需要接收数据源,而且数据源是一个字节数组。因为这 两个流对象在操作的时候没有使用系统资源(直接操作数组) ,所以不需要关闭,主算关闭 了也能照样使用也不会发生任何 IO 异常。2编码?乱码:一个字符或者一串字符,从 a 端传到 b 端,整个数据有三个存在地点:a 端,传递途中,b 端。数据在传输过程中,总是以字节的形式,数据在内存里以及硬盘中也是以字节的形式。 只要在客户端为了让用户识别才会转成字符。由字符到字节,叫编码,反之为解码。a 端数 据在传递前要编码成字节, 端在接收到字节后再对字节进行解码, b 两端用的码表必须一致, 否则就会乱码。 如果一开始编码就出错了,比如,我们对中文字符按照 iso8859-1 去编,之后无论怎么解, 得到的都是乱码, 因为西欧码表压根就没有中文字符, 你给它一个中文字符让它按照西欧码 表去编码,它找半天也找不到中文对应的码(数字) ,最终它里面压根就没中文,最终它采 取的办法拿着你给的中文去找了?(英文)这个字符,然后将?对应的码作为数据的码,然后 将这个码作为字节传递到 b 端了,b 端接收的必然是问号,而英文里的问号,用任何中文码 表去解,得到的都是问号(中文码表兼容英文码表) 。拿 n 个字符按某个码表解码,若该码 表不识别,这些字符,则将 n 个字符(不论何种字符,包括汉字)全部被看成 n 个?英文问 号字符。这就是为什么乱码经常以问号的形式表现出来。如果某段乱码全部是问号,则很有 可能是一开始编码的时候就完全编错了,用了错的码表。 全部是问号的情况还有,假若我们将一个中文字符按 UTF-8 去编码,然后用 ISO8859-1 去 解,得到的肯定也全部是问号(三个问号,原因很简单,前者用三个字节来表示一个汉字, 后者都是用一个字节来表示一个西欧字符,这里要注意的是,尽管西欧码表不支持汉字,但 并不意味着按照支持汉字的码表编的码不在西欧码表里,在,不过对应的是乱码而已,以问 号的形式表现给客户端,但是这种对应关系依然存在着,意思就是,我们拿着这些乱码再次 查找这个西欧码表,依然能得到一开始就编成的码,然后再拿着得到的码去查 UTF-8 解码 就能再次得到正常的中文,这就是下面的解决乱码的原理) 。 还有一种情况,一开始编对了,即被编码的字符在码表里存在,但是接收端在解码的时候用 了错误的码表,然后接收端将经过错误解码的数据拿去查看,看到的肯定也是乱码,这种情 况通常是可能更正的,原因尽管解码解错了,但是因为对应关系还存在,所以再去编码还是 能得到正确的字符。2Socket 编程UDPUdp 分为发送端和接收端(两端一般同时运行在同一台电脑上) DatagramSocket:表示用来发送和接收数据报包的套接字。 receive(DatagramPacket dp); send(DatagramPacket dp); DatagramPacket:数据报包。发送数据,构造数据报包的时候要指定发送的地址和端口。 Send 端的流程: 1、 创建 udpsocket 服务,通过 DatagramSocket 对象。 (发送端也是一个程序, 所以也会有一个端口,不指定的话是系统为它随机生成的,因为它是发端,它要 把指定的数据发送到指定的主机指定的端口上,不用来接收数据,所以可以不显 示指定端口,如果我们同时也在此端接收数据,则必须要指定端口,让给它发送 数据的应用程序指定目的地。 ) 2、 确定数据,并封装数据及输相关信息,该信息要指定要发送到哪台主机及该 主机上的哪个应用程序(通过端口指定) 。 3、 通过之前建立起的 socket 服务将数据发送出去。 4、 关闭资源。 注意:在面向无连接的 upd 编程中,如果只做了以上步骤,而没有写服务端,则数据 发送后就丢失了。 Rece 端的流程: 首先要明白发送端和接收一般不位于同一台电脑上,它们是不同的应用程序,所以都 有 main 函数。 1、 创建 udpsocket 服务(如果我们不指定监听端口,系统为接收端随机创建一个 端口,我们一定要指定一个端口,这个端口就是发送端发送数据用的,这个端口 就是在说,我就是 1000 端口,你是给1000端口发送数据的,所以我能接收你 发送的数据。 ) 2、 定义一个数据包(为空) ,用来存储要接收的字节数据。数据包能更好的封装 接收来的数据,所以用它来接收,后续的操作会比较方便。 (定义数据包的时候, 我们需要手动建立一个字节数据给它,让它用来将接收到的数据放到该字节中。 ) 3、 通过 socket 服务的接收方法,将数据接收到上面定义好的数据包中。 4、 通过数据包中的方法取出数据,作相应处理。 5、 关闭资源。 如下是一个完整的命令行聊天程序:/*Chat.java*/ import java.io.BufferedR import java.io.InputStreamR import java.net.DatagramP import java.net.DatagramS import java.net.InetA class Send implements Runnable { private DatagramS public Send(DatagramSocket ds){ this.ds= } @Override public void run() { try{ BufferedReader br=new BufferedReader(new InputStreamReader(System.in)); String line= while((line=br.readLine())!=null){ if(&886&.equals(line)) byte[] buf=line.getBytes(); DatagramPacket dp=new DatagramPacket(buf,buf.length,InetAddress.getByName(&192.168.1.126&), 10000); ds.send(dp); } }catch(Exception e){ throw new RuntimeException(e); } } } class Rece implements Runnable{ private DatagramS public Rece(DatagramSocket ds){ this.ds= } @Override public void run() { try{ while(true){ byte[] buf=new byte[1024]; DatagramPacket dp=new DatagramPacket(buf,buf.length); ds.receive(dp); String ip=dp.getAddress().getHostAddress(); String data=new String(dp.getData(),0,dp.getLength()); System.out.println(ip+&:&+data); } }catch(Exception e){ throw new RuntimeException(e); } } } public class Chat{ public static void main(String[] args) throws Exception{ DatagramSocket sendSocket=new DatagramSocket(); DatagramSocket receSocket=new DatagramSocket(10000); new Thread(new Send(sendSocket)).start(); new Thread(new Rece(receSocket)).start(); } }程序分析:当整个程序在一台电脑上运行的时候,我们可以从主类的主方法中看出,首 先会创建一个发送服务和一个接收服务。 发送服务在自己的 run 方法中指定了要将数据发送 给哪个主机(也运行了此应用程序)的哪个端口(要注意的是,在本代码中,发送服务的 run 方法定死了某台主机的地址及端口号: 192.168.1.126 1000, 即是说我的程序启动起来后, 只能给这个 ip 的这个端口发送数据,这一点我们可以在做界面程序的时候改善,如,让用 户选择要跟哪个主机连接,当然了,端口一般不会变,因为要保持发送和接收的一致。 )在 主类的 main 方法中创建接收服务线程的时候,指定了监听端口,这很好理解,意思就是说 本线程接收任何发到该端口的数据, 这个端口与发送数据指定的目的主机的端口当一致, 否 则接收线程接不到数据! ) 要注意, 以上程序启动运行的时候会开起发送线程和线程, 其中发送线程用来发送数据, 和接收线程运行在一个主线程中, 因为是发送线程, 所以本身也占用端口, 但我们不用管他, 让系统随机分配即可,它只负责将数据发到指定主机的指定端口,它的任务就完成了。 要在局域网内试验此程序,比如两台电脑的 ip 分别是 192.168.1.125 和 192.168.1.126, 将 Chat.java 分别拷到两台电脑上, 其中 125 电脑中的代码应该将发送 Send 类的发送目的改 为 126,126 则正好相反。这样二者就能互相监听互相传递信息了。TCPTcp 分为客户端和服务端。Socket 和 Servercket。 构造 Socket 需要连接到服务端的主机和端口。因为是面向连接的,所以运行程序的时候需 要提前启动服务端。如 Socket s=new Socket(“12.12.12.12” 10000);通路一旦建立,就建立了 一个抽象的 Socket 流用来进行数据读写。 一个服务端会连接多个客户端: 当客户端A和客户B都连接进服务端的时候, 对服务端 来说有两个 SOCKET 连接,也即有两个 SCOKET 流。 客户端:1、创建客户端的 Socket 服务,指定目的主机和端口 2、获取 Socket 流里的输出流往服务端写数据。 服务端:1、建立服务端的 SOCKET 服务 ServerSocket。并监听一个端口。 2、获取连接过来的客户端对象。通过 ServeSocket 的 accept 方法,它是阻塞 式方法,没有连接就会等。 3、通过 socket 流获取客户端发来的数据。 4、服务端可关也可不关。Socket 编程走的是传输层,如果我们算定义一个浏览器(客户端)来接收服务器发送过 来的信息,会将服务器所有的信息都接收到。这个“所有的信息”包括,响应头,如果我们 用 URL.openConnection()(返回一个 HttpURLConnection,是 URLConnection 的子类) 来建立与 服务器端的连接:URLConnection conn=url.openConnection();这时接收服务器端传来的信 息: InputStremain=conn.getInputStream(); byte[]buf=newByte[1024]; intlen=in.read(buf); 这时打印接收内容的话,会发现消息响应头没有了,因为 URLConnection 面向应用层, 传输层传来的信息,在向应用层传送的时候被拆包了,拆包以后,只把有效的信息(具体页 面)显示给用户。 (发送过来的数据包包括头信息和具体页面内容) 。之所以会这样,是因为 应用层有协议(这里指的是 http 协议)只会解析能够识别的数据。3 正则表达式 功能 1:匹配字符串n 个规则会先验证字符串里的前 n 个字符,默认从字符串的第一个字符开始验证,一直 验证到最后一个字符。 Stringstr=&a9a9&; Stringreg=&[abc]\\d&;//第一个规则验证完第一个字符后, 第二个规则会验证剩下的所有字 符 str.matches(reg);//它返回 false 在限定规则里,如数字,是这样的:\d 即所检查的字符只要是数字就行。这里的\d 是 一个整体,并非所谓的转义字符。但是\是 java 里的特殊字符,所以需要转义。如果要限定 某位必须是某个字符,可以直接用该字符限定也可用[char]来限定。.在正则表达式里代表任 意字符,真正的字符.在正则表达式中是\.,所以在 java 在字符串里指定匹配规则的时候,一 定得是这样:\\. 如下是对手机号码的限定: Stringtel=&&; Stringregx=&[1][358]\\d{9}&;//&1[358]\\d{9}&“手机号码必须以 1 开头,且第二个字符目 前只能是 358 中的一种,后面是 9 个数字功能 2:切割字符串比如对这样一个字符串:Stringnames=&zhangsanlisiqianzhihui&; 如果用一般的切割方式,不太容易实现: names.split(&&);这样切割出来的会有许多空格,因为原来的 names 里面的空格有连续多 个。所以如果想把名字真正的切割出来 可以这样: Stringregx=&+&;//一个空格一个加号,表明以一个或多个空格来作为切割参考 names.split(regx);//其实第一种切割方式本质上也是利用了正则表达式, 只是只以一个空 格作为参考,所以切割不成功。 组的概念:为了可以让规则被重用,可以将一个规则用()封装成一个组,后续的规则可 以引用这个规则对后续的字符加以匹配判断。 如下是按照叠词切割字符串: Strings=&abceeedefgg&; Stringregx=&(.)\\1{1,}&;//其中\n 中的 n 代表组的编号(按照组的声明顺序来编号,最先 声明的为第 1 组) String[]arr=s.split(regx);功能 3:替换Strings=&abceeedefgg&; Stringregex=&(.)\\1+&; s=s.replaceAll(regex,&#&); 下面的要求是,将叠词只保留一个: Strings=&abceeedefgg&; Stringregex=&(.)\\1+&; s=s.replaceAll(regex,&$1&);//$是正则表达式里的特殊符号,这里,它引用前面的组(.),即 用相应的单字符去替换叠词。功能 4:获取符合相应规则的字符串Strings=&mingtinajiufangjia&; /* *将规则封装成对象 */ Stringregex=&\\b[a-z]{3}\\b&;//\b 代表单词边界 Patternp=Pattern.compile(regex); /* *让正则对象和要作用的字符串相关联,获取匹配器对象 */ Matcherm=p.matcher(s); /* *m.find()将规则作用到字符串上,但是它只是作用一次,找到了第一个符合规则的 *子串之后就返回了(布尔值) *m.group()用于获取匹配后的结果,前提是一定要确保正则规则已经作用要字符串了 *否则会引发异常 *配合以上两个方法,可有以下代码: */ while(m.find()){ System.out.println(m.group()); } /* *另外要注意的是,本质上字符串类的 replaceAll 和 matches 方法就是用了 Pattern *和 Matcher 这两个类。 *同一个匹配器,用的是一个指针,比如在上述代码中,我们先 m.matches();此时它作用 于 *整个字符串,只用当整个字符串符合规则,该方法才会返回 true。无论该方法返回什 么结果, *它都改变了匹配指针的位置,比如在此代码中先调用这个方法的话,它返回后(false), 指针 *会指向下一个单词的位置。如果我们接着调用 m.find,则它从第二个单词开始匹配。 */练习:将 ip 地址排序:/* *思路是: *1、先将所有的段值前边都补上两个 0, *2、然后将本身就是 3 位的段值(补上两个 0 后变成了 5 位)的前两个 0 去掉, *这时得到的所有段值都是等长的 3 位(这时候就算是按字典排序,效果也等同于 *按数值) , *3、然后将所有的 ip 切割出来,并存到 TreeSet 中去,则排序完成 *4、为了还原原来的 ip 形式,此时将补过的 0 去掉 */ Stringip=&192.168.1.12.1.3..1.100&; ip=ip.replaceAll(&(\\d+)&,&00$1&); ip=ip.replaceAll(&0*(\\d{3})&,&$1&); String[]arr=ip.split(&+&); TreeSet&String&ts=newTreeSet&String&(); for(Stringstr:arr){ ts.add(str); } for(Stringstr:ts){ System.out.println(str.replaceAll(&0*(\\d+)&,&$1&));/此处的正则写法很重要 //0*(\\d+)意思是匹配这样的子串:它以 0 个或多个 0 开始,后面是一个或者多个数字 //102 中的 0 之所以没有被替换掉,这是因为,它在匹配这个子串时(\\d++)匹配的是整 个 102 //(因为它前面没有 0) ,所以用自己替换了自己,不会将中间的 0 去掉。 //假如我们这样来写正则: 0+(\\d+), 则会将子串的 0 都替换掉, 因为它找子串的规则是: //以 1 个或多个 0 开始,并且后面是\\d+(一个或者多个数字)这时就找到了 102 中的 02, 然后 //就用 2 替换了 0,从而产生我们不想要的结果。 }4 Java 类的继承多态及内部类子类不能比父类抛出更“广泛”异常;字节输出流一般都没有缓冲。 我们在打印一个对象的时候如果输出为 null,有两种可能,一个是对象本身为 null(对象没有 覆写 toString 方法),另一种可能是对象的 toString 方法本身就输出 null。 3、局部内部类(比如在外部类的方法体中定义的类)不能被访问修饰符修饰,因为可以得 到外部类的引用 this 所以可以访问外部类的成员变量,但是不可以访问它所在的局部(该方法体)中的变量,也 不能访问该方法的参数 ,除非被 final 修饰。定义一个匿名内部类必须要求该类继承一个类或者实现一个接口,这 new 这个类或者接口的时候 从而提供实现。 4、关于多态的一点新发现(似乎和 C++不一样) :如果通过父类的引用(指向子类)调用子 类方法,则该方法必须是被覆写的方法。?? 即子类独有的方法不能通过父类的引用来调用。 静态的内部类可以直接作为一个普通类来使用,而不需实例一个外部类。 定义方法中的参数为 final,对于基本类型的变量,这样做并没有什么实际意义,因为基本 类型的变量在调用方法时是传值的, 也就是说你可以在方法中更改这个参数变量而不会影响 到调用语句,然而对于对象变量,却显得很实用,因为对象变量在传递时是传递其引用,这 样你在方法中对对象变量的修改也会影响到调用语句中的对象变量, 当你在方法中不需要改 变作为参数的对象变量时,明确使用 final 进行声明。5 StrngBuilder 和 StringBuffer单线程的情况下用 StrngBuilder 要比用 StringBuffer 的效率高些, 在多线程下用 StringButffer, 因为 StringBuffer 是线程安全的。6 Java 的类加载器系统有三个类加载器: 最底层的是系统类加载器 BootStartup 加载器-加载 jre\lib 下的 rt.jar 里的所有系统类。 (c/c++写的) 接下来是扩展类加载器 ExtClassLoader,加载 jre\lib\ext 里的所有 jar 文件(java 写的, 是 ClassLoader 的子类) 接下来是用户类加载器 AppClassLoader 加载我们自己的类。 (java 写的,ClassLoader 的子类) ,我们自己定义的类(class 文件一般在当前项目的 lib 下) ,都由它加载。通过一个 类 的 字 节 码 对 象 可 以 获 得 该 类 的 加 载 器 , 如 ( Person.class.getClassLoader() ) 。 BootStartup ClassLoaderExtClassLoaderAppClassLoader左边的三个加载器,采用父类委托机制。 通 过 Person.class.getClassLoader().getParent();可以 获得父加载器,但不能一直往上得到根加载 器,因为 sun 公司不允许对根加载器操作。 另外要注意的是,加载器的父子关系并非类 继承关系,而且扩展加载器类和 app 加载器 类的父类是 ClassLoader,它是一个抽象类。 如果我们要自定义类加载器类来加载远程类 (指定目录下的类文件) ,就要扩展这个类。 注意区别加载器和加载器类,加载器是一个 具体的加载器类实例,真下的工作是实例干 的。根据父类委托机制,如果我们自定义一个类,然后将其编译打包成 jar 放到 jre\lib\exi 下,这时我们再打 开 eclipse,引入该类的包声明后,我们就能直接使用该类了。如果我们再次在当前工作目录下定义一个 同名的类,我们在使用我们定义好的这个类的时候,因为是父类委托机制,子加载器将加载工作交给你 加载器完成,如果父加载器找到了目标类,就会加载,否则就去找父加载器的父加载器,如果最上层加 载器没有找到,则才接着向下路由任务。问题是我们已经将目标类放到扩展加载器的加载路径下了,所 以我们以为在使用自己的类,其实我们在使用扩展加载器给我加载的类,无论我在当前目录下怎么改我 们的代码,只要类同名,使用的就一定是扩展加载器加载的类,这就是父类委托机制。如果我们有两个 一模一样的类,但是是由不同的类加载器加载的,这样,二者就不会兼容,一者的 Object(可以这样获 得 Class.forName(“qzh.Person”).newInstance()这个类由 App 加载器(或者我们自定义的加载器)加载) 不能强转为另一者(假设有另一个 Person 类由扩展加载器加载并也实例了一个对象) 。7 tomcat 配置及网络相关当在地址栏中回车后, 先去 hosts 文件中去找域名, 找不到再去 dns 服务器上去找。 一个 Host 相当于 一个虚拟主机,其下可以配置多个网站(应用程序) ,一个域名一般对应一个主机,例如当 我们在 server.xml 中增加一个&Host/&元素,相当于增加了一个虚拟主机,此时可以为该主机配置相 应的域名 ,这个域名也遵从域名访问规则,所以我们如果只在&Host/&元素里配置了域名,而没有到 hosts 文件里 作映射到本机的话,则不能访问,localhost 默认是本机。配置好虚拟主机后,可以为该主机 指定应用 程序(网站)根目录,后续做的网站都放在这里,可以放一个 Root 根网站在这里,在该网 站里做一个 index.html 则默认为整个虚拟主机的主页。 tomcat 下 web 应用配置详解:一般情况下,如果我们将 web 应用直接发布到 tomcat 的 webapps 下,tomcat 则直接就为我 们映射好了虚拟路径,不用再配置,但实际应用,我们一般都要手动配置。 首先要明白这样的体系结构: Engine(默认是的引擎名称是 catalina) -----Host(默认的主机名称是 localhost,它默认的地址即本机回环地址 127.0.0.1) ----Context 一个引擎下可有多个虚拟主机,如 localhost,www.book.com 等。 一个虚拟主机下(也叫网站)可以有多个 web 应用。 通过修改 server.xml 文件,可配置默认的 80 端口。如下操作都假设已经配置好了 80 端口。 一:在默认的引擎和主机下,配置默认的 web 应用: 达到的效果是:输入 http://localhost,即可访问 book web 应用,假设映射名称和项目名都 为 book,且放于 c 盘根目录下。 配置方式 1:在 server.xml 中增加: &Context path=‖‖ docBase=‖ c:/book‖/& 配置方式 2:在 conf/catalina/localhost,下建一个 Root.xml,在其中配置: &Context docBase=‖ c:/book‖/& 注意,这种配置方式是最佳实践,除非我们配置了 localhost 默认的 web 应用,否则不用重 启服务器(因为我们的配置覆盖了 tomcat 自身的默认 web 应用) ,通常我们可以用这种方 式配置一个 web 应用,xx.xml,其中如果 xx 不是 ROOT,则就为了一个 web 应用配置好 了一个虚拟映射路径 xx,以后便可这么访问:http://localhost/xx(假设默认页面已经配置, 即已经配置好了主页) 二:配置虚拟主机(即自定义网站)下的 web 应用配置: 达到的效果是:输入 www.book.com 即可访问 c:\myapps\下的 book 项目。 1、在 server.xml 中新增一个虚拟主机:虚拟主机对应的硬盘目录是 c:\myapps,它里面可 以放置该网站的多个 web 应用。 2、为虚拟主机配置 web 应用:注意:一定要在 C:\WINDOWS\system32\drivers\etc 下的 HOSTS 文件中作好本机地址与 www.book.com 的映射,毕竟这个地址未注册到 DNS 上。 (浏览器拿到一个地址后,会先查 HOSTS 中的映射,后查 DNS 中的映射) 可看出,该网站下的默认 web 应用的配置方式是通过“配置方式 1”来配置的。 配 置 方 式 2 : 如 果 想 在 自 定 义 的 主 机 下 配 置 默 认 的 web 应 用 , 则 不 仅 要 在 conf/catalina/www.book.com 下增加一个 ROOT.xml 文件, 还要把 C:\myapps 下的默认应用 的发布名字改为 ROOT8 javaScript 及 DHTMLjavaScript 中的函数参数不需要 var 关键字限定,否则出错。java 里的正则表达式匹配与 javaScript 有个不同之处是前者在匹配的时候, 默认是匹配整个字符串, 必须要求整个字符串都符合规 则才行, 而后者在匹配字符串的时候,它是这样,如果你不指定匹配的开始和结束的话(能过^和$) , 它会 在整个字符串里查找,只要能找到符合规则的子串,就会匹配成功。 span.style.display 的值为 inline 则 span 里的内容与上一个元素处于同行,若为 block 则作为 单独的 元素在下行显示。 event 对象 代表事件状态,如事件发生的元素,键盘状态,鼠标位置和鼠标按钮状态。例如我们为某个 元素配置了 一个 onXXX 事件的响应方法,则在该方法体中,则 event 能获取当前元素的相关信息, 如, event.srcElement 则能获得触发该事件的元素, 即当前元素。 event.retrunValue 若置为 false 则 能将元素的默认行为取消。 每个 div 默认占一行。 div 之所以不能放在 p 标签里,因为 p 是段落标签,里面的所有内容都在同一段落,能在同 一行 就在同一行,div 为行级元素,一个 div 默认就要占一行。 document.forms[0]拿到当前页面中的第一个表单 div 盒子模型:通过控制 div 的 border-top 等属性可以控制一个 div(盒子)相应边框的大小。 控制数据在盒子里面的位置:Padding-控制盒子间的位置 Margin-left 等。 要改变 div 默认的行级,则通过浮动定位(float)技术(用 css 控制)可实现将多个 div 排 在一行上。也 可以通过 position 定位技术。 跟着浮动。position 的相对(relvative)定位技术使得被定位的 div 没有脱离文档流,absolute 绝对 定位技术则脱离了。默认定位技术都是相对浏览器,如果想通过 absolute 让子 div 相对于父 div 来 absolute 定位,父 div 则必须要设置 Position:relative. 整个网页要通过一个 div 套起来。通过设置 body 标签来完成整个网页的全局设置,如 text-align:center 整个网页即最大的那个 div 的宽度最好设置为 960-1002.。9 与 JSP 和 Servlet 相关的零碎知识ServletContext 中的 getRealPath(&/&)获得当前项目的真实路径 在 web.xml 为 ServletContext 配置参数(如编码方式)则对整个项目都有效。 sendRirect 不能重定向到 WEB-INF 的 jsp 而请求转发则可以。 tomcat 之所以能显示 404 是因为为某个 servlet 配置成了默认了的将 url 配成/别人不处理的, 我都处理。 解决 servlet 线程安全问题的三种方式: 如果下载的文件名有中文,则应当用 URL 编码。 xml 各种方式的实验对比。 servlet.apisun 公司规范的接口。 以 get 方式向服务器发请求,没有请求类型头:content-type 为 null。 刷新是重复上一次操作。比如,如果不处理,付过钱以后,刷新的话又会再一次付款,这种 情况当处理。 post for(;;) Objecto=newObject();//因为虚拟机的特殊性,这样是不能执行的。必须要加大括号。 当第一个用户的请求到来时,会创建一个相应的 request,这时会首次创建相应的 servlet, 这个 servlet 只创建一次,以后再有请求到来时不再创建这个 servlet,但是对用户的每一次请求都 会 创建 request。 向服务器上传数据必须使用 post 方式。我们也可以直接访问处理上传的页面(正常的访问 应该是在相应的 jsp 页面选择好要上传的文件提交后才会访问这个处理上传的 servlet) ,此 时就是以 Get 方式向服务器发送请求,此时会执行处理上传的 Servlet 的 doGet 方法,此时 用户上传过来的数据只包括一些头信息或者用户在浏览器输入的一些附加参数, 这没有什么 用,我们应当处理用户的这种上传方式,即禁用 get 上传方式。10 有关输入输出流我们在 newFileoutputStream()的时候可以给它传递路径名,也可以传递一个 File 对象, File 对象在构造的时候也可以传递路径名。通过这两种方式来 new 一个 FIleouputStream 看 上去 没有什么不同,其实有很大的差异。比如,如下代码: publicclassXMLUtils{ privatestaticDocumentdocument= privatestaticFilefile= static{ Stringpath=XMLUtils.class.getClassLoader().getResource(&file.xml&).getPath(); file=newFile(path); SAXReaderr=newSAXReader(); try{ document=r.read(file); }catch(DocumentExceptione){ //TODOAuto-generatedcatchblock e.printStackTrace(); } } publicstaticDocumentgetDocument(){ } publicstaticvoidsave(){ try{ OutputFormatformat=OutputFormat.createPrettyPrint(); XMLWriterout=newXMLWriter(newFileOutputStream(&file.xml&),format); out.write(document); out.close(); }catch(Exceptione){ thrownewRuntimeException(e); } } } 这段代码是有问题的,在 save 方法,创建输出流的时候传递的参数是一个文件名也即一个 路径名,当 tomcat 在执行这段代码的时候,它会在在 tomcat_home/bin 下重新创建一个 file.xml,这也是 FileOutputStream 的特点,你如果指定了绝对路径,我就用你的绝对 路径上的文件来作为目的,如果没有指定,我则创建一个目的,这个目的则创建在当前 路径下。对 tomcat 来说,当前路径是 tomcat_home/bin 下。 想要避免这个问题, 就要用 File 对象来作为 FileoututStream 构造的参数, 因为在静态代码块 中已经指定了 file 对象,它是不变的,所以后来在输出的时候,仍然是以它作为目的。 产生这个问题的根本原因是你不指定路径, 就以当前路径为参考, 先看看当前路径下有没有 这 个文件,如果有,则直接以其为目的输出数据,如果没有则创建一个。 这个现象也告诉我们,尽管我们的 web 应用发布到服务器中,对于我们的 servlet 等与 web 相关的类来说,当前路径是 web 应用的根路径,但是对于发布到服务器的普通 java 类 来说 当前路径是其调用者所在的路径,tomcat 的调用程序就在 tomcat_home 下的 bin 目录下。它 在执行 到 newFileOutputStream(&file.xml&)的时候,会在自己的当前路径找,当然找不到,所以就新 建一个。 newFile(&d:/a&)如果路径不存在,则会创建一个 File 对象,但不会在硬盘上创建相应的 目录,与 newFileOutputStream(&a.xml&)不同。要注意这一点。如果想创建则则调用 File 的 makeDir11 JavaScript 的学习总结: 1、语法相关JavaScript 是 NetScape 公司的产品。这是基于对象的,基于事件驱动的,弱类型的语言。 document.write(something);//将内容直接写到浏览器中。 定义数组的方式:var arr=[1,2,3,4];此时数组尽管只有四个元素,但在 javaScript 里边数组相 当于集合,是可变长度的,我们如果访问 arr[4]及以后的元素,则得到的就是 undefined。另 外数组里可存放任意不同类型的元素。 函数的参数是没有类型的。 函数尽管可以返回一个值, 但是定义函数的时候是不用指定返回 类型的。函数没有重载形式,即如果你定义了一个有两个参数的函数,在调用的时候如果只 传递了一个,也能照样执行,没有值的参数为 undefined。再如果我们传递的参数多于定义 函数时的参数,则函数照样能接收到,因为函数内部有一个数组用来接收参数的。叫 arguments。 &script type=”text/javascript”& function show() { For(var x=0;x&arguments.++x) { Alert(arguments[x]);; } } Show(1,2,3,4);//调用 &/script& 函数本身在 javascript 里面就是一个对象。函数名就是对象名。函数名指向这个函数对象。 如果我们在调用一个有返回值的函数将这个返回值赋给一个变量的时候,如果忘写了(),则 相当于将这个变量指向了这个函数对象, 如果打印这个变量的话会发现打印出来的是整个完 整的函数定义。 匿名函数: Var show=function()//此函数虽然没有名字,但是已经有引用指向它了,所以可以说是有名 函数 { Alert(Dhaha‖); } 再如: Window.onload=function() { Alert(D启动完毕‖); } 动态函数本身也说明了函数是一个对象: &script type=”text/javascript”& Var show=new Function(“x”,”y”,”var sum=x+y;”); Show(1,2);//调用 &/script& 正是因为 javascript 是基于对象的,所以我们可以通过函数的方式来创建一个类。 函数和 this 关键字: 在 js 里,函数和方法还是有区别的,当我们将一个函数赋值给一个对象的属性时,我 们称该对象具有了一个方法。另外,在 js 的函数体,我们可以通过 this 来引用调用它的对 象,即它的所属者。如: &script type=”text/javascript”& function f(){ this.i=100; } &/script& 这个函数很简单,为调用者添加了一个属性 i,并为其赋值为 100。那么到底为哪个对 象添加了这个属性呢。其实,仅仅是定义一个函数,它不会有任执行效果,当我们调用的时 候,它才会执行。如果我们直接调用: f();则它在执行 this.i=100 的时候,直接就将 i 这个属性添加到了全局对象当中 window 中,可以通过 alert(window.i)查看。这是因为,this 永远指向该函数的调用者,全局调用自 然就指向全局对象 window。 还有一种情况: var o=new Object(); o.m=f; o.m();//这是至为重要的,要不然是不会执行方法的 this.i=100 这条语句,自然也就 不会为其添加属性了。 alert(o.i); 因为这是在对象 o 上调用,所以 this.i=100 等于为 o 添加了一个属性。12 关于上传和下载/* 下行代码 * 相当于request.setCharacterEncoding(&UTF-8&);可以防止提交过来的 中文文件名乱码 * 但是对表单域里的数据无效, 即如果表单域里的中文编码方式仍未指定, 可能乱码。 */ sf.setHeaderEncoding(&UTF-8&); try { List&FileItem& list=sf.parseRequest(request); String separator=System.getProperties().getProperty(&file.separator&); String folderPath=getServletContext().getRealPath(separator+&uploads&); File resDir=new File(folderPath); if(!resDir.exists()){ resDir.mkdir(); } for(FileItem fi:list){ if(fi.isFormField()){ System.out.println(fi.getString(&UTF-8&));//专门限制 表单域里提交过来的数据的编码方式 }else{13 Cookie & Session会话跟踪技术:cookie 和 session,前者保存在客户端后者由服务器端管理。1、CookieCookie 保存的时间通过设置 setMaxAge 来设置(默认值为-1,关掉浏览器就没有了) 如果大于 0,就表示在客户机的硬盘上保存 N 秒。 如果小于 0,就表示不将 Cookie 保存到客户机的硬盘上,当浏览器关闭时,Cookie 当即 消失。 如果等于 0,就表示删除保存在客户机上的 Cookie。 cookie.setPath(“/”);设置 Cookie 的有效使用域。默认为当前 Servlet 所在的目录。 ? 设置为/则整个 tomcat 有效。 ? 设置为/day07 即,整个 day07 项目有效。 Cookie 的 key 和 value 不能是中文,如果要使用中文必须要 URL 编码:String address = &中国北京&; address = URLEncoder.encode(address, &UTF-8&); Cookie c = new Cookie(&addr&, address); c.setMaxAge(60 * 60); c.setPath(request.getContextPath()); response.addCookie(c); 读取的时候也要相应的 URLDecoder. javaScript 也能对 Cookie CRUD 删除删除 Cookie: 删除 Cookie 时,除 value 以外的信息必须保持完全一致(关于路径,可以不一致,但要保证 新的路径是之前路径的子目录),否则会导致删除不成功。代码1: Cookie c1=new Cookie(&boy&,&jack&); c1.setMaxAge((60*60));//我们要明白一点:我们设置的这个时间是给相应的 //web参考的,假如我们以后再了不去访问该网站了,则cookie就算过期了也不会被删除掉的 c1.setPath(request.getContextPath()); response.addCookie(c1); Cookie c2=new Cookie(&girl&,&rose&); c2.setMaxAge(60*660); c2.setPath(&/&);//该路径是整个网站(虚拟机)与上面的路径(当前应用)不一 样,所以又客户端 //又创建了一个Cookie文件。 response.addCookie(c2); String address =&中国北京&; address=URLEncoder.encode(address, &UTF-8&); Cookie c3=new Cookie(&addr&,address); c3.setMaxAge(60*60); //没有setPath则默认路径是当前servlet所在路径 response.addCookie(c3); 代码 1 创建了三个 Cookie,路径分别不同。 代码 2: Cookie[] cs=request.getCookies(); if(null!=cs){ for(Cookie c :cs){ c.setMaxAge(0); c.setPath(request.getContextPath()); response.addCookie(c); } } 代码 2 只能将路径为 request.getContextPath()或者其父级路径的 cookie 删除。 不能删 除路径为 addr Cookie,因为它的路径比 request.getContextPath()深。如果将代码 2 的 c.setPath(request.getContextPath()); 删 除 , 则 默 认 设 置 的 路 径 为 当 前 页 面 (servlet)的路径,因为代码 1 所创建的 Cookie 的路径都大于等于这个路径,所以可以将 代码 1 创建的 cookie 都删除。读取 我们只能 getName getValue,因为安全性不能 getPath(). 写DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD Cookie c1=new Cookie(&name&,&jack&); c1.setMaxAge((60*60)); c1.setPath(request.getContextPath()); response.addCookie(c1); Cookie c2=new Cookie(&name&,&rose&); c2.setMaxAge(60*660); c2.setPath(&/&);//该路径是整个网站(虚拟机)与上面的路径(当前应用)不一 样,所以在客户端 //又创建了一个Cookie文件。(若路径一样,则多个键值对就会被写在一个cookie 文件中,也即多个cookie被写入一个cookie文件中――这是针对iE浏览器来说的,谷歌只有 一个文件保存所有的Cookie)。 response.addCookie(c2); DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD Cookie c1=new Cookie(&name&,&jack&); c1.setMaxAge((60*60)); c1.setPath(request.getContextPath()); response.addCookie(c1); Cookie c2=new Cookie(&name&,&rose&); c2.setMaxAge(60*660); //此处没有指定path则默认为当前servlet页面。 //又创建了一个Cookie文件。 response.addCookie(c2); path 越高级则被别的页面访问的权利就越大。即在细致的页面能访问父级页面的 Cookie。如 以下代码能将路径为当前页面所在路径或者当前页面路径的父路径的 Cookie(路径通过 getPath 可以获得当初在保存这个 cookie 时所设置的路径)全部删除: Cookie[] cs=request.getCookies(); if(null!=cs){ for(Cookie c :cs){ c.setMaxAge(0); c.setPath(c.getPath()); response.addCookie(c); } }2、SessionSession 依赖于 Cookie,但是并不完全依赖,因为可以重写 URL。 当用户第一次访问本应用时, 只要被访问的页面执行了代码 getSessiona (通过查看任何一个 jsp 页 面 翻 译 成 的 java 代 码 可 以 看 出 , 作 为 隐 式 对 象 之 一 的 session 是 在 调 用 pageContext.getSession()的时候创建的。,就会为用户创建一个 session,创建后如果再次访 ) 问 getSession,则不再创建。在没有关闭浏览器的情况,会根据 getLastAccessTime()来为用 户保存 30 分钟,然后会销毁这个 session。当用户关闭了浏览器,在服务器端也会保存相应 的 session30 分钟。可以在 web.xml 配置 session 的有效期。 关于 session 到底是何时创建的,我们很容易可以证明,写一个 HttpSessionListener,在sessionCreated 方法里随便输出一句话,如果输出了,则说明 session 确实被创建了。然 后我们准备两个页面,一个 jsp,一个 servlet,在 servlet 里我们什么也不做,我们已经 知道访问 jsp 肯定会调用 getSession 方法,所以当我们访问 jsp 页面的时候会发现语句输 出了,当我们直接去访问这个 servlet 的时候,会发现语句并没输出,即 session 并没有被 创建出来,然后我们在这个 servlet 里添加:request.getSession(),再次直接访问这个 servlet 页面,会发现语句输出了,说明 session 确实是在调用 getSession 方法的时候才 被创建的出来的。一般情况下我们都是通过 jsp 转到一个 servlet 的,这时候与用户相关的 session 已经创建好了, 所以我们在 servlet 里得到这个 session 的时候可以这样写以提高 效率:request.getSession(false);这句代码的意思是如果已经存在 session 直接将这 个 session 返回就行了。Session 是基于 Coolie 的。当用户访问本站第一次创建 session 的时候,服务器会为其创建一 个 cookie,key 为 Jsession value 为一个 id 号 path 为当前应用。但是这个 cookie 在回写的时 候没有设置有效期,所以导致用户在退出浏览器的时候,这个 cookie 就没有了,下次打开 浏览器访问的时候(尽管在 30 分钟(默认)以内服务器端内存里还保存着这个 session)没 有带 id 过来,所以又会为用户创建一个会话。在购物网站里,这个问题必须要解决,解决 办法是手动回写一个一样的 cookie,但是设置有效期(30 分钟,过长没有意义) 。 但是如果用户禁用了 cookie,如果不采用 URL 重写,因为 Session 是基于 Cookie 的,就算 用户不关闭浏览器,也无法为用户创建一个浏览器进程周期内的 cookie,所以此时用户购买 了书,就算不关闭浏览器,这时去查看自己曾买过的商品也看不到,本质原因就是因为 Session 是基于 Cookie 的。URL 重写的原理:为每个超链接 response.encodeURL(url)一下, 返回的 url 后面就会带上 session 的 id 号,则用户点超链接进到另一个页面的时候就会带着 这个 id 号。对同一个用户,所有重写过的 url 后面跟的 id 号是一样的,不同的用户则不同。 尽管如此,URL 重写并不能解决关闭浏览器 30 分钟内再次访问还能看到商品的问题。 当用户第一次访问能够创建 session 的代码的时候, 服务器并不知道用户有没有禁用 Cookie, 它先会将 session 的 id 号以 cookie 的形式重写,并且也会 URL 重写。当第二次来访问的时 候(刷新,关闭的话,保存 session id 号的 cookie 就挂了,我们并没有手工设置这个 cookie 的有效期) ,此时服务器已经知用户有没有禁用,如果禁了,则仍采用 URL 重写,否则就直 接使用 cookie 里的 id,被重写过的 url 后面也就不会有 id 了(通过查看网页源代码可以看 出来) 。 其它细节:同一个浏览器的不同的选项卡共用同一个 session。弹出的新窗口也会与以前的 窗口共享同一个 session。 在同一台电脑上,每当打开一个新页面时,对 IE6 、7 来说,每次在同一台电脑上打开浏览 器都为在内存里新建一个进程,所以相当于一个新的用户,彼此不共享 Session。但是对于 火狐和高版本的 IE 浏览器来说,打开多个浏览器窗口它们是共享内存的,所以共享一个 Session。 访问服务器页面时,如果 url 后面有分号;+ID,服务器会拿你指定的这个 id 值去服务器内存 中查找,如果已经有了这个 id 的会话,则直接用这个会话与用户交互,如果没有则服务器 为你创建一个你指定 id 的 session。 如果我们在访问网站的时候不在地址栏里指定 session 的 id,则服务器会读取保存在客户端的 Cookie 里的 session 的 id,如果找不到则为你创建一个 session。 防止别人拦截 session 的 id(它是 sessio 的唯一标识,可以用 getID 演绎法获取) ,可以为用 户提供一个安全退出。14 代码风格?技巧以前经常看到有人写代码作判断的时候这样:if(null= =var);if(2= =var);if(“aa”.equals(var));现 在才明白, 原来之的以把不能作为左值的常量入在比较的左边是因为这样可以防止将等等于 号写成赋值的等于号,真写成了,因为常量为左值会在编译期就提示错误。对于字符串常量 与变量比较,这样可以防止空指针异常。15 JSP? ? ? ? JSP 指令。 ? &%@ 指令名 属性=“值”%& JSP 声明。成员变量,或是成员方法. ? &%! ?. %& Java 程序片段。不论有多少这样的代码块,都将包含_jspSerivice 方法中. ? &% ? %& Java 表达式。-快速输出信息. _jspSerivice ? &%= ?%&contentType=”text/charset=UTF-8” 不仅能设置当前 jsp 页面的编码也能设置输出流的编 码,比 pageEncoding 强。 &% %&里的 java 代码都在翻译后的_jsgService 方法里,所以它中不能再定义一个方法。如 果想声明成员方法和成员变量: &%! %&应该声明在它里面。 如果在 jsp 页面将 session 设置为 false,则不能 getSession 了但是能往 session 里添加数据, 可是不能用 el 表达式取出来。 静态包含,将主页面和被包含的页面一起转成一个 java 类,所以这两个页面可以共享变量。 尽管共享变量的时候会有编译错误,但是运行没有问题。&%@ include file=”url” %& 动态包含:本质使用的是转发技术,所以不能共享变量,但是共享同一个 request。会将包 含与被包含的页面翻译成两个 java 类。&jsp include包含技术主要用在为网页做页头面脚。隐式对象: ? request ? response ? pageContext C javax.servlet.jsp.PageContext ? application ? out C javax.servlet.jsp.JspWriter ? session C session=false. ? exception - 只有当 isErrorPage=true 时才会拥有此对像iframe 隐藏帧: 16 MVC及三层框架:mvc 层对应着框架 struts2.。服务层和数据访问层不属于 MVC 的范畴,它们分别对应着框架 Spring 和 Hibernate。工具类不属于任何层,它一般只被数据访问层和 MVC 层的 controller 使用。 我们为服务层声明一个 dao 成员变量的时候, 称为注入 (依赖注入 DI)我们为 controller 。 声明一个 Service 的时候,因为 Servlet 是单例的,所以它的成员每一个也只有一个实例,从 而 service 里的成员变量(dao)也只会有一个实例。因为 controller(servlet)由服务器维护 管理, 所以我们为其定义的成员变量及成员的成员变量都由服务器来维护, 所以这样就增加 了服务器的负担, 日后学习的 srping 技术可以解决这个问题。 另外, 我们目前也可以将 servic 定为 controller 的局部变量也行。 17 自定义标签TagSupport 类对应的标签只支持属性,不支持 body,即只是原样输出。 BodyTagSupport 要处理体内容必须在 doEndTage 中处理,因为这时才接收到体内容,否则 会有空指针异常。 当我们在一个 jsp 页面使用自己的(或者 JSTL)标签时,在当前页面翻译成的 java 代码里, 会将当前页面的 pageContext 传给自定义标签类的 setPageContex()方法,从而就共享了 pageContext。 (翻译成的 java 代码中有一个我们自定义标签类中的对象) 。 18 数据库 关于表的各种 join:1、等值连接:select * from customers,orders where customer.id=orders. 2、自然连接:select customers.id,customers.name from customers,orders where customer.id=orders.因为去掉了重复的属性列(customers 中的 id 和从 orders 中选取的 id)所以是自然连接。 3、内连接:select * from customers,orders where customer.id=orders.这跟等 值 连 接 的 效 果 是 一 样 的 ; select customers.id,customers.name from customers,orders where customer.id=orders.这跟自然连接的效果是一样 的。 4、外连接: 外连接分左右, 一般说的左连接和右连接指的就是左外连接和右 外连接。 使用外连接的时候必须指定左右, 这样的语句是错误的: SELECT * FROM customers c OUTER JOIN orders o ON c.id=o.。 5、左外连接:对 cutomers 和 orders 两张来说,一个 customer 可能没有订单, 一个订单也可能没有对应的 customer。左右又是相对于什么来说的呢。对 于下面的 sql 语句: SELECT * FROM customers LEFT OUTER JOIN orders ON customers.id=orders.左边的表就是 customers, 右边的表就是 orders。 所以上面的就是左连接查询, 效果是: 拿着左边的表的一条记录与右边表 的所有记录逐一匹配,如果符合条件(customers.id=orders.id)则生成一 条记录放到结果集中,如果不符合,也会生成一条记录,其中左表的相关 字段依然保留,而右表的字段都置空,也就是以保留左表为主。上述左连 接 的 结 果 :右 连 接 : select * from customers.id=orders.idcustomers 的left outer 结join orders 果on :6、右外连接:无非是换个方向考虑而已,跟左外连接没有本质区别。 7、全外连接:当条件不成立的时候,等号两边的表都会显示。 关于外连接的另一种写法: Oracle:系统学习:前提知识&基本概念:orcl 是整个数据库名称(gloable database name,可以设置 password) ,可以有多个数据库, 每个数据库下有若干用户,每个用户下才是具体的表。orcl 下的用户有 SCOTT(默认密码 tiger) ,HR(默认密码 hr)等,每个用户下对应着若干表。刚安装时,默认,这些用户是被 Lock 的,要注意在安装过程的选项里解锁,也可以安装后在命令行用语句解锁。 一个 Orcale 服务器:是一个数据库管理系统(RDBMS) ,由一个 Orcale 实例和一个 Orcale 数据库构成。实例是逻辑概念,数据库则是硬盘上实实在在的文件。我们都是通过实例去操 作数据库的,硬盘上的文件是数据库,读到内存中便是实例,任何时刻一个实例只能与一个 数据库关联,一个数据库上只能有一个实例对其操作(假如不考虑 RAC 即集群的情况) 。 RAC:real application cluster,目标:负载均衡(Load Balance) ,失败迁移(Fail over) 。基本操作:1 解锁用户:sqlplus sys/alter user alter user scott2spool 录屏到 txt:spool c:\录屏.txt,接下来,在 sqlplus 命令行一系列操作后,再 spool off,便会写到 txt。3 显示当前登录用户:show user4 显示当前用户下所有的表:select * from tab,tab 是数据字典表。5 查看一个表的信息:desc emp6 设置行宽:set linesize 1507 执行上条 sql右斜线/8 为字段设置列宽:col(umn) ename for(mat) a8,col sal for 9999。9 设置显示的行数:set pagesize 2010host cls 11distinct作用一列:select distinct deptno from emp。 作用多列:select distinct deptno ,job emp。//只有后面两个都一样才认为重复。12between..and..含边界,小值在前,大值在后。 模糊查询 like: 查询名字以 s 开头:select * from emp where ename like ?s%?。 查询名字为 4 个字符:select * from emp where ename like ?----?。 查询名字含下划线的员工:select * from emp where ename like ?%\_%? escape ?\?。13order by后面可以跟列名,别名,表达式,列名所在的序号: select * from emp order by 3 desc(按照第 3 个字段降序)。14 主机认证的方式登录:sqlplus / 后面如果跟多列, 则先按第一个字段排, 然后在第一个字段相同的前提下, 在排第二个字段…. (默认是升序,如果一个字段要显示指定排序方式,必须要在该字段后紧跟 asc 或者 desc, 与 distict 不一样) : select * from emp order by empno desc,deptno desc。 null 值:升序没影响,降序时:select * from emp order by comm desc null last。null 值总结之一:包含空值的算术表达式仍为空(用虑空函数 nvl(可能为空的字段,值)解决,将空值转我 们想要的值) ,null!=null(解决办法:用 is 或者 is not)。字段别名:select empno as “员工号” ,ename “姓名” ,sal 月薪有没有 as 都行, 有无双引号的区 别在于:如果别名里有特殊字符,则必须要有双引号,否则是错的。伪表 dual:为了满足语法和便于测试。字符串的拼接:select ‘hello’|| ?world? from dual《=》select concat(?hello?,?world?) from dual。字符串:日期和字符串都被作为字符串看待,都要加??。设置日期(或其他类型字段)的格式:查看系统一些数据类型的格式:select * from v$nls_parameters。 更改日期格式: alter session set NLS_DATE_FORMAT =”yyyy-mm-dd”。session 意味只对本次会话有效,也 可以是 global,但得是管理员。函数:单行函数只操作一行, 有字符函数, 数值函数, 日期函数, 转换函数, 通用函数 (如虑空函数 nvl (a,b)。 ) 字符函数: 大小写控制函数-lower,upper,initcap: 字符控制函数: 取第四个字符(含)以后的所有 取第四个字符(含)后共三个字符。查找子串的出现位置。 去掉前后指定的字符。数值函数: round(19.26,1)=19.3,trunc(19.26,1)=19.2 日期函数: 计算工龄: 方式一(粗略) 方式二 通用函数: nvl2(e1,e2,e3)若 1 为空则返回 3,否则返回 2。 nullif(e1,e2)若相等则空,否则则 e1。 coalesce(e1,e2,e3,…..en)找到第一个不为空的。sql 语句中的条件操作:sql99 操作: select ename,empjob,sal 涨前薪水,case empjob when 'PRESIDENT' then sal+1000 when 'MANAGER' then sal+800 else sal+400 end 涨后薪水 orcale 操作: select ename,empjob,sal 涨前薪水, decode(empjob,?PRESIDENT?,sal+1000,?MANAGER?,sal+800,sal+400) 涨后薪水多行函数(组函数) :对一组数据返回一个值:agv,count,max,min,sum 等。组函数会自动虑空,如 select count(*) from emp 返回的个数与 select count(comm) from emp 返回的个数不一定一样, 因为 comm 为空的话,会被组函数虑空,可以这样避免虑空 count(nvl(comm,0))。null 值总结之二:select count(*) from emp 返回的个数与 select count(comm) from emp 返回的个数不一定一样, 因为 comm 为空的话,会被组函数虑空,可以这样避免虑空 count(nvl(comm,0))。分组 group by:使用注意:所有没有包含在组函数中的列,必须包含在 by 后: select a,b,avg(c) group by a,b。但 by 后的,并非一定要出现在 select 后。 作用于多列: 先按第一列分组,若相同,再按照第二列分组,以此类推。另外,having 条件子句只能用于 group by 后。 例 1 找出每个部门各职位的平均工资: select deptno,empjob,avg(sal) from emp group by deptno,empjob order by deptno,可以看出,组 函数 avg 是在分组后对每个最终小组进行的 sal 进行统计的,而这正是我们想要的。 例 2 找出每个部门平均工资大于 1000 职位: select deptno,empjob,avg(sal) from emp group by deptno,empjob having avg(sal)&1000;group by 语句增强:例 1:原理解释:group by rollup(a,b)达到的分组效果是:先 group by a,b,然后对分组后的结果, 再 group a,再对分组后的结果 group by null(即每个记录自为一组,不分组) 。即: group by rollup(a,b)= group by a,b+ group by a+ group by null。 即右边三个集合的并就等于左边。 体现在该例中便是: select deptno,empjob,sum(sal) from emp group by rollup(deptno,empjob)= select deptno,empjob,sum(sal) from emp group by deptno,empjob + select deptno,sum(sal) from emp group by deptno+ select sum(sal) from emp group by null。 最终的语句是: break on deptno skip 2; select deptno,empjob,sum(sal) from emp group by rollup(deptno,empjob) 可以这样来理解执行过程: 1)首先找出各部门每个工作的总工资: select deptno,empjob,sum(sal) from emp group by (deptno,empjob) 执行结果如下:2) 可以看出, 在以上基础上, 对以 deptno 分组, 便可得 3 组, 假设有一个如上的表名为 tmp1 (第三个字段不妨叫 job_total_sal)那么可以这样统计出每部门的总工资: select deptno,sum(job_total_sal) from tmp1 group by de 3)要想统计所有的工资总合,很简单,不用分组直接统计即可。 所以,可以看出 group by rollup 的强大之处,相当于在做集合的运算。 break on deptno skip 2;意思是后续的查询中,deptno 多行相同的话,只显示一次,显示下一 个不同的 deptno 前空两行。可以通过 break on null 来恢复取消设定。where 和 having:where 不能跟组函数,having 只能用于 group by 后。 二者是有差异的, 配合 group by 操作时, where 是先过虑再分组, having 是先分组再过虑。 而 尽量使用 where。等值连接和不等值连接:体现在语句中,就是两张表靠字段用”=”连接,不等值连接,符号是&、&或者 between..and..。 不等值连接的例子:两张表,salgrade(grade,lowsal,highsal) 和 emp 表。查出每个员工的工资 级别: select e.ename,e.sal,s.grade from emp e,salgrade s where e.sal&s.losal and e.sal&s.hisal外连接:等值连接中隐含的问题:比如,我们要查出每个部门的人数,用等值连接的话是这样: select d.deptno,d.dname,count(empno) 人数 from emp e,dept d where e.deptno=d.deptno group by d.deptno,d.dname order by 1 因为等值连接用的是等号,所以如果员工表中某压根没有某部门的记录,现实情况中,该部 门为 0 人,但等值连接在作笛卡尔积的时候,因为不满足条件,所以结果表中也没有把没有 员工的部门连接进来。 有时,某些不满足条件(比如 e.deptno=d.deptno)的记录,也要连接到结果表中,这时,便 可使用外连接。 左外连接&右外连接: 当条件不成立的时候,等号左边的表的信息仍然显示在结果表中,写法: where e.deptno=d.deptno(+) 图示:可见,要想查出所有部门的员工数,使得人数为 0 的部门也显示,那么就要使用右外连接了 (如果等值条件表达式如此:e.deptno=d.deptno,如果相反,当然就要使用左外连接了)即 e.deptno(+)=d.deptno。最终在按部门号统计结果表的员工人数的时候,会根据组函数的虑空 特性,统计得 4 号部门人数为 0。 完整语句: select d.deptno,count(e.empno) from emp e,dept d where e.deptno(+)=d.deptno group by d.deptno 左外连接的情况道理相同。 全外连接: 条件不成立,两边都显示,写法:where e.deptno(+)=d.deptno(+)。 自连接: 对于一张表来说, scott 下的 emp 表, 比 每个员工都有一个 MGR 号, 每个员工的老板 (MGR) 也是员工表里的员工,要查出每个人的老板的名字,则要用到该表的自连接。 原理:通过表的别名,将一张表视为两张表。 select a.ename || '的老板是' || b.ename from emp a,emp b where a.mgr=b.empno层次查询:自连接操作只适合小表,大表效率低。 通过上面自连接的查询我们知道,员工 Simth 的老板是 Ford,Ford 的老板是 Jones??这样 下去,其实形成的一种关系是树形结构:当然这种结构是表中各记录之间的关系决定的,表本身有自引用的字段。 起始字段为树根 7839,前一次操作的员工号等于后一次操作的老板号。遍历一棵树必须要 起始条件即树根。level 是伪列。 select level ,ename,empno,mgr from emp connect by prior empno=mgr//在结果表中可看出前一条记录员工号等于后一条记录老板号 start with mgr is null //从没有父结点的记录起始即根记录,事实上可以从任何记录开始 order by level子查询:主 查 询 后 的 group by 后 不 能 跟 子 查 询 , 子 查 询 的 结 果 可 以 看 作 一 个 表 , select,where,from,having 都可以跟子查询。主子查询可以不是同一张表。select 后只能跟单行 子查询,即子查询只返回一条记录。单行操作符后也只能跟单行子查询,如&,&=等。多行操 作符如,in ,any,all 等。any 的使用:查询工资比 20 号部门任意一个员工的工资高的员工信息: select * from emp where sal & any (select sal from emp where deptno=20); 事实上,大于集合的任意一个值就等价于大于集合中的最小值,故上句等价于: select * from emp where sal &(select min(sal) from emp );all 的使用:查询工资比 20 号部门的有员工的工资都高的员工信息: select * from emp where sal & all (select sal from emp where deptno=20),不难理解,即大于最大值。案例:not in 关于结果为空值的子查询:首先要明白,当我们查询一个字段的时候,如果有的记录该字段为空值,则结果表中仍然会 一个空的记录存在: select count(*) from ( select mgr from emp);发现 emp 有些记录的 mgr 值为 null,通过 count(*) 也统计出来了,而 select count(mgr) from ( select mgr from emp)。 选出所有不管理别人的员工信息,下面的统计是不准确的: select * from emp where empno not in (select mgr from emp) 因为 not in 本质上与&& all 完全一样,而与含有 null 值的结果作条件运算,结果也为 null, 所以上述语句导致错误的结果。而 in 与= any 等价,与含有空值的结果作运算,结果不一定 为空。解决办法是,让作运算的结果不含有 null: select * from emp where empno not in (select mgr from emp where mgr is not null)案例:集合运算:查询出 10 和 20 号部门的的员工信息,用集合运算的方式可以这样实现: select * from emp where deptno=10 union select * from emp where deptno=20; 集合运算要注意的问题: 参与运算的集合要列数相同类型一致, 采用第一个集合的表头作为 最终的表头,如果排序必须每个集合都要跟 order by。 现在可以这样理解 group by rollup(a,b)增强了: 根据集合运算的规则可知,三个集合必须列数一样且类型一致,所以: select deptno,empjob,sum(sal) from emp group by rollup(deptno,empjob)= select deptno,empjob,sum(sal) from emp group by deptno,empjob union select deptno,to_char(null),sum(sal) from emp group by deptno union select to_number(null),to_char(null), sum(sal) from emp group by null。 rollup 的性能要好些。 一般情况下集合运算只合适小表。计算 sql 语句执行的时间:set timing on,默认是关闭的。用子查询建表:create table emp_copy as select *将子查询插入表中:insert into emp_copy select *delete 和 truncate 的区别: ..\杂项\优秀网摘\技术\oracle 中 delete 和 truncate 的区别 - 1111 - 博客频道 CSDN.NET.htm前者逐条删除,后者先摧毁表再重建。?? 前者会产生碎片,后者不会。 前者不会释放空间,后者会。 前者是可以闪回,后者不可以。 (前者是 DML,后者是 DDL)Orcale 中的事务&savepoint:只要遇到 DML 语句,就会自动开启事务。 savepoint 保存点测试: insert into table1 values(1,?Kite?); insert into table1 values(2,?Jonoes?); delete from table1 where id=2; rol//回滚到保存点,要注意,此时整个事务并没有结束,即保存点之前的 操作并未提交,//提交保存点之前的操作 Orcale 隔离级别:Oracle 只支持标准中的两个,read committed,serializable,自己定义了一个:read only。 read only 是 serializable 的子集。它们都避免了不可重复读和幻读。区别是在 read only 中是 只读;而在 serializable 中可以进行 DML 操作案例:利用伪列 rownum 进行 Top-N 分析示例rownum 只能使用&和&=不能用&或&=操作符, 永远从 1 开始, 它的生成永远伴随着表的生成, 如果这张表是 DB 中已经存在的表,则 rownum 在 DB 有记录开始便开始生成了,即,无论 怎么查这张表,rownum 对应的记录是不变的,举例: select rownum,empno,sal from e 尽管排序了,但是结果表的 rownum 字段是从原始表中查得的,而原始表的中 rownum 与记 录是已经对应的,所以结果表中查得的 rownum 不会重新生成按顺序显示。但是要明白,结 果表也是一张表,也有自己的 rownum,仍然按照从 1 开始的顺序对应结果表中的记录,利 用这一点,可以实现 top-N 分析: 找到员工表中工资最高的前 3 人: select rownum,empno,sal from ( select empno,sal from emp order by sal desc) where rownum&=3案例:子表与父表的连接查询找出员工薪水大于其所在部门平均薪水的员工: select empno,ename,sal,e.deptno, avgsal from emp e,( select deptno,avg(sal) avgsal from emp group by deptno) tmp where e.deptno=tmp.deptno and sal&avgsal案例:decode&sum 应用:统计每年入职的员工数:decode 函数形式: decode(target,condition1,value1,condition2,value2,condition3,value3,??,defaultValue) 若 target 等于 codition1 则取 value1?? 首先体会 sum 的功能: select sum(1) ,sum(2) ,sum(3) from emp//emp 原表中有 14 条记录,该语句结果是:select sum(1) ,sum(2) ,sum(3) from emp where rownum&=7//该语句结果是:可见,每个 sum 都维护了一个用来累加的计数器,而累加次数则与原表记录个数无关,与 结果表的记录个数保持一致,sum 的参数正是每次累加的加数。理解了这一点,下面的语句 也不难理解了: select count(*) tatal, sum(decode(to_char(hiredate,'yyyy'),',0)) &1980&, sum(decode(to_char(hiredate,'yyyy'),',0)) &1981&, sum(decode(to_char(hiredate,'yyyy'),',0)) &1982&, sum(decode(to_char(hiredate,'yyyy'),',0)) &1987& from emp 注意,用数字作别名必须要加双引号,单引号不行。其实每个 sum 都将自己的参数累加了 14 次,14 为结果表中的记录数。只不过,根据 hiredate 的判断,每加 1 或者加 0,从而求得 每年的入职人数。 从而我们可以揣测 sum 的执行是这样进行的:逐个取出结果表的每条记录,然后判断记录 的 hiredate 字段,根据条件将 sum 的计数器加 1 或者加 0。有关表&字段的 DDL:1、子查询建表 create table tmp1 as select ename,empno,sal from emp 2、增加字段 alter table tmp1 add image blob 3、改字段类型 alter table tmp1 modify ename varchar(100) 4、删除字段 alter table tmp1 drop column image 5、改字段名称 alter table tmp1 rename column sal to salaryOracle 的回收站:查看回收站: show recyclebin,里面有 drop 掉的表,仍然可以通过里面的表名去访问原表, 清空回收站:purge recyclebin,这时表才真正删除掉。 也可以这样彻底删除:drop table tmp1 purge。约束:约束是表级的限制,not null,unique,primary key,foreign key,check 五种约束。 check 举例: create table tmp1 (id number, name varchar2(20), gender varchar2(2) check (gender in('男','女')), age number check (age&0), salary number check (salary&0) )命名约束&主、外键约束:create table users (pid varchar2(18) constraint users_PK primary key, name varchar2(20) constraint users_Name not null, gender varchar2(20) constraint users_Gender check (gender in ('男','女')), email varchar2(20) constraint users_Email unique, dno number(2) constraint users_FK references dept(deptno) ON DELETE CASCADE )//其中 on delete casacde 是级联删除,即主表相关记录删除,子表(本表)记录也会被删除, 一般不推荐。还可以调成 on delete set null。视图&索引&序列&同义词:视图: 视图的优点: 限制数据访问,简化复杂查询,视图不能提高性能。视图只能创建或者替换,不能修改,可 以删除:drop view viewname。 视图一般用来查询数据,不建议用来更新,所以一般创建视图都会跟上 with read only。 简单视图: create view empincome as select empno,ename,sal,sal*6 普通用户没有创建 view 的权限,可以给当前用户 scott 授权,以主机认证的方式登入 sys 用 户:sqlplus / as sysdba,然后授权:grant create view to scott。 复杂视图: create view dept_sal_view (name,minsal,maxsal,avgsal) as select d.dname,min(sal) minsal,max(sal) maxsal,avg(sal) avgsal from emp, e,dept d where e.deptno=d.deptno group by d.dname。 序列: orcale 使用序列的机制实现类似 mysql 的 auto_increment 的功能。序列可以看成一个数据, 存着连续的整数,主要为记录提供键值,任何用户都能访问。 定义序列: create sequence sequenceName [increment by n] [start with n] [{maxvalue n|nomaxvalue}] [{minvalue n|nominvalue}] [{cycle|nocycle}] [{cache n|nocache}] 缓存的默认值是 20 即一次在内存中产生长度为 20 的数组即 20 个数。因为使用了缓存(内 存) ,所以序列可以提高性能。 nextval 和 currval 是为了使用序列提供的伪列,最初的时候,currval 没值,要先访问一次 nextval。 select sequenceName. 应用: insert into table1 values(sequenceName.nextval,?jack?,?20?) ; 索引: 索引建立一个或者多个列上,目的是为提高检索速度(比如,表中所有的 10 部门员工所有 位置并不一定连续,若能连续,则检索速度便会很快) 。 create index myIndex on emp(deptno);执行后,会生成一个虚表即索引表,索引表存的都是 rowid,让指向同样部门的员工的 rowid 连续排列在索引表中, 在多列上建索引:会导致维护多个索引表,查询的时候,选按第一列的索引查,若相同则第 二列,类推。 Orcale 索引两种类型:B 树索引和位图索引。 当检索一个表的时候,orcale 会检查它没有没索引,如果有,则用索引,即索引我们只需要 创建,orcale 自动使用。 不适合建索引: 当表不大,或者数据经常更新的时候,查询的数据占整张表的数大于 2%~4%,列不经常出 现在 where 子句中。 同义词: 如果一个表的名字过长,访问起来不方便,可以为其起别名,注意这个别名是永久,日后都 可以以该别名访问,与之前临时别名不一样: create [public] synonym hremp for hr.employees(一般用户没有 create [public] synonym 权限, 要 授权。然后就可以 scott 这样轻松 hr 的 employees 表(也要 select 权)访问了: select * from hremp。 同义词也可以增加安全性, 因为用户看到别名并不知道该知的真实拥有 者,甚至该表的真实作用。数据字典:本质就是表,存放了大量的元信息。数据库中包含两大类型的表,一个就是普通用户的商业 用表,如 scott 的 emp 之类,一个就是存放一些与普通用户的表相关的元信息的表,普通用 户只能查这些表,不能改,这类表就是数据字典,由管理员维护,如 user_objects,user_tables 等还有总表:DICTIONARY,总表存储着其他表的信息。PLSQL 程序设计:PLSQL 也是一种程序语言,Procedure Language /SQL,Orcale 对 sql 的过程化扩展(面向过 程) ,即在 sql 语句中增加了过程处理命令,如分支循环等,使具有过程处理能力。 程序举例: 无论在 sqlplus 环境还是在 SqlDeveloper 环境,要想到看到 PLSQL 输出结果,都要 declare --变量声明 begin --程序体 dbms_output.put_line(?hellow world?); dbms_output 是程序包, put_line 是该包的一个 procedure。 可以通过 desc dbms_output 来查看 这个程序包中有哪些过程或方法。 如果在 sqlplus 环境下,要看到输出还要:set serveroutput on。 变量声明规则: var1 varchar2(18); married boolen:= pname emp.name%type//保证 panme 与 DB 中的字段类型一致。 emp_rec emp%rowtype//记录型变量 emp_rec.ename:=?King?//给记录型变量的字段赋值,代表表中的一行 带参数的光标,光标参数不能具体长度,但可以用%指定类型。触发器:触发器是与一个表相关联的,存储的 PL/SQL 程序,每当有 insert,update,delete 操作指定的 表时,Orcale 会自动执行相应的触发器,注意,操作不包括 select。 触发器有语句级的,行级的, CREATE [or REPLACE] TRIGGER 触发器名 {BEFORE | AFTER} {DELETE | INSERT | UPDATE [OF 列名]} ON 表名 [FOR EACH ROW [WHEN(条件) ] ] PLSQL 块/****************************************************************************** * 常识:建表时尤其是 varchar2 一定要指定长度,否则建不成功,总是提示缺失括号 几个案例: 案例 1:查出员工表 emp 中工资最高的前三名。 解前分析:每个 oracle 表中都有一个伪列,记录了表的行号,我们可以将其选出来,就能看 见了。select rownum emp.* from emp,结果中可以看到有一个列记录了行号。但是要注意的 是行号永远按照默认的顺序生成,而且生成后就不会变了,order by 也改变不了,即这样的 语句:select rownum emp.* from emp order by sal;结果是 sal 按默认的从低到高显示了,但 是行号因为在 order by、之前已经生成,所以行号的顺序是乱的。所以,我们不能试图通过 这来控制行号来选出工资最高的前三名。 案例 2:找到员工表中薪水大于本部门平均薪水的员工 select * from emp where emp.sal&( select avgsal from (select deptno,avg(sal) avgsal from emp group by deptno) tmp where tmp.deptno=emp.deptno);; 解析:明确一点:任何一个子查询都可以当作一个临时表来使用,可以跟任何需要一个表的 地方。而且要注意:& 、=等操作符只能操作单个结果。要保证它们后面的结果是单个结果。 案例 3:统计每年入职的员工个数 select count(*) &total&, sum(decode(to_char(hiredate),',0)) &1980&, sum(decode(to_char(hiredate,'yyyy'),',0)) &1981&, sum(decode(to_char(hiredate,'yyyy'),',0)) &1982&, sum(decode(to_char(hiredate,'yyyy'),',0)) &1987& 注意:sum(numeric)的功能! ! 分页: select * from (select rownum r,e1.* from (select * from emp order by sal) e1 where rownum &=8 ) where r &=5;//功能是选出 5 到 8 之间的记录 最后一行,之所以能用&=是因为,r 已经是一个实实在在的一列了,不再在伪列。 group by: 组函数会自动滤空,如 count(name),若某人的名为空,则不会被统计,这样就可以解决问题: count(nvl(name,0),若 name 为空,则取一个非空值给 count 统计,0 就是非空值。 ) group by 作用于多列的话,先按照第一列分组,如果组内的相同,再按照第二列分组,依此 类推。 having 在分组的基础之上(分组操作之后的我们也可以看作一个表)再过滤,having 就是过 滤这个分组之后的表的。 另一个案例: declare --要注意参数 dnamex 的类型不能用引用字段类型的形式,必须自己指定 cursor gradecursor(dnamex varchar2) is select tmp.grade --所有系的所有学生的物理成 绩 from ( select * from student s,sc,course ,dep d where s.sno=sc.sno and sc.cno=course.cno and s.dno= d.dno and course.cname='大学物理') tmp where tmp.dname= count1 number:=0;--低于 60 分人数 count2 number:=0;--60 到 85 分之间的人数 count3 number:=0;--其他分数的人数 avggrade number:=0;--保存平均成绩 gradesum number:=0;--保存一个系的物理成绩总和 countgrade number:=0;--保存一个系的成绩个数,也即学生人数 pgrade sc.grade%--一个具体的成绩 cursor dcursor is s--所有系的名字 cursor pdname dep.dname%--保存一个具体的系名字
loop --进入内层循环之前为每个系作统计之前先初始化参数 count1:=0; count2:=0; count3:=0; avggrade:=0; gradesum:=0; countgrade:=0; fetch--取到一个系的名字 exit when dcursor% open gradecursor(pdname);--按名字查出该系所有学生的物理成绩 loop fetch gra--取出一个学生的成绩 exit when gradecursor% countgrade:=countgrade+1; gradesum:=gradesum+ if(pgrade&60) then count1:=count1+1; elsif(pgrade&60 and pgrade&85) then--必须得是 adn 不能是&& count2:=count2+1; else count3:=count3+1;--cursor 是一种资源,用完后要及时关闭 avggrade:=gradesum/ dbms_output.put_line(' 大 学 物 理 '||' '||pdname||' '||count1||' '||count2||' '|| count3||' '||avggrade);关于 oracle 的 null 总结:1、与任何值运算都为空值 2、sql 中要判断一个值是不是空值不能用=或者!=运算符,要用 is 或 is not 3、s如果某个记录的 ename 为空,依然会被选出来放到结果集中,且 为 null。 4、 关于子查询中 in 和 not in 后有 null 值的问题:Returning Nulls in the Resulting Set of a Subquery The SQL statement on the slide attempts to display all the employees who do not have any subordinates. Logically, this SQL statement should have returned 12 rows. However, the SQL statement does not return any

我要回帖

更多关于 java 正则表达式 数字 的文章

 

随机推荐