1.什么是面向对象编程
当我们在讨论面向对象编程时,不得不说到另一个常见的编程范式——面向过程编程(POP)。简单来说面向过程编程就是把解决问题的过程拆成一个个方法,通过一个个方法的执行解决问题。
面向对象的程序是由对象组成的,每个对象包含对用户公开的特定功能部分和隐藏的实现部分。程序中的很多对象来自标准库,还有一些是自定义的 。面向对象编程会先抽象出对象,然后用对象执行方法的方式解决问题。 OOP 通过“封装、继承和多态” 这三大特性来帮助开发者构建高效的程序结构。
2.对象与类
2.1 类
类( class )是构造对象的模板或蓝图 。由类构造( construct )对象的过程称为创建类的实例( instance ) 。
封装是指把一个对象的状态信息(也就是属性)隐藏在对象内部,不允许外部对象直接访问对象的内部信息。但是可以提供一些可以被外界访问的方法来操作属性。实现封装的关键在于绝对不能让类中的方法直接地访问其他类的实例域 。
//定义了一个学生类
public class Student {
private String name;//学生的姓名
private Integer age;//学生的年龄
//无参构造函数
public Student() {
}
//有参构造函数
public Student(Integer age, String name) {
this.age = age;
this.name = name;
}
//Student类中的Getter和Setter方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
OOP会让用户自定义Java类变得轻而易举。事实上,在 Java 中,所有的类都源自于一个“神通广大的超类”,它就是 Object。
2.2 对象
要想使用对象,就必须首先构造对象,并指定其初始状态 。 然后,对对象应用方法 。在Java中使用构造器( constructor)构造新实例 。构造器的名字应该与类名相同 。如上图学生类中的:
public Student() {
}
//有参构造函数
public Student(Integer age, String name) {
this.age = age;
this.name = name;
}
这两个分别为:无参构造函数和有参构造函数,都是构造器。要想构造一个 Date 对象,需要在构造器前面加上 new 操作符。
new Student();
这个表达式构造了一个新对象 。这样构造的对象仅使用了一次 。如果希望构造的对象可以多次使用,因此,需要将对象存放在一个变量中:
Student s = new Student();表达式 new Student()构造了一个Student类型的对象,并且它的值是对新创建对象的引用。
但要注意的是:Student s1;这条语句变量s1不是一个对象,实际上也没有引用对象。此时,不能将任何Student方法应用于这个变量上。必须首先初始化变量s1,这里有两个选择。当然,可以用新构造的对象初始化这个变量 :
s1 = new Student();
也可以让这个变量引用一个己存在的对象:
s1 = s; 现在,这两个变量s和s1引用同一个对象
一个对象变量并没有实际包含一个对象,而仅仅引用一个对象。在Java中,任何对象变量的值都是存储在另外一个地方的一个对象的引用。new 操作符的返回值也是一个引用。
3.继承
利用继承,人们可以基于已存在的类构造一个新类。继承已存在的类就是复用(继承)这些类的方法和域。在此基础上,还可以添加一些新的方法域,以满足新的需求。
Java 中的继承是通过 extends关键字实现的。子类可以继承父类的属性和方法,还可以重写父类的方法来实现特定的功能。
以下定义了一个男孩类,男孩类继承学生类。
public class Boy extends Student{
private String className;
public void study() {
System.*out*.println("男孩正在学习");
}
public void play() {
System.*out*.println("男孩正在玩");
}
}
男孩类中新增了一个className班级号的成员变量并且重写了父类中study()方法,还增加了一个paly()方法。
如果定义了一个Boy对象b,那么b可以用父类中的Getter和Setter方法为它赋上姓名和年龄的值。
关于继承如下 3 点请记住:
1) 子类拥有父类对象所有的属性和方法(包括私有属性和私有方法),但是父类中的私有属性和方法子类是无法访问,只是拥有。
2) 子类可以拥有自己属性和方法,即子类可以对父类进行扩展。
3) 子类可以用自己的方式实现父类的方法。
C++中支持多继承(一个子类可以继承多个父类),但Java不支持多继承。不过可以通过接口的方式,类似的实现多继承的作用。
4.多态
多态,顾名思义,表示一个对象具有多种的状态,具体表现为父类的引用指向子类的实例。
Student boy = new Boy();这条语句就是父类引用指向子类对象。
在main方法中运行以上程序语句,输出结果为:
在这个例子中,Student类有一个study()方法,而Boy类重写了该方法。通过父类类型的引用,程序可以表现出不同的行为,这是多态的典型应用。
多态在面向对象编程中可以体现在以下几个方面:
①方法重载:是指同一类中可以有多个同名方法,它们具有不同的参数列表(参数类型、数量或顺序不同)。虽然方法名相同,但根据传入的参数不同,编译器会在编译时确定调用哪个方法。
如:对于一个add()方法,可以定义为add(int a , int b)和add(double a , double b)。
②方法重写:是指子类能够提供对父类中同名方法的具体实现。在运行时,JVM会根据对象的实际类型确定调用哪个版本的方法。这是实现多态的主要方式。
如:在学生类中有study()这个方法,在Boy类中可以重写这个study()方法。
③多态也体现在接口的使用上,多个类可以实现同一个接口,并且用接口类型的引用来调用这些类的方法。这使得程序在面对不同具体实现时保持一贯的调用方式。
5.final关键字
在类上加入final关键字,表明这个类不能被继承。
如果再定义一个Dog类继承于Animal类就会报错,
成员变量也可以被声明为final。对于 final类型的成员变量来说,构造对象之后就不允许改变它们的值了。不过如果将一个类声明为final类,只有其中的方法自动成为final,不包括成员变量。
将方法或类声明为final主要目的是:确保它们不会在子类中改变语义。
6.抽象类
抽象的核心思想是隐藏复杂的实现细节,只展示必要的接口,从而简化复杂系统的理解和使用。Java 中通过abstract关键字实现抽象类和抽象方法。在抽象类中可以有普通方法(有方法体的方法)和抽象方法(没有方法体的方法)。
其中Animal类中eat()方法是普通方法,run()方法是抽象方法(没有方法体)。
子类继承抽象类时,必须实现所有抽象方法,否则子类也必须声明为抽象类。
下面是两种解决方式:将Dog类声明为抽象
public abstract class Dog extends Animal {
}
和在Dog类中实现Animal类中的抽象方法
public class Dog extends Animal {
@Override
public void run() {
System.*out*.println("狗跑");
}
}
由此可知,抽象类的实现实在子类中,并且抽象类不能直接实例化(即不能用new创建对象)。
7.接口
在Java程序设计语言中,接口不是类,而是对类的一组需求描述,这些类要遵从接口描述的统一格式进行定义 。
下面我们来定义一个接口类,
public interface Person {
void eat();
}
用interface关键字表明这个类是接口类,Person类中有一个eat()方法。
为了让类实现一个接口,通常需要下面两个步骤:
1) 将类声明为实现给定的接口 。
2) 对接口中的所有方法进行定义 。
要将类声明为实现某个接口,需要使用关键字implements:
public class Girl implements Person{}
并且要在Girl类中对Person接口的所有方法进行定义,
public class Girl implements Person{
@Override
public void eat() {
System.*out*.println("女孩在吃饭");
}
}
然而,尽管不能构造接口的对象,却能声明接口的变量:Person p;//正确
接口变量必须引用实现了接口的类对象:p = new Girl();
与接口中的方法都自动地被设置为public一样,接口中的域将被自动设为 public static final 。
尽管一个类只能允许有一个父类,但却可以实现多个接口。
以上就是这篇文章的所有内容了,我将在下一篇文章中详细的解释一下Object类,枚举类和反射。
作者:刘小花花
链接:https://juejin.cn