关于 java_关于Java你不知道的十件事

这篇博客探讨了Java编程中的一些鲜为人知的特性,包括未检查异常、方法重载规则、二维数组的表示、条件表达式的行为、复合赋值运算符的工作原理、随机数生成的奥秘、类型关系的不确定性以及类型交集的使用。通过这些深入的技术细节,揭示了Java语言的复杂性和微妙之处。
摘要由CSDN通过智能技术生成

那么,您从一开始就一直在使用Java?还记得那些被称为“ Oak”的日子,OO仍然是热门话题,C ++人士认为Java没有机会,Applet还是一件事吗?

c48a4ee1a6e765270acbf85e101572f5.png

我敢打赌,您至少不了解以下一半内容。

1.没有被检查的异常

那就对了!JVM不知道任何这样的事情,只有Java语言知道。

您是否想要证明JVM不知道这样的事情?尝试以下代码:

public class Test {

// No throws clause here

public static void main(String[] args) {

doThrow(new SQLException());

}

static void doThrow(Exception e) {

Test. doThrow0(e);

}

@SuppressWarnings("unchecked")

static

void doThrow0(Exception e) throws E {

throw (E) e;

}

}

2.您可以使方法重载仅在返回type上有所不同

那不会编译,对吗?

class Test {

Object x() { return "abc"; }

String x() { return "123"; }

}

对。Java语言不允许两个方法 在同一类中“覆盖等效”,无论它们的throws子句或returntype可能如何不同。

哇,是的,这很有意义。实际上,这几乎就是您编写以下内容时发生的情况:

abstract class Parent {

abstract T x();

}

class Child extends Parent {

@Override

String x() { return "abc"; }

}

在中检出生成的字节码Child:

// Method descriptor #15 ()Ljava/lang/String;

// Stack: 1, Locals: 1

java.lang.String x();

0 ldc [16]

2 areturn

Line numbers:

[pc: 0, line: 7]

Local variable table:

[pc: 0, pc: 3] local: this index: 0 type: Child

// Method descriptor #18 ()Ljava/lang/Object;

// Stack: 1, Locals: 1

bridge synthetic java.lang.Object x();

0 aload_0 [this]

1 invokevirtual Child.x() : java.lang.String [19]

4 areturn

Line numbers:

[pc: 0, line: 1]

因此,T实际上只是Object字节码。很好理解。

合成桥方法实际上是由编译器生成的,因为Parent.x()可能期望签名的返回typeObject在某些调用位置。如果没有此类桥接方法,则无法以二进制兼容的方式添加泛型。因此,更改JVM以使其具有此功能的痛苦就较小(这也使协变量重载成为副作用……)聪明吧?

您是否熟悉语言细节和内部知识?

3.所有这些都是二维数组!

class Test {

int[][] a() { return new int[0][]; }

int[] b() [] { return new int[0][]; }

int c() [][] { return new int[0][]; }

}

对,是真的。即使您的心理分析器可能无法立即理解上述方法的返回type,它们也是相同的!与以下代码段相似:

class Test {

int[][] a = {{}};

int[] b[] = {{}};

int c[][] = {{}};

}

你觉得这很疯狂吗?

@Target(ElementType.TYPE_USE)

@interface Crazy {}

class Test {

@Crazy int[][] a1 = {{}};

int @Crazy [][] a2 = {{}};

int[] @Crazy [] a3 = {{}};

@Crazy int[] b1[] = {{}};

int @Crazy [] b2[] = {{}};

int[] b3 @Crazy [] = {{}};

@Crazy int c1[][] = {{}};

int c2 @Crazy [][] = {{}};

int c3[] @Crazy [] = {{}};

}

输入注释。仅凭其力量超越神秘感的设备

换句话说:

当我这样做时,我的四周假期前的最后一次提交

我将为您找到上述任何一个用例的实际练习。

4.您没有条件表达式

因此,您认为使用条件表达式时就知道这一切吗?我告诉你,你没有。你们大多数人会认为以下两个片段是等效的:

Object o1 = true ? new Integer(1) : new Double(2.0);

…一样吗?

Object o2;

if (true)

o2 = new Integer(1);

else

o2 = new Double(2.0);

不。让我们进行快速测试

System.out.println(o1);

System.out.println(o2);

该程序将打印:

1.0

1个

是的 有条件的运营商将实现数字式的推广,如果“被需要”,对一个非常非常非常强的引号的“需要”。因为,您希望该程序抛出一个NullPointerException?

Integer i = new Integer(1);

if (i.equals(1))

i = null;

Double d = new Double(2.0);

Object o = true ? i : d; // NullPointerException!

System.out.println(o);

5.您也没有得到复合赋值运算符

够古怪吗?让我们考虑以下两段代码:

103ffc17baedd52ca9474a38ceaf1962.png

凭直觉,它们应该等效,对吗?但猜猜怎么了。他们不是!JLS指定:

形式为E1 op = E2的复合赋值表达式等效于E1 =(T)(((E1)op(E2))),其中T是E1的type,只是E1仅被评估一次。

这是如此的美丽,我想引用彼得Lawrey的回答这个堆栈溢出问题:

这种转换的一个很好的例子是使用* =或/ =

字节b = 10;

b * = 5.7;

System.out.println(b); //打印57

要么

字节b = 100;

