四、面向对象编程(基础)

一、类与对象

1、入门案例

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

public class object01 {
    public static void main(String[] args) {
        /*
        1. 如果使用单独的变量解决
            只有两只猫可以轻松解决,但是如果有上千只猫呢?
            如果1000只猫突然要增加体重属性、爱好属性呢?    
            此时发现,使用单独变量解决,不利于数据的管理(把一只猫的信息拆解)
         */
        // 第一只毛的信息
        String cat1Name = "小白";
        int cat1Age = 3;
        String cat1Color = "白色";
        
        // 第二只猫的信息
        String cat2Name = "小花";
        int cat2Age = 100;
        String cat2Color = "花色";
        
        /*
        2. 如果使用数组解决,有以下问题
            a.数据类型无法提现(比如年龄,体重)
            b.获取信息只能通过下标,造成变量名和内容的对应关系不明确
            c.不能提现猫的行为(如:捕猎,编写java)
            d.追加新属性过程繁琐
         */
        // 第一只猫信息
        String[] cat1 = {"小白","3","白色"};
        String[] cat2 = {"小花","100","花色"};
    }
}

以上技术缺点:

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

==>类与对象
一个程序就是一个世界,这个世界有很多事物(对象[ 属性,行为 ])
把猫的所有特性(属性)提取出来,就形成了一个猫的类,这个类就是一种自定义的数据类型。
Java中int类型的数据,我们可以定义一个值为100的int,也可以定义一个值为0的int。同理,我们可以通过猫类,创建一只布偶猫、也可以创建一只英短猫。在人’类’中,可以创建一个亚洲人,也可以创建一个欧洲人。
在这里插入图片描述
在这里插入图片描述

public class object01 {
    public static void main(String[] args) {
        // 使用OOP解决
        // 实例化一只猫(创建一只猫对象)
        // 1. new Cat() 创建一只猫
        // 2. Cat RagdollCat = new Cat(); 把创建的猫赋给RagdollCat
        // 3. ragdollCat就是Cat的一个对象
        Cat ragdollCat = new Cat();
        ragdollCat.name = "小白";
        ragdollCat.age = 3;
        ragdollCat.color = "白色";
        // 创建第二只猫,并赋给maineCat
        Cat maineCat = new Cat();
        maineCat.name = "小花";
        maineCat.age = 100;
        maineCat.color = "粉色";

        // 怎么访问对象的属性
        System.out.println("布偶猫的信息:" + ragdollCat.name +
                " " + ragdollCat.age + "岁 " + ragdollCat.color);
        System.out.println("缅因猫的信息:" + maineCat.name +
                " " + maineCat.age + "岁 " + maineCat.color);
    }
}

// 使用面向对象的方式解决养猫问题
// 定义一个猫类 Cat -> 自定义的数据类型
class Cat {
    // 属性
    String name;
    int age;
    String color;

    // 行为……
}

布偶猫的信息:小白 3岁 白色
缅因猫的信息:小花 100岁 粉色

2、类和对象的区别和联系

通过上面的案例和讲解我们可以看出:
1) 类是抽象的,概念的,代表一类事物,比如人类,猫类…,即它是数据类型。
2) 对象是具体的,实际的,代表一个具体事物,即 是实例
3) 类是对象的模板,对象是类的一个个体,对应一个实例

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

在这里插入图片描述
(注:从jdk1.7开始,字符串常量池已经从方法区移至堆中)

4、属性/成员变量

基本介绍

  1. 从概念或叫法上看:成员变量 = 属性 = field / 字段(即 成员变量是用来表示属性
    的)

    案例演示:Car(name,price,color)
public class object02 {
    public static void main(String[] args) {   }
}
class Car{
    // 属性/成员变量/字段(field)
    String name;    
    double price;
    String color;
}
  1. 属性是类的一个组成部分
    一般是基本数据类型,也可是引用类型(对象,数组)。
    比如我们前面定义猫类 的 int age 就是属性

