10.[Java 面向对象] 对象的属性与方法


1. 类与对象

1.1 类和对象的区别和联系

  • 是抽象的概念,是一个数据类型,代表一类事物,比如人类,鸟类等等…

  • 对象 是具体的实际的,代表一个具体事物,即实例,比如 我的同学 “小明” 是人类的一个实例

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

1.2 类的属性和成员变量

  1. 成员变量是用来表示属性的,即 : 成员变量 = 属性 = field

  2. 属性是类的一个组成部分,可以为任意类型,包含基本类型或引用类型(对象,数组等)

  3. 比如 1.2 中定义的猫类的 age 就是属性

  4. 属性的定义语法与变量相同 访问修饰符 属性类型 属性名;

  5. 访问修饰符为: public,proctected ,private,默认(不使用访问修饰符)

  • 属性如果不赋值,则为默认值,默认值规则如下:
类型 默认值
byte, short, int, long 0
float, double 0.0
char \u0000
boolean false
其它引用类型 null
  • 代码示例
public class PropletiesDetail{
  public static void main(String args[]){
    // 创建 Person 对象
    Person p1 = new Person();
    // p1 是对象名(对象引用)
    // new Person() 创建的对象空间(数据) 这才是真的对象
  }
}

class Person{
  // 定义类的属性
  int age;
  String name;
  double sal;
  boolean isPass;
}

1.3 创建对象的方式

// 方式1. 先声明再创建
Cat cat;
cat = new Cat();

// 方式2. 直接创建
Cat cat = new Cat();

1.4 类和对象的内存分配机制

Java 内存有几下几个重要的部分组成

  1. 栈: 一般存放基本数据类型的数据(局部变量)
  2. 堆: 存放引用类型的数据(对象,数组)等
  3. 方法区: 常量池(常量,比如字符串),类加载信息等
  • 对象在内存中存在的形式1
// 实例化一个 cat 对象
Cat cat = new Cat();
cat.name = "汤姆";
cat.age = 100;
cat.color = "blue";
  • 对象在内存中存在的形式2
Persion p1 = new Persion();
p1.name = "小明";
p1.age = 10;
Persion p2 = new Persion();
p2 = p1;
System.out.println(p2.age)
// 思考一个问题,上面代码中 p2.age 是多少?
上述代码的内存图

1.5 创建对象的流程

Person p = new Person();
p.name = "小明";
p.age = 10;

// 1. 先加载 Person 类信息(属性和方法信息, 只会加载一次)
// 2. 把堆中分配空间, 进行默认初始化
// 3. 把堆中的地址地址赋给 p, p 就指向对象
// 4. 进行制定初始化, 比如 p.name = "小明", p.age = 10

2. 成员方法介绍

2.1 基本介绍

在某些情况下,我们需要定义成员方法,

比如人类: 除了有一些属性(年龄、姓名)外,还有一些行为,比如可以说话、 跑步等,

这时就要用成员方法才能完成,现在要求对 person 类进行完善

成员方法可以提高代码的复用性,还可以将实现的细节封装起来,然后供其他用户来调用

2.2 成员方法的定义

  • 成员方法由如下几个部分组成:
  1. 访问修饰符:用来控制方法的使用范围, 如果不写则使用默认访问范围, 一共有四种(public, protected,无修饰符(默认),private)
  2. 形参列表:表示成员方法输入参数
  3. 返回数据类型:表示成员方法运行完成之后输出的数据类型, 当返回数据类型位置上为 void 时, 表示没有返回值
  4. 方法主体:表示为了实现方法功能的代码块
  5. return 语句:表示方法返回的数据, 与返回数据类型需一直, 当没有返回值时, 则不需要 return 语句
// 定义成员方法语法
访问修饰符 返回数据类型 方法名(形参列表 ... ){
  // 方法体
  语句 1;
  语句 2;
  ...
  return 返回值
}

// 示例
public int sum(int a,int b){
  // 方法体
  int c = a+b;
  // 返回值
  return c;
}

