再讲异常之前我们就应该要知道异常和错误的区别
Error类和Exception类的父类都是throwable类,他们的区别是:
Error类一般是指与虚拟机相关的问题,如系统崩溃,虚拟机错误,内存空间不足,方法调用栈溢等。对于这类错误的导致的应用程序中断,
仅靠程序本身无法恢复和和预防,遇到这样的错误,建议让程序终止。
Exception类表示程序可以处理的异常,可以捕获且可能恢复。遇到这类异常,应该尽可能处理异常,使程序恢复运行,而不应该随意终止异常。
1.checked 异常检查期异常 java.lang.Excetion 在编译期需要人工处理否则编译失败:Exception的子类除了运行期异常都是检查期异常
2.非Checked异常运行时异常 java.lang.RuntimeException 不需要处理的直接能编译通过:所有的RuntimeException以及其子类都是运行异常
举例:运行期异常
结果:运行期异常,当你敲好代码时不会报错,而当你运行时才会报除数不能为0的错误
举例:检查期异常:
结果:检查期异常,当你编译的时候就会报错,一定要抛出异常编译才能通过
三.异常的处理机制
Java语言主要依赖于 try catch finally 和throws throw 五个关键字来描述异常
1) 在发生异常的地方直接处理
使用try catch finally 直接处理异常
a) try-catch-finally结构中try块是必须有的,catch和finally块为可选,但两者至少必须出现其中之一。
b) catch 可以同时出现多个,但一个异常最多捕获一个catch,而且catch的顺序是从上往下
c) finally 无论是否捕获异常都会执行的一行代码
演示1:try异常
1 public class TestException { 2 public static void main(String[] args) { 3 int c = 0; 4 try 5 { 6 int a = 3; 7 int b = 0; 8 // 这块代码出现了异常 9 c = a / b; 10 // 那么异常之后的代码都不会被执行 11 System.out.println("Hello World"); 12 } 13 catch (ArithmeticException e) 14 { 15 System.out.println("除数不能为零"); 16 } 17 finally 18 { 19 //不管有没有发生异常,finally语句块都会被执行 20 System.out.println("Welcome"); 21 } 22 System.out.println(c); 23 // 当b为0时,有异常,输出为c的初始值0 24 } 25 } 26 //输出结果:除数不能为零 Welcome 0
演示2:带有return的异常
1 import java.io.FileInputStream; 2 import java.io.FileNotFoundException; 3 public class DemoException { 4 public static void main(String[] args) { 5 int a=test3(); 6 System.out.println(a); 7 } 8 @SuppressWarnings("finally") 9 public static int test3(){ 10 try { 11 System.out.println(9 / 0); 12 return 1; 13 } catch (Exception e) { 14 System.out.println("呵呵"); 15 return 2; 16 }finally{ 17 System.out.println("哈哈"); 18 return 3; 19 } 20 } 21 } 22 //输出结果 "呵呵""哈哈" 3
得出结论:作用范围 return 终止整个方法体,但在finally出现之前 return是老大 finally 作用范围> return
2)将异常抛给调用者让调用者处理
1 //throws在方法体头部通过声明 抛出异常... 2 public void dealFile() throws FileNotFoundException{ 3 FileInputStream fis =new FileInputStream("C:/name.txt"); 4 } 5 //那么那么上面调用这个方法可以选择是继续抛出,还是捕获异常
案例一:通过throws抛出异常,调用者直接捕获抛出的异常
throws异常运行结果:
非数据类型不能转换。
注意:使用Throws是的限制
两小原则
使用throws 声明抛出异常一个限制
子类继承父类重写父类的方法
子类抛出的异常必须比父类少
子类抛出的异常必须比父类小
两小原则是针对检查期异常的,运行期异常不遵循这个规则(RuntimeException 以及子类)
案例二:通过throw抛出异常
throw异常运行结果如下:
面试题:Throw 和Throws有什么区别?
Throw语句是用在方法体内表示抛出的异常由方法体内的语句处理
Throws 语句是在方法声明之后抛出异常表示在抛出的异常交给调用者处理
Throws 要么使用try –catch –finally 处理要么继续抛出
四.自定义异常
所谓自定义异常,通常就是定义一个类,去继承Exception类或者它的子类。因为异常必须直接或者间接地继承自Exception类。
通常情况下,会直接继承自Exception类,一般不会继承某个运行时的异常类。
自定义异常可以用于处理用户登录错误,用户输入错误提示等。
自定义异常需要遵循以下步骤
举例:自定义异常:
public class MyException extends Exception { public MyException() { super(); } public MyException(String message) { super(message); } }
一种处理异常方式
public class ExceptionTest4 { public void method(String str) throws MyException { if(null == str) { throw new MyException("传入的字符串参数不能为null!"); } else { System.out.println(str); } } public static void main(String[] args) throws MyException //异常处理方式1,不断向外抛出 { ExceptionTest4 test = new ExceptionTest4(); test.method(null); } }
另一种异常处理方式:
1 public class ExceptionTest4 2 { 3 4 public void method(String str) throws MyException 5 { 6 if (null == str) 7 { 8 throw new MyException("传入的字符串参数不能为null!"); 9 } 10 else 11 { 12 System.out.println(str); 13 } 14 } 15 16 public static void main(String[] args) 17 { 18 //异常处理方式2,采用try...catch语句 19 try 20 { 21 ExceptionTest4 test = new ExceptionTest4(); 22 test.method(null); 23 24 } 25 catch (MyException e) 26 { 27 e.printStackTrace(); 28 } 29 finally 30 { 31 System.out.println("程序处理完毕"); 32 } 33 34 } 35 }
最后说一句,try-catch-finally虽好用,但是如果是滥用,这样只是会让程序的可读性变的很糟糕,当程序报错,就无法快速准确的定位了。
如果有个别地方写的不到位或者不够完善希望大家多多指点,看了有不明白的地方也可以留言,我也会尽快帮助解答。
正则表达式---常用符号
首先声明,我这里列表的是经常使用的一些符号,如果你想得到全部,那建议你通过API中,搜索Pattern类,会得到所有符号。
字符类 | |
---|---|
[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 | 非单词边界 |
Greedy 数量词 | |
---|---|
X? | X,一次或一次也没有 |
X* | X,零次或多次 |
X+ | X,一次或多次 |
X{n} | X,恰好 n 次 |
X{n,} | X,至少 n 次 |
X{n,m} | X,至少 n 次,但是不超过 m 次 |
为了让大家更加明白,对上面的进行部分解释
元字 符 | 举例 |
.点 | 例如正则表达式r.t匹配这些字符串:rat、rut、r t,但是不匹配root。 |
$ | 例如正则表达式weasel$ 能够匹配字符串"He's a weasel"的末尾 但是不能匹配字符串"They are a bunch of weasels." |
^ | 匹配一行的开始。例如正则表达式^When in能够匹配字符串"When in the"的开始,但是不能匹配"What and When in the" |
* | 匹配0或多个正好在它之前的那个字符。例如正则表达式。*意味着能够匹配任意数量的任何字符。 |
\ | 这个是用来转义用的。例如正则表达式\$被用来匹配美元符号,而不是行尾,类似的,正则表达式\.用来匹配点字符,而不是任何字符的通配符。 |
| | 将两个匹配条件进行逻辑“或”(Or)运算。例如正则表达式(him|her) 匹配" to him"和"i to her",但是不能匹配" to them."。 |
+ | 匹配1或多个正好在它之前的那个字符。例如正则表达式9+匹配9、99、999等。 |
? | 匹配0或1个正好在它之前的那个字符。 |
{i} {i,j} |
例如正则表达式A[0-9]{3} 能够匹配字符"A"后面跟着正好3个数字字符的串,例如A123、A348等,但是不匹配A1234。 而正则表达式[0-9]{4,6} 匹配连续的任意4个、5个或者6个数字字符。 |
最后讲一个初学者很容易搞混的知识点
正则表达式的() [] {}的区别
() 是为了提取匹配的字符串。表达式中有几个()就有几个相应的匹配字符串。圆括号中的字符视为一个整体。
[]是定义匹配的字符范围。比如 [a-zA-Z0-9] 表示相应位置的字符要匹配英文字符和数字。
{}一般用来表示匹配的长度,比如 \s{3} 表示匹配三个空格,\s[1,3]表示匹配一到三个空格。
(0-9) 匹配 '0-9′ 本身。 [0-9]* 匹配数字(注意后面有 *,可以为空)[0-9]+ 匹配数字(注意后面有 +,不可以为空){1-9} 写法错误。
[0-9]{0,9} 表示长度为 0 到 9 的数字字符串。
注意:关于() [] {}区别,如果你还没用明白的话,也没有关系,这两天我还会写正则表达式类文章,到时候我会用列子说明。
正则表达式
说真的正则表达式真不好写,当我收集资料准备开始写的时候,发现收集的东西越来越多范围也越来越广,我文章的前提就是文章要清晰,
在缕清自己思路之后,我从先简后难的方式来写有关正表达式,你们如果觉得这篇写的还可以的话,可以先关注我,接下来我会陆续更新。
一.什么是正则表达式
正则表达式(regular expressions)是一种描述字符串集的方法,它是以字符串集中各字符串的共有特征为依据的。
正则表达式可以用于搜索、编辑或者是操作文本和数据。这是官方表达听的有点绕口,用通俗的话来说就是:正则表达式主要用来处理和文本有关的内容
常见的处理方式有四种:1.匹配 2.切割 3.替换 4.获取 在下面我也会一一举例说明。
二.正则表达式常见的符号含义
这个我在正则表达式(1)中,有关常用的也大概做个介绍,大家可以往前翻阅。
三.常见的处理方式有四种
(1)匹配 我这里使用的是字符串对象的方法 match(String regex),
1 import java.util.regex.*; 2 public class TestException{ 3 public static void main(String[] args) throws Exception { 4 String tel="18600000111"; 5 String reg="1[3578]\\d{9}"; //首字母1,第二字母3,5,7,8,后面都是数字共有9位 6 boolean b1 =tel.matches(reg); 7 System.out.println(b1);//输出结果true 8 } 9 }
(2)切割 我这里使用的是字符串中的split方法
案例一:切割一个或多个空格
1 //切割一个或者多个空格 2 import java.util.regex.*; 3 public class TestException{ 4 public static void main(String[] args) throws Exception { 5 String str ="aaa bbb ccc ddd eee"; 6 String [] arr =str.split(" +");//“ +”表示至少有一个空格 7 for(String s:arr){ 8 System.out.print(s); 9 } 10 } 11 }
运行结果;
aaabbbcccdddeee
案例二:通过.来切割字符串
1 //通过.来切割字符串 2 import java.util.regex.*; 3 public class TestException{ 4 public static void main(String[] args) { 5 String str2="zhangsan.lisi.wangwu"; 6 /* \\是代表转义字符,如果你直接放split("."),是无法切割的,因为.在正则表达式中.有它特有的含义 7 当你用转义之后\\.那么它就只代表一个点,而不具有特殊意义*/ 8 String [] arr2 =str2.split("\\."); 9 for(String s:arr2){ 10 System.out.println(s); 11 } 12 } 13 } 14 /* 补充:在java中需要转义的除了.外,还有需要先转义不能直接切割的: 15 * $ ( ) * + [ ] ? \ ^ { } | 16 * 这么几个大家用它来切割的时候,转义后就可以了 17 */
运行结果:
zhangsan lisi wangwu
案例三:用重复项来切割
1 //用重复项来切割 2 import java.util.regex.*; 3 public class TestException{ 4 public static void main(String[] args) { 5 String str3 ="wer#######tayuio****asdfg"; 6 String reg ="(..)"; //(.)代表第一个任意字符 \\1代表回去第一组数据 +代表1个或者多个 7 String [] arr3=str3.split(reg); 8 for(String s:arr3){ 9 System.out.println(s); 10 } 11 } 12 } 13 /* 补充:我怕初学者对"(.)\\1+",还没有搞懂,我这里在单独解释一下:(.)的字符视为一个整体。 \\1代表回去第一组数据 14 * 那它其实在这里也就代表(.),所以就相当于(.)(.)+,这里仅仅是相当于,也是为了好理解,其实他们还是有本质区别的 15 * 因为(.)==\\1,就是说如果.代表a,那么\\1也就代表a,而(.)≠(.),前面代表a后面可以代表b,因为前后两个点不是同一个点 16 * 我也不知道这样比较是否恰当, 反正意思就是这个意思 17 */
运行结果:
wer tayuio asdfg
(3)替换 使用String字符串汇总的方法
案例一:把重复的数据 替换为#
1 //把重复的数据 替换为# 2 import java.util.regex.*; 3 public class TestException{ 4 public static void main(String[] args) { 5 String str="wer#####yw****fghj"; 6 //把重复的数据 替换为# 7 str=str.replaceAll("(.)\\1+", "#");//(.) 第一个任意字符 \\1 取第一组数据 + 1个或者多个 8 System.out.println(str) ; 9 } 10 }
运行结果:
wer#yw#fghj
案列二:把重复项都变成单个
1 import java.util.regex.*; 2 public class TestException{ 3 public static void main(String[] args) { 4 String str="wer#####yw****fg???hj"; 5 //后一个参数的含义 可以通过$ 数字引用第一个参数中的组,这个美元符号代表就是前面小括号里的内容 6 str=str.replaceAll("(.)\\1+", "$1"); 7 System.out.println(str); 8 } 9 }
运行结果:
wer#yw*fg?hj
案例三:电话号码中间几位用*表示
1 import java.util.regex.*; 2 public class TestException{ 3 public static void main(String[] args) { 4 String str2="15889895644";//158****5644 5 str2=str2.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2"); 6 System.out.println(str2);// $1表示符号前面第一个小括号,$2代表第二个,如果有第三个小括号,那也可以$3; 7 } 8 }
运行结果:
158****5644
(4)获取 字符串中没有直接提供该功能 只能通过正则表达匹配
案例一:获取正则表达式所匹配的字符串
1 import java.util.regex.*; 2 public class TestException{ 3 public static void main(String[] args) { 4 String str="da jio zhu yi laa, ming tian fang jia laa"; 5 //1.定义规则 6 String reg="\\b[a-z]{3}\\b";//任意三个字符 \\b 是单词的边界(明白为什么加这个) 7 Pattern p =Pattern.compile(reg); 8 //3.通过正则表达对象 获取匹配器对象 并把操作的字符串关联 9 Matcher m =p.matcher(str); 10 while(m.find()){ //find()用来搜索与正则表达式相匹配的任何目标字符串 11 System.out.println(m.start()+"....."+m.group()+"..."+m.end()); 12 } //start()开始位置 group()用来返回包含了所匹配文本的字符串 end()结束位置 13 } 14 } 15 /* 有关: 在regex(正则表达式)包中,包括了两个类,Pattern(模式类)和Matcher(匹配器类)。 16 * 这个大家也可以多去了解 17 */
运行结果:
3.....jio...6 7.....zhu...10 14.....laa...17 35.....jia...38 39.....laa...42
四:最后来一个综合小案例
题目1:10.10.10.10 192.168.118.40 192.168.1.200 127.0.0.108 按照升序排序
1 import java.util.Arrays; 2 import java.util.regex.*; 3 public class TestException{ 4 public static void main(String[] args) { 5 String ip="10.10.10.10 192.168.118.40 192.168.1.200 127.0.0.108"; 6 /* 7 * 为了方便 每一个端都补零 保证每一个字段至少是三位 8 */ 9 ip=ip.replaceAll("(\\d+)", "00$1");//补0,让至少有三位数 10 11 ip=ip.replaceAll("0*(\\d{3})", "$1");//所有都变成三位数 12 13 String [] ips =ip.split(" +");//用空格来切割 14 Arrays.sort(ips);//升序排序 15 for(String x:ips){ 16 System.out.println(x.replaceAll("0*(\\d+)", "$1"));//还原 17 } 18 } 19 } 20 /** 这个题目或许看着不难,难就难在思维模式,它这每一步都很关键,也是希望大家在学习的途中多思考,而不是停留在看的基础上 21 */
运行结果:
10.10.10.10 127.0.0.108 192.168.1.200 192.168.118.40
这篇文章到这里结束了,接下来对于正则表达式我还会再写,比如Pattern(模式类)和Matcher(匹配器类),再比如如何获取文本中的电话号码等等深入的一些东西,不过最近应该不写了,
接下来我会写一些其它有关的知识。
Patchca是Piotr Piastucki写的一个java验证码开源库,打包成jar文件发布,patchca使用简单但功能强大。
本例实现了自定义背景,由于生成图片较小,波动太大时会导致部分文字显示不全,所以更改了滤镜属性。
效果图:
代码如下:
package com.ninemax.cul.servlet; import java.awt.Color; import java.awt.Graphics; import java.awt.image.BufferedImage; import java.awt.image.BufferedImageOp; import java.io.IOException; import java.io.OutputStream; import java.util.ArrayList; import java.util.List; import java.util.Random; import javax.imageio.ImageIO; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.patchca.background.BackgroundFactory; import org.patchca.color.ColorFactory; import org.patchca.color.RandomColorFactory; import org.patchca.filter.ConfigurableFilterFactory; import org.patchca.filter.library.AbstractImageOp; import org.patchca.filter.library.WobbleImageOp; import org.patchca.font.RandomFontFactory; import org.patchca.service.Captcha; import org.patchca.service.ConfigurableCaptchaService; import org.patchca.text.renderer.BestFitTextRenderer; import org.patchca.text.renderer.TextRenderer; import org.patchca.word.RandomWordFactory; /** * 验证码生成类 * * 使用开源验证码项目patchca生成 * 依赖jar包:patchca-0.5.0.jar * 项目网址:https://code.google.com/p/patchca/ * * @author zyh * @version 1.00 2012-7-12 New */ public class ValidationCodeServlet extends HttpServlet { private static final long serialVersionUID = 5126616339795936447L; private ConfigurableCaptchaService configurableCaptchaService = null; private ColorFactory colorFactory = null; private RandomFontFactory fontFactory = null; private RandomWordFactory wordFactory = null; private TextRenderer textRenderer = null; public ValidationCodeServlet() { super(); } /** * Servlet销毁方法,负责销毁所使用资源. <br> */ public void destroy() { wordFactory = null; colorFactory = null; fontFactory = null; textRenderer = null; configurableCaptchaService = null; super.destroy(); // Just puts "destroy" string in log } public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("image/png"); response.setHeader("cache", "no-cache"); HttpSession session = request.getSession(true); OutputStream outputStream = response.getOutputStream(); // 得到验证码对象,有验证码图片和验证码字符串 Captcha captcha = configurableCaptchaService.getCaptcha(); // 取得验证码字符串放入Session String validationCode = captcha.getChallenge(); session.setAttribute("validationCode", validationCode); // 取得验证码图片并输出 BufferedImage bufferedImage = captcha.getImage(); ImageIO.write(bufferedImage, "png", outputStream); outputStream.flush(); outputStream.close(); } /** * Servlet初始化方法 */ public void init() throws ServletException { configurableCaptchaService = new ConfigurableCaptchaService(); // 颜色创建工厂,使用一定范围内的随机色 colorFactory = new RandomColorFactory(); configurableCaptchaService.setColorFactory(colorFactory); // 随机字体生成器 fontFactory = new RandomFontFactory(); fontFactory.setMaxSize(32); fontFactory.setMinSize(28); configurableCaptchaService.setFontFactory(fontFactory); // 随机字符生成器,去除掉容易混淆的字母和数字,如o和0等 wordFactory = new RandomWordFactory(); wordFactory.setCharacters("abcdefghkmnpqstwxyz23456789"); wordFactory.setMaxLength(5); wordFactory.setMinLength(4); configurableCaptchaService.setWordFactory(wordFactory); // 自定义验证码图片背景 MyCustomBackgroundFactory backgroundFactory = new MyCustomBackgroundFactory(); configurableCaptchaService.setBackgroundFactory(backgroundFactory); // 图片滤镜设置 ConfigurableFilterFactory filterFactory = new ConfigurableFilterFactory(); List<BufferedImageOp> filters = new ArrayList<BufferedImageOp>(); WobbleImageOp wobbleImageOp = new WobbleImageOp(); wobbleImageOp.setEdgeMode(AbstractImageOp.EDGE_MIRROR); wobbleImageOp.setxAmplitude(2.0); wobbleImageOp.setyAmplitude(1.0); filters.add(wobbleImageOp); filterFactory.setFilters(filters); configurableCaptchaService.setFilterFactory(filterFactory); // 文字渲染器设置 textRenderer = new BestFitTextRenderer(); textRenderer.setBottomMargin(3); textRenderer.setTopMargin(3); configurableCaptchaService.setTextRenderer(textRenderer); // 验证码图片的大小 configurableCaptchaService.setWidth(82); configurableCaptchaService.setHeight(32); } /** * 自定义验证码图片背景,主要画一些噪点和干扰线 */ private class MyCustomBackgroundFactory implements BackgroundFactory { private Random random = new Random(); public void fillBackground(BufferedImage image) { Graphics graphics = image.getGraphics(); // 验证码图片的宽高 int imgWidth = image.getWidth(); int imgHeight = image.getHeight(); // 填充为灰色背景 graphics.setColor(Color.GRAY); graphics.fillRect(0, 0, imgWidth, imgHeight); // 画100个噪点(颜色及位置随机) for(int i = 0; i < 100; i++) { // 随机颜色 int rInt = random.nextInt(255); int gInt = random.nextInt(255); int bInt = random.nextInt(255); graphics.setColor(new Color(rInt, gInt, bInt)); // 随机位置 int xInt = random.nextInt(imgWidth - 3); int yInt = random.nextInt(imgHeight - 2); // 随机旋转角度 int sAngleInt = random.nextInt(360); int eAngleInt = random.nextInt(360); // 随机大小 int wInt = random.nextInt(6); int hInt = random.nextInt(6); graphics.fillArc(xInt, yInt, wInt, hInt, sAngleInt, eAngleInt); // 画5条干扰线 if (i % 20 == 0) { int xInt2 = random.nextInt(imgWidth); int yInt2 = random.nextInt(imgHeight); graphics.drawLine(xInt, yInt, xInt2, yInt2); } } } } }
由于是个Servlet所以web.xml配置如下:
<servlet> <servlet-name>validationCode</servlet-name> <servlet-class>com.ninemax.cul.servlet.ValidationCodeServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>validationCode</servlet-name> <url-pattern>/validationCodeServlet.png</url-pattern> </servlet-mapping>
JSP引用(部分):
<img id="validationCode" alt="验证码图片" title="验证码图片" src="<%=path %>/validationCodeServlet.png" onclick="refreshCode(this)" /> <a id="aRecode" href="javascript:void(0);" onclick="refreshCode()">换一张</a>
JS重新载入图片方法(参考):
/** * 刷新验证码 * @param imgObj 验证码Img元素 */ function refreshCode(imgObj) { if (!imgObj) { imgObj = document.getElementById("validationCode"); } var index = imgObj.src.indexOf("?"); if(index != -1) { var url = imgObj.src.substring(0,index + 1); imgObj.src = url + Math.random(); } else { imgObj.src = imgObj.src + "?" + Math.random(); } }
数组增删 集合删除
package com.test; import java.util.List; import java.util.ArrayList; import java.util.Set; import java.util.HashSet; public class ArrayBlock { /* 去掉数组中重复的值 */ public static void testA() { String [] str = {"cat", "dog", "pig", "dog",}; // 先把数组转为集合 List<String> list = new ArrayList<String>(); for (int i=0; i<str.length; i++) { if(!list.contains(str[i])) { list.add(str[i]); } } /* toArray(T[] a)用法 * 因为list集合默认是object类型,那传入new String[2],首先有泛型作用 * 如果list长度大于2,那new String[2]只有泛型作用,如果长度等于2 * 那就用new String[2]这个数组,如果小于2, 那数组多余部分为null */ String[] newStr = list.toArray(new String[1]); } //删除数组中其中一个元素 public static void testB() { String [] str = {"cat", "dog", "pig", "dog",}; //删除pig List<String> list = new ArrayList<String>(); for (int i=0; i<str.length; i++) { list.add(str[i]); } //list移除记得放外面 list.remove(2); //返回一个包含所有对象的指定类型的数组 String[] newStr = list.toArray(new String[1]); } //在数组中增加一个元素 public static void testC() { String [] str = {"cat", "dog", "pig", "dog",}; //增加pee List<String> list = new ArrayList<String>(); for (int i=0; i<str.length; i++) { list.add(str[i]); } // list.add()默认在集合最后插入数据,而 add(2, "pee")就指定在索引第二个位置插入 list.add(2, "pee"); String[] newStr = list.toArray(new String[1]); } }
package com.test; import java.util.List; import java.util.ArrayList; import java.util.Iterator; public class ListBlock { public void deliect() { List<String> list = new ArrayList<String>(); list.add("a"); list.add("b"); list.add("c"); //方法一: 用for增强方法删除 for (String str : list) { if (str.equals("a")) /* 在使用增强for循环的过程不能对元素进行删除、修改、增加的操作等操作。 * 但是,如果操作一下,立刻break跳出,也是不会报错的! */ list.remove(str); } //方法二:使用传统for循环遍历 for (int i = 0; i < list.size(); i++) { String str = list.get(i); if (str.equals("a")) { /* 不会报错,但会少都一条信息, 因为执行删除操作;删除完成,则集合后边的元素会自动前移, * 导致下次遍历漏掉一个元素,所以少删后面那个元素。 * 解决方法:在remove();方法下写上 --i; */ list.remove(i); } } //方法三:使用api提供的方法list.iterator(),这个方法不会出现问题 Iterator<String> iterator = list.iterator(); while (iterator.hasNext()) { String str = iterator.next(); if (str.equals("a")) { iterator.remove(); } } } }
package com.test; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; public class ListBlock { public static void main(String[] args) { HashMap<String, String> map = new HashMap<String, String>(); map.put("1", "one"); map.put("2", "two"); map.put("3", "three"); map.put("4", "four"); // 删除元素 Iterator<Map.Entry<String, String>> it = map.entrySet().iterator(); while (it.hasNext()) { Map.Entry<String, String> entry = it.next(); String key = entry.getKey(); int k = Integer.parseInt(key); if (k % 2 == 1) { /*iterator的remove()方法,也有需要我们注意的地方: * 每调用一次iterator.next()方法,只能调用一次remove()方法。 * 调用remove()方法前,必须调用过一次next()方法。 */ it.remove(); } } } }
大家看下哪里不对,或者需要补充的,欢迎指点。
这篇文章讲的不仅仅是map排序,比如把对象按某一属性排序,它都可以解决这些问题。
比如,有N个对象,每个对象有个属性就是成绩,成绩分:优秀,良好,合格。那我们如何按照成绩的好坏进行排序呢,下面请看代码。
1.people对象
package com.test; /*people对象其实很简单,就提供了三个属性*/ class People { private String Name; //姓名 private String Score; //成绩 private String id; //学号 public String getName() { return Name; } public void setName(String name) { Name = name; } public String getScore() { return Score; } public void setScore(String score) { Score = score; } public String getId() { return id; } public void setId(String id) { this.id = id; } public People(String name, String score, String id) { super(); Name = name; Score = score; this.id = id; } }
2.主要方法
package com.test; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.TreeMap; /** * Map进行多条件排序输出 * 成绩具有优秀,合格,不合格好吃属性。 * 入口Map * 首先按照优秀,合格,不合格排序 * 然后按照人名的标志Id排序 * 出口Map * * */ public class MainSort { /** * 准备参数,创建对象 * @return */ private static Map<String, People> getPeopleMap() { Map<String,People> PeopleMap = new TreeMap<>(); // 创建对象 People b = new People("小明" , "优秀", "b"); People a = new People("小红" , "合格", "a"); People c = new People("丁丁" , "合格", "c"); People d = new People("冬冬" , "良好", "d"); People e = new People("小黄" , "优秀", "e"); People f = new People("小李" , "良好", "f"); People g = new People("小钟" , "优秀", "g"); // 添加乱序key值,把对象放入map集合 PeopleMap.put("xniem", b); PeopleMap.put("akjd", a); PeopleMap.put("uioo", c); PeopleMap.put("qw84", d); PeopleMap.put("584sdf'", e); PeopleMap.put("4aisdf", f); PeopleMap.put("458jsf", g); return PeopleMap; } /** * 循环打印Map */ private static void show(Map<String, People> PeopleMap) { // 循环Map 这个打印肯定是无序的,也不是按放入的先后顺序 for (Map.Entry<String, People> PeopleOneMap : PeopleMap.entrySet()) { People People = PeopleOneMap.getValue(); System.out.println(People.getName() + " " + People.getScore()+ " " + People.getId() ); } } /* * 由于List能够直接使用Collections进行排序 * 但是Map不行。 * 这边所做的操作就是先将Map--》List * 然后对List进行排序 * 然后在讲List--》转换成LinkedHashMap * */ public static Map<String, People> sortMapByValue(Map<String, People> PeopleMap) { if (PeopleMap == null || PeopleMap.isEmpty()) { return null; } // LinkedHashMap是有序的、或者TreeMap都是有序的(这里只能用LinkedHashMap) Map<String, People> sortedMap = new LinkedHashMap<String, People>(); /* Set set=PeopleMap.entrySet(); PeopleMap.entrySet()返回的是一个set集合 * 再讲ArrayList(Collection<? extends E> c) 可以放collection,set集合是其子类,map不行哦 * 这步就是把map集合转为ArrayList集合 */ List<Map.Entry<String, People>> entryList = new ArrayList<Map.Entry<String, People>>(PeopleMap.entrySet()); //这步是关键,进过这步之后,entryList已经是个有序的ArrayList集合了 Collections.sort(entryList, new MapValueComparator()); //通过迭代器取出 Iterator<Map.Entry<String, People>> iter = entryList.iterator(); // Map.Entry<String, People>,就是包装了一个map节点,这个节点封装了key,value值,以及别的值(比如hashmap中哈希码和next指针) Map.Entry<String, People> tmpEntry = null; while (iter.hasNext()) { tmpEntry = iter.next(); sortedMap.put(tmpEntry.getKey(), tmpEntry.getValue()); } return sortedMap; } /** * 主方法 * */ public static void main(String[] args) { // 获取Map Map<String,People> PeopleMap = getPeopleMap(); // 打印未排序的Map show(PeopleMap); System.out.println("-----------before-----------"); // 打印排序完了的Map show(MainSort.sortMapByValue(PeopleMap)); System.out.println("-----------after------------"); } }
3.Comparator方法
package com.test; import java.util.Comparator; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; class MapValueComparator implements Comparator<Map.Entry<String, People>> { @Override public int compare(Entry<String, People> o1, Entry<String, People> o2) { // 获取比较的两个对象 People People1 = o1.getValue(); People People2 = o2.getValue(); // 将成绩映射成具有比较关系的字符1、2、3 Map<String,Integer> tasteLev = new HashMap<>(); tasteLev.put("优秀", 1); tasteLev.put("良好", 2); tasteLev.put("合格", 3); int cr = 0; // 判断成绩 int a = tasteLev.get(People2.getScore())-tasteLev.get(People1.getScore()); if (a!=0) { cr = (a>0) ? -1 : 2; } else { /*其实上面就可以按成绩优秀,良好,合格排序了, *在做一步目的,就是在成绩相同的情况下,在按照学号进行排序 */ // 按照对应的Id排序 a = People2.getId().compareTo(People1.getId()); if (a!=0) { cr = (a>0)? -2 : 1; } } /* 注意上面对一个返回值对应的就是形成比较层次 * 上层 * --> 2 * --> -1 * 下层 * --> 1 * --> -2 */ return cr; } }
最后我们再来看后台输出
一 序列化是干什么的?
我们知道,在jvm中引用数据类型存在于栈中,而new创建出的对象存在于堆中。如果电脑断电那么存在于内存中的对象就会丢失。那么有没有方法将对象保存到磁盘(对象持久化存储)或通过网络传输到远处的其他地方呢?
答案是可以,但是我们必须要求所有支持持久化存储的类实现Serializable接口。Serializable就像个通行证,只有持有这个通行证,jvm才让类创建的对象进行持久化。这个接口将类与一个称为serialVersionUID的变量关联起来,这个serialVersionUID就是在反序列化中用来确定由哪个类来加载这个对象。
二、什么情况下需要序列化
a)当你想把的内存中的对象状态保存到一个文件中或者数据库中时候;
b)当你想用套接字在网络上传送对象的时候;
c)当你想通过RMI传输对象的时候;
三、JavaBean为什么要实现java.io.Serializable接口实现序列化?
找了个比较好理解的例子:客户端访问了某个能开启会话功能的资源, web服务器就会创建一个与该客户端对应的HttpSession对象,每个HttpSession对象都要站用一定的内存空间。如果在某一时间段内访问站点的用户很多,web服务器内存中就会积累大量的HttpSession对象,消耗大量的服务器内存,即使用户已经离开或者关闭了浏览器,web服务器仍要保留与之对应的HttpSession对象,在他们超时之前,一直占用web服务器内存资源。
web服务器通常将那些暂时不活动但未超时的HttpSession对象转移到文件系统或数据库中保存,服务器要使用他们时再将他们从文件系统或数据库中装载入内存,这种技术称为Session的持久化。
将HttpSession对象保存到文件系统或数据库中,需要采用序列化的方式将HttpSession对象中的每个属性对象保存到文件系统或数据库中;将HttpSession对象从文件系统或数据库中装载如内存时,需要采用反序列化的方式,恢复HttpSession对象中的每个属性对象。所以存储在HttpSession对象中的每个属性对象必须实现Serializable接口。当然如果不是存储在session中的JavaBean可以不用存储哈。
举个简单例子:
Student
import java.io.Serializable; public class Student implements Serializable { /*serialVersionUID来决定由哪个类来加载存在于文件中的对象 * 如果指定serialVersionUID的数值,那就能使得其不再与类的成员变量相关联 * 不然你已经把对象保存到数据库,这个时候你再给这个对象新增属性,那么反序列化 * 就会报:本地类不匹配的错误,但如果指定serialVersionUID值那就不会报错。 */ private static final long serialVersionUID = -5182532647273106745L; //成员变量写成static的话是不能被持久化的 public static String countryName="china"; private String name; private int age; //如果想对非静态的数据也不想序列化,则需要加入关键字 transient String sex; /* 提供set和get方法,无参和有参方法*/ }
测试类
1 import java.io.*; 2 public class SerializableTest { 3 public static void main(String[] args) { 4 writeObj(); 5 readObj(); 6 } 7 public static void writeObj() 8 { 9 Student student=new Student("小筱", 1, "女"); 10 try { 11 ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("d:\\student.txt")); 12 oos.writeObject(student); 13 oos.close(); 14 } catch (IOException e) { 15 e.printStackTrace(); 16 } 17 } 18 public static void readObj() 19 { 20 try { 21 ObjectInputStream ooi=new ObjectInputStream(new FileInputStream("d:\\student.txt")); 22 try { 23 Object obj=ooi.readObject(); 24 Student student=(Student)obj; 25 System.out.println("age:"+student.getAge()+",name:"+student.getName()+",countryName:"+student.countryName+",sex:"+student.getSex()); 26 } catch (ClassNotFoundException e) { 27 e.printStackTrace(); 28 } 29 ooi.close(); 30 } catch (IOException e) { 31 e.printStackTrace(); 32 } 33 } 34 }
第二个小例子我没有亲自测试:用套接字在网络上传送对象
1.首先建立要传输的对象
1 //建立用来持续化的对象 2 import java.io.Serializable; 3 public class ObjectSeri implements Serializable{ 4 5 //成员变量写成static的话是不能被持久化的 6 //private关键字是不能被持久化的,脱离了JVM,成员变量是不在JVM的安全机制之内 7 private String name; 8 private String age; 9 /*set和get方法*/ <span style="color:#333333;"><strong> 10 </strong></span>
2.有了传输的对象,下一步就是建立一个服务端线程来监听socket端口,并且在run方法里面实现读取对象的数据
1 import java.io.IOException; 2 import java.io.ObjectInputStream; 3 import java.net.ServerSocket; 4 import java.net.Socket; 5 //serverTest类继承thread类,监听端口来的信息 6 public class serverTest extends Thread { 7 // private final String serverIP = "127.0.0.1"; 8 private final int serverPort = 3400; 9 private ServerSocket server; 10 public serverTest() { 11 try { 12 // ServerSocket server=new ServerSocket(serverPort); 13 server = new ServerSocket(serverPort); 14 System.out.println("正在监听3400端口"); 15 } catch (IOException e) { 16 e.printStackTrace(); 17 } 18 } 19 public void run() { 20 Socket socket = null; 21 ObjectInputStream in; 22 while (true) { 23 try { 24 synchronized (server) { 25 socket = server.accept(); 26 } 27 System.out.println("当前的连接是:" 28 + socket.getInetAddress().toString()); 29 socket.setSoTimeout(20000); 30 in = new ObjectInputStream(socket.getInputStream()); 31 ObjectSeri data = (ObjectSeri) in.readObject(); 32 System.out.println("The name is:" + data.getName() 33 + "and age is:" + data.getAge()); 34 in.close(); 35 in = null; 36 socket.close(); 37 } catch (IOException | ClassNotFoundException e) { 38 e.printStackTrace(); 39 } 40 } 41 } 42 public static void main(String args[]) { 43 (new serverTest()).start(); 44 }
3.最后,建立一个客户端来测试下
1 import java.io.ObjectOutputStream; 2 import java.net.InetSocketAddress; 3 import java.net.Socket; 4 //建立一个client测试类 5 public class TestClient { 6 private String address = "127.0.0.1"; 7 private int port = 3400; 8 9 public TestClient() { 10 // Prepare the data need to transmit 11 ObjectSeri data = new ObjectSeri(); 12 data.setName("Scott"); 13 data.setAge("34"); 14 Socket client = new Socket(); 15 InetSocketAddress adr = new InetSocketAddress(this.address, this.port); 16 try { 17 client.connect(adr, 10000); 18 ObjectOutputStream out = new ObjectOutputStream( 19 client.getOutputStream()); 20 // send object 21 out.writeObject(data); 22 out.flush(); 23 out.close(); 24 out = null; 25 data = null; 26 client.close(); 27 client = null; 28 } catch (java.io.IOException e) { 29 30 System.out.println("IOException :" + e.toString()); 31 } 32 } 33 public static void main(String[] args) { 34 new TestClient(); 35 } 36 } <span style="color: rgb(51, 51, 51);"> </span>
输出结果如下:
正在监听3400端口
当前的连接是:/127.0.0.1
The name is:Scottand age is:34
今天:2017:12:05 发现自己用到了,就是jquery表单提交,用post提交,这样有个最大的好处,就是我不用一个值一个值提交,而是把表单提交过去
$.post('your url', $("form").serialize(), function(data) { // your code } });
小筱的窝:水滴石穿,成功的速度一定要超过父母老去的速度