黑马程序员--java基础复习之多态与Object及匿名内部类

时间:2021-03-17 00:43:35
------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------


多态
定义:某一类事物的多种存在形态 例如:动物中的猫、狗。猫这个对象对应的类型是猫类型,如:猫 x = new(); 同时猫也是动物中的一种,也可以把猫称为动物。 动物  y = new (); 那么动物就是猫和狗具体事物中抽取出来的父类型。父类型引用指向了子类对象。
多态:可以理解为事物存在的多种体现形态
1、多态的体现
父类的引用指向了自己的子类对象。
父类的引用也可以接收自己的子类对象。
2、多态的前提
必须是类与类之间有关系。要么继承,多么实现
通常还有一个前提:存在覆盖

3、多态的好处
多态的出现大大的提高程序的扩展性
4、多态的弊端
提高了扩展性,但是只能使用父类的引用访问父类中的成员

如下代码:
abstract class Animal
{
abstract void eat(); //定义一个eat()的抽象方法,所以这个类也是一个抽象类
//其子类调用此类的时候,要对其抽象方法全部重写
}

interface Catch
{
abstract void catchMou();
}

//继承动物这个类
class Cat extends Animal implements Catch
{
void eat()
{
System.out.println("我是猫,我吃鱼");
}
public void catchMou() //重写(覆盖)接口中的抽象类
{
System.out.println("接口:抓老鼠抓老鼠抓老鼠抓老鼠");
}

void CatchMouse() //子类的独有方法
{
System.out.println("我是猫,这是我的独有功能:抓老鼠");
}
}

class Dog extends Animal //继承动物这个类
{
void eat() //重写eat()这个方法
{
System.out.println("我是狗,吃骨头");
}
void kanJia() //Dog类的独有方法
{
System.out.println("我是狗,这是我的独有功能:看家");
}
}

class Pig extends Animal //继承动物这个类
{
void eat()
{
System.out.println("我是猪,吃饲料");
}
void GongDi()
{
System.out.println("我是猪,这是我的独有功能:拱地");
}
}


class DuoTaiDemo
{
public static void main(String[] args)
{
System.out.println("*****************使用多态前******************");
/*****************前期做法******************/
function(new Cat()); //调用猫的eat()方法
function(new Dog()); //调用狗的eat()方法
function(new Pig()); //调用猪的eat()方法
/*******************************************/
System.out.println("*****************使用多态******************");
/*****************使用多态******************/
functionDuoTai(new Cat());
functionDuoTai(new Dog());
functionDuoTai(new Pig());
/*******************************************/


/*******************************************/

}

/*****************前期做法******************/
//以下为以往做法,代码重复冗余
public static void function(Cat c)
{
c.eat();
c.catchMou();
}
public static void function(Dog d)
{
d.eat();
}
public static void function(Pig p)
{
p.eat();
}
/*******************************************/

/*****************使用多态******************/
public static void functionDuoTai(Animal a)
{


//运行动物的独有功能
//多态的类型转换

if
(a instanceof Cat) //如果Animal是猫
{
Cat c=(Cat)a; //将动物向下转换成猫
c.CatchMouse(); //猫的特有功能
}
else if(a instanceof Dog)
{
Dog d=(Dog)a; //将动物向下转换成狗
d.kanJia();
}
else if(a instanceof Pig)
{
Pig p=(Pig)a;
p.GongDi();
}

}
/*******************************************/
}
结果:黑马程序员--java基础复习之多态与Object及匿名内部类

 这代码就是多态的体现,假设子类Cat中有特有的抓老鼠功能,父类型的 a就不能直接调用。这上面的代码中,可以理解为Cat类型提升了,向上转型。如:
Animal a=new Cat();//类型提升,向上转型
//如果想要调用猫的特有方法时,得强制将父类的引用,转成子类类型
Cat c=(Cat)a;
        如果此时父类的引用想要调用Cat中特有的方法,就需要强制将父类的引用,转成子类类型,向下转型。如:Catc = (Cat)a;
注:如果父类可以创建对象,如:Animal a = new Animal(); 此时,就不能向下转型了,Cat c = (Cat)a; 这样的代码就变得不容许,编译时会报错。所以千万不能出现这样的操作,就是将父类对象转成子类类型。
        我们能转换的是父类引用指向了自己的子类对象时,该引用可以被提升,也可以被强制转换。多态至始至终都是子类对象在做着变化。

