1.用String.format拼接字符串

String.format方法拼接url请求参数,日志打印等字符串。

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

2.创建可缓冲的IO流

//尽量使用try-with-resources语句,可以在程序结束时自动关闭资源
try (ServletOutputStream outStr = response.getOutputStream();BufferedOutputStream buff = new BufferedOutputStream(outStr)){buff.write(text.getBytes("UTF-8"));buff.flush();
} catch (Exception e) {log.error("导出文件文件出错:{}",e);
}

使用缓冲流

        File srcFile = new File("/Users/Documents/test1/1.txt");File destFile = new File("/Users/Documents/test1/2.txt");try(FileInputStream fis = new FileInputStream(srcFile);FileOutputStream fos = new FileOutputStream(destFile);BufferedInputStream bis  = new BufferedInputStream(fis);BufferedOutputStream 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();} 

3.减少循环次数

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

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

如下代码所示:

//正常逻辑2层for循环处理
for(User user: userList) {for(Role role: roleList) {if(user.getRoleId().equals(role.getId())) {user.setRoleName(role.getName());}}
}

优化后的代码如下所示:

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.用完资源记得及时关闭

参考第二点尽量使用try-with-resources语句或者手动关闭资源

5.使用池技术

数据库连接池、线程池

6.消除if...else的锦囊妙计,反射时添加缓存

我们都知道通过反射创建对象实例,比使用new关键字要慢很多。

由此,不太建议在用户请求过来时,每次都通过反射实时创建实例。

有时候,为了代码的灵活性,又不得不用反射创建实例,这时该怎么办呢?

答:加缓存

先看以下代码

publicinterface IPay {  void pay();
}  @Service
publicclass AliaPay implements IPay {  @Overridepublic void pay() {  System.out.println("===发起支付宝支付===");  }
}  @Service
publicclass WeixinPay implements IPay {  @Overridepublic void pay() {  System.out.println("===发起微信支付===");  }
}  @Service
publicclass JingDongPay implements IPay {  @Overridepublic void pay() {  System.out.println("===发起京东支付===");  }
}  @Service
publicclass PayService {  @Autowiredprivate AliaPay aliaPay;  @Autowiredprivate WeixinPay weixinPay;  @Autowiredprivate JingDongPay jingDongPay;  public void toPay(String code) {  if ("alia".equals(code)) {  aliaPay.pay();  } elseif ("weixin".equals(code)) {  weixinPay.pay();  } elseif ("jingdong".equals(code)) {  jingDongPay.pay();  } else {  System.out.println("找不到支付方式");  }  }
}

这里违法了设计模式六大原则的:开闭原则 和 单一职责原则

开闭原则:对扩展开放,对修改关闭。就是说增加新功能要尽量少改动已有代码。

单一职责原则:顾名思义,要求逻辑尽量单一,不要太复杂,便于复用。

  1. 先创建一个注解
  2. 在所有的支付类上都加上该注解
  3. 增加最关键的类PayService2
