Java 面向对象编程

面向对象编程概述

程序设计的思路

面向对象,是软件开发中的一类编程风格、开发范式。除了面向对象,还有面向 过程、指令式编程和函数式编程。在所有的编程范式中,我们接触最多的还是面 向过程和面向对象两种。

早期先有面向过程思想,随着软件规模的扩大,问题复杂性的提高,面向过程 的弊端越来越明显,出现了面向对象思想并成为目前主流的方式。

面向过程的程序设计

面向过程的程序设计思想(Process-OrientedProgramming),简称POP

关注的焦点是过程:过程就是操作数据的步骤。如果某个过程的实现代码重复出现,那么 就可以把这个过程抽取为一个函数。这样就可以大大简化冗余代码,便于维护。

典型的语言:C 语言
代码结构:以函数为组织单位。 是一种“执行者思维”,适合解决简单问题。扩展能力差、后期维护难度较大。

面向对象的程序设计

面向对象的程序设计思想( Object Oriented Programming),简称 OOP 关注的焦点是类:在计算机程序设计过程中,参照现实中事物,将事物的属性特征、行为

特征抽象出来,用类来表示。 典型的语言:Java、C#、C++、Python、Ruby 和 PHP 等 代码结构:以类为组织单位。每种事物都具备自己的属性和行为/功能。 是一种“设计者思维”,适合解决复杂问题。代码扩展性强、可维护性高。

由实际问题考虑如何设计程序

思考 1:如何开车?

面向过程思想思考问题时,我们首先思考“怎么按步骤实现?”并将步骤对应成方 法,一步一步,最终完成。 这个适合简单任务,不需要过多协作的情况。针对 如何开车,可以列出步骤:

面向过程适合简单、不需要协作的事务,重点关注如何执行。

思考 2:如何造车?

造车太复杂,需要很多协作才能完成。此时我们思考的是“车怎么设计?”,而不 是“怎么按特定步骤造车的问题”。这就是思维方式的转变,前者就是面向对象 思想。所以,面向对象(Oriented-Object)思想更契合人的思维模式。

用面向对象思想思考“如何设计车”:

 自然地,我们就会从“车由什么组成”开始思考。发现,车由如下结构组成:

我们找轮胎厂完成制造轮胎的步骤,发动机厂完成制造发动机的步骤,...;这 样,大家可以同时进行车的制造,最终进行组装,大大提高了效率。但是,具 体到轮胎厂的一个流水线操作,仍然是有步骤的,还是离不开面向过程思维!

因此,面向对象可以帮助我们从宏观上把握、从整体上分析整个系统。 但是, 具体到实现部分的微观操作(就是一个个方法),仍然需要面向过程的思路去 处理。注意:

我们千万不要把面向过程和面向对象对立起来。他们是相辅相成的。 面向对象离不开面向过程!

类比举例 1

 当需求单一,或者简单时,我们一步步去操作没问题,并且效率也挺 高。

可随着需求的更改,功能的增多,发现需要面对每一个步骤很麻烦 了,这时就开始思索,能不能把这些步骤和功能进行封装,封装时根 据不同的功能,进行不同的封装,功能类似的封装在一起。这样结构 就清晰了很多。用的时候,找到对应的类就可以了。这就是面向对象 的思想。

类比举例 2:人把大象装进冰箱

面向过程: 打开冰箱-把大象装进冰箱-把冰箱门关住

面向对象

人{ 
    打开(冰箱){
        冰箱.开门();
    }
    操作(大象){ 
        大象.进入(冰箱);
    } 
    关闭(冰箱){
        冰箱.关门(); 
    }
}

冰箱{
    开门(){ }
    关门(){ }
    
}

大象{
   进入(冰箱){ }
}

类和对象

类和对象概述

类(Class)和对象(Object)是面向对象的核心概念。

类: 具有相同特征的事物的抽象描述,是抽象的、概念上的定义。

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

可以理解为:类 => 抽象概念的人;对象 => 实实在在的某个人 

类的成员概述

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

 现实世界的生物体,大到鲸鱼,小到蚂蚁,都是由最基本的细胞构成的。同理,Java 代码 世界是由诸多个不同功能的类构成的。

