Java 是一种非常成熟的编程语言 - 事实上, 它已经走过 21 年了, 如果它是一个人, 它可以在美国随便混! 随着年龄的增长, 智慧也在增长, 而至少有时候, 有些东西会变得很怪异. 在本文中, 我将介绍 Java 语言的一些奇技淫巧的行为和特性.
在这里, 没有特别的顺序去介绍一系列 Java 的奇技淫巧, 仅供娱乐, 或者你向朋友们推介它吧.
Java 有 goto 和 const 关键字
虽然 Java 没有 goto, 但它确实作为保留关键字. const 也是这样. 这意味着您无法使用这两个名称定义变量:
- int goto = 0;
- int const = 0;
这样的定义是非法的, 无法正常编译!
用 "_" 符号定义数字
Java 允许您使用 "_" 符号定义数字. 因此, 您可以像这样编写数值:
- int thousand = 1_000;
- double bigValue = 1_000_000.456_555;
- long thisIsSilly = 3______4__3;
Double.MIN_VALUE 的值是我们无法设定的
为了展示 Double.MAX_VALUE 结果比预期的效果要完美, 提供以下这样的数值: 1.7976931348623157E308. 您认为 Double.MIN_VALUE 会为您带来什么? 4.9E-324! 好, 执行开始 - 结果这个值大于 0!
Double.MIN_VALUE 是返回大于 0 的最小 Double 值(最小正数). 如果您想要最小的 Double 值, 则需要使用:-Double.MAX_VALUE. 他们实际上可以更好地命名这些东西, 我想知道这样的做法引起了多少人为的错误!
有趣的整数相等问题
谈到这个错误...... 让我告诉你一些迷惑不解的事情:
- Integer ten = Integer.parseInt("10");
- System.out.println(ten == Integer.valueOf(10));
- //this is true
- Integer thousand = Integer.parseInt("1000");
- System.out.println(thousand == Integer.valueOf(1000));
- //this is false
Integer 对象的缓存值的大小范围是在 [-128 127] 区间. 这意味着当我们在此数值范围内操作时,"==" 比较能正常返回结果. 但当数值不在此范围, 对象相等的比较是否正常返回结果就很难说了!
想象一下, 你可以编写单元测试, 且一切都正常运行, 只要你没有使用足够大的数字, 但这可能会导致严重的错误, 所以为了安全 - 提醒: 当你经常使用对象数值比较相等时, 请使用 ".equals()", 而不是依赖于 "==" 比较相等, 除非你非常肯定这种做法没有错误.
反射可以 (大多数情况) 做任何事情
这不应该作为一个 Java 奇技淫巧的内容, 但通过反射, 你可以重写 final 值 (大多数时间) 并可访问私有字段...... 但不一定经常是这样.
在我写 How to write horrible Java 的文章时, 当我重写 final 值得时候, 我发现了无法与预期结果一致的问题. Java 中的常量, 当 final 被内联时(通指内联函数), 即使你的代码看起来可以正常运行 - 没有数值情况也会改变, 这太不可思议了(查看我的文章了解详情和 Stack Overflow 答案).
如果你需要, 这是重写 final 的数值代码例子:
- public static void notSoFinal() throws NoSuchFieldException, IllegalAccessException, InterruptedException {
- ExampleClass example = new ExampleClass(10);
- System.out.println("Final value was:"+ example.finalValue);
- Field f = example.getClass().getDeclaredField("finalValue");
- Field modifiersField = Field.class.getDeclaredField("modifiers");
- modifiersField.setAccessible(true);
- modifiersField.setInt(f, f.getModifiers() & ~Modifier.FINAL);
- f.setInt(example, 77);
- System.out.println("Final value was:"+ example.finalValue);
- }
你知道 Java 标签是什么玩意吗?
好吧, 我们离开上面调皮的话题, 回到 Java 正规上来. 你知不知道 Java 循环标签? 看一下这个循环语句:
- outerLoop:
- while (true) {
- System.out.println("I'm the outer loop");
- while(true){
- System.out.println("I am the inner loop");
- break outerLoop;
- }
- }
使用标签可以让你在处理嵌套循环的时候, 继续或中断一个特定的循环...... 在不同语言环境下, 这个有点类似 goto,
现在让我们编写一个编译好了的, 形迹可疑的代码:
- int i = 3;
- http://www.e4developer.com
- while(i> 0){
- System.out.println("http://www.e4developer.com");
- i--;
- }
可以正常编译并且正常运行, 因为它只是一个附加注释的标记为 http 的循环. 这会让那些不熟悉标签的人, 使得它变得特别有趣!
枚举类
好吧, 你可能知道这一点, 但我还是想重复提一下. 枚举是具有有限数量实例的特殊类. 这意味着枚举可以:
实现接口
具有构造函数
实现不同方法
对于 Scott Logic 博客, 我写了一篇名为 Java Enums - how to use them smarter 的文章, 我展示了一些其他的使用方法.
For 循环的灵活性
循环的标准, 我相信你使用它们的次数, 比你记忆中的还要多:
- for(int i = 0; i <100; i++){
- //...
- }
您知不知道里面所有条件是可选的吗? 你不需要初始化一个变量, 你也不需要一个条件停止, 你也不需要增加任何东西...... 如果省略所有内容, 你最终会得到一个有趣的无限循环语法:
- for(;;){
- //Infinite loop!
- }
Java 有初始化程序...... 以防万一...
这是一个非常通用的特性, 但是我还是依然会遇到一些具有经验的 Java 开发人员, 他们并不会真正意识到它的存在. 在 Java 中, 您可以写类加载 (静态初始化程序) 或构造函数 (标准初始化程序) 之前运行的代码块. 它是这样的.
标准初始化程序:
- int sum = 0;
- {
- for(int i = 0; i < 1; i++){
- sum += 1;
- }
- }
静态初始化程序:
- static double value = 0;
- static {
- for(int i = 0; i < 1; i++){
- value += 1;
- }
- }
只需要将这些代码块放在类中, 不要放在任何方法或构造函数中.
双括号初始化集合
关于初始化的话题, 我会向你展示 Java 初始化集合的奇技淫巧:
- Map<String, String> map = new HashMap<String, String>() {{
- put("it", "really");
- put("works", "!");
- }};
- Set<String> set = new HashSet<String>() {{
- add("It");
- add("works");
- add("with");
- add("other");
- add("collections");
- add("too");
- }};
它在 Java 中被称为双括号初始化, 我从未见过这样的写法, 被任何人使用过...... 难道是因为没有人知道使用它吗?
在发表这篇文章之后, 很多读者很快告诉我, 这是我们应该避免的一个危险行为! 比如应当使用辅助方法 List.of()代替.(注意, 双括号初始化会派生匿名类. 派生的类 this 可以指向外部的类. 通常这不是什么大问题, 但在某些情况下使用不当会引起悲剧的问题, 例如在序列化或垃圾回收时, 应当注意这个问题)
Final 值的可以放在后面初始化
这是一个小事情, 但有些人认为, 你在定义它们时时候, 必须初始化常量值. 实际情况不是这样, 您只需要初始化它们一次就够了, 你可以使用以下有效代码核对一下:
- final int a;
- if(someCondition){
- a = 1;
- } else {
- a = 2;
- }
- System.out.println(a);
当我们混合初始化代码块和其他构造函数时, 这么做会变得棘手.
泛型扩展的桥接
尽管存在可疑的实现(类型擦除), 但泛型在 Java 中还是非常强大. 我吃惊的是, 我们可以允许定义我们需要的泛型类型. 看看这个例子:
- public class SomeClass<T extends ClassA & InterfaceB & InterfaceC>
- {
- }
你特别需要注意你定义的 T, 这特性会对你非常有用!
你还有更多吗?
我希望你喜欢我分享的 Java 奇技淫巧. 如果你还知道其他的 Java 特性和行为, 并值得分享, 请务必在评论或 Twitter https://twitter.com/e4developer 上告诉我!
来源: https://www.cnblogs.com/david1989/p/9897562.html