面向对象的三大特性——封装 、继承、多态
一. 面向对象特征:封装性
1.JAVA中的数据类型
JAVA中所有的数据类型分为两种
一种为值类型,值类型的变量存储的就是数据本身,所有的基本数据类型都是值类型
另外一种称为“引用类型”,或者对象类型,引用类型的变量中存储的不是数据而是地址
2.实例方法说明
属性对于每个对象来说各持一份,方法本质是一段可被执行的代码,方法对于该类型的所有对象来说共享一份
可以在实例方法的方法体中通过关键字this访问调用方法的当前对象
实例方法中访问当前对象的属性值this.可以省略
3.成员变量和局部变量的区别
局部变量:定义在方法中的变量
成员变量:定义在类中的变量(类的属性) 或者称为实例变量
- 定义的位置不同
- 作用范围不同:局部变量只能在定义的当前方法内被访问 而成员变量可以在当前类的所有方法中被访问
- 默认值不同:局部变量定义完成后没有自动初始化功能 成员变量在分配完空间只有由系统自动根据变量的类型进行初始化
int--->0 double--->0.0 char---->'\0' String---->null (引用类型数据) boolean---->false
-
生命周期不一样
局部变量在方法被调用时分配空间,在方法执行结束后立马销毁
成员变量在每次new对象的时候分配空间,在对象称为垃圾的时候被销毁(由GC来完成的)
注意:当一个类中方法的局部变量和当前类中的成员变量同名了,按照就近原则优先访问方法中定义的局部变量,如果非要访问同名的成员变量则需要在变量名前加上一个this.限定
4.方法的重载 overload
定义:方法重载指的是在同一个类中可以出现多个同名的方法,但是参数列表不同,这样就形成了方法的重载
在调用时自动根据传入的参数匹配到某个具体的方法进行调用
注意:方法的重载之和方法名以及方法的形参有关
要求:
1.方法名必须相同
2.参数列表不同:参数的个数不同 类型不同 顺序不同 ,参数名不能作为参数的区分标志
3.和返回类型无关
使用方法重载的好处:对于调用者来说只需要记住一个方法名即可,调用时会自动的根据传入的实参找到对应的方法执行
5.类中的构造方法
构造方法是类中的一种特殊方法,和普通方法不一样:
- 从功能上来,普通的方法主要是用来实现软件的业务功能,而构造方法关注的当前类的对象创建
- 从定义了说,构造方法的定义有两个特殊点:1.方法名和类同名 2.方法无返回类型 连void都不能写
- 从调用来说:普通的方法的调用格式:对象名.方法名(参数列表); 构造方法只能使用new关键字进行调用
类中的构造方法的作用就是用来创建该类的对象,如果一个类中没有构造方法则该类将无法创建对象
规则:当一个类没有显示的定义构造方法,则编译器会自动为这个类添加一个无参并且方法体为空的构造方法
无参构造器负责将对象所占的堆中的空间分配下来,但是只能按照默认的方式对对象属性进行初始化
如果我们自己在类中定义了构造方法,则编译器就不会再为这个类提供一个无参的构造方法了
所有对象都是通过调用构造方法构造出来的
6.封装性
封装:对象的属性应该是对象私有的东西
如何对类中的属性进行封装的处理?
1.只要在定义类的属性时加上一个访问修饰private 私有的即可,
2.同时对外提供针对这些属性访问的get和set方法
get方法用来获取某个属性的值 set方法用于在类的外部修改某个属性的值
被private 修饰的属性只能在本类的内部被访问
快速为私有的属性提供get和set方法:alt+shift+s
根据业务的需要我们可以为某个属性对外只提供get不提供set,那么这样的属性称为“只读”属性
相反如果一个属性只有set没有get,这样的属性称为只写属性
如果在本类的一个方法中去调用另一个方法直接写:方法名() 前面省略了this.
对象的方法一般是公开的,如果对于类中的某个方法不希望外界直接调用也可以使用private关键字进行修饰,那么此时这个方法只能在本类的其他方法中调用
如何设计一个JAVA类:
public class 类名{
//私有属性的定义;
//get和set方法
//构造方法(建议在每个类中都要提供一个无参的构造器)
//普通方法
}
补充:this关键字的两种用法:
1.在类的方法体中可以使用this.来引用调用这个方法的当前对象,构造方法中的this.指的是当前创建的这个对象
2.普通方法相互之间可以调用,构造方法之间也可以相互调用 (了解)
this(参数)
public Person() {
//this("aaa", 19);
System.out.println("无参构造...");
}
public Person(String name,int age) {
this.name=name;
this.age=age;
System.out.println("有参构造....");
}
注意:在一个构造方法中调用类的另一个构造方法,调用语句必须写在第一行
二. 面向对象特征之二:继承性
1.为什么要有继承?
多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中, 那么多个类无需再定义这些属性和行为,只要继承那个类即可。
作用:继承的出现减少了代码冗余,提高了代码的复用性。继承的出现,更有利于功能的扩展。 继承的出现让类与类之间产生了关系,提供了多态的前提。
2.关于继承的规则:
类继承语法规则:
class Subclass extends SuperClass{ }
-
子类继承了父类,就继承了父类的方法和属性。
-
在子类中,可以使用父类中定义的方法和属性,也可以创建新的数据和 方法。
-
在Java 中,继承的关键字用的是“extends”,即子类不是父类的子集, 而是对父类的“扩展”。
-
子类不能直接访问父类中私有的(private)的成员变量和方法。
-
Java只支持单继承和多层继承,不允许多重继承 一个子类只能有一个父类 一个父类可以派生出多个子类
问题:父类构造方法有没有被继承到?
因为构造方法必须和当前类同名,因此构造方法不能被子类继承
私有的成员也被继承到了只是不能在子类中直接访问
/**
*设计一个程序员类SE包含属性 姓名 性别 年龄使用的编程语言
*设计一个项目经理类PE包含属性 姓名 性别 年龄 项目管理年限
*/
public class Employee {
String name;
String gender;
int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public void test() {
System.out.println("我是父类中定义的方法。。。");
}
}
/**
*
*程序员类
*/
public class SE extends Employee{
private String language;
public String getLanguage() {
return language;
}
public void setLanguage(String language) {
this.language = language;
}
public SE() {
}
public SE(String name, String gender, int age, String language) {
this.name = name;
this.gender = gender;
this.age = age;
this.language = language;
}
//自我介绍的方法
public void sayHello() {
System.out.println("姓名:"+ name + "性别:" + this.gender + "年龄:" + this.age +"使用的语言:" + this.language);
}
}
3.子类对象的创建内存分析
在调用子类的构造方法之前先调用了父类的构造方法
创建子类对象之前先创建它的父类对象
在一个子类的对象中完整的包含了一个父类对象
在子类构造方法的第一行默认有一条调用父类构造方法的语句 super()
还可以在子类的方法中通过super.的方式引用子类对象中所包含的父类对象
4. super关键字的使用
- 可以通过super()显示的调用父类的构造方法(必须写在子类构造方法的第一条)
- 还可以在子类的方法中通过super.的方式访问父类中定义的成员
5. JAVA中的访问修饰符
在Java中一共提供了四种访问权限修饰
-
public :公开的 在程序的任何地方都可以被访问
-
private:私有的 只能在本类中访问 即使子类中也不能被访问
-
default: 默认的 表示成员前面没有写任何访问修饰 包级别的访问权限(本类中可以访问 同一个包的其他类中也可以访问)
-
protected:受保护的 本类中 同包中可以访问 不同包但是有继承关系
| 本类 | 本包的其他类 | 不同包的子类 | 不同包也没有继承关系 | |
|---|---|---|---|---|
| private | 可以 | 不可以 | 不可以 | 不可以 |
| default | 可以 | 可以 | 不可以 | 不可以 |
| protected | 可以 | 可以 | 可以 | 不可以 |
| public | 可以 | 可以 | 可以 | 可以 |
6. 继承的两大特性
-
单根性:JAVA中一个类最多只能继承一个父类 但是一个父类下面可以有多个子类,整个JAVA继承体系是一个树形结构
JAVA中如果一个类没有显示的继承一个父类,则默认从Object类继承,Object类是JAVA中根基类
结论:JAVA中的所有类都直接或间接的继承自Object
Object是一切JAVA类的父类
-
传递性: A类继承了B类 ,B类又继承了C类 则我们可以说A类也继承了C类
三. 面向对象特征:多态
1.概念
多态指的是不同的对象在接收到同一个方法调用时自动表现出不同的行为方式
2. 对象的转型
向上转型:一个父类的引用可以指向任何一个子类对象 反之是不行的(默认的)
但是通过父类的引用只能访问到子类对象继承到的那部分成员
向下转型:可以将一个指向子类对象的父类引用通过强制的方式转成子类的引用
类型的转换并没有影响对象在堆中的存储结构,只是改变了栈中的引用(看待一个对象的视角发生了变化)
Employee emp1=new SE("张三", "male", 21, "C#");
SE ss= (SE)emp1;
注意:强制类型转换只能按照堆中实际的对象类型进行转换,否则在运行时会抛出 类型转换异常ClassCastException
JAVA中为了保证程序执行的安全,在强制类型转换之前可以使用instanceof运算符来对要转换的对象实际类型进行检测
if(emp2 instanceof PE) {
PE ss= (PE)emp2;
System.out.println("转型成功!");
}else {
System.out.println("对象类型不匹配!");
}
3. 对象转型的实际应用
通过面向对象的方式模拟动物园的饲养员给动物喂食
需求:通过方法的重载实现饲养员可以给多种不同的动物喂食
//通过在Feeder类中提供多个不同的方法重载实现能够给多种不同的动物喂食
public class Feeder {
//给老虎喂食
public void feed(Tiger tiger) {
System.out.println("正在给老虎喂食...");
tiger.eatMeat();
}
//给狗喂食
public void feed(Dog dog) {
System.out.println("正在给狗喂食...");
dog.eatGT();
}
}
问题:能否在Feeder中通过一个方法就能够满足给所有的动物喂食?
public void feed(Animal animal) {
if(animal instanceof Tiger) {
Tiger tiger = (Tiger)animal;
tiger.eatMeat();
}else if(animal instanceof Dog) {
Dog dog=(Dog)animal;
dog.eatGT();
}else if(animal instanceof Cat) {
Cat cat=(Cat)animal;
cat.eatFish();
}
}
使用多态的方式进行处理:
多态的三个条件:
-
要求继承
-
要有父类引用指向子类对象
-
要有方法的重写
方法重写是发生在父子类之间,子类重写(覆盖)父类中的方法,重写的要求:方法名 参数列表 返回类型必须完全一样
编译期统一调用父类中的方法,但是在运行期自动调用实际子类的方法
在子类重写的方法上面可以使用一个注解@override来强制对重写的语法进行检查
public class Animal {
public void eat() {
}
}
public class Monkey extends Animal{
@Override
public void eat() {
System.out.println("猴子在吃香蕉..");
}
}
public void feed(Animal animal) {
animal.eat();
}
4.补充:方法的重写和重载(override/overwrite)
重载(Overload)
定义:重载是在一个类里面,方法名字相同,而参数不同。返回类型可以相同也可以不同。
每个重载的方法(或者构造函数)都必须有一个独一无二的参数类型列表。
最常用的地方就是构造器的重载。
重载规则:
- 被重载的方法必须改变参数列表(参数个数或类型不一样);
- 被重载的方法可以改变返回类型;
- 被重载的方法可以改变访问修饰符;
- 被重载的方法可以声明新的或更广的检查异常;
- 方法能够在同一个类中或者在一个子类中被重载。
- 无法以返回值类型作为重载函数的区分标准。
重写(Override)
定义:在子类中可以根据需要对从父类中继承来的方法进行改造,也称 为方法的重置、覆盖。在程序执行时,子类的方法将覆盖父类的方法。
重写规则:
-
子类重写的方法必须和父类被重写的方法具有相同的方法名称、参数列表
-
子类重写的方法的返回值类型不能大于父类被重写的方法的返回值类型
-
子类重写的方法使用的访问权限不能小于父类被重写的方法的访问权限
子类不能重写父类中声明为private权限的方法
-
子类方法抛出的异常不能大于父类被重写方法的异常
注意: 子类与父类中同名同参数的方法必须同时声明为非static的(即为重写),或者同时声明为 static的(不是重写)。因为static方法是属于类的,子类无法覆盖父类的方法。
关键字:super
在Java类中使用super来调用父类中的指定操作:
- super可用于访问父类中定义的属性
- super可用于调用父类中定义的成员方法
- super可用于在子类构造器中调用父类的构造器
注意:
-
尤其当子父类出现同名成员时,可以用super表明调用的是父类中的成员
-
super的追溯不仅限于直接父类
-
super和this的用法相像,this代表本类对象的引用,super代表父类的内存 空间的标识
