首先,我们要先了解常量池的概念,常量池在java中用于保存在编译期已确定的,存在于已编译的class文件中的一份数据。它包括了关于类,方法,接口等中的常量,也包括字符串常量,如String s = “str”这种申明方式;当然也可扩充,执行器产生的常量也会放入常量池,故认为常量池是JVM的一块特殊的内存空间。
问题一:
String是最基本的数据类型吗?
基本数据类型包括byte、int、char、long、float、double、boolean和short。
java.lang.String类是final类型的,因此不可以继承这个类、不能修改这个类。为了提高效率节省空间,我们应该用StringBuffer类,或者用StringBuffer类。前者效率高,后者支持线程同步。
问题二:
final String str1 = "aaa";
final String str2 = "bbb";
String str3 = "aaabbb";
String str4 = str1 + str2;
System.out.println(str3 == str4);//true
解析:
因为str1与str2都定义成了常量,在编译时就已经确定,编译时就会将变量替换为常量,等同于 str4 = “aaa”+”bbb”,因此不产生新的对象。
问题三:
String s = new String("abc");
String s1 = "abc";
String s2 = new String("abc");
System.out.println(s == s1);
System.out.println(s == s2);
System.out.println(s1 == s2);
解析:
第一句执行后内存中有两个对象,而不是一个。一个由new String(“abc”)中的”abc”在字符串常量池生成一个值为”abc”的对象;第二个由new在堆里产生一个值为”abc”的对象,该对象完全是String Pool里的”abc”的一个拷贝。变量s最后指向堆中产生的”abc”对象;
第二句执行时,变量s1先去String Pool找是否有值为”abc”的对象,很显然在上一步中java已经在String Pool里生成一个”abc”对象了,所以s1直接指向String Pool中的这个”abc”;
第三句中又有一个new,首先会去字符串常量池中找“abc”,若能找到则直接获取地址,不再创建新的常量。在java中凡遇到new时,都会在堆里产生一个新的对象。因此,该句执行后堆里又多了一个”abc”对象,这与执行第一句后生成的”abc”是两个不同的对象,s2最后指向这个新生成的对象。
因此,执行后面的打印语句的结果是三个false 。
问题四:
String hello = "hello";
String hel = "hel";
String lo = "lo";
System.out.println(hello == "hel" + "lo");
System.out.println(hello == "hel" + lo);
解析:
前三句在String Pool里分别产生“hello”、“hel”、“lo”三个常量字符串对象。
当做第一个加法连接时,+号两边都是常量字符串,”hel” + “lo”在编译时就变成了“hello”,java就会将两者拼起来后到String Pool里找与之相等(用equals)的字符串,若存在则将其地址返回;不存在则在String Pool里新建一个常量对象,其值等于拼接后的字符串,并将其地址返回。
而第二个+号两边有一个是变量,此时,java会在堆里新建一个对象,其值是两字符串拼接后的值,此时返回的地址是堆中新对象的地址。
所以,第一句做+连接后返回String Pool中“hello”的地址,显然与变量hello的地址相等; 第二句返回的是堆中地址,显然与变量hello的地址不等;
问题五:类似于问题四:
public class Test {
public static void main(String[] args) {
final String str1 = "HelloWorld";
final String str2 = "Hello" + "World";
System.out.println(str1 == str2);//true
final String str3 = "Hello" + String.valueOf("World");
System.out.println(str1 == str3);//false
}
}
解析:
str3需要通过valueOf方法调用之后才能确定。而不是在编译时确定。
问题六:
在Java中String是不可变的和无法改变的,有什么好处?
1,可以使用字符串池来存储字符串,提高存储效率。
2,增加安全性,在存储一些敏感信息,如数据库用户名,密码等是,黑客不能改变它的值。java的类加载器加载类时,字符串的不变性可以确保正确的类被加装。
3,由于String是不可变的,它是安全的,在多线程环境下,我们不需要任何同步。
问题七:
使用两种方法把这个字符串中的单词首字母转为大写:String str = “hello java and android!”
@Test
public void test1(){
String str = "hello java and android!";
//这是一种方式
String[] strs=str.split(" ");
String st="";
for (int i = 0; i < strs.length; i++) {
String name = strs[i];
st+=name.substring(0, 1).toUpperCase()+name.substring(1);
st+=" ";
}
System.out.println(st);
//另一种方式
char[] array = str.toCharArray();
array[0]=(char)((int)array[0]-32);
for (int i = 0; i < array.length; i++) {
if(' '==array[i]){
array[i+1]=(char)((int)array[i+1]-32);
}
}
String sts = new String(array);
System.out.println(sts);
}
问题八、String是线程安全的吗?
String是不可变类,一旦创建了String对象,我们就无法改变它的值。因此,它是线程安全的,可以安全地用于多线程环境中。
问题九、intern的使用和原理
解释参考:https://www.cnblogs.com/wxgblogs/p/5635099.html
问题10、字符串包含问题
给定两个分别由字母组成的字符串str1和字符串str2,字符串str2的长度比字符串str1短,请问,如何最快地判断字符串str2中所有字母是否都在字符串str1里?例如,字符串str1为“ABCD”,str2为“AC”,则返回true,因为字符串str2中的字母A和B都在字符串str1中,但如果str2为“AE”,则返回false,因为字符串str2中的字母E不在字符串sstr1中。
@Test
public void test4(){
String s2 ="abcd";
String s1="ac";
String[] split = s1.split("");
for (int i = 0; i < split.length; i++) {
if(!s2.contains(split[i])){
System.out.println("不包含");
}
}
System.out.println("包含");
}另一种:
public static boolean stringContain(String str1,String str2) {
//将字符串转为字符数组
char[] a = str1.toCharArray();
char[] b = str2.toCharArray();
//进行循环比较
for (int i = 0; i < b.length; ++i) {
int j;
//如果a[j]等于b[i],则跳出本次循环,反之j+1
for (j = 0; (j < a.length) && (a[j] != b[i]); ++j) ;
if (j >= a.length) { return false; } } return true;
}
问题11、输出字符串的所有组合
输入一个字符串,输出该字符串中字符的所有组合。举个例子,如果输入abc,它的组合有a、b、c、ab、ac、bc、abc。
https://blog.csdn.net/qq_35246620/article/details/53263806
问题12、输出字符串中第一个不重复的字符
在一个字符串中找到第一个只出现一次的字符。例如,输入“abaccdeff”,则输出b。
https://blog.csdn.net/qq_35246620/article/details/53270801
@Test
public void test5(){
String s2 ="abaccdeff";
String end="";
for (int i = 0; i < s2.length(); i++) {
end=s2.substring(i, i+1);
String all = s2.replaceAll(end, "");
if((s2.length()-all.length())==1){
System.out.println(end);
return ;
}
}
}
问题 13、输出字符串的全排列
输入一个字符串,打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a、b、c所能排列出来的所以字符串abc、bac、bca、cab和cba。
https://blog.csdn.net/qq_35246620/article/details/53240670
问题14、最长公共子串
求两个字符串的最长公共子串,例如str1=“123china”,str2=“23hit”,则其最长公共子串为“23”。
https://blog.csdn.net/qq_35246620/article/details/53195381
Java数字格式化输出时前面补0
int youNumber = 1;
// 0 代表前面补充0
// 4 代表长度为4
// d 代表参数为正数型
String str = String.format("%04d", youNumber);
System.out.println(str); // 0001
15、intern方法(下面的题在周志明第三版虚拟机书中,第二章)
intern是一个本地方法,它的作用是如果字符串常量池中已经包含一个等于此String对象的字符串,则返回常量池中这个字符串的String对象的引用;否则,会将此String对象包含的字符串添加到常量池中,并且返回此String对象的引用。
public static void main(String[] args) {
String str1 = new StringBuilder("SB").append("java").toString();
System.out.println(str1);
System.out.println(str1.intern());
System.out.println(str1 == str1.intern());
String str2 = new StringBuilder("ja").append("va").toString();
System.out.println(str2);
System.out.println(str2.intern());
System.out.println(str2 == str2.intern());
/**
运行结果:
* SBjava
* SBjava
* true
* java
* java
* false
*/
}
除了java,其他字符串都是true
* 按照代码结果,java字符串为false,必然是两个不同的java,那另外一个java是怎么加载进来的?
* 原因:有一个初始化的java字符串(jdk出娘胎自带的),在加载sun.misc.Version这个类的时候进入常量池
String str1 = new StringBuilder("SB").append("java").toString();
System.out.println(str1);
System.out.println(str1.intern());
System.out.println(str1 == str1.intern());
String str3 = new StringBuilder("SB").append("java").toString();
System.out.println("---");
System.out.println(str3 == str1);
System.out.println(str3.intern() == str1);
System.out.println(str3.intern() == str3);
System.out.println(str3 == str1.intern());
System.out.println("---");
SBjava
SBjava
true
---
false
true
false
false
---
还待补充!!!