练习1
分析需求:我这里就只看出要创建一个类,包括姓名,血量两个属性以及一个攻击的方法 ,攻击方法我想的也很简单,就是攻击一次属性血量-10,写成blood = blood - 1;,这个是有问题的,应该是被攻击者的血量-10,但是我只写这一句代码就等于调用者的血量减下去了,是不对的,而且也没什么思路,脑子很混乱,对于上一节中对于对象的设计并没有什么体会。
误区1:就是一个对象调用方法时我总是考虑这个对象的一些属性是怎么变化的,这是错误的,我应该关注的是方法的设计,关注错了重点。
误区2:就是在方法中总感觉只可以对由这个类所创建的这个对象中的属性进行操作。
但其实在类的方法设计的时候我们是可以对其他对象的属性进行操作的,根据需求而来,比如这道题里就是对另一个对象的blood属性进行操作。
首先设计一个类来描述对象。
一、攻击方法设计的思路:
1、首先需要攻击者与被攻击者,那么如何获得这两个对象呢?调用攻击方法的自然是攻击者,在类中体现为this,而被攻击者通过参数传入就可以了。
2、被攻击者的血量减少:
①首先获取被攻击者的血量;
②伤害使用随机数模拟产生 ;
③定义一个变量remainblood用来接收被攻击之后的血量,如果攻击之前的血量小于受到的伤害数,相减的结果会为负数,明显不符合我们的题意,所以当相减之后结果小于0的话,我们就将remainblood设为0。
④将被攻击之后的血量重新设置为被攻击者的血量。
至此已完成受到伤害之后被攻击者的血量计算。
经过1、2两步攻击方法的设计已经完成。关于角色这个类的设计只需要再加上名字和血量两个属性就可以了。
二、下面是游戏过程的设计:
首先用while(true)来控制攻击的一直进行:
1、乔峰攻击鸠摩智,判断鸠摩智的血量是否为0,如果为0则跳出循环,即游戏结束;
2、鸠摩智攻击乔峰,判断乔峰的血量是否为0如果为0则跳出循环,即游戏结束。
下面是代码的实现:
(1)Role类的实现:
import java.util.Random;
public class Role {
private String name;
private int blood;
public Role() {
}
public Role(String name, int blood) {
this.name = name;
this.blood = blood;
}
/**
* 获取
* @return name
*/
public String getName() {
return name;
}
/**
* 设置
* @param name
*/
public void setName(String name) {
this.name = name;
}
/**
* 获取
* @return blood
*/
public int getBlood() {
return blood;
}
/**
* 设置
* @param blood
*/
public void setBlood(int blood) {
this.blood = blood;
}
public String toString() {
return "Role{name = " + name + ", blood = " + blood + "}";
}
public void attack(Role r){
//造成的伤害使用随机数生成
Random random = new Random();
int hurt = random.nextInt(20) + 1;
int remainBlood = r.getBlood() - hurt;
remainBlood = remainBlood < 0 ? 0 : remainBlood;
r.setBlood(remainBlood);
System.out.println(this.getName() + "举起拳头打了" +r.getName() + "一下,造成了"
+ hurt +"伤害, " +r.getName() + "还剩下" +r.getBlood()+ "点血。");
}
}
(2)游戏过程的实现:
import com.liu.object.Role;
public class test02 {
public static void main(String[] args) {
//初始化两个角色
Role r1 = new Role();
r1.setName("乔峰");
r1.setBlood(100);
Role r2 = new Role();
r2.setName("鸠摩智");
r2.setBlood(100);
while (true) {
r1.attack(r2);
if (r2.getBlood() == 0) {
System.out.println(r1.getName() + "K.O.了" + r2.getName());
break;
}
r2.attack(r1);
if (r1.getBlood() == 0) {
System.out.println(r2.getName() + "K.O.了" + r1.getName());
break;
}
}
}
}
改进之后的需求:
为角色增加了性别和长相 2个属性,性别要求为男或者女,长相要求从一个数组里随机选择一个,然后角色招式的描述也要求从一个数组中随机选中一个。
1、如何对对象的属性进行初始化?
(1)直接赋值:姓名、血量、性别这三个属性都可以在创建对象的时候可以通过构造方法或者相应的setXxx方法对其直接赋值实现初始化。
(2)如果不能直接赋值,比如长相要求从数组中随机选择一个,则可以将对长相进行初始化的方法放到构造函数中。
(1)Role1类的设计:
import java.util.Random;
public class Role1 {
private String name;
private int blood;
private char gender;
private String appearance;
//外貌描述
String[] boyfaces= {"风流俊雅","气宇轩昂","相貌英俊","五官端正","相貌平平","一塌糊涂","面目狰狞"};
String[] girlfaces ={"美奂绝伦","沉鱼落雁","婷婷玉立","身材娇好","相貌平平","相貌简陋","惨不忍睹"};
//attack 攻击描述
String[] attacks_desc={
"%s使出了一招【背心钉】,转到对方的身后,一掌向%s背心的灵台穴拍去。",
"%s使出了一招【游空探爪】,飞起身形自半空中变掌为抓锁向%s。",
"%s大喝一声,身形下伏,一招【劈雷坠地】,捶向%s双腿。",
"%s运气于掌,一瞬间掌心变得血红,一式【掌心雷】,推向%s。",
"%s阴手翻起阳手跟进,一招【没遮拦】,结结实实的捶向%s。",
"%s上步抢身,招中套招,一招【劈挂连环】,连环攻向%s。"
};
//injured 受伤描述
String[] injureds_desc={
"结果%s退了半步,毫发无损",
"结果给%s造成一处瘀伤",
"结果一击命中,%s痛得弯下腰",
"结果%s痛苦地闷哼了一声,显然受了点内伤",
"结果%s摇摇晃晃,一跤摔倒在地",
"结果%s脸色一下变得惨白,连退了好几步",
"结果『轰』的一声,%s口中鲜血狂喷而出",
"结果%s一声惨叫,像滩软泥般塌了下去"
};
public Role1() {
}
public Role1(String name, int blood, char gender) {
this.name = name;
this.blood = blood;
this.gender = gender;
setAppearance();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getBlood() {
return blood;
}
public void setBlood(int blood) {
this.blood = blood;
}
public char getGender() {
return gender;
}
public void setSex(char gender) {
this.gender = gender;
}
public String getAppearance() {
return appearance;
}
public void setAppearance(char gender) {
Random r = new Random();
int index = r.nextInt(boyfaces.length);
if(gender == '男'){
this.appearance = boyfaces[index];
} else if (gender == '女') {
this.appearance = girlfaces[index];
}
}
public String toString() {
return "Role{name = " + name + ", blood = " + blood + "}";
}
public void attack(Role1 r){
//攻击
Random random = new Random();
int index = random.nextInt(attacks_desc.length);
System.out.printf(attacks_desc[index], this.getName(), r.getName());
System.out.println();
//造成的伤害使用随机数生成
int hurt = random.nextInt(20) + 1;
//剩余血量
int remainBlood = r.getBlood() - hurt;
//判断剩余血量是否大于0
remainBlood = remainBlood < 0 ? 0 : remainBlood;
r.setBlood(remainBlood);
//受伤描述
if(remainBlood >= 90){
System.out.printf(injureds_desc[0], r.getName());
} else if((remainBlood >= 80) && (remainBlood < 90)) {
System.out.printf(injureds_desc[1], r.getName());
} else if((remainBlood >= 70) && (remainBlood < 80)) {
System.out.printf(injureds_desc[2], r.getName());
} else if((remainBlood >= 60) && (remainBlood < 70)) {
System.out.printf(injureds_desc[3], r.getName());
} else if((remainBlood >= 40) && (remainBlood < 60)) {
System.out.printf(injureds_desc[4], r.getName());
} else if((remainBlood >= 20) && (remainBlood < 40)) {
System.out.printf(injureds_desc[5], r.getName());
} else if((remainBlood >= 10) && (remainBlood < 20)) {
System.out.printf(injureds_desc[6], r.getName());
} else if(remainBlood < 10) {
System.out.printf(injureds_desc[7], r.getName());
}
System.out.println();
}
}
2、printf中%s的用法
System.out.printf("%s使出了一招【背心钉】,转到对方的身后,一掌向%s背心的灵台穴拍去。", this.getName(), r.getName());
有两个%s,代表占位数,就是将 this.getName(), r.getName()这两个值分别填充到两个%s位置去。
错误:
Condition 'remainBlood < 90' is always 'true' when reached
因为如果按照判断条件往下走的话,remainBlood < 90一定为真,就没有写上去的意义了,但是为了代码的阅读性,还是写上去了。
(2)下面是游戏过程的实现,与刚开始相比是没有变化的:
import com.liu.object.Role1;
public class test03 {
public static void main(String[] args) {
//创建两个角色
Role1 r1 = new Role1("乔峰",100,'男');
Role1 r2 = new Role1("鸠摩智",100,'男');
//攻击
while (true) {
r1.attack(r2);
if(r2.getBlood() == 0){
System.out.println(r1.getName() + "K.O." + r2.getName());
break;
}
r2.attack(r1);
if(r1.getBlood() == 0){
System.out.println(r2.getName() + "K.O." + r1.getName());
break;
}
}
}
}
其实对于题目的要求,我觉得描述的也有问题,就是描述的不够准确,我有时候写出来的就相差甚远。
练习2:对象数组
一、
由于商品类的设计非常简单,这里就不再提示了。
需求的实现:
1、首先创建一个存放商品对象的数组;
2、创建3个商品对象;
3、把三个商品放到数组中;
4、输出展示。
import com.liu.object.Goods;
public class test04 {
public static void main(String[] args) {
//首先创建一个存放商品的数组
Goods[] g = new Goods[3];
//创建3个商品
Goods g1 = new Goods(1, "苹果", 5.99, 200);
Goods g2 = new Goods(2, "梨", 3.99, 100);
Goods g3 = new Goods(3, "香蕉", 1.99, 300);
//把三个商品放到数组中
g[0] = g1;
g[1] = g2;
g[2] = g3;
//输出
for (int i = 0; i < g.length; i++) {
System.out.println(g[i]);
}
}
}
快捷键:crtl+p,显示参数。
二、
关于Car这个类的实现都是一样的。
然后就是如何实现上述需求:
1、创建Scanner对象;
2、调用方法接收键盘录入的数据并存进变量;
3、将变量设置为对象相应的属性值 。
import com.liu.object.Car;
import java.util.Scanner;
public class test05 {
public static void main(String[] args) {
//创建一个存放汽车对象的数组
Car[] cars = new Car[3];
//创建三个汽车对象
Car car1 = new Car();
Car car2 = new Car();
Car car3 = new Car();
//通过键盘录入汽车的相关数据
//1.创建一个Scanner对象
Scanner sc = new Scanner(System.in);
//2.通过对象调用相应的方法获得录入的数据
String brand1 = sc.next();
int price1 = sc.nextInt();
String color1 = sc.next();
car1.setBrand(brand1);
car1.setPrice(price1);
car1.setColor(color1);
String brand2 = sc.next();
int price2 = sc.nextInt();
String color2 = sc.next();
car2.setBrand(brand2);
car2.setPrice(price2);
car2.setColor(color2);
String brand3 = sc.next();
int price3 = sc.nextInt();
String color3 = sc.next();
car3.setBrand(brand3);
car3.setPrice(price3);
car3.setColor(color3);
//将三个对象存入数组中
cars[0] = car1;
cars[1] = car2;
cars[2] = car3;
//输出
for (int i = 0; i < cars.length; i++) {
System.out.println(cars[i]);
}
}
}
其实上面这个代码仔细看是可以优化的。
3、接收键盘录入的数据
1、第一套接收体系:
(1)nextInt():接收整数
(2)nextDouble():接收小数
(3)next():接收字符串
第一种接收体系当首次遇到空格、换行、制表符时会停止接收,即只接收这些符号以前的。
例子:
import java.util.Scanner;
public class other {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int num1 = sc.nextInt();
System.out.println("请输入第一个数:");
System.out.println(num1);
int num2 = sc.nextInt();
System.out.println("请输入第二个数:");
System.out.println(num2);
}
}
重点是后面的输入详情:
对于空格这种的录入,只有可以看到num1接收了123,然后剩下的456就给了num2,7已经没有可以接收的方法了,于是被舍弃了。
emmm,大概明白了,其实我以前在键盘录入数据的时候,当第一个数据敲完以后换行,然后录入第二个数据再次换行,此时数据的录入就结束了。
这里的换行和空格都是一个含义,代表了数据的分割。 所以下面这个在数据接收的含义上是一样的:
123 456 7
或者
123
456
7
2、第二套接收体系:
(1)nextLine():接收字符串
第二种接收体系可以接收空格和制表符,遇到回车才停止接收。
而且我发现我在数据接收和将对象放入数组中有问题,其实可以用循环实现,不用一个一个敲:
import com.liu.object.Car;
import java.util.Scanner;
public class test05 {
public static void main(String[] args) {
//创建一个存放汽车对象的数组
Car[] cars = new Car[3];
for (int i = 0; i < cars.length; i++) {
//创建汽车对象
Car car = new Car();
//1.创建一个Scanner对象
Scanner sc = new Scanner(System.in);
//2.通过对象调用相应的方法获得录入的数据
String brand = sc.next();
int price = sc.nextInt();
String color = sc.next();
car.setBrand(brand);
car.setPrice(price);
car.setColor(color);
//将对象存入数组
cars[i] = car;
}
//输出
for (int i = 0; i < cars.length; i++) {
System.out.println(cars[i]);
}
}
}
4、对数组中的每个对象的属性进行初始化
1、直接在创建的时候使用构造函数进行初始化;
2、如果需要通过键盘录入来进行设置的话则可通过for循环访问每一个对象然后对其属性进行初始化。
练习3
统计对象数组中某些需求的次数:
import com.liu.object.Phone;
public class test06 {
public static void main(String[] args) {
Phone[] phones = new Phone[3];
Phone p1 = new Phone("iPhone",5999, "白色");
Phone p2 = new Phone("小米",3999, "白色");
Phone p3 = new Phone("OPPO",1999, "白色");
phones[0] = p1;
phones[1] = p2;
phones[2] = p3;
int sum = 0;
for (int i = 0; i < phones.length; i++) {
sum = sum + phones[1].getPrice();
}
System.out.println(sum/phones.length);
}
}
这个我还是用一个一个对象调用getXxx方法去获取数据,要用数组,啊啊啊!
练习4
统计某些需求的次数:
import com.liu.object.Friend;
public class test07 {
public static void main(String[] args) {
Friend[] friends = new Friend[4];
Friend f1 = new Friend("zhang", 18, '女', "看电影");
Friend f2 = new Friend("liu", 22, '女', "看电影");
Friend f3 = new Friend("zhao", 25, '女', "看电影");
Friend f4 = new Friend("li", 28, '女', "看电影");
friends[0] = f1;
friends[1] = f2;
friends[2] = f3;
friends[3] = f4;
int sum = 0;
for (int i = 0; i < friends.length; i++) {
sum = sum + friends[i].getAge();
}
int average = sum / friends.length;
System.out.println("平均年龄为" + average);
int count = 0;
for (int i = 0; i < friends.length; i++) {
if(friends[i].getAge() < average){
count++;
System.out.println(friends[i]);
}
}
System.out.println(count);
}
}
5、统计思想
当满足条件时自增。
练习5
看到题目时的思考:
刚开始我放了两个学生到数组中,图简单,然后测试s3能否添加到数组中。
1、添加:学号不能重复,所以当新添加的时候使用equals方法将学号和数组中的所有对象的学号进行比较,不存在相同的时候添加,可以我又不知道要添加的索引,通过计算数组中实际所存的元素就知道下次存放的下标;
2、添加后遍历所有学生信息:判断是否为null,不是则输出;
3、删除某个id的学生,将此id与数组中的所有对象的id一一比较,找到则将此元素置为null,即实现删除;
4、删除后遍历所有元素:与2同理;
5、找到某个id的学生将其年纪+1;
类的实现没什么好说的,下面是要求的实现代码:
import com.liu.object.Student;
public class test08 {
public static void main(String[] args) {
Student[] students = new Student[3];
Student s1 = new Student("heima001", "小刘", 18);
Student s2 = new Student("heima002", "小张", 20);
Student s3 = new Student("heima003", "小李", 22);
students[0] = s1;
students[1] = s2;
//测试s3是否能添加成功
System.out.println("添加学生信息:");
boolean flag = false;
for (int i = 0; i < students.length; i++) {
if(students[i] != null){
if((s3.getId().equals(students[i].getId())) && (s3.getName().equals(students[i].getName()))){
flag = true;
break;
}
}
}
if(!flag){
//其中index是数组中实际存储的元素个数,同时也是下次添加到数组中的下标
int index = countArray(students);
students[index] = s3;
}
for (int i = 0; i < students.length; i++) {
System.out.println(students[i]);
}
//添加完成之后遍历所有学生的信息
System.out.println("添加完成之后遍历所有学生的信息:");
for (int i = 0; i < students.length; i++) {
if(students[i] != null){
System.out.println(students[i]);
}
}
//通过id删除学生信息
System.out.println("通过id删除学生信息:");
String id1 = "heima001";
for (int i = 0; i < students.length; i++) {
if (students[i] != null) {
if (id1.equals(students[i].getId())) {
students[i] = null;
System.out.println("删除成功!");
}
}
}
//删除完毕后遍历所有学生的信息
for (int i = 0; i < students.length; i++) {
if(students[i] != null){
System.out.println(students[i]);
}
}
//查询id为"heima002"的学生,如果存在则他的年龄+1
String id2 = "heima002";
for (int i = 0; i < students.length; i++) {
if (students[i] != null) {
if (id2.equals(students[i].getId())) {
int age = students[i].getAge() + 1;
students[i].setAge(age);
}
}
}
}
static int countArray(Student[] students){
int count = 0;
for (int i = 0; i < students.length; i++) {
if(students[i] != null){
count++;
}
}
return count;
}
}
功能是大部分完成了,可是和视频中的有些不太一样。
下面进行改进:
1、将判断某个id是否已存在抽取为一个方法;
2、用if语句对存在和不存在两种情况分别进行书写,存在时进行提示,不存在时添加;
3、对于添加,要首先判断数组是否已满,如果已满,数组长度+1,并将新元素放进去,不满则直接将对象放进去。
6、如何实现数组长度+1?
因为数组的长度一旦确定就不可再改变,那这里是如何实现数组长度+1的呢?答案就是创建一个新的数组,然后长度为原数组长度+1,并将原数组的元素按照索引一一挪到新数组中去。
//创建长度+1的新数组
public static Student[] createNewArr(Student[] students){
Student[] newArr = new Student[students.length + 1];
for (int i = 0; i < students.length; i++) {
newArr[i] = students[i];
}
return newArr;
}
下面是具体代码的实现:
import com.liu.object.Student;
public class test09 {
public static void main(String[] args) {
Student[] students = new Student[3];
Student s1 = new Student("heima001", "小刘", 18);
Student s2 = new Student("heima002", "小张", 20);
Student s3 = new Student("heima003", "小李", 22);
students[0] = s1;
students[1] = s2;
//测试s3是否能添加成功
System.out.println("添加学生信息:");
boolean flag = contains(s3.getId(),students);
if(flag){
System.out.println("此id已存在,请重新输入!");
} else {
int count = getCount(students);
if(count == students.length) {
Student[] newArr = createNewArr(students);
newArr[count] = s3;
printArr(newArr);
} else{
students[count] = s3;
printArr(students);
}
}
}
public static boolean contains(String id, Student[] students){
boolean flag = false;
for (int i = 0; i < students.length; i++) {
if(students[i] != null){
if(id.equals(students[i].getId())){
flag = true;
break;
}
}
}
return flag;
}
//创建长度+1的新数组
public static Student[] createNewArr(Student[] students){
Student[] newArr = new Student[students.length + 1];
for (int i = 0; i < students.length; i++) {
newArr[i] = students[i];
}
return newArr;
}
//判断数组中存放的元素个数
public static int getCount(Student[] students){
int count = 0;
for (int i = 0; i < students.length; i++) {
if(students[i] != null){
count++;
}
}
return count;
}
//遍历数组
public static void printArr(Student[] students){
for (int i = 0; i < students.length; i++) {
if(students[i] != null){
System.out.println(students[i]);
}
}
}
}
上面使用了一个新的if格式使用:
if(条件表达式1){
//语句体1;
} else {
//语句体2;
if(条件表达式2){
//语句体3;
}
即else里面可以写一些语句,然后再进行判断。
练习6
在本类中可直接访问,在有继承关系的类中也可直接访问非私有化成员,而在没有关系的类中需要对象进行访问。