1. 垃圾回收
JVM运行环境中垃圾对象的定义:
一个对象创建后被放置在JVM的堆内存(heap)中,当永远不再引用这个对象时,它将被JVM在堆内存(heap)中回收。被创建的对象不能再生,同时也没法通过程序语句释放它们。
不可到达的对象被JVM视为垃圾对象,JVM将给这些对象打上标记,然后清扫回收它们,并将散碎的内存单元收集整合。
JVM管理的两种类型的内存:
堆内存(heap),主要存储程序在运行时创建或实例化的对象与变量。
栈内存(stack),主要存储程序代码中声明为静态(static)(或非静态)的方法。
堆内存(heap)通常情况下被分为两个区域:新对象(new object)区域与老对象(old object)区域。
新对象区域:
又可细分为Eden区域、From区域与To区域。
Eden区域保存新创建的对象。当该区域中的对象满了后,JVM系统将做可达性测试,主要任务是检测有哪些对象由根集合出发是不可到达的,这些对象就可被JVM回收,且将所有的活动对象从Eden区域拷到To区域,此时有一些对象将发生状态交换,有的对象就从To区域被转移到From区域,此时From区域就有了对象。
该过程执行期间,JVM的性能非常低下,会严重影响到正在运行的应用的性能。
老对象区域:
在老对象区域中的对象仍有一个较长的生命周期。经过一段时间后,被转入老对象区域的对象就变成了垃圾对象,此时它们被打上相应的标记,JVM将自动回收它们。
建议不要频繁强制系统做垃圾回收,因为JVM会利用有限的系统资源,优先完成垃圾回收工作,致使应用无法快速响应来自用户端的请求,这样会影响系统的整体性能。
2. JVM中对象的生命周期
对象的整个生命周期大致分为7个阶段:创建(creation)、应用(using)、不可视(invisible)、不可到达(unreachable)、可收集(collected)、终结(finalized)、释放(free)。
1) 创建阶段
系统通过下面步骤,完成对象的创建:
a) 为对象分配存储空间
b) 开始构造对象
c) 递归调用其超类的构造方法
d) 进行对象实例初始化与变量初始化
e) 执行构造方法体
在创建对象时的几个关键应用规则:
• 避免在循环体中创建对象,即使该对象占用内存空间不大
• 尽量及时使对象符合垃圾回收标准
• 不要采用过深的继承层次
• 访问本地变量优于访问类中的变量
关于“在循环体中创建对象”,如下方式会在内存中产生大量的对象引用,浪费大量的内存空间,增大了系统做垃圾回收的负荷:
而如下方式,仅在内存中保存一份对该对象的引用:
另外,不要对一个对象进行多次初始化,这同样会带来较大的内存开销,降低系统性能。
2) 应用阶段
该阶段是对象得以表现自身能力的阶段,即是对象整个生命周期中证明自身“存在价值”的时期。此时对象具备下列特征:
a) 系统至少维护着对象的一个强引用(Strong Reference)
b) 所有对该对象的引用全是强引用(除非我们显示使用了软引用(Soft Reference)、弱引用(Weak Reference)或虚引用(Phantom Reference))
软引用
主要特点是具有较强的引用功能。只有当内存不够时,才回收这类内存。另外,这些引用对象还能保证在Java抛出OutOfMemory异常前,被置为null。它可用于实现一些常用资源的缓存,保证最大限度的使用内存而不引起OutOfMemory。
例:
软引用技术使Java应用能更好地管理内存,稳定系统,防止系统内存溢出,避免系统崩溃。在处理一些占用内存较大,且声明周期较长,但使用并不频繁的对象时,应尽量应用该技术。
但某些时候对软引用的使用会降低应用的运行效率与性能,如:应用软引用的对象的初始化过程较耗时,或对象的状态在运行过程中发生了变化,都会给重新创建对象与初始化对象带来不同程度的麻烦。
弱引用
与软引用对象的最大不同在于:GC在进行回收时,需通过算法检查是否回收软引用对象,而对于弱引用对象,GC总是进行回收。故弱引用对象拥有更短的生命周期,更易更快被GC回收。
虽然GC在运行时一定回收弱引用对象,但是负责关系的弱对象群常常需要好几次GC的运行才能完成。弱引用对象常用于Map数据结构中,引用占用内存空间较大的对象,一旦该对象的强引用为null,GC能快速回收该对象空间。
例:
虚引用
用途较少,主要用于辅助finalize方法。虚对象指一些执行完了finalize方法,且为不可达对象,但还未被GC回收的对象。这种对象可辅助finalize进行一些后期的回收工作,通过覆盖Reference的clear方法增强资源回收机制的灵活性。
注意:实际程序设计中一般很少使用弱引用与虚引用,使用软引用的情况较多,因为软引用能加上JVM对垃圾内存的回收速度,能维护系统的运行安全,防止内存溢出等。
3) 不可视阶段
对象经历应用阶段后,便处于不可视阶段,说明我们在其他区域的代码中已不可再引用它,其强引用已消失。如:本地变量超出其可视范围。
例:
若一个对象已使用完,且在其可视区域不再使用,应主动将其设置为null。针对上面的例子,可主动添加obj = null;这样一行代码,强制将obj置为null。这样的意义是,帮助JVM及时发现这个垃圾对象,且可以及时回收该对象所占用的系统资源。
4) 不可到达阶段
处于不可到达阶段的对象,都是要被GC回收的预备对象,但此时该对象并不能被GC直接回收。其实所有垃圾回收算法所面临的问题是相同的——找出由分配器分配的,但用户程序不可到达的内存块。
5) 可收集阶段、终结阶段与释放阶段
此时JVM就可以直接回收对象了,而对象可能处于下面三种情况:
a) GC发现该对象已不可到达
b) finalize方法已被执行
c) 对象空间已被重用
3. Java中的析构方法finalize
Java中的finalize()与C++中的析构函数的职能极为类似。
虽然我们可以在一个Java类调用其父类的finalize方法,但由于该方法未自动实现递归调用,我们必须手动实现,因此该方法的最后一个语句通常是super.finalize()语句。通过这种方式,我们能实现从下到上finalize的迭代调用,即先释放用户类自身的资源,再释放父类资源。通过可在finalize()中释放一些不易控制,且非常重要的资源,如:一些I/O操作,数据的连接。
finalize()最终由JVM中的垃圾回收器调用,其调用时间是不确定或不及时的,调用时机对我们来说是不可控的。因此,有时我们需要通过其他手段释放系统资源,如声明一个destroy(),在该方法中添加释放系统资源的处理代码。虽然我们可以通过调用自定义的destroy()释放系统资源,但最好将对destroy()的调用放入当前类的finalize()中,因为这样更保险、安全。在类深度继承的情况下,这种方法就显得更有效了。
例:
MyTest的运行结果:
create a
create b
release b
release a
初始化B对象时,其构造器产生了递归调用,由父类开始依次调用,而在调用B对象的destroy()时,系统产生了与初始化调用相反的递归调用,释放资源是由子类开始的。由此可见,在设计类时,应尽可能避免在类的默认构造器中创建、初始化大量对象,或执行某种复杂、耗时的运算逻辑。
4. 数组的创建
如果数组中所保存的元素占用内存空间较大,或数组本身长度较长,我们可采用软引用的技术来引用数组,以“提醒”JVM及时回收垃圾内存,维护系统的稳定性。
例:
JVM根据运行时的内存资源的使用情况,来把握是否回收该对象,释放内存。虽然这样会对应用程序产生一些影响(如当需要使用该数组对象时,该对象被回收了),但却能保证应用整体的稳健性,达到合理使用系统内存的目的。
5. 共享静态变量存储空间
静态变量生命周期较长,不易被系统回收。因此如果不合理使用静态变量,会造成大量的内存浪费。建议在具备下列全部条件的情况下,尽量使用静态变量:
1) 变量所包含的对象体积较大,占用内存较多
2) 变量所包含的对象生命周期较长
3) 变量所包含的对象数据稳定
4) 该类的对象实例有对该变量所包含的对象的共享需求
6. 对象重用与GC
通常我们把用来缓存对象的容器对象成为对象池(Object Pool)。对象池通过对其所保存对象的共享与重用,缩减了应用线程反复重建、装载对象的过程所需的时间,有效避免了频繁GC带来的巨大系统开销,能大大提高应用性能,减少内存需求。
但如果长时间将对象保存在对象池中,即驻留在内存中,而这些对象又不被经常使用,无疑会造成内存资源浪费,又或者该对象在对象池中遭到破坏,若不对其及时清除会非常麻烦。因此,若决定使用对象池技术,需要采取相应的手段清除遭到破坏的对象,甚至在某些情况下需要清除对象池中所有对象。或者可为对象池中的每个对象分配一个时间戳,设定对象的过期时间,对象过期则及时将其从内存中清除。可以专门创建一个线程来清除此类对象。
例:
7. transient变量
在做远程方法调用(RMI)类的应用开发时,应该会遇到transient变量与Serializable接口,之所以要对象实现Serializable接口,是因为这样就可以从远程环境以对象流的方式,将对象传递到相应的调用环境中,但有时这些被传递的对象的一些属性并不需要被传递,因为这些数据成员对于应用需求而言是无关紧要的,那么这些属性变量就可被声明为transient。被声明为transient的变量是不会被传递的,这样能节约调用方的内存资源,减少网络开销,提高系统性能。这个transient变量所携带的数据量越大(如数据量较大的数组),其效用越大。
8. JVM内存参数调优
-XX:NewSize,设置新对象成产堆内存(set new generation heap size)
通常该选项数值为1024的整数倍且大于1MB。取值规则为,取最大堆内存(maximum heap size)的1/4。
-XX:MaxNewSize,设置最大新对象生产堆内存(set maximum new generation heap size)
其功用同-XX: NewSize。
-Xms,设置堆内存池的最小值(set minimum heap size)
要求系统为堆内存池分配内存空间的最小值。通常该选项数值为1024的整数倍且大于1MB。取值规则为,取与最大堆内存(-Xmx)相同的值,以降低GC的频度。
-Xmx,设置堆内存池的最小值(set maximum heap size)
要求系统为堆内存池分配内存空间的最大值。通常该选项数值为1024的整数倍且大于1MB。取值规则为,取与最大堆内存(-Xms)相同的值,以降低GC的频度。
例:
java –XX:NewSize=128m –XX:MaxNewSize=128m –Xms512m –Xmx512m MyApp
9. Java程序设计中有关内存管理的其他经验
1) 尽早释放无用对象的引用,加速GC的工作
2) 尽量少用finalize()。finalize()是Java给程序员提供一个释放对象或资源的机会,但会加大GC的工作量
3) 对常用到的图片,可采用SoftReference
4) 注意集合数据类型,包括数组、树、图、链表等数据结构,它们对GC来说,回收更复杂。另外,注意一些全局变量、静态变量,它们易引起悬挂对象,造成内存浪费
5) 尽量避免在类的默认构造器中创建、初始化大量的对象,防止在调用其子类的构造器时造成不必要的内存资源浪费
6) 尽量避免强制系统做GC(通过显式调用System.gc()),增长系统GC的最终时间,降低系统性能
7) 尽量避免显式申请数组空间,当不得不显式申请时,尽量准确估计出其合理值
8) 在做远程方法调用(RMI)类应用开发时,尽量使用transient变量
9) 在合适的场景下使用对象池技术以提高系统性能,缩减系统内存开销,但需注意对象池的尺寸不宜过大,及时清除无效对象释放内存资源
JVM运行环境中垃圾对象的定义:
一个对象创建后被放置在JVM的堆内存(heap)中,当永远不再引用这个对象时,它将被JVM在堆内存(heap)中回收。被创建的对象不能再生,同时也没法通过程序语句释放它们。
不可到达的对象被JVM视为垃圾对象,JVM将给这些对象打上标记,然后清扫回收它们,并将散碎的内存单元收集整合。
JVM管理的两种类型的内存:
堆内存(heap),主要存储程序在运行时创建或实例化的对象与变量。
栈内存(stack),主要存储程序代码中声明为静态(static)(或非静态)的方法。
堆内存(heap)通常情况下被分为两个区域:新对象(new object)区域与老对象(old object)区域。
新对象区域:
又可细分为Eden区域、From区域与To区域。
Eden区域保存新创建的对象。当该区域中的对象满了后,JVM系统将做可达性测试,主要任务是检测有哪些对象由根集合出发是不可到达的,这些对象就可被JVM回收,且将所有的活动对象从Eden区域拷到To区域,此时有一些对象将发生状态交换,有的对象就从To区域被转移到From区域,此时From区域就有了对象。
该过程执行期间,JVM的性能非常低下,会严重影响到正在运行的应用的性能。
老对象区域:
在老对象区域中的对象仍有一个较长的生命周期。经过一段时间后,被转入老对象区域的对象就变成了垃圾对象,此时它们被打上相应的标记,JVM将自动回收它们。
建议不要频繁强制系统做垃圾回收,因为JVM会利用有限的系统资源,优先完成垃圾回收工作,致使应用无法快速响应来自用户端的请求,这样会影响系统的整体性能。
2. JVM中对象的生命周期
对象的整个生命周期大致分为7个阶段:创建(creation)、应用(using)、不可视(invisible)、不可到达(unreachable)、可收集(collected)、终结(finalized)、释放(free)。
1) 创建阶段
系统通过下面步骤,完成对象的创建:
a) 为对象分配存储空间
b) 开始构造对象
c) 递归调用其超类的构造方法
d) 进行对象实例初始化与变量初始化
e) 执行构造方法体
在创建对象时的几个关键应用规则:
• 避免在循环体中创建对象,即使该对象占用内存空间不大
• 尽量及时使对象符合垃圾回收标准
• 不要采用过深的继承层次
• 访问本地变量优于访问类中的变量
关于“在循环体中创建对象”,如下方式会在内存中产生大量的对象引用,浪费大量的内存空间,增大了系统做垃圾回收的负荷:
for (int i = 0; i < 1000; i++) { Object obj = new Object(); ... }
而如下方式,仅在内存中保存一份对该对象的引用:
Object obj = null; for (int i = 0; i < 1000; i++) { obj = new Object(); ... }
另外,不要对一个对象进行多次初始化,这同样会带来较大的内存开销,降低系统性能。
2) 应用阶段
该阶段是对象得以表现自身能力的阶段,即是对象整个生命周期中证明自身“存在价值”的时期。此时对象具备下列特征:
a) 系统至少维护着对象的一个强引用(Strong Reference)
b) 所有对该对象的引用全是强引用(除非我们显示使用了软引用(Soft Reference)、弱引用(Weak Reference)或虚引用(Phantom Reference))
软引用
主要特点是具有较强的引用功能。只有当内存不够时,才回收这类内存。另外,这些引用对象还能保证在Java抛出OutOfMemory异常前,被置为null。它可用于实现一些常用资源的缓存,保证最大限度的使用内存而不引起OutOfMemory。
例:
… import java.lang.ref.SoftReference; … A a = new A(); …// 使用a SoftReference sr = new SoftReference(a);// 使用完了a,将它设置为软引用类型 a = null;// 释放强引用 … // 下次使用时 if (sr != null) { a = sr.get(); } else { // GC由于内存资源不足,系统已回收了a的软引用,故需重新装载 a = new A(); sr = new SoftReference(a); } …
软引用技术使Java应用能更好地管理内存,稳定系统,防止系统内存溢出,避免系统崩溃。在处理一些占用内存较大,且声明周期较长,但使用并不频繁的对象时,应尽量应用该技术。
但某些时候对软引用的使用会降低应用的运行效率与性能,如:应用软引用的对象的初始化过程较耗时,或对象的状态在运行过程中发生了变化,都会给重新创建对象与初始化对象带来不同程度的麻烦。
弱引用
与软引用对象的最大不同在于:GC在进行回收时,需通过算法检查是否回收软引用对象,而对于弱引用对象,GC总是进行回收。故弱引用对象拥有更短的生命周期,更易更快被GC回收。
虽然GC在运行时一定回收弱引用对象,但是负责关系的弱对象群常常需要好几次GC的运行才能完成。弱引用对象常用于Map数据结构中,引用占用内存空间较大的对象,一旦该对象的强引用为null,GC能快速回收该对象空间。
例:
… import java.lang.ref.WeakReference; … A a = new A(); …// 使用a WeakReference wr = new WeakReference(a);// 使用完了a,将它设置为弱引用类型 a = null;// 释放强引用 … // 下次使用时 if (wr != null) { a = wr.get(); } else { a = new A(); wr = new WeakReference(a); } …
虚引用
用途较少,主要用于辅助finalize方法。虚对象指一些执行完了finalize方法,且为不可达对象,但还未被GC回收的对象。这种对象可辅助finalize进行一些后期的回收工作,通过覆盖Reference的clear方法增强资源回收机制的灵活性。
注意:实际程序设计中一般很少使用弱引用与虚引用,使用软引用的情况较多,因为软引用能加上JVM对垃圾内存的回收速度,能维护系统的运行安全,防止内存溢出等。
3) 不可视阶段
对象经历应用阶段后,便处于不可视阶段,说明我们在其他区域的代码中已不可再引用它,其强引用已消失。如:本地变量超出其可视范围。
例:
public void process() { try { Object obj = new Object(); obj.doSomething(); } catch (Exception e) { e.printStackTrace(); } while (isLoop) {// … loops forever // 该区域对应obj对象来说已是不可视的了 // obj. doSomething(); 在编译时会引发错误 } }
若一个对象已使用完,且在其可视区域不再使用,应主动将其设置为null。针对上面的例子,可主动添加obj = null;这样一行代码,强制将obj置为null。这样的意义是,帮助JVM及时发现这个垃圾对象,且可以及时回收该对象所占用的系统资源。
4) 不可到达阶段
处于不可到达阶段的对象,都是要被GC回收的预备对象,但此时该对象并不能被GC直接回收。其实所有垃圾回收算法所面临的问题是相同的——找出由分配器分配的,但用户程序不可到达的内存块。
5) 可收集阶段、终结阶段与释放阶段
此时JVM就可以直接回收对象了,而对象可能处于下面三种情况:
a) GC发现该对象已不可到达
b) finalize方法已被执行
c) 对象空间已被重用
3. Java中的析构方法finalize
Java中的finalize()与C++中的析构函数的职能极为类似。
虽然我们可以在一个Java类调用其父类的finalize方法,但由于该方法未自动实现递归调用,我们必须手动实现,因此该方法的最后一个语句通常是super.finalize()语句。通过这种方式,我们能实现从下到上finalize的迭代调用,即先释放用户类自身的资源,再释放父类资源。通过可在finalize()中释放一些不易控制,且非常重要的资源,如:一些I/O操作,数据的连接。
finalize()最终由JVM中的垃圾回收器调用,其调用时间是不确定或不及时的,调用时机对我们来说是不可控的。因此,有时我们需要通过其他手段释放系统资源,如声明一个destroy(),在该方法中添加释放系统资源的处理代码。虽然我们可以通过调用自定义的destroy()释放系统资源,但最好将对destroy()的调用放入当前类的finalize()中,因为这样更保险、安全。在类深度继承的情况下,这种方法就显得更有效了。
例:
public class A { Object a = null; public A() { a = new Object(); System.out.println("create a"); } protected void destroy() { a = null; System.out.println("release a"); } protected void finalize() throws Throwable { destroy(); super.finalize();// 递归调用父类的finalize() } } public class B extends A { Object b = null; public B() { b = new Object(); System.out.println("create b"); } protected void destroy() { b = null; System.out.println("release b"); super.destroy(); } protected void finalize() throws Throwable { destroy(); super.finalize();// 递归调用父类的finalize() } } public class MyTest { B obj = null; public MyTest() { obj = new B(); } protected void destroy() { if (obj != null) { obj.destroy(); } else { System.out.println("obj already released"); } } public static void main(String[] args) { MyTest mt = new MyTest(); mt.destroy(); } }
MyTest的运行结果:
create a
create b
release b
release a
初始化B对象时,其构造器产生了递归调用,由父类开始依次调用,而在调用B对象的destroy()时,系统产生了与初始化调用相反的递归调用,释放资源是由子类开始的。由此可见,在设计类时,应尽可能避免在类的默认构造器中创建、初始化大量对象,或执行某种复杂、耗时的运算逻辑。
4. 数组的创建
如果数组中所保存的元素占用内存空间较大,或数组本身长度较长,我们可采用软引用的技术来引用数组,以“提醒”JVM及时回收垃圾内存,维护系统的稳定性。
例:
char[] obj = new char[1000000]; SoftReference ref = new SoftReference(obj);
JVM根据运行时的内存资源的使用情况,来把握是否回收该对象,释放内存。虽然这样会对应用程序产生一些影响(如当需要使用该数组对象时,该对象被回收了),但却能保证应用整体的稳健性,达到合理使用系统内存的目的。
5. 共享静态变量存储空间
静态变量生命周期较长,不易被系统回收。因此如果不合理使用静态变量,会造成大量的内存浪费。建议在具备下列全部条件的情况下,尽量使用静态变量:
1) 变量所包含的对象体积较大,占用内存较多
2) 变量所包含的对象生命周期较长
3) 变量所包含的对象数据稳定
4) 该类的对象实例有对该变量所包含的对象的共享需求
6. 对象重用与GC
通常我们把用来缓存对象的容器对象成为对象池(Object Pool)。对象池通过对其所保存对象的共享与重用,缩减了应用线程反复重建、装载对象的过程所需的时间,有效避免了频繁GC带来的巨大系统开销,能大大提高应用性能,减少内存需求。
但如果长时间将对象保存在对象池中,即驻留在内存中,而这些对象又不被经常使用,无疑会造成内存资源浪费,又或者该对象在对象池中遭到破坏,若不对其及时清除会非常麻烦。因此,若决定使用对象池技术,需要采取相应的手段清除遭到破坏的对象,甚至在某些情况下需要清除对象池中所有对象。或者可为对象池中的每个对象分配一个时间戳,设定对象的过期时间,对象过期则及时将其从内存中清除。可以专门创建一个线程来清除此类对象。
例:
class CleanUpThread extends Thread { private ObjectPool pool; private long sleepTime; CleanUpThread(ObjectPool pool, long sleepTime) { this.pool = pool; this.sleepTime = sleepTime; } public void run() { while (true) { try { sleep(sleepTime); } catch (InterruptedException e) { …// 相应处理 } pool.cleanUp(); } } }
7. transient变量
在做远程方法调用(RMI)类的应用开发时,应该会遇到transient变量与Serializable接口,之所以要对象实现Serializable接口,是因为这样就可以从远程环境以对象流的方式,将对象传递到相应的调用环境中,但有时这些被传递的对象的一些属性并不需要被传递,因为这些数据成员对于应用需求而言是无关紧要的,那么这些属性变量就可被声明为transient。被声明为transient的变量是不会被传递的,这样能节约调用方的内存资源,减少网络开销,提高系统性能。这个transient变量所携带的数据量越大(如数据量较大的数组),其效用越大。
8. JVM内存参数调优
-XX:NewSize,设置新对象成产堆内存(set new generation heap size)
通常该选项数值为1024的整数倍且大于1MB。取值规则为,取最大堆内存(maximum heap size)的1/4。
-XX:MaxNewSize,设置最大新对象生产堆内存(set maximum new generation heap size)
其功用同-XX: NewSize。
-Xms,设置堆内存池的最小值(set minimum heap size)
要求系统为堆内存池分配内存空间的最小值。通常该选项数值为1024的整数倍且大于1MB。取值规则为,取与最大堆内存(-Xmx)相同的值,以降低GC的频度。
-Xmx,设置堆内存池的最小值(set maximum heap size)
要求系统为堆内存池分配内存空间的最大值。通常该选项数值为1024的整数倍且大于1MB。取值规则为,取与最大堆内存(-Xms)相同的值,以降低GC的频度。
例:
java –XX:NewSize=128m –XX:MaxNewSize=128m –Xms512m –Xmx512m MyApp
9. Java程序设计中有关内存管理的其他经验
1) 尽早释放无用对象的引用,加速GC的工作
2) 尽量少用finalize()。finalize()是Java给程序员提供一个释放对象或资源的机会,但会加大GC的工作量
3) 对常用到的图片,可采用SoftReference
4) 注意集合数据类型,包括数组、树、图、链表等数据结构,它们对GC来说,回收更复杂。另外,注意一些全局变量、静态变量,它们易引起悬挂对象,造成内存浪费
5) 尽量避免在类的默认构造器中创建、初始化大量的对象,防止在调用其子类的构造器时造成不必要的内存资源浪费
6) 尽量避免强制系统做GC(通过显式调用System.gc()),增长系统GC的最终时间,降低系统性能
7) 尽量避免显式申请数组空间,当不得不显式申请时,尽量准确估计出其合理值
8) 在做远程方法调用(RMI)类应用开发时,尽量使用transient变量
9) 在合适的场景下使用对象池技术以提高系统性能,缩减系统内存开销,但需注意对象池的尺寸不宜过大,及时清除无效对象释放内存资源
相关推荐
在北大青鸟学习时第一期的笔记,包括基础应用、java、C#基础、SQL Server基础 、HTML基础等等
Java编程思想(第4版)读书笔记by CZFJava编程思想(第4版)读书笔记by CZFJava编程思想(第4版)读书笔记by CZF
java语言编写的画板程序,实现的功能基本上和笔记本电脑自带的画板功能差不多,可以直接运行成功
JAVA编程题全集(100题及答案).doc java面试书籍源码 Java面试笔试题库.CHM Java面试问题集.pdf Java面试题以及答案(小生).pdf java面试题(题库全).doc JS 数据库答案.doc Land.the.Tech.Job.You.Love-人人都有好...
JAVA编程题全集(100题及答案).doc java面试书籍源码 Java面试文档题库 Java面试笔试题库.CHM Java面试问题集.pdf Java面试题以及答案(小生).pdf java面试题(题库全).doc JS 数据库答案.doc Land.the.Tech.Job.You....
JAVA学习笔记第八天——示例代码Day08,包含接口作为方法参数和返回值示例代码、多态示例代码、内部类示例代码
JAVA学习笔记第七天——示例代码Day07,其中包含访问修饰符示例代码、final关键字示例代码、接口示例代码、接口和类的关系示例代码
类是相同对象的集合,并为这些对象定义了编程语言上的属性和方法。类修饰符:new在类声明时使用,public公共的,访问不受限,protected只能从其所在类和所在类的子类中进行访问,internal只有其所在类才能进行访问,...
000000_【课程介绍 —— 写在前面的话】_Java学习概述笔记.pdf 010101_【第1章:JAVA概述及开发环境搭建】_JAVA发展概述笔记.pdf 010102_【第1章:JAVA概述及开发环境搭建】_Java开发环境搭建笔记.pdf 010201_【第2...
java面试笔试资料Java经典项目集锦java笔试题大集合及答案题库java...JAVA编程题全集(100题及答案).doc Java面试文档题库 Java面试笔试题库.CHM java面试笔试题库资料合集.zip Java面试问题集.pdf Java面试题以及答案
一.用引用操纵对象 每种编程语言都有自己的数据操纵方式。有时候,程序员必须注意将要处理的数据是什么类型。...这样做就消除了这类编程问题(即“内存泄漏”),这是由于程序员忘记是放内存而产生的问题。
JAVA编程题全集(100题及答案).doc Java面试文档题库 Java面试笔试题库.CHM java面试笔试题库资料合集.zip Java面试问题集.pdf Java面试题以及答案(小生).pdf java面试题(题库全).doc JS 数据库答案.doc Land.the....
第一章面向对象的Java实现——封装 第二章面向对象的Java实现——继承和多态 第三章面向对象的Java实现——接口
Java 基础 第2阶段:面向对象编程——尚硅谷学习笔记(含面试题) 2023年
JAVA 的面向对象编程——课堂笔记,挺经典的。里面打包了pdf和txt两种版本,共33页~
它旨在提供一个全面、深入的学习和研究工具,适用于本科课程设计、毕业设计以及任何希望深入学习Java编程的学习者。 详细内容: 源代码——提供了一套完整、经过良好注释的Java源代码,实现了留言管理程序项目的...
Java 语言是一种面向对象的编程语言。虽然 Java 仅仅只产生了短短 20 年,但是它的发展是非常迅速的。在 2009 年 4 月 20 号,ORACLE 收购了 Sun 公司,也就是说 Java 这门语言现在归属于 ORACLE 这家公司
面试题包含了不同技术层面的面试问题,同时也能对一些没有面试开发经验的小白给予不可估量的包装, 让你的薪水绝对翻倍, 本人亲试有效.Java面试题84集、java面试专属及面试必问课程,所有的面试题有视屏讲解, 解答方案....
高级java笔试题 一个Java 攻城狮的笔记 涉及Java,数据结构,算法,前端,数据库的相关知识,补充面试的相关知识。 推荐使用vscode-Markdown Preview Enhanced插件打开。 推荐阅读人群 求职者可以看 :面试技巧; ...
个人总结,集10年开发经验,精挑细选!不要错过哦。非常经典,非常实在的资料.轻松上手,附上经典实例,