一、类与对象

1.1 简单介绍

为什么引入?
现有技术解决:Object01.java

  1. import java.util.Scanner;
  2. public class Object01 {
  3. public static void main(String[] args) {
  4. /*
  5. 需求:
  6. 张老太太有两只猫,一只叫小白,今年3岁,白色;另一只叫小黑,今年12岁,黑色。
  7. 请编写一个程序,当用户输入猫的名字时,就显示该猫的名字年龄,颜色。
  8. 如果用户输入错误,则显示 张老太太没有这只猫。
  9. */
  10. Scanner scan = new Scanner(System.in);
  11. // 传统方法
  12. // 数组存放
  13. // 缺点:无法同时保存不同的数据类型
  14. // 只能通过[下标]获取信息,名字和内容的对应关系不明确
  15. // 不能体现猫的行为
  16. String cat1[] = {"小白", "3", "白色"};
  17. String cat2[] = {"小黑", "12", "黑色"};
  18. System.out.println("请输入小猫的名字");
  19. String cat = scan.next();
  20. if (cat.equals(cat1[0])) {
  21. for (int i = 0; i < cat1.length; i++) {
  22. System.out.print(cat1[i] + " ");
  23. }
  24. } else if (cat.equals(cat2[0])) {
  25. for (int i = 0; i < cat2.length; i++) {
  26. System.out.print(cat2[i] + " ");
  27. }
  28. } else {
  29. System.out.println("张老太太没有这只猫");
  30. }
  31. // 传统方法
  32. // 单独变量解决 缺点:不利于数据的管理
  33. // 第1只猫信息
  34. String cat1Name = "小白";
  35. int cat1Age = 3;
  36. String cat1Color = "白色";
  37. // 第2只猫信息
  38. String cat2Name = "小花";
  39. int cat2Age = 100;
  40. String cat2Color = "花色";
  41. }
  42. }

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

  1. 不利于数据的管理
  2. 效率低

1.2 类与对象的关系示意图

面向对象编程(基础部分) - 图1
说明:

  1. 类就是数据类型,比如 Cat
  2. 对象就是一个具体的实例

1.3 案例演示

Object02.java

  1. public class Object02 {
  2. public static void main(String[] args) {
  3. // 使用OOP面向对象解决
  4. // 实例化一只猫/创建一只猫/把猫实例化 (一个意思)
  5. // 解读:
  6. // 1. new Cat() 创建一只猫(猫对象)
  7. // 2. Cat cat1 = new Cat(); 把创建的猫赋给 cat1
  8. // 3. cat1 就是一个对象
  9. Cat cat1 = new Cat();
  10. cat1.name = "小白";
  11. cat1.age = 3;
  12. cat1.color = "白色";
  13. cat1.weight = 10.5;
  14. // 创建第二只猫,并赋给 cat2
  15. // cat2 也是一个对象(猫对象)
  16. Cat cat2 = new Cat();
  17. cat2.name = "小花";
  18. cat2.age = 100;
  19. cat2.color = "花色";
  20. cat2.weight = 20.2;
  21. // 访问对象的属性
  22. System.out.println("第1只猫信息 " + cat1.name + " "
  23. + cat1.age + " " + cat1.color + " " + cat1.weight);
  24. System.out.println("第2只猫信息 " + cat2.name + " "
  25. + cat2.age + " " + cat2.color + " " + cat2.weight);
  26. }
  27. }
  28. // 使用面向对象的方式来解决养猫问题
  29. //
  30. // 定义一个猫类 Cat -> 自定义的数据类型
  31. class Cat {
  32. // 属性
  33. String name; // 名字
  34. int age; // 年龄
  35. String color; // 颜色
  36. double weight; // 体重
  37. }

1.4 类与对象的区别和联系

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

1.5 对象在内存中的存在形式(重要)

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

1.6 创建对象的方法

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

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

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

根据代码,分析

  1. Person p1 = new Person();
  2. p1.age = 10;
  3. p1.name = "小明";
  4. Person p2 = p1; // 把p1赋给p2,即让p2指向p1
  5. System.out.println(p2.age);
  6. p2.age = 15;
  7. System.out.println(p1.age);

面向对象编程(基础部分) - 图3
Java内存的结构分析:

  1. 栈:一般存放基本数据类型(局部变量)
  2. 堆:存放对象(Cat cat,数组等)
  3. 方法区:常量池(常量、比如字符串),类加载信息

Java创建对象的流程简单分析:
Person p = new Person();
p.name = “jack”;
p.age = 10;

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

二、属性(成员变量)

2.1 基本介绍

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

2.2 属性的注意事项和细节说明

  1. 属性的定义语法同变量,示例:访问修饰符 属性类型 属性名;
    访问修饰符:用于控制属性的访问范围
    4种访问修饰符:public,protected,默认,private
  2. 属性 的定义类型可以为任意类型,包含基本类型和引用类型
  3. 属性如果不赋值,有默认值,规则和数组一致

2.3 案例演示

PropertiesDetail.java

  1. public class PropertiesDetail {
  2. public static void main(String[] args) {
  3. // 创建Person对象
  4. // p1 是对象名(对象引用)
  5. // new Person() 创建的对象空间(数据) 才是真正的对象
  6. Person p1 = new Person();
  7. // 对象的属性默认值,遵守数组规则:
  8. // int:0,short:0,byte:0,long:0,float:0.0,
  9. // double:0.0,char:\u0000,boolean:false,String:null
  10. System.out.println("\n当前这个人的信息");
  11. System.out.println("age=" + p1.age + " name=" + p1.name + " sal="
  12. + p1.sal + " isPass=" + p1.isPass);
  13. }
  14. }
  15. class Person {
  16. // 四个属性
  17. int age;
  18. String name;
  19. double sal;
  20. boolean isPass;
  21. }

2.4 如何访问属性

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

三、成员方法

3.1 基本介绍

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

