Java字符串与数组

问题及答案来源自《Java程序员面试笔试宝典》第四章 Java基础知识 4.5字符串与数组

 

 

 

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

Java中字符串声明与初始化主要有两种情况:

(1)String s1 = new String("abc")与String s2 = new String("abc")语句   

执行String s1 = new String("abc")语句,字符串池中不存在"abc",则会创建一个字符串常量"abc",

并将它添加到字符串常量池中,然后new String()会在堆中创建一个新的对象,s1指向堆中的String对象

紧接着创建String s2 = new String("abc")语句,因为字符串常量池中已经有了字符串常量"abc",所以

不会再创建"abc",直接new String()在堆中创建一个新的对象,然后使用s2指向这个对象

s1与s2指向堆中的不同String对象,地址自然也不相同

(2)String s1 = "abc"语句与String s2 = "abc"语句

在JVM中存在着一个字符串常量池,其中保存了着很多String 对象,s1,s2引用的是同一个常量池中的对象。

当创建一个字符串常量时,例如String s1 = "abc",会首先在字符串常量池中查找是否已经有相同的字符串被定义,

若已经定义,则直接获取对其的引用,此时不需要创建字符串常量"abc",如果没有定义,则首先创建字符串常量

"abc",然后把它加入到字符串池中,再将引用返回

 

例子1:

String s1 = new String("abc");   //  先查找常量区有无"abc"常量,若无则将其"abc"添加到常量区,再在堆中创建对象,将s1指向堆中的对象

String s2 = new String("abc");    //  发现在常量区已经有了"abc",在堆中创建对象,将s2指向堆中的对象

例子2:

String s1 = "abc";  //  在常量区里面创建一个"abc"字符串对象,s1获取对其的引用

String s2 = "abc";  //  发现在常量区已经有了"abc",s2直接获取对其的引用

 

引申 - 对于String类型的变量s,赋值语句s=null和赋值语句s=""有什么区别?

s=null,是指s不指向任何一个字符串;s=""中的s指向空字符串

 

笔试题 - new String("abc")创建了几个对象?

一个或两个,如果常量池中原来就有"abc",那么只创建一个对象,否则创建两个对象

 

 

2、==、equals和hashCode有什么区别?

==:是运算符,用于比较两个变量是否相等。当比较对象时,比较的是对象在内存中的地址

equals方法:用于比较两个对象是否相等,默认Object类的equals方法是比较两个对象的地址,跟==的结果一样

如果一个类没有自己定义equals方法,它默认的equals方法就是使用"=="运算符,也就是在比较两个变量指向

的对象是否是同一个对象

hashCode方法:是用来鉴定两个对象是否相等,默认Object类中的hashCode方法返回对象在内存中地址转换成

的一个int值,所以如果没有重写hashCode()方法,任何对象的hashCode()方法都是不相等的。返回一个离散的int型整数。

在集合类操作中使用,为了提高查询速度。(HashMap,HashSet等)

 

equals方法实际使用:

 1 class Person{
 2     
 3 }
 4 
 5 public class ObjectDemo {
 6     public static void main(String[] args) {
 7         // Person类没有重写equals方法 而Object类中的equals方法是比较两个对象的地址
 8         Person p1 = new Person();
 9         Person p2 = new Person();
10         System.out.println(p1.equals(p2));            // false
11         
12         // String中重写了equals方法  比较的是两个字符串的内容
13         String s1 = new String("hello");
14         String s2 = new String("hello");
15         System.out.println(s1.equals(s2));            // true
16 
17         String s3 = "hello";
18         String s4 = "hello";
19         System.out.println(s3.equals(s4));            // true
20     }
21 }

 

java中的数据类型,可分为两类: 

(1)基本数据类型

Java中基本数据类型有八种:byte,short,char,int,long,float,double,boolean,

他们之间的比较,用双等号(==),比较的是他们的值。

 

(2)复合数据类型(类)

当他们用(==)进行比较的时候,比较的是他们在内存中的存放地址,所以,除非是同一个new

出来的对象,他们的比较后的结果为true,否则比较后结果为false。 

在Java的Object中的基类中定义了一个equals的方法,这个方法的初始行为是比较对象的内存地址,

但在一些类库当中这个方法被覆盖掉了,如String等,在这些类当中equals有其自身的实现,而不再是

比较对象的内存地址了

 

equals方法和hashCode方法的关系(用于set、map):

hashCode方法只有在set或map集合中用到

当覆盖了equals方法时,比较对象是否相等将通过覆盖后的equals方法进行比较(判断对象的内容是否相等)

将对象放入到集合中时,首先判断要放入对象的hashCode值与集合中的任意一个元素的hashCode值是否相等,

如果不相等直接将该对象放入集合中。如果hashCode值相等,然后再通过equals方法判断要放入对象与集合中的

任意一个对象是否相等,如果equals判断不相等,直接将该元素放入到集合中,否则不放入。

 

 

3、String、StringBuffer、StringBuilder和SringTokenizer有什么区别?

Java中有5个类可以对字符或字符串进行操作,分别是Character、String、StringBuffer、StringBuilder

和SringTokenizer,其中Character用于单个字符操作,String用于字符串操作(属于不可变类),而

StringBuffer也是用于字符串操作(属于可变类)

 

Character的API:

  • boolean isDigit(char ch)        确定指定字符是否为数字。
  • boolean isLowerCase(char ch)  确定是否是小写字母字符
  • boolean isUpperCase(char ch)  确定是否大写字母字符
  • char toLowerCase(char ch)     将给定字符转换成大写字符 
  • char toUpperCase(char ch)     将给定字符转换成小写字符 

 

