面向对象
学习内容
1. 不同编程语言在编程思想上又什么区别?
-
C语言采用面向过程
-
C++一半采用面向对象一半采用面向过程(半面向对象)
-
Java采用面向对象(完全面向对象)
2. 面向过程和面向对象的区别
面向过程
面向过程开发特点
面向过程注重实现功能的步骤,以及步骤之间的因果关系(高耦合,低内聚)
实现一个功能需要分为四个步骤:A步骤、B步骤、C步骤,对于步骤之间的因果关系可以这样来看:
因为A步骤----->所以B步骤,因为B步骤----->所以C步骤。
对于一个项目要求实现两个功能:A功能,B功能,A功能需要2个步骤(A1步骤---->A2步骤),B功能也需要2个步骤(B1步骤--->B2步骤) ,
在面向过程开发会这样做:将A功能的步骤划分称一个模块A,将B功能中的步骤划分一个模块B,因为模块A(A1步骤---->A2步骤)----> 所以模块B(B1步骤--->B2步骤)
从上面的步骤来看,面向过程开发中,代码之间的关联程度很高,如果有一个步骤出错,会导致所有的功能都出现问题(牵一发而动全身)。
面向过程开发的优缺点
- 优点:在面向过程开发中,对于小型项目的开发,效率较高,前期不需要进行对象的提取、模型的建立,可以直接上手写代码,编写因果关系。(快速开发)
- 缺点:在面向过程开发中,没有对象的概念,代码直接按的黏连程度很高,扩展力很差。不适合用于大型项目开发。
面向对象
面向对象开发特点
面向对象编程思想,是以对象为主,更符合人类的思维方式。面向对象在开发中,把程序分为不同的单元,每个单元都实现成对象(这些对象都是独立的,彼此之间的关联程度很低),在给这些对象一个环境驱动一下,让他们彼此协作起来形成一个系统(使用面向对象开发程序:耦合度低,扩展里强)
面向对象开发的优缺点
用案例去理解面向过程与面向对象
蛋炒饭与盖饭:
蛋炒饭:
对于蛋炒饭来说,米饭和蛋混合在了一起,没有独立对象的概念。
假设客户提出需求,只要米饭不要鸡蛋怎么办?
对于客户提出的需求,软件开发者必须实现,于是开始扩展程序,这对于软件开发者来说是一场噩梦。
盖饭:对于盖饭来说,盖饭可以是由不同的菜(鱼香肉丝、香辣肉丝…) + 米饭组成,是有独立对象的概念,
鱼香肉丝可以看成一个独立对象,米饭也可以看成一个独立对象。
假设对象提出需求,我想吃鱼香肉丝盖饭。
软件开发者只需要将鱼香肉丝和米饭准备好,并且给他们一个“盖”的动作,这两个对象便组合在了一起。
假设用户不想吃鱼香肉丝盖饭了,想吃香辣肉丝盖饭
这时只需要将"鱼香肉丝"对象换成"香辣肉丝"对象即可。
面向过程主要关注的是:实现功能的步骤以及整个过程
面向过程主要关注的是:对象A、对象B、对象C,这些对象可以任意组合(ABC、BCA、CAB等…)
3. 面向对象的特征
4. 面向对象开发软件生命周期
- OOA -----> 面向对象分析(Object-Oriented Analysis)
- OOD -----> 面向对象设计(Object-Oriented Design)
- OOP -----> 面向对象编程(Object-Oriented Programing)
5. Java面向对象
什么是类?
- 概念:类是能描述对象的共同特征。是一个模板,是不存在的
- 内容:
什么是对象?
- 概念:对象是一个真实存在、具体的事物
- 内容
类与对象的关系
- 类:是抽象、概念性的,是在现实世界中不存在的,是由人类观察一类对象并对其进行共性抽取(抽象) 而形成的一个模板(这个“模板”可以用于描述一类对象的共同特征)
- 对象:对象是在现实世界中一个具体存在的个体,世界上的任何物体都可以看作为一个对象,在程序开发中,对象是由类实例化(创建) 出来的。
- 实例:对象又被称作为实例
- 实例化:在程序开发中,创建对象便是类的实例化
- 抽象:此处的抽象是指,对一类对象进行共性提取,从而形成类
由上图可见,HUAWEI实体机和小米实体机都是一个对象,他们都是于安卓手机,将这两个对象的共同特征提取(抽象)出来,便是一个Android类,对于Android手机和Ios手机,将他们的共同特征继续提取(抽象)便成了一个手机类…
如何创建一个对象
- 首先需要有一个类,用于实例化对象
class User{
//属性(描述对象的状态信息)
int no; //用户编号
String username; //用户名
String password; //密码
//行为(描述对象的动作/行为)
sayHello();
public static void sayHello(){
System.out.println("你好,我是" + username + "很高兴认识你");
}
}
-
创建(实例化)对象:User u1 = new User();
new User(); ----> 此处会调用User类的无参构造方法
构造方法用于创建对象并给对象属性初始化。
访问对象的属性(实例变量)和方法
- 访问对象的属性——格式:引用(对象名).属性名
- 访问对象的方法——格式:引用(对象名).方法名()
注意:引用不能为空,引用为空时访问实例相关的数据会报空指针异常(NullPointException)。
静态方法/静态变量 和 实例方法/实例变量 的区别
静态方法和静态变量都被static关键字修饰;实例方法和实例变量都没有被static关键字修饰。
对于被static修饰的变量/方法被称作为静态变量/方法(也叫做类变量/方法),静态变量/方法不属于对象而是属于类的,静态变量存储在方法区内存中的静态区中。静态变量/方法可以通过"类名.变量名","类名.方法名()“的方式去访问;同时也可以通过"对象.变量名”,"对象.方法名()“的方式去访问(不建议使用)。
对于未被static修饰的变量/方法被称作为实例变量/方法,实例变量/实例方法不属于类而是属于对象,实例变量被存储在堆内存中。实例变量/方法可以通过"引用.变量名”,"引用.方法名()"的方式区访问。
编译过程(多个源程序)
共有三个java源程序,如果想要运行TestUser类中的main方法,那么该如何去编译?
第一个程序
public class TestUser {
public static void main(String[] args){
User user = new User();
}
}
第二个程序
public class User{
int no; //用户编号
String username; //用户名
String password; //密码
}
第三个程序
public class Student {
int student_id; //学号
String name; //姓名
}
这是源文件所在目录(用于查看编译结果的)
由于main方法中涉及到的源程序只有TestUser.java和User.java所以并不需要对Student.java进行编译。对于这些程序的编译可以有多种方式
- 使用javac TestUser.java;javac User.java
编译结果如图所示
可以看出,使用这种方式需要对源文件一个一个的进行编译,太麻烦了,那么有没有更好的方式,可以继续看第二步
- 使用javac *.java
编译结果结果如图所示
可以看出,使用这种方式会将与程序运行无关的源程序进行编译,那么又没有更好的方式呢
- 使用javac Test.java
编译结果如图所示
可以看出直接对带有main方法的源程序进行编译,可以顺带对main方法中所有需要用到的类进行编译,并没有对其他源程序进行编译。
所以在对源文件进行编译时,只需要对编写main方法的源程序进行编译,编译器会自动对其中涉及到的所有源程序进行编译。
创建对象时JVM内存分析
以下面的程序为例
class User{
int no; //用户编号
String username; //用户名
String password; //密码
Address addr; //地址
//打招呼介绍自己
public void sayHello() {
System.out.println("我叫做:"+ username +",我的家庭地址是:" + addr.toString());
}
}
class Address {
String province; //省
String city; //市
String area; //区
//打印地址
public String toString() {
return "省:"+ province +",市:"+ city +",区:" + area;
}
}
public class Test {
public static void main(String[] args){
//创建一个Address对象
Address addr = new Address();
//创建一个User对象
User user = new User();
//初始化addr对象中的信息
addr.province = "吉林省";
addr.city = "长春市";
addr.area = "高薪区";
//初始化user对象中的信息
user.no = 1;
user.username = "张三";
user.password = "123456";
user.addr = addr;
//user对象打招呼介绍自己
user.sayHello();
}
}
运行结果如下
内存分析
对于main方法中的程序
- Address addr = new Address(); ----> 在堆内存中创建一个Address对象,并把这个对象的内存地址赋值给addr引用。
- User user = new User(); ----> 在堆内存中创建一个User对象,并把这个独享的内存地址赋值给user引用。
- addr.city = “长春市” ----> 通过addr这个引用找到这个Address对象,并修改这个对象的city属性(从null(default) —> “长春市”)。
- addr.area = “高薪市” ----> 通过addr这个引用找到这个Address对象,并修改这个对象的area属性(从null(default) —> “高薪区”)。
- user.no = 1 ----> 通过user这个引用找到这个User对象,并修改这个对象的no属性(从0(default) —> 1)。
- user.username = “张三” ----> 通过user这个引用找到这个User对象,并修改这个对象的username属性(从null(default) —> “张三”)。
- user.password = “123456” ----> 通过user这个引用找到这个User对象,并修改这个对象的password属性(从null(default) —> “123456”)。
- user.addr = addr ----> 通过user这个引用找到这个User对象,并修改这个对象的password属性(此处addr存放的是Address对象的内存地址,将这个内存地址赋值给user对象的addr属性)。
- user.sayHello(); ----> user对象调用sayHello(),在sayHello()中调用user.addr.toString()
6.Java方法
方法基础内容
对方法结构中的修饰符列表可以看下图