3.2 案例演示

  1. 添加speak 成员方法,输出 我是一个好人
    2. 添加cal01 成员方法,可以计算从1+…+1000的结果
    3. 添加cal02 成员方法,该方法可以接收一个数n,计算1+…+n的结果
    4. 添加getSum 成员方法,可以计算两个数的和
    Method01.java ```java import java.util.Scanner; public class Method01 {

    public static void main(String[] args) {

    1. Scanner scan = new Scanner(System.in);
    2. // 方法使用
    3. // 1. 方法写好后如果不调用,不会主动运行
    4. // 2. 先创建对象,然后调用方法即可
    5. Person p1 = new Person(); // 创建对象
    6. p1.speak(); // 调用方法
    7. p1.cal01();
    8. p1.cal02(100); // 调用cal02方法,同时给形参n = 100
    9. // 调用 getSum方法,同时 num1 = 10, num2 = 20
    10. // 把方法 getSum方法 返回的值 赋给 变量returnSum
    11. int returnSum = p1.getSum(10, 20);
    12. System.out.println("两数之和等于" + returnSum);

    } }

class Person { String name; int age; // 方法(成员方法) // 添加speak 成员方法,输出 我是一个好人 // 解读: // 1. public 表示方法是公开 // 2. void:表示方法没有返回值 // 3. speak():speak 方法名 ,()形参列表 // 4. { } :方法体,可以写我们要执行的代码,实现相关功能 // 5. System.out.println(“我是一个好人”); 表示我们的方法就是输出一句话 public void speak() { System.out.println(“我是一个好人”); }

  1. // 添加cal01 成员方法,可以计算从1+...+1000的结果
  2. public void cal01() {
  3. int sum = 0;
  4. for (int i = 1; i <= 1000; i++) {
  5. sum += i;
  6. }
  7. System.out.println("sum=" + sum);
  8. }
  9. // 添加cal02 成员方法,该方法可以接收一个数n,计算1+...+n的结果
  10. // 解读:
  11. // 1. (int n) 形参列表,表示当前有一个形参 n 可以接收用户输入
  12. public void cal02(int n) {
  13. int sum = 0;
  14. for (int i = 1; i <= n; i++) {
  15. sum += i;
  16. }
  17. System.out.println("sum=" + sum);
  18. }
  19. // 添加getSum 成员方法,可以计算两个数的和
  20. // 解读:
  21. // 1. public:表示方法是公开的
  22. // 2. int:表示方法执行后返回一个 int 值
  23. // 3. getSum:方法名
  24. // 4. (int num1,int num2) 形参列表,2个形参,可以接收用户输入的两个数
  25. // 5. return sum; 表示把 sum 的值返回
  26. public int getSum(int num1,int num2) {
  27. int sum = num1 + num2;
  28. return sum;
  29. }

}

  1. <a name="rrHnA"></a>
  2. #### 3.3 方法的调用机制原理(重要)
  3. ![](https://cdn.nlark.com/yuque/0/2021/jpeg/12677686/1630395986738-5200d278-c0cc-4803-ab53-b092aff0a031.jpeg)<br />说明:
  4. 1. 当程序执行到方法时,就会开辟一个独立的空间(栈空间)
  5. 1. 当方法执行完毕,或者执行到 return语句时,就会返回
  6. 1. 返回到调用方法的地方
  7. 1. 返回后继续执行方法后面的代码(独立空间自动销毁)
  8. 1. 当 main方法(栈)执行完毕,整个程序退出
  9. <a name="ei7qY"></a>
  10. #### 3.4 成员方法的必要性
  11. 看一个需求:<br />请遍历一个数组,输出数组的各个元素值。<br />**Method02.java**
  12. ```java
  13. public class Method02 {
  14. public static void main(String[] args) {
  15. // 请遍历一个数组,输出数组的各个元素值。
  16. int[][] map = {{0, 0, 1}, {1, 1, 1}, {1, 1, 3}};
  17. // 遍历map数组
  18. // 传统方法:for遍历
  19. for (int i = 0; i < map.length; i++) {
  20. for (int j = 0; j < map[i].length; j++) {
  21. System.out.print(map[i][j] + " ");
  22. }
  23. System.out.println();
  24. }
  25. // ...要求再次遍历map数组
  26. // 传统方法:再for遍历一遍
  27. for (int i = 0; i < map.length; i++) {
  28. for (int j = 0; j < map[i].length; j++) {
  29. System.out.print(map[i][j] + " ");
  30. }
  31. System.out.println();
  32. }
  33. // ...再次遍历
  34. // 传统方法冗余度太高,同样的代码写很多次
  35. // 如果同样的代码某个地方需要做同样的修改,很麻烦
  36. // 使用方法完成输出
  37. MyTools tool = new MyTools();
  38. tool.printArr(map); // map必须填写,方法里的只是形参,这是是实参
  39. // ...再次遍历
  40. tool.printArr(map);
  41. }
  42. }
  43. // 把遍历输出map的功能,写到一个类的方法中,然后调用该方法即可
  44. class MyTools {
  45. // 方法,接收一个二维数组
  46. public void printArr(int[][] map) { // 这里的map是形参 名字可以随意定义 不影响
  47. System.out.println("==== 使用方法输出的map数组 ====");
  48. // 对传入的map数组进行遍历输出
  49. for (int i = 0; i < map.length; i++) { // 方法里面的map要保持和形参名字一致
  50. for (int j = 0; j < map[i].length; j++) {
  51. System.out.print(map[i][j] + " ");
  52. }
  53. System.out.println();
  54. }
  55. }
  56. }

成员方法的好处:

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

3.5 成员方法的定义

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

  1. 访问修饰符:控制方法的使用范围 如public
  2. 返回数据类型:表示成员方法输出,void 表示没有返回值
  3. 形参列表:表示成员方法输入
  4. 方法主体:表示为了实现某一功能代码块
  5. return 语句不是必须的

3.6 方法的注意事项和使用细节

访问修饰符
有四种(public,protected,默认,private),如果不写就是默认访问

返回数据类型

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

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

参数列表

  1. 一个方法可以有0个参数,也可以有多个参数,中间用逗号隔开
  2. 参数类型可以为任意类型,包含基本类型和引用类型
  3. 调用带参数的方法时,一定对应着参数列表传入相同类型或兼容类型的参数
  4. 方法定义时的参数成为形式参数,简称形参;方法调用时的参数成为实际参数,简称实参,实参和形参的类型要一致或兼容,个数、顺序必须一致

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

方法调用细节说明

  1. 同一个类中的方法调用:直接调用即可。比如: print(参数);
  2. 跨类中的方法A类调用B类方法:需要通过对象名调用。比如:对象名.方法名(参数);
  3. 特别说明:跨类的方法调用和方法的访问修饰符相关。

