引言
计算机是用来处理数据的,
比如你用java语言创建一个整数,一般都是 int i=10;1个数还好说,那十个数一百个数呢?
这时就有了数组 int[] arr = new int[100];这样就创建了100个数,是不是很方便?
面对对象也是如此。
1 什么是面向对象编程,为什么要用面向对象编程。
1.1什么是对象
要想搞清楚什么是面向对象,首先得知道什么是对象:
Java的祖师爷,詹姆斯高斯林认为,在这个世界中 **万物皆对象!**任何一个对象都可以包含一些数据,数据属于哪个对象,就由哪个对象来处理。
对象的含义是指具体的某一个事物,即在现实生活中能够看得见摸得着的事物。比如,汽车、手机、衣服…,在面向对象程序设计中,对象所指的是计算机系统中的某一个成分。在面向对象程序设计中,对象包含两个含义,其中一个是数据,另外一个是动作。对象则是数据和动作的结合体。对象不仅能够进行操作,同时还能够及时记录下操作结果。 方法是指对象能够进行的操作,方法同时还有另外一个名称,叫做函数。方法是类中的定义函数,其具体的作用就是对对象进行描述操作。(部分截取自百度百科)
1.2 什么是面向对象
知道什么是对象了,现在来理解什么是面向对对象
面向对象是相对于面向过程来讲的,面向对象方法,把相关的数据和方法组织为一个整体来看待,从更高的层次来进行系统建模,更贴近事物的自然运行模式。
1.3 什么是面向过程
我现在知道什么是对象了,什么是面在那个对象了,那面向过程又是个啥?
面向过程编程(Procedural Programming)是一种编程范式,其中程序的控制流是通过一系列的过程和函数实现的。这种编程方式更加关注于完成特定任务的算法和过程,而不是数据和对象。
1.4 举代码实例说明
好家伙,你隔着套娃呢是吧,你这一堆名词我也看不太懂啊!
哈哈,看不太懂我就举个例子来说明。
案例:我要实现一个数自增10次
面向过程:
public class Num {
public static void main(String[] args) {
int num = 0;
for(int i = 0; i < 10; i ++){
num ++;
}
System.out.println("num自增10次的结果为"+num);
}
}
结果
从代码和结果可以看出,面向过程就是顺序执行代码,强调一步一步完成。
面向对象:
Num类:
public class Num {
int num;
public void add(){
for(int i = 0; i < 10; i ++){
num ++;
}
System.out.println("num自增的结果为"+num);
}
}
Test类
public class Test {
public static void main(String[] args) {
Num n1 = new Num();
n1.num = 0;
n1.add();
}
}
结果
从代码和结果来看,面向对象就是将需要操作的数和行为包装到一个类里面,通过new来创建对象,来调用类里面的数据和方法。
1.5 举现实实例说明
如果你还不太明白,那我就举个现实的例子来说(不给你讲明白了,我今天不吃饭了,嘿嘿):人都是要吃饭的,你饿了,想吃饭。
面向过程
你得实现下面的步骤:
1 去菜市场买菜
2 洗菜
3 炒菜、蒸米饭或者做其他主食
4 炒好的菜装盘,盛米饭吃饭
5 洗盘子,收拾
这5部操作就是一个吃饭的过程,也就是面向过程。
面向对象
你雇了一个保姆,这个保姆来帮你干买菜、洗菜、炒菜…的工作,你只需要张嘴吃饭就行了,其他什么都不用你管,是不是很方便?
面向对象也让程序员从操作者向指挥者的转变。
1.6 简单的思维导图整理一下
1.7 面向对象有什么优点
面向对象编程具有以下优点:
1 易维护
采用面向对象思想设计的结构,可读性高,由于继承的存在,即使改变需求,那么维护也只是在局部模块,所以维护起来是非常方便和较低成本的。
2 易扩展
通过继承,我们可以大幅减少多余的代码,并扩展现有代码的用途;
我们可以在标准的模块上(这里所谓的”标准”指程序员之间彼此达成的协议)构建我们的程序,而不必一切从头开始。这可以减少软件开发时间并提高生产效率;
3 模块化
封装可以定义对象的属性和方法的访问级别,通过不同的访问修饰符对外暴露安全的接口,防止内部数据在不安全的情况下被修改。这样可以使程序具备更高的模块化程度,方便后期的维护和修改。
同时,面向对象语言允许一个对象的多个实例同时存在,而且彼此之间不会相互干扰;
4 方便建模
虽然面向对象语言中的对象与现实生活中的对象并不是同个概念,但很多时候,往往可以使用现实生活中对象的概念抽象后稍作修改来进行建模,这大大方便了建模的过程。(但直接使用现实中的对象来建模有时会适得其反)。
2 对象在计算机中的执行原理
要想知道对象在计算机中的执行原理,就得知道JVM
2.1 什么是JVM
JVM是Java Virtual Machine的简称,是Java虚拟机,是一种模拟出来的虚拟计算机,它通过在不同的计算机环境当中模拟实现计算功能来实现的。
引入Java虚拟机后,Java语言在不同平台上运行时就不需要重新编译。在其中,Java虚拟机屏蔽了与具体平台的相关信息,使得Java源程序在编译完成之后即可在不同的平台运行,达到“一次编译,到处运行”的目的,Java语言重要的特点之一跨平台,也即与平台的无关性,其关键点就是JVM。
2.2 JVM的结构
1. 程序计数器(Program Counter Register)
程序计数器就是当前线程所执行的字节码的行号指示器,通过改变计数器的值,来选取下一行指令,通过他来实现跳转、循环、恢复线程等功能。
● 在任何时刻,一个处理器内核只能运行一个线程,多线程是通过线程轮流切换,分配时间来完成的,这就需要有一个标志来记住每个线程执行到了哪里,这里便需要到了程序计数器。
● 程序计数器是线程私有的,每个线程都已自己的程序计数器。
2. 虚拟机栈(JVM Stacks)
虚拟机栈是线程私有的,随线程生灭。虚拟机栈描述的是线程中的方法的内存模型,每个方法被执行的时候,都会在虚拟机栈中同步创建一个栈帧(stack frame),方法被执行时入栈,执行完后出栈,每个栈帧的包含如下的内容
● 局部变量表: 局部变量表中存储着方法里的java基本数据类型(byte/boolean/char/int/long/double/float/short)以及对象的引用(注:这里的基本数据类型
指的是方法内的局部变量)
● 操作数栈
● 动态连接
● 方法返回地址
虚拟机栈可能会抛出两种异常:
● 如果线程请求的栈深度大于虚拟机所规定的栈深度,则会抛出StackOverFlowError即栈溢出
● 如果虚拟机的栈容量可以动态扩展,那么当虚拟机栈申请不到内存时会抛出OutOfMemoryError即OOM内存溢出
产生StackOverFlowError的原因是:
● 无限递归循环调用(最常见)。
● 执行了大量方法,导致线程栈空间耗尽。
● 方法内声明了海量的局部变量。
3. 本地方法栈(Native Method Stacks)
本地方法栈与虚拟机栈的作用是相似的,都会抛出OutOfMemoryError和StackOverFlowError,都是线
程私有的,主要的区别在于:
● 虚拟机栈执行的是java方法
● 本地方法栈执行的是native方法
4. Java堆(Java Heap)
java堆是JVM内存中最大的一块,由所有线程共享, 是由垃圾收集器管理的内存区域,主要存放对象实例,当然由于java虚拟机的发展,堆中也多了许多东西,现在主要有:
● 对象实例
○ 类初始化生成的对象
○ 基本数据类型的数组也是对象实例
● 字符串常量池
○ 字符串常量池原本存放于方法区,从jdk7开始放置于堆中。
○ 字符串常量池存储的是string对象的直接引用,而不是直接存放的对象,是一张string table
● 静态变量
○ 静态变量是有static修饰的变量,jdk7时从方法区迁移至堆中
● 线程分配缓冲区(Thread Local Allocation Buffer)
○ 线程私有,但是不影响java堆的共性
○ 增加线程分配缓冲区是为了提升对象分配时的效率
java堆既可以是固定大小的,也可以是可扩展的(通过参数-Xmx和-Xms设定),如果堆无法扩展或者无法分配内存时也会报OOM。
5. 方法区(Method Area)
它用于存储已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等。
(1)类型信息:
对每个加载的 类型 (类class、接口interface、枚举enum、注解annotation),JVM必须在方法区中存储以下类型信息:
● 这个类型的完整有效名称(全名=包名.类名)
● 这个类型直接父类的完整有效名(对于interface或是java.lang. Object,都没有父类)
● 这个类型的修饰符( public, abstract,final的某个子集)
● 这个类型实现接口的一个有序列表。
(2) 域(Field)信息:
JVM必须在方法区中保存类型的所有域的相关信息以及域的声明顺序。域的相关信息包括如下内容:
● 域名称
● 域类型
● 域修饰符(public,private,protected,static,final, volatile,transient的某个子集)
(3) 方法(Method)信息:
JVM必须在方法区中保存类型的所有方法的相关信息以及方法的声明顺序。方法的相关信息包括:
● 方法名称
● 方法的返回类型(或void)
● 方法参数的数量和类型(按顺序)
● 方法的修饰符(public,private,protected,static,final,synchronized,native,abstract的一个子集)
● 方法的字节码(bytecodes)、操作数栈、局部变量表及大小( abstract和native方法除外)
● 异常表(abstract和 native方法除外)每个异常处理的开始位置、结束位置、代码处理在程序计数器中的偏移地址、被捕获的异常类的常量池索引
(4)静态变量(non-final的)
静态变量和类关联在一起,随着类的加载而加载,它们成为类数据在逻辑上的一部分。类变量被类的所有实例共享,即使没有类实例时你也可以访问它。
补充说明:被声明为final的静态变量的处理方法则不同,被static和final修饰的变量也称为全局变量,每个全局常量在编译的时候就会被赋值了。
2.3 面向对象编程原理
在计算机中,对象就相当于一个表格,这个表格里面就有这个对象的数据和操作的一些行为,而class就相当于设计这张表的结构体,这张表设计成什么样,取决于你在结构体里面如何去定义数据和方法。在上面的JVM结构里面,我们主要了解栈、堆和方法区就可以了。
下面我用实例来说明:
public class Student {
String name;
int studentId;
int chinese;
int math;
public void totalScore(){
System.out.println("学号为"+studentId+"的学生"+name+"总分为"+(chinese+math));
}
public void averageScore(){
System.out.println("学号为"+studentId+"的学生"+name+"平均分为"+(chinese+math)/2);
}
}
现在就相当于我在计算机中创建了一个学生表,下面我们来创建对象
Test类
public class Test {
public static void main(String[] args) {
Student s1 = new Student();
s1.name = "小明";
s1.studentId = 1001;
s1.chinese = 80;
s1.math = 70;
s1.totalScore();
s1.averageScore();
Student s2 = new Student();
s2.name = "小丽";
s2.studentId = 1002;
s2.chinese = 90;
s2.math = 90;
s2.totalScore();
s2.averageScore();
}
}
结果为
代码执行原理见下图
下面将代码和图片结合起来讲解
public class Test {
//程序执行到这一步,会在方法区里面加载Test类的信息,main方法
public static void main(String[] args) {
//将main方法压入JVM的栈里面
Student s1 = new Student();
//在方法区里面加载Student的类信息,包括类的属性:name ,studentID ,chinese,math ),方法:totalScore()和averageScore(),然后在堆里面创建空间,里面有空间地址0x123和name= null(初始值),studentID= 0(初始值),chinese = 0(初始值),math= 0(初始值)和类的地址0x1245,这个地址指向方法区里面的Student类信息,并将空间地址0x123赋值给s1,此时s1指向堆里面的0x123空间地址,s1为引言地址。
s1.name = "小明";
//将name = null值更新为小明
s1.studentId = 1001;
//将studentId = 0更新为1001
s1.chinese = 80;
//将chinese = 0 更新为80
s1.math = 70;
//将math = 0 更新为70
s1.totalScore();
//s1指向的0x123地址空间中的类地址指向方法区中0x1245,找到方法区中的Student类信息中的toltalScore方法,将在堆中地址为0x123空间中的数据带入其中,输出
s1.averageScore();
//s1指向的0x123地址空间中的类地址指向方法区中0x1245,找到方法区中的类信息中的averageScore方法,将在堆中地址为0x123空间中的数据带入其中,输出
//s2和s1过程之一样的,他们在堆里面创建的空间不一样,但空间里面的类地址都为0x1245,都指向方法区中的Student类信息,因为创建这两个对象的类是同一个
Student s2 = new Student();
s2.name = "小丽";
s2.studentId = 1002;
s2.chinese = 90;
s2.math = 90;
s2.totalScore();
s2.averageScore();
}
}
3 this
3.1 什么是this
this是一个变量,用在方法中,用来指向当前对象
3.2 实例
AAA类
public class AAA {
public void findthis(){
System.out.println(this);
}
}
Test类
public class Test {
public static void main(String[] args) {
AAA a1 = new AAA();
System.out.println(a1);
a1.findthis();
System.out.println("---------------");
AAA a2 = new AAA();
System.out.println(a2);
a2.findthis();
}
}
结果
可以看出,s1中this和s1是相同的都指向相同的地址,s2中this和s2是相同的都指向相同的地址。
4 构造器
4.1 什么是构造器
构造器是一种特殊的方法,名字必须和所在类的名字一样,而且不能写返回值类型,这时我们把这个特殊的方法就叫构造器,和普通方法一样,构造器也是可以重载的。
构造器里面有参数的叫有参构造器,没有参数的叫无参构造器。
4.2 构造器的特点
Student s1 = new Student();
当我们执行上述代码时,右面的new Student(),有两个作用,一个是创建了一个对象Student,也就是创建了一张Student表,二是()里面有无参数,来具体调用咋个构造器。
举个实例
Student类
public class Student {
public Student(){
System.out.println("无参构造器被调用了。。。");
}
public Student(String name,int score){
System.out.println("有参构造器被调用了。。。");
}
}
Test1类
public class Test1 {
public static void main(String[] args) {
Student s1 = new Student();
}
}
结果
Test2类
public class Test2 {
public static void main(String[] args) {
Student s2 = new Student("小明",100);
}
}
结果
4.3 构造器的应用
创建对象时,同时能够对对象成员变量(属性)进行初始化赋值。
实例说明
Student类
public class Student {
String name;
int score;
public Student(){
}
public Student(String name,int score){
this.name = name;
this.score =score;
}
}
Test类
public class Test1 {
public static void main(String[] args) {
Student s1 = new Student("李华",99);
System.out.println(s1.name);
System.out.println(s1.score);
}
}
结果
4.4 注意事项
1 类在设计时,如果不写构造器,java会为类生成一个无参的构造器。
2 一旦定义了有参构造器,java就不会自动帮我们生成无参构造器了,此时就需要我们手动写出一个无参构造器。
5 封装
5.1 什么是封装
封装就是用类设计对象处理数据时,应该把这些要处理的数据以及处理数据的方法涉及到一个对象中去。
实例
public class Student {
String name;
int studentId;
int chinese;
int math;
public void totalScore(){
System.out.println("学号为"+studentId+"的学生"+name+"总分为"+(chinese+math));
}
public void averageScore(){
System.out.println("学号为"+studentId+"的学生"+name+"平均分为"+(chinese+math)/2);
}
}
像上面这样,将学生的数据和处理数据的方法设计到Student类中,这就是封装。
5.2 封装设计的规范
合理隐藏、合理暴露
向上面的例子一样,那么现在就会出现一个问题,我如果创建学生对象,将对象的语文和数学成绩赋值成负数可不可以?
显然是不可以的,所以我们不能让外部类随便对其进行操作,我们需要将语文和数学成绩设置成私有,设置get和set方法,并做限制才可以
public class Student {
String name;
int studentId;
private int chinese;
//将语文成绩设置成私有,这样外界就不能随意访问了,加上get和set方法,并在其中加以限制条件,这样,外界就不能随便给其赋值了
private int math;
//将数学成绩设置成私有,这样外界就不能随意访问了,加上get和set方法,并在其中加以限制条件,这样,外界就不能随便给其赋值了
public int getChinese() {
return chinese;
}
public void setChinese(int chinese) {
if(chinese >0 && chinese <= 100) {
this.chinese = chinese;
}else {
System.out.println("数据非法");
}
}
public int getMath() {
return math;
}
public void setMath(int math) {
if(math >0 && math <= 100) {
this.math = math;
}else {
System.out.println("数据非法");
}
}
public void totalScore(){
System.out.println("学号为"+studentId+"的学生"+name+"总分为"+(chinese+math));
}
public void averageScore(){
System.out.println("学号为"+studentId+"的学生"+name+"平均分为"+(chinese+math)/2);
}
}
Test类
public class Test {
public static void main(String[] args) {
Student s1 = new Student();
s1.name = "小明";
s1.studentId = 1001;
s1.setChinese(80);
s1.setMath(70);
s1.totalScore();
s1.averageScore();
Student s2 = new Student();
s2.name = "小丽";
s2.studentId = 1002;
s2.setChinese(90);
s2.setMath(90);
s2.totalScore();
s2.averageScore();
}
}
6 实体类JavaBean
6.1 什么是实体类
1 成员变量必须私有,且为他们设置get和set方法,必须有无参数构造器。
2 仅仅是用来保存数据的类,可以用它创建对象,保存某个实物的数据。
举个实例
下面这个是实体类
下面这个不是实体类
6.2 实体类应用场景
1 实体类对应的是现在比较流行的开发方法,数据和数据处理相分离。
2 我个人看法,这样保证了数据的纯粹性,使代码看起来和使用起来更有逻辑性,更清晰。
举个例子:你在职场中只能负责一种工作,或者说你只能负责一类工作,不可能说你在公司又处理业务,又干保洁,又干前台,这样很乱,没有章法,迟早出问题。
6.3 如何去使用
Student类,只管保存数据
public class Student {
private String name;
private int chinese;
private int math;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getChinese() {
return chinese;
}
public void setChinese(int chinese) {
this.chinese = chinese;
}
public int getMath() {
return math;
}
public void setMath(int math) {
this.math = math;
}
}
StudentOperator类,只管处理数据
public class StudentOperator {
private Student student;
//将Student数据引入进来
public StudentOperator(Student student){
this.student = student;
//构造方法
}
public void add(){
System.out.println(student.getName()+"的语文和数学成绩总和 = "+(student.getChinese()+student.getMath()));
}
}
Test类,测试类
package JavaBean;
public class StudentOperator {
private Student student;
public StudentOperator(Student student){
this.student = student;
}
public void add(){
System.out.println(student.getName()+"的语文和数学成绩总和 = "+(student.getChinese()+student.getMath()));
}
}
7 实践
光说不练假把式。
知道理论只是第一步,现在我们要通过实战来加深对面向对象编程的理解与掌握。
题目:用面向对象做一个电影信息系统,该系统有两个功能:
1 显示系统中的全部电影(每部电影展示名称、价格等信息)。
2 允许用户根据电影编号(id)查询出某个电影的详细信息。
7.1 分析
根据功能分析:
1 首先要建两个类,一个是Movie类,用来保存数据,电影的编号、导演、主演、价格、多少人想看这部电影等等信息。
2 其次要有个MovieOperator类,用来对电影的数据进行处理,输出信息等。
3 要显示全部电影,你不可能输入一个电影输出一个电影吧,那样太麻烦。这时需要建一个数组,将电影全部放到里面方便访问。
4 允许用户根据电影编号查询电影,得有Scanner和for或者其他循环语句进行循环判断。
5 功能逻辑要贴近现实,不可能进入系统出不来或者根本进入不了系统,要对系统有所控制。
6 …
7.2 动手实践
下面是我借鉴网上的代码稍加改动完成的。
下面的电影详细信息纯属虚构。
代码如下:
package A_MovieSystem;
public class Movie {
private String movieName;
private String director;
private String actor;
private int price;
private int peopleWantSee;
private int movieId;
public int getMovieId() {
return movieId;
}
public void setMovieId(int movieId) {
this.movieId = movieId;
}
public Movie() {
}
public Movie(int movieId,String movieName, String director, String actor, int price, int peopleWantSee) {
this.movieId = movieId;
this.movieName = movieName;
this.director = director;
this.actor = actor;
this.price = price;
this.peopleWantSee = peopleWantSee;
}
public String getMovieName() {
return movieName;
}
public void setMovieName(String movieName) {
this.movieName = movieName;
}
public String getDirector() {
return director;
}
public void setDirector(String director) {
this.director = director;
}
public String getActor() {
return actor;
}
public void setActor(String actor) {
this.actor = actor;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
public int getPeopleWantSee() {
return peopleWantSee;
}
public void setPeopleWantSee(int peopleWantSee) {
this.peopleWantSee = peopleWantSee;
}
}
MovieOperater类
package A_MovieSystem;
import java.util.Scanner;
public class MovieOperator {
private Movie[] movie;
public MovieOperator(Movie[] movie) {
this.movie = movie;
}
public void checkAll(){
System.out.println("-------所有电影如下----------");
for(int i = 0; i < movie.length; i ++){
Movie m = movie[i];
System.out.println("电影的编号:"+m.getMovieId());
System.out.println("电影的名称:"+m.getMovieName());
System.out.println("电影的导演:"+m.getDirector());
System.out.println("电影的主演:"+m.getActor());
System.out.println("电影的价格:"+m.getPrice());
System.out.println("电影想看的人数:"+m.getPeopleWantSee());
System.out.println("---------------------------");
}
}
public void searchMovieId(int id){
for(int i = 0; i < movie.length; i ++) {
Movie m = movie[i];
if (id == m.getMovieId()) {
System.out.println("电影的编号:"+m.getMovieId());
System.out.println("电影的名称:"+m.getMovieName());
System.out.println("电影的导演:"+m.getDirector());
System.out.println("电影的主演:"+m.getActor());
System.out.println("电影的价格:"+m.getPrice());
System.out.println("电影想看的人数:"+m.getPeopleWantSee());
System.out.println("---------------------------");
return;
}
}
System.out.println("没有该电影~");
}
}
Test类
package A_MovieSystem;
import java.util.Scanner;
public class Test {
//1 展示系统中的全部电影信息
// 2 允许用户根据电影编号查询出某个电影的详细信息
public static void main(String[] args) {
Movie[] movies = new Movie[5];
MovieOperator movieOperator = new MovieOperator(movies);
movies[0] = new Movie(1001,"一个人的世界","李华","张三",120,12000);
movies[1] = new Movie(1002,"一只猫的故事","赵新","李四",100,108000);
movies[2] = new Movie(1003,"家乡的故事","钱建国","王五",90,126000);
movies[3] = new Movie(1004,"奇妙探险","董欣","孙六",110,27000);
movies[4] = new Movie(1005,"梦想终将到达","周一梦","吴七",50,1200000);
int sc = 0;
while(true) {
System.out.println("是否进入电影信息系统,输入yes或者no");
Scanner yn = new Scanner(System.in);
Scanner in = new Scanner(System.in);
String yn1 = yn.next();
if (yn1.equalsIgnoreCase("yes")) {
while (sc != 3) {
System.out.println("------欢迎进入电影信息系统-------");
System.out.println("请输入你的操作代码(“1”是查看所有电影信息,“2”是通过电影编号查看电影信息,“3”退出当前系统)");
sc = in.nextInt();
switch (sc) {
case 1:
movieOperator.checkAll();
break;
case 2:
System.out.println("请输入电影的编号:");
int id = in.nextInt();
movieOperator.searchMovieId(id);
break;
case 3:
break;
default:
System.out.println("输入错误,请重新输入");
}
}
} else {
return;
}
}
}
}
运行结果见下图
代码实现过程流程图如下:
8 总结
本篇文章介绍了什么是面向对象、如何去创建对象、对象在计算机中的执行原理、jvm结构、this、JavaBean实例化、如何逻辑清晰的去使用面向对象编程以及最后的动手实际操作等内容,但要学好面向对象还要去学好多内容,继承、多态还有其他一些知识,有时间多去了解、多看、多体会、多练,加油!!!