String的API:

 1 引用类型String中的方法(4532)
 2 第一组: 判断方法
 3           boolean equals(String str);            // 比较两个字符串的内容是否相等
 4           boolean equalsIgnoreCase(String str);     // 比较两个字符串的内容是否相等(忽略大小写)
 5           boolean startsWith(String subStr);       // 判断某个字符串是否以指定的子串开头
 6           boolean endsWith(String subStr):        // 判断某个字符串是否以指定的子串结尾
 7  
 8 第二组: 获取方法
 9           int length();                           // 获取字符串中字符个数
10           char charAt(int index);                   // 谋取字符串某一个字符(指定下标)
11           String subString(int startIndex);              // 从指定下标开始截取字符串,直到字符串末尾
12           String subString(int startIndex, int endIndex);     // 从指定下标开始截取字符串,到指定下标结束(左闭右开)
13           int indexOf(String subStr);                    // 获取子串第一次出现的下标
14   
15 第三组: 转换方法
16           String toLowerCase();    // 转成小写串
17           String toUpperCase();    // 转成大写串
18           Char[] toCharArray();    // 变成字符数组
19   
20 第四组: 其他方法
21           String trim();                     // 去掉字符串两端的空格
22           String[] split(String str);        // 切割字符串

 

String和StringBuffer的区别:

String是不可变类,一旦对象创建其值将不能改变,而StringBuffer是可变类,对象创建后值可以改变

实例化String时可以用赋值也可以用new初始化,而实例化StringBuffer只能用new初始化

 

String字符串修改的原理:

当用String类型来对字符串进行修改时,其实现方法是首先创建一个StringBuffer对象,然后调用StringBuffer的

append方法,最后调用StringBuffer的toString方法把结果返回

1 String s = "Hello";
2 s += " World";
3 // 以上代码等价于
4 StringBuffer sb = new StringBuffer(s);
5 sb.append(" World");
6 s = sb.toString();

 

StringBuilder和StringBuffer:

StringBuilder也是可以被修改的字符串,它与StringBuffer类似,都是字符串缓冲区,但是StringBuilder

不是线程安全的,如果只是在单线程中使用字符串缓冲区,那么StringBuilder的效率会更高些

而StringBuffer在必要时可以对方法进行同步,所以任意特定实例上的操作就好像是以串行顺序

发生,该顺序与所涉及的每个线程进行的方法调用顺序一致

因此在只有单线程访问时可以使用StringBuilder,当有多个线程访问时最好用线程安全的StringBuffer

 

StringTokenizer:

StringTokenizer是用来分割字符串的工具类,示例如下:

1 public class Demo{    
2     public static void main(String[] args) {
3         StringTokenizer st = new StringTokenizer("Welcome to our country");
4         while(st.hasMoreTokens()){
5             System.out.println(st.nextToken());
6         }
7     }    
8 }

输出结果:

Welcome
to
our
country

 

总结:

在执行效率方面,StringBuilder最高,StringBuffer次之,String最低,使用如下:

  • 如果操作的数据量比较小,应优先使用String类
  • 如果是在单线程下操作大量数据应优先使用StringBuilder类
  • 如果是在多线程下操作大量数据应优先使用StringBuffer类

 

 

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

数组是指具有相同类型的数据的集合,它们一般具有固定的长度,并且在内存中占据连续的空间 

在C/C++中,数组名只是一个指针,这个指针指向了数组的首元素,没有属性也没有方法

而在Java中,数组不仅具有自己的属性,还有方法可以调用,故在Java中数组是对象

每个数组类型都有其对于的类型,可以通过instanceof来判断数据的类型,如下:

 1 public class FindLeftAndRightBigger {
 2     public static void main(String[] args) {
 3         int[] a = {1, 2, 3};
 4         int[][] b = new int[2][3];
 5         String[] s = {"asfd", "saf"};
 6         if(a instanceof int[]){
 7             System.out.println("int[]");
 8         }
 9         if(b instanceof int[][]){
10             System.out.println("int[][]");
11         }
12         if(s instanceof String[]){
13             System.out.println("String[]");
14         }
15     }    
16 }
17 // 输出结果:
18 // int[]
19 // int[][]

 

 

5、Java数组的初始化方法有哪几种?

Java数组的初始化方法如下:

 1 // 一维数组声明:
 2 type arrayName[] 或 type[] arrayName
 3 // 二维数组声明:
 4 type arrayName[][] 或 type[][] arrayName 或 type[] arrayName[]
 5 // 数组初始化:可以在声明时直接赋值也可以在之后new
 6 // 注:type既可以是基本数据类型也可以是类
 7 
 8 // 一维数组初始化实例:
 9 int[] a = new int[5];  // 动态创建了一个包含5个整形值得数组,默认值为0   
10 int[] a = {1, 2, 3};    // 声明一个数组并初始化
11 
12 // 二维数组初始化实例:
13 // Java中二维数组的第二维长度可以不同
14 int[][] arr = {{1, 2}, {3, 4, 5}};
15 int[][] arr = new int[2][];
16 arr[0] = new int[]{1, 2};
17 arr[1] = new int[]{3, 4, 5};

注:原生类指未被实例化的类,数组是实例化、被分配空间的类,数组不属于原生类 

 

 

6、length属性和length()方法有什么区别?

  • length属性:获取数组长度
  • length方法:计算字符串长度
 1 public class test{f
 2     public static void testArray(int[] arr){
 3         System.out.println("数组长度为" + arr.length);
 4     }
 5     
 6     public static void testString(String s){
 7         System.out.println("字符串长度为" + s.length());
 8     }
 9     
10     public static void main(String[] args) {
11         testArray(new int[]{1, 2, 3});      // 3
12         testString("hello");            // 5
13     }    
14 }

 

posted @ 2019-01-22 20:19  woz333333  阅读(9090)  评论(0编辑  收藏  举报