Chaoter07 面向对象 (Object)

Chapter07 面向对象

7.1 类与对象

  1. 类是抽象的,概念的,代表一类事物,比如人类,猫类.., 即它是数据类型.

  2. 对象是具体的,实际的,代表一个具体事物, 即 是实例.

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

image-20220119162711327

public class Object01 {
    public static void main(String[] args) {
        //使用 OOP 面向对象解决
//实例化一只猫[创建一只猫对象]
//老韩解读
//1. new Cat() 创建一只猫(猫对象)
//2. Cat cat1 = new Cat(); 把创建的猫赋给 cat1
//3. cat1 就是一个对象
        Cat cat1 = new Cat();
        cat1.name = "小白";
        cat1.age = 3;
        cat1.color = "白色";
        cat1.weight = 10;
//创建了第二只猫,并赋给 cat2
//cat2 也是一个对象(猫对象)
        Cat cat2 = new Cat();
        cat2.name = "小花";
        cat2.age = 100;
        cat2.color = "花色";
        cat2.weight = 20;
//怎么访问对象的属性呢
        System.out.println("第 1 只猫信息" + cat1.name
                + " " + cat1.age + " " + cat1.color + " " + cat1.weight);
        System.out.println("第 2 只猫信息" + cat2.name
                + " " + cat2.age + " " + cat2.color + " " + cat2.weight);
    }
}
//使用面向对象的方式来解决养猫问题
//定义一个猫类 Cat -> 自定义的数据类型
class Cat {
    //属性/成员变量
    String name; //名字
    int age; //年龄
    String color; //颜色
//double weight; //体重
//行为
}

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

image-20220119162641691

7.1.2 属性 / 成员变量

  • 基本介绍:从概念或叫法上看: 成员变量 = 属性 = field(字段) (即 成员变量是用来表示属性的,授课中,统一叫 属性) 案例演示:Car(name,price,color)
  • 细节
    • 属性的定义语法同变量,示例:访问修饰符 属性类型 属性名;
    • 这里老师简单的介绍访问修饰符: 控制属性的访问范围 有四种访问修饰符 public, proctected, 默认, private ,后面我会详细介绍
    • 属性的定义类型可以为任意类型,包含基本类型或引用类型
    • 属性如果不赋值,有默认值,规则和数组一致。具体说: int 0,short 0, byte 0, long 0, float 0.0,double 0.0,char \u0000, boolean false,String null
public class PropertiesDetail {
    public static void main(String[] args) {
        //创建Person对象
        //p1 是对象名(对象引用)
        //new Person() 创建的对象空间(数据)才是真正的对象
        Person p1 = new Person();

        //对象的属性默认值,遵守数组规则
//        属性如果不赋值,有默认值,规则和数组一致。具体说: int 0,short 0, byte 0, long 0,
//        float 0.0,double 0.0,char \u0000,
//        boolean false,String null
        System.out.println("输出这个人的信息");
        System.out.println("age=" + p1.age + " name=" + p1.name
                + " sal=" + p1.sal + " isPass=" + p1.isPass);
    }
}
class Person{
    //四个属性
    int age;
    String name;
    double sal;
    boolean isPass;
}

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

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

Object03

public class Object03 {
    public static void main(String[] args) {
        Person1 p1 = new Person1();
        p1.age = 10;
        p1.name = "小明";
        Person1 p2 = p1; //把p1 赋给了 p2 ,也就是把 p1 地址值赋给了 p2
        System.out.println(p2.age);//10
    }
}
class Person1{
    //四个属性
    int age;
    String name;
}

image-20220119210427577

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

Object04

Person a=new Person();
a.age=10;
a.name="小明";
Person b;
b=a;
System.out.println(b.name);//小明
    b.age=200;
    b =null; //把 b 的地址值变成 null
System.out.println(a.age);//200
System.out.println(b.age);//异常

image-20220119212808465

7.2 成员方法