看一个例子:
/*
基础班学生:学习,睡觉
高级班学生:学习,睡觉
可以将这两类事物进行抽取
*/
abstract class Student
{
//定义一个抽象方法
public abstractvoid study();

//这个方法可以继承(子类对象可以直接使用),也可以复写
void sleep()
{
System.out.println("躺着睡");
}
}

//基础班学生
class BaseStudent extends Student
{
//复写父类的study方法
public void study()
{
System.out.println("基础学习");
}
//复写父类的sleep方法
void sleep()
{
System.out.println("坐着睡");
}
}
class AdvStudent extends Student
{
//复写父类的study方法
public void study()
{
System.out.println("高级学习");
}
}

class DoStudent
{
//多态的使用
void DoSome(Student s)
{
s.study();
s.sleep();
}
}

class DuoTaiDemo
{
public static void main(String[] args)
{
DoStudent ds=new DoStudent();

ds.DoSome(new BaseStudent());
ds.DoSome(new AdvStudent());
}
}
上述代码是定义了一个中间的类,将基础班学生和高级班学生的共同行为封装在一个类中,使用其父类作为参数传递,也是用到了多态。
下面也是一个多态的小应用,其分为三个部分

1、定义好工具类,即将共同行为封装在一个类中。

2、对类型进行抽取,---->多态的产生。

3、操作同一父类型,对其中的子类型均可操作


如下:
/*
多态的应用

需求:
电脑运行实例,电脑运行是基本于主板的。
假设主板只是提供电脑运行,但是没有上网,听歌等功能。
而上网、听歌需要硬件的支持。而现在主板上没有网卡和声卡,这时可以定义一个规则,叫PCI,
只要符合这个规则的网卡和声卡都可以在主板上使用,这样就降低了主板和网卡、声卡之间的耦合性。
用程序体现。
*/

//创建一个主板类
class MainBoard
{
public void run()
{
System.out.println("主板运行");
}

public void usePCI(PCI p) //PCI p=new NetCard();接口型引用指向自己的子类对象
{
if(p!=null) //当使用设备时
{
p.open();
p.close();
}

}
}

//创建一个接口 PCI
interface PCI
{
void open();
void close();
}

//网卡实现PCI接口
class NetCard implements PCI
{
public void open()
{
System.out.println("网卡运行");
}
public void close()
{
System.out.println("网卡停止");
}
}

//声卡实现PCI接口
class SoundCard implements PCI
{
public void open()
{
System.out.println("声卡运行");
}
public void close()
{
System.out.println("声卡停止");
}
}

class DuoTaiDemo5
{
public static void main(String[] args)
{
//运行主板
MainBoard mb=new MainBoard();
mb.run();
//主板通过PCI接口操作设备
mb.usePCI(null);
mb.usePCI(new SoundCard());
mb.usePCI(new NetCard());
}
}

1、在多态中成员函数(非静态)的特点:

在编译时期:参阅引用型变量所属的类中是否有调用的方法。如果有,编译通过,如果没有编译失败。

在运行时期:参阅对象所属的类中是否有调用的方法。

如下:

class Fu
{
void method1()
{
System.out.println("Fu Method1");
}
void method2()
{
System.out.println("Fu Method2");
}
}

class Zi extends Fu
{
void method1()
{
System.out.println("Zi Method1");
}
void method3()
{
System.out.println("Zi Method2");
}
}

class DuoTaiDemo3
{
public static void main(String[] args)
{
Fu z=new Zi();
z.method1();
z.method2();
//z.method3(); //编译失败
}
}
结果: 黑马程序员--java基础复习之多态与Object及匿名内部类

简单总结:成员函数在多态调用时,编译看左边,运行看右边


2、在多态中成员变量的特点:无论编译或运行,都参考左边(引用型变量所属的类)

如下面代码:

class Fu
{
int num=5;
}

class Zi extends Fu
{
int num=8;
}

class DuoTaiDemo4
{
public static void main(String[] args)
{
Fu f=new Zi();
System.out.println(f.num);
Zi z=new Zi();
System.out.println(z.num);
}
}
运行结果: 黑马程序员--java基础复习之多态与Object及匿名内部类



3、在多态中,静态成员函数的特点(在开发中很少见,因为很少会去复写静态方法) 无论编译和运行,都参考左边    如下:
class Fu
{
int num=5;
static void method()
{
System.out.println("Fu method");
}
}

class Zi extends Fu
{
int num=8;
static void method()
{
System.out.println("Zi method");
}
}

