美文网首页
2020-08-12--Java--day11【引用类型,发红包

2020-08-12--Java--day11【引用类型,发红包

作者: program_white | 来源:发表于2020-08-14 00:11 被阅读0次

引用类型用法总结

1.类作为成员变量

在定义一个类Role(游戏角色)时,代码如下:

class Role {
        int id; // 角色id
        int blood; // 生命值
        String name; // 角色名称
}

使用 int 类型表示 角色id和生命值,使用 String 类型表示姓名。此时, String 本身就是引用类型,由于使用的方式类似常量,所以往往忽略了它是引用类型的存在。如果我们继续丰富这个类的定义,给 Role 增加武器我们将如何编写呢?

因为在实际的生活中,年龄,生命值等属性都是确定的,但是武器这个属性它是包含很多东西的,受多个内容的影响,例如:武器的等级,武器的稀有程度等等,所以我们把武器作为类传入角色类中。

Weapon类:

public class Weapon {

    private String code; // 武器的代号
    private int level;   //武器的等级

    public Weapon() {
    }

    public Weapon(String code,int level) {
        this.code = code;
        this.level = level;
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }
    public int getlevel() {
        return level;
    }

    public void setlevel(int level) {
        this.level = level;
    }
}

Hero类:

public class Hero {

    private String name; // 英雄的名字
    private int age; // 英雄的年龄
    private Weapon weapon; // 英雄的武器,类型为类

    public Hero() {
    }

    public Hero(String name, int age, Weapon weapon) {
        this.name = name;
        this.age = age;
        this.weapon = weapon;
    }

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }

    public Weapon getWeapon() {
        return weapon;
    }
    public void setWeapon(Weapon weapon) {
        this.weapon = weapon;
    }
    //攻击方法
public void attack() {
        System.out.println("年龄为" + age + "的" + name + "用" +weapon.getlevel()+"级的"+ weapon.getCode() + "攻击敌方。");
    }
}

Main类:

public class DemoMain {

    public static void main(String[] args) {
        // 创建一个英雄角色
        Hero hero = new Hero();
        // 为英雄起一个名字,并且设置年龄
        hero.setName("盖伦");
        hero.setAge(20);

        // 创建一个武器对象
        Weapon weapon = new Weapon("AK-47",3);
        // 为英雄配备武器
        hero.setWeapon(weapon);

        // 年龄为20的盖伦用多兰剑攻击敌方。
        hero.attack();      //年龄为20的盖伦用3级的AK-47攻击敌方。
    }
}

Main思路:
首先创建Hero类对象,设置name和age,创建一个武器类对象,给武器类对象初始化(传参),将该武器类对象设置到Hero类对象hero上,调用攻击方法。

接口作为成员变量类型

当接口作为成员变量时,与类作为成员变量的大概一致,只是类能够自己独立完成变量的赋值和方法的调用,接口需要实现类还完成(也可以使用匿名内部类)。

实例:
当英雄有很多技能时,我们定义一个技能接口,在接口中定义多个技能的抽象方法,供实现类来使用。

1.首先定义Skill接口:

public interface Skill {
    void use1(); // 释放技能的抽象方法
    void use2();
}

2.定义接口的实现类SkillImp:

public class SkillImpl implements Skill {
    @Override
    public void use1() {
        System.out.println("Biu~biu~biu~");
    }
    @Override
    public void use2() {
        System.out.println("Pa~Pa~Pa");
    }
}
  1. 定义Hero类:
public class Hero {

    private String name; // 英雄的名称
    private Skill skill; // 英雄的技能

    public Hero() {
    }

