0%

面向对象编程-1

面向对象编程(基础部分)

★★★第七章 面向对象编程(基础部分)

★类与对象

  • 类是引用数据类型,类中定义有属性方法

    对象是类的一个具体实例。

  • 类是抽象的、概念的,代表一类事物,例如狗类、猫类等等,类是一种数据类型;

    对象是具体的、实际的,代表一个具体事物,即对象是类的实例。

    类是对象的模板,对象是类的一个个体,对应一个实例。

★对象内存布局
  • 对象内存布局,即对象在内存中的存在形式

    ![](../../../../../Running Noob/计算机/Typora笔记/笔记-git仓库/Java-notebook/img/Java/C7-1.jpg)

★属性/成员变量
  • 属性是类的一个组成部分,一般是基本数据类型,也可以是引用类型(对象、数组)

    成员变量 == 属性 == 字段,叫法不同而已。

    例如上图 cat.age 中的 age 就是猫类的年龄属性。

  • 注意

    • 属性的定义语法同变量,**访问修饰符 属性类型 属性名**;
    • 属性的定义类型可以为任意类型,包括基本类型和引用类型
    • 属性如果不赋值,有默认值,规则和数组一致。
      • int、short、byte、long ——> 默认值为 0
      • float、double ——> 默认值为 0.0
      • char ——> 默认值为 \u0000(空字符)
      • boolean ——> 默认值为 false
      • String ——> 默认值为 null
创建对象与访问属性
  • 创建对象:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    public class Test{
    public static void main(String[] args){
    /* 1.先声明再创建 */
    Cat cat1;
    cat1 = new Cat();
    /* 2.直接创建 */
    Cat cat2 = new Cat();
    }
    }
    class Cat{
    String name;
    int age;
    String color;
    }
  • 访问属性:

    基本语法:对象名.属性名

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    public class Test {
    public static void main(String[] args) {
    Cat cat1 = new Cat();
    cat1.name = "小白";
    cat1.age = 3;
    cat1.color = "白色";
    /* 使用属性 */
    System.out.println("cat1的具体信息为:" + cat1.name + " " + cat1.age + "岁 " + cat1.color);
    }
    }
    class Cat{
    String name;
    int age;
    String color;
    }
★★★类与对象的内存分配机制
  • 接下来分析下图中的代码:

    1. 首先在 JVM 的方法区中会加载 Person 类信息(包括属性信息和方法信息)(一个类的类信息只会加载一次);

    2. 然后 new Person() 会在中分配一段空间,由于 Person 类有两个属性,所以该空间中分配有两个存储属性内容的地方(初始时未赋值则这两个地方存储的是默认值,即 p1.age = 0;p1.name = null;默认初始化);

    3. 然后 Person p1 创建了变量 p1p1 指向了上面的那段空间,即把这段空间的地址赋给 p1

      ![](../../../../../Running Noob/计算机/Typora笔记/笔记-git仓库/Java-notebook/img/Java/C7-2.jpg)

    4. 然后通过 p1.age = 10;p1.name = "小明"; 给那段空间中的两个存储属性内容的地方赋值,且对于属性 name ,在堆中存储的是地址,该地址指向常量池中的一个地址空间,那个空间中存储的才是 “小明” ;

    5. 最后 Person p2 = p1; 创建了变量 p2,并让 p2 指向和 p1 相同的空间;

      ![](../../../../../Running Noob/计算机/Typora笔记/笔记-git仓库/Java-notebook/img/Java/C7-3.jpg)

  • 另:Java 内存的结构分析

    • :一般存放基本数据类型(局部变量)
    • :存放对象(Cat cat,数组等);
    • 方法区常量池(常量,比如字符串),类加载信息
