Java面向对象
一、面向过程和面向对象
面向过程和面向对象都是对软件分析、设计和开发的一种思想,它指导着人们以不同的方式去分析、设计和开发软件。
1.1 面向过程
- 执行者思维
适合简单任务,按步骤对应成方法,一步一步,最终完成,不需要过多协作
1.2 面向对象
- 面向对象是设计者思维|
宏观,可面向复杂项目,便于协作。
整体大方向考虑产生类,
二、对象和类的理解
2.1对象
- 对象:Object,instance(实例)。
2.2 类
-
2.2.1 定义
- 类是用于描述同一类型的对象的一个抽象概念,类中定义了这一类对象所应具有的共同的属性、方法。
- 类:类可以看做是一个模版,或者图纸,系统根据类的定义来造出对象。
- 一般有三种常见的成员:属性field、方法method、构造器constructor。
-
2.2.2 属性field
- 属性用于定义该类或该类对象包含的数据或者说静态特征。
- [修饰符] 属性类型 属性名 = [默认值] ;
-
2.2.3 方法method
- 方法用于定义该类或该类实例的行为特征和功能实现。
- [修饰符] 方法返回值类型 方法名(形参列表) {
// n条语句
}
-
2.2.4 构造器constructor
- 构造器也叫构造方法(constructor),用于对象的初始化。构造器是一个创建对象时被自动调用的特殊方法,目的是对象的初始化。构造器的名称应与类的名称一致。
- Java通过new关键字来调用构造器,从而返回该类的实例,是一种特殊的方法。
- [修饰符] 类名(形参列表){
//n条语句
} - 特点
- 1.构造器通过new关键字调用!!
- 2.构造器虽然有返回值,但是不能定义返回值类型(返回值的类型肯定是本类),不能在构造器里使用return返回某个值。
- 3.如果我们没有定义构造器,则编译器会自动定义一个无参的构造函数。如果已定义则编译器不会自动添加!
- 4.构造器的方法名必须和类名一致!
构造方法也是方法,只不过有特殊的作用而已。与普通方法一样,构造方法也可以重载。
三、面向对象内存分析
Java虚拟机的内存可以简单的分为三个区域:虚拟机栈stack、堆heap、方法区method area。
3.1 栈
- 1.栈描述的是方法执行的内存模型。每个方法被调用都会创建一个栈帧(存储局部变量、操作数、方法出口等)
- 2.JVM为每个线程创建一个栈,用于存放该线程执行方法的信息(实际参数、局部变量等)
- 3.栈属于线程私有,不能实现线程间的共享!
- 4.栈的存储特性是“先进后出,后进先出”
- 5.栈是由系统自动分配,速度快!栈是一个连续的内存空间!
3.2 堆
- 1.堆用于存储创建好的对象和数组(数组也是对象)
- 2.JVM只有一个堆,被所有线程共享
- 3.堆是一个不连续的内存空间,分配灵活,速度慢!
3.3 方法区
-
1.方法区是JAVA虚拟机规范,可以有不同的实现。
- i.JD7以前是“永久代”
- ii.JDK7部分去除“永久代”,静态变量、字符串常量池都挪到了堆内存中
- iii.JDK8是“元数据空间”和堆结合起来。
-
2.JVM只有一个方法区,被所有线程共享!
-
3.方法区实际也是堆,只是用于存储类、常量相关的信息!
-
4.用来存放程序中永远是不变或唯一的内容。(类信息【Class对象,反射机制中会重点讲授】、静态变量、字符串常量等)
3.4 参数传值机制
-
1.基本数据类型参数的传值
- 传递的是值的副本。 副本改变不会影响原件。
-
2.引用类型参数的传值
- 传递的是值的副本。但是引用类型指的是“对象的地址”。因此,副本和原参数都指向了同一个“地址”,改变“副本指向地址对象的值,也意味着原参数指向对象的值也发生了改变”。
- 传递的是值的副本。但是引用类型指的是“对象的地址”。因此,副本和原参数都指向了同一个“地址”,改变“副本指向地址对象的值,也意味着原参数指向对象的值也发生了改变”。
四、垃圾回收机制
4.1 垃圾回收的原理和算法
-
4.1.1 内存管理
- Java的内存管理很大程度指的就是:堆中对象的管理,其中包括对象空间的分配和释放。
- 对象空间的分配:使用new关键字创建对象即可
- 对象空间的释放:将对象赋值null即可。垃圾回收器将负责回收所有”不可达”对象的内存空间。
-
4.1.2 垃圾回收过程
- 1.发现无用的对象
- 2.回收无用对象占用的内存空间。
-
4.1.3 垃圾回收相关算法
-
1.引用计数法
- 堆中的每个对象都对应一个引用计数器,当有引用指向这个对象时,引用计数器加1,而当指向该对象的引用失效时(引用变为null),引用计数器减1
优点是算法简单,缺点是“循环引用的无用对象”无法别识别。
- 堆中的每个对象都对应一个引用计数器,当有引用指向这个对象时,引用计数器加1,而当指向该对象的引用失效时(引用变为null),引用计数器减1
-
2.引用可达法(根搜索算法)
- 程序把所有的引用关系看作一张图,从一个节点GC ROOT开始,寻找对应的引用节点,找到这个节点以后,继续寻找这个节点的引用节点,当所有的引用节点寻找完毕之后,剩余的节点则被认为是没有被引用到的节点,即无用的节点。
-
4.2 通用的分代垃圾回收机制
- MinorGC负责清理Eden区
- Major GC负责清理Survivor区
- FullGC清理年轻代和年老代,成本较高,会对系统性能产生影响
4.3 JVM调优和Full GC
-
很大一部分工作就是对于Full GC的调节。
-
可能导致Full GC的原因
- 1.年老代(Tenured)被写满
- 2.持久代(Perm)被写满
- 3.System.gc()被显示调用
- 4.上一次GC之后Heap的各域分配策略动态变化
4.4 开发中容易造成内存泄露的操作
- 创建大量无用对象
- 静态集合类的使用
- 各种连接对象(IO流对象、数据库连接对象、网络连接对象)未关闭
- 监听器的使用
4.5 其他注意点
- 1.程序员无权调用垃圾回收器。
- 2.程序员可以调用System.gc(),该方法只是通知JVM,并不是运行垃圾回收器。尽量少用,会申请启动Full GC,成本高,影响系统性能。
- 3.finalize方法,是Java提供给程序员用来释放对象或资源的方法,但是尽量少用
五、this和static关键字
5.1创造对象四步
构造方法是创建Java对象的重要途径,通过new关键字调用构造器时,构造器也确实返回该类的对象,但这个对象并不是完全由构造器负责创建。创建一个对象分为如下四步:
- 1.分配对象空间,并将对象成员变量初始化为0或空
- 2.执行属性值的显示初始化
- 3.执行构造方法
- 4.返回对象的地址给相关的变量
5.2 this
-
5.2.1 本质
- this的本质就是“创建好的对象的地址”
-
5.2.2常用用法
- 1.在程序中产生二义性之处,应使用this来指明当前对象;普通方法中,this总是指向调用该方法的对象。构造方法中,this总是指向正要初始化的对象。
- 2.使用this关键字调用重载的构造方法,避免相同的初始化代码。但只能在构造方法中用,并且必须位于构造方法的第一句。
- 3.this不能用于static方法中。
5.3 static静态
-
5.3.1 特点
- 1.为该类的公用变量,属于类,被该类的所有实例共享,在类被载入时被显式初始化。
- 2.对于该类的所有对象来说,static成员变量只有一份。被该类的所有对象共享!!
- 3.一般用“类名.类属性/方法”来调用。(也可以通过对象引用或类名(不需要实例化)访问静态成员。)
- 4.在static方法中不可直接访问非static的成员。
- 在类中,用static声明的成员变量为静态成员变量,也称为类变量。 类变量的生命周期和类相同,在整个应用程序执行期间都有效。
-
5.3.2 静态化初始块
- 构造方法用于对象的初始化!静态初始化块,用于类的初始化操作!在静态初始化块中不能直接访问非static成员。
- 静态初始化块执行顺序:
1.上溯到Object类,先执行Object的静态初始化块,再向下执行子类的静态初始化块,直到类的静态初始化块为止。
2.构造方法执行顺序和上面顺序一样!!
六、继承
6.1 继承的概念和实现
- 继承有两个主要作用:
- 代码复用,更加容易实现类的扩展
- 方便对事务建模
6.2 Instanceof运算符
- instanceof是二元运算符,左边是对象,右边是类
6.3 方法的重写override
- 子类通过重写父类的方法,可以用自身的行为替换父类的行为
- 方法的重写需要符合下面的三个要点:
- “= =”: 方法名、形参列表相同。
- “≤”:返回值类型和声明异常类型,子类小于等于父类。
- “≥”: 访问权限,子类大于等于父类。
6.4 继承和组合
- “组合”的核心就是“将父类对象作为子类的属性”,然后,“子类通过调用这个属性来获得父类的属性和方法”。
- 对于“is -a”关系建议使用继承,“has-a”关系建议使用组合。
6.5 final关键字
- 1.修饰变量: 被他修饰的变量不可改变。一旦赋了初值,就不能被重新赋值。
- 2.修饰方法:该方法不可被子类重写。但是可以被重载!
- 3.修饰类: 修饰的类不能被继承。比如:Math、String等。
七、Object类详解
7.1 Object类基本特性
- Object类是所有Java类的根基类,也就意味着所有的Java对象都拥有Object类的属性和方法。如果在类的声明中未使用extends关键字指明其父类,则默认继承Object类。
7.2 toString()方法
- 默认会返回“类名+@+16进制的hashcode”
- 重写toString()
7.3 ==和equals()
- 1.==用于比较两个对象是不是相同,equals在Object中的默认实现就是如此。
- 2.对于==,如果作用于基本数据类型的变量,则直接比较其存储的 “值”是否相等;
如果作用于引用类型的变量,则比较的是所指向的对象的地址 - 3.注意:equals方法不能作用于基本数据类型的变量
如果没有对equals方法进行重写,则比较的是引用类型的变量所指向的对象的地址;
诸如String、Date等类对equals方法进行了重写的话,比较的是所指向的对象的内容。
7.4 super关键字
- 直接父类对象的引用。可以通过super来访问父类中被子类覆盖的方法或属性。
7.5 继承树追溯
-7.5.1 属性/方法查找顺序:(比如:查找变量h)
-
1.查找当前类中有没有属性h
-
2.依次上溯每个父类,查看每个父类中是否有h,直到Object
-
3.如果没找到,则出现编译错误。
-
4.上面步骤,只要找到h变量,则这个过程终止。
-
7.5.2 构造方法调用顺序:
构造方法第一句总是:super(…)来调用父类对应的构造方法。所以,流程就是:先向上追溯到Object,然后再依次向下执行类的初始化块和构造方法,直到当前子类为止。
八、多态
8.1 多态的概念和实现
- 1.多态是方法的多态,不是属性的多态(多态与属性无关)。
- 2.多态的存在要有3个必要条件:继承,方法重写,父类引用指向子类对象。
- 3.父类引用指向子类对象后,用该父类引用调用子类重写的方法,此时多态就出现了。
8.2 对象的转型
-
8.2.1 向上转型
- 父类引用指向子类对象,
-
8.2.2 向下转型
- 上转型后的父类引用变量进行类型的强制转换
- 异常:须将引用变量转成真实的子类类型(运行时类型)
- instanceof运算符进行判断
九、封装
9.1 特点
- 1.提高代码的安全性。
- 2.提高代码的复用性。
- 3.“高内聚”:封装细节,便于修改内部代码,提高可维护性。
- 4.“低耦合”:简化外部调用,便于调用者使用,便于扩展和协作。
9.2 实现一使用访问控制符
- 1.private 表示私有,只有自己类能访问
- 2.default表示没有修饰符修饰,只有同一个包的类能访问
- 3.protected表示可以被同一个包的类以及其他包中的子类访问
- 4.public表示可以被该项目的所有包中的所有类访问
9.3 使用细节
- 1.一般使用private访问权限。
- 2.提供相应的get/set方法来访问相关属性,这些方法通常是public修饰的,以提供对属性的赋值与读取操作(注意:boolean变量的get方法是is开头!)。
- 3.一些只用于本类的辅助性方法可以用private修饰,希望其他类调用的方法用public修饰。
十、IDEA部分快捷键
IDEA快捷键和相关操作:
- 类的结构视图:alt+7
- 看类的源码:ctrl+左键
- 查看类的关系:鼠标放到类名上,右键–>Diagram–>show Diagram
- 自动生成构造器、get、set方法、equals等:alt+insert
XMind: ZEN - Trial Version