Java中实现代码优化的技巧分享

目录
  • 1.用String.format拼接字符串
  • 2.创建可缓冲的IO流
  • 3.减少循环次数
  • 4.用完资源记得及时关闭
  • 5.使用池技术

1.用String.format拼接字符串

不知道你有没有拼接过字符串,特别是那种有多个参数,字符串比较长的情况。

比如现在有个需求:要用get请求调用第三方接口,url后需要拼接多个参数。

以前我们的请求地址是这样拼接的:

String url = "http://susan.sc.cn?userName="+userName+"&age="+age+"&address="+address+"&sex="+sex+"&roledId="+roleId;

字符串使用+号拼接,非常容易出错。

后面优化了一下,改为使用StringBuilder拼接字符串:

StringBuilder urlBuilder = new StringBuilder("http://susan.sc.cn?");
urlBuilder.append("userName=")
.append(userName)
.append("&age=")
.append(age)
.append("&address=")
.append(address)
.append("&sex=")
.append(sex)
.append("&roledId=")
.append(roledId);

代码优化之后,稍微直观点。

但还是看起来比较别扭。

这时可以使用String.format方法优化:

String requestUrl = "http://susan.sc.cn?userName=%s&age=%s&address=%s&sex=%s&roledId=%s";
String url = String.format(requestUrl,userName,age,address,sex,roledId);

代码的可读性,一下子提升了很多。

我们平常可以使用String.format方法拼接url请求参数,日志打印等字符串。

但不建议在for循环中用它拼接字符串,因为它的执行效率,比使用+号拼接字符串,或者使用StringBuilder拼接字符串都要慢一些。

2.创建可缓冲的IO流

IO流想必大家都使用得比较多,我们经常需要把数据写入某个文件,或者从某个文件中读取数据到内存中,甚至还有可能把文件a,从目录b,复制到目录c下等。

JDK给我们提供了非常丰富的API,可以去操作IO流。

例如:

