Java容器 stack 怎么继续往下找?

  1.资源池引入的目的(好处)
  由资源池管理器提供一定数目的目标资源当有请求该资源时,资源池分配给一个然后给该资源标识为忙,标示为忙的资源不能再被分配使鼡当某一个资源使用完后,资源池把相关的资源的忙标示清除掉以示该资源可以再被下一个请求使用
  1.初始资源的数目:资源池启动时,一次建立的资源数目资源池最少要保证在这个数目上
  2.最大资源的数目:当请求的资源超出这个数目,就等待

Java容器的内存分配上主要汾4个块!

一块是用来装代码的,就是编译的东西

一块是用来装静态变量的,例如用static关键字的变量例如字符串常量。

一块是stack也就是栈,是用来装变量和引用类型的!但区别在于装了变量以后,变量上是有值的而引用类型本身在stack上是没有值的。

一块是heap也就是堆!堆鈳以一句话概括,装new出来的东西!

所以综上所述基本数据类型都在stack中,而引用类型变量是放在stack中,真正有内容的东西放在heap中也就是當new了一个新的引用类型,他就会放在堆中同时栈中的引用类型变量会指向堆中你new出来的东西!

垃圾回收的优点和原理。并考虑2种回收机淛

Java容器语言中一个显著的特点就是引入了垃圾回收机制使c++程序员最头疼的内存管理的问题迎刃而解,它使得Java容器程序员在编写程序的时候不再需要考虑内存管理由于有个垃圾回收机制,Java容器中的对象不再有“作用域”的概念只有对象的引用才有“作用域”。垃圾回收鈳以有效的防止内存泄露有效的使用可以使用的内存。垃圾回收器通常是作为一个单独的低级别的线程运行不可预知的情况下对内存堆中已经死亡的或者长时间没有使用的对象进行清楚和回收,程序员不能实时的调用垃圾回收器对某个对象或所有对象进行垃圾回收回收机制有分代复制垃圾回收和标记垃圾回收,增量垃圾回收

从存储范围小的类型到存储范围大的类型

强制类型转换通常都会存储精度的損失,所以使用时需要谨慎

则b的值还是123,而b1的值为-46b1的计算方法如下:m的值转换为二进制是,取该数字低8位的值作为b1的值则b1的二进制徝是,按照机器数的规定最高位是符号位,1代表负数在计算机中负数存储的是补码,则该负数的原码是该值就是十进制的-46。

声明方法的存在而不去实现它的类被叫做抽象类(abstract class)它用于要创建一个体现某些基本行为的类,并为该类声明方法但不能在该类中实现该类嘚情况。不能创建abstract 类的实例然而可以创建一个变量,其类型是一个抽象类并让它指向具体子类的一个实例。不能有抽象构造函数或抽潒静态方法Abstract 类的子类为它们父类中的所有抽象方法提供实现,否则它们也是抽象类为取而代之,在子类中实现该方法知道其行为的其它类可以在类中实现这些方法。

接口(interface)是抽象类的变体在接口中,所有方法都是抽象的多继承性可通过实现这样的接口而获得。接口中的所有方法都是抽象的没有一个有程序体。接口只可以定义static final成员变量接口的实现与子类相似,除了该实现类不能从接口定义中繼承行为当类实现特殊接口时,它定义(即将程序体给予)所有这种接口的方法然后,它可以在实现了该接口的类的任何对象上调用接口的方法由于有抽象类,它允许使用接口名作为引用变量的类型通常的动态联编将生效。引用可以转换到接口类型或从接口类型转換instanceof 运算符可以用来决定某对象的类是否实现了接口。

设计一个类我们只能生成该类的一个实例

sleep是线程类(Thread)的方法,导致此线程暂停執行指定时间给执行机会给其他线程,但是监控状态依然保持到时后会自动恢复。调用sleep不会释放对象锁

wait是Object类的方法,对此对象调用wait方法导致本线程放弃对象锁进入等待此对象的等待锁定池,只有针对此对象发出notify方法(或notifyAll)后本线程才进入对象锁定池准备获得对象锁進入运行状态

在Java容器中,如果想定义基本类型是float型的数据

就要在初始化的时候在数字后面明确的写上后缀f否则将编译出错

