一、Integer是int的包装类,int则是java的一种基本数据类型 二、Integer变量必须实例化后才能使用,而int变量不须要 三、Integer实际是对象的引用,当new一个Integer时,其实是生成一个指针指向此对象;而int则是直接存储数据值 四、Integer的默认值是null,int的默认值是0 延伸: 关于Integer和int的比较 一、因为Integer变量其实是对一个Integer对象的引用,因此两个经过new生成的Integer变量永远是不相等的(由于new生成的是两个对象,其内存地址不一样)。 Integer i = new Integer(100); Integer j = new Integer(100); System.out.print(i == j); //false 二、Integer变量和int变量比较时,只要两个变量的值是向等的,则结果为true(由于包装类Integer和基本数据类型int比较时,java会自动拆包装为int,而后进行比较,实际上就变为两个int变量的比较) Integer i = new Integer(100); int j = 100; System.out.print(i == j); //true 三、非new生成的Integer变量和new Integer()生成的变量比较时,结果为false。(由于非new生成的Integer变量指向的是java常量池中的对象,而new Integer()生成的变量指向堆中新建的对象,二者在内存中的地址不一样) Integer i = new Integer(100); Integer j = 100; System.out.print(i == j); //false 四、对于两个非new生成的Integer对象,进行比较时,若是两个变量的值在区间-128到127之间,则比较结果为true,若是两个变量的值不在此区间,则比较结果为false Integer i = 100; Integer j = 100; System.out.print(i == j); //true Integer i = 128; Integer j = 128; System.out.print(i == j); //false 对于第4条的缘由: java在编译Integer i = 100 ;时,会翻译成为Integer i = Integer.valueOf(100);,而java API中对Integer类型的valueOf的定义以下: public static Integer valueOf(int i){ assert IntegerCache.high >= 127; if (i >= IntegerCache.low && i <= IntegerCache.high){ return IntegerCache.cache[i + (-IntegerCache.low)]; } return new Integer(i); } java对于-128到127之间的数,会进行缓存,Integer i = 127时,会将127进行缓存,下次再写Integer j = 127时,就会直接从缓存中取,就不会new了
在 Java 5 中,为 Integer 的操做引入了一个新的特性,用来节省内存和提升性能。整型对象在内部实现中经过使用相同的对象引用实现了缓存和重用。java
这种 Integer 缓存策略仅在自动装箱(autoboxing)的时候有用,使用构造器建立的 Integer 对象不能被缓存。编程
在建立新的 Integer 对象以前会先在 IntegerCache.cache (是个Integer类型的数组)中查找。有一个专门的 Java 类来负责 Integer 的缓存。数组
这个类是用来实现缓存支持,并支持 -128 到 127 之间的自动装箱过程。最大值 127 能够经过 JVM 的启动参数 -XX:AutoBoxCacheMax=size 修改。 缓存经过一个 for 循环实现。从小到大的建立尽量多的整数并存储在一个名为 cache 的整数数组中。这个缓存会在 Integer 类第一次被使用的时候被初始化出来。之后,就可使用缓存中包含的实例对象,而不是建立一个新的实例(在自动装箱的状况下)。缓存
public static Integer valueOf(int i) { if (i >= IntegerCache.low && i <= IntegerCache.high) return IntegerCache.cache[i + (-IntegerCache.low)]; return new Integer(i); } private static class IntegerCache { static final int low = -128; static final int high; static final Integer cache[]; static { // high value may be configured by property int h = 127; String integerCacheHighPropValue = sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high"); if (integerCacheHighPropValue != null) { try { int i = parseInt(integerCacheHighPropValue); i = Math.max(i, 127); // Maximum array size is Integer.MAX_VALUE h = Math.min(i, Integer.MAX_VALUE - (-low) -1); } catch( NumberFormatException nfe) { // If the property cannot be parsed into an int, ignore it. } } high = h; cache = new Integer[(high - low) + 1]; int j = low; for(int k = 0; k < cache.length; k++) cache[k] = new Integer(j++); // range [-128, 127] must be interned (JLS7 5.1.7) assert IntegerCache.high >= 127; } private IntegerCache() {} }
这种缓存行为不只适用于Integer对象。咱们针对全部整数类型的类都有相似的缓存机制。安全
装箱就是 自动将基本数据类型转换为包装器类型;拆箱就是 自动将包装器类型转换为基本数据类型。数据结构
//拆箱 int yc = 5; //装箱 Integer yc = 5; 3.2 装箱和拆箱是如何实现的 以Interger类为例,下面看一段代码来了解装箱和拆箱的实现 public class Main { public static void main(String[] args) { Integer y = 10; int c = i; } }
而后来编译一下,看下所示:ide
建议避免无心中的装箱、拆箱行为,尤为是在性能敏感的场合,建立 10 万个 Java 对象和 10 万个整数的开销可不是一个数量级的,不论是内存使用仍是处理速度,光是对象头的空间占用就已是数量级的差距了。布局
Java自带的线程安全的基本类型包括: AtomicInteger, AtomicLong, AtomicBoolean, AtomicIntegerArray,AtomicLongArray等性能
200个线程,每一个线程对共享变量 count 进行 50 次 ++ 操做优化
int 做为基本类型,直接存储在内存栈,且对其进行+,-操做以及++,–操做都不是原子操做,都有可能被其余线程抢断,因此不是线程安全。int 用于单线程变量存取,开销小,速度快
int count = 0; private void startThread() { for (int i = 0;i < 200; i++){ new Thread(new Runnable() { @Override public void run() { for (int k = 0; k < 50; k++){ count++; } } }).start(); } // 休眠10秒,以确保线程都已启动 try { Thread.sleep(1000*10); } catch (InterruptedException e) { e.printStackTrace(); }finally { Log.e("打印日志----",count+""); } } //指望输出10000,最后输出的是9818 //注意:打印日志----: 9818
AtomicInteger类中有有一个变量valueOffset,用来描述AtomicInteger类中value的内存位置 。
当须要变量的值改变的时候,先经过get()获得valueOffset位置的值,也即当前value的值.给该值进行增长,并赋给next
compareAndSet()比较以前取到的value的值当前有没有改变,若没有改变的话,就将next的值赋给value,假若和以前的值相比的话发生变化的话,则从新一次循环,直到存取成功,经过这样的方式可以保证该变量是线程安全的
value使用了volatile关键字,使得多个线程能够共享变量,使用volatile将使得VM优化失去做用,在线程数特别大时,效率会较低。
private static AtomicInteger atomicInteger = new AtomicInteger(1); static Integer count1 = Integer.valueOf(0); private void startThread1() { for (int i = 0;i < 200; i++){ new Thread(new Runnable() { @Override public void run() { for (int k = 0; k < 50; k++){ // getAndIncrement: 先得到值,再自增1,返回值为自增前的值 count1 = atomicInteger.getAndIncrement(); } } }).start(); } // 休眠10秒,以确保线程都已启动 try { Thread.sleep(1000*10); } catch (InterruptedException e) { e.printStackTrace(); }finally { Log.e("打印日志----",count1+""); } } //指望输出10000,最后输出的是10000 //注意:打印日志----: 10000 //AtomicInteger使用了volatile关键字进行修饰,使得该类能够知足线程安全。 private volatile int value; /** * Creates a new AtomicInteger with the given initial value. * * @param initialValue the initial value */ public AtomicInteger(int initialValue) { value = initialValue; }
Java 的泛型某种程度上能够算做伪泛型,它彻底是一种编译期的技巧,Java 编译期会自动将类型转换为对应的特定类型,这就决定了使用泛型,必须保证相应类型能够转换为Object。
Java 的对象都是引用类型,若是是一个原始数据类型数组,它在内存里是一段连续的内存,而对象数组则否则,数据存储的是引用,对象每每是分散地存储在堆的不一样位置。这种设计虽然带来了极大灵活性,可是也致使了数据操做的低效,尤为是没法充分利用现代 CPU 缓存机制。
Java 为对象内建了各类多态、线程安全等方面的支持,但这不是全部场合的需求,尤为是数据处理重要性日益提升,更加高密度的值类型是很是现实的需求。
对象在内存中存储的布局能够分为3块区域:对象头(Header)、实例数据(Instance Data)和对齐填充(Padding)。
HotSpot虚拟机的对象头包括两部分信息,第一部分用于存储对象自身的运行时数据,如哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等,这部分数据的长度在32位和64位的虚拟机(未开启压缩指针)中分别为32bit和64bit,官方称它为"Mark Word"。
对象头的另一部分是类型指针,即对象指向它的类元数据的指针,虚拟机经过这个指针来肯定这个对象是哪一个类的实例。并非全部的虚拟机实现都必须在对象数据上保留类型指针,换句话说,查找对象的元数据信息并不必定要通过对象自己,这点将在2.3.3节讨论。另外,若是对象是一个Java数组,那在对象头中还必须有一块用于记录数组长度的数据,由于虚拟机能够经过普通Java对象的元数据信息肯定Java对象的大小,可是从数组的元数据中却没法肯定数组的大小。
获取一个JAVA对象的大小,能够将一个对象进行序列化为二进制的Byte,即可以查看大小
//获取一个JAVA对象的大小,能够将一个对象进行序列化为二进制的Byte,即可以查看大小 Integer value = 10; ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos ; try { oos = new ObjectOutputStream(bos); oos.writeObject(value); oos.close(); } catch (IOException e) { e.printStackTrace(); } // 读出当前对象的二进制流信息 Log.e("打印日志----",bos.size()+""); //打印日志----: 81