五、面向对象初步

发布于 2022-02-27  175 次阅读


面向过程与面向对象

面向过程(POP):Procedure Oriented Programming

面向对象(OOP):Object Oriented Programming

面向对象更加强调运用人类在日常的思维逻辑中采用的思想方法与原则,如抽象、分类、继承、聚合、多态等。

面向对象的三大特征:

封装 (Encapsulation) 继承 (Inheritance) 多态 (Polymorphism)

面向过程与面向对象的理解

面向过程:强调的是功能行为,以函数为最小单位,考虑怎么做

面向对象:强调具备了功能的对象,以类、对象为最小单位,考虑谁来做

image-20220223214031045

三条主线(尚硅谷自创)

  1. 类及类的成员:属性、方法、构造器;代码块、内部类
  2. 面向对象的三大特征:封装、继承、多态
  3. 其它关键字:this, super, abstract, interface, static, final, package, import

完成一个项目(或功能)的思路

根据问题需要,选择问题所针对的现实世界中的实体

从实体中寻找解决问题相关的属性和功能,这些属性和功能就形成了概念世界中的类

把抽象的实体用计算机语言进行描述,形成计算机世界中类的定义。即借助某种程序语言,把类构造成计算机能够识别和处理的数据结构。

类实例化成计算机世界中的对象。对象是计算机世界中解决问题的最终工具。

类和对象

面向对象的两个要素:(Class)和对象(Object)

类:是对一类事物的描述,是抽象的、概念上的定义

对象:是实际存在的该类事物的每个个体,因而也称为实例(instance)

二者的关系:对象是由类派生(new)出来的。

面向对象程序设计的重点是类的设计,设计类就是设计类的成员

“万事万物皆对象”的理解

  1. 在Java语言范畴中,我们将功能、结构等封装到类中,通过类的实例化,来调用具体的功能结构。
    (1)Scanner,String等
    (2)文件:File
    (3)网络资源:URL
  2. 涉及到Java语言与前端语言、后端数据库交互时,前后端的结构在Java层面都体现为类、对象。比如:数据库的每一个表都是一个类,每一行是一个对象,每一类是一个类的属性。

概念说明

属性:对应类中的成员变量

行为:对应类中的成员方法

Field = 属性 = 成员变量 = 域、字段

Method = (成员)方法 = 函数

创建类的对象=类的实例化

面向对象思想的实现

  1. 创建类,设计类的成员
  2. 创建类的对象(类的实例化)
    创建对象的语法:类名 对象名=new 类名();
  3. 调用对象的结构:”对象.属性“ ”对象.方法“

对象的内存解析

如果创建(new)了一个类的对象,则每个对象都独立的拥有一套类的属性(非static的)。

经典代码:

Person p1=new Person();

Person p2=new Person();

Person p3=p1;//没有创建一个对象

编译完程序以后,生成一个或多个字节码文件(.class)。我们使用JVM中的类的加载器和解释器对生成的字节码文件进行解释运行。意味着,需要将字节码文件对应的类加载到内存中,涉及到内存解析。

JVM内训结构

  • 堆(Heap),此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。这一点在 Java虚拟机规范中的描述是:所有的对象实例以及数组都要在堆上分配。
  • 通常所说的栈(Stack),是指虚拟机栈。虚拟机栈用于存储局部变量等。局部变量表存放了编译期可知长度的各种基本数据类型(boolean、byte、 char 、 short 、 int 、 float 、 long 、 double)、对象引用(reference类型, 它不等同于对象本身,是对象在堆内存的首地址)。 方法执行完,自动释放。
  • 方法区(Method Area),用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。

《JVM规范》

引用类型内存解析的说明

引用类型的变量,只可能储存两类值:null或地址值(含变量的类型)

匿名对象的使用

我们创建变量,没有显示赋值给一个变量名,即匿名对象。

使用情况:

如果对一个对象只需要进行一次方法调用,那么就可以使用匿名对象。如:new Person().shout();

我们经常将匿名对象作为实参传递给一个方法调用。如:mall.show(new Phone());

属性(成员变量)vs.局部变量

相同点:定义变量的格式相同,先声明后使用,都有对应的作用域

不同点:

属性/成员变量 局部变量
声明的位置 直接声明在类中 方法内、方法形参,构造器内、构造器形参,代码块内
修饰符 private、public、缺省、protected 不能用权限修饰符修饰
初始化值 有默认初始化值 没有默认初始化值,必须显示赋值方可使用
内存加载位置 堆空间(非static) 栈空间

1、在类中声明的位置不同

属性:直接定义在类的一对{}内

局部变量:声明在方法内、方法形参、代码块内、构造器形参、构造器内部的变量

2、关于权限修饰符的不同

属性:可以在声明属性时,指明其权限,使用权限修饰符

​ 常用的权限修饰符:privite、public、缺省、protected ——>封装性

局部变量:不可以使用权限修饰符

3、默认初始化值不同

属性:

  • 整型(byte、short、int、long):0
  • 浮点型(float、double):0.0
  • 字符型(char):0(或'\u0000')
  • 布尔型(boolean):false
  • 引用数据类型(类、数组、接口):null

局部变量:没有默认初始化值,必须要显示赋值。特别地,形参在调用时赋值即可。

