面向对象和面向过程的思想概述
什么是面向过程?什么又是面向对象?
面向过程——步骤化
面向过程就是分析出实现需求所需要的步骤,通过函数(方法)一步一步实现这些步骤,接着依次调用即可
面向对象——行为化(概念相对抽象,可结合下面的例子理解)
面向对象是把整个需求按照特点、功能划分,将这些存在共性的部分封装成类(类实例化后才是对象),创建了对象不是为了完成某一个步骤,而是描述某个事物在解决问题的步骤中的行为
能举个例子谈谈你对面向过程和面向对象的理解吗?
例如我们设计一个桌球游戏(略过开球,只考虑中间过程)
A:面向过程方式思考:
把下述的步骤通过函数一步一步实现,这个需求就完成了。(只为演示概念,不细究逻辑问题)。
① palyer1 击球 —— ② 实现画面击球效果 —— ③ 判断是否进球及有效 —— ④ palyer2击球
⑤ 实现画面击球效果 —— ⑥ 判断是否进球及有效 —— ⑦ 返回步骤 1—— ⑧ 输出游戏结果
B:面向对象方式思考:
经过观察我们可以看到,其实在上面的流程中存在很多共性的地方,所以我们将这些共性部分全集中起来,做成一个通用的结构
玩家系统:包括 palyer1 和 palyer2
击球效果系统:负责展示给用户游戏时的画面
规则系统:判断是否犯规,输赢等
我们将繁琐的步骤,通过行为、功能,模块化,这就是面向对象,我们甚至可以利用该程序,分别快速实现8球和斯诺克的不同游戏(只需要修改规则、地图和球色即可,玩家系统,击球效果系统都是一致的)
面向过程和面向对象的优缺点
A:面向过程
优点:性能上它是优于面向对象的,因为类在调用的时候需要实例化,开销过大。
缺点:不易维护、复用、扩展
用途:单片机、嵌入式开发、Linux/Unix等对性能要求较高的地方
B:面向对象
优点:易维护、易复用、易扩展,由于面向对象有封装、继承、多态性的特性,可以设计出低耦合的系统,使系统更加灵活、更加易于维护
缺点:一般来说性能比面向过程低
面向对象的两个要素:类和对象
什么是类?
类:是一类具有相同特性的事物的抽象描述,是一组相关属性和行为的集合。可以看成是一类事物的模板,使用事物的属性特征和行为特征来描述该类事物。
现实中,描述一类事物:
属性:就是该事物的状态信息。
行为:就是该事物能够做什么。
举例:小猫。
属性:名字、体重、年龄、颜色。 行为:走、跑、叫。
什么是对象?
对象:是一类事物的具体体现。对象是类的一个实例(对象并不是找个女朋友),必然具备该类事物的属性和行为。
现实中,一类事物的一个实例:一只小猫 。
举例:一只小猫。
属性:tom、5kg、2 years、yellow。 行为:溜墙根走、蹦跶的跑、喵喵叫。
类与对象的关系。
类是对一类事物的描述,是抽象的。
对象是一类事物的实例,是具体的。
类是对象的模板,对象是类的实体。
类的定义和对象的创建
事物与类的对比
现实世界的一类事物:
属性:事物的状态信息。 行为:事物能够做什么。
Java中用class描述事物也是如此:
成员变量:对应事物的属性 成员方法:对应事物的行为
类的定义格式:
public class ClassName{
//成员变量
//成员方法
}
定义类:就是定义类的成员,包括成员变量和成员方法。
成员变量:和以前定义变量几乎是一样的。只不过位置发生了改变。在类中,方法外。
成员方法:和以前写的main方法格式类似。只不过功能和形式更丰富了。在类中,方法外。
类的定义格式举例:
public class Person {
//成员变量
String name;//姓名
int age;//年龄
boolean isMarried;
public void walk(){
System.out.println("人走路...");
}
public String display(){
return "名字是:" + name + ",年龄是:" + age + ",Married:" + isMarried;
}
}
对象的创建
创建对象:
new 类名();//也称为匿名对象
//给创建的对象命名
//或者说,把创建的对象用一个引用数据类型的变量保存起来
类名 对象名 = new 类名();
那么,对象名中存储的是什么呢?答:对象在内存中的内存地址。
class Student{
}
public class TestStudent{
//Java程序的入口
public static void main(String[] args){
System.out.println(new Student());//Student@7852e922
Student stu = new Student();
System.out.println(stu);//Student@4e25154f
int[] arr = new int[5];
System.out.println(arr);//[I@70dea4e
}
}
//Student和TestStudent没有位置要求,谁在上面谁在下面都可以
//但是如果TestStudent类的main中使用了Student类,那么要求编译时,这个Student已经写好了,不写是不行的
//如果两个类都在一个.java源文件中,只能有一个类是public关键字修饰的
对象的内存解析
类中成员变量的使用
成员变量的分类
实例变量:没有static关键字修饰的变量,也叫对象属性,属于某个对象的,通过对象来使用
类变量:有static修饰关键字修饰的变量,也叫类变量,属于整个类的,不是属于某个实例
成员变量的声明
【修饰符】 class 类名{
【修饰符】 数据类型 属性名; //属性有默认值
【修饰符】 数据类型 属性名 = 值; //属性有初始值
}
说明:属性的类型可以是Java的任意类型,包括基本数据类型、引用数据类型(类、接口、数组等)
如何在该类外面访问该类的成员变量?
静态成员变量(类变量)的访问方式:
类名.静态成员变量名 //推荐
对象名.静态成员变量名 //不推荐
实例成员变量(实例变量)的访问方式:
对象名.实例变量名 //只有这一种访问方式
例如:
public class TestChinese {
public static void main(String[] args) {
//类名.静态成员变量
System.out.println(Chinese.country);
//错误,普通成员变量必须通过对象.进行访问
// System.out.println(Chinese.name);
Chinese c1 = new Chinese();
//对象名.普通成员变量
System.out.println(c1.name);
//静态的成员变量也可以通过对象.进行访问
//对象名.普通成员变量
System.out.println(c1.country);
System.out.println(c1.gender);
}
}
class Chinese{
static String country;
String name;
char gender = '男';
}
成员变量的特点
- 成员变量都有默认初始化值
数据类型分类 | 默认初始化值 | |
基本数据类型(4种) | 整型(byte,short,int,long) | 0 |
浮点型(float,double) | 0.0 | |
字符型(char) | '\u0000' | |
布尔型(boolean) | false | |
引用数据类型(3种) | 数组,类,接口 | null |
- 类变量的值是所有对象共享的,而实例变量的值是每个对象独立的
类中成员方法的使用
- 方法的概念
方法也叫函数,是一个独立功能的定义,是一个类中最基本的功能单元。
把一个功能封装为方法的目的是,可以实现代码重用,从而简少代码量。
- 使用方法的原则
必须先声明后使用
类,变量,方法等都要先声明后使用
不调用不执行,调用一次执行一次。
- 成员方法的分类
成员方法分为两类:
实例方法:没有static修饰的方法,必须通过实例对象来调用。
静态方法:有static修饰的方法,也叫类方法,可以直接由类名来调用。
- 成员方法的声明
方法声明的位置必须在类中方法外(方法中不能再声明方法,但可以调用方法。)
语法格式:
【修饰符】 返回值类型 方法名(【参数列表:参数类型1 参数名1,参数类型2 参数名, ...... 】){
方法体;
【return 返回值;】
}
修饰符: 修饰符后面一一介绍,例如:public,static等都是修饰符
返回值类型: 表示方法运行的结果的数据类型,方法执行后将结果返回到调用者
基本数据类型
引用数据类型
无返回值类型:void
方法名:给方法起一个名字,见名知意,能准确代表该方法功能的名字。遵循标识符的命名规则和规范。
参数列表:方法内部需要用到其他方法中的数据,需要通过参数传递的形式将数据传递过来,可以是基本数据类型、引用数据类型、也可以没有参数,什么都不写
方法体:特定功能代码
return:结束方法,并将方法的结果返回去,
如果返回值类型不是void,方法体中必须保证一定有(return 返回值;)语句,并且要求该返回值结果的类型与声明的返回值类型一致或兼容。
如果返回值类型为void时,return 后面不用跟返回值(return;),甚至也可以没有return语句。
return语句后面就不能再写其他代码了,否则会报错:Unreachable code
声明位置示例:
类{
方法1(){
}
方法2(){
}
}
错误示例:
类{
方法1(){
方法2(){ //位置错误
}
}
}
- 如何在该类外面访问该类的成员方法?
实例方法:
对象名.方法名(实参列表);//只能通过这一种方式调用实例方法。
静态方法:
类名.类方法(【实参列表】) //推荐
对象名.类方法(【实参列表】) //不推荐
总结:
形参:在定义方法时方法名后面括号中声明的变量称为形式参数(简称形参)即形参出现在方法定义时。
实参:调用方法时方法名后面括号中的使用的值/变量/表达式称为实际参数(简称实参)即实参出现在方法调用时。
总结:
(1)调用时,需要传“实参”,实参的个数、类型、顺序顺序要与形参列表一一对应
如果方法没有形参,就不需要也不能传实参。
(2)调用时,如果方法有返回值,可以接受或处理返回值结果,当然也可以不接收,那么此时返回值就丢失了。
如果方法的返回值类型是void,不需要也不能接收和处理返回值结果。
- 方法调用及内存分析
方法不调用不执行,调用一次执行一次,每次调用会在栈中有一个入栈动作,即给当前方法开辟一块独立的内存区域,用于存储当前方法的局部变量的值,当方法执行结束后,会释放该内存,称为出栈,如果方法有返回值,就会把结果返回调用处,如果没有返回值,就直接结束,回到调用处继续执行下一条指令。
栈结构:先进后出,后进先出。
示例一:
public class TestCount {
public static void main(String[] args) {
int a = 4;
int b = 2;
int m = CountTools.max(a, b));
}
}
class CountTools{
static int max(int a, int b) {
return a > b ? a : b;
}
}
示例二:
public class TestCircle {
public static void main(String[] args) {
Circle c1 = new Circle();
c1.radius = 1.2;
int area1 = c1.area();
Circle c2 = new Circle();
c2.radius = 2.5;
int area2 = c2.area();
}
}
class Circle{
double radius;
public double area() {
return Math.PI * radius * radius;
}
}
- 方法的参数传递机制
方法的参数传递机制:实参给形参赋值
方法的形参是基本数据类型时,形参值的改变不会影响实参;
方法的形参是引用数据类型时,形参地址值的改变不会影响实参,但是形参地址值里面的数据的改变会影响实参,例如,修改数组元素的值,或修改对象的属性值。
public class MethodTest {
//方法的形参是基本数据类型时,形参值的改变不会影响实参
// public static void swap(int a, int b) {
// int temp = a;
// a = b;
// b = temp;
// }
public static void main(String[] args) {
// int x = 1;
// int y = 2;
// System.out.println("调用前:" + "x:" + x + "y:" + y);
// swap(x, y);//调用完之后,x与y的值不变
// System.out.println("调用后:" + "x:" + x + "y:" + y);
//方法的形参是引用数据类型时,形参地址值的改变不会影响实参,但是形参地址值里面的数据的改变会影响实参,例如,修改数组元素的值,或修改对象的属性值。
MyData m = new MyData();
m.num = 1;
System.out.println("调用前:" + m.num);
change(m);//调用完之后,m对象的num属性值就变为2
System.out.println("调用前:" + m.num);
}
public static void change(MyData my) {
my.num *= 2;
}
}
class MyData {
int num;
}
怎么在本类中访问本类的成员变量和成员方法
在实例方法中:可以直接使用成员变量和成员方法,不需要通过“对象名."和"类名."
在静态方法中:只能直接调用静态成员变量和静态成员方法;非静态成员方法只能通过"对象名.方法名(实参列表)",非静态成员变量只能通过"对象名.属性名"调用。
/**
* 在本类中访问本类的成员变量和成员方法:
* 1.在实例方法中:可以直接使用成员变量和成员方法,不需要通过“对象名."和"类名."
* 2.在静态方法中:
* 只能直接调用静态成员变量和静态成员方法;非静态成员方法只能通过"对象名.方法名(实参列表)",非静态成员变量只能通过"对象名.属性名"调用。
*/
class ClassTest1 {
/**
* 变量的使用应该注意什么?
* 2.1先声明后使用
* 如果没有声明,会报“找不到符号”错误
* 2.2在使用之前必须初始化
*如果没有初始化,会报“未初始化”错误
* 2.3变量有作用域
* 如果超过作用域,也会报“找不到符号”错误
* 2.4在同一个作用域中不能重名
*/
int age;
static int number;//static关键字修饰的变量是静态成员变量
public void eat() {
age = 0;
number = 1;
run();
water();
}
public void water() {
age = 1;
number = 5;
run();
eat();
}
public static void run() {
//编译报错:Non-static field 'age' cannot be referenced from a static context
//非静态字段'age'不能从静态上下文中引用
//age = 0;//实例成员变量
number = 0;//静态成员变量
//编译报错:Non-static method 'eat()' cannot be referenced from a static context
//非静态方法eat()不能从静态上下文中引用
// eat();//非静态方法
jump();//静态方法
System.out.println("你在跑步吗?");
ClassTest1 a = new ClassTest1();
a.eat();
a.age = 0;
}
public static void jump() {
System.out.println("你在跳吗?");
//编译报错:Non-static method 'eat()' cannot be referenced from a static context
// eat();//非静态方法eat()不能从静态上下文中引用
//编译报错:Non-static field 'age' cannot be referenced from a static context
//age = 0;//编译失败
number = 1;
}
}
成员变量和局部变量的区别
变量的分类
成员变量
静态变量(有static关键字修饰的变量)
实例变量(没有static关键字修饰的变量)
局部变量
相同点
定义变量的格式:数据类型 变量名 = 变量值;
先声明,后使用
变量都有其对应的作用域
不同点(6种)
声明位置和方式不同
(1)静态变量:在类中方法外,并且有static关键字修饰的变量
(2)实例变量:在类中方法外,没有static关键字修饰的变量
(3)局部变量:在方法体{}中或方法的形参列表、代码块中、构造器形参、构造器内
在内存中存储的位置不同
(1)静态变量:方法区
(2)实例变量:堆
(3)局部变量:栈
生命周期不同
(1)静态变量:和类的生命周期一样,因为它的值是该类所有对象共享的,早于对象的创建而存在。(2)实例变量:和对象的生命周期一样,随着对象的创建而存在,随着对象被GC回收而消亡, 而且每一个对象的实例变量是独立的。
(3)局部变量:和方法调用的生命周期一样,每一次方法被调用而存在,随着方法执行的结束而消亡, 而且每一次方法调用都是独立。
作用域不同
(1)静态变量和实例变量:不谈作用域在本类中,唯一的限制,静态方法或静态代码块中不能直接使用非静态的,其他都可以直接使用。在其他类中,能不能使用看修饰符(public,protected,private等)(2)局部变量:有作用域出了作用域就不能使用,不能使用权限修饰符修饰。
修饰符的不同
(1)静态变量:很多public,protected,private,final,volatile等,一定有的是static
(2)实例变量:public,protected,private,final,volatile,transient等,一定没有的是static
(3)局部变量:final,不能使用权限修饰符修饰。
权限修饰符(4种):public、protected、缺省、private
常量修饰符final:修饰的变量是常量,其值不能修改。
volatile:和多线程有关
transient:是否序列化,和IO有关
默认值
(1)静态变量:有默认值
(2)实例变量:有默认值
(3)局部变量:没有默认值,必须初始化后才能使用。其中的形参比较特殊,靠实参给它初始化。
可变参数的使用
在JDK1.5之后,如果我们定义一个方法时,此时某个形参的类型可以确定,但是形参的个数不确定,那么我们可以使用可变参数。
格式:
【修饰符】 返回值类型 方法名(【非可变参数部分的形参列表,】参数类型... 形参名){ }
可变参数的使用要求:
一个方法最多只能有一个可变参数。
如果一个方法包含可变参数,那么可变参数必须是形参列表的最后一个。
【修饰符】 返回值类型 方法名(【非可变参数部分的形参列表,】参数类型[] 形参名){ }
只是后面这种定义,在调用时必须传递数组,而前者更灵活,既可以传递数组,又可以直接传递数组的元素,这样更灵活了。
方法的重载
方法的重载:指在同一个类中,允许存在一个以上的同名方法,只要它们的参数列表不同即可,与修饰符和返回值类型无关。
参数列表:①数据类型不同②参数个数不同③数据类型顺序不同。
重载方法调用:JVM通过方法的参数列表,调用不同的方法。
对象数组
数组是用来存储一组数据的容器,一组基本数据类型的数据可以用数组装,那么一组对象也可以使用数组来装。
即数组的元素可以是基本数据类型,也可以是引用数据类型。
当元素是引用数据类型时,我们称为对象数组。
注意:对象数组,首先要创建数组对象本身,即确定数组的长度,然后再创建每一个元素对象,如果不创建,数组的元素的默认值就是null,所以很容易出现空指针异常NullPointerException。