Java是一门纯面向对象的编程语言,在Java中任何事物都会被抽象成类,C++中可以出现全局变量和全局函数,而Java则不可以,因为类和对象以及继承和多态知识点较多,所以本篇博客先对类和对象进行记录,下一篇博客记录Java如何实现继承与多态。
面向对象的编程思维
在学习类和对象之前需要先了解什么是面向对象的编程思维,与面向对象相对应的还有面向过程,例如C语言就是面向过程的编程语言。面向过程的编程的精力主要放在实现什么东西,而面向对象则放在了抽象实体上。
1、面向过程的优点和缺点
(1)优点:对于业务逻辑简单的程序,可以实现快速开发,前期投入的成本比较低。
(2)缺点:比较难以解决较为复杂的业务逻辑,而且面向过程使软件元素之间的“耦合度”较高,导致扩展力比较弱,无法达到组件复用。举一个简单的例子就是,当我们以纯面向过程的思维来编写一个软件,然后某一时刻我们需要新增一个功能,此时新增的这个功能可能会牵一发而动全身,导致整个软件的编码逻辑都发生改变,这就是耦合度高和扩展力差的体现。就好像制作一支笔,笔芯笔头笔杆都连在了一块,是不可拆卸不可更换的,此时如果三个元件坏了一个就不能用了。
2、面向对象的优点和缺点
(1)优点:耦合度低,扩展力强,更容易解决现实世界额复杂业务逻辑,组件复用性强。再用笔的例子来解释:我们把笔芯笔头笔杆造成可拆卸式的,当某一个零件坏掉了我们可以更换新的元件,或者某一个部位我们觉得不好看了也可以更换,这就是扩展力的体现。
(2)缺点:前期投入成本高,需要进行独立体的抽象以及大量的系统设计。就好似面向过程时我们造笔只需要一个模具,最后灌上墨水就好,但面向对象的话我们需要制作三个模具,然后再将三个模具拼装等等,这就是前期投入较大。
3、面向对象的三大特征
(1)封装:封装就是对每个独立体进行一定程度的包装,在类和对象这一部分会记录一下封装性。
(2)继承:由于每一个类之间不会是毫无关系的,所以当一些共同的特征出现在两个不同类之间时,可以采取继承的方式来减少编码量,起到代码复用的效果。
(3)多态:在继承之后,子类会产生多种特点,继承父类的特点同时兼具有自己的特性,这就是简单的多态。
4、面向对象开发软件的三个阶段
(1)面向对象的分析:Object oriented analysis,简称OOA。
(2)面向对象的设计:Object oriented design,简称OOD。
(3)面向对象的编程:Object oriented program,简称OOP。
一、类
类是从现实世界抽象出来的一种抽象体,比如动物、人、车等,而每一个类都会有最基本的状态(属性)和动作(行为)。
1、类的定义方法
我们以动物类为例,要去定义一个人类,它的属性包含姓名,体重,身高,年龄等等,动作则包含吃,睡,走路等。
public class Person{
//属性
private String name;
private int height;
private int weight;
private int age;
//行为
public void eat(){
System.out.println("Person is eating");
}
public void sleep(){
System.out.println("Person is sleeping");
}
public void walk(){
System.out.println("Person is walking");
}
}
在定义时使用了一些关键字,和C++基本相同,只是Java在每一个属性和行为的前面都加上了关键字。由上面的代码我们也可以看出Java中的类如何定义:
[修饰符列表] class [类名]{
//类体,包含属性和行为
}
2、类需注意的一些点
(1)在类体内,方法体外定义的变量称之为“实例变量”,例如我们上面声明的name、height、weight、age就是实例变量,在上一节我写过实例变量在定义时若未初始化则系统会赋予默认值,而局部变量则不会。
(2)上一节写过Java中有两种数据类型:基本数据类型和引用数据类型,而类属于引用数据类型,我个人喜欢把这个数据类型当做指针理解,也就是说Java中的以下语句:
Person p=new Person();
等同于C++中的
Person *p=new Person();
因为Java中的引用存储的是对象的地址,而对象又是存在堆内存上的,这就和C++中的指针不谋而合了,同为存储地址,同为指向的数据存储在堆上。个人是这么理解的,各位看官视情况而定了。
(3)同C++,Java中也是有静态变量的,C++中的静态变量存储在虚拟地址空间的.bss段或者.data段,但Java中的静态数据则存储在方法区,关于静态关键字修饰的实例变量后面会展开说一下。
二、对象
类是由实际存在的东西抽象出来的,而由类实例化出来的东西便称作对象。上面的Person实例代码就是通过new运算符去创建对象,我们将上面的代码放下来分析一下
Person p=new Person();
1、实例化对象的方法
如上,我们需要先声明一个类类型的引用p,再使用new关键字,然后调用相应类的构造方法(构造方法下面会说)。就创建好了一个对象。相对于C++来说,Java的对象只能存储在堆区,所以必须要使用new关键字。
2、引用和对象的区别
上面我们说了实例化对象的方法,下来着重说一下引用和对象的区别
(1)引用
引用类似于C++中的指针,但没有显式的写出指针符号,引用用于存储对象的地址,是函数栈帧中保存堆上内存地址的局部变量。JVM会在栈区读取引用中的地址数据,然后去堆区中查找相应的对象。
(2)对象
对象是在堆区中真正存储数据的内存块,对象需要使用引用来读取,一个对象占用一块独立的内存,用于存储各种数据。
(3)例
public class Test{
public static void main(String[] args){
Person p=new Person();
System.out.println("Hello world!");
}
}
class Person{
//Person的属性及行为
}
以上这段代码先创建了一个Person对象,使用引用p指向该对象,然后输出"Hello world",以此程序分析一下JVM中的内存划分。
事实上引用就是个指针,只是在说的时候需要区分开。
3、垃圾回收机制
Java中没有指针,所以只能使用引用来操作堆内存,但当一个引用指向新的对象的时候,原来的对象就会没有相应的引用去指向,此时就出现了类似于C++中的“内存泄漏”问题,但Java中没有delete关键字,而是使用一种自动回收垃圾的机制:Garbage Collection(GC),也就是说Java中不需要程序员去管理已经失效的对象,而是由JVM自行判断并对其进行回收。那么已经失效是怎么断定的呢?
当一个对象没有更多的引用指向时,此时对象无法被访问(因为访问对象只能通过引用的方式进行),该对象就变成了垃圾,JVM会调用垃圾回收器对该对象进行回收
当引用被置空后,再使用该引用去访问“实例”相关的数据,运行时则会产生“java.lang.NullPointerException”,其实就是空指针异常。
4、封装性
(1)封装的好处
a.封装之后,对于该事物来说只能看到简单的情况,封装复杂的逻辑,然后对外提供简单的操作入口即可。
b.封装之后才可以真正形成独立体。
c.封装意味着程序可以重复使用,适应性强。
d.封装之后也提高了安全性
(2)如何封装
a.和C++相同,属性私有化,限制访问权限在类内。
b.对外提供简单的外部接口,通过方法来访问私有变量。
三、构造方法
构造方法又称作构造函数、构造器、Constructor,用于在创建对象的同时初始化实例变量的内存空间。他的定义方法和在C++中相似,无返回值,方法名和类名相同。例如:
//Test类,提供了构造方法
class Test{
Test(){
System.out.println("Test的构造方法");
}
...
}
Java中构造方法的调用形式:new 类名(实参列表);例如:
public class TestClass{
public static void main(String[] args){
Test t=new Test();
}
}
class Test{
Test(){
System.out.println("Test的构造方法");
}
...
}
构造方法没有显式的写出返回值,但其返回值事实上是一个引用,对新对象的引用,和C++一样,Java在用户未提供构造方法时会默认生成一个方法体委空的缺省构造方法,但在显式定义构造方法后系统不会再缺省构造方法,另外构造方法也可重载(多态语言的特征)。
注意:因为java不支持函数参数默认值,所以如果某一个类的构造方法需要包含默认值,则可以在无参构造方法中调用有参构造方法,将默认值传入:this(默认值列表);且该语法只可以出现在构造方法的第一行。例如:
//通过无参构造调用有参构造的方式构造一个默认值为10和20的对象
class Test{
public int a;
public int b;//两个类的实例变量
//无参构造
Test(){
this(10,20);
System.out.println("Test的无参构造方法");
}
//有参构造
Test(int a,int b){
this.a=a;
this.b=b;
}
}
但注意this关键字需要出现在无参构造的第一行,否则会报错,这里将this和输出语句换一下位置试一下则会报错:
关于this关键字会在下面详细解释。
四、相关的关键字
1、this关键字
在实例化成功对象申请到内存后,对象内部生成一个this指针,该指针指向该对象本身,用于标识自身对象的内存,毕竟在程序运行过程中不会只new一个对象,因为C++中使用到“this指针”这个名称,所以在这里我也用一下,比较容易理解。java的this参数多数情况可以省略,但当实例方法中的参数名和实例变量名相同则需要加上this关键字,否则会无法识别,比如:
//在此处使用到了局部变量a和b来向类的实例变量a和b传值,此时为了注意不混淆两个变量,会用this关键字来特别指明。
class Test{
public int a;
public int b;//两个类的实例变量
//有参构造
Test(int a,int b){
this.a=a;
this.b=b;
}
}
和C++一样,Java中的static静态方法也不会产生this指针(static关键字在下面说),虽然使用对象也可以访问静态方法,但是并不提倡,因为会给使用者造成误解。但也因为静态方法和对象无关,所以不会产生空指针异常(java.lang.NullPointerException)。
2、static关键字
静态变量
一般将体现整个类的特征的变量定义为静态变量,若定义成实例变量则在每一个对象中都会保存数据,浪费空间。凡是使用了static关键字定义的变量都称作静态变量。静态变量存储在方法区内存中,在类加载的时候就已经开辟,需要在方法体外初始化(声明时初始化);
静态代码块
(1)语法格式:static{}
(2)静态代码块在类加载时执行,并且只执行一次,在main方法前执行。
(3)静态代码块可以编写多个,遵循自上而下的顺序依次执行
(4)可以做记录日志
(5)静态代码块是java为程序员准备的一个特殊的时刻,即类加载时刻。
(6)通常在静态代码块中完成预备工作,例如:初始化连接池,解析配置文件等。
与静态代码块相对应的还有实例代码块:只需要将代码块放在{}中即可,实例代码块在构造方法之前执行,也可以编写多个,这个时机就是“对象初始化时机”
静态方法
(1)静态方法中无法直接访问实例变量和实例方法
(2)大多数工具类中的方法都是静态方法,因为工具类只是为了方便编程,不用new对象是最好的(逻辑类似于C和C++中的全局方法)
以上是关于Java类和对象的一些知识,因为初学,难免会有疏漏和错误,大神多多指正。下一篇记录继承和多态。