首页
留言
关于
友链
更多
足迹
Search
1
SpringMVC+Spring+MyBatis整合完整版Web实例(附数据)
2,910 阅读
2
关于在Flutter实现Google地图的方法
1,879 阅读
3
druid报异常 “sql injection violation, part alway true condition not allow”的解决方案
1,375 阅读
4
git删除remote
1,338 阅读
5
MyBatis的TooManyResultsException异常的解决办法
1,149 阅读
发现
技术
生活
户外
登录
Search
标签搜索
Git
JavaScript
Flutter
Oracle
Git学习
Java
MySQL
SQL Server
秦岭户外
IntelliJ IDEA
Spring Boot
Flutter 2.0
对称加密算法
Google地图
Maven
ES6
linux
Tomcat
Redis
Spring
Bai Keyang
累计撰写
288
篇文章
累计收到
277
条评论
首页
栏目
发现
技术
生活
户外
页面
留言
关于
友链
足迹
搜索到
8
篇与
Java
的结果
2022-09-28
java split | (竖线)
java split | (竖线)在java中,有一个字符串"866|10|1824261|214|",需要根据“|”截取。而直接用split("|");是不行的。原因是竖线 | 在正则中是特殊字符,需要转义,也就是split(“|”);但实际在java中使用时,又是java的特殊字符,需要转义,最终变成了split("\|");public static void main(String[] args) { String line = "866|10|1824261|214|"; String[] cells = line.split("\\|"); for(int i=0;i<cells.length;i++){ System.out.println(cells[i]); } }输出结果:866 10 1824261 214原文来自: CSDN
2022年09月28日
148 阅读
0 评论
0 点赞
2018-05-26
[转]35 个 Java 代码性能优化总结
前言代码优化,一个很重要的课题。可能有些人觉得没用,一些细小的地方有什么好修改的,改与不改对于代码的运行效率有什么影响呢?这个问题我是这么考虑的,就像大海里面的鲸鱼一样,它吃一条小虾米有用吗?没用,但是,吃的小虾米一多之后,鲸鱼就被喂饱了。代码优化也是一样,如果项目着眼于尽快无BUG上线,那么此时可以抓大放小,代码的细节可以不精打细磨;但是如果有足够的时间开发、维护代码,这时候就必须考虑每个可以优化的细节了,一个一个细小的优化点累积起来,对于代码的运行效率绝对是有提升的。代码优化的目标是:1、减小代码的体积2、提高代码运行的效率代码优化细节1、尽量指定类、方法的final修饰符带有final修饰符的类是不可派生的。在Java核心API中,有许多应用final的例子,例如java.lang.String,整个类都是final的。为类指定final修饰符可以让类不可以被继承,为方法指定final修饰符可以让方法不可以被重写。如果指定了一个类为final,则该类所有的方法都是final的。Java编译器会寻找机会内联所有的final方法,内联对于提升Java运行效率作用重大,具体参见Java运行期优化。此举能够使性能平均提高50%。2、尽量重用对象特别是String对象的使用,出现字符串连接时应该使用StringBuilder/StringBuffer代替。由于Java虚拟机不仅要花时间生成对象,以后可能还需要花时间对这些对象进行垃圾回收和处理,因此,生成过多的对象将会给程序的性能带来很大的影响。3、尽可能使用局部变量调用方法时传递的参数以及在调用中创建的临时变量都保存在栈中速度较快,其他变量,如静态变量、实例变量等,都在堆中创建,速度较慢。另外,栈中创建的变量,随着方法的运行结束,这些内容就没了,不需要额外的垃圾回收。4、及时关闭流Java编程过程中,进行数据库连接、I/O流操作时务必小心,在使用完毕后,及时关闭以释放资源。因为对这些大对象的操作会造成系统大的开销,稍有不慎,将会导致严重的后果。5、尽量减少对变量的重复计算明确一个概念,对方法的调用,即使方法中只有一句语句,也是有消耗的,包括创建栈帧、调用方法时保护现场、调用方法完毕时恢复现场等。所以例如下面的操作:for (int i = 0; i < list.size(); i++){ // TODO ... }建议替换为:for (int i = 0, int length = list.size(); i < length; i++){ //TODO ... }这样,在list.size()很大的时候,就减少了很多的消耗6、尽量采用懒加载的策略,即在需要的时候才创建例如:String str = "aaa"; if (i == 1){ list.add(str); }建议替换为:if (i == 1){ String str = "aaa"; list.add(str); }7、慎用异常异常对性能不利。抛出异常首先要创建一个新的对象,Throwable接口的构造函数调用名为fillInStackTrace()的本地同步方法,fillInStackTrace()方法检查堆栈,收集调用跟踪信息。只要有异常被抛出,Java虚拟机就必须调整调用堆栈,因为在处理过程中创建了一个新的对象。异常只能用于错误处理,不应该用来控制程序流程。8、不要在循环中使用try…catch…,应该把其放在最外层除非不得已。如果毫无理由地这么写了,只要你的领导资深一点、有强迫症一点,八成就要骂你为什么写出这种垃圾代码来了9、如果能估计到待添加的内容长度,为底层以数组方式实现的集合、工具类指定初始长度比如ArrayList、LinkedLlist、StringBuilder、StringBuffer、HashMap、HashSet等等,以StringBuilder为例:(1)StringBuilder() // 默认分配16个字符的空间(2)StringBuilder(int size) // 默认分配size个字符的空间(3)StringBuilder(String str) // 默认分配16个字符+str.length()个字符空间可以通过类(这里指的不仅仅是上面的StringBuilder)的来设定它的初始化容量,这样可以明显地提升性能。比如StringBuilder吧,length表示当前的StringBuilder能保持的字符数量。因为当StringBuilder达到最大容量的时候,它会将自身容量增加到当前的2倍再加2,无论何时只要StringBuilder达到它的最大容量,它就不得不创建一个新的字符数组然后将旧的字符数组内容拷贝到新字符数组中—-这是十分耗费性能的一个操作。试想,如果能预估到字符数组中大概要存放5000个字符而不指定长度,最接近5000的2次幂是4096,每次扩容加的2不管,那么:(1)在4096 的基础上,再申请8194个大小的字符数组,加起来相当于一次申请了12290个大小的字符数组,如果一开始能指定5000个大小的字符数组,就节省了一倍以上的空间(2)把原来的4096个字符拷贝到新的的字符数组中去这样,既浪费内存空间又降低代码运行效率。所以,给底层以数组实现的集合、工具类设置一个合理的初始化容量是错不了的,这会带来立竿见影的效果。但是,注意,像HashMap这种是以数组+链表实现的集合,别把初始大小和你估计的大小设置得一样,因为一个table上只连接一个对象的可能性几乎为0。初始大小建议设置为2的N次幂,如果能估计到有2000个元素,设置成new HashMap(128)、new HashMap(256)都可以。10、当复制大量数据时,使用System.arraycopy()命令11、乘法和除法使用移位操作例如:for (val = 0; val < 100000; val += 5){ a = val * 8; b = val / 2; }用移位操作可以极大地提高性能,因为在计算机底层,对位的操作是最方便、最快的,因此建议修改为:for (val = 0; val < 100000; val += 5){ a = val << 3; b = val >> 1; }移位操作虽然快,但是可能会使代码不太好理解,因此最好加上相应的注释。12、循环内不要不断创建对象引用例如:for (int i = 1; i <= count; i++){ Object obj = new Object(); }这种做法会导致内存中有count份Object对象引用存在,count很大的话,就耗费内存了,建议为改为:Object obj = null; for (int i = 0; i <= count; i++) { obj = new Object(); }这样的话,内存中只有一份Object对象引用,每次new Object()的时候,Object对象引用指向不同的Object罢了,但是内存中只有一份,这样就大大节省了内存空间了。13、基于效率和类型检查的考虑,应该尽可能使用array,无法确定数组大小时才使用ArrayList14、尽量使用HashMap、ArrayList、StringBuilder,除非线程安全需要,否则不推荐使用Hashtable、Vector、StringBuffer,后三者由于使用同步机制而导致了性能开销15、不要将数组声明为public static final因为这毫无意义,这样只是定义了引用为static final,数组的内容还是可以随意改变的,将数组声明为public更是一个安全漏洞,这意味着这个数组可以被外部类所改变16、尽量在合适的场合使用单例使用单例可以减轻加载的负担、缩短加载的时间、提高加载的效率,但并不是所有地方都适用于单例,简单来说,单例主要适用于以下三个方面:(1)控制资源的使用,通过线程同步来控制资源的并发访问(2)控制实例的产生,以达到节约资源的目的(3)控制数据的共享,在不建立直接关联的条件下,让多个不相关的进程或线程之间实现通信17、尽量避免随意使用静态变量要知道,当某个对象被定义为static的变量所引用,那么gc通常是不会回收这个对象所占有的堆内存的,如:/** * Java学习交流QQ群:589809992 我们一起学Java! */ public class A{ private static B b = new B(); }此时静态变量b的生命周期与A类相同,如果A类不被卸载,那么引用B指向的B对象会常驻内存,直到程序终止18、及时清除不再需要的会话为了清除不再活动的会话,许多应用服务器都有默认的会话超时时间,一般为30分钟。当应用服务器需要保存更多的会话时,如果内存不足,那么操作系统会把部分数据转移到磁盘,应用服务器也可能根据MRU(最近最频繁使用)算法把部分不活跃的会话转储到磁盘,甚至可能抛出内存不足的异常。如果会话要被转储到磁盘,那么必须要先被序列化,在大规模集群中,对对象进行序列化的代价是很昂贵的。因此,当会话不再需要时,应当及时调用HttpSession的invalidate()方法清除会话。19、实现RandomAccess接口的集合比如ArrayList,应当使用最普通的for循环而不是foreach循环来遍历这是JDK推荐给用户的。JDK API对于RandomAccess接口的解释是:实现RandomAccess接口用来表明其支持快速随机访问,此接口的主要目的是允许一般的算法更改其行为,从而将其应用到随机或连续访问列表时能提供良好的性能。实际经验表明,实现RandomAccess接口的类实例,假如是随机访问的,使用普通for循环效率将高于使用foreach循环;反过来,如果是顺序访问的,则使用Iterator会效率更高。可以使用类似如下的代码作判断:if (list instanceof RandomAccess) { for (int i = 0; i < list.size(); i++){ // TODO ... } }else{ Iterator<?> iterator = list.iterable(); while (iterator.hasNext()){ iterator.next(); } }foreach循环的底层实现原理就是迭代器Iterator,参见Java语法糖1:可变长度参数以及foreach循环原理。所以后半句”反过来,如果是顺序访问的,则使用Iterator会效率更高”的意思就是顺序访问的那些类实例,使用foreach循环去遍历。20、使用同步代码块替代同步方法这点在多线程模块中的synchronized锁方法块一文中已经讲得很清楚了,除非能确定一整个方法都是需要进行同步的,否则尽量使用同步代码块,避免对那些不需要进行同步的代码也进行了同步,影响了代码执行效率。21、将常量声明为static final,并以大写命名这样在编译期间就可以把这些内容放入常量池中,避免运行期间计算生成常量的值。另外,将常量的名字以大写命名也可以方便区分出常量与变量22、不要创建一些不使用的对象,不要导入一些不使用的类这毫无意义,如果代码中出现”The value of the local variable i is not used”、”The import java.util is never used”,那么请删除这些无用的内容23、程序运行过程中避免使用反射关于,请参见反射。反射是Java提供给用户一个很强大的功能,功能强大往往意味着效率不高。不建议在程序运行过程中使用尤其是频繁使用反射机制,特别是Method的invoke方法,如果确实有必要,一种建议性的做法是将那些需要通过反射加载的类在项目启动的时候通过反射实例化出一个对象并放入内存—-用户只关心和对端交互的时候获取最快的响应速度,并不关心对端的项目启动花多久时间。24、使用数据库连接池和线程池这两个池都是用于重用对象的,前者可以避免频繁地打开和关闭连接,后者可以避免频繁地创建和销毁线程25、使用带缓冲的输入输出流进行IO操作带缓冲的输入输出流,即BufferedReader、BufferedWriter、BufferedInputStream、BufferedOutputStream,这可以极大地提升IO效率26、顺序插入和随机访问比较多的场景使用ArrayList,元素删除和中间插入比较多的场景使用LinkedList这个,理解ArrayList和LinkedList的原理就知道了27、不要让public方法中有太多的形参public方法即对外提供的方法,如果给这些方法太多形参的话主要有两点坏处:1、违反了面向对象的编程思想,Java讲求一切都是对象,太多的形参,和面向对象的编程思想并不契合2、参数太多势必导致方法调用的出错概率增加至于这个”太多”指的是多少个,3、4个吧。比如我们用JDBC写一个insertStudentInfo方法,有10个学生信息字段要插如Student表中,可以把这10个参数封装在一个实体类中,作为insert方法的形参28、字符串变量和字符串常量equals的时候将字符串常量写在前面这是一个比较常见的小技巧了,如果有以下代码:String str = "123"; if (str.equals("123")) { // TODO ... }建议修改为:String str = "123"; if ("123".equals(str)){ // TODO ... } 这么做主要是可以避免空指针异常29、请知道,在java中if (i == 1)和if (1 == i)是没有区别的,但从阅读习惯上讲,建议使用前者平时有人问,”if (i == 1)”和”if (1== i)”有没有区别,这就要从C/C++讲起。在C/C++中,”if (i == 1)”判断条件成立,是以0与非0为基准的,0表示false,非0表示true,如果有这么一段代码:int i = 2; if (i == 1){ // TODO ... } else { // TODO ... }C/C++判断”i==1″不成立,所以以0表示,即false。但是如果:int i = 2;if (i = 1) { // TODO ... }else{ // TODO ... }万一程序员一个不小心,把”if (i == 1)”写成”if (i = 1)”,这样就有问题了。在if之内将i赋值为1,if判断里面的内容非0,返回的就是true了,但是明明i为2,比较的值是1,应该返回的false。这种情况在C/C++的开发中是很可能发生的并且会导致一些难以理解的错误产生,所以,为了避免开发者在if语句中不正确的赋值操作,建议将if语句写为:int i = 2; if (1 == i) { // TODO ... }else{ // TODO ... }这样,即使开发者不小心写成了”1 = i”,C/C++编译器也可以第一时间检查出来,因为我们可以对一个变量赋值i为1,但是不能对一个常量赋值1为i。但是,在Java中,C/C++这种”if (i = 1)”的语法是不可能出现的,因为一旦写了这种语法,Java就会编译报错”Type mismatch: cannot convert from int to boolean”。但是,尽管Java的”if (i == 1)”和”if (1 == i)”在语义上没有任何区别,但是从阅读习惯上讲,建议使用前者会更好些。30、不要对数组使用toString()方法看一下对数组使用toString()打印出来的是什么:public static void main(String[] args){ int[] is = new int[]{1, 2, 3}; System.out.println(is.toString()); }结果是:[I@18a992f本意是想打印出数组内容,却有可能因为数组引用is为空而导致空指针异常。不过虽然对数组toString()没有意义,但是对集合toString()是可以打印出集合里面的内容的,因为集合的父类AbstractCollections重写了Object的toString()方法。31、不要对超出范围的基本数据类型做向下强制转型这绝不会得到想要的结果:public static void main(String[] args){ long l = 12345678901234L; int i = (int)l; System.out.println(i); }我们可能期望得到其中的某几位,但是结果却是:1942892530解释一下。Java中long是8个字节64位的,所以12345678901234在计算机中的表示应该是:0000 0000 0000 0000 0000 1011 0011 1010 0111 0011 1100 1110 0010 1111 1111 0010一个int型数据是4个字节32位的,从低位取出上面这串二进制数据的前32位是:0111 0011 1100 1110 0010 1111 1111 0010这串二进制表示为十进制1942892530,所以就是我们上面的控制台上输出的内容。从这个例子上还能顺便得到两个结论:1、整型默认的数据类型是int,long l = 12345678901234L,这个数字已经超出了int的范围了,所以最后有一个L,表示这是一个long型数。顺便,浮点型的默认类型是double,所以定义float的时候要写成”"float f = 3.5f”2、接下来再写一句”int ii = l + i;”会报错,因为long + int是一个long,不能赋值给int32、公用的集合类中不使用的数据一定要及时remove掉如果一个集合类是公用的(也就是说不是方法里面的属性),那么这个集合里面的元素是不会自动释放的,因为始终有引用指向它们。所以,如果公用集合里面的某些数据不使用而不去remove掉它们,那么将会造成这个公用集合不断增大,使得系统有内存泄露的隐患。33、把一个基本数据类型转为字符串,基本数据类型.toString()是最快的方式、String.valueOf(数据)次之、数据+”"最慢把一个基本数据类型转为一般有三种方式,我有一个Integer型数据i,可以使用i.toString()、String.valueOf(i)、i+”"三种方式,三种方式的效率如何,看一个测试:public static void main(String[] args){ int loopTime = 50000; Integer i = 0; long startTime = System.currentTimeMillis(); for (int j = 0; j < loopTime; j++){ String str = String.valueOf(i); } System.out.println("String.valueOf():" + (System.currentTimeMillis() - startTime) + "ms"); startTime = System.currentTimeMillis(); for (int j = 0; j < loopTime; j++) { String str = i.toString(); } System.out.println("Integer.toString():" + (System.currentTimeMillis() - startTime) + "ms"); startTime = System.currentTimeMillis(); for (int j = 0; j < loopTime; j++) { String str = i + ""; } System.out.println("i + \"\":" + (System.currentTimeMillis() - startTime) + "ms"); }运行结果为:String.valueOf():11ms Integer.toString():5ms i + "":25ms 所以以后遇到把一个基本数据类型转为String的时候,优先考虑使用toString()方法。至于为什么,很简单:1、String.valueOf()方法底层调用了Integer.toString()方法,但是会在调用前做空判断2、Integer.toString()方法就不说了,直接调用了3、i + “”底层使用了StringBuilder实现,先用append方法拼接,再用toString()方法获取字符串三者对比下来,明显是2最快、1次之、3最慢34、使用最有效率的方式去遍历Map遍历Map的方式有很多,通常场景下我们需要的是遍历Map中的Key和Value,那么推荐使用的、效率最高的方式是:public static void main(String[] args){ HashMap<String, String> hm = new HashMap<String, String>(); hm.put("111", "222"); Set<Map.Entry<String, String>> entrySet = hm.entrySet(); Iterator<Map.Entry<String, String>> iter = entrySet.iterator(); while (iter.hasNext()){ Map.Entry<String, String> entry = iter.next(); System.out.println(entry.getKey() + "\t" + entry.getValue()); } }如果你只是想遍历一下这个Map的key值,那用”Set keySet = hm.keySet();”会比较合适一些35、对资源的close()建议分开操作意思是,比如我有这么一段代码:try{ XXX.close(); YYY.close(); }catch (Exception e) { // TODO ... }建议修改为:try{ XXX.close(); }catch (Exception e) { // TODO ... } try{ YYY.close(); }catch (Exception e) { // TODO ... }虽然有些麻烦,却能避免资源泄露。我们想,如果没有修改过的代码,万一XXX.close()抛异常了,那么就进入了cath块中了,YYY.close()不会执行,YYY这块资源就不会回收了,一直占用着,这样的代码一多,是可能引起资源句柄泄露的。而改为下面的写法之后,就保证了无论如何XXX和YYY都会被close掉。原文链接:https://juejin.im/post/59d982716fb9a00a600f9e8d
2018年05月26日
273 阅读
0 评论
0 点赞
2018-05-11
Java依赖注入标准(javax.inject)使用
Spring自带的@Autowired的缺省情况等价于JSR-330的@Inject注解; Spring自带的@Qualifier的缺省的根据Bean名字注入情况等价于JSR-330的@Named注解; Spring自带的@Qualifier的扩展@Qualifier限定描述符注解情况等价于JSR-330的@Qualifier注解。用过Spring框架的我们都知道,每当生成依赖注入的时候,我们都必须生成相应类的set方法,而且要在set方法上面写上@Autowired,才能实现依赖注入,如下:package com.kaishengit.web; import com.kaishengit.service.ProjectService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; @Controller public class FolderController { private ProjectService projectService; //set @Autowired public void setProjectService(ProjectService projectService) { this.projectService = projectService; } }每次都要生成相应的set方法感觉好麻烦,现在如果我们使用javax.inject.jar,只需要在相应类的属性上面加上@Inject,如下代码:package com.kaishengit.web; import com.kaishengit.service.ProjectService; import org.springframework.stereotype.Controller; import javax.inject.Inject; @Controller public class FolderController { @Inject private ProjectService projectService; }Maven引入:<!-- JSR-330依赖注入 --> <dependency> <groupId>javax.inject</groupId> <artifactId>javax.inject</artifactId> <version>1</version> </dependency>引入参考资料:@Inject@Inject支持构造函数、方法和字段注解,也可能使用于静态实例成员。可注解成员可以是任意修饰符(private,package-private,protected,public)。注入顺序:构造函数、字段,然后是方法。父类的字段和方法注入优先于子类的字段和方法,同一类中的字段和方法是没有顺序的。@Inject注解的构造函数可以是无参或多个参数的构造函数。@Inject每个类中最多注解一个构造函数。参考资料地址:资料1:https://www.cnblogs.com/langtianya/p/4702757.html(推荐)资料2:http://www.xuetimes.com/archives/840
2018年05月11日
797 阅读
0 评论
0 点赞
2018-05-10
[温故]Java 字符串对象池的作用
从Java5.0开始,Java虚拟机在启动的时候回实例化9个对象池。这9个对象池分别用来存储8中基本类型的包装类对象和String对象,主要是为了效率问题。大家应该知道,创建String对象有俩种办法,代码如下:String str1="hello"; String str2=new String("hello");这俩种创建String对象的方法有什么区别吗?区别就在于第一种方法在对象池中取对象,第二种方法直接生成新的对象。当在程序中直接用双引号引起来一个字符串时,Java虚拟机(JVM)就到String的对象池中就去检查是否有一个值相同的对象。如果有,就取现成的对象;如果没有则在对象池中创建,并返回其引用。因此,可以发现下面的代码输出true:String str1="hello"; String str2="hello"; System.out.printn(str1==str2);//true这说明str1和str2指向同一个对象,因为它们都是在对象池中取到的,而下面的代码输出为falseString str1="hello"; String str2=new String("hello"); System.out.printn(str1==str2);//false因为在任何情况下,只要new一个String对象都是创建了新的对象。其他的基本类型包装类的对象池与此类似,工作原理是一样的。对象池的存在是为了避免频繁的创建和销毁对象而影响系统性能,那么自定义的类是否也可以使用对象池呢?当然可以,我这里就不写代码了~ 原文链接:https://blog.csdn.net/qq_33642117/article/details/52214236
2018年05月10日
269 阅读
1 评论
0 点赞
2018-04-18
[转]JAVA多线程实现的四种方式
这是网上一篇关于介绍多线程的一篇文章,个人感觉总结的很不错。顺便自己也收藏了。Java多线程实现方式主要有四种:继承Thread类、实现Runnable接口、实现Callable接口通过FutureTask包装器来创建Thread线程、使用ExecutorService、Callable、Future实现有返回结果的多线程。 其中前两种方式线程执行完后都没有返回值,后两种是带返回值的。1、继承Thread类创建线程 Thread类本质上是实现了Runnable接口的一个实例,代表一个线程的实例。启动线程的唯一方法就是通过Thread类的start()实例方法。start()方法是一个native方法,它将启动一个新线程,并执行run()方法。这种方式实现多线程很简单,通过自己的类直接extend Thread,并复写run()方法,就可以启动新线程并执行自己定义的run()方法。例如:public class MyThread extends Thread { public void run() { System.out.println("MyThread.run()"); } } MyThread myThread1 = new MyThread(); MyThread myThread2 = new MyThread(); myThread1.start(); myThread2.start();2、实现Runnable接口创建线程 如果自己的类已经extends另一个类,就无法直接extends Thread,此时,可以实现一个Runnable接口,如下:public class MyThread extends OtherClass implements Runnable { public void run() { System.out.println("MyThread.run()"); } }为了启动MyThread,需要首先实例化一个Thread,并传入自己的MyThread实例:MyThread myThread = new MyThread(); Thread thread = new Thread(myThread); thread.start();事实上,当传入一个Runnable target参数给Thread后,Thread的run()方法就会调用target.run(),参考JDK源代码:public void run() { if (target != null) { target.run(); } }3、实现Callable接口通过FutureTask包装器来创建Thread线程 Callable接口(也只有一个方法)定义如下:public interface Callable<V> { /** * Computes a result, or throws an exception if unable to do so. * * @return computed result * @throws Exception if unable to compute a result */ V call() throws Exception; } public class SomeCallable<V> extends OtherClass implements Callable<V> { @Override public V call() throws Exception { // TODO Auto-generated method stub return null; } }//由Callable<Integer>创建一个FutureTask<Integer>对象: Callable<V> oneCallable = new SomeCallable<V>(); //注释:FutureTask<Integer>是一个包装器,它通过接受Callable<Integer>来创建,它同时实现了Future和Runnable接口。 FutureTask<V> oneTask = new FutureTask<V>(oneCallable); //由FutureTask<Integer>创建一个Thread对象: Thread oneThread = new Thread(oneTask); //至此,一个线程就创建完成了。 oneThread.start();4、使用ExecutorService、Callable、Future实现有返回结果的线程 ExecutorService、Callable、Future三个接口实际上都是属于Executor框架。返回结果的线程是在JDK1.5中引入的新特征,有了这种特征就不需要再为了得到返回值而大费周折了。而且自己实现了也可能漏洞百出。 可返回值的任务必须实现Callable接口。类似的,无返回值的任务必须实现Runnable接口。 执行Callable任务后,可以获取一个Future的对象,在该对象上调用get就可以获取到Callable任务返回的Object了。 注意:get方法是阻塞的,即:线程无返回结果,get方法会一直等待。 再结合线程池接口ExecutorService就可以实现传说中有返回结果的多线程了。 下面提供了一个完整的有返回结果的多线程测试例子,在JDK1.5下验证过没问题可以直接使用。代码如下:import java.util.concurrent.*; import java.util.Date; import java.util.List; import java.util.ArrayList; /** * 有返回值的线程 */ @SuppressWarnings("unchecked") public class Test { public static void main(String[] args) throws ExecutionException, InterruptedException { System.out.println("----程序开始运行----"); Date date1 = new Date(); int taskSize = 5; // 创建一个线程池 ExecutorService pool = Executors.newFixedThreadPool(taskSize); // 创建多个有返回值的任务 List<Future> list = new ArrayList<Future>(); for (int i = 0; i < taskSize; i++) { Callable c = new MyCallable(i + " "); // 执行任务并获取Future对象 Future f = pool.submit(c); // System.out.println(">>>" + f.get().toString()); list.add(f); } // 关闭线程池 pool.shutdown(); // 获取所有并发任务的运行结果 for (Future f : list) { // 从Future对象上获取任务的返回值,并输出到控制台 System.out.println(">>>" + f.get().toString()); } Date date2 = new Date(); System.out.println("----程序结束运行----,程序运行时间【" + (date2.getTime() - date1.getTime()) + "毫秒】"); } } class MyCallable implements Callable<Object> { private String taskNum; MyCallable(String taskNum) { this.taskNum = taskNum; } public Object call() throws Exception { System.out.println(">>>" + taskNum + "任务启动"); Date dateTmp1 = new Date(); Thread.sleep(1000); Date dateTmp2 = new Date(); long time = dateTmp2.getTime() - dateTmp1.getTime(); System.out.println(">>>" + taskNum + "任务终止"); return taskNum + "任务返回运行结果,当前任务时间【" + time + "毫秒】"; } }代码说明:上述代码中Executors类,提供了一系列工厂方法用于创建线程池,返回的线程池都实现了ExecutorService接口。public static ExecutorService newFixedThreadPool(int nThreads) 创建固定数目线程的线程池。 public static ExecutorService newCachedThreadPool() 创建一个可缓存的线程池,调用execute 将重用以前构造的线程(如果线程可用)。如果现有线程没有可用的,则创建一个新线程并添加到池中。终止并从缓存中移除那些已有 60 秒钟未被使用的线程。 public static ExecutorService newSingleThreadExecutor() 创建一个单线程化的Executor。 public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) 创建一个支持定时及周期性的任务执行的线程池,多数情况下可用来替代Timer类。ExecutoreService提供了submit()方法,传递一个Callable,或Runnable,返回Future。如果Executor后台线程池还没有完成Callable的计算,这调用返回Future对象的get()方法,会阻塞直到计算完成。 原文地址:https://www.cnblogs.com/felixzh/p/6036074.html
2018年04月18日
359 阅读
0 评论
0 点赞
2018-03-26
instanceof与getClass的区别
在java中,在比较一个类是否和另一个类属于同一个类实例的时候,我们通常可以采用instanceof和getClass两种方法通过两者是否相等来判断,但是两者在判断上面是有差别的,通过下面从演示代码中就可以看出区别:public class Test { public static void testInstanceof (Object x) { System.out.println("x instanceof Parent: "+(x instanceof Parent)); System.out.println("x instanceof Child: "+(x instanceof Child)); System.out.println("x getClass Parent: "+(x.getClass() == Parent.class)); System.out.println("x getClass Child: "+(x.getClass() == Child.class)); } public static void main(String[] args) { testInstanceof(new Parent()); System.out.println("---------------------------"); testInstanceof(new Child()); } } class Parent { } class Child extends Parent { } /* 输出: x instanceof Parent: true x instanceof Child: false x getClass Parent: true x getClass Child: false --------------------------- x instanceof Parent: true x instanceof Child: true x getClass Parent: false x getClass Child: true */从程序输出的结果可以看出,通过用instanceof进行类型检查的结果是:你属于该类吗?或者你属于该类的派生类吗?而通过使用getClass获得类型信息采用==来进行检查是否相等的操作是严格的判断,不会存在继承方面的考虑。 原文:https://blog.csdn.net/hzw19920329/article/details/51095413
2018年03月26日
265 阅读
0 评论
0 点赞
2017-06-08
GBK内码转换成汉字
如有如下GBK内码:-76, -13, -48, -51, -58, -5, -77, -75。在大部分情况下,我们无法将GBK内码直接显示,而需要转换成汉字。具体的转换过程如下:byte[] b = {-76, -13, -48, -51, -58, -5, -77, -75}; String s = new String(b,"GBK"); System.out.println(s);打印转换后的结果就是:大型汽车
2017年06月08日
319 阅读
0 评论
0 点赞
2012-06-29
[Java]JAVA万年历
这是一个用JAVA做的万年历!下面把源代码发出来和大家分享下!同时也是想测试下代码高亮度的作用!import java.util.Scanner; public class Nl { public static void main(String[] args){ Scanner in = new Scanner(System.in); System.out.println("请输入年:"); int year = in.nextInt(); System.out.println("请输入月:"); int month = in.nextInt(); boolean isRun = false; if(year%4==0&&year%100!=0||year%400==0) { isRun = true; } int yearDaySum=0; for(int i=1900;i<year;i++) { if(i%4==0&&i%100!=0||i%400==0) { yearDaySum+=366; }else{ yearDaySum+=365; } } int monthDaySum=0; for(int i=1;i<month;i++) { switch(i) { case 1: case 3: case 5: case 7: case 8: case 10: case 12: monthDaySum+=31; break; case 4: case 6: case 9: case 11: monthDaySum+=30; break; case 2: if(isRun) { monthDaySum+=29; }else{ monthDaySum+=28; } break; } } int daySum=yearDaySum+monthDaySum; int week=(daySum+1)%7; int monthDay = 0; switch(month) { case 1: case 3: case 5: case 7: case 8: case 10: case 12: monthDay=31; break; case 4: case 6: case 9: case 11: monthDay=30; break; case 2: if(isRun) { monthDay=29; }else{ monthDay=28; } break; } System.out.println("星期日\t星期一\t星期二\t星期三\t星期四\t星期五\t星期六"); int count=0; for(int i=0;i<week;i++) { System.out.print("\t"); count++; } for(int i=1;i<=monthDay;i++) { System.out.print(i+"\t"); count++; if(count%7==0) { System.out.println(""); } } } }
2012年06月29日
257 阅读
0 评论
0 点赞