你真的能写好业务代码吗?

目录

 

如何用好最常见的字符串String

并发编程的利器-锁

Java中最简单的浮点数真的懂吗?

用好最简单的集合

事务真的生效了吗?

异常与日志


如何用好最常见的字符串String

String的两种创建方式

方式一:String name = “hejianfeng”; 这种方式JVM会根据常量池中的对象情况来判断是否创建对象,如果常量池中已经有该对象,就直接复用该对象。(也就是创建1个或者0个对象)

方式二:String name = new String(“hejianfeng”); 这种方式无论如何肯定会在堆内存中创建一个对象,是否在常量池中创建对象取决于常量池中是否有存在(也就是创建1个或者2个对象)

 String.intern()手动入池

当调用 intern 方法时,如果池已经包含一个等于此 String 对象的字符串(用 equals(Object) 方法确定),则返回池中的字符串。否则,将此 String 对象添加到池中,并返回此 String 对象的引用。

分析一段程序

 String name = new String(“hejianfeng”) .intern();

 String nickName = new String(“hejianfeng”).intern();

 return name == nickName;

 //结果返回true

 分析一下:创建name时,毫无疑问会在堆内创建一个对象,intern()方法会去常量池中看看有没有与之equals的对象,如果有,返回该引用,没有创建。nickName创建时候也是如此。

那有什么作用呢,用了intern方法之后,如果常量池中有对象就直接返回引用,堆内存中的对象没有引用指向就会被GC,从而节省内存空间

 String的编译期优化

 常见的String name = “he” + “jian” + “feng”,这样的常量拼接并不会创建多个对象,因为Java 编译器对于类似“常量+字面值”的组合,其值在编译的时候就能够被确定了

 Java 编译器对于含有 “String引用”的组合,则在运行期会产生新的对象 (通过调用StringBuilder类的toString()方法),因此这个对象存储在堆中

如    String str6 = "b"; 

        String str7 = "a" + str6;

        System.err.println("ab" == str7);

在第二行String str7 = "a" + str6; 由于str6是一个变量是不会被JVM优化的

  那如果我想这种情况可以被优化怎么办呢,final String str6 =”b”; 也就是申明为一个常量,在编译器确定那么就能够优化了.

  这个知识点很重要,在公司中拼接sql的时候,大家都知道直接+肯定不好,但是也有人错误的认为编译器会对此优化,其实只有常量,在编译时期确定下来的值才能被优化

 

并发编程的利器-锁

Synchonized:

修饰同步代码块:

public void method2() {

       Object o = new Object();

       synchronized (o) {

           // code

       }

}

Synchronized 在修饰同步代码块时,是由 monitorenter 和 monitorexit 指令来实现同步的。进入 monitorenter 指令后,线程将持有 Monitor 对象,退出 monitorenter 指令后,线程将释放该 Monitor 对象。

修饰方法:

public synchronized void method1() {

       // code

}

在修饰同步方法的时候是用使用了 ACC_SYNCHRONIZED 访问标志来区分一个方法是否是同步方法。调用指令将会检查该方法是否被设置 ACC_SYNCHRONIZED 访问标志。如果设置了该标志,执行线程将先持有 Monitor 对象,然后再执行方法。在该方法运行期间,其它线程将无法获取到该 Mointor 对象,当方法执行完成后,再释放该 Monitor 对象

Lock:

 

 

Java中最简单的浮点数真的懂吗?

首先一个共识就是计算机中的数据以二进制存储,也会出现像在十进制中3/10这样无限循环小数的情况,如表示0.1的二进制0.00110011…,但是计算机中存储一个数据类型的空间是有限的,所以一定会出现误差。

所以浮点数表达一些数字的时候是不精确的,这对于一些金融系统来讲尤为重要。

一般情况下我们会用BigDecimal来解决这个问题。

但是需要注意的是有几个大坑需要避免

 

1.在new BigDecimal()中传入的必须是字符串才能精确表达,如果传浮点数,由于浮点数本身可能已经无法精确表达数字了,所以计算的源头就已经错误

   System.out.println(new BigDecimal(0.1).add(new BigDecimal(0.1)));     //0.2000000000000000111022302462515654042363166809082031250

 所以如果是Double类型的怎么传入呢,可用BigDecimal.valueOf()

