文章目录
![在这里插入图片描述](https://img-blog.csdnimg.cn/65537c5419bd48c28c717c121b231cdf.png#pic_center)
面向对象三大特征
一.封装
1.为什么要封装?封装有什么好处?
- 封装之后,对于那个事务来说,看不到这个事务比较复杂的那一面,只能看到该事务简单的那一面。复杂性封装,对外提供简单的操作入口。照相机就是一个很好的封装例子,照相机的原理非常复杂,但是对于使用照相机的人来说,操作是非常方便快捷的。还有像电视机也是封装的,电视机实现非常复杂,但对于使用者来说,不需要关心内部的实现原理,只需要会操作遥控器就行。
- 封装之后才会形成真正的“对象”,真正的“独立体”。
- 封装就意味着以后的程序可以重复使用,并且这个事物适应性比较强,在任何场所都可以使用。
- 封装之后,对于事务本身,提高了安全性。
例子一:
public class UserTest
{
public static void main(String[] args)
{
//创建User对象
User user = new User();
//访问age
//读取年龄值【get】
System.out.printlin("该用户年龄"+user.age);
//修改年龄值【set】
user.age = 20;
//读取年龄值【get】
System.out.println("该用户年龄"+user.age);
//修改年龄值
//这里的age属性显然是完全暴露给外部程序的,对于程序员来说可以操作User对象当中所有的细节,导致User中部分数据不安全
//不建议这样,建议对User类型进行封装,建议在外部程序中不能随意访问User对象当中的属性,这样可以保证属性的安全。
user.age = -100;
//读取年龄值
System.out.println("该用户年龄"+user.age);
}
}
2.封装的步骤
-
所有属性私有化,使用private关键字进行修饰,private表示私有的修饰的所有数据只能在本类中访问。
-
对外提供简单的操作入口,也就是说以后外部程序想访问age属性,必须通过这些简单的入口进行访问
- 对外提供两个公开的方法,分别是set方法和get方法。
- 想修改age属性,调用set方法
想读取age属性,调用get方法
-
set方法命名规范:
public void setAge(int a){
age = a;
} -
get方法命名规范
public int getAge(){
return age;
} -
背会:
setter和getter方法没有static关键字
有static关键字的方法调用: 类名.方法名(实参);
没有static关键字的方法调用: 引用.方法名(实参); -
关于java类中的构造方法:
(1)构造方法又被称为构造函数/构造器/Constructor
(2)构造方法语法结构:
[修饰符列表] 返回值类型 方法名(参数列表){
构造方法体;
}
(3)回顾普通方法的方法体:
[修饰符列表] 返回值类型 方法名(形参列表){
方法体;
}
(4)对于构造方法来说,“返回值类型”不需要指定,也不能写void,只要写上void,这个方法就成为了普通方法。
(5)对于构造方法来说,构造方法的方法名必须和类名保持一致
(6)构造方法的作用:
构造方法的意义是,通过方法的调用,可以创建对象。
(7)构造方法应该怎么调用?
----普通方法的调用:有static关键字的方法调用: 类名.方法名(实参);
没有static关键字的方法调用: 引用.方法名(实参);
----new 构造方法名(实参列表)
(8)构造方法调用执行之后,有返回值吗?
每一个构造方法实际上执行结束之后都有返回值,但是这个“return值”这样的语句不需要写。构造方法结束的时候java程序自动返回值。并且返回值类型是构造方法所在类的类型。由于构造方法的返回值就是类本身,所以返回值类型不需要编写。
(9)当一个类中没有定义任何构造方法的话,系统默认给该类一个无参数的构造方法,这个构造方法被称为缺省构造器。
(10)当一个类显示的将构造方法定义出来了,那么系统不再默认为这个类提供缺省构造器。建议开发中手动的为当前类提供违法参数的构造方法,因为无参数的构造方法太常用了。
(11)构造方法支持重载机制,在一个类中编写多个构造方法,这多个构造方法显然已经构成方法重载。
注:只要构造函数就会创建对象,并且一定是在堆内存中开辟内存空间。
例子二:
public class User{
//无参数的构造方法
public User(){
System.out.println("User's Default Constructor Invoke!");
}
public User(int i){
System.out.println("带有int类型参数的构造器");
}
public User(String name){
System.out.println("带有String类型的构造器");
}
public User(int i,String name){
System.out.println("带有int,String类型的构造器");
}
}
//测试类
public class ConstructorTest01{
public static void main(Stirng[] args){
//创建User对象
//调用User类的构造方法来完成对象的创建
User u1 = new User();
User u2 = new User(10);
User u3 = new User("zhangsan");
User u4 = new User(10,"zhangsan");
}
}
二.继承
1 .继承是面向对象三大特征之一,三大特征分别是:封装、继承、多态。
2 .继承的基本作用是:代码复用。但是继承最“重要”的作用是:有了继承,才有了以后的“方法覆盖”和“多态机制”。
3 .继承语法格式:
[修饰符列表] class类名 extends父类名{
类体 = 属性+方法
}
4 .Java语言当中的继承只支持单继承,一个类不能同时继承很多类,只能继承一个类,在c++中支持多继承
5 .关于继承的一些术语
B类继承A类,其中:
A类称为:父类,基类,超类。superclass
B类称为:子类,派生类。 subclass
6 .在Java语言中子类都继承哪些父类?
- 私有的不支持继承
- 构造方法不支持继承
- 其他数据都可以被继承
7 . 虽然java语言中只支持单继承,但是一个类也可以间接继承其他类:
例如:
C extends B{
}
B extends A{
}
A extends T{
}
C类直接继承B类,但是C类间接继承T、A类。
8 . java语言中假设一个类没有显示继承任何类,该类默认继承JavaSE库当中提供的Java.lang.Object 类.
java语言中任何一个类都有Object类特征。
三.方法的覆盖
1.回顾Java语言中的方法重载
- 方法重载又被称为overload
- 方法重载何时使用?
当在同一个类当中,方法完成的功能是相似的,建议方法名相同,这样方便程序员编程,就像在使用一个方法似的,代码美观 - 什么条件满足之后构成方法重载?
在同一个类中 方法名相同
参数列表不同:类型,个数,顺序 - 方法重载和什么无关?
和方法的返回值无关
和方法的修饰符列表无关
2.概念:
方法覆盖又被称为方法重写,英文单词:override【官方】/overwrite
3.什么时候使用方法重写?
当父类中的方法已经无法满足当前子类的业务需求,子类有必要将父类继承过来的方法进行重新编写,这个重新编写的过程就成为方法重写/方法覆盖。
4.什么条件满足时会发生重写?【代码满足什么条件之后】
- 方法重写发生在具有继承关系的父子类之间
- 方法名相同,返回值类型相同,形参列表相同
- 访问权限不能更低,可以更高
- 抛出异常不能更多,可以更少【后续讲异常后解释】
5.注意:
私有方法不能继承,所以不能覆盖
构造方法不能继承,所以不能覆盖
静态方法不存在覆盖【讲完多态后解释】
覆盖只针对方法,不谈属性
四.多态
1.Animal、Cat、Bird之间的关系:
Cat继承Animal
BIrd继承Animal
Cat和Bird之间没有任何继承关系
2.关于多态中涉及到的几个概念;
(1)向上转型(upcastng):
子类型–>父类型
又被称为:自动类型转换。
(2)向下转型(downcasting):
父类型–>子类型
又被称为:强制类型转换【需要加强制类型转换符】
需要记忆:无论是向上转型,还是向下转型,两种类型之间必须要有继承关系,没有继承关系,编译无法通过。
3.Java中允许这种语法:父类型指向子类型对象
以前编写:
Animal a1 = new Animal();
现在使用多态:
Animal a2 = new Cat();
- Java程序永远都分为编译阶段和运行阶段。
- 先分析编译阶段,在分析运行阶段1,编译无法通过,根本无法运行。
- 编译阶段编译器检查a2这个引用的数据类型为Animal,由于Animal.class字节码当中有more()这个方法,所以编译通过了。这个过程我们称为静态绑定,编译阶段绑定,只有静态绑定成功之后才有后续的运行。
- 在程序运行阶段,JVM堆中真实创建的对象是Cat对象,那么以下程序在运行阶段一定会调用Cat对象的more()方法,此时发生了程序的动态绑定,运行阶段绑定。
- 无论是Cat类有没有重写more方法,运行阶段一定调用的是Cat对象的more方法,因为底层真实对象是Cat对象。
- 父类型引用指向子类型对象这种机制导致程序在编译阶段绑定和运行阶段绑定两种不同的形态/状态,这两种机制可以成为一种多态语法机制。
//分析以下程序为什么不能调用?
a2.catchMouse();
//因为编译阶段编译器检查到a2的类型是Animal类型,从Animal.class字节码文件当中查找catchMouse()方法。最终没有找到方法,导致静态绑定失败,没有绑定成功,也就是说编译失败了,更别谈运行了
假设想让a2对象执行catMouse方法,怎么办?
a2是无法直接调用的,因为a2的类型是Animal,Animal中没有catchMouse方法。我们可以将a2强制类型转换成Cat类型。
a2的类型是Animal(父类),转换成Cat类型(子类),被称为向下转型,强制类型转换。
注:
向下转型也需要两种类型之间必须有继承关系,不然会编译报错,强制类型转换符。
什么时候需要使用强制类型转换符?
当调用的方法是子类型中特有的,在父类中不存在,必须进行向下转型。
Cat c2 = (cat)a2;
4.著名异常出现
类型转换异常:
程序编译虽然通过了,但是程序在运行阶段会出现异常,因为JVM堆内存当中真实存在的对象是Bird类型,Bird对象无法转换成Cat对象,因为两种类型之间不存在任何继承关系,此时出现了著名异常
java.lang.ClassCastException
类型转换异常,这种异常总是在“向下转型”中出现。
5.如何避免类型转换异常:
(1)以上异常只存在强制类型转换的时候发生,也就是说“向下转型”存在隐患(编译过了,但是运行错了)
(2)向上转型只要编译通过,运行一定不会出问题
(3)向下转型编译通过,运行可能会出错
(4)怎么避免向下转型出现的ClassCastException?
使用instanceof运算符可以避免出现以上异常
(5)instanceof运算符怎么用?
语法格式:引用 instanceof 数据类型名
以上运算符的执行结果类型是布尔类型,结果可能是true/false
关于运算结果是true/false
假设(a instanceof Animal)
true表示:a这个引用指向的对象是一个Animal类型
false表示; a这个引用指向的对象不是一个Animal类型
if(a3 instanceof Cat)
{
Cat.c3 = (cat)a3;
c3.catchMouse();
}
else if(a3 instanceof Bird)
{
Bird b2 = (Bird)a3;
b2.fly();
}
(6) java规范中要求:在进行强制类型转换之前,建议采用instanceof运算符进行判断。避免ClassCastException异常。
6. 多态在实际开发中的作用,以下以主人喂养宠物为例说明多态的作用
(1)分析:主人喂养宠物这个场景要实现需要进行类型的抽象
- 主人(类):主人可以喂养宠物,所以主人有喂养这个动作
- 宠物(类):宠物可以吃东西,所以宠物有吃东西这个动作
(2)面向对象编程的核心:
定义好类,然后将类实例化为对象,给一个环境驱使一下,让各个对象协作起来形成一个系统
(3)多态的作用;
降低程序的耦合度,提高程序的扩展力,能使用多态尽量使用多态。
核心:面向抽象编程,尽量不要面向具体编程。