现实生物世界中的细胞又是由什么构成的呢?细胞核、细胞质、...

Java 中用类 class 来描述事物也是如此。类,是一组相关属性和行为的集合,这也是类最 基本的两个成员。

属性:该类事物的状态信息。对应类中的成员变量

• 成员变量 <=> 属性 <=> Field

行为:该类事物要做什么操作,或者基于事物的状态能做什么。对应类中的成 员方法

• (成员)方法 <=> 函数 <=> Method

面向对象完成功能的三步骤

步骤一: 类的定义

类的定义使用关键字:class。格式如下:

[修饰符] class 类名{ 
    属性声明;
    方法声明; 
}


public class Dog{ 
    //声明属性
    String type; //种类
    String nickName; //昵称 String hostName; //主人名称
    
    //声明方法——吃东西
    public void eat(){ 
        System.out.println("狗狗进食"); 
    }
}


public class Person{ 
    int age; //声明属性 age
    char gender;
    Dog dog;


    //声明方法 showAge() 
    public void eat() {
        System.out.println("人吃饭"); 
    }

    //喂宠物
    public void feed(){
        dog.eat(); 
    }
}

步骤二: 对象的创建

创建对象,使用关键字:new

创建对象语法:

//方式 1:给创建的对象命名 
//把创建的对象用一个引用数据类型的变量保存起来,这样就可以反复使用这个对象 了
类名 对象名 = new 类名();


//方式 2:
new 类名()//也称为匿名对象


class PersonTest{
    public static void main(String[] args){
        //创建 Person 类的对象 
        Person per = new Person(); //创建 Dog 类的对象
        Dog dog = new Dog();
    } 
}

步骤三:对象调用属性或方法 

对象是类的一个实例,必然具备该类事物的属性和行为(即方法)。

使用"对象名.属性" 或 "对象名.方法"的方式访问对象成员(包括属性和方法)

//声明 Animal 类
public class Animal { //动物类
  public int legs;
  public void eat() { System.out.println("Eating.");
  }
  public void move() { System.out.println("Move.");
  } 
}

//声明测试类
public class AnimalTest {
  public static void main(String args[]) { //创建对象
    Animal xb = new Animal(); 
    xb.legs = 4;  // 访问属性 
    System.out.println(xb.legs); 
    xb.eat();  //访问方法 
    xb.move();//访问方法
  } 
}

public class Game{
  public static void main(String[] args){
    Person p = new Person(); //通过 Person 对象调用属性 
    p.name = "康师傅"; 
    p.gender = '男';
    p.dog = new Dog(); //给 Person 对象的 dog 属性赋值
    
    //给 Person 对象的 dog 属性的 type、nickname 属性赋值 
    p.dog.type = "柯基犬";
    p.dog.nickName = "小白";
    
    //通过 Person 对象调用方法
    p.feed(); }
}

匿名对象 (anonymous object)

我们也可以不定义对象的句柄,而直接调用这个对象的方法。这样的对象叫做匿名对象。

  • 如:new Person().shout();

使用情况

  • 如果一个对象只需要进行一次方法调用,那么就可以使用匿名对象。

  • 我们经常将匿名对象作为实参传递给一个方法调用。

对象的内存解析

JVM 内存结构划分

HotSpot Java 虚拟机的架构图如下。其中我们主要关心的是运行时数据区部分(Runtime Data Area)。

堆(Heap):此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例 都在这里分配内存。这一点在 Java 虚拟机规范中的描述是:所有的对象实例以 及数组都要在堆上分配。

栈(Stack):是指虚拟机栈。虚拟机栈用于存储局部变量等。局部变量表存放 了编译期可知长度的各种基本数据类型(boolean、byte、char、short、int、 float、long、double)、对象引用(reference 类型,它不等同于对象本身,是 对象在堆内存的首地址)。 方法执行完,自动释放。

方法区(Method Area):用于存储已被虚拟机加载的类信息、常量、静态变 量、即时编译器编译后的代码等数据。

对象内存解析

//类:人 
class Person { 
  String name;
  int age;
  boolean isMale; 
}

//测试类
public class PersonTest { 
  public static void main(String[] args) {
    Person p1 = new Person(); 
    p1.name = "赵同学"; 
    p1.age = 20;
    p1.isMale = true;
    
    Person p2 = new Person(); 
    p2.age = 10;
    Person p3 = p1;
    p3.name = "郭同学"; 
  }
}

堆:凡是 new 出来的结构(对象、数组)都放在堆空间中。 对象的属性存放在堆空间中。

创建一个类的多个对象(比如 p1、p2),则每个对象都拥有当前类的一套 "副本"(即属性)。当通过一个对象修改其属性时,不会影响其它对象此 属性的值。

当声明一个新的变量使用现有的对象进行赋值时(比如 p3 = p1),此时 并没有在堆空间中创建新的对象。而是两个变量共同指向了堆空间中同一 个对象。当通过一个对象修改属性时,会影响另外一个对象对此属性的调 用。

对象名中存储的是什么呢

public class StudentTest{
  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
  } 
}

直接打印对象名和数组名都是显示“类型@对象的 hashCode 值",所以说类、数 组都是引用数据类型,引用数据类型的变量中存储的是对象的地址,或者说指 向堆中对象的首地址。

类的成员之一:成员变量(field) 

如何声明成员变量

语法格式:

[修饰符 1] class 类名{
        [修饰符 2] 数据类型 成员变量名 [= 初始化值];

}

语法说明:

  • 位置要求:必须在类中,方法外

  • 修饰符 2(暂不考虑)

    • 常用的权限修饰符有:private、缺省、protected、public

    • 其他修饰符:static、final

  • 数据类型

        • 任何基本数据类型(如 int、Boolean) 或 任何引用数据类型。

  • 成员变量名

        • 属于标识符,符合命名规则和规范即可。

  • 初始化值

        • 根据情况,可以显式赋值;也可以不赋值,使用默认值

语法示例

public class Person{
    private int age; //声明 private 变量 age
    public String name = “Lila”;  //声明 public 变量 name
}

成员变量 vs 局部变量

变量的分类:成员变量与局部变量

在方法体外,类体内声明的变量称为成员变量。

在方法体内部等位置声明的变量称为局部变量。

static 可以将成员变量分为两大类,静态变量和非静态变量。其 中静态变量又称为类变量,非静态变量又称为实例变量或者属性。

成员变量 与 局部变量 的对比

相同点:

  • 变量声明的格式相同: 数据类型 变量名 = 初始化值

  • 变量必须先声明、后初始化、再使用。

  • 变量都有其对应的作用域。只在其作用域内是有效的

不同点:

  • 声明位置和方式

实例变量:在类中方法外

局部变量:在方法体 {}中或方法的形参列表、代码块中

  • 在内存中存储的位置不同

实例变量:堆

局部变量:栈

class Person {//人类 //1.属性
    String name; //姓名
    int age = 1; //年龄
    boolean isMale; //是否是男性
    public void show(String nation) { 
        //nation:局部变量
        String color; //color:局部变量 
        color = "yellow";
    } 
}

//测试类
class PersonTest {
    public static void main(String[] args) {
        Person p = new Person();
        p.show("CHN");
    }
}

  • 生命周期

实例变量:和对象的生命周期一样,随着对象的创建而存 在,随着对象被 GC 回收而消亡, 而且每一个对象的实例变量是独立的。

局部变量:和方法调用的生命周期一样,每一次方法被调用而在存在,随 着方法执行的结束而消亡, 而且每一次方法调用都是独立。

  • 作用域

实例变量:通过对象就可以使用,本类中直接调用,其他类中 “对象.实例变量”

局部变量:出了作用域就不能使用

  • 修饰符

实例变量: public,protected,private,final,volatile,transient 等

局部变量:final

  • 默认值

实例变量:有默认值

局部变量:没有,必须手动初始 化。其中的形参比较特殊,靠实参给它初始化。

对象属性的默认初始化赋值

当一个对象被创建时,会对其中各种类型的成员变量自动进行初始化赋值。

类的成员之二:方法(method)

方法的引入

