【Java】面向对象(基础) - 1

Java 面向对象(基础)

public class Object01{
        //编写一个mian方法
        public static void mian(String[] args){
                //两只猫 - 单独变量表示猫信息 => 不利于数据的>管理
                String cat1Name = "小白";
                String cat2Name = "小花";

                int cat1Age = 3;
                int cat2Age = 100;

                String cat1Color = "白色";
                String cat2Color = "花色";

                //数组表示 ==> 数据类型体现不出                
                //获取信息要下标,造成变量名字和内容的对应关系不明确
                //不能体现猫的行为
                String[] cat1 = {"小白","3","白色"};
                String[] cat2 = {"小花","100","白色"};
        }
}
  • 定义两只猫的对象信息

    • 单独的定义变量解决

    • 使用数组解决

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

    • 不利于数据的管理
    • 效率低下
  • java 设计者引入类与对象 ( OOP ),根本原因就是现有的技术,不能完美的解决新的新的需求。

1. 类与对象的概述

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

在这里插入图片描述

创建一个对象 实例化一个对象 把类实例化

java最大的特点就是面向对象

public class Object02{
        public static void main(String[] args){
                //使用OOP面向对象解决
                //实例化一只猫【创建一只猫对象】
                //把创建的猫赋值给 cat1
                //cat1 就是一个对象
                Cat cat1 = new Cat();
                cat1.name = "小白";
                cat1.age = 3;
                cat1.color = "白色";

                //创建第二只猫,赋给 cat2
                //cat2 就是一个对象(猫对象)
                Cat cat2 = new Cat();
                cat2.name = "小花";
                cat2.age = 100;
                cat2.color = "花色";

                //怎么访问对象的属性
                System.out.println("第一只猫信息" + cat1.name + " " + cat1.age + " " + cat1.color);
                System.out.println("第二只猫信息" + cat2.name + " " + cat2.age + " " + cat2.color);
        }
}

//定义一个猫类 Cat -> 自定义的数据类型
class Cat {
        //属性
        String name;
        int age;
        String color;
}

类与对象的区别和联系

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

对象内存布局

Cat cat1 = new Cat();
cat1.name = "小白";
cat1.age = 3;
cat1.color = "白色";

在这里插入图片描述

属性概念

属性/成员变量基本介绍

  • 从概念或叫法上看:成员变量 = 属性 = field字段(即 成员变量 是用来表示属性的,授课中,统一叫 属性)
class Car{
        String name;    //属性,成员变量,字段,filed
        double price;
        String color;
        String[] master;	//属性可以是基本数据类型,也可以是引用类型(对象,数组)
}
  • 属性是类的一个组成部分,一般是基本数据类型,也可是引用类型(对象,数组)。比如前面定义猫类的 int age 就是属性。

注意细节

  1. 属性的定义语法同变量,示例:访问修饰符 属性 类型 属性名;
  • 访问修饰符:控制属性的访问范围
  • 有四种访问修饰符 public,proctected,默认,private
  1. 属性的定义类型可以为任意类型,包含基本类型或引用类型。

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

2. 对象的创建

创建对象访问属性

如何创建对象

  1. 先声明再创建
Cat cat;
cat = new Cat();
  1. 直接创建
Cat cat = new Cat();

如何访问属性

对象名.属性名;

cat.name;
cat.age;
cat.color;

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

Java内存的结构分析

栈:一般存放基本数据类型(局部变量)

堆:存放对象(Cat cat,数组等)

方法区:常量池(常量,比如字符串),类加载信息

public class Object03{
        public static void main(String[] args){
                Person p1 = new Person();
                p1.age = 10;
                p1.name = "小明";
                Person p2 = p1; //把 p1 赋值给 p2,让 p2 指向 p1
                System.out.println(p2.age);
                //p2.age 是什么?
                //10
        }
}

class Person {
        String name;
        int age;
}

内存图

在这里插入图片描述

对象创建过程

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

对象机制练习

public class Object04{
        public static void main(String[] args){
                Person a = new Person();
                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);	//发生异常
        }
}

class Person {
        String name;
        int age;
}

在这里插入图片描述

在这里插入图片描述

3. 方法的调用

成员方法

在某些情况下,我们要需要定义成员方法(简称方法)。

比如人类:除了有一些属性外(年龄,姓名 … )。

我们人类还有一些行为比如:可以说话、跑步 … ,通过学习,还可以做算术题。

这时就要用成员方法才能完成。

public class Method01 {
        public static void main(String[] args) {
                //方法使用
                //方法写好,如果不调用就不会输出
                //先创建对象,然后调用方法即可
                Person p1 = new Person();
                p1.speak();
                p1.cal01();
                p1.cal02(5);    //1加到5
                p1.cal02(10);   //1加到10

                int returnRes = p1.getSum(10,20);       //调用getSum 方法,同时 num1 = 10,num2 = 20
                System.out.println("getSum方法返回的是:" + returnRes);
        }
}

class Person {
        String name;
        int age;

        //方法(成员方法)
        //添加 speak 成员方法
        //public 表示方法是公开的
        //void 表示方法没有返回值
        //speak() speak是方法名,()形参列表
        //{ } 方法体,可以写我们要执行的代码
        public void speak() {
                System.out.println("我是一个好人");
        }

        //添加cal01 成员方法,可以计算从 1 + ... + 1000 的结果
        public void cal01() {
                //循环
                int res = 0;
                for(int i = 1; i <= 1000; i++){
                        res += i;
                }
                System.out.println("计算结果 = " + res);
        }

        //添加cal02 成员方法,该方法可以接收一个数 n,计算从 1... + n 的结果
        //(int n) 形参列表,表示当前有一个形参 n,可以接受用户输入
        public void cal02(int n){
                int res = 0;
                for(int i = 1;i <= n;i++){
                        res += i;
                }
                System.out.println("计算结果 = " + res);
        }

        //添加getSum 成员方法,可以计算两个数的和
        //int 表示方法执行后,返回一个 int 值
        //形参列表,可以写多个,接受用户传入的数
        //return res; 表示把 res 的值,返回
        public int getSum(int num1,int num2){
                int res = num1 + num2;
                return res;
        }
}

方法调用机制

Person p1 = new Person();
int returnRes = p1.getSum(10,20);
System.out.println(returnRes);

public int getSum(int num1,int num2){
    int res = num1 + num2;
    return res;
}

在这里插入图片描述

方法调用小结

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

方法的使用

public class Method02 {
        public static void main(String[] args) {
                //遍历数组,输出数组的各个元素值
                int [][] map = { {0,0,1} , {1,1,1} , {1,1,3} };

                //传统方法:直接遍历数组
                for (int i = 0; i < map.length; i++) {
                        for (int j = 0; j < map[i].length; j++) {
                                System.out.print(map[i][j] + " ");
                        }
                        System.out.println();
                }

                //要求再次遍历 - 麻烦,代码重复
                //使用方法完成输出
                MyTools tool = new MyTools();
                tool.printArr(map);
        }
}

//把输出的功能,写到类方法
class MyTools {
        public void printArr(int[][] map) {
                //对传入的 map 数组进行遍历输出
                for (int i = 0; i < map.length; i++) {
                        for (int j = 0; j < map[i].length; j++) {
                                System.out.print(map[i][j] + " ");
                        }
                        System.out.println();
                }
        }
}

成员方法的好处

  • 提高代码的复用性

  • 可以将实现的细节封装起来,然后供其他用户来调用即可。

方法的定义

成员方法的定义

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

//1. 形参列表:表示成员方法输入 cal(int n), getSum(int num1, int num2)
//2. 返回数据类型:表示成员方法输出,void表示没有返回值
//3. 方法主体:表示为了实现某一功能代码块
//4. return语句不是必须的

方法使用注意点和使用细节

访问修饰符

作用是控制方法使用的范围

如果不写默认访问,[ 有四种:public、protected、默认、private ]

返回数据类型

  1. 一个方法最多有一个返回值 [ 思考,如何返回多个结果返回数组 ]
  2. 返回类型可以为任意类型,包含基本类型或引用类型(数组,对象)
  3. 如果方法要求有返回数据类型,则方法体中最后的执行语句必须为 return 值; 而且要求返回值类型必须和return的值类型一致或兼容
  4. 如果方法是 void,则方法体中可以没有 return 语句,或者只写 return;
  5. 方法名遵循驼峰命名法,最好见名知义,表达出该功能的意思即可,比如得到两个数的和getSum,开发中按照规范