2.3 返回数据类型

  1. 一个方法最多有一个返回值,如果想返回多个值, 可以使用数组,集合等
  2. 方法的返回值可以为任意类型,包含基本数据类型和引用数据类型
  3. 如果方法要求有返回数据值,则方法体中最后一条执行语句必须为: return 返回值;
  4. 要求返回值类型必须和 return 的值类型一致或兼容
  5. 如果方法返回类型位置为 void,则方法体中可以不写 return 语句,或者只写 return;
// 有返回值的方法
public int sum(int a,int b){
  // 方法体
  int c = a+b;
  // 使用 return 关键字返回数据,
  return c; // 返回值的类型需要与设置的返回的类型一致
}

// 没有返回值的方法
public void say(String name){
    System.out.println("My name  is "+ name)
}

2.4 方法命名规则

方法命名规则与标识符命名规则基本相同,但是在符合规则的情况下最好使用驼峰法命名,最好见名知意,表达出该方法功能的意思.

方法命名注意遵循以下规则:

  1. 方法名可以由26 个英文字母, 数字 0~9, 下划线 _ ,美元 $ 符号组成, 但是不能以数字开头
  2. 方法名不能是 Java 关键字和保留字, 但关键字和保留字可以作为方法名的一部分
  3. 方法名只能包含美元符号 $ 不能包含 @,# 等特殊符号.
  4. 方法名不能包含空格
  5. Java中严格区字母分大小写
  6. 方法名长度无限制, 但是一般不建议使用过长的方法名
// 错误,extends 是关键字
public void extends(){ }
// 不能以数字开头
public void 1hello(){ }

// 正确
public void hello$(){ }
// 错误,不能使用 @ 符号
public void hello@(){ }
// 正确
public void hello_(){ }
// 正确
public void _hello(){ }

2.5 形参列表

  1. 一个方法可以有 0 到多个参数,形参之间用逗号分隔
  2. 参数类型可以为任意数据类型,包括基本类型或引用数据类型
  3. 方法定义时设置的参数称为形式参数,简称形参
  4. 方法调用时传入的参数称为实际参数,简称实参
  5. 调用带参数的方法时,实参与形参的类型需要一致或兼容,参数个数、参数顺序必须一致
public class Method03 {
    public static void main(String[] args) {
        Method03 method03 = new Method03();
        // 定义 int 类型变量 a 值为 10
        int a = 10;
        // 定义 boolean 类型变量 a 值为 false
        boolean b = false;
        // 定义 String 类型变量 a 值为 hello
        String c = "hello";
        // 调用方法时传入的参数是实参,实参的顺序与类型跟形参要保持一致
        method03.test(a,b,c);

        // 报错参数数量不一致
        method03.test(a);

        // 报错参数类型不兼容
        method03.test(c,b,a);
    }

    /**
     * 这里定义一个有如下三个形参的方法
     * @param a int 类型的形参 a
     * @param b boolean 类型的形参 b
     * @param c String 类型的形参 c
     */
    public void test(int a,boolean b,String c){
        // 方法体
        System.out.println("a = "+a+", b= "+b+", c= "+c);
    }
}

2.6 方法体

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

  • 方法体内不能再定义方法,即 方法不能嵌套定义

public void test(int a,boolean b,String c){
    // 方法体
    System.out.println("a = "+a+", b= "+b+", c= "+c);
}

2.7 成员方法使用示例

  1. 同一个类中的方法之间调用,可以直接调用,比如 print(参数)

  2. 跨类中的方法调用,需要通过对象名调用,比如: 对象名.方法名(参数列表)

  3. 特别说明一下: 跨类的方法调用和方法的访问修饰符相关,后面讲到访问修饰符时会详细说明

  4. 添加 speak 方法,输出 “你好”

  5. 添加 sum01 方法,可以计算 1+2+3…+1000 的结果

  6. 添加 sum02 方法,接收一个参数,计算 1+2+…+n 的结果

  7. 添加一个 sum 方法,计算两个数的和

  • 代码示例
public class Method01 {
    public static void main(String args[]) {
        // 先创建一个 person 对象
        Person person = new Person();
        // 使用对象名调用 speak 方法
        person.speak();
        person.sum01();
        person.sum02(100);
        int result = person.sum(1,2);
        System.out.println(result);
    }

}
class Person{
    String name;
    int age;

   public void open(){
        System.out.println("先张嘴再说话");
    }

