黑马程序员——Java基础_面向对象之封装、继承和多态

时间:2023-02-13 23:49:42

 ----------------------Android培训Java培训、期待与您交流! ----------------------

1 何为面向对象

     面向对象(Object-Oriented,简称OO)就是一种常见的程序结构设计方法。

     面向对象思想的基础是将相关的数据和方法放在一起,组合成一种新的复合数据类型,然后使用新创建的复合数据类型作为项目的基础。

     面向对象是一个很抽象的概念,它相对面向过程而言。过程与对象都是一种解决问题的思想。

               面向过程:强调的是功能行为,一种过程,先干啥,再干啥;

               面向对象:将功能封装到对象里,强调的是具备某功能的对象;

     按照面向对象的思想,可以把任何的东西看做对象。

-----------------------------分割线-----------------------------

2  面向对象的三大特征之一:封装

     封装是干嘛的?

               把对象的状态和行为看成一个统一的整体,将二者存放在一个独立的模块中()

               "信息隐藏", 把不需要让外界知道的信息隐藏起来,尽可能隐藏对象功能实现细节,字段。

               封装机制在程序中的体现是:把描述对象的状态用字段表示,描述对象的行为用方法表示,把字段和方法定义在一个类中,并保证外界不能任意更改其内部的字段值,也不许任意调动其内部的功能方法。

-----------------------------分割线-----------------------------

3 面向对象的三大特征之一:继承

     继承: 提高代码的复用性;让类与类之间产生了关系,有了这关系,才有了多态的特性。

只有类与类之间有所属关系才可以继承。两者之间都应有同意方法或同一属性时可以继承。

          当子类继承父类,沿袭了父类的功能到子类中。但是子类虽具备该功能,但是功能的内容却和父类不一致,这时,没有必要定义新功能,而是使用覆盖函数,保留父类的功能定义,并重写功能内容。

-----------------------------分割线-----------------------------

4 函数的一个特性:覆盖

          子类覆盖父类,必须保证子类权限大于等于父类权限,才可以覆盖,否则编译失败

          静态只能覆盖静态

 重载与重写的区别:

          重载(Overload):只看同名函数的参数列表

          覆盖(Override):子父类方法要一模一样

this:相当于是本类中对象

super:相当于是本类的父类对象

子父类中的构造函数

子类的实例化过程如下:

          在对子类对象进行初始化时,父类的构造函数也会执行。那是因为子类的构造函数默认第一行有一条隐式的语句super()

          super():会访问父类中空参数的构造函数,而且子类中所有的构造函数默认第一行都是super() 

          当父类中没有空参数的构造函数时,子类必须手动通过super或者this方法形式来指定要访问的构造函数。

          也就是说,如果需要访问父类中指定的构造函数,可以通过手动定义super语句的方式来指定。

PSsuper()语句只能定义在子类构造函数第一行,this()方法和super()只能择其一。

注意:java中只支持单继承,不支持多继承,只能继承一个类。

java中支持多层继承。如父类的子类可以再被继承。

具体调用时,一般是创建子类的对象。为什么?

          原因一:父类可能不可以创建对象;原因二:子类能具有更多的功能。

简单来说:查阅父类的功能,创建子类对象使用功能。

注意:java继承中,访问权限:父类中被private修饰的不能被子类继承,子类在继承时访问权限不能低于父类,比如说父类中是protect修饰的话,子类应当用protect、public

-----------------------------分割线-----------------------------

5 final关键字

final:最终。作为一个修饰符,

          可以修饰类,函数,变量

          final修饰的类不可以被继承,为了避免被继承,被子类复写功能

          final修饰的方法不可以被复写

          final修饰的变量是一个常量只能赋值一次,既可以修饰成员变量,又可以修饰局部变量当在描述事物时,一些数据的出现值是固定的,那么这时为了增强阅读性,都给这些值七个名字,方便于阅读。(PS常量的书写规范:所有字母都大写,如果由多个单词组成,单词间通过下划线_连接)

          ⑤ 内部类定义在类中的局部位置上时,只能访问该局部被final修饰的局部变量。