    public Hero(String name, Skill skill) {
        this.name = name;
        this.skill = skill;
    }
    //攻击方法
    public void attack() {
        System.out.println("我叫" + name + ",开始施放技能:");
        skill.use1(); // 调用接口中的抽象方法
        skill.use2();
        System.out.println("施放技能完成。");
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Skill getSkill() {
        return skill;
    }

    public void setSkill(Skill skill) {
        this.skill = skill;
    }
}
  1. Main类:
    在给Hero类对象设置Skill时,有多种设置方式:
  • 创建接口实现类,将实现类对象传入hero对象中
  • 将实现类的对象名省略,传入hero中
  • 还可以改成使用匿名内部类,不再创建实现类SkillImpl的对象
  • 进一步简化,同时使用匿名内部类和匿名对象,把匿名对象也省略了,直接传入hero中。
public class DemoGame {

    public static void main(String[] args) {
        Hero hero = new Hero();
        hero.setName("艾希"); // 设置英雄的名称

        // 1.创建接口实现类,将实现类对象传入hero对象中
        Skill sk = new SkillImpl();
        hero.setSkill(sk);
        // 调用攻击方法
        hero.attack();

        // 2.将实现类的对象名省略,传入hero中
        hero.setSkill(new SkillImpl()); // 使用单独定义的实现类
        hero.attack();

        // 3.还可以改成使用匿名内部类,不再创建实现类SkillImpl的对象
        Skill skill = new Skill() {
            @Override
            public void use1() {
                System.out.println("Pia~pia~pia~");
            };
            @Override
            public void use2() {
                System.out.println("Pa~Pa~Pa");
            }

        };
        hero.setSkill(skill);
        hero.attack();

        // 4.进一步简化,同时使用匿名内部类和匿名对象,把匿名对象也省略了,直接传入hero中
        hero.setSkill(new Skill() {
            @Override
            public void use1() {
                System.out.println("Biu~Pia~Biu~Pia~");
            }
            @Override
            public void use2() {
                System.out.println("Pa~Pa~Pa");
            }
        });
        hero.attack();
    }
}

运行:

接口作为方法的参数/返回值

当接口作为方法的参数时,需要传递什么呢?当接口作为方法的返回值类型时,需要返回什么呢?对,其实都是它的子类对象。
ArrayList 类我们并不陌生,查看API我们发现,实际上,它是 java.util.List 接口的实现类。
所以,当我们看见 List 接口作为参数或者返回值类型时,当然可以将 ArrayList 的对象进行传递或返回。

实例:

向ArrayList中添加内容:

import java.util.ArrayList;
import java.util.List;

/*
java.util.List正是ArrayList所实现 的接口。
 */
public class DemoInterface {

    public static void main(String[] args) {

        // 创建接口List<String>的子类对象list,
        // 左边是接口名称,右边是实现类名称,这就是多态写法
        List<String> list = new ArrayList<>();

        //接收方法的返回值(list类型)
        List<String> result = addNames(list);
        for (int i = 0; i < result.size(); i++) {
            System.out.println(result.get(i));
        }
    }

    public static List<String> addNames(List<String> list) {
        list.add("迪丽热巴");
        list.add("古力娜扎");
        list.add("玛尔扎哈");
        list.add("沙扬娜拉");
        return list;
    }
}
  1. 首先创建一个创建接口List<String>的子类对象list,实现类名称为ArrayList<>
  2. 定义一个参数为List<String>(接口类型)的参数list,返回值也是如此,在方法中添加数据,返回该对象。
  3. 调用该方法,将之前定义的ArrayList<>实现类对象传进去,对结果进行遍历打印。

发红包案例

场景说明:
红包发出去之后,所有人都有红包,大家抢完了之后,最后一个红包给群主自己。
大多数代码都是现成的,我们需要做的就是填空题。

红包分发的策略:
1. 普通红包(平均):totalMoney / totalCount,余数放在最后一个红包当中。
2. 手气红包(随机):最少1分钱,最多不超过平均数的2倍。应该越发越少。

1.准备工作

因为是有图形界面的,所以我们要使用一些实现图形化的类:
RedPacketFrame,该类为继承JFrame的抽象方法,它已经帮我们做完很多的事情了。

RedPacketFrame抽象类的重要方法:

  /**
     * 构造方法:生成红包界面。
     *
     * @param title 界面的标题
     */

