Java SE 5 新特性
来源:互联网 发布:馒头日记 知乎 编辑:程序博客网 时间:2024/05/19 06:38
本文转载自:http://www.cnblogs.com/taven/archive/2011/12/18/2291877.html
JDK5.0新特性系列---1.自动装箱和拆箱
/** * 基本类型的数据值可以直接赋给基本数据对象,基本数据的对象也可以直接赋给基本数据变量 * 在表达式中,基本类型的数据值可以和基本数据对象进行运算 * 基本数据类型的数组不能实现自动装箱和拆箱,即int[]不能当成Integer[]使用 *//** * 演示基本数据类型的自动拆箱和装箱 */public class AutoBox { /**整数类型的自动拆箱和装箱*/ public static void intAutoBox(){ //可以装基本数字类型赋给数字对象 //在J2SE之前,必须用iObj = new Integer(200); int i = 100; Integer iObj = 200; //将200装箱 System.out.println("开始时: i = " + i + "; iObj = " + iObj); //将数字对象赋给基本数字类型 //在J2SE5.0之前,必须使用i = tempObj.intValue(); Integer tempObj = iObj; iObj = i; i = tempObj; //将对象拆封 System.out.println("将i与iObj的值互换后: " + "i = " + i + "; iObj = " + iObj); //在表达式内可以自动拆箱和装箱 iObj += i + tempObj; i *= iObj + tempObj; System.out.println("i = " + i +"; iObj = " + iObj); } /**boolean类型的自动拆箱与装箱*/ public static void booleanAutoBox(){ boolean b = false; Boolean bObj = true; //装箱 if(bObj){//拆箱 System.out.println("bObj = " + true); } if(b || bObj){ b = bObj; //拆箱 System.out.println("bObj = " + bObj +"; b = " + b); } } /**字符类型的自动拆箱与装箱*/ public static void charAutoBox(){ char ch = 'A'; Character chObj = 'B'; System.out.println("ch = " + ch + "; chObj = " + chObj); if(ch != chObj){ //拆箱 ch = chObj; //拆箱 System.out.println("ch = " + ch + "; chObj = " + chObj); } } public static void main(String[] args){ intAutoBox(); booleanAutoBox(); charAutoBox(); //注意,支持基本数据类型的自动拆箱和装箱,但是不支持基本类型数组的自动拆箱和装箱 int[] is = {12,34,56}; //Integer[] iObj = is; //error!!! }}
JDK5.0新特性系列---2.新的for循环
import java.util.ArrayList;import java.util.List; /** * 新的for循环,格式为for(type x:type y) * 表示遍历数组或集合y的元素,把元素值赋给x */public class ForEach { /**对整数数组求和*/ public static long getSum(int[] nums) throws Exception{ if(nums == null) throw new Exception("错误的参数输入,不能为null!"); long sum = 0; //依次取得nums元素的值并累加 for(int x : nums){ sum += x; } return sum; } /**对整数列表求和*/ public static long getSum(List<Integer> nums) throws Exception{ if(nums == null) throw new Exception("错误的参数输入,不能为null!"); long sum = 0; //可以与遍历数组一样的方式遍历列表 for(int x:nums){ sum += x; } return sum; } /**求多维数组的平均值*/ public static int getAvg(int[][] nums) throws Exception{ if(nums == null) throw new Exception("错误的参数输入,不能为null!"); long sum = 0; long size = 0; //对于二维数组,每个数组元素都是一维数组 for(int[] x : nums){ //一维数组中的元素才是数字 for(int y : x){ sum += y; size ++; } } return (int)(sum/size); } public static void main(String[] args)throws Exception{ int[] nums = {456,23,-739,163,390}; List<Integer> list_I = new ArrayList<Integer>(); for(int i = 0; i < 5; i++){ list_I.add(nums[i]); } System.out.println(getSum(nums)); System.out.println(getSum(list_I)); int[][] numss = {{1,2,3},{4,5,6},{7,8,9,10}}; System.out.println(getAvg(numss)); }}
JDK5.0新特性系列---3.枚举类型
/** *enum关键字表示枚举类型,它的作用相当于类声明中的class关键字 *枚举类型不能有public的构造方法 *所有的枚举值都是public、static、final的,这些修饰符都是自动加上,无须程序员手动添加 *枚举值之间用逗号","分开,最后一个枚举值后面接分号";" *每一个枚举值是一个枚举类型的实例 *可以在枚举类型中定义非枚举值变量,这些变量可以使用任何修饰符 *变量和方法的定义必须在枚举值后面定义*/public class EnumType { /**人Person的枚举类型*/ enum Person{ CHINESE, //中国人 AMERICAN, //美国人 ENGLISH; //英国人 } public static void main(String[] args){ System.out.println("Person枚举值的数目: " + Person.values().length); //遍历枚举类型中的所有值 System.out.println("Person枚举值如下: "); Person[] ps = Person.values(); //values()方法返回Person类型的数组 for (Person p : ps){ System.out.print(p + " "); //toString()方法返回枚举值的名称,此处打印CHINESE AMERICAN ENGLISH } System.out.println(); Person p = Person.CHINESE; //比较枚举值 if(p == Person.CHINESE){ System.out.println("p' value equals Person.CHINESE"); } //使用valueOf获得字符串描述的枚举值 p = Person.valueOf("AMERICAN"); //在switch中使用枚举值 //switch中可以放置的类型有byte,short,int,char,enum,注意没有long switch(p){ case CHINESE: System.out.println("p is Chinese"); break; case AMERICAN: System.out.println("p is American"); break; case ENGLISH: System.out.println("p is English"); break; } //获得枚举值在枚举类型中声明的顺序 System.out.println("AMERICAN的序号: " + Person.AMERICAN.ordinal()); System.out.println("CHINESE的序号: " + Person.CHINESE.ordinal()); System.out.println("ENGLISH的序号: " + Person.ENGLISH.ordinal()); //使用更复杂的枚举类型ComplexPerson ComplexPerson cp = ComplexPerson.CHINESE; //因为为CHINESE枚举值覆盖了toString()方法,所以调用的是CHINESE的toString方法 System.out.println("cp.toString(): " + cp); cp = ComplexPerson.AMERICAN; //因为没有为AMERICAN枚举值覆盖toString方法,所以调用默认的toSting方法 cp = ComplexPerson.OTHER; System.out.println("cp.getValue(): " + cp.getValue()); } /**一个更复杂的枚举类型*/ enum ComplexPerson{ //枚举值 //CHINESE的value属性为"中国人" CHINESE("中国人"){ public String toString(){ return "这是个中国人"; } }, AMERICAN("美国 人"), ENGLISH("英国人"){ public String toString(){ return "这是个英国佬"; } }, OTHER{ public String toString(){ return "这是个其它国家的人"; } }; private String value = null; //枚举类值的value属性,只能声明在枚举值的后面 //默认的构造方法 ComplexPerson(){ value = "其他人"; } //带参数的构造方法 ComplexPerson(String value){ this.value = value; } //获取value属性 public String getValue(){ return this.value; } }}
JDK5.0新特性系列---4.静态导入
/** *静态导入:是指可以import类的静态方法和静态变量,在使用时,无须指定类名, * 便可以使用这些被import的静态方法和静态变量,这就是静态导入 *写import语句时,可以定位到一个静态方法或静态变量(以前是定位到类) *可以使用通配符(*)代表导入该类的所有静态方法和静态变量 *不允许静态方法和静态变量出现重名的情况*/import static java.lang.Math.max; //导入了Math的max方法import static java.lang.Math.min; //导入了Math的min方法import static java.lang.Integer.*; //导入了Integer的所有静态方法和静态属性public class StaticImport { public static void main(String[] args){ //通过静态导入使用Math的静态方法 System.out.println(max(5,10)); System.out.println(min(5,10)); //通过静态导入使用Integer的静态方法 System.out.println(parseInt("55544555")); System.out.println(toBinaryString(2354)); //通过静态导入使用Integer的静态属性 System.out.println(MAX_VALUE); System.out.println(MIN_VALUE); }}
JDK5.0新特性系列---5.可变长参数Varargs
/** * 在J2SE5.0之前,当传入到方法的参数个数不固定时,经常采用数组的方式传递参数 * 在J2SE5.0之后,可以使用可变长参数的我给方法传递参数 *//** * 在参数类型和参数名之间使用"..."(三个英文的点),表示该参数为可变长的 * 通过新的for循环读取可变长参数中的值 * 一个方法里最多只能有一个变长参数,而且这个变长参数一定要放在参数表的最后一个参数*/import static java.lang.System.*;public class VarArgs { public static void print(Integer... s){ for(int s2:s){ //解封 out.print(s2+"/t"); } out.println(); } public static void main(String... args) { print(); //调用public static void print()方法 print(1); //调用public static void print(Integer i)方法 封箱 print(2,3); //调用public static void print(Integer... s)方法 封箱 print(4,5,6); //调用public static void print(Integer... s)方法 封箱 }}
JDK5.0新特性系列---6.格式化输出
import java.util.Date; /** * 使用printf输出 *//**关键技术点 * 使用java.io.PrintStream的printf方法实现C风格的输出 * printf 方法的第一个参数为输出的格式,第二个参数是可变长的,表示待输出的数据对象 */public class Printf { public static void main(String[] args) { /*** 输出字符串 ***/ // %s表示输出字符串,也就是将后面的字符串替换模式中的%s System.out.printf("%s", new Integer(1212)); // %n表示换行 System.out.printf("%s%n", "end line"); // 还可以支持多个参数 System.out.printf("%s = %s%n", "Name", "Zhangsan"); // %S将字符串以大写形式输出 System.out.printf("%S = %s%n", "Name", "Zhangsan"); // 支持多个参数时,可以在%s之间插入变量编号,1$表示第一个字符串,3$表示第3个字符串 System.out.printf("%1$s = %3$s %2$s%n", "Name", "san", "Zhang"); /*** 输出boolean类型 ***/ System.out.printf("true = %b; false = ", true); System.out.printf("%b%n", false); /*** 输出整数类型***/ Integer iObj = 342; // %d表示将整数格式化为10进制整数 System.out.printf("%d; %d; %d%n", -500, 2343L, iObj); // %o表示将整数格式化为8进制整数 System.out.printf("%o; %o; %o%n", -500, 2343L, iObj); // %x表示将整数格式化为16进制整数 System.out.printf("%x; %x; %x%n", -500, 2343L, iObj); // %X表示将整数格式化为16进制整数,并且字母变成大写形式 System.out.printf("%X; %X; %X%n", -500, 2343L, iObj); /*** 输出浮点类型***/ Double dObj = 45.6d; // %e表示以科学技术法输出浮点数 System.out.printf("%e; %e; %e%n", -756.403f, 7464.232641d, dObj); // %E表示以科学技术法输出浮点数,并且为大写形式 System.out.printf("%E; %E; %E%n", -756.403f, 7464.232641d, dObj); // %f表示以十进制格式化输出浮点数 System.out.printf("%f; %f; %f%n", -756.403f, 7464.232641d, dObj); // 还可以限制小数点后的位数 System.out.printf("%.1f; %.3f; %f%n", -756.403f, 7464.232641d, dObj); /*** 输出日期类型***/ // %t表示格式化日期时间类型,%T是时间日期的大写形式,在%t之后用特定的字母表示不同的输出格式 Date date = new Date(); long dataL = date.getTime(); // 格式化年月日 // %t之后用y表示输出日期的年份(2位数的年,如99) // %t之后用m表示输出日期的月份,%t之后用d表示输出日期的日号 System.out.printf("%1$ty-%1$tm-%1$td; %2$ty-%2$tm-%2$td%n", date, dataL); // %t之后用Y表示输出日期的年份(4位数的年), // %t之后用B表示输出日期的月份的完整名, %t之后用b表示输出日期的月份的简称 System.out.printf("%1$tY-%1$tB-%1$td; %2$tY-%2$tb-%2$td%n", date, dataL); // 以下是常见的日期组合 // %t之后用D表示以 "%tm/%td/%ty"格式化日期 System.out.printf("%1$tD%n", date); //%t之后用F表示以"%tY-%tm-%td"格式化日期 System.out.printf("%1$tF%n", date); /*** 输出时间类型***/ // 输出时分秒 // %t之后用H表示输出时间的时(24进制),%t之后用I表示输出时间的时(12进制), // %t之后用M表示输出时间的分,%t之后用S表示输出时间的秒 System.out.printf("%1$tH:%1$tM:%1$tS; %2$tI:%2$tM:%2$tS%n", date, dataL); // %t之后用L表示输出时间的秒中的毫秒 System.out.printf("%1$tH:%1$tM:%1$tS %1$tL%n", date); // %t之后p表示输出时间的上午或下午信息 System.out.printf("%1$tH:%1$tM:%1$tS %1$tL %1$tp%n", date); // 以下是常见的时间组合 // %t之后用R表示以"%tH:%tM"格式化时间 System.out.printf("%1$tR%n", date); // %t之后用T表示以"%tH:%tM:%tS"格式化时间 System.out.printf("%1$tT%n", date); // %t之后用r表示以"%tI:%tM:%tS %Tp"格式化时间 System.out.printf("%1$tr%n", date); /*** 输出星期***/ // %t之后用A表示得到星期几的全称 System.out.printf("%1$tF %1$tA%n", date); // %t之后用a表示得到星期几的简称 System.out.printf("%1$tF %1$ta%n", date); // 输出时间日期的完整信息 System.out.printf("%1$tc%n", date); }}/** *printf方法中,格式为"%s"表示以字符串的形式输出第二个可变长参数的第一个参数值; *格式为"%n"表示换行;格式为"%S"表示将字符串以大写形式输出;在"%s"之间用"n$"表示 *输出可变长参数的第n个参数值.格式为"%b"表示以布尔值的形式输出第二个可变长参数 *的第一个参数值. *//** * 格式为"%d"表示以十进制整数形式输出;"%o"表示以八进制形式输出;"%x"表示以十六进制 * 输出;"%X"表示以十六进制输出,并且将字母(A、B、C、D、E、F)换成大写.格式为"%e"表 * 以科学计数法输出浮点数;格式为"%E"表示以科学计数法输出浮点数,而且将e大写;格式为 * "%f"表示以十进制浮点数输出,在"%f"之间加上".n"表示输出时保留小数点后面n位. *//** * 格式为"%t"表示输出时间日期类型."%t"之后用y表示输出日期的二位数的年份(如99)、用m * 表示输出日期的月份,用d表示输出日期的日号;"%t"之后用Y表示输出日期的四位数的年份 * (如1999)、用B表示输出日期的月份的完整名,用b表示输出日期的月份的简称."%t"之后用D * 表示以"%tm/%td/%ty"的格式输出日期、用F表示以"%tY-%tm-%td"的格式输出日期. *//** * "%t"之后用H表示输出时间的时(24进制),用I表示输出时间的时(12进制),用M表示输出时间 * 分,用S表示输出时间的秒,用L表示输出时间的秒中的毫秒数、用P表示输出时间的是上午还是 * 下午."%t"之后用R表示以"%tH:%tM"的格式输出时间、用T表示以"%tH:%tM:%tS"的格式输出 * 时间、用r表示以"%tI:%tM:%tS %Tp"的格式输出时间. *//** * "%t"之后用A表示输出日期的全称,用a表示输出日期的星期简称. */
JDK5.0新特性系列---7.使用ProcessBuilder执行本地命令
import java.io.BufferedReader;import java.io.BufferedWriter;import java.io.File;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;import java.io.OutputStreamWriter;import java.util.ArrayList;import java.util.Arrays;import java.util.Iterator;import java.util.List;import java.util.Map; /** * 在J2SE5.0之前使用Runtime的exec方法执行本地命令. * 在J2Se5.0之后,可以使用ProcessBuilder执行本地命令 * 它提供的功能更加丰富,能够设置设置工作目录、环境变量等 * 本例PorcessBuilder执行Windows操作系统的"ipconfig/all"命令,获取本机网卡的MAC地址*//**关键技术剖析 * 用本命令名和命令的参数选项构造ProcessBuilder对象,它的start方法执行命令,启动一个进程,返回一个Process对象 * ProcessBuilder的environment方法获得运行进程的环境变量,得到一个Map,可以修改环境变量 * ProcessBuilder的directory方法切换工作目录 * Process的getInputStream方法获得进程的标准输出流,getErrorStream方法获得进程的错误输出流*/public class UsingProcessBuilder { /**获取Windows系统下的网卡的MAC地址*/ public static List<String> getPhysicalAddress(){ Process p = null; List<String> address = new ArrayList<String>(); //物理网卡列表 try{ p = new ProcessBuilder("ipconfig","/all").start(); //执行ipconfig/all命令 }catch(IOException e){ return address; } byte[] b = new byte[1024]; int readbytes = -1; StringBuffer sb = new StringBuffer(); //读取进程输出值 //在JAVA IO中,输入输出是针对JVM而言,读写是针对外部数据源而言 InputStream in = p.getInputStream(); try{ while((readbytes = in.read(b)) != -1){ sb.append(new String(b,0,readbytes)); } }catch(IOException e1){ }finally { try{ in.close(); }catch (IOException e2){ } } //以下是分析输出值,得到物理网卡 String rtValue = sb.toString(); int i = rtValue.indexOf("Physical Address. . . . . . . . . :"); while (i > 0){ rtValue = rtValue.substring(i + "Physical Address. . . . . . . . . :".length()); address.add(rtValue.substring(1,18)); i = rtValue.indexOf("Physical Address. . . . . . . . . :"); } return address; } /**执行自定义的一个命令,该命令放在C:/temp下,并且需要两个环境变量的支持*/ public static boolean executeMyCommand1(){ //创建系统进程创建器 ProcessBuilder pb = new ProcessBuilder("myCommand","myArg1","myArg2"); Map<String, String> env = pb.environment(); //获得进程的环境 //设置和去除环境变量 env.put("VAR1", "myValue"); env.remove("VAR0"); env.put("VAR2", env.get("VAR1") + ";"); //迭代环境变量,获取属性名和属性值 Iterator it=env.keySet().iterator(); String sysatt = null; while(it.hasNext()) { sysatt = (String)it.next(); System.out.println("System Attribute:"+sysatt+"="+env.get(sysatt)); } pb.directory(new File("C:/temp")); try{ Process p = pb.start(); //得到进程实例 //等待进程执行完毕 if(p.waitFor() != 0){ //如果进程运行结果不为0,表示进程是错误退出的 //获得进程实例的错误输出 InputStream error = p.getErrorStream(); //do something } InputStream sdin = p.getInputStream(); //获得进程实例的标准输出 //do something }catch(IOException e){ }catch(InterruptedException e){ } return true; } public static void executeMyCommand2(){ ProcessBuilder pb = null; String sysatt = null; try { //创建一个进程示例 pb = new ProcessBuilder("cmd.exe"); //获取系统参数并打印显示 Map<String, String> env = pb.environment(); Iterator it=env.keySet().iterator(); while(it.hasNext()) { sysatt = (String)it.next(); System.out.println("System Attribute:"+sysatt+"="+env.get(sysatt)); } //设置工作目录 pb.directory(new File("d://myDir")); Process p = pb.start(); //将要执行的Windows命令写入 BufferedWriter bw=new BufferedWriter(newOutputStreamWriter(p.getOutputStream())); //'/r/n'是必须写入的 bw.write("test.bat /r/n"); bw.write("ping -t www.yahoo.com.cn /r/n"); //flush()方法是必须调用的 bw.flush(); //将执行结果打印显示 InputStream is = p.getInputStream(); InputStreamReader isr = new InputStreamReader(is, "GBK"); BufferedReader br = new BufferedReader(isr); String line; while ((line = br.readLine()) != null) { System.out.println(line); } } catch (Exception e) { e.printStackTrace(); } } public static void main(String[] args){ List<String> address = UsingProcessBuilder.getPhysicalAddress(); for(String add : address){ System.out.printf("物理网卡地址: %s%n",add); } executeMyCommand1(); executeMyCommand2(); }}
JDK5.0新特性系列---8.泛型编程
import java.util.ArrayList;import java.util.Collection;import java.util.List; /** *泛型编程关键掌握两点: *1.在方法参数中使用通配符 *2.在方法的参数类型和返回类型中使用通用类型 *//**关键技术 *1.通配符问号(?)表示任意类型.如"List<?>"表示可以存放任意对象类型的List *2.通配符可以接extends和super,表示有限制的通配符.如"List<? extends Parent>" * 声明的List能且仅能存放Parent及其子类的对象,而"List<? super Child>" * 声明的List仅能存放Child及其父类的对象 *3.通用类型是指不指定参数或者返回值的类型,常用一个大写的字母代表类型, * 它能代表任何类型,需要在方法声明的返回值前用<>声明通用类型.如"public <T> String * getName(T data)"的方法声明中,String前用<T>表示T是通用类型,它的data参数的类型是T, * 表示能接收任意类型的参数,方法的返回值是String类型 */public class GeneralProgram { /**使用问号"?*通配符,"?"代表任何类型,所以它的参数可以是任何类型的Collection*/ public static void printCollection(Collection<?> collect){ if(collect == null){ return; } for(Object obj : collect){ System.out.println(obj + " "); } System.out.println(); } /**使用有限制的通配符"? extends",可以接受任何Parent及其子类的Collection*/ public static void printNames(Collection<? extends Parent> collect){ if(collect == null){ return; } for(Object obj : collect){ System.out.println(obj + " "); } System.out.println(); } /**将一个任意类型的数组添加到列表里*/ public static <T> List<T> arrayToList(T[] datas){ if (datas == null){ return null; } List<T> list_T = new ArrayList<T>(); for(T x : datas){ list_T.add(x); } return list_T; } /**在一组parent对象中查找名字为name的Parent对象*/ public static <T extends Parent> T findParent(T[] parents, String name){ if(parents == null){ return null; } T parent = null; //依次遍历Parent对象数组 for(T x : parents){ //如果Parent对象的name与参数name匹配,则退出遍历 if(x.name.equals(name)){ parent = x; break; } } //返回 return parent; } public static void main(String[] args){ /***指定具体的类型***/ //声明一个用于装字符串的列表,该列表只能装字符串的对象 List<String> list_S = new ArrayList<String>(); list_S.add("first"); list_S.add("second"); list_S.add("third"); //不能装整数对象 Integer iObj = 10; //list_S.add(iObj);//error!! //从列表中取值时,不用作强制类型转换 String firstS = list_S.get(0); String thirdS = list_S.get(2); System.out.println("firstS: " + firstS + "; thirdS: " + thirdS); /***泛型和继承***/ //String继承Object String s = "sss"; Object o = s; //但List<String>不继承List<Object> //List<Object> list_O = list_s;//error!!!!!!!!! /***通配符***/ //调用具有"?"通配符的方法 List<Integer> list_I = new ArrayList<Integer>(); list_I.add(5555); list_I.add(6666); list_I.add(7777); //该方法可以打印整型列表,也可以打印字符串列表 printCollection(list_I); printCollection(list_S); /***调用具有"?"通配符的方法***/ //只接受父类以及子类类型的列表 List<Parent> list_Parent = new ArrayList<Parent>(); list_Parent.add(new Parent("parentOne")); list_Parent.add(new Parent("parentTwo")); list_Parent.add(new Parent("parentThree")); List<Child> list_Child = new ArrayList<Child>(); list_Child.add(new Child("childOne",20)); list_Child.add(new Child("childTwo",22)); list_Child.add(new Child("childThree",21)); printNames(list_Parent); printNames(list_Child); //不能接受其它类型的参数 //printNames(list_S);//error! /***泛型方法***/ //arrayToList方法将任意类型的对象数组变成相应的列表 Integer[] iObjs = {55,66,77,88,99}; List<Integer> result_I = arrayToList(iObjs);//转换整型数组 printCollection(result_I); String[] ss = {"temp","temptemp","hehe","he","hehehe"}; //转换字符串数组 List<String> result_S = arrayToList(ss); printCollection(result_S); //findParent方法在一组Parent对象中根据name查找Parent Parent[] parents = {new Parent("abc"),new Parent("bcd"),newParent("def")}; Parent parent = findParent(parents, "bcd"); System.out.print("找到的bcd: "+parent); Child[] children = {new Child("abc",22),new Child("bcd",23),newChild("def",22)}; Child child = findParent(children, "bcd"); System.out.print("找到的bcd: "+child); //但是不能在字符串数组中进行查找 //String sss = findparent(ss,"temp");//error! }}class Parent{ public String name; public Parent(String name){ this.name = name; } public String toString(){ return "name = " + this.name; }}class Child extends Parent{ public int age; public Child(String name,int age){ super(name); this.age = age; } public String toString(){ return super.toString() + "; age = " + age; }}
JDK5.0新特性系列---9.注释功能Annotation
import java.lang.annotation.ElementType;import java.lang.annotation.Target;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;/**关键技术 * 1.Java常用的内置注释 * @Override放在方法的修饰符前,表示该方法覆盖了父类的同名同参数方法,如果该方法没有覆盖父 类的方法而使用了该注释,则编译器会报错 * @Deprecated放在变量和方法的访问修饰符前,表示该变量或者方法已经不推荐使用,通常是因为它很危险或者存在更好的选择。 * 2.可以自定义注释类型,与enum定义枚举类型一样,使用@interface定义注释类型 * 3. 能在自定义的注释中定义其它类、属性和方法。定义属性时,以属性名为方法名,以属性类型为方法的返回值类型,方法后接default关键字表示属性的默认值。如"String name();"表示该注释类型具有name属性,类型为String;而"int age() default 20;"表示该注释具有age属性,类型为int,默认值为20. * 4. 可以在自定义的注释类型的声明中添加注释,即注释的元注释。Java有2个常用的内置元注释: * @Target指定Annotation类型可以应用的程序元素,程序元素的类型由java.lang.annotation.ElementType枚举类定义。如ElementType.METHOD表示该注释类型只能用于方法声明中。 * @Retention和Java编译器处理Annotation类型的方式有关,这些方式由java.lang.annotation.RetentionPolicy枚举值定义。如RetentionPolicy.RUNTIME表示该注释类型 将被编译成class文件 * @Document指明需要在Javadoc中包含注释(默认是不包含的) */public class AnnotationData { @Deprecated private String name; public AnnotationData(String name){ this.name = name; } //方法声明中使用了内置的@Override元数据,表示该方法覆盖了父类的同名同参数方法 //如果父类不存在该方法,则编译不会通过 @Override public String toString(){ return super.toString() + this.name; } @Override public int hashCode(){ return toString().hashCode(); } /**方法中使用了内置的@Deprecated元数据,表示该方法已经不被推荐使用了*/ @Deprecated public String getName(){ return name; } public String getAnnotationDataName(){ return this.name; } //下面定义元数据类型 //使用@interface声明Annotation类型 public @interface MyAnnotation{ //在元数据中可以定义其它类 public enum Severity{ CRITICAL,IMPORTANT,TRIVIAL,DOCUMENT }; //定义数据类型不需要定义getter和setter方法 //只需要定义一个以成员名称命名的方法,并指定返回类型为需要的数据类型 //default关键字为Annotation类型的成员设置默认值 Severity severity() default Severity.IMPORTANT; String item(); String assignedTo(); String dateAssigned(); } //使用自定义的Annotation类型,在使用时, //如果Annotation类型在其他的包下,需要与使用类一样,import它 @MyAnnotation(severity = MyAnnotation.Severity.CRITICAL, item = "Must finish this method carefully", assignedTo = "Programmer A", dateAssigned = "2006/09/10") public void doFunction(){ //do something } //下面再定义一个Annotation类型,使用了元数据的元数据 //@Retention(RetentionPolicy.RUNNTIME)这个meta-annotation //表示了此类型的annotation将编译成class文件,而且还能被虚拟机读取 //而@Target(ElementType.METHOD)表示此类型的annotation只能用于修饰方法声明 @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface MyNewAnnotation{ }}
JDK5.0新特性系列---10.监控与管理虚拟机
import java.lang.management.ClassLoadingMXBean;import java.lang.management.CompilationMXBean;import java.lang.management.GarbageCollectorMXBean;import java.lang.management.ManagementFactory;import java.lang.management.MemoryMXBean;import java.lang.management.OperatingSystemMXBean;import java.lang.management.RuntimeMXBean;import java.lang.management.ThreadMXBean;import java.util.List; /** 本例介绍如何监控和管理Java虚拟机,包括获取Java虚拟机的内存使用情况、线程数、运行时间等。 在J2SE5.0中使用Bean监控和管理Java虚拟机,java.lang.management.ManagementFactory是管理Bean的工厂类,通过它的get系列方法能够获得不同的管理Bean的实例。1. java.lang.management.MemoryMXBean: 该Bean用于管理Java虚拟机的内存系统。 一个Java虚拟机具有一个实例。2.java.lang.management.ClassLoadingMXBean: 该Bean用于管理Java虚拟机的类加载系统。 一个Java虚拟机具有一个实例。3.java.lang.management.TreadMXBean: 该Bean用于管理Java虚拟机的线程系统。 一个Java虚拟机具有一个实例。4.java.lang.management.RuntimeMXBean: 该Bean用于管理Java虚拟机的线程系统。 一个Java虚拟机具有一个实例。5.java.lang.management.OperatingSystemMXBean: 该Bean用于管理操作系统。 一个Java虚拟机具有一个实例。6.java.lang.management.CompilationMXBean: 该Bean用于管理Java虚拟机的编译系统。 一个Java虚拟机具有一个实例。7.java.lang.management.GarbageCollectorMXBean: 该Bean用于管理Java虚拟机的垃圾回收系统。 一个Java虚拟机具有一个或者多个实例。 */public class JDKMBean { public static void printMemoryMXBean(){ //获得单一实例 MemoryMXBean instance = ManagementFactory.getMemoryMXBean(); System.out.printf("%n---%s---%n",instance.getClass().getName()); //返回用于对象分配的堆的当前内存使用量 System.out.printf("%s: %s%n","HeapMemoryUsage",instance.getHeapMemoryUsage()); //返回Java虚拟机使用的非堆内存的当前使用量 System.out.printf("%s: %s%n","getNonHeapMemoryUsage",instance.getNonHeapMemoryUsage()); instance.gc(); } public static void printClassLoadingMXBean(){ //获得单一实例 ClassLoadingMXBean instance = ManagementFactory.getClassLoadingMXBean(); System.out.printf("%n---%s---%n",instance.getClass().getName()); //返回当前加载到Java虚拟机中的类的数量 System.out.printf("%s: %s%n","LoadedClassCount",instance.getLoadedClassCount()); //返回自Java虚拟机开始执行到目前已经加载的类的总数 System.out.printf("%s: %s%n","TotalLoadedClassCount",instance.getTotalLoadedClassCount()); //返回自Java虚拟机开始执行到目前已经卸载的类的总数 System.out.printf("%s: %s%n","UnloadedLoadedClassCount",instance.getUnloadedClassCount()); } public static void printThreadMXBean(){ //获得单一实例 ThreadMXBean instance = ManagementFactory.getThreadMXBean(); System.out.printf("%n---%s---%n",instance.getClass().getName()); //返回活动线程的当前数目,包括守护线程和非守护线程 System.out.printf("%s: %s%n","ThreadCount",instance.getThreadCount()); //返回活动线程ID System.out.printf("%s: %n","Thread IDs"); long[] ids = instance.getAllThreadIds(); for(long id : ids){ System.out.printf("%s; ", id); } System.out.println(); //返回活动守护线程的当前数目 System.out.printf("%s: %s%n","DaemonThreadCount",instance.getDaemonThreadCount()); //返回自从java虚拟机启动或峰值重置以来峰值活动线程计数 System.out.printf("%s: %s%n","PeakThreadCount",instance.getPeakThreadCount()); //返回当前线程的总CPU时间 System.out.printf("%s: %s%n","CurrentCpuTime",instance.getCurrentThreadCpuTime()); //返回当前线程在用户模式中执行的CPU时间 System.out.printf("%s: %s%n","CurrentThreadCpuTime",instance.getCurrentThreadUserTime()); } public static void printRuntimeMXBean(){ //获得单一实例 RuntimeMXBean instance = ManagementFactory.getRuntimeMXBean(); System.out.printf("%n---%s---%n",instance.getClass().getName()); //返回由引导类加载器用于搜索类文件的引导类路径 System.out.printf("%s: %s%n","BootClassPath",instance.getBootClassPath()); //返回系统类加载器用于搜索类文件的Java类路径 System.out.printf("%s: %s%n","ClassPath",instance.getClassPath()); //引用传递给Java虚拟机的输入变量,其中不包括传递给main方法的变量 System.out.printf("%s: %n","InputArguments"); List<String> args = instance.getInputArguments(); for(String arg : args){ System.out.printf("%s; ",arg); } //返回Java库路径 System.out.printf("%s: %s%n","LibraryPath",instance.getLibraryPath()); //返回正在运行的Java虚拟机实现的管理接口的规范版本 System.out.printf("%s: %s%n","ManagementSpecVersion",instance.getManagementSpecVersion()); //返回正在运行的Java虚拟机的名称 System.out.printf("%s: %s%n","Name",instance.getName()); //返回Java虚拟机规范名称 System.out.printf("%s: %s%n","SpecName",instance.getSpecName()); //返回Java虚拟机规范提供商 System.out.printf("%s: %s%n","SpecVendor",instance.getSpecVendor()); //返回Java虚拟机规范版本 System.out.printf("%s: %s%n","SpecVersion",instance.getSpecVersion()); //返回Java虚拟机实现名称 System.out.printf("%s: %s%n","VmName",instance.getVmName()); //返回Java虚拟机实现提供商 System.out.printf("%s: %s%n","VmVendor",instance.getVmVendor()); //返回Java虚拟机实现版本 System.out.printf("%s: %s%n","VmVersion",instance.getVmVersion()); //返回Java虚拟机的启动时间 System.out.printf("%s: %s%n","startTime",instance.getStartTime()); //返回Java虚拟机的正常运行时间 System.out.printf("%s: %s%n","Uptime",instance.getUptime()); } public static void printOperatingSystemMXBean(){ //获得单一实例 OperatingSystemMXBean instance = ManagementFactory.getOperatingSystemMXBean(); System.out.printf("%n---%s---%n",instance.getClass().getName()); //返回操作系统的架构 System.out.printf("%s: %s%n","Arch",instance.getArch()); //返回Java虚拟机可以使用的处理器数目 System.out.printf("%s: %s%n","AvailableProcessors",instance.getAvailableProcessors()); //返回操作系统名称 System.out.printf("%s: %s%n","Name",instance.getName()); //返回操作系统版本 System.out.printf("%s: %s%n","Version",instance.getVersion()); } public static void printCompilationMXBean(){ //获得单一实例 CompilationMXBean instance = ManagementFactory.getCompilationMXBean(); System.out.printf("%n---%s---%n",instance.getClass().getName()); //返回即时(JIT)编译器的名称 System.out.printf("%s: %s%n","JIT",instance.getName()); //返回在编译上花费的累积耗费时间的近似值 System.out.printf("%s: %s%n","TotalCompilationTime",instance.getTotalCompilationTime()); } public static void printGargageCollectorMXBean(){ //获得单一实例 List<GarbageCollectorMXBean> instances = ManagementFactory.getGarbageCollectorMXBeans(); System.out.printf("%n---%s---%n",GarbageCollectorMXBean.class.getName()); //遍历每个实例 for(GarbageCollectorMXBean instance : instances){ //返回垃圾收集器的名字 System.out.printf("***%s: %s***%n","Name",instance.getName()); //返回已发生的回收的总次数 System.out.printf("%s: %s%n","CollectionCount",instance.getCollectionCount()); //返回近似的累积回收时间 System.out.printf("%s: %s%n","CollectionTime",instance.getCollectionTime()); } } public static void main(String[] args){ JDKMBean.printMemoryMXBean(); JDKMBean.printClassLoadingMXBean(); JDKMBean.printThreadMXBean(); JDKMBean.printRuntimeMXBean(); JDKMBean.printOperatingSystemMXBean(); JDKMBean.printCompilationMXBean(); JDKMBean.printGargageCollectorMXBean(); }}
JDK5.0新特性系列---11.1线程 Callable和Future
import java.util.concurrent.Callable;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.Future; /** 从本节开始,主要介绍J2SE5.0与线程相关的新特性,新的线程类主要集中在java.util.concurrent包中,本节实例将介绍如何使用java.util.concurrent.Callable和java.util.concurrent.Future类,用Callable定义一个任务并启动它,然后用它的Future获取输出结果并停止它.关键技术剖析: 关于Callable和Future类的关键技术如下: 1.Callable是类似于Runnable的接口,实现Callable接口的类和实现Runable的类都是可被其他线程执行的任务. 2.Callable和Runnable的区别如下: 1)Callable定义的方法是call,而Runnable定义的方法是run. 2)Callable的call方法可以有返回值,而Runnable的run方法不能有返回值 3)Callable的call方法可抛出异常,而Runnable的run方法不能抛出异常 3.Future表示异步计算的结果,它提供了检查计算是否完成的方法,以等待计算的完成,并检索计算的结果.Future的cancel方法取消任务的执行,有一个布尔参数,参数为true表示立即中断任务的执行,参数为false表示允许正在运行的任务运行完成.Future的get方法等待计算完成,获取计算结果. */public class CallableAndFuture { /**自定义一个任务类,实现Callable接口*/ public static class MyCallableClass implements Callable{ private int flag = 0; public MyCallableClass(int flag){ this.flag = flag; } public String call() throws Exception{ if(this.flag == 0){ return "flag = 0";//如果flag的值为0,则立即返回 } if(this.flag == 1){ //如果flag的值为1,做一个无限循环 try{ while(true){ System.out.println("looping..."); Thread.sleep(2000); } }catch(InterruptedException e){ System.out.println("Interrupted"); } return "false"; }else{ throw new Exception("Bad flag value!");//flag不为1或0,则抛出异常 } } } public static void main(String... args){ //定义3个Callable类型的任务 MyCallableClass task1 = new MyCallableClass(0); MyCallableClass task2 = new MyCallableClass(1); MyCallableClass task3 = new MyCallableClass(2); //创建一个执行任务的服务 ExecutorService es = Executors.newFixedThreadPool(3); try{ //提交并执行任务,任务启动时返回了一个Future对象 //如果想得到任务执行的结果或者是异常可对这个Future对象进行操作 Future future1 = es.submit(task1); //获得第一个任务的结果,如果调用get方法,当前线程会等待任务执行完毕后才往下执行 System.out.println("task1: " + future1.get()); Future future2 = es.submit(task2); //等待5秒后,再停止第二个任务,因为第二个任务进行的是无限循环 Thread.sleep(5000); System.out.println("task2 cancel: " + future2.cancel(true)); //获取第三个任务的输出,因为执行第三个任务会引起异常 //所以下面的语句将引起异常的输出 Future future3 = es.submit(task3); System.out.println("task3: " + future3.get()); }catch(Exception e){ System.out.println(e.toString()); } es.shutdown();//立即停止任务执行服务 }}
JDK5.0新特性系列---11.2线程 任务执行架构
import java.util.concurrent.Callable;import java.util.concurrent.ExecutionException;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.Future;import java.util.concurrent.ScheduledExecutorService;import java.util.concurrent.TimeUnit; /** 在J2SE之前启动一个任务是通过调用Thread类的start方法来实现的,任务的提交和执行是同时进行的,如果想对任务的执行进行调度,或是控制同时执行的线程数量就需要额外的编写代码来完成. J2SE5.0提供了一个新的任务执行架构,可以轻松地高度和控制任务的执行,并且可以建立一个线程池来执行任务. 实例介绍如何使用新的任务执行架构,运行Runnable和Callable任务,包括定时执行任务,按规律执行任务和停止任务.关键技术剖析: 使用新的任务执行框架的关键技术如下: 1.Executor服务对象是用来执行Runnable任务的,常用的方法如下: execute方法用于执行Runnable类型的任务. 2.ExecutorService服务对象能执行和终止Callable任务,它继承了Executor,所以也能执行Runnable任务.常用的方法如下 a) submit方法用来提交Callable或Runnable任务,并返回代表此任务的Future对象. b) invokeAll方法批处理任务集合,并返回一个代表这些任务的Future对象集合 c) shutdown方法在完成自己已提交的任务后关闭服务,不再接受新任务. d) shutdownNow方法停止所有正在执行的任务并关闭服务. e) isTerminated测试是否所有任务都执行完毕了 g) isShutdown测试是否该ExecutorService已被关闭 3.ScheduledExecutorService服务对象继承ExecutorService,提供了按时间安排执行任务的功能.常用的方法如下: a)schedule(task,initDelay)方法安排所提交的Callable或Runnable任务在initDelay指定的时间后执行. b)scheduleAtFixedRate方法安排所提交的Runnable任务按指定的间隔重复执行. c)scheduleWithFixedDelay方法安排所提交的Runnable任务在每次执行完后,等待delay所指定的时间后重复执行. 4.Executors类用来创建各种服务对象,常用的方法如下: a)callable(Runnable task)方法将Runnable的任务转化成Callable的任务. b)newSingleThreadExecutor方法产生一个ExecutorService对象,这个对象带有一个线程池,线程池的大小会根据需要调整,线程执行完任务后返回线程池,供执行下一次任务使用. c)newCachedThreadPool方法会产生一个ExecutorService对象,这个对象带有一个线程池,线程池的大小会根据需要调整,线程执行完任务后返回线程池,供执行下一次任务使用. d)newFixedThreadPool(int poolSize)方法产生一个ExecutorService对象,这个对象带有一个大小为poolSize的线程池,若任务数量大于poolSize,任务会被放在一个队列里顺序执行. e)newSingleThreadScheduledExecutor方法产生一个ScheduledExecutorService对象,这个对象的线程池大小为1,若任务多于一个,任务将按先后顺序执行. f)newScheduledThreadPool(int poolSize)方法产生一个ScheduledExecutorService对象,这个对象的线程池大小为poolSize,若任务数量大于poolSize,任务会在一个队列里等待执行.*/public class ExecuteArch { /**该线程输出一行字符串*/ public static class MyThread implements Runnable{ public void run(){ System.out.println("Task repeating. " + System.currentTimeMillis()); try{ Thread.sleep(1000); }catch(InterruptedException e){ System.out.println("Task interrupted. " + System.currentTimeMillis()); } } } /**该Callable结束另一个任务*/ public static class MyCallable implements Callable{ private Future future; public MyCallable(Future future){ this.future = future; } public String call(){ System.out.println("To cancell Task..." + System.currentTimeMillis()); this.future.cancel(true); return "Task cancelled!"; } } public static void main(String... args)throwsInterruptedException,ExecutionException{ //产生一个ExcutorService对象,这个对象带有一个线程池,线程池的大小会根据需要调整 //线程执行完任务后返回线程池,供执行下一次任务使用 ExecutorService cachedService = Executors.newCachedThreadPool(); Future myThreadFuture = cachedService.submit(new MyThread()); Future myCallableFuture = cachedService.submit(newMyCallable(myThreadFuture)); System.out.println(myCallableFuture.get()); System.out.println("--------------------"); //将Runnable任务转换成 Callable任务 Callable myThreadCallable = Executors.callable(new MyThread()); Future myThreadCallableFuture = cachedService.submit(myThreadCallable); //对于Runnable任务,转换成Callable任务后,也没有返回值 System.out.println(myThreadCallableFuture.get()); cachedService.shutdownNow(); System.out.println("--------------------"); //产生一个ExecutorService对象,这个对象带有一个大小为poolSize的线程池 //若任务大于poolSize,任务会被放在一个queue里顺序执行 ExecutorService fixedService = Executors.newFixedThreadPool(2); fixedService.submit(new MyThread()); fixedService.submit(new MyThread()); //由于线程池大小为2,所以后面的任务必须等待前面的任务执行完毕后才能被执行 myThreadFuture = fixedService.submit(new MyThread()); myThreadFuture = fixedService.submit(new MyCallable(myThreadFuture)); System.out.println(myCallableFuture.get()); fixedService.shutdown(); System.out.println("--------------------"); //产生一个ScheduleExecutorService对象,这个对象的线程池大小为poolSize //若任务数量大于poolSize,任务会在一个queue里等待执行 ScheduledExecutorService fixedScheduledService = Executors.newScheduledThreadPool(2); MyThread task1 = new MyThread(); //使用任务执行服务立即执行任务1,而且此后每隔2秒执行一次任务1 myThreadFuture = fixedScheduledService.scheduleAtFixedRate(task1, 0, 2, TimeUnit.SECONDS); MyCallable task2 = new MyCallable(myThreadFuture); //使用任务执行服务等待5秒后执行任务2,执行它后将任务1关闭. myCallableFuture = fixedScheduledService.schedule(task2,5,TimeUnit.SECONDS); System.out.println(myCallableFuture.get()); fixedScheduledService.shutdownNow(); }}
JDK5.0新特性系列---11.3线程 锁Lock
import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.Future;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReadWriteLock;import java.util.concurrent.locks.ReentrantLock;import java.util.concurrent.locks.ReentrantReadWriteLock; /** 在多线程编程里,一个重要的概念是锁定,如果一个资源是多个线程共享的,为了保证数据的完整性,在进行事务性操作时需要将共享资源锁定,这样可以保证只有一个线程能对资源进行操作,从而保证了数据的完整性.在J2SE5.0以前锁定的功能是由synchronized关键字来实现的.本节实例介绍如何使用J2SE 5.0中的新特性实现锁定,包括一般的锁、读写锁等.关键技术剖析: 在J2SE5.0中的锁是由java.util.concurrent.locks.lock实现的,使用它的关键技术点如下: 1.ReentrantLock类实现了Lock接口,通过它可以完全取代synchronized关键字. 2.ReentrantLock的lock方法取得锁,如果该锁定没有被其他线程占据,则获取该锁定并返回,将保持计数器置为1;如果当前线程已经占据锁,则立即返回,将保持计数器加1;如果锁定被其他线程占据,则当前线程进入睡眠状态,等待其他线程释放锁,此时保持计数器置为1. 3.ReentrantLock的unlock方法释放锁,如果当前线程是锁的占有者,则将保持计数器减1,如果保持计数器等于0,则释放锁.如果当前线程不是锁的占有者,则抛出异常. 4.ReadWriteLock是一个继承Lock的接口,定义了读写锁.它的一个实现类是ReentrantReadWriteLock 5.ReentrantReadWriteLock的writeLock方法获得用于写入操作的锁定,当获得写入锁时,其他线程想进行读写操作都必须等待. 6.ReentrantReadWriteLock的readLock方法获得用于读操作的锁定,当获得读取锁时,其他读的线程可以继续获得读取锁,但是不能获得写入锁. */public class Lockers { /**测试Lock的使用.在方法中使用Lock,可以避免使用synchronized关键字*/ public static class LockTest{ Lock lock = new ReentrantLock();//锁 double value = 0d; //值 int addtimes = 0; /** * 增加value的值,该方法的操作分为2岁,而且相互依赖,必须实现在一个事务中 * 所以该方法必须同步,以前的做法是在方法声明中使用synchronized关键字 */ public void addValue(double v){ lock.lock();//锁住锁 System.out.println("LockTest to addValue: " + v + " " + System.currentTimeMillis()); try { Thread.sleep(1000); }catch(InterruptedException e){ } this.value += v; this.addtimes++; lock.unlock(); } public double getValue(){ return this.value; } } public static void testLockTest() throws Exception{ final LockTest lockTest = new LockTest(); //新建任务1,调用lockTest的addValue方法 Runnable task1 = new Runnable(){ public void run(){ lockTest.addValue(55.55); } }; //新建任务2,调用lockTest的getValue方法 Runnable task2 = new Runnable(){ public void run(){ System.out.println("value: " + lockTest.getValue()); } }; //新建任务执行服务 ExecutorService cachedService = Executors.newCachedThreadPool(); Future future = null; //同时执行任务1三次,由于 addValue方法使用了锁机制,所以,实质上会顺序执行 for(int i=0;i<3;i++){ future = cachedService.submit(task1); } future.get();//等待最后一个任务1被执行完 future = cachedService.submit(task2);//再执行任务2,输出结果 future.get();//等待任务2执行完后,关闭任务执行服务 cachedService.shutdownNow(); } /** * ReadWriteLock内置两个Lock,一个是读的Lock,一个是写的Lock * 多个线程可同时得到读的Lock,但只有一个线程能得到写的Lock * 而且写的Lock被锁定后,任何线程都不能得到Lock.ReadWriteLock提供的方法有: * readLock():返回一个读的Lock * writeLock():返回一个写的lock,此lock是排它的 * ReadWriteLockTest很适合处理类似文件的读写操作 * 读的时候可以同时读,但是不能写,写的时候既不能同时写,也不能读 */ public static class ReadWriteLockTest{ ReadWriteLock lock = new ReentrantReadWriteLock();//锁 double value = 0d; //值 int addtimes = 0; /**增加value的值,不允许多个线程同时进入该方法*/ public void addValue(double v){ //得到writeLock并锁定 Lock writeLock = lock.writeLock(); writeLock.lock(); System.out.println("ReadWriteLockTest to addValue: " + v + " " + System.currentTimeMillis()); try{ Thread.sleep(1000); }catch(InterruptedException e){ } try{ //做写的工作 this.value += v; this.addtimes++; }finally{ writeLock.unlock(); } } /** *获得信息.当有线程在调用addValue方法时,getInfo得到的信息可能是不正确的. *所以,也必须保证该方法在被调用时,没有方法在调用addValue方法. */ public String getInfo(){ //得到 readLock并锁定 Lock readLock = lock.readLock(); readLock.lock(); System.out.println("ReadWriteLockTest to getInfo "+System.currentTimeMillis()); try{ Thread.sleep(1000); }catch(InterruptedException e){ } try{ return this.value + " : " + this.addtimes;//做读的工作 }finally{ readLock.unlock();//释放readLock } } } public static void testReadWriteLockTest() throws Exception{ final ReadWriteLockTest readWriteLockTest = new ReadWriteLockTest(); //新建任务1,调用lockTest的addValue方法 Runnable task_1 = new Runnable(){ public void run(){ readWriteLockTest.addValue(55.55); } }; //新建任务2,调用lockTest的getValue方法 Runnable task_2 = new Runnable(){ public void run(){ System.out.println("info " + readWriteLockTest.getInfo()); } }; //新建任务任务执行服务 ExecutorService cachedService_1 = Executors.newCachedThreadPool(); Future future_1 = null; //同时执行5个任务,其中前2个任务是任务1,后两个任务是任务2 for(int i=0;i<2;i++){ future_1 = cachedService_1.submit(task_1); } for(int i=0;i<2;i++){ future_1 = cachedService_1.submit(task_2); } //最后一个任务是任务1 future_1 = cachedService_1.submit(task_1); //这5个任务的执行顺序应该是 //第一个任务1先执行,第二个任务1再执行;这是因为不能同时写,所以必须等 //然后两个任务2同时执行;这是因为在写的时候,就不能读,所以都等待写结束 //又同时可以同时读,所以它们同时执行 //最后一个任务1再执行.这是因为在读的时候,也不能写,所以必须等待读结束后, //才能写.等待最后一个任务2被执行完 future_1.get(); cachedService_1.shutdownNow(); } public static void main(String... args)throws Exception{ Lockers.testLockTest(); System.out.println("--------------------------"); Lockers.testReadWriteLockTest(); }}/** * ReentrantReadWriteLock 获取顺序如下: * This class does not impose a reader or writer preference ordering for lock access. * However, it does support an optional fairness policy. * When constructed as fair, threads contend for entry using an approximatelyarrival-order policy. * When the write lock is released either the longest-waiting single writer will beassigned the write lock, or if there is a reader waiting longer than any writer, theset of readers will be assigned the read lock. * When constructed as non-fair, the order of entry to the lock need not be in arrivalorder. * In either case, if readers are active and a writer enters the lock then nosubsequent readers will be granted the read lock until after that writer has acquiredand released the write lock.*/
JDK5.0新特性系列---11.4线程 Condition
import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.locks.Condition;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock; /** *有时候线程取得lock后需要在一定条件下才能做某些工作,比如经典的Producer和Consumer问题 *在Java 5.0以前,这种功能是由Object类的wait(),notify()和notifyAll()等方法实现的 *在5.0里面,这些功能集中到了Condition这个接口实现 *//** * 使用Condition的关键技术点如下: * 1.通过Lock的newCondition方法创建Condition的对象 * 2.Condition的await方法使当前线程进入等待状态,并且释放它占据的Lock,直到有其他的线程唤醒当前线程时,重新占有Lock. * 3.Condition的signal方法唤醒其他正在等待该Condition的线程. */public class ConditionTest { /** * 篮子程序,这里为了简化问题,篮子中最多只能有一个苹果 * Consumer必须在篮子里有苹果的时候才能吃苹果,否则它必须暂时放弃对篮子的锁定 * 等到Producer往篮子里放了苹果后再去拿来吃。 * 否则它也需要暂时解锁等Comsumer把苹果吃了才能往篮子里放苹果。 */ public static class Basket{ //锁 Lock lock = new ReentrantLock(); //根据锁产生Condition对象 Condition produced = lock.newCondition(); Condition consumed = lock.newCondition(); //篮子里的苹果数,最多为1 int num = 0; //生产苹果,往篮子里放 public void produce() throws InterruptedException{ //获得锁 lock.lock(); System.out.println("Producer get a lock..."); try{ //判断是否满足生产条件 while(num == 1){ //如果有苹果,则不生产,放弃锁,进入睡眠 //等待消费者消费 System.out.println("Producer sleep..."); consumed.await(); System.out.println("Producer awaked..."); } //生产苹果 Thread.sleep(500); System.out.println("Producer produced an Apple."); num = 1; //通知等待produced Condition的线程 produced.signal(); }finally{ lock.unlock(); } } //消费苹果,从篮子里取 public void consume() throws InterruptedException{ //获得锁 lock.lock(); System.out.println("Consumer get a lock..."); try{ //判断是否满足消费条件 while(num == 0){ //如果没有苹果,无法消费,则放弃锁,进入睡眠 //等待生产者生产苹果 System.out.println("Consumer sleep..."); produced.await(); System.out.println("Consumer awaked..."); } //吃苹果 Thread.sleep(500); System.out.println("Consumer consumed an Apple."); num = 0; //发信号唤醒某个等待consumed Condition的线程 consumed.signal(); } finally { lock.unlock(); } } } //测试Basket程序 public static void testBasket() throws Exception{ final Basket basket = new Basket(); //定义一个producer Runnable producer = new Runnable(){ public void run() { try{ basket.produce(); }catch(InterruptedException ex){ ex.printStackTrace(); } } }; //定义一个consumer Runnable consumer = new Runnable(){ public void run(){ try{ basket.consume(); }catch(InterruptedException ex){ ex.printStackTrace(); } } }; //各生产3个consumer和producer ExecutorService service = Executors.newCachedThreadPool(); for(int i = 0; i <3; i++){ service.submit(producer); } for(int i = 0;i<3;i++){ service.submit(consumer); } service.shutdown(); } public static void main(String... args)throws Exception{ ConditionTest.testBasket(); }}
JDK5.0新特性系列---11.5.1线程 同步装置之Semaphore
import java.util.ArrayList;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.Semaphore;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock; /** *Java 5.0里新增加了4个协调线程间进程的同步装置,它们分别是:Semaphore,CountDownLatch,CyclicBarrier和Exchanger *Semaphore可以控制运行线程的个数 *Semaphore是一个用来管理资源池的工具,可以看成是个通行证,线程要想从资源池拿到资源必须先拿到通行证,如果线程暂时拿不到通告证,线程就会被阻断,进入等待状态. *//** *使用Semaphore的关键技术点如下: *1.在构造Semaphore对象时,必须提供通行证的数目,如"newSemaphore(3)"将创建一个具有3个通行证的Semaphore对象,一旦该对象被创建,其通行证数量是不能改变的. *2.Semaphore的acquire方法取得一个通行证,如果通行证已经发完了,当前线程将进入等待状态,直到有其他线程释放了通行证. *3.Semaphore的release方法释放了资源池. */public class SemaphoreTest { /** * 模拟资源池的类 * 只为池发放2个通行证,即同时只允许2个线程获得池中的资源 */ public static class Pool{ ArrayList<String> pool = null; //保存资源池中的资源 Semaphore pass = null; //通行证 Lock lock = new ReentrantLock(); public Pool(int size){ //初始化资源池 pool = new ArrayList<String>(); for(int i = 0; i < size; i++){ pool.add("Resource " + i); } //发送2个通行证 pass = new Semaphore(2); } public String get()throws InterruptedException{ //获取通行证,只有得到通行证后才能得到资源 System.out.println("Try to get a pass..."); pass.acquire(); System.out.println("Got a pass"); return getResource(); } private String getResource(){ lock.lock(); String result = pool.remove(0); System.out.println("资源 " + result + "被取走"); lock.unlock(); return result; } public void put(String resource){ //归还通行证,并那还资源 System.out.println("Released a pass"); pass.release(); releaseResource(resource); } private void releaseResource(String resource){ lock.lock(); System.out.println("资源 " + resource + " 被归还"); pool.add(resource); lock.unlock(); } } public static void testPool(){ //准备10个资源的资源池 final Pool aPool = new Pool(10); Runnable worker = new Runnable(){ @Override public void run() { String resource = null; try{ resource = aPool.get();//取得resource //用resource做工作 System.out.println("I finished on " + resource); Thread.sleep(500); System.out.println("I finished on " + resource); }catch(InterruptedException ex){ } aPool.put(resource); } }; //启动5个任务 ExecutorService service = Executors.newCachedThreadPool(); for(int i = 0; i<5; i++){ service.submit(worker); } service.shutdown(); } public static void main(String... args){ SemaphoreTest.testPool(); }}
JDK5.0新特性系列---11.5.2线程 同步装置之CountDownLatch
import java.util.concurrent.CountDownLatch;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;/** * CountDownLatch维护一个计数器,等待这个CountDownLatch的线程必须等到计数器为0时才可以继续. * 以下实例模拟服务器的启动,假设启动一个服务器需要初始化3个组件,当3个组件初始化完毕后,服务器才算成功启动. *//** * 使用CountDownLatch的关键技术点如下: * 1.构造CountDownLatch对象时,需要指定计数器的初始值,该值必须大于等于0,一旦对象被创建,其初始值将不能被改变. * 2.CountDownLatch的await方法使当前线程进入等待状态,直到计数器为0 * 3.CountDownLatch的 和countDown方法使计数器减1. */public class CountDownLatchTest { /** 初始化组件的线程 */ public static class ComponentThread implements Runnable { CountDownLatch latch; //计数器 int ID; //组件ID //构造方法 public ComponentThread(CountDownLatch latch, int ID) { this.latch = latch; this.ID = ID; } public void run() { //初始化组件 System.out.println("Initializing component " + ID); try { Thread.sleep(500 * ID); } catch (InterruptedException e) {} System.out.println("Component " + ID + " initialized!"); latch.countDown(); //将计数器减1 } } /** 启动服务器 */ public static void startServer() throws Exception { System.out.println("Server is starting."); //初始化一个初始值为3的CountDownLatch CountDownLatch latch = new CountDownLatch(3); //启动3个线程分别去3个组件 ExecutorService service = Executors.newCachedThreadPool(); service.submit(new ComponentThread(latch, 1)); service.submit(new ComponentThread(latch, 2)); service.submit(new ComponentThread(latch, 3)); service.shutdown(); latch.await();//等待3个组件的初始化工作都完成 System.out.println("Server is up!");//当所需的三个组件都完成时,Server就可以继续了 } public static void main(String... args) throws Exception { CountDownLatchTest.startServer(); }}
JDK5.0新特性系列---11.5.3线程 同步装置之CyclicBarrier
import java.util.Random;import java.util.concurrent.CyclicBarrier; /** * CyclicBarrier维持一个计数器,与CountDownLatch不同的是,等待这个CyclicBarrier的线程必须等到计数器的某个值时,才可以继续. * CyclicBarrier就像它名字的意思一样,可看成是个障碍,所有的线程必须到齐后才能一起通过这个障碍. *//** * 本实例实现一个数组相邻元素的加法,一个线程给数组的第一个元素赋值,然后等待其它线程给数组第二个元素赋值,然后将第一个元素和第二个元素相加. *//** * CyclicBarrier的关键技术点如下: * 1.构造CyclicBarrier对象时,需要指定计数器的目标值,计数器的初始值为0. * 还可以在构造方法中带一个 Runnable参数,表示当计数器到达目标值是,在等待CyclicBarrier的线程被唤醒之前,指定该Runnable任务. * 2.CyclicBarrier的await方法使当前线程进入等待状态,同时将计数器值加1,当计数器到达目标值时,当前线程被唤醒. */public class CyclicBarrierTest { public static class ComponentThread implements Runnable{ CyclicBarrier barrier;//计数器 int ID;//组件 int[] array; //数据数组 public ComponentThread(CyclicBarrier barrier,int[] array,int ID){ this.barrier = barrier; this.ID = ID; this.array = array; } public void run(){ try{ //Random的nextInt(int n)方法返回一个[0,n)范围内的随机数 array[ID] = new Random().nextInt(100); System.out.println("Componet " + ID + " sleep..."); barrier.await(); System.out.println("Componet " + ID + " awaked..."); //计算数据数组中的当前值和后续值 int result = array[ID] + array[ID + 1]; System.out.println("Component " + ID + " result: " + result); }catch(Exception ex){ } } } /**测试CyclicBarrier的用法*/ public static void testCyclicBarrier(){ final int[] array = new int[3]; CyclicBarrier barrier = new CyclicBarrier(2,new Runnable(){ public void run(){ System.out.println("testCyclicBarrier run..."); array[2] = array[0] + array[1]; } }); //启动线程 new Thread(new ComponentThread(barrier,array,0)).start(); new Thread(new ComponentThread(barrier,array,1)).start(); } public static void main(String... args){ CyclicBarrierTest.testCyclicBarrier(); }}
JDK5.0新特性系列---11.5.4线程 同步装置之Exchanger
/** * Exchanger让两个线程互换信息 * 实例模拟服务生和顾客,服务生往空杯子中倒水,顾客从装满水的杯子中喝水,然后互换杯子,服务生接着倒水,顾客接着喝水. *//** * 使用Exchanger的关键技术点如下: * 1.初始化Exchanger对象时,可以通过泛型指定杯子能交换的信息类型.如"new Exchanger<String>;"表示只能交换String类型的信息 * 2.Exchanger的exchange方法表示当前线程准备交换信息,等待其他线程与它交换信息.当有其他线程调用该Exchanger对象的exchange方法时,立即交换信息 */public class ExchangerTest { //描述一个装水的杯子 public static class Cup{ private boolean full = false; //标识杯子是否有水 public Cup(boolean full){ this.full = full; } //添水,假设需要5s public void addWater(){ if(!this.full){ try{ Thread.sleep(5000); }catch(InterruptedException e){ } this.full = true; } } //喝水,假设需要10s public void drinkWater(){ if(this.full){ try{ Thread.sleep(10000); }catch(InterruptedException e){ } this.full = false; } } } public static void testExchanger(){ //初始化一个Exchanger,并规定可交换的信息类型是杯子 final Exchanger<Cup> exchanger = new Exchanger<Cup>(); //初始化一个空的杯子和装满水的杯子 final Cup initialEmptyCup = new Cup(false); final Cup initialFullCup = new Cup(true); //服务生线程 class Waiter implements Runnable{ public void run(){ Cup currentCup = initialEmptyCup; try{ int i = 0; while(i < 2){ System.out.println("服务生开始往杯子里倒水: " + System.currentTimeMillis()); //往空的杯子里倒水 currentCup.addWater(); System.out.println("服务生添水完毕: " + System.currentTimeMillis()); //杯子满后和顾客的空杯子交换 System.out.println("服务生等待与顾客交换杯子: " + System.currentTimeMillis()); currentCup = exchanger.exchange(currentCup); System.out.println("服务生与顾客交换杯子完毕: " + System.currentTimeMillis()); i++; } }catch(InterruptedException ex){ } } } //顾客线程 class Customer implements Runnable{ public void run(){ Cup currentCup = initialFullCup; try{ int i = 0; while(i < 2){ System.out.println("顾客开始喝水: " + System.currentTimeMillis()); //把杯子里的水喝掉 currentCup.drinkWater(); System.out.println("顾客喝水完毕: " + System.currentTimeMillis()); //将空杯子和服务生的满杯子交换 System.out.println("顾客等待与服务生交换杯子: " + System.currentTimeMillis()); exchanger.exchange(currentCup); System.out.println("顾客与服务生交换杯子完毕: " + System.currentTimeMillis()); i++; } }catch(InterruptedException ex){ } } } new Thread(new Waiter()).start(); new Thread(new Customer()).start(); } public static void main(String... args){ ExchangerTest.testExchanger(); }}/**Waiter是模拟服务生的线程,首先往空杯子中添水,然后调用Exchanger的exchange方法,等待和别人交换杯子.Customer是模拟了顾客的线程,首先把装满水的杯子喝光,然后调用Exchange的exchange方法,等待和别人交换杯子.当服务生和顾客都准备交换杯子时,由Exchanger将服务生手中装满水的杯子和顾客手中的空杯子交换.服务生可以继续倒水,而顾客可以继续喝水.*/
JDK5.0新特性系列---11.6线程 BlockingQueue
importjava.util.concurrent.ArrayBlockingQueue;import java.util.concurrent.BlockingQueue;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors; /** 本例介绍一个特殊的队列:BlockingQueue,如果BlockQueue是空的,从BlockingQueue取东西的操作将会被阻断进入等待状态,直到BlockingQueue进了东西才会被唤醒.同样,如果BlockingQueue是满的,任何试图往里存东西的操作也会被阻断进入等待状态,直到BlockingQueue里有空间才会被唤醒继续操作. 本例再次实现11.4线程----条件Condition中介绍的篮子程序,不过这个篮子中最多能放的苹果数不是1,可以随意指定.当篮子满时,生产者进入等待状态,当篮子空时,消费者等待. *//** 使用BlockingQueue的关键技术点如下: 1.BlockingQueue定义的常用方法如下: 1)add(anObject):把anObject加到BlockingQueue里,即如果BlockingQueue可以容纳,则返回true,否则招聘异常 2)offer(anObject):表示如果可能的话,将anObject加到BlockingQueue里,即如果BlockingQueue可以容纳,则返回true,否则返回false. 3)put(anObject):把anObject加到BlockingQueue里,如果BlockQueue没有空间,则调用此方法的线程被阻断直到BlockingQueue里面有空间再继续. 4)poll(time):取走BlockingQueue里排在首位的对象,若不能立即取出,则可以等time参数规定的时间,取不到时返回null 5)take():取走BlockingQueue里排在首位的对象,若BlockingQueue为空,阻断进入等待状态直到Blocking有新的对象被加入为止 2.BlockingQueue有四个具体的实现类,根据不同需求,选择不同的实现类 1)ArrayBlockingQueue:规定大小的BlockingQueue,其构造函数必须带一个int参数来指明其大小.其所含的对象是以FIFO(先入先出)顺序排序的. 2)LinkedBlockingQueue:大小不定的BlockingQueue,若其构造函数带一个规定大小的参数,生成的BlockingQueue有大小限制,若不带大小参数,所生成的BlockingQueue的大小由Integer.MAX_VALUE来决定.其所含的对象是以FIFO(先入先出)顺序排序的 3)PriorityBlockingQueue:类似于LinkedBlockQueue,但其所含对象的排序不是FIFO,而是依据对象的自然排序顺序或者是构造函数的Comparator决定的顺序. 4)SynchronousQueue:特殊的BlockingQueue,对其的操作必须是放和取交替完成的. 3.LinkedBlockingQueue和ArrayBlockingQueue比较起来,它们背后所用的数据结构不一样,导致LinkedBlockingQueue的数据吞吐量要大于ArrayBlockingQueue,但在线程数量很大时其性能的可预见性低于ArrayBlockingQueue. */public class BlockingQueueTest { /**定义装苹果的篮子*/ public static class Basket{ //篮子,能够容纳3个苹果 BlockingQueue<String> basket = new ArrayBlockingQueue<String>(3); //生产苹果,放入篮子 public void produce() throws InterruptedException{ //put方法放入一个苹果,若basket满了,等到basket有位置 basket.put("An apple"); } //消费苹果,从篮子中取走 public String consume() throws InterruptedException{ //take方法取出一个苹果,若basket为空,等到basket有苹果为止 return basket.take(); } } //测试方法 public static void testBasket(){ final Basket basket = new Basket();//建立一个装苹果的篮子 //定义苹果生产者 class Producer implements Runnable{ public void run(){ try{ while(true){ //生产苹果 System.out.println("生产者准备生产苹果: " + System.currentTimeMillis()); basket.produce(); System.out.println("生产者生产苹果完毕: " + System.currentTimeMillis()); //休眠300ms Thread.sleep(300); } }catch(InterruptedException ex){ } } } //定义苹果消费者 class Consumer implements Runnable{ public void run(){ try{ while(true){ //消费苹果 System.out.println("消费者准备消费苹果: " + System.currentTimeMillis()); basket.consume(); System.out.println("消费者消费苹果完毕: " + System.currentTimeMillis()); //休眠1000ms Thread.sleep(1000); } }catch(InterruptedException ex){ } } } ExecutorService service = Executors.newCachedThreadPool(); Producer producer = new Producer(); Consumer consumer = new Consumer(); service.submit(producer); service.submit(consumer); //程序运行5s后,所有任务停止 try{ Thread.sleep(5000); }catch(InterruptedException ex){ } service.shutdownNow(); } public static void main(String[] args){ BlockingQueueTest.testBasket(); }}
0 0
- Java SE 5 新特性
- Java SE 5 新特性
- Java SE 5 也支持 Java SE 6新特性
- Java SE 6 新特性
- Java SE 7 新特性
- Java SE 6 新特性
- Java SE 7 新特性
- Java SE 6 的新特性
- Java SE 6 的新特性
- Java SE 6 新特性: Instrumentation 新功能
- Java SE 6 新特性: Instrumentation 新功能
- Java SE 6.0新特性体验
- Java SE 6 新特性系列
- Java SE 6 新特性: HTTP 增强
- 了解 Java SE 6 新特性系列
- Java SE 6 新特性系列
- 了解 Java SE 6 新特性系列
- Java SE 6 新特性: Instrumentation 新功能
- 折腾paramiko
- 【Error】Python:SyntaxError: Non-ASCII character '\xe5'解决方法
- 指称语义简介
- android 只允许运行一个Activity实例
- C#实现google Pr查询的方法
- Java SE 5 新特性
- Log4j 实现运行时的日志级别更改
- web.config 配置
- Linux zip command
- 经典C语言程序设计100例 -- C 和 Python 版 (01 - 05)
- wxWidgets初体验
- android示例之AppWidget(1)
- Thirft框架介绍
- [POJ][1015]Jury Compromise