7.2.1 成员方法的定义

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

} 
  1. 形参列表:表示成员方法输入 cal(int n) , getSum(int num1,
  2. 返回数据类型:表示成员方法输出, void 表示没有返回值
  3. 方法主体:表示为了实现某一功能代码块
  4. return 语句不是必须的。
  5. 老韩提示: 结合前面的题示意图, 来理解
public class Method01 {
    public static void main(String[] args) {
        Person03 p1 = new Person03();
        p1.cal02(5);
        //调用getSum方法,同时num1 = 10;num2 = 20;
        //把 方法 getSum 返回的值,赋给 变量 returnRes
        int returnRes = p1.getSum(10,20);
        System.out.println("getSum方法返回的值=" + returnRes);
    }
}
class Person03{
    String name;
    int age;
    //方法(成员方法)
    //添加speak 成员方法,输出“我是一个好人”
    //
    public void cal01(){
        System.out.println("我是一个好人");
    }

    //添加cal02 成员方法 该方法可以接收一个数 n , 计算 1 + …… + n 的结果
    //老韩解读
    //1.(int n )形参列表, 表示当前有一个 形式参数 n ,可以直接用户输入
    public void cal02(int n ){
        int sum = 0;
        for (int i = 1; i <= n; i++){
            sum += i;
        }
        System.out.println("cal02 的值为=" + sum);
    }
    //添加getSum成员方法,可以计算两个数的和
    //public 表示方法是公开的
    //int :表示方法执行后,返回一个int 值
    //getSum:方法名
    //(int num1, int num2)形式参数,2个形参,可以接收用户传入的两个数
    //return res:表示把 res 的值,返回
    public int getSum(int num1, int num2){
        int res = num1 + num2;
        return res;
    }
}
  • 遍历数组
public class Method02 {
    public static void main(String[] args) {
        //请遍历一个数组
        int[][] map = {{0,0,1},{1,1,1},{1,1,3}};

        //使用方法完成输出,创建Mytools对象,tool
        MyTools tool = new MyTools();
        tool.printArr(map);

        //遍历map数组
//        传统解决方式:解决思路1
//        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 MyTools{
    public void printArr(int[][] 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("");
        }
    }
}

7.2.2 方法的调用机制

image-20220120163946781

7.2.3 方法使用的细节 1

  • 访问修饰符 (作用是控制 方法使用的范围) 如果不写默认访问
  1. [有四种: public, protected, 默认, private], 具体在后面说
  • 返回数据类型
  1. 一个方法最多有一个返回值 [思考,如何返回多个结果 返回数组 ]
  2. 返回类型可以为任意类型,包含基本类型或引用类型(数组,对象)
  3. 如果方法要求有返回数据类型,则方法体中最后的执行语句必须为 return 值; 而且要求返回值类型必须和 return 的 值类型一致或兼容
  4. 如果方法是 void,则方法体中可以没有 return 语句,或者 只写 return ;
  5. 方法名 遵循驼峰命名法,最好见名知义,表达出该功能的意思即可, 比如 得到两个数的和 getSum, 开发中按照规范
  • 形参列表
  1. 方法可以有0个参数,也可以有多个参数,中间用逗号隔开, 比如:getSum(int n1,int n2)
  2. 参数类型可以为任意类型,包含基本类型或引用类型, 比如:printArr(int[]map)
  3. 调用带参数的方法时,一定对应着参数列表传入相同类型或兼容类型的参数!【getSum)
  4. 方法定义时的参数称为形式参数,简称形参;方法调用时的传入参数称为实际参数,简称实参
  5. 实参和形参的类型要一致或兼容、个数、顺序必须一致!
  • 方法体
  1. 里面写完成功能的具体的语句,可以为输入、输出、变量、运算、分支、循环、方法调用,但里面不能再定义方法!即:方法不能嵌套定义。
public class MethodDetail {
    public static void main(String[] args) {
        AA a = new AA();
        int[] res = a.getSumAndSub(2,3); //2,3实参
        System.out.println("差="  + res[0]);
        System.out.println("和="  + res[1]);
    }
}
class AA {

    //1. 一个方法最多有一个返回值 [思考,如何返回多个结果 返回数组]
    public int[] getSumAndSub(int n ,int m){ // n , m 形参
        int[] resArr = new int[2];
        resArr[0] = n - m;
        resArr[1] = n + m;
        return resArr;//可以通过返回数组来实现

//        2.返回类型可以为任意类型,包含基本类型或引用类型(数组,对象)
//        具体看 getSumAndSub

        //3. 如果方法要求有返回数据类型,则方法体中最后的执行语句必须为 return 值;
        // 而且要求返回值类型必须和 return
        public double f1(){
            double d1 = 1.1 * 3;
            int n = 100;
//            return n;
            return d1; //类型兼容

        //4.如果方法是 void,则方法体中可以没有 return 语句,或者 只写 return ;

        public void f2(){
                System.out.println("hello world");
//                return ;//可以写,但不要代值
            }

        // 5.老韩提示:在实际工作中,我们的方法都是为了完成某个功能,所以方法名要有一定含义
        // 最好是见名知意
        }
    }
}

7.2.4 方法调用的细节 2

  1. 同一个类中的方法调用:直接调用即可。 比如print(参数); 案例演示:A类sayOk 调用print()
  2. 跨类中的方法A类调用B类方法:需要通过对象名调用。 比如对象名.方法名(参数); 案例演示:B类sayHello调用print()
public class MethodDetail02 {
    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 b = new B();
        b.hi();
        System.out.println("m1() 继续执行");
    }
}

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

7.2.5 练习题 (成员方法)

import java.util.Scanner;

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


        Scanner sc = new Scanner(System.in);
        System.out.println("请输入你判断的数字");
        int num = sc.nextInt();
        AA01 a = new AA01();
        if (a.b(num)){
            System.out.println("是一个偶数");
        }else {
            System.out.println("是一个奇数");
        }

        //使用print方法
        a.print(4,4,'#');

    }
}
class AA01 {
    //        编写类 AA ,有一个方法:判断一个数是奇数 odd 还是偶数, 返回 boolean
    //思路:
    //1.方法的返回类型 boolean
    //2.方法的名字 b
    //3.方法的形参(int n)
    //4.方法体,判断
    public boolean b(int n){

//        if (n % 2 == 0){
//            return true;
//        }else {
//            return false;
//        }
//        return n % 2 == 0 ? true:false;
        return n % 2 == 0;
    }
    
