Java面向对象的四大特性
继承 封装 多态 (抽象)
一 多态
同一个对象, 体现出来的多种不同形态(身份) , 将一种行为表现出不同的效果。
要想实现多态的效果 ,需要现有继承关系。
体现:
-
父类类型的引用 指向 子类的对象
Person p = new Teacher();
-
该引用只能调用父类中定义的属性或方法
-
如果子类中将父类的方法重写,那么调取方法后执行的结果是子类重写之后的那个结果。
如果父类与子类有同名的属性---------执行父类的属性
如果父类与子类有同名的方法(重写)----执行子类重写之后的方法 -
若想要调用子类中独有的成员
(强制类型转化) 造型 铸型 (向上/向下转型)
Teacher tt = (Teacher)p;
-
造型时(强制向下转型时) 可能会出现一个运行时异常
ClassCastException
造型 铸型 异常
可能因为同级别强制类型转换,就会异常,只有父类和子类才可以造型强制类型转换。
如果想要避免造型的异常,可以用instance of
关键字来进行判断
对象 instanceof 类
if(o instanceof Person){//对象是否属于后面类型
System.out.println("类型匹配 可以造型");
Student s = (Student)o;//运行时异常 ClassCastException
s.study();
}else{
System.out.println("对不起 类型不匹配 不帮您造型啦 否则会出问题");
}
多态例子:
Person类:
public class Person {
public String name = "person的name属性";
public void eat(){
System.out.println("person的吃饭方法");
}
public void sleep(){
System.out.println("人类的睡觉方法");
}
public void talk(){
System.out.println("人类的说话方法");
}
}
Teacher类:
public class Teacher extends Person {
public String name = "teacher的name属性";
public void eat(){ // 重写
System.out.println("做老师的通常不按时吃饭");
}
public void teach(){ // 独有方法
System.out.println("做老师的独有方法 一般人不会讲课 我会");
}
}
主方法:
public class Test {
public static void main(String[] args){
// Teacher 对象
Teacher t=new Teacher();
t.sleep();
t.talk();
t.eat(); //重写的方法
t.teach(); //独有的方法
System.out.println("----------------------------");
/*
人类的睡觉方法
人类的说话方法
做老师的通常不按时吃饭
做老师的独有方法 一般人不会讲课 我会
----------------------------
*/
// 多态,让老师作为另一种身份-------人类的方法
Person p = new Teacher(); //此时,这个老师体现的是一个人的身份,不能使用老师的重写和独有方法了
p.talk();
p.sleep();
p.eat(); // 做老师的通常不按时吃饭
// 如果子类中将父类的方法重写,那么调取方法后执行的结果是子类重写之后的那个结果
//p.teach(); //报错,不能使用,老师独有的方法了
System.out.println(p.name);
//如果父类与子类有同名的属性-----执行父类的属性
System.out.println("----------------------------");
/*
人类的说话方法
人类的睡觉方法
做老师的通常不按时吃饭
person的name属性
----------------------------
*/
//如果想要调用子类独有的属性或方法
//需要将类型还原会真实类型 强制类型转化 造型 向上转型 向下转型
Teacher tt = (Teacher)p; //这次还是那个人,只不过,现在体现的是老师的身份
tt.teach();
System.out.println(tt.name);
/*
做老师的独有方法 一般人不会讲课 我会
teacher的name属性
*/
}
}
输出结果:
人类的睡觉方法
人类的说话方法
做老师的通常不按时吃饭
做老师的独有方法 一般人不会讲课 我会
----------------------------
人类的说话方法
人类的睡觉方法
做老师的通常不按时吃饭
person的name属性
----------------------------
做老师的独有方法 一般人不会讲课 我会
teacher的name属性
多态实例:
银行Bank
设计一个方法 等待用户来办理业务
profession(需要一个人)
叫一个号码-->排队
去窗口办理-->办理
办理完毕离开->离开
老人 年轻 土豪
首先分析两者之间的关系,
- A is-a B 继承
- A has-a B 包含, 整体和部分的关系。通过一个类的对象当做另一个类的属性来存储。一个类当中有另一个类作为属性。车有轮子。
- A use-a B 依赖,不是整体和部分的关系。某一件事情产生了关系;临时组合在一起,这件事情一旦做完关系即解散。某一种方法使用另一个类的对象。农夫养猪。
银行处理业务的方法,需要人来介入其中,所以是某种方法将他们组合在了一起,是一种依赖关系。
先根据依赖关系写一下各个类以及方法:
- OldMan的类:
package Bank;
public class OldMan {
private String name;
//两个构造方法
public OldMan(){}
public OldMan(String name){
this.name=name;
}
public void setName(){
this.name = name;
}
public String getName(){
return this.name;
}
//1.进银行 叫一个号码 排队
public void callNumber(){
System.out.println("年事已高 不知道在哪儿叫号 请求大堂经理的帮忙");
}
//2.去窗口办理
public void transact(){
System.out.println("到窗口 掏出手绢儿 拿出存折 取钱");
}
//3.办理完毕离开啦
public void leave(){
System.out.println("办理完毕 慢慢的离开啦");
}
}
Bank类:
package Bank;
public class Bank {
//开门 等待用户进来办理业务
public void profession(OldMan om) { //某一个方法使用了另一个类的对象
System.out.println(om.getName() + "客户进入银行啦");
om.callNumber();
om.transact();
om.leave();
}
}
- 主方法测试一下:
package Bank;
public class Test {
public static void main(String[] args){
Bank bank = new Bank();
OldMan om = new OldMan("长者");
bank.profession(om);//银行欢迎长者进来办理业务
}
}
- 结果:
长者客户进入银行啦
年事已高 不知道在哪儿叫号 请求大堂经理的帮忙
到窗口 掏出手绢儿 拿出存折 取钱
办理完毕 慢慢的离开啦
所以年轻人和土豪也同样是这三个方法,只不过具体里面可能有所不同。
- YounMan类会变为:
package Bank;
public class YoungMan {
private String name;
//两个构造方法
public YoungMan(){}
public YoungMan(String name){
this.name=name;
}
public void setName(){
this.name = name;
}
public String getName(){
return this.name;
}
//1.进银行 叫一个号码 排队
public void callNumber(){
System.out.println("自己知道在门口按按钮 拿到号码小票");
}
//2.去窗口办理
public void transact(){
System.out.println("去窗口 汇款");
}
//3.办理完毕离开啦
public void leave(){
System.out.println("办理完迅速离开啦");
}
}
- 同样,Bank类也要添加新的方法:
// 为年轻人添加的方法
public void profession(YoungMan ym) {
System.out.println(ym.getName() + "客户进入银行啦");
ym.callNumber();
ym.transact();
ym.leave();
}
- 主方法测试一下
package Bank;
public class Test {
public static void main(String[] args){
Bank bank = new Bank();
OldMan om = new OldMan("长者");
bank.profession(om);//银行欢迎长者进来办理业务
YoungMan ym = new YoungMan("年轻");
bank.profession(ym);//银行欢迎长者进来办理业务
}
}
结果:
长者客户进入银行啦
年事已高 不知道在哪儿叫号 请求大堂经理的帮忙
到窗口 掏出手绢儿 拿出存折 取钱
办理完毕 慢慢的离开啦
年轻客户进入银行啦
自己知道在门口按按钮 拿到号码小票
去窗口 汇款
办理完迅速离开啦
土豪同理。不在赘述。
按照刚才的设计可能的问题:
1.三个不同的人类方法名不一致(可以)
2.银行办理业务的方法写了三个
解决如上所述的问题,可以在三个人类之上创建一个父类
可以解决的问题:
1.解决三个人类中的相同代码 比如name属性 比如get方法之类的
2.父类定义的三个方法可以是抽象,解决了子类命名不一致的问题 子类执行也不一致。
3.父类可以作为参数传入银行,然后通过多态调用,执行子类中重写的方法。
- Person基类的代码:
package Bank;
public abstract class Person {
protected String name;
public void setName(){
this.name = name;
}
public String getName(){
return this.name;
}
// 三个抽象的方法,子类可以去重写
//1.进银行 叫一个号码 排队
public abstract void callNumber();
//2.去窗口办理
public abstract void transact();
//3.办理完毕离开啦
public abstract void leave();
}
- OldMan子类的代码:
package Bank;
public class OldMan extends Person{
public OldMan(){}
public OldMan(String name){
this.name=name;
}
//1.进银行 叫一个号码 排队
public void callNumber(){
System.out.println("年事已高 不知道在哪儿叫号 请求大堂经理的帮忙");
}
//2.去窗口办理
public void transact(){
System.out.println("到窗口 掏出手绢儿 拿出存折 取钱");
}
//3.办理完毕离开啦
public void leave(){
System.out.println("办理完毕 慢慢的离开啦");
}
}
YoungMan:
package Bank;
public class YoungMan extends Person {
public YoungMan(){}
public YoungMan(String name){
this.name=name;
}
//1.进银行 叫一个号码 排队
public void callNumber(){
System.out.println("自己知道在门口按按钮 拿到号码小票");
}
//2.去窗口办理
public void transact(){
System.out.println("去窗口 汇款");
}
//3.办理完毕离开啦
public void leave(){
System.out.println("办理完迅速离开啦");
}
}
- 主方法测试一下
package Bank;
public class Test {
public static void main(String[] args){
Bank bank = new Bank();
Person p = new OldMan("长者");
//多态的使用,父类的引用指向一个子类的使用,
//表示这个Person的身份现在是他的子类,老人这一个具体的类别
//调用方法的时候是看父类中是如何定义的,四个方法定义了,就能调得到,但是执行的结果是看方法有没有被重写
//执行重写之后的结果
bank.profession(p);//银行欢迎长者进来办理业务
}
}
- 结果:
长者客户进入银行啦
年事已高 不知道在哪儿叫号 请求大堂经理的帮忙
到窗口 掏出手绢儿 拿出存折 取钱
办理完毕 慢慢的离开啦
调用一个方法 名字一样 传递参数却不同
1.利用方法重载—静态加载
2.利用多态效果—动态加载
二 内部类
指的是在Java中可以将一个类定义在另一个类的内部
- 内部类可以定义在 ,类的内部 (与类成员层次一致)
- 内部类可以定义在 ,方法/块内部 (与类成员相差一个层次 方法的局部变量一个层次)
1.成员内部类
2.局部内部类
3.匿名内部类
4.静态内部类
- 成员内部类
- 将一个类直接定义在类的里面,作为成员,与属性或方法层次一致
- 成员内部类可以与正常类一样 使用不同的修饰符来修饰
- 好处1.省略了一个.java文件
好处2.成员内部类中可以访问外部类的所有成员 包括私有的 - 若想要在内部类中通过对象.调用外部类成员 外部类.this.外部类成员;
Demo类
package innerclass;
public class Demo {
private String name = "这是正常类中的属性";
public void testDemo(){
System.out.println("这是正常类中的方法");
}
//成员内部类
public class InnerDemo{
private String name="我是内部类的属性";
public void testInnerDemo(){
System.out.println("我是成员内部类的方法:"+this.name); //this.是自己
System.out.println("我是成员内部类的方法:"+Demo.this.name); //Demo.this是外部
Demo.this.testDemo();
//testDemo(); //或者写这个,
//this.testDemo(); //但是不能写这个,因为InnerDemo类中的对象只能是InnerDemo的对象,然而InnerDemo类中没有testDemo这个函数
}
}
}
主方法测试:
package innerclass;
import innerclass.Demo.InnerDemo;
public class TestMain {
public static void main(String[] args){
//内部类属于外部类的(相当于是一个成员) 需要外部类对象才能操作
//创建内部类的对象---调用内部类的方法
Demo demo = new Demo();
InnerDemo innerDemo = demo.new InnerDemo();
//调用内部类的方法
innerDemo.testInnerDemo();
}
}
结果:
我是成员内部类的方法:我是内部类的属性
我是成员内部类的方法:这是正常类中的属性
这是正常类中的方法
- 局部内部类【不常用】
- 将一个类定义在方法/块里面,作为成员的内部结构,与临时的局部变量一个层次。
- 局部内部类像是一个局部的变量一样,不能用public protected private及static,他们是修饰成员的
- 只能用abstract或final
- 匿名内部类
成员匿名内部类
局部匿名内部类
public interfase Test{
public void test();
}
Test t = new Test(){
public void test(){
}
};
- 通常接口或抽象类的具体子类这样写
- 开发中为了省略一个类文件 上述写法比较常见
- 匿名内部类很特殊 只有类体 没有类的所有结构( 修饰符 名字 继承 实现)
- 不能用任何修饰符来修饰 匿名内部类也没有构造方法
- 静态内部类
成员静态内部类
不需要外部类对象,通过正常的方式直接创建内部类
静态元素不能访问非静态成员(自己类和外部类)