java代码小tips记录

465 阅读12分钟

最近写java代码学到的一些point,主要是我在公司实习的时候,自己碰到的一些问题,总结一下。会持续更新。

1.知识点

1.解决dao层频繁访问数据库问题

问题:平常写java的都经常会有功能点:service.getElementByID(id),以前我在写的时候,比如从前端接收到对象的list-》根据list中某个对象的id查询数据库的某张表的其他属性name-》第一反应是直接for循环遍历获取对象id,然后根据id调用service中的getnameByID方法。

这个做法实际上是可以的,但是如果数据量很大的情况下,多次访问数据库IO消耗巨大。

解决:先select * from 数据中的某张表A,获取全部需要的数据,然后把你需要查找的一列name与ID一一对应存入map。然后for循环list中的id时,直接从map中查找。

这样操作的话就相当于只访问了一次数据库。同时因为map的get操作的时间复杂度在理想的情况下为O(1)。

代码

    //定义map list为一次从数据库获取所有数据
    Map<String,Object> map = new HashMap<>();
    List<Object> lists = service.findAll();
    if(lists!=null && !lists.isEmpty()){
        //将list中所有数据存入map
        lists.forEach(bean->{
            map.put(bean.getID(),bean);
        });
    }
    //在操作的时候,如果map中存在对应的key,直接get
   if(map.containsKey(bean.getID()) ){
          System.out.println(bean.getName());          
    }


2.异常捕捉

2.0 异常关键词

异常关键字
1:try catch
try catch是这之间所包含的代码如果出现异常时捕获他,并进行处理的,如果代码之间有错误,不会影响程序继续执行下去,程序会继续往后执行。
2:throw
是在程序中明确抛出引发的异常,比如throw new Exception();
3:throws
表明方法可能会引发异常,在方法内部并不处理这个异常,想要得到异常的话,调用者使用try catch语句即可得到
4:finally
不管有没有异常程序段中都会被执行的代码 

2.1 for循环异常捕捉

问题:其实我们在学校自己做项目的时候,几乎不会去主动catch异常,很多异常情况都不能很规范的考虑到。而在公司中业务则要求:某一个块碰到什么问题,一定要以日志的形式打印出来,碰到什么异常捕获并返回jsonMap说明异常情况。

解决:养成异常捕获的习惯。

站在用户的角度去思考业务,而不是站在代码的角度。

我们在写一个业务的时候, 不要理所当然的觉得按照我们的逻辑是不会出现问题的,但是用户是小白,他们在操作的时候不完全按照我们的逻辑。

代码

private Map<String,Object> getRes(String A) {
        Map<String, Object> jsonMap = new HashMap<>();
        try {
            List<Object> a = service.getByA(A);//这个方法可能抛异常
            Map<String, Object> map = new HashMap<>();
            for(Object p:a){
                if(StringUtils.isBlank(p.getBootStatus())){
                    continue;//过滤异常情况,避免抛异常
                }
                if(StringUtils.isNotBlank(p.getId())&&StringUtils.isNotBlank(p.getIp())){
                    map.put(p.getName(),p.getIp());
                }
            }
            jsonMap.put("success", true);
            jsonMap.put("data",map);
        } catch (Exception e) {
            jsonMap.put("success", false);
            jsonMap.put("errorMsg", "A = " + A + " 访问 api  发生异常!");
            log.error("exception:{} ",e);
        }
        return jsonMap;
    }

备注:对于异常的处理,很多公司都会比较重视。

  • 某次面试,被面试官问:用过或者遇到过哪些异常?

对于我来说,常见的异常有NullPointerException,ArrayIndexOutOfBoundsException,SQLException,IOException。

2.2 新建一个异常类

偶然看到一段代码是自己自定义的异常,不明白为什么需要自己定义异常并且抛异常?所以去查阅了一些资料。

为什么要自定义异常
大的项目业务线比较多,不同功能模块的异常类型不一样,如果不自定义的区分的话,对于捕获异常来就没有区别性。所有一般业务中不同场景抛不同的异常,便于统一捕捉并且根据类型做进一步的处理。 但是自定义异常一般会增加工作量,需要自己权衡是否自定义异常类。

如何自定义异常类