    //        2) 根据行、列、字符打印 对应行数和列数的字符,比如:行:4,列:4,字符#,则打印相应的效果
    //思路:
    //1.方法的返回类型 void
    //2.方法的名字 print
    //3.方法的形参(int row ,int col,char c)
    //4.方法体,循环
    public void print(int row ,int col,char c){
        for (int i = 1; i <= row; i++){
            for (int j = 1; j <= col; j++){
                System.out.print(c + " ");
            }
            System.out.println("");
        }
    }
}

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

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

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

image-20220121160301308

public class MethodParameter01 {
    public static void main(String[] args) {
        int a = 10;
        int b = 20;
        //创建对象AA,名字obj
        AA02 obj = new AA02();
        obj.swap(a,b);
        System.out.println("main方法中的a=" + a + "b=" + b);//a=10 b=20,因为主栈中的a,b 没有发生改变,所以这里不变
    }
}

class AA02 {
    public void swap(int a, int b){
        //交换之前
        System.out.println("\na和b交换前的值\na=" + a + "\tb=" + b);//a=10	b=20

        //完成 a 和 b 的交换
        int temp = a;
        a = b;
        b = temp;
       // swap这个栈发生的变化不会影响到其他栈,所以,main方法中的a,b没有发生改变
        System.out.println("\na和b交换后的值\na=" + a + "\tb=" + b);//a=20	b=10,
    }
}

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

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

image-20220121144039567

public class MethodParameter02 {
    public static void main(String[] args) {
        BB b = new BB();
        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]);
        }

    }
}
class BB{
    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]);
        }
    }
}

3. 克隆对象

public class MethodExercise02 {
    public static void main(String[] args) {
        Person02 p = new Person02();
        p.name = "joker";
        p.age = 20;

        MyTools01 tools = new MyTools01();
        Person02 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
    }
}
class Person02{
    String name;
    int age;
}
class MyTools01{
    //        编写一个方法 copyPerson,可以复制一个 Person 对象,返回复制的对象。克隆对象, 注意要求得到新对象和原来的
//        对象是两个独立的对象,只是他们的属性相同
    //编写方法的思路
//1. 方法的返回类型 Person
//2. 方法的名字 copyPerson
//3. 方法的形参 (Person p)
//4. 方法体, 创建一个新对象,并复制属性,返回即可
    public Person02 copyPerson(Person02 p){
        //创建一个对象
        Person02 p2 = new Person02();
        p2.name = p.name;//把原来对象的名字赋给p2.name
        p2.age = p.age;//把原来对象的名字赋给p2.age
        return p2;
    }
}

4. 方法递归调用

  • 简单的说: 递归就是方法自己调用自己,每次调用时传入不同的变量.递归有助于编程者解决复杂问题,同时可以让代码变 得简洁
4.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);
        }
    }
4.2 阶乘问题
public class Recursion01 { //打印问题
    public static void main(String[] args) {
       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;
        }
    }
}
4.3 递归的重要规则
  1. 执行一个方法时,就创建一个新的受保护的独立空间(栈空间)
  2. 方法的局部变量是独立的,不会相互影响,比如n变量
  3. 如果方法中使用的是引用类型变量(比如数组,对象),就会共享该引用类型的数据.
  4. 递归必须向退出递归的条件逼近,否则就是无限递归,出现(StackOverflowError,死龟了)
  5. 当一个方法执行完毕,或者遇到return,就会返回,遵守谁调用,就将结果返回给谁,同时当方法执行完毕或者返回时,该方法也就执行完毕。
4.4 汉诺塔
public class HanoiTower {
    //编写一个 main 方法
    public static void main(String[] args) {
        Tower tower = new Tower();
        tower.move(5, '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);
        }
    }
}
4.5 迷宫
public class MiGong {
    //编写一个 main 方法
    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; //测试回溯
// map[2][1] = 1;
// map[2][2] = 1;
// map[1][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 给老鼠找路
        T0 t1 = new T0();
//下右上左
        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 T0 {
//使用递归回溯的思想来解决老鼠出迷宫
//老韩解读
//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 { //map[i][j] = 1 , 2, 3
            return false;
        }
    }
}
    //修改找路策略,看看路径是否有变化
//下->右->上->左 ==> 上->右->下->左
    public boolean findWay2(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(findWay2(map, i - 1, j)) {//先走上
                    return true;
                } else if(findWay2(map, i, j + 1)){//右
                    return true;
                } else if(findWay2(map, i+1, j)) {//下
                    return true;
                } else if(findWay2(map, i, j-1)){//左
                    return true;
                } else {
                    map[i][j] = 3;
                    return false;
                }
            } else { //map[i][j] = 1 , 2, 3
                return false;
            }
        }
    }
}