    /**
     * public 公开访问
     * void 没有返回值
     * speak 方法名
     * () 形参列表, 此方法没有形参, 所以括号内无内容
     * {} 方法体
     * System.out.println("你好"); 方法内的执行语句
     */
    public void speak(){
        open(); // 同类中的方法可以直接调用
        System.out.println("你好");
    }
    /**
     * 添加 方法, 从 1 累加到 1000
     */
    public void sum01(){
        int sum = 0;
        for (int i = 0; i <= 1000; i++) {
            sum +=i;
        }
        System.out.println("sum01=>"+sum);
    }

    /**
     * 添加 方法, 从 1 累加到 x
     */
    public void sum02(int x){
        int sum = 0;
        for (int i = 0; i <= x; i++) {
            sum +=i;
        }
        System.out.println("sum02=>"+sum);
    }

    /**
     * 添加 方法, x + y 然后把结果返回
     * public 公开访问
     * int 返回值为 int 类型
     * sum 方法名
     * () 形参列表
     * int x 第一个参数为 int 类型, 名称为 x
     * int y 第二个参数为 int 类型, 名称为 y
     * {} 方法体
     * int result = x+y; 将 x , y 相加
     * return result; 将 result 返回
     */
    public int sum(int x,int y){
        int result = x+y;
        return result;
    }
}

2.8 调用方法的原理

  1. 当程序执行到方法时,就会开辟一个独立的栈空间
  2. 当方法执行完毕,或者执行到 return 语句时,就会将结果返回到调用方法的地方
  3. 被调用的方法返回后,继续执行后续代码
  4. 当 main 方法执行完成后,整个程序退出
sum 方法调用流程图

2.9 基本类型参数传递

方法的参数传递时,基本类型的参数传递是值拷贝,形参的改变不影响实参的具体值

  • 测试代码
public class Method02 {
    public static void main(String args[]) {
        A a  = new A();
        int i = 1,j = 3;
        a.swap(1,3);
        System.out.println("main 方法: i= "+i+", j= "+j);
        // main 方法: i= 1, j= 3
    }

    static class A{
        public void swap(int i,int j){
            System.out.println("交换前: i= "+i+", j= "+j);
            // 交换前: i= 1, j= 3
            int tmp = i;
            i = j;
            j = tmp;
            System.out.println("交换后: i= "+i+", j= "+j);
            //交换后: i= 3, j= 1
        }
    }
}

基本类型参数传递

2.10 引用类型参数传递

引用类型参数传递是地址传递,在对形参进行修改时会影响到实参

引用类型参数传递后,形参与实参仍是两个不同的引用变量,但是指向相同的实体

  • 测试代码
public class Method02 {
    public static void main(String args[]) {
        int arr[] = {1,2,3};
        A a  = new A();
        a.param(arr);
        System.out.println("main 函数");
        for (int k = 0; k < arr.length; k++) {
            System.out.print(arr[k]+",  "); // 100,2,3
        }
    }

    static class A{
        public void param(int[] arr){
            System.out.println("修改数组前");
            for (int k = 0; k < arr.length; k++) {
                System.out.print(arr[k]+",  "); // 1,2,3
            }
            arr[0] = 100;
            System.out.println("修改数组后");
            for (int k = 0; k < arr.length; k++) {
                System.out.print(arr[k]+",  "); // 100,2,3
            }
        }
    }
}

引用类型参数参数传递

3. 方法递归调用

3.1 递归的介绍

  • 简单的说,方法的递归调用就是方法调用自己本身,每次调用传入不同的变量, 递归有助于解决复杂问题, 同时可以让代码变得更简洁。

  • 那么递归能解决什么问题呢?

  1. 递归可以应用于各种数学问题,如 8 皇后问题,汉诺塔问题,阶乘问题,迷宫问题,球和篮子的问题等

  2. 各种算法中也会使用到递归,如快速排序,归并排序,二分法查找,分治算法等

将用栈解决的问题 ,递归代码比较简洁

