java: 封装缓存池(int与Integer)、常量池(拘留池)、static变量 static代码块 static方法、 final变量、final 方法、final类 整理


java 内存模型:

JVM主要管理两种类型内存:堆和非堆,堆内存(Heap Memory)是在 Java 虚拟机启动时创建,

非堆内存(Non-heap Memory)是在

JVM堆之外的内存

 对象的具体(属性 方法);

 非堆区

栈:方法执行时 在此操作,局部变量,堆中对象的地址;

静态数据区

static修饰的数据区(假设 技术不能假设!!):如果经static修饰的数据 在static区,

经static修饰过的变量 比如成员变量

创建一个对象时,这个成员变量不会在堆里再有这个变量了,这就是static 变量的

作用;它是多对象共享的;

代码:

/*
 * 记住:静态数据区不属于堆,当然更不属于对象
Static : static修饰的,在编译期就有了,其早于对象的建立;属于类;
在静态方法区内,不能调用其他方法(因为其他方法属于对象),也不能用this,super,但可以使用静态变量
反之:在其他方法区内可以调用静态方法
 */
public class Sta {

	private static String name = "duck";
	public int age = 21;
	
	
	static int hh = 99;
	
	public void say() {
		System.out.println("我 是一只可爱的鸭子");
	}
	public  String getName() {
		return name;
	}
	
	public static void action() {
		name = "my name is not duck";
		System.out.println("我是静态方法");
	}
	
}
package com.stati;

/*
 * 
 * Out.In in = new Out().new In()可以用来生成内部类的对象,这种方法存在两个小知识点需要注意

  1.开头的Out是为了标明需要生成的内部类对象在哪个外部类当中

  2.必须先有外部类的对象才能生成内部类的对象,因为内部类的作用就是为了访问外部类中的成员变量
 */

public class Test {
	
	//private int h = 7;
	public static int a;
	int b;
	public static void main(String[] args) {
		 a = 3;
//		 b = 4;  //这是错误的,其属于对象成员变量,static修饰的在方法块,早于对象的建立;
		// TODO Auto-generated method stub
		
		/*
		 * Java关键字this只能用于方法方法体内。当一个对象创建后,Java虚拟机(JVM)就会给这个对象分配一个引用自身的指针,这个指针的名字就是this。
		 * 因此,this只能在类中的非静态方法中使用,静态方法和静态的代码块中绝对不能出现this,
		 * 这在“Java关键字static、final使用总结”一文中给出了明确解释。并且this只和特定的对象关联,而不和类关联,同一个类的不同对象有不同的this
		 * Static 修饰方法:不属于对象,属于类,用类名直接调用
		 */
		Sta.action();
		
		Sta sta = new Sta();
		System.out.println("我的名字是:" + sta.getName());
		
		Sta s1 = new Sta();
		
		s1.hh = 88;
		
		Sta s2 = new Sta();
		System.out.println(s2.hh);
		
	}

}
测试结果:
我是静态方法
我的名字是:my name is not duck
88
 
 static修饰方法: 
 

/*静态方法可以直接通过类名调用,任何的实例也都可以调用,
		因此静态方法中不能用this和super关键字,不能直接访问所属类的实例变量和实例方法(就是不带static的成员变量和成员成员方法),
		只能访问所属类的静态成员变量和成员方法。*/
		
		/* 原因 :jvm 会在 没有执行之前 首先对 static的 变量  代码块 函数 进行处理 所以 可以如此 
		 * //静态的方法 变量 代码块 在静态去 在所有的对象创建之前 就存在  因此   用类名引用*/
		
		public static void StaticWay(){
			
			//System.out.println(age); //错误
			//action();  //错误
			
			
			System.out.println(a); // a是静态成员变量 所以在此可以
			
			System.out.println("我是静态方法");
			
			StaticWay2();   // static 是静态方法所以 在此可以
			
			Animal.StaticWay2();  //通过类名 也可以
			
		}
static代码块:

