关于java&面向对象
这学期才接触java,以前学了C语言,没有接触过面向对象编程,所以一开始对于怎么写,以及java语法有点不太适应。
关于类:
- 类似C语言里面的结构体struct,构造函数,方法都挺相似的。
- 一开始并不适应面向对象程序编程,所以一开始的代码还是以结构化语言的思维方式去思考问题,丑态百出。
- 对问题进行拆分,对问题的主题进行合理的抽象化能大大简化代码的复杂和代码的流畅度。
关于Java语法
- 一开始接触Java我认为Java和C语言最大的区别有两个,一个是对主体的抽象和剥离,另一个就是Java对指针的隐藏和半自动的快捷性以及隐性危险性。
- 对于两个基本数据相等要特别小心区分==和equals()方法的效果和原理。
- 特别要认清楚那些是传参数还是传地址,应该得对基本数据的类型有所了解。
自己的感悟:对于OOP(Java)我学习的不多,好多方面都有欠缺,没有花时间去啃书,做题目和打代码,以后再开逐渐的展开这方面的个人看法和感悟。
目录:
- 面向对象程序设计A---第1次题目集:7-7 判断三角形类型
- 面向对象程序设计A---第2次题目集:7-1 菜单计价程序-1
- 面向对象程序设计A---第2次题目集:7-2 菜单计价程序-2
- 面向对象程序设计A---第3次题目集:7-1 菜单计价程序-3
面向对象程序设计A---第1次题目集:7-7 判断三角形类型
难度
:***
类图
:无
此处为Pta.1-7中的Source Monitor 图表
代码行数 语句行数 分支占比 方法调用 注释占比 类和接口占比 平均类方法数 平均函数语句
此处为Pta.2-1中的PowerDesigner图表
代码如下
:
import java.util.*; public class Main { public static void main(String[] args) { Scanner in = new Scanner (System.in); double x = in.nextDouble(); double y = in.nextDouble(); double z = in.nextDouble(); int p=0; if (x<1 || y<1 || z<1 || x>200 || y>200 || z>200) System.out.println("Wrong Format"); else if (x+y<=z || y+z<=x || x+z<=y) System.out.println("Not a triangle"); else if (x==y && x==z) System.out.println("Equilateral triangle"); else if ((x==y && x!=z) || (x==z && x!=y) || (y==z && y!=x))/*等腰三角形 */ { if ((x*x+y*y-z*z<0.0001) || (x*x+z*z-y*y<0.0001) || (y*y+z*z-x*x<0.0001))/*直角三角形*/ { System.out.println("Isosceles right-angled triangle"); p++; } else System.out.println("Isosceles triangle"); } else if (p==0 && ((x*x+y*y-z*z<0.0001) || (x*x+z*z-y*y<0.0001) || (y*y+z*z-x*x<0.0001)))/*直角三角形*/ { if((x==y && x!=z) || (x==z && x!=y) || (y==z && y!=x))/*等腰三角形*/ { System.out.println("Isosceles right-angled triangle"); } else System.out.println("Right-angled triangle"); } else if (p==0) System.out.println("General triangle"); } }
测试点如下
:
分析
:
第一部分:首先对输入的三个数进行相对应的数值范围判断,判断各个数值,三角形条件输出对应结果。
第二部分:判断三条边是否符合等边三角形,再来判断直角三角形和等腰三角形。
第三部分:输出对应三角形的结果。
难点
:
这里判断直角三角形的if条件是小于0.0001,而不是==0,是因为double浮点数运算的情况会产生误差,因为计算机运算是将10进制转为2进制进行运算,再转为10进制进行输出,而2进制运算无法完全准确地表达出算数的乘除关系。
总结
:
应该清楚分析题目要求的功能,能够实现对三角形的判断和对每种三角形判断的前后有所考虑。
面向对象程序设计A---第2次题目集:7-1 菜单计价程序-1
难度
:***
类图
:(这里我直接只用多重判断来实现对应功能,所以没有建立类)
此处为Pta.2-1中的Source Monitor 图表
代码行数 语句行数 分支占比 方法调用 注释占比 类和接口占比 平均类方法数 平均函数语句
此处为Pta.2-1中的PowerDesigner图表
代码如下
:
import java.util.*; public class Main { public static void main (String[] args) { Scanner in = new Scanner (System.in); String dish; String[] dish_Nothing = new String[10]; dish = in.next(); double totalMoney = 0; int size = 0; int number = 0; while (!dish.equals("end")) { switch (dish) { case("西红柿炒蛋"): size = in.nextInt(); totalMoney = totalMoney + 15 * (0.5 * (size + 1)); break; case("清炒土豆丝"): size = in.nextInt(); totalMoney = totalMoney + 12 * (0.5 * (size + 1)); break; case("麻婆豆腐"): size = in.nextInt(); totalMoney = totalMoney + 12 * (0.5 * (size + 1)); break; case("油淋生菜"): size = in.nextInt(); totalMoney = totalMoney + 9 * (0.5 * (size + 1)); break; default: size = in.nextInt(); dish_Nothing[number] = dish.substring(0); number++; break; } dish = in.next(); } for (int i = 0; i<number ; i++){ System.out.println(dish_Nothing[i] + " does not exist"); } System.out.println(Math.round(totalMoney)); } }
测试点如下
:
分析
:
第一部分:首先读入菜单的菜名,并存入长度为10的数组里
第二部分:当输入点菜数据的时候,用一个while循环把每一个订单的总钱数相加,并把end作为退出循环的依据
第三部分:输出错误的菜品点菜提示
第四部分:最后输出总钱数
总结
:
该题目的难度不难,菜品的单价已经给出,只需要根据输入的String来判断是否采取加上总价钱还是输出“*** does not exist”,同时定义一个总价钱的变量来存储全部订单的价钱。
面向对象程序设计A---第2次题目集:7-2 菜单计价程序-2
类图
:
此处为Pta.2-1中的Source Monitor 图表
代码行数 语句行数 分支占比 方法调用 注释占比 类和接口占比 平均类方法数 平均函数语句
此处为Pta.2-1中的PowerDesigner图表
代码如下
:
import java.util.Scanner; public class Main { public static void main(String[] args) { Scanner in = new Scanner (System.in); int sum_Price = 0; //总价钱 int dishs_Number = 0; //菜品个数 String[] dishs_Name = new String[300]; //菜品名字 double[] dishs_Price = new double[300]; //菜品单价 int Order_Number = 0; //订单序号 String[] Order_Name = new String[300]; //订单名字 int[] Order_portion = new int[300]; //订单份额 int[] Order_num = new int[300]; //订单份数 int[] Order_Price = new int[300]; //订单单个总金额 int Order_error_Number = 0; //错误订单个数 String[] Order_error_Name = new String[300]; //错误订单 int DD_Number = 0; //删除序号 int DD_Error = 0; //错误删除 String Line = in.nextLine(); //读入输入流 while (!Line.equals("end")) { String[] Line_Arrays = Line.split(" "); if (Line_Arrays.length > 2) { boolean Order_error = true; Order_Number++; Order_Name[Order_Number] = Line_Arrays[1]; Order_portion[Order_Number] = Integer.parseInt(Line_Arrays[2]); Order_num[Order_Number] = Integer.parseInt(Line_Arrays[3]); for (int i = 1 ; i <= dishs_Number ; i++) { if (Order_Name[Order_Number].equals(dishs_Name[i])) { Order_error = false; break; } } if (Order_error) { //订单错误 Order_error_Number++; Order_error_Name[Order_error_Number] = Order_Name[Order_Number]; Order_Number--; } else { //订单正确 double Price = Order_num[Order_Number] * (0.5) * (Order_portion[Order_Number] + 1) * dishs_Price[Order_Number]; Order_Price[Order_Number] = (int) Math.round(Price); sum_Price = sum_Price + Order_Price[Order_Number]; } } //订单 else if (Line_Arrays[1].equals("delete")) { int PP = Integer.parseInt(Line_Arrays[0]); if (PP > Order_Number || PP < 1 || Order_Price[PP] == 0) { DD_Error++; } else { DD_Number = PP; sum_Price = sum_Price - Order_Price[PP]; } } //删除订单 else { boolean PPP = false; int i; dishs_Number++; dishs_Name[dishs_Number] = Line_Arrays[0]; dishs_Price[dishs_Number] = Integer.parseInt(Line_Arrays[1]); for (i = 1 ; i < dishs_Number ; i++) { if (dishs_Name[dishs_Number].equals(dishs_Name[i])) { PPP=true; break; } } if (PPP) { dishs_Number--; dishs_Price[i] = dishs_Price[dishs_Number]; } } //菜单 Line = in.nextLine(); } for (int i = 1 ; i <= Order_Number ; i++) { System.out.println(i + " " + Order_Name[i] + " " + Order_Price[i]); } for (int i = 1 ; i <= Order_error_Number ; i++) { System.out.println(Order_error_Name[i] + " does not exist"); } for (int i = 1 ; i <= DD_Error; i++) { System.out.println("delete error;"); } System.out.println(sum_Price); } }
测试点如下
:
分析
:
- 输入流,并进行循环判断,对输入数据的大小进行相对应的处理,大致分为订单,菜单,删除
难点
:
- 得熟练使用Scanner.nextLine()的作用,并利用String[ ]进行存储,学会使用Line.split(“ ”)进行行字符串的断点,同时学会使用Integer.parseInt(String)把String转换成Int类型进行价格的相加
- 得清楚判断菜单,订单,删除的判断条件,通过String[]的内容和行字符串断点的个数来进行判断,从而实行相对应的功能。
总结
:
- 这里是我不会用类的明显的错误展现,因为没有使用类和ArrayList的快捷性,所以用固定长度的数组来存储数据,就会显得特别死板和生硬,对每个输入数据的判断和使用没有类会显得特别的不舒服,这里的代码能在PTA上对可以说是运气。
踩坑心得
:
- 这里我是使用数组,有时候长度太小会无法存储题目要求的数据,太多又会占据太多的空间影响运行的速度,这里如果用ArrayList就会好好很多,也会变得很便捷。
- 一开始看到这个题目的时候,根本不清楚怎么读取不同类型的行数据。
之前都是Scanner.nextInt(),Scanner.nextDouble,Scanner.next()等方法来读取单个数据没有想到有直接读取一行的的Scanner的方法,并不清楚把String类型转为Int,Double的方法,导致我一直没办法做下去。
后面还是看了某位大佬的代码,发现有Scanner.nextLine()和Integer.parseInt()的使用,才学会利用这两个方法进行判断,总而实现对输入数据的分类和读取。
面向对象程序设计A---第3次题目集:7-1 菜单计价程序-3
类图
:
此处为Pta.2-1中的Source Monitor 图表
代码行数 语句行数 分支占比 方法调用 注释占比 类和接口占比 平均类方法数 平均函数语句
此处为Pta.2-1中的PowerDesigner图表
代码如下
:
import java.util.ArrayList; import java.util.List; import java.util.Scanner; public class Main { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); //创建menu的类 Menu menu = new Menu(); //创建order的类 Order order = new Order(menu); //读入一整行 String line = scanner.nextLine(); //判断是否跳出 while (!line.equals("end")) { //分隔字符串,变成小的字符串组 String[] lineArray = line.split(" "); //判断情况一 if (lineArray.length > 2) { if (!lineArray[0].equals("table")){ //变成整数 int orderNum = Integer.parseInt(lineArray[0]); //存储菜品名字 String dishName = lineArray[1]; //读入份额 int portion = Integer.parseInt(lineArray[2]); //读入份数 int num = Integer.parseInt(lineArray[3]); //加入订单 order.addARecord(orderNum, dishName, portion, num); } else { } } //判断情况二 else if ("delete".equals(lineArray[1])) { //Order 的 方法 order.delARecordByOrderNum(Integer.parseInt(lineArray[0])); } //判断情况三 else { menu.addDish(lineArray[0], Integer.parseInt(lineArray[1])); } //继续读入下一行 line = scanner.nextLine(); } //输出总结果 System.out.println(order.getTotalPrice()); } } //Dish类,菜品的相关数据,包括菜品名字和单价 //由于private有一大堆的set和get //以及两个初始化和一个getPrice的方法去获得对应份额的单价 class Dish { String name;//菜品名称 int unit_price; //单价 public String getName() { //得到名字 return name; } public int getUnit_price() { //得到单价 return unit_price; } public void setName(String name) { //设定名字 this.name = name; } public void setUnit_price(int unit_price) { //设定单价 this.unit_price = unit_price; } public Dish(String name, int unit_price) { //初始化 this.name = name; this.unit_price = unit_price; } public Dish() { //空的初始化,为了可以无错误的new一个Dish类 } //计算菜品价格的方法,输入参数是点菜的份额(输入数据只能是1/2/3,代表小/中/大份) int getPrice(int portion) { float b[] = {1, 1.5f, 2}; return Math.round((unit_price * b[portion - 1])); } } //Menu类,菜单的好像只有一个List的dishs菜品数组,怎么保存的不太清楚 //void addDish(Dish dish) 的作用???? //一个空的初始化 //一个searchDish查找对应菜品的方法 //一个addDish方法 对应相同的菜名,返回覆盖单价的菜对象,不同的菜名,返回新单价的对象 class Menu { private List<Dish> dishs = new ArrayList<>();//菜品数组,保存所有菜品信息 Menu() { //空的初始化,为了可以无错误的new一个Dish类 } Dish searthDish(String dishName) { //查找Dish的位置,并且返回该该Dish的对象 ,否则返回null for (Dish dish : dishs) { if (dish.getName().equals(dishName)) { return dish; } } return null; } Dish addDish(String dishName, int unit_price) { //添加一道菜品信息 for (Dish dish : dishs) { if (dish.getName().equals(dishName)) { //如果名字相等,就重新覆盖掉该菜品的单价,并返回该菜品对象 dish.setUnit_price(unit_price); return dish; } } //如果为新菜品,加添一个新的菜品,并返回该菜品对象 Dish dish = new Dish(dishName, unit_price); dishs.add(dish); return dish; } } //Record类,序号,菜品,份额,数量,删除与否,查早与否 //setNotFound,isNotFound,setDelete,isDelete的用法????? //对应的菜品类的set,get方法,orderNum,num,portion的set,get方法 class Record { private int orderNum;//序号\ private Dish d;//菜品\ private int portion;//份额(1/2/3代表小/中/大份)\ private int num;//数量 private boolean isDelete = false; //检查是否删除 private boolean notFound = false; //检查是否找到 public boolean isNotFound() { return notFound; } public void setNotFound(boolean notFound) { this.notFound = notFound; } public void setDelete(boolean delete) { // isDelete = delete; } public boolean isDelete() { // return isDelete; } public Record(Dish d, int portion) { //Record的构造器 this.d = d; this.portion = portion; } public Record(int orderNum, Dish d, int portion, int num) { //Record的另外一个构造器 this.orderNum = orderNum; //序号 this.d = d; //菜品对象 this.portion = portion; //菜品份额 this.num = num; //菜品数量 } //计价,计算本条记录的价格 int getPrice() { //返回单个菜品的总价 return d.getPrice(portion) * this.num; } public void setOrderNum(int orderNum) { //设定菜品的序号 this.orderNum = orderNum; } public int getOrderNum() { //获得菜品的序号? return orderNum; } public void setD(Dish d) { //设定菜品d this.d = d; } public Dish getD() { //获得菜品d return d; } public void setPortion(int portion) { //设定菜品份额 this.portion = portion; } public int getPortion() { //获得菜品份额 return portion; } public void setNum(int num) { //设定数量 this.num = num; } public int getNum() { //得到数量 return num; } } //***Order类*** // class Order { //私有化Menu类的menu的变量 private Menu menu; //建立private的Record类ArrayList的数组 private static List<Record> records = new ArrayList<>();//保存订单上每一道的记录 //初始化Order,把menu传进去 (构造器) public Order(Menu menu) { this.menu = menu; } //计算订单的总价 int getTotalPrice() { int sum = 0; for (Record record : records) { int price = record.getPrice(); if (!record.isDelete()) { sum = sum + price; } } return sum; } //添加一条菜品信息到订单中。 Record addARecord(int orderNum, String dishName, int portion, int num) { //通过菜单类查找dishName Dish dish = menu.searthDish(dishName); //如果没找到,返回null if (dish == null) { System.out.println(dishName + " does not exist"); return null; } //如果找到,new一个Record,存储该信息 Record record = new Record(orderNum, dish, portion, num); //加到该record里面去 records.add(record); //计算单个菜品的总价 int price = record.getPrice(); //输出结果 System.out.println(record.getOrderNum() + " " + record.getD().getName() + " " + price); //返回生成的新记录 return record; } //Delete的方法 public boolean delARecordByOrderNum(int orderNum) { for (Record record : records) { if (!record.isNotFound() && !record.isDelete() && record.getOrderNum() == orderNum) { //成功找到并且删除 record.setDelete(true); return true; } } //没找到 System.out.println("delete error;"); return false; } }
测试点如下
:
分析
:
- 这里相对上一次的菜单-2多的功能是添加了桌号,点菜时间,代点菜的折扣以及店面开业的事件。
- 这里存储时间可以用Date的类来进行存储,并利用报错来,对输入错误的时间进行对应的输出。
- 这里还得对开业时间外的订单进行对应的错误输出-->“table“+t.tableNum + “ out of opening hours”。
- 这里对折扣得按题目的要求得先对单个分量的订单金额,按四舍五入的规则,保留整数,再在份数计算的时候再按四舍五入的规则进行取整。
- 这里新出的功能是代点菜的功能,对输入的数据流得要有更多的判断,来对不同的桌号,时间,菜单,订单,删除,代点菜的输入数据的读入。
- 这里的自定义类要有Record,Mean,Main(运行),Order,Dish,Table。
每个自定义类都要有适当的关系和方法联系。
例如Record是Order的单个记录,而Order是整个菜单的全部记录,Order里面就得要有Record的ArrayList动态数组。同时Order里面得要有遍历每一个Record用来查找和删除的功能实现。
又例如Table和Record类之间也得要有相对应的关系,Table里面又得要有两种类型的Record,一个是用来存储自己Table中的Order,另一个是用来存储代点菜的Order,当计算总价格是单独利用的自己的Order,而输出Table中的全部种类菜品的单个总价格得同时加上另外一个Order的订单的数量价格,这里又得同时考虑时间对Table点菜和代点菜的影响(例如代点菜那桌的时间超过开业时间不能加进去,被代点菜的那桌如果没有或者是时间超过时间也得要省去)
踩坑心得
:
- 对于这种现实中看起来不会很难的东西得要有警惕心,对于任何题目都得要先上手,构建一个大致的框架,去浅看一下这个题目应该在那些方面得要小心,特别在旁边做出相对应的注释,来提示说明自己应该注意那些方面。
- 对于这中很难的题目得要留有大量的时间去坐,如果没有时间去慢慢磨它的话,一般来说是很难做出来,不能抱有侥幸心理。
这里我就是因为抱有侥幸心理,没有时间去慢慢磨,自己实力又不够,所以这一题我直接0分,来不及写。
- 对于各个类与各个类之间的关系要明确,在分析自定义类和自定义类里面的方法得要有清晰明确的认识。
- 在很难做的时候,得要考虑要不要建一个自定义的类来简化自己的代码,不要怕麻烦,新建立一个类在长远来看是不会浪费时间,反而是节省时间和改善自己写代码的流畅度,长痛不如短痛。
总结
:
- 深刻认识到Java面向对象程序编程(OOP)的特点,逐渐理解结构化语言(C)带来的局限性和繁琐性(相对面向对象编程语言)。
- 进一步了解到面向对象编程对事件的提取的重要性,对类的划分的合理性以及方法定义的快捷性。参考菜单-3里面每个类之间的关系,可以清楚准确定位类的位置和清楚类方法的实用性的重要性。
- 对于老师布置的题目最好要布置下来就要每天都要去慢慢磨它。
- 对于类简化代码运行有了更加深刻的理解。在菜单-3里面尤为突出合理构建类的重要性。
- 大致的思路流程没有错误,但是就是错了,可以考虑是不是精度问题,就像上面的三角形判断问题。
- 在对于界值要有特别的敏感性,得合理的提出可能导致代码运行错误的边界值可以大大提高题目正确率。