第8章:接口与抽象类(深入多态)

抽象类

用abstract关键字声明抽象类,抽象类不能用new 关键字进行实例化。在设计继承结构时,必须决定清楚什么类是抽象类,什么类是具体类。编译器不会让你初始化一个抽象类。抽象类,除了被继承以外,是没有其它任何用途的。抽象类中,必须包含有抽象方法,还可以包含非抽象方法。

抽象方法

即用 abstract关键字声明的方法,抽象方法没有方法实体,即没有具体的实现过程。拥有抽象方法的类,必须声明为抽象类。抽象类中的抽象方法,用于规定一组子类共同的协议。

abstract class Animal {// 抽象方法,没有方法体public abstract void eat();
}

在继承过程中,具体类必须实现抽象父类的所有抽象方法

抽象方法没有具体的方法体,它只是为了标记出多态而存在。在覆写抽象父类的抽象方法时,方法名、参数列表必须相同,返回值类型必须兼容。Java很在乎你是否实现了抽象类的抽象方法。

public class Canine extends Animal {// 覆写抽象类的抽象方法public void eat() {System.out.println("Canine,会吃食物!!");}// 非继承的方法public void roam() {}
}
多态的使用

在Java中,所有类都是从Object这个类继承而来的,Object是所有类的源头,它是所有类的父类。Object有很有用的方法,如 equals(), getClass(), hashCode(), toString()等。

Object类,是抽象类吗? 答:不是,它没有抽象方法。
是否可以覆写Object中的方法? 答:Object类中带有 final关键字的方法,不能被覆写。
Object类有什么用? 答:用途一,它作为多态可以让方法应付多种类型的机制,以及提供Java在执行期对任何对象都需要的方法实现。另一个用途,它提供了一部分用于线程的方法。
既然多态类型这么有用,为什么不把所有的参数类型、返回值类型都设定为Object? 答:因为Java是强类型语言,编译器会检查你调用的是否是该对象确实可以响应的方法。即,你只能从确实有该方法的类中去调用。
Object dog = new Dog();
dog.toString(); // 这可以通过编译,因为toString()是Object类中自有的方法。
dog.eat(); // 这将无法通过编译,因为dog是Object类型,它调用的eat()方法在Object类中没有。
在使用多态时,要注意对象多种类型之间的差异。如下代码:

Dog dog1 = new Dog();
Animal dog2 = new Dog();
Object dog3 = new Dog();

注意这三个dog对象的区别: dog1 拥有 Dog / Animal / Object中所有的方法。dog2 拥有 Animal / Object 中的方法,不能调用 Dog 类特有的方法。 dog3 只拥有Object 中的方法,不能调用 Animal / Dog类中的方法。这就是在使用多态过程中,需要特别注意的问题。

那么该如何把 Object 类型的 dog转化成真正的 Dog 类型呢?

if (dog2 instanceof Dog) {Dog dog4 = (Dog)dog2;
}
if (dog3 instanceof Dog) {Dog dog5 = (Dog)dog3;
}

// 此时,dog4 / dog5 就是真正的 Dog类型了。

接口

接口,是一种100%纯抽象的类。接口中的所有方法,都是未实现的抽象方法。

接口的作用

接口存在的意义,就是为了解决Java多重继承带来的致命方块问题。为什么接口可以解决致命方块的问题呢?因为在接口中,所有方法都是抽象的,如此一来,子类在实现接口时就必须实现这些抽象方法,因此Java虚拟机在执行期间就不会搞不清楚要用哪一个继承版本了。

// interface关键字,用于定义接口
public interface Pet {public abstract void beFriendly();public abstract void play();
}
// 继承抽象父类 Animal类, 实现 Pet接口
public class Dog extends Animal implements Pet {// 实现接口中的抽象方法public void beFriendly() {System.out.println("实现 Pet接口中的 beFriendly()方法");}// 实现接口中的抽象方法public void play() {System.out.println("实现 Pet接口中的 play()方法");}// 覆写抽象父类中的抽象方法public void eat() {System.out.println("覆写抽象父类中的eat()抽象方法");}
}

