图解Java多态内存分配以及多态中成员方法的特点


Person worker = new Worker(); 子类实例对象地址赋值给父类类型引用变量。多态的体现。

多态中成员方法的特点分析:

【子类有,父类没有】编译失败!!!
worker.startWork(); 为什么编译不通过呢?提示:找不到符号。
因为引用变量worker是Person类型,在Person类的方法表中查找方法startWork(),找得到吗?找不到,更别提常量池解析了。编译失败。


【子类有,父类有,重写,非静态】调用子类!!!
worker.say(); 子类重写父类方法,被重写的方法在子类跟父类的方法表中索引相同。
调用的时候,在父类类型的方法表中查找say方法的索引,然后把索引存到PolDemo类的常量表中(常量池解析,就是用Person类方法表中的索引项来代替常量池中的符号引用)。
因为索引相同,直接拿常量表中say方法的索引去子类方法表中调用say()方法。 所以,此时调用的是被子类重写的方法。见图中的内存分配。


【子类有,父类有,静态】调用当前引用类型变量类型中的方法。
因为静态是属于类的,由实例共享,所以只看当前引用变量所属的类中的静态方法。


多态中(父类引用指向子类对象)成员方法(非静态)有以下特点:
编译期根据引用类型变量所属的类型方法表中是否有调用的方法,没有编译失败。
运行期根据在堆中创建对象的实际类型找到对应的方法表,从中确定具体的方法在内存中的位置。


堆中实例对象:子类包含父类,子类对父类说:你的就是我的,我的还是我的。


多态中成员变量的特点分析:
无论编译期还是运行期,都只参考引用类型变量所属的类中是否有对象的成员变量。


小的总结:
再来看这条语句:Person worker = new Worker(): 多态,父类引用指向子类对象。
跟这句代码的作用是一样的,Worker w = new Worker(); Person worker = w; 就是子类的引用传给父类类型的引用。向上转型,都指向一个实例对象(子类)。
1:为什么可以将子类实例对象的引用传给父类类型引用呢? 
答:多态的体现,鸽子是鸟类,鹦鹉是鸟类,喜鹊是鸟类,它们都是鸟类,这就是鸟类的多种表现形态(多态),
它们有鸟类的基本特征与行为,并且还有自己特有的行为,那就会把鸟类的基本特征与行为继承过来给自己。extends关键字声明了他们的关系。
程序中这句话 鸟类 xx鸟 = new 鸽子();就创建了一个鸟类的引用,它是鸽子。
类的概念是抽象的,所以它就仅仅是个引用,只有引用到实例对象,它才真正实现了自己。


2:父与子的关系会怎样呢?
由于Worker类继承自Person类,所以会先加载Person类并初始化成员变量在Worker类实例对象中。这就是子类继承过来的成员变量。
如果子类中定义了与父类相同的成员变量,变量不会存在重写,子类实例对象中会有两份相同名称的实例变量,调用时,父类的会被隐藏。
如果想调用父类被隐藏的成员变量,那就要使用super关键字。
同样,子类会继承父类的成员方法作为自己的成员方法,如果子类定义了与父类相同的成员方法(重写),多态中会调用子类自己的成员方法。


3:多态,又有什么好处呢?跟这样写(Worker worker = new Worker();)有什么区别,不一样继承父类的属性方法吗?
首先明确: Worker worker = new Worker();这样写仅仅是继承关系。不存在多态特性。
Person worker = new Worker(); 这样写就展示了多态的关系,鸽子是鸟类的一种表现形态。
有啥好处?一句话概括:提高了程序的扩展性,表现在什么地方呢?比如:
现在有个工厂,能解析鸟类的语言,鸽子是鸟类吧?Ok,那建一个工厂,让鸽子说。
public static void doWork(Dove dove) {
String sound = dove.say(); // ... 拿到鸽子说的话
}
鹦鹉是鸟类吧?Ok,那建一个工厂,让鹦鹉说。
public static void doWork(Parrot parrot) {
String sound = parrot.say(); // ... 拿到鹦鹉说的话
}
如果还有一千个鸟类要说话,那我岂不得建一千座工厂?。。。累不累?那既然都是鸟类,我建一个鸟类的工厂不就行了嘛?
public static void doWork(Bird bird) {
String sound = bird.say(); // ... 拿到xx鸟说的话。。。
}
这时候,无论你什么鸟进来,我都让你说话,而且说得都是你自己的话,因为你继承我并重写了你的功能。
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Worker w = (Worker)worker; // 向下转型。目的,为了调用子类非继承父类、自己特有的方法。
因为多态有个弊端, 只能使用父类的引用访问父类的成员。所以向下转型是为了访问子类自己的成员。
首先,worker引用指向的实例对象本来就是子类类型的。所以赋值给子类类型引用变量非常可以。
然后现在用子类类型的引用就可以访问自己的成员方法了啦啦啦。