/*** @Author: Ywh* @Date: 2022/7/25 14:50* @Description*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface PayCode {String value();String name();
}@PayCode(value = "alia", name = "支付宝支付")
@Component("alia")
public class AliaPay implements IPay {@Overridepublic void pay() {System.out.println("===发起支付宝支付===");}
}@PayCode(value = "jingdong", name = "京东支付")
@Component("jingdong")
public class JingDongPay implements IPay {@Overridepublic void pay() {System.out.println("===发起京东支付===");}
}@PayCode(value = "weixin", name = "微信支付")
@Component("weixin")
public class WeixinPay implements IPay {@Overridepublic void pay() {System.out.println("===发起微信支付===");}
}@Service
@Slf4j
public class PayService2 implements ApplicationListener<ContextRefreshedEvent> {private static Map<String, IPay> payMap = null;@Overridepublic void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {//在初始化或刷新ApplicationContext时发布ApplicationContext applicationContext = contextRefreshedEvent.getApplicationContext();//获取所有拥有特定payCode注解的Bean(AliPay、WeiXinPay、JindDongPay)Map<String, Object> beansWithAnnotation = applicationContext.getBeansWithAnnotation(PayCode.class);if (beansWithAnnotation != null) {payMap = new HashMap<>();beansWithAnnotation.forEach((key, value) -> {String bizType = value.getClass().getAnnotation(PayCode.class).value();payMap.put(bizType, (IPay) value);});}}public void pay(String code) {payMap.get(code).pay();}
}@GetMapping("/pay")@ApiOperation("测试支付")public void pay(String code){payService2.pay(code);}

PayService2类实现了ApplicationListener接口,这样在onApplicationEvent方法中,就可以拿到ApplicationContext的实例。这一步,其实是在spring容器启动的时候,spring通过反射我们处理好了。

我们再获取打了PayCode注解的类,放到一个map中,map中的key就是PayCode注解中定义的value,跟code参数一致,value是支付类的实例。

这样,每次就可以每次直接通过code获取支付类实例,而不用if...else判断了。如果要加新的支付方法,只需在支付类上面打上PayCode注解定义一个新的code即可。

注意:这种方式的code可以没有业务含义,可以是纯数字,只要不重复就行。

7.多线程处理

一句话把串行执行的接口变成并行执行;

并行执行

在java8之前可以通过实现Callable接口,获取线程返回结果。

java8以后通过CompleteFuture类实现该功能。我们这里以CompleteFuture为例:

public UserInfo getUserInfo(Long id) throws InterruptedException, ExecutionException {final UserInfo userInfo = new UserInfo();CompletableFuture userFuture = CompletableFuture.supplyAsync(() -> {getRemoteUserAndFill(id, userInfo);return Boolean.TRUE;}, executor);CompletableFuture bonusFuture = CompletableFuture.supplyAsync(() -> {getRemoteBonusAndFill(id, userInfo);return Boolean.TRUE;}, executor);CompletableFuture growthFuture = CompletableFuture.supplyAsync(() -> {getRemoteGrowthAndFill(id, userInfo);return Boolean.TRUE;}, executor);CompletableFuture.allOf(userFuture, bonusFuture, growthFuture).join();userFuture.get();bonusFuture.get();growthFuture.get();return userInfo;
}

8.懒加载

有时候,创建对象是一个非常耗时的操作,特别是在该对象的创建过程中,还需要创建很多其他的对象时。

我们以单例模式为例。

在介绍单例模式的时候,必须要先介绍它的两种非常著名的实现方式:饿汉模式 和 懒汉模式

8.1 饿汉模式

实例在初始化的时候就已经建好了,不管你有没有用到,先建好了再说。具体代码如下:

public class SimpleSingleton {//持有自己类的引用private static final SimpleSingleton INSTANCE = new SimpleSingleton();//私有的构造方法private SimpleSingleton() {}//对外提供获取实例的静态方法public static SimpleSingleton getInstance() {return INSTANCE;}
}

使用饿汉模式的好处是:没有线程安全的问题,但带来的坏处也很明显。

8.2 懒汉模式

顾名思义就是实例在用到的时候才去创建,“比较懒”,用的时候才去检查有没有实例,如果有则返回,没有则新建。具体代码如下:

public class SimpleSingleton2 {private static SimpleSingleton2 INSTANCE;private SimpleSingleton2() {}public static SimpleSingleton2 getInstance() {if (INSTANCE == null) {INSTANCE = new SimpleSingleton2();}return INSTANCE;}
}

示例中的INSTANCE对象一开始是空的,在调用getInstance方法才会真正实例化。

懒汉模式相对于饿汉模式,没有提前实例化对象,在真正使用的时候再实例化,在实例化对象的阶段效率更高一些。

除了单例模式之外,懒加载的思想,使用比较多的可能是:

  1. spring的@Lazy注解。在spring容器启动的时候,不会调用其getBean方法初始化实例。

  2. mybatis的懒加载。在mybatis做级联查询的时候,比如查用户的同时需要查角色信息。如果用了懒加载,先只查用户信息,真正使用到角色了,才取查角色信息。

9.初始化集合时指定大小

在创建集合时指定了大小,比没有指定大小,添加10万个元素的效率提升了一倍。

如果你看过ArrayList源码,你就会发现它的默认大小是10,如果添加元素超过了一定的阀值,会按1.5倍的大小扩容。

你想想,如果装10万条数据,需要扩容多少次呀?而每次扩容都需要不停的复制元素,从老集合复制到新集合中,需要浪费多少时间呀。

//示例
List<Integer> list = new ArrayList<>();
//正例
List<Integer> list2 = new ArrayList<>(100000);

10.不要满屏try...catch异常

可以使用全局异常处理:RestControllerAdvice

@RestControllerAdvice
public class GlobalExceptionHandler {@ExceptionHandler(Exception.class)public String handleException(Exception e) {if (e instanceof ArithmeticException) {return "数据异常";}if (e instanceof Exception) {return "服务器内部异常";}retur nnull;}
}

11.位运算效率更高

12.巧用第三方工具类

如果你引入com.google.guava的pom文件,会获得很多好用的小工具。这里推荐一款com.google.common.collect包下的集合工具:Lists

      //guava提供的字符串工具类Strings.isNullOrEmpty("");//返回trueStrings.nullToEmpty(null);//""Strings.nullToEmpty("chen");//返回"chen"Strings.emptyToNull("");//返回nullStrings.emptyToNull("chen");//返回"chen"  Strings.commonPrefix("aaab", "aac");//"aa"否则返回""Strings.commonSuffix("aaac", "aac");//"aac"否则返回""

13.用同步代码块代替同步方法

在某些业务场景中,为了防止多个线程并发修改某个共享数据,造成数据异常。

为了解决并发场景下,多个线程同时修改数据,造成数据不一致的情况。通常情况下,我们会:加锁

但如果锁加得不好,导致锁的粒度太粗,也会非常影响接口性能。

在java中提供了synchronized关键字给我们的代码加锁。

通常有两种写法:在方法上加锁 和 在代码块上加锁

先看看如何在方法上加锁:

public synchronized doSave(String fileUrl) {mkdir();uploadFile(fileUrl);sendMessage(fileUrl);
}

这里加锁的目的是为了防止并发的情况下,创建了相同的目录,第二次会创建失败,影响业务功能。

但这种直接在方法上加锁,锁的粒度有点粗。因为doSave方法中的上传文件和发消息方法,是不需要加锁的。只有创建目录方法,才需要加锁。

我们都知道文件上传操作是非常耗时的,如果将整个方法加锁,那么需要等到整个方法执行完之后才能释放锁。显然,这会导致该方法的性能很差,变得得不偿失。

这时,我们可以改成在代码块上加锁了,具体代码如下:

public void doSave(String path,String fileUrl) {synchronized(this) {if(!exists(path)) {mkdir(path);}}uploadFile(fileUrl);sendMessage(fileUrl);
}

这样改造之后,锁的粒度一下子变小了,只有并发创建目录功能才加了锁。而创建目录是一个非常快的操作,即使加锁对接口的性能影响也不大。

最重要的是,其他的上传文件和发送消息功能,任然可以并发执行。

14.不用的数据及时清理

在Java中保证线程安全的技术有很多,可以使用synchroizedLock等关键字给代码块加锁

但是它们有个共同的特点,就是加锁会对代码的性能有一定的损耗。

其实,在jdk中还提供了另外一种思想即:用空间换时间

没错,使用ThreadLocal类就是对这种思想的一种具体体现。

ThreadLocal为每个使用变量的线程提供了一个独立的变量副本,这样每一个线程都能独立地改变自己的副本,而不会影响其它线程所对应的副本。

ThreadLocal的用法大致是这样的:

  1. 先创建一个CurrentUser类,其中包含了ThreadLocal的逻辑。

    public class CurrentUser {private static final ThreadLocal<UserInfo> THREA_LOCAL = new ThreadLocal();public static void set(UserInfo userInfo) {THREA_LOCAL.set(userInfo);}public static UserInfo get() {THREA_LOCAL.get();}public static void remove() {THREA_LOCAL.remove();}
    }
  2. 在业务代码中调用CurrentUser类。

    public void doSamething(UserDto userDto) {UserInfo userInfo = convert(userDto);CurrentUser.set(userInfo);...//业务代码UserInfo userInfo = CurrentUser.get();...
    }

    在业务代码的第一行,将userInfo对象设置到CurrentUser,这样在业务代码中,就能通过CurrentUser.get()获取到刚刚设置的userInfo对象。特别是对业务代码调用层级比较深的情况,这种用法非常有用,可以减少很多不必要传参。

    但在高并发的场景下,这段代码有问题,只往ThreadLocal存数据,数据用完之后并没有及时清理。

    ThreadLocal即使使用了WeakReference(弱引用)也可能会存在内存泄露问题,因为 entry对象中只把key(即threadLocal对象)设置成了弱引用,但是value值没有。

    那么,如何解决这个问题呢?

public void doSamething(UserDto userDto) {UserInfo userInfo = convert(userDto);try{CurrentUser.set(userInfo);...//业务代码UserInfo userInfo = CurrentUser.get();...} finally {CurrentUser.remove();}
}

需要在finally代码块中,调用remove方法清理没用的数据。

15.用equals方法比较是否相等

16.避免创建大集合

尽量分页处理

17.状态用枚举

public enum OrderStatusEnum {  CREATE(1, "下单"),  PAY(2, "支付"),  DONE(3, "完成"),  CANCEL(4, "撤销");  private int code;  private String message;  OrderStatusEnum(int code, String message) {  this.code = code;  this.message = message;  }  public int getCode() {  return this.code;  }  public String getMessage() {  return this.message;  }  public static OrderStatusEnum getOrderStatusEnum(int code) {  return Arrays.stream(OrderStatusEnum.values()).filter(x -> x.code == code).findFirst().orElse(null);  }
}

而且使用枚举的好处是:

  1. 代码的可读性变强了,不同的状态,有不同的枚举进行统一管理和维护。

  2. 枚举是天然单例的,可以直接使用==号进行比较。

  3. code和message可以成对出现,比较容易相关转换。

  4. 枚举可以消除if...else过多问题。

聊聊Java中代码优化的30个小技巧18.把固定值定义成静态常量

使用static final关键字修饰静态常量,static表示静态的意思,即类变量,而final表示不允许修改

两个关键字加在一起,告诉Java虚拟机这种变量,在内存中只有一份,在全局上是唯一的,不能修改,也就是静态常量

19.避免大事务

很多小伙伴在使用spring框架开发项目时,为了方便,喜欢使用@Transactional注解提供事务功能。

没错,使用@Transactional注解这种声明式事务的方式提供事务功能,确实能少写很多代码,提升开发效率。

但也容易造成大事务,引发其他的问题。

下面用一张图看看大事务引发的问题。

从图中能够看出,大事务问题可能会造成接口超时,对接口的性能有直接的影响。

我们该如何优化大事务呢?

  1. 少用@Transactional注解

  2. 将查询(select)方法放到事务外

  3. 事务中避免远程调用

  4. 事务中避免一次性处理太多数据

  5. 有些功能可以非事务执行

  6. 有些功能可以异步处理

大家可以参考关于大事务的这篇文章《让人头痛的大事务问题到底要如何解决?》

20.消除过长的if...else

更详细的内容可以看看这篇文章《消除if...else是9条锦囊妙计》

21.防止死循环

22.注意BigDecimal的坑

通常我们会把一些小数类型的字段(比如:金额),定义成BigDecimal,而不是Double,避免丢失精度问题。

常识告诉我们使用BigDecimal能避免丢失精度。

但是使用BigDecimal能避免丢失精度吗?

答案是否定的。

为什么?

BigDecimal amount1 = new BigDecimal(0.02);
BigDecimal amount2 = new BigDecimal(0.03);
System.out.println(amount2.subtract(amount1));

结果:

0.0099999999999999984734433411404097569175064563751220703125

不科学呀,为啥还是丢失精度了?

使用BigDecimal构造函数初始化对象,也会丢失精度。

那么,如何才能不丢失精度呢?

BigDecimal amount1 = BigDecimal.valueOf(0.02);
BigDecimal amount2 = BigDecimal.valueOf(0.03);
System.out.println(amount2.subtract(amount1));

聊聊Java中代码优化的30个小技巧23.尽可能复用代码

24.foreach循环中不remove元素

循环有很多种写法,比如:while、for、foreach等。
public class Test2 {public static void main(String[] args) {List<String> list = Lists.newArrayList("a","b","c");for (String temp : list) {if ("c".equals(temp)) {list.remove(temp);}}System.out.println(list);}
}//执行结果:
Exception in thread "main" java.util.ConcurrentModificationExceptionat java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)at java.util.ArrayList$Itr.next(ArrayList.java:851)at com.sue.jump.service.test1.Test2.main(Test2.java:24)

这种在foreach循环中调用remove方法删除元素,可能会报ConcurrentModificationException异常。

如果想在遍历集合时,删除其中的元素,可以用for循环,例如:

 List<String> list = Lists.newArrayList("a","b","c");for (int i = 0; i < list.size(); i++) {String temp = list.get(i);if ("c".equals(temp)) {list.remove(temp);}}System.out.println(list);

聊聊Java中代码优化的30个小技巧25.避免随意打印日志

使用isDebugEnabled判断一下,如果当前的日志级别是debug才打印日志。生产环境默认日志级别是info,在有些紧急情况下,把某个接口或者方法的日志级别改成debug,打印完我们需要的日志后,又调整回去。

方便我们定位问题,又不会产生大量的垃圾日志,一举两得

@PostMapping("/query")
public List<User> query(@RequestBody List<Long> ids) {if (log.isDebugEnabled()) {log.debug("request params:{}", ids);}List<User> userList = userService.query(ids);if (log.isDebugEnabled()) {log.debug("response:{}", userList);}return userList;
}

聊聊Java中代码优化的30个小技巧26.比较时把常量写前面

private static final String FOUND_NAME = "苏三";
...if(null == user) {return;
}
if(FOUND_NAME.equals(user.getName())) {System.out.println("找到:"+user.getName());
}

在使用equals做比较时,尽量将常量写在前面,即equals方法的左边。

这样即使user.getName()返回的数据为null,equals方法会直接返回false,而不再是报空指针异常。

27.名称要见名知意

28.SimpleDateFormat线程不安全

使用java8的DateTimeFormatter类。

聊聊Java中代码优化的30个小技巧29.少用Executors创建线程池

我们都知道JDK5之后,提供了ThreadPoolExecutor类,用它可以自定义线程池

线程池的好处有很多,下面主要说说这3个方面。

  1. 降低资源消耗:避免了频繁的创建线程和销毁线程,可以直接复用已有线程。而我们都知道,创建线程是非常耗时的操作。

  2. 提供速度:任务过来之后,因为线程已存在,可以拿来直接使用。

  3. 提高线程的可管理性:线程是非常宝贵的资源,如果创建过多的线程,不仅会消耗系统资源,甚至会影响系统的稳定。使用线程池,可以非常方便的创建、管理和监控线程。

当然JDK为了我们使用更便捷,专门提供了:Executors类,给我们快速创建线程池

该类中包含了很多静态方法

  • newCachedThreadPool:创建一个可缓冲的线程,如果线程池大小超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。

  • newFixedThreadPool:创建一个固定大小的线程池,如果任务数量超过线程池大小,则将多余的任务放到队列中。

  • newScheduledThreadPool:创建一个固定大小,并且能执行定时周期任务的线程池。

  • newSingleThreadExecutor:创建只有一个线程的线程池,保证所有的任务安装顺序执行。

在高并发的场景下,如果大家使用这些静态方法创建线程池,会有一些问题。

那么,我们一起看看有哪些问题?

  • newFixedThreadPool:允许请求的队列长度是Integer.MAX_VALUE,可能会堆积大量的请求,从而导致OOM。

  • newSingleThreadExecutor:允许请求的队列长度是Integer.MAX_VALUE,可能会堆积大量的请求,从而导致OOM。

  • newCachedThreadPool:允许创建的线程数是Integer.MAX_VALUE,可能会创建大量的线程,从而导致OOM。

那我们该怎办呢?

优先推荐使用ThreadPoolExecutor类,我们自定义线程池。

ExecutorService threadPool = new ThreadPoolExecutor(8, //corePoolSize线程池中核心线程数10, //maximumPoolSize 线程池中最大线程数60, //线程池中线程的最大空闲时间,超过这个时间空闲线程将被回收TimeUnit.SECONDS,//时间单位new ArrayBlockingQueue(500), //队列new ThreadPoolExecutor.CallerRunsPolicy()); //拒绝策略

聊聊Java中代码优化的30个小技巧顺便说一下,如果是一些低并发场景,使用Executors类创建线程池也未尝不可,也不能完全一棍子打死。在这些低并发场景下,很难出现OOM问题,所以我们需要根据实际业务场景选择。

30.Arrays.asList转换的集合别修改

在我们日常工作中,经常需要把数组转换成List集合。

因为数组的长度是固定的,不太好扩容,而List的长度是可变的,它的长度会根据元素的数量动态扩容。

在JDK的Arrays类中提供了asList方法,可以把数组转换成List

正例

String [] array = new String [] {"a","b","c"};
List<String> list = Arrays.asList(array);
for (String str : list) {System.out.println(str);
}

在这个例子中,使用Arrays.asList方法将array数组,直接转换成了list。然后在for循环中遍历list,打印出它里面的元素。

如果转换后的list,只是使用,没新增或修改元素,不会有问题。

反例

String[] array = new String[]{"a", "b", "c"};
List<String> list = Arrays.asList(array);
list.add("d");
for (String str : list) {System.out.println(str);
}

执行结果:

Exception in thread "main" java.lang.UnsupportedOperationException
at java.util.AbstractList.add(AbstractList.java:148)
at java.util.AbstractList.add(AbstractList.java:108)
at com.sue.jump.service.test1.Test2.main(Test2.java:24)

会直接报UnsupportedOperationException异常。

为什么呢?

答:使用Arrays.asList方法转换后的ArrayList,是Arrays类的内部类,并非java.util包下我们常用的ArrayList

Arrays类的内部ArrayList类,它没有实现父类的add和remove方法,用的是父类AbstractList的默认实现。

我们看看AbstractList是如何实现的:

public void add(int index, E element) {throw new UnsupportedOperationException();
}public E remove(int index) {throw new UnsupportedOperationException();
}

该类的addremove方法直接抛异常了,因此调用Arrays类的内部ArrayList类的add和remove方法,同样会抛异常。

说实话,Java代码优化是一个比较大的话题,它里面可以优化的点非常多,我没办法一一列举完。在这里只能抛砖引玉,介绍一下比较常见的知识点,更全面的内容,需要小伙伴们自己去思考和探索。

这篇文章写了很久,花了很多时间和心思,如果你看了文章有些收获,记得给我点赞鼓励一下喔。

聊聊Java中代码优化的30个小技巧

Java中代码优化的30个小技巧相关推荐

  1. Java 中代码优化的 30 个小技巧(中)

    11 位运算效率更高 如果你读过 JDK 的源码,比如 ThreadLocal.HashMap 等类,你就会发现,它们的底层都用了位运算. 为什么开发 JDK 的大神们,都喜欢用位运算? 答:因为位运 ...

  2. Java 中代码优化的 30 个小技巧(下)

    21 防止死循环 有些小伙伴看到这个标题,可能会感到有点意外,代码中不是应该避免死循环吗?为啥还是会产生死循环? 殊不知有些死循环是我们自己写的,例如下面这段代码: while(true) {if(c ...

  3. 聊聊我们Java中代码优化的30个小技巧

    今天我们一起聊聊Java中代码优化的30个小技巧,希望会对你有所帮助. 1.用String.format拼接字符串 不知道你有没有拼接过字符串,特别是那种有多个参数,字符串比较长的情况. 比如现在有个 ...

  4. Java 中代码优化的 30 个小技巧(上)

    前言 今天我们一起聊聊 Java 中代码优化的 30 个小技巧,希望会对你有所帮助. 1 用 String.format 拼接字符串 不知道你有没有拼接过字符串,特别是那种有多个参数,字符串比较长的情 ...

  5. Java中不可或缺的59个小技巧,贼好用!

    来源:https://blog.dogchao.cn/?p=70 <Effective JavaJava>名著,必读.如果能严格遵从本文的原则,以编写API的质量来苛求自己的代码,会大大提 ...

  6. Java中char 转化为int小技巧

    char a = '3'; int b = a; 上图这样是可以将char类型强转为int类型,但是的是ASCII值,并不是我们想要的int类型值 char a = '3'; int b = a - ...

  7. java 有趣注释_Java8 中有趣酷炫的小技巧

    https://mp.weixin.qq.com/s/ZlbcfT-fUoVEctSqBeZWcg Java8 中有趣酷炫的小技巧 执行注释 大多数开发人员认为 注释 永远不会在程序中执行,并用于帮助 ...

  8. Java内存管理的9个小技巧

    1.最基本的建议是尽早释放无用对象的引用.如:  ...  A a = new A();  //应用a对象  a = null; //当使用对象a之后主动将其设置为空  -.  注:如果a 是方法的返 ...

  9. echart 数据视图_关于数据可视化图表的制作,你需要关注的30个小技巧

    优秀的数据可视化图表只是罗列.总结数据吗?当然不是!数据可视化其真正的价值是设计出可以被读者轻松理解的数据展示,因此在设计过程中,每一个选择,最终都应落脚于读者的体验,而非图表制作者个人. 今天就给大 ...

最新文章

  1. Matlab图像处理创新实践-实验1【图像滤波基础(1)】
  2. MySQL: Starting MySQL….. ERROR! The server quit without updating PID file解决办法
  3. ArcUser 2006第2期拾零
  4. JS Math对象(算数、四舍五入、随机数)
  5. python求一元三次方程的根_关于二次、三次、四次方程求解方法讨论
  6. 文档级关系抽取方法,EMNLP 2020 paper
  7. java中if结构用图表示_Java if语句结构和指令流水线
  8. 下载eclipse太慢怎么办?
  9. 使用cxf3.0.4搭建webservice服务需要的最精简jar包
  10. HFSS阵列天线设计与仿真2
  11. Python语言程序设计 第七周 文件和数据格式化
  12. 生信学习——转录组测序分析的大致流程
  13. 拍沪牌服务器响应,拍中了四张沪牌,再来聊聊拍牌经验
  14. SDUT —— 计算组合数
  15. EASE:一种融合实体信息的句子嵌入对比学习方法
  16. 第三方支付如何帮助在线教育打通支付
  17. 从成人网站年终数据统计看各操作系统的份额表现
  18. 推荐一个视频播放器potplayer
  19. three - 3 - 基础知识(1. three渲染结构,2.对canvas 进行响应式布局,3.让canvas 画布自适应设备分辨率 )
  20. Source Insight 4.0首次安装提示unable to open or create...解决方案

热门文章

  1. 《千与千寻》中的40个暗示!
  2. 深入理解StrongReference,SoftReference, WeakReference和PhantomReference
  3. 计算机音乐数学歌,和数学有关的歌曲盘点
  4. PHP使用phpqrcode生成带LOGO或文字的二维码
  5. 傲骨外表下的时尚宠儿——山茶花
  6. c语言oled p14x16str,OLED液晶屏幕(4)串口读取文字并分割,液晶屏幕显示
  7. 精选最新20个优秀源码下载网站排行
  8. 从强迫思维了解人的认知
  9. iOS在图片上添加文字 图片
  10. Eclipse常用操作烂笔头记忆