7.3 方法的重载

  1. java 中允许同一个类中,多个同名方法的存在,但要求形参列表不一致
  2. 方法名:必须相同
  3. 形参列表:必须不同(形参类型或个数或顺序,至少有一样不同参数名无要求)
  4. 返回类型:无要求

练习

public class OverLoadExercise {
    //编写一个 main 方法
    public static void main(String[] args) {
//在主类的 main ()方法中分别用参数区别调用三个方法
        Methods method = new Methods();
        method.m(10);//100
        method.m(10, 20);//200
        method.m("韩顺平教育 hello");//字符串信息
//测试
        System.out.println(method.max(10, 24)); // 24
        System.out.println(method.max(10.0, 21.4)); // 21.4
        System.out.println(method.max(10.0, 1.4, 30.0)); // 30.0
    }
}
/*
编写程序,类 Methods 中定义三个重载方法并调用。方法名为 m。
三个方法分别接收一个 int 参数、两个 int 参数、一个字符串参数。分别执行平方运算并输出结果,
相乘并输出结果,输出字符串信息。在主类的 main ()方法中分别用参数区别调用三个方法
定义三个重载方法 max(),第一个方法,返回两个 int 值中的最大值,
第二个方法,返回两个 double 值中的最大值,第三个方法,
返回三个 double 值中的最大值,并分别调用三个方法
*/
class Methods {
    //分析
//1 方法名 max
//2 形参 (int,int)
//3.int
    public int max(int n1, int n2) {
        return n1 > n2 ? n1 : n2;
    }
    //分析
//1 方法名 max
//2 形参 (double,double)
//3.double
    public double max(double n1, double n2) {
        return n1 > n2 ? n1 : n2;
    }
    //分析
//1 方法名 max
//2 形参 (double,double,double)
//3.double
    public double max(double n1, double n2, double n3) {
        System.out.println("max(double n1, double n2, double n3)");
//求出 n1 和 n2 的最大值
        double max1 = n1 > n2 ? n1 : n2;
        return max1 > n3 ? max1 : n3;
    }
    public double max(double n1, double n2, int n3) {
        System.out.println("max(double n1, double n2, int n3)");
//求出 n1 和 n2 的最大值
        double max1 = n1 > n2 ? n1 : n2;
        return max1 > n3 ? max1 : n3;
    }
    //分析
//1 方法名 m
//2 形参 (int)
//3.void
    public void m(int n) {
        System.out.println("平方=" + (n * n));
    }
//1 方法名 m
//2 形参 (int, int)
//3.void
public void m(int n1, int n2) {
    System.out.println("相乘=" + (n1 * n2));
}
    //1 方法名 m
//2 形参 (String)
//3.void
    public void m(String str) {
        System.out.println("传入的 str=" + str);
    }
}

7.4 可变参数

1.基本

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

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

  1. 可变参数的实参可以为0个或任意多个。
  2. 可变参数的实参可以为数组。
  3. 可变参数的本质就是数组.
  4. 可变参数可以和普通类型的参数一起放在形参列表,但必须保证可变参数在最后
  5. 一个形参列表中只能出现一个可变参数
public class VarParameterDetail {
    //编写一个 main 方法
    public static void main(String[] args) {
//细节: 可变参数的实参可以为数组
        int[] arr = {1, 2, 3};
        TA t1 = new TA();
        t1.f1(arr);
    }
}
class TA {
    public void f1(int... nums) {
        System.out.println("长度=" + nums.length);
    }
    //细节: 可变参数可以和普通类型的参数一起放在形参列表,但必须保证可变参数在最后
    public void f2(String str, double... nums) {
    }
//细节: 一个形参列表中只能出现一个可变参数
//下面的写法是错的. 
// public void f3(int... nums1, double... nums2) {
// }
}

练习:

public class VarParameterExercise {
    //编写一个 main 方法
    public static void main(String[] args) {
        HspMethod hm = new HspMethod();
        System.out.println(hm.showScore("milan" , 90.1, 80.0 ));
        System.out.println(hm.showScore("terry" , 90.1, 80.0,10,30.5,70 ));
    }
}
class HspMethod {
    /*
    有三个方法,分别实现返回姓名和两门课成绩(总分),
    返回姓名和三门课成绩(总分),返回姓名和五门课成绩(总分)。
    封装成一个可变参数的方法
    */
//分析 1. 方法名 showScore 2. 形参(String ,double... ) 3. 返回 String
//听课小伙伴,老师要求必须自己动手写
    public String showScore(String name,double...score){
                            double totalScore = 0;
                            for(int i = 0; i < scores.length; i++) {
                                totalScore += scores[i];
                            }
                            return name + " 有 " +scores.length + "门课的成绩总分为=" + totalScore;
    }
}

7.5 作用域(Scope)(重要)

