面向对象(基础)

序言

面向对象的三条主线:

  • 类及类的成员:属性、方法、构造器;代码块、内部类
  • 特征:封装性、继承性、多态性
  • 其他关键字:static、package、import

概述

对比面向对象和面向过程

  • POP(Procedure Oriented Programming)以函数为单位,是一种执行者的思维
  • OOP(Object Oriented Programming)以类为单位,是一种设计者的思维

面向对象编程的两个核心概念:类(Class)和对象(Object),类是具有相同特征的集合,是抽象的;对象是实际存在的该类事物的个体,是具体的。

面向对象完成具体功能的操作流程:

  1. 创建类,设计类的成员;
  2. 创建对象;
  3. 通过对象,调用其内部声明的属性或方法,完成相关功能。

类的使用参考代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class PersonTest {//测试类
public static void main(String[] args) {
Person p1= new Person();//创建对象
p1.name="Miku";//通过对象,调用其内部声明的属性或方法
p1.id=233;
p1.age=14;
p1.gender='女';
System.out.println("姓名:"+p1.name+"标号:"+p1.id+"年龄: "+p1.age+"性别: "+p1.gender);
}
}
class Person{//创建类,设计类的成员;
String name;
int id;
int age;
char gender;
}
//运行结果: 姓名:Miku标号:233年龄: 14性别: 女

类中对象的内存解析

Java中内存结构的划分:虚拟机栈、堆、方法区;程序计数器、本地方法栈

  • 虚拟机栈:以栈帧为基本单位;入栈对应方法的执行;方法的局部变量会存储在栈帧中
  • 堆空间:new出来的结构(数组、对象)1.数组的元素2.对象的成员变量。
  • 方法区:加载的类的模板结构。

参考一段代码:

1
2
3
4
5
6
7
Person p1= new Person();
p1.age=12;
class Person{//创建类
String name;
int id;
int age;
}

在内存上,使用new之后,会在堆里创建一段空间用于存储name、id、age,而p1在栈中存储这段空间的首地址。之后对p1.age进行赋值,是在堆中改变了age的值。

  • 可以总结出引用数据类型存储的其实都是地址

类的成员之一:属性

变量可以按类型分类也可以按照声明位置分类,按位置可以分为成员变量和局部变量。

属性的别名:成员变量、field(字段、域)

成员变量和局部变量对比

  1. 位置不同
  2. 内存分配不同:局部变量保存在栈、成员变量保存在堆
  3. 生命周期:
    成员变量随对象产生随对象消亡,局部变量在栈帧中分配,随方法出栈消亡
  4. 成员变量可用权限修饰符修饰,局部变量不行
  5. 默认值:局部变量没有默认初始化值,属性都有其默认初始化值

类的成员之二:方法

Java的方法不能独立存在,必须定义在类里

  1. 方法的声明格式:
    权限修饰符 + [其他修饰符] + 返回值类型 + 方法名(形参列表)[throws 异常类型]{//方法头
    //方法体
    }

  2. 调用本类方法:方法可以调用本类其他方法,也可以调用自己(递归)

  3. return的使用:

  • 结束方法

  • 将结果返回给其调用者

  1. 形参和实参
  • 形参:定义方法时,声明的变量成为形参
  • 实参:调用时实际传给形参的变量/常量/表达式

再谈方法

方法是设计类的核心,能否实现功能主要看方法。下面就重载、可变个数形参、值传递机制、迭代等方法的应用再进行补充说明。

重载(overload)

重载是指同一个类中两个方法名相同的情况,要讨论的核心其实是Java编译其辨别不同方法的机制

是否为不同方法仅与方法名形参列表有关

  • 注:形参列表不同只与形参的类型有关,而与其名称无关。相应的,类型相同,顺序不同也是两个方法。
  • 关于自动类型提升,编译方面会能不自动提升就不自动提升。
可变个数形参(jdk5.0的新特性)

场景:形参类型确定,个数不确定

格式:(类型 ... 参数名)

说明:

  • 个数为0,1,2……
  • 与其他同名的方法可以构成重载,且当类型相同时可变个数的方法优先级较低
  • 特例:可变个数的方法与另一个同名的以同类型数组为参数的方法不构成重载
  • 方法内调用还是用数组
  • 可变形参必须为于列表最后
  • 在一个方法的形参列表中最多出现一次
值传递机制

Java方法里的参数传递方式只用一种,就是值传递机制

方法内:基本数据类型传递数据值,引用数据类型传递地址值

方法参数传递:同样的,基本数据类型传递数据值,引用数据类型传递地址值

递归

方法自己调自己的操作就叫递归

  • 可分为间接递归和直接递归
  • 迭代其实是一种隐式循环,要避免无穷递归,会发生爆栈(StackOverflowError)
  • 在使用递归的时候要注意朝着已知的反向走
  • 注意:递归具有显著的缺点,层次多时运行较慢。相较于循环,递归花时间又耗内存,所以能用循环解决的问题就不要用递归

对象数组

数组的元素是引用数据类型中的类时,就称为对象数组

注意:在对象数组赋值前一定要实例化,否则会报空指针异常

1
2
3
4
5
Student[] students = new Student[20];//Student为类,这里创建对象数组
for(int i=0;i<students.length;i++){
students[i]=new Student();//实例化
students[i].id=i+1;//赋值
}

package和import关键字

package:
  1. 一个源文件只能有一个package语句

  2. package语句必须作为Java源文件的第一条语句,若缺省则为无名包

  3. 包对应于文件目录

  4. 同一个包下的类不能重名,不同包可以有重名的类

MVC设计模式:

  • Model(模型) - 模型代表一个存取数据的对象或 JAVA POJO。它也可以带有逻辑,在数据变化时更新控制器。
  • View(视图) - 视图代表模型包含的数据的可视化。
  • Controller(控制器) - 控制器作用于模型和视图上。它控制数据流向模型对象,并在数据变化时更新视图。它使视图与模型分离开。
import:
  1. 用于导入想用的类所在的包

  2. 使用:import 包名.类名,import 包名.*表示该包下的所有类

  3. .a包的类已经导入,如果想用该包子包下的类,仍要导入

  4. 如果用类名冲突,可以使用全类名的方式调用

面向对象特征之一:封装性

表格:四种权限修饰符的范围

public protected 缺省 private
同类
同包 ×
子类 × ×
不同包 × × ×

问:如何理解封装性?

  1. Java规定了4中权限修饰符。我们可以使用这四种权限修饰符来修饰类的内部成员。当这些成员被调用的时候,体现可见性的大小。

  2. 封装性的体现(举例)

  • 私有化类的属性,提供公共的get 和 set方法来更改

  • 将类中不需要对外暴露的方法设为private

  • 单例模式(在static后讲)

  1. 体现了程序设计的原则:高内聚、低耦合

注:类只能使用public和缺省修饰

类的成员之三:构造器

构造器(constructor)

构造器的定义格式
权限修饰符 类名(形参列表){}

构造器的作用

  1. 搭配new关键字,创建类的对象
  2. 在创建对象的同时,可以给对象的相关属性赋值

在没有显式提供构造器的情况下,系统会默认提供一个空参的构造器,且构造器的权限和类的权限相同

一旦显式声明了构造器,系统就不再提供空的构造器

构造器重载,一个类中可以声明多个构造器,彼此构成重载

匿名对象
直接使用(new 类名(参数列表))作为实参传递给方法,适合用于一次性的对象