目录
一、封装
1.1、封装介绍
封装概念:对事物进行包装。
封装的体现:
在Java中函数本身就是一个封装体,在函数中主要封装可以完成某个独立功能的代码。
类也属于一个封装体,在这个类中封装了某个事物的属性和行为。
1.2、封装举例
生活中封装的案例:
机箱就是一个封装体:
机箱中的的内容:主板、电源、cpu、硬盘、显卡、内存、风扇等。这些硬件组合在一起就可以使用。
这些组件组合在一起,可以使用,但不安全。
于是我们就找到六面板把这些硬件进行包装(封装),同时还要在板面预留一些按钮或者插口。
于是我们使用专用的机箱把这些硬件封装在其中,而不要用户操作的所有硬件全部通过机箱隔离起来,然后需要用户操作的部分,也不能让用户直接去操作,而是通过一些按钮或接口,进而我们间接的让用户对其中的硬件进行访问和操作。
比如一个机器人,提供向前、退后等操作按钮
封装的好处:
它把不需要外界直接操作的细节隐藏起来,然后通过第三方的按钮或者接口等手段保证外界还可以间接的去使用这些被隐藏起来的组件。
封装是隐藏实现的细节,对外暴露某些使用细节的功能。
以后再写代码的时候,一个类中不需要外界操作的内容全部给封装起来,如果需要外界操作的,这时提供相应的访问方式。
1.3、private关键字介绍
class Person
{
String name;
int age;
void say()
{
System.out.println("name = "+name +" , age = "+age);
}
}
class PrivateDemo
{
public static void main(String[] args)
{
//创建Person对象,给属性赋值
Person p = new Person();
p.name = "李伦";
p.age = -18;
p.say();
}
}
在给人的年龄赋值的时候,由于人的年龄这个属性在生活中是不可能出现负数
但是由于在Java中人的年龄是通过一个int类型的变量表示
而 int 类型的变量中就可以保存负数,
就需要我们在程序对这个变量中的数据进行控制。
需要在程序中对这个int 类型变量空间中的值进行合法性的判断
这时我们就需要对Person类中的age 进行控制,保证外界在访问的时候,数据是合法的
要保证在一个类中可以限定其他类中的变量时,那么就需要对这个类中的成员变量使用相对应的成员权限修饰符完成。可以使用private 来修饰。
我们希望对age这个变量进行控制,于是在Person类中给age 添加了权限修饰符 private ,编译报错:
导致发生这个问题的原因是Person类中的age使用private修饰符,在一个类中当使用private关键字修饰了某个成员,这时这个成员只能在这个类中访问,当出了这个类之后,再也无法访问了。
可以一个类中的成员变量,肯定还是需要外界可以操作的,这时可以对外提供相应的函数,通过函数来间接的访问这个类中被私有的内容。
//描述一个人
class Person
{
String name;
//私有成员变量
private int age;
//对外提供给age设置值的函数
void setAge( int a )
{
//在给age赋值之前,我们可以通过这个函数接受的参数对传递进来的年龄进行判断
if( !(a > 0 && a < 150) )
{
throw new RuntimeException("请传递地球上可以接收的年龄");
}
age = a;
}
void say()
{
System.out.println("name = "+name +" , age = "+age);
}
}
要求:以后再定义类的时候,要求类中的所有成员变量全部私有,并对外提供相应的访问函数(函数)。
开发中的书写习惯:
一个类中成员变量全部私有,对外提供getXxxx函数或者setXxxx函数 , Xxxx表示的成员变量的名字,而成员变量的名字中的第一个字母要大写。
//演示一个类中的成员变量私有,对外提供对应的函数
class Person
{
private String name;
private int age;
public void setName( String nm )
{
name = nm;
}
public String getName()
{
return name;
}
public void setAge( int a )
{
age = a;
}
public int getAge()
{
return age;
}
}
二、继承
2.1、继承引入
面向语言三大基本特征:
封装、继承、多态。
Java中的类是描述生活中的任何事物的。而在生活中任何事物之间必然会存在一些联系。那么我们在前面学习的过程中,仅仅只是在描述单个的事物,并没有去把握事物和事物之间的关系。
接下来我们要研究的是如何通过Java中的类,展示所描述的事物和事物之间的关系。
一般事物和事物之间会有继承的关系。在Java中我们的类就是描述事物的,那么也就是说我们的类之间也应该存在继承的关系。
/*
学生:
属性:年龄、性别、姓名
行为:学习、吃饭
*/
class Student
{
String name;
int age;
String sex;
Student( String name , int age , String sex )
{
this.name = name;
this.age = age;
this.sex = sex;
}
//行为
void study()
{
System.out.println(name+"在 学习 java ......");
}
void eat()
{
System.out.println("吃饭.......");
}
}
/*
老师:
属性:年龄、性别、姓名、薪水
行为:上课、吃饭
*/
class Teacher
{
String name;
int age;
String sex;
double salary;
Teacher( String name , int age , String sex , double salary )
{
this.name = name;
this.age = age;
this.sex = sex;
this.salary = salary;
}
void teach()
{
System.out.println(name+"正在讲 Java....");
}
void eat()
{
System.out.println("吃饭.......");
}
}
上述程序在描述学生和老师这2类事物,但是学生和老师这两类事物中有共同的属性和行为,我们应该只描述一次就可以了,然后让学生和老师共享这些属性和行为。
把学生和老师中的共性行为和属性进行抽取。抽取到哪里合适呢?
在描述事物的时候,发现描述的两个事物之间没有继承的关系,但是2个事物之间有共同的属性和行为,那么我们就找这2个事物的共同祖先。
学生和老师,没有继承关系,但是学生和老师都属于人这类事物。那么我们就可以把学生和老师的共性内容抽取到人这个类中,然后让学生和老师与人这个类产生关系即可。
2.2、Java中的继承体现
/*
把学生和老师中的共性内容抽取到人这个类中
*/
class Person
{
String name;
int age;
String sex;
void eat()
{
System.out.println("吃饭.......");
}
}
/*
学生:
属性:年龄、性别、姓名
行为:学习、吃饭
*/
class Student extends Person
{
Student( String name , int age , String sex )
{
this.name = name;
this.age = age;
this.sex = sex;
}
//行为
void study()
{
System.out.println(name+"在 学习 java ......");
}
}
/*
老师:
属性:年龄、性别、姓名、薪水
行为:上课、吃饭
*/
class Teacher extends Person
{
double salary;
Teacher( String name , int age , String sex , double salary )
{
this.name = name;
this.age = age;
this.sex = sex;
this.salary = salary;
}
void teach()
{
System.out.println(name+"正在讲 Java....");
}
}
class ExtendsDemo2
{
public static void main(String[] args)
{
Student s = new Student("9527",23,"男");
s.eat();
s.study();
Teacher t = new Teacher("华安",28,"女",9999.99);
t.eat();
t.teach();
}
}
Java中的继承代码体现:
当某个类需要继承其他类的时候,在当前定义这个类的地方使用 extends 关键字,然后让这个类去继承需要继承的其他类。
这时被继承的类称为 父类。 实现继承的这个类称为 子类。
在Java的继承语法中,只要使用extends 关键字 任何2个类之间都可以完成继承。但是我们在写程序的时候,一定要心里清楚2个类之间有没有联系,能不能继承。
2.3、继承的特点
Java语言只支持单继承,不支持多继承,但支持多重继承。
多重继承
class A{
void show(){}
}
class B extends A{
void show(){}
}
class C extends B{
}
多继承:
class A{}
class B {}
class C extends B,A{} 不支持这种继承
Java中不支持多继承的原因:
class A{
void show(){}
}
class B{
void show(){}
}
class C extends B,A{}
//当我们new C 对象的时候, 调用show方法,由于B 和A类中都有show方法,这是通过C对象就不知道到底调用的是哪个父类中的show方法。
单继承:一个类只能有一个直接父类。不能有多个父类。但可以有多个间接父类。但一个类可以有多个子类。
2.4、继承成员变量的特点
学习继承:围绕 类中的成员变量 、成员函数 、 构造函数的变化。
继承好处:让类和类之间产生的关系,父类定义好的成员,子类可以直接使用。提供代码的复用性。
继承弊端:打破的封装性。
//继承中成员变量的特点:
class Fu
{
int x = 3;
}
class Zi extends Fu
{
int y = 10;
void show()
{
System.out.println("x="+x+",y="+y);
}
}
class Extends Demo3
{
public static void main(String[] args)
{
Zi z = new Zi();
z.show();
}
}
在上述程序中 Zi 类 继承 Fu 类,在测试的程序中 我们仅仅只创建了 Zi类的对象,而没有创建Fu类的对象,但是在程序运行的过程中,使用到了Fu类中的成员变量,说明在程序一定给Fu类的成员变量开辟的空间。
当子类继承了父类,在创建子类对象的时候,堆中的子类对象中会划分出一片空间来保存父类中的成员变量。
面试题:
子父类中出现了同名的成员变量,如何在子类中访问父类的成员变量?
//继承中子父类中出现同名的成员变量:
class Fu
{
int x = 3;
}
class Zi extends Fu
{
int x = 10;
void show()
{
int x = 111;
System.out.println("局部变量x="+x);
System.out.println("成员变量x="+this.x);
System.out.println("父类成员变量x="+super.x);
System.out.println("this="+this);
//System.out.println("super="+super);
}
}
class ExtendsDemo4
{
public static void main(String[] args)
{
Zi z = new Zi();
z.show();
}
}
如果在子类中要访问父类中的成员变量,可以使用super关键字
在Java中super关键字主要是用来在子类中去访问父类的内容。
格式:
super.父类成员(变量,函数)
super(参数); 访问父类的构造函数
this表示的本类中的成员,super表示父类中的成员。
在Java中,this是一个引用变量,它中保存的是当前调用这个函数的那个对象的内存地址
而super仅仅表示的父类的内容。但不是父类的对象。
2.5、继承中成员方法特点(复写)
在程序中,父类都是用来描述这个事物体系中共性的属性和行为。一般在父类中描述好的属性和行为,子类是可以直接继承到,并且在子类中可以直接使用父类中定义好的属性和行为(前提是父类中没有私有)。
在继承中子父类中出现了一模一样的成员函数,这种应用在开发十分常见。
一般在描述事物的时候,父类描述的行为是当前这个体系中的共性行为,但是随着事物的进化,子类依然会具备父类的这些行为,但是行为体和父类有会差异,这是我们一般情况下,在描述类的时候,会在父类中把这个共性的行为描述清楚,接着再子类重新定义和父类中相同的功能,但是把功能体修改为更符合子类的实际情况。
而把子父类中出现了相同的函数的这种体现:
在Java中称为函数的复写(重写、覆盖 override)。
函数重写(复写):(三同)
在子父类中,出现了一模一样的函数。一模一样:要求函数的返回值类型、函数名、函数的参数必须相同。
//演示函数的复写
class Fu
{
int x = 123;
void show()
{
System.out.println("Fu show run.....");
}
}
class Zi extends Fu
{
//假设父类中的show方法的方法体不适合子类时,
//子类可以沿用父类的方法定义格式,但重写书写新的方法体
void show()
{
System.out.println("Zi show run ...........");
}
}
class FunctionOverrideDemo
{
public static void main(String[] args)
{
Zi z = new Zi();
z.show();
}
}
2.6、成员方法复写应用
/*
需求:描述手机这类事物。原始的手机和现代的手机
*/
class Phone
{
void call()
{
System.out.println("打电话");
}
void sendMessage()
{
System.out.println("发信息");
}
}
class NewPhone extends Phone
{
//新一代的手机具备手机原有的功能,但是在打电话的基础上可以增加更多的显示效果
void call()
{
//打电话 , 显示大头贴 显示 归属地
//System.out.println("打电话"); 父类已经实现了,这时我们只要沿用父类的功能,
//在父类的功能基础上增加子类的功能
super.call();
System.out.println("显示大头贴和归属地");
}
void sendMessage()
{
//现在的手机依然可以发短信,但也可以发送图片,音频等信息
//System.out.println("发信息");
super.sendMessage();
System.out.println("发彩信");
}
}
class FunctionOverrideTest
{
public static void main(String[] args)
{
NewPhone np = new NewPhone();
np.call();
np.sendMessage();
}
}
结论:
当父类描述好的行为,但是行为体不适合自己的时候,子类可以沿用父类的行为定义方式,对行为体进行重新的书写。
2.7、成员方法复写注意事项
- 必须有继承关系,然后在子父类中出现了一模一样的函数。
- 函数的复写要求子类中的函数返回值类型 、 函数名 、 参数 必须和父类全部相同。
- 子类在复写父类的函数时,子类的方法访问权限,必须大于等于父类的权限。
- 子类复写父类的方法时,不能修改方法的访问方式。
父类中的方法是通过对象访问的,那么子类复写完之后,也必须是通过对象访问。
父类的方法是非静态,子类复写完也必须非静态的。如果父类是静态的,子类复写完也必须是静态的。
2.8、子父类中构造函数特点
在前面学习对象的创建流程中,在使用new关键字创建对象的时候,先在堆中给对象分配内存空间,接着给非静态的成员变量进行默认初始化,开始调用对应的构造函数。而在执行构造函数中有隐式的三步:
- super(); 它是在调用自己的父类空参数的构造函数。
- 成员变量显示赋值
- 构造代码块运行
4、本构造函数中的代码运行。
//继承中的构造函数细节
class Fu
{
int x = 3;
{
System.out.println("Fu 构造代码块 x="+x);
}
Fu()
{
//super();
System.out.println("Fu的构造函数执行");
show();
}
void show()
{
System.out.println("Fu show x="+x);
}
}
class Zi extends Fu
{
int y = 10;
{
System.out.println("Zi 的构造代码块 x="+x);
System.out.println("Zi 的构造代码块 y="+y);
}
Zi()
{
super();
System.out.println("Zi的构造函数执行。。。。。");
show();
}
void show()
{
System.out.println("Zi show x="+x);
System.out.println("Zi show y="+y);
}
}
class ConstructorDemo
{
public static void main(String[] args)
{
Zi z = new Zi();
}
}
2.9、子类的实例化过程
1、为什么任何一个类(不包含Object)的构造函数中需要一个隐式的super() 语句?
因为任何的子类在继承了父类之后,都会继承到父类的成员变量,这时在创建子类对象的时候,会在子类的对象空间中分配出空间存储父类的成员变量。而父类的成员变量初始化动作必须由父类的自己的构造函数完成。所以在任何类的构造函数中有一个隐式的super()语句。
2、如果一个类中的所有构造函数全部私有了,问还可以有子类吗?子类能实例化吗?
一个类的构造函数全部私有,这时这个类是无法再有子类的。就不存在实例化问题。
3、如果一个类没有空参数的构造函数,问这个类可以有子类吗?子类可以实例化吗?
如果这个类还有其他的构造函数可以被访问,那么这个类就可以有子类。
这时要求在子类的构造函数中必须手动的书写super语句,同时在super( 具体的参数 )。根据指定的参数去调用父类类型相同的构造函数。
4、this调用构造函数可以和super调用父类构造函数共存吗?
不可以。因为this和super调用构造函数都必须在构造函数中的第一行。
this是调用本类其他构造函数对当前这个对象初始化,而super是调用父类构造函数进行初始化,而初始化动作在一个构造函数中只能有一个。
在一个类中有多个构造函数,之间可以使用this调用,但不能形成嵌套调用。最后肯定有一个构造函数中是没有this调用的,最后它中必然会有super语句调用父类的构造函数。
2.10、super的应用
/*
需求:描述学生和工人,把共性抽取到Person类中,使用super语句完成初始化动作。
分析
学生:
年龄
姓名
性别
学习
吃饭
工人
年龄
姓名
性别
薪水
工作
吃饭
共性收取:
人:
年龄
姓名
性别
吃饭
*/
class Person
{
private String name;
private String sex;
private int age;
String getName()
{
return this.name;
}
Person( String name , String sex , int age )
{
this.name = name;
this.age = age;
this.sex = sex;
}
void eat()
{
System.out.println("吃饭");
}
}
//描述学生
class Student extends Person
{
//学生一创建就应该有自己的属性数据
Student( String name , String sex , int age )
{
super(name,sex,age);
}
void study()
{
System.out.println( getName() + " good good study");
}
}
//描述工人
class Worker extends Person
{
private double salary;
Worker( String name , String sex , int age , double salary )
{
super(name,sex,age);
this.salary = salary;
}
void work()
{
System.out.println( getName() + " 努力工作");
}
}
class SuperTest
{
public static void main(String[] args)
{
Worker w = new Worker( "王宝强" ,"女" , 33 , 1.28 );
w.work();
Student s = new Student( "林志玲" ,"男" , 29 );
s.study();
}
}
三、多态技术
3.1、多态介绍
多态:表示的是一个事物的多种表现形态。同一个事物,以不同的形态表现出来。
多态来源于生活,在生活中我们经常会对某一类事物使用它的共性统称来表示某个具体的事物,这时这个具体的事物就以其他的形式展示出来。
苹果:说苹果,说水果。
猫:说猫,说动物
3.2、多态技术在Java中的体现
在Java中的多态代码体现:
使用父类的引用,表示自己的子类对象。
Cat c = new Cat(); 使用猫类型表示自己,这里不会发生多态现象
Animal a = new Cat(); 使用动物的类型再表示猫,这时就发生的多态的现象。
在Java中要使用多态技术:
前提:必须要有继承/实现。
好处:可以通过父类统一管理子类。
//演示多态技术
class Animal
{
void eat()
{
}
void show()
{
System.out.println("show run .....");
}
void show2()
{
System.out.println("show2 run .....");
}
}
class Cat extends Animal
{
void eat()
{
System.out.println("猫吃鱼");
}
}
class Dog extends Animal
{
void eat()
{
System.out.println("狗啃骨头");
}
}
class DuoTaiDemo
{
public static void main(String[] args)
{
Cat c = new Cat();
demo(c);
Dog d = new Dog();
demo(d);
Animal a = new Animal();
demo(a);
}
/*
在调用方法的时候发生了多态的现象
Animal a = new Cat(); 这里发生了多态
Animal a = new Dog(); 这里也是多态
Animal a = new Animal(); 这不是多态
我们在使用多态的时候,永远只能使用父类的父类接受子类的对象,而不能使用
子类的类型接受父类的对象。
*/
public static void demo( Animal a )
{
a.eat();
a.show();
a.show2();
}
}
演示多态弊端
class Animal
{
void eat()
{
}
}
class Cat extends Animal
{
void eat()
{
System.out.println("猫吃鱼");
}
//猫有自己的特有行为 抓老鼠
void catchMouse()
{
System.out.println("猫抓老鼠");
}
}
class Dog extends Animal
{
void eat()
{
System.out.println("狗啃骨头");
}
//狗也有自己的行为 看家
void lookHome()
{
System.out.println("狗看家");
}
}
class DuoTaiDemo2
{
public static void main(String[] args)
{
Cat c = new Cat();
c.eat();
c.catchMouse();
Dog d = new Dog();
d.eat();
d.lookHome();
//使用多态调用方法
Animal a = new Dog();
a.eat();
a.lookHome();
}
}
在使用多态技术的时候,程序在编译的时候,
使用多态调用成员(变量和函数),要求被调用的成员在父类中一定要存在。
如果父类中没有编译就会失败。
多态的弊端:
把一个子类类型提升成了父类的类型,那么在程序编译的过程中,
编译不会考虑具体是哪个子类类型,而只会根据当前的父类类型去操作,
通过父类的引用在调用方法的时候,只会去父类类型所属的类中找有没有这些成员,
如果有编译通过,如果没有编译失败。
因此在多态中,我们只能调用所有子类共性的行为(在父类中描述的行为或属性),
而不能调用子类自己的特有属性和行为。
只要有多态的地方,一定发生类型的提升(肯定是把子类对象使用父类类型在表示)。
3.3、多态中的转型
class DuoTaiDemo3
{
public static void main(String[] args)
{
Cat c = new Cat();
c.eat();
c.catchMouse();
Dog d = new Dog();
d.eat();
d.lookHome();
//使用多态调用方法
Animal a = new Dog();
a.eat();
/*
如果已经发生多态现象,但是我们还想调用子类的特有属性或者行为,这时需要使用
强制类型转换,把当前父类类型转成具体的子类类型。
在多态中的类型转换问题:
1、隐式的类型提升。只要有多态就会发生类型提升(向上转型)。
2、把父类类型转成子类类型(强制类型转换,向下转型)。
什么时候使用向下转型:
只要在程序中我们需要使用子类的特有属性或行为(方法、函数)的时候,才会使用向下转型。
*/
//(Dog)(a).lookHome();
Dog dd = (Dog)a; //多态的转型
dd.lookHome();
/
Animal a2 = new Dog();
demo(a2);
}
public static void demo( Animal a )
{
System.out.println("=============");
a.eat();
//把Animal类型的a转成 Dog类的d
/*
向下转型有风险,使用需谨慎。
在Java中要使用向下转型,必须先做类型的判断,然后在转型
Java中的类型判断 需要使用关键字 instanceof
格式:
被转的引用变量名 instanceof 被转成的类型
如果引用变量所在的那个对象 和被转成的类型一致,这个表达式返回的是true,否则是false
在多态中使用转型的时候,一定要判断,防止类型转换异常的发生:
如果在程序发生ClassCastException,一定是把不是这种类型的对象转成了这种类型。
*/
if( a instanceof Dog )
{
Dog d = (Dog)a;
d.lookHome();
}
else if( a instanceof Cat )
{
Cat c = (Cat)a;
c.catchMouse();
}
}
}
总结:
只要有多态,就会有类型的转换。
把子类对象赋值给父类的引用,这时发生了向上的转型(隐式类型转换)。
如果我们需要使用子类的特有行为或属性,这时必须向下转型,需要把父类的引用转成具体所指的那个对象的类型。
在向下转型的时候一定要做类型的判断,防止ClassCastException异常的发生。
判断格式:
if( 父类引用变量名 instanceOf 子类对象所属的类名 )
{
进行转换。
}
3.4、多态中的成员特点
学习多态中的成员使用规律:需要掌握的是以多态形式使用成员,需要考虑程序的编译和运行2个阶段。
成员变量:是直接使用父类引用操作变量。而没有通过其他的函数来操作。
//多态中的成员变量使用规律
class Fu{
//int x = 5;
}
class Zi extends Fu{
int y = 6;
int x = 5;
}
class DuoTaiDemo {
public static void main(String[] args) {
Fu f = new Zi();
f.x = 123; 编译失败 因为父类中没有x变量
System.out.println("f.x="+f.x);
}
}
在多态中,使用父类的引用访问成员变量,代码在编译的时期,需要查看父类中有没有这个成员变量,如果有,编译通过,没有编译失败。
多态中,,使用父类的引用访问成员变量,编译通过的前提下,如果直接使用父类引用操作成员变量,这时操作的依然是父类中的成员变量。
简化记忆:引用变量,编译运行都看引用类中的变量(编译运行看左边。)。
成员函数:
在多态中,使用父类引用调用成员函数的时候,一般函数都复写存在。
在使用父类应用调用函数的时候,编译时期要看父类中有没有这个函数,有,编译通过,没有编译失败。
在运行的时候,运行的是子类中复写父类之后的那个函数。如果没有复写,运行的肯定还是父类的函数。
简化记忆:编译时期看左边,运行时期看右边。
//多态中的成员函数使用规律
class Fu{
int x = 5;
void show()
{
System.out.println("Fu show run ......");
}
}
class Zi extends Fu{
int y = 6;
int x = 55;
void show()
{
System.out.println("Zi show run ......");
}
}
class DuoTaiDemo2 {
public static void main(String[] args) {
Fu f = new Zi();
f.show();
}
}
静态成员函数:
静态函数的调用和对象本身是无关的。因此使用多态调用静态函数的时候,编译运行都要看父类中的函数。
简化记忆:编译运行都看赋值号左边。
//多态中的静态成员函数使用规律
class Fu{
int x = 5;
static void show()
{
System.out.println("Fu show run ......");
}
}
class Zi extends Fu{
int y = 6;
int x = 55;
static void show()
{
System.out.println("Zi show run ......");
}
}
class DuoTaiDemo3 {
public static void main(String[] args) {
Fu f = new Zi();
f.show();
}
}
总结多态中成员使用规律:
成员变量和静态成员函数,编译运行都看左边(父类中的)。只有非静态成员函数,编译看父类,运行看子类对象。
3.5、多态的练习
练习的目的:需要掌握多态中,至始至终只有子类对象存在,没有父类的对象,并且把子类对象交给父类的引用在使用。
练习毕姥爷和毕老师的故事。
老白:
睡觉(){}
钓鱼(){}
小白:
睡觉(){}
看电影(){}
//多态练习
class 老白
{
void 睡觉()
{
System.out.println("睡觉打呼噜");
}
void 钓鱼()
{
System.out.println("老白在钓鱼");
}
}
class 小白 extends 老白
{
void 睡觉()
{
System.out.println("爱做白日梦");
}
void 看电影()
{
System.out.println("小白在看《悟空传》");
}
}
class DuoTaiTest
{
public static void main(String[] args)
{
/*
小白 xb = new 小白();
xb.睡觉();
xb.钓鱼();//继承后,小白也会钓鱼
*/
老白 lb = new 小白(); //多态
lb.睡觉();
小白 xxb = (小白)lb; //多态的老白卸妆 向下转型
xxb.看电影();
}
}