目录

一、字符串创建与存储的机制是什么?

二、对于String类型的变量s,赋值语句s=null与s=“”是否相同?

三、“==”、equals和hashCode有什么区别?

 五、Java中数组是不是对象?

 六、数组的初始化方式有哪几种?


一、字符串创建与存储的机制是什么?

          创建机制: 如String s1=new String("abc")语句与String s2=new String("abc")语句,存在两个引用对象s1、s2,两个内容相同的字符串对象"abc",它们在内存中的地址是不同的。只要用到new总会生成新的对象。

        为了便于理解,可以把String s=new String("abc")语句的执行人为地分解成两个过程:第一个过程是新建对象的过程,即new String("abc");第二个过程是赋值的过程,即String s=。由于第二个过程只是定义了一个名为s的String类型的变量,将一个String类型对象的引用赋值给s,因此在这个过程中不会创建新的对象。第一个过程中new String("abc")会调用String类的构造函数

存储机制:

        语句new String("abc")也就等价于"abc"和new String()两个操作了。若在字符串池中不存在"abc",则会创建一个字符串常量"abc",并将其添加到字符串池中;若存在,则不创建,然后new String()会在堆中创建一个新的对象,指向的是堆中不同的String对象,地址自然也不相同了

new String(“abc”)创建了几个对象?

        一个或两个。如果常量池中原来有“abc”,那么只创建一个对象;如果常量池中原来没有字符串“abc”,那么就会创建两个对象。

二、对于String类型的变量s,赋值语句s=null与s=“”是否相同?

        对于赋值语句s=null,其中s是一个字符串类型的引用,它不指向任何一个字符串。而赋值语句s=“ ”中的s是一个字符串类型的引用,它指向另外一个字符串(这个字符串的值为“”,即空字符串),因此,这两者是不同的。

三、“==”、equals和hashCode有什么区别?

        1)“==”运算符用来比较两个变量的值是否相等。也就是说,该运算符用于比较变量对应的内存中所存储的数值是否相同,要比较两个基本类型的数据或两个引用变量是否相等,只能使用“==”运算符。

具体而言,如果两个变量是基本数据类型,可以直接使用“==”运算符来比较其对应的值是否相等。如果一个变量指向的数据是对象(引用类型),那么,此时涉及了两块内存,对象本身占用一块内存(堆内存),变量也占用一块内存,例如,对于赋值语句String s=new String(),变量s占用一块存储空间,而new String()则存储在另外一块存储空间里,此时,变量s所对应内存中存储的数值就是对象占用的那块内存的首地址。对于指向对象类型的变量,如果要比较两个变量是否指向同一个对象,即要看这两个变量所对应内存中的数值是否相等(这两个对象是否指向同一块存储空间),这时候就可以用“==”运算符进行比较。但是,如果要比较这两个对象的内容是否相等,那么用“==”运算符就无法实现了。

        2)equals是Object类提供的方法之一。每一个Java类都继承自Object类,所以每一个对象都具有equals这个方法。Object类中定义的equals(Object)方法是直接使用“==”运算符比较的两个对象,所以在没有覆盖equals(Object)方法的情况下,equals(Object)与“==”运算符一样,比较的是引用。

相比“==”运算符,equals(Object)方法的特殊之处就在于它可以被覆盖,所以可以通过覆盖的方法让它不是比较引用而是比较数据内容,例如String类的equals方法是用于比较两个独立对象的内容是否相同,即堆中的内容是否相同,

如:String s1 = "hello world"

String s2 = "hello world"

两条new语句创建了两个对象,然后用s1、s2这两个变量分别指向一个对象,这是两个不同的对象,它们的首地址是不同的,即a和b中存储的数值是不相同的,所以,表达式a==b将返回false,而这两个对象中的内容是相同的,所以,表达式a.equals(b)将返回true。

如果一个类没有自己定义equals()方法,那么它将继承Object类的equals()方法,它默认的equals()方法(从Object类继承的)就是使用“==”运算符,也是在比较两个变量指向的对象是否是同一对象,此时使用equals()方法和使用“==”运算符会得到同样的结果。若比较的是两个独立的对象,则总返回false。如果编写的类希望能够比较该类创建的两个实例对象的内容是否相同,那么必须覆盖equals()方法,由开发人员自己编写代码来决定在什么情况下即可认为两个对象的内容是相同的。