1. 基本使用

  1. 在java编程中,主要的变量就是属性(成员变量)和局部变量。
  2. 我们说的局部变量一般是指在成员方法中定义的变量。【举例Cat类: cry)java中作用域的分类
  3. 全局变量:也就是属性,作用域为整个类体Cat类:cry eat等方法使用属性【举例】
  4. 局部变量:也就是除了属性之外的其他变量,作用域为定义它的代码块中!
  5. 全局变量(属性)可以不赋值,直接使用,因为有默认值局部变量必须赋值后,才能使用,因为没有默认值。[举例】
public class VarScope {
    //编写一个 main 方法
    public static void main(String[] args) {
    }
}
class Cat {
    //全局变量:也就是属性,作用域为整个类体 Cat 类:cry eat 等方法使用属性
//属性在定义时,可以直接赋值
    int age = 10; //指定的值是 10
    //全局变量(属性)可以不赋值,直接使用,因为有默认值,
    double weight; //默认值是 0.0
    public void hi() {
//局部变量必须赋值后,才能使用,因为没有默认值
        int num = 1;
        String address = "北京的猫";
        System.out.println("num=" + num);
        System.out.println("address=" + address);
        System.out.println("weight=" + weight);//属性
    }
    public void cry() {
//1. 局部变量一般是指在成员方法中定义的变量
//2. n 和 name 就是局部变量
//3. n 和 name 的作用域在 cry 方法中
        int n = 10;
        String name = "jack";
        System.out.println("在 cry 中使用属性 age=" + age);
    }
    public void eat() {
        System.out.println("在 eat 中使用属性 age=" + age);
//System.out.println("在 eat 中使用 cry 的变量 name=" + name);//错误
    }
}

2. 细节

  1. 属性和局部变量可以重名,访问时遵循就近原则。
  2. 在同一个作用域中,比如在同一个成员方法中,两个局部变量,不能重名。[举例]
  3. 属性生命周期较长,伴随着对象的创建而创建,伴随着对象的销毁而销毁。
  4. 局部变量,生命周期较短,伴随着它的代码块的执行而创建,伴随着代码块的结束而销毁.即在一次方法调用过程中。
  5. 作用域范围不同
    全局变量/属性:可以被本类使用,或其他类使用(通过对象调用)
  6. 局部变量:只能在本类中对应的方法中使用
    修饰符不同
    全局变量/属性可以加修饰符局部变量不可以加修饰符
public class VarScopeDetail{
    //编写一个 main 方法
    public static void main(String[] args) {
        Person p1 = new Person();
/*
属性生命周期较长,伴随着对象的创建而创建,伴随着对象的销毁而销毁。
局部变量,生命周期较短,伴随着它的代码块的执行而创建,
伴随着代码块的结束而销毁。即在一次方法调用过程中
*/
//p1.say();//当执行 say 方法时,say 方法的局部变量比如 name,会创建,当 say 执行完毕后
//name 局部变量就销毁,但是属性(全局变量)仍然可以使用
//
        TAC t1 = new TAC();
        t1.test(); //第 1 种跨类访问对象属性的方式
        t1.test2(p1);//第 2 种跨类访问对象属性的方式
    }
}
class TAC {
    //全局变量/属性:可以被本类使用,或其他类使用(通过对象调用)
    public void test() {
        Person p1 = new Person();
        System.out.println(p1.name);//jack
    }
    public void test2(Person p) {
        System.out.println(p.name);//jack
    }
}
class Person {
    //细节: 属性可以加修饰符(public protected private..)
// 局部变量不能加修饰符
    public int age = 20;
    String name = "jack";
    public void say() {
//细节 属性和局部变量可以重名,访问时遵循就近原则
        String name = "king";
        System.out.println("say() name=" + name);
    }
    public void hi() {
        String address = "北京";
//String address = "上海";//错误,重复定义变量
        String name = "hsp";//可以
    }
}

image-20220125160027245

7.6 构造器

7.6.1 基本语法

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

7.6.3 基本介绍

构造方法又叫构造器(constructor),是类的一种特殊的方法,它的主要作用是完成对新对象的初始化。它有几个特点:

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

7.6.4 快速入门

public class Constructor01 {
    //编写一个 main 方法
    public static void main(String[] args) {
//当我们 new 一个对象时,直接通过构造器指定名字和年龄
        Person002 p1 = new Person002("smith", 80);
        System.out.println("p1 的信息如下");
        System.out.println("p1 对象 name=" + p1.name);//smith
        System.out.println("p1 对象 age=" + p1.age);//80
    }
}
//在创建人类的对象时,就直接指定这个对象的年龄和姓名
//
class Person002 {
    String name;
    int age;

    //构造器
//老韩解读
//1. 构造器没有返回值, 也不能写 void
//2. 构造器的名称和类 Person 一样
//3. (String pName, int pAge) 是构造器形参列表,规则和成员方法一样
    public Person002(String pName, int pAge) {
        System.out.println("构造器被调用~~ 完成对象的属性初始化");
        name = pName;
        age = pAge;
    }
}

7.6.5 构造器的细节

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

        Dog dog1 = new Dog();//使用的是默认的无参构造器

    }
}
class Person006{
    String name;
    int age;//默认值 = 0
    //第一个构造器
    public Person006(String pName, int pAge){
        name = pName;
        age = pAge;
    }
    //第二个构造器,只指定人名
    public Person006(String pName){
        name = pName;
    }
}
class Dog {
    //如果程序员没有定义构造器,系统会自动给类生成一个默认无参构造器(也叫默认构造器)
    //使用 javap 指令 反编译看看
/*
默认构造器
Dog() {
}
*/
    //一旦定义了自己的构造器,默认的构造器就覆盖了,就不能再使用默认的无参构造器,
    //除非显式的定义一下,即: Dog(){} 写 (这点很重要)
//
    public Dog(String dName) {
//... }
        Dog() { //显式的定义一下 无参构造器
        }
    }

7.7 This关键字

7.7.1 This关键字细节