//1.自定义异常类:继承RuntimeException(运行时异常) 或 Exception
public class MyException extends Exception { 
  private static final long serialVersionUID = 1L; 
  // 提供无参数的构造方法
  public MyException() { 
  } 
  // 提供一个有参数的构造方法,可自动生成
  public MyException(String message) { 
    super(message);// 把参数传递给Throwable的带String参数的构造方法 
  } 
} 
//2.抛异常类
public class CheckScore { 
  // 检查分数合法性的方法check() 如果定义的是运行时异常就不用抛异常了 
  public void check(int score) throws MyException {// 抛出自己的异常类 
    if (score > 120 || score < 0) { 
      // 分数不合法时抛出异常 
      throw new MyException("分数不合法,分数应该是0--120之间");// new一个自己的异常类 
    } else { 
      System.out.println("分数合法,你的分数是" + score); 
    } 
  } 
} 

//3.捕获异常
import java.util.Scanner; 
 
/* 
 * 自定义的异常测试类 
 */ 
public class Student { 
  public static void main(String[] args) { 
    Scanner sc = new Scanner(System.in); 
    int score = sc.nextInt(); 
    CheckScore check = new CheckScore(); 
    try { 
      check.check(score); 
    } catch (MyException e) {// 用自己的异常类来捕获异常 
      e.printStackTrace(); 
    } 
  } 
 
} 

备注: 异常分类方便捕捉不同的异常类型,并进行对应的操作。在稍微复杂的业务系统中,仅仅使用Java自身的异常类是不够的,比如常见的NPE/IAE等。

  1. 有时候需要对参数进行各种检查,NPE不足以清晰的说明异常的具体内容。比如有多个参数,对每个参数进行自定义的校验规则,可以通过IAE(message: String)来让调试过程更加友好。
  2. 自定义参数,比如数据解析异常,可能中间多处可能抛出,这里可以定义一个MyParseException,针对此类异常通常使用相同的善后操作。
  3. 自定义异常可以定义层次结构,即某个业务系统中的所有异常统一顶一个根类,然后再定义一些子类异常,分类进行针对性的操作。比如定义一个MyRootException,然后对网络通信解析的异常MyParseException,对数据访问的异常MyDataException等,这些异常都代表了不同的类型,操作句柄也都是不一样的。

参考链接:www.cnblogs.com/yanggb/p/10…

3.@Value注解使用

作用:读取配置文件的属性
注解方式

 定义(map类型): 
    property.key = {"A":"a","B":"b"}   
 使用:
    @Value("#{${property.key}}")
    private Map<String, String> propertykey;
    
    propertykey.get(A);

4.controller接收list参数的时候需要添加@RequestBody注解

5.mybatis-generator生成bean的时候对于mysql中text字段的特殊处理

在generatorconfig中添加
<columnOverride column="your column" jdbcType="VARCHAR" />
即如下:
<table schema="XXX" tableName="table" >
    <columnOverride column="your text column" jdbcType="VARCHAR" />
</table>

6.logger

打印错误的时候,最好包括:类名,方法名,输入的参数,错误代码,错误信息等

logger.info("classname.method input:{}, error code:{},message:{},request ID:{}",A,B,C,D); 

7.后端String转化成Map,返回前端并以json格式显示

//后端拿到的是String数据,如input-String,但前端需以json的格式显示。
Gson gson = new Gson();
Map<String,Object> map = new HashMap<String,Object>();
map = gson.fromJson("input-String",map.getClass());
jsonMap.put("data",map);

eg:
1.input-String
"{\"24\":\"公务员补收\",\"25\":\"特殊手工补收\",\"10\":\"正常应缴\",\"32\":\"一次性清算\",\"20\":\"补收\",\"21\":\"离休人员补收\"}"

2.map
{
	"24": "公务员补收",
	"25": "特殊手工补收",
	"10": "正常应缴",
	"32": "一次性清算",
	"20": "补收",
	"21": "离休人员补收"
}

//返回的数据 直接使用JSON.stringify(data,null,2)显示
 if(response.success){
    var data = response.data;
    var myJSON = JSON.stringify(data,null,2);
    console.log(myJSON);
    $("#"+domID2).val(myJSON);//在显示框中显示
  } else {
       error_messages("msg", null, response.message);
  }


//JSON.stringify(value, replacer, space) value是对象 按两个字符缩进 

8.SSH 堡垒机 线上查看日志