    public RedPacketFrame(String title) {
        super(title);
        // 页面相关的初始化
        init();
    }
    //设置群主名称
    public void setOwnerName(String ownerName) {
        this.ownerName = ownerName;
    }
    //设置红包打开方式,接受接口类型的参数
    public void setOpenWay(OpenMode openWay) {
        this.openWay = openWay;
    }

我们自己要做的事情有:
1. 设置一下程序的标题,通过构造方法的字符串参数
2. 设置群主名称
3. 设置分发策略:平均,还是随机?

接下来就实现一下。

2.普通红包(平均)

1.我们要设置一下发红包的swing标题,该标题的实现是在RedPacketFrame类的构造方法中实现的,所以我们定义一个实现类MyRed类,在类中将父类的title继承下来,便于之后修改。

MyRed 类:

import cn.itcast.day11.red.RedPacketFrame;

public class MyRed extends RedPacketFrame {
    /**
     * 构造方法:生成红包界面。
     *
     * @param title 界面的标题
     */
    public MyRed(String title) {
        super(title);
    }
}

在之后的Main中就可以在创建对象时,将title传进去进行修改了。

2.因为我们要完成的红包类型为两种类型,所以我们有必要提供一个接口供其使用,该接口有一个抽象方法,参数为总钱数和总人数。返回一个ArrayList数组。

这个接口的名称要与RedPacketFrame中打开方式方法接受的参数类型一致。

OpenMode 接口:

public interface OpenMode {
    /**
     * 请将totalMoney分成count份,保存到ArrayList<Integer>中,返回即可。
     *
     * @param totalMoney            总金额为方便计算,已经转换为整数,单位为分。
     * @param totalCount            红包个数
     * @return ArrayList<Integer>   元素为各个红包的金额值,所有元素的值累和等于总金额。
     */
    ArrayList<Integer> divide(int totalMoney, int totalCount);

3.定义普通红包的方法,实现OpenMode接口,重写接口中的divide方法。

在方法中计算平均值和剩余值,将红包一一加入数组中,在最后一个红包
中加入余数。

NormalMode 类:

import cn.itcast.day11.red.OpenMode;

import java.util.ArrayList;

public class NormalMode implements OpenMode {
    @Override
    public ArrayList<Integer> divide(final int totalMoney, final int totalCount) {

        ArrayList<Integer> list = new ArrayList<>();

        int avg = totalMoney / totalCount; // 平均值
        int mod = totalMoney % totalCount; // 余数,模,零头

        // 注意totalCount - 1代表,最后一个先留着
        for (int i = 0; i < totalCount - 1; i++) {
            list.add(avg);
        }

        // 有零头,需要放在最后一个红包当中
        list.add(avg + mod);

        return list;
    }
}

4.在Main中创建MyRed类对象,将title传入构造函数中,调用对象的setOwnerName()设置群主名称。

创建NormalMode类对象normal,使用多态的写法,再次调用red对象的setOpenWay()将normal传进去-->RedPacketFrame。

Main类:

public class Bootstrap {