  1. this 关键字可以用来访问本类的属性、方法、构造器

  2. this 用于区分当前类的属性和局部变量

  3. 访问成员方法的语法:this.方法名(参数列表);

  4. 访问构造器语法:this(参数列表); 注意只能在构造器中使用(即只能在构造器中访问另外一个构造器, 必须放在第一 条语句)

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

public class ThisDetail {
    public static void main(String[] args) {
        T2 t2 = new T2();
    }
}
class T2{
    String name = "jack";
    int num = 100;

//    细节4:访问构造器语法:this(参数列表); 注意只能在构造器中使用
//    (即只能在构造器中访问另外一个构造器, 必须放在第一 条语句)
    public T2(){
        //这里去访问 T2(String name, int age).
        //注意:即只能在构造器中访问另外一个构造器, 必须放在第一 条语句
        this("jack",20);
        System.out.println("T() 构造器");
    }
    public T2(String name, int age){
        System.out.println("T2(String name, int age) 构造器");
    }
//    细节3:访问成员方法的语法:this.方法名(参数列表);
    public void f1(){
        System.out.println("f1() 方法。");
    }
    public void f2(){
        System.out.println("f2() 方法。");
        //调用本类的f1
        //第一种方式
        f1();
        //第二种方式
        this.f1();
    }
// 细节1:this 关键字可以用来访问本类的属性、方法、构造器
    public void f3(){
        System.out.println("name=" + this.name + "num=" + this.num);
    }
}

7.7.2 练习

public class TestPerson {
    //编写一个 main 方法
    public static void main(String[] args) {
        Person p1 = new Person("mary", 20);
        Person p2 = new Person("mary", 20);
        System.out.println("p1 和 p2 比较的结果=" + p1.compareTo(p2));
    }
}
/*
定义 Person 类,里面有 name、age 属性,并提供 compareTo 比较方法,
用于判断是否和另一个人相等,提供测试类 TestPerson 用于测试, 名字和年龄完全一样,就返回 true, 否则返回 false
*/
class Person {
    String name;
    int age;
    //构造器
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    //compareTo 比较方法
    public boolean compareTo(Person p) {
//名字和年龄完全一样
// if(this.name.equals(p.name) && this.age == p.age) {
// return true;
// } else {
// return false;
// }
        return this.name.equals(p.name) && this.age == p.age;
    }
}

image-20220125221654996

总结:

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

本章作业

public class Homework01 {
    public static void main(String[] args) {
        A01 a01 = new A01();
        double[] arr = {1.0,5.6,5.7};
        Double res = a01.max(arr);
        if (res != null) {
            System.out.println(a01.max(arr));
        } else {
            System.out.println("arr的输入有误");
        }
    }
}
/*
编写类A01,定义方法max,实现求某个double数组的最大值,并返回

思路分析
1.类名 A01
2.方法名 max
3.形参 (double[])
4.返回值 double
 */
class A01{
    public Double max(double[] num){
        //先判断数组是否为空,再判断
        if ( num != null && num.length > 0) {
            double max = num[0];//假设第一个为最大值
            for (int i = 0; i < num.length; i++) {
                if (max < num[i]) {
                    max = num[i];
                }
            }
            return max;
        }else{
            return null;
        }
    }
}
public class Homework02 {
    public static void main(String[] args) {
        String[] strs = {"jack", "tom","mary","milan"};
        A02 a02 = new A02();
        int index = a02.find("tom",strs);
        if (index == -1) {
            System.out.println("没有找到");
        } else {
            System.out.println(index);
        }

    }
}
//2.编写类A02,定义方法find,实现查找某字符串是否在字符串数组中,并返回索引,如果找不到,返回-1. Homework02.java

class A02{
    public int find(String findStr, String[] strs) {
        //直接遍历字符串数组,找到,返回索引值

            for (int i = 0; i < strs.length; i++) {
                if (findStr.equals(strs[i])) {
                    return i;
                }
            }
            return -1;
        }
    }
public class Homework03 {
    public static void main(String[] args) {
        Book book = new Book("笑傲江湖",30);
        book.info();
        book.updatePrice();
        book.info();
    }
}
//3.编写类Book,定义方法updatePrice,实现更改某本书的价格,具体:
//如果价格>150,则更改为150,如果价格>100,更改为100,否则不变HomeworkO3.java-定要自己写代码
//分析
/*
1.类名 Book
2.属性 price name
3.方法名 updatePrice
4.形参()
5。返回值,void
 */
class Book{
    String name;
    int price;
    public Book(String name, int price){
        this.name = name;
        this.price = price;
    }
    public void updatePrice(){
        //如果方法中,没有 price 局部变量,this.price 等价 price
        if (this.price > 150){
            this.price = 150;
        }else if (this.price > 100){
            this.price = 100;
        }
    }
    //显示书籍情况
    public void info(){
        System.out.println("书名=" + this.name + " " + "价格=" + this.price);
    }
}
public class Homework04 {
    public static void main(String[] args) {
        int[] oldArr = {2,5,6,7};
        A03 a03 = new A03();
        int[] res = a03.copeArr(oldArr);
        for (int i = 0; i < res.length; i++){
            System.out.println(res[i]);
        }

    }
}
//4.编写类AO3,实现数组的复制功能copyArr,输入旧数组,返回一个新数组,元素和旧数组一样Homework04.java
class A03{
    public int[] copeArr(int[] oldArr){
        System.out.println("调用此方法");
        int[] newArr = new int[oldArr.length];
        for (int i = 0; i < oldArr.length; i++){
            newArr[i] = oldArr[i];
        }
        return newArr;
    }
}
public class Homework05 {
    public static void main(String[] args) {
        Circle circle = new Circle(5);
        System.out.println("周长为:" + circle.c());
        System.out.println("面积为:" + circle.s());
    }
}
//5.定义一个圆类Circle,定义属性:半径,提供显示圆周长功能的方法,提供显示圆面积的方法HomeworkO5.java
class Circle{
    double r;
    public Circle(double r){
        this.r = r;
    }
    public double c(){
        double num = 2 * Math.PI * r;
        return num;
    }
    public double s(){
        double num = r * r * Math.PI;
        return num;
    }
}
public class Homework06 {
    public static void main(String[] args) {
        Cale cale = new Cale(5, 0);
        System.out.println("两数相加=" + cale.sum());
        System.out.println("两数相减=" + cale.minus());
        System.out.println("两数相乘=" + cale.multiply());
        Integer divRes = cale.div();
        if (divRes != null) {
            System.out.println("两数相除=" + cale.div());
        }

    }
}
//6.编程创建一个Cale计算类,在其中定义2个变量表示两个操作数,
//定义四个方法实现求和、差、乘、商(要求除数为0的话,要提示)并创建两个对象,分别测试
class Cale{
    int a;
    int b;
    public Cale(int a,int b){
        this.a = a;
        this.b = b;
    }
    public int sum(){
        return a + b;
    }
    public int minus(){
        return a - b;
    }
    public int multiply(){
        return a * b;
    }
    public Integer div() {
        //判断
        if (b == 0) {
            System.out.println("除数不能为0");
            return null;
        } else {
            return a / b;
        }
    }
}

public class Homework07 {
    public static void main(String[] args) {
        Dog01 dog01 = new Dog01("小花", 2, "花色");
        dog01.show();
    }
}
class Dog01{
    String name;
    int age;
    String color;
    public Dog01(String name, int age, String color){
        this.name = name;
        this.age = age;
        this.color = color;
    }
    public void show(){
        System.out.println("狗名是=" + name + "年龄是=" + age + "颜色是=" + color );
    }
}

public class Homework09 {
    public static void main(String[] args) {
        Music music = new Music("哈哈哈", 36);
        music.play();
        System.out.println(music.getInfo());

    }
}
//9.定义Music类,里面有音乐名name、音乐时长times属性,
// 并有播放play功能和返回本身属性信息的功能方法getInfo. Homework09.java
class Music{
    String name;
    double times;