注意事项:

  1. 属性的定义语法和变量一样,示例:访问修饰符 属性类型 属性名;
  2. 属性的定义类型可以为任意类型,包含基本类型或引用类型
  3. 属性如果不赋值,有默认值,规则和数组一致。
    案例演示:[Person类】
public class PropertiesDetail {
    public static void main(String[] args) {
        // 创建Person对象(new Person()创建出来的对象空间(数据)
        // 是真正的对象。p1只是指向这个地址的对象引用,又叫对象名
        Person p1 = new Person();

        // 对象的属性默认值,遵守数组默认值规则
        System.out.println("此人信息:");
        System.out.println("name = " + p1.name);
        System.out.println("age = " + p1.age);
        System.out.println("salary = " + p1.salary);
        System.out.println("isPass = " + p1.isPass);
    }
}
class Person{
    String name;
    int age;
    double salary;
    boolean isPass;
}

此人信息:
name = null
age = 0
salary = 0.0
isPass = false

如何创建对象:

  1. 先声明再创建:
    Cat cat;                  // 声明对象cat
    cat = new Cat();     //创建
  2. 直接创建
    Cat cat = new Cat();

类和对象的内存分配机制

  1. 栈:一般存放基本数据类型(局部变量)
  2. 堆:存放对象(Cat cat,数组等)
  3. 方法区:常量池(常量,比如字符串),类加载信息
    在这里插入图片描述
    在这里插入图片描述
    例题:计算以下代码输出结果
    在这里插入图片描述
    b = null ==> 将b置空,即b对象失去了它在堆中的age与name,程序报错:NullPointerException.

二、成员方法

1、基本介绍

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

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

public class Method01 {
    public static void main(String[] args) {
        /*
        speak方法的使用:
        1、方法定义好后,如果不去调用,就不会有任何结果
        2、先创建对象,然后调用方法即可
         */
        Person1 person1 = new Person1();
        person1.speak();    //调用方法
        person1.cal01();
        person1.cal02(666);
        int returnRes = person1.getSum(1,2);
        System.out.println("getSum方法返回值:" + returnRes);
    }
}

class Person1{
    String name;
    int age;
    /*
    方法(成员方法)
    public:表示方法是公开的
    void:表示方式没有返回值
    speak():speak是方法名,()是形参列表
    {}:方法体,编写执行的代码
     */
    public void speak(){
        System.out.println("我是一个好银");
    }
    //添加cal01成员方法,可以计算从1+……+1000的结果
    public void cal01(){
        int sum = 0;
        for (int i = 1; i <= 1000 ; i++) {
            sum += i;
        }
        System.out.println("1 + …… + 1000 = " + sum);
    }
    //添加cal02 成员方法,该方法可以接收一个数n,计算从 1+……+n 的结果
    //(int n):形参列表,表示当前有一个形参,可以接收用户输入
    public void cal02(int n){
        int sum = 0;
        for (int i = 1; i <= n ; i++) {
            sum += i;
        }
        System.out.println("1 + …… + " + n + " = " + sum);
    }
    //添加getSum成员方法,可以计算两个数的和
    //public int 中的int:表示方法执行后返回一个int值
    //return sum:表示把sum的值返回
    public int getSum(int num1,int num2){
        int sum = num1 + num2;
        return sum;
    }
}

我是一个好银
1 + …… + 1000 = 500500
1 + …… + 666 = 222111
getSum方法返回值:3

2、方法的调用机制原理(重点)

程序执行过程[cal02]+说明:
在这里插入图片描述
getSum方法中的return语句执行后,“ getSum栈 ” 就被回收(销毁)了

3、方法的优点:

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

public class Method02 {
    public static void main(String[] args) {
        //请遍历一个数组,输出数组的各个元素值
        int[][] arr = {{0, 0, 0},{0, 1, 0},{0, 0, 1}};
        //传统解决方式
        for (int i = 0; i < arr.length; i++) {
            for (int j = 0; j < arr[i].length; j++) {
                System.out.print(arr[i][j] + "\t");
            }
            System.out.println();
        }

        //要求再次遍历:需要写重复的代码
        //以下使用方法
        System.out.println("使用方法完成输出");
        MyTools myTools = new MyTools();
        myTools.printArray(arr);
    }
}
//把输出的功能,写到一个类的方法中,然后调用该方法即可
class MyTools{
    //方法,接收一个二维数组
    public void printArray(int[][] arr){
        for (int i = 0; i < arr.length; i++) {
            for (int j = 0; j < arr[i].length; j++) {
                System.out.print(arr[i][j] + "\t");
            }
            System.out.println();
        }
    }
}

4、注意点

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

  1. 访问修饰符: 控制方法的使用范围,如果不写,则是默认访问修饰符。有四种(public、protected、默认、private)
  2. 返回数据类型:
    • 一个方法最多有一个返回值(可以通过数组返回多个结果)
    • 返回类型可以为任意类型,包含基本类型或引用类型(数组,对象)
    • 如果方法要求有返回数据类型,则方法体中最后的执行语句必须为return 值;而
      且要求返回值类型必须和return的值类型一致或兼容
    • 如果方法是void,则方法体中可以没有return语句,或者只写return;
    • 方法名:遵循驼峰命名法,最好见名知义,表达出该功能的意思即可,比如 得到两个数的和::getSum,开发中按照规范
  3. 参数列表: 表示成员方法输入 public void cal( int n ) {   }
    • 一个方法可以有0个参数,也可以有多个参数,中间用逗号隔开,比如 getSum (int n1,int n2)
    • 参数类型可以为任意类型,包含基本类型或引用类型,比如 printArr(int[][] map)
    • 调用带参数的方法时,一定对应着参数列表传入相同类型或兼容类型的参数!
    • 方法定义时的参数称为形式参数,简称形参;方法调用时传入
    • 的参数称为实际参数,简称实参,实参和形参的类型要一致或兼容个数、顺序必须一致
  4. 数据类型 (返回类型) : 表示成员方法输出,void 表示没有返回值
  5. 方法主体: 表示为了实现某一功能代码块
    • 里面写完成功能的具体的语句,可以为输入、输出、变量、运算、分支、循环、方法调用,但里面不能再定义方法!即:方法不能嵌套定义
  6. 方法调用细节
    • 一个类中的方法调用:直接调用即可。比如 print(参数);
    • 跨类中的方法A类调用B类方法:需要先创建B类的对象,通过对象名调用。比如 对象名 . 方法名(参数);
  7. return 语句不是必须的
public class MethodDetail {
    public static void main(String[] args) {
        //一个方法最多有一个返回值=>使用数组,返回多个结果
        AA aa = new AA();
        int[] sumAndSub = aa.getSumAndSub(1, 4);
        System.out.println(Arrays.toString(sumAndSub));
    }
}
class AA{
    public int[] getSumAndSub(int n1,int n2){
        int[] arr = new int[2];
        arr[0] = n1 + n2;
        arr[1] = n1 - n2;
        return arr;
    }
    public double returnDouble(){
        int n = 1;
        return n;   //返回int,可以自动转换为double
    }
}

[5, -3]
0 0 0
0 1 0
0 0 1
使用方法完成输出
0 0 0
0 1 0
0 0 1

/*
编写类AAA,有一个方法:
判断一个数奇偶性,偶数返回true,奇数反之
 */
public class MethodExercise01 {
    public static void main(String[] args) {
        AAA aaa = new AAA();
        if (aaa.isOdd(1)){
            System.out.println("奇数");
        } else {
            System.out.println("偶数");
        }
    }
}
class AAA{
    public boolean isOdd(int num){
        return num % 2 != 0;
    }
}

奇数

/*
根据行、列、字符打印 对应行数和列数的字符,
如:行:4,列:4,字符#,则打印相应的效果
    ####
    ####
    ####
    ####
 */
public class MethodExcercise02 {
    public static void main(String[] args) {
        Print print = new Print();
        print.printChar(6,6,'我');
    }
}
class Print{
    public void printChar(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();
        }
    }
}

我我我我我我
我我我我我我
我我我我我我
我我我我我我
我我我我我我
我我我我我我

三、成员方法传参机制

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

public class MethodParameter01 {
    public static void main(String[] args) {
        int a = 10;
        int b = 1;
        // 创建AABB对象
        AABB aabb = new AABB();
        aabb.swap(a, b);
        System.out.println("HELLO: a="+a+"\tb="+b);
    }
}
class AABB{
    public void swap(int a,int b){
        System.out.println("a和b交换前的值:a="+a+"\tb="+b);
        int temp = a;
        a = b;
        b = temp;
        System.out.println("a和b交换后的值:a="+a+"\tb="+b);
    }
}

a和b交换前的值:a=10 b=1
a和b交换后的值:a=1 b=10
HELLO: a=10 b=1

基本数据类型传递的是值,可以理解为将一个文件复制到了桌面上,桌面上的文件修改不会影响到原来的文件

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

	/*
在BBAA类中写一个方法test100,可以接收一个数组,在方法中
修改该数组,查看原来的数组是否发生变化
 */
public class MethodParameter02 {
    public static void main(String[] args) {
        BBAA bbaa = new BBAA();
        int[] arr = {0, 0, 0};
        bbaa.test100(arr);
        System.out.println("mian的数组:" + Arrays.toString(arr));

		Personnn personnn = new Personnn();
        personnn.name = "pig";
        personnn.age = 1;
        bbaa.test200(personnn);
        System.out.println("main的age:" + personnn.age);
    }
}
class BBAA{
    public void test100(int[] arr){
        arr[0] = 1;
        System.out.println("test100的数组:" + Arrays.toString(arr));
    }

	public void test200(Personnn personnn){
        personnn.age = 152;     //修改对象属性
        System.out.println("test200的age:" + personnn.age);
        //personnn = null;-->不会影响原来的对象,置空的是自己的地址
    }
}

test100的数组:[1, 0, 0]
mian的数组:[1, 0, 0]
test200的age:152
main的age:152

引用数据类型传递的是地址,可以通过形参影响实参

public class MethodExercise03 {
    public static void main(String[] args) {
    	/*
   		编写一个方法copyPerson,可以复制一个PersonA对象,
    	返回复制的对象。克隆对象,要求得到新对象和原来的对象是
    	两个独立的对象,只是它们属性相同
     	*/
        PersonA p = new PersonA();
        p.name = "ha";
        p.age = 18;
        //创建tool
        MyTool myTool = new MyTool();
        PersonA p2 = myTool.copyPerson(p);

        //至此,p和p2是两个属性相同的独立对象
        System.out.println("p的属性:age="+p.age+";name="+p.name);
        System.out.println("p2的属性:age="+p2.age+";name="+p2.name);
        System.out.println("p和p2是否为同一对象:"+(p==p2));
    }
}

class PersonA{
    String name;
    int age;
}

class MyTool{
    public PersonA copyPerson(PersonA pp){
        //创建一个新对象
        PersonA personA1 = new PersonA();
        //把原来对象personA的name赋给personA1
        personA1.name = pp.name;
        //把原来对象personA的age赋给personA1
        personA1.age = pp.age;
        return personA1;
    }
}

p的属性:age=18;name=ha
p2的属性:age=18;name=ha
p和p2是否为同一对象:false

四、递归(recursion)

递归就是方法自己调用自己,每次调用时传入不同的变量,递归有助于解决复杂问题


public class Recursion01 {
    public static void main(String[] args) {
        T t = new T();
        t.test(5);
        int factorial = t.factorial(5);
        System.out.println("factorial= " + factorial);
    }
}
class T{
    public void test(int n){
        if ( n > 2 ){
            test(n - 1);
        }
        System.out.println("n= "+n);
    }

    //factorial:阶乘
    public int factorial(int n){
        if (n == 1){
            return 1;
        } else{
            return factorial(n-1)*n;
        }
    }
}

n= 2
n= 3
n= 4
n= 5

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

1、斐波那契数列

给定一个整数n,使用递归方法,求出其斐波那契数
斐波那契数列(1,1,2,3,5,8……):第三位开始,每一位的值前两个数的和

public class RecursionEx01 {
    public static void main(String[] args) {
        Test test=new Test();
        int i = 11;
        int fibonacci = test.fibonacci(i);
        if(fibonacci != -1){
            System.out.println("当n="+ i +"时,fibonacci为:"+fibonacci);
        }
    }
}
class Test{
    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;
        }
    }
}

当n=11时,fibonacci为:89

2、猴子吃香蕉

若干香蕉,猴子第一天吃掉一半,且再多吃一个。每日如此。
第10天时只有一个香蕉(当日未食)。求共有多少香蕉
day10: 1
day9: (day10+1) * 2 = 4
day8: (day9+1) * 2 = 10
即:前一天的香蕉数量=(后一天数量+1)* 2

public class RecursionEx02 {
    public static void main(String[] args) {
        Test2 test2=new Test2();
        int day = 1;
        int banana = test2.banana(day);
        if(banana != -1){
            System.out.println("当day="+ day +"时,banana为:"+banana);
        }

    }
}

class Test2{
    public int banana(int day){
        if (day == 10){
            return 1;
        } else if (day >= 1 && day <= 9){
            return (banana(day + 1) + 1) * 2;
        } else {
            System.out.println("day的值域:[1,10]");
            return -1;
        }
    }
}

当day=1时,banana为:1534

3、老鼠走迷宫