3.2 递归的规则

  1. 递归执行一个新的方法时,就创建一个新的受保护的独立栈空间
  2. 方法的局部变量在自己的栈内,也就是独立的,不同方法的变量不会相互影响
  3. 如果方法中使用的是引用变量(比如数组),就会共享该引用类型的数据
  4. 递归必须向退出递归的条件逼近,否则就会出现无限递归,会出现 StackOverflowError
  5. 当一个方法执行完毕,或者遇到 return,就会返回,遵守谁调用就将结果返回给谁的原则
  6. 当方法执行完毕或者返回时,该方法就执行结束

3.3 方法递归调用

public class Recursion {
    public static void main(String args[]) {
        Recursion recursion = new Recursion();
        recursion.test(4);
    }

    public void test(int n){
        if(n >2){
            test(n-1);
        }
        System.out.println("n = "+ n);
    }
}
  • 运行示例
方法递归调用

3.4 递归计算阶乘

public class Recursion {
    public static void main(String args[]) {
        Recursion recursion = new Recursion();
        int result = recursion.factorial(2);
        System.out.println(result);
    }

    public int factorial(int n){
        if(n == 1){
            return 1;
        }else{
            return factorial(n-1)*n;
        }
    }
}

递归计算阶乘图示

3.5 递归练习

  • 猴子吃桃子的问题

有一堆桃子,猴子第一天吃了其中的一半,并且再多吃一个,以后每天都吃了一半再多吃一个,当到第 10 天时,想再吃,发现只有 1 个桃子了,问最初一共有多少桃子

public class Test02Peaches {
    public static void main(String args[]) {
        for (int i = 1; i <= 10; i++) {
            System.out.println("第 "+i+" 天 桃子数量为: "+count(i));
        }
    }
//    第 1 天 桃子数量为: 1534
//    第 2 天 桃子数量为: 766
//    第 3 天 桃子数量为: 382
//    第 4 天 桃子数量为: 190
//    第 5 天 桃子数量为: 94
//    第 6 天 桃子数量为: 46
//    第 7 天 桃子数量为: 22
//    第 8 天 桃子数量为: 10
//    第 9 天 桃子数量为: 4
//    第 10 天 桃子数量为: 1

    /**
     * 有一堆桃子,
     * 猴子第一天吃了其中的一半, 并且再多吃一个,
     * 以后每天都吃了一半再多吃一个,
     * 当到第 n 天时, 想再吃, 发现只有 1 个桃子了,
     * 问最初一共有多少桃子
     * // 第十天 桃子数量 1
     * // 第九天 桃子数量 (1+1)*2 = 4
     * // 第八天 桃子数量 (4+1)*2 = 10
     *
     * 规律为 前一天的桃子 = (后一天的桃子 +1) * 2
     */
    public static int count(int day){
        if(day == 10){
            return 1;
        }else{
            return (count(day+1)+1)*2;
        }
    }
}
  • 斐波那契数列
public class Fibonacci {
    public static void main(String args[]) {
        for(int i=1;i<10;i++){
            System.out.println(getFibonacci(i));
        }
    }

    public static long getFibonacci(int n){
        if(n>=1){
            if(n == 1|| n ==2){
                return 1;
            }else{
                return getFibonacci(n-1)+getFibonacci(n-2);
            }
        }
        return 0;
    }
}

4. 方法重载

Java 中允许同一个类中写多个同名方法,这些方法就构成了重载,方法的重载还有以下详细的要求:

  1. 方法名称:必须相同
  2. 形参列表:形参的数据类型 或 形参个数 至少 有一样不同,对于形参名称没有要求
  3. 返回类型:无要求
  • 方法重载代码示 例1
public class Overload01 {
    public static void main(String args[]) {
        Overload01 o = new Overload01();

        // 调用方法 1
        int a = o.sum(1, 2);
        System.out.println("a = " + a);

        // 调用方法 2
        double b = o.sum(10.1, 2);
        System.out.println("b = " + b);

        // 调用方法 3
        double c = o.sum(9.9, 3.3);
        System.out.println("c = " + c);

        // 调用方法 4
        int d = o.sum(1, 2, 3);
        System.out.println("d = " + d);
    }

    // 下面的四个 sum 方法构成了重载

    /**
     * 方法1 两个参数 int,int
     */
    public int sum(int a, int b) {
        return a + b;
    }

