Skip to content

7.1 类与对象

7.1.5 类与对象的关系示意图 0192

graph LR
   A["把所有猫的
   特性(属性)
   提取出来"] --> B("猫类(Cat)-数据类型");
   B --> C1("对象(实例)");
   B --> C2("对象(实例)");
   B1("属性
   name, age, color."):::attr --> B;
   B2("行为
   run cry eat..."):::action --> B;
   style A fill:#f9f,stroke:#333,stroke-width:2px
   style B fill:#ccf,stroke:#333,stroke-width:2px
   style C1 fill:#ccf,stroke:#333,stroke-width:2px
   style C2 fill:#ccf,stroke:#333,stroke-width:2px
   classDef attr fill:#ffcc80,stroke:#333,stroke-width:2px
   classDef action fill:#aaffaa,stroke:#333,stroke-width:2px
  1. 类就是数据类型,比如 Cat
  2. 对象就是一个具体的实例,

7.1.7 快速入门-面向对象的方式 0193

以猫为例

class Cat{
    String name; // 名字
    int age; // 年龄
    String color; // 颜色
}

public static void main(String[] args){
    Cat littleCat = new Cat;
}

  1. 类是抽象的,概念的,代表一类事物, 比如人类, 猫类.., 即它是数据类型.
  2. 对象是具体的,实际的,代表一个具体事物, 即是实例.
  3. 类是对象的模板,对象是类的一个个体,对应一个实例

![[JVM内存中对象的存在形式.png]]

graph LR
   A[猫->0x011] --> B[0x011]
   B --> C[0x022<br>12]
   B --> D[0x033]
   C --> E[方法区常量池]
   D --> E
   E --> F[加载Cat信息包<br>1. 加载信息<br>2. 行为方法]

7.1.10 属性/成员变量/字段

基本介绍 1) 从概念或叫法上看: 成员变量 = 属性 = field (字段) (即==成员变量是用来表示属性==的,授课中,统一叫属性)。 2) 属性是类的一个组成部分,一般是基本数据类型, 也可是引用类型 (对象,数组)。比如我们前面定义猫类的 int age 就是属性。

注意事项和细节说明 1) 属性的定义语法同变量,示例: 访问修饰符属性类型属性名; 这里老师简单的介绍访问修饰符: 控制属性的访问范围 有四种访问修饰符 public, proctected, 默认, private ,后面我会详细介绍

2) 属性的定义类型可以为任意类型,包含==基本类型==或==引用类型==

3) 属性如果不赋值,有默认值,规则和数组一致。

4) 具体说: int 0, short 0, byte 0, long 0, float 0.0,double 0.0, char \u0000,olean false, String null ![[数据类型#基本数据类型默认值|数据类型]]


7.1.11 如何创建对象 0197

1) 先声明再创建 Cat cat ; //声明对象 cat cat = new Cat(); //创建

2) 直接创建 Cat cat = new Cat();

7.1.12 如何访问属性

基本语法 对象名.属性名; 案例演示赋值和输出cat.name ; cat.age; cat.color;

类和对象的内存分配机制 (重要) 我们定义一个人类 (Person)(包括名字,年龄)。(Object 03. java) 我们看看下面一段代码:

Person p1 = new PersonO:
p1.age = 10;
p1.name = "小明";
Person p2 = p1;//把p1赋给了p2,让p2指向p1
System.out.println(p2.age):
请问:p2. age 究竞是多少? 10


7.1.13 类和对象的内存分配机制 0199

Java 内存的结构分析

[[JVM]] 1) 栈: 一般存放基本数据类型 (局部变量) 2) 堆: 存放对象 (Cat cat , 数组等) 3) 方法区: 常量池 (常量,比如字符串),类加载信息 4) 示意图 [Cat (name, age, price)]

Java 创建对象的流程简单分析

Person p = new Person();
p.name = jack;
p.age = 10
1. 先加载 Person 类信息(属性和方法信息, 只会加载一次) -> 方法区
2. 在堆中分配空间, 进行默认初始化(看规则)
3. 把地址赋给 p , p 就指向对象

我们看看下面一段代码,会输出什么信息:

Person a=new PersonO;
a.age=10;
a.name="小明";
Person b;
b=a;
System.out.println(b.name);//小明
b.age=200;
b=null;
System.out.println(a.age); //200
System.out.println(b.age);//异常
![[对象内存布局.png]]


