引用类型用法总结
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");
}
}
- 定义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;
}
}
- 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;
}
}
- 首先创建一个创建接口
List<String>
的子类对象list,实现类名称为ArrayList<>
。 - 定义一个参数为
List<String>
(接口类型)的参数list,返回值也是如此,在方法中添加数据,返回该对象。 - 调用该方法,将之前定义的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修饰的参数赋值给局部变量,用于发红包时修改。
- for循环红包数量-1次,因为最后一个红包不用随机,在循环中,生成每次随机的钱数,加入list中,并且计算剩余钱数和剩余红包数量。
- 循环结束后,将剩余的钱加入list中,返回该list即可。
网友评论