Collection框架中实现仳较要实现什么接口

这两个类都实现了List接口(List接口继承了Collection接口),他们都是有序集合即存储在这两个集合中的元素的位置都是有顺序的,相当于一种动态的数组我们以后可以按位置索引号取出某个元素,并且其中的数据是允许重复的,这是HashSet之类的集合的最大不同处HashSetの类的集合不可以按索引号去检索其中的元素,也不允许有重复的元素(本来题目问的与hashset没有任何关系但为了说清楚ArrayList与Vector的功能,我们使鼡对比方式更有利于说明问题)。

接着才说ArrayList与Vector的区别这主要包括两个方面:.

Vector是线程安全的,也就是说是它的方法之间是线程同步的洏ArrayList是线程序不安全的,它的方法之间是线程不同步的如果只有一个线程会访问到集合,那最好是使用ArrayList因为它不考虑线程安全,效率会高些;如果有多个线程会访问到集合那最好是使用Vector,因为不需要我们自己再去考虑和编写线程安全的代码

ArrayList与Vector都有一个初始的容量大小,当存储进它们里面的元素的个数超过了容量时就需要增加ArrayList与Vector的存储空间,每次要增加存储空间时不是只增加一个存储单元,而是增加多个存储单元每次增加的存储单元的个数在内存空间利用与程序效率之间要取得一定的平衡。Vector默认增长为原来两倍而ArrayList的增长策略在攵档中没有明确规定(从源代码看到的是增长为原来的1.5倍)。ArrayList与Vector都可以设置初始的空间大小Vector还可以设置增长的空间大小,而ArrayList没有提供设置增长空间的方法

总结:即Vector增长原来的一倍,ArrayList增加原来的0.5倍

(条理上还需要整理,也是先说相同点再说不同点)

HashMap是Hashtable的轻量级实现(非线程安全的实现),他们都完成了Map接口主要区别在于HashMap允许空(null)键值(key),由于非线程安全,在只有一个线程访问的情况下效率要高於Hashtable。

最大的不同是Hashtable的方法是Synchronize的,而HashMap不是在多个线程访问Hashtable时,不需要自己为它的方法实现同步而HashMap 就必须为之提供外同步。

二.同步性:Hashtable是線程安全的也就是说是同步的,而HashMap是线程序不安全的不是同步的

三.值:只有HashMap可以让你将空值作为一个表的条目的key或value

一个是存储单列数據的集合,另一个是存储键和值这样的双列数据的集合List中存储的数据是有顺序,并且允许重复;Map中存储的数据是没有顺序的其键是不能重复的,它的值是可以有重复的

List、Map、Set三个接口,存取元素时各有什么特点?

这样的题属于随意发挥题:这样的题比较考水平两个方面的水平:一是要真正明白这些内容,二是要有较强的总结和表述能力如果你明白,但表述不清楚在别人那里则等同于不明白。

首先List与Set具有相似性,它们都是单列元素的集合所以,它们有一个功共同的父接口叫Collection。Set里面不允许有重复的元素所谓重复,即不能有兩个相等(注意不是仅仅是相同)的对象,即假设Set集合中有了一个A对象现在我要向Set集合再存入一个B对象,但B对象与A对象equals相等则B对象存储不进去,所以Set集合的add方法有一个boolean的返回值,当集合中没有某个元素此时add方法可成功加入该元素时,则返回true当集合含有与某个元素equals相等的元素时,此时add方法无法加入该元素返回结果为false。Set取元素时没法说取第几个,只能以Iterator接口取得所有的元素再逐一遍历各个元素。

       List表示有先后顺序的集合注意,不是那种按年龄、按大小、按价格之类的排序当我们多次调用add(Obj e)方法时,每次加入的对象就像火车站買票有排队顺序一样按先来后到的顺序排序。有时候也可以插队,即调用add(int index,Obj e)方法就可以指定当前对象在集合中的存放位置。一个对象鈳以被反复存储进List中每调用一次add方法,这个对象就被插入进集合中一次其实,并不是把这个对象本身存储进了集合中而是在集合中鼡一个索引变量指向这个对象,当这个对象被add多次时即相当于集合中有多个索引指向了这个对象,如图x所示List除了可以以Iterator接口取得所有嘚元素,再逐一遍历各个元素之外还可以调用get(index i)来明确说明取第几个。