7.2 成员方法

7.2.2 成员方法快速入门 0201

1) 添加 speak 成员方法, 输出 “我是一个好人” 2) 添加 cal01 成员方法, 可以计算从 1+..+1000 的结果 3) 添加 cal02 成员方法, 该方法可以接收一个数 n,计算从 1+..+n 的结果 4) 添加 getSum 成员方法, 可以计算两个数的和 5)

class Person{  
    String name;  
    int age;  

    public void speak(){  
        System.out.println("我是一个好人");  
    }  

    public void cal01(){  
        int total = 0;  
        for (int i = 1; i <= 1000; i++){  
            total += i;  
        }  
        System.out.println( total);  
    }  

    public void cal02(int n){  
        int total = 0;  
        for (int i = 1; i <= n; i++){  
            total += i;  
        }  
        System.out.println( total);  
    }  

    public int getSum(int x,int y){  
        return x + y;  
    }  
}


7.2.3 方法的调用机制原理 0203

![[成员方法调用机制.png]]

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

7.2.5 成员方法的好处

1) 提高代码的复用性 2) 可以将实现的细节封装起来,然后供其他用户来调用即可


7.2.6 成员方法的定义

定义方式:

  `访问修饰符 返回数据类型 方法名( 形参列表..) {`
     `//方法体语句;`
     `return 返回值; `
 `}`
  • 形参列表: 表示成员方法输入 cal (int n) , getSum (int num 1, int num 2)
  • 返回数据类型: 表示成员方法输出, void 表示没有返回值
  • 方法主体: 表示为了实现某一功能代码块
  • return 语句不是必须的。

访问修饰符 (作用是控制方法使用的范围) - 如果不写默认访问,有四种: public, protected, 默认, private, 具体在后面说

返回数据类型 - 一个方法最多有一个返回值 (思考,如何返回多个结果:返回数组 ) - 返回类型可以为任意类型,包含基本类型或引用类型 (数组,对象) - 如果方法要求有返回数据类型,则方法体中最后的执行语句必须为 return 值; 而且要求返回值类型必须和 return 的值类型一致或兼容 - 如果方法是 void,则方法体中可以没有 return 语句,或者只写 return ;

方法名 - 遵循驼峰命名法,最好见名知义,表达出该功能的意思即可, - 比如得到两个数的和 getSum, - 开发中按照规范

形参列表 - 一个方法可以有 0 个参数,也可以有多个参数,中间用逗号隔开, - 比如 getSum (int n1, int n2) - 参数类型可以为任意类型,包含基本类型或引用类型, - 比如 printArr (int[][] map) - 调用带参数的方法时,一定对应着参数列表传入相同类型或兼容类型的参数 getSum 方法定义时的参数称为形式参数,简称形参; - 方法调用时的传入参数称为实际参数,简称实参, 实参和形参的类型要一致或兼容、个数、顺序必须一致!

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

方法调用细节说明 1. 同一个类中的方法调用:直接调用即可。比如 print(参数); 案例演示:A 类 sayOk 调用 print () 2. 跨类中的方法 A 类调用 B 类方法:需要通过对象名调用。 比如对象名,方法名(参数): 案例演示:B 类 sayHello 调用 print() 3. 特别说明一下:跨类的方法调用和方法的访问修饰符相关,先暂时这么提一下, 后面我们讲到访问修饰符时,还要再细说。

class A{
    public void ptintA(){
        System.out.print("A");
    }
    public void APrint(){
        printA();
    }
}
class B{
    public void PrintAByB(){
        A a = new A();
        a.printA();

    }
}


7.2.8 类定义的完善

flowchart LR
    A["class 类名{<br/>属性:<br/>}"] --> B["class 类名{<br/>属性(成员变量);<br/>成员方法;<br/>}"]
    B --> C["待定..."]

7.2.9 课堂练习题 #练习

1) 编写类 AA ,有一个方法: 判断一个数是奇数 odd 还是偶数, 返回 boolean 2) 根据行、列、字符打印对应行数和列数的字符, 比如: 行:4 列:4,字符#,则打印相应的效果

public class Main {  
    public static void main(String[] args) {  
        AA aa = new AA();  
        System.out.println(aa.judgeOdd(5));  
        aa.printChar(4,4,'#');  

    }  
}  