《街霸》游戏中,每次人物出拳、出脚或跳跃等动作都需要编写 50-80 行的代 码,在每次出拳、出脚或跳跃的地方都需要重复地编写这 50-80 行代码,这样 程序会变得很臃肿,可读性也非常差。为了解决代码重复编写的问题,可以将 出拳、出脚或跳跃的代码提取出来放在一个{}中,并为这段代码起个名字,这样 在每次的出拳、出脚或跳跃的地方通过这个名字来调用这个{}的代码就可以了。

上述过程中,所提取出来的代码可以被看作是程序中定义的一个方法,程序在需要出拳、出脚或跳跃时调用该方法即可。

方法(method、函数)的理解

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

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

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

Math.random()的 random()方法
Math.sqrt(x)的 sqrt(x)方法
System.out.println(x)的 println(x)方法
new Scanner(System.in).nextInt()的 nextInt()方法
Arrays 类中的 binarySearch()方法、sort()方法、equals()方法

public class Person{
    private int age;
    publicintgetAge() { //声明方法getAge()
        return age; 
    }
    public void setAge(int i) { //声明方法 setAge age = i; //将参数 i 的值赋给类的成员量 age
    } 
}

如何声明方法

声明方法的语法格式

[修饰符] 返回值类型 方法名([形参列表])[throws 异常列表] {

        方法体的功能代码

}

一个完整的方法 = 方法头 + 方法体

方法头就是[修饰符] 返回值类型 方法名([形参列表])[throws 异常列表],也称为 方法签名。通常调用方法时只需要关注方法头就可以,从方法头可以看出这个方法的功能 和调用格式。

方法体就是方法被调用后要执行的代码。对于调用者来说,不了解方法体如何实现的,并 不影响方法的使用。

方法头可能包含 5 个部分

修饰符: 可选的。方法的修饰符也有很多,例如:public、protected、private、static、 abstract、native、final、synchronized 等

  • 其中,权限修饰符有 public、protected、private。在讲封装性之前,我们 先默认使用 pulbic 修饰方法。

  • –  其中,根据是否有 static,可以将方法分为静态方法和非静态方法。其中静 态方法又称为类方法,非静态方法又称为实例方法。咱们在讲 static 前先 学习实例方法。

返回值类型: 表示方法运行的结果的数据类型,方法执行后将结果返回到调用者。

  • 无返回值,则声明:void

  • –  有返回值,则声明出返回值类型(可以是任意类型)。与方法体中“return 返回值”搭配使用

方法名:属于标识符,命名时遵循标识符命名规则和规范,“见名知意”

形参列表:表示完成方法体功能时需要外部提供的数据列表。可以包含零个,一个或多个参数。

  • 无论是否有参数,()不能省略

  • 如果有参数,每一个参数都要指定数据类型和参数名,多个参数之间使用 逗号分隔,例如
    • 一个参数: (数据类型 参数名)
    • 二个参数: (数据类型1 参数1, 数据类型2 参数2)
  • 参数的类型可以是基本数据类型、引用数据类型

throws 异常列表:可选 , 详情查看异常处理这篇文章

方法体

方法体必须有{}括起来,在{}中编写完成方法功能的代码

关于方法体中 return 语句的说明

return 语句的作用是结束方法的执行,并将方法的结果返回去

如果返回值类型不是 void,方法体中必须保证一定有 return 返回值; 语句,并且要求该返 回值结果的类型与声明的返回值类型一致或兼容。

如果返回值类型为 void 时,方法体中可以没有 return 语句,如果要用 return 语句提前结 束方法的执行,那么 return 后面不能跟返回值,直接写 return ; 就可以。

return 语句后面就不能再写其他代码了,否则会报错:Unreachable code

方法的分类:按照是否有形参及返回值

类比举例

代码示例

public class MethodDefineDemo {
    /**
     * 无参无返回值方法的演示
    */
    public void sayHello(){
        System.out.println("hello");
    }

    /**
     * 有参无返回值方法的演示
     * @param length int 第一个参数,表示矩形的长
     * @param width int 第二个参数,表示矩形的宽
     * @param sign char 第三个参数,表示填充矩形图形的符号 */
    public void printRectangle(int length, int width, char sign){
        for (int i = 1; i <= length ; i++) {
            for(int j=1; j <= width; j++){
                System.out.print(sign);
            }
            System.out.println();
        }
    }