public class Method03 {
        public static void main(String[] args) {
                AA a = new AA();        //错误: 不兼容的类型: int[]无法转换为int
                //int res = a.getSumAndSub(1,4);
                int[] res = a.getSumAndSub(1,4);
                System.out.println("和=" + res[0]);
                System.out.println("差=" + res[1]);
        }
}

class AA {
        //1.一个方法最多有一个返回值    [如果返回多个结果?>返回数组]
        //2.返回类型可以为任意类型,包含基本类型或引用类型(数组,对象)
        public int[] getSumAndSub(int n1, int n2) {
                int[] resArr = new int[2];      //创建一个数组
                resArr[0] = n1 + n2;
                resArr[1] = n1 - n2;
                return resArr;
        }

        //3.如果方法要求有返回数据类型,则方法体中最后的执行语句必须为 return 值; 
        //而且要求返回值类型必须和return的值类型一致或兼容
        public double f1() {
                double d1 = 1.1 * 3;
                int n = 100;
                return n;       //可以的,兼容性,int 转类型-> double 
        }
//      public int f2() {
//              double d1 = 1.1 * 3;
//              //错误: 不兼容的类型: 从double转换到int可能会有损失
//              return d1;      //不可以,double -> int
//      }

        //4.如果方法是 void,则方法体中可以没有 return 语句,或者只写 return;
        public void f3() {
                System.out.println("hello");
                int n = 10;
                return; //可以写
                //return n;     //不可以写,错误: 不兼容的类型: 意外的返回值
        }
}
  1. 一个方法可以有0个参数,也可以有多个参数,中间用逗号隔开。比如 getSum(int n1,int n2)

  2. 参数类型可以为任意类型,包含基本类型或引用类型,比如 printArr(int map)

  3. 调用带参数的方法时,一定对应着参数列表传入相同类型或兼容类型的参数 【getSum】

  4. 方法定义时的参数称为形式参数,简称形参;方法调用时的传入参数称为实际参数,简称实参,实参和形参的类型要一致或兼容、个数、顺序必须一致

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

public class Method03 {
        public static void main(String[] args) {
                AA a = new AA();        //错误: 不兼容的类型: int[]无法转换为int
                //int res = a.getSumAndSub(1,4);
                int[] res = a.getSumAndSub(1,4);
                System.out.println("和=" + res[0]);
                System.out.println("差=" + res[1]);

                //细节:调用带参数的方法时,一定对应着参数列表传入相同类型或兼容类型的参数              
                byte b1 = 1;
                byte b2 = 2;
                a.getSumAndSub(b1,b2);  //可以,低精度转高精度,byte -> int
                //a.getSumAndSub(1.1,1.8);        //不可以,从double转换到int可能会有损失
                //实参和形参的类型要一直或兼容、个数、顺序必须一致
                //a.getSumAndSub(100);    //个数不一致,原因: 实际参数列表和形式参数列表长度不同           
                a.f4("tom",10);         //可以
                //a.f4(10,"jack");      //不可以,实际参数和形式参数顺序不对,原因: 实际参数列表和形式参数列表长度不同
        }
}

class AA {
        public int[] getSumAndSub(int n1, int n2) {
                int[] resArr = new int[2];      //创建一个数组
                resArr[0] = n1 + n2;
                resArr[1] = n1 - n2;
                return resArr;
        }
        public void f4(String str,int n) {

        }
}

方法调用细节说明

  1. 同一个类中的方法调用:直接调用即可。比如print(参数)

  2. 跨类中的方法A类调用B类方法:需要通过对象名调用。比如对象名.方法名(参数);

  3. 跨类的方法调用和方法的访问修饰符相关

public class Method04 {
        public static void main(String[] args) {
                A a = new A();
                a.sayOk();
                a.m1();
        }
}

class A {
        //同一个类,直接调用
        public void print(int n) {
                System.out.println("print()方法被调用 n=" + n);
        }

        public void sayOk() {   //sayOk调用 print(直接调用)
                print(10);
                System.out.println("继续执行sayOk");
        }