第5点的 原因:

          编译程序实现上有困难,因为内部类对象的生命周期会超过局部变量的生命期。

          局部变量的生命期:当该方法被调用时,该方法中的局部变量在栈中被创建,当方法调用结束时,退栈,这些局部变量全部死亡。

          内部类对象生命期,与其它类一样,当创建一个局部内部类对象后,只有当没有其它人再引用它时,它才能死亡。所以完全可能一个方法已调用结束(局部变量已死亡),但该局部类的对象仍然活着。即:局部类的对象生命期会超过局部变量。

          定义为final后,编译程序的实现方法:将所有的局部内部类对象要访问的final型局部变量,都拷贝成为该内部类对象中的一个数据成员。这样,即使栈中局部变量(含final)已死亡,但由于它是final,其值永不变,因而局部内部类对象在变量死亡后,照样可以访问final型局部变量。

PS:能够修饰class的修饰符只有publicfinal。

-----------------------------分割线-----------------------------

6 抽象类

抽象类的适用:当多个类中出现了相同的功能,但是功能的主体不一样,这时可以进行向上抽取,此时只抽取功能定义,而不抽取功能主体。

抽象类特点:

          ①.抽象方法一定在抽象类中

          ② 抽象类中可以拥有非抽象方法

          ③ 抽象方法和抽象类都要被关键字abstract修饰

          ④ 抽象类不可以用new对象创建,因为调用抽象类是没有意义的 (PS:匿名内部类的特例)

          ⑤ 抽象类中的抽象方法若要被使用,必须由子类复写完所有的抽象方法后,建立子类对象调用;若子类只覆盖了部分抽象方法,那么该子类还是一个抽象类。

抽象类与一般类的区别:

          没有太大的不同,抽象类主要是在事物的描述时,该事物出现了一些看不懂的东西,这些不能确定的部分(功能),需要明确出现,但是无法定义主体。通过抽象方法来表示。

          抽象类比一般类多了个抽象方法,在类中可以定义抽象方法;抽象类不能实例化

-----------------------------分割线-----------------------------

7 接口

接口:一个特殊的抽象类,当抽象类中的方法都是抽象的,那么该类就可以用接口来表示

接口中成员都是public的。

接口的格式:

          常量:public static final

          方法:public abstract

interface Inter

{

          public static final int NUM=3;

          public abstract void show();

}

          格式中publicabstractfinalstatic修饰符没写,它也会自动补上,但是为了阅读性,建议写全。

          接口是不可以创建对象的,因为有抽象方法,需要被子类实现,子类对接口中的抽象方法全都覆盖后,子类才可以实例化,否则子类是一个抽象类。

          接口可以被类多实现。只有在接口与接口之间存在多继承。

抽象类和接口异同:
相同:
          ① 都可以在内部定义抽象方法。
          ② 通常都在顶层。
          ③ 都不可以实例化,都需要子类来实现。
不同点:
          ① 抽象类中可以定义抽象方法和非抽象方法,而接口中只能定义抽象方法。(在Java8中接口也可以定义非抽象方法了)
          ② 接口的出现可以多实现。抽象类只能单继承。也就是说:接口的出现避免了单继承的局限性。
          ③ 继承和实现的关系不一致。继承:is a,实现:like a 。

-----------------------------分割线-----------------------------

8 面向对象之多态

多态指同一个实体同时具有多种形式。比如说:养个动物,可以养一只狗,也可以养只猫,养条鱼也行,它们都是动物的表示形态。

实现多态的机制

     父类的引用变量可以指向子类的实例对象,而程序调用的方法在运行期才动态绑定,就是引用变量所指向的真正实例对象的方法,也就是内存里正在运行的那个对象的方法,而不是引用变量的类型中定义的方法。

多态的作用:

     把不同的子类对象都当作父类来看,可以屏蔽不同子类对象之间的差异,写出通用的代码,做出通用的编程,以适应需求的不断变化。