class AA {  
    public boolean judgeOdd(int a) {  
        return a % 2 == 0;  
    }  

    public void printChar(int row,int line,char character){  
        for (int i = 0; i < row; i++){  
            for (int j = 0; j < line; j++){  
                System.out.print(character);  
            }  
            System.out.println();  
        }  
    }  
}


7.3 成员方法传参机制 (非常非常重要)

7.3.1 基本数据类型的传参极致

  1. 看一个案例,分析结果是什么?

    public void swap (int a, int b){
        int tmp = a;
        a = b;
        b = tmp;
    }
    System.out.println ("a=" + a + "\tb=" + b);
    
    a = b b = a

  2. 结论及示意图

    • 基本数据类型,
    • 传递的是值(值拷贝),形参的任何改变不影响实参!

7.3.2 引用数据类型的传参机制

  • B 类中编写一个方法 test 100,可以接收一个数组,在方法中修改该数组,看看原来的数组是否变化? 会变化
  • B 类中编写一个方法 test 200,可以接收一个 Person (age, sal) 对象,在方法中修改该对象属性,看看原来的对象是否变
public class Main {  
    public static void main(String[] args) {  
        B b = new B();  
        int[] test = new int[]{1, 2, 3, 4, 5};  
        test = b.test100(test);  
        for (int x : test) {  
            System.out.println(x);  
        }  

        Person p = new Person(18);  
        p = b.test200(p);  
        System.out.println(p.age);  
    }  
}  

class Person{  
    public int age;  
    private String name;  
    public Person(int age) {  
        this.age = age;  
    }  

}  

class B {  
    public int[] test100(int[] arr) {  
        arr[0] = 99999999;  
        return arr;  
    }  

    public Person test200(Person p) {  
        p.age = 99999999;  
        return p;  
    }  
}

引用类型传递的是地址(传递也是值,但是值是地址) ,可以通过形参影响实参! [[数据类型|数据类型]]

class B {
    public void setNull(Person p){  
        p = null;  
    }
}

class Person{  
    public int age;  
    private String name;  
    public Person(int age) {  
        this.age = age;  
    }  

}

public class Main{
    B b = new B();
    Person p = new Person(18);  

    b.setNull(p);  
    System.out.println(p.age);
}
// p.age 还是18

[[参数传递]]

  • 是按照值传递的,传递的是对象的副本
  • 对象参数传递可以看作传递了一个“名称的副本”(引用的副本),这个副本指向堆内存中的同一个“值”(对象地址)。
  • 通过副本名称修改对象内容会影响原对象,但重新给副本名称分配新地址不会改变原始名称和值的对应关系

  • C++ 是直接传递地址。


7.3.3 成员方法返回类型是引用类型应用实例

1) 编写类 MyTools 类,编写一个方法可以打印二维数组的数据。 2) 编写一个方法 copyPerson,可以复制一个 Person 对象,返回复制的对象。克隆对象,注意要求得到新对象和原来的

对象是两个独立的对象,只是他们的属性相同

class MyTools {  
    public void printTwoDimArray(int[][] arr) {  
        for (int[] ints : arr) {  
            for (int anInt : ints) {  
                System.out.print(anInt + "\t");  
            }  
            System.out.println();  
        }  
    }  

    public Person clonePerson(Person p) {  
        return new Person(p.name, p.age);  
    }  
}  

class Person {  
    public String name;  
    public int age;  

    public Person(String name, int age) {  
        this.name = name;  
        this.age = age;  
    }  
}  

class Main {  
    public static void main(String[] args) {  
        Person p = new Person("张三", 18);  
        System.out.println("旧张三:" + p.name + "\t" + p.age + "\t" + System.identityHashCode(p));  

        MyTools mt = new MyTools();  
        Person newP = mt.clonePerson(p);  
        System.out.println("新张三:" + newP.name + "\t" + newP.age + "\t" + System.identityHashCode(newP));  
    }  
}

旧张三:张三 18 793589513 新张三:张三 18 214126413


7.4 方法递归调用(非常非常重要,比较难)

7.4.1 基本介绍

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


7.4.2 递归能解决什么问题?

  1. 数学问题:八皇后,汉诺塔,阶乘
  2. 算法:快排,归并排序,二分查找
  3. 用栈解决的问题:->递归代码简洁