Object类中的hashCode()方法返回对象在内存中地址转换成的一个int值,所以如果没有重写hash-Code()方法,任何对象的hashCode()方法都是不相等的。

虽然equals()方法也是用来判断两个对象是否相等的,但是它与hashCode()方法是有区别的。一般来讲,equals()方法是给用户调用的,如果需要判断两个对象是否相等,可以重写equals()方法,然后在代码中调用,这样就可以判断它们是否相等了。对于hashCode()方法,用户一般不会去调用它,例如在hashmap中,由于key是不可以重复的,它在判断key是否重复时就判断了hashCode()这个方法,而且也用到了equals()方法。此处“不可以重复”指的是equals()和hashCode()只要有一个不等就可以了。所以,hashCode()方法相当于是一个对象的编码,就好像文件中的md5,它与equals()方法的不同之处就在于它返回的是int型,比较起来不直观。

一般在覆盖equals()方法的同时也要覆盖hashCode()方法,否则,就会违反Ob-ject.hashCode的通用约定,从而导致该类无法与所有基于散列值(hash)的集合类(Hash-Map、HashSet和Hashtable)结合在一起正常运行。
hashCode()方法的返回值和equals()方法的关系如下:如果x.equals(y)返回true,即两个对象根据equals方法比较是相等的,那么调用这两个对象中任意一个对象的hashCode()方法都必须产生同样的整数结果。如果x.equals(y)返回false,即两个对象根据equals()方法比较是不相等的,那么x和y的hashCode()方法的返回值有可能相等,也有可能不相等。反之,hashCode()方法的返回值不相等,一定能推出equals()方法的返回值也不相等,而hashCode()方法的返回值相等,equals方法的返回值则可能相等,也可能不相等。

四、 String、StringBuffer、StringBuilder和StringTokenizer有什么区别?

Java语言有4个类可以对字符或字符串进行操作,它们分别是Character、String、String-Buffer和StringTokenizer

其中Character用于单个字符操作,String用于字符串操作,属于不可变类,而StringBuffer也是用于字符串操作,不同之处是StringBuffer属于可变类。

String是不可变类,也就是说,String对象一旦被创建,其值将不能被改变,而StringBuffer是可变类,当对象被创建后仍然可以对其值进行修改。由于String是不可变类,因此适合在需要被共享的场合中使用,而当一个字符串经常需要被修改时,最好使用StringBuffer来实现。如果用String来保存一个经常被修改的字符串时,在字符串被修改时会比StringBuffer多很多附加的操作,同时生成很多无用的对象,由于这些无用的对象会被垃圾回收器来回收,因此会影响程序的性能。在规模小的项目里面这个影响很小,但是在一个规模大的项目里面,这会对程序的运行效率带来很大的影响。

String与StringBuffer的另外一个区别在于当实例化String时,可以利用构造函数(String s1=new String("world"))的方式来对其进行初始化,也可以用赋值(String s="Hello")的方式来初始化,而StringBuffer只能使用构造函数(StringBuffer s=new StringBuffer("Hello"))的方式来初始化。

String字符串修改实现的原理如下:当用String类型来对字符串进行修改时,其实现方法是首先创建一个StringBuffer,其次调用StringBuffer的append()方法,最后调用StringBuffer的toString()方法把结果返回

StringBuilder也是可以被修改的字符串,它与StringBuffer类似,都是字符串缓冲区,但StringBuilder不是线程安全的,如果只是在单线程中使用字符串缓冲区,那么StringBuilder的效率会更高些。因此在只有单线程访问时可以使用StringBuilder,当有多个线程访问时,最好使用线程安全的StringBuffer。因为StringBuffer必要时可以对这些方法进行同步,所以任意特定实例上的所有操作就好像是以串行顺序发生的,该顺序与所涉及的每个线程进行的方法调用顺序一致。