    /**
     * 方法2
     * 两个参数 double,int
     */
    public double sum(double a, int b) {
        return a + b;
    }

    /**
     * 方法3
     * 两个参数 double,double
     */
    public double sum(double a, double b) {
        return a + b;
    }

    /**
     * 方法4
     * 三个参数 int,int,int
     */
    public int sum(int a, int b, int c) {
        return a + b;
    }
}
  • 方法重载代码示 例2
  1. 编写程序,类 Overload02 中定义三个重载方法并调用,方法名为 m

第一个 m 方法接收一个 int 参数,输出 平方 信息

第二个 m 方法接收两个 int 参数,输出 求和 信息

第三个 m 方法接收字符串,并打印该 字符串 信息

  1. 在 Overload02 类中定义 max 方法的重载,

第一个 max 方法, 返回 两个 int 数据的最大值

第二个 max 方法, 返回 两个 double 数据的最大值

第三个 max 方法, 返回 三个 double 数据的最大值

public class Overload02 {
    public static void main(String args[]) {
        Overload02 o = new Overload02();
        //调用 m 方法
        System.out.println(o.m(9));
        System.out.println(o.m(9, 2));
        o.m("hello");

        //调用 max 方法
        System.out.println(o.max(1, 2));
        System.out.println(o.max(1.0, 2.0));
        System.out.println(o.max(1.0, 2.0, 3.0));
    }

    // 返回 两个 int 数据的最大值
    public int max(int a, int b) {
        if (a > b) {
            return a;
        } else {
            return b;
        }
    }
    // 返回 两个 double 数据的最大值
    public double max(double a, double b) {
        if (a > b) {
            return a;
        } else {
            return b;
        }
    }
    // 返回 三个 double 数据的最大值
    public double max(double a, double b, double c) {
        double m = a > b ? a : b;
        m = m > c ? m : c;
        return m;

    }

    // 输出 平方 信息
    public int m(int a) {
        return a * a;
    }
    // 输出 求和 信息
    public int m(int a, int b) {
        return a + b;
    }
    // 输出字符串
    public void m(String str) {
        System.out.println(str);
    }


}

5. 方法的可变参数

Java 中的方法,可以将形参中 多个 同类型的形参 合并成一个形参,这种情况就是 可变参数

  • 可变参数基本语法:
// public 访问修饰符
// void 返回数据类型
// methodName 方法名称
// int ... arrs 定义可变参数
public void methodName(int ... arrs){
  // 方法体
}
  • 可变参数注意事项
  1. 可变参数的实参可以为 0 到任意多个
  2. 可变参数的实参可以为数组
  3. 可变参数的本质就是数组
  4. 可变参数可以和普通参数一起放在形参列表中,但必须保证可变参数在最后一位
  5. 一个形参列表中最多 只能 定义 一个 可变参数
  • 编写代码, 计算一个学生的 n 门课程的总成绩
public class VarParameter01 {
    public static void main(String args[]) {
        VarParameter01 v = new VarParameter01();
        System.out.println("累加和 为: " + v.sum(1, 2, 3));
        System.out.println(v.score("小明", 90, 98, 2));
    }

    /**
     * int...  表示接收的是可变参数, 类型是 int , 可以接收 0 到多个 int 值
     * 遍历 nums , 可以当做数组进行遍历
     */
    public int sum(int... nums) {
        System.out.println("参数个数" + nums.length);
        int result = 0;
        for (int i : nums) {
            result += i;
        }
        return result;
    }

    /**
     * 编写方法, 返回学生 n 门课程的总成绩
     *
     * @param name 学生姓名
     * @param socres 多门课程的成绩
     * @return
     */
    public String score(String name, int... socres) {
        System.out.println("科目数" + socres.length);
        int total = 0;
        for (int i : socres) {
            total += i;
        }
        return name + " 总分数:" + total;
    }
}

6. 作用域

  • 作用域介绍
  1. 作用域是指变量的有效范围,在一个方法中能够访问到该变量,就说明方法在变量的有效作用域中
  2. Java 中根据变量的定义位置不同,作用域也有很大的差别,如:
    • 全局变量,即成员属性,作用域为整个类
    • 局部变量,也就是除了成员属性之外的变量,一般是在方法中定义的变量,其作用域为定义它的代码块中
  3. 全局变量和局部变量可以重名,访问同名变量时,遵循就近原则
  4. 在同一个作用域中的两个变量不能重名
  • 全局变量与局部变量的区别
