多态
使用一段简单的java代码实现多态用于后续测试
package Class_load.init_fun;
import java.io.IOException;
public class polymorphic {
public static void test(Animal animal) {
animal.eat();
System.out.println(animal.toString());
}
public static void main(String[] args) throws IOException {
test(new Cat());
test(new Dog());
System.in.read();
}
}
abstract class Animal {
public abstract void eat();
@Override
public String toString() {
return "我是" + this.getClass().getSimpleName();
}
}
class Dog extends Animal {
@Override
public void eat() {
System.out.println("啃骨头");
}
}
class Cat extends Animal {
@Override
public void eat() {
System.out.println("吃鱼");
}
}
在这里使用hsdb这个工具,
使用jps查看进程id,再在hsdb当中进行连接,运行Java代码,使用jps查看进程id,进行连接,在这里hsdb弹出了一个报错,如下图所示:
很显然表示在这个文件夹下面没有swaindbg.dll文件,这个文件又要去哪里找呢?
- 首先我们找到jdk安装的路径(如果不记得了,可以再环境变量当中查看,java_home就是对应的位置)
- 找到之后进入,在jre文件夹下的bin文件夹下就可以找到这个swaindbg.dll文件,如下图所示:
- 最后一步就是把这个文件复制到报错信息当中的那个文件路径当中,
- 再一次打开hsdb,连接一个进程id就不会出现报错了,
使用,在Tool当中打开Find Object by Query
弹出一个窗口,在这里使用的语法与sql语句相似,select ?from 数据类型 别名,如:select d from Class_load.init_fun.Dog d
执行这条语句,可以看到会出现这个对象所存储的地址,点击这个地址,会打开一个窗口,也就是对象在内存当中的实际表示:
查看内存所占的实际地址,打开Windows 下的 console,使用命令 mem + 地址 + 要查看几个地址。
mem 有两个参数,参数 1 是对象地址,参数 2 是查看 2 行
查看对象的完整的表示,在Tool下打开inspector,输入前面查询的地址,回车,
查看多态的方法
接着上面,在inspector当中有一个内存地址,在这里方法都存在vtable当中,vtable和这个地址之间相差1b8(16进制数)所以我们对前面的那个地址加上一个1b8即可进行查找,在我这里,inspector当中的地址现在是 0x0000000017914028 加上一个1b8 的值为1e0 ,之后进行console当中使用mem进行查看,在这里还没有获取到vtable的长度,在inspector当中的最后面可以查看。
这样之后我们就有了对应的地址和长度就可以进行查看了,并且打开Tool当中的Class Browser窗口,输入Dog类,会弹出Dog类对应的地址,点击进去,在这里可以发现,eat方法也正好对应了console当中查询出来的eat方法。
并且在这里我们对应这个Dog类的父类进行对比,可以发现toString方法是从Animal当中继承过来的,在这里所有类都继承至Object方法,在这里也就不进行测试Object方法了。
多态的调用步骤
- 先通过栈帧中的对象引用找到对象
- 分析对象头,找到对象的实际 Class
- Class 结构中有 vtable,它在类加载的链接阶段就已经根据方法的重写规则生成好了
- 查表得到方法的具体地址
- 执行方法的字节码
异常处理
相同的,使用一段简单的java代码还进行测试,查看对应的字节码
package Class_load.init_fun;
public class try_catch {
public static void main(String[] args) {
int a = 0;
try {
a = 10;
} catch (Exception e) {
a = 30;
}
}
}
使用javap对class文件进行反编译,查看对应的字节码:
0: iconst_0
1: istore_1
2: bipush 10
4: istore_1
5: goto 12
8: astore_2
9: bipush 30
11: istore_1
12: return
Exception table:
from to target type
2 5 8 Class java/lang/Exception
- 可以看到多出来一个 Exception table 的结构,[from, to) 是前闭后开的检测范围,一旦这个范围内的字节码执行出现异常,则通过 type 匹配异常类型,如果一致,进入 target 所指示行号
- 8 行的字节码指令 astore_2 是将异常对象引用存入局部变量表的 slot 2 位置
多个catch块的异常处理
package Class_load.init_fun;
public class many_catch {
public static void main(String[] args) {
int i = 0;
try {
i = 10;
} catch (ArithmeticException e) {
i = 30;
} catch (NullPointerException e) {
i = 40;
} catch (Exception e) {
i = 50;
}
}
}
同理获得反编译的字节码:因为异常出现时,只能进入 Exception table 中一个分支,所以局部变量表 slot 2 位置被共用
0: iconst_0
1: istore_1
2: bipush 10
4: istore_1
5: goto 26
8: astore_2
9: bipush 30
11: istore_1
12: goto 26
15: astore_2
16: bipush 40
18: istore_1
19: goto 26
22: astore_2
23: bipush 50
25: istore_1
26: return
Exception table:
from to target type
2 5 8 Class java/lang/ArithmeticException
2 5 15 Class java/lang/NullPointerException
2 5 22 Class java/lang/Exception
multi-catch 的情况
在jdk1.7版本之后支持的一种编码方式,表示在这里的异常可以写在一起
package Class_load.init_fun;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class multi_catch {
public static void main(String[] args) {
try {
Method test = multi_catch.class.getMethod("test");
test.invoke(null);
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
}
public static void test() {
System.out.println("ok");
}
}
反编译后查看对应的字节码
0: ldc #2 // class Class_load/init_fun/multi_catch
2: ldc #3 // String test
4: iconst_0
5: anewarray #4 // class java/lang/Class
8: invokevirtual #5 // Method java/lang/Class.getMethod:(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;
11: astore_1
12: aload_1
13: aconst_null
14: iconst_0
15: anewarray #6 // class java/lang/Object
18: invokevirtual #7 // Method java/lang/reflect/Method.invoke:(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;
21: pop
22: goto 30
25: astore_1
26: aload_1
27: invokevirtual #11 // Method java/lang/ReflectiveOperationException.printStackTrace:()V
30: return
Exception table:
from to target type
0 22 25 Class java/lang/NoSuchMethodException
0 22 25 Class java/lang/IllegalAccessException
0 22 25 Class java/lang/reflect/InvocationTargetException
finally异常处理的情况
使用一段简单的带finally的代码进行测试
package Class_load.init_fun;
public class try_finally {
public static void main(String[] args) {
int i = 0;
try {
i = 10;
} catch (Exception e) {
i = 20;
} finally {
i = 30;
}
}
}
对class进行反编译:
0: iconst_0
1: istore_1
2: bipush 10
4: istore_1
5: bipush 30
7: istore_1
8: goto 27
11: astore_2
12: bipush 20
14: istore_1
15: bipush 30
17: istore_1
18: goto 27
21: astore_3
22: bipush 30
24: istore_1
25: aload_3
26: athrow
27: return
Exception table:
from to target type
2 5 11 Class java/lang/Exception
2 5 21 any
11 15 21 any
可以看到 finally 中的代码被复制了 3 份,分别放入 try 流程,catch 流程以及 catch 剩余的异常类型流程。
finally 当中出现return
如下代码所示:输出的结果是?
package Class_load.tryOrCatch;
public class finally_return {
public static void main(String[] args) {
int result = test();
System.out.println(result);
}
public static int test() {
try {
return 10;
} finally {
return 20;
}
}
}
我们先查看字节码:
0: bipush 10
2: istore_0
3: bipush 20
5: ireturn
6: astore_1
7: bipush 20
9: ireturn
Exception table:
from to target type
0 3 6 any
输出的结果是20;
- 由于 finally 中的 ireturn 被插入了所有可能的流程,因此返回结果肯定以 finally 的为准
- 跟上例中的 finally 相比,发现没有 athrow 了,这告诉我们:如果在 finally 中出现了 return,会吞掉异常。
异常会被吞掉,我们在try块当中添加一条会抛出异常的语句,如 int j =1/0;
再一次运行代码。不会抛出异常,且打印的值还是20。
相同的,如果去掉finally当中的return,那输出的结果又是?
public class finally_not_return {
public static void main(String[] args) {
int result = test();
System.out.println(result);
}
public static int test() {
int i = 10;
try {
return i;
} finally {
i = 20;
}
}
}
同样的先查看字节码:
0: bipush 10
2: istore_0
3: iload_0
4: istore_1 // 10 -> slot 1,暂存至 slot 1,目的是为了固定返回值
5: bipush 20
7: istore_0 // 20 -> i
8: iload_1 // <- slot 1(10) 载入 slot 1 暂存的值
9: ireturn // 返回栈顶的 int(10)
10: astore_2
11: bipush 20
13: istore_0
14: aload_2
15: athrow
Exception table:
from to target type
3 5 10 any
所以在这里返回的是10,
synchronized块
在Java代码当中synchronized关键字表示每个代码块是要一起执行的,也就加锁操作。
使用一段简单的java加锁代码:
public class synchronized_test {
public static void main(String[] args) {
Object lock = new Object();
synchronized(lock){
System.out.println("ok");
}
}
}
同样的,对class文件进行反编译:查看对应的字节码和异常表
0: new #2 // class java/lang/Object
3: dup
4: invokespecial #1 // Method java/lang/Object."<init>":()V
7: astore_1
8: aload_1
9: dup
10: astore_2
11: monitorenter //加锁
12: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
15: ldc #4 // String ok
17: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
20: aload_2
21: monitorexit //解锁
22: goto 30
25: astore_3
26: aload_2
27: monitorexit //有异常抛出同样会解锁
28: aload_3
29: athrow
30: return
Exception table:
from to target type
12 22 25 any
25 28 25 any