7.4.3 递归举例

1) 打印问题

class recursion {  
    public void test(int n) {  
        if (n > 2) {  
            test(n - 1);  
        }  
        System.out.println( "n =" + n);  
    }  
}  

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

class recursion {  
    public void test(int n) {  
        if (n > 2) {  
            test(n - 1);  
        } else {  
            System.out.println("n =" + n);  
        }  
    }  
}
// 输出 n = 2 

2) 阶乘问题

class Factorial {  
    public int fact(int x) {  
        if (x == 1) {  
            return 1;  
        } else {  
            return x * fact(x - 1);  
        }  
    }  
}  

class Main {  
    public static void main(String[] args) {  
        Factorial f = new Factorial();  
        System.out.println(f.fact(5));  
    }  
}

方法调用时会开辟一个栈,这个栈包含这个方法的传入的参数等;


7.4.4 递归重要规则

方法调用时会开辟一个栈,这个栈包含这个方法的传入的参数等;

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

7.4.4 课堂练习 #练习

  • 请使用递归的方式求出斐波那契数 1, 1, 2, 3, 5, 8, 13.“给你一个整数 n,求出它的值是多
class Factorial {  
    public int fact(int x) {  
        if (x == 2 || x == 1) {  
            return 1;  
        } else {  
            return fact(x-1) + fact(x-2);  
        }  
    }  
}  

class Main {  
    public static void main(String[] args) {  
        Factorial f = new Factorial();  
        System.out.println(f.fact(7));  
    }  
}
  • 猴子吃桃子问题:有一堆桃子,猴子第一天吃了其中的一半,并再多吃了一个!以后每天猴子都吃其中的一半,然后再多吃一个。当到第 10 天时,想再吃时(即还没吃),发现只有 1 个桃子了。问题:最初共多少个桃子?
    class monkeyEarPeach {  
        public int eatPeach(int n) {  
            if (n == 10){  
                return 1;  
            } else if (n >= 1 && n <10) {  
                return (eatPeach(n+1) +1)*2;  
            }else{  
                return -1;  
            }  
        }  
    }  
    
    class Main {  
        public static void main(String[] args) {  
            monkeyEarPeach mep = new monkeyEarPeach();  
            System.out.println(mep.eatPeach(1));  
        }  
    }
    

7.4.6 递归调用应用实例-迷宫问题

  1. 小球得到的路径,和程序员设置的找路策略有关即:找路的上下左右的顺序相关
  2. 再得到小球路径时,可以先使用(下右上左), 再改成(上右下左), 看看路径是不是有变化
  3. 测试回溯现象
  4. 扩屐思考:如何求出最短路径?
public class Maze {  
    public static void main(String[] args) {  

        int[][] Map = {  
                {1, 1, 1, 1, 1, 1, 1, 1, 1, 1},  
                {1, 0, 0, 0, 0, 0, 0, 0, 0, 1},  
                {1, 0, 1, 1, 1, 1, 1, 1, 0, 1},  
                {1, 0, 0, 0, 0, 0, 0, 1, 0, 1},  
                {1, 0, 1, 0, 1, 1, 0, 0, 0, 1},  
                {1, 0, 1, 0, 1, 0, 0, 1, 0, 9}  
        };  

        Maze maze = new Maze();  
        maze.showMaze(Map);  

        System.out.println("--------------------------------");  
        maze.findWay(Map, 1, 1);  
        maze.showMaze(Map);  

    }  

    public void showMaze(int[][] mazeMap) {  
        for (int[] row : mazeMap) {  
            for (int data : row) {  
                System.out.print(data + " ");  
            }  
            System.out.println();  
        }  
    }  

    public boolean findWay(int[][] mazeMap, int i, int j) {  
        if (mazeMap[i][j] == 9) {  
            return true;  
        } else if (mazeMap[i][j] == 0) {  
            // 尝试找  
            // 0路 1障碍 2通路 3四路  
            if (mazeMap[i][j] == 0) {  
                // 假设可以过  
                mazeMap[i][j] = 2;  
                // 判断是否真得通路  
                if (findWay(mazeMap, i - 1, j)) { // 上  
                    return true;  
                } else if (findWay(mazeMap, i, j + 1)) { // 右  
                    return true;  
                } else if (findWay(mazeMap, i + 1, j)) { //  下  
                    return true;  
                } else if (findWay(mazeMap, i, j - 1)) {  
                    return true;  
                } else {  
                    mazeMap[i][j] = 3;  
                }  
            } else {  
                return false;  
            }  
        }  
        return false;  
    }  

}

