目录:

  1. 类与对象
  2. 成员方法
  3. 成员方法的传参机制
  4. 重载
  5. 可变参数
  6. 作用域
  7. 构造器
  8. this

    1.类与对象

    1.1 看一个养猫猫问题:

    张老太养了两只猫猫:一只名字叫小白,今年 3 岁,白色。还有一只叫小花,今年 100 岁,花色。请编写一个程序,当用户输入小猫的名字时,就显示该猫的名字,年龄,颜色。如果用户输入的小猫名错误,则显示 张老太没有这只猫猫。

    1.2 使用现有技术解决

    Object01.java

    1) 单独的定义变量解决

  • 单独变量来解决 => 不利于数据的管理(你把一只猫的信息拆解)

    2) 使用数组解决

  • (1) 数据类型体现不出来

  • (2) 只能通过[下标]获取信息,造成变量名字和内容的对应关系不明确
  • (3) 不能体现猫的行为

    3) 使用类的优势

  • 可以容纳多种数据类型

  • 引用时知道具体变量含义,而不是下标

    1.3 现有技术解决的缺点分析

  1. 不利于数据的管理
  2. 效率低===》 引出我们的新知识点 类与对象 哲学, 道家思想
  3. java 设计者 引入 类与对象(OOP) ,根本原因就是现有的技术,不能完美的解决新的新的需求.

    1.4 一个程序就是一个世界,有很多事物(对象[属性, 行为])

    image.png

    1.5 类与对象的关系示意图

    image.png

    1.6 类与对象的关系示意图

    image.png

    1.7 快速入门-面向对象的方式解决养猫问题

    Object01.java -> 看前面的代码

  4. 定义一个类

  5. 实例化这个类
  6. 使用 OOP 面向对象解决//实例化一只猫[创建一只猫对象]

    老韩解读:

  7. new Cat() 创建一只猫(猫对象)

  8. Cat cat1 = new Cat(); 把创建的猫赋给 cat1
  9. cat1 就是一个对象

    1.8 类和对象的区别和联系

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

    1.9 对象在内存中存在形式(重要的)必须搞清楚

    image.png
    数组 对象 字符串都是引用类型
    创建对象时:加载cat类的信息-属性和方法

    1.10 属性/成员变量/字段

    基本介绍

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

    注意事项和细节说明

    PropertiesDetail.java
    1) 属性的定义语法同变量,示例:访问修饰符 属性类型 属性名;
    这里老师简单的介绍访问修饰符: 控制属性的访问范围
    有四种访问修饰符 public/proctected/默认/private (后面我会详细介绍)
    2) 属性的定义类型可以为任意类型,包含基本类型或引用类型
    3) 属性如果不赋值,有默认值,规则和数组一致。
    具体说: int 0,short 0, byte 0, long 0, float 0.0,double 0.0,char \u0000,boolean false,String null
    案例演示:[Person 类]
    Person p1 = new Person()->
    new Person()创建对象的空间(数据),堆空间加载的类信息,包括常量池
    p1是对象引用/对象名

    1.11 如何创建对象

    1) 先声明再创建

    Cat cat ; //声明对象 cat
    cat = new Cat(); //创建
    有数据空间就一定有地址

    2) 直接创建

    Cat cat = new Cat();

    1.12 如何访问属性

    基本语法

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

    类和对象的内存分配机制(重要)

    看一个思考题
    我们定义一个人类(Person)(包括 名字,年龄)。 (Object03.java)
    image.png
    image.png

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

    Java 内存的结构分析

    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 就指向对象
4) 进行指定初始化, 比如 p.name =”jack” p.age = 10
先加载类且加载一次,后创建对象

看一个练习题

并分析画出内存布局图,进行分析
image.png

image.png
Person b = null 对象地址为空
此时b是一个Person类的对象名,没有对象
所谓空指针异常-本身为空,然后引出属性和方法就会空指针异常

空指针异常:

对象为null,对应属性和方法为空指针异常

2.成员方法

2.1 基本介绍

在某些情况下,我们要需要定义成员方法(简称方法)。比如人类:除了有一些属性外( 年龄,姓名..),我们人类还有一些行为比如:可以说话、跑步..,通过学习,还可以做算术题。这时就要用成员方法才能完成。现在要求对 Person 类完善。

2.2 成员方法快速入门

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

  1. class Person {
  2. String name;
  3. int age;
  4. //方法(成员方法)
  5. public void speak() {
  6. System.out.println("我是一个好人");
  7. }
  8. }

