Java对象为Null时无法调用非静态方法

今天玩了下Java重写toString()方法:

Object是所有类的父类:

Object类中存在toString方法是所有类重写该方法的根本:

Integer.toHexString(hashCode()) 以对象的哈希码为参数,以16进制无符号整数形式返回此哈希码的字符串表示形式;

可以简单理解为根据对象的的Hash值返回字符代表对象的地址值

在重写该方法时脑袋蹦出一个想法:在我以往的代码开发其中 经常出先对象tostring报错:空指针异常的情况 

解决方法:在企业生产中 所有实体的父类重写toString()方法:判读当前对象是否为null 如果为null 

则返回”null 提示 “ 如下:

运行测试方法 发现仍会报错空指针异常:

查阅资料知道:

当一个对象为null时 不能调用该对象的非静态方法 静态方法会存在静态池中 被该对象的所有实例使用:

null对象自身的存储空间内是没有任何方法和属性的,所以任何空对象.xxx调用非静态方法或属性都会出现空指针异常;

Static 方法是存储在静态池当中的,一个类的所有对象共享静态池的方法;

tostring方法无法修改为静态:

看来Java这些基础的东西已经很完整了!

=======分割线=============================================================

是否可以通过切面的形式实现呢?

* 1、execution(public * *(..)) 任意的公共方法

我们可以针对所有的toString方法切面 根据point 获取当前对象 如果为null 不报错呢?

成功使用切面前 解决maven依赖版本问题:

大事化简!

终于解决aop切面日志注解未生效的问题!!!!

总结:抛弃无用依赖 回归原始基础依赖 缺什么再补!

2.Spring Aop 默认不代理测试类中的方法 所以之前在测试类方法添加注解 不会生效!

3.AfterReturning()的切面顺序高于After!

4.Around()环绕通知未生效! 原因是未添加注解:

发现添加环绕通知后 before 和 after 等切点均为生效 如下:

后来发现 环绕通知后 并未让程序继续运行!

可以看出执行顺序:

环绕通知Around -》Before-》 AfterReturning-》After

生产环境中 貌似before 以及 after 等 不常使用 :

注:@Slf4j == private Logger logger = LoggerFactory.getLogger(LogAop.class);

不信请看:

所以使用该注解 在代码中可直接使用log.xxxx

二、日志具体优化 

1.简化类名:

logger.info("执行方法:"+ joinPoint.getTarget().getClass().getName()+"."+name);
logger.info("执行方法优化后:"+ joinPoint.getTarget().getClass().getSimpleName() + "." + joinPoint.getSignature()
                .getName());
.getSimpleName()可以获取简单的类名 getName()获取全路径下的类名 生产环境中 包层级复杂 建议使用.getSimpleName()

2.参数优化

优化之前分别输出方法和参数值 减少日志条数 采用StringBuilder拼接字符串 提高性能的同时以程序员习惯的方式输出方法调用 关于字符串拼接的性能问题 可以参考文档:

关于 Java 字符串拼接的几种方式以及性能比较 - 简书

总结就是 

三、输出方法执行时间+返回值

以上就是简单的日志格式优化以及内容丰富 

下一步要实现怎么弄到数据库中

附源码:

package xbh.aop.impl;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.net.InetAddress;
import java.net.UnknownHostException;

@Component
@Aspect
@Slf4j
public class LogAop {
    /**
     * 定义切入点:对要拦截的方法进行定义与限制,如包、类Z
     *
     * 1、execution(public * *(..)) 任意的公共方法 //注 使用不加限定的切点 程序启动会报错!
     * 2、execution(* set*(..)) 以set开头的所有的方法
     * 3、execution(* com.lingyejun.annotation.LoggerApply.*(..))com.lingyejun.annotation.LoggerApply这个类里的所有的方法
     * 4、execution(* com.lingyejun.annotation.*.*(..))com.lingyejun.annotation包下的所有的类的所有的方法
     * 5、execution(* com.lingyejun.annotation..*.*(..))com.lingyejun.annotation包及子包下所有的类的所有的方法
     * 6、execution(* com.lingyejun.annotation..*.*(String,?,Long)) com.lingyejun.annotation包及子包下所有的类的有三个参数,第一个参数为String类型,第二个参数为任意类型,第三个参数为Long类型的方法
     * 7、execution(@annotation(com.lingyejun.annotation.Lingyejun))
     */

    private Logger logger = LoggerFactory.getLogger(LogAop.class);
    private static String IP = null;

    static {
        try {
            IP = InetAddress.getLocalHost().getHostAddress();
        } catch (UnknownHostException e) {
            log.error("ip get fail");
        }
    }

    /**
     * 拦截的方法进行定义与限制
     * xbh.aop.impl包下任意的公共方法
     */
    @Pointcut("execution(public * xbh.aop.impl..*(..))")
    public void getLogByExe(){

    }

    /**
     * 自定义注解
     */
    @Pointcut("@annotation(xbh.aop.impl.LogAnno)")
    public void getLogByAnno(){

    }

//    @Before("getLogByExe()")
//    public void beforeMethod(){
//        logger.info("调用函数前!");
//    }
//
//    @After("getLogByExe()")
//    public void afterMethod(){
//        logger.info("调用函数后!");
//    }
//
//    @AfterReturning("getLogByExe()")
//    public void afterReturn(){
//        logger.info(" @AfterReturning");
//    }

    @AfterThrowing(pointcut = "execution(public * xbh.aop.impl..*(..))", throwing = "error")
    public void afterThrowing(JoinPoint jp, Throwable error){
        logger.error("@AfterThrowing:"+error.getMessage());
    }


    @Around("getLogByExe()")
    public void logAspect(ProceedingJoinPoint joinPoint) throws Throwable {
        logger.info("host ip : "+IP);
        // 获取目标方法的名称
        String name = joinPoint.getSignature().getName();
        //getSimpleName 类名简化形式
        String method = joinPoint.getTarget().getClass().getSimpleName() + "." + joinPoint.getSignature()
                .getName();
        // 获取方法传入参数
        Object[] args = joinPoint.getArgs();
        if (args.length==0){
            logger.info("执行方法 : "+ method+"()");
        }else{
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < args.length; i++) {
                //getSimpleName 参数类型简化形式
                sb.append(args[i].getClass().getSimpleName());
                sb.append(" ");
                sb.append(args[i]);
                if (i!=args.length-1){
                    sb.append(",");
                }
            }
            logger.info("执行方法 : "+ method+"("+sb+")");
        }

        long start = System.currentTimeMillis();
        long cost;
        //获取方法返回值:
        Object proceed = joinPoint.proceed();
        if (proceed != null){
            logger.info("方法返回:"+proceed.getClass().getSimpleName()+" "+proceed);
        }
        //方法调用花费时间
        cost = System.currentTimeMillis() - start;
        logger.info("方法执行时间:"+cost);

    }

}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值