7.4.7 递归调用应用实例-汉诺塔

汉诺塔: 汉诺塔(又称河内塔) 问题是源于印度一个古老传说的益智玩具。大梵天创造世界的时候做了三根金刚石柱子,在一根柱子上从下往上按照大小顺序摞着 64 片圆盘。大梵天命令婆罗门把圆盘从下面开始按大小顺序重新摆放在另一根柱子上。并且规定,在小圆盘上不能放大圆盘,在三根柱子之间一次只能移动一个圆盘。

假如每秒钟移动一次,共需多长时间呢? 移完这些金片需要 5845.54 亿年以上,太阳系的预期寿命据说也就是数百亿年。真的过了 5845.54 亿年,地球上的一切生命,连同梵塔、庙宇等,都早已经灰飞烟灭

public class HanoiTower {  
    public static void main(String[] args) {  
        HanoiTower hanoi = new HanoiTower();  
        hanoi.hanoi(3, 'A', 'B', 'C');  
    }  

    /*  
     * 汉诺塔  
     * @param num 盘子数量  
     * @param a 源柱  
     * @param b 临时柱  
     * @param c 目标柱  
     */    public void hanoi(int num, char a, char b, char c) {  
        if (num == 1) {  
            System.out.println("将盘子 1 从 " + a + " 移动到 " + c);  
        } else {  
            // 把除了最后一个的盘子从源柱移动到临时柱  
            hanoi(num - 1, a, c, b);  
            // 把最后一个盘子从源柱移动到目标柱  
            System.out.println("将盘子 " + num + " 从 " + a + " 移动到 " + c);  

            // 递归 hanoi(num - 1, b, a, c); 就是子问题,3层,子问题就是2层  
            // 把临时柱的盘子b从临时柱移动到目标柱  
            hanoi(num - 1, b, a, c);  
        }  
    }  

}

7.4.8 递归调用应用实例-八皇后问题

八皇后问题,是一个古老而著名的问题,是回溯算法的典型案例。该问题是国际西洋棋棋手马克斯· 贝瑟尔于 1848 年提出: - 在 8 × 8 格的国际象棋上摆放八个皇后,使其不能互相攻击, - 即: 任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。


7.5 方法重载 (OverLoad)

7.5.1 基本介绍

java 中允许同一个类中,多个同名方法的存在,但要求形参列表不一致! 比如: System.out.println(); outPrintStream 类型 println() 方法有形参重载


7.5.2 重载的好处

1) 减轻了起名的麻烦 2) 减轻了记名的麻烦


7.5.4 注意事项和使用细节

  1. 方法名:必须相同
  2. 形参列表:必须不同(形参类型或个数或顺序,至少有一样不同,参数名无要求)
  3. 返回类型:无要求

7.5.5 课堂练习题

  1. 判断题
  2. void show (int a, char b, double c){} 构成重载的有:b c d e

    • a) void show (int x, char y, double z){} //不是
    • b) int show (int a, double c, char b){} //是
    • c) void show (int a, double c, char b){} //是
    • d) boolean show (int c, char b){} //是
    • e) void show (double c){} //是
    • f) double show (int x, char y, double z){}//不是
    • g) void shows (){} //不是
  3. 编写程序,类 Methods 中定义三个重载方法并调用。方法名为 m。三个方法分 别接收一个 int 参数、两个 int 参数、一个字符串参数。分别执行平方运算输出 结果,相乘并输出结果,输出字符串信息。在主类的 main() 方法中分别用参数 区别调用三个方法。

class Methods {  
    public int m(int a) {  
        return a * a;  
    }  

    public int m(int a, int b) {  
        return a * b;  
    }  

    public char m(char c) {  
        return c;  
    }  
}  

public class Main {  
    public static void main(String[] args) {  
        Methods m = new Methods();  
        System.out.println(m.m(5));  
        System.out.println(m.m(5, 6));  
        System.out.println(m.m('a'));  
    }  
}
  1. Methods 类,定义三个重载方法 max(),第一个方法,返回两个 int 值中的最 大值,第二个方法,返回两个 double 值中的最大值,第三个方法,返回三个 double 值中的最大值,荇分别调用三个方法。