        //跨类中的方法A类调用B类方法:需要通过对象名调用。比如对象名.方法名(参数);
        public void m1() {
                System.out.println("m1()方法被调用");
                //先创建对象
                B b = new B();
                //再调用方法
                b.hi();
                System.out.println("m1()继续执行");
        }
}

class B {
        public void hi() {
                System.out.println("B类中的hi()被执行");
        }
}

练习

1.

public class Method05 {
        public static void main(String[] args) {
                AA a = new AA();
                if(a.isOdd(1)) {
                        System.out.println("是奇数");
                } else {
                        System.out.println("是偶数");
                }
        }
}

//判断一个数是技术还是偶数,返回Boolean
class AA {
        //思路
        //方法的返回类型boolean
        //方法的名字isodd
        //方法的形参(int num)
        //方法体,判断
        public boolean isOdd(int num) {
//              if(num % 2 != 0) {
//                      return true;
//              } else {
//                      return false;
//              }

                //简化
                //return num % 2 != 0 ? true : false;

                //简化
                return num % 2 != 0;
        }
}

2.

public class Method06 {
        public static void main(String[] args) {
                AA a = new AA();
                a.print(5,5,'0');
        }
}

class AA {
        //根据行、列、字符打印对应行数和列数的字符,
        //比如:行:4,列:4,字符#,则打印相应的效果
        /*
        *       ####
        *       ####
        *       ####
        *       ####
        *
        */
        //方法的返回类型 void
        //方法的名字 print
        //方法的形参(int row, int col, char c)
        //方法体,判断
        public void print(int row, int col, char c) {
                for(int i = 0; i < row; i++) {
                        for(int j = 0; j < col; j++) {
                                System.out.print(c);
                        }
                        System.out.println();   //换行
                }
        }
}

4. 方法传参机制

基本数据类型的传参机制

public class Method07 {
        public static void main(String[] args) {
                int a = 10;
                int b = 20;
                //创建AA对象 obj
                AA obj = new AA();
                obj.swap(a,b);  //调用swap方法
                System.out.println();
                System.out.println("main方法 a=" + a + " b=" + b);          
        }
}

class AA {
        public void swap(int a,int b) {
                System.out.println("\na和b交换前的值\na=" +  a + "\tb=" +b);
                //完成 a 和 b 的交换
                int tmp = a;
                a = b;
                b = tmp;
                System.out.println("\na和b交换后的值\na=" + a + "\tb=" + b);
        }
}

内存流程图

在这里插入图片描述

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

引用数据类型的传参机制

1

  • B类中编写一个方法test100,可以接收一个数组,在方法中修改该数组,看看原来的数组是否变化?

  • 会变化

public class Method08 {
        public static void main(String[] args) {
                //测试
                B b = new B();
                int[] arr = {1,2,3};
                b.test100(arr); //调用方法
                System.out.println("main 的 arr 数组");
                //遍历数组
                for(int i = 0; i < arr.length; i++) {
                        System.out.println(arr[i] + "\t");
                }
                System.out.println();
        }
}

class B {
        //B类可以接收一个数组,在方法中修改该数组,看看原来的数组是否变化
        public void test100(int[] arr) {
                arr[0] = 200;   //修改元素
                //遍历数组
                System.out.println("test100 的 arr 数组");
                for(int i = 0; i< arr.length; i++){
                        System.out.println(arr[i] + "\t");
                }
        }
}

内存流程图

在这里插入图片描述

2

  • B类中编写一个方法test200,可以接收一个Person(age,sal)对象,在方法中修改该对象属性,看看原来的对象是否变化?
  • 会变化.
public class Method09 {
        public static void main(String[] args) {
                //测试
                B b = new B();

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

                b.test200(p);
                System.out.println("main 的 p.age = " + p.age); //      1000
        }
}

class Person {
        String name;
        int age;
}

class B {
        public void test200(Person p) {
                p.age = 1000;   //修改对象属性
        }
}

内存流程图

在这里插入图片描述

结论

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

练习1

