zoukankan      html  css  js  c++  java
  • System.out.printf使用以及注意点

    一、System.out.printf格式化输出

    1、常用控制符

    控制符

    说明

    %d

    按十进制整型数据的实际长度输出。

    %ld

    输出长整型数据。

    %md

    m 为指定的输出字段的宽度。如果数据的位数小于 m,则左端补以空格,若大于 m,则按实际位数输出。

    %u

    输出无符号整型(unsigned)。输出无符号整型时也可以用 %d,这时是将无符号转换成有符号数,然后输出。但编程的时候最好不要这么写,因为这样要进行一次转换,使 CPU 多做一次无用功。

    %c

    用来输出一个字符。

    %f

    用来输出实数,包括单精度和双精度,以小数形式输出。不指定字段宽度,由系统自动指定,整数部分全部输出,小数部分输出 6 位,超过 6 位的四舍五入。

    %.mf

    输出实数时小数点后保留 m 位,注意 m 前面有个点。

    %o

    以八进制整数形式输出,这个就用得很少了,了解一下就行了。

    %s

    用来输出字符串。用 %s 输出字符串同前面直接输出字符串是一样的。但是此时要先定义字符数组或字符指针存储或指向字符串,这个稍后再讲。

    %x(或 %X 或 %#x 或 %#X)

    以十六进制形式输出整数,这个很重要。

    代码演示:

    public static void main(String[] args) {
    2         //最常用的主要是三个:字符串 %s, 整型%d, 浮点型保留小数位%.mf(m表示小数点后m位), 
    表示换行符
    3         System.out.printf("*学生资料*
     姓名:%s
     年龄:%d岁
     考试成绩(保留两位小数): %.2f
    ", 
                                "小明", 15, 98.456);
    4     }

    控制台显示:

    另外System.out.printf有一定程度的输出格式化效果

    输出结果:

    如果 使用System.out.print(ln)格式就出现了明显不同

    2、示例代码

    package system.out;
     
    public class Printf
    {
     
        public static void main(String[] args)
        {
            //%代表格式化
            //f代表输出浮点数,9代表输出长度,如果浮点数长度不足,则补空格,如果浮点数长度超出,则按实际长度输出,2代表保留小数点后几位小数
            System.out.printf("%9.2f",1111.3);
            System.out.println();
            //-号代表向左对齐,默认向右对齐
            System.out.printf("%-9.2f", 1111.3);
            System.out.println();
            //+号代表显示正负号
            System.out.printf("%+9.2f", 1111.3);
            System.out.println();
            //+-号代表显示正负号,且向左对齐
            System.out.printf("%+-9.2f", 1111.3);
            System.out.println();
            //d代表输出整数
            System.out.printf("%4d",15);
            System.out.println();
            //o代表输出8进制整数
            System.out.printf("%-4o",15);
            System.out.println();
            //x代表输出16进制整数
            System.out.printf("%-4x",15);
            System.out.println();
            //#x代表输出带有16进制标志的整数
            System.out.printf("%#x",15);
            System.out.println();
            //s代表输出字符串
            System.out.printf("%-8s", "我们是中心");
            System.out.println();
            //x$,整数加$表示第几个变量,如果不加,变量按默认顺序排列
            System.out.printf("%2$-5s:夺得世界杯总冠军,进球数:%1$3d,对方进球:%3$2d", 4,"法国",2);
        }
    }

    二、看下底层代码实现

    public PrintStream printf(String format, Object ... args) {
            return format(format, args);
        }
    public PrintStream format(String format, Object ... args) {
            try {
                synchronized (this) {
                    ensureOpen();
                    if ((formatter == null)
                        || (formatter.locale() != Locale.getDefault()))
                        formatter = new Formatter((Appendable) this);
                    formatter.format(Locale.getDefault(), format, args);
                }
            } catch (InterruptedIOException x) {
                Thread.currentThread().interrupt();
            } catch (IOException x) {
                trouble = true;
            }
            return this;
        }

    可以看到和String.format底层实现类似调用Formatter()类的format方法。

    public static String format(String format, Object... args) {
            return new Formatter().format(format, args).toString();
        }

    三、接下来分析下 String.format与StringBuilder与String +比较

    1、测试代码:

    class StringTest {
    
        public static void main(String[] args) {
           //  testOperatorPlus();
            //testAppend();
           testFormat();
        }
    
        private static void testFormat() {
            Runtime runtime = Runtime.getRuntime();
            long memory;
            long prev_time;
            int i;
            long time;
            StringBuilder sb = new StringBuilder();
            memory = runtime.freeMemory();
            prev_time = System.currentTimeMillis();
            for (i = 0; i < 10000; i++) {
                String s = String.format("Blah %d Blah %d Blah %d", i, i, i);
            }
            long ww=runtime.freeMemory();
            time = System.currentTimeMillis() - prev_time;
            memory = memory - ww;
            System.out.println("Time: " + time + "    Memory Usage: " + memory);
        }
    
        private static void testAppend() {
            Runtime runtime = Runtime.getRuntime();
            long memory;
            long prev_time;
            int i;
            long time;
            StringBuilder sb = new StringBuilder();
            memory = runtime.freeMemory();
            prev_time = System.currentTimeMillis();
            for (i = 0; i < 10000; i++) {
                sb.append("Blah ");
                sb.append(i);
                sb.append("Blah ");
                sb.append(i);
                sb.append("Blah ");
                sb.append(i);
            }
            time = System.currentTimeMillis() - prev_time;
            memory = memory - runtime.freeMemory();
            System.out.println("Time: " + time + "    Memory Usage: " + memory);
        }
    
        private static void testOperatorPlus() {
            Runtime runtime = Runtime.getRuntime();
            long memory;
            long prev_time;
            int i;
            long time;
            StringBuilder sb = new StringBuilder();
            memory = runtime.freeMemory();
            prev_time = System.currentTimeMillis();
            for (i = 0; i < 1000000; i++) {
                String s = "Blah " + i + "Blah " + i + "Blah " + i;
            }
            time = System.currentTimeMillis() - prev_time;
            memory = memory - runtime.freeMemory();
            System.out.println("Time: " + time + "    Memory Usage: " + memory);
        }
    }
    View Code

    结果如下

    Method Time(ms) Memory Usage(long)
    ‘+’ operator 102 44053736
    StringBuilder.append 6 884768
    String.foramt 110 22639000
      
      
      可以看到StringBuilder.append的执行时间和内存占用都是最优的。'+'运算符比直接调用StringBuilder.append要慢上不少,特别是要连接的字符串数量较多时,内存占用也特别大。String.format由于每次都有生成一个Formatter对象,较慢也是情理之中。
    分析下String.format源码可以看到底层也用到了StringBuilder
    public static String format(String format, Object... args) {
            return new Formatter().format(format, args).toString();
        }
    public Formatter() {
            this(Locale.getDefault(Locale.Category.FORMAT), new StringBuilder());
        }

    四、由此引发的优化探讨

    在编码中 System.out.println将对象结果输出到控制台,会花费大量的CPU资源,因此发布的代码中不要包含System.out.println 或 System.out.printf。

    使用日志框架代替,生产环境注意控制输出级别。

    即便使用日志框架也要注意输出编码方式,例如
    反例(不要这么做):
    logger.debug("Processing trade with id: " + id + " symbol: " + symbol);

    字符串拼接,这样会产生很多String对象,占用空间,影响性能。

    另外关于日志输出其他建议:

    1、使用[]进行参数变量隔离

    如有参数变量,应该写成如下写法:

    logger.debug("Processing trade with id:[{}] and symbol : [{}] ", id, symbol);
    这样的格式写法,可读性更好,对于排查问题更有帮助。
    2、并不是所有的service都进行出入口打点记录,单一、简单service是没有意义的(job除外,job需要记录开始和结束,)。
    反例(不要这么做):
    public List listByBaseType(Integer baseTypeId) {
     log.info("开始查询基地");
     BaseExample ex=new BaseExample();
     BaseExample.Criteria ctr = ex.createCriteria();
     ctr.andIsDeleteEqualTo(IsDelete.USE.getValue());
     Optionals.doIfPresent(baseTypeId, ctr::andBaseTypeIdEqualTo);
     log.info("查询基地结束");
     return baseRepository.selectByExample(ex);
    }

    对于复杂的业务逻辑,需要进行日志打点,以及埋点记录,比如电商系统中的下订单逻辑,以及OrderAction操作(业务状态变更)。

    如果所有的service为SOA架构,那么可以看成是一个外部接口提供方,那么必须记录入参。
    调用其他第三方服务时,所有的出参和入参是必须要记录的(因为你很难追溯第三方模块发生的问题)

    3、 生产环境需要关闭DEBUG信息

    如果在生产情况下需要开启DEBUG,需要使用开关进行管理,不能一直开启。

     
     
     
    参考文章:
  • 相关阅读:
    20200209 ZooKeeper 3. Zookeeper内部原理
    20200209 ZooKeeper 2. Zookeeper本地模式安装
    20200209 Zookeeper 1. Zookeeper入门
    20200206 尚硅谷Docker【归档】
    20200206 Docker 8. 本地镜像发布到阿里云
    20200206 Docker 7. Docker常用安装
    20200206 Docker 6. DockerFile解析
    20200206 Docker 5. Docker容器数据卷
    20200206 Docker 4. Docker 镜像
    Combining STDP and Reward-Modulated STDP in Deep Convolutional Spiking Neural Networks for Digit Recognition
  • 原文地址:https://www.cnblogs.com/better-farther-world2099/p/11989696.html
Copyright © 2011-2022 走看看