class Methods {  
    public int max(int a,int b){  
        return a > b ? a : b;  
    }  
    public double max(double a,double b){  
        return a > b ? a : b;  
    }  
    public double max(double a,double b,double c){  
        return a>b ? a : b > c ? b : c;  
    }  
}  

public class Main {  
    public static void main(String[] args) {  
        Methods m = new Methods();  
        System.out.println(m.max(1,2));  
        System.out.println(m.max(1.1,2.2));  
        System.out.println(m.max(1.1,2.2,3.3));  
    }  
}

7.6 可变参数

7.6.1 基本概念

- java 允许将同一个类中多个同名同功能但参数个数不同的方法, 封装成一个方法。就可以通过可变参数实现

7.6.2基本语法

  • 访问修饰符 返回类型 方法名(数据类型... 形参名) { }
  • eg : public int m(int... x)

  • int... 表示接受的是可变参数,类型是 int ,即可以接收多个 int(0-多)

  • 使用可变参数时,可以当做数组来使用即 nums 可以当做数组
  • 遍历 nums 求和即可

7.6.4 注意事项和使用细节

  • 可变参数的实爹可以为 0 个或任意多个
  • 可变参数的实参可以为数组
    public class Main {  
        public static void main(String[] args) {  
    
        //细节: 可变参数的实参可以为数组  
            int[] arr = {1, 2, 3};  
            T t1 = new T();  
            t1.f1(arr);  
        }  
    }  
    
    class T {  
        public void f1(int... nums) {  
            System.out.println("长度=" + nums.length);  
    
        }  
    }
    
  • 可变参数的本质就是数组
  • 可变参数可以和普通类型的参数一起放在形参列表,但必须保证可变参数在最后
  • 一个形参列表中只能出现一个可变参数

7.7 作用域

7.7.1 基本使用

  1. 在 java 编程中,主要的变量就是属性(成员变量)和局部变量·
  2. 我们说的局部变量一般是指在成员方法中定义的变量。
    • 举例Cat 类:cry
  3. java 中作用域的分类
    • 全局变量:也就是属性,作用域为整个类体
      • Cat 类:cryeat 等方法使用属性
    • 局部变量:也就是除了属性之外的其他变量,作用域为定义它的代码块中!
  4. 全局变量(属性)可以不赋值,直接使用,因为有默认值,局部变量必须值后,才能使用,因为没有默认值。 ![[作用域细节.png]]

7.7.2 注意事项和细节使用

  • 属性和局部变量可以重名,访问时遵循就近原则。
  • 在同一个作用域中,比如在同一个成员方法中,两个局部变量,不能重名。
  • 属性生命周期较长,伴随着对象的创建而创建,伴随着对象的销毁而销毁。局部变量生命周期较短,伴随着它的代码块的执行而创建,伴随着代码块的结束而毁:即在一次方法调用过程中。
  • 作用域范围不同
    • 全局变量/属性:可以被本类使用,或其他类使用(通过对象调用)
    • 局部变量:只能在本类中对应的方法中使用
  • 修饰符不同
    • 全局变量/属性可以加修饰符
    • 局部变量不可以加修饰符

7.8 构造方法 / 构造器

7.8.2 基本语法

[修饰符] 方法名(形参列表) {方法体;}

  1. 构造器的修饰符可以默认, 也可以是 public protected private
  2. 构造器没有返回值
  3. 方法名和类名字必须一样
  4. 参数列表和成员方法一样的规则
  5. 构造器的调用, 由系统完成

7.8.3 基本介绍

构造方法又叫构造器(constructor),是类的一种特殊的方法,它的主要作用是完成对新对象的初始化。它有几个特点: - 方法名和类名相同 - 没有返回值 -> 不能是 void - 在创建对象时,系统会自动的调用该类的构造器完成对象的初始化。