    /**
     * 无参有返回值方法的演示 * @return
     */
    public int getIntBetweenOneToHundred(){
        return (int)(Math.random()*100+1);
    }
    
    /**
     * 有参有返回值方法的演示
     * @param a int 第一个参数,要比较大小的整数之一 * @param b int 第二个参数,要比较大小的整数之二 * @return int 比较大小的两个整数中较大者的值
     */
    public int max(int a, int b){
        return a > b ? a : b;
    }
}

如何调用实例方法

方法通过方法名被调用,且只有被调用才会执行。

方法调用语法格式

对象.方法名([实参列表])

方法调用案例演示

public class MethodInvokeDemo {
  public static void main(String[] args) {

  //创建对象
  MethodDefineDemo md = new MethodDefineDemo();

  System.out.println("-----------------------方法调用演示------- ------------------");
  //调用 MethodDefineDemo 类中无参无返回值的方法 sayHello md.sayHello();
  md.sayHello();
  md.sayHello();
  
  //调用一次,执行一次,不调用不执行
  System.out.println("----------------------------------------- -------");
  
  //调用 MethodDefineDemo 类中有参无返回值的方法 printRectangle md.printRectangle(5,10,'@');
  System.out.println("------------------------------------------------");
  
  //调用 MethodDefineDemo 类中无参有返回值的方法 getIntBetweenOneTo
  Hundred md.getIntBetweenOneToHundred();//语法没问题,就是结果丢失
  int num = md.getIntBetweenOneToHundred(); 
  System.out.println("num = " + num);
  System.out.println(md.getIntBetweenOneToHundred());
  //上面的代码调用了 getIntBetweenOneToHundred 三次,这个方法执行了 三次
  
  System.out.println("----------------------------------------- -------");
  
  //调用 MethodDefineDemo 类中有参有返回值的方法 max md.max(3,6);//语法没问题,就是结果丢失
  int bigger = md.max(5,6); System.out.println("bigger = " + bigger);
  System.out.println("8,3 中较大者是:" + md.max(8,9)); }
}
//1、创建 Scanner 的对象
Scanner input = new Scanner(System.in); //System.in 默认代表键盘输入
//2、提示输入 xx
System.out.print("请输入一个整数:"); //对象.非静态方法(实参列表)
//3、接收输入内容
int num = input.nextInt(); //对象.非静态方法()

使用的注意点

(1)必须先声明后使用,且方法必须定义在类的内部

(2)调用一次就执行一次,不调用不执行。

(3)方法中可以调用类中的方法或属性,不可以在方法内部定义方法。

关键字 return 的使用

  • –  作用 1:结束一个方法

  • –  作用 2:结束一个方法的同时,可以返回数据给方法的调用者

注意点:在 return 关键字的直接后面不能声明执行语句

方法调用内存分析

方法没有被调用的时候,都在方法区中的字节码文件(.class)中存储。

方法被调用的时候,需要进入到栈内存中运行。方法每调用一次就会在栈中有一个入栈动 作,即给当前方法开辟一块独立的内存区域,用于存储当前方法的局部变量的值。

当方法执行结束后,会释放该内存,称为出栈,如果方法有返回值,就会把结果返回调用 处,如果没有返回值,就直接结束,回到调用处继续执行下一条指令。

栈结构:先进后出,后进先出。

public class Person {
  public static void main(String[] args) {
    Person p1 = new Person(); 
    p1.eat();
  }
  public static void eat() {
    sleep();
    System.out.println("人:吃饭"); 
  }
  public static void sleep(){ 
    System.out.println("人:睡觉"); 
    doSport();
  }
  public static void doSport(){
    System.out.println("人:运动"); 
  }
}

对象数组

数组的元素可以是基本数据类型,也可以是引用数据类型。当元素是引用类型 中的类时,我们称为对象数组。

案例 Student 类

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

问题一:打印出 3 年级(state 值为 3)的学生信息。

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

提示:

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

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

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值