/*static代码块也叫静态代码块,是在类中独立于类成员的static语句块,可以有多个,位置可以随便放,它不在任何的方法体内,
		 * JVM加载类时会执行这些静态的代码块,如果static代码块有多个,JVM将按照它们在类中出现的先后顺序依次执行它们,每个代码块只会被执行一次*/
		
		static {
			
			int ui = 88;  //不是成员变量  
			System.out.println("我是static区1 ");
			
			System.out.println(ui);
			
		}
static与final:

static final作用 : 在一块公共的内存 且只读;static final用来修饰成员变量和成员方法,

可简单理解为“全局常量”!对于变量,表示一旦给值就不可修改,并且通过类名可以访问  对于方法,

表示不可覆盖,并且可以通过类名直接访问。

常量区(只读): final修饰的;

final修饰成员变量:final int W_FINAL = 9;//常亮 大写下划线

修饰作用:只读,在常量区,在编译期解决;

final修饰方法: 对于方法,表示不可覆盖,并且可以通过类名直接访问;

final修饰类:表示该类不可以被继承;


 java缓存池(相对于封装int与Integer):


装箱:把基本类型用它们相应的引用类型包装起来,使其具有对象的性质。int包装成Integer、

float包装成Float;

拆箱:和装箱相反,将引用类型的对象简化成值类型的数据;

Integer a = 100; 这是自动装箱  (编译器调用的是static Integer valueOf(int i))即:

等同Integer a = Integer.valueOf(100)
int     b = new Integer(100); 这是自动拆箱


jvm会编译期就在堆里创建缓存池数值【-128,127】;

Integer i = 12;  //会在堆里找12这个数值,找到就把缓存池中该值得地址返回给i;

Integer j= 12; //i的地址与j的地址相同;

//超出范围System.out.println(u==j); false

Integer u = 128; 

Integer j = 128;


Integer与Integer间的比较,从jdk1.5开始,有自动装箱这么一个机制,在byte-128到127范围内

(ps整型的八位二进制的表示的范围为-128到127),如果存在了一个值,再创建相同值的时候就不会重

新创建,而是引用原来那个,但是超过byte范围还是会新建的对象:代码如下

 /*   
  * 返回一个表示指定的 int 值的 Integer 实例。如果不需要新的 Integer 实例,则   
  * 通常应优先使用该方法,而不是构造方法 Integer(int),因为该方法有可能通过   
  * 缓存经常请求的值而显著提高空间和时间性能。    
  * @param  i an <code>int</code> value.   
  * @return a <tt>Integer</tt> instance representing <tt>i</tt>.   
  * @since  1.5   
  */    