原创文章,转载请注明出处: http://blog.youkuaiyun.com/thinking_in_android

<think>嗯,用户之前问过关于在Java方法中添加类型参数的问题,现在又问static的区别,可能是在学习Java的基础知识,特别是方法的修饰符。用户可能是刚开始学习Java,或者在实际编码中遇到了相关问题。需要确保解释清楚static方法与非static的区别,特别是内存、访问权限和使用场景方面。 要回答这个问题,首先得明确static方法属于类,而非static属于实例。内存分配上,static在类加载时分配,而非static随实例创建。访问权限方面,static不能直接访问实例变量和方法,需要创建对象实例,而非static可以访问静态成员。使用场景的话,static常用于工具类、单例模式,而非static用于对象特定的行为。 用户可能没有完全理解static的实际应用,或者在使用时遇到了错误,比如尝试在static方法中调用非静态变量。需要举例子说明,比如main方法是static的,如果在里面直接调用实例方法会报错,必须创建对象。同时,设计上的考虑,比如工具类Math的静态方法,或者单例模式的静态实例。 还要注意用户可能的误区,比如认为static方法效率更高,其实不一定,过度使用可能导致内存问题。另外,多态性在static方法中的表现不同,static方法不能被重写,而是隐藏。这些都需要在回答中提及,帮助用户全面理解。 最后,结构要清晰,分点列出核心区别、内存、访问权限、使用场景、注意事项,并给出示例代码,便于用户理解。确保用中文,语言通俗,避免术语堆砌,让初学者也能明白。</think>在 Java 中,`static` 关键字用于修饰类成员(包括方法和变量),以下是关于 **static 方法** 和 **普通实例方法** 的核心区别详解: --- ### 一、核心区别(对比表格) | **特性** | **static 方法** | **普通实例方法** | |-----------------------|-----------------------------------------|------------------------------------| | 所属对象 | **类级别**(属于类本身) | **实例级别**(属于类的某个对象) | | 内存分配时机 | 类加载时分配 | 对象实例化时分配 | | 访问权限 | 只能直接访问 **静态成员** | 可直接访问 **静态+实例成员** | | 调用方式 | `类名.方法名()` 或 `对象.方法名()` | 必须通过 **对象实例** 调用 | | 多态性 | **不支持** 方法重写(隐藏) | 支持方法重写(运行时多态) | | 生命周期 | 与类共存(类卸载时销毁) | 与对象共存(对象销毁时释放) | --- ### 二、内存机制图解 ```java class MyClass { static int sVar = 1; // 存在方法区 int iVar = 2; // 存在堆内存 static void sMethod() { /* 访问 sVar */ } void iMethod() { /* 访问 sVar 和 iVar */ } } ``` - **static 方法**:存储在 **方法区**,所有对象共享同一份代码 - **实例方法**:通过 **对象指针** 访问,每个对象有独立的方法调用栈 --- ### 三、典型使用场景 #### 1️⃣ static 方法适用场景 - **工具类方法**(无需对象状态) ```java Math.sqrt(25); // 数学计算 Collections.sort(list); // 集合操作 ``` - **单例模式**(控制实例化) ```java public class Singleton { private static Singleton instance; public static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } } ``` - **常量/配置管理** ```java public class Constants { public static final double PI = 3.1415926; } ``` #### 2️⃣ 实例方法适用场景 - **对象状态相关操作** ```java public class BankAccount { private double balance; public void deposit(double amount) { this.balance += amount; } } ``` - **继承与多态** ```java class Animal { public void makeSound() { /* 默认实现 */ } } class Dog extends Animal { @Override public void makeSound() { System.out.println("Woof!"); } } ``` --- ### 四、关键注意事项 1. **静态方法限制** ```java public static void demo() { // System.out.println(iVar); // 错误!不能直接访问实例变量 // iMethod(); // 错误!不能直接调用实例方法 new MyClass().iMethod(); // 正确:通过对象实例访问 } ``` 2. **隐藏 vs 重写** ```java class Parent { static void staticMethod() { System.out.println("Parent"); } void instanceMethod() { System.out.println("Parent"); } } class Child extends Parent { static void staticMethod() { System.out.println("Child"); } // 隐藏 @Override void instanceMethod() { System.out.println("Child"); } // 重写 } ``` 3. **线程安全问题** - 静态方法若操作共享静态变量,需同步控制: ```java public class Counter { private static int count = 0; public static synchronized void increment() { count++; // 需要加锁 } } ``` --- ### 五、设计原则建议 - **优先使用实例方法**:面向对象设计的核心是对象行为 - **谨慎使用静态方法**:适合与对象状态无关的工具方法 - **避免静态滥用**:过度使用会导致代码耦合度高,难以测试 如果需要具体场景的代码示例或进一步解释,请随时告诉我! 🚀
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值