在公司实习,一般项目有三种环境,分别为dev:本地开发环境 test:线上的测试环境 online:线上真实环境。

  • dev环境一般就是每个人的本地环境,dev,test,online是统一的登录接口,大概是为了方便管理,不过我还不清楚为什么dev环境也需要公司内网才能运行。
  • test和online一般都是在服务器上,一般的工作流程是本地功能实现测试完成之后,会推送到测试环境,再测试一遍,但是这个时候如果出现问题,是不能调试的,如果想要进测试环境的话,是不能直接ssh 测试环境的服务器,而是通过堡垒机。
  • 每次登录线上的机器都要ssh xxxxip到堡垒机上,然后再到堡垒机上选择你项目组中的测试环境ip连接。
//查看线上日志,实时打印
tail -F XXX.log

9.Linux查看某进程是否运行并杀掉

ps -ef | grep tomcat
kill -9 pid

10.解决idea闪退(idea如何修改内存)

20191220:今天早上突然idea闪退,多次打开皆没过多久就闪退,并且打开任务管理器发现每次idea一打开CPU直逼100%,想到我可是24G的运行内存,觉得很好奇。

突然想起来昨天在idea运行的时候,弹框outofmemory,类似下图,我直接设置了-Xmx为1024。

为了查看idea的内存情况,打开memory indicator。

发现内存显示:980 of 990M(下图为修改之后内存情况)
于是百度 idea 闪退 ,发现很多朋友也是出现这个问题,大部分人都说是因为idea的-Xmx设置的太小了,然后我就去Idea的安装路径去设置了

-Xms128m
-Xmx2048m
-XX:MaxPermSize=350m
-XX:ReservedCodeCacheSize=240m
-XX:+UseConcMarkSweepGC
-XX:SoftRefLRUPolicyMSPerMB=50
-ea
-Dsun.io.useCanonCaches=false
-Djava.net.preferIPv4Stack=true
-XX:+HeapDumpOnOutOfMemoryError
-XX:-OmitStackTraceInFastThrow

设置之后idea仍然闪退,后面发现idea的启动文件根据你装的idea是32位还是64位而加载不同的启动文件。

尝试设置idea.exe.vmoptions的-Xmx为2048,发现还是会闪退。有点绝望。 偶然看到一篇博客说可能是设置的太大了,尝试性的把-Xmx设置为512,解决了闪退问题。

-server
-Xms128m
-Xmx512m
-XX:MaxPermSize=250m
-XX:ReservedCodeCacheSize=240m
-XX:+UseConcMarkSweepGC
-XX:SoftRefLRUPolicyMSPerMB=50
-ea
-Dsun.io.useCanonCaches=false
-Djava.net.preferIPv4Stack=true
-XX:+HeapDumpOnOutOfMemoryError
-XX:-OmitStackTraceInFastThrow
参数说明
-XX:ParallelGCThreads=  的值修改为你cpu的核数.

-Xms512m 设置初时的内存大小,提高Java程序的启动速度

-Xmx750m 设置最大内存数,提高该值,可以减少内存Garage收集的频率,提高程序性能

-Xverify:none 关闭Java字节码验证,从而加快了类装入的速度,并使得在仅为验证目的而启动的过程中无需装入类,缩短了启动时间

-XX:+UseParNewGC 使用并行收集算法

-server 控制内存garage方式,这样你无需在花一到两分钟等待内存garage的收集

-Dsun.awt.keepWorkingSetOnMinimize=true 可以让IDEA最小化到任务栏时依然保持以占有的内存,当你重新回到IDEA,能够被快速显示,而不是由灰白的界面逐渐显现整个界面,加快回复到原界面的速度。

问题:事实证明idea内存不是设置的越大越好。为什么我的电脑设置的1024M太大会闪退而需要设置为512M,而有的用户设置的750太小会闪退而需要设置1024M甚至更多???

参考:www.jianshu.com/p/5600ed195…

11.gc overhead limit exceed

这个问题是我在推包到虚机上的时候发生的,参考:juejin.cn/post/684490…
看到这个问题的时候查阅了文档发现是因为:超过了GC的开销限制,导致内存溢出。也就是说超过98%的时间用来做GC,并且回收了不到2%的堆内存时会抛出此异常。
查阅百度的时候:

