想入2k的dell显示器器,看了dell和lg的哪个好一点

Java(620)
一.相关概念
什么是常量
用final修饰的成员变量表示常量,值一旦给定就无法改变!
final修饰的变量有三种:静态变量、实例变量和局部变量,分别表示三种类型的常量。
Class文件中的常量池
在Class文件结构中,最头的4个字节用于存储魔数&Number,用于确定一个文件是否能被JVM接受,再接着4个字节用于存储版本号,前2个字节存储次版本号,后2个存储主版本号,再接着是用于存放常量的常量池,由于常量的数量是不固定的,所以常量池的入口放置一个U2类型的数据(constant_pool_count)存储常量池容量计数值。
常量池主要用于存放两大类常量:字面量(Literal)和符号引用量(Symbolic References),字面量相当于Java语言层面常量的概念,如文本字符串,声明为final的常量值等,符号引用则属于编译原理方面的概念,包括了如下三种类型的常量:
类和接口的全限定名字段名称和描述符方法名称和描述符
方法区中的运行时常量池
运行时常量池是方法区的一部分。
CLass文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池,用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后进入方法区的运行时常量池中存放。
运行时常量池相对于CLass文件常量池的另外一个重要特征是具备动态性,Java语言并不要求常量一定只有编译期才能产生,也就是并非预置入CLass文件中常量池的内容才能进入方法区运行时常量池,运行期间也可能将新的常量放入池中,这种特性被开发人员利用比较多的就是String类的intern()方法。
常量池的好处
常量池是为了避免频繁的创建和销毁对象而影响系统性能,其实现了对象的共享。
例如字符串常量池,在编译阶段就把所有的字符串文字放到一个常量池中。
(1)节省内存空间:常量池中所有相同的字符串常量被合并,只占用一个空间。
(2)节省运行时间:比较字符串时,==比equals()快。对于两个引用变量,只用==判断引用是否相等,也就可以判断实际值是否相等。
双等号==的含义
基本数据类型之间应用双等号,比较的是他们的数值。
复合数据类型(类)之间应用双等号,比较的是他们在内存中的存放地址。
二.8种基本类型的包装类和常量池
java中基本类型的包装类的大部分都实现了常量池技术,即Byte,Short,Integer,Long,Character,Boolean;
Integer i1 = 40;
Integer i2 = 40;
System.out.println(i1==i2);
这5种包装类默认创建了数值[-128,127]的相应类型的缓存数据,但是超出此范围仍然会去创建新的对象。
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);
Integer i1 = 400;
Integer i2 = 400;
System.out.println(i1==i2);
两种类型的包装类Float,Double并没有实现常量池技术。
Double i1=1.2;
Double i2=1.2;
System.out.println(i1==i2);
应用常量池的场景
(1)Integer i1=40;Java在编译的时候会直接将代码封装成Integer
i1=Integer.valueOf(40);,从而使用常量池中的对象。
(2)Integer i1 = new Integer(40);这种情况下会创建新的对象。
Integer i1 = 40;
Integer i2 = new Integer(40);
System.out.println(i1==i2);
Integer比较更丰富的一个例子
Integer i1 = 40;
Integer i2 = 40;
Integer i3 = 0;
Integer i4 = new Integer(40);
Integer i5 = new Integer(40);
Integer i6 = new Integer(0);
System.out.println(&i1=i2
& + (i1 == i2));
System.out.println(&i1=i2+i3
& + (i1 == i2 + i3));
System.out.println(&i1=i4
& + (i1 == i4));
System.out.println(&i4=i5
& + (i4 == i5));
System.out.println(&i4=i5+i6
& + (i4 == i5 + i6));
System.out.println(&40=i5+i6
& + (40 == i5 + i6));
i1=i2+i3
i4=i5+i6
40=i5+i6
解释:语句i4 == i5 + i6,因为+这个操作符不适用于Integer对象,首先i5和i6进行自动拆箱操作,进行数值相加,即i4
== 40。然后Integer对象无法与数值进行直接比较,所以i4自动拆箱转为int值40,最终这条语句转为40 == 40进行数值比较。
三.String类和常量池
String对象创建方式
String str1 = &abcd&;
String str2 = new String(&abcd&);
System.out.println(str1==str2);
这两种不同的创建方法是有差别的,第一种方式是在常量池中拿对象,第二种方式是直接在堆内存空间创建一个新的对象。
只要使用new方法,便需要创建新的对象。
连接表达式 +
(1)只有使用引号包含文本的方式创建的String对象之间使用“+”连接产生的新对象才会被加入字符串池中。
(2)对于所有包含new方式新建对象(包括null)的“+”连接表达式,它所产生的新对象都不会被加入字符串池中。
String str1 = &str&;
String str2 = &ing&;
String str3 = &str& + &ing&;
String str4 = str1 + str2;
System.out.println(str3 == str4);
String str5 = &string&;
System.out.println(str3 == str5);
public static final String A = &ab&;
public static final String B = &cd&;
public static void main(String[] args) {
String s = A + B;
String t = &abcd&;
if (s == t) {
System.out.println(&s等于t,它们是同一个对象&);
System.out.println(&s不等于t,它们不是同一个对象&);
s等于t,它们是同一个对象
A和B都是常量,值是固定的,因此s的值也是固定的,它在类被编译时就已经确定了。也就是说:String s=A+B; 等同于:String s=”ab”+”cd”;
public static final String A;
public static final String B;
public static void main(String[] args) {
String s = A + B;
String t = &abcd&;
if (s == t) {
System.out.println(&s等于t,它们是同一个对象&);
System.out.println(&s不等于t,它们不是同一个对象&);
s不等于t,它们不是同一个对象
A和B虽然被定义为常量,但是它们都没有马上被赋值。在运算出s的值之前,他们何时被赋值,以及被赋予什么样的值,都是个变数。因此A和B在被赋值之前,性质类似于一个变量。那么s就不能在编译期被确定,而只能在运行时被创建了。
String s1 = new String(&xyz&);&创建了几个对象?
考虑类加载阶段和实际执行时。
(1)类加载对一个类只会进行一次。”xyz”在类加载时就已经创建并驻留了(如果该类被加载之前已经有”xyz”字符串被驻留过则不需要重复创建用于驻留的”xyz”实例)。驻留的字符串是放在全局共享的字符串常量池中的。
(2)在这段代码后续被运行的时候,”xyz”字面量对应的String实例已经固定了,不会再被重复创建。所以这段代码将常量池中的对象复制一份放到heap中,并且把heap中的这个对象的引用交给s1 持有。
这条语句创建了2个对象。
java.lang.String.intern()
运行时常量池相对于CLass文件常量池的另外一个重要特征是具备动态性,Java语言并不要求常量一定只有编译期才能产生,也就是并非预置入CLass文件中常量池的内容才能进入方法区运行时常量池,运行期间也可能将新的常量放入池中,这种特性被开发人员利用比较多的就是String类的intern()方法。
String的intern()方法会查找在常量池中是否存在一份equal相等的字符串,如果有则返回该字符串的引用,如果没有则添加自己的字符串进入常量池。
public static void main(String[] args) {
String s1 = new String(&计算机&);
String s2 = s1.intern();
String s3 = &计算机&;
System.out.println(&s1 == s2? & + (s1 == s2));
System.out.println(&s3 == s2? & + (s3 == s2));
s1 == s2? false
s3 == s2? true
字符串比较更丰富的一个例子
public class Test {
public static void main(String[] args) {
String hello = &Hello&, lo = &lo&;
System.out.println((hello == &Hello&) + & &);
System.out.println((Other.hello == hello) + & &);
System.out.println((other.Other.hello == hello) + & &);
System.out.println((hello == (&Hel&+&lo&)) + & &);
System.out.println((hello == (&Hel&+lo)) + & &);
System.out.println(hello == (&Hel&+lo).intern());
class Other { static String hello = &Hello&; }
public class Other { public static String hello = &Hello&; }
true true true true false true
在同包同类下,引用自同一String对象.
在同包不同类下,引用自同一String对象.
在不同包不同类下,依然引用自同一String对象.
在编译成.class时能够识别为同一字符串的,自动优化成常量,引用自同一String对象.
在运行时创建的字符串具有独立的内存地址,所以不引用自同一String对象.
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:464564次
积分:11239
积分:11239
排名:第1111名
原创:627篇
转载:19篇
评论:344条
(93)(192)(284)(57)(20)常量池小结 - Eason_S - 博客园
Java中的常量池,实际上分为两种形态:静态常量池和运行时常量池。 & &
所谓静态常量池,即*.class文件中的常量池,class文件中的常量池不仅仅包含字符串(数字)字面量,还包含类、方法的信息,占用class文件绝大部分空间。&
而运行时常量池,则是jvm虚拟机在完成类装载操作后,将class文件中的常量池载入到内存中,并保存在方法区中,我们常说的常量池,就是指方法区中的运行时常量池。
以下面的例子来讲解常量池:
1 String s1 = "Hello";
2 String s2 = "Hello";
3 String s3 = "Hel" + "lo";
4 String s4 = "Hel" + new String("lo");
5 String s5 = new String("Hello");
6 String s6 = s5.intern();
7 String s7 = "H";
8 String s8 = "ello";
9 String s9 = s7 + s8;
11 System.out.println(s1 == s2);
12 System.out.println(s1 == s3);
13 System.out.println(s1 == s4);
14 System.out.println(s1 == s9);
15 System.out.println(s4 == s5);
16 System.out.println(s1 == s6);
首先说明一点,在java 中,直接使用==操作符,比较的是两个字符串的引用地址,并不是比较内容,比较内容请用String.equals()。
  s1 == s2这个非常好理解,s1、s2在赋值时,均使用的字符串字面量,说白话点,就是直接把字符串写死,在编译期间,这种字面量会直接放入class文件的常量池中,从而实现复用,载入运行时常量池后,s1、s2指向的是同一个内存地址,所以相等。
  s1 == s3这个地方有个坑,s3虽然是动态拼接出来的字符串,但是所有参与拼接的部分都是已知的字面量,在编译期间,这种拼接会被优化,编译器直接帮你拼好,因此String s3 = "Hel" + "lo";在class文件中被优化成String s3 = "Hello";,所以s1 == s3成立。&
  s1 == s4当然不相等,s4虽然也是拼接出来的,但new String("lo")这部分不是已知字面量,是一个不可预料的部分,编译器不会优化,必须等到运行时才可以确定结果,结合字符串不变定理,鬼知道s4被分配到哪去了,所以地址肯定不同。配上一张简图理清思路:
  s1 == s9也不相等,道理差不多,虽然s7、s8在赋值的时候使用的字符串字面量,但是拼接成s9的时候,s7、s8作为两个变量,都是不可预料的,编译器毕竟是编译器,不可能当解释器用,所以不做优化,等到运行时,s7、s8拼接成的新字符串,在堆中地址不确定,不可能与方法区常量池中的s1地址相同。
  s4 == s5已经不用解释了,绝对不相等,二者都在堆中,但地址不同。
  s1 == s6这两个相等完全归功于intern方法,s5在堆中,内容为Hello ,intern方法会尝试将Hello字符串添加到常量池中,并返回其在常量池中的地址,因为常量池中已经有了Hello字符串,所以intern方法直接返回地址;而s1在编译期就已经指向常量池了,因此s1和s6指向同一地址,相等。
至此,我们可以得出三个非常重要的结论:&
& &&1、必须要关注编译期的行为,才能更好的理解常量池。
& & 2、运行时常量池中的常量,基本来源于各个class文件中的常量池。
& & 3、程序运行时,除非手动向常量池中添加常量(比如调用intern方法),否则jvm不会自动添加常量到常量池。
& & 以上所讲仅涉及字符串常量池,实际上还有整型常量池、浮点型常量池等等,但都大同小异,只不过数值类型的常量池不可以手动添加常量,程序启动时常量池中的常量就已经确定了,比如整型常量池中的常量范围:-128~127,只有这个范围的数字可以用到常量池。
   &《深入理解java虚拟机&&&jvm高级特性与最佳实践》什么是字符串常量池? - ImportNew
| 标签: ,
在理解字符串常量前,我们先熟悉一下如何创建一个字符串,在Java中有两种方法可以创建一个字符串对象:
使用new运算符。例如:
String str = new String(&Hello&);
使用字符串常量或者常量表达式。例如:
String str=&Hello&; //(字符串常量) 或者
String str=&Hel& + &lo&; //(字符串常量表达式).
这些字符串的创建方式之间有什么区别呢?在Java中,equals方法被认为是对象的值进行深层次的比较,而操作符==是进行的浅层次的比较。equals方法比较两个对象的内容而不是引用。==两侧是引用类型(例如对象)时,如果引用是相同的-即指向同一个对象-则执行结果为真。如果是值类型(例如原生类型),如果值相同,则执行结果为真。equals方法在两个对象具有相同内容时返回真-但是,java.lang.Object类中的equals方法返回真-如果类没有覆盖默认的equals方法,如果两个引用指向同一个对象。
让我们通过下面的例子来看看这两种字符串的创建方式之间有什么区别吧。
public class DemoStringCreation {
public static void main(String args[]) {
String str1 = &Hello&;
String str2 = &Hello&;
System.out.println(&str1 and str2 are created by using string literal.&);
System.out.println(&
str1 == str2 is & + (str1 == str2));
System.out.println(&
str1.equals(str2) is & + str1.equals(str2));
String str3 = new String(&Hello&);
String str4 = new String(&Hello&);
System.out.println(&str3 and str4 are created by using new operator.&);
System.out.println(&
str3 == str4 is & + (str3 == str4));
System.out.println(&
str3.equals(str4) is & + str3.equals(str4));
String str5 = &Hel& + &lo&;
String str6 = &He& + &llo&;
System.out.println(&str5 and str6 are created by using string constant expression.&);
System.out.println(&
str5 == str6 is & + (str5 == str6));
System.out.println(&
str5.equals(str6) is & + str5.equals(str6));
String s = &lo&;
String str7 = &Hel& +
String str8 = &He& + &llo&;
System.out.println(&str7 is computed at runtime.&);
System.out.println(&str8 is created by using string constant expression.&);
System.out.println(&
str7 == str8 is & + (str7 == str8));
System.out.println(&
str7.equals(str8) is & + str7.equals(str8));
输出结果为:
str1 and str2 are created by using string literal.
str1 == str2 is true
str1.equals(str2) is true
str3 and str4 are created by using new operator.
str3 == str4 is false
str3.equals(str4) is true
str5 and str6 are created by using string constant expression.
str5 == str6 is true
str5.equals(str6) is true
str7 is computed at runtime.
str8 is created by using string constant expression.
str7 == str8 is false
str7.equals(str8) is true
使用相同的字符序列而不是使用new关键字创建的两个字符串会创建指向Java字符串常量池中的同一个字符串的指针。字符串常量池是Java节约资源的一种方式。
字符串常量池
字符串的分配,和其他的对象分配一样,耗费高昂的时间与空间代价。JVM为了提高性能和减少内存开销,在实例化字符串常量的时候进行了一些优化。为了减少在JVM中创建的字符串的数量,字符串类维护了一个字符串池,每当代码创建字符串常量时,JVM会首先检查字符串常量池。如果字符串已经存在池中,就返回池中的实例引用。如果字符串不在池中,就会实例化一个字符串并放到池中。Java能够进行这样的优化是因为字符串是不可变的,可以不用担心数据冲突进行共享。例如:
public class Program
public static void main(String[] args)
String str1 = &Hello&;
String str2 = &Hello&;
System.out.print(str1 == str2);
其结果是:
不幸的是,当使用:
String a=new String(&Hello&);
一个字符串对象在字符串常量池外创建,即使池里存在相同的字符串。考虑到这些,要避免new一个字符串除非你明确的知道需要这么做!例如:
public class Program
public static void main(String[] args)
String str1 = &Hello&;
String str2 = new String(&Hello&);
System.out.print(str1 == str2 + & &);
System.out.print(str1.equals(str2));
false true
JVM中有一个常量池,任何字符串至多维护一个对象。字符串常量总是指向字符串池中的一个对象。通过new操作符创建的字符串对象不指向字符串池中的任何对象,但是可以通过使用字符串的intern()方法来指向其中的某一个。java.lang.String.intern()返回一个保留池字符串,就是一个在全局字符串池中有了一个入口。如果以前没有在全局字符串池中,那么它就会被添加到里面。例如:
public class Program
public static void main(String[] args)
// Create three strings in three different ways.
String s1 = &Hello&;
String s2 = new StringBuffer(&He&).append(&llo&).toString();
String s3 = s2.intern();
// Determine which strings are equivalent using the ==
// operator
System.out.println(&s1 == s2? & + (s1 == s2));
System.out.println(&s1 == s3? & + (s1 == s3));
s1 == s2? false
s1 == s3? true
为了优化空间,运行时实例创建的全局字符串常量池中有一个表,总是为池中每个唯一的字符串对象维护一个引用。这就意味着它们一直引用着字符串常量池中的对象,所以,在常量池中的这些字符串不会被垃圾收集器回收。
Java语言规范第三版中的字符串常量
每一个字符串常量都是指向一个字符串类实例的引用。字符串对象有一个固定值。字符串常量,或者一般的说,常量表达式中的字符串都被使用方法 String.intern进行保留来共享唯一的实例。
package testP
class Test {
public static void main(String[] args) {
String hello = &Hello&, lo = &lo&;
System.out.print((hello == &Hello&) + & &);
System.out.print((Other.hello == hello) + & &);
System.out.print((other.Other.hello == hello) + & &);
System.out.print((hello == (&Hel&+&lo&)) + & &);
System.out.print((hello == (&Hel&+lo)) + & &);
System.out.println(hello == (&Hel&+lo).intern());
class Other { static String hello = &Hello&; }
编译单元:
public class Other { static String hello = &Hello&; }
产生输出:
true true true true false true
这个例子说明了六点:
同一个包下同一个类中的字符串常量的引用指向同一个字符串对象;
同一个包下不同的类中的字符串常量的引用指向同一个字符串对象;
不同的包下不同的类中的字符串常量的引用仍然指向同一个字符串对象;
由常量表达式计算出的字符串在编译时进行计算,然后被当作常量;
在运行时通过连接计算出的字符串是新创建的,因此是不同的;
通过计算生成的字符串显示调用intern方法后产生的结果与原来存在的同样内容的字符串常量是一样的。
原文链接:
- 译文链接: [ 转载请保留原文出处、译者和译文链接。]
关于作者:
2.2)通过设置Bean定义的“primary”属性为false来把指定自动装配时候选者中首选Bea...
关于ImportNew
ImportNew 专注于 Java 技术分享。于日 11:11正式上线。是的,这是一个很特别的时刻 :)
ImportNew 由两个 Java 关键字 import 和 new 组成,意指:Java 开发者学习新知识的网站。 import 可认为是学习和吸收, new 则可认为是新知识、新技术圈子和新朋友……
新浪微博:
推荐微信号
反馈建议:@
广告与商务合作QQ:
– 好的话题、有启发的回复、值得信赖的圈子
– 写了文章?看干货?去头条!
– 为IT单身男女服务的征婚传播平台
– 优秀的工具资源导航
– 活跃 & 专业的翻译小组
– 国内外的精选博客文章
– UI,网页,交互和用户体验
– JavaScript, HTML5, CSS
– 专注Android技术分享
– 专注iOS技术分享
– 专注Java技术分享
– 专注Python技术分享
& 2017 ImportNewjava从一个方法的调用看class文件、方法区、常量池等问题_Java_第七城市
java从一个方法的调用看class文件、方法区、常量池等问题
先随便写一个非常简单的类Testpublic class Test{
public void testMethod(){
i=i+1; }}然后被下面这个main方法生成并调用,期间我们通过期间发生的内容,来简单了解一下JVM的内部机制。public static void main(String[] args) {
Test test =
new Test();
test.testMethod(); }首先是编译:Test类被编译成一个叫做Test.class的类文件,它的内容以字节码的形式存在。即Ox0F这样的形式存在。这个类文件中依次包含了魔数、常量池、类索引、父类索引、接口索引集合、字段表集合、方法表集合、属性表集合。这里我们来回答几个和方法的问题:1、对一个方法进行调用,入口地址在哪:常量池中的方法常量(包括继承来的方法),在编译期间记录的是符号引用(里面的nameAndType唯一确定了这个方法对应类中的哪一个,特征为参数和方法名,不指向代码),这个常量的符号引用确定了调用哪个方法,已经解决了方法重载的问题,关于如何实现覆盖(override)是运行时实现的。2、一个方法的属性、参数类型、返回值类型、名称等在哪:方法表集合中记录了所有属于这个类的方法(不包括继承来的),常量池中的方法常量中也有一个NameAndType的引用。3、一个方法的Code在哪:属性表集合中有一个叫做Code属性的字段,它的内容是Code表集合,里面有属于这个类的各个方法Code(不包括继承来的方法)。所以一个方法在一个类中的存在,被三个地方记录,一个是入口,一个是属性,一个是code。在完成编译之后,几样东西已经确定:即main方法的code中,调用方法的指令指向了Test类的常量池中的testMethod方法,已经确定了调用的符号引用。然后是类加载期间:加载过程被分为了5个阶段,加载、校验、准备、解析、初始化。其中,加载通过classLoader把类加载进了内存中的方法区。方法区包括了类信息和运行时常量区两个部分,其中class的常量池被加载入运行时常量区,其他信息加载入类信息处。到这里,我们跟踪一下:1、调用入口在运行时常量池,内容还是符号引用,。2、方法描述在方法区的类信息区。3、code在方法区的属性表集合的code属性的code属性表中。此时,与上一步没有本质的变化。然后是校验、准备、解析。每一步的功能可以查阅其他资料。这里解析这一步比较关键,它将常量池中的符号引用替换为了真实的内存引用。但是注意了,这里并没有把所有的方法的符号引用都替换掉。这里被替换掉的符号引用有:静态方法、私有方法、实例构造器方法、父类方法四类。他们有个共同特点:唯一确定,没有override。所以这里的testMethod没有被解析,还是一个符号引用。而如果有构造方法,那么在这里已经被解析了。这样做实现了重载。因为方法到这一步的时候,并不知道他的实际调用对象是谁,只知道他的静态类型是Test,可能是Test,也可能是test的子类,如果是test的子类,事实上可能指向子类重载的方法,也可能还是指向父类的方法(子类没有重载)。这就涉及到运行是的动态分派了。最后初始化,这时没有发生太大的根方法有关的变化。针对testMethod,继续跟踪一下,发现没有变化。但如果假设他是静态方法,那么会是这样:1、入口在运行是常量池,内容是直接引用,指向code所在的内存(这里可能不准确,实际可能还要带上方法信息)。2、其他内容位置不变。最后是运行。当运行到方法被调用的时候,这个时候就需要获得这个类调用对象的信息了:分析这里的test,得到他的类型为Test。同时根据调用信息,得到符号引用。根据符号引用在Test的方法表集合中搜索符合符号引用的方法,如果找到了,则将调用地址指向Test中的testMethod的code。否则依次在父类中查找,找到则指向那个方法。如果查找失败,则抛异常。通过这个方式实现了多态性。随后根据动态指向的code中的信息,生成栈帧。这里需要根据code得到max_locals、max_stacks数据,这些数据是在编译时计算好放在code属性中的。到这里,就完成了一次方法的调用。由于涉及到jvm的知识点较多,阅读前需要先做了解,如有错误,请指正。
最新教程周点击榜
微信扫一扫

我要回帖

更多关于 dell显示器无信号输入 的文章

 

随机推荐