    public static void main(String[] args) {
        MyRed red = new MyRed("传智播客双元课程");
        // 设置群主名称
        red.setOwnerName("王思聪");

        // 普通红包
        OpenMode normal = new NormalMode();
        red.setOpenWay(normal);
    }
}

这里的setOpenWay(normal)在底层会自动调用normal类对象的divide对红包进行分割。

3.手气红包

本质上,手气红包就是把总金额 totalMoney 随机分成指定的 count 份,所以必须规定每一份金额的取值范围。如果范围太小,可能导致后分配红包金额特别大。反之范围太大,可能导致后分配红包金额为0,不够分。可见,取值范围的定义规则,是手气红包的关键所在。

我们规定:每一份随机金额范围(除最后一份),最小值为1,最大值为当前剩余平均金额的2倍 ,单位为"分"。

计算公式:
当前剩余平均金额 = 剩余总金额 / 剩余红包个数

代码实现,实现接口OpenMode的类RandomMode :

import java.util.ArrayList;
import java.util.Random;

public class  RandomMode implements OpenMode {
    @Override
    public ArrayList<Integer> divide(final int totalMoney, final int totalCount) {
        ArrayList<Integer> list = new ArrayList<>();

        // 随机分配,有可能多,有可能少。
        // 最少1分钱,最多不超过“剩下金额平均数的2倍”
        // 第一次发红包,随机范围是0.01元~6.66元
        // 第一次发完之后,剩下的至少是3.34元。
        // 此时还需要再发2个红包
        // 此时的再发范围应该是0.01元~3.34元(取不到右边,剩下0.01)

        // 总结一下,范围的【公式】是:1 + random.nextInt(leftMoney / leftCount * 2);
        Random r = new Random(); // 首先创建一个随机数生成器
        // totalMoney是总金额,totalCount是总份数,不变
        // 额外定义两个变量,分别代表剩下多少钱,剩下多少份
        int leftMoney = totalMoney;
        int leftCount = totalCount;

        // 随机发前n-1个,最后一个不需要随机
        for (int i = 0; i < totalCount - 1; i++) {
            // 按照公式生成随机金额
            int money = r.nextInt(leftMoney / leftCount * 2) + 1;
            list.add(money); // 将一个随机红包放入集合
            leftMoney -= money; // 剩下的金额越发越少
            leftCount--; // 剩下还应该再发的红包个数,递减
        }

        // 最后一个红包不需要随机,直接放进去就得了
        list.add(leftMoney);
        System.out.println(list);

        return list;
    }
}

1.创建一个ArrayList对象list,创建随机生成器对象r,将传进来的final修饰的参数赋值给局部变量,用于发红包时修改。

  1. for循环红包数量-1次,因为最后一个红包不用随机,在循环中,生成每次随机的钱数,加入list中,并且计算剩余钱数和剩余红包数量。
  2. 循环结束后,将剩余的钱加入list中,返回该list即可。

相关文章

  • 2020-08-12--Java--day11【引用类型,发红包

    引用类型用法总结 1.类作为成员变量 在定义一个类Role(游戏角色)时,代码如下: 使用 int 类型表示 角色...

  • JavaScript基础三

    1、引用类型有哪些?非引用类型有哪些 引用类型:Object非引用类型:number string null ...

  • 引用类型与对象拷贝

    1.引用类型有哪些?非引用类型有哪些 引用类型: 对象 object 数组 函数 正则非引用类型...

  • 引用类型对象拷贝思考

    1、引用类型有哪些?非引用类型有哪些 非引用类型 String 类型、Null 类型、Number 类型、Unde...

  • js-引用类型对象拷贝

    1.引用类型; 2.过滤数组; 3.深浅拷贝方法; 1.引用类型有哪些?非引用类型有哪些 引用类型:引用类型(Ob...

  • JavaScript引用类型与对象拷贝

    引用类型有哪些?非引用类型有哪些 引用类型:对象、数组、函数、正则非引用类型:string、number、bool...

  • 进阶任务-4

    引用类型有哪些?非引用类型有哪些2.如下代码输出什么?为什么? 引用类型有哪些,非引用类型有哪些引用类型:数组,对...

  • 面向对象学习笔记

    - 数组其实就是一种引用类型。 int是基本类型,int[]是引用类型 数组是引用类型,引用类型和基本类型的区别在...

  • 深拷贝与浅拷贝

    一.引用类型与值类型 我们都知道,js有两种基本类型,引用类型与值类型。引用类型的“=”只是拷贝了引用,而基本类型...

  • 引用类型对象拷贝

    1.引用类型有哪些?非引用类型有哪些 引用类型:对象、数组、正则、函数非引用类型:number、string、bo...

网友评论

      本文标题:2020-08-12--Java--day11【引用类型,发红包

      本文链接:https://www.haomeiwen.com/subject/btuvdktx.html