/** * no if statement in this method * use shift operators * if positive num : num >> 31 == 0 * if negative num : num >> 31 == -1 * ~0 = -1 (ffffffff) * ~-1 = 0 (0) */ publicstaticvoidsumAvoidBranchPrediction(int[] array, int arraySize) { longstart= System.nanoTime(); longsum=0;
for (inti=0; i < arraySize; ++i) { for (intj=0; j < arraySize; ++j) { sum += ~((array[j] - 128) >> 31) & array[j]; } } System.out.println(System.nanoTime() - start + " ns"); // _606_267_300 ns System.out.println("sum : " + sum); }
// Variant type parameters could be declared in interfaces or delegates only!
interfaceICovariant<outT> { }
classCovariant<T> : ICovariant<T> { }
interfaceIContravariant<inT> { }
classContravariant<T> : IContravariant<T> { }
interfaceIInvariant<T> { }
classInvariant<T> : IInvariant<T> { }
classProgram { privatestaticvoidCovariant(/* out */) { ICovariant<object> obj = new Covariant<object>(); ICovariant<string> str = new Covariant<string>();
// You can assign "Derived" to "Base" obj = str; str = obj; // error }
privatestaticvoidContravariant(/* in */) { IContravariant<object> obj = new Contravariant<object>(); IContravariant<string> str = new Contravariant<string>();
// You can assign "Base" to "Derived" str = obj; obj = str; // error }
privatestaticvoidInvariant(/* none */) { IInvariant<object> obj = new Invariant<object>(); IInvariant<string> str = new Invariant<string>();
// You can't do any assign obj = str; // error str = obj; // error } }
Java 用起来如此舒适的一个因素在于,它是一门安全的语言 (safe language)。这意味着,它对于缓冲区溢出、数组越界、非法指针以及其他的内存破坏错误都自动免疫,而这些错误却困扰着诸如 C 和 C++ 这样的不安全语言。在一门安全语言中,在设计类时,无论系统的其他部分发生什么问题,类的约束都可以保持为真。对于那些 “把所有内存当作一个巨大的数组来对待” 的语言来说,这是不可能的。
即使在安全的语言中,如果不采取一点措施,还是无法与其它的类隔离开来。建设类的客户端会尽其所能来破坏这个类的约束条件,因此你必须保护性地设计程序。实际上,只有当有人试图破坏系统的安全性时,才可能发生这种情况;更有可能的是,对你的 API 产生误解的程序员,所导致的各种不可预期的行为,只好由类来处理。无论是何种情况,编写面对客户端的不良行为仍保持健壮性的类,这是非常值得投入时间去做的事情。
// Broken "immutable" time period class publicclassPeriod { privatefinal Date start; privatefinal Date end;
publicPeriod(Date start, Date end) { if (start.compareTo(end) > 0) thrownewIllegalArgumentException(start + " after " + end); this.start = start; this.end = end; }
public Date start() { return start; }
public Date end() { return end; } }
乍看之下,此类似乎是不可变的,并且加了周期的起始时间 (start) 不能在结束时间 (end) 之后。然而,因为 Date 类本身是可变的,因此很容易违反这个约束条件:
1 2 3 4 5
// Attack the internals of a Period instance Datestart=newDate(); Dateend=newDate(); Periodp=newPeriod(start, end); end.setYear(78); // Modifies internals of p!
可以肯定地说,只要有可能,都应该使用不可变的对象作为对象内部的组件,这样就不必再为保护性拷贝(见第15条)操心。在前面的 Period 例子中,有经验的程序员通常使用 Date.getTime() 返回的 long 基本类型作为内部的时间表示法, 而不是使用 Date 对象引用。他们之所以这样做,主要因为 Date 是可变的。
var output = $@"The First five items are: { src.Take( 5 ).Select( n => $@"Item: {n.ToString()}" ).Aggregate( (c, a) => $@"{Environment.NewLine}{a}" ) }";
上面的这种写法可能不太会出现在正式的产品代码中,但可以看出,内插字符串和 C# 之间结合的相当密切。ASP.NET MVC 框架中的 Razor View 引擎也支持内插字符串,使得开发者在编写 Web 应用程序时能够更便捷地以 HTML 的形式来输出信息。默认的 MVC 应用程序本身就演示了怎样在 Razor View 中使用内插字符串,以下示例节选自 controller 部分,它可以显示当前登入的用户名:
// Initialize unitCircle to contain all Points on the unit circle privatestaticfinal Set<Point> unitCircle = Set.of( newPoint(1, 0), newPoint(0, 1), newPoint(-1, 0), newPoint(0, -1) );
// try-with-resources - the best way to close resources! publicstatic String firstLineOfFile(String path)throws IOException { try (BufferedReaderbr=newBufferedReader(newFileReader(path))) { return br.readLine(); } }
// try-with-resources on multiple resources - short and sweet publicstaticvoidcopy(String src, String dst)throws IOException { try (FileInputStreamin=newFileInputStream(src); OutputStreamout=newFileOutputStream(dst)) { byte[] buf = newbyte[BUFFER_SIZE]; int n; while ((n = in.read(buf)) >= 0) out.write(buf, 0, n); } }