一、面向对象思想总结
1. 什么是面向对象?
面向对象思想,是基于面向过程而言的:
1)面向过程:实现一个功能的时候,每一个具体的步骤都需要亲力亲为,详细的处理每一个细节。
2)面向对象:实现一个功能的时候,不关心中间具体的步骤,找一个已经具有该功能的人,来帮我做事情。
2. 举例(洗衣服)说明:
1)面向过程:脱衣服—>拿一个盆—>导入洗衣液—>加水—>浸泡几分钟—>开始搓洗—>清洗泡沫—>拧干—>晾晒
2)面向对象:脱衣服—>打开全自动洗衣机(对象)—>放进衣服—>按下按钮—>取出晾晒
3. 特点:
1)是一种更加符合人们思考习惯的思想。
2)可以将复杂的事情简单化。
3)以前的过程中的执行者,变成了指挥者。
4. 何为对象?何为类?
面向对象,对象是谁?从哪里来?
1)对象:是一类事物的具体提现。是类的一种实例化,同时具备该类事物的属性和行为。比如:洗衣机、手机、小狗。。。
2)什么又是类?
类,是一组相关属性和行为的集合,可以理解为一类事物的模板。
属性:状态信息(名字、年龄。。。)
行为:能够做什么(吃、睡、玩。。。)
在Java中对于类的属性描述就是用成员变量,行为描述就是成员方法。
3)类与对象的关系:
5. 简单理解:
在面对一个问题时,首先应该分析这个问题可以由哪些对象和类来实现,
然后再分析这些对象和类应该具有哪些属性和方法,最后分析总结出:将合适的方法放在合适的类当中!
二、面向对象三大特征
1.封装性
1)概述:
封装就是把一个对象的属性和行为的代码封装在一个类中,并对成员变量进行私有化,将属性和实现细节隐藏起来,仅对外提供公共访问方式。
封装可以理解是一个保护屏障,防止该类的代码被外部类定义的代码随机访问。
2)优点:
- 减少耦合性
- 隐藏信息,提高安全性
- 提高复用性
3)实现方法:
一般使用private关键字来修饰成员变量,只能在本类访问,其他类都不能直接访问。
但对每个属性可以提供对外的公共方法,用于对私有属性进行间接访问。
3)代码举例:
// 定义一个学生类
class Student{
//成员变量私有化
private String name ;
private int age ;
//给name赋值
public void setName(String n){
name = n ;
}
//获取name值
public String getName(){
return name ;
}
//给年龄赋值
public void setAge(int a){
age = a ;
}
//获取年龄
public int getAge(){
return age ;
}
}
2. 继承性
1)概述:
将多个类中的共性内容抽取出来,放在一个独立的类中, 让这多个类和当前该类产生一种关系"继承"。
这个公共的类就是父类,也称为超类或者基类,其他的类就是子类,也称派生类。
子类继承父类,使得子类对象具有与父类相同的属性、相同的行为。子类可以直接访问父类中的非私有的属性和行为
格式为:
class 父类 {
...
}
class 子类 extends 父类 {
...
}
2)特点:
- 只能单继承,即:一个类只能有一个父类。
- 可以多层继承,即:父类还可以有父类。
- 一个子类的父类是唯一的,但是一个父类可以拥有多个子类。
3)优点:
- 提高了代码的复用性
- 提高了代码的维护性
- 继承关系是实现多态的前提
4)缺点:
当一个类存在多个子类的时候,如果该类发生变化,那么这些子类也会跟着一起变化,造成类与类之间的“强耦合”!
5)成员变量的访问特点:
1 子类父类中成员变量不重名,可正常访问
class Fu {
int num1 = 10;
}
class Zi extends Fu {
int num2 = 20;
public void show(){
System.out.println("Fu = " + num1);// 父类成员变量
System.out.println("Zi = " + num2);// 子类成员变量
}
}
class ZiTest {
public static void main(String[] args){
Zi zi = new Zi();
zi.show();
}
}
/* 显示结果:
Fu = 10
Zi = 20
*/
2 子类父类的成员变量重名时,在子类中需要访问父类中非私有成员变量时,需要使用super关键字。
class Fu {
int num = 10;
}
class Zi extends Fu {
int num = 20;
public void show(){
int num = 30;
System.out.println("Zi1 = " + num); // 子类局部变量
System.out.println("Zi2 = " + this.num);// 子类成员变量
System.out.println("Fu = " + super.num);// 父类成员变量
}
}
class ZiTest {
public static void main(String[] args){
Zi zi = new Zi();
zi.show();
}
}
/* 显示结果:
Zi1 = 30
Zi2 = 20
Fu = 10
*/
6)成员方法的访问特点:
1 子类父类中成员方法不重名
子类对象调用方法时,首先,在子类中查找有没有对应的方法,若子类中存在就会执行子类中的方法;
若子类中不存在就会向上在父类中查找有没有对应的方法,若父类中存在就会执行子类中的方法;
若不存在就会编译报错!
class Fu {
public void show(){
System.out.println("Fu类中的show方法执行");
}
}
class Zi extends Fu {
int num = 20;
public void show2(){
System.out.println("Zi类中的show2方法执行");
}
}
class ZiTest {
public static void main(String[] args){
Zi zi = new Zi();
// 子类中没有show方法,但是可以向上找到父类方法去执行
zi.show();
zi.show2();
}
}
/* 显示结果:
Fu类中的show方法执行
Zi类中的show2方法执行
*/
2 子类父类中成员方法重名,此时是一种特殊的访问,叫做方法重写 (Override)。
方法重写:子类中出现与父类一模一样的方法时(方法名和参数列表都相同),会出现覆盖效果,称为重写或者复写。
方法重写的应用举例
// 手机父类
class Phone {
public void sendMessage(){
System.out.println("发短信");
}
public void call(){
System.out.println("打电话");
}
public void showNum(){
System.out.println("来电显示号码");
}
}
// 新手机子类
class NewPhone extends Phone {
//重写父类的来电显示号码功能,并增加自己的显示姓名和图片功能
public void showNum(){
super.showNum(); //调用父类已经存在的功能使用
//增加自己特有显示姓名和图片功能
System.out.println("显示来电姓名");
System.out.println("显示头像");
}
}
public class PhoneTest {
public static void main(String[] args) {
NewPhone np = new NewPhone();
// 调用父类继承而来的方法
np.sendMessage();
np.call();
// 调用子类重写的方法
np.showNum();
}
}
方法覆盖重写:
子类方法返回值的范围必须【小于等于】父类方法返回值范围!
8)构造方法的访问特点:
构造方法的作用是始化成员变量。
所以子类的初始化过程中,必须先执行父类的初始化动作。子类的构造方法中默认有一个super(),表示调用父类的构造方法,父类成员变量初始化后,才可以给子类使用。
class Fu {
private int n;
public Fu(){
System.out.println("Fu()");
}
}
class Zi extends Fu {
public Zi(){
// super(),调用父类构造方法
System.out.println("Zi()");
}
}
class ExtendsTest{
public static void main (String args[]){
Zi zi = new Zi();
}
}
/* 显示结果:
Fu()
Zi()
*/
3. 多态性
1)什么是多态:
多态指的是一类事物有多种形态,体现代码中,就是在堆内存中的形态不一样。
2)多态的前提:
- 必须存在继承关系
- 子类必须重写父类的方法
- 父类引用指向子类对象
3)格式:
父类名称 对象名 = new 子类名称();
或
接口名称 对象名 = new 实现类名称();
4)优点:
- 降低耦合
- 灵活性
- 可维护性(继承保证)
- 可扩展性(多态保证)
5)缺点:
不能够访问子类的特有功能!
6)成员变量特点:
编译看(等号)左,运行看(等号)左!
class Fu {
int num = 10;
}
class Zi extends Fu {
int num = 20;
}
class MultiTest{
public static void main (String args[]){
// 使用多态写法,父类引用指向子类对象
Fu fu = new Zi();
System.out.println(fu.num);
// 编译看左:父类里有num,编译通过
// 运行看左:执行父类num变量
}
}
// 输出结果:10
7)成员方法特点:
编译看(等号)左,运行看(等号)右!
class Fu {
int num = 10;
public void show(){
System.out.println("父类方法执行");
}
}
class Zi extends Fu {
int num = 20;
public void show(){
System.out.println("子类方法执行");
}
}
class ExtendsTest{
public static void main (String args[]){
Fu fu = new Zi();
fu.show();
// 编译看左:父类里有show(),编译通过
// 运行看右:执行子类show()方法
}
}
// 输出结果:子类方法执行
8)构造方法特点:
都是对对象中的数据进行初始化,仍需要遵循分层初始化!
9)对象的转型:
代码演示:
class Animal {
public void eat(){
System.out.println();
}
}
class Cat extends Animal {
public void eat(){
System.out.println("猫吃鱼");
}
public void catchMouse(){
System.out.println("猫抓老鼠");
}
}
class Dog extends Animal{
public void eat(){
System.out.println("狗吃骨头");
}
public void watchDoor(){
System.out.println("狗看门");
}
}
class ExtendsTest{
public static void main (String args[]){
// 向上转型
Animal animal = new Cat();
animal.eat();// 猫吃鱼
// 向下转型,进行“还原”动作
Cat cat = (Cat) animal;
cat.catchMouse();// 猫抓老鼠
// 错误的向下转型:本来是猫,非要“还原”成狗
//Dog dog = (Dog) animal;// 编译不会报错,运行会出现异常:ClassCastException
}
}
10)instanceof关键字:
那么问题来了,我怎么知道你之前是什么,new的对象太多,记不得了,这时,我该怎么快速的正确完成“还原”嘞?
instanceof 来解决!
instanceof :表示该引用是否能表示该类实例,也就是判断前面的对象能不能当做后面类型的实例,返回boolean类型。
格式:
对象 instanceof 类名称
class Animal {
public void eat(){
System.out.println();
}
}
class Cat extends Animal {
public void eat(){
System.out.println("猫吃鱼");
}
public void catchMouse(){
System.out.println("猫抓老鼠");
}
}
class Dog extends Animal{
public void eat(){
System.out.println("狗吃骨头");
}
public void watchDoor(){
System.out.println("狗看门");
}
}
class Test{
public static void main (String args[]){
Animal animal = new Cat();
// 判断父类引用animal本来是不是Dog
if (animal instanceof Cat){
Cat cat = (Cat) animal;
cat.catchMouse();
}
// 判断父类引用animal本来是不是Cat
if (animal instanceof Dog){
Dog dog = (Dog) animal;
dog.watchDoor();
}
}
}
// 运行结果:猫抓老鼠