【基本概念】
① Java是面向对象的语言,所有东西都包含在类中
② 源文件的扩展名为.java
,编译后成为类文件,扩展名为.class
;真正被执行的是类
③ 执行程序就是:Java虚拟机(JVM)去加载这个类,开始执行他的main(),一直运行到main中所有代码结束为止
【类与对象】
① 一个类可以说是由实例变量( instance variable )和方法( method )组成的
② Java是面相对象的,因此没有全局的变量和方法;其实,声明为public、static、final后,基本上就获得global的效果了
【primitive主数据类型和引用】
① 变量有两种:primitive主数据类型和引用
② primitive主数据类型(“就是”) 的变量值就是该值的字节表示的
③ 引用(“管理者”) 变量值代表的是位于堆的对象的存取方法
④ 如下表
primitive主数据类型 | 位数 | 值域 |
---|---|---|
boolean | (由Java虚拟机决定) | true或false |
char | 2字节 | 0~65535 |
byte | 1字节 | -128~127 |
short | 2字节 | -32768~32767 |
int | 4字节 | -2147483648~2147483647 |
long | 8字节 | -很大~很大 |
float | 4字节 | 范围规模可变 |
double | 8字节 | 范围规模可变 |
⑤ 需要注意的是,诸如32.5默认为double类型,声明float的正确姿势是:float num = 32.5f
,结尾的" f "必不可少
⑥ 溢位(大杯的内容放进小杯)demo : byte num = 150
但反过来可以,因为小值可以隐含展开(implicit widening)后转入大杯中
⑦ 引用(reference)绝不是对象本身,也不是个容器;对象引用变量保存的是存取对象的方法,可以理解为地址,或者指针。
这就是“管理者”的思想
⑧ 对于同一个Java虚拟机来说,所有的引用都具有相同的大小;其被声明的类型的作用是“检查”而非初始化其大小
【实例变量与方法】
① Java是通过值传递(pass by value)的,也就是传递的都是拷贝
② return时也存在隐含展开现象
③ 一个典型的封装(Encapsulation)操作:
将实例变量声明为private,将getter与setter声明为public(也就是说,别人不能直接改变实例变量的值,必须通过某些方法对其进行修改等操作)
这样"多此一举"有什么好处呢?
安全性和可维护性。
比如,Person类有一个age实例变量,强迫其他程序必须通过setter来修改这个值。这样,当这个值非常不合理时,比如设置为5000,setter方法可以对其进行修改或者直接抛出异常
也许某些setter,getter什么事情也没做,只是把值传给变量而已。但从防患未然和可维护的角度出发,这种习惯十分有必要
④ 实例变量(类中的变量)永远会有默认值;局部变量(方法中使用的变量)没有默认值
⑤ 关于" == "的意义老生常谈,不再赘述;这里提一个小tip:
用 == 直接比较两个primitive主数据类型是否相等(字节组合相等那么数据当然相等)
用 == 比较两个引用是否指向同一个对象
【API】
① API(Application Programming Interface,应用程序编程接口)
② 运行Java程序时,虚拟机装载程序的class文件所使用的Java API class文件。
所有被装载的class文件(包括从应用程序中和从Java API中提取的)和所有已经装载的动态库(包含本地方法)共同组成了在Java虚拟机上运行的整个程序。
③ 特点:无需访问源码,或理解内部工作机制的细节
④ 我们讲使用API,实际上是使用已经写好的类,而类是被包装在包中的(package机制)。
因此,使用API类时,必须知道它被放在哪个包中,并且使用其全名(包名+类名)
可是,我们平常使用API时也不是写它的全名啊?这是因为有import
⑤ 我们必须理解import的本质。import是在加载该类吗?
并不是。
这是一个误区,认为import是在将类加载到内存中。这一点java的import与C的include不同:
import的作用是帮你省下每个类前面的包的名称,仅此而已。
当忘记import时,报错是因为缺少该包的导入吗?不全是。报错的真正原因是由于使用的类不是包含包名的全称,编译时无法准确找到这个类并加载到内存中。也就是说,我们可以这样:
java.util.List<Integer> a = new java.util.ArrayList<>();
等同于:
import java.util.List;
import java.util.ArrayList;
List<Integer> a = new ArrayList<Integer>();
⑥ java.lang不需要指定全名,因此也不需要import
【继承与多态】
① IS-A测试。如果能通过IS-A测试(“是一个”)那么便存在继承关系,且可以使用多态
demo: Dog is a Animal
② 子类会继承父类所有的public类型的实例变量和方法,但不会继承父类所有private类型的实例变量和方法
③ 继承时,方法可以被覆盖,但实例变量不能被覆盖
④ 从子类调用父类的方法可以使用super关键字:super.method();
④ 调用某个对象的方法时,Java虚拟机会首先从该对象的类开始寻找该方法;若没有找到,则一直向父类寻找直到找到该方法。也就是说,会使用最近的方法。
⑤ 一个例子看懂多态:
Animal mydog = new Dog();
不仅引用类型可以是实际对象类型的父类,参数和返回类型也可以体现多态
笔者的思考:
我们给primitive主数据类型声明类型,比如 int a,目的是什么?
是为了根据类型分配内存空间(容器的大小),从而保证内存空间的充分高效利用。
可是,对于引用,它类似指针,其大小都是相同的,这是给其声明类型,比如Animal a,又有什么用呢?
是"检查"。Animal类型的引用只能指向Animal类型或者其子类类型的对象(is A 测试同样适用)。从这个角度,我们能更加深入的理解多态。
⑥ 有三种方法可以防止某个类被做成子类:
-
存取控制。虽然不能将一个类标记为private,但可以不标记为public。这样非公有的类只能被同一个包的类作出子类;
-
使用final修饰符。这表示它是继承树的末端,不能被继承;
-
让类只拥有private的构造程序(constructor)
⑦ 用final标记某个特定的方法,可以防止该方法被覆盖;而将整个类标记为final,该类所有的方法都无法被继承,毕竟该类已经无法被继承了。
⑧ 区分覆盖
(override
)和重载
(overload
)
⑨ 覆盖是指覆盖(重写)父类的方法
覆盖的规则:
-
参数必须要一样;返回类型必须要兼容。解释:父类的方法返回类型为long,重写的方法返回类型可以为int
-
不能降低方法的存取权限。解释:存取权限必须相同,或者更开放,比如,你不能覆盖掉一个公有的方法并将它标记为私有
⑩ 重载也被称为过载,只是刚好两个方法的名称相同,参数不同(与多态完全是两码事)
重载的规则:
-
重载的关键是参数不同,返回类型可以任意改变。解释:仅仅返回类型不同,方法名称和参数相同,是无法通过编译的
-
可以更改存取权限(重载方法彼此是相对独立的)
【abstract 抽象类和抽象方法】
① 如果你不想某个类被初始化,就以abstract将这个类标记为抽象类
② 编译器不会允许初始化抽象类,也就是说,无法new出一个对象,但可以用它的名字创建引用。比如Animal通常被声明为抽象类(abstract):
Animal pet = new Animal(); // (✘)抽象类无法被初始化,这没有意义,也不被编译器允许
Animal mydog = new Dog(); // (✔)可以作为引用的类型;这是多态的体现:Dog is a Animal
Animal[] animals = new Animal[5] // (✔)这是可以的,Animal只是声明为数组类型,这是允许的,只是数组内部的成员必须是具体的类(Dog、Cat...)
③ 除了类之外,也可以将方法标记为abstract:
抽象的类代表此类必须
要被extend过,抽象的方法代表此方法必须
要被覆盖过
解释:抽象的方法没有实体内容(声明抽象方法后直接分号结束),因此它必须被覆盖。
④ 抽象的类中可以带有抽象和非抽象的方法
⑤ 如果类带有抽象方法,则此类必定被标为抽象
可以这样理解:如果一个类带有抽象方法而这个类本身没有被标识为abstract,那么这个类被实体化后,便有机会调用这个抽象方法,这是危险且不被允许的。
⑥ 抽象方法为何存在?多态。
⑦ 在继承树结构下的第一个具体类必须要实现出所有的抽象方法,也就是说,写出它们的内容
⑧ Java中所有的类都是Object(java.lang.Object
)直接或间接的子类
⑨ 编译器是根据引用类型来判断有哪些方法可以调用,而不是根据这个对象实际的类型
【接口与多态】
① 当一个父类A被两个类(B, C)继承时,且这两个子类都覆盖(重写)了父类的某一个方法,接着又有一个子类(D)多继承了这两个类
——此时问题浮现出来:D类new出的对象,在调用这个方法时,该运行哪一个版本呢?
这就是多重继承机制的 致命方块 (或者称为 致命菱形 )
② Java不允许多重继承,而使用接口(interface);
③ 接口是一个100%的纯抽象类;而且接口的方法自带public和abstract的意义,
我们可以将其标识出来,也可以不这麽做;
④ 接口虽然不是真正的继承(extends)而是实现(implements),但仍能通过" IS A “测试,即” 是一个 "
⑤ 接口虽然不是一个真正意义上的父类,当仍可使用 多态性(这恰恰是我们使用接口的目的);
也就是说,引用类型、参数、返回值都可以是接口。
☀ 《Head First Java》Kathy Sierra & Bert Bates