• 渣渣小本求职复习之路每天一博客系列——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.也许,这就是成长的代价

  • 相关阅读:
    Ubuntu16.04下搭建LAMP环境
    关于下载SAE日志签名认证的方法——PHP版
    时隔这么长时间,又回来写博客了
    转战网站后台与python
    学习之路
    周末随笔
    Shell基础-环境变量配置文件
    关于骑行
    MYSQL 8.0 linux安装步骤
    一个golang项目笔记 (二) 动态加载库
  • 原文地址:https://www.cnblogs.com/levenyes/p/3409368.html
Copyright © 2020-2023  润新知