编程语言有面向过程的也有面向对象的。像我们学习的C语言就是面向过程的语言,C++、Java等就是面向对象的语言。
可能很多人都听过面向过程,面向对象。那么到底什么是面向过程,什么又是面向对象呢?
面向过程,是一种以过程为中心的编程思想,就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了。
面向对象,是一种以事物为中心的编程思想,是把构成问题事务分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描叙某个事物在整个解决问题的步骤中的行为。与面向过程明显不同的就是封装、继承、类。
学习Java语言就要学习面向对象的编程思想,掌握封装、继承、多态、类等。
简单来说,面向对象的思想就是,将我们实现某个方法或者功能的代码进行封装,封装的好处就是,结构清晰,当我们用到该方法或者功能的时候直接找到对应的类就好了。
面向对象的思想有其明显的特点:
- 面向对象的思想是一种更为符合人类思维方式的思想
- 可以将复杂的事情简单化
- 很大程度上,减轻了编程者的编程负担
面向对象的特征是:
- 封装
- 继承
- 多态
类
Java语言中最基本的单位就是类,所以万事万物都可以用类来进行表示。
那么什么是类呢?
类是一组属性和方法(功能)的集合。属性是事物的描述信息,方法则是事物的行为。
我们将类中的属性和方法称为成员属性和成员方法。定义类说白了就是定义类中的成员属性和成员方法。
那么什么是对象呢?
对象就是类的实例(类的具体体现)。由于类是一组事物相同或相似的属性和行为的概括和抽象,所以我们不能直接使用类中的成员属性和成员方法。若要使用,我们就要创建类的实例即对象来使用。
如何定义类呢?
class 类名{
成员属性;
成员方法;
}
举例:
学会了定义类后,就可以创建对象来使用类的成员了。
对象的创建:
类名 对象名 = new 类名();
成员变量的使用:
对象名.成员变量
成员方法的使用:
对象名.成员方法( . . .)
举例:
以上述例子为例,简述一下创建对象这个过程在内存中的流程。
谈到对象这个概念时,有一个概念叫匿名对象。我们一般创建对象时都是 类名 对象名 = new 类名();这样的形式,实际上我们以这样的方式创建对象时是给对象起了一个名字。那么匿名对象就是我们在创建对象的时候没有给他起名字,即直接以 new 类名();的方式创建的对象。
匿名对象使用的次数不是很多,也不是特别常用。可以在以下场景中使用匿名对象:
- 当我们调用方法,且仅调用一次时可以使用匿名对象,匿名对象可以当做实际参数进行传递。
举例:
在类中,有成员属性(成员变量)和成员方法。那么类中的成员变量和局部变量有什么区别呢?
- 首先,两者在类中的位置不同,成员变量是在类中方法外;而局部变量是定义在方法内或者是方法声明中
- 其次,两者的生存周期不同,成员变量是随着类的调用或者对象的创建而产生,随着对象的消失而消失;局部变量是随着方法的调用而产生,随着方法的调用完毕而消失
- 然后,两者在内存中的位置不同,成员变量是在堆内存中;局部变量在栈内存中
- 最后,两者的初始化值不同,成员变量有默认的初始化值,若为int类型,则默认初始化值为0,若为float,double,则为0.0,若为char,则为空格字符;局部变量没有默认的初始化值,所以必须定义赋值后才能使用
当类的成员变量名和局部变量名重名时,在使用原则上采取的是就近原则。
之前谈到过方法的调用问题,方法的形参一般有两种类型:
- 基本类型,传的是值,即形参的值的改变不影响实参的值,这样的调用方式可以成为值传递
- 引用类型,传的是引用,即形参的值的改变会影响实参的值,这样的调用方式可以成为引用传递
当方法的形参的类型是类 类型的时候,这个时候需要传的是该类的对象。
构造方法
什么是构造方法?
构造方法是在创建对象的时候,给对象的成员进行初始化的。
构造方法的特点:
- 方法名与类名相同
- 没有返回值类型
- 没有具体的返回值
举例:
有的人可能会想到,既然构造方法是用来初始化对象的,那么可以在创建对象的时候,就对对象的成员变量进行赋值操作吗?
当然可以了,此时就需要进行构造方法的重载了。
举例:
需要注意的是:
- 如果我们没有自己在类中定义构造方法,系统会默认给我们提供一个无参构造方法
- 如果我们自己在类中定义了构造方法,那么系统不会给我们提供无参构造方法。创建对象时只会调用我们自己的构造方法。
- 如果我们自己在类中定义了构造方法,还想使用无参构造方法时,就需要自己定义无参构造方法。最好就是每次定义类时,自己定义无参构造方法。
总结一下创建对象(Student student = new Student())都做了哪些事情:
- 首先将该类对应的class文件(Student.class)加载进内存的方法区
- 在栈内存中为该类对象对应的变量(student)开辟内存空间
- 在堆内存为该类的对象(new Student())开辟内存空间
- 对对象的成员变量进行默认的初始化
- 对对象的成员变量进行显式初始化
- 通过构造方法对对象的成员变量进行赋值
- 对象初始化完毕后,将该内存的地址值赋给变量(student)
封装
何为封装?
封装就是隐藏对象的属性和实现细节,只提供对外公共访问的方式。
封装的优点:
- 隐藏了实现细节,提供了公共访问方式
- 提高了代码的复用性
- 提高了代码的可维护性
- 提高了程序的安全性
封装的原则:
- 可以将不需要对外提供的内容隐藏起来,如属性
- 对外只提供可以公共访问的方法
那么,问题来了,我们如何做到将不需要对外提供的内容隐藏起来呢,一般来说在类中我们只需将属性隐藏起来。这个时候,我们可以使用Java中的private关键字。
private:
private:私有的,是一个权限修饰符。
private可以修饰成员变量和成员方法,被修饰的成员只能在本类中被访问。
这样使用private关键字,我们就可以实现封装的目的。但存在一个问题就是,我们的成员变量无法直接访问进行赋值、输出等操作,所以还需要在类中提供成员变量对应的get和set方法来间接访问成员变量。
一般而言,对于成员变量xxx,对应一个getXxx()和setXxx()方法。set方法用来进行赋值操作,get方法用来获取成员变量值。
举例:
this
在上述例子中,set方法里,有一个关键字this。那么为什么会有this关键字呢?
在类的成员方法中,若方法的局部变量和类的成员变量同名且需要在方法中用到两个变量时就需要使用this关键字来说明该关键字后的变量是成员变量。如果不使用该关键字就会出现一个现象:局部变量隐藏成员变量(就近原则)即两个变量都是局部变量。
this可以理解为当前类的对象引用。简单来说就是它代表当前类的一个对象。谁调用这个方法,那么该方法的内部的this就代表谁。
使用this就可以解决局部变量隐藏成员变量的问题。
至此,我们可以通过两种方式来进行类的对象的成员变量的初始化和赋值操作:
- 通过构造方法,即在创建对象时,就进行赋值操作。
- 通过set方法。
static
在创建好几个对象时,有的时候对象的某个属性的值是一样的,除了可以在类中将成员变量的值直接赋值为对应的值外,还有一个方法就是使用static关键字。
static:静态的,它是一个状态修饰符。
由它修饰的变量是存储在方法区的静态区的,且该变量是共享的变量。
具体来说static关键字的特点:
- 它是随着类的加载而加载的
- 它是优先于对象存在的(对象是要创建才会存在)
- 它是被类的所有对象所共享的(该变量只有一份,在内存中只给它开辟一份内存空间)
- 不同于成员变量,它可以通过类名调用(且最好使用类名调用)也可通过对象名调用。在类中由static修饰的成员,我们一般称之为类成员。
在使用static关键字时也需要注意以下几点:
- static中不能出现this关键字。
原因:static是随着类的加载而加载的,而this是创建对象后才可以用的,又由于,静态是先于对象存在的,一个存在的事物怎么可以使用不存在的事物呢? - 静态方法只能访问静态的成员和静态的方法,不能访问非静态的成员和方法;但是非静态的即可以访问非静态的,也可以访问静态的。
static是一个状态修饰符,可以修饰变量,被static修饰的变量称为静态变量,那么静态变量和成员变量有什么区别呢?
- 首先,两者所属不同。静态变量是属于类的,也称为类变量;成员变量是属于对象的,也称为实例变量。
- 其次,两者在内存位置不同。静态变量是存储在方法区的静态区;成员变量是存储在堆内存。
- 然后,两者生存周期不同。静态变量是随着类的加载而加载,随着类的消失而消失;成员变量是随着对象的创建而产生,随着对象的消失而消失。
- 最后,两者的调用方式不同。静态变量是用类名或对象名来调用,但一般使用类名调用;成员变量是只能用对象名来调用。