public class RecursionMiGong {
    public static void main(String[] args) {
        //走迷宫
        //1.先用二维数组创建迷宫
        //2.规定数组的元素值:
        //  0->全图可通过的路
        //  1->障碍物
        //  2->老鼠探测过的可以走的路
        //  3->走过但走不通的路(死路)
        int[][] map = new int[8][7];
        //3.将最上面的一行和最下面的一行全部设置为1
        for (int i = 0; i < 7; i++) {
            map[0][i] = 1;
            map[7][i] = 1;
        }
        //设置障碍物
        map[3][1] = 1;
        map[4][2] = 1;
        //4.将第一列和最后一列全部设置为1
        for (int i = 0; i < 8; i++) {
            map[i][0] = 1;
            map[i][6] = 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();
        }

        //使用findWay找路
        Tt tt = new Tt();
        tt.findWay(map, 1, 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();
        }
    }
}

class Tt{
    //使用递归回溯的思想来解决老鼠出迷宫
    //1.findWay方法专门找出迷宫的路径
    //2.如果找到,返回true,否则返回false
    //3.map就是二维数组,即表示迷宫
    //4.i,j就是老鼠的位置,初始化位置为(1,1)
    //5.当map[6][5]=2,表示找到了通路,否则继续找
    //6、确定老鼠找路的策略:下->右->上->左
    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;
            }
        }
    }
}

1 1 1 1 1 1 1
1 0 0 0 0 0 1
1 0 0 0 0 0 1
1 1 0 0 0 0 1
1 0 1 0 0 0 1
1 0 0 0 0 0 1
1 0 0 0 0 0 1
1 1 1 1 1 1 1
======== 路径如下 ========
1 1 1 1 1 1 1
1 2 0 0 0 0 1
1 2 2 0 0 0 1
1 1 2 2 0 0 1
1 0 1 2 0 0 1
1 0 0 2 0 0 1
1 0 0 2 2 2 1
1 1 1 1 1 1 1

4、汉诺塔

思路分析:
假设有n个盘组成的汉诺塔,则:
(1)如果只有一个盘,即n=1,则A->C
(2)如果n>=2,我们总是可以看做是两个盘:1、最下面的盘;2、上面的盘

  1. 先把最上面的盘:A->B
  2. 把最下面的盘A->C
  3. 把B塔的所有盘从B->C
package classandobject;

public class HanioTower {
    public static void main(String[] args) {
        Tower tower = new Tower();
        tower.move(3, "塔A", "塔B", "塔C");
    }
}

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

塔A->塔C
塔A->塔B
num1 = 2, a = 塔A, b = 塔C, c = 塔B
塔C->塔B
塔A->塔C
num1 = 3, a = 塔A, b = 塔B, c = 塔C
塔B->塔A
塔B->塔C
num1 = 2, a = 塔B, b = 塔A, c = 塔C
塔A->塔C

五、重载(overload)

如果同一个类中包含了两个及两个以上方法名相同,方法参数的个数、顺序或者类型不同的方法,则称为方法的重载

优点:

  1. 利于接口编程
  2. 减轻了起名、记名的麻烦

使用细节:

  1. 方法名相同
  2. 参数列表必须不同 (形参类型、个数、顺序,至少有一项不同,参数名无要求)
  3. 返回类型:无要求
/*
1. 编写程序,类Methods中定义三个重载方法并调用。方法名为m。三个方法
分别接收一个int参数、两个int参数、一个字符串参数。分别执行平方运算并輸出
结果,相乘并输出结果,输出字符串信息。在主类的main()方法中分别用参数
区别调用三个方法。
 */
public class OverLoad01 {
    public static void main(String[] args) {
        Method method = new Method();
        method.m(3);
        method.m(1,2);
        method.m("666");
    }
}
class Method{
    void m(int i){
        System.out.println( "平方 :" + i * i);
    }
    void m(int i , int j){
        System.out.println("相乘 : " + i * j);
    }
    void m(String s){
        System.out.println("接收String :" + s);
    }
}

平方 :9
相乘 : 2
接收String :666

/*
2.在Methods类,定义三个重载方法max0,第一个方法,返回两个int值中的最
大值,第二个方法,返回两个double值中的最大值,第三个方法,返回三个
double值中的最大值,并分别调用三个方法
 */
public class OverLoad02 {
    public static void main(String[] args) {
        Methods methods = new Methods();
        System.out.println(methods.max(1,6));;
        System.out.println(methods.max(1.2,6));;
        System.out.println(methods.max(6,0.6,3));;
    }
}

class Methods{
    public int max(int i , int j){
        return i > j ? i : j;
    }
    public double max(double i , double j){
        return i > j ? i : j;
    }
    public double max(double i , double j , double z){
        return (i > j ? i : j) > z ? (i > j ? i : j) : z;
    }
}

6
6.0
6.0

六、可变参数

概念:
Java允许将同一个类中多个同名同功能,但参数个数不同的方法,封装成一个方法
基本语法:
访问修饰符 返回类型 方法名(数据类型… 形参名){      }

/*
VPMethod类中有可以计算1个数的和、2个数的和、3个……的方法
 */
public class VarParameter01 {
    public static void main(String[] args) {
        VPMethod vpMethod = new VPMethod();
        System.out.println(vpMethod.sum(1,5,9,45));
    }
}
class VPMethod{
    public int sum(int... nums){    //nums理解为数组
        System.out.println("接收的参数个数为;" + nums.length);
        //遍历nums求和
        int res = 0;
        for (int i = 0; i < nums.length; i++) {
            res += nums[i];
        }
        return res;
    }
}

接收的参数个数为;4
60

总结:

  1. 可变参数的实参可以为0个或任意多个
  2. 可变参数的实参可以为数组
  3. 可变参数的本质即是数组
  4. 可变参数可以和普通类型的参数一起放在形参列表且放置在可变参数之前
public void f1 (String s, double... nums){}
  1. 一个形参列表中只能出现一个可变参数
/*
有三个方法:
1、返回姓名和两门课成绩(总分)、
2、返回姓名和三门课成绩(总分)、
3、返回姓名和五门课成绩(总分)
在VPMethodEx类中封装成一个可变参数方法showScore
 */