class DuoTaiDemo4
{
public static void main(String[] args)
{
Fu f=new Zi();
f.method();
Zi z=new Zi();
z.method();
}
}
结果:黑马程序员--java基础复习之多态与Object及匿名内部类









Object类
类Object是类层次结构的根类。每个类都使用Object作为超 类。 Object :是所有对象的直接或者间接父类,传说中的上帝 该类中定义的肯定是所有对象都具备的功能。
下面讲的就是Object中的常用的方法
equals   boolean equals(Object obj)           指示其他某个对象是否与此对象“相等”。(其实比较的就是对象在内存中的地址值)
参数类型是Object ,这里也是用到了多态。
如下代码:
class Demo
{

}

class ObjectDemo
{
public static void main(String[] args)
{
Demo d1=new Demo();
Demo d2=new Demo();
Demo d3=d1;

System.out.println(d1.equals(d2)); //d1和 d2分别指向不同的对象,地址值自然不同 false
System.out.println(d1==d2); //在比较对象时,==和equals 效果一样 false

System.out.println(d1.equals(d3)); //d1和d3均指向同一个对象,所以地址值自然一样 true
System.out.println(d1==d3); //同上

}
}
结果:黑马程序员--java基础复习之多态与Object及匿名内部类





Object类中已经提供了对对象是否相同的比较方法。 如果自定义类中也有比较相同的功能,没有必要重新定义,只要沿袭父类Object中的功能,建立自己特有的比较内容即可。这就是覆 盖。如下:
class Demo
{
private int num;
Demo(int num)
{
this.num=num;
}
//复写父类Object中的equals方法
public boolean equals(Object obj)
{
if(obj instanceof Demo) //判断是否是Demo类型的对象
{
Demo d=(Demo)obj;
return this.num==d.num;
}
return false;
}
}

class ObjectDemo
{
public static void main(String[] args)
{
Demo d1=new Demo(5);
Demo d2=new Demo(5);

System.out.println(d1.equals(d2));
}
}

结果:黑马程序员--java基础复习之多态与Object及匿名内部类


内部类
内部类:将一个类定义在另一个类的里面,对里面那个类就称为内部类(内置类,嵌套类)
访问特点:
A、内部类可以直接访问外部类中的成员,包括私有成员
之所以可以直接访问外部类中的成员,是因为外部类中持有了一个外部内的引用,格式 外部类名.this
B、而外部类要访问内部类中的成员必须要建立内部类的对象


如下代码:
//外部类
class Outer
{
private int x=3;

//内部类
class Inner
{
void function()
{
//访问外部类中的成员,包括私有成员
System.out.println("Inner:"+x);
}
}

void method()
{
//外部类要访问内部类中的成员必须要建立内部类的对象
Inner in=new Inner();
in.function();
}
}

class InnerClassDemo
{
public static void main(String[] args)
{
Outer ou=new Outer();
ou.method();

/*********在其他类中直接访问内部类中的成员************/
//当内部类定义在外部类的成员位置上,而且非私有,可以在外部其他类中,直接建立内部类对象。
Outer.Inner in=new Outer().new Inner();
in.function();
}
}

再看下面一段代码:
class Outer
{
private int x=3;
private int y=6;
class Inner
{
int x=4;
void function()
{
int x=5;
System.out.println("Inner:"+x); //访问本类中的局部变量x
System.out.println("Inner:"+this.x); //访问本类中的x,所以用this
System.out.println("Inner:"+Outer.this.x);//访问外部类中的本类变量x,所以加上Outer.this
System.out.println("Inner:"+y); //这里因为没有同名变量,所以省略了Outer.this
}
}

void method()
{
Inner in=new Inner();
in.function();
}
}

class InnerClassDemo0
{
public static void main(String[] args)
{
Outer ou=new Outer();
ou.method();
}
}

结果:黑马程序员--java基础复习之多态与Object及匿名内部类 从上述代码中可以得出:之所以可以直接访问外部类中的成员,是因为外部类中持有了一个外部内的引用,格式 外部类名.this
从上面的代码中可以看到内部类的访问格式 访问格式:
1、当内部类定义在外部类的成员位置上,而且非私有,可以在外部其他类中,直接建立内部类对象。
格式:外部类名.内部类名  变量名=外部类对象.内部类对象
                                           如 Outer.Inner  in=new Outer().new Inner();
2、当内部类在成员位置上,就可以被成员修饰符所修饰。
比如,private:将内部类在外部类中进行封装


