在学习上一个类TreeMap的时候,提到了这个类,这个类是jdk1.7新增的,里面有不少实用的方法。就是一个工具类,熟悉之后,若是里面有已经实现的方法,那么就不要再去实现了,省时省力省测试。java
这是一个工具类,介绍相对会简单些,基本都是方法的介绍。编程
Objects是一个主要针对对象的工具类,因此它的命名只是在后面加上一个s,就像Arrays是操做数组的工具类同样。这就涉及到一种设计理念,那就是工具方法应该放在哪:数组
第一种不太建议,既然都是一个工具类,那么就应该拿出来给别人用,或者本身之后用。less
第二种和第三种我比较不出来,我用的是第三种,创建一个大而全的Utils类,固然它们之间仍是能够比较的:jvm
先看下面一部分代码:ide
public final class Objects { private Objects() { throw new AssertionError("No java.util.Objects instances for you!"); } }
由于是工具类加上final不让别人继承,还有就是构造方法加上私有并抛异常。工具
说实话我以为没有必要这么作,或许他们考虑的比较多,可是这么作也没什么坏处,之后我写工具类也这样,显得高大上一些。学习
Objects里面提供两种比较方式:测试
public static boolean equals(Object a, Object b) { return (a == b) || (a != null && a.equals(b)); } public static boolean deepEquals(Object a, Object b) { if (a == b) return true; else if (a == null || b == null) return false; else return Arrays.deepEquals0(a, b); }
其中咱们广泛使用的就是equals,而后我看deepEquals的源码的时候,发现了一个以前忽略的事而后我作了测试:ui
int[] a1=new int[]{1}; int[] a2=new int[]{1}; System.out.println(a1.equals(a2));
结果是false,也就是说由于数组(以数字为下标,不表明里面的内容是数字)的equals都是false,除非它们是同一个对象。也就是说咱们之后若是要作两个对象的相等判断,若是比较的是两个数组那么记得使用Objects的deepEquals方法。
你们都知道在null值对象上调用任何方法都会报空指针异常,在一些须要判断对象是不是null的状况下,抛异常是有必要的,可是当能够容许null值的时候,不少Object经常使用的方法就须要额外的处理,而Objects就帮咱们作了:
public static String toString(Object o) { return String.valueOf(o); } public static int hash(Object... values) { return Arrays.hashCode(values); } public static String toString(Object o, String nullDefault) { return (o != null) ? o.toString() : nullDefault; }
因此使用起来很方便,可是必定要注意使用场景,保证null值是有效值的时候使用。
不少时候,咱们须要保证入参的正确性,最简单的就是传入的不能是null,若是说上层须要将错误缘由展现给用户看,咱们就须要主动检查是否为null,而且主动返回null值所表明的信息(好比说用户名不能为空啦,邮箱不能为空啦),而不是抛异常。
可是当不须要给用户看的时候,那就能够直接将异常抛出来就能够了,这就有两种策略:
如今大多数人都不多主动去检查null值,我以为这是很差的编程习惯,我认为应该在方法入口的时候主动检查null值并抛异常,缘由以下:
因此建议之后写代码的时候多多主动检查,使用
public static <T> T requireNonNull(T obj) { if (obj == null) throw new NullPointerException(); return obj; } public static <T> T requireNonNull(T obj, String message) { if (obj == null) throw new NullPointerException(message); return obj; }
上面说的好处你会慢慢体会到的。
如今先看下面两个方法,一样是判断null值的:
public static <T> T requireNonNull(T obj, String message) { if (obj == null) throw new NullPointerException(message); return obj; } public static <T> T requireNonNull(T obj, Supplier<String> messageSupplier) { if (obj == null) throw new NullPointerException(messageSupplier.get()); return obj; }
传入的message是在当参数是null值的时候传入给异常使用的,可是为何会有两个方法呢,并且第二个看起来远远比第一个麻烦,咱们先看怎么使用:
public void test1(String param){ Objects.requireNonNull(param, "param can not be null"); //do something about param }
上面是方法一调用的方式,很简洁明了,下面是方法二的调用方式:
public void test2(String param){ Objects.requireNonNull(param, new Supplier<String>() { @Override public String get() { return "param can not be null"; } }); //do something about param } public void test3(String param){ Objects.requireNonNull(param, ()->"param can not be null"); //do something about param }
有两种方式,第二种是jdk8的lambada表达式,若是不了解的话,但愿抽时间学习一下仍是有必要的。
能够看到延迟消息的调用要比普通消息复杂,可是为何还要推荐这种方式呢?再举一个例子,之前我看过以下记录日志的方式:
if(log.isDebugEnabled()){ log.debug("read file sucess, read info is :"+properties); }
当时我对这种代码嗤之以鼻,我以为明明log.debug在里面已经判断日志级别了,为何在外面还要在判断一次,如今想一想当时仍是太年轻。若是你仔细想一想或许能发现,若是咱们提早判断日志级别那么,当不符合日志级别的时候咱们就不须要拼接字符串,而若是咱们直接用:
log.debug("read file sucess, read info is :"+properties);
用上面代码的话,咱们就须要每次都要拼接完字符串后再进行判断,而debug日志基本上量都特别大,并且基本不会打印,这也就形成了字符串拼接过多,而字符串会放到常量池里。
因此简单总结下延迟消息的好处,那就是在须要的时候才进行拼接,这个在log4j2中已经提供相应的支持,可是这个好处到底有多少,我如今还不清楚。
这个类是一个工具类,不少方法用起来都很方便,若是用的好的话,应该能解决程序中出现的大部分NullPointerException,因此呢之后对经常使用的方法要作到封装复用。
大体看来遗留一个未深刻研究的问题
上面1.6讲解了延迟消息,我只知道延迟消息有好处,虽然jdk注释中也明说了:
the costs of creating the message supplier are less than the cost of just creating the string message directly
建立延迟消息要比直接建立字符串高效,可是能高效多少呢,这是一个疑问,固然这也牵扯到jvm对字符串的处理。我会抽时间作以下深刻测试和研究:
固然研究和测试只是为了验证论点,这不能成为不用这个技术的理由,之后的使用场景我仍是会使用延迟消息的。