Java面向对象三大基本特征:封装、继承、多态(超详细)
Java面向对象的三大基本特征
@
学习目标
熟练掌握三大基本特征的的概念与应用
一、学习步骤
1.封装
学习封装我们需要了解类和对象的概念
(1)、类
- 类是拥有相同特性(状态)和行为(功能)的多个事物的抽象描述。
- 类是用来描述群体的,是对群体的抽象描述。例如eacher类用于描述老师这个群体,张三是老师,李四是老师,王五也是老师。
- 在程序中,类就可以作为一种新型的数据类型。可以使用类来声明变量
(2)、对象
- 对象是类的一个具体实例,它用于描述一个具体的个体。
- 对象一定属于某一类,具备该类的特征和行为。例如张三老师必须具备老师这个类的特征,老师有教学生的行为,那么张三是老师,当然也会教学生
- 对象是独立的,唯一的个体。
(3)、封装
- 封装的定义:
把对象的字段和方法存放在一个独立的模块中(类)
信息隐藏,尽可能隐藏对象的数据和功能的实现细节 - 封装的好处:
保证数据的安全性,防止调用者随意修改数据
提高组件的重用性,把公用功能放到一个类中,谁需要该功能,直接调用即可
(4)、访问修饰符
封装其实就是要让有些类看不到另外一些类中定义的字段和方法。Java提供了不同的访问权限修饰符来限定类中的成员让谁可以访问到。
2.继承
继承可以解决代码复用问题,当多个类存在相同的属性和方法时,可以从这些类中提取出共同的属性和方法给父类,然后我们的子类就可以省去这些共同的属性和方法,只需要通过extends语句来声明继承即可
例子:
老师:拥有名字、年龄、级别三个状态,有授课和休息两个功能
学生:拥有名字、年龄、学号三个状态,有学习和休息两个功能
以上老师和学生都拥有名字和你年龄2个字段,都有休息的功能,我们可以吧共同的字段提取出来给父类来定义,把休息的功能也提取出来给父类,
然后老师和学生类就不需要定义这2个字段了,也可以不定义休息的方法了,只需要用extends去继承父类即可。
(1)、继承语法
public class 子类名 extends 父类名{ }
注意:Java中类只支持单继承,但是支持多重继承。也就是说一个子类只能有一个直接的父类,父类也可以再有父类,Java中接口是支持多继承的。
(2)、 子类能继承的父类成员
子类继承父类之后,可以拥有到父类的某一些成员(字段和方法),根据访问修饰符来判断:
- 如果父类中的成员使用public和protected修饰,子类都能继承
- 如果父类和子类在同一个包中,使用缺省访问修饰的成员,此时子类可以继承到
- 如果父类中的成员使用private修饰,子类继承不到。private只能在本类中访问(这里引入一个拥有的概念,子类可以拥有父类的private修饰的方法,当时只能通过get和set方法访问)
- 父类的构造器,子类也不能继承,因为构造器必须和当前的类名相同
(3)、方法覆盖
- 方法覆盖的定义:
子类继承了父类,可以拥有父类的部分方法和成员变量。可是当父类的某个方法不适合子类本身时,我们就可以使用方法覆盖去重写父类的方法
特征时 - 方法覆盖的语法:
首先子类得继承父类,当子类存在一个和父类一模一样的方法时,我们就称之为子类覆盖了父类的方法,也称之为重写。
- 方法的调用顺序:
通过对象调用方法时,先在子类中查找有没有对应的方法,若存在就执行子类的,若子类不存在就执行父类的,如果父类也没有,报错。
- 方法覆盖的细节:
1> private修饰的方法不能被子类所继承,也就不存在覆盖的概念。
2> 实例方法签名必须相同 (方法签名= 方法名 + 方法的参数列表)
3> 子类方法的返回值类型是和父类方法的返回类型相同或者是其子类
子类方法的访问权限比父类方法访问权限更大或相等
4> 如果父类方法是private,子类方法不能重写。==> 重写建立在继承的基础上,没有继承,就不能重写。
5> 子类方法中声明抛出的异常小于或等于父类方法声明抛出异常类型
上述的方法覆盖细节真多,记不住,那么记住下面这句话就万事OK了。
精华:直接把你要重写的方法复制粘贴到子类中,然后重新编写子类方法体就好了!
(4)、super关键字
在子类中的某一个方法中需要去调用父类中被覆盖的方法,此时得使用super关键字。
子类方法名() {
super.父类方法名;
}
3.多态
所谓多态,表示一个对象具有多种形态。说的具体点就是同一引用类型变量调用同一方法时,由于引用实例不同,方法产生的结果不同。
例子:
// 现有3个类,狗和猫都继承了动物这个类
public class Animal{}
public class Dog extends Animal{} // Dog is a Animal
public class Cat extends Animal{} // Cat is a Animal
// 下面是2种实例化对象的方式
Dog d = new Dog(); //创建一只狗对象,赋给子类类型变量
Animal a = new Cat(); //创建一只猫对象,赋给父类类型变量
我们看第二种实例化方式
编译时类型:声明对象变量的类型——>Animal
运行时类型:对象的真实类型 ——>Dog
当编译类型和运行类型不一致的时候,此时多态就产生了,结合多态语法理解记忆
(1)、多态语法
- 操作继承关系
父类 变量名 = new 子类();
变量名.方法();
- 操作实现关系(实际开发中较为常见)
接口 变量名 = new 实现类();
变量名.方法();
(2)、 多态例题
- 操作继承关系例题
Animal类:
public class Animal {
public void shut() {
System.out.println("Animal...shout...");
}
}
Dog类:
public class Dog extends Animal{
public void shut() {
System.out.println("旺旺旺...");
}
}
Cat类:
public class Cat extends Animal{
public void shut() {
System.out.println("妙妙妙...");
}
}
测试类:
public class AnimalDemo {
public static void main(String[] args) {
// 创建Cat对象
Animal a = new Cat();
a.shout();
// 创建Dog对象
a = new Dog();
a.shout();
}
}
运行结果:
妙妙妙...
旺旺旺...
- 操作实现关系例题(实际开发中较为常见)
ISwimable 接口:
public interface ISwimable {
void swim();
}
Fish类:
public class Fish implements ISwimable{
public void swim() {
System.out.println("游啊游...");
}
}
测试类:
public class FishDemo {
public static void main(String[] args) {
// 创建Fish对象
ISwimable fish = new Fish();
fish = new Dog();
//Dog也是实现了ISwimable接口的
Fish f = new Fish();
fish.swim();
}
}
运行结果:
游啊游...
(3)、多态中方法的调用
文字解释:
例如上面的动物类:
Animal a = new Cat();
a.shout();
使用多态调用方法时,先判断shout方法是否在父类Animal类中:
1> 找不到:编译报错
找 到:再看shout方法是否在子类Cat类中:
2> 找不到:运行父类方法
找 到:运行子类方法(这个才是真正的多态方法调用)
(4)、多态中的类型转换
- 自动类型转换:把子类对象赋给父类变量(多态)
Object obj = new 子类对象();
Animal a = new Dog();
Object obj = new Dog(); //Object是所有类的根类
- 强制类型转换:把父类类型对象赋给子类类型变量。
子类类型 变量 = (子类类型)父类对象;
- (前提:该对象的真实类型应该是子类类型),在实际开发过程中,如果需要调用子类特有的方法时,一定要进行强制类型转换。
Animal a = new Dog();
Dog d = (Dog) a; //正确
Cat c = (Cat) a; //错误
注意:上面的 a 是父类引用指向子类对象, a 对象可以直接使用父类的方法和子类对父类重写的方法,但是不能直接访问子类独有的方法,要想访问需要把 a 对象转换成对应的子类类型,这也叫向上转型。
(5)、instanceOf
instanceof 运算符:判断该对象是否是某一个类/接口的实例,在开发中运用不是很多
语法格式:
boolean b = 对象A instanceof 类B; //判断 A对象是否是 B类的实例?如果是,返回true
Animal a = new Dog();
System.out.println(a instanceof Animal); //true
System.out.println(a instanceof Dog); //true
System.out.println(a instanceof Cat); //false