静态内部类  static:内部类就具备static 的特性。 当内部类被static 修饰后,只能直接访问外部类中的static成员。出现了访问局限。

如下代码片段:
class Outer
{
private int x=3;

//静态内部类
static class Inner
{
void function()
{
System.out.println("Inner:"+x);
}
}
}
结果:黑马程序员--java基础复习之多态与Object及匿名内部类

因为内部类用static修饰后,就具备了static的特性。所以静态不能直接访问非静态
注意:当内部类中定义了静态成员,该内部类必须是静态的n
 当外部类中的静态方法访问内部类时,内部类也必须是static的


如下代码段:
class Outer
{
private int x=3;

//静态内部类(当内部类中有静态成员时,该内部类必须是静态的)
static class Inner
{
static void function()
{
System.out.println("Inner:"+x);
}
}
}


3、内部类定义在局部时:
1、不可以被成员修饰符修饰(static  private)
2、可以直接访问外部类中的成员,因为还持有外部类中的引用。
但是不可以访问它所在的局部中的变量。只能访问被final修饰的局部变量


用如下代码说明:
<span style="color:#666666;">class Outer
{
int x=3;
void method()
{
final int y=4;
//局部内部类
class Inner
{
void function()
{
System.out.println("局部内部类:"+x);
//如果y没有用final修饰,则访问y时编译器会提示:从内部类中访问局部变量y;
//需要被声明为最终类型
System.out.println(y);
}
}

//要想访问非静态,必须通过对象

new Inner().function();
}
}

class InnerClassDemo1
{
public static void main(String[] args)
{
new Outer().method();
}
}</span>






匿名内部类
1、匿名内部类其实就是内部类的简写格式。
2、定义匿名内部类的前提:内部类必须是继承一个类或者实现接口
3、匿名内部类的格式:new 父类或者接口(){定义子类的内容}
4、其实匿名内部类就是一个匿名子类对象。而且这个对象有点胖。
5、匿名内部类中定义的方法最好不要等于或超过3个


用一个例子来说明:
//定义一个抽象类,作为匿名内部类的父类
abstract class AbsDemo
{
abstract void show();
}

//外部类
class Outer
{
int x=8;
void method()
{
//new Inner().function();
/**********使用匿名内部类1***********/

new AbsDemo()
{
//复写父类的抽象方法
void show()
{
System.out.println("我是匿名内部类:"+x);
}
}.show();
/**********使用匿名内部类2***********/
new AbsDemo()
{
//复写父类的抽象方法
void show()
{
System.out.println("我是匿名内部类:"+x);
}
void haha()
{
System.out.println("haha");
}
}.haha();

/**********使用匿名内部类2***********/

//这个是加载中的向上提升
AbsDemo ad=new AbsDemo()
{
//复写父类的抽象方法
void show()
{
System.out.println("我是匿名内部类:"+x);
}
//这个方法其实没什么意义
void haha()
{
System.out.println("haha");
}
};

ad.show();
//ad.haha();//这句会编译失败,因为父类中根本没有haha这个方法
}


}

class InnerClassDemo3
{
public static void main(String[] args)
{
new Outer().method();
}
}

下面来看一个匿名内部类的题目:
interface Inter
{
void method();
}
class Test
{
//补足代码。通过匿名内部类
}

class InnerClassTest
{
public static void main(String[] args)
{
Test.function().method();
}
}
代码及思路如下:
//这个接口是作为内部类的父类存在的(注:接口中全部都是public 类型的抽象方法)
interface Inter
{
void method();
}
class Test
{
//补足代码。通过匿名内部类
static Inter function()
{
return new Inter()
{
public void method()
{
System.out.println("这是一个匿名内部类的小题目");
}
};
}
}

class InnerClassTest
{
public static void main(String[] args)
{
Test.function().method();
//思路:
//Test.function():Test类中有一个静态的方法 function.
//.method():function这个方法运算后的结果是一个对象。
//因为只有是Inter类型的对象,才可以调用method方法。
}
}

再看另一个匿名内部类的题目:
/*补全下面的代码,想使用一个function方法。要求使用匿名内部类的方式完成*/

class InnerTest
{
public static void main(String[] args)
{
//补足代码。通过匿名内部类
}
}

完成:因为没有定义父类或接口,所以想到Object是所有类的直接或间接父类。
class InnerTest
{
public static void main(String[] args)
{
//补足代码。通过匿名内部类
new Object()
{
void function()
{
System.out.println("Hello world");
}
}.function();
}
}