public class Method10 {
        public static void main(String[] args) {
                //测试
                B b = new B();

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

                b.test200(p);
                //如果test200 执行的是 p = null,测试的结果是?                
            	System.out.println("main 的 p.age = " + p.age); //      10
                //测试的结果是:10
                //p=null,意味着test的p不再指向0x11,指向空,test的age=0,name=null
                //最后main输出p=10,是因为main的p指向0x11的地址不与test的冲}
}

class Person {
        String name;
        int age;
}

class B {
        public void test200(Person p) {
                //p.age = 1000; //修改对象属性
                //思考 - 执行以下语句会怎么样?
                p  = null;
        }
}

内存流程图

在这里插入图片描述

练习2

public class Method11 {
        public static void main(String[] args) {
                //测试
                B b = new B();

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

                b.test200(p);
                //test200 执行的是 p = new Person(); 结果是?                
            	System.out.println("main 的 p.age = " + p.age); //10
                //结果是:10
                //因为 test200 创建新的对象,就是指向新的对象
                //更改对象不会对 main 的对象冲突,就不会发生变化
                //所以还是打印 main 指向的对象 p 的 age = 10
        }
}

class Person {
        String name;
        int age;
}

class B {
        public void test200(Person p) {
                //p.age = 1000; //修改对象属性
                p = new Person();
                p.name = "tom";
                p.age = 99;
        }
}

内存流程图

在这里插入图片描述

注意:test 的 新对象在test方法结束后会被当作垃圾回收

练习3:克隆对象

编写一个方法copyPerson,可以复制一个Person对象,返回复制的对象。

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

public class Method12 {
        public static void main(String[] args) {
                Person p = new Person();
                p.name = "milan";
                p.age = 100;
                //创建tools
                MyTools tools  = new MyTools();
                Person p2 = tools.copyPerson(p);

                //到此P和P2是Person对象,但是是两个独立的对象,属性相同
                System.out.println("p的属性 age = " + p.age + " 名字 = " + p.name);
                System.out.println("p2的属性 age = " + p2.age + " 名字 = " + p2.name);
                //可以同 对象比较  看看对象是否是同一个对象
                System.out.println(p == p2);    //false
                System.out.println(p == p);     //true
        }
}

class Person {
        String name;
        int age;
}

class MyTools {
        //编写一个方法copyPerson,可以复制一个Person对象,返回复制的对象。克隆对象,
        //注意要求得到新对象和原来的对象是两个独立的对象,只是他们的属性相同
        //
        //1.方法的返回类型 Person
        //2.方法的名字 copyPerson
        //3.方法的形参 (Person P)
        //4.方法体,创建一个新对象,并复制属性,返回即可

        public Person copyPerson(Person p) {
                //创建一个新的对象
                Person p2 = new Person();
                p2.name = p.name;       //把原来对象的名字赋给p2.name
                p2.age = p.age;         //把原来对象的年龄赋给p2.age

                return p2;
        }
}

内存流程图

在这里插入图片描述

5. 递归

递归能解决什么问题?

  1. 各种数学问题如:8皇后问题,汉诺塔,阶乘问题,迷宫问题,球和篮子的问题(Google编程大赛)
  2. 各种算法中也会使用到递归,比如快排,归并排序,二分查找,分治算法等
  3. 将用栈解决的问题 --> 递归代码比较简洁

递归的机制

1

public class Recursion01 {
        public static void main(String[] args) {
                T t1 = new T();
                t1.test(4);
        }
}

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

在这里插入图片描述

2

public class Recursion02 {
        public static void main(String[] args) {
                T t1 = new T();
                t1.test(4);
        }
}

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

在这里插入图片描述

3

public class Recursion03 {
        public static void main(String[] args) {
                T t1 = new T();
                int res = t1.factorial(5);
                System.out.println("res=" + res);
        }
}

class T {
        //阶乘
        public int factorial(int n) {
                if (n == 1) {
                        return 1;
                } else {
                        return factorial(n + 1) * n;
                }
        }
}

在这里插入图片描述

递归重要规则

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

练习

求出斐波那契数

  • 请使用递归的方式求出斐波那契数1,1,2,3,5,8,13 … 给你一个整数n,求出它的值是多少?

思路分析