全局变量 局部变量
默认值 如果没有显式赋值则系统赋默认值 必须显式赋值
作用域 整个类 所在的方法或代码块
生命周期 伴随着对象的声明而创建,对象的销毁而销毁 跟所在的方法或代码块一致,(一次方法的调用)
访问 可以被本类和其他类访问 只能在本类对应的方法中使用
修饰符 可以加访问修饰符 不可以加访问修饰符
  • 测试代码
public class VarScope {
    public static void main(String args[]) {
        Cat cat = new Cat();
        cat.hi();
        cat.eat();
        cat.rename();
    }
}

class Cat {
    // 全局变量, 也就是类的属性, 作用域为整个类, 包括类中所有的方法都可以访问类的属性
    // 在定义属性时, 可以直接赋值, 也可以不进行赋值
    String name = "tom";

    // 定义属性, 不进行显式赋值, 系统会根据类型为变量赋予默认值
    int age;

    public void hi(){
        age = 10;
        // 局部变量一般是指定义在方法内的变量
        // 定义局部变量, 局部变量必须显式的赋值后再使用
        // 局部 gender 变量的作用域为 hi 方法
        char gender = 'M';
        System.out.println("name = "+ name+", age = "+ age+", gender = "+ gender);
    }

    public void eat(){
        // 定义局部变量
        String food = "jerry";
        System.out.println(name +" 喜欢吃 "+ food);
    }

    // 定义重名变量, name, 将原来的 name = tom 改成 name  = jerry
    public void rename(){
        // 重名变量遵循就近访问原则
        String name = "jerry";
        System.out.println("name = "+ name+", age = "+ age);
    }
}

7. 构造方法

  • 介绍

构造方法,又称 构造函数 或 构造器(constructor) ,是类的一种特殊方法,它的主要作用是完成对新对象的初始化

  1. 构造方法名和类名必须相同
  2. 构造方法没有返回值
  3. 构造方法的调用由系统自动完成
  4. 构造方法的修饰符可以使用 public,protected,默认修饰符 或 private
  5. 构造方法可以有 0 到多个参数
  • 基本语法
// 定义成员方法语法
访问修饰符 方法名(形参列表 ... ){
  // 方法体
  语句 1;
  语句 2;
}

// 示例
public Cat(String name,int age){
  // 方法体
  this.name = name;
  this,age = age;
}

// 访问修饰符: 控制方法的使用范围, 如果不写则使用默认访问范围, 一共有四种(public, protected,默认,private)
// 形参列表: 表示成员方法输入参数
// 方法主体: 表示为类的属性进行初始化
  • 代码示例

在创建人类时,直接指定这个对象的年龄和姓名

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

        // 直接通过我们创建的构造函数创建新的对象
        Person01 xiaom = new Person01("xiaom", 18);
    }
}

class Person01{
    String name;
    int age;

    // 定义 Person 类的构造函数
    public Person01(String name, int age) {
        this.name = name;
        this.age = age;
    }
}
  • 构造方法的其他细节
  1. 构造方法可以重载,即一个类可以定义多个参数不同的构造方法
  2. 构造方法主要作用是完成对象的初始化,并不是创建对象
  3. 创建对象时,系统会自动调用该类的构造器完成对对象的初始化
  4. 如果没有自定义构造方法,那么系统会添加一个默认无参的构造方法
  5. 如果自定义了构造方法,那么系统便不会添加默认的构造方法,除非显式的定义一个无参的构造方法
  • 代码示例
public class Constructor01 {
    public static void main(String args[]) {
        // 调用两个参数的构造方法创建新的对象
        Person01 p1 = new Person01("小明", 18);
        // 调用一个参数的构造方法创建新的对象
        Person01 p2 = new Person01("小雪");
        // 调用无参构造方法创建新的对象
        Person01 p3 = new Person01();
    }
}