public class VarParameterEx {
    public static void main(String[] args) {
        VPMethodEx vpMethodEx = new VPMethodEx();
        System.out.println(vpMethodEx.showScore("张万森", 66,77,88,99));
    }
}

class VPMethodEx{
    public String showScore(String name , double... nums){    //nums理解为数组
        double res = 0;
        for (int i = 0; i < nums.length; i++) {
            res += nums[i];
        }
        return name + nums.length + "门课的总分:" + res;
    }
}

张万森4门课的总分:330.0

七、作用域(重点)

  1. 在Java编程中,主要的变量就是属性(成员变量)和局部变量。
  2. 局部变量一般是指在成员方法中定义的变量。【举例 Cats类:call方法中的变量n和name】
  3. java中作用域的分类
    全局变量:也就是属性,作用域为整个类体。在Cats类中call、eat 等方法均可使用属性
    局部变量:也就是除了属性之外的其他变量,作用域为定义它的代码块中!
  4. 全局变量可以不赋值,直接使用时自动赋默认值,局部变量定赋值后才能使用,因为没有默认值。

为什么局部变量不赋初始值直接使用会报错:
类加载过程:
在这里插入图片描述

准备阶段是正式为类变量分配内存并设置其初始值的阶段,这些变量所使用的内存都将在方法区中分配。关于这点,有两个地方注意一下:
        这时候进行内存分配的仅仅是类变量(被static修饰的变量),而不是实例变量,实例变量将会在对象实例化的时候随着对象一起分配在Java堆中
        这个阶段赋初始值的变量指的是那些不被final修饰的static变量,比如"public static int value = 666",value在准备阶段过后是0而不是666,给value赋值为666的动作将在初始化阶段才进行;比如"public static final int value =666;"就不一样了,在准备阶段,虚拟机就会给value赋值为666。
注意 :
这是因为局部变量不像类变量那样存在准备阶段
类变量有两次赋初始值的过程
一次在准备阶段,赋予初始值(也可以是指定值)
另外一次在初始化阶段,赋予程序员定义的值
因此,即使程序员没有为类变量赋值也没有关系,它仍然有一个默认的初始值。但局部变量就不一样了,如果没有给它赋初始值,是不能使用的。
总结:
类变量在准备阶段进行了默认值赋值操作,后续初始化阶段不进行赋值操作也有默认值,因此程序可以正常运行
局部变量在准备阶段不会进行初始值操作,后续初始化阶段也不赋值则不能使用,编译失败
(来源:为什么局部变量不赋初始值报错

作用域的使用事项和细节:

  1. 属性和局部变量可以重名,访问时遵守“就近原则”
  2. 在同一个作用域中,比如在同一个成员方法中,两个局部变量不能重名
  3. 属性生命周期较长,伴随着对象的创建而创建,伴随着对象的销毁而销毁。局部变量生命周期较短,伴随着它的代码块的执行而创建,伴随着代码块的结束而销毁,即在一次方法调用过程中生效。
  4. 作用域范围不同
    局部变量:只能在本类中对应的方法中使用
    全局变量:可以被本类使用以及其他类使用(通过对象调用)
public class ex{
	public static void main(String args[]){
		T t = new T();
		Person p1 = new Person();
		t.test1();	//方法一
		t.test2(p1);//方法二
	}
}
class T{
	public void test1(){
		Person p = new Person();
		System.out.println(p.name);
	}
	public void test2(Person p){
		System.out.println(p.name + 2);
	}
}
class Person{
	String name = "jack";
}

jack
jack2

  1. 修饰符不同
    全局变量/属性可以加访问修饰符(public、protected、private)
    局部变量不可以加

八、构造器(构造方法)

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

概念:
构造方法也称构造器(constructor),是类的一种特殊方法,主要作用是完成对新对象的初始化(而非创建对象)
基本语法:
[修饰符〕方法名(形参列表){
              方法体;
}
注意:
1)构造器的修饰符可以默认
2)构造器没有返回值,也不能写void
3)构造方法名 和 类名相同
4)参数列表 和 成员方法一样的规则
5)创建对象时,系统会自动的调用该类的构造器完成对象的初始化

public class Constructor01 {
    public static void main(String[] args) {
        //当new一个对象时,直接通过构造器指定名字和年龄
        Human h = new Human("jack", 66);
        System.out.println("h的信息如下:" );
        System.out.println("h的名字:" + .name);
        System.out.println("h的年龄:" + h.name);
    }
}
class Human{
    String name;
    int age;
    //构造器
    public Human(String hName, int hAge){
        System.out.println("构造器被调用,完成对象的初始化");
        name = hName;
        age = hAge;
    }
}

构造器使用细节:
1.一个类可以定义多个不同的构造器,即构造器重载
比如:我们可以再给Person类定义一个构造器,用来创建对象的时候,只指定人名,不需要指定年龄

public class ConstructorDetail {
    public static void main(String[] args) {
        DHuman d1 = new DHuman("jack", 66);
        DHuman d2 = new DHuman(66);
        DHuman d3 = new DHuman("jack");
    }
}
class DHuman{
    String name;
    int age;
    //第一个构造器
    public DHuman(String hName, int hAge){
        System.out.println("使用的第一个构造器");
        name = hName;
        age = hAge;
    }
    //第二个构造器,只指定名字,不需要指定年龄
    public DHuman(String hName){
        System.out.println("使用的第二个构造器");
        name = hName;
    }
    //第三个构造器,只指定年龄,不指定名字
    public DHuman(int hAge){
        System.out.println("使用的第三个构造器");
        age = hAge;
    }
}

使用的第一个构造器
使用的第三个构造器
使用的第二个构造器

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

练习:在Humann类中添加两个构造器:

  1. 无参构造器:利用构造器设置所有人的age属性初始值都为18
  2. 带pName和pAge两个参数的构造器:使得每次创建Humann对象的同时初始化对象的age属性值和name属性值。分别使用不同的构造器,创建对象
