Java基础(九)
综合案例此前我们已经练习了根据集合当中的字符串对象读写文件,而本综合案例主要练习根据集合当中的自定义对象来读写文件。 场景介绍很多网络游戏当中都有组队模式,例如魔兽世界、DotA、英雄联盟(LOL)、王者荣耀等,均为5人组队进行游戏。即使在现实生活中,打麻将、斗地主也都是多人进行的游戏。那么对于多人组队的游戏,典型的场景为: 1.有5个人都在玩游戏; 2.这5个人组成一个队伍; 3.这支队伍进行游戏; 4.队伍成员彼此欣赏,决定以后还要组队一起玩; 5.一段时间后大家再次组队游戏。 分析整体思路? ? ? ? 实现框架代码//自定义一个类,代表英雄 public class Hero { //成员变量 private String name; //名字 private int attack; //英雄的攻击力 private String type; //英雄的类型 public Hero() { } public Hero(String name,int attack,String type) { this.name = name; this.attack = attack; this.type = type; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAttack() { return attack; } public void setAttack(int attack) { this.attack = attack; } public String getType() { return type; } public void setType(String type) { this.type = type; } } ? //测试类 public class Test { public static void main(String[] args) { //1.首先创建一个集合,用来存储五个英雄 ArrayList<Hero> list = new ArrayList<>(); //2.读文件,把数据加到集合当中. System.out.println("加载文件的数据到集合当中."); //list.add(new Hero()); //假设已经从文件当中加载得到了数据,并且加入集合当中 //3.判断一下集合当中有没有内容,是不是新的? boolean isNew = list.size() == 0; //如果集合的长度为0,说明这是新的队伍 System.out.println("这是不是新队伍:" +isNew); //如果是新的队伍,没有人,就需要创建五个对像加入到集合当中 if(isNew) { System.out.println("创建5个对象加入集合中."); } //如果不是新的队伍,已经有人了,就不需要创建了. System.out.println("我们队伍的阵容是:"); //遍历集合,输出其中的每一个对象的具体信息 System.out.println("循环5次进行遍历"); //统计一下总和战斗力是多少. int totalAttack = 0; //稍后在计算 System.out.println("我们队伍的总战斗力是:" + totalAttack); //判断一下集合是不是新的 if(isNew) { System.out.println("将集合的数据写到文件中."); } //如果集合不是新的,而是从文件中加载得到的数据,就什么都不用做 System.out.println("退出游戏"); } } ? 英雄问世(创建对象)在测试类mian方法外添加方法 /* * 定义一个方法,用来向集合当中添加五位英雄.三要素: 返回值类型: 参数集合是引用类型,形式参数的操作会影响实际参数,所以不需要返回值,void 方法名称: * addFiveHeros 参数列表: ArrayList<Hero> */ public static void addFiveHeros(ArrayList<Hero> list) { // 英雄信息来自于键盘输入(Scanner) Scanner sc = new Scanner(System.in); // 创建一个Scanner用来进行键盘输入 for (int i = 1; i <= 5; i++) { //循环5次 //键盘输入英雄的三个信息 System.out.println("请输入第" + i + "英雄的名字:"); String name = sc.next(); //获取字符串 System.out.println("请输入第" + i + "英雄的攻击力:"); int attack = sc.nextInt(); //获取int数字 System.out.println("请输入第" + i + "英雄的类型:"); String type = sc.next(); Hero hero = new Hero(name,attack,type); //创建一个英雄对象 list.add(hero); //把英雄对象添加到集合当中 } } ? 在main方法对应的地方调用方法进行添加 ???? // 如果是新的队伍,就需要创建五个对像加入到集合当中 if (isNew) { //调用方法,向集合当中添加五个英雄 addFiveHeros(list); } ? 阵容展示(遍历集合)?????? 在测试类mian方法外添加遍历方法 ? ? ?? /* * 定义一个方法,用来遍历集合当中所有对象的信息,三要素: * 返回值类型:只是进行打印输出而已,所以使用void * 方法名称:showHeros * 参数列表:ArrayList<Hero> */ public static void showHeros(ArrayList<Hero> list) { for (int i = 0; i < list.size(); i++) { Hero hero = list.get(i); //当前英雄 System.out.println("英雄名字: " + hero.getName() + ",攻击力:" + hero.getAttack() + ",类型:" + hero.getType()); } } ? mian方法中调用相应的方法进行遍历
System.out.println("我们队伍的阵容是:"); // 遍历集合,输出其中的每一个对象的具体信息 showHeros(list); ? 战力计算(对象成员变量求和)?????? 在测试类mian方法外添加计算方法 /* * 定义一个方法,用来根据集合求出战斗力总值.三要素: * 返回值类型:int,代表结果总和 * 方法名称:getTotalAttack * 参数列表:ArrayList<Hero> */ public static int getTotalAttack(ArrayList<Hero> list) { int total = 0; //代表总攻击力 for (int i = 0; i < list.size(); i++) { Hero hero = list.get(i); //将每个英雄的战斗力全都累加到total当中 total += hero.getAttack(); } return total; } ? mian方法中调用相应的方法进行计算 // 统计一下总和战斗力是多少. int totalAttack = getTotalAttack(list); //根据集合的内容求出总攻击力 System.out.println("我们队伍的总战斗力是:" + totalAttack); ? 桃园结义(写文件)在测试类main方法外添加写入文件数据方法 /* * 定义一个方法,用来将集合当中的对象数据全部都写到文件里.三要素: * 返回值类型:void * 方法名称:saveToFile * 参数列表:ArrayList<Hero> */ public static void saveToFile(ArrayList<Hero> list) throws IOException { BufferedWriter bw = new BufferedWriter(new FileWriter("friends.txt")); //遍历集合,一个对象一个对象地挨个儿写 for (int i = 0; i < list.size(); i++) { Hero hero = list.get(i); //当前英雄 //需要将一个Hero对象转换成为字符串,将三个成员变量拼接成为一个字符串 //后羿,200,射手 //宫本武藏,1400,刺客 //机器人300,500,辅助 String str = hero.getName()+","+hero.getAttack()+","+hero.getType(); //将对应的字符串写道文件中 bw.write(str); bw.newLine(); //不要忘记把每个属性换行 } bw.close(); //最后别忘记关闭流 } ? mian方法中调用相应的方法进行写文件 // 判断一下集合是不是新的 if (isNew) { saveToFile(list); //调用方法将集合的信息写到文件中.报错记得抛异常 } ? 再度相约(读文件)扩展知识: 如何才能将一个字符串"100"转换成为Int数字100? 格式: Integer.parseInt(String str); 参数是字符串,返回值是int数字,可以将字符串转换成为int数字. 但是有前提:这个参数字符串必须只含有数字,格式必须是int数字! 在测试类main方法外添加读取文件数据方法 /* * 定义一个方法,用来读取文件,将数据添加到集合当中.三要素: * 返回值类型:参数集合是引用类型,所以void * 方法名称:loadFileToList * 参数列表:ArrayList<Hero> */ public static void loadFileToList(ArrayList<Hero> list) throws NumberFormatException,IOException { BufferedReader br = new BufferedReader(new FileReader("friends.txt")); String line; //代表一行字符串 while((line = br.readLine()) != null){ //需要将字符串转换成为一个Hero对象 //首先将一个完整的字符串切分成为若干小段儿 String[] array = line.split(","); String name = array[0]; //姓名 //将字符串转换成为对应的int基本类型数字 int attack = Integer.parseInt(array[1]); //攻击力 String type = array[2]; //类型 //根据三条信息创建一个英雄对象 Hero hero = new Hero(name,type); list.add(hero); //将对象添加到集合当中 } br.close(); //不要忘记关闭流 } ? mian方法中调用相应的方法进行读文件 // 2.读文件,把数据加到集合当中. loadFileToList(list); ? 接口现实生活的接口举例
? ? ? 面向对象的接口思想? ? ? ? 使用接口的好处? ? ? ? 格式与组成部分
//接口的基本定义格式: public interface 接口名称 { //... } ? 接口当中可以包含的组成部分有:
抽象方法定义
/* * 如何定义一个抽象方法: * public abstract 返回值类型 方法名称(参数类型 参数名称); * 注意: * 1.接口中的抽象方法,修饰如果写必须是:public abstract * 2.接口中的抽象方法,修饰符可以省略不写,默认是:public abstract * 3.抽象方法只有方法头,不能有方法体大括号. */ public interface Animal { //定义了一个抽象方法,吃东西 public abstract void eat(); //定义了另一个抽象方法:睡觉 //省略了public abstract也照样是抽象方法 /*public abstract*/ void sleep(); }
? ? ? 实现类的定义/* * 如果想要使用定义好的接口,必须有一个接口的"实现类". * 定义实现类格式为: * * public class 实现类名称 implements 接口名称 { * //一定要覆盖重写所有的抽象方法 * } * 什么是覆盖重写(Override)抽象方法? * 1.将接口当中的抽象方法抄写过来 * 2.去掉abstract关键字 * 3.写上大括号方法体 * * Cat就是Animal接口的实现类,Cat类实现了Animal接口. */ public class Cat implements Animal { public void eat() { System.out.println("猫吃鱼"); } public void sleep() { System.out.println("猫睡觉"); } } ? 与实现类的基本使用
如何使用接口与实现类? * * 创建: * 接口名称 引用名 = new 实现类名称(); * * 调用: * 引用名.抽象方法名(参数); * * 注意: * 1.左边是接口类型,那么只能调用接口当中定义好的内容,不能调用右侧实现类当中特有的内容.(接口隔离) * 2.当调用接口当中的抽象方法时,真正进行运行的是右侧new的时候类的具体方法内容. * 总结:调用的时候看左边,运行的时候看右边. */ //实现类添加特有的方法: public class Cat implements Animal { //省略吃睡两个方法; //这是一个Cat自己特有的方法,接口当中并没有定义 public void catchMouse() { System.out.println("猫抓老鼠"); } } //接口调用特有方法 错误! public class Demo01Interface { public static void main(String[] args) { //创建: Animal cat = new Cat(); //省略调用吃睡两个方法 //cat.catchMouse(); //错误!无法调用右侧实现类专有的方法,只能调用接口的方法. } } ? 面向接口编程
//添加一个狗类 public class Dog implements Animal{ public void eat() { System.out.println("狗吃骨头"); } public void sleep() { System.out.println("狗睡觉"); } //特有方法 public void watchHouse() { System.out.println("狗看家"); } } /* * 使用接口作为左侧类型的好处所在: * 屏蔽掉了右侧的个性特有的内容,达到隔离,统一的目的. * * 面向接口编程: * 如果使用的功能,接口已经可以满足,那么就不在乎具体的类是谁,只在乎接口即可. */ public class Demo02Interface { public static void main(String[] args) { Cat cat = new Cat(); // 创建了一只猫 method(cat); // 将猫对象传递给方法,间接使用其中的eat和sleep方法. Dog dog = new Dog(); // 创建了一只狗 method(dog); } // 使用接口作为参数类型,这样就不区分到底是猫还是狗. public static void method(Animal animal) { animal.eat(); animal.sleep(); // animal.watchHouse(); //错误!无法调用狗的专有的方法 // animal.catchMouse(); //错误!无法调用猫的专有的方法 } } ? 不必要的接口实现类//定义一个计算器的接口 public interface Calculator { public abstract int sum(int a,int b); } //计算器接口的实现类 public class CalculatorImpl implements Calculator{ public int sum(int a,int b) { int result = a + b; return result; } } public class Demo01Calc { public static void main(String[] args) { //首先使用接口的格式来创建了一个计算器对象 Calculator calculator = new CalculatorImpl(); //将计算器对象交给method方法去使用 method(calculator); } //参数是接口类型,计算器接口 public static void method(Calculator calculator) { int result = calculator.sum(10,20); System.out.println("结果是:"+result); } } ? Lambda表达式体验Lambda表达式(新世界大门)
//定义一个计算器的接口 public interface Calculator { public abstract int sum(int a,int b); } //Lambda 函数式 public class Demo01Calc { public static void main(String[] args) { //将计算器对象交给函数式 method方法去使用 method((a,b) -> a + b); } //参数是接口类型,计算器接口 public static void method(Calculator calculator) { int result = calculator.sum(100,200); System.out.println("结果是:"+result); } } ? 理解Lambda的语义//Lambda 函数式 public class Demo01CalcLambda { public static void main(String[] args) { method((a,b) -> a + b); //Lambda表达式:(a,b) -> a + b //method方法需要一个Calculator接口类型的参数 //Lambda表达式就是充当了Calculator接口类型的参数 //初步理解: //1.Lambda表达式前面的小括号,其实就是接口抽象方法的小括号. //2.箭头代表拿着小括号的数据做什么事情,是一个指向的动作. //3.箭头后面就代表拿到了参数之后做什么事情. //Lambda表达式的语义本身就代表了怎么做这件事情,没有对象的概念在里面.(更加简单直观.) } //参数是接口类型,200); System.out.println("结果是:"+result); } } ? 函数式接口/* * Java当中使用Lambda表达式的前提是:必须有"函数式接口". * * 概念:有且仅有一个抽象方法的接口,叫做函数式接口. * 可以同时有其它:默认方法、静态方法.....但不能再有抽象方法了 * * 如何才能万无一失地检测一下当前接口是不是函数式接口呢? * 用一个固定的格式写在public interface之前一行即可: * @FunctionalInterface * public interface 函数式接口名{ * //... *} */ @FunctionalInterface public interface Calculator { public abstract int sum(int a,int b); } ? 标准格式/* * Lambda表达式要想使用,一定要有函数式接口的推断环境。 * 1.要么通过方法的参数类型来确定是哪个函数式接口 * 2.要么通过赋值操作来确定是哪个函数式接口 * Lambda的格式就是为了将抽象方法,翻译成为以下三点: * 1.一些参数(方法参数) * 2.一个箭头 * 3.一些代码(方法体,大括号) * 例如抽象方法: * public abstract int sum(int a,int b); * 翻译成为Lambda的标准格式: * (int a,int b) -> { return a + b;} */ public class Demo03Lambda { public static void main(String[] args) { method( (int a,int b) -> { return a + b;} ); } public static void method(Calculator calculator) { int result = calculator.sum(100,200); System.out.println("结果是:"+result); } } ? 上下文推断public class Demo03Lambda { public static void main(String[] args) { //调用方法的时候,参数类型是函数式接口,所以Lambda可以推断出来是哪个接口 method( (int a,int b) -> { return a + b;} ); System.out.println("============================"); //也可以根据赋值语句左侧的类型来进行Lambda上下文推断 Calculator param = (int a,int b) -> { return a + b ; }; method(param); // (int a,int b) -> { return a + b ; }; //错误写法! 没有上下文环境,Lambda就无法推断是哪个函数式接口 } public static void method(Calculator calculator) { int result = calculator.sum(100,200); System.out.println("结果是:"+result); } } ? 简便格式//注意!不管写不写@FunctionalInterface,只要是有且仅有一个抽象方法的接口,就是函数式接口! @FunctionalInterface public interface MyInter { //作用:将参数++,然后返回结果 public abstract int singlePlus(int num); } /* * 在Lambda表达式当中,凡是可以推导的,都是可以省略的. * 1.Lambda表达式当中的参数类型可以省略不写. * 2.如果参数有且只有一个,那么小括号可以省略. * 3.如果语句只有一个,那么大括号和return也可以省略. */ public class DemoLambdaSimple { public static void main(String[] args) { //标准格式: method( (int x) -> { return ++x; } ); //省略参数类型: method( (x) -> { return ++x; } ); //省略参数小括号: method( x -> { return ++x; } ); //省略大括号和return关键字: method( x -> ++x ); } public static void method(MyInter inter) { int result = inter.singlePlus(10); System.out.println("结果:" + result); } } ? 知识总结1.使用接口的好处:通用性、隔离性。 2.定义接口的格式:
public interface接口名{ //... } ? 3.接口的组成部分: a)抽象方法【核心内容】 b)常量 c)默认方法(Java8) d)静态方法(Java8) e)私有方法(Java9) 4.定义一个抽象方法,格式: public abstract 返回值类型 方法名(参数类型参数名); a)修饰符 public abstract也可以省略不写 b)抽象方法不可以写方法体大括号 5.接口要想使用,一定要有一个实现类。如何实现?
public class类名称implements接口名{ //覆盖重写所有的抽象方法 } ? 6.如何覆盖重写(Override)抽象方法呢?一般步骤: a)将抽象方法抄写过来 b)去掉abstract关键字 c)写上方法体 7.使用接口和实现类的一般格式: a)创建:接口名称 引用名 = new实现类名称(); b)调用:引用名.抽象方法名(参数); c)注意: i. 只能调用左侧接口当中定义好的方法,无法调用右侧类当中特有的方法。 ii. 运行程序的时候,方法的执行者其实是右边的类对象。 iii. "调用的时候看左边,但是运行的时候看右边”。 8.从Java8开始,没有接口的实现类,也可以直接使用接口:Lambda表达式。Lambda表达式就替代了实现类。 9.Lambda使用前提:一定要有函数式接口才能用,没有函数式接口就不能用Lambda表达式。 10.函数式接口:有且仅有一个抽象方法的接口。无所谓有没有@FunctionalInterface,这是一个可选的检测手段而已。 11.使用Lambda必须有推断环境: a)要么根据参数类型传参来推断函数式接口。 b)要么就根据赋值语句左侧类型来推断函数式接口。 12.Lambda表达式的标准格式:(int a,int b) -> { return a+b; } a) 一些参数(方法的参数) b)一个箭头 c)一些代码(方法体) 13.Lambda表达式的简便格式: (int num) -> { return ++num; } (num) -> { return ++num; } num -> { return ++num; } num -> ++num; a)参数的类型可以省略。 b) 如果有且仅有一个参数,那么小括号可以省略 c)如果有且仅有一个语句,那么大括号和return也可以省略 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |