java易错知识点梳理

来源:互联网 发布:php双轨直销系统源码 编辑:程序博客网 时间:2024/05/17 07:51

Java

Java的内存分配上,主要分4个块!

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

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

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

昨日收获

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

 

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

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

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

 

类型转换

自动类型转换

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

具体规则为:

         byte→short(char)→int→long→float→double

 

强制类型转换

从存储范围大的类型到存储范围小的类型。
具体规则为:
         double→float→long→int→short(char)→byte

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

示例代码:

 

        int n = 123;

        byte b =(byte)n;

        int m =1234;

        byte b1 =(byte)m;

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

 

 

abstract class和interface有什么区别?

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

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

 

 

 

 

 

 

 

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

public class Singleton {

        privatestatic Singleton singleton;

        // 注意点: 私有化构造函数

        privateSingleton() {

        }

        //synchronized 多线程环境 

        publicsynchronized static Singleton getInsatnce() {

               if(singleton==null) {

                       singleton = new Singleton();

               }   

               return singleton;

        }  

}

 

sleep() 和 wait() 有什么区别?

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

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

 

 

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

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

例如 float a=2.0f

 

 

 

JAVA中的CollectionFrameWork(包括如何写自己的数据结构)?

Collection

       ├List

       │├LinkedList

       │├ArrayList

       │└Vector

       │ └Stack

       └Set

       Map

       ├Hashtable

       ├HashMap

       └WeakHashMap

       Collection是最基本的集合接口,一个Collection代表一组Object,即Collection的元素(Elements)

Map提供key到value的映射

 

Collection

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

comparable/comparator

 

ArrayList和Vector的区别

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

 

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

(1)同步性:

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

 

备注:对于Vector&ArrayList、Hashtable&HashMap,要记住线程安全的问题,记住Vector与Hashtable是旧的,是java一诞生就提供了的,它们是线程安全的,ArrayList与HashMap是java2时才提供的,它们是线程不安全的。所以,我们讲课时先讲老的。

(2)数据增长:

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

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

HashMap和Hashtable的区别

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

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

HashMap允许将null作为一个entry的key或者value,而Hashtable不允许。

HashMap把Hashtable的contains方法去掉了,改成containsvalue和containsKey。因为contains方法容易让人引起误解。

Hashtable继承自Dictionary类,而HashMap是Java1.2引进的Mapinterface的一个实现。

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

Hashtable和HashMap采用的hash/rehash算法都大概一样,所以性能不会有很大的差异。

 

就HashMap与HashTable主要从三方面来说。

一.历史原因:Hashtable是基于陈旧的Dictionary类的,HashMap是Java 1.2引进的Map接口的一个实现

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

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

List 和 Map 区别?

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

List, Set, Map是否继承自Collection接口?

   List,Set是,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)来明确说明取第几个。

       Map与List和Set不同,它是双列的集合,其中有put方法,定义如下:put(obj key,obj value),每次存储时,要存储一对key/value,不能存储重复的key,这个重复的规则也是按equals比较相等。取则可以根据key获得相应的value,即get(Object key)返回值为key 所对应的value。另外,也可以获得所有的key的结合,还可以获得所有的value的结合,还可以获得key和value组合成的Map.Entry对象的集合。

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

HashSet按照hashcode值的某种运算方式进行存储,而不是直接按hashCode值的大小进行存储。例如,"abc" --->78,"def" ---> 62,"xyz"---> 65在hashSet中的存储顺序不是62,65,78,这些问题感谢以前一个叫崔健的学员提出,最后通过查看源代码给他解释清楚,看本次培训学员当中有多少能看懂源码。LinkedHashSet按插入的顺序存储,那被存储对象的hashcode方法还有什么作用呢?学员想想!hashset集合比较两个对象是否相等,首先看hashcode方法是否相等,然后看equals方法是否相等。new 两个Student插入到HashSet中,看HashSet的size,实现hashcode和equals方法后再看size。

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

说出ArrayList,Vector,LinkedList的存储性能和特性

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

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

 

rehash

Java常用的集合

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

        Java集合构架支持三种类型的集合:规则集(Set)、线性表(List)和图(Map),它们分别定义在接口         Set、List与Map中。

        Set的实例存储一组无序不重复的元素;List的实例存储一组顺序排列可重复的元素;Map的实例存储一    组对象,每个对象都有一个关联的键值。

        Java集合构架中定义的接口和类都存储在包Java.util中,提供了一组完整的API,可以有效地存储和处     理这些对象构成的集合。用接口定义构架,用抽象类提供接口的一部分实现,用具体类通过具体的数据结构     实现整个接口。

        由于Java集合构架中的所有具体类都实现了Cloneable和Serializable接口,所以它们的实例都是可复     制和可序列化的。

     

     *、上面提到的Set的无序是指添加元素时的先后顺序不一定是元素在集合中的顺序(如                   TreeSet,HashSet)。但TresSet是有按内容大小的,二HashSet是随意的。

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

 

一、Collection接口和AbstractCollection类

       Collection接口是处理对象集合的根接口。Collection接口提供在集合中添加、删除、查询元素等基本     操作(供继承)。

       AbstractCollection类是提供对Collection接口部分实现的便利类,除了Size方法和interator方法之     外,它实现了Collection接口中的所有方法,而Size和iterator方法在适当的子类中实现。

 

