目录
今天我将为大家详细介绍面向对象语言最重要的三大特征,因为只有在了解本篇内容并且在日后练习中真正能理解掌握这三大特征,才能确保在后期成为一个合格的面向对象语言程序员。
一、封装
1.概念
封装是指,将类的某些信息隐藏在类内部,不允许外部直接访问。而是通过该类提供的方法来访问和操作这些信息。
2.封装的特点
(1)实现对需要的信息的隐藏
(2)只能通关特定的方法来访问
(3)便于修改操作,可自行加入控制语句
3.封装的具体表现
根据实际要求,类中的属性在不同包、不同类中需要被访问的条件,来给相应属性添加合适的访问权限修饰符。如下是两种常见情况:
(1)成员变量设为私有权限,不能在其他类中访问。
private String name;//没设私有前,外部类可随意对属性进行赋值操作
private int age;
解决方法:
向外界提供一个公共方法来访问。
public void setName(String name){ //提供公共的get、set方法供其使用
if (name.length()>2&&name.length()<6) {//设置控制语句,控制外部使用条件
this.name = name;
}
}
public String getName(){
return this.name;
}
public void setAge(int age){
this.age = age;
}
public int getAge(){
return this.age;
}
(2)构造方法私有化,外部无法直接调用
在生活中,类似于任务管理器窗口,我们不想让外界创建多个对象,点击几次只会出现一个窗口。
因此创建静态对象,在类加载时,只会创建一次,对外提供。
static Fz f3 = new Fz();
于是将构造方法私有化
private Fz(){
}
构造方法私有化导致外部类无法创建该类的对象,只能通过类名调用该类的方法。因此该方法是静态的。
解决方法:
向外界提供获取该对象的方法
public static Fz getF3(){
return f3;
}
二、继承
1.定义
将多个类的相同属性和行为进行抽取,定义到单独的一个类(基类)中。其他类可以继承基类,拥有基类的属性。
2.继承的优点
(1)减少代码冗余,提高复用性。
(2)更利于功能扩展(在子类中扩展自己独有的属性)。
(3)继承的出现使类与类间产生了 is - a 关系,为多态的使用提供前提。
3.继承使用语法
通过extends关键字,声明一个类B继承类A,格式如下:
[修饰符] class A{
......
}
[修饰符] class B extends A{
......
}
其中,B为子类,A为基类/父类。
4.实例演示
例如猫和狗,他们的共同属性——为他们get、set名字和年龄。这时我们可以抽取一个动物类,写出get、set方法,减少代码冗余。
猫类:
public class Cat extends Animal{ //继承了基类(动物类)
public void catch(){}
}
狗类:
public class Dog extends Animal{ //继承了基类(动物类)
}
基类(动物类):
//Object类是Java类体系中最大的类
//当一个类没有显示的继承其他类,那就默认继承Object类
//public class Animal extends Object{
public class Animal {
private String name;
private int age;
public void setName(String name){
this.name = name;
}
public String getName(){
return this.name;
}
public int getAge() {
return this.age;
}
public void setAge(int age) {
this.age = age;
}
}
因为猫类,狗类继承了基类(动物类),因此在main方法中为猫类,狗类名字、年龄赋值时,直接调用get、set方法即可。
注:子类会继承父类所有的实例变量和实例方法。
子类不能直接访问父类中私有的(private)成员变量和方法。
一个父类可以同时拥有多个子类,java只支持单继承,不支持多重继承。
5.方法重写
当父类方法功能实现不能满足子类需求时,可以对父类方法进行重写。程序执行时,将调用子类重写方法,因此也称为方法的覆盖。
●方法重写的规则:
1. 子类重写的方法必须和父类被重写的方法名称,参数列表,返回值类型一致
2.子类重写方法访问权限要大于等于父类方法访问权限。
注:构造方法,静态方法不能重写,成员变量不存在重写。
●代码演示
public class Animal {
public void print(){
System.out.println("狗不是人");
}
}
public class Dog extends Animal{
@Override
public void print(){
super.print();
System.out.println("狗吃饭");
}
}
这里,我们在Dog类中重写了Animal类的print()方法。
●@Override使用说明:
Java中的一个注解标签,定义在重写方法上,表示此方法是父类的重写。
非必要符号,只要重写格式正确即可。
保留原因:1.编译器可以判断重写是否正确
2.便于后续阅读代码,避免歧义
6.super关键字
在java类中使用super关键字来调用父类中的指定操作:
super可用于访问父类中定义的属性
super可用于调用父类中定义的成员方法
super可用于在子类构造器中调用父类的构造器
注:1.当父类子类出现同名成员时,super可以用来特指父类成员
2.super和this虽然用法相似,但两者实质截然不同。this代表对本类对象的引用;
但super可不是父类对象,在创建子类对象时把父类信息存到子类对象中,并不会创建父类对象,因此super只表示调用的是该子类对象中存储的父类成员。
●继承中的构造方法
子类继承父类时,不会继承父类的构造方法。只能通过“super(形参列表)” 的方式调用父类指定的构造方法。
规定super(形参列表),必须声明在构造器的首行。要么就不要在子类构造器中出现,编译器会默认super()写在首行。千万不能写在首行以外的地方,这是为了保证先对父类成员初始化。如果不先对父类成员初始化,就无法调用父类成员。
public class Dog extends Animal{
public Dog(){ //无参构造
super(); //在子类的构造方法首行,调用父类构造方法。就算不写出来,也会默认存在
System.out.println("dog构造方法");
}
public Dog(String name,int age){ //有参构造
super(name, age);
}
}
注:这里建议父类把无参和有参的构造方法都写上,以免子类构造器未显示的调用父类构造器,父类中又没有无参构造方法而报错。
三、多态
●多态定义
表示同一事物,在不同时刻,表现不同状态。
即父类的引用变量,指向子类对象。前提是必须存在继承关系,这就构成了多态
public class Test { //例如我们拥有一个基类Animal,子类Dog
public static void main(String[] args) {
Animal dog = new Dog();
}
●两个时期
1.编译期:写代码期间,类型是父类类型
2.运行期:运行代码期间,类型是子类类型
●向上转型
多态也称向上转型,将子类类型转为父类类型(编译期间只能调用父类中的方法,如果子类重写了父类方法,运行期间执行子类重写的方法)
public class Dog extends Animal{
int num = 4;
public void eat(){
System.out.println("狗吃");
}
static void show(){
System.out.println("狗");
}
}
public class Animal {
int num = 3;
public void eat(){
System.out.println("动物吃");
}
static void show(){
System.out.println("动物");
}
}
多态环境下对成员方法的调用、静态成员方法、成员变量的调用。
public class Test {
public static void main(String[] args) {
Animal dog = new Dog(); //向上转型,Dog对象提升到Animal对象
dog.eat(); //多态中成员方法的调用--编译看左边(父类),运行看右边(调用子类重写的方法)
dog.show(); //静态成员方法的调用--编译运行都看左边(调的是Animal类的方法)
System.out.println(dog.num); //打印结果显示3
//只有方法可以被子类覆写,变量不存在该说法--因此编译运行都看创建对象时等号左边(Animal类)
}
●多态的优点
提高代码扩展性
例如我们有两个子类(Cat)、狗类(Dog),他们继承于父类动物类(Animal)。
public class Dog extends Animal{ //狗类
public void sleep(){
System.out.println("睡觉");
}
}
public class Cat extends Animal{ //猫类
}
public class Animal { //动物类
public void eat(){
System.out.println("动物吃");
}
}
这时我们想对这些动物进行装箱操作。每对一种动物装箱,就要在装箱类(ZX)中写一个相应动物的put()方法会十分冗余,不利于后期维护。这时我们就可以用父类Animal类引用变量代表的各种子类对象这种多态思想,只写一个put()方法给他传入Ainmal变量即可。
public class ZX {
/*public void put(Dog dog){
System.out.println("狗进冰箱");
}
public void put(Cat cat){
System.out.println("猫进冰箱");
这种写法不利于程序扩展,每添加一个动物,就要添加一个方法,后期维护非常麻烦
}*/
//父类类型Animal既可以表示Dog也可以表示Cat,拥有多种状态,这就称为多态。大大减少代码冗余
public void put(Animal animal){
System.out.println("把"+animal+"装进冰箱");
animal.eat();
//instanceof关键字用于判断,表示父类类型持有的实际类型,是否是指定的类型
//调Dog类里的方法就要判断,传过来的animal是否指的是Dog类
if (animal instanceof Dog){
Dog dog = (Dog)animal;
dog.sleep();
}
}
}
如此一来,我们在使用时只需给定装箱方法传入的参数,即可完成各种动物的装箱操作。
public class Test {
public static void main(String[] args) {
ZX zx = new ZX();
zx.put(dog);
zx.put(cat);
}
}
●向下转型
父类引用仅能访问父类所声明的属性和方法,不能访问子类独有的属性和方法。
为了访问子类独有方法,用类似强制类型转化的方法将父类类型转为子类类型
public class Test {
public static void main(String[] args) {
Animal dog = new Dog();
Dog d = (Dog) dog;
d.sleep();
}
}
有关面向对象语言三大特征的部分到这里就结束了,但是java语言的学习之路才刚刚开始。本篇内容对于日后java语言学习有至关重要的作用,只有真正掌握这三种思想并能在实践中融会贯通才能更快捷,更深度的理解日后的学习内容,希望大家能有所收获,继续加油,共同进步!