class Person01 {
    String name;
    int age;
    // 显式定义 Person 类 无参构造方法, 
    //当未定义构造方法时, 系统便会生成该默认构造方法
    public Person01() {}
    // 定义 Person 类 有两个参数的构造方法,
    // 如果声明了一个自定义的构造方法, 系统便不会生成默认的构造方法
    public Person01(String name, int age) {
        this.name = name;
        this.age = age;
    }
    // 定义 Person 类 有一个参数的构造方法
    public Person01(String name) {
        this.name = name;
    }
}

8. 对象创建流程

class Person{ // Person 类
  int age = 90; //定义成员属性,并赋予 默认值90
  String name; // 定义成员属性,没有给默认值
  Person(String name, int age){ // 构造函数,有name,age 两个参数
    this.name = name; // 给 n3ame 赋值
    this.age = age; // 给 age 赋值
  }
}
Person P  = new Person("阿花",18); // 实例化对象

1. 首先加载类的信息

2. 在堆中创建存储该对象的空间

3. 为对象进行默认初始化

4. 为对象进行显式赋值

5. 创建构造器中的字符串常量,先在常量池中查找

6. 未在常量池中找到,创建新的字符串常量

7. 根据构造方法为对象初始化赋值

8. 在栈中创建对象的引用并指向堆中的对象

9. 将对象的引用返回

9. this 关键字

  • this 介绍
  1. Java 虚拟机会给每个对象分配一个 this,代表当前对象
  2. 简单的说哪个对象使用this调用属性或方法,那么这个对象就是 this
  3. this 关键字可以用来访问本类的属性,方法,构造器等
  4. this 用来区分当前类的属性和局部变量
  5. 使用 this 访问成员方法,this.方法名(参数列表)
  6. 使用 this 访问构造方法,this(参数列表),只能在一个构造器中使用 this 访问另外一个构造器
  7. 不能在成员方法中使用 this 访问构造方法
  8. this 只能在类定义的方法中使用,不能再类定义的外部使用
this 内存示意图
  • 使用代码测试 this 是否代表了当前对象
// 使用打印 hashCode 的方式查看是否为同一个对象
public class This02 {
    public static void main(String args[]) {
        Animal animal = new Animal();
        System.out.println("animal.hashCode() "+ animal.hashCode());
       // animal.hashCode() 918221580
        animal.say();
    }
}

class Animal{
    public void say(){
        System.out.println("this.hashCode() "+this.hashCode());;
        // this.hashCode() 918221580
        System.out.println("动物叫");
    }
}
  • 测试代码
public class This01 {
    public static void main(String args[]) {
        Person02 person02 = new Person02();
        person02.method02();

        Person02 person03 = new Person02("小花", 20);
        System.out.println(person03.toString());
    }
}

class Person02 {
    String name;
    int age;

    public void method01() {
        System.out.println("method01");
    }

    public void method02() {
        System.out.println("method02");
        // 调用本类的 method01 方法
        // 第一种调用方式
        method01();
        // 第二种调用方式
        this.method01();
    }

    // 添加无参构造方法
    Person02() {}

    // 添加一个参数的构造方法
    Person02(String name) {
        this.name = name;
    }

    // 添加两个参数的构造方法
    Person02(String name, int age) {
        // 在构造方法中使用 this 调用构造方法只能放在方法中的第一行
        this(name);
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person02{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

文章作者: hnbian
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 hnbian !
评论
 上一篇
11.[Java 面向对象] 包与访问修饰符 11.[Java 面向对象] 包与访问修饰符
1. 包1.1 介绍包 包的本质就是创建不同的文件夹,来保存类文件 同一个包内不允许有重名的类,但是可以在不同包中相同名字的类 通过包可以进行访问权限的控制 当系统比较复杂时, 类会比较多, 使用包可以方便对类进行管理 声明包的基本语法
2018-01-30
下一篇 
9.[Java 基础]数组 9.[Java 基础]数组
1. 数组类型介绍 数组是一种常见的数据结构, 用于存放多个同一类型的数据 数组中存放的数据,既可以是基本数据类型也可以是引用数据类型 数组本身是一种引用数据类型 通过 数组名称[数组下标] 来访问数组的元素 数组元素如果没有显式的赋值,
2018-01-25
  目录