面向对象三大特征
(封装、继承、多态)
一、封装
1.1存在的问题
- 在对象的外部,为对象的属性赋值,可能存在非法数据的录入。
- 就目前技术,无法对属性的赋值加以控制
- 使用者对类内部定义的属性直接操作会导致数据的错误、混乱或安全性问题
1.2什么是封装
概念:尽可能隐藏对象的内部实现细节,控制对象的修改及访问的权限。
1.3如何实现封装
-
将属性使用访问修饰符
private
进行修饰,被private
修饰后,属性仅在本类可见。 -
提供公共的方法(被
public
修饰)getXXX
和setXXX
实现对该属性的操作。 -
在公共的访问方法内部,添加逻辑判断,进而过滤掉非法数据,以保证数据安全(可选的)
私有化属性 对外提供公共方法(get set)从而隐藏对象内部细节
package com;
public class Person {
private String name;// 姓名
private int age;// 年龄
private String gender;// 性别
private String id;// 身份证号
private String tel;// 电话
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
/*
在公共的访问方法内部,添加逻辑判断,
进而过滤掉非法数据,以保证数据安全。
*/
public void setAge(int age) {
if(age > 0 && age < 130) {
this.age = age;
} else {
this.age = 20;
}
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getTel() {
return tel;
}
public void setTel(String tel) {
this.tel = tel;
}
}
测试:
public class Test {
public static void main(String[] args) {
Person person = new Person();
person.setAge(1000);
System.out.println(person.getAge());
}
}
注意:
- 不可以直接访问属性,仅可访问公共方法;
get/set
方法是外界访问对象私有属性的唯一通道,方法内部可对数据进行检验和过滤。
1.5JavaBean设计
- 类:实体类 JavaBean
- JavaBean分为两种:
- 数据承载Bean
- 业务逻辑Bean
- 数据承载Bean
- 私有化属性
- 对外提供公共的get、set方法
- 提供有参无参构造
package com.JavaBean;
public class User {
private String userName;
private String userPassword;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getUserPassword() {
return userPassword;
}
public void setUserPassword(String userPassword) {
this.userPassword = userPassword;
}
public User() {
}
public User(String userName, String userPassword) {
super();
this.userName = userName;
this.userPassword = userPassword;
}
}
二、继承
2.1什么是继承
2.1.1生活中的继承
-
生活中的
继承
是施方一种赠与,受方的一种获得。 -
将一方所拥有的东西给予另一方。
-
施方和受方往往二者具有继承关系(直系亲属、亲属)。
2.1.2程序中的继承
-
程序中的继承,是
类与类
之间特征(属性)和行为(方法)的一种赠与或获得
。 -
两个类之间的继承关系,必须满足
is a
的关系。-
Dog is an Animal;
-
Cat is an Animal。
-
-
根据上面的关系我们可以设计三个类,Dog、Cat、Animal
- Dog继承Animal;
- Cat继承Animal。
-
被继承的类称为父类,上述案例中的Animal。
-
继承的类称为子类,上述案例中的Dog和Cat。
2.1.3、父类的选择
-
现实生活中,很多类别之间都存在着继承关系,都满足
is a
的关系。 -
狗是一种动物、狗是一种生物。
-
多个类别都可作为狗的父类,需要从中选择出最适合的父类。
//生物
属性:
品种、年龄、性别
方法:
呼吸
//动物
属性:
品种、年龄、性别
方法:
呼吸、吃、睡
- 功能越精细,重合点越多,越接近直接父类。
- 功能越粗略,重合点越少,越接近
Object
类。
2.1.4父类的抽象
- 在实际开发中,可根据程序需要使用到的多个具体类,进行共性提取,进而定义父类。
//狗
属性:
品种、年龄、性别、毛色
方法:
吃、睡、跑
//鱼
属性:
品种、年龄、性别
方法:
吃、睡、游
//鸟
属性:
品种、年龄、性别、毛色
方法:
吃、睡、飞
//蛇
属性:
品种、年龄、性别
方法:
吃、睡、爬
//动物
属性:
品种、年龄、性别
方法:
吃、睡
在一组相同或类似的类中,抽取出共性的特征(属性)和行为(方法),定义在父类中,实现重用。
//父类
public class Animal {
//公共属性
String name;
int age;
String type;
//公共方法
public void sleep(){
System.out.println("sleep");
}
public void eat(){
System.out.println("eat");
}
}
//子类
public class Cat extends Animal {
//独有属性
String color;
//独有方法
public void tmq(){
System.out.println("吐毛球");
}
}
//子类
public class Dog extends Animal{
//独有属性
String color;
//独有方法
public void lookHome(){
System.out.println("看家");
}
public void swim(){
System.out.println("游泳");
}
}
package com.jicheng;
public class Test1 {
public static void main(String[] args) {
Dog dog = new Dog();
//使用父类继承的属性
dog.name="旺财";
dog.age=5;
dog.type="阿拉斯加";
// 使用子类中独有的属性
dog.color="yello";
System.out.println("name"+dog.name+"\tage"+dog.age+"\ttype"+dog.type+"\tcolor"+dog.color);
//使用父类继承的方法
dog.eat();
dog.sleep();
//使用子类独有的方法
dog.swim();
dog.lookHome();
Cat cat = new Cat();
cat.name="局座";
cat.color="orange";
System.out.println(cat.name+cat.color);
cat.sleep();
cat.tmq();
}
}
2.2继承
-
继承的概念:
类与类
之间特征和行为的一种赠与或获得
。 -
继承的作用:减少代码冗余,提高代码复用
-
语法:
class 子类名 extends 父类 { //定义子类时,指定其父类 //属性 //构造方法 //方法 }
2.3继承的特点
- 子类继承父类可以继承父类的属性及方法
- 子类也可以拥有自己独立的属性及方法
- Java为单继承,一个类只能有一个直接父类,但一个父类可以有多个子类
- 多重继承即一个父类还可以继承另一个类,java中最大的object(如果一个没有显示的写出extends 父类 默认继承Object)多级继承,属性和方法逐级叠加。
3.4不可继承
- 构造方法不可继承
- private 修饰的属性方法不能继承
- 父类中使用的默认修饰符的属性和方法在不同包的子类中不能继承
3.5访问修饰符
- public:公共的
- private:私有的
- default:默认的 缺省的 友好的
- protected:受保护的
本类 | 同包不同类 | 不同包不同类 | 其他 | |
---|---|---|---|---|
private | ✔ | ✘ | ✘ | ✘ |
default | ✔ | ✔ | ✘ | ✘ |
protected | ✔ | ✔ | ✘ | ✘ |
public | ✔ | ✔ | ✔ | ✔ |
✔:表示可以访问;✘:表示不可以访问;
3.6方法的重写
- 子类中有父类相同名字的方法称之为方法重写
- 重写特点:
- 方法名称、参数列表、返回值类型必须与父类相同
- 访问修饰符可与父类相同或是比父类更宽泛
- 作用:
- 当父类提供的方法无法满足子类需求时
@Override
建议在重写方法上加 书写 否则会报错
- 注意:不能被继承的方法也不能被重写
- 重写特点:
public class Dog extends Animal {
private String color;//毛色
//跑
public void run() {
System.out.println("run...");
}
//子类重写父类中的方法,方法名称、参数列表、返回值类型必须与父类相同。
@Override
public void eat() {
System.out.println("狗吃骨头...");
}
}
public class Test {
public static void main(String[] args) {
Dog dog = new Dog();
dog.eat(); //调用子类中重写的方法。
}
}
3.7super
关键字
-
含义:直接父类对象
-
作用:可以调用父类的属性 方法 构造方法
- 调用父类属性和方法的时候
- 当子类和父类有相同名字方法时,this.属性 this.方法名() 调用子类属性和方法
- 当子类和父类有相同名字方法时,super.属性 super.方法名() 调用父类中方属性和方法
- 如果没有重名属性或者方法,那么this和super都是表示调用父类方法(子类继承而来)
- 调用父类构造方法时:
- 注意:创建子类对象前,必须创建父类对象
- super()调用的是父类的无参构造,可以省略但是不建议(建议:保留父类无参构造)
- super 调用父类有参构造时,要放到构造方法的第一行 super和this不能同时出现
- 注意:创建子类对象前,必须创建父类对象
public class Dog extends Animal { private String color;//毛色 //跑 public void run() { System.out.println("run..."); } //子类重写父类中的方法,方法名称、参数列表、返回值类型必须与父类相同。 @Override public void eat() { super.eat(); System.out.println("狗吃骨头..."); } }
- 调用父类属性和方法的时候
public class A {
int value = 10;
}
public class B extends A {
int value = 20; //子类属性遮蔽父类属性
public void print() {
int value = 30;
System.out.println(value); //访问局部变量
System.out.println(this.value); //访问本类的属性
System.out.println(super.value); //访问父类的属性
}
}
public class MyTest2 {
public static void main(String[] args) {
B b = new B();
b.print();//分别输出30、20、10
}
}
三、多态
3.1什么是多态
- 概念:多态指的是多种形态,父类引用指向子类对象,从而产生多种形态
//父类
public class Pet {
//健康值
int health;
public void toHealth(){
System.out.println("回复健康");
}
}
//狗 子类
public class Dog extends Pet{
@Override
public void toHealth(){
System.out.println("啃骨头");
}
}
//猫 子类
public class Cat extends Pet{
@Override
public void toHealth(){
System.out.println("吃鱼");
}
}
public class Owner {
/* public void seedDoctor(Dog dog){
if (dog.health<80){
dog.toHealth();
dog.health=90;
}
System.out.println("看完病健康值为:"+dog.health);
}
public void seedDoctor(Cat cat){
if (cat.health<80){
cat.toHealth();
cat.health=90;
}
System.out.println("看完病健康值为:"+cat.health);
}*/
public void seedDoctor(Pet pet){
if (pet.health<80){
pet.toHealth();
pet.health=90;
}
System.out.println("看完病健康值为:"+pet.health);
}
}
public class Test {
public static void main(String[] args) {
//创建主人对象
Owner owner = new Owner();
//创建宠物子类对象
Dog dog = new Dog();
dog.health-=60;
owner.seedDoctor(dog);
Pet cat = new Cat();
cat.health=20;
owner.seedDoctor(cat);
}
}
注意:
- 父类引用仅可调用父类所声明的属性和方法,不可调用子类独有的属性和方法;
- 子类对象赋值给父类引用,如果运行被重写的方法,实际运行的是子类中的方法。
特点:编译看左边运行看右边
3.2向上转型
-
子类引用的对象转换为父类类型称为向上转型。
-
程序运行的状态
-
编译,看左边,左侧是什么类型,就能调用什么样的方法;
-
运行,看右边,右侧是什么类型,运行时就调用什么样的方法
-
3.3向下转型
-
父类引用的对象转换为子类类型称为向下转型。
-
注意:只有转换回子类真实类型,才可调用子类独有的属性和方法。
-
向下转型时,如果父类引用中的子类对象类型和目标类型不匹配,则会发生类型转换异常(
ClassCastException
)。 -
通常向下转型前,应判断引用中的对象真实类型,保证类型转换的正确性。
- 语法:
父类引用 instanceof 类型
- 语法:
if(a instanceof Dog) {
Dog d = (Dog)a;
d.eat();
}
3.4多态的两种应用场景
- 使用父类作为方法形参,实现多态
- 调用方法是,可传递的实参类型包括:本类型对象+其所有的子类对象
- 使用父类作为方法返回值,实现多态
- 调用方法后,可得到的结果类型包括:本类型对象+其所有子类对象
3.5多态的作用
- 屏蔽子类间的差异
- 灵活、耦合度低
3.6多态产生条件:
- 必须有继承关系(满足 is a)
- 必须要有重写
- 父类引用指向子类
型,就能调用什么样的方法;
- 运行,看右边,右侧是什么类型,运行时就调用什么样的方法
3.3向下转型
-
父类引用的对象转换为子类类型称为向下转型。
-
注意:只有转换回子类真实类型,才可调用子类独有的属性和方法。
-
向下转型时,如果父类引用中的子类对象类型和目标类型不匹配,则会发生类型转换异常(
ClassCastException
)。 -
通常向下转型前,应判断引用中的对象真实类型,保证类型转换的正确性。
- 语法:
父类引用 instanceof 类型
- 语法:
if(a instanceof Dog) {
Dog d = (Dog)a;
d.eat();
}
3.4多态的两种应用场景
- 使用父类作为方法形参,实现多态
- 调用方法是,可传递的实参类型包括:本类型对象+其所有的子类对象
- 使用父类作为方法返回值,实现多态
- 调用方法后,可得到的结果类型包括:本类型对象+其所有子类对象
3.5多态的作用
- 屏蔽子类间的差异
- 灵活、耦合度低
3.6多态产生条件:
- 必须有继承关系(满足 is a)
- 必须要有重写
- 父类引用指向子类