public class ConstructorEx {
    public static void main(String[] args) {
        Humann h1 = new Humann();//无参构造器
        System.out.println("h1的信息 name=" + h1.name + "  age=" + h1.age);
        Humann h2 = new Humann("jack", 66);
        System.out.println("h2的信息 name=" + h2.name + "  age=" + h2.age);
    }
}

class Humann{
    String name;    //默认值:null
    int age = 18;   //默认值:0
    //第一个构造器
    public Humann(){
        System.out.println("使用的第一个构造器");
    }
    //第二个构造器,只指定名字,不需要指定年龄
    public Humann(String hName, int hAge){
        System.out.println("使用的第二个构造器");
        name = hName;
        age = hAge;
    }
}

使用的第一个构造器
h1的信息 name=null age=18
使用的第二个构造器
h2的信息 name=jack age=66

流程分析:

  1. 加载Humann类信息(Humann.class),只会加载一次
  2. 在堆中分配空间(地址)
  3. 完成对象初始化[ 1、默认初始化 age=0 name=null 2、显式初始化 age=18 name=null 3、构造器初始化 age=66 name=jack]
  4. 把对象在堆中的地址赋值/返回给h2(h2是对象名,也可以理解为对象的引用)
    在这里插入图片描述

九、this关键字

public class This01 {
    public static void main(String[] args) {
        Dog dog1 = new Dog("jack", 66);
        dog1.info();
    }
}
class Dog{
    String name;
    int age;
    public Dog(String dName, int dAge){ //构造器
        name = dName;
        age = dAge;
    }
    public void info(){ //成员方法:输出属性信息
        System.out.println("name: " + name +"\tage: " + age);
    }
}

name: jack age: 66

如果构造器的形参名能直接写成属性名,则可以减少命名难度

public Dog(String name, int age){ //构造器
	name = name;
    age = age;
}

但此时输出结果为

name: null age: 0
根据变量的作用域原则(就近原则),构造器的将局部变量的值赋给了局部变量,即它自身,而非属性。局部变量伴随着它的代码块的执行而创建,伴随着代码块的结束而销毁。所以info方法输出的结果依然是仅仅被系统赋默认值的成员变量

解决方法:this关键字
概念: Java虚拟机会给每个对象分配this,this代表当前对象

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

name: jack age: 66
在这里插入图片描述

深入理解this:

public class This01 {
    public static void main(String[] args) {
        Dog dog1 = new Dog("jack", 66);
        System.out.println("dog1.hashCode=" + dog1.hashCode());
        dog1.info();
        Dog dog2 = new Dog("mike", 666);
        System.out.println("dog2.hashCode=" + dog2.hashCode());
        dog2.info();
    }
}
class Dog{
    String name;
    int age;
    public Dog(String name, int age){ //构造器
        this.name = name;
        this.age = age;
        System.out.println("this.hashCode=" + this.hashCode());
    }
    public void info(){ //成员方法:输出属性信息
        System.out.println("this.name: " + name +"\tthis.age: " + age +
                "\t当前对象的hashCode是:" + this.hashCode());
    }
}

this.hashCode=1735600054
dog1.hashCode=1735600054
this.name: jack this.age: 66 当前对象的hashCode是:1735600054
this.hashCode=21685669
dog2.hashCode=21685669
this.name: mike this.age: 666 当前对象的hashCode是:21685669

this的注意事项和使用细节

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

import sun.management.Agent;

public class ThisDetail {
    public static void main(String[] args) {
        TThisDetail t1 = new TThisDetail();
        t1.f2();
        t1.f3();
    }
}
class TThisDetail{
    String name = "jack";
    int num = 100;
    //this访问成员方法的语法:this.方法名(参数列表);
    public void f1(){
        System.out.println("f1()方法…");
    }

    public void f2(){
        System.out.println("f2()方法…");
        //调用本类的f1
        //方法一
        f1();
        //方法二
        this.f1();
    }

    //this关键字可以用来访问本类的属性
    public void f3(){
        String name = "smith";
        //访问属性的传统方式,根据“就近原则”,name=smith
        System.out.println("name=" + name + "\tnum=" + num);
        //也可以使用this进行访问
        System.out.println("name=" + this.name + "\tnum=" + this.num);
    }

    //this访问构造器的语法:this(参数列表);此语句必须作为构造函数体中的第一条语句
    //注意只能在构造器中使用(即只能在构造器中访问另外一个构造器
    public TThisDetail(){
        this("jack",66);
        System.out.println("TThisDetail()构造器被调用");
        //this("jack",66); 报错:Call to 'this()' must be first statement in constructor body
    }
    public TThisDetail(String name, int age){
        System.out.println("TThisDetail(String name, int age)构造器被调用");
    }
}

TThisDetail(String name, int age)构造器被调用
TThisDetail()构造器被调用
f2()方法…
f1()方法…
f1()方法…
name=smith num=100
name=jack num=100

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

package classandobject;

public class TestStudent {
    public static void main(String[] args) {
        Student mary = new Student("mary", 66);
        Student smith = new Student("smith", 66);
        System.out.println(mary.compareTo(smith));  //smith对象作为compareTo的参数传入
        System.out.println(mary.compareTo(mary1));
    }
}
class Student{
    String name;
    int age;
    //构造器
    public Student(String name, int age){
        this.name = name;
        this.age = age;
    }
    //compareTo比较方法
    public boolean compareTo(Student s){
        return this.name.equals(s.name) && this.age == s.age;
    }
}

false
true

十、本章练习:

解题步骤:依次确定:1,类名;2,方法名;3,返回值;4,形参

  1. 编写类A01,定义方法max,实现求某个double数组的最大值,并返回
    注意:
    传入数组时要注意数组的长度是否为0,以及是否为null。