  1. 当 n = 1 斐波那契数是 1
  2. 当 n = 2 斐波那契数是1
  3. 当 n >= 3 斐波那契数是前两个数的和
  4. 这就是一个递归的思路
public class Recursion04 {
        public static void main(String[] args) {
                T t1 = new T();
                System.out.println("当 n = 7 对应的斐波那契数 = " + t1.fibonacci(7));
        }
}

class T {
        /** 用递归的方式求出斐波那契数  */
        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;
                }
        }
}

猴子吃桃子问题:

有一堆桃子。猴子第一天吃了其中的一半,并再多吃了一个!以后每天猴子都吃其中的一半,

然后再多吃一个。当到第10天时,想再吃时(即还没吃),发现只有1个桃子了。

问题:最初共多少个桃子?

public class Recursion05 {
        public static void main(String[] args) {
                T t1 = new T();
                int day = 1;
                int peachNum = t1.peach(day);
                if (peachNum != -1) {
                        System.out.println("第1天有"+  peachNum + "个桃子");
                }
        }
}

class T {
        //猴子吃桃问题
        //规律就是 前一天的桃子 = (后一天的桃子 + 1) * 2
        public int peach(int day) {
                if (day == 10) {        //第十天只有一个套
                        return 1;
                } else if (day >= 1 && day <= 9) {
                        return (peach(day + 1) + 1) * 2;
                } else {
                        System.out.println("day在 1 - 10");
                        return -1;
                }
        }
}

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

  1. 小球得到的路径,和程序员设置的找路策略有关即:找路的上下左右的顺序相关
  2. 再得到小球路径时,可以先使用( 下 右 上 左),再改成( 上 右 下 左 ),看看路径是不是有变化
  3. 测试回溯现象

画出地图

public class MiGong {
        public static void main(String[] args) {
                //老鼠出迷宫
                //思路
                //1.先创建迷宫,用二维数组表示int[][] map = new int[8][7];
                //2.先规定map数组的元素值:0 表示可以走;1 表示障碍物
                int[][] map = new int[8][7];
                //3.将最上面的一行和最下面的一行,全部设置为1
                for (int i = 0; i < 7; i++) {
                        map[0][i] = 1;
                        map[7][i] = 1;
                }
                //4.将最右面的的一列和最左面的一列,全部设置为1
                for (int i = 0; i< 8; i++) {
                        map[i][0] = 1;
                        map[i][6] = 1;
                }

                //输出当前的地图
                System.out.println("=====当前地图情况=====");
                for (int i = 0; i< map.length; i++) {
                        for (int j = 0; j < map[i].length; j++) {
                                System.out.print(map[i][j] + " ");      //输>出一行
                        }
                        System.out.println();
                }
        }
}

完整代码

public class MiGong {
	public static void main(String[] args) {
		//老鼠出迷宫
		//思路
		//1.先创建迷宫,用二维数组表示int[][] map = new int[8][7];
		//2.先规定map数组的元素值:0 表示可以走;1 表示障碍物
		int[][] map = new int[8][7];
		//3.将最上面的一行和最下面的一行,全部设置为1
		for (int i = 0; i < 7; i++) {
			map[0][i] = 1;
			map[7][i] = 1;
		}
		//4.将最右面的的一列和最左面的一列,全部设置为1
		for (int i = 0; i< 8; i++) {
			map[i][0] = 1;
			map[i][6] = 1;
		}

		map[3][1] = 1;
		map[3][2] = 1;

		//输出当前的地图
		System.out.println("=====当前地图情况=====");
		for (int i = 0; i< map.length; i++) {
			for (int j = 0; j < map[i].length; j++) {
				System.out.print(map[i][j] + " ");	//输出一行
			}
			System.out.println();
		}
			
		//使用findWay 给老鼠找路
		T t1 = new T();
		t1.findWay(map, 1, 1);

		System.out.println("\n=====找路的情况如下=====");
		for (int i = 0; i< map.length; i++) {
                	for (int j = 0; j < map[i].length; j++) {
                                  System.out.print(map[i][j] + " ");	//输出一行
                	}
			System.out.println();
		}
	}
}

class T {
	//使用递归回溯的思想来解决老鼠出迷宫
	