在执行效率方面,StringBuilder最高,StringBuffer次之,String最低,鉴于这一情况,一般而言,如果要操作的数据量比较小,应优先使用String类;如果是在单线程下操作大量数据,应优先使用StringBuilder类;如果是在多线程下操作大量数据,应优先考虑StringBuffer类。

StringTokenizer是用来分割字符串的工具类

public static void main( String args[] ){

StringTokenizer st = new StringTokenizer( " Welcome to our country" ) ;

while ( st. hasMoreTokens( )) {

System. oul. println( sl. ncxtToken( )) ;

}

}

输出的结果:welcome

                        to

                        out

                        country

 五、Java中数组是不是对象?

        数组是指具有相同类型的数据的集合,它们一般具有固定的长度,并且在内存中占据连续的空间。在C/C++语言中,数组名只是一个指针,这个指针指向了数组的首元素,既没有属性也没有方法可以调用,而在Java语言中,数组不仅有其自己的属性(例如length属性),也有一些方法可以被调用(例如clone方法)。由于对象的特点是封装了一些数据,同时提供了一些属性和方法,从这个角度来讲,数组是对象。每个数组类型都有其对应的类型,可以通过instanceof来判断数据的类型

public static void main( String[] args) {

int[] a={1,2};

int[][] b= new int[2][4];

String[] s={"a" ,"b"};

if(a.instanceof int[ ])

System. out. println(" the type fora is int[ ]");

if(b.instanceof int[][])

System, out. println(" the type for b is int[][]");

if(s.instanceof String[ ])

System. out. println( " the type for s is String[ ]");
}

输出:

the type for a is int[]

thetypeforbisint[][]

the type for s is Sring[ ]

 六、数组的初始化方式有哪几种?

在Java语言中,一维数组的声明方式为

type arrayName[ ]或type[ ]arrayName

其中,type既可以是基本的数据类型,也可以是类,arrayName表示数组的名字,[ ]用来表示这个变量的类型为一维数组。与C/C++语言不同的是,在Java语言中,数组被创建后会根据数组存放的数据类型初始化成对应的初始值(例如,int类型会初始化为0,对象会初始化为null)。另外一个不同之处是Java数组在定义时,并不会给数组元素分配存储空间,因此[ ]中不需要指定数组的长度,对于使用上面方式定义的数组在使用时还必须为之分配空间,分配方法为:

arrayName=new type[arraySize];

在完成数组的声明后,需要对其进行初始化,下面介绍两种初始化方法:

1)int[ ] a=new int[5];//动态创建了一个包含5个整型值的数组,默认初始化为0
2)int[ ] a={1,2,3,4,5};//声明一个数组类型变量并初始化
当然,在使用时也可以把数组的声明和初始化分开来写,例如:
1)int[ ] a;//声明一个数组类型的对象a
a=new int[5];//给数组a申请可以存放5个int类型大小的空间,默认值为0
2)int[ ] a;//声明一个数组类型的对象a
a=new int[ ]{1,2,3,4,5};//给数组申请存储空间,并初始化为默认值

二维数组的声明与初始化的方式,二维数组有3种声明的方法:

1)type arrayName[ ][ ];

2)type[ ][ ]arrayName;

3)type[ ]arrayName[ ];

需要注意的是,在声明二维数组时,其中[]必须为空。

二维数组也可以用初始化列表的方式来进行初始化,其一般形式为

type arrayName[ ][ ]={{c11,c12,c13..},{c21,c22,c23..},{c31,c32,c33…}…};

除了以上介绍的方法以外,也可以通过new关键字来给数组申请存储空间,形式如下:

type arrayname[ ][ ]=new type[行数][列数]

与C/C++语言不同的是,在Java语言中,二维数组的第二维的长度可以不同。假如要定义一个有两行的二维数组,第一行有两列,第二行有三列,定义方法如下:

1)int[ ][ ]arr={{1,2},{3,4,5}};
2)int[ ][ ]a=new int[2][ ];
a[0]=new int[ ]{1,2};
a[1]=new int[ ]{3,4,5};
对二维数组的访问也是通过下标来完成,一般形式为arryName[行号][列号],