public class HEx01 {
    public static void main(String[] args) {
        A01 a01 = new A01();
        //数组正常输入
        double[] dMax = new double[]{1.1, -24.2, 3.3, 0.0, -5.5};
        //传入一个长度为0的数组
//        double[] dMax = new double[]{};
        //传入一个空数组
//        double[] dMax = null;


        Double res = a01.max(dMax);
        if (res != null){
            System.out.println("数组最大值:" + res);
        }else {
            System.out.println("数组输入有误(可能为null或者{})");
        }
    }
}
class A01{
    public Double max(double[] dMax){
        //先判断数组是否为空
        if (dMax != null && dMax.length < 1){
            return null;//因为返回值类型Double为一个包装类,所以可以返回null
        }
        double max = dMax[0];
        for (int i = 1; i < dMax.length; i++) {
            max = (max > dMax[i] ? max : dMax[i]);
        }
        return max;
    }
}

数组最大值:3.3

  1. 编写类A02,定义方法find,查找字符串数组中是否存在某字符串,存在则返回索引,否则返回-1
public class HEx02 {
    public static void main(String[] args) {
        A02 a02 = new A02();
        String[] arrStr = new String[]{"666", "777", "888", "999"};
        System.out.println(a02.find(arrStr, "999"));
    }
}
class A02{
    public Integer find(String[] arrStr, String str){
        //先判断数组是否为空
        if (arrStr != null && arrStr.length < 1){
            System.out.println("数组输入有误(可能为null或者{})");
            return null;
        }
        for (int i = 0; i < arrStr.length; i++) {
            if (str.equals(arrStr[i]) == true){
                System.out.print("已找到" + str + ",对应下标为:");
                return i;
            }
        }
        return -1;
    }
}

已找到999,对应下标为:3

  1. 编写类Book,定义方法updatePrice,实现更改某本书的价格,具体:如果价格>150,则更改为150,如果价格>100,更改为100,否则不变
public class HEx03 {
    public static void main(String[] args) {
        double price = 169;
        String bookName = "悲惨世界";
        Book book = new Book(bookName,price);
        book.info();
        System.out.println("价格更新:");
        book.updatePrice();
        book.info();
    }
}
class Book{
    String bookName;
    double price;
    public Book(String bookName, double price){
        this.bookName = bookName;
        this.price = price;
    }
    public double updatePrice(){
        //updatePrice方法中没有price局部变量,this.price 等价 price
//        if (this.price > 150){
//            this.price = 150;
//        } else if(this.price > 100){
//            this.price = 100;
//        }
        this.price = this.price > 150 ? 150 : this.price > 100 ? 100 : this.price;
        return this.price;
    }
    //显示书籍情况
    public void info(){
        if (this.price <= 0){
            System.out.println("价格有误");
            return;
        }
        System.out.println(this.bookName + "现价为" + this.price + "元");
    }
}

悲惨世界现价为169.0元
价格更新:
悲惨世界现价为150.0元

  1. 编写类A03,实现数组的复制功能copyArr,输入旧数组,返回一个新数组,元素和旧数组一样
import java.util.Arrays;
public class HEx04 {
    public static void main(String[] args) {
        A04 a04 = new A04();
        int[] oldArr = new int[]{1, 2, 3, 4, 5, 6};
        System.out.println("原数组:" + Arrays.toString(oldArr));
        System.out.println("新数组:" + Arrays.toString(a04.copyArr(oldArr)));
    }
}
class A04{
    public int[] copyArr(int[] oldArr){
        //在堆中创建一个长度为oldArr.length的数组
        int[] newArr = new int[oldArr.length];
        //遍历oldArr,拷贝到newArr
        for (int i = 0; i < oldArr.length; i++) {
            newArr[i] = oldArr[i];
        }
        return newArr;
    }
}

原数组:[1, 2, 3, 4, 5, 6]
新数组:[1, 2, 3, 4, 5, 6]

  1. 定义一个圆类Circle,定义属性:半径,提供显示圆周长、面积的方法
public class HEx05 {
    public static void main(String[] args) {
        Circle circle = new Circle(5);
        System.out.println("周长为:" + circle.perimeter() + "\t\t面积为:" + circle.area());
    }
}
class Circle{
    double radius;
    public Circle(double radius){
        this.radius = radius;
    }
    //面积
    public double area(){
        return Math.PI * radius * radius;
    }
    //周长
    public double perimeter(){
        return Math.PI * 2 * radius;
    }
}

周长为:31.41592653589793 面积为:78.53981633974483

  1. 编程创建一个Cale计算类,在其中定义2个变量表示两个操作数,定义四个方法实现求和、差、乘、商(要求除数为0的话,要提示) 井创建两个对象,分别测试
public class HEx06 {
    public static void main(String[] args) {
        Cale cale = new Cale(5, 10);
        System.out.println("和=" + cale.sum());
        System.out.println("差=" + cale.minus());
        System.out.println("积=" + cale.mul());
        Double divRes = cale.div();
        if (divRes != null){
            System.out.println("除=" + divRes);
        }
    }
}
class Cale{
    double x;
    double y;
    public Cale(double x, double y){
        this.x = x;
        this.y = y;
    }
    //和
    public double sum(){
        return x + y;
    }
    //差
    public double minus(){
        return x - y;
    }
    //乘
    public double mul(){
        return x * y;
    }
    //除
    public Double div(){
        if (y == 0){
            System.out.println("不能为0");
            return null;
        }
        return x / y;
    }

}

和=15.0
差=-5.0
积=50.0
除=0.5

  1. 设计一Pig类,有名字、颜色和年龄属性,定义输出方法show() 显示其信息。并创建对象,进行测试、【提示 this.属性】
public class HEx07 {
    public static void main(String[] args) {
        Pig pig = new Pig("pp", "white", 66);
        pig.show();
    }
}
class Pig{
    String name;
    String color;
    int age;
    public Pig(String name, String color, int age){
        this.name = name;
        this.color = color;
        this.age = age;
    }
    public void show(){
        System.out.println("小猪" + name + "的颜色为:" + color + ",年龄为:" + age);
    }
}

小猪pp的颜色为:white,年龄为:66

  1. 给定一个Java程序的代码如下所示,则编译运行后,输出结果是(      )