b / = 2.5;

System.out.println(b); //打印40

要么

char ch ='0';

ch * = 1.1;

System.out.println(ch); //打印'4'

要么

char ch ='A';

ch * = 1.5;

System.out.println(ch); //打印'a'

现在,这有多么难以置信?我将在我的应用程序中将字符转换/乘法。因为,你知道...

6.随机整数

现在,这更像是一个难题。尚未阅读解决方案。看看您是否可以自己找到这个。当我运行以下程序时:

fd6b2c7f2fc2603afcf51132d37a9df6.png

…然后“有时”,我得到以下输出:

92

221

45

48

236

183

39

193

33

84

7.转到

这是我的最爱之一。Java有GOTO!输入...

int goto = 1;

这将导致:

Test.java:44:错误:预期

int goto = 1;

^

这是因为goto是一个未使用的关键字,以防万一...

但这不是令人兴奋的部分。令人兴奋的是,你可以真正实现与藤break,continue并标记块:

向前跳

label: {

// do stuff

if (check) break label;

// do more stuff

}

在字节码中:

2 iload_1 [检查]

3 ifeq 6 //向前跳

6 ..

向后跳

label: do {

// do stuff

if (check) continue label;

// do more stuff

break label;

} while(true);

在字节码中:

2 iload_1 [检查]

3 ifeq 9

6 goto 2 //向后跳

9 ..

8. Java具有type别名

在其他语言中(例如Ceylon),我们可以非常轻松地定义type别名:

interface People => Set;

People这样构造的type可以与Set以下项互换使用:

People? p1 = null;

Set? p2 = p1;

People? p3 = p2;

在Java中,我们不能在顶级定义type别名。但是我们可以在类或方法的范围内这样做。假设我们对Integer,Long等的命名不满意,我们希望使用更短的名称:I和L。简单:

class Test {

void x(I i, L l) {

System.out.println(

i.intValue() + ", " +

l.longValue()

);

}

}

在上述程序中,Integer“别名” I用于Test类的范围,而Long“别名” L用于x()方法的范围。然后我们可以像上面这样调用上面的方法:

new Test().x(1, 2L);

当然,这种技术不应被认真对待。在这种情况下,Integer和Long都是最终type,这意味着typeI和L是有效的别名(几乎,赋值兼容仅是一种方式)。如果我们使用了非最终type(例如Object),那么我们真的会使用普通的泛型。

这些愚蠢的把戏足够了。现在换个真正了不起的东西!

9.一些type关系是不确定的!

好吧,现在这将变得非常时髦,因此可以喝杯咖啡并集中精力。请考虑以下两种type:

// A helper type. You could also just use List

interface Type {}

class C implements Type> {}

class D

implements Type>>> {}

现在,做typeC和D甚至是什么意思?

它们有些递归,采用递归的相似(但略有不同)的方式java.lang.Enum。考虑:

public abstract class Enum> { ... }

使用以上规范,实际的enum实现仅仅是语法糖:

// This

enum MyEnum {}

// Is really just sugar for this

class MyEnum extends Enum { ... }

考虑到这一点,让我们回到两种type。以下内容可以编译吗?

class Test {

Type super C> c = new C();

Type super D> d = new D();

}

C是Type ?

步骤0)C <?: Type <?superC>

步骤1)Type > :type(继承)

步骤2)C(检查通配符?superC)

步 。。。(永远循环)

接着:

D是Type >?

步骤0)D <?: Type <?superC >

步骤1)Type >>> <?:type <?superD >

步骤2)D <?: Type <?superD >>

步骤3)Type >> <?:type <?superC >

步骤4)D > <?: Type <?superD >>

步 。。。(永远扩展)

尝试在Eclipse中编译以上代码,它将崩溃!

10.类型交点

Java具有一个非常独特的功能,称为类型交集。您可以声明一个(通用)类型,它实际上是两种类型的交集。例如:

class Test {

}

泛型类型参数T,你绑定的类的实例Test必须实现两个Serializable和Cloneable。例如,String不是可能的界限,而是Date:

// Doesn't compile

Test s = null;

// Compiles

Test d = null;

Java 8中已重用了此功能,您现在可以在其中将类型转换为临时类型的交集。这有什么用?几乎没有,但是如果您想将lambda表达式强制为这种类型,则别无选择。假设您的方法受到这种疯狂的类型约束:

void execute(T t) {}

您Runnable还Serializable希望这样做,以防万一您想在其他地方执行它并通过电线发送它。Lambda和序列化有点古怪。

Lambda可以序列化:

如果lambda表达式的目标类型和捕获的参数可序列化,则可以对其进行序列化

但是,即使这是真的,他们也不会自动实现Serializable标记器接口。要强制他们使用这种类型,必须强制转换。但是当你只投给Serializable……

execute((Serializable) (() -> {}));

…然后,lambda将不再可运行。

嗯...

所以…

将其强制转换为两种类型:

execute((Runnable & Serializable) (() -> {}));

最后,开发这么多年我也总结了一套学习Java的资料与面试题,如果你在技术上面想提升自己的话,可以关注我,私信发送领取资料或者在评论区留下自己的联系方式,有时间记得帮我点下转发让跟多的人看到哦。

b9c5f39f126fd662edf596134bbbe219.png

11f9310572d39aab89645bd36857029d.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值