7.8.5 注意事项和使用细节

  • 一个类可以定义多个不同的构造器,即构造器重载
    • 比如:我们可以再给 Person 类定义一个构造器,用来创建对象的时候,只指定人名,不需要指定年龄
  • 构造器名和类名要相同
  • 构造器没有返回值
  • 构造器是完成对象的初始化,并不是创建对象
    • 创建完成后才进行构造
    • 构造用于赋值
  • 在创建对象时,系统自动的调用该类的构造方法
  • 如果程序员没有定义构造器,系统会自动给类生成一个默认无参构造器(也叫默认构造器), 比如 Dog (){}, 使用 [[#Javap]] 指令反编译看看
  • 一旦定义了自己的构造器,默认的构造器就覆盖了,就不能再使用默认的无参构造器,除非显式的定义一下
    • 即:Dog(){} 写(这点很重要)→ 显式定义无参构造器

7.9 对象创建的流程分析

[[对象创建流程]]

![[对象创建的流程分析.png]]

graph LR
    A["class 类名{\n成员变量;\n}"] --> B["class 类名{\n成员变量;\n成员方法;\n}"]
    B --> C["class 类名{\n成员变量;\n构造器;\n成员方法;\n}"]
    C --> D["待定.."]

7.10 this关键字

7.10.1 this 引入

class Dog {  
    int age;  
    String name;  

    public Dog(String name, int age) {//构造器//this.name 就是当前对象的属性 name this.name = name;    //this.age 就是当前对象的属性   
// age this.age = age;  

        System.out.println("this.hashCode=" + this.hashCode());  
    }  

    public void info() {//成员方法,输出属性 x 信息  
        System.out.println("this.hashCode=" + this.hashCode());  
        System.out.println(name + "\t" + age + "\t");  

    }  
}

简单的说,哪个对象调用,this 就代表哪个对象


7.10.3 this 的注意事项和使用细节

1) this 关键字可以用来访问本类的属性、 方法、 构造器 2) this 用于区分当前类的属性和局部变量 3) 访问成员方法的语法: this.方法名(参数列表); 4) 访问构造器语法: this(参数列表); 注意只能在构造器中使用(即只能在构造器中访问另外一个构造器 必须放在第一条语句 5) this 不能在类定义的外部使用,只能在类定义的方法中使用。


7.11 本章作业 #练习

![[Test.java]] ![[Homework14.java]] ![[Maze.java]] ![[HanoiTower.java]] ![[Java/Java_SE/韩顺平/Code/Chapter07/Homework01.java]] ![[Java/Java_SE/韩顺平/Code/Chapter07/Homework02.java]] ![[Java/Java_SE/韩顺平/Code/Chapter07/Homework03.java]] ![[Java/Java_SE/韩顺平/Code/Chapter07/Homework04.java]] ![[Java/Java_SE/韩顺平/Code/Chapter07/Homework05.java]] ![[Java/Java_SE/韩顺平/Code/Chapter07/Homework06.java]] ![[Java/Java_SE/韩顺平/Code/Chapter07/Homework07.java]] ![[Homework09.java]] ![[Homework10.java]] ![[Homework11.java]] ![[Homework12.java]] ![[Homework13.java]]


类的构成:

一个类通常由以下几个部分构成:

  1. 类名(Class Name): 类的标识符,用于区分不同的类。

  2. 属性(Attributes): 也称为成员变量或字段。它们定义了类的状态。例如,一个 Car 类可能有 color(颜色)、model(型号)、speed(速度)等属性。

  3. 方法(Methods): 也称为成员函数。它们定义了类的行为。例如,一个 Car 类可能有 accelerate()(加速)、brake()(刹车)、turn()(转向)等方法。

  4. 构造函数(Constructor): 一种特殊的方法,用于创建类的实例(对象)。它负责初始化对象的属性。通常构造函数的名字与类名相同。

  5. 析构函数(Destructor): (有些语言有)一种特殊的方法,在对象被销毁时自动调用,用于释放对象占用的资源。

总结:

  • 类是蓝图,属性是类的特征,方法是类的行为。
  • 属性存储数据,方法执行操作。
  • 一个类由类名、属性、方法、构造函数(和可能的析构函数)构成。

Javap

javap xx.class: 反编译 class 文件;

Javap的使用

  • javap 是 JDK 提供的一个命令行工具巧 Javap 能对给定的 class 文件提供的字节代码进行反编译“
  • 通过它,可以对照源代码和字节码,从而了解很多编译器内部的工作,对更深入地理解如何提高程序执行的效率等问题有极大的帮助。
  • 使用格式
    • javap <options> <classes>
    • 常用:javap -c/-v 类名
    • .class 可以省略
      • -c 反汇编
      • -v 输出附加信息