二、规则集Set

        Set接口扩展Collection接口。它没有引入新的方法和常量,它只是规定它的实例不能包含相同的元       素。实现Set接口的具体类必须保证不能向它添加重复的元素。也就是说,在一个规则集中,一定不能存在     元素e1、e2使得e1.equals(e2)的返回值为true。

       AbstractSet类是一个便利类,它扩展了AbstractCollection类并实现了Set接口,AbstractSet类提供     了equals方法和hashCode方法的具体实现。一个规则集的散列码是这个集合中的所有元素散列码的和。由于     AbstractSet类没有实现size方法和iterator方法,所以AbstractSet类还是一个抽象类。

        Set接口的三个具体类是:HashSet、LinkedHashSet和TreeSet

 

三、散列集HashSet

       HashSet类是一个实现Set接口的具体类。可以使用它的无参构造方法创建HashSet对象,HashSet类可以     用来存储不重复的任何元素。添加到散列集之中的对象必须实现hashCode方法,适当地散列分布散列     码。Java API的大多数类实现了hashCode方法。

 

四、链式散列集LinkedHashSet

       LinkedHashSet使用链表实现了对HashSet类的扩展,支持规则集内元素的排序(添加的先后顺序与在集     合中的顺序是一样的)。在HashSet中元素是没有顺序的,而在LinkedHashSet中,可以按元素插入集合的顺     序进行提取。可以使用其无参构造方法来创建LinkedHashSet对象。

 

五、树形集TreeSet

       SortedSet是Set的一个子接口,它保证规则集中的元素是有序的。TreeSet是实现SortedSet接口的一个     具体类,可以用它无参构造方法来创建TreeSet对象,也可以用new  TreeSet(Collection)来创建。只有当     对象之间可以互相比较时,才可以将他们添加到树形集TreeSet中。

 

           *********************************************************

             注意:由于Set要求插入的元素必须是不重复的,所以,当我们在插

                  入一个元素时,它会与在集合中的其他元素进行内容的比较,

                  自动调用相关的比较内容的方法,如equals或CompareTo方法

                  所以,我们要重写 equals 和 hashSet 或继承Compareable

                  接口(重写里面的方法),这样保证了当x.equals(y)时

                  x.hashcode()一定等于y.hashcode()。

           *********************************************************

 

六、线性表List

         List是Collection的子接口,它用于保存有序的可重复的对象,它增加了对于索引的定义,放置在       List内的对象被有序排列(根据添加的顺序),但也可以通过一个整数索引来访问、添加指定的对象,索引的     值是从0开始。List还重载了add()和addAll()等方法,使得程序员可以通过索引值来指定插入对象在List中     的位置。如add(int index,Object obj)、addAll(intindex,Collection c)。

        List接口三个常用的具体类是:ArrayList、LinkedList、Vector

 

七、数组线性表ArrayList、链表LinkedList和Vector

       ArrayList将元素存储在一个数组中,该数组时动态创建的。超过数组的容量时,则创建一个更大的数

    组并将当前数组中的所有元素复制到新的数组中。LinkedList将元素存储在链表中。选用哪种,根据特定的     需求:如果需要使用下标随机访问元素,并且除了在末尾之外,不在其他位置上插入或删除元素,则           ArrayList提供了效率最高的集合结构。反过来,如果应用程序需要在线性表的任意位置上插入或删除元       素,应该选用LinkedList类。可以将ArrayList想象成一种可以自动增加容量可以存放不同类型对象的数组     (Array)。Vector和ArrayList非常类似,它们最大的区别在于,Vector是线程安全的,ArrayList不是。

 

             ****************************************************************

              注意:由于List是可重复的,所以当我们插入元素时,不需要去比较元素

                   内容的大小,所以无需重写equals和hashcode方法或继承Comparable

                   接口。

            ****************************************************************

 

八、图Map

       映射表(Map)用于存储元素对(称为“键”和“值”),一个映射不能包含重复的键,每个键最多只能映     射一个值,在Map中键和值的保存顺序不是他们的插入顺序。

        Map用put(key,value)方法来添加一个值,用get(key)方法获取与key键相关联的值。

 

九、Collections类

      Collections类包含了许多对集合进行运算的专用静态方法。这是一个处理集合的工具类

 

 

Transient

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

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

 

简述synchronized和java.util.concurrent.locks.Lock的异同 ?

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

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

 

 

 

简述逻辑操作(&,|,^)与条件操作(&&,||)的区别

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

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

 

Static NestedClass 和Inner Class的不同。

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

 

 

Java内存管理机制

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

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

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

 

 

 

 

 

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

Java内存泄露

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

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

 

1 Vector v=new Vector(10);

2 for (int i=1;i<100; i++){

3 Object o=new Object();

4 v.add(o);

5 o=null;

6 }

  

在这个例子中,代码栈中存在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 模块的一个方法如:

public void registerMsg(Object b);

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

 

7、单例模式

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

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

 

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

例如:

 

 1 public classFileSearch{

 2

 3     private byte[] content;

 4     private File mFile;

 5    

 6     public FileSearch(File file){

 7         mFile = file;

 8     }

 9

10     publicboolean hasString(String str){

11         int size= getFileSize(mFile);

12         content =new byte[size];

13         loadFile(mFile,content);

14        

15         String s= new String(content);

16         returns.contains(str);

17     }

18 }

 

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

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

Java的四种引用

1.强引用

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

 

2.软引用(SoftReference)

 

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

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

 

3.弱引用(WeakReference)

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

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

 

4.虚引用(PhantomReference)

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

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

==和equal的区别

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

2、String的equals()方法比较的是两个String对象的内容是否一样

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

short s1=1;s1=s1+1;有什么错?shorts1=1;s1+=1;有什么错?

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

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

且运算结果与运算符左边数值类型相同。在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只被计算一次。

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

原创粉丝点击