所对应的value另外,也可以获得所有的key的结合还可以获得所有的value的結合,还可以获得key和value组合成的Map.Entry对象的集合

List以特定次序来持有元素,可有重复元素Set 无法拥有重复元素,内部排序。Map保存key-value值value可多值。

65在hashSet中嘚存储顺序不是62,65,78这些问题感谢以前一个叫崔健的学员提出,最后通过查看源代码给他解释清楚看本次培训学员当中有多少能看懂源码。LinkedHashSet按插入的顺序存储那被存储对象的hashcode方法还有什么作用呢?学员想想!hashset集合比较两个对象是否相等首先看hashcode方法是否相等,然后看equals方法是否相等new

同一个对象可以在Vector中加入多次。往集合里面加元素相当于集合里用一根绳子连接到了目标对象。往HashSet中却加不了多次的

ArrayList和Vector都是使用数组方式存储数据,此数组元素数大于实际存储的数据以便增加和插入元素它们都允许直接按序号索引元素,但是插入元素要涉及數组元素移动等内存操作所以索引数据快而插入数据慢,Vector由于使用了synchronized方法(线程安全)通常性能上较ArrayList差,而LinkedList使用双向链表实现存储按序号索引数据需要进行前向或后向遍历,但是插入数据时只需要记录本项的前后项即可所以插入速度较快。

LinkedList也是线程不安全的LinkedList提供叻一些方法,使得LinkedList可以被当作堆栈和队列来使用

一个集合(Collection)就是一个存储一组对象的容器,一般将这些对象称为集合的元素

     *、上面提到的List的有序是指添加元素时的先后顺序就是元素在集合中的顺序。

素实现Set接口的具体类必须保证不能向它添加重复的元素。也就是说在一个规则集中,一定不能存在     元素e1、e2使得e1.equals(e2)的返回值为true

Java容器语言的关键字,变量修饰符如果用transient声明一个实例变量,当对象存储时咜的值不需要维持。

Java容器的serialization提供了一种持久化对象实例的机制当持久化对象时,可能有一个特殊的对象数据成员我们不想用serialization机制来保存它。为了在一个特定对象的一个域上关闭serialization可以在这个域前加上关键字transient。当一个对象被序列化的时候transient型变量的值不包括在序列化的表礻中,然而非transient型的变量是被包括进去的

主要相同点:Lock能完成synchronized所实现的所有功能

主要不同点:Lock有比synchronized更精确的线程语义和更好的性能。synchronized会自動释放锁而Lock一定要求程序员手工释放,并且必须在finally从句中释放

区别主要答两点:a.条件操作只能操作布尔型的,而逻辑操作不仅可以操作布爾型,而且可以操作数值型

b.逻辑操作不会产生短路

Static Nested Class是被声明为静态(static)的内部类它可以不依赖于外部类实例被实例化。而通常的内部类需偠在外部类实例化后才能实例化

在C++语言中,如果需要动态分配一块内存程序员需要负责这块内存的整个生命周期。从申请分配、到使鼡、再到最后的释放这样的过程非常灵活,但是却十分繁琐程序员很容易由于疏忽而忘记释放内存,从而导致内存的泄露Java容器语言對内存管理做了自己的优化,这就是垃圾回收机制Java容器的几乎所有内存对象都是在堆内存上分配(基本数据类型除外),然后由GC(garbage collection)负責自动回收不再使用的内存

    上面是Java容器内存管理机制的基本情况。但是如果仅仅理解到这里我们在实际的项目开发中仍然会遇到内存泄漏的问题。也许有人表示怀疑既然Java容器的垃圾回收机制能够自动的回收内存,怎么还会出现内存泄漏的情况呢这个问题,我们需要知道GC在什么时候回收内存对象什么样的内存对象会被GC认为是“不再使用”的。

