本文前面是铺垫多态的,如果想学习多态,请直接转到多态。
面向对象深入了解
字节码文件
class文件其中包括常量池,代码指令。
电脑就把这些输入到jvm里面,高速执行。
常量池分为两种
- class常量池
- 存放一些静态信息,比如类名,方法名,属性名,各种字面量(基础类型的值,final值,字符串)等等
- 运行时常量池
- 这个比较特殊,只有在内存中加载时才能知道他是什么样子。
进行转换后的常量池
Constant pool:
#1 = Methodref #7.#27 // java/lang/Object."<init>":()V
#2 = Class #28 // com/ydlclass/Computer
#3 = Methodref #2.#27 // com/ydlclass/Computer."<init>":()V
#4 = Fieldref #29.#30 // java/lang/System.out:Ljava/io/PrintStream;
#5 = Methodref #2.#31 // com/ydlclass/Computer.plus:(II)I
#6 = Methodref #32.#33 // java/io/PrintStream.println:(I)V
#7 = Class #34 // java/lang/Object
#8 = Utf8 <init>
#9 = Utf8 ()V
#10 = Utf8 Code
#11 = Utf8 LineNumberTable
#12 = Utf8 LocalVariableTable
#13 = Utf8 this
#14 = Utf8 Lcom/ydlclass/Computer;
#15 = Utf8 plus
#16 = Utf8 (II)I
#17 = Utf8 i
#18 = Utf8 I
#19 = Utf8 j
#20 = Utf8 main
#21 = Utf8 ([Ljava/lang/String;)V
#22 = Utf8 args
#23 = Utf8 [Ljava/lang/String;
#24 = Utf8 computer
#25 = Utf8 SourceFile
#26 = Utf8 Computer.java
#27 = NameAndType #8:#9 // "<init>":()V
#28 = Utf8 com/ydlclass/Computer
#29 = Class #35 // java/lang/System
#30 = NameAndType #36:#37 // out:Ljava/io/PrintStream;
#31 = NameAndType #15:#16 // plus:(II)I
#32 = Class #38 // java/io/PrintStream
#33 = NameAndType #39:#40 // println:(I)V
#34 = Utf8 java/lang/Object
#35 = Utf8 java/lang/System
#36 = Utf8 out
#37 = Utf8 Ljava/io/PrintStream;
#38 = Utf8 java/io/PrintStream
#39 = Utf8 println
#40 = Utf8 (I)V
对象创建流程
我们使用那么就的java,是否清楚java对象创建的流程呢
其流程大概分为
- 1,内存分配(属性为默认值)
- 2,初始化属性值
- 3,执行构造器(加载所有父类)
在堆中开启对象内存,将对象名具体为地址,然后在虚拟机栈中存放该地址,执行堆对象。
多态
接下主要以多态为核心进行讲解,因为在java中实在太重要了。
Human man = new Man();
上面这行代码是我们熟悉的多态吧,用的多一看就知道。我们先介绍一下专有名词。
- 在左边的Human是叫 静态类型,他属于静态的(这个后面具体会讲到)
- 在右边的Man是叫 动态类型,也叫运行时类型。
和刚开始的一样,也是静态和运行时的区别,运行时是先要加载到内存中才能确定是什么样子。
举个最简单的例子
Animal animal = Math.random() > 0.5 ? new Dog():new Cat();
下面我们来介绍一下其中的体现
重载方法
public class Human {
public static void main(String[] args) {
Human human = new Human();
Human man = new Man();
Human woman = new Woman();
human.test(human);
human.test(man);
human.test(woman);
}
public void test(Human human){
System.out.println("我是人类");
}
public void test(Man man){
System.out.println("我是男的");
}
public void test(Woman woman){
System.out.println("我是女的");
}
}
class Man extends Human{
}
class Woman extends Human{
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XSCyHbeq-1671072276839)(C:\Users\86135\AppData\Roaming\Typora\typora-user-images\image-20221215101440268.png)]
从运行结果来看,我们知道都是调用的是第一个方法,也就是Human的方法。这就是静态类型的体现了。
说明在重载方法中类型是取决于静态解析的,在编译java文件为字节码的时候以及确定了类型,根本不是在内存中决定的运行时类型。
再看一个很经典例子
public class Overload {
public void sayHello(Object arg){
System.out.println("hello object");
}
public void sayHello(int arg){
System.out.println("hello int");
}
public void sayHello(long arg){
System.out.println("hello long");
}
public void sayHello(Character arg){
System.out.println("hello Character");
}
public void sayHello(char arg){
System.out.println("hello char");
}
public void sayHello(char... arg){
System.out.println("hello char...");
}
public static void main(String[] args) {
new Overload().sayHello('a');
}
}
hello int
hello long
hello Character
hello object
hello char...
如果我们一个个把出现的注释掉,最终可以得到的结果是下面的
优先级
char > int > long > Character > Object > char…
其实这个是jvm的工作,在静态解析中,它会为我们进行重载方法的匹配,选择它自己认为最优的版本。
重写方法
这个比重载方法要简单,它也是属于静态解析,是从下到上进行寻找方法匹配。
比如先看看自己有没有这个方法进行匹配,若无再一层层往上找。
类型转换
创建对象,再用对象调用方法,这个是根据静态类型来决定你可以调用什么方法
但是对象的类型仍然是运行时决定。
public class Human {
public static void main(String[] args) {
//------测试1
Human human = new Man();
human.test();
//------测试2
Man man = (Man)human;
man.man();
//------测试3
Woman woman = (Woman)human;
woman.woman();
}
public void test(){
System.out.println("我是人类");
}
}
class Man extends Human{
public void man(){
System.out.println("我是男的");
}
}
class Woman extends Human{
public void woman(){
System.out.println("我是女的");
}
}
运行结果
说说结论:
- 方法的可调用数是看静态类型,就是看左边有什么方法可以调用
- 所以,在向上转换类型后,会丢失一些方法的使用。
(){
System.out.println(“我是男的”);
}
}
class Woman extends Human{
public void woman(){
System.out.println(“我是女的”);
}
}
- 所以,在向上转换类型后,会丢失一些方法的使用。
运行结果

说说结论:
- 方法的可调用数是看静态类型,就是看左边有什么方法可以调用
- 所以,在向上转换类型后,会丢失一些方法的使用。
- 但是方法的实际调用对象是看运行时类型,就像上面的你运行时类型是man,向上转换后是human,再向下转换为woman,一个男的转为女的显然不可行。