类和对象
类的定义和使用
类的定义:
同c++中的类一样,由构造函数,成员变量和成员方法组成,并且当不重写构造函数的时候java有提供默认的构造函数,如果自己定义了构造函数则默认的构造函数 不会起作用。
成员变量的定义:
成员函数的定义:
关于static关键字:表示变量或方法不属于某个对象而属于整个类本身,被此类的所有 对象共享。
构造函数的定义:
类的使用、构造对象:
实例对象在内存中的存储形式:
对于没有任何引用变量指向的变量,会被垃圾回收器直接清理。
关于static修饰符:
关于java中的方法:
我发现这样看有点慢。。到这里为止,后面的内容照着java面试题过一遍拉倒。。
关于选用合适的变量:不同的变量的作用域和在内存中的驻留时间都是不同的,应该尽量符合其实际需要的作用范围。
隐藏和封装
封装可以实现的目的:
封装的要求:
通过关键字设置不同的封装级别:
外部类就是main函数所在的类,可以看成是一个文件实现主要功能的入口
编程哲学:
java中的包package的概念
package的出现主要是为了复杂项目中的命名冲突解决和文件管理用途。
一个包对应着一个文件夹,这个包里的所有类文件编译后都自动存放在这个文件夹里,这样才能实现重名类文件的隔离
就像这样:
在文件管理器中打开项目文件夹可以看到这种组织结构:
.java文件
.class文件
关于package语句:
import语句:
单独引入某个类:
一次性引入包里的所有类:
引入某个包时不能引入其子包:
import static语句:
成员方法的重载
比较特殊的一种用法是构造方法的重载,以及各个重载的构造方法之间的相互调用
就像下面这样:这种用法是不多见的,见到的话要知道是怎么回事
类的继承
继承的方法比较简单,用extends关键字声明即可
java中不允许直接多继承,一个类只能有一个直接父类,但是可以有多个间接父类
关于java.lang.Object类:
重写父类的方法:
注意,在父类有方法重载的情况下,是通过方法名+形参列表来唯一确定被重写的方法的。在子类中重写的方法必须在这两个上和它重写的父类方法保持一致。
另外:静态成员/方法和非静态成员/方法之间不能重写
super关键字的用法
不过要注意:super关键字不能在静态方法里使用
继承后的子类的成员变量覆盖顺序:
局部变量->本类中的成员变量(包括子类继承父类的和自己新定义的)->父类中的成员变量(其实指的就是直接继承过来而没有做任何修改的变量)
理解子类重写父类成员的实质,从而理解为什么可以用super关键字访问被覆盖掉的父类成员
父类和子类的构造方法是什么关系?
如图所示i:
另外需要注意一点:
就像这样:
只要记住:在构造方法中调用其他任何构造方法时,都必须放在第一句
多态性
多态的基本含义
多态的一个示例:
对其解释:
父类和子类哪个范围更大要搞清楚,比如父类是人类,有两个子类分别为男人和女人,男人一定是人类,但是人类不一定是男人,所以子类对象可以赋给父类引用变量。
注意在这里,编译时类型指的是父类,也就是引用变量(不就能指吗)的类型,运行时类型指的是子类,也就是实例对象(所指)的类型。多态性的实质在于,对于两个同样类型的引用变量,他们拥有的成员变量和方法的种类都是一样的(前面提到过,引用变量只能使用编译时类型的方法,运行时类型(子类)拓展出的方法是不能用的),但是这些同样种类的方法可能有不同的内容,这就是因为通过把拓展后的子类赋值给父类引用变量引发了【多态】。
理解了多态的实质后,我们就不难理解,只要通过强制类型转换,就可以恢复运行时类型的对象的所有成员和方法,因为确实是有一个子类的实例对象在内存里。
而这里问一个问题:如果把父类实例对象赋值给一个子类的引用对象会怎么样呢?
当然也不是不能这样做,只是在大多数情况下不能简单地强制转换,因为子类通常是比父类更丰富的,父类对象赋值给子类引用变量时,不得不考虑有空缺部分的问题。
instanceof运算符
它用于判断前面的对象是否是后面的类,或者其子类、实现类的实例。如果是,则返回true,否则返回false
在使用instanceof运算符时需要注意:instanceof运算符前面操作数的编译时类型要么与后面的类相同,要么与后面的类具有父子继承关系,否则会引起编译错误。
(感觉并没有正面解决这个问题,只是通过这个判断运算符来避免了小对象赋值给大引用变量的情况,该不能转换的还是不能)
继承和组合
继承的弊端:
保证良好封装性的父类设计标准:
除此之外,在有些情况下设计子类继承父类完全是多余的,可以通过省略掉这些不必要的继承来保护父类的封装性:
利用组合实现类的复用
组合的实现方式是:
仔细读这三个类就会发现,他们实现的效果和bird、wolf分别作为子类继承animal是一样的,但是区别在于继承的方式破坏了父类的封装性,而在组合的方式中,bird和wolf类并没有直接得到animal的内部成员,而是将animal类作为其成员变量,从外部使用animal类,就和一个不相关的类调用animal类对外公开的成员方法一样的。这种方式很好地保留了animal的封装性。
如何通过问题条件选用合适的方式?什么时候继承什么时候组合?
java类的初始化块
什么是初始化块?
示例:
可以看出:
初始化块之间有执行顺序
初始化块大致相当于一个成员方法,可以在其中访问类的成员和方法,并且可以把初始化块写在成员的定义之前,这样初始化块中对于成员方法的调用仍然可以正常执行。
在这里可以看出,静态初始化块中只能访问静态方法和成员
可以这样理解,静态指的就是这些内容可以只依赖于类存在,非静态就是跟随实例,指的就是这些内容必须依赖于创造好的实例对象存在。而如果在静态内容中访问非静态内容,在没有任何实例存在的情况下就是逻辑错误,所以静态只能访问静态,非静态可以访问静态或非静态,有类不一定有对象,有对象一定有类。
静态初始化块也只会在构造这个类的对象时才会被执行。
当连续创建多个实例对象时,实例初始化块在每次创造对象时执行一次,而静态初始化块只在第一次创造对象时执行一次。
初始化块与构造方法之间的关系:
通过下面的实验可以看出,初始化块的执行是在构造对象之前的。
初始化块、类定义中的实例变量初始化和构造方法之间的执行顺序:
如图所示:实例变量的初始化与初始化块的执行顺序与其在类的定义中的书写顺序一致。
实例初始化块的正确打开方式
通过恰当地使用实例初始化块,可以使构造方法简化。
甚至实例初始化块的实现原理就是这样:
实例初始化块和构造方法的执行存在从下到上再到下的一个回溯过程:
示例: