面向对象的主要特征
一、抽象
- 面向对象设计首先要做的就是抽象
类抽象,就是发现类并定义类的属性和方法
- 抽象的步骤如下
- 发现名词。通过阅读需求,发现需求中的关键词,比如有类型、价格等。
- 确定类和属性。比如,漂亮、高挑、香气依附于美女这个名词,帅气、英俊、霸道这些词依附于帅哥这个名词。(注意!不是所有依附于类的名词都需要抽象成属性)
- 确定方法 。通过分析需求中的动词,发现显示车辆车辆信息是轿车和卡车的行为,所以可以将这个行为抽象为类的方法。(同样!不是所有依附于类名词的动词都需要抽象成类的方法,只有需要参与业务处理的动词才能确定成方法)
二、封装
封装就是将抽象得到的属性和方法结合起来,形成一个有机的整体——类。
Alt + Insert 快捷键生成getter和setter方法
- 类里面的一些属性,需要隐藏起来,不希望直接对外公开,但同时也提供了外部访问方法(setter和getter方法),用于访问这些需要访问的属性。比如说,我们在开汽车的时候,只用去关注如何开车,我们并不在意车子是如何实现的,这就是封装。
- 如何去实现类的封装呢?
- 修改属性的可见性,在属性的前面添加修饰符 (private)
- 对每个值属性提供对外的公共方法访问,如创建 getter/setter(取值和赋值)方法,用于对私有属性的访问
- 在 getter/setter 方法里加入属性的控制语句,例如我们可以加一个判断语句,对于非法输入给予否定。
public class People {
//属性(成员变量)有什么,前面添加了访问修饰符private
//变成了私有属性,必须通过方法调用
private double height; //身高
//属性已经封装好了,如果用户需要调用属性
//必须用getter和setter方法进行调用
//getter和setter方法需要程序员自己定义
public double getHeight(){
//getter 方法命名是get关键字加属性名(属性名首字母大写)
//getter 方法一般是为了得到属性值
return height;
}
//同理设置我们的setter方法
//setter 方法命名是set关键字加属性名(首字母大写)
//setter 方法一般是给属性值赋值,所以有一个参数
public void setHeight(double newHeight){
height = newHeight;
}
}
现在 main 函数里的对象,不能再直接调用属性了,只能通过 getter 和 setter 方法进行调用。
public class NewObject {
public static void main(String[] args) {
People LiLei = new People(); //创建了一个People对象LiLei
//利用setter方法为属性赋值
LiLei.setHeight(170.0);
//利用getter方法取属性值
System.out.println("LiLei的身高是"+LiLei.getHeight());
}
}
其运行结果如下:
LiLei的身高是170.0
这样做有什么好处?
- 只能通过规定的方法访问数据。
- 隐藏类的实例细节,方便修改和实现。
三、继承
Java继承是使用已存在的类的定义作为基础建立新类的技术,
- 新类的定义可以增加新的属性或新的方法,也可以用已存在的类的属性和方法。这种技术能够非常容易地复用以前的代码,大大缩短开发周期,降低开发费用。
- 继承可以看成是类与类之间的衍生关系。比如狗类是动物类,牧羊犬类又是狗类。于是我们可以说狗类继承了动物类,而牧羊犬类就继承了狗类。于是狗类就是动物类的子类(或派生类),动物类就是狗类的父类(或基类)。
所以继承需要符合的关系是:is-a,父类更通用,子类更具体。
语法:
class 子类 extends 父类
例如我们定义了一个 Animal 类,再创建一个 Dog 类,我们需要它继承 Animal 类。
class Dog extends Animal {
...
}
为什么需要继承?
如果有两个类相似,那么它们会有许多重复的代码,导致后果就是代码量大且臃肿,后期的维护性不高。通过继承就可以解决这个问题,将两段代码中相同的部分提取出来组成一个父类,实现代码的复用。
继承的特点
- 子类可以继承父类中访问权限修饰符为public和protected的属性和方法。
- 子类可以继承符类中用默认访问权限修饰(default)的属性和方法,但子类和父类必须在同一个包中。
- 子类无法继承父类中访问权限修饰符为private的属性和方法
- 子类无法继承父类的构造方法
- 子类可以重写实现父类的方法
- Java中的继承为单继承,一个类只有一个父类
提示:Java实现多继承的一个方法是implements(实现)接口,但接口不能有非静态的属性,这一点请注意。
四、多态
多态就是同一种行为,作用在不同的对象上,有不同的结果。 比如说,动物叫是一种行为,作用在猫身上是喵喵喵~~~,作用在狗身上就是汪汪汪~~~;作用在鸽子身上就是咕咕咕~~~;
- 写法:左侧父类,右侧子类
Animal animal = new Cat();
animal.bark;
多态的实现条件:
Java 实现多态有三个必要条件:继承、重写和向上转型(即父类引用指向子类对象)。
只有满足上述三个条件,才能够在同一个继承结构中使用统一的逻辑实现代码处理不同的对象,从而达到执行不同的行为。
多态的实现方式
Java 中多态的实现方式:继承父类进行方法重写,抽象类和抽象方法,接口实现。
要理解多态必须要明白什么是"向上转型",比如,一段代码如下,Dog 类是 Animal 类的子类:
Animal a = new Animal(); //a是父类的引用指向的是本类的对象
Animal b = new Dog(); //b是父类的引用指向的是子类的对象
在这里,可以认为由于 Dog 继承于 Animal,所以 Dog 可以自动向上转型为 Animal,所以 b 是可以指向 Dog 实例对象的。
注:不能使用一个子类的引用去指向父类的对象,因为子类对象中可能会含有父类对象中所没有的属性和方法。
新建一个Test.java,例如:
class Animal {
//父类方法
public void bark() {
System.out.println("动物叫!");
}
}
class Dog extends Animal {
//子类重写父类的bark方法
public void bark() {
System.out.println("汪、汪、汪!");
}
//子类自己的方法
public void dogType() {
System.out.println("这是什么品种的狗?");
}
}
public class Test {
public static void main(String[] args) {
Animal a = new Animal();
Animal b = new Dog();
Dog d = new Dog();
a.bark();
b.bark();
//b.dogType();
//b.dogType()编译不通过
d.bark();
d.dogType();
}
}
编译运行:
动物叫!
汪、汪、汪!
汪、汪、汪!
这是什么品种的狗?
在这里,由于 b 是父类的引用,指向子类的对象,因此不能获取子类的方法(dogType() 方法), 同时当调用 bark() 方法时,由于子类重写了父类的 bark() 方法,所以调用子类中的 bark() 方法。
因此,向上转型,在运行时,会遗忘子类对象中与父类对象中不同的方法,也会覆盖与父类中相同的方法——重写(方法名,参数都相同)。