这次博客内容我想分为两个部分。第一部分是自己总结的初次接触java的一些语法、用法及思想,主要目的是帮助一些和我一样菜的菜鸟,减少一些初次接触java的理解时间,这部分当然是为后来人的。第二部分则是对作业的分析和感悟了,这一部分是写给自己的。
=== 菜鸡上路 ===
感谢观看。如果你也是初次接触学习java的话,希望本文能给你带来帮助,或让你找到共鸣。前提是要有C语言的基础哦~因为本文可不是从讲解int、if-else、while开始的哦~如果是第一次编程的话,那请在bilibili搜索“java”,有更详细的课程~话休絮繁,上路。
1.IDE选择与配置
如果你已经选择了自己的IDE,那么是不需要看这一部分的,初学学习一下简单的新建文件、运行(RUN)及相应的debug方式就可以了,至于更多的功能等待你继续探索。若没有IDE,那么我这里先暂时推荐一个比较小巧的——Editplus(当然在课程中我也同时在用功能比它多的Eclipse)。这里我不详细介绍配置方式以免使博文显得头重脚轻,不知道怎么配置的请移步这个链接:https://www.bilibili.com/video/av6588278/?p=1,里面同时也有教怎么使用命令行运行程序。
2.Hello,world!——套路,都是套路!
来来来,第一个程序走起~学语言第一步怎么能不是输出”Hello,world!”呢~先看看熟悉的C语言,再看看java:
1 #include<stdio.h> 2 void main(){ 3 printf("Hello,world!"); 4 }
1 class Hello { 2 public static void main(String[] args) { 3 System.out.println("Hello World!"); 4 } 5 }
C语言中 #include<stdio.h> 目的是为了让我们使用scanf和printf等常用的输入输出函数。java这里对于输入有对应的 import java.util.Scanner; ,程序需要输入时使用;而本程序只涉及到的输出在java中不需要import。
如果脱去 class Hello { } 这层大括号的话,你看结构就差不多了吧。(本次代码为了统一格式,在C语言里也运用了 void main() { } )至于java版本中void前面的两个词,可以理解为一种类型限定,在函数定义时根据需要添加,当然java中对于主函数是必须要这么写的,否则运行会出现如下情况:
至于主函数小括号内的参数,如果在C语言接触过命令行操作的话应该好理解,不理解没关系,就当作固定写法吧,参见上图。
接下来就是最实际的那一行代码了,我们也是可以先暂时记住。java中其实也有printf(),即 System.out.printf() ,小括号内的用法与C语言相同,如 System.out.printf("%d",a); ,而我们同时可以选择使用print()和println(),它们不需要格式化字符串,两个的区别则是,后者输出之后自动换行,及相当于末尾输出一个'\n'。
至于最外面那一层 class Hello { } ,是面向对象语言较为独特的地方——对类(class)的定义。其实面向对象的程序就是由一个一个类构成的,它们这些类的内部,则是我们之前比较熟悉的函数们,在面向对象程序中我们叫他“方法”。最初写些小程序,我们可能还是采用C语言初学时的模式,由一个main()函数做完任务,不过任务复杂起来,则需要一个一个类之间的配合。若程序中有多个类,其中也肯定有一个主类,它包含着我们的main()函数,是程序最开始要运行的代码。(这个主类的名称要和文件名(xxx.java)一致噢~)
以上内容若理解有困难,则不妨都先像初学C语言时那样,先记住,慢慢就会习惯了呢~
3.数据类型
这个问题还是要简单说一下,毕竟和C语言还是有一定区别。java中基本数据类型的定义与C基本相同,这些类型有:
byte:1个字节 short:短整型,2个字节 int:整型,4个字节 long:长整型,8个字节 float:浮点型,4个字节 double:双精度浮点型,8个字节 char:字符型,2个字节,Unicode字符 boolean:布尔类型,仅有两个值,即 true、false
(1)布尔类型
C语言中是没有专门表达逻辑真假的布尔类型的,用0的时候就表示假,用其他数字就表示真。而java中 while(1){} 这种写法是不可以的,要用布尔类型哦~
(2)精度
在使用数字常量时需要注意。比如代码 float a = 0.1; 在编译时就会报错,提示“会有损失”。原因是java默认浮点数常量为double类型,所以在转化时它会认为有精度损失。若想表示float类型常量,请加f float a = 0.1f; 。
同样地,整数常量会被默认为int类型,所以定义long型常量,请加L long b = 666L; 。
4.Scanner输入
这里我先推荐一个练习编程的网站——洛谷。我这里以网站上的一道入门练手题为例,再看一下java程序的结构吧。
题目要求很简单,输入两个int类型的数,输出两个数的和。(点击上方超链接或复制该段网址:https://www.luogu.org/problemnew/show/P1001)
1 import java.util.Scanner;//不要忘了import哦 2 class Main { 3 public static void main(String[] args) { 4 Scanner sc = new Scanner (System.in); 5 int A = sc.nextInt(); 6 int B = sc.nextInt(); 7 System.out.println(A+B); 8 } 9 }
诸如Scanner这样的“类”,我们常常使用如此的实例化格式:
A a = new A(); //A是类名
而括号中有无参数则要看这个class定义时是否需要。比如在需要输入时我们有这样的代码:
Scanner sc = new Scanner (System.in); //sc是自己起的名
然后通过 sc.next()等 获取输入内容。
sc.nextInt(),sc.nextFloat,sc.nextBoolean()…… //next加基本数据类型即读取一个该数据类型的量 sc.next() //读取一个字符串,类似于C语言的scanf(),即字符串不包含空格 sc.nextLine() //读取一个字符串,类似与C语言的gets(),即读取一行
这时请再回看上方的程序“A+B”,希望你能对程序的基本格式有了一定的了解。
5.数组
首先是定义数组的常用格式:
int[] array = new int[10];
(另外,调用 array.length 可以直接获取数组长度)
另放出我个人曾遇到过的错误。通过一个例子看出java与C的区别:
if (array[i]<S)
(1)数组越界
这行代码,由于我在前面对i++的处理不当,导致i的数值超过了数组的长度,那么这个时候java在运行过程中的处理和C语言是有区别的。C语言数组下标越界只会得到一个内存中不确定的值,不会报错;而java则是会终止运行,报出“ArrayIndexOutOfBoundsException”的错误。所以在处理数组时要注意这一点。
(2)对逻辑判断的影响
对上方代码做出更改之后,写成如下的格式:
if (i<array.length & array[i]<S)
然而程序还是报出相同的数组越界的错误。再看一眼代码就发现果然是写C语言的不良习惯导致的错误。C语言如此写逻辑表达式的话,若用按位与&,和&&结果是相同的;而java中由于会报错,所以使用按位与并不能得到结果。换为使用&&,则当前半部分为false时将不会判断后半部分的真值,直接得出false。
6.动态数组and字符串
java中有很多为大家准备好的很多好用的类~例如:
(1)ArrayList
在C语言中,若涉及到元素的动态增加或删除,我们常使用链表。在java,有一个更加方便的对象,那就是ArrayList,定义如下:
import java.util.ArrayList; ArrayList<Integer> array = new ArrayList<Integer>();
它提供插入元素(add)、删除元素(remove)等好用的方法,具体用法请用搜索引擎啦~
(2)String
java中字符串也较为灵活。其中一种定义方式如下,各种用法不再赘述,还是请用搜索引擎~
String a = "Hello,world!";
//检查字符串是否符合某种格式,或从字符串中搜索想要的片段,请学习正则表达式。
7.面向对象
面向对象是重点所在,而本次我先简单介绍一下。
通过class可以定义一个“类”,在使用的过程中通过 A a = new A(); //A是类名 来创建一个对象。
首先可以回忆一下C语言中的struct,它可以让你自己构造一个类型,其中封装了一些量。例如:
struct A{ int a; char b; };
而面向对象中的class就可以与其对比来看(虽然有些地方不一样)。class不仅能包含那些“量”(属性),还有一些“函数”(方法)。
class A{ int a; char b; boolean f(int c){ …… return d; } }
而面向对象和面向过程程序的区别我想用一个例子来展示:
1 /* C语言 侦探破案过程 */ 2 void main(){ 3 发现案发现场(); 4 报警(); 5 取证(); 6 嫌疑人询问(); 7 嫌疑人排除(); 8 嫌疑人确定(); 9 破案成功(); 10 } 11 void 发现案发现场(){ 12 去卫生间; 13 发现一个尸体; 14 尖叫; 15 小五郎和柯南闻声赶来; 16 小五郎和柯南露出惊讶的深情; 17 小五郎说赶紧报警; 18 } 19 //其他函数略
1 /* java 侦探破案过程 */ 2 class Main { 3 public static void main(String[] args) { 4 …… 5 lan.find();//毛利兰.发现尸体 6 lan.callPolice();//毛利兰.报警 7 Police police = new Police();//警察来了 8 police.search();//警察.调查 9 police.interrogate(suspect1,suspect2,suspect,3);//警察.询问 10 mouri.pointTo(suspect1);//毛利小五郎.说嫌疑人1是凶手 11 conan.shot(mouri);//柯南.把小五郎麻醉 12 conan.solve();//柯南.解决案子 13 } 14 } 15 class Lan { 16 void find(){ 17 System.out.println("aaaaaaaaa"); 18 } 19 void callPoice(){ 20 …… 21 } 22 } 23 //其他类略
总之可以理解为各司其职。这些就是每个对象各自使用各自方法的例子,真正应用还是要在具体的练习中自己领悟。
8.引用类型
这一点也是java中需要新手了解的关键问题之一了。我想再次通过C语言和java的对比来展现这一点。下面的程序内容为建立长度为5的结构体/对象数组,逐个读取并存到数组中。
1 #include<stdio.h> 2 struct A { 3 int number; 4 }; 5 void main(){ 6 struct A array[5]; 7 struct A a; 8 int i; 9 for (i=0;i<5;i++){ 10 scanf("%d",&a.number); 11 array[i] = a; 12 } 13 for (i=0;i<5;i++){ 14 printf("%d",array[i].number); 15 } 16 }
1 import java.util.Scanner; 2 class A { 3 int number;//仅作为示例,真正编程不要如此定义 4 } 5 class Main { 6 public static void main(String[] args) { 7 A[] array = new A[5]; 8 A a = new A(); 9 int i; 10 Scanner sc = new Scanner(System.in); 11 for (i=0;i<5;i++){ 12 a.number = sc.nextInt(); 13 array[i] = a; 14 } 15 for (i=0;i<5;i++){ 16 System.out.print(array[i].number); 17 } 18 } 19 }
为了做对比,以上两个程序的代码顺序我写成了完全相同的形式。而当你执行过后你会发现,结果完全不同。例如输入"1 2 3 4 5"
上述C语言程序的输出结果为"12345";java程序的输出结果为"55555"。
造成这种现象的主要原因,是因为struct是传“值”,而class是传“址”。a在“new”的时候在内存里开一块区域,而a本身存的是相当于指向内存中该区域的“指针”,java中我们叫做这个对象的“引用”。也就是说这个数组的5个元素其实都存了相同的东西,那就是a的“地址”,对a进行最后一次修改之后,内容是“5”,而array的5个元素都指向这个“5”,所以会输出这样的情况。即上述java程序可以对应于下面的C程序:
1 #include<stdio.h> 2 #include<stdlib.h> 3 struct A { 4 int number; 5 }; 6 void main(){ 7 struct A *array[5]; 8 struct A *a = (struct A*)malloc(sizeof(struct A)); 9 int i; 10 for (i=0;i<5;i++){ 11 scanf("%d",&a->number); 12 array[i] = a; 13 } 14 for (i=0;i<5;i++){ 15 printf("%d",array[i]->number); 16 } 17 }
java程序中改成传“值”可达到预期效果:
array[i] = new A(); array[i].number = a.number;//仅作为示例,真正编程不要如此使用对象的属性
其中值得注意的是,若java中数组要如此使用,应对数组每个元素进行以上的“new”操作,以在内存中开出对应空间。
以上是我列出的初次从C到java时需要注意的若干重点,即 菜鸡上路(1),也算是一种教程。下一篇我还将继续写一些比这次略深一层的内容(初步打算对面向对象的一些关键点展开讨论)。若有C语言基础,那么看第一篇的这些内容我认为是可以写一些简单的java程序的。当然我只是从我学习的角度看的,每个人在学习过程中遇到的问题可能不尽相同,如果你是新手且初学遇到一些问题,那么可以发出来大家一起讨论,祝学习愉快!
=== 作业分析 ===
嗯……作业分析还是要做的呀。其实有很多共性问题我在上面也提到了,下面就单针对这三次作业分析一下,内容可能就会少一些。
一、Homework01——一元多项式加减
嗯……第一次写java程序(如果Helloworld那种不算的话),所以还是用了比较长的时间。比如正则表达式就学了一段时间,学完之后写java再回过头来写C,发现没有正则表达式还有点不知所措。这几次作业都可以感受到java里import进来的各种类超级好用,谁用谁知道。
1.代码结构和风格转变
第一次作业没有要求要有哪些类以及不能出现public属性,所以这次的作业完全是按照我心中对面向对象最初的猜想写的,虽然肯定和规范的写法有些出入,但是还是划分了一些类,每个类做一些自己的事。比如:
LongExpression类我让他读取最初的一整个字符串,并对它进行初步解析,即用正则表达式匹配只要是符合±{}±{}……的形式即可,不要求大括号内部的内容。这样滤掉出现在大括号外的ERROR之后,就用正则表达式.find()找到每个大括号,进行第二步的解析。
Multinomial类我用来做第二步解析,正则表达式匹配只要是符合±{(),(),……}的形式即可,不要求小括号内部的内容。过滤掉小括号外部的ERROR之后,用正则表达式.find()找到每个小括号,进行第三步的解析。
Term顾名思义就是对每一项做最终的解析了,提取系数和指数,加到数组中。这里就看到我使用的是 index[term.index]+=(option)?(-term.coe):term.coe; ,是直接使用term的系数属性和指数属性,还是不规范,主要原因是没有了解到该方面知识。这种情况在以后的作业中没有出现。
作为第一个程序,总体设计基本达到最初的期望值。
2.Bug分析
没有加入最大数量是50和20的限制。其实这次公测报出的错误完全是由于我对课程流程的不理解导致的。由于是第一次作业,我还没有弄清楚OJ的事,所以并没有设计完全。这种情况在以后的作业没有出现。
至于我拿到的代码,是写在一个类里的,不过也并无大问题,功能也都实现了,只是在数据量比较大的时候没有hold住。
二、Homework02——傻瓜电梯
我认为这一次作业的设计要优于其他两次。第一次作业由于是初次写java,还没有找到感觉;第三次作业则是由于增加了一些功能,有没有做到很好地规划,导致参数传来传去,而且代码量也增加了很多。而这一次作业就是:Command类读取请求并解析是否符合规范,CommandQueue则将Command的数据存入数组,Controller读请求序列,将电梯应做的动作发送给Elevator类。这是在结构上,另外本程序也没有使用public属性,算是更深一层地习惯了面向对象编程。
至于bug方面,只出了一个,即输出过程中出现了科学计数法……所以还是细节上的问题没掌握。
没有找到对方的错误。
三、Homework03——可稍带电梯
1.结构分析
这次问题就有点多……由于是在上次作业的基础上加的代码,也没有做一定的规划,这次的作业在结构上主要存在两个问题:
(1)对类的功能划分不完善。新增加了捎带队列,放入Scheduler中,与此同时加入了判断时间的一些数组,经常需要在Scheduler和CommandQueue中来回传递。写代码的时候各种逻辑考虑不过来,所以还出现了本来不需要传结果传了的情况,足以看出写代码时思路的混乱。
(2)部分方法中分支冗杂。例如表中呈现的:
比如这里Elevator的goTo()方法,就是因为判断FR或ER以及UP或DOWN导致分支庞大。另外Scheduler的initialize()和insert()方法同样也出现了该情况。
2.Bug
在测试对方程序的时候,我拿出了之前准备的测试用例,结果竟然真的发现了和我的程序不一样的结果。本来是比较欣喜,结果仔细一看,是我的程序错了ヾ(。 ̄□ ̄)ツ゜゜゜。仔细分析发现是,当新请求发生时间在上次执行完所有请求之后,计算时间出了问题。其实第二次作业这里是没问题的,在第三次作业进行修改的时候,因为一些代码的位置发生了变化,我又没有考虑到这方面的问题,导致了错误。所以这其实还是归结到上面说的代码结构的问题,之后写程序会注意。
四、总结
我认为最重要的还是架构、架构、架构,第三次明显结构混乱了许多,以后再写程序要再多从整体的角度看一下。另外还是要多多学习大佬们的思路和技术,尽量在之后的作业中能把结构写得更清晰一点。
还有就是做第三次作业的时候对指导书的理解不够导致写代码多花了一些时间,以后还是要多研究指导书啊!