public class IoTest1 {
    public static void main(String[] args) {
        FileInputStream fis = null;
        FileOutputStream fos = null;
        try {
            File srcFile = new File("/Users/dv_susan/Documents/workspace/jump/src/main/java/com/sue/jump/service/test1/1.txt");
            File destFile = new File("/Users/dv_susan/Documents/workspace/jump/src/main/java/com/sue/jump/service/test1/2.txt");
            fis = new FileInputStream(srcFile);
            fos = new FileOutputStream(destFile);
            int len;
            while ((len = fis.read()) != -1) {
                fos.write(len);
            }
            fos.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (fos != null) {
                    fos.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (fis != null) {
                    fis.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

这个例子主要的功能,是将1.txt文件中的内容复制到2.txt文件中。这例子使用普通的IO流从功能的角度来说,也能满足需求,但性能却不太好。

因为这个例子中,从1.txt文件中读一个字节的数据,就会马上写入2.txt文件中,需要非常频繁的读写文件。

优化:

public class IoTest {
    public static void main(String[] args) {
        BufferedInputStream bis = null;
        BufferedOutputStream bos = null;
        FileInputStream fis = null;
        FileOutputStream fos = null;
        try {
            File srcFile = new File("/Users/dv_susan/Documents/workspace/jump/src/main/java/com/sue/jump/service/test1/1.txt");
            File destFile = new File("/Users/dv_susan/Documents/workspace/jump/src/main/java/com/sue/jump/service/test1/2.txt");
            fis = new FileInputStream(srcFile);
            fos = new FileOutputStream(destFile);
            bis = new BufferedInputStream(fis);
            bos = new BufferedOutputStream(fos);
            byte[] buffer = new byte[1024];
            int len;
            while ((len = bis.read(buffer)) != -1) {
                bos.write(buffer, 0, len);
            }
            bos.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (bos != null) {
                    bos.close();
                }
                if (fos != null) {
                    fos.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (bis != null) {
                    bis.close();
                }
                if (fis != null) {
                    fis.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

这个例子使用BufferedInputStream和BufferedOutputStream创建了可缓冲的输入输出流。

最关键的地方是定义了一个buffer字节数组,把从1.txt文件中读取的数据临时保存起来,后面再把该buffer字节数组的数据,一次性批量写入到2.txt中。

这样做的好处是,减少了读写文件的次数,而我们都知道读写文件是非常耗时的操作。也就是说使用可缓存的输入输出流,可以提升IO的性能,特别是遇到文件非常大时,效率会得到显著提升。

3.减少循环次数

在我们日常开发中,循环遍历集合是必不可少的操作。

但如果循环层级比较深,循环中套循环,可能会影响代码的执行效率。

反例:

for(User user: userList) {
   for(Role role: roleList) {
      if(user.getRoleId().equals(role.getId())) {
         user.setRoleName(role.getName());
      }
   }
}

这个例子中有两层循环,如果userList和roleList数据比较多的话,需要循环遍历很多次,才能获取我们所需要的数据,非常消耗cpu资源。

正例:

Map<Long, List<Role>> roleMap = roleList.stream().collect(Collectors.groupingBy(Role::getId));
for (User user : userList) {
    List<Role> roles = roleMap.get(user.getRoleId());
    if(CollectionUtils.isNotEmpty(roles)) {
        user.setRoleName(roles.get(0).getName());
    }
}

减少循环次数,最简单的办法是,把第二层循环的集合变成map,这样可以直接通过key,获取想要的value数据。

虽说map的key存在hash冲突的情况,但遍历存放数据的链表或者红黑树的时间复杂度,比遍历整个list集合要小很多。

4.用完资源记得及时关闭

在我们日常开发中,可能经常访问资源,比如:获取数据库连接,读取文件等。

我们以获取数据库连接为例。

反例:

//1. 加载驱动类
Class.forName("com.mysql.jdbc.Driver");
//2. 创建连接
Connection connection = DriverManager.getConnection("jdbc:mysql//localhost:3306/db?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8","root","123456");
//3.编写sql
String sql ="select * from user";
//4.创建PreparedStatement
PreparedStatement pstmt = conn.prepareStatement(sql);
//5.获取查询结果
ResultSet rs = pstmt.execteQuery();
while(rs.next()){
   int id = rs.getInt("id");
   String name = rs.getString("name");
}

上面这段代码可以正常运行,但却犯了一个很大的错误,即:ResultSet、PreparedStatement和Connection对象的资源,使用完之后,没有关闭。

我们都知道,数据库连接是非常宝贵的资源。我们不可能一直创建连接,并且用完之后,也不回收,白白浪费数据库资源。

正例:

//1. 加载驱动类
Class.forName("com.mysql.jdbc.Driver");

Connection connection = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
    //2. 创建连接
    connection = DriverManager.getConnection("jdbc:mysql//localhost:3306/db?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8","root","123456");
    //3.编写sql
    String sql ="select * from user";
    //4.创建PreparedStatement
    pstmt = conn.prepareStatement(sql);
    //5.获取查询结果
    rs = pstmt.execteQuery();
    while(rs.next()){
       int id = rs.getInt("id");
       String name = rs.getString("name");
    }
} catch(Exception e) {
  log.error(e.getMessage(),e);
} finally {
   if(rs != null) {
      rs.close();
   }

   if(pstmt != null) {
      pstmt.close();
   }

   if(connection != null) {
      connection.close();
   }
}

这个例子中,无论是ResultSet,或者PreparedStatement,还是Connection对象,使用完之后,都会调用close方法关闭资源。

在这里温馨提醒一句:ResultSet,或者PreparedStatement,还是Connection对象,这三者关闭资源的顺序不能反了,不然可能会出现异常。

5.使用池技术

我们都知道,从数据库查数据,首先要连接数据库,获取Connection资源。

想让程序多线程执行,需要使用Thread类创建线程,线程也是一种资源。

通常一次数据库操作的过程是这样的:

  • 创建连接
  • 进行数据库操作
  • 关闭连接

而创建连接和关闭连接,是非常耗时的操作,创建连接需要同时会创建一些资源,关闭连接时,需要回收那些资源。

如果用户的每一次数据库请求,程序都都需要去创建连接和关闭连接的话,可能会浪费大量的时间。

此外,可能会导致数据库连接过多。

我们都知道数据库的最大连接数是有限的,以mysql为例,最大连接数是:100,不过可以通过参数调整这个数量。

如果用户请求的连接数超过最大连接数,就会报:too many connections异常。如果有新的请求过来,会发现数据库变得不可用。

这时可以通过命令:

show variables like max_connections

查看最大连接数。

然后通过命令:

set GLOBAL max_connections=1000

手动修改最大连接数。

这种做法只能暂时缓解问题,不是一个好的方案,无法从根本上解决问题。

最大的问题是:数据库连接数可以无限增长,不受控制。

这时我们可以使用数据库连接池。

目前Java开源的数据库连接池有:

  • DBCP:是一个依赖Jakarta commons-pool对象池机制的数据库连接池。
  • C3P0:是一个开放源代码的JDBC连接池,它在lib目录中与Hibernate一起发布,包括了实现jdbc3和jdbc2扩展规范说明的Connection 和Statement 池的DataSources 对象。
  • Druid:阿里的Druid,不仅是一个数据库连接池,还包含一个ProxyDriver、一系列内置的JDBC组件库、一个SQL Parser。
  • Proxool:是一个Java SQL Driver驱动程序,它提供了对选择的其它类型的驱动程序的连接池封装,可以非常简单的移植到已有代码中。

目前用的最多的数据库连接池是:Druid。

到此这篇关于Java中实现代码优化的技巧分享的文章就介绍到这了,更多相关Java代码优化内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 详解Java代码常见优化方案

    首先,良好的编码规范非常重要.在 java 程序中,访问速度.资源紧张等问题的大部分原因,都是代码不规范造成的. 单例的使用场景 单例模式对于减少资源占用.提高访问速度等方面有很多好处,但并不是所有场景都适用于单例. 简单来说,单例主要适用于以下三个方面: 多线程场景,通过线程同步来控制资源的并发访问. 多线程场景,控制数据共享,让多个不相关的进程或线程之间实现通信(通过访问同一资源来控制). 控制实例的产生,单例只实例化一次,以达到节约资源的目的: 不可随意使用静态变量 当某个对象被定义为 s

  • Java代码优化细节

    代码优化细节  1.尽量指定类.方法的final修饰符 带有final修饰符的类是不可派生的.在Java核心API中,有许多应用final的例子,例如java.lang.String,整个类都是final的.为类指定final修饰符可以让类不可以被继承,为方法指定final修饰符可以让方法不可以被重写.如果指定了一个类为final,则该类所有的方法都是final的.Java编译器会寻找机会内联所有的final方法,内联对于提升Java运行效率作用重大 2.尽量重用对象  特别是String对象的

  • 44条Java代码优化建议

    前言 2016年3月修改,结合自己的工作和平时学习的体验重新谈一下为什么要进行代码优化.在修改之前,我的说法是这样的: 就像鲸鱼吃虾米一样,也许吃一个两个虾米对于鲸鱼来说作用不大,但是吃的虾米多了,鲸鱼自然饱了.代码优化一样,也许一个两个的优化,对于提升代码的运行效率意义不大,但是只要处处都能注意代码优化,总体来说对于提升代码的运行效率就很有用了. 这个观点,在现在看来,是要进行代码优化的一个原因,但不全对.在机械工艺发展的今天,服务器动辄8核.16核,64位CPU,代码执行效率非常高,Stri

  • 分享几个Java工作中实用的代码优化技巧

    目录 1.类成员与方法的可见性最小化 2.使用位移操作替代乘除法 3.尽量减少对变量的重复计算 4.不要捕捉RuntimeException 5.使用局部变量可避免在堆上分配 6.减少变量的作用范围 7.懒加载策略 8.访问静态变量直接使用类名 9.字符串拼接使用StringBuilder 10.重写对象的HashCode 11.HashMap等集合初始化 12.循环内创建对象引用 13.遍历Map 使用 EntrySet 方法 14.不要在多线程下使用同一个 Random 15.自增推荐使用L

  • Java中实现代码优化的技巧分享

    目录 1.用String.format拼接字符串 2.创建可缓冲的IO流 3.减少循环次数 4.用完资源记得及时关闭 5.使用池技术 1.用String.format拼接字符串 不知道你有没有拼接过字符串,特别是那种有多个参数,字符串比较长的情况. 比如现在有个需求:要用get请求调用第三方接口,url后需要拼接多个参数. 以前我们的请求地址是这样拼接的: String url = "http://susan.sc.cn?userName="+userName+"&a

  • Java中的SPI机制案例分享

    目录 1 简单介绍 2 SPI 案例 3 SPI 的原理剖析 1 简单介绍 当我们封装了一套接口,其它项目想要调用我们的接口只需要引入我们写好的包,但是其它项目如果想要对我们的接口进行扩展,由于接口是被封装在依赖包中的,想要扩展并不容易,这时就需要依赖于Java为我们提供的SPI机制. SPI的全称是Service Provider Interface,服务提供者接口,而与之最接近的概念就是API,全称Application Programming Interface,应用程序编程接口.那么这两

  • 10 个Python中Pip的使用技巧分享

    目录 Python pip 1.安装 pip 2.升级 pip 3.安装库 4. 库的批量安装 5.卸载和升级包 6. 冻结 Python pip 依赖 7.查看库信息 8.查看需要升级的库 9. 检查兼容性问题 10. 将库下载到本地 众所周知,pip 可以安装.更新.卸载 Python 的第三方库,非常方便.你们中的许多人可能已经使用 pip 很长时间了,但不清楚它有哪些还不错的功能.希望我今天分享的技巧能让你从 Python pip 中受益. Python pip 让我们从 Python

  • Java中反射的学习笔记分享

    目录 简介 一个简单的例子 设置使用反射 模拟instanceof运算 了解类的方法 获取有关构造函数的信息 查找类字段 按名称调用方法 创建新对象 更改字段的值 使用数组 总结 简介 反射是Java编程语言中的一个特性.它允许执行的Java程序检查或 操作 自身,并操作程序的内部属性.例如,Java类可以获取其所有成员的名称并显示它们. 从程序内部检查和操作Java类的能力听起来可能不太显示,但是在其他编程语言中,这个特性根本不存在.例如,在C或C ++ 程序中无法获取有关该程序中定义的函数的

  • Python中字符串的处理技巧分享

    一.如何拆分含有多种分隔符的字符串? 实际案例 我们要把某个字符串依据分隔符号拆分不同的字符段,该字符串包含多种不同的分隔符,例如: s = 'asd;aad|dasd|dasd,sdasd|asd,,Adas|sdasd;Asdasd,d|asd' 其中<,>,<;>,<|>,<\t>都是分隔符,如何处理? 解决方案 连续使用split()方法,每次处理一种分隔符 # 使用Python2 def mySplit(s,ds): res = [s] for d

  • SpringBoot中Jackson日期格式化技巧分享

    目录 Jackson 日期格式化技巧 后续问题 补充:Jackson 统一配置 日期转换格式 参考资料 Jackson 日期格式化技巧 使用 Spring Boot 时,需要使用 Jackson 处理一些 Java Time API 类型的 JSON 序列化问题,在处理一些类的字段时,可以通过直接在属性上加注解的方式来指定其格式化样式.但是,昨天同事遇到一个格式化 Map 数据的问题,这样就不能通过加注解来解决格式化样式的问题了. 在网上各种搜索,各种尝试后,终于解决了这个问题,记录一下,以备不

  • JAVA中JNI的简单使用分享

    了解JNI:JAVA因其跨平台特性而受人们喜爱,也正因此,使得它和本机各种内部联系变得很少,所以JNI(Java Native Interface)就是用来解决JAVA本地操作的一种方式.JAVA通过JNI调用本地方法,而本地方法是以库文件的形式存放的(在WINDOWS平台上是DLL文件形式,在UNIX机器上是SO文件形式).通过调用本地的库文件的内部方法,使JAVA可以实现和本地机器的紧密联系,调用系统级的各接口方法. 步骤如下: 1.写好.java源文件: 复制代码 代码如下: packag

  • java中输出pdf文件代码分享

    package snake; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import com.lowagie.text.Document; import com.lowagie.text.DocumentException; import com.lowag

  • java中Calendar类用法实例详解

    本文实例讲述了java中Calendar类用法.分享给大家供大家参考,具体如下: java中的Calendar在开发中经常被忽略,这篇博客总结一下这个类,对后面项目中使用时期的时候有帮助. Calendar常量(field)的作用 Calendar cal = Calendar.getInstance(); cal.get(Calendar.DATE);//-----------------------当天 1-31 cal.get(Calendar.DAY_OF_MONTH);//------

  • Java中join线程操作实例分析

    本文实例讲述了Java中join线程操作.分享给大家供大家参考,具体如下: 一 点睛 Tread提供了让一个线程等待另外一个线程完成的方法--join()方法.当在某个程序执行流中调用其他线程的join()方法时,调用线程将被阻塞,直到被join()方法加入的join线程执行完后为止. join()方法通常由使用线程的程序调用,以将大问题划分成许多小问题,每个小问题分配一个线程.当所有小问题都得到处理后,再调用主线程来进一步操作. 二 代码 public class JoinThread ext

随机推荐