多态的前提:

     必须是类与类之间有关系。要么继承,要么实现;通常还有一个前提:存在覆盖。

多态的好处:

     多态的出现大大的提高程序的扩展性。

多态的弊端:

     提高了扩展性,但是只能使用父类的引用访问父类中的成员。

-----------------------------分割线-----------------------------

9 多态中引用变量类型转换

向上转型(子类→父类):(自动完成)

     父类名称 父类对象 子类实例 ;

向下转型(父类→子类):(强制完成)

     子类名称 子类对象 (子类名称)父类实例 ;

切记:多态自始至终都是子类对象在做着变化。

论坛上有个很有意思的比喻,形象的以武侠来形容了多态和继承。特摘到日记历来:

     继承:你有一个武林高手师父,人称“玉罗刹”,然后你从你师傅那里学到了他的全部武功。

     扩展:后来你又自创了你自己的武功。

     多态:你现在可以用你师傅的名号“玉罗刹”去行侠仗义,你师父会的你都会。别人可以认为你就是你师父。但是,你不能使用你自己的武功,因为你还带着“玉罗刹”的面具。如果你想要使用自己的武功,就必须先拿下面具(使用强制转换,将父类对象引用转换成子类对象类型)。

java不允许的:你师父不会使用你的武功,所以你师父不能伪装成你(父类对象不能强转成子类对象类型) 

     然而,你的师傅可以收很多的徒弟,每个徒弟都可以修炼自己的武功,所以到最后,你师父能干的事,他的徒弟们都可以取代他的位置

下面是代码演示:

class A//玉罗刹
{
public void Jiuyinzhenjing()//九阴真经
{
System.out.println("this is A Say");
}
}

class B extends A//你继承了你师父的全部武学
{
public void Rulaishenzhang()//如来神掌
{
System.out.println("this is B Sing");
}
}
public class Duotai
{
public static void main(String[] args)
{
A shifu = new B();
shifu.Jiuyinzhenjing();//使用“玉罗刹”的名号使用九阴真经
B tudi= new B();
tudi.Jiuyinzhenjing();//使用师父的九阴真经
tudi.Rulaishenzhang();//自己使用如来神掌

//a.Rulaishenzhang();//不可以,你师父不会如来神掌
B you;
you = (B)shifu;//你拿下面具,露出真面目,可以使用如来神掌了
you.Rulaishenzhang();

Sharen(shifu);//师父用九阴真经去杀了10000人
Sharen(tudi);//徒弟也可以用九阴真经去杀10000人

}
public static void Sharen(A a)//师父的绝学九阴真经杀人
{
a.Jiuyinzhenjing();
System.out.println("使用九阴真经杀了10000人!");
}
}

-----------------------------分割线-----------------------------

10 多态中引用变量类型转换

在多态中成员函数的特点:

     在编译时期:参阅引用型变量所属的类中是否有调用的方法。如果有,编译通过,如果没有编译失败。

     在运行时期:参阅对象所属的类中是否有调用的方法。

     简单总结就是:成员函数在多态调用时,编译看左边,运行看右边。

在多态中,成员变量的特点:

     无论编译和运行,都参考左边(引用型变量所属的类)。

     在多态中,静态成员函数的特点:

     无论编译和运行,都参考做左边。

代码举例:

class Fu
{
static int num = 5;
void method1()
{
System.out.println("fu method_1");
}
void method2()
{
System.out.println("fu method_2");
}
static void method4()
{
System.out.println("fu method_4");
}
}
class Zi extends Fu
{
static int num = 8;
void method1()
{
System.out.println("zi method_1");
}
void method3()
{
System.out.println("zi method_3");
}

static void method4()
{
System.out.println("zi method_4");
}
}
public class DuoTaiDemo
{
public static void main(String[] args)
{
Fu f = new Zi();
System.out.println(f.num);
f.method4();
Zi z = new Zi();
z.method4();
//输出为5、fu method_4、zi method_4
}
}


 ----------------------Android培训Java培训、期待与您交流! ----------------------