⏱ 小练习

  1. 编写类 AA ,方法:判断一个数是奇数odd 还是偶数even,返回boolean ```java

public class MethodExercise01 {

  1. public static void main(String[] args) {
  2. int num = 1;
  3. AA a = new AA();
  4. if (a.isOdd(num)) {
  5. System.out.println(num + "是奇数");
  6. } else {
  7. System.out.println(num + "是偶数");
  8. }
  9. }

} class AA { public boolean isOdd(int i) { // if (i % 2 != 0) { // return true; // } else { // return false; // }

  1. // return i % 2 !=0 ? true : false;
  2. return i % 2 != 0;
  3. }

}

  1. 2. 根据行、列、字符 打印对应的行数和列数的字符,比如:行:4,列:4,字符 #,打印相应的效果
  2. ```java
  3. public class MethodExercise01 {
  4. public static void main(String[] args) {
  5. AA a = new AA();
  6. a.printChar(4,4,'#');
  7. }
  8. }
  9. class AA {
  10. public void printChar(int row,int column,char c) {
  11. for (int i = 0; i < row; i++) {
  12. for (int j = 0; j < column; j++) {
  13. System.out.print(c + " ");
  14. }
  15. System.out.println();
  16. }
  17. }
  18. }

3.7 成员方法传参机制

方法的传参机制对我们今后的编程非常重要,一定要搞的清清楚楚明明白白。

基本数据类型的传参机制
MethodParameter01.java

  1. public class MethodParameter01 {
  2. public static void main(String[] args) {
  3. int a = 10;
  4. int b = 20;
  5. AA obj = new AA();
  6. obj.swap(a,b);
  7. System.out.println(" a=" + a + " b=" + b);
  8. }
  9. }
  10. class AA {
  11. public void swap(int a,int b) {
  12. System.out.println("交换前 a=" + a + " b=" + b);
  13. int temp = a;
  14. a = b;
  15. b = temp;
  16. System.out.println("交换后 a=" + a + " b=" + b);
  17. }
  18. }

分析代码的运行流程及结果
面向对象编程(基础部分) - 图4
结论:基本数据类型,传递的是值(值拷贝),形参的任何改变不影响实参!

引用数据类型的传参机制
MethodParameter02.java

  1. public class MethodParameter02 {
  2. public static void main(String[] args) {
  3. /*
  4. 看一个案例
  5. B类中编写一个方法 test100,可以接收一个数组,
  6. 在方法中修改该数组,看看原数组是否变化?
  7. B类中编写一个方法 test200,可以接受一个Person(age,sal)对象,
  8. 在方法中修改该对象属性,看看原来的对象是否变化?
  9. */
  10. int arr[] = {1,2,3};
  11. B b = new B();
  12. b.test100(arr); //调用方法
  13. System.out.println("main中的arr数组");
  14. for (int i = 0; i < arr.length; i++) {
  15. System.out.print(arr[i] + " ");
  16. }
  17. System.out.println();
  18. Person p1 = new Person();
  19. p1.age = 10;
  20. p1.sal = 100.8;
  21. b.test200(p1);
  22. System.out.println(p1.age);
  23. }
  24. }
  25. class B {
  26. public void test100(int[] a) {
  27. a[0] = 100;
  28. System.out.println("test100方法中的arr数组");
  29. for (int i = 0; i < a.length; i++) {
  30. System.out.print(a[i] + " ");
  31. }
  32. System.out.println();
  33. }
  34. public void test200(Person p) {
  35. p.age = 50;
  36. }
  37. }
  38. class Person {
  39. int age;
  40. double sal;
  41. }

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

面向对象编程(基础部分) - 图6
思考:如果 test200 中是 p = null; 或者 p = new Person(); main中 执行 b.test200(p1); 后会输出什么?

小练习
编写一个方法copyPerson,可以复制一个Person对象,返回复制的对象。
克隆对象,注意要求新对象和原来的对象是两个独立的对象,只是它们的属性相同。

  1. public class MethodExercise {
  2. public static void main(String[] args) {
  3. MyTools tool = new MyTools();
  4. Person p1 = new Person();
  5. p1.name = "Jack";
  6. p1.age = 18;
  7. Person p2 = tool.copyPerson(p1);
  8. //到此 p1 和 p2 是两个独立的对象,下面验证
  9. p1.name = "Bob";
  10. p1.age = 20;
  11. System.out.println("拷贝的新对象p2的属性 name=" + p2.name + " age=" + p2.age);
  12. System.out.println("原对象p1修改后的属性 name=" + p1.name + " age=" + p1.age);
  13. // 也可通过输出对象的 HashCode 看看对象是否是一个
  14. }
  15. }
  16. class Person {
  17. String name;
  18. int age;
  19. }
  20. class MyTools {
  21. //编写一个方法copyPerson,可以复制一个Person对象,返回复制的对象。
  22. //克隆对象,注意要求新对象和原来的对象是两个独立的对象,只是它们的属性相同。
  23. //
  24. //编写方法的思路
  25. //1. 方法的返回类型 Person
  26. //2. 方法的名字 copyPerson
  27. //3. 方法的形参 (Person p)
  28. //4. 方法体,创建一个新对象,并复制属性,返回即可
  29. public Person copyPerson(Person p) {
  30. // 创建一个新的Person
  31. Person p2 = new Person();
  32. p2.name = p.name; // 把原来对象的name赋给p2
  33. p2.age = p.age; // 把原来对象的age赋给p2
  34. return p2;
  35. }
  36. }

四、方法递归调用

4.1 基本介绍

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

4.2 递归能解决什么问题?

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

4.3 递归调用机制

结合下面代码进行分析

  1. public class Recursion01 {
  2. public static void main(String[] args) {
  3. T t = new T();
  4. t.test(5);
  5. }
  6. }
  7. class T {
  8. public void test(int n) {
  9. if (n > 2) {
  10. test(n - 1);
  11. }
  12. System.out.println("n=" + n);
  13. }
  14. }

面向对象编程(基础部分) - 图7
阶乘问题:结合上图试分析递归过程

  1. public class Recursion02 {
  2. public static void main(String[] args) {
  3. T t = new T();
  4. System.out.println(" = " + t.factorial(5));
  5. }
  6. }
  7. class T {
  8. public int factorial(int n) {
  9. if (n == 1) {
  10. System.out.print(n);
  11. return 1;
  12. } else {
  13. System.out.print(n + " * ");
  14. return factorial(n - 1) * n;
  15. }
  16. }
  17. }

4.4 递归重要规则

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

⏱ 小练习

  1. 请使用递归的方式求出斐波那契数1,1,2,3,5,8,13….给你一个整数n,求出第n个数的值是多少 ```java import java.util.Scanner; public class RecursionExercise01 {

    public static void main(String[] args) {

    1. Scanner scan = new Scanner(System.in);
    2. System.out.println("请问要输出第几位斐波那契数?");
    3. int n = scan.nextInt();
    4. T t = new T();
    5. System.out.println(t.resNum(n));

    } }

class T { public int resNum(int n) { if (n > 0) { if (n == 1 || n == 2) { return 1; } else { return resNum(n - 1) + resNum(n - 2); } } else { return 0; } } }

  1. 2. 猴子吃桃问题:有一堆桃子,猴子第一天吃了其中的一半,并再多吃了一个!以后每天都是如此,当到第10天时,想再吃时(即还没吃),发现只有1个桃子了。问:最初有多少个桃子?
  2. ```java
  3. public class RecursionExercise02 {
  4. public static void main(String[] args) {
  5. Monkey monkey = new Monkey();
  6. System.out.println(monkey.peach(1));
  7. }
  8. }
  9. class Monkey {
  10. public int peach(int day) {
  11. if (day == 10) {
  12. return 1;
  13. } else {
  14. return (peach(day + 1) + 1) * 2;
  15. }
  16. }
  17. }