解读:

  1. public 表示方法是公开
  2. void : 表示方法没有返回值
  3. speak() : speak 是方法名, () 形参列表
  4. {} 方法体,可以写我们要执行的代码
  5. System.out.println(“我是一个好人”); 表示我们的方法就是输出一句话

    1. public void cal02(int n) {
    2. //循环完成
    3. int res = 0;
    4. for(int i = 1; i <= n; i++) {
    5. res += i;
    6. }
    7. System.out.println("cal02 方法 计算结果=" + res);
    8. }

    解读:

  6. (int n) 形参列表, 表示当前有一个形参 n, 可以接收用户输入

  7. 通过对象名.方法名(填写数据)调用,( )也可以为空,数据可以是①基本类型也可以是②引用类型
  8. 方法可以多次调用
  9. 形参可以传入数据

    1. public int getSum(int num1, int num2) {
    2. int res = num1 + num2;
    3. return res;
    4. }

    解读:

  10. public 表示方法是公开的

  11. int :表示方法执行后,返回一个 int 值
  12. getSum 方法名
  13. (int num1, int num2) 形参列表,2 个形参,可以接收用户传入的两个数(不同类型,不同值都可以)
  14. return res; 表示把 res 的值返回给调用者,① 方法调用者接收 (可以理解对象.方法存储数据的),② 也可以不接收

    1. int returnRes = p1.getSum(10, 20);
    2. System.out.println("getSum 方法返回的值=" + returnRes);

    解读

  15. 调用 getSum 方法,同时 num1=10, num2=20

  16. 把方法 getSum 返回的值,赋给变量 returnRe

    方法使用:

  17. 方法写好后,如果不去调用(使用),不会输出
    2. 先创建对象 ,然后调用方法即可

    2.3 方法的调用机制原理:(重要!-示意图!!!)

    提示:画出程序执行过程[getSum]+说明
    image.png
    谁调用返回给谁
    成员方法都有独立的栈空间

    小结:

  18. 执行方法,独立开辟栈空间

  19. 方法执行完毕,会返回调用者
  20. 返回后,继续执行后面代码
  21. mian方法结束,程序退出

    2.4 为什么需要成员方法

    Method02.java

    看一个需求:

    请遍历一个数组 , 输出数组的各个元素值。

    解决思路1 :

    传统的方法,就是使用单个 for 循环,将数组输出,大家看看问题是什么?

    解决思路 2:

    定义一个类 MyTools ,然后写一个成员方法,调用方法实现,看看效果又如何。
    优点:前者多次遍历数组,代码冗余,后者代码复用高。

    2.5成员方法的好处

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

    2.6成员方法的定义

    访问修饰符 返回数据类型 方法名(形参列表..) {
    方法体语句;
    return 返回值;
    }

    解读:

    1) 形参列表:表示成员方法输入 cal(int n) , getSum(int num1, int num2)
    2) 返回数据类型:表示成员方法输出, void 表示没有返回值
    3) 方法主体:表示为了实现某一功能代码块
    4) return 语句不是必须的。
    5) 老韩提示: 结合前面的题示意图, 来理解

2.7注意事项和使用细节

MethodDetail.java

访问修饰符 (作用是控制方法使用的范围):

  • 如果不写默认访问,[有四种: public/protected/默认/private], 具体在后面说

    返回数据类型:

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

    方法名:

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

    编程思想体现重点难点

    question: 一个方法最多返回一个返回值,思考,如果需要返回多个结果怎么办?
    answer: 创建一个数组接收
    1. public int[] getSumAndSub(int n1, int n2) {
    2. int[] resArr = new int[2]; //创建一个数组
    3. resArr[0] = n1 + n2;
    4. resArr[1] = n1 - n2;
    5. return resArr;
    6. }

    image.png总结:

  1. 实参和形参的类型、个数、顺序一致
  2. 方法内不能定义方法

image.png

总结:

  1. 同一个类中的方法调用:直接调用
  2. 跨类中的方法 A 类调用 B 类方法:需要通过对象名调用
  3. 方法a内调用方法b,方法b执行完毕才会继续执行方法a后面代码

    2.8类定义的完善

    image.png

    2.9 课堂练习题

    MethodExercise01.java
    1) 编写类 AA ,有一个方法:判断一个数是奇数 odd 还是偶数, 返回 boolean
    2) 根据行、列、字符打印 对应行数和列数的字符,比如:行:4,列:4,字符#,则打印相应的效果
    老师建议:一定要自己写一遍,不要嫌太简单.

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

方法的传参机制对我们今后的编程非常重要,一定要搞的清清楚楚明明白白。我们通过案例来学习

3.1基本数据类型的传参机制

image.png

总结:

调用方法,产生新的栈
基本数据类型是值传递,形参改变不影响实参
image.png

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

1) 看一个案例

MethodParameter02.java

  1. B 类中编写一个方法 test100,可以接收一个数组,在方法中修改该数组,看看原来的数组是否变化?
  • 会变化
  1. B 类中编写一个方法 test200,可以接收一个 Person(age,sal)对象,在方法中修改该对象属性,看看原来的对象是否变
  • 会变化

    2) 结论及示意图

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

    总结:

    引用类型是地址传递,形参改变影响实参
    image.png
    image.png

    3) 在看一个案例

    下面的方法会对原来的对象有影响吗?p=null 和 p = new Person(); 对应示意图
    image.png
    image.png

    总结:

  1. 每个方法独一份变量,变量名相同也不受影响
  2. 每次new,堆中创建新的对象

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

    MethodExercise02.java
    1) 编写类 MyTools 类,编写一个方法可以打印二维数组的数据。
    2) 编写一个方法 copyPerson,可以复制一个 Person 对象,返回复制的对象。克隆对象, 注意要求得到新对 象和原来的对象是两个独立的对象,只是他们的属性相同——>本质就是克隆一个对象
    image.png

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

    4.1 基本介绍

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

    4.2递归能解决什么问题?

    image.png4.3递归举例

    列举两个小案例,来帮助大家理解递归调用机制
    1) 打印问题
    2) 阶乘问题

    1) 打印问题

    1. t1.test(4);//输出什么? n=2 n=3 n=4
    2. public void test(int n) {
    3. if (n > 2) {
    4. test(n - 1);
    5. }
    6. System.out.println("n=" + n);
    7. }

    为了理解,假设的执行步骤

    1. t1.test(4);//输出什么? n=2 n=3 n=4
    2. public void test(int 4) {
    3. test(3);
    4. System.out.println("n=" + n);
    5. }
    6. public void test(int 3) {
    7. test(2);
    8. System.out.println("n=" + n);
    9. }
    10. public void test(int 2) {
    11. System.out.println("n=" + n);
    12. }

    总结:

  3. 调用者调用方法,方法内再次调用方法,直到方法内不再调用方法

  4. 被调用者先执行,调用者后执行
  5. 当被调用者方法B执行完毕之后,返回到调用者方法A继续执行

    重点:

  • 方法执行完毕,返回到调用者继续执行

调用方法,开辟新栈
栈是后进先出,符合:被调用者先执行,调用者后执行(因为被调用者后执行,先出栈先执行)
每一个栈完整的执行方法体,前面语句没有执行完毕,后面语句执行完毕之后,继续执行前面语句
image.png

2) 阶乘问题

  1. int res = t1.factorial(5);
  2. public int factorial(int n) {
  3. if (n == 1) {
  4. return 1;
  5. } else {
  6. return factorial(n - 1) * n;
  7. }
  8. }

为了理解,假设的执行步骤

  1. public int factorial(int 5) {
  2. return factorial(4) * 5;//1 * 2 * 3 * 4 * 5
  3. }
  4. public int factorial(int 4) {
  5. return factorial(3) * 4;// 1 * 2 * 3 * 4
  6. }
  7. public int factorial(int 3) {
  8. return factorial(2) * 3;/1 * 2 * 3
  9. }
  10. public int factorial(int 2) {
  11. return factorial(1) * 2;//1 * 2
  12. }
  13. public int factorial(int 1) {
  14. return 1;//1
  15. }

总结:

  1. 先执行调用的方法,就算是return也是
  2. return是返回给调用者
  3. 递归从顶端栈开始返回

    数学函数解答过程:

    f(n) = f(n - 1) n f(5) = f(4) 5 f(5) = f(3) 4 5 f(5) = f(2) 3 4 5 f(5) = f(1) 2 3 4 5 f(5) =1 2 3 4 * 5 = 120

image.png

4.4递归重要规则

image.png

