多态 的内存分析
多态:父类的引用类型变量指向了子类的对象
或者是接口 的引用类型变量指向了接口实现类的对象
(因为接口的方法都是非static的,所以必须要用实现类将接口方法实现)
Animal a =new mouse();
多态要注意 的细节:
1. 多态情况下,子父类存在同名的成员变量时,访问的是父类的成员变量。
2. 多态情况下,子父类存在同名的非静态的成员函数时,访问的是子类的成员函数。
3. 多态情况下,子父类存在同名的静态的成员函数时,访问的是父类的成员函数。
4. 多态情况下,不能访问子类特有的成员。
总结:多态情况下,子父类存在同名的成员时,访问的都是父类的成员,除了在同名非静态函数时才是访问子类的。
如果要访问①子类特有的成员 ②同名的静态成员函数 ③同名的成员变量
则需要进行类型强转。
为什么会这样呢?
java的特点: 编译看左边, 运行不一定看右边
如果引用类型的变量不具备指定的成员,就报错。
实际上
Person worker = new Worker();
等价于
Worker w = new Worker(); Person worker = w;
所以堆内存中其实已经存在一个Worker对象, 因为是继承关系,所以Worker对象中有一块用于继承的内存空间(其实是一个Person对象),把这个对象的引用赋给a。所以显然a调用的属性和方法都是父类Person的。
只有非静态的被子类重写的方法例外 , 为什么呢?
这里要重点要搞清楚堆内存中的调用。
图片引用自:http://blog.csdn.net/thinking_in_android
在方法区中含有 类代码,static变量和 常量池,
常量池中有一些 字符串, 已经方法的入口。
①首先因为运行demo.class 里面有关键字 Person和 Worker,所以这三个类的类代码加载进方法区
②将static变量和static代码块加载进方法区 ,所有的类只维护一份
③因为new Worker(),在堆中创建一个Worker对象,并且把Worker对象中的一些常量放入方法区中的常量池,其中就包括Worker类中重写的方法。
④Person Worker调用say方法,去哪里找呢? 首先在堆内存的Worker对象中的Person内存空间找, 找到say()方法索引,于是顺着这个索引去方法区中的常量池中找say()方法的入口,因为此时只new了子类Worker对象,所以这个常量池中的say()方法入口其实是因为创建Worker对象而存入常量池的,于是Person类对象顺着这个入口调用了子类重写的方法。
内部类
分为: 成员内部类, 局部内部类
成员内部类
1. 成员内部类: 与成员变量等价,是一个成员属性。
成员内部类的访问方式:
方式一:在**外部类提供一个方法创建内部类的对象**进行访问。!!!!!!注意在本类的主方法中野不能创建。
方式2二:在其他类直接创建内部类的对象。 格式:外部类.内部类 变量名 = new 外部类().new 内部类();
注意: 如果是一个静态内部类,那么在其他类创建 的格式:
外部类.内部类 变量名 = new 外部类.内部类();
内部类可以直接访问外部类的所有成员
内部类要注意的细节:
1. 如果外部类与内部类存在同名的成员变量时,在内部类中默认情况下是访问内部类的成员变量。
可以通过”外部类.this.成员变量名” 指定访问外部类的 成员。
因为this不知道指向谁!!!
2. 私有的成员内部类只能在外部类提供一个方法创建内部类的对象进行访问,不能在其他类创建对象了。
3. 成员内部类一旦出现了静态的成员,那么该类也必须 使用static修饰。
局部内部类
局部内部类: 在一个类 的方法内部定义另外一个类,那么另外一个类就称作为局部内部类。
局部内部类要注意的细节:
. 如果局部 内部类访问了一个局部变量,那么该局部变量必须使用final修饰、
class Outer{
String name= "外部类的name";
public void test(){
//局部变量
final int y =100; // y 什么时候从内存中消失? 方法执行完毕之后y消失。
//局部内部类
class Inner{ /*
当test方法执行完毕之后,那么y马上从内存中消失,而Inner对象在方法
执行完毕的时候还没有从内存中消失,而inner对象的print方法还在访问着
y变量,这时候的y变量已经消失了,那么就给人感觉y的生命变量已经被延长了
.
解决方案: 如果一个局部内部类访问一个局部变量的时候,那么就让该局部内部类访问这个局部 变量 的复制品。(所以要用final修饰)
*/
int x = 10;
public void print(){
System.out.println("这个是局部内部类的print方法.."+y);
}
}
Inner inner = new Inner(); //这个inner对象什么时候消失? Inner对象的生命周期比局部变量y的生命周期要长。
inner.print();
}
}
匿名内部类
前提:必须要有继承或者实现 关系
匿名内部类 只是没有类名,其他的成员都具备。
因为没有类名 要借用父类的名字,其实是创建Animal子类对象
abstract class Animal{
public abstract Animal run();
public abstract void sleep();
}
new Animal(){ //多态
//匿名内部的成员
public Animal run(){
System.out.println("狗在跑..");
return this;
}
public void sleep(){
System.out.println("狗趴在睁开眼睛睡..");
}
//特有的方法
public void bite(){
System.out.println("狗在咬人..");
}
};
如果要调用一个方法 直接new Animal{xxxx}.run() 即可
如果要调用两个方法怎么办呢?
①将其中一个方法的返回值设为Animal , 然后return this;
②将这个对象赋给一个Animal变量, 用多态的特点运行子类重写的方法,但是如果要调用的是新增的方法, 就只能用①中的方法。。或者换成局部内部类