    public Music(String name,double times){
        this.name = name;
        this.times = times;
    }
    public void play(){
        System.out.println("音乐" + name + "正在播放中……时长" + times + "秒");
    }
    public String getInfo(){
        return "音乐" + name + "播放时间为" + times;
    }
}
class Demo {
    int i = 100;
    public void m() {
        int j = i++;
        System.out.println("i=" + i);//101
        System.out.println("j=" + j);//100
    }
}
class Homework10 {
    public static void main(String[] args) {
        Demo d1 = new Demo();
        Demo d2 = d1;//把d1 值赋给d2
        d2.m();
        //下面两句话指向的都是同一个对象
        System.out.println(d1.i);//101
        System.out.println(d2.i);//101
    }
}

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

    }
}
//12.创建一个Employee类,属性有(名字,性别,年龄,职位,薪水),提供3个构造方法,
//可以初始化(1)(名字,性别,年龄,职位,薪水),(2)(名字,性别,年龄)(3)(职位,薪水).要求充分复用构造器Homework12.java
class Employee{
    String name;
    char gender;
    int age;
    String posts;//职位
    double salary;

    public Employee(String name, char gender,int age){
        this.name = name;
        this.gender = gender;
        this.age = age;
    }
    public Employee(String posts,double salary){
        this.posts = posts;
        this.salary = salary;
    }
    public Employee(String name, char gender,int age,String posts,double salary){
        this(name,gender,age);//这就是复用构造器,用this调用别的构造器,必须排在第一位,即后面不能在调用其他的构造器
        this.posts = posts;
        this.salary = salary;
    }
}