总结:

  1. 局部变量独立的,互相不受影响
  2. 递归必须有 迭代条件 和 终止条件
  3. 方法执行完毕 或return 会返回调用者
  4. 谁调用就把结果返回给谁

    4.5课堂练习

    RecursionExercise01.java
    image.png ```java /* 请使用递归的方式求出斐波那契数 1,1,2,3,5,8,13…给你一个整数 n,求出它的值是多 思路分析
    1. 当 n = 1 斐波那契数 是 1
    2. 当 n = 2 斐波那契数 是 1
    3. 当 n >= 3 斐波那契数 是前两个数的和
    4. 这里就是一个递归的思路 */ int res = t1.fibonacci(n); if(res != -1) { System.out.println(“当 n=”+ n +” 对应的斐波那契数=” + res); }

方法一: public int fibonacci(int n) { if( n >= 1) { if( n == 1 || n == 2) { return 1; } else { return fibonacci(n-1) + fibonacci(n-2); } } else { System.out.println(“要求输入的 n>=1 的整数”); return -1; } }

//方式二: public int fibonacci(int n){ if(n < 1){ System.out.println(“请属于大于1的数”); return -1; } // if(n == 1 || n == 2){ if(n <= 2){ System.out.println(“**“); return 1; }else{ return fibonacci(n - 1) + fibonacci(n - 2); } }

  1. ```java
  2. /*
  3. 思路分析 逆推
  4. 1. day = 10 时 有 1 个桃子
  5. 2. day = 9 时 有 (day10 + 1) * 2 = 4
  6. 3. day = 8 时 有 (day9 + 1) * 2 = 10
  7. 4. 规律就是 前一天的桃子 = (后一天的桃子 + 1) *2 //就是我们的能力;
  8. 5. 递归
  9. */
  10. int peachNum = t1.peach(day);
  11. if(peachNum != -1) {
  12. System.out.println("第 " + day + "天有" + peachNum + "个桃子");
  13. }
  14. public int peach(int day) {
  15. if(day == 10) {//第 10 天,只有 1 个桃
  16. return 1;
  17. } else if ( day >= 1 && day <=9 ) {
  18. return (peach(day + 1) + 1) * 2;//规则,自己要想
  19. } else {
  20. System.out.println("day 在 1-10");
  21. return -1;
  22. }
  23. }

总结:

  • 调用者调用方法可以看成函数 f(n)
  • 弄清自变量x与y的关系

    4.6递归调用应用实例-迷宫问题(难点难点难点难点难点难点难点)

    MiGong.java
  1. 小球得到的路径,和程序员设置的找路策略有关,即:找路的上下顺序相关
  2. 再得到小球路径时,可以先使用(下右上左),再改为(上右下左),看路径是否有变化
  3. 测试回溯现象
  4. 拓展思考:如何求出最短路径?

image.png

  1. //使用递归回溯的思想来解决老鼠出迷宫
  2. //老韩解读
  3. //1. findWay 方法就是专门来找出迷宫的路径
  4. //2. 如果找到,就返回 true ,否则返回 false
  5. //3. map 就是二维数组,即表示迷宫
  6. //4. i,j 就是老鼠的位置,初始化的位置为(1,1)
  7. //5. 因为我们是递归的找路,所以我先规定 map 数组的各个值的含义
  8. // 0 表示可以走 1 表示障碍物 2 表示可以走 3 表示走过,但是走不通是死路
  9. //6. 当 map[6][5] =2 就说明找到通路,就可以结束,否则就继续找. //7. 先确定老鼠找路策略 下->右->上->左
  10. t1.findWay(map, 1, 1);
  11. public boolean findWay(int[][] map , int i, int j) {
  12. if(map[6][5] == 2) {//说明已经找到
  13. return true;//如果是终点,无需判断,也是微观方向判断
  14. } else {
  15. if(map[i][j] == 0) {//当前这个位置 0,说明表示可以走
  16. //我们假定可以走通
  17. map[i][j] = 2;
  18. //使用找路策略,来确定该位置是否真的可以走通,策略就是宏观方向判断
  19. //下->右->上->左 或上->右->下->左,2返回 3回溯
  20. if(findWay(map, i + 1, j)) {//先走下
  21. return true;
  22. } else if(findWay(map, i, j + 1)){//右
  23. return true;
  24. } else if(findWay(map, i-1, j)) {//上
  25. return true;
  26. } else if(findWay(map, i, j-1)){//左
  27. return true;
  28. } else {
  29. map[i][j] = 3;
  30. return false;
  31. }
  32. } else { //map[i][j] = 1 2,3
  33. return false;
  34. }
  35. }
  36. }
  37. //改进
  38. public boolean findWay(int[][] map,int i,int j){
  39. if(map[6][5] == 2){
  40. return true;
  41. }
  42. if(map[i][j] == 0){
  43. map[i][j] = 2;
  44. if(findWay(map,i + 1,j)){//0变2
  45. return true;
  46. }else if(findWay(map,i,j + 1)){
  47. return true;
  48. }else if(findWay(map,i - 1,j)){
  49. return true;
  50. }else if(findWay(map,i,j - 1)){
  51. return true;
  52. }else{//0变3
  53. map[i][j] = 3;
  54. return false;
  55. }
  56. }else{//map[i][j] == 1 2,3
  57. return false;
  58. }
  59. }
  1. //if有三种情况一般为false/继续循环/true.
  2. //一般为继续循环,一直盘问下去
  3. //一般当前位置为0,设置为2;如果当前位置不为0(1),判断条件为false,改为策略(右)
  4. if(map[1][1] == 0) {//当前这个位置 0,说明表示可以走
  5. map[1][1] = 2;
  6. if(findWay(map, 2, 1)) {//下,findWay(map, 2, 1)为0,继续循环,所以进去了
  7. return true;
  8. }
  9. }
  10. if(map[2][1] == 0) {
  11. map[2][1] = 2;
  12. if(findWay(map, 3, 1)) {//下 //findWay(map, 3, 1)为1,因此false
  13. return true;
  14. }else if(findWay(map, 2, 2)){//右 //findWay(map, 2, 2)为0,继续循环判断,所以进去了
  15. return true;
  16. }
  17. }
  18. if(map[2][2] == 0) {
  19. map[2][2] = 2;
  20. if(findWay(map, 3, 2)) {//下 //findWay(map, 3, 1)为1,因此false
  21. return true;
  22. }else if(findWay(map, 2, 3)){//右 //findWay(map, 2, 2)为0,继续循环判断,所以进去了
  23. return true;
  24. }
  25. }
  26. if(map[2][3] == 0) {
  27. map[2][3] = 2;
  28. if(findWay(map, 3, 3)) {//下,findWay(map, 3, 3)为0,继续循环,所以进去了
  29. return true;
  30. }
  31. }
  32. if(map[3][3] == 0) {
  33. map[3][3] = 2;
  34. if(findWay(map, 4, 3)) {//下,findWay(map, 4, 3)为0,继续循环,所以进去了
  35. return true;
  36. }
  37. }
  38. ...
  39. if(map[6][3] == 0) {
  40. map[6][3] = 2;
  41. if(findWay(map, 7, 3)) {//下 //findWay(map, 7, 3)为1,因此false
  42. return true;
  43. }else if(findWay(map, 6, 4)){//右 //findWay(map, 6, 4)为0,继续循环判断,所以进去了
  44. return true;
  45. }
  46. }
  47. if(map[6][4] == 0) {
  48. map[6][4] = 2;
  49. if(findWay(map, 7, 4)) {//下 //findWay(map, 7, 4)为1,因此false
  50. return true;
  51. }else if(findWay(map, 6, 5)){//右 //findWay(map, 6, 5)为0,继续循环判断,所以进去了
  52. return true;
  53. }
  54. }
  55. if(map[6][5] == 2) {//终点
  56. return true;
  57. }
  • 没走过的路要么为0,要么为1
  • 走过之后一般都为2,上下左右都走不通才为3(四面围堵)
  • 2就是走过的轨迹
  • 回溯现象:当 map[2][2] = 1;map[2][1] = 3;此时方法不通,回去重新走
    • 本质是老鼠测试向下走(走不通,换向右走)
    • 最开始f(1,1)有四个方向,f(2,1)为faslse,还有其余三个方向

      总结:

  1. 递归先判断终止条件,先宏观方向,再微观方向
  2. 如果正向不好判断,可以试试逆向
  3. 把大问题递归拆分为一个方法
  4. return返回结果,谁调用返回给谁

    if-else:

  5. 先if判断,再else if判断,最后才else判断

  6. else就是if或if else都不通过剩下的

    原则:

  • 什么时候退出
  • 事先指定规则
  • 有策略

image.png

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

汉诺塔传说

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

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

汉诺塔代码实现

看老师代码演示 HanoiTower.java
image.png

  1. tower.move(3, 'A', 'B', 'C');
  2. //num 表示要移动的个数, a, b, c 分别表示 A 塔,B 塔, C 塔
  3. public void move(int num , char a, char b ,char c) {
  4. //如果只有一个盘 num = 1
  5. if(num == 1) {
  6. System.out.println(a + "->" + c);
  7. } else {
  8. //如果有多个盘,可以看成两个, 最下面的和上面的所有盘(num-1)
  9. //因为中间的不输出,因此借助放中间(这一步暂时不需要)
  10. //(1)先移动上面所有的盘到 b, 借助 c
  11. move(num - 1 , a, c, b);
  12. //(2)把最下面的这个盘,移动到 c
  13. System.out.println(a + "->" + c);
  14. //(3)再把 b 塔的所有盘,移动到 c ,借助 a
  15. move(num - 1, b, a, c);
  16. }
  17. }
  1. move(2,a,b,c)
  2. move(1,a,c,b); a->b
  3. (a + "->" + c); a->c
  4. move(1,b,a,c); b->c

