java中的引用变量,引用对象
Child t = new Child();
t是一个引用变量(对象句柄),指向了一个Child类型的对象。
在内存中你首先声明了一个Child类型的引用变量,然后你创建了一个Child对象,并把这个对象在堆内存中的地址赋给t
好比你要用勺子喝汤,引用变量或对象句柄就是勺子的把手,通过它来实现属于勺子这个对象的喝汤的方法。
类之间的两种关系
一般->特殊关系
- 典型的
继承
关系is-a
- java语言使用extends关键字来表示这种继承关系
- 水果->苹果 大类(父类)->小类(子类)
- 苹果is a 水果
整体->部分结构关系
- 组装结构,典型的组合关系
has a
- 通过在一个类中保存另一个对象的引用来实现这种组合关系
面向对象
- 定义一个类,对应客观世界的哪种事物
- 业务需要关心这个事物的哪些
状态
,程序就为这些状态定义成员变量
- 业务需要关心这个事物的哪些
行为
,程序就为这些行为定义方法
面向过程和面向对象
- 吃(猪八戒,西瓜)
- 猪八戒.吃(西瓜)
面向对象的基本特征
封装,继承,多态
对于封装的理解
- 封装字面上理解就是“包装”,专业点就是
信息隐藏
,是指:利用抽象数据类型将数据和基于数据的操作封装在一起
,使其构成一个不可分割的独立实体。- 数据被保护在抽象数据类型的内部,尽可能地隐藏内部的细节,只保留一些对外接口使之与外部发生联系。
- 系统的其他对象只能通过包裹在数据外面的已经授权的来操纵与这个封装的随想进行交流和交互。
- 用户无需知道对象内部的细节(当然也无从知道),但可以通过该对象对外的提供的接口来访问对象。
对于封装,一个对象他所封装的是自己的属性和方法,不需要依赖其他对象就可以完成的操作。
封装的好处
- 良好的封装能够减少耦合(相互影响相互作用)
- 类内部的结构可以自由修改
- 可以对成员进行更精确的控制
- 隐藏信息,实现细节
Husband里面wife引用是没有getter()的,Wife的age也是没有getter()方法的。男人金屋藏娇,女人不愿别人知道自己的年龄。
- 封装***把一个对象的属性私有化,同时提供一些可以被外界访问的属性的方法***。
- 如果不想被外界访问,我们可以不提供方法给外界访问。
- 如果一个类没有提供给外界访问的方法,那么这个类也没什么意义。
如果不使用封装,那么该对象就没有setter()和getter()
如果我们需要修改Husband,将age改为String类型?如果你有几十个甚至上百个这样的地方,你是不是要改到崩溃?如果使用了封装,只需要改一下Husband类的setAge()方法就好。
封装可以使我们容易的修改类的内部实现,而无需修改使用了该类的客户代码
- 可以对成员进行更精确的控制
Husband husband = new Husband();
husband.age = 300;
使用封装我们就可以避免这个问题,对age的访问入口做一些控制(setter)
上面都是对setter方法的控制,其实通过封装也能够对对象的出口做出很好的控制。
理解继承
继承是使用已存在的类定义作为基础建立新类的技术,新类的定义可以增加新的数据或新的功能,也可以用父类的功能能,但不能选择性的继承父类。
- 继承所描述的是
is-a
的关系,如果有两个对象A和B,如果可以描述为“A是B”,则表示A继承B,B为父类或超类,A为子类或派生类。- 继承者除了拥有被继承者的特性外,还有自己独有的特性。
- 继承者完全可以替换被继承者,反之不行。(向上转型)
- 猫是动物 对
- 动物是猫 错
- 继承定义了类如何相互关联,共享特性。对于若干个相同或相识的类,我们可以抽象出他们共有的行为或者属相并将其定义为一个父类,然后用这些类继承该父类,他们不仅拥有父类的属性,方法还可以定义自己独有的属性或方法。
- 三句话:
- 子类拥有父类非private的属性和方法
- 子类可以对父类进行扩展
- 子类可以用自己的方式实现父类的方法
构造器
- 子类不能继承父类的private还有构造器
- 对于构造器,他只能被调用,而不能被继承。调用父类的构造方法我们使用super()即可
- 对于子类而已,其构造器的正确初始化是非常重要的,而且当且只有一个方法可以保证这点:在构造器中调用父类构造器来完成初始化,而父类构造器具有执行父类初始化所要的所有知识和能力
结果是:
Person Constrctor…
Husband Constructor…
通过这个示例可以看出,构造过程是从父类"向外"扩散的,也就是从父类开始向子类一级一级的完成构建。而且我们并没有引用父类的构造器。但是***编译器会默认给子类调用父类的构造器***。
但是这个默认调用父类的构造器是有前提的:父类有默认构造器。如果父类没有默认构造器,我们必须显示的使用super()来调用父类构造器,否则编译器会报错:无法找到符合父类形式的构造器
输出:
person Constrctor…lala
Husband Constrctor…
对于继承而已,子类会默认调用父类构造器,但是如果没有默认的父类构造器,子类必须要指定父类的构造器,而且必须是在子类构造器中做的第一件事(第一行代码)。
protected关键字
private访问修饰符,对于封装而言,是最好的选择,但这个只是基于理想世界,有时候我们需要这样的需求:我们需要将某些事物尽可能的对这个世界隐藏,但是仍允许子类的成员来访问他们。这个时候就要用到protected
对于protected而言,他指明就类用户而言,他是private,但是对于任何继承与此类的子类而言或者其他任何位于同一个包的类而言,他却是可以访问的。
可以看出来子类Husband可以明显的调用父类Person的setName()
向上转型
- 在上面的继承中我们谈到的是继承是一种
is-a
的相互关系。
- 猫继承动物,可以说猫是一种动物或者说猫是动物的一种。这样可以讲猫看做动物就是向上转型。
person.display(husband); //通过这句话我们可以看出husband是person类型
- 将子类转换成父类,在继承关系上面是向上移动的,所以一般称为向上转型。由于向上转型是从一个叫专用类型向较通用类型转换,所以他总是安全的,唯一发生变化的可能就是属性和方法的丢失。这就是为什么编译器在“未曾明确表示转型”或“未曾指定特殊标记”的情况下,仍然允许向上转型的原因。
慎用继承
理解多态
子类对象可以直接赋给父类变量,但运行时依然表现出子类的行为特征,意味着同一个类型的对象在执行统一个方法时,可能表现出多种行为特征。
多态是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量到底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须要在由程序运行期间才能决定。因为在程序运行时才确定具体的类,这样,不用修改源程序代码就可以让引用变量绑定到各种不同的类实现上,从而导致该引用调用的具体方法随之改变,即不修改程序代码就可以改变程序运行时所绑定的具体代码,让程序可以选择多个运行状态,这就是多态性。
比如桌子上有几个装了酒的相同的杯子,从外表我们不能看出是什么酒,只有喝了之后才能知道。
酒a = 剑南春
酒b = 五粮液
这里所表现的就是多态。剑南春,五粮液都是酒的子类,我们只能通过酒这一个父类就能引用不同的子类,这就是多态—只有在运行的时候才会知道引用变量所指向的具体实例对象