2.判断两个BigDecimal数字的相等问题

 首先 ==  肯定是不能用的,因为比较的包装类是两个不同的对象,很明显地址不同

那equals呢

         BigDecimal numberA = new BigDecimal("1.0");

        BigDecimal numberB = new BigDecimal("1");

       System.out.println(numberA.equals(numberB));//返回false

    BigDecimal类中重写了equals方法,方法作用是不仅比较值相等,还要小数精确的位数相等才可以,所以慎用

 

   我们可以使用CompareTo方法

    System.out.println(numberA.compareTo(numberB))

    numberA  > numberB     返回1

   numberA = numberB      返回 0

   numberA < numberB      返回-1

用好最简单的集合

是不是在面试的时候问比较ArrayList和LinkedList有什么不同的时候,增删改查效率对比的时候你张口就来,may be你可能是比较粗浅的认知哦

具体的比较可以看我写的另外一篇博文: https://blog.csdn.net/weixin_39634532/article/details/102983246

 

事务真的生效了吗?

Spring为我们提供了声明式的事务,只要加上@Transactional注解,那么这个方法就会有事务了。但是会有很多坑哦,不一定会生效哦。要是这个坑不懂,造成数据不一致,找bug可能要996哦!

首先明确一点 spring的事务是通过AOP来实现的,而AOP的原理是动态代理

想了解动态代理的看我另外一篇博文https://blog.csdn.net/weixin_39634532/article/details/103129482

  1. 由于Spring默认通过动态代理的方式来实现Aop,对目标的方法进行增强。Private方法是没办法被代理到的。所以除非特殊的配置,@Transactional注解只有在public方法上才会生效,在其他的访问限制符如private,protected等方法上面注解不会报错,但是不会生效。
  2. 事务虽然生效了,但是不一定会回滚哦

        默认情况下,事务方法出现了RuntimeException(非受检异常)或者Error的时候才会回滚。

       所以需要注意的要点是

  1. 不要吞掉异常,一定要抛出去,否则事务是不会会滚的

public Envelop query(Envelop request, Envelop response) throws CheckedException {
   
try {
       
TbBizEnauItemsub tbBizEnauItemsub = new TbBizEnauItemsub();
       
tbBizEnauItemsub.setId("12345678");
       
addSaveCommonValue(tbBizEnauItemsub,getSessionInfo(request));
       
tbBizEnauItemsubDaoExt.doStore(tbBizEnauItemsub);
        int
i = 1/0;
       
tbBizEnauItemsub.setId("1234");
        
tbBizEnauItemsubDaoExt.doStore(tbBizEnauItemsub);
   
}catch (Exception e){
       
e.printStackTrace();
   
}
   
return response;

}

以上代码有两个dao操作,中间发生了int I = 1/0这个异常,本来应该两个操作都不生效,但是由于try catch中自己吞掉了异常,没有重新抛出,也没有写回滚事务的操作,所以会导致不会滚异常代码前的执行结果

那假如由于我要捕获异常然后做一些业务上的处理怎么办呢?

要么才catch中重新抛出异常

要么在catch中手动回滚事务TransactionAspectSupport.currentTransactionStatus().setRollbackOnly()

 

 

  1. 受检异常在默认情况下是不会触发会滚的

   

上面代码createUserWrong2方法是一个事务方法,但是抛出的异常是IOException,这是一个CheckException,所以是不会触发回滚

怎么解决呢

我们可以在注解上加上一个声明,来突破受检异常不会滚的限制

@Transational(rollbackFor = Exception.class)

 

异常与日志

异常与日志这块其实是很重要,但是很多刚入行的新手不重视或者不懂,导致排查问题的时候,会出现睁眼瞎的状况。

以我以往的经验,遇到异常应该遵循的处理方式如下

 

  1. 千万不要生吞掉异常,不记录不抛出还不如不捕获异常

     2.不要转化异常

(有空继续补充中。。。。。。。。。。。)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值