当一个盘子,只需A移到C
当2个盘子,A上面移到B,A下面移动C,B移动到C

4.8递归调用应用实例-八皇后问题[同学们先尝试做,后面老师评讲.]

八皇后问题说明

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

5.方法重载

5.1基本介绍

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

5.2重载的好处

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

5.3快速入门案例

OverLoad01.java
案例:类:MyCalculator 方法:calculate
1) calculate(int n1, int n2) //两个整数的和
2) calculate(int n1, double n2) //一个整数,一个 double 的和
3) calculate(double n2, int n1)//一个 double ,一个 Int 和
4) calculate(int n1, int n2,int n3)//三个 int 的和

5.4注意事项和使用细节

image.png

  1. 必须相同:方法名相同
  2. 必须不同:参数类型或顺序或个数
  3. 先类型匹配,类型不配自动类型提升

    没有构成重载:

  4. 方法重复定义:① 形参名无要求 ② 返回类型无要求(包括void)

  5. 两个方法:方法名不相同

    5.5课堂练习题

    image.png
    image.png

    6 可变参数

    6.1基本概念

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

    6.2基本语法

    访问修饰符 返回类型 方法名(数据类型… 形参名) {
    }

    6.3快速入门案例

    VarParameter01.java
    看一个案例 类 HspMethod,方法 sum 【可以计算 2 个数的和,3 个数的和 , 4. 5, 。。】 ```java //1. int… 表示接受的是可变参数,类型是 int ,即可以接收多个 int(0-多) //2. 使用可变参数时,可以当做数组来使用 即 nums 可以当做数组

//1.1 多个int类型的值: m.sum(1, 5, 100); //1.2 可变参数的实参可以为数组 int[] arr = {1, 2, 3}; m.sum(arr); public int sum(int… nums) { int res = 0; for(int i = 0; i < nums.length; i++) { res += nums[i]; } return res; }

  1. <a name="jA86k"></a>
  2. #### 总结:
  3. 1. int... 表示接受的是可变参数,类型是 int ,即可以接收多个 int(0-多)
  4. 1. 使用可变参数时,可以当做数组来使用即:nums 可以当做数组——本质:数组
  5. 1. 可变形参=一维数组
  6. 1. 谁调的方法返回结果就是调用者的方法的值
  7. <a name="vXn6n"></a>
  8. ### 6.4注意事项和使用细节
  9. ![image.png](https://cdn.nlark.com/yuque/0/2021/png/12555714/1620957621413-eecfe80f-6121-4441-bf13-761972b1b118.png#clientId=uee2c0d94-1cf2-4&from=paste&height=201&id=u9e19c1dc&name=image.png&originHeight=201&originWidth=797&originalType=binary&size=253508&status=done&style=none&taskId=ubbb17bcd-c0a4-4609-a69f-7dbb8a2fdca&width=797)
  10. <a name="QO60B"></a>
  11. #### 总结:
  12. 1. 可变参数的实参可以为数组
  13. 1. 可变参数的本质就是数组(形参和形参都是数组)
  14. 1. 可变参数和普通类型参数一起放在形参,但是可变形参必须放在last
  15. <a name="KzpYj"></a>
  16. ### 6.5课堂练习
  17. <a name="nXzfK"></a>
  18. #### ![image.png](https://cdn.nlark.com/yuque/0/2021/png/12555714/1620959385329-2117b7e9-6192-4054-aaee-f365672e3b1d.png#clientId=uee2c0d94-1cf2-4&from=paste&height=144&id=ue1467af4&margin=%5Bobject%20Object%5D&name=image.png&originHeight=144&originWidth=762&originalType=binary&size=183841&status=done&style=none&taskId=ue3b53801-2e28-4133-be4d-685592ac664&width=762)总结:
  19. 多个返回值:①返回输出用字符串,②返回计算用数组
  20. <a name="Ydcxc"></a>
  21. # 7. 作用域
  22. <a name="C49z6"></a>
  23. #### 7.1 基本使用![image.png](https://cdn.nlark.com/yuque/0/2021/png/12555714/1620961363456-f5a60358-a8c3-40a0-8a36-9bc9db583ec7.png#clientId=uee2c0d94-1cf2-4&from=paste&height=295&id=uef11b950&name=image.png&originHeight=295&originWidth=808&originalType=binary&size=443631&status=done&style=none&taskId=u08221de6-3321-430f-b787-451f21cf843&width=808)
  24. <a name="ldmwf"></a>
  25. #### 总结:
  26. - 变量分为:成员变量(属性or全局变量)和局部变量
  27. - 局部变量定义:属性之外的变量,有成员方法中定义的变量(作用域为方法中)、代码块中定义的变量(作用域为代码中)
  28. - 全局变量作用域:属性就是全局(作用域为整个类)
  29. 属性可以不赋值使用,有默认值,局部变量赋值才能使用
  30. <a name="RZVyz"></a>
  31. #### 7.2 注意事项和细节使用
  32. ![image.png](https://cdn.nlark.com/yuque/0/2021/png/12555714/1620961423428-4f4c6f01-a5e2-40d3-a146-246884b070d6.png#clientId=uee2c0d94-1cf2-4&from=paste&height=243&id=ua70d121a&name=image.png&originHeight=243&originWidth=1038&originalType=binary&size=470053&status=done&style=none&taskId=u58b13908-a4a1-4ac3-9c8c-ea584fea484&width=1038)<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/12555714/1620961443792-0fae36f2-901b-4733-921c-f7c9f0fdd60d.png#clientId=uee2c0d94-1cf2-4&from=paste&height=241&id=ubd381a72&name=image.png&originHeight=241&originWidth=863&originalType=binary&size=321803&status=done&style=none&taskId=ua889b1ee-e2df-4fb5-a5cd-2011cad29f8&width=863)
  33. <a name="J1lpd"></a>
  34. #### 总结:
  35. - 属性和局部变量可以重名,访问遵守就近原则
  36. - 调用方法会开独立栈空间,所以不同方法可以同名变量
  37. - 属性伴随对象创建而创建,伴随对象消亡而消亡——跟对象共生
  38. - 局部变量伴随代码块的执行而创建,伴随代码块的结束而消亡——跟代码块或方法共生
  39. - 属性可以加修饰符,局部变量不可以
  40. <a name="VjkkZ"></a>
  41. #### 全局变量在其他类中使用的2种方式:
  42. 1. 先创建对象,通过对象调用属性
  43. 1. 直接写方法,在形参中定义对象
  44. ```java
  45. public static void main(String[] args){
  46. //第一种
  47. T t = new T();
  48. t.test();
  49. //第二种
  50. Person p = new Person();
  51. t.test2(p);
  52. }
  53. class T{
  54. //第一种
  55. public void test(){
  56. Person p = new Person();
  57. sout(p.name);
  58. }
  59. //第二种
  60. public void test2(Person p){
  61. sout(p.name);
  62. }
  63. }
  64. class Person{
  65. String name = "张三";
  66. }