4、在内存中加载的位置不同

属性:堆空间(非static)

局部变量:栈空间

变量的分类方式:按照在类中声明的位置

image-20220225110603211

方法

什么是方法(method、函数):

方法是类或对象行为特征的抽象,用来完成某个功能操作。在某些语言中也称为函数或过程。

将功能封装为方法的目的是,可以实现代码重用,简化代码。

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

方法的声明(除修饰符外与C语言相同)

修饰符 返回值类型 方法名(参数类型 形参1, 参数类型 形参2, ….){

​ 方法体程序代码

​ return 返回值;

修饰符:public、缺省、private、protected等,封装性时再细说

注意:static、final、abstract来修饰的方法之后再讲

方法的使用:

方法中可以调用同一个类中的方法,也可以调用类的属性,但不可以在方法中声明方法。

其他内容(如return关键字)与C语言相同。

练习:对象数组题目

定义类Student,包含三个属性:学号number(int),年级state(int),成绩 score(int)。创建20个学生对象,学号为1到20,年级和成绩都由随机数确定。

问题:使用冒泡排序按学生成绩排序,并遍历所有学生信息

提示: 1) 生成随机数:Math.random(),返回值类型double;

2) 四舍五入取整:Math.round(double d),返回值类型long。

public class StuTest{
    Student[] stus=new Student[20];
    for(int i=0;i<stus.length;i++){
        stus[i]=new Student();
        //用随机数赋值初始化
        stus[i].number=(i+1);
        stus[i].state=(int)(Math.random()*(6-1+1)+1);
        stus[i].score=(int)(Math.random()*(100-0+1));
    }
    //问题:使用冒泡排序按学生成绩排序,并遍历所有学生信息
    for(int i=0;i<stus.length-1;i++){
        for(int j=0;j<stus.length-1-i;j++){
            //如果需要换序,交换的是数组的元素:Student对象
            Student temp=stus[j];
            stus[j]=stus[j+1];
            stus[j+1]=temp;
        }
    }
    for(i=0;i<stus.length;i++){
        System.out.println(stus[i].info());
    }
}

class Student{
    int number;
    int state;
    int score;
    public String info(){
        return("学号:"+number+"年级:"+state+"成绩:"+score);
    }
}

建议:把功能封装到方法中,例如:

pubilc void print(Student[] stu);

需要先在main里造当前类的对象:

StudentTest test=new StudentTest();

再通过test调用封装的方法,例如:test.sort(stus);

方法的重载(Overload)

定义:在同一个类中,允许存在一个以上的同名方法,只要它们的参数个数或参数类型不同即可

两同一不同:同一个类,同一个方法名,参数类型不同

如果调用方法时,没有完全匹配的参数类型,会自动选择参数类型提升

在通过对象调用方法时,如何确定某一个指定的方法:方法名——>参数列表

可变(个数)形参的方法

jdk5.0新增内容

格式:数据类型 ... 变量名

调用方法时传入参数个数可以是0个,1个,2个......

可变个数形参的方法与本类中方法名相同,形参不同的方法之间构成重载。

public void show(String ... strs);与pubilc void show(String[] strs);之间冲突,不构成重载

//test.show("aa","bb","cc");
//test.show(new String[]{"aa","bb","cc"});
public void show(String ... strs){
    for(int i=0;i<strs.length;i++){
        System.out.println(strs[i]);
    }
}

可变个数形参在声明中必须声明在参数列表的末尾,且在形参列表中只能声明一个。例如:public void show(String ... strs,int i);是错误的。

可变形参在数据库中的使用:

sql = select 某某 where id = ? name = ?

?的值可以用可变形参来表示

方法参数的值传递机制

形参:方法定义时,声明的小括号内的参数

实参:方法调用时,实际传递给形参的数据

如果参数是基本数据类型,则实参赋给形参的是实参真实储存的数据值

如果参数是引用数据类型,则实参赋给形参的是实参存储数据的地址值

统称为“按值传递”。(没理解,和按引用传递有什么不同?)

例题:

public class TransferTest3 {
    public static void main(String args[]) {
        TransferTest3 test = new TransferTest3();
        test.first();
    }
    public void first() {
        int i = 5;
        Value v = new Value();
        v.i = 25;
        second(v, i);
        System.out.println(v.i);
    }
    public void second(Value v, int i) {
        i = 0;
        v.i = 20;
        Value val = new Value();
        v = val;
        System.out.println(v.i + " " + i);
    }
}

class Value {
    int i = 15;
}

image-20220225151328375

小知识

eclipse中control+shift+t 打开搜索栏搜索类,在左侧Outline中可以看到其中定义的方法。

选中方法名,alt+右箭头:进入,alt+左箭头:退回

在填入多个形参时,填好第一个后按tab键,光标切换至第二个形参的位置

选中多行代码,ctrl+tab往前移动一个缩进

选中多行代码,ctrl+D同时删除多行

选中多行代码,ctrl+Alt复制一份

在方法前插入文档注释,在需要插入的位置输入/**再输入一个回车。在调用方法时,将鼠标放在方法名上可以看到自己写的注释。一般使用@Discription@date@Parameters@Author。

通常一个源文件(.java)中只放一个类,也可以放在同一个原文件中

Math.PI