	//1. findway方法就是专内来找出迷宫的路径
	//2.如果找到,就返回 true,否则返回false
	//3. map就是二维数组,即表示迷宫
	//4. i,j就是老鼠的位置,初始化的位置为(1,1)
	//5. 因为我们是递归的找路,所以先规定 map数组的各个值的含义
	//	0表示可以走	1表示障碍物	2表示可以走	3表示走过,但是走不通时死路
	//6. 当 map[6][5] = 2 就说明找到通路,就可以结束,否则就继续找
	//7. 先确定老鼠找路策略 下->右->上->左
	public boolean findWay(int[][] map, int i, int j) {
		if (map[6][5] == 2) {	//说明已经找到
			return true;
		} else {
			if (map[i][j] == 0) {	//当前这个位置0,说明表示可以走
				//假定可以走通
				map[i][j] = 2;

				//使用找路策略,来确定该位置是否真的可以走通
				//下->右->上->左
				if (findWay(map, i + 1, j)) {	//先走下
					return true;	
				} else if (findWay(map, i, j + 1)) {	//向走右
					return true;
				} else if (findWay(map, i - 1, j)) {	//向上走
					return true;
				} else if (findWay(map, i, j -1)) {
					return true;
				} else {
					map[i][j] = 3;
					return false;
				}
								
			} else {
				return false;
			}
		}				
	}
}

在这里插入图片描述

修改找路策略

public class MiGong1 {
	public static void main(String[] args) {
		//老鼠出迷宫
		//思路
		//1.先创建迷宫,用二维数组表示int[][] map = new int[8][7];
		//2.先规定map数组的元素值:0 表示可以走;1 表示障碍物
		int[][] map = new int[8][7];
		//3.将最上面的一行和最下面的一行,全部设置为1
		for (int i = 0; i < 7; i++) {
			map[0][i] = 1;
			map[7][i] = 1;
		}
		//4.将最右面的的一列和最左面的一列,全部设置为1
		for (int i = 0; i< 8; i++) {
			map[i][0] = 1;
			map[i][6] = 1;
		}

		map[3][1] = 1;
		map[3][2] = 1;

		//输出当前的地图
		System.out.println("=====当前地图情况=====");
		for (int i = 0; i< map.length; i++) {
			for (int j = 0; j < map[i].length; j++) {
				System.out.print(map[i][j] + " ");	//输出一行
			}
			System.out.println();
		}
			
		//使用findWay 给老鼠找路
		T t1 = new T();
		t1.findWay(map, 1, 1);

		System.out.println("\n=====找路的情况如下=====");
		for (int i = 0; i< map.length; i++) {
                	for (int j = 0; j < map[i].length; j++) {
                                  System.out.print(map[i][j] + " ");	//输出一行
                	}
			System.out.println();
		}
	}
}

class T {
	//使用递归回溯的思想来解决老鼠出迷宫
	
	//1. findway方法就是专内来找出迷宫的路径
	//2.如果找到,就返回 true,否则返回false
	//3. map就是二维数组,即表示迷宫
	//4. i,j就是老鼠的位置,初始化的位置为(1,1)
	//5. 因为我们是递归的找路,所以先规定 map数组的各个值的含义
	//	0表示可以走	1表示障碍物	2表示可以走	3表示走过,但是走不通时死路
	//6. 当 map[6][5] = 2 就说明找到通路,就可以结束,否则就继续找
	//7. 先确定老鼠找路策略 
	//8.修改找路策略,看看路径是否有变化
	//	下->右->上->左	=>	上->右->下->左
	public boolean findWay(int[][] map, int i, int j) {
		if (map[6][5] == 2) {	//说明已经找到
			return true;
		} else {
			if (map[i][j] == 0) {	//当前这个位置0,说明表示可以走
				//假定可以走通
				map[i][j] = 2;

				//使用找路策略,来确定该位置是否真的可以走通
				//上->右->下->左
				if (findWay(map, i - 1, j)) {	//先走上
					return true;	
				} else if (findWay(map, i, j + 1)) {	//向走右
					return true;
				} else if (findWay(map, i + 1, j)) {	//向下走
					return true;
				} else if (findWay(map, i, j -1)) {
					return true;
				} else {
					map[i][j] = 3;
					return false;
				}
								
			} else {
				return false;
			}
		}				
	}
}