4.5 迷宫问题

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

    public static void main(String[] args) {

    1. // 思路
    2. // 1. 先创建迷宫,用二维数组表示
    3. // 2. 先规定 map 数组的元素值:0表示可以走,1表示障碍物
    4. int[][] map = new int[8][7];
    5. // 3. 将最上面的一行和最下面的一行,全部设置为1
    6. for (int i = 0; i < 7; i++) {
    7. map[0][i] = 1;
    8. map[7][i] = 1;
    9. }
    10. // 4. 将最右面的一列和最左面的一列,全部设置为1
    11. for (int i = 0; i < 8; i++) {
    12. map[i][0] = 1;
    13. map[i][6] = 1;
    14. }
    15. map[3][1] = 1;
    16. map[3][2] = 1;
    17. map[2][2] = 1; // 测试回溯
    18. // 输出当前的迷宫地图
    19. System.out.println("===== 当前地图 =====");
    20. for (int i = 0; i < map.length; i++) {
    21. for (int j = 0; j < map[i].length; j++) {
    22. System.out.print(map[i][j] + " ");
    23. }
    24. System.out.println();
    25. }
    26. // 使用findWay给老鼠找路
    27. T t = new T();
    28. t.findWay(map, 1, 1);
    29. // 输出找到的路线
    30. System.out.println("\n===== 找到的路线 =====");
    31. for (int i = 0; i < map.length; i++) {
    32. for (int j = 0; j < map[i].length; j++) {
    33. System.out.print(map[i][j] + " ");
    34. }
    35. System.out.println();
    36. }

    } }

class T { // 使用递归回溯的思想来解决老鼠出迷宫