8.构造方法/构造器

8.1看一个需求

我们来看一个需求:前面我们在创建人类的对象时,是先把一个对象创建好后,再给他的年龄和姓名属性赋值,如果现在我要求,在创建人类的对象时,就直接指定这个对象的年龄和姓名,该怎么做? 这时就可以使用构造器。

8.2基本语法

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

老韩说明:

1) 构造器的修饰符可以默认, 也可以是 public protected private
2) 构造器没有返回值
3) 方法名和类名字必须一样
4) 参数列表和成员方法一样的规则
5) 类默认无参构造区(隐藏的)

8.3基本介绍

构造方法又叫构造器(constructor),是类的一种特殊的方法,它的主要作用是完成对新对象的初始化。
创建对象时,空间已经产生;属性的值的初始化通过构造器完成
构造器不是创建对象,而是初始化对象

它有几个特点:

1) 方法名和类名相同
2) 没有返回值
3) 在创建对象时,系统会自动的调用该类的构造器完成对象的初始化。

8.4 快速入门

现在我们就用构造方法来完成刚才提出的问题:在创建人类的对象时,就直接指定这个对象的年龄和姓名
Constructor01.java

总结:

  1. 构造器没有返回值, 也不能写 void
  2. 构造器的名称和类 Person 一样
  3. (String pName, int pAge) 是构造器形参列表,规则和成员方法一样
  4. 当我们 new 一个对象时,直接通过构造器指定名字和年龄

    8.5注意事项和使用细节

    image.png
    image.png

  5. 构造器重载(跟方法重载一样)

  6. 构造器跟方法最大区别是没有返回值,甚至没有void
  7. 构造器是对象初始化,不是创建对象
  8. 创建对象时,系统自动调用该类构造器,
  9. 如果没有定义构造器,系统自动给类生成默认无参构造器
  10. 一旦定义了构造器,默认构造器就覆盖了,不能使用默认构造器了,除非显式定义

    默认无参构造器:

    ① Dog(){}
    ② Dog dog = new Dog();

  11. ①和②的Dog()互相对应

  12. 虽然构造器可以自定义形参,但是实参(创建对象)跟形参不能对应会报错,所有规则跟成员方法一样
  13. 一旦定义了构造器,默认构造器就覆盖了,不能使用默认构造器了,除非显式定义
  14. 构造器形参不一定是属性

    8.6课堂练习题

    ConstructorExercise.java
    在前面定义的 Person 类中添加两个构造器:
    第一个无参构造器:利用构造器设置所有人的 age 属性初始值都为 18
    第二个带 pName 和 pAge 两个参数的构造器:使得每次创建 Person 对象的同时初始化对象的 age 属性值和 name 属性值。分别使用不同的构造器,创建对象

    9 对象创建的流程分析

    9.1看一个案例

    image.png

    总结:

    创建对象流程:先加载类信息,然分配空间,再对象初始化,最后地址值返回给p
    image.png

    总结:

  15. 先后:默认初始化—>显式初始化—>构造器初始化—>显式赋值

  16. 任何情况下都会有默认初始化
  17. 创建对象时,先加载类一次(仅此一次),然后创建对象
  18. 产生的堆空间才是对象,p是对象引用
  19. 构造器只赋值属性,不开辟空间