在这里插入图片描述

测试回溯现象

public class MiGong2 {
	public static void main(String[] args) {
		//老鼠出迷宫
		//思路
		//1.先创建迷宫,用二维数组表示int[][] map = new int[8][7];
		//2.先规定map数组的元素值:0 表示可以走;1 表示障碍物
		int[][] map = new int[8][7];
		//3.将最上面的一行和最下面的一行,全部设置为1
		for (int i = 0; i < 7; i++) {
			map[0][i] = 1;
			map[7][i] = 1;
		}
		//4.将最右面的的一列和最左面的一列,全部设置为1
		for (int i = 0; i< 8; i++) {
			map[i][0] = 1;
			map[i][6] = 1;
		}

		map[3][1] = 1;
		map[3][2] = 1;
		//更改点
		map[2][2] = 1;	//测试回溯现象

		//输出当前的地图
		System.out.println("=====当前地图情况=====");
		for (int i = 0; i< map.length; i++) {
			for (int j = 0; j < map[i].length; j++) {
				System.out.print(map[i][j] + " ");	//输出一行
			}
			System.out.println();
		}
			
		//使用findWay 给老鼠找路
		T t1 = new T();
		t1.findWay(map, 1, 1);

		System.out.println("\n=====找路的情况如下=====");
		for (int i = 0; i< map.length; i++) {
                	for (int j = 0; j < map[i].length; j++) {
                                  System.out.print(map[i][j] + " ");	//输出一行
                	}
			System.out.println();
		}
	}
}

class T {
	//使用递归回溯的思想来解决老鼠出迷宫
	
	//1. findway方法就是专内来找出迷宫的路径
	//2.如果找到,就返回 true,否则返回false
	//3. map就是二维数组,即表示迷宫
	//4. i,j就是老鼠的位置,初始化的位置为(1,1)
	//5. 因为我们是递归的找路,所以先规定 map数组的各个值的含义
	//	0表示可以走	1表示障碍物	2表示可以走	3表示走过,但是走不通时死路
	//6. 当 map[6][5] = 2 就说明找到通路,就可以结束,否则就继续找
	//7. 先确定老鼠找路策略 下->右->上->左
	public boolean findWay(int[][] map, int i, int j) {
		if (map[6][5] == 2) {	//说明已经找到
			return true;
		} else {
			if (map[i][j] == 0) {	//当前这个位置0,说明表示可以走
				//假定可以走通
				map[i][j] = 2;

				//使用招录策略,来确定该位置是否真的可以走通
				//下->右->上->左
				if (findWay(map, i + 1, j)) {	//先走下
					return true;	
				} else if (findWay(map, i, j + 1)) {	//向走右
					return true;
				} else if (findWay(map, i - 1, j)) {	//向上走
					return true;
				} else if (findWay(map, i, j -1)) {
					return true;
				} else {
					map[i][j] = 3;
					return false;
				}
								
			} else {
				return false;
			}
		}				
	}
}

在这里插入图片描述

递归调用实例 - 汉诺塔

public class HanoiTower {
	public static void main(String[] args) {
		System.out.println("当一个盘时:");
		Tower tower = new Tower();
		tower.move(1,'A', 'B', 'C');

		System.out.println("当两个盘时:");
		tower.move(2, 'A', 'B', 'C');
		
		System.out.println("当三个盘时:");
		tower.move(3, 'A', 'B', 'C');
	}
}

class Tower {
	//方法
	//num 表示要移动的个数,a,b,c 分别表示A塔,B塔,C塔
	public void move(int num, char a, char b, char c) {
		//如果只有一个盘 num = 1
		if (num == 1) {
			System.out.println(a + "->" + c);
		} else {
			//如果有多个盘,可以看成两个,最下面的和上面的所有盘(num-1)
			//(1)先移动上面所有的盘到b,借助c
			move(num -1, a, c, b);
			//(2)最下面的这个盘,移动到 c
			System.out.println(a + "->" + c);
			//(3)再把 b塔的所有盘,移动到 c ,借助 a
			move(num - 1, b, a, c);
		}
	}	
}

GitHub代码
gitee代码

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值