多态、内部类
-----Java培训、Android培训、期待与您交流! -------
11 多态
多态: 一个对象具有多种形态。(父类的引用类型变量指向了子类的对象)
多态前提: 必须要存在继承或者实现的关系。
多态要注意的细节:
1. 多态情况下,子父类存在同名的成员变量时,都是访问父类的成员变量。 2. 多态情况下,子父类存在着同名的非静态函数时,默认访问的是子类的成员函数。 3. 多态情况下,子父类存在着同名的静态函数时,默认访问的是父类的成员函数。 4. 多态情况下,不能访问或者调用子类特有的成员,如果真的需要访问那么需要进行强制类型转换。 |
总结:多态情况下,子父类存在同名的成员时,默认都是访问父类的成员,
只有子父类存在非静态函数是才是访问子类的成员函数。
编译看左边,运行不一定看右边。
编译看左边: java编译器在编译的时候,会检查引用类型所属的类是否具备指定的成员,如果不具备那么编译报错。
运行:多态情况下,子父类存在同名的成员时,默认都是访问父类的成员,
只有子父类存在非静态函数是才是访问子类的成员函数。
多态的应用场景:
1.多态应用于形参类型的时候,方法可以接收更多类型的参数。
2.多态用于返回值类型的时候,可以返回更多类型的参数。
多态的好处:提高了程序的拓展性。
总结
1:当父类和子类具有相同的非静态成员变量,那么在多态下访问的是父类的成员变量
2:当父类和子类具有相同的静态成员变量,那么在多态下访问的是父类的静态成员变量
所以:父类和子类有相同的成员变量,多态下访问的是父类的成员变量。
3:当父类和子类具有相同的非静态方法(就是子类重写父类方法),多态下访问的是子类的成员方法。
4:当父类和子类具有相同的静态方法(就是子类重写父类静态方法),多态下访问的是父类的静态方法。
2:多态体现
1:父类引用变量指向了子类的对象
2:父类引用也可以接受自己的子类对象
3:多态前提
1:类与类之间有关系,继承或者实现
4:多态弊端
1:提高扩展性,但是只能使用父类引用指向父类成员。
5:多态特点
非静态
1:编译时期,参考引用型变量所属的类是否有调用的方法,如果有编译通过。没有编译失败
2:运行时期,参考对象所属类中是否有调用的方法。
3:总之成员函数在多态调用时,编译看左边,运行看右边。
在多态中,成员变量的特点,无论编译和运行参考左边(引用型变量所属的类)。
在多态中,静态成员函数特点,无论编译和运行都参考左边
例子:
/* * 需求:定义一个函数可以接收任意类型的图形对象。 */
//图形类 abstract class MyShape{
public abstract void getArea();
public abstract void getLength(); }
//矩形 class Rect extends MyShape{ int width ; int height ;
public Rect(intwidth , intheight){ this.width =width; this.height =height; }
public void getArea(){ System.out.println("矩形面积是:"+width*height); }
public void getLength(){ System.out.println("矩形周长是:"+ 2*(width+height)); } } //圆形 class Circle extends MyShape{ public static final double PI = 3.14; int r; public Circle(intr){ this.r =r; }
public void getArea(){ System.out.println("圆形面积是:"+PI*r*r); } public void getLength(){ System.out.println("圆形周长是:"+ 2*PI*r); } }
public class Duotai { public static void main(String[] args) { /* Circle c = new Circle(4); Rect r = new Rect(3,4); print(r); */ MyShape c = getShape(1); c.getArea(); c.getLength(); }
//需求:定义一个函数可以接收任意类型的图形对象。 public static void print(MyShape m){ // MyShape m = new Rect(3,4); m.getLength(); m.getArea(); }
//需求:编写一个函数可以返回任意类型的图形对象。 public static MyShape getShape(inti){ if (i==0){ return new Circle(4); }else{ return new Rect(4,5); } } } |
多态与数据类型转换的例子
/* 引用类型数据转换: 小数据类型数据---------->大数据类型 自动类型转换。 大数据类型数据----------->小数据类型 强制类型转换。 */
//动物 class Animal { String name; String color; static int x = 10; //构造函数 public Animal(Stringname,String color){ this.name =name; this.color =color; } public void eat(){ System.out.println("动物在吃饭..."); } }
//狗 class Dog extends Animal { static int x = 20; public Dog(Stringname,String color){ super(name,color); } public void eat(){ System.out.println("狗在吃狗粮..."); } //狗特有的方法咬人 public void bite(){ System.out.println(name+"狠狠的咬人!!"); } } //鱼 class Fish extends Animal{ public Fish(Stringname,String color){ super(name,color); } public void eat(){ System.out.println("鱼在吃草..."); } //鱼特有的方法 public void swing(){ System.out.println("鱼在游泳..."); } }
public class Duotai { public static void main(String[] args) { /*System.out.println("Hello World!"); Fish f = new Fish("草鱼","墨绿色"); Dog d = new Dog("哈巴狗","白色"); print(d);
Animal a =getAnimal(0); a.swing(); Animal a = new Fish("草鱼","墨绿色"); //在多态的情况下,就是不能访问到子类特有的成员,如果需要访问子类特有的成员,那么需要做强制类型转换。 Fish f = (Fish)a; //把动物又强制转换成鱼 f.swing(); */ Fish f = new Fish("草鱼","墨绿色"); Dog d = new Dog("哈巴狗","白色"); print2(f); } //需求3:定义一个函数可以接收任意类型的动物对象,在方法内部调用动物对象特有的方法。 public static void print2(Animal a){ if(ainstanceof Fish){ Fish f = (Fish)a; //把动物强制转换成鱼 f.swing(); }else if(a instanceof Dog){ Dog d = (Dog)a; d.bite(); } } //需求1:定义一个函数可以接收任意类型的动物对象。 public static void print(Animal a){ a.eat(); } //需求2:编写一个函数可以返回任意类型的动物对象。 public static Animal getAnimal(inti){ if(i==0){ return new Fish("草鱼","墨绿色"); }else{ return new Dog("哈巴狗","白色"); } } } |
12 内部类
成员内部类:定义一个类在类的内部,方法的外部,称作为成员内部类。
成员内部类访问方式:
方式1:在外部提供一个方法创建内部类 的对象进行访问。
方式2: 在其他类直接创建内部类对象进行访问。
创建对象的格式:
外部类.内部类 变量名= new 外部类().new 内部类();
注意:如果是静态的成员内部类的在其他类创建对象:外部类.内部类 变量名= new 外部类.内部类();
Outer.Inner inner = new Outer.Inner(); //静态内部类的访问方式 |
内部类的好处:内部类的好处就是可以直接访问外部类的成员。
内部类的写法
class Outer{ class Inner { public void show(){ System.out.println("内部类的show方法"); } } public void print(){ new Inner().show(); } } |
12.1成员内部类要注意的细节:
1. 成员内部类可以直接访问外部类的成员。 2. 如果外部类与内部类存在同名的成员变量时,在内部类中默认是访问内部类的成员,可以通过"外部类.this.成员变量名" 指定访问外部类 的成员变量。 3. 如果内部类使用了private修饰,那么访问该内部类的时候就只能在外部类提供一个方法进行访问了。不能在其他类直接创建了。 4. 如果成员内部类出现了静态的成员,那么该成员内部类也需要使用static修饰。 疑问:如果成员内部类出现了静态的成员,那么该成员内部类也需要使用static修饰? 原因:假如没有使用static修饰,那访问非静态成员时,得创建对象再调用new Outer().Inner.x,与Java规范有矛盾,使用成员内部类也需要使用static修饰。 java规范:静态的数据不需要依赖于对象进行访问。 |
细节的说明范例
//外部类 class Outer{
//成员变量. int x = 100;
//成员内部类 static class Inner{
static int x = 999;
//成员变量 int i = 10;
//成员函数 public void print(){ System.out.println("这个是内部类的print方法.. x = "+x); } }
//在外部提供一个方法创建内部类的对象进行访问。 public void visitedInner(){ //创建一个内部的对象 Inner in = new Inner(); System.out.println("i = "+in.i); }
}
//其他类 class Way {
public static void main(String[] args) {
/* //创建一个外部类的对象 Outer outer = new Outer(); outer.visitedInner();
Outer.Inner inner = new Outer().new Inner(); System.out.println("i = "+inner.i); inner.print();
Outer.Inner inner = new Outer.Inner(); //静态内部类的访问方式 inner.print(); */ } } |
12.2局部内部类:
局部类要注意的细节:
局部内部类访问局部变量的时候,局部变量是需要使用final修饰的。
//外部类 class Outer{ public void test(){ //局部变量 final int y = 100; /* y的生命周期:test方法执行完毕之后马上从内存消失。 局部内部类访问局部变量的时候,局部变量是需要使用final修饰,为什么? */ //局部内部类 class Inner{ //成员变量 intx = 10; //成员函数 public void print(){ System.out.println("这个是局部内部类的print方法..."+y); /* y变量当方法执行完毕的时候就已经从内存中消失了, 这时候test方法执行完毕的时候Inner对象还没有消失,y消失了, 但是Innner对象的方法还在访问着y变量,这时候给人的感觉就是y还没有消失, y变量的生命周期被延长了。
解决方案:如果局部内部类访问一个局部变量的时候,那么就让局部内部类访问的是 一个变量复制品。源变量可以正常消失。 */ } } //创建局部内部类对象 Inner inner = new Inner();// Inner对象生命周期:反正Inner的对象当test执行完毕之后还不会马上消失。 // Inner对象的生命周期是比y要长的。 inner.print(); } }
class Way { public static void main(String[] args) { Outer outer = new Outer(); outer.test(); } } |
12.3 匿名内部类
使用前提:必须要存在着继承或者实现的关系
好处:简化书写
继承关系下的匿名内部类
//动物类 abstract class Animal{ public abstract void run(); public abstract void sleep(); }
class Outer{ public void print(){ /* //需求:定义一个类继承Animal,然后创建该类的对象调用run、sleep方法。 class Bird extends Animal{
public void run(){ System.out.println("鸟飞得更高!!"); } public void sleep(){ System.out.println("鸟在睡觉!!"); } }
//使用该内部类创建对象。 Bird b = new Bird(); b.run(); b.sleep(); */
//匿名内部类只是没有类名,其他一个类该有的成员,匿名内部类都是具备的。
//匿名内部类实现 Animal b = new Animal(){ //匿名内部类与Animal的关系是继承。 多态 //匿名内部的成员 public void run(){ System.out.println("鸟飞得更高!!"); } public void sleep(){ System.out.println("鸟在睡觉!!"); } //子类特有的方法... public void eat(){ System.out.println("鸟在吃!!"); //return this; } };
//b.eat(); b.run(); b.sleep(); } }
public class Demo { public static void main(String[] args) { Outer outer= new Outer(); outer.print(); } } |
接口关系下的匿名内部类
interface A{ public void add(); }
class Outer{ public void print(){ //实现关系下的匿名内部类 new A(){ //这个匿名内部类与A接口的关系是实现关系。 //匿名内部类的成员 public void add(){ System.out.println("这个是添加的方法..!"); } }.add(); } }
class Demo3 { public static void main(String[] args) { Outer outer = new Outer(); outer.print(); } } |