面向对象的思想
JAVA是一门完全的面向对象的语言,这是一种思想,即把各种事物当成一种对象,然后对象有自己的属性和行为,这种认知方式也比较适合人的认知。
为了实现这个思想,java具备三大特征:封装 、继承 和 多态。
- 封装
封装就是把事物所具有的属性和行为抽象出来,封装到一个类里面。所以类是对事物的封装,是抽象的,而对象才是具体的,是实例。
所以,在分析问题时,将某事物进行分析抽象并生成类的是哦户,就是一个封装的过程。类就是一种模版,在需要的时候,可以按照这个模版生成具体对象,而且可以是重复的。
- 对象内存图
之前我们知道,main函数等各种方法在程序运行的时候,是存在方法栈中的,而类的定义是在方法区中,生成的对象数据是存在堆内存中的。
如图所示,当函数执行到需要生成Phone类的p对象时,会根据方法区中Phone.class的类结构,在堆内存中生成实例对象p,然后把p的地址返回给函数中的p这个引用变量,之后调用p的时候,都是通过p存储的地址去找数据的。
多个对象时内存图:
当生成多个对象时,由于类中的方法大家是共同都有的,所以为了节约内存,就在生成对象时只生成一个方法标记,不用每个对象都生成一个完整的方法体占用内存。当需要的时候,在根据方法标记调用方法即可。
成员变量和局部变量
成员变量是类中对属性的封装,而局部变量是对类中行为封装内的变量,是不同层次,不同作用域的两个概念。
成员变量是跟着类的对象走的,它的生命周期是跟着对象实例的。而局部变量是只限于对象中的函数内部的,当出了函数体,局部变量就没了,也就是说局部变量生命周期是跟着函数走的,当函数执行完毕就销毁了。
- 封装优化
我们虽然实现了封装,但在应用的时候,从安全角度考虑,类中的属性不应该被任何人直接访问,有些不需要泄漏的信息,都可以限制在类的内部使用。这就需要不同的权限,在类中,对属性和函数提供了集中不同级别的权限:
private,即私有的,只能在类的内部调用,不能通过类名.变量名的方式调用;
protected:受保护的,
public:公共的,即可以直接通过类名进行调用的。
通常我们处理方式是,属性不能直接访问,而是通过get 和 set方法进行间接使用,然后部分函数功能私有,部分提供外部调用的函数设置公有。
其次,还有一些方便使用的优化,
1)this关键字:可以在类中使用,通俗讲指的是其本身,原理就是this存储的是实例化的对象本身的地址,所以通过http://this.xxx调用的都是对对象自身的调用。
2)构造函数:可以在对象生成的时候,就完成类的初始化功能。默认是无参数,即不赋值,只初始化内存空间。可以进行重载,定义多个构造函数,然后根据不同需求进行初始化。
3)JavaBean标准规范:是对类的一种设计规范,要求类必须是公共的、具体的,要有无参数的构造函数,提供用于操纵成员变量的get和set方法。
- 继承
继承实际是一种进一步的封装,把公共的提取出来,作为父类,被大家共有,把特有的写到子类内部。这样就进一步降低冗余,提高复用率。
- 继承的关键字是extends
格式很简单,在子类的定义时,后面加上extends 父类名即可实现继承。
- 继承后的内容
成员变量重名时,要调用父类的成员变量时,要用super关键字,super指向的时父类对象地址,与this类似但功能不同,this是指向对象自身地址。
当成员方法重名时,子类中的方法就对父类的同名方法进行了重写override,重写是直接覆盖掉原函数。与重载不同,重载是改变参数生成并立的多个同名函数,重写是直接覆盖,只有一个函数能用。但要注意,重写时,返回值类型/函数名/参数列表都要与父类保持一致。
- 继承后的构造函数与初始化顺序
继承后,子类的构造函数中会有super();这句话表明了父类与子类的初始化关系。即先对父类进行初始化,再对子类进行初始化。如图
在每次创建子类对象时,先初始化父类空间,再创建其子类对象本身。目的在于子类对象中包含了其对应的父类空间,便可以包含其父类的成员,如果父类成员非private修饰,则子类可以随意使用父类成员。代码体现在子类的构造方法调用时,一定先调用父类的构造方法。
- JAVA只支持单继承
一个类只能有一个父类,即对类来说,只能继承一个类,不能同时继承多个类。
但多个类之间可以一一对应,实现多层次的继承,同时,一个父类可以被多个子类继承,这些是允许的。
- 多态
通过继承有了父类与子类,多个子类对父类的复方进行重写,就是对同属一个父类的子类们来说,同样的一个行为(如鸣叫)却有了不同的表现形式,每个子类的表现都是独立的个体。这种多样化就是多态的体现。
简言之,多态就是同一种行为的多种表现形态。
- 多态的前提
1 . 继承或者实现【二选一】
2. 方法的重写【意义体现:不重写,无意义】
3. 父类引用指向子类对象【格式体现】
- 多态的体现
多态就是在生成子类对象的时候,将类型转换为父类类型,这就叫父类引用指向子类对象。
如Animal是父类,Cat是子类,用多态形式创建对象
Animal cat = new Cat();
- 多态的好处
之所以改为父类的类型,是因为其都有共同的方法,只是子类重写了父类方法。当调用这些方法时,利用其同属于父类类型的共同之处,可将这个类型抽取出来,作为方法的参数,即可实现多样化的表现方式。节省代码,减少冗余,提高复用率。如:
public class Test {
public static void main(String[] args) {
// 多态形式,创建对象
Cat c = new Cat();
Dog d = new Dog();
// 调用showCatEat
showCatEat(c);
// 调用showDogEat
showDogEat(d);
/*
以上两个方法, 均可以被showAnimalEat(Animal a)方法所替代
而执行效果一致
*/
showAnimalEat(c);
showAnimalEat(d);
}
public static void showCatEat (Cat c){
c.eat();
}
public static void showDogEat (Dog d){
d.eat();
}
public static void showAnimalEat (Animal a){
a.eat();
}
}
通过示例可以看到,不论有多少种动物,其都有一个共同的行为,就是吃eat(),通过抽取父类类型作为方法参数,只需生成不同的子类对象,传递实例化对象,就可用一个函数匹配所有子类的对吃的行为的调用。
- 类型转换
我们明白了多态在实现上就是一种类型转换,我们称从子类转换为父类的过程为向上转换;从父类向子类转换的过程为向下转换。
向上转换是默认的,向下转换需要强制类型转换。
在强制类型转换的时候,就会有出错的风险,比如转换的是没有继承关系的类,就会发生异常。java提供isntanceof关键字给引用变量做类型的校验,变量名 instanceof 数据类型,如果变量属于该类型返回true,否则返回false。
ps:之前写过一遍,结果草稿的自动保存出了问题,只能重写一次,没第一次详细,属实有点心态崩。