学习完构造器后,我们类的定义就应该更加完善了
image.png

10 this 关键字

10.1 先看一段代码,并分析问题

image.png

  • 如果我们构造器的形参,能够直接写成属性名,就更好了
  • 但是出现了一个问题,根据变量的作用域就近原则
  • 构造器的 name 是局部变量,而不是属性
  • 构造器的 age 是局部变量,而不是属性

    1. class Dog{
    2. String name;
    3. int age;
    4. public Dog(String name,int age){
    5. name = name;
    6. age = age;
    7. }
    8. }

    image.png

    总结:

    this就是当前对象
    当前对象是谁调用就是谁的(如:谁调用构造器,构造器中的this就是谁)
    image.png
    image.png

    this总结

    那个对象调用,this代表那个对象
    this本质就是对象调用
    this可以看成隐藏属性,他指向他自己的对象

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

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

    总结:

  • 构造器调用构造器,this(参数列表),只能在构造器使用,

  • this只能在本类中使用(包括属性、方法、构造器)
  • 变量访问遵守就近原则,如果没有局部变量,才是属性;this.变量=本类属性

    this构造器运用

    this(name,gender,age);

    10.4 this 的课堂案例

    TestPerson.java
    定义 Person 类,里面有 name、age 属性,并提供 compareTo 比较方法,用于判断是否和另一个人相等,提供测试类 TestPerson用于测试, 名字和年龄完全一样,就返回 true, 否则返回 false

    11 本章作业

    image.png

    总结:E

    return如果没有正确返回值,

  • 可以设置返回值类型为包装类,判断不为null输出

空指针异常,对象为null,调用属性和方法;如:null.length,

  • 判断数组不为空条件

    代码健壮性:

  1. 以后要考虑数组长度不能为0,数组不为null;
  2. 没有返回值:① 返回索引为-1;②基本类型转包装,返回null
  3. 引用不能与基本类型比较
  4. 否定条件两个用&&,极少数情况写在前面,少数写后面
  5. else如果没有可写的,可以省略
  6. 如果没有重名局部变量,调用属性可以省略this

    1. public Double max(double[] arr){...}
    2. if(res != null){
    3. //输出语句
    4. }
    5. //代码健壮性
    6. if(arr > 0){}
    7. //引用类型不能与基本比较,因此要先用,null不能拆箱,null不能跟-1比较,
    8. Integer res = a.find(arr,target);
    9. if(res != null && res != -1){

    image.pngimage.png
    image.png
    image.png
    image.png
    image.png

    总结:

    如果不想把比较结果在被调用者输出,可以return返回给调用者

    随机数

  7. (int)Math.random ——>[0,1)

  8. Random r = new Random( );
  • int num = r.nextInt(max); ——>[0,max) int类型

    拓展题:

    请输入你要出的拳(0-拳头,1-剪刀,2-布):
    0
    ===============================
    局数 玩家的出拳 电脑的出拳 输赢情况
    1 0 0 平手
    ===============================

===============================
局数 玩家的出拳 电脑的出拳 输赢情况
1 0 0 平手
2 1 1 平手
3 2 2 平手
===============================
你赢了0次