public static Integer valueOf(int i) {     
      final int offset = 128;     
      if (i >= -128 && i <= 127) { // must cache      
        return IntegerCache.cache[i + offset];     
     }     
     return new Integer(i);     
可以看到对于范围在-128到127的整数,valueOf方法做了特殊处理。 
采用IntegerCache.cache[i + offset]这个方法。 
从名字,我们可以猜出这是某种缓存机制。 
进一步跟踪IntegerCache这个类,此类代码如下:
/*   
  * IntegerCache内部类   
  * 其中cache[]数组用于存放从-128到127一共256个整数   
  */    
private static class IntegerCache {     
    private IntegerCache(){}     
    
    static final Integer cache[] = new Integer[-(-128) + 127 + 1];     
    
    static {     
        for(int i = 0; i < cache.length; i++)     
        cache[i] = new Integer(i - 128);     
    }     
}    
这就是valueOf方法真正的优化方法,当-128=<i<=127的时候,返回的是IntegerCache中的数组的值;当 i>127 或 i<-128 时,返回的是Integer类对象

例子:

package com.third_day;


public class Wrapper {
	
	
	public static void main(String[] args) {
		
		String s1 = "123";
		
		int i1 = Integer.parseInt(s1);
		
		System.out.println(i1);
		
		
		Integer i = 12; 
		
		int ii = 12;
		
		System.out.println(i==ii);
		
		
		 Integer a = new Integer(100); //在堆中创建了一个对象
		 
	     Integer b = 100; //等同Integer。valueOf(100); 即从return IntegerCache.cache[i + offset]; 如果zai
	    /* 堆里找到100 就把 堆里100值得地址 给栈里的b*/
	     
	     int bb = 100;
	     
	     System.out.println("m11 result " + (a == b)); //false
	     
	     System.out.println("m11 result " + (bb == b));
	     
	     
	     Integer c = new Integer(101);
	     Integer d = new Integer(101);
	     System.out.println("m21 result " + (c == d));
	     
	     
	     Integer k = new Integer(101);
	     
	     Integer e = Integer.valueOf(101); //相当于Integer f = 101;
	     
	     Integer f = 101;
	     
	     
	     System.out.println("m41 result " + (e == f));
	     
	     System.out.println("m41 result " + (k == f));
	     
	     System.out.println("m41 result " + (k == e));
	     
	     
	     
	     
	     /*注意::基本数据类型和对象比较的时候,对象会自动拆箱为基本数据类型再比较,比较的就是里面的值而不是地址*/
	     
	     Integer i11 = new Integer(120);
	     
	     int i2 = 120;
	     
	     Integer i22 = 120;
	     // newInteger与int11: Integer i11 = new Integer(120);会自动拆箱(即:堆里的内容 拿到栈里面)变成 i11=120; 就比较值了 而不是地址了
	     System.out.println("---newInteger与int11---"+(i11==i2));  
	     
	     //比较的是地址 一个指向缓存池(池范围:-128--127) 也是在堆里的 跟 常量池不同 ,一个指向 新建的堆里的内容
	     System.out.println("---newInteger与int---"+(i11==i22));
	     
	     //比较内容 i22 会转换 int i22 = 120; 
	     System.out.println("---newInteger与int---"+(i2==i22));
	}

}


java常量池(拘留池):

String对象的初始化

由于String对象特别常用,所以在对String对象进行初始化时,Java提供了一种简化的特殊语法,格式如下:

                            String s = “abc”;

                            s = “Java语言”;

                   其实按照面向对象的标准语法,其格式应该为:

                            String s = new String(“abc”);

                            s = new String(“Java语言”);

只是按照面向对象的标准语法,在内存使用上存在比较大的浪费。例如String s = new String(“abc”);实际上

创建了两个String对象,一个是”abc”对象,存储在常量空间中,一个是使用new关键字为对

s申请的空间其它的构造方法的参数,

可以参看String类的API文档。


 String str1 = "abc"; 分析---》

公共语言运行库通过维护一个表来存放字符串,该表称为拘留池,它包含程序中以编程方式声明或创建

的每个唯一的字符串的一个引用。

因此,具有特定值的字符串的实例在系统中只有一个。


例如,如果将同一字符串分配给几个变量,

运行库就会从拘留池中检索对该字符串的相同引用,并将它分配给各个变量。

个初始时为空的字符串池,它由类 String 私有地维护。 当调用 intern 方法时,

如果池已经包含一个等于此 String 对象的字符串(该对象由 equals(Object) 方法确定),

则返回池中的字符串。否则,将此 String 对象添加到池中,并且返回此 String 对象的引用

(1) String str1 = "abc"; 
    System.out.println(str1 == "abc");

步骤: 
1) 栈中开辟一块空间存放引用str1;
2) String池中开辟一块空间,存放String常量"abc"; 
3) 引用str1指向池中String常量"abc";
4) str1所指代的地址即常量"abc"所在地址,输出为true;

(2) String str2 = new String("abc"); 
    System.out.println(str2 == "abc");

步骤: 
1) 栈中开辟一块空间存放引用str2; 
2) 堆中开辟一块空间存放一个新建的String对象"abc"; 
3) 引用str2指向堆中的新建的String对象"abc";
4) str2所指代的对象地址为堆中地址,而常量"abc"地址在池中,输出为false;



私自感觉不错的博客:@http://blog.csdn.net/u010644448/article/details/51980370



小结:注意java编译期(生成.class) 与执行期(jvm解释class 字节码)

  • 5
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值