  1. // 解读
  2. // 1. findWay方法就是专门来找出迷宫的路径
  3. // 2. 如果找到,就返回 true,否则返回 false
  4. // 3. map 就是二维数组,即表示迷宫
  5. // 4. i,j就是老鼠的位置,初始化的位置为(1,1)
  6. // 5. 因为我们是递归的找路,所以我们先规定 map数组各个值的含义
  7. // 0 表示可以走 1 表示障碍物 2 表示已走过可以走 3 表示走过,但是是障碍物(死路)
  8. // 6. 当 map[6][5] = 2 就说明找到通路,就可以结束,否则就继续找
  9. // 7. 先确定老鼠找路的策略 下->右->上->左
  10. public boolean findWay(int[][] map,int i,int j) {
  11. if (map[6][5] == 2) { // 说明已经找到通路
  12. return true;
  13. } else {
  14. if (map[i][j] == 0) { // 当前这个位置0,说明可以走
  15. // 我们假定可以走通
  16. map[i][j] = 2;
  17. // 使用找路策略,来确定该位置是否真的可以走通
  18. if (findWay(map, i + 1, j)) { // 先走下
  19. return true;
  20. } else if (findWay(map, i, j + 1)) { // 走右
  21. return true;
  22. } else if (findWay(map, i - 1, j)) { // 走上
  23. return true;
  24. } else if (findWay(map, i, j - 1)) { // 走左
  25. return true;
  26. } else {
  27. map[i][j] = 3;
  28. return false;
  29. }
  30. } else { // map[i][j] = 1,2,3
  31. return false;
  32. }
  33. }
  34. }
  35. // 修改找路策略,看看路径是否有变化
  36. // 下->右->上->左 ==> 上->右->下->左
  37. public boolean findWay2(int[][] map,int i,int j) {
  38. if (map[6][5] == 2) { // 说明已经找到通路
  39. return true;
  40. } else {
  41. if (map[i][j] == 0) { // 当前这个位置0,说明可以走
  42. // 我们假定可以走通
  43. map[i][j] = 2;
  44. // 使用找路策略,来确定该位置是否真的可以走通
  45. if (findWay2(map, i - 1, j)) { // 先走上
  46. return true;
  47. } else if (findWay2(map, i, j + 1)) { // 走右
  48. return true;
  49. } else if (findWay2(map, i + 1, j)) { // 走下
  50. return true;
  51. } else if (findWay2(map, i, j - 1)) { // 走左
  52. return true;
  53. } else {
  54. map[i][j] = 3;
  55. return false;
  56. }
  57. } else { // map[i][j] = 1,2,3
  58. return false;
  59. }
  60. }
  61. }

}

  1. <a name="o87PE"></a>
  2. #### 4.6 汉诺塔问题
  3. **汉诺塔**(Tower of Hanoi),又称**河内塔**,是一个源于[印度](https://baike.baidu.com/item/%E5%8D%B0%E5%BA%A6/121904)古老传说的[益智玩具](https://baike.baidu.com/item/%E7%9B%8A%E6%99%BA%E7%8E%A9%E5%85%B7/223159)。[大梵天](https://baike.baidu.com/item/%E5%A4%A7%E6%A2%B5%E5%A4%A9/711550)创造世界的时候做了三根金刚石柱子,在一根柱子上从下往上按照大小顺序摞着64片黄金圆盘。大梵天命令[婆罗门](https://baike.baidu.com/item/%E5%A9%86%E7%BD%97%E9%97%A8/1796550)把圆盘从下面开始按大小顺序重新摆放在另一根柱子上。并且规定,在小圆盘上不能放大圆盘,在三根柱子之间一次只能移动一个圆盘。<br />[点击查看【codepen】](https://codepen.io/finnhvman/embed/gzmMaa)<br />**HanoiTower.java**
  4. ```java
  5. public class HanoiTower {
  6. public static void main(String[] args) {
  7. Tower tow = new Tower();
  8. tow.move(5, 'A', 'B', 'C');
  9. }
  10. }
  11. class Tower {
  12. // 方法
  13. // num 表示要移动的个数,a, b , c分别表示A塔,B塔,C塔
  14. public void move(int num, char a, char b, char c) {
  15. // 如果只有一个盘 num = 1
  16. if (num == 1) {
  17. System.out.println(a + "->" + c);
  18. } else {
  19. // 如果有多个盘,可以看作两个,最下面的和上面的所有
  20. // 1. 先移动上面所有的盘到 B,借助 C
  21. move(num - 1, a, c, b);
  22. // 2. 把最下面的这个盘,移动到C
  23. System.out.println(a + "->" + c);
  24. // 2. 再把 B塔的所有盘,移动到C,借助A
  25. move(num - 1, b, a, c);
  26. }
  27. }
  28. }

4.7 八皇后问题

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

  1. public class EightQueen {
  2. public static void main(String[] args) {
  3. // map[]表示棋盘,假设map[] = {2, 5, 7, 0, 4, 6, 1 ,3}
  4. // 则 map[0] = 2 表示第一(层)行第三列放置第一颗皇后棋子
  5. // map[1] = 5 表示第二(层)行第六列放置第二颗皇后棋子
  6. // ......
  7. // map[7] = 3 表示第八(层)行第四列放置第八颗皇后棋子
  8. int[] map = new int[8];
  9. Q q = new Q(); // 新建q对象
  10. q.findWay(map,0); // 引用q对象的findWay方法,
  11. //传入棋盘,第一颗棋子先放在第一层第一列
  12. System.out.println(q.count); // 输出总方法数
  13. }
  14. }
  15. class Q {
  16. int count; // 用于记录成功摆放的次数 全局变量 默认初始值为0
  17. public void findWay(int[] map, int queen) {
  18. if (queen == 8) { // 如果八颗皇后全部放在了棋盘上
  19. for (int i = 0; i < 8; i++) { //循环遍历数组
  20. System.out.print((i + 1) + " " + (map[i] + 1) + "\t");
  21. } // 将这次摆放结果输出
  22. System.out.println();
  23. count++; // 成功计数器+1
  24. } else {
  25. for (int i = 0; i < 8; i++) { // 找到这一层(行)可以放置的位置(列)
  26. map[queen] = i;
  27. if (isSafe(map, queen)) { // 如果当前位置可以放置
  28. findWay(map, queen + 1); // 进入下一层尝试
  29. // 下一层也会找当前层可以放置的位置(列),并再进入下一层
  30. // 层层遍历,最终如果没有将八颗棋子都放在棋盘上,
  31. // 由于不满足上面的if (queen == 8) 所以结果不会输出,等于舍弃了这次
  32. // 直到找到可以将八颗棋子都放在棋盘上的结果,即map数组每一位都成功赋值
  33. // 会将这次摆放结果输出,并寻找下一个正确的摆放结果
  34. // 直至第一层每一列都尝试了,全部结果也就都找到了
  35. }
  36. }
  37. }
  38. }
  39. public boolean isSafe(int[] map, int queen) {
  40. for (int i = 0; i < queen; i++) {
  41. // 这一层放置的位置依次与前面层放置的位置对比
  42. // 由于这一次放置的是相对之前最下面的一层,所以肯定不会与之前在同一层
  43. // map[i] == map[queen] 表示放置的位置与之前层的某个位置同列
  44. // Math.abs(i - queen) == Math.abs(map[i] - map[queen]) 表示同斜线
  45. // Math.abs() 表示取绝对值,保证结果大于零
  46. // 这里可以借助等腰直角三角形理解
  47. // 假设 到这一层 棋盘的结构如下 此时 queen = 2
  48. // 第一层 { #, 0, 0, 0, 0, 0, 0, 0 } 这里 # 表示 map[0] = 0,queen = 0
  49. // 第二层 { 0, 0, #, 0, 0, 0, 0, 0 } 这里 # 表示 map[1] = 2,queen = 1
  50. // 第三层 { 0, 0, 0, #, 0, 0, 0, 0 } 这里 # 表示 map[2] = 3,queen = 2 当前
  51. // 第二层的#和它正下方的0以及第三层(当前层)的# 组成了一个等腰直角三角形
  52. // 由于i < queen时,i++ 所以当i = 1时
  53. // map[i] - map[queen] = 2 - 3
  54. // i - queen = 1 - 2
  55. // Math.abs(i - queen) == Math.abs(map[i] - map[queen]) = 1
  56. // 此时构成了等腰直角三角形 即当前层所在列和之前层所在列处在了同一斜线上
  57. if (map[i] == map[queen] || Math.abs(i - queen) == Math.abs(map[i] - map[queen])) {
  58. return false; // 同列或者同斜线都返回不能放置
  59. }
  60. }
  61. return true; //不是同列或者同斜线,可以放置
  62. }
  63. }

五、方法重载(OverLoad)

5.1 基本介绍

Java种允许同一个类中,多个同名方法的存在,但要求 形参列表不一致!
比如:System.out.println(); out 是 PrintStream 类型
image.png
重载的好处:

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

5.2 案例演示

  1. public class OverLoad01 {
  2. public static void main(String[] args) {
  3. MyCalculator mc = new MyCalculator();
  4. System.out.println(mc.calculate(5,3));
  5. System.out.println(mc.calculate(5,.3));
  6. System.out.println(mc.calculate(.5,3));
  7. System.out.println(mc.calculate(5,3,6));
  8. }
  9. }
  10. class MyCalculator {
  11. // 两个整数的和
  12. public int calculate(int n1, int n2) {
  13. return n1 + n2;
  14. }
  15. // 一个整数 一个double 的和
  16. public double calculate(int n1, double n2) {
  17. return n1 + n2;
  18. }
  19. // 一个double 一个整数 的和
  20. public double calculate(double n1, int n2) {
  21. return n1 + n2;
  22. }
  23. // 三个整数的和
  24. public int calculate(int n1, int n2, int n3) {
  25. return n1 + n2 + n3;
  26. }
  27. }

5.3 方法重载注意事项和细节

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

⏱ 小练习

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

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

  3. 在 Methods 类,定义三个重载方法 max(),第一个方法,返回两个 int 值中的最大值,第二个方法,返回两个 double 值中的最大值,第三个方法,返回三个 double 值中的最大值,并分别调用三个方法。

  1. public class OverLoadExercise {
  2. public static void main(String[] args) {
  3. Methods me = new Methods();
  4. me.m(6);
  5. me.m(5,4);
  6. me.m("java is a game");
  7. System.out.println(me.max(3,6));
  8. System.out.println(me.max(.6,.8));
  9. System.out.println(me.max(.3,.2,9)); // 可以运行,9 自动转成 double
  10. // 但是如果再新建一个 double max(double x, double y, int z) 方法
  11. // 会调用新的这个方法 新方法和原来的不会冲突,依然符合重载
  12. }
  13. }
  14. class Methods {
  15. public void m(int i) {
  16. int j = 0;
  17. j = i * i;
  18. System.out.println(i + "的平方 = " + j);
  19. }
  20. public void m(int i, int j) {
  21. int k = 0;
  22. k = i * j;
  23. System.out.println(i + " × " + j + " = " + k);
  24. }
  25. public void m(String x) {
  26. System.out.println(x);
  27. }
  28. public int max(int i, int j) {
  29. return i > j ? i : j;
  30. }
  31. public double max(double i, double j) {
  32. return i > j ? i : j;
  33. }
  34. public double max(double x, double y, double z) {
  35. double max = x > y ? x : y;
  36. return max > z ? max : z;
  37. }
  38. }

六、可变参数

6.1 基本介绍

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

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

6.2 案例演示

VarParameter01.java

  1. public class VarParameter01 {
  2. public static void main(String[] args) {
  3. VarMethod var = new VarMethod();
  4. System.out.println("所求的和" + var.sum(15,56,17));
  5. }
  6. }
  7. class VarMethod {
  8. // 可以计算2个数的和,3个数的和,4,5.....
  9. // 可以使用方法重载
  10. /*
  11. public int sum(int n1, int n2) {
  12. return n1 + n2;
  13. }
  14. public int sum(int n1, int n2, int n3) {
  15. return n1 + n2 + n3;
  16. }
  17. public int sum(int n1, int n2, int n3, int n4) {
  18. return n1 + n2 + n3 + n4;
  19. }
  20. // ......
  21. */
  22. // 上面三个方法名称相同,功能相同,参数个数不同 -> 使用可变参数优化
  23. // 解读
  24. // 1. int... 表示接收的是可变参数,类型是int,即参数可以接收多个int(0-多)
  25. // 2. 使用可变参数时,可以当作数组来使用 即 nums 可以当作数组
  26. // 3. 遍历 nums 数组,求和即可
  27. public int sum(int... nums) {
  28. System.out.println("接收的参数个数:" + nums.length);
  29. int sum = 0;
  30. for (int i = 0; i < nums.length; i++) {
  31. sum += nums[i];
  32. }
  33. return sum;
  34. }
  35. }

6.3 可变参数注意事项和使用细节

  1. 可变参数的实参可以为0个或任意个
  2. 可变参数的实参可以为数组
  3. 可变参数的本质就是数组
  4. 可变参数可以和普通类型的参数一起放在形参列表,但必须保证可变参数在最后
  5. 一个形参列表中只能出现一个可变参数

⏱ 小练习

有三个方法,分别实现返回姓名和两门课成绩(总分),返回姓名和三门课成绩(总分),返回姓名和五门课成绩(总分)。封装成一个方法。
VarParameterExercise.java

  1. public class VarParameterExercise {
  2. public static void main(String[] args) {
  3. VarMethod var = new VarMethod();
  4. var.showScore("小王", 66, 78.5);
  5. var.showScore("小白", 56, 89.5, 98);
  6. var.showScore("小真", 79, 86, 90.5, 99,100);
  7. }
  8. }
  9. class VarMethod {
  10. public void showScore(String name, double... score) {
  11. double sum = 0;
  12. for (int i = 0; i < score.length; i++) {
  13. sum += score[i];
  14. }
  15. System.out.println(name + "的" + score.length + "门课总分为" + sum);
  16. }
  17. }

七、作用域

7.1 基本介绍

面向对象中,变量作用域是非常重要的知识点,相对来说不是特别好理解 ,请注意认真思考,深刻掌握变量作用域。

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

7.2 案例演示

Scope01.java

  1. public class Scope01 {
  2. public static void main(String[] args) {
  3. Cat cat = new Cat();
  4. cat.cry();
  5. cat.eat();
  6. }
  7. }
  8. class Cat {
  9. // 全局变量:也就是属性,作用域为整个类体
  10. // 属性在定义时,可以直接赋值
  11. int age = 10; // 可以不赋值,有默认值
  12. public void cry() {
  13. // 1. 局部变量一般是指在成员方法中定义的变量
  14. // 2. n 和 name 就是局部变量
  15. // 3. n 和 name 的作用域在 cry 方法中
  16. int n = 10; // 必须赋值,因为没有默认值
  17. String name = "Jack";
  18. System.out.println("在 cry 方法中使用属性 age= " + age);
  19. }
  20. public void eat() {
  21. System.out.println("在 eat 方法中使用属性 age= " + age);
  22. // System.out.println("在 eat 方法中使用cry方法中的局部变量name" + name); // 会报错
  23. }
  24. }

7.3 作用域的注意事项和使用细节

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

八、构造方法/构造器

8.1 基本介绍

前面我们在创建对象时,是先把一个对象创建后,再给它的属性赋值
现在通过构造器,可以实现在创建对象同时,就可以指定这个对象的各个属性
构造方法又叫构造器(constructor),是类的一种特殊的方法,它的主要作用是完成对新对象的初始化。

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

8.2 构造器的说明和特点

  1. 构造器的修饰符可以默认
  2. 构造器没有返回值
  3. 方法名 和类名字 必须一样
  4. 参数列表 和 成员方法一样的规则
  5. 构造器的调用 由系统完成
    即在创建对象时,系统会自动的调用该类的构造器完成对对象的初始化

8.3 案例演示

Constructor01.java

  1. public class Constructor01 {
  2. public static void main(String[] args) {
  3. // 当我们new 一个对象时,直接通过构造器指定名字和年龄
  4. Person p1 = new Person("Smith", 80);
  5. System.out.println(p1.name);
  6. System.out.println(p1.age);
  7. }
  8. }
  9. // 在创建人类的对象时,就直接指定这个对象的年龄和姓名
  10. class Person {
  11. String name;
  12. int age;
  13. // 构造器
  14. // 解读:
  15. // 1. 构造器没有返回值,也不能写void
  16. // 2. 构造器的名称和类Person一样
  17. // 3. (String pName, int pAge) 是构造器形参列表,规则和成员方法一样
  18. public Person(String pName, int pAge) {
  19. System.out.println("构造器被调用");
  20. name = pName;
  21. age = pAge;
  22. }
  23. }

8.4 构造器的注意事项和使用细节

  1. 一个类可以定义多个不同的构造器,即构造器重载
    比如:我们可以再给Person类定义一个构造器,用来创建对象的时候,只指定人名,不需要指定年龄
  2. 构造器名和类名要相同
  3. 构造器没有返回值
  4. 构造器是完成对象的初始化,不是创建对象
  5. 在创建时,系统会自动的调用该类的构造方法
  6. 如果程序员没有定义构造方法,系统会自动给类生成一个默认无参构造方法(也叫默认构造方法),比如 Person(){},使用 javap指令 反编译看看
  7. 一旦定义了自己的构造器,默认的构造器就覆盖了,就不能再使用默认的无参构造器,除非显式的定义一下,即:Person(){}

ConstructorDetail.java

  1. public class ConstructorDetail {
  2. public static void main(String[] args) {
  3. Person p1 = new Person("King", 40); // 调用第一个构造器
  4. System.out.println(p1.name + "\t" + p1.age);
  5. Person p2 = new Person("Ling"); // 调用第二个构造器
  6. System.out.println(p2.name + "\t" + p2.age);
  7. }
  8. }
  9. class Person {
  10. String name;
  11. int age; // 默认0
  12. // 第一个构造器
  13. public Person(String pName, int pAge) {
  14. name = pName;
  15. age = pAge;
  16. }
  17. // 第二个构造器,只指定人名
  18. public Person(String pName) {
  19. name = pName;
  20. }
  21. }

⏱ 小练习

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

8.5 对象创建流程分析

  1. class Person{
  2. int age = 90;
  3. String name;
  4. Person(String n, int a) {
  5. name = n;
  6. age = a;
  7. }
  8. }
  9. Person p = new Person("小倩", 20);

面向对象编程(基础部分) - 图9
流程分析:

  1. 加载Person类信息(Person.class),只会加载一次
  2. 在堆中分配空间(地址)
  3. 完成对象初始化
    1. 默认初始化
    2. 显式初始化
    3. 构造器初始化
  4. 把对象在堆中的地址返回给p(p是对象名,也可以理解成对象引用)

九、this 关键字

9.1 基本介绍

当对象被创建时,默认隐含一个this方法,this指向对象本身。可用于在对象的成员方法中调用对象的属性(即全局变量),以此和成员变量同名的局部变量区分开来。

9.2 案例演示

This01.java

  1. public class This01 {
  2. public static void main(String[] args) {
  3. Dog dog1 = new Dog("小花", 5);
  4. dog1.info();
  5. }
  6. }
  7. class Dog {
  8. String name;
  9. int age;
  10. // public Dog(String dName, int dAge) { // 构造器
  11. // name = dName;
  12. // age = dAge;
  13. // }
  14. // 如果我们构造器的形参,能够直接写成属性名,就更好了
  15. public Dog(String name, int age) { // 构造器
  16. // this.name 就是当前对象的属性name
  17. this.name = name;
  18. // this.name 就是当前对象的属性age
  19. this.age = age; // 如果不加this,前后两个age都指的是局部变量形参age
  20. }
  21. public void info() { // 成员方法
  22. System.out.println(name + "\t" + age + "\t");
  23. }
  24. }

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

  1. this 关键字可以用来访问本类的属性、方法、构造器
  2. this 用于区分当前类的属性和局部变量
  3. 访问成员方法的语句:this.方法名(参数列表)
  4. 访问构造器语法:this(参数列表);注意只能在构造器中使用,即只能 构造器中访问其他构造器

且 对 this 的调用必须是构造器中的第一个语句

  1. this 不能再类定义外部使用,只能在类定义的方法中使用

ThisDetail.java

  1. public class ThisDetail {
  2. public static void main(String[] args) {
  3. T t1 = new T();
  4. t1.f2();
  5. }
  6. }
  7. class T {
  8. public void f1() {
  9. System.out.println("f1() 方法...");
  10. }
  11. public void f2() {
  12. System.out.println("f2() 方法...");
  13. // 调用本类的 f1
  14. // 第一种方式
  15. f1();
  16. // 第二种方式
  17. this.f1();
  18. }
  19. }

⏱ 小练习

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

  1. public class ThisExercise {
  2. public static void main(String[] args) {
  3. Person p1 = new Person("小明", 12);
  4. Person p2 = new Person("小明", 15);
  5. p1.compareTo(p2);
  6. }
  7. }
  8. class Person {
  9. String name;
  10. int age;
  11. public Person(String name, int age) {
  12. this.name = name;
  13. this.age = age;
  14. }
  15. public boolean compareTo(Person p) {
  16. return this.name.equals(p.name) && this.age == p.age; // 重点理解
  17. }
  18. }

十、本章作业

  1. 编写类 A01,定义方法 max,实现求某个 double 数组的最大值,并返回

  2. 编写类 A02,定义方法 find,实现查找某字符串数组中的元素查找,并返回索引,如果找不到,返回-1

  3. 编写类 Book,定义方法 updatePrice,实现更改某本书的,具体:如果价格 > 150,则改为150,如果价格 > 100,则改为100,否则不更改。

  4. 编写类 A03,实现数组的复制功能copyArr,输入旧数组,返回一个新数组,元素和旧数组一样

  5. 定义一个圆类 Circle,定义属性:半径,提供显示周长功能的方法,提供显示圆面积的方法

  6. 编程创建一个 Cale 计算类,在其中定义2个变量表示两个操作数,定义四个方法实现求和、差、乘、商(要求除数为0的时候,要给出提示) 并创建两个对象,分别测试

  7. 设计一个Dog类,有名字、颜色和年龄属性,定义输出方法 show() 显示其信息。并创建对象,进行测试、【提示 this.属性】

  8. 给定一个Java程序的代码如下所示,则编译运行后,输出结果是

public class Test {
int count = 9;
public void count1() {
count = 10;
System.out.println( “count1=” + count );
}
public void count2() {
System.out.println(“count1=” + count++);
}
public static void main(String args[]) {
new Test().count1(); // 匿名对象 输出 count1=10
Test t1 = new Test(); // 新 t1对象
t1.count2(); // 输出 count1=9
t1.count2(); // 输出 count1 = 10
}

  1. 定义 Music类,里面有音乐名 name、音乐时长 times 属性,并有播放 play 功能和返回本身属性信息的功能方法 getlnfo

  2. 试写出以下代码的运行结果

class Demo {
int i = 100;
public void m( ) {
int j = i++;
System.out.println(“i=” + i); // 101
System.out.println(“j=” + j); // 100
}
}
class Test {
public static void main(String[] args) {
Demo d1 = new Demo();
Demo d2 = d1;
d2.m( );
System.out.println(d1.i); // 101
System.out.println(d2.i); // 101
}
}

  1. 在测试方法中,调用method方法,代码如下,编译正确!试写出method方法的定义形式,调用语句为:System.out.printin( method( method( 10.0, 20.0), 100);
    答案:[ public ] double method(double num1, double num2) { }

  2. 创建一个Employee类,属性有(名字,性别,年龄,职位,薪水),提供3个构造方法,可以初始化

    1. (名字,性别,年龄,职位,薪水)
    2. (名字,性别,年龄)
    3. (职位,薪水)

要求充分复用构造器

  1. public class Homework12 {
  2. public static void main(String[] args) {
  3. }
  4. }
  5. class Employee {
  6. String name;
  7. char gender;
  8. int age;
  9. String job;
  10. double salary;
  11. public Employee(String job, double salary) {
  12. this.job = job;
  13. this.salary = salary;
  14. }
  15. public Employee(String name, char gender, int age) {
  16. this.name = name;
  17. this.gender = gender;
  18. this.age = age;
  19. }
  20. public Employee(String job, double salary, String name, char gender, int age) {
  21. this(name, gender, age);
  22. // this(job, salary); 不能再使用,因为不在首句
  23. this.job = job;
  24. this.salary = salary;
  25. }
  26. }
  1. 将对象作为参数传递给方法

题目要求:

  1. 定义一个 Circle 类,包含一个double型的radius属性代表圆的半径,一findArea( ) 方法返回圆的面积。
  2. 定义一个类 PassObject,在类中定义一个方法printAreas( ),该方法的定义如下:

public void printAreas(Circle c, int times)//方法签名

  1. 在printAreas方法中打印输出1到times之间的每个整数半径值,以及对应的面积。

例如,times为5,则输出半径1,2,3,4,5,以及对应的圆面积。

  1. 在main方法中调用printAreas()方法,调用完毕后输出当前半径值。程序运行结果如国所示
    image.png

    1. public class Homework13 {
    2. public static void main(String[] args) {
    3. Circle c = new Circle();
    4. PassObject obj = new PassObject();
    5. obj.printAreas(c,5);
    6. }
    7. }
    8. class Circle {
    9. double radius;
    10. public double findArea() {
    11. return Math.PI * radius * radius;
    12. }
    13. // 添加方法setRadius,修改对象的半径值
    14. public void setRadius(double radius) {
    15. this.radius = radius;
    16. }
    17. }
    18. class PassObject {
    19. public void printAreas(Circle c, int times) {
    20. System.out.println("Radius\tArea");
    21. for (double i = 1; i <= times; i++) {
    22. c.setRadius(i);
    23. // c.radius = i;
    24. System.out.println(i + "\t" + c.findArea());
    25. }
    26. }
    27. }
  1. 扩展题,学员自己做.
    有个人Tom,设计他的成员变量,成员方法,可以电脑猜拳。

电脑每次都会随机生成0,1,2
0 表示石头 1 表示剪刀 2 表示布,并要可以显示Tom的输赢次数(清单)

  1. import java.util.Random;
  2. import java.util.Scanner;
  3. public class Homework14 {
  4. public static void main(String[] args) {
  5. Tom t = new Tom();
  6. // 用来记录最后输赢的次数
  7. int isWinCount = 0;
  8. // 创建一个二维数组,用来接收局数,Tom出拳情况以及电脑出拳情况
  9. int[][] arr1 = new int[3][3];
  10. int j = 0;
  11. // 创建一个一维数组,用来接收输赢情况
  12. String[] arr2 = new String[3];
  13. Scanner scanner = new Scanner(System.in);
  14. for (int i = 0; i < 3; i++) {
  15. // 获取玩家出的拳
  16. System.out.println("请输入你要出的拳头 (0-拳头, 1-剪刀, 2-布):");
  17. int num = scanner.nextInt();
  18. t.setTomGuessNum(num);
  19. int tomGuess = t.getTomGuessNum();
  20. arr1[i][j + 1] = tomGuess;
  21. // 获取电脑出的拳
  22. int comGuess = t.computerNum();
  23. arr1[i][j + 2] = comGuess;
  24. // 将玩家猜的拳与电脑作比较
  25. String isWin = t.vsComputer();
  26. arr2[i] = isWin;
  27. arr1[i][j] = t.count;
  28. // 对每一局的情况进行输出
  29. System.out.println("========================================");
  30. System.out.println("局数\t玩家出\t电脑出\t输赢情况");
  31. System.out.println(t.count + "\t" + tomGuess + "\t" + comGuess + "\t" + isWin);
  32. System.out.println("========================================");
  33. System.out.println("\n\n");
  34. isWinCount = t.winCount(isWin);
  35. }
  36. // 对游戏的最终结果进行输出
  37. System.out.println("局数\t玩家的出拳\t电脑的出拳\t\t输赢情况");
  38. for (int a = 0; a < arr1.length; a++) {
  39. for (int b = 0; b < arr1[a].length; b++) {
  40. System.out.print(arr1[a][b] + "\t\t");
  41. }
  42. System.out.print(arr2[a]);
  43. System.out.println();
  44. }
  45. System.out.println("你赢了" + isWinCount + "次");
  46. }
  47. }
  48. class Tom {
  49. // 玩家出拳的类型
  50. int tomGuessNum;
  51. // 电脑出拳的类型
  52. int comGuessNum;
  53. // 玩家赢的次数
  54. int winCountNum;
  55. // 比赛的次数
  56. int count = 1;
  57. public void showInfo() {
  58. //.....
  59. }
  60. /**
  61. * 电脑随机生成猜拳的数字的方法
  62. * @return
  63. */
  64. public int computerNum() {
  65. Random r = new Random();
  66. comGuessNum = r.nextInt(3); // 方法 返回0-2的随机数
  67. return comGuessNum;
  68. }
  69. /**
  70. * 设置玩家猜拳数字的方法
  71. * @param tomGuessNum
  72. */
  73. public void setTomGuessNum(int tomGuessNum) {
  74. if (tomGuessNum > 2 || tomGuessNum < 0) {
  75. // 抛出异常
  76. throw new IllegalArgumentException("数字输入错误");
  77. }
  78. this.tomGuessNum = tomGuessNum;
  79. }
  80. public int getTomGuessNum() {
  81. return tomGuessNum;
  82. }
  83. /**
  84. * 比较猜拳的结果
  85. * @return 玩家赢返回true,否则返回false
  86. */
  87. public String vsComputer() {
  88. if (tomGuessNum == comGuessNum) {
  89. return "平手";
  90. } else if ((tomGuessNum + 1) % 3 == comGuessNum) {
  91. return "你赢了";
  92. } else {
  93. return "你输了";
  94. }
  95. }
  96. /**
  97. * 记录玩家赢的次数
  98. */
  99. public int winCount(String s) {
  100. count++;
  101. if (s.equals("你赢了")) {
  102. winCountNum++;
  103. }
  104. return winCountNum;
  105. }
  106. }

学习参考(致谢):

  1. B站 @程序员鱼皮 Java学习一条龙
  2. B站 @韩顺平 零基础30天学会Java