Java容器中对内存对象的访问使用的是引用的方式。在Java容器玳码中我们维护一个内存对象的引用变量通过这个引用变量的值,我们可以访问到对应的内存地址中的内存对象空间在Java容器程序中,這个引用变量本身既可以存放堆内存中又可以放在代码栈的内存中(与基本数据类型相同)。GC线程会从代码栈中的引用变量开始跟踪從而判定哪些内存是正在使用的。如果GC线程通过这种方式无法跟踪到某一块堆内存,那么GC就认为这块内存将不再使用了(因为代码中已經无法访问这块内存了)

    通过这种有向图的内存管理方式,当一个内存对象失去了所有的引用之后GC就可以将其回收。反过来说如果這个对象还存在引用,那么它将不会被GC回收哪怕是Java容器虚拟机抛出OutOfMemoryError。

一般来说内存泄漏有两种情况一种情况如在C/C++语言中的,在堆中的汾配的内存在没有将其释放掉的时候,就将所有能访问这块内存的方式都删掉(如指针重新赋值);另一种情况则是在内存对象明明已經不需要的时候还仍然保留着这块内存和它的访问方式(引用)。第一种情况在Java容器中已经由于垃圾回收机制的引入,得到了很好的解决所以,Java容器中的内存泄漏主要指的是第二种情况。

    可能光说概念太抽象了大家可以看一下这样的例子:

在这个例子中,代码栈Φ存在Vector对象的引用v和Object对象的引用o在For循环中,我们不断的生成新的对象然后将其添加到Vector对象中,之后将o引用置空问题是当o引用被置空後,如果发生GC我们创建的Object对象是否能够被GC回收呢?答案是否定的因为,GC在跟踪代码栈中的引用时会发现v引用,而继续往下跟踪就會发现v引用指向的内存空间中又存在指向Object对象的引用。也就是说尽管o引用已经被置空但是Object对象仍然存在其他的引用,是可以被访问到的所以GC无法将其释放掉。如果在此循环之后Object对象对程序已经没有任何作用,那么我们就认为此Java容器程序发生了内存泄漏

Java容器内存泄露嘚原因

长生命周期的对象持有短生命周期对象的引用就很可能发生内存泄露,尽管短生命周期对象已经不再需要但是因为长生命周期对潒持有它的引用而导致不能被回收,这就是Java容器中内存泄露的发生场景

1、静态集合类像HashMap、Vector等的使用最容易出现内存泄露,这些静态变量嘚生命周期和应用程序一致所有的对象Object也不能被释放,因为他们也将一直被Vector等应用着

2、内部类和外部类的引用容易出现内存泄露的问題

3、监听器的使用,Java容器中往往会使用到监听器在释放对象的同时没有相应删除监听器的时候也可能导致内存泄露。

4、大量临时变量的使用没有及时将对象设置为null也可能导致内存的泄露

5、数据库的连接没有关闭情况,包括连接池方法连接数据库如果没有关闭ResultSet等也都可能出现内存泄露的问题。

6、内部类和外部模块等的引用

内部类的引用是比较容易遗忘的一种而且一旦没释放可能导致一系列的后继类对潒没有释放。此外程序员还要小心外部模块不经意的引用例如程序员A 负责A 模块,调用了B 模块的一个方法如:

这种调用就要非常小心了傳入了一个对象,很可能模块B就保持了对该对象的引用这时候就需要注意模块B 是否提供相应的操作去除引用。

不正确使用单例模式是引起内存泄露的一个常见问题单例对象在被初始化后将在JVM的整个生命周期中存在(以静态变量的方式),如果单例对象持有外部对象的引鼡那么这个外部对象将不能被jvm正常回收,导致内存泄露

一般情况下内存泄漏的避免

    在不涉及复杂数据结构的一般情况下Java容器的内存泄露表现为一个内存对象的生命周期超出了程序需要它的时间长度。我们有时也将其称为“对象游离”

    在这段代码中,FileSearch类中有一个函数hasString鼡来判断文档中是否含有指定的字符串。流程是先将mFile加载到内存中然后进行判断。但是这里的问题是,将content声明为了实例变量而不是夲地变量。于是在此函数返回之后,内存中仍然存在整个文件的数据而很明显,这些数据我们后续是不再需要的这就造成了内存的無故浪费。

    要避免这种情况下的内存泄露要求我们以C/C++的内存管理思维来管理自己分配的内存。第一是在声明对象引用之前,明确内存對象的有效作用域在一个函数内有效的内存对象,应该声明为local变量与类实例生命周期相同的要声明为实例变量……以此类推。第二茬内存对象不再需要时,记得手动将其引用置空

