面向对象的理解
类和对象的关系
举个粒子:
正所谓“万物皆对象”,在我们生活中所见的东西都可以视为对象,如一个杯子、一辆汽车、一个房子等,那么问题来了,这些对象是怎么产生的呢,就汽车来说吧,汽车是怎么生产出来的?为什么可以造制出那么多一样的汽车呢?
分析:
正是因为我们的设计师在一开始,先设想汽车的结构,制作出每个零件的“模型/模板”,最终绘制出了一个完整的汽车“模板”,机械工程师只需要有“模板”就可以按照规格,一步步的完成零件和一整辆车的生产,这样我们的“对象”就这么出来了,有了“模板”工程师还可以不断的生产更多的“汽车对象”,我们就可以得到汽车来驾驶汽车。
细分:
这里的汽车“模板”,里面就包括了各种各样的零件和汽车的每个功能,如加速、刹车、前进、后退等,这些零件的生产也是通过零件“模板”生产出来的,而这一些“模板”都不是真实的,都是一些基本的原理和结构图,等工程师完成了零件和汽车生产后,生成了汽车“对象”,这个汽车“对象”,才是我们真正所能驾驶的汽车,这个汽车实现了“模板”的每个零件,以及功能,我们只要会使用他们就可以完成汽车的驾驶做业。
说明:
“模板”也就是代码的“类”,汽车的“零件”和“功能”也就是类的“属性”和“方法”;
而真实的汽车就是我们的“对象”,汽车是被工程师按照“模板”制造出来的,而程序代码中的“对象”就是程序员在调用类时使用“new“创建出来的,这样就new创建出了对象,程序员通过创建出来的对象,可以通过对象名.方法 / 属性名使用每个方法和属性,也就是和驾驶汽车一样,可以使用每个功能,在生活中我们操作的是每个汽车对象,在程序代码中程序员操作的也是每个new出来的对象,所以这也就是我们所说的“面向对象”。
现在对那蒙着神秘面纱的“面向对象”是不是有了一个赤裸裸的理解了。
总结
类就是“模板”,”模板“分为属性和方法。
- 类:定义了对象应具有的特征和行为,类是对象的模板。
- 对象:拥有多个特征和行为的实体,对象是类的实例。
在次”面向对象“的认识也到已经可以下课了·····
认识变量
局部变量:方法或者语句块内部定义的变量。
实例变量:方法外部,类的内部定义的变量。
局部变量与实例变量的区别
注释:
实例变量默认值:
类型 | 默认值 |
---|---|
整数型 | 0 |
小数 | 0.0 |
字符 | \u0000 |
布尔 | false |
其他(引用类型) | null |
局部变量没有默认值,要求必须先赋值再使用,否则编译错误。
包
定义
在Java语言中,为了对同一个项目中的多个类和接口进行分类和管理,防止命名冲突,以及将若干相关的类组合成较大的程序单元,则把这些类放在同一个文件夹下进行管理,此文件夹称其为包。
举个栗子:
在学校我们有一个个不同的学院,学院中又分了很多专业,每个专业又分了很多班级,因为每个班级都有1、2、3…班,为了防止班级的名字发生冲突,我们设立了专业,可是专业有很多的种类,为了方便管理,我们把专业进行分类,分到不同的学院中,在这里的班级相当于属性和方法,专业就是类/接口,而我们的学院就是包,类可以把属性和方法进行管理,防止命名冲突,而学院可以把类/接口进行组合,变成一个更强大的功能,这个包就可以是项目的某个系统/功能。而整个完整的项目就相当于学校,可以是一个学院或多个学院组成。
类
类可以将数据和函数封装在一起,其中数据表示类的属性,函数表示类的行为。定义类就是要定义类的属性与行为(方法)。
(方法)重载
在一个类中,出现多个方法名相同,但参数个数或参数类型不同的方法,称为方法的重载。
Java在执行具有重载关系的方法时,将根据调用参数的个数和类型区分具体执行的是哪个方法。
特点:
- 方法名相同
- 参数列表不同(类型、个数、顺序)
- 和返回值、修饰符无关
构造方法(主要用于创建对象)
- 方法名和类名一样
- 不能用static、final等修饰
- 没有返回值
- 在对象初始化的时候调用,用关键字new来初始化
- 如果一个类没有构造方法,系统会默认提供一个公共无参的构造方法,如果有构造方法,就使用用户定义的构造方法。
封装
举个粒子:
在前面讲到,创建了对象之后可以使用对象名.属性名来调用属性(变量),我们调用属性可以是读取数值,也可以改变数值,假如定义的类(student)类,这个类的属性有name、age、grade、ID等属性变量,而这些属性变量都是有特殊的含义的,比如age年龄一般都不会超过120岁,但是,创建了对象之后,我可以随意修改age的值,把它修改为1000岁,那是不是很不合里,可是程序又不会报错,这是封装就有了它的作用了,封装可以决解用户输入非法/不合理的之进行预先处理。
定义:
封装就是把对象的属性和服务结合成一个独立的单位,并尽可能隐藏对象的内部细节,从编程角度理解就是将属性设置为私有的,并为属性提供公共的访问方法。
目的:
- 隐藏类的实现细节
- 让使用者只能通过事先定制好的方法来访问数据,可以方便地加入控制逻辑,限制对属性的不合法操作。
- 便宜修改,增强代码的可维护性。
- 可进行数据检查。
get/set方法
get/set方法是外界访问对象私有属性的唯一通道,方法内部可对数据进行检测和过滤。
权限访问修饰符
继承
举个栗子:
继承也就是和我们生活中所说的继承是一样的概念,就是可以继承父类(超类)的所有属性和方法,不同的是程序中的继承很比较多的限制的操作。
继承规则
- Java中只允许单继承(Java简单性的体现)。
- 子类继承了父类的代码和数据,但它不能访问声明为private的超类成员。
- 在子类中可以增加父类中没有的变量和方法。
- 在子类中可以覆盖父类中已有的方法,包括构造方法,实例方法和成员方法。
- 在子类的结构方法中,可以通过super()方法将父类的变量初始化
- Java里根类是Object。
重写(覆盖)
- 发生在父类和子类之间。
- 方法名字相同。
- 参数列表相同。
- 子类方法返回值的类型必须是父类方法返回值类型的子类或它本身。
- 子类方法的访问控制权限必须跟父类一样或者比父类更广。
方法继承的各种限制
- 修饰符相同的方法覆盖,即只改内部,不改外部。
- 访问权限不同的方法覆盖,子类只能相对父类越来越宽松。
- 返回值的类型覆盖,只允许相容的返回值类型。
- final的方法覆盖,只能是父类无,子类有,而不能是父类有,子类无。
- static的方法覆盖不能有任何变动,即父有子必有,父无子必无。
this
关键字this在Java类中表示对象自身的引用值。
this可以有两种用法:
- 代表当前对象
- 调用本类的其他构造方法
super
关键字super在Java’类中表示父类的引用值。
super可以有三种用法:
- 表示父类的对象
- 调用父类的构造方法
- 调用父类被隐藏的成员变量和成员方法
this与super的关系 - this表示当前对象引用,调用本类(包括继承)的属性、方法、本类构造方法。
- super表示父类对象引用,调用父类的属性、方法、构造方法。
- 两者不能同时用在一个构造方法中,都是要求在首行。
继承中对象创建
- 在具有继承关系的对象创建中,构建子类对象会先构建父类对象。
- 由父类的共性内容叠加子类的独有内容,组合成完整的子类对象。
多态
举个粒子:
在我们生活中,每个人的阅历以及经历的都是不一样的,比如车,在每个人的了解程度不一情况下,有的认为汽车就是用来方便出行的,家用的;有的认为是商务用的,用来承载乘客的;有的认为是可以用来住的,也就是大型的房车是可以居住的;也有的认为是可以方便货物的运输的等等,所以同一个事物在不同角度ta是有不一样的用途或者作用的,可是ta都是属于某一个类别,如例子中的车,所以这也就是多态,在不同角度上看有不同的用途。
不同的人看到同一个物体是不一样的,称之为“多态”。
概念及作用:
- 多态性指相同的操作可作用于多种类型的对象上获得不同的结构。
- 不同的对象收到同一消息可以产生不同的结构,这种现象称为多态性。
- 多态指的是编译时的类型变化,而运行时类型不变。简单来讲,就是用一个父类的引用指向子类的对象。
- 父类引用指向子类对象,从而产生多种形态。
- 父类引用仅可调用父类所声明的属性和方法,不可调用子类独有的属性和方法。*
思考:如果子类中覆盖了父类的方法,以父类类型引用调用此方法时,优先执行父类还是子类的方法?
答案:优先执行子类的方法。实际运行过程中,依旧遵循覆盖原则。
多态的应用
-
场景一:使用父类作为方法的形参实现多态,使方法参数的类型更为宽阔。
-
场景二:使用父类作为方法的返回值实现多态,使方法可以返回不同子类对象。
向上转型(装箱)
父类引用中保存真实子类对象,称为向上转型(即多态的核心概念)
注意:仅可调用父类中所声明的属性和方法。
向下转型(拆箱)
父类引用中保存真实子类对象,强制转换成子类本省类型称为向下转型。
解释:要有先向上转型,才能有向下转型。
注意:只有转换回子类真实类型,才可调用子类独有的属性和方法。
类型转换异常问题
向下转型时,如果父类引用中的子类对象类型和目标类型不匹配,则会发生类型转换异常。
instanceof
向下转型前,应判断引用中的对象真实类型,保证类型转换的正确性,可以使用instanceof关键字完成。
语法:引用(变量) instanceof 类型 //返回boolean类型结果
多态的作用
- 屏蔽子类间的差异
- 灵活、耦合度低
紧接着介绍三个非常重要的关键字,拿出你的小本本…
abstract抽象
什么是抽象:似是而非的,像却又不是;具备某种对象的特征,但不完整。
举个例子:
在生活中有很多东西都是抽象化的,梵高的抽象画,一幅画看上去像小鸟,仔细看其实又不是小鸟,这就是抽象,而代码中的抽象是怎样的?为什么要有抽象?有什么作用呢?其实很简单,在代码中定义的父类在子类继承了之后调用父类的某个方法,可是发现这个方法的功能是很早以前实现的了,存在缺陷,所以子类就只能重写(覆盖)这个方法,这是一个子类,同样还有第二个第三个第四个等等,很多子类都继承了这个父类,都发现这个方法需要重写,所以原本父类中的方法实现就没有有任何作用了,可是这个方法又是子类必要的方法,不能删除掉,实现的内容又用不上,所以就可以把父类的这个方法定义为抽象的,这样继承它的子类都要去重写这个类代码,就行了。这就是代码中的抽象,声明是必要,方法实现却多余。
引用:abstract修饰类,此类不能new对象。
作用:
- 可被子类继承,提供共性属性和方法。
- 可声明为引用,更自然的使用多态。
经验:
- 抽象父类,可作为子类的组成部分,依附于子类对象存在,由父类共性+子类独有组成完整的子类对象。
- 方法声明必要,方法实现多余。
- 说明:父类提供的方法很难满足子类不同需求,如不定义,则表示所有子类的共同方法就没有了,使用父类引用子类时,将无法调用共同方法(失去了多态,没有了向上转型的特性),如定义,略显多余,多数会被子类覆盖。则可使用抽象方法。
抽象方法
- 抽象方法,只有声明,没有方法实现({}的部分)。意为不完整的方法,必须包含在抽象类中。
- 产生继承关系后,子类必须重写父类中所有的抽象方法,否则子类还是抽象类。
总结
- abstract修饰类:不能new对象,但可以声明引用。(实现多态)
- abstract修饰方法:只有方法的声明,没有方法的实现。(需包含在抽象类中)
- 抽象类中不一定包含抽象方法,但有抽象方法的类一定是抽象类。
- 子类继承抽象类后,必须重写父类中所有的抽象方法,否则子类还是抽象类。
static静态
举个粒子:
在一个类中可以声明很多的属性(变量),而在调用者引用这个类时,可以创建(new)出多个类对象,这些属性(变量)也是对应这多份,在一个对象中修改属性,其他对象是不会修改的,可是现在有个需求:“在一个类(student类),可以创建学生对象,每次都可以创建一个学生,可是需要这个类中的一个cont属性变量来统计创建的学生个数”;按照普通的属性特征是无法完成这个需求的,因为每次创建一个学生对象,对应的cont又重新创建出一个独立的,这是就可以使用static来给cont属性进行修饰,表明这个属性cont是静态的,静态表示只有独立的一份,不管创建多少个student对象,都只有一份cont属性,而且只会初始化一次,第一次创建student对象是,可以将它初始化为0,并且编写一个给cont自加1的方法,用对象.方法名调用一次即可实现统计。这就实现了上面的需求。
实例属性
实例属性是每个对象各自持有的独立空间(多份),对象单方面修改,不会影响其他对象。
静态属性
属于整个类共有的属性。静态属性是整个类共同持有的共享空间(唯有一份),任何对象修改,都会影响其他对象。在此不建议使用对象进行修改,需要修改则使用类名.静态属性来访问和修改,因为静态属性属于整个类的。
静态方法
- 由static修饰的静态方法。
- 可在本类中,通过“静态方法名”访问。
- 可在其他类中,通过“类名。静态方法名”访问,无需创建对象。
静态的特点
- 静态方法允许直接访问静态成员
- 静态方法不能直接访问非静态成员(因为非静态成员是对象引用,而静态方法无需对象引用)
- 静态方法中不允许使用this或super关键字(因为this和super都是非静态的)
- 静态方法可以继承,不能重写,没有多态
静态代码块
- 语法:static{//代码…}
- 类加载时,触发静态代码块的执行(仅一次)。
- 执行地位:静态属性初始化之后。
- 作用:可为静态属性赋值或必要的初始行为
类加载
- JVM首次使用某个类时,需要通过CLASSPATH来查找该类的.class文件
- 将.class文件中对类的描述信息加载到内存中,进行保存。如:包名、类名、父类、属性、方法、构造方法…
- 加载时机:
(1)创建对象
(2)创建子类对象
(3)访问静态属性
(4)调用静态方法
(5)主动加载:Class.forName(“权限定名”)
总结一下
- 可以修饰变量、方法、初始化代码块,成为类变量、静态方法、静态初始化代码块。
- 类变量、静态方法、静态初始化代码块与具体的某个对象无关,只与类相关,是类的共有的,可以在没有对象的情况下用:类名。方法名/变量名来访问。
- 在静态方法里不能访问非静态的变量/方法及this关键字。
- 静态方法初始化代码块只在类加载的时候运行一次,以后再也不执行,所以一般被用来初始化静态成员
- static不能修饰局部变量
- 创建一个对象时的初始化顺序:静态变量→执行静态代码块→初始化属性→执行构造方法
- main()方法前面必须加static修饰符。由于Java虚拟机需调用main()所以权限必须为public。而且在执行main()方法时不必创建对象,所以必须是static,该方法接受一个String类型的数组参数。
final最终
理解:
final顾名思义表示的就是最终的、最后的、不可改变的;被他修饰的也是不可以改变的。
final类
- final修饰类:此类不能被继承。如String、Math、System类均为final修饰的类,不能被继承。
- final修饰方法:此方法不能被覆盖。
- final修饰变量:此变量值不能被修改(即为常量)
实例常量
- 实例常量,则在定义实例常量时需要给默认值,如果不给默认值,系统也不会给默认值,可能尚未初始化变量(实例常量)而报错。
- 实例常量不再提供默认值。必须手动赋予初始值。
- 赋值时机:显示初始化、构造方法时。
- 注意:如果在构造方法中为实例常量赋值,必须保证所有的构造方法都能对其正确赋值。
静态常量
- 静态常量不再提供默认值,必须手动赋予初始值
- 赋值时机:显示初始化、静态代码块。
对象常量/引用类型常量
地址不可变,引用类型的属性是可以变的
总结
-
final修饰类:此类不能被继承。
-
final修饰方法:此方法不能被覆盖。
-
final修饰变量:此变量值不能被修改。(无初始值,只允许赋值一次)
-
特点:
-
局部常量:显示初始化。
-
实例常量:显示初始化、构造方法。
-
静态常量:显示初始化、静态代码块。
-
基本类型常量:值不变。
-
引用类型常量:地址不可变。
结束语
经过不断的学习你又更接近光头强了,我们已经能赤裸裸的看着“面向对象”了。