一、封装
1. 概述
定义:将类的某些信息隐藏在类的内部,不允许外部程序直接访问。只能通过该类提供的 特定的方法 来实现对隐藏信息的操作和访问,也就是:
要隐藏对象的信息
同时也要留出访问的接口
2. 封装的特点
隐藏类的实现细节,实现了信息的隐藏及安全性,方便修改和实现
提高了程序的模块化,提高系统独立性和软件的可重用性,且易于维护
具体实现是编写该类的人控制的,让使用者只能通过事先定制好的 方法 来访问数据,实现者可以方便地加入控制逻辑,限制对属性的不合理操作
- 封装的实现
变量:使用 private 修饰,这就是变量的封装
方法:也是一种封装,封装了多条代码
类: 也是一种封装,封装了多个方法
封装的实现步骤:
封装的实现
public class Cat {
//成员属性:
//修改属性可见性---private 限定只能在当前类内访问,只能修饰成员变量
private String name;
public Cat() {
}
//创建get/set方法
//在get/set方法当中添加属性的限定
public void setName(String name) { //set方法一般没有返回值
this.name = name;
}
public String getName() {
return "我是一只名叫"+this.name+"的猫咪";
}
public int getMonth() {
return month;
}
public void setMonth(int month) { //对年龄进行限定
if(month<=0)//对属性的限定信息
System.out.println("输入信息错误,宠物猫的年龄必须大于0");
else
this.month = month;
}
}
二、 继承
1. 概述
一种类于类之间的关系,使用已存在的类作为基础建立新类。
使用已存在的类的定义作为基础创建新类
新类的定义可以增加新的数据或新的功能,也可以用父类的功能,但 不能选择性地继承父类,必须继承父类 所有开放的特征
使用 extends 关键字实现
- 继承的逻辑关系
不能为了继承某个功能而随意进行继承操作,必须要符合 “A is a B” 关系
- 继承的特点:
子类会自动拥有父类所有 非 private 修饰的属性和方法
通过 子类对象 既可以调用自身的 非 private 修饰的成员,也可以调用父类的 非 private 修饰的成员
父类 不可以 访问子类 特有成员,即使是共有的
继承的出现提高了代码的复用性,提高软件开发效率。
继承的出现让类与类之间产生了关系,提供了多态的前提
- 继承的注意事项:
在Java中,类只支持单继承,不允许多继承,一个类只能有一个直接父类
多个类可以继承一个父类:
class B extends A{}
class C extends A{} // 类B和类C都可以继承类A
在Java中,多层继承 是可以的,即一个类的父类可以再去继承另外的父类
class A{}
class B extends A{} // 类B继承类A,类B是类A的子类
class C extends B{} // 类C继承类B,类C是类B的子类,同时也是类A的子类 (类的继承具有传递性)
在Java中,子类和父类是一种相对概念,一个类是某个类父类的同时,也可以是另一个类的子类
- 继承后子类父类成员变量的特点
子类的对象调用成员变量的时候,子类自己有,使用子类,子类自己没有则调用父类
子父类中出现了 同名 的成员变量时,在子类中需要访问父类中非私有成员变量时,需要使用 super 关键字
- 继承后子类父类成员方法的特性
子类的对象调用方法的时候,子类自己有,使用子类,子类自己没有调用的父类
子类中出现与父类一模一样的方法时,会出现覆盖操作,也称为 override 重写、复写或者覆盖
- 继承的初始化顺序
父类中有:静态代码块,构造代码块,无参构造方法,静态属性。
子类中有:静态属性,静态代码块,构造代码块,无参构造方法
执行顺序:
- 父类的静态代码块
- 子类的静态代码块
- 父类的构造代码块
- 父类的无参构造方法
- 子类的构造代码块
-
子类的无参构造方法
- 方法重载和方法覆盖
方法重载:
- 同一个类中
- 方法名:重载的两个方法的方法名必须相同
- 参数列表: 不同,三者至少满足一个(参数顺序、个数、类型),三者至少满足一个
- 重载与返回值类型、权限修饰符 无关
- 与方法的参数名 无关
方法覆盖 - 有继承关系的 子类 中, 子类覆盖父类的方法
- 方法名:子类方法和父类方法 必须相同,
- 参数列表:子类方法和父类方法的形参列表 必须相同
- 访问修饰符:子类方法的权限 >= 父类的方法的权限(不能比父类更严格)
- 返回值类型:(父类的返回值类型的子类)
- 基本数据类型: 必须相同
- 引用数据类型:相同 或者 子类方法的返回值类型是父类方法的返回值类型的 子类
注意:
当子类重写父类方法后,子类对象调用的是 覆盖后 的方法。
属性名也是一样的道理,没有重名之前,调用的是父类的,重名之后用的是子类的
被 final 修饰的方法不允许在子类中覆盖
父类被覆盖的方法的参数列表中被声明为 final 的参数,在子类的覆盖方法的中可以不必指定为 final
子类覆盖方法声明的 异常列表中的异常类 必须与父类被覆盖方法声明的异常列表中的异常类 兼容
只有在 子类类体中可以访问 的父类或祖先类的方法才能被覆盖
静态方法 不能被覆盖,只能被隐藏
如果通过子类对象访问父类方法,在 父类类体 中,访问到的任然是子类中的覆盖方法
三、 多态
-
概述
定义:多种形态,是面向对象语言最核心特征,封装和继承都是为多态准备的,非常重要
Java中多态的 代码体现 在一个子类对象(实现类对象)既可以给这个子类(实现类对象)引用变量赋值,又可以给这个子类(实现类对象)的父类(接口)变量赋值。
最终多态体现为 父类引用变量可以指向子类对象
如 Student 类可以为 Person 类的子类。那么一个S tudent 对象既可以赋值给一个 Student 类型的引用,也可以赋值给一个 Person 类型的引用。
多态的分类:
Java中的多态一般是运行时多态
编译时多态:设计时多态,方法重载来实现
运行时多态:程序运行时动态决定调用哪个方法
多态的前提:必须有 子父类关系 或者类 实现接口关系,否则无法完成多态。
多态的优点:
提高代码的可维护行
提高代码的扩展性
多态的弊端:不能使用子类的特有功能
如何解决?
法1:创建子类对象调用子类方法
法2:把父类的引用强转为子类引用 - 多态调用的三种格式
父类的引用变量指向子类对象:父类类型 变量名 = new 子类类型();
普通类多态定义的格式:父类 变量名 = new 子类();
抽象类多态定义格式:抽象类 变量名 = new 抽象类子类();
接口多态定义的格式:接口 变量名 = new 接口实现类();
- 注意事项:
同一个父类的方法会被不同的子类重写。在调用方法时,调用的为各个 子类覆盖后的方法
Person p1 = new Student();
Person p2 = new Teacher();
p1.work(); //p1会调用Student类中重写的work方法
p2.work(); //p2会调用Teacher类中重写的work方法
当变量名指向不同的子类对象时,由于每个子类覆盖父类方法的内容不同,所以会调用不同的方法。
- 多态中成员访问的特点
成员变量
编译看左边(引用变量的声明类型),运行看左边(实际访问到的成员变量的值,也是由引用变量的声明类型来决定)
方法
编译看左边(引用变量的声明类型),运行看右边(实际访问到的方法,是由引用变量所指向的对象的实际类型来决定)
- 编译时多态(方法重载 overload)
因为对于方法重载而言,虽然多个方法的方法名相同,但是我们的编译器,可以根据方法调用代码推断出,所要调用的那个方法的方法签名,从而根据方法签名(jvm唯一的),确定要调用的方法
注:方法签名: 方法名+方法参数列表**
- 运行时多态
因为在编译器编译的时候,无法知道,具体调用的是哪个方法的代码,只有当 jvm 具体真正执行到调用代码的地方,jvm才能知道调用的究竟是哪个方法
实现运行时多态:继承、方法覆盖/重写(override)、父类引用指向子类对象
- 多态的转型
向上转型:当有 子类对象赋值给一个父类引用 时,便是向上转型,多态本身就是向上转型的过程。(也叫:隐式转型、自动转型)
格式:父类类型 变量名 = new 子类类型();
向下转型:一个 已经向上转型 的子类对象可以使用 强制类型转换 的格式,将父类引用转为子类引用,这个过程是向下转型。如果是直接创建父类对象,是无法向下转型的!
格式:子类类型 变量名 = (子类类型) 父类类型的变量;
什么时候使用向上转型?
当不需要面对子类类型时,通过提高扩展性,或者使用父类的功能就能完成相应的操作,这时就可以使用向上转型。
什么时候使用向下转型?
当要使用子类特有功能时,就需要使用向下转型。
向下转型的好处:可以使用子类特有功能。
但是弊端是需要面对具体的子类对象;在向下转型时容易发生 ClassCastException 类型转换异常。在转换之前必须做 类型判断
如: if( !a instanceof Dog){…}
- instanceof 关键字
作用: 可以通过 instanceof 关键字来判断是否能够对象转化。也就是,一个引用型的变量,是不是这个类型的对象,提高向下转型的安全性
格式: boolean b = 对象 instanceof 数据类型; ,返回 true / false
注意:null instanceof <类名> 结果永远是 false
Person p1 = new Student(); // 前提条件,学生类已经继承了人类
boolean flag = p1 instanceof Student; //flag结果为true
boolean flag2 = p2 instanceof Teacher; //flag结果为false
来源:51CTO
作者:凉白开dream
链接:https://blog.51cto.com/14232658/2475548