public class HEx08 {        //公有类
    int count = 9;          //属性
    public void count1() {  //HEx08类的成员方法
        count = 10;         //这个count就是属性 改成了10
        System.out.println("count1=" + count);  //10
    }
    public void count2() {  //HEx08类的成员方法
        System.out.println("count1=" + count++);
    }
    //HEx08类的main方法,任何一个类都可以有main
    public static void main(String args[]) {
        //1. new HEx08()是匿名对象(也是在堆中,只能用一次)
        //2. new HEx08().count1():创建好匿名对象后,就调用count1()
        new HEx08().count1();
        HEx08 t1 = new HEx08();
        t1.count2();
        t1.count2();
    }
}

count1=10
count1=9
count1=10

  1. 定义Music类,里面有音乐名name、音乐时长times属性,并有播放play功能和返回本身属性信息的功能方法getlnfo
public class HEx09 {
    public static void main(String[] args) {
        Music m = new Music("夜曲", 229);
        System.out.println(m.play());
        System.out.println(m.getInfo());
    }
}
class Music{
    String name;
    double times;
    public Music(String name, double times){
        this.name = name;
        this.times = times;
    }
    public String play(){
        return "开始播放";
    }
    public String getInfo(){
        return "歌曲《" + name + "》的时长为" + times + "秒";
    }
}

开始播放
歌曲《夜曲》的时长为229.0秒

  1. 写出以下代码的运行结果(       )
public class HEx10 {
    public static void main(String[] args){
        Demo d1 = new Demo();
        Demo d2 = d1;
        d2.m();
        System.out.println(d1.i);
        System.out.println(d2.i);
    }
}
class Demo{
    int i = 100;
    public void m (){
        int j = i++;
        System.out.println("i=" + i);
        System.out.println("j=" + j);
    }
}

i=101
j=100
101
101

  1. 在测试方法中,调用method方法,代码如下,编译正确,试写出method方法的定义形式 ,调用语句为:System.out.println(method(method (10.0, 20.0),100));
public double method(double d1, double d2){
        ……
}
  1. 创建一个Employee类,属性有(名字,性别,年龄,职位,薪水),提供3个构造方法,可以初始化 (1)(名字,性别,年龄,职位,薪水),(2)(名字,性别,年龄)(3)(职位,薪水),要求充分复用构造器
class Employee{
    String name;
    char gender;
    int age;
    String job;
    double salary;
    public Employee(String job, double salary){
        this.job = job;
        this.salary = salary;
    }
    public Employee(String name, char gender, int age){
        this.name = name;
        this.gender = gender;
        this.age = age;
    }
    public Employee(String name, char gender, int age,
                    String job, double salary){
        this(name, gender, age);    //通过this使用前面的构造器
        //因为只能this调用构造器语句只能放在代码块的第一行,
        //所以后面的属性需手动
        this.job = job;
        this.salary = salary;
    }
}
  1. 将对象作为参数传递给方法
    (1)定义一个Circles类,包含一个double型的radius属性代表圆的半径,findArea()方法返回圆的面积
    (2)定义一个类PassObject,在类中定义一个方法printAreas(),该方法的定义如下:public void printAreas(Circles c, int times)     //方法签名/声明
    (3)在printAreas方法中打印输出1到times之间的每个整数半径值,以及对应的面积。
    例如,times为5,则输出半径1,2,3,4,5,以及对应的圆面积。
    (4)在main方法中调用printAreas()方法,调用完毕后输出当前半径值。程序运行结果如下:

Radius     Area
1.0           3.141592653589793
2.0           12.566370614359172
3.0           28.274333882308138
4.0           50.26548245743669
5.0           78.53981633974483

public class HEx13 {
    public static void main(String[] args) {
        PassObject passObject = new PassObject();
        Circles circles = new Circles();
        passObject.printAreas(circles, 5);
    }
}
class Circles{
    double radius;  //半径
    public double findArea(){   //面积
        return Math.PI * radius * radius;
    }
    public void setRadius(double radius){
        this.radius = radius;
    }
}
class PassObject{
    public void printAreas(Circles c, int times){
        System.out.println("Radius\tArea");
        for (int i = 1; i <= times; i++) {
            c.setRadius(i);
            System.out.println((double)i + "\t\t" + c.findArea());
        }
    }
}

Radius     Area
1.0           3.141592653589793
2.0           12.566370614359172
3.0           28.274333882308138
4.0           50.26548245743669
5.0           78.53981633974483

  1. Tom 设计他自己的成员变量、成员方法,可以电脑猜拳。电脑每次都会随机生成 0、
    1、2。0 表示 石头 1 表示剪刀 2 表示 布。并要可以显示Tom的输赢次数(清单)
package classandobject;

import programcontrolstructure.If01;

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

public class HEx14 {
    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 scanner = new Scanner(System.in);
        for (int i = 0; i < 3; i++) {
            //获取玩家出的拳
            System.out.println("输入您要出的拳(0-拳头;1-剪刀;2-布):");
            int num = scanner.nextInt();
            t.setTomGuessNum(num);
            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输赢情况");
            for (int k = 0; k < arr1.length; k++) {
                for (int l = 0; l < arr1[k].length; l++) {
                    System.out.print(arr1[k][l] + "\t\t\t");
                }
                System.out.print(arr2[k]);
                System.out.println();
            }
            System.out.println("你赢了" + isWinCount + "次");
        }
    }
}
class Tom{
    //玩家出拳的类型
    int tomGuessNum;
    //电脑出拳的类型
    int comGuessNum;
    //玩家赢的次数
    int winCountNum;
    //比赛次数
    int count = 1;

    public void showInfo(){

    }

    //电脑生成猜拳数字
    public int computerNum(){
        Random r = new Random();
        //返回0~2的随机数(也可以使用(int)(3*Math.random())
        comGuessNum = r.nextInt(3);
        System.out.println(comGuessNum);
        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;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值