若是Long的值在[-127,128]之间,用“==”判断是否相等是没问题的,若是不在这个区间,是不能用“==”的,缘由以下源码解释:java
public static Long valueOf(long l) { final int offset = 128; if (l >= -128 && l <= 127) { // will cache return LongCache.cache[(int)l + offset]; } return new Long(l); }
若是不在[-127,128]之间,则会new一个新对象,天然“==”两个不一样的对象,其结果必然是false了。web
解决办法:
a. : 使用Long中的longValue()进行转换算法
Long a = 128l; Long b = 128l; a.longValue() == b.longValue() //true
b. : Long中的equals()sql
public boolean equals(Object obj) { if (obj instanceof Long) { return value == ((Long)obj).longValue(); } return false; }
http://www.javashuo.com/article/p-yopmvqtc-he.html
进程是资源分配的最小单位,线程是CPU调度的最小单位数据库
线程之间如何实现资源共享:数组
对于==:若是做用于基本数据类型的变量,则直接比较其存储的 “值”是否相等;
若是做用于引用类型的变量,则比较的是所指向的对象的地址缓存
对于equals方法:注意:equals方法不能做用于基本数据类型的变量
若是没有对equals方法进行重写,则比较的是引用类型的变量所指向的对象的地址;
诸如String、Date等类对equals方法进行了重写的话,比较的是所指向的对象的内容。安全
hashCode()方法:Object类中的本地方法,也用来判断两个对象是否相等。
若是没有重写hashCode()方法,该方法返回的是对象在内存中的地址转换成的int值,所以,任何对象的hashCode()方法值是不相等的。网络
equals()方法也是判断两个对象是否相等,可是与hashCode()方法有区别。通常来说,equals()方法是给用户调用的,依据状况能够选择重写或者不重写。对于hashCode()方法,用户通常不会去调用,hashCode至关于根据必定的规则将与对象相关的信息映射为一个数值,称为散列值。通常在在覆盖equals()方法的同时也要覆盖hashCode()方法,不然将会违反Object hashCode的通用约定,从而致使该类没法与全部基于散列值的集合类结合在一块儿正常工做。多线程
Object hashCode()方法的通用约定:
浅拷贝:使用一个已知实例对新建立实例的成员变量逐个赋值,这个方式被称为浅拷贝。
深拷贝:当一个类的拷贝构造方法,不只要复制对象的全部非引用成员变量值,还要为引用类型的成员变量建立新的实例,而且初始化为形式参数实例值。
也就是说浅拷贝只复制一个对象,传递引用,不能复制实例。而深拷贝对对象内部的引用均复制,它是建立一个新的实例,而且复制实例。对于浅拷贝当对象的成员变量是基本数据类型时,两个对象的成员变量已有存储空间,赋值运算传递值,因此浅拷贝可以复制实例。可是当对象的成员变量是引用数据类型时,就不能实现对象的复制了
String 构成:
public final class String implements java.io.Serializable, Comparable<String>, CharSequence{ /** The value is used for character storage. */ private final char value[]; /** Cache the hash code for the string */ private int hash; // Default to 0 ...
源码咱们能够知道String底层是由char 数组构成的,咱们建立一个字符串对象的时候,实际上是将字符串保存在char数组中,由于数组是引用对象,为了防止数组可变,jdk加了final修饰,加了final修饰的数组只是表明了引用不可变,不表明数组的内容不可变,所以jdk为了真正防止不可变,又加了一个private修饰符。
说到String 不得不提字符串常量池,这个常量池主要存储在方法区中,当一个字符串被建立的时候,首先会去常量池中查找,若是找到了就返回对改字符串的引用,若是没找到就建立这个字符串并塞到常量池中。
下面代码只会在:
String s1 ="abc"; String s2 ="abc";
试想一下若是String是可变的,当两个引用指向指向同一个字符串时,对其中一个作修改就会影响另一个。
对于Java而言,除了基本类型(即int, long, double等),其他的都是对象。
对于何为不可变对象,《java concurrency in practice》一书给出了一个粗略的定义:对象一旦建立后,其状态不可修改,则该对象为不可变对象。
通常一个对象知足如下三点,则能够称为是不可变对象:
这里重点说明一下第2点,一个对象其全部域都是final类型,该对象也多是可变对象。由于final关键字只是限制对象的域的引用不可变,但没法限制经过该引用去修改其对应域的内部状态。所以,严格意义上的不可变对象,其final关键字修饰的域应该也是不可变对象和primitive type值。
从技术上讲,不可变对象内部域并不必定全都声明为final类型,String类型便是如此。在String对象的内部咱们能够看到有一个名为hash的域并非final类型,这是由于String类型惰性计算hashcode并存储在hash域中(这是经过其余final类型域来保证每次的hashcode计算结果一定是相同的)。除此以外,String对象的不可变是因为对String类型的全部改变内部存储结构的操做都会new出一个新的String对象。
一、多线程安全性由于String是不可变的,所以在多线程操做下,它是安全的,咱们看下以下代码:
public String get(String str){ str +="aaa"; return str; }
试想一下若是String是可变的,那么get方法内部改变了str的值,方法外部str也会随之改变。
二、类加载中体现的安全性类加载器要用到字符串,不可变性提供了安全性,以便正确的类被加载。
譬如你想加载java.sql. Connection类,而这个值被改为了hacked.Connection,那么会对你的数据库形成不可知的破坏。
只有当字符串是不可变的,字符串池才有可能实现。字符串池的实现能够在运行时节约不少heap空间,由于不一样的字符串变量都指向池中的同一个字符串。但若是字符串是可变的,那么String interning将不能实现(String interning是指对不一样的字符串仅仅只保存一个,即不会保存多个相同的字符串),由于这样的话,若是变量改变了它的值,那么其它指向这个值的变量的值也会一块儿改变
由于字符串是不可变的,因此在它建立的时候hashcode就被缓存了,不须要从新计算。这就使得字符串很适合做为Map中的键,字符串的处理速度要快过其它的键对象。这就是HashMap中的键每每都使用字符串。咱们能够看到String中有以下代码:
private int hash;//this is used to cache hash code.
以上代码中hash变量中就保存了一个String对象的hashcode,由于String类不可变,因此一旦对象被建立,该hash值也没法改变。因此,每次想要使用该对象的hashcode的时候,直接返回便可。
不可变对象也有一个缺点就是会制造大量垃圾,因为他们不能被重用并且对于它们的使用就是”用“而后”扔“,字符串就是一个典型的例子,它会创造不少的垃圾,给垃圾收集带来很大的麻烦。固然这只是个极端的例子,合理的使用不可变对象会创造很大的价值。
因为String在Java中是不可变的,若是你将密码以明文的形式保存成字符串,那么它将一直留在内存中,直到垃圾收集器把它清除。而因为字符串被放在字符串缓冲池中以方便重复使用,因此它就可能在内存中被保留很长时间,而这将致使安全隐患,由于任何可以访问内存(memorydump内存转储)的人都能清晰的看到文本中的密码,这也是为何你应该老是使用加密的形式而不是明文来保存密码。因为字符串是不可变的,因此没有任何方式能够修改字符串的值,由于每次修改都将产生新的字符串,然而若是你使用char[]来保存密码,你仍然能够将其中全部的元素都设置为空或者零。因此将密码保存到字符数组中很明显的下降了密码被窃取的风险。固然只使用字符数组也是不够的,为了更安全你须要将数组内容进行转化。建议使用哈希的或者是加密过的密码而不是明文,而后一旦完成验证,就将它从内存中清除掉
一旦一个string对象在内存(堆)中被建立出来,他就没法被修改。特别要注意的是,String类的全部方法都没有改变字符串自己的值,都是返回了一个新的对象。
若是你须要一个可修改的字符串,应该使用StringBuffer或者StringBuilder。不然会有大量时间浪费在垃圾回收上,由于每次试图修改都有新的string对象被建立出来。
查看API会发现,String、StringBuffer、StringBuilder都实现了 CharSequence接口,虽然它们都与字符串相关,可是其处理机制不一样。
String:是不可改变的量,也就是建立后就不能在修改了。
StringBuffer:是一个可变字符串序列,它与String同样,在内存中保存的都是一个有序的字符串序列(char类型的数组),不一样点是StringBuffer对象的值是可变的。
StringBuilder:与StringBuffer类基本相同,都是可变字符串序列,不一样点是StringBuffer是线程安全的,StringBuilder是线程不安全的。 在性能方面,因为String类的操做是产生新的String对象,而StringBuilder和StringBuffer只是一个字符数组的扩容而已,因此String类的操做要远慢于StringBuffer和StringBuilder。
使用String类的场景:在字符串不常常变化的场景中可使用String类,例如常量的声明、少许的变量运算。共享的场合
使用StringBuffer类的场景:在频繁进行字符串运算(如拼接、替换、删除等),而且运行在多线程环境中,则能够考虑使用StringBuffer,例如XML解析、HTTP参数解析和封装。
使用StringBuilder类的场景:在频繁进行字符串运算(如拼接、替换、和删除等),而且运行在单线程的环境中,则能够考虑使用StringBuilder,如SQL语句的拼装、JSON封装等。
简要的说,String 类型和 StringBuffer 类型的主要性能区别其实在于 String 是不可变的对象, 所以在每次对 String 类型进行改变的时候其实都等同于生成了一个新的 String 对象,而后将指针指向新的 String 对象。因此常常改变内容的字符串最好不要用 String,由于每次生成对象都会对系统性能产生影响,特别当内存中无引用对象多了之后,JVM 的 GC 就会开始工做,那速度是必定会至关慢的。
而若是是使用StringBuffer类则结果就不同了,每次结果都会对 StringBuffer 对象自己进行操做,而不是生成新的对象,再改变对象引用。因此在通常状况下咱们推荐使用 StringBuffer,特别是字符串对象常常改变的状况下。
在某些特别状况下, String 对象的字符串拼接实际上是被 JVM 解释成了 StringBuffer 对象的拼接,因此这些时候 String 对象的速度并不会比 StringBuffer 对象慢,而特别是如下的字符串对象生成中, String 效率是远要比 StringBuffer 快的:
String S1 = "This is only a" + "simple" + "test"; StringBuffer Sb =new StringBuilder(“This is only a").append("simple").append(" test");
你会很惊讶的发现,生成 String S1 对象的速度简直太快了,而这个时候 StringBuffer 竟然速度上根本一点都不占优点。其实这是 JVM 的一个把戏,在 JVM 眼里,这个
String S1 = “This is only a" + “ simple" + “test";
其实就是:String S1 = “This is only a simple test";
因此固然不须要太多的时间了。但你们这里要注意的是,若是你的字符串是来自另外的 String 对象的话,速度就没那么快了,譬如:
String S2 = "This is only a"; String S3 = "simple"; String S4 = "test"; String S1 = S2 +S3 + S4;
这时候 JVM 会规规矩矩的按照原来的方式去作。
在大部分状况下 StringBuffer > String
Java.lang.StringBuffer是线程安全的可变字符序列。一个相似于 String 的字符串缓冲区,但不能修改。虽然在任意时间点上它都包含某种特定的字符序列,但经过某些方法调用能够改变该序列的长度和内容。在程序中可将字符串缓冲区安全地用于多线程。并且在必要时能够对这些方法进行同步,所以任意特定实例上的全部操做就好像是以串行顺序发生的,该顺序与所涉及的每一个线程进行的方法调用顺序一致。
StringBuffer 上的主要操做是 append 和 insert 方法,可重载这些方法,以接受任意类型的数据。每一个方法都能有效地将给定的数据转换成字符串,而后将该字符串的字符追加或插入到字符串缓冲区中。append 方法始终将这些字符添加到缓冲区的末端;而 insert 方法则在指定的点添加字符。
例如,若是 z 引用一个当前内容是“start”的字符串缓冲区对象,则此方法调用 z.append(“le”) 会使字符串缓冲区包含“startle”(累加);而 z.insert(4, “le”) 将更改字符串缓冲区,使之包含“starlet”。
在大部分状况下 StringBuilder > StringBuffer
java.lang.StringBuilder是一个可变的字符序列,是JAVA 5.0新增的。此类提供一个与 StringBuffer 兼容的 API,但不保证同步,因此使用场景是单线程。该类被设计用做 StringBuffer 的一个简易替换,用在字符串缓冲区被单个线程使用的时候(这种状况很广泛)。若是可能,建议优先采用该类,由于在大多数实现中,它比 StringBuffer 要快。二者的使用方法基本相同。
Java序列化是指把Java对象保存为二进制字节码的过程,Java反序列化是指把二进制码从新转换成Java对象的过程。
1.java对象的生命周期要比java虚拟机短,实际应用中但愿虚拟机中止运行以后可以持久化指定的对象,此时能够将对象序列化保存;
2.java对象经过网络传输的时候,由于数据只能以二进制的形式在网络中进行传输,所以当对象经过网络发送出去以前,须要先序列化为二进制数据,在接收端收到二进制数据以后反序列化成二进制对象。
在序列化的时候使用默认的方式来进行序列化,这种序列化方式仅仅对对象的非transient的实例变量进行序列化,而不会序列化对象的transient的实例变量,也不会序列化静态变量,因此咱们对不想持久化的变量能够加上transient关键字。注意使用默认机制,在序列化对象时,不只会序列化当前对象自己,还会对该对象引用的其它对象也进行序列化,一样地,这些其它对象引用的另外对象也将被序列化,以此类推。因此,若是一个对象包含的成员变量是容器类对象,而这些容器所含有的元素也是容器类对象,那么这个序列化的过程就会较复杂,开销也较大。若是须要实现自定义序列化和反序列化,那么须要重写writeObject()方法和readObject()方法。在序列化过程当中,若是被序列化的类中定义了writeObject 和 readObject 方法,将会使用反射的方式调用自定义的 writeObject 和 readObject 方法,进行用户自定义的序列化和反序列化。
Serializable既能够采用默认的序列化和反序列化方式,也可使用用户自定义的序列化和反序列化的方式。
Externalizable序列化,虽然Externalizable接口继承自Serializable接口,可是须要序列化类继承此接口的话,Serializable全部序列化机制所有失效。Externalizable序列化的过程:使用Externalizable序列化时,在进行反序列化的时候,会从新实例化一个对象,而后再将被反序列化的对象的状态所有复制到这个新的实例化对象当中去,所以必须有一个无参构造方法供其调用,而且权限是public。
Java序列化和反序列化本质上是将对象信息生成一串二进制字节码和从二进制字节码解析的过程。序列化算法:
1)当前类的描述
2)当前类属性的描述
3)父类描述
4)父类属性描述
5)父类属性值描述
6)子类属性值描述
类描述是从下到上,类属性描述是从上到下。
面向对象的三个特征:封装、继承、多态
封装:封装是将客观事物抽象成类,每一个类都包含自身的数据以及操做,没必要须要其余的类来完成操做。类内部的实现能够自由的修改;具备清晰的对外接口。良好的封装可以减小耦合;
继承:继承是从已有的类中派生出新的类称为子类,子类继承父类的属性和行为,并可以根据本身的需求扩展新的行为,提供了代码的复用性。
多态:多态容许不一样类的对象对同一消息作出响应。提供继承关系,子类重写父类的方法;父类的引用执行子类的对象;在调用父类的方法是实际上表现的是子类的状态。
从内存角度考虑:
局部变量在栈内存中存在,当for循环语句结束,那么变量会及时被gc(垃圾回收器)及时的释放掉,不浪费空间
从应用场景角度考虑:
若是一个需求明确循环的次数,那么使用for循环(开发中使用for循环的概率大于while循环)