问题及答案来源自《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 }