同一个类,可以实现多个接口!

public class Dog extends Animal implements Pet, Saveable, Paintable { ... }
super关键字

super代表父类,在子类中使用 super关键字指代父类,通过super还可以调用父类的方法。

// 抽象父类
abstract class Animal {void run () {}
}
// 继承父类
class Dog extends Animal {void run () {super.run();  // 这里,调用并执行父类的 run() 方法// do other things}
}
Dog d = new Dog();
d.run();    // 这调用的是子类Dog对象的 run()方法。

第9章:构造器与垃圾收集器

堆(heap)、栈(stack)

当Java虚拟机启动时,它会从底层操作系统中取得一块内存,以此区段来执行Java程序。实例变量保存在所属的对象中,位于堆上。如果实例变量是对象引用,则这个引用和对象都是在堆上。

构造函数与对象创建的三个步骤

对象创建的三个步骤:声明、创建、赋值。
构造函数,让你有机会介入 new 的过程。构造函数,没有显示的指定返回值类型,构造函数不会被继承。如果一个类,没有显示地编写构造器函数,Java编译器会默认地为该类添加一个没有参数的构造器函数。反之,Java编译器则不会再添加任何默认的构造函数。

Dog dog = new Dog();

构造器函数重载

即一个类,有多个构造器函数,且它们的参数都不能相同,包括参数顺序不同、或者参数类型不同、或者参数个数不同。重载的构造器,代表了该类在创建对象时可以有多种不同的方式。

public class Mushroom {
// 以下五个构造器,都是合法的,即构造器重载
public Mushroom() {}
public Mushroom( int size ) {}
public Mushroom( boolean isMagic ) {}
public Mushroom( boolean isMagic, int size ) {}
public Mushroom( int size, boolean isMagic ) {}
}

构造函数链 super()

构造函数在执行的时候,第一件事就是去执行它的父类的构造函数。这样的链式过程,就被称为“构造函数链(Constructor Chaining)”。

class Animal {public Animal() {System.out.println("Making an Animal");}
}class Dog extends Animal {public Dog() {super();    // 如果没有这句,Java编译器会默认添加上这句,即调用父类的无参构造器System.out.println("Making an Dog");}
}public class ChainTest {public static void main(String[] args) {System.out.println("Starting...");Dog d = new Dog();}
}

如果一个类,没有显示地书写构造器函数,Java编译器会为它添加上默认的无参构造器。如果在一个子类的构造器中没有使用super()调用父类的某个重载构造构造器,Java编译器会为这个子类的构造器默认添加上super(),即在子类的构造器函数中调用父类的无参构造器。
父类的构造器函数,必须在子类的构造器函数之前调用。在子类构造器函数中调用父类构造器时,必须与父类构造器的参数列表一致。

在类中,this 和 super 有什么区别? this() 和 super() 有什么区别?

使用 this() 可以在某个构造函数中调用同一个类的另外一个构造函数。 this() 只能在构造函数中使用,并且必须是第一行。 this() 和 super() 不能同时使用。

class Car {private String name;// 父类的有参构造器public Car(String name) {this.name = name;System.out.println(name);}
}class MiniCar extends Car {// 构造器public MiniCar(String name) {// 调用父类的有参构造器super(name);}// 另一个构造器public MiniCar() {// 调用同一个类的另一个构造器this("这里子类汽车的名称");}
}public class TestThis {public static void main(String[] args) {MiniCar mc1 = new MiniCar();MiniCar mc2 = new MiniCar("动态的名字");}
}
对象、变量的生命周期

对象的生命周期决定于对象引用变量的生命周期,如果引用还在,则对象也在;如果引用死了,对象会跟着被 GC 回收。当最后一个引用消失时,对象就会变成可回收的。
局部变量,只存活在对象的方法中,方法结束,局部变量就死了。
实例变量,存活在对象中,它的生命周期与对象一致。

Life 和 Scope的区别

Life,只要变量的推栈块还存在于堆栈上,局部变量就算是活着。局部变量会活到方法执行完毕为止。
Scope,局部变量的作用范围只限于它所在的方法中。当该方法调用别的方法时,该局部变量还活着,但不在目前的范围内,当被调用的其它方法执行完毕后,该总局变量的范围又跟着回来了。


第10章:数字与静态性

Math的特点

在 Java 中没有东西是全局(global)的。但,Math 方法是接近全局的方法。Math不能用来创建实例变量。因为Math是用来执行数据计算的,所以没有必要创建对象来进行数学计算,创建对象是浪费内存空间的做法。Math中所有方法都静态方法。
···java
Long a = Math.round(46.25);
int b = Math.max(2, 3);
int c = Math.abs(-500);
···

非静态方法与静态方法

静态方法,使用 static 关键字声明,以类的名称进行调用。
非静态方法,以实例对象进行调用,没有 static修饰。
Math类是如何阻止被实例化的?

Math类阻止被实例化,采取的策略是使用 private 修饰了其构造函数。

静态变量,会被同类的所有实例共享,因为它隶属于类。静态变量,在类第一次载入时初始化。静态变量,会在所有对象创建之前进行初始化,也会在任何静态方法执行之前就初始化。静态变量,只能由类来调用。
非静态变量,只被单个对象独有,它隶属于实例。非静态变量,在类实例化时进行初始化。
实例对象不会维护静态变量的拷贝,静态变量由类进行维护。非静态变量由实例对象进行维护。

class Duck {// 非静态变量,属于对象private int size = 0;// 静态变量,属于类private static int count = 0;public Duck() {size++;count++;System.out.println("size " + size);System.out.println("count " + count);}public void setSize(int s) {size = s;}public int getSize() {return size;}
}public class TestStatic {public static void main(String[] args) {Duck d1 = new Duck();  // size = 1     count = 1Duck d2 = new Duck();  // size = 1     count = 2Duck d3 = new Duck();  // size = 1     count = 3}
}
声明一个静态常量

public static final double PI = 3.1415926;
public 表示可供各方读取。 static 表示静态。 final 表示“固定不变”。 常量的标识符,一般建议字母大写,字母之间可以用下划线连接。

深入理解 final

final 的核心意思是,它所修饰的元素不能被改变。final 不仅可以修饰变量,还可以修饰方法和类。

// 用 final 修饰的类,不能被继承
final class Foo {// final 修饰静态变量,得到静态常量public static final double PI = 3.1415926;// final 修饰非静态变量,该变量将无法再被修改final String name = "geekxia";void changeName() {// 修改 final变量,失败// name = "Evatsai";}// final 修饰局部变量,该局部变量也将无法再被修改void doFoo(final int x) {// 修改局部变量,失败// x = 100;}// final 修饰的方法,子类将不能覆写final String getName() {return name;}
}
主数据类型的包装类

Boolean / Character / Byte / Short / Integer / Long / Float / Double
主数据类型的包装类,都放在 java.lang 中,所以无需 import 它们。当你需要以对象的方式来处理主数据类型时,你就需要用包装类把它们包装起来,Java5.0之前必须这么做。

int a = 123;
// 包装
Integer aWrap = new Integer(a);
// 解包
int b = aWrap.intValue();

Java5.0以后,autoboxing 使得主数据类型和包装类型不必分得那么清楚。autoboxing 的功能能够自动地将主数据类型和包装类型进行转换。看看下面的例子:

ArrayList<Integer> list = new ArrayList<Integer>();
// 自动地把主数据类型和包装类型进行转换
list.add(3);
int c = list.get(0);Boolean bool = new Boolean(null);
if (bool) {}Integer d = new Integer(3);
d++;
Integer e = d + 3;void takeNumber(Integer i) {}
Integer getNumber() { return 4; }
主数据类型与字符串之间的相互转化
// 把字符串转化成主数据类型
String a = "12";
int b = Integer.parseInt(a);
double c = Double.parseDouble("50.789");
boolean d = new Boolean("true").booleanValue();// 把主数据类型转化成字符串
double e = 34.789;
String f = "" + e;
String g = Double.toString(e);
如何对数字进行格式化?// 第二个参数,以第一个参数的格式化指令进行输出
String s = String.format("%, d", 1000000000);
String m = String.format("I have %,.2f bugs to fix.", 489369.123456);
如何对日期进行格式化?String d = String.format("%tB", new Date());
更多有关“格式化指令”的参考,请查看相关手册。使用 Calendar 抽象类操作日期// 获取日历的实例对象
Calendar cal = Calendar.getInstance();
cal.set(2014, 1, 7, 15, 40);long day1 = cal.getTimeInMillis();
day1 += 1000*60*60;cal.setTimeInMillis(day1);
cal.add(cal.DATE, 35);

第11章:异常处理

如果你把有风险的程序代码包含在 try/catch 块中,那么编译器会放心很多。 try/catch 块会告诉编译器你确实知道所调用的方法会有风险,并且也已经准备好要处理它。

try {// 有风险的行为Sequencer seq = MidiSystem.getSequencer();System.out.println("had got a sequencer");
} catch (MidiUnavailableException ex) {System.out.println(ex);
} finally {System.out.println("总会被执行!");
}

异常也是多态的,Exception子类的实例对象,都是 Exception类型的。编译器会忽略 RuntimeException类型的异常,RuntimeException类型的异常不需要声明或被包含在 try/catch 块中。

如果 try 或 catch 块中有 return 指令,finally还是会执行。流程会跳到 finally执行,finally执行完毕后再回跳到return 指令。

处理多重异常
class Laundry {public void doLaundry() throws PantsException, LingerieException {}
}public class HandleException {public void go() {Laundry lau = new Laundry();try {lau.doLaundry();} catch (PantsException e) {System.out.print(e);} catch (LingerieException e) {System.out.print(e);} finally {}}
}

如果有必要的话,方法可以抛出多个异常。但该方法的声明必须要有含有全部可能的检查异常(如果两个或两个以上的异常有共同的父类时,可以只声明该父类就行)。

有多重异常需要捕获时,异常要根据继承关系从小到大排列,如果更高一级的异常排在了前面,将会导致低一级的异常将没有机会被使用。同级别的异常,排列顺序无所谓。

异常

异常是由方法 throws 来的。

public void doLaundry() throws PantsException, LingerieException {}
如果你调用了一个有风险的方法,但你又不想 try/catch捕获时怎么办?你可以继续使用 throws 关键字,把异常抛给下一个调用我的人,这好比是在踢皮球哈哈。

从上面看,程序有两种方式来处理异常,一种是使用 try/catch 来捕获异常,另一种是使用 throws 把异常抛给下一个调用者。

Head First Java读书笔记(二)相关推荐

  1. Effective Java读书笔记---二、创建和销毁对象

    二.创建和销毁对象 何时以及如何创建对象, 何时以及如何避免创建对象, 如何确保它们能够适时地销毁, 如何管理对象销毁之前必须进行的各种清理动作 1.用静态工厂方法代替构造器 优势: 它们有名称 不必 ...

  2. Effective Java读书笔记二:枚举和注解

    第30条:用enum代替int常量 当需要一组固定常量的时候,应该使用enum代替int常量,除了对于手机登资源有限的设备应该酌情考虑enum的性能弱势之外. 第31条:用实例域代替序数 枚举的ord ...

  3. Java读书笔记(4)-多线程(二)

    Java读书笔记(4)-多线程(二) 2016-1-2 线程通信 传统的线程通信 Object类提供了wait(),notify()和notifyAll三个方法 适用情况:synchronized修饰 ...

  4. Effective Java读书笔记(二)

    Effective Java 读书笔记 (二) 创建和销毁对象 遇到多个构造器参数时要考虑使用构建器 创建和销毁对象 何时以及如何创建对象? 何时以及如何避免创建对象? 如何确保它们能够适时地销毁? ...

  5. 《How Tomcat Works》读书笔记(二)

    <How Tomcat Works>读书笔记(二) 这是<How Tomcat Works>第一二章的读书笔记.第一张主要写了一个静态资源处理的web服务器,第二章加了对ser ...

  6. head first java读书笔记

    head first java读书笔记 1. 基本信息 页数:689 阅读起止日期:20170104-20170215 2. 标签 Java入门 3. 价值 8分 4. 主题 使用面向对象的思路介绍J ...

  7. oracle直查和call哪个更快,让oracle跑的更快1读书笔记二

    当前位置:我的异常网» 数据库 » <>读书笔记二 <>读书笔记二 www.myexceptions.net  网友分享于:2013-08-23  浏览:9次 <> ...

  8. Java学习笔记二:数据类型

    Java学习笔记二:数据类型 1. 整型:没有小数部分,允许为负数,Java整型分4种:int short long byte 1.1 Int最为常用,一个Int类型变量在内存中占用4个字节,取值范围 ...

  9. think in java 读书笔记 2 —— 套接字

    目录 think in java 读书笔记 1 --移位 think in java 读书笔记 2 -- 套接字 think in java 读书笔记 3 -- 数据报 概要 1. 套接字基本知识 2 ...

  10. Java读书笔记(8)-单例模式

    Java读书笔记(8)-单例模式 今天在阅读<Effective Java 2>第3条时,获知一种使用枚举enum实现单例模式的新方法,然而书上并没有就此展开深入说明,于是上网查阅了一些资 ...

最新文章

  1. mysql删除开放用户权限
  2. 【转】BT5汉化步骤
  3. 利用74LS161计数器芯片分别实现模12,模20的计数器,并在QuartusⅡ上进行仿真
  4. DIP第二章习题解答
  5. cisco tftp 操作
  6. 内网通 去广告_新高一攻略|让我们一起跟升学e网通名师看看如何学好高中化学...
  7. 计算机无法上网的软件故障,解决你99%无法联网问题,高手教你只用1招轻松搞定...
  8. c语言 显示 图形界面,「分享」C语言如何编写图形界面
  9. upload file more than 4MB
  10. php阿拉伯语字符串,按字母顺序命名阿拉伯语名称Mysql和php
  11. 关于MFC实现圆角矩形窗口
  12. Spring解决bean之间的循环依赖(循环引用)
  13. boost 安装_Win10 + VS2019 编译安装 Boost
  14. Java == 和 equals
  15. LVS学习系列(1)--入门
  16. paip.截屏功能流程说明
  17. 本地文件上传到阿里云生成网址
  18. typeScript构建失败:Non-string value passed to `ts.resolveTypeReferenceDirective`
  19. Y400本本SSD装win10与机械硬盘上的系统组成双系统
  20. 线段树(segment tree),看这一篇就够了

热门文章

  1. 可以给我个创业的建议吗?
  2. 要么解决问题,要么把问题升华
  3. C++动态内存分配与命名空间
  4. php rfc3986规范,C# 符合RFC3986标准的urlencode 类
  5. liunx GHOST clonezilla(再生龙)UltraISO刻录问题
  6. Win10 PE出现无法创建新的分区也找不到现有的分区解决方案
  7. 将eFPGA应用于嵌入式360度视域视觉系统中
  8. 老子的软件之道 - 道篇 5 企业管理
  9. 如何快速理清并绘制复杂人物关系图
  10. Plone-最好的、最适合企业级应用的国外开源CMS系统