渣渣小本求职复习之路每天一博客系列——Java基础(4)
前情回顾:昨天回顾了继承与多态,还介绍了方法重写、方法重载、抽象方法、抽象类、关键字与范围等内容,最后还介绍一下GC的内容。
这两天看书越来越有感觉了,喜欢上坐在书桌前翻书的feel。这让我想起了作业本在微博上发的一段话:“在你的一生中,有一个“开窍时刻”,就是你的职业或学业带给你那些百思不得其解的问题、矛盾、纠结,在某个时刻你突然全懂了,好像夜海航行中突然阳光普照...学业、工作、生活给人生的烦恼,都烟消云散。这个时刻是成长最重要的部分,他们说享受工作与生活的乐趣,就是从那个“开窍时刻”开始。”
博客写到这个地步,已经不是为了找工作,享受过程才最重要。
——————————————————————————闲聊结束———————————————————————————
第六章:接口与多态
上一章聊过继承,提到“别滥用继承”,或者有人看到过“优先考虑接口而不是用继承”的说法。那么,什么样的情况叫滥用继承?接口又是什么东西呢?这一章,我们就来了解一下关于继承与多态的内容。
第一节:接口定义行为
如果我们要定义一个鲨鱼类,它会游泳,那么按照我们刚学过的继承,好像是可以定义一个鱼类作为父类,让鲨鱼类进行继承。到这里,是没有问题的,但,如果我们现在要定义一个Human(人)类,也会游泳,是不是又要继承鱼类呢?
1 Class Human extends Fish{
2 ...
3 }
按照继承中“is-a”的说法,我们可以理解成“人是一种鱼”!o(╯□╰)o这显然是错误的。那么,有什么办法可以解决呢?下面,我们隆重推出interface(接口)这一概念。
会游泳,我们可以定义成一种行为,或者一种能力。那么对于“定义行为”,在Java中可以使用interface关键字进行定义:
1 public interface Swimmer{
2 public abstract void swim();
3 }
如果类要实现接口,必须使用implements关键字。在定义接口时,对接口中定义的方法有两种处理方式,一是操作接口中定义的方法,二是再度将该方法标示为abstract。
第二节:接口与多态
对比继承,我们可以写出这样的代码(在昨天的博客中有引用)——
1 Animal animal1=new Cat();
2 Animal animal2=new Dog();
那么接口是否也可以进行这样的写法呢?答案是可以的。判断标准就是“右边是不是拥有左边的行为”,或者“右边对象是不是操作了左边接口”。接着说Human类和Fish类都实现了Swimmer接口,那么,我们可以这样写:
1 Swimmer swimmer1=new Human();
2 Swimmer swimmer2=new Fish();
那么,既然说到多态,我们是如何使用的呢?看以下的代码,我们就可以用同一种写法,调用不同的方法:
1 package cc.openhome;
2
3 public class Ocean {
4 public static void doSwim(Swimmer swimmer) {
5 swimmer.swim();
6 }
7
8 public static void main(String[] args) {
9 doSwim(new Anemonefish("尼莫"));
10 doSwim(new Shark("兰尼"));
11 doSwim(new Human("贾斯汀"));
12 doSwim(new Submarine("黄色一号"));
13 }
14 }
第三节:接口的默认
在Java中,在定义interface的时候,一定要把方法声明为publice abstract,不需要也不能进行方法内容的撰写。不过呢,为了方便,也可以省略public abstract。但是我们要清楚,接口里的方法都是公开(权限)并且是抽象的。
下面看一道偶尔会出现的题目:
1 interface Action{
2 void execute();
3 }
4
5 class Some implements Action{
6 void execute(){
7 System.out.println("完成指定动作");
8 }
9 }
10
11 public class Main{
12 public static void main(String[] args){
13 Action action=new Some();
14 action.execute();
15 }
16 }
“请问执行结果如何?”答案是编译发生错误。为什么呢?因为Action中定义的execute()方法默认为public abstract,而Some类在操作execute()方法时,没有用public修饰,则默认为包权限,也就是说将Aciton中的public权限方法降权限为包权限,所以编译失败。所以大家在实现接口的时候就要注意了,记得是实现方法时加上public。
另外,在接口中也可以定义public static final的枚举常数,跟定义方法一样,也可以省略public static final,但是在理解该“变量”时,一定要注意脑补加上“public static final”哟。
第四节:匿名内部类
在写Java程序的时,经常会有临时继承某个类或操作某个接口并建立实例的需求。由于这类子类或接口操作类只使用一次,不需要为这些类定义类名,这时可以使用匿名内部类(Anonymous inner class)来解决这个需求。匿名内部类的语法是这样的:
1 new 父类()|接口(){
2 //类本体操作
3 };
如果是操作某个接口,例如若Some接口定义了doService()方法,要建立匿名类实例,可以这么写:
1 Some some = new Some(){ //操作Some接口并直接产生实例
2 public void doService(){
3 System.out.println("完成指定动作");
4 }
5 };
说到匿名内部类,就不得不提一下内部类了。(本来想对比一下内部类和匿名内部类的区别的,但是一时没找到特别好的资料,先放放,等合适的时候再不上)
第五节:使用enum枚举常数
从JDK5之后就新增了enum语法,用来定义枚举常数。我们直接来看范例:
1 public enum Action{
2 STOP,RIGHT,LEFT,UP,DOWN
3 }
实际上,enum定义了特殊的类,继承自java.lang.Enum,不过这是由编译程序处理,直接撰写程序继承Enum类是会被编译程序拒绝的。而enum中列举的常数,实际上也是public static final,而且是枚举类型的实例,程序员们是无法撰写程序直接实例化枚举类型的,因为构造函数的权限设定为private,只有在类里面才可以实例化。建议大家用反编译器对.class文件进行反编译,就可以看到里面的情况到底是怎样的。
第七章:异常处理
程序员最怕的不是写不出程序,而是在测试,甚至是生产环境下因为一些想都想不到的状况引发异常或者是错误。在Java中,错误也是以面向对象的方式呈现的,都是java.lang.Throwable的各种子类实例。我们可以通过捕捉(Catch)封装错误的对象,针对该错误作出应对,给出解决方案。例如,试图返回至正常的流程、进行日志(Logging)记录,或者是以某种形式作出提醒和警告。
第一节:使用try、catch
首先,我们来看一段简单的程序,就是我们可以从控制台连续输入整数,最后输入0结束后会显示输入数的平均值:
1 package cc.openhome;
2
3 import java.util.Scanner;
4
5 public class Average {
6 public static void main(String[] args) {
7 Scanner scanner = new Scanner(System.in);
8 double sum = 0;
9 int count = 0;
10 int number;
11 while(true) {
12 number = scanner.nextInt();
13 if(number == 0) {
14 break;
15 }
16 sum += number;
17 count++;
18 }
19 System.out.printf("平均 %.2f%n", sum / count);
20 }
21 }
如果我正确地输入每个合法的数据,程序当然会跟我们预期的那样显示平均数,如图
但是,总会有输错的时候,那结果会如何呢?例如我把50输成5p了,会怎样?
看到错误的信息,以前的我一般都是挺沮丧的,因为自己写的程序出了问题嘛。但是,这段错误信息对于排除错误是很有价值的。根据提示的错误,例如图中的java.util.InputMismatchException。就是输入的内容,跟定义的数据类型不符合的意思。可是,如果用户是小白,看不懂这些信息呢?ok,我们可以用try-catch语句进行处理,给予小白用户看得懂的错误提示。看到以下代码:
1 package cc.openhome;
2
3 import java.util.*;
4
5 public class Average2 {
6
7 public static void main(String[] args) {
8 try {
9 Scanner scanner = new Scanner(System.in);
10 double sum = 0;
11 int count = 0;
12 int number;
13 while (true) {
14 number = scanner.nextInt();
15 if (number == 0) {
16 break;
17 }
18 sum += number;
19 count++;
20 }
21 System.out.printf("平均 %.2f%n", sum / count);
22 } catch (InputMismatchException ex) {
23 System.out.println("必须输入整数");
24 }
25 }
26 }
跟上次一样,输入同样的内容,会怎样呢?
第二节:异常继承架构
刚才我们提到“在Java中,错误也是以面向对象的方式呈现的,都是java.lang.Throwable的各种子类实例”,那么,现在我们来看看Throwable继承架构图
首先呢,我们要了解到错误都是会被封装为对象,设计这些错误对象都继承自java.lang.Throwable类,Throwable定义了取得错误信息以及堆栈错误(Stack Trace)等方法,它有两个子类:java.lang.Error与java.lang.Exception。
Error与其子类实例代表严重系统错误,例如硬件层面的错误啦、JVM错误或内存不足等问题。虽然我们也可以用try-catch语句对这些对象进行捕获,但是这并不是被倡导的,因为发生严重系统错误的时,Java应用程序本身是无法回复的。举个例子,如果JVM所需要的内存不足,我们可以用语句要求操作系统给JVM分配更多的内存呢?所以,Error对象抛出时,基本上就不用处理,任其传播至JVM为止,最多也就是留下日志信息。
而try-catch语句处理的错误基本上都是Exception或其子类实例,所以通常把错误处理称为异常处理(Exception handling),对于某些异常,可以用try、catch语法尝试将应用程序回到正常的轨道上(可执行状态)。值得注意的是,如果某个方法声明会抛出Throwable、Exception或子类实例,但又不属于java.lang.RuntimeException或其子类实例,就必须明确使用try-catch语法加以处理,或者用throws声明这个方法会抛出异常,否则就会发生编译错误。
Exception或其子对象,但不属于RuntimeException或其子对象,成为受检异常(Checked Exception)。受检,指的是受编译程序检查。API设计者认定,调用这个方法时,出错的几率极高,因此要求编译程序协助提醒调用API的用户明确使用语法处理,程序员无法选择要不要进行处理。与之相反的,就是非受检异常(Unchecked Exception)。
由于明天有个面试,今晚还是要复习准备一下的,所以第七章《异常处理》就先写到这里,明天再继续。其实今天的内容已经比之前多一点点了,慢慢来。
———————————————————————————第18天——————————————————————————
其实,我曾经是一个文艺少年。
1.我高中在广东深圳念的书,哪怕到了高三,也不觉得有多么紧张。不像北方的某些省份那么恐怖,听说有些学校的高三学生早上五点多起床,一直学习到晚上十一点多,几乎没有多少自己的时间。
2.在高三刚开始的时候,我突然迷上了看课外书,村上春树的小说,梁文道《常识》、《读者》,好多虚构类的文学作品,好多非虚构类的书籍,大半年下来大概在卓越亚马逊花了两千多块钱。语文突然好了起来,作文尤其写得不错,模拟考经常考第一。其他的科目也有所稳定提升,我想,大概是看书让我的内心平静了下来。上了大学,看书的频率骤然降低,也几乎很少写东西了。写到这里,我突然明白,明白自己为什么迷茫了那么一段时间。也许是因为大学的诱惑多了,不再有那么多的耐心坐下来看书做读书笔记了。最近自制力开始有点上来了,又重拾了阅读(虽然是从课外书转变到技术书)和写作的乐趣,用自己的方法,按照自己的节奏,一点一点地进步。
3.也许,这就是成长的代价