面向对象概念
面向对象和面向过程的区别
面向过程:
-
引导
把大象放入冰箱需要几步?打开冰箱,放进大象,关上冰箱.
-
解决:
openRefrigerator()
,putIn()
,closeRefrigerator()
-
总结:将问题分解成具体的步骤,然后按照不同的步骤实现函数,最终依次调用函数解决问题.
-
优缺点:
- 优点:性能比面向对象高,不需要实例化
- 缺点:不容易维护,复用和扩展.
类似于一个草稿本中你进行各个科目的演算,随便抽一张就可以用.但是当你对错题进行复盘的时候,就很麻烦.
面向对象:
-
引导
把大象放入冰箱需要几步?哦!大象是一个对象,冰箱是一个对象.冰箱可以存放东西(方法).
-
解决:
// 这里只是简单的示例 class Elephant{...} class Refrigerator{ private Float volume; public Refrigerator(Float volume){ this.volume = volume; } public putIn(Object object){ System.out.println("冰箱放入了" + Object); } } class Demo{ public static void main(String[] args){ Elephant elephant = new Elephant(); Refrigerator refrigerator = new Refrigerator(10000); refrigerator.putIn(elephant); } }
-
面向对象的优缺点:
- 优点:面向对象具有封装,继承,多态的特性,因此容易维护,复用和扩展,也可以据此设计出低耦合的程序(Spring框架)
- 缺点:需要实例化对象,性能比面向过程低.
每个科目有自己的草稿本,这样你每次做题都可以在上次的草稿的基础上继续进行(对象之间界定清晰).但是你不同科目演算时候,就需要掏出不同的草稿本(实例化不同的对象).
对象和类
对象是现实生活中的一个活生生的人,类是生物课本上对人的定义.
对象具有自己独立的状态和行为,对象的状态由数据域的集合构成.对象的行为由方法的集合构成.
类是定义同一类型对象的结构,是对具有相同特征的对象的抽象。类是一个模板,用来定义对象的数据域和方法。可以从一个类创建多个对象,创建对象称为实例化。
参照上面冰箱的案例 [3]就是冰箱类的定义,它提供了一个冰箱具备容量这一属性和放东西进去的方法.
[15]就是一个冰箱对象:它具有真实的容量,并且最终在里面放了一个大象.
练习
- 面向对象和面向过程的区别有哪些?分别有什么优缺点?
- 对象和类之间有哪些联系?
继承
在面向对象程序设计中,可以从已有的类(父类)派生出新类(子类),称为继承.
你设计了一个图形类,现在你想要一个圆.那这个时候你就需要尝试使用继承.
子类和父类
子类从父类中继承可访问的类成员,也可以添加新的类成员.子类通常包含比父类更多的类成员.继承用来为is-a关系建模,子类和父类之间必须存在is-a关系.
如果一个类在定义时没有指定继承,它的父类默认是Object.
圆形具有图形的一些性质,那么我们在圆形的定义过程中就不需要去关注图形的性质.
这里圆形和图形傻傻分不清楚. T T
关键字super
-
super关键字的用处:
-
调用父类的构造方法
super()
或super(参数)
必须是子类构造方法的第一个语句class ElectricAppliance{ public ElectricAppliance(Float volume){ this.volume = volume; } } class Refrigerator extends ElectricAppliance{ private Float volume; public Refrigerator(Float volume){ super(volume); } public putIn(Object object){...} }
-
调用父类中的方法
super.method()
-
方法构造链
如果构造方法没有显式地调用同一个类中其他的构造方法或父类的构造方法,将隐性地调用父类的无参数构造方法,即编译器会把super()
作为构造方法的第一个语句。
构造一个类的实例时,将会沿着继承链调用所有父类的构造方法,父类的构造方法在子类的构造方法之前调用,称为构造方法链。
public Class1{
// 这里是 class1的构造方法
public Class1(){
System.out.println("白");
}
}
public Class2 extends Class1{
// 优先调用父类 Class1的构造方法
public Class2(){
this.("熊");
System.out.println("冰");
}
public class2(String s){
System.out.println(s);
}
}
// 没有显式地调用同一个类中其他的构造方法或父类的构造方法
// 将隐性地调用父类的无参数构造方法
public Class3 extends Class2{
// 调用父类 Class2的无参构造方法
public Class3(){
System.out.println("啤");
}
}
-------------------------------------------------
白熊冰啤
方法的重写
子类从父类中继承方法。如果子类修改了父类中定义的方法,则称为方法重写。方法重写要求子类的方法和父类的方法的签名相同。
如果方法的返回值类型是基本数据类型或者 void,则要求子类的方法的返回值类型和父类的方法的返回值类型相同。如果方法的返回值类型是引用类型,则要求返回值类型相同或者子类的方法的返回值类型是父类的方法的返回值类型的子类。
实例方法只有当可访问时才能被重写。由于私有方法不能在定义该方法的类外访问,因此私有方法不能被重写。
静态方法可以被继承,但是不能被重写。
比如图形中有一个 draw()方法,圆形继承了图形之后,就可以重新写一个属于自己的draw()方法
重写和重载的区别
可见性修饰符protected
关键字final
关键字 final 可以用于声明常量,表示常量不会改变。
关键字 final 也可以用于修饰类和方法。使用 final 修饰的类是终极类,不能被继承。使用 final 修饰的方法不能被子类重写。
例如,String、StringBuffer 和 StringBuilder 类都使用了关键字 final 修饰,因此这些类不能被继承。
这里提一个小问题:抽象类可以使用final关键字修饰吗?为什么?
习题
- 在继承关系中,子类是不是父类的子集?
- 定义继承关系时,父类和子类之间应该存在什么关系?
- 有继承结构时,构造方法的调用顺序是什么?
- 重写中,子类的方法返回值类型是否可以和父类不完全一致?
- 重写和重载的区别是什么?
- 可见性修饰符
protected
表示可以从哪里访问类成员? - 关键字
final
用于修饰类和方法时,其含义是什么?
垃圾回收
垃圾回收,顾名思义就是释放垃圾占用的空间,从而提升程序性能,防止内存泄露。当一个对象不再被需要时,该对象就需要被回收并释放空间。
Java 内存运行时数据区域包括程序计数器、虚拟机栈、本地方法栈、堆等区域。其中,程序计数器、虚拟机栈和本地方法栈都是线程私有的,当线程结束时,这些区域的生命周期也结束了,因此不需要过多考虑回收的问题。而堆是虚拟机管理的内存中最大的一块,堆中的内存的分配和回收是动态的,垃圾回收主要关注的是堆空间。
使用关键字
new
出来的对象都会在堆上开辟一块内存.当我们的程序长期运行的过程中如果不释放内存,这些new
出来的对象就可能会导致内存的溢出,服务器就崩了.
调用垃圾回收器的方法gc
该方法在 System
类和 Runtime
类中都存在。
- 在
Runtime
类中,方法gc
是实例方法,方法System.gc
是调用该方法的一种传统而便捷的方法。 - 在
System
类中,方法gc
是静态方法,该方法会调用Runtime
类中的gc
方法。 java.lang.System.gc
等价于java.lang.Runtime.getRuntime.gc
的简写,都是调用垃圾回收器。- 方法
gc
的作用是提示Java虚拟机进行垃圾回收,该方法由系统自动调用,不需要人为调用。该方法被调用之后,由 Java 虚拟机决定是立即回收还是延迟回收。
finalize 方法
与垃圾回收有关的另一个方法是finalize
方法。该方法在Object
类中被定义,在释放对象占用的内存之前会调用该方法。该方法的默认实现不做任何事,如果必要,子类应该重写该方法,一般建议在该方法中释放对象持有的资源。
判断对象是否可回收
垃圾回收器在对堆进行回收之前,首先需要确定哪些对象是可回收的。
如果你创建一个对象还没来得及用就给你回收掉,你气不气?
常用的算法有两种,引用计数算法和根搜索算法。
-
引用计数算法
引用计数器为每个对象添加一个引用计数,引用计数为0的对象就是可回收的对象.
-
引用计数算法实现简单,判定效率也很高,但是无法解决对象之间的循环引用问题.
A引用B,B引用A,这样的话A和B之间的引用计数永远不会成为0
-
-
根搜索算法
从若干被称为 GC Roots的对象开始进行搜索,不能够到达的对象就是可回收对象.
-
GC Roots包含的对象的种类:
- 虚拟机栈中引用的对象.
- 本地方法栈中的本地方法引用的对象
- 方法区中的类静态属性引用的对象
- 方法区中常量引用的对象
-
引用的分类:强引用、软引用、弱引用、虚引用
-
强引用是指在程序代码中普遍存在的引用。
垃圾回收器永远不会回收被强引用关联的对象。
-
软引用描述还有用但并非必需的对象。
只有在系统将要发生内存溢出异常时,被软引用关联的对象才会被回收。在 JDK 1.2 之后,提供了 SoftReference 类实现软引用。
-
弱引用描述非必需的对象,其强度低于软引用。
被弱引用关联的对象只能存活到下一次垃圾回收发生之前,当垃圾回收器工作时,被弱引用关联的对象一定会被回收。在 JDK 1.2 之后,提供了
WeakReference
类实现弱引用。 -
虚引用是最弱的引用关系。
一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用取得一个对象实例。为一个对象设置虚引用关联的唯一目的就是能在这个对象被回收时收到一个系统通知。在 JDK 1.2 之后,提供了
PhantomReference
类实现虚引用。
-
-
垃圾回收算法(4)
-
标记—清除算法
- 标记清除算法概述:
- 标记阶段: 标记所有需要回收的对象.
- 清除阶段: 标记阶段结束后,统一回收所有被标记的对象.
- 标记清除算法的缺点:
- 标记清除算法的效率比较低.
- 标记清除算法会产生大量不连续的内存碎片,可能会导致后续无法为较大对象找到足够的连续内存.
- 标记清除算法概述:
-
复制算法
-
复制算法概述:
将内存划分成为两个等大的区域,每次都只使用其中的某一块,当某一块内存用尽后,将仍旧存活的对象复制到另一块内存,然后将已经使用的内存空间一次性清空.
-
复制算法的优缺点:
- 优点: 实现简单运行高效.
- 缺点:将可使用内存缩小为原来的一半,在对象存活率较高时,复制操作的次数较多,导致效率降低.
-
-
标记—整理算法
- 标记整理算法概述:
- 根据老年代的特点提出的算法.
- 标记阶段和标记清除算法一致.
- 整理阶段: 让所有存活对象统一向一端移动,清除边界意外的内存.
- 标记整理算法概述:
-
分代收集算法
- 分代收集算法概述:
- 根据对象的存活周期的不同将内存划分成为多个区域,为每个区域选用不同的垃圾回收算法.
- 一般Java堆分为新生代和老年代.在新生代中,大多数对象的生命周期都很短,因此采用复制算法.在老年代中,对象的存活率高,一般使用标记清除算法或者标记整理算法.
- 分代收集算法概述:
分配内存与回收策略(6)
Java堆内存可以分为新生代和老年代.其中新生代又可以进一步划分为:Eden区,From Survivor区,To Survivor区.
对于不同的对象有着相应的内存分配规则.
-
Minor GC 和 Full GC
- Minor GC:指发生在新生代的垃圾回收操作.因为新生代中大多数对象的生命周期都很短,因此 Minor GC会频繁的执行,一般回收的速度也比较快.
- Full GC: 指在发生在老年代的垃圾回收操作.因为老年代对象的存活时间常,因此Full GC很少执行,执行速度会比Minor GC慢很多.
-
对象优先在 Eden 区分配(字面意思)
-
大对象直接进入老年代
-
大对象的概念: 需要连续内存空间的对象
如很长的字符串,很大的数组.
大对象对于虚拟机内存分配而言坏消息.如果经常出现大对象会导致内存中具有空闲空间却提前触发了垃圾回收来满足获取足够的连续空间分配给大对象.
-
大对象的处理:
大对象直接放在老年区的目的是避免在 Eden区和 Survivor区之间出现大量的内存复制.
-
-
长期存活的对象进入老年代
-
如何识别对象位于新生代还是老年代
虚拟机对每个对象定义了一个年龄计数器,对象经过第一次Minor GC之后如果仍然存活就进入Survivor区,同时对象年龄增1,对象每在 Sruvivor中经历一次 Minor GC,对象的年龄计数就增1,增加到一定的阈值则进入老年代(阈值默认为15).
-
-
动态对象年龄判定
因为不同程序的运行状态不同,虚拟机不总是要求对象的年龄必须达到阈值才能进入老年代,而是采用了灵活的年龄判断机制.如果Survivor区中相同年龄的所有对象空间总和大于Survivor区空间的半时(假设这个年龄值为X),年龄大于等于X的对象直接进入老年代.
-
空间分配担保
在进行 Minor GC之前需要判断老年代中的最大连续空间的大小是否大于新生代中所有的对象的空间总和.只有满足这个条件的 Minor GC才是安全的.
所以老年代的最大连续空间的大小大于新生代中所有对象的大小总和或者历次晋升的平均大小,就进行 Minor GC,否则就进行 Full GC.
习题
- 垃圾回收主要是针对 Java 内存的哪一个区域?
- 方法
System.gc
的作用是什么?该方法与Runtime
类中的方法gc
有什么区别和联系? - 方法
finalize
在哪个类中定义,以及它的默认实现是什么?该方法的作用是什么? - 判断对象是否可回收,有哪两种算法?Java 使用的是哪一种算法?另一种算法有什么不足之处?
- Java 中的四种引用按照引用强度从高到低的顺序依次是什么?
- 新生代和老年代分别适合使用哪种垃圾回收算法?
- 在分配内存空间时,为什么大对象直接在老年代种分配?