克隆对象
  • 编写一个方法 copyPerson,可以复制一个 Person 对象,返回复制的对象。

    克隆对象,注意要求得到的新对象和原来的对象是两个独立的对象,只是他们的属性相同。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    public class Test {
    public static void main(String[] args) {
    Person p1 = new Person();
    p1.name = "小明";
    p1.age = 10;
    Person p2 = new MyTools().copyPerson(p1);
    System.out.println("修改属性前 p1 的信息为:" + p1.name + " " + p1.age + "岁");
    System.out.println("修改属性前 p2 的信息为:" + p2.name + " " + p2.age + "岁");
    System.out.println(String.class.getName() + "@" + Integer.toHexString(System.identityHashCode(p1.name))); //p1.name 地址
    System.out.println(String.class.getName() + "@" + Integer.toHexString(System.identityHashCode(p2.name))); //p2.name 地址
    p2.name = "小王";
    p2.age = 20;
    System.out.println("修改属性后 p1 的信息为:" + p1.name + " " + p1.age + "岁");
    System.out.println("修改属性后 p2 的信息为:" + p2.name + " " + p2.age + "岁");
    System.out.println(String.class.getName() + "@" + Integer.toHexString(System.identityHashCode(p1.name))); //p1.name 地址
    System.out.println(String.class.getName() + "@" + Integer.toHexString(System.identityHashCode(p2.name))); //p2.name 地址
    }
    }

    class Person{
    String name;
    int age;
    }

    class MyTools{
    public Person copyPerson(Person pOld){
    Person pNew = new Person();
    pNew.name = pOld.name;
    pNew.age = pOld.age;
    return pNew;
    }
    }
    • 注意:当执行完 Person p2 = new MyTools().copyPerson(p1); 后,p2.namep1.name 指向的是常量池中的同一个地方,只是在 p2.name = "小王"; 执行完毕后,会在常量池中分配有新的空间p2.name 重新指向了这个新的空间,该处存储有 “小王” 字符串。

成员方法

方法的定义
1
2
3
4
访问修饰符 返回数据类型 方法名 (形参列表..) {//方法体
语句;
return 返回值;
}
  • 注意
    1. 访问修饰符:用于控制方法的使用范围,有 publicprotectedprivate、默认;
    2. 返回数据类型:表示方法输出,void 表示没有返回值;
    3. 形参列表:表示方法输入;
    4. 方法体:表示为了实现某一功能的代码块;
    5. return 语句不是必须的(对于返回类型为 void 的方法,可以不需要 return 语句)。
★方法的调用机制
  • 如下图所示,是方法的调用机制:

    1. 首先由于在 main 方法中执行 Person p1 = new Person();,所以在栈中会有一个 **main栈**,在该栈中执行这句话,就会在堆中分配一段空间,然后变量 p1 指向这个空间;

    2. 然后 p1 调用方法 getSum(10,20),此时就会在栈中分配一段独立的空间,由于 getSum 函数有两个形参和一个返回参数,所以在该段独立的空间中就有三个存储空间,分别存储 num1num2res

    3. 然后 return res; ,即在独立的空间(getSum栈。这里是为了方便讲解而命名该空间为 getSum栈,实际上该空间没有这个名字)中执行 return res; ,就会把结果 res 返回给 main栈 中的 getSum(10,20)

      ![](../../../../../Running Noob/计算机/Typora笔记/笔记-git仓库/Java-notebook/img/Java/C7-4.jpg)

    4. 在返回完结果 res 后,那个独立空间就会被释放main栈继续执行 println 函数。

      ![](../../../../../Running Noob/计算机/Typora笔记/笔记-git仓库/Java-notebook/img/Java/C7-5.jpg)

  • 综上所述:

    • 当程序执行方法时,就会分配一个独立的空间(栈空间)

    • 当方法执行完毕或执行到 return 语句时,就会返回到调用该方法的地方,并且该独立的栈空间就会释放

      (由此可知,当 main栈 执行完毕后,也会被释放,整个程序退出)

★方法使用细节
  • 返回数据类型

    1. 一个方法最多只有一个返回值(为了返回多个值,可以返回一个数组);
    2. 返回数据类型可以是任意类型,包括基本数据类型引用数据类型(数组、对象等);
    3. 如果方法要求有返回数据类型,则方法体中最后的执行语句必须为 return 值;而且要求返回值类型必须和 return 的值类型一致或兼容
    4. 如果方法是 void,则方法体中可以没有 return 语句,或者只写 return
  • 方法名

    • 多单词组成时,第一个单词首字母小写,第二个单词开始每个单词首字母大写:xxxYyyZzz;—> (小)驼峰命名方式

      方法名使用小驼峰命名方式

  • 形参列表

    1. 一个方法可以有 0 个参数,也可以有多个参数,中间用逗号隔开,例如 int getSum(int num1, int num2)
    2. 形参类型可以是任意类型,包括基本数据类型引用数据类型(数组、对象等),例如 printArr(int[][] map)
    3. 调用带参数的方法时,一定对应着参数列表传入相同类型或兼容类型的参数
    4. 方法定义时的参数称为形式参数,简称形参方法调用时的参数称为实际参数,简称实参实参和形参的类型要一致或兼容,个数、顺序必须一致
  • 方法体

    • 里面写完成功能的具体的语句,可以为输入、输出、变量、运算、分支、循环、方法调用,但里面不能再定义方法,即方法不能嵌套定义
  • 方法调用

    1. 同一个类中的方法调用:直接调用即可;

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      public class Test{
      public static void main(String[] args) {
      Person p = new Person();
      p.name = "小明";
      p.intro();
      }
      }

      class Person {
      String name;
      int age;
      public void speak() {
      System.out.println("我是" + this.name);
      }
      public void intro(){
      this.speak();
      System.out.println("退出intro()");
      }
      }
    2. 跨类的方法调用,A 类方法调用 B 类方法,需要通过 B 类的对象调用;

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      public class Test{
      public static void main(String[] args) {
      A a = new A();
      a.print();
      }
      }

      class A {
      public void print(){
      System.out.println("这里是 A 的 print 函数");
      new B().print();
      }
      }

      class B {
      public void print(){
      System.out.println("这里是 B 的 print 函数");
      }
      }

      注意:如果在 B 类中,print() 方法的访问修饰符为 private,则此时 A 类就不能调用 B 类的 print() 方法了,该方法就只能在 B 类内部使用。即跨类的方法调用和方法的访问修饰符相关

★方法传参机制
  • 对于基本数据类型,传递的是(值拷贝),形参的任何改变不影响实参。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    public class Test {
    public static void main(String[] args) {
    AA a = new AA();
    int num1 = 1, num2 = 2;
    System.out.println("num1 和 num2 执行函数前的值为:num1 = " + num1 + " num2 = " + num2);
    //输出为:num1 和 num2 执行函数前的值为:num1 = 1 num2 = 2
    a.swap(num1, num2);
    System.out.println("num1 和 num2 执行函数后的值为:num1 = " + num1 + " num2 = " + num2);
    //输出为:num1 和 num2 执行函数后的值为:num1 = 1 num2 = 2
    }
    }

    class AA {
    public void swap(int a, int b) {
    System.out.println("a 和 b 交换前的值为:a = " + a + " b = " + b);
    //输出为:a 和 b 交换前的值为:a = 1 b = 2
    int temp = a;
    a = b;
    b = temp;
    System.out.println("a 和 b 交换后的值为:a = " + a + " b = " + b);
    //输出为:a 和 b 交换后的值为:a = 2 b = 1
    }
    }

    ![](../../../../../Running Noob/计算机/Typora笔记/笔记-git仓库/Java-notebook/img/Java/C7-6.jpg)

  • 对于引用数据类型,传递的是地址(传递的也是值,但是这个值是地址),可以通过形参影响实参。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    public class Test {
    public static void main(String[] args) {
    AA a = new AA();
    int[] array = {0,0,0};
    System.out.println("执行函数前 array[0] = " + array[0]);
    //输出为:执行函数前 array[0] = 0
    a.change(array);
    System.out.println("执行函数后 array[0] = " + array[0]);
    //输出为:执行函数后 array[0] = 100
    }
    }

    class AA {
    public void change(int[] arr){
    arr[0] = 100;
    }
    }
---------------The End---------------