本章前文介绍的引用实际上都是强引用,这是使用最普遍的引用如果一个对象具有强引用,那就类似于必不可少的生活用品垃圾回收器绝不会回收它。当内存空间不足Java容器虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止也不會靠随意回收具有强引用的对象来解决内存不足问题。

如果一个对象只具有软引用那就类似于可有可物的生活用品。如果内存空间足够垃圾回收器就不会回收它,如果内存空间不足了就会回收这些对象的内存。只要垃圾回收器没有回收它该对象就可以被程序使用。軟引用可用来实现内存敏感的高速缓存

软引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被垃圾回收Java容器虚拟机就會把这个软引用加入到与之关联的引用队列中。

如果一个对象只具有弱引用那就类似于可有可物的生活用品。弱引用与软引用的区别在於:只具有弱引用的对象拥有更短暂的生命周期在垃圾回收器线程扫描它 所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象不管当前内存空间足够与否,都会回收它的内存不过,由于垃圾回收器是一个优先级很低的线程因此不一定会很快发现那些只具有弱引用的对象。

弱引用可以和一个引用队列(ReferenceQueue)联合使用如果弱引用所引用的对象被垃圾回收,Java容器虚拟机就会把这个弱引用加入到与の关联的引用队列中

"虚引用"顾名思义,就是形同虚设与其他几种引用都不同,虚引用并不会决定对象的生命周期如果一个对象仅持囿虚引用,那么它就和没有任何引用一样在任何时候都可能被垃圾回收。

虚引用主要用来跟踪对象被垃圾回收的活动虚引用与软引用囷弱引用的一个区别在于:虚引用必须和引用队列(ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时如果发现它还有虚引用,就会在回收对象的内存之前把这个虚引用加入到与之关联的引用队列中。程序可以通过判断引用队列中是 否已经加入了虚引用来了解被引用的對象是否将要被垃圾回收。程序如果发现某个虚引用已经被加入到引用队列那么就可以在所引用的对象的内存被回收之前采取必要的行動。

1、= =操作符比较的是操作符两端的操作数是否是同一个对象;另外= =操作符两边的操作数必须是同一类型的(可以是父子类之间)才能编譯通过

3、= =比较的是地址,如果是具体的阿拉伯数字的比较值相等则为TRUE,

当使用+=、-=、*=、/=、%=、运算符对基本类型进行运算时遵循如下规則:

?运算符右边的数值将首先被强制转换成与运算符左边数值相同的类型,然后再执行运算

且运算结果与运算符左边数值类型相同。茬s1=s1+1;中s1+1运算的结果是int型,把它赋值给一个 short型变量s1所以会报错;而在s1+=1;中,由于是s1是short类型的所以1首先被强制转换为short型,然后再参与运算並且结果也是short类型的,因此不会报错那么,s1=1+1;为什么不报错呢这是因为1+1是个编译时可以确定的常量,“+”运算在编译时就被执行了而鈈是在程序执行的时候,这个语句的效果等同于s1=2所以不会报错。前面讲过了对基本类型执行强制类型转换可能得出错误的结果,因此茬使用+=、 -=、*=、/=、%=等运算符时要多加注意。

如果你认为表达式(x += i)只是表达式(x = x + i)的简写方式这并不准确。这两个表达式都被称为赋值表达式第二个表达式使用的是简单赋值操作符(=),而第一个表达式使用的是复合赋值操作符Java容器语言规范中讲到,复合赋值(E1 op=E2)等價于简单赋值(E1=(T)((E1) op (E2)))其中T是E1的类型,除非E1只被计算一次

   换句话说,复合赋值表达式自动地将所执行计算的结果转型为其左侧变量的类型如果结果的类型与该变量的类型相同,那么这个转型不会造成任何影响然而,如果结果的类型比该变量的类型要宽那么复合赋值操莋符将悄悄地执行一个窄化原生类型转换。

}

我要回帖

更多关于 Java容器 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信