猜拳

public class Homework13 {
    public static void main(String[] args) {
        Circle02 circle = new Circle02();
        PassObject po = new PassObject();
        po.printAreas(circle,5);
    }
}
/*
13.将对象作为参数传递给方法。Homework13.java题目要求:
        (1)定义一个Circle类,包含一个double型的radius属性代表圆的半径,findArea()方法返回圆的面积。
        (2)定义一个类PassObject,在类中定义一个方法printAreas(),该方法的定义如下:
         public void printAreas(Circle c, int times)//方法签名/声明
        (3)在printAreas方法中打印输出1到times之间的每个整数半径值,以及对应的面积。例如,times为5,则输出半径1,2,3,4,5,以及对应的圆面积。
        (4)在main方法中调用printAreas()方法,调用完毕后输出当前半径值。程序运行结果如图所示,
        老韩提示,如果还是不明确,自己画一个内存图,一定可以搞定
*/
class Circle02{ //类
    double radius; //半径
    public Circle02(){ //无参构造器

    }
    public Circle02(double radius){
        this.radius = radius;
    }
    public double findArea(){
        return radius * radius * Math.PI;
    }
    //添加一个方法setRadius,修改对象的半径值
    public void setRadius(double radius){
        this.radius = radius;
    }
}
class PassObject{
    public void printAreas(Circle02 c,int times){
        System.out.println("radius\tarea");
        for ( int i = 1; i <= times; i++){
            c.setRadius(i);
            System.out.println((double) i + "\t" + c.findArea());

        }
    }
}
import java.util.Random;
import java.util.Scanner;

public class Homework14 {
    public static void main(String[] args) {
        //创建一个玩家对象
        Tom t = new Tom();
        //用来记录最后输赢的次数
        int isWinCount = 0;

        //创建二维数组,用来接收局数,Tom出拳情况及电脑出拳情况
        int[][] arr1 = new int[3][3];
        int j = 0;

        //创建一个一维数组,用来接受输赢情况
        String[] arr2 = new String[3];

        //获取玩家的出拳
        Scanner sc = new Scanner(System.in);
        for (int i = 0; i < 3; i++) {
            System.out.println("数字0-表示石头,数字1-表示剪刀,数字2-表示布");
            System.out.println("请输入你的数字");
            int tomNum = sc.nextInt();
            t.setTomGuessNum(tomNum);
            int tomGuess = t.getTomGuessNum();
            arr1[i][j + 1] = tomGuess;


            //获取电脑的出拳
            int comGuess = t.computerNum();
            arr1[i][j + 2] = comGuess;

            //将玩家猜的拳与电脑做对比
            String isWin = t.vsComputer();
            arr2[i] = isWin;
            arr1[i][j] = t.count;

            //对每一局的情况进行输出
            System.out.println("===========================");
            System.out.println("局数\t玩家的出拳\t电脑的出拳\t输赢情况");
            System.out.println(t.count + "\t" + tomGuess + "\t\t" + comGuess + "\t\t" + isWin);
            System.out.println("===========================");
            System.out.println("\n\n");
            isWinCount = t.winCount(isWin);
        }

        //对游戏的最终结果进行输出
        System.out.println("局数\t玩家出拳\t电脑出拳\t\t输赢情况");
        for (int a = 0; a < arr1.length; a++){
            for (int b = 0; b < arr1[a].length; b++){
                System.out.println(arr1[a][b] + "\t\t\t");
            }

            System.out.println(arr2[a]);
            System.out.println();
        }
        System.out.print("你赢了" + isWinCount + "次");

    }
}
/*
思路
1.随机生成0,1,2
 */
class Tom{
    //玩家出拳
    int tomGuessNum;
    //电脑出拳
    int comGuessNum;
    //赢的次数
    int winCountNum;
    //比赛的次数
    int count = 1;//一共比赛三次

    //电脑随机猜拳的数字的方法
    public int computerNum(){
        Random r = new Random();
        comGuessNum = r.nextInt(3);// 方法 返回 0 - 2的随机数
        return comGuessNum;
    }
    //规定玩家的玩法
    public void setTomGuessNum(int tomGuessNum){
        if (tomGuessNum > 2 || tomGuessNum < 0){
            throw new IllegalArgumentException("数字输入有误");
        }
        this.tomGuessNum = tomGuessNum;
    }
    public int getTomGuessNum(){
        return tomGuessNum;
    }
    //比较
    public String vsComputer(){
        if (tomGuessNum == 0 && comGuessNum == 1){
            return "你赢了";
        }else if (tomGuessNum == 1 && comGuessNum ==2) {
            return "你赢了";
        }else if (tomGuessNum == 2 && comGuessNum ==0) {
            return "你赢了";
        }else if (tomGuessNum == comGuessNum){
            return "平手";
        }else {
            return "你输了";
        }
    }
    public int winCount(String s){
        count++;
        if (s.equals("你赢了")){
            winCountNum++;
        }
        return winCountNum;
    }
}
posted @ 2022-02-02 20:39  Joker-bea  阅读(63)  评论(0编辑  收藏  举报