修改bin目录下catalina.sh文件
在cygwin=false之上
添加以下语句
JAVA_OPTS="-Xms1024m -Xmx4096m -Xss1024K -XX:PermSize=512m -XX:MaxPermSize=2048m"

其中-xms为jvm初始化堆的大小,-xmx为jvm堆的最大值
2. window 下调整tomcat的内存设置
修改bin目录下catalina.bat文件@echo off下追加
set JAVA_OPTS=-XX:PermSize=64M -XX:MaxPermSize=128m -Xms512m -Xmx1024m

3.eclipse下修改外置tomcat的内存设置
open launch configuration=>arguments=>vm arguments
追加-Xms1024M -Xmx4096M -XX:PermSize=512m -XX:MaxPermSize=2048m
 
————————————————
版权声明:本文为CSDN博主「liny丶」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_23154263/article/details/79503903

这个部分能够解决大部分问题,所以收集了。但是没有解决我的问题。看了很久才发现我的问题是因为我开了多个Tomcat进程。kill重启Tomcat即可。哈哈哈我是个逗比。但是解决这个问题中还是学到了不少。

12.git基于本分支创新新分支

1.切换到当前分支
git checkout develop //进入develop分支
2.根据当前分支新建一个分支
git checkout -b fromdevelop //以develop为源创建本地分支fromdevelop
3.推送到远程分支
git push origin fromdevelop //将本地fromdevelop分支作为远程fromdevelop分支

13.idea remote Tomcat

blog.csdn.net/qq_35246620…

14.13位时间戳转化为Date格式

public static Date numberDateFormatToDate(String timestamp,String  simpleDateFormatType){
        SimpleDateFormat sdf = new SimpleDateFormat(simpleDateFormatType);//要转换的时间格式
        Date date = null;
        try {
            if (timestamp.length() == 13){
                date = sdf.parse(sdf.format(Long.parseLong(timestamp)));
            }else{
                date = sdf.parse(sdf.format(Long.parseLong(timestamp)*1000));
            }
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return date;
    }
 

15.US格式时间转化成Date

SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        System.out.println(df.format(date));
        t.setCreateDate(date);
        try {
            SimpleDateFormat sdf = new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy", Locale.US);
            Date d=sdf.parse(td.get("CreationDate").toString());
            SimpleDateFormat sdf2=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            t.setBucketCreationDate(sdf2.parse(sdf2.format(d)));
        } catch (ParseException e) {
            e.printStackTrace();
        }

16.格林威治时间转化Date

    private Date StringToDate(String date) {
        Date d = null;
        try {
            date = date.replace("Z", "").replace("T", " ");
            SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            d  = format.parse(date);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return d;
    }

17.js base64加密

window.btoa('china is so nb') // 编码
"Y2hpbmEgaXMgc28gbmI="
window.atob("Y2hpbmEgaXMgc28gbmI=") // 解码
"china is so nb"

2.开发工具

1.mybatis-generator

这是一款逆向工程工具,针对数据库表自动生成mybatis执行所需要的代码,一般有三种用法:命令行,eclipse插件,maven插件。比较通用的就是maven插件。
具体使用方法比较常见:
(1)在pom.xml导入依赖的包
(2)generatorConfig.xml文件
(3)IDEA中直接点击插件执行

至此,其中的pojo dao mapper xml等文件都会自动生成。
想起以前搭建框架的时候,每次都是手动创建

2.postman测试工具

上手还是很快的,对于接口测试非常有用

3.IDEA debug

debug的一方面是为了调试代码,找到问题。另一方面也可以帮助我们理解新代码。
作为一个实习生,刚到公司,每次写一个业务之前,一定是要熟悉别人的代码。而熟悉代码最快路径第一步是找一个接口测试这个功能点,第二步是在代码中打断点调试,看每个函数调用的返回值的变化。

1.断点尽量打在有返回值的语句、if判定条件语句
2.F9跳转断点,快捷。代码修改之后,可重复发送请求继续调试
3.80%的问题都可以用debug解决

4.热部署Jrebel

这是一个非常实用的后端项目热部署插件。因为idea没有集成热部署工具,所以只能安装插件使用,可以让你效率加倍。众所周知,Spring项目如果很大的话,每修改一点代码就要重启Tomcat,耗时长。

n.总结

  • 公司的大佬还是很多的,某天看到小组老大写的代码,惊叹:这就是架构师和程序员的区别啊!! 好好学习。