相信接触过Java的朋友们都听过一句话,在Java中,一切皆对象;而Java本身就是一种面向对象的一种编程语言,其核心思想是使用对象封装数据和操作。今天就让我们来浅谈一下Java面向对象的编程的几大特点——封装、继承和多态。由于本人不是专业的Java开发人员,因此下文只是用自己所学的Java知识和自己的语言来描述自己对Java这几个关键特性的理解。
在Java中,一切皆对象 ,对象是类的实例,而类可以描述为对象的模版或蓝图,在类里面,类定义了对象的成员变量和方法。
1.封装
1.1对封装的具体理解
用专业用语来说,封装是面向对象编程的一个核心概念,他指的是将对象的属性(数据)和操作(方法)组合在一起,并隐藏对象的内部细节,只对外公开一个可以操作的接口。封装的目的是为了保护对象的数据不背外部直接访问或修改,同时提供一种安全的方式来与对象交互。通俗点来说,封装就像是在下雨天给对象的属性和方法一把雨伞,外界不能直接访问对象的内部信息,当需要访问某个对象的属性或方法时,只需把他要访问的属性或方法给他,其他使用不到的属性和方法就可以得到保护。
1.2封装实现的具体实例
下面我们来举个例子,定义了一个public类Student,在这个类中定义了四个属性一个方法,其中在属性前面均定义了属性或方法的类型,如name为String型,方法是void型。Student类将name、age、height、weight和方法introduction()封装成一个整体,当要用到这个类中的属性时,必须先创建一个新的对象,即Student类的一个实例,代码为Student student1=new Student();// 用new
关键字来创建一个新的 Student类的对象,student1是新创建的Student类的实例名称,这是就可通过student1.属性来访问和调用Student中的属性,也可调用其中的方法。
public class Studen { String name; int age; float height; float weight; public void introduction(){ System.out.println("我最喜欢的一句话是:“梅子熟时,乍知春去,始觉情深”"); }
下面通过定义一个主类调用这个类
public class person{ public static void main(String[] args){ Student Zhang=new Student();//创建一个名为Zhang的Student类的一个实例对象,该对象可以 访问Student类中的成员变量和实例方法 Zhang.name="张明";//自定义赋值Student类中的变量 Zhang.age=20;//注意给变量声明具体值时,要匹配对应的变量类型 Zhang.height=183; System.out.println("我是"+Zhang.name+","+"今年"+Zhang.age+"岁"+","+"我的身高是"+Zhang.height+"cm");//可以输出对应的变量内容 Zhang.introduction();//这里调用了Student类中的方法introduction(),只需这一句就可执行方法里的具体内容。 } }
运行结果如下:
这里没有访问Student类中的weight属性,所以weight属性就不用被拿出来,只取出来能用到的,用不到的属性和方法就得到了保护。
注意:一个java源文件中里可以有多个类,但最多只能有一个主类,并且只有主类中有main()函数的程序才能够运行,并且要求Java文件的命名必须要与主类的名字一致才可以。像上文介绍的例子两个类都带有public,即两个都是主类,一山不容二虎,所以如果将这两个类放到一个源文件中电脑就会报错,换个方面理解一下,上面说Java的命名和主类的名字是一致的,那现在有两个主类该给Java写哪个名字呢?这样是不是就可以清楚地理解为什么一个源文件中最多只能有一个主类的原因。如果想要保持第一个Student类的public属性,又想在main()函数中正常调用Student类只需要将这两个文件在文件夹中的保存位置对其即可,即保存在同一包中。当然如果想都写在同一个源文件中的话,只需将Student类前的public删除即可,即只保留主类的public关键字。
2.继承
2.1对继承的具体理解
继承,专业用语来讲是继承是一种由已有的类创建新类的机制。它允许一个类(称为子类或派生类)继承另一个类(成为父类或超类)的属性和方法。子类可以重用父类的代码,并在此基础上添加新的功能或修改现有的功能,即实现以类创类。利用继承,我们可以先创建一个共有属性的一般类,根据该一般类再创建具有特殊属性的新类,新类继承一般类的状态和行为,并根据需要增加它自己的新的状态和行为。由继承而得到的类称为子类,被继承的类称为父类(超类)。
接下来谈谈我自己对继承的理解,我们都知道类中有两个重要成员:成员变量和方法,所谓继承在我看来就是子类继承父类的成员变量作为自己的一个成员变量,就好像他们在子类中直接定义了一样,可以被子类中自己定义的任何实例方法调用,也就是说,如果子类中定义的是类方法不能操作弗雷德某个成员变量或方法,那么该成员变量就没有被子类继承。
在Java中使用关键字extends来定义一个类的子类,格式如下:
class 子类名 extends 父类名 {
…
}
例如:
class Student extends People {
…
}
说明:把Student类定义为People类的子类、People类是Student类的父类
一个子类只能有一个父类,即Java不支持多重继承,但一个父类可以被多个子类继承,大家习惯称子类与父类的关系是"is-a"关系。
下面具体讨论一下子类和父类在不同位置情况下的继承性:
- 子类和父类在同一个包中的继承性:如果子类和父类位于同一个包中,那么子类可以继承父类中不是private类型的成员变量和private类型的方法作为子类的成员变量,即无法继承私有类成员变量和方法。
- 子类和父类不在同一包中的继承性:如果子类和父类不在同一个包中,那么子类只能继承protected和public类型的成员变量和方法作为自己的成员变量和方法,即无法继承友好类和私有类成员变量和方法。
子类继承父类只是继承父类中的某一部分成员变量和方法,但用子类创建对象时,不仅子类中生命的成员变量被分配了内存,而且弗雷德成员变量也都分配了内存空间,但只将其中一部分(子类继承的那部分)作为分配给子类对象的变量。子类中还有一部分方法是从父类继承来的,这部分方法可以操作这部分未继承的成员变量。
2.2继承实现的具体实例
在下面的示例中,创建了一个父类Animal和一个子类Dog,子类继承父类,在子类中添加了新的成员变量和方法,子类可以调用父类中的方法,如本例中dog.speak();speak()是在Animal中定义的方法,但Dog从Animal中继承得来,所以可以使用,但父类不能调用子类的方法,比如在最后一句加上anima.eat();会发生报错"Can't reslove method 'eat' in 'Animal' ",意思就是在Animal这个类中找不到eat()方法。
//定义Animal为父类 class Animal{ String name;//定义父类的变量和方法 double weight; public Animal(String name, double weight){ this.name=name; this.weight=weight; } void speak(){ System.out.println("Hello,大家好,我是"+name+"!"); } } //定义子类Dog继承父类Animal,通过extends实现 class Dog extends Animal { int age;//定义子类新的变量 public Dog(String name, double weight, int age){ super(name, weight);// 调用父类构造函数 this.age=age; } //定义子类新的方法eat() void eat(){ System.out.println(name+"正在吃美味的骨头!"); } } //实例验证 public class Example { public static void main(String[] args){ Animal animal=new Animal("狗狗王",50);//创建一个Animal实例 animal.speak();//输出“Hello,大家好,我是狗狗王!” Dog dog = new Dog("臭臭",6,1 );//创建Dog实例 dog.speak();//输出“Hello,大家好,我是臭臭!” dog.eat();//输出“臭臭正在吃美味的骨头!” } }
注意:子类不继承父类的构造方法,因此,子类如果想使用父类的构造方法,必须在子类的构造方法中使用,并且必须使用关键字super来表示,而且super必须是子类构造方法中的头一条语句。
3.多态
3.1对多态的具体理解
多态也是面向对象编程的一个重要特性,它允许使用一个接口来表示多种不同的数据类型。简单来说,多态意味着同一个方法可以应用于不同类型的对象,并且每个对象都会根据其自身的类型执行特定的操作。
在 Java 中,多态主要体现在两个方面:
-
方法重写(继承与多态):
当一个类有很多子类时,这些子类都重写了父类中的某个实例方法,那么当我们把子类创建的对象的引用放到一个父类的对象中时,就得到了该对象的一个上转型对象,那么这个上转型对象在调用这个方法时就可能产生不同的行为。比如,Dog类的上转型对象调用“叫声”方法时产生的行为是“汪汪”,而“Cat”类的上转型对象调用“叫声”方法时,产生的行为是“喵喵”等。所以多态性在继承方面的体现就是指父类某个方法被其子类重写时,可以各自产生自己的功能行为。
- 为了实现方法重写,子类中的方法需要满足以下条件:
- 方法名称、返回类型和参数列表必须与父类中的方法完全相同。
- 子类方法的访问修饰符(public, protected, 或者 default)不能比父类更严格。
- 如果父类方法是
final
的,那么子类就不能重写这个方法。
- 子类可以覆盖(或重写)父类中同名的方法。当使用子类的引用调用这个方法时,会根据实际的对象类型来决定执行哪个版本的方法。
-
接口实现(接口与多态):
- 接口回调就是把实现接口的类的实例的引用赋值给接口变量后,该接口变量就可以毁掉累充写的接口方法。由接口产生的多态就是指不同的类在实现同一个接口时可能具有不同的实现方式,那么接口变量在回调接口方法时就可能有多种形态。就比如说,对于两个正数a和b,有的人使用算术平均公式计算(算数)平均值,而有的人使用几何平均公式计算(几何)平均值。
- 类可以实现一个或多个接口,从而继承接口中定义的所有抽象方法。这使得不同的类可以提供对同一接口的实现,实现了多态。
- 接口中的所有方法默认都是
public
和abstract
的,因此不需要显式声明。
3.2多态实现的具体实例
(继承与多态实例)
在本例中定义了一个父类(Animal)两个子类(Dog和Cat类),父类中有一个方法,在子类中可以对这个方法进行重写以适应该子类的功能行为,如 Dog类speak()发出的叫声为“汪汪...汪”,Cat就是“喵喵...喵”,即父类某个方法被其子类重写时,可以各自产生自己的功能行为。
//父类 class Animal{ void speak(){ } } //子类 class Dog extends Animal{ @Override//方法重写 void speak() { System.out.println("汪汪...汪");//方法重写,产生适合Dog类的行为 } } //子类 class Cat extends Animal{ void speak(){ System.out.println("喵喵...喵");//方法重写,产生适合Cat类的行为 } } //实例验证 public class Exemple2 { public static void main(String[] args){ Animal animal=new Animal(); animal=new Dog();//创建实例对象 animal.speak();//调用适应Dog类的方法 animal=new Cat();//创建实例对象 animal.speak();//调用适应Cat类的方法 } }
运行结果:
(接口与多态实例)
下面这个实例是使用一个共同的接口计算两个正数a和b的平均值,可以使用算术平均公式计算(算数)平均值,也可以使用几何平均公式计算(几何)平均值。
//定义一个接口 interface ComputeAverage{ public double average(double a,double b); } class A implements ComputeAverage{ @Override//自定义回调接口方法,求算数平均值 public double average(double a, double b) { double aver=0; aver=(a+b)/2; return aver; } } class B implements ComputeAverage{ @Override//方法重写,自定义回调接口方法,求几何平均值 public double average(double a, double b) { double aver=0; aver=Math.sqrt(a*b); return aver; } } public class Exemple3 { public static void main(String[] args){ ComputeAverage computer; double a=11.23,b=22.78; computer=new A(); double result= computer.average(a,b); System.out.printf("%5.2f和%5.2f的算术平均值:%5.2f\n",a,b,result); computer=new B(); result= computer.average(a,b); System.out.printf("%5.2f和%5.2f的几何平均值:%5.2f\n",a,b,result); } }
运行结果:
综上所述,封装,提供了数据的保护机制,使得对象的状态对外部不可见,只通过公共接口进行访问。继承,基于封装,子类可以复用父类的属性和方法,并且可以扩展或修改这些特性。多态,利用了继承,允许使用相同的接口(如方法名)处理不同类型的对象,提高了代码的灵活性和可维护性。面向对象编程通过封装、继承和多态实现了数据抽象、代码复用和灵活的设计。在实际开发中,这三个概念通常会结合使用,以构建出更加健壮、易于理解和维护的软件系统。