1、java程序中创建对象的常规方式有4种:
(1)通过new调用构造器创建对象。
(2)通过Class对象的newInstance()方法调用构造器创建java对象
(3)通过java的反序列化机制从IO流中恢复java对象。
(4)通过java对象提供的clone()方法复制一个新的java对象。
此外,对于字符串以及Byte,Short,Int,Long,Character,Float,Double和Boolean这些基本类型的包装类,
java还允许以直接量的方式来创建java对象,(String str="abc"; Intefer in =5; )
还可通过简单的算法表达式,连接运算来创建java对象(String str2="abc"+"xyz");
2:关于字符串常量的一些问题:
JVM会使用一个字符串池来保存它们,当第一次使用某个字符串直接量是,JVM会将它放入字符串池进行缓存。在
一般情况下,字符串池中字符串对象不会被垃圾回收,当程序再次需要使用该字符串时,无需重新创建一个新的字符串,而是
直接让引用变量执行字符串池中已有的字符串。
(1)请问:String java=new String("疯狂java");
上面的语句创建了几个字符串常量。
答案:一个是“疯狂java”这个直接量对应的字符串对象,一个是由new String()构造器返回的字符串对象,
(2) 判断str1和str2是那创建字符串的方式。
public static void main(String[] args) {
String str1="Hello"; //str1指向字符串缓存池中的“Hello”字符串
String str2="Hello"; //str2指向字符串缓存池中的“Hello”字符串
System.out.println(str1==str2);
}
str1和str2两个字符串变量的值都是直接量,它们都指向jvm的字符串池里面的“Hello”字符串。
(3)通过字符串连接表达式来创建字符串对象,故可以将一个字符串连接表达式赋值给字符串常量。
如果这个字符串连接表达式的值可以在编译时确定下来,那么jvm会在编译时计算该字符串变量的值,
并让他指向字符串池中对应的字符串。
public static void main(String[] args) {
String str1="Hello java的长度:10";
String str2="Hello "+"java"+"的长度:"+10;
System.out.println(str1==str2); //true
}
虽然str2的值是一个字符串连接表达式,但是这个字符串连接表达式的值在编译时就确定下来了。
str2中的所有运算数,它们多少字符串直接量,整数直接量,没有变量的参与,没有方法的调用。
因此,jvm可以在编译时就确定该字符串连接表达式的值,可以让该字符串变量指向字符串池中对应的字符串。
(4)如果程序使用了变量,或者调用了方法,那就只能等到运行时才可以确定该字符串连接表达式的值,
也就无法再编译时确定该字符串变量的值,因此无法利用jvm的字符串池。
public static void main(String[] args) {
String str1="Hello java的长度:10";
String str2="Hello "+"java"+"的长度:"+"Hello java".length();
System.out.println(str1==str2);
int len=10;
String str3="Hello "+"java"+"的长度:"+len;
System.out.println(str1==str3);
}
答案:false,false。
str2和str3的值也是字符串连接运算。但是由于str2变量的连接表达式中包含了一个方法的调用,str3字符串连接
表达式中包含了一个len变量,因此str2和str3都不会指向jvm字符串池中对应的字符串。
(5)特殊情况:字符串连接运算符中的所有变量都可执行“宏替换”,那么jvm一样可以在编译时就确定字符串连接表达式的值,
一样会让字符串变量指向JVM字符串池中的对应字符串。
public static void main(String[] args) {
String str1="Hello java的长度:10";
final String s1="Hello ";
String str2=s1+"java"+"的长度:10";
System.out.println(str1==str2);
final int len=10;
String str3="Hello "+"java"+"的长度:"+len;
System.out.println(str1==str3);
}
答案:true,true。
str2和str3对应的字符串连接表达式中包含了s1和len变量,但由于编译器会对s1和len执行“宏替换”,
jvm同样可以在编译时确定str2和str3变量的值,可以让str2和str3指向字符串池中对应的字符串。
(6)String str="Hello "+"java ,"+"crazyit.org"; 到底创建了几个字符串对象?
答案:只创建了一个字符串对象,str的值可以在编译时确定下来。jvm会在编译时就计算出str的值
为“Hello java ,crazyit.org”,然后将该字符串直接量放入字符串池中,并让str指向该它。
3:、String类是一个典型的不可变类。当一个String对象创建后,该String类里面包含的字符串序列就被固定下来了,
以后永远都不能改变。
(1)
public static void main(String[] args) {
String str1="Hello"; //(1)
System.out.println(System.identityHashCode(str1));
str1=str1+"java"; //(2)
System.out.println(System.identityHashCode(str1));
str1=str1+",crazyit.org"; //(3)
System.out.println(System.identityHashCode(str1));
}
当运行输出str1时,每一次的str1变量对应的字符串序列一直在变化,开始等于“Hello”,第一次连接运算后为“Hello java”
,第二次连接后等于“Hello java,crazyit.org”,看起来是str对应的字符序列发生了变化,str只是一个引用类型的变量,
它并不是真正的String对象,它只是指向String对象。
当程序执行(1)行代码时,str指向一个字符串池中对应的字符串。(执行完(1)行代码后的内存分配)
当执行(2)行代码的连接运算,此时的连接运算会把“Hello”,“java”两个字符串连接起来得到一个新的字符串,并让str指向这个新的的字符串。(执行完(2)后的内存分配)
(str变量原来指向的字符串对象并没有任何改变,它所包含的字符序列依然是“Hello”,只是str变量不在指向它而已。
str变量指向了一个新的String对象,因此看到str变量所引用String对象的字符序列发生了改变。发生改变的不是String对象,而是str变量本身,它改变了指向,指向了一个新的string对象)
重要:(1)“Hello”字符串也许以后永远都不会用到了,但这个字符串并不会被垃圾回收,它将一直存在字符串常量池中,
这就是java内存泄漏的原因之一。
(2)System类的identityHashCode()静态方法来获取str的identityHashCode值,
发生了3次返回的identityHashCode值并不相同的状况,这表明了3次访问str是分别指向3个不同的String对象。
(System提供的identityHashCode()静态方法用于获取某个对象唯一的HashCode值,这个identityHashCode()的返回值与该类是否重写了HashCode()方法无关,只有当两个对象相同时,它们的identityHashCode值才会相同)
4、String类,它代表了序列不可改变的字符串。
需要一个字符串序列会发生改变的字符串,应考虑使用StringBuider或StringBuffer。
(很多资料上推荐使用StringBuffer,那是因为资料都是在JDK1.5问世之前了)
实际上通常优先考虑使用StringBuider。StringBuffer与StringBuilder唯一个区别在于:StringBuffer是线程安全的,
也就是说StringBuffer类中绝大部分方法都增加了synchronized修饰符。对方法增加了sychronized修饰符可以保证该方法线程安全,但会降低该方法的执行效率,在没有多线程的环境下,应该优先使用StringBuilder类来表示字符串。
public static void main(String[] args) {
StringBuilder str=new StringBuilder("Hello ");
System.out.println(str);
System.out.println(System.identityHashCode(str));
str.append("java");
System.out.println(str);
System.out.println(System.identityHashCode(str));
}
str应用变量没有发生改变,它一直指向同一个StringBuilder对象,但是它所指向的StringBuilder所包含的字符序列
发生了变化。输出的str的identityHashCode完全相同。
5、StringBuilder,StringBuffer都代表了字符串序列可变的字符串,其中StringBuffer是线程不安全的版本,
StringBuffer是线程安全的版本。String则代表了字符序列不可变的字符串,但String不需要线程安全,线程不安全两个版本,因为String本身是不可变类,而不可变类总是线程安全的。
(Java是一个近乎纯洁的面向对象编程语言,但是为了编程的方便还是引入了基本数据类型,
但是为了能够将这些基本数据类型当成对象操作,Java为每一个基本数据类型都引入了对应的包装类型(wrapper class)。)
int
i =
5
;
Integer it =
new
Integer(i);//基本类型转换成封装类型
int
i =
5
;
Integer it =
new
Integer(i);
//基本类型转换成封装类型
int
i2 = it.intValue();//封装类型转换成基本类型
(1)不需要调用构造方法,通过=符号自动把 基本类型 转换为 类类型 就叫装箱
public static void main(String[] args){
int i=5;
Integer i1=i;
System.out.println(i1);
}
(2)不需要调用Integer的intValue方法,通过=就自动转换成 int类型,就叫拆箱
public static void main(String[] args){
int i=1;
Integer it=new Integer(i);
int i1=it;
System.out.println(i1);
}
public static void main(String[] args){
System.out.println(Integer.MAX_VALUE);
System.out.println(Integer.MIN_VALUE);
}
(1)使用String类的静态方法valueOf。
public static void main(String[] args){
int i=5;
String str=String.valueOf(i);
System.out.println(str);
}
(2)先把基本类型装箱为对象,然后调用对象的toString。
public static void main(String[] args) {
int i = 53245;
Integer it = i;
String str = it.toString();
System.out.println(str);
}
调用Integer的静态方法parseInt
public static void main(String[] args) {
String str = "1234";
int i = Integer.parseInt(str);
System.out.println(i);
}
public static void main(String[] args){
String str="123456";
char array[]=new char[100];
array=str.toCharArray();//*
System.out.println("遍历这个字符数组");
for(int i=0;i
//通过String类型的构造函数将字符数组转换为字符串
public static void main(String[] args){
String str=null;
char array[]={'1','s','f'};
str=new String(array);//*
System.out.println("输出字符串: "+str);
}
public static void a(){
//将String类型转换为StringBuffer类型的字符串
String str="12345";
StringBuffer strbuf;
strbuf=new StringBuffer(str);//*
System.out.println("输出StringBuffer类型:"+strbuf);
}
public static void b(){
//将StringBuffer类型的字符串转换为String类型
String str;
StringBuffer strbuf=new StringBuffer("Hello");
str=strbuf.toString();
System.out.println("输出字符串:"+str);
}
(1)四舍五入round()
float f1=5.4f;
float f2=5.5f;
System.out.println(Math.round(f1));
System.out.println(Math.round(f2));
(2)随机数Math.random()
System.out.println(Math.random());//得到一个0-1之间的随机浮点数(取不到1)
System.out.println((int)( Math.random()*10));//得到一个0-10之间的随机整数 (取不到10)
(3)开方Math.sqrt()
System.out.println(Math.sqrt(9));
(4)次方Math.pow()
System.out.println(Math.pow(2,4));//2的4次方
(5)π Math.PI
System.out.println(Math.PI);
(6)自然常数Math.E
System.out.println(Math.E);
(1)格式化输出
(2)printf和format
public static void main(String[] args) {
String name = "粉粉";
int a = 2;
String grade = "表扬";
String sentenceFormat = "%s 得到了 %d的%s";
System.out.printf(sentenceFormat, name, a, grade);
System.out.println();
System.out.printf(sentenceFormat, name, a, grade);
}
(3)换行符
(4)总长度,右对齐,补0,千位分隔符,小数点位数,本地化表达
public static void main(String[] args) {
int year = 2000;
System.out.format("%d%n", year);//直接打印数字
System.out.format("%8d%n", year);//总长度是8,默认右对齐。
System.out.format("%-8d%n", year);//总长度是8,默认左对齐。
System.out.format("%08d%n", year);//总长度是8,不够补0
System.out.format("%,8d%n", year * 10000);//千位分隔符
System.out.format("%.2f%n", Math.PI);//小数点位数
}
(1)char对应的封装类Character(装箱拆箱)
public static void main(String[] args) {
char c='a';
Character c1=c;//自动装箱
c=c1;//自动拆箱
}
(2)Character类中的常见方法
public static void main(String[] args) {
System.out.println(Character.isLetter('a'));//判断是否为字母
System.out.println(Character.isDigit('a')); //判断是否为数字
System.out.println(Character.isWhitespace(' ')); //是否是空白
System.out.println(Character.isUpperCase('a')); //是否是大写
System.out.println(Character.isLowerCase('a')); //是否是小写
System.out.println(Character.toUpperCase('a')); //转换为大写
System.out.println(Character.toLowerCase('A')); //转换为小写
String a = 'a'; //不能够直接把一个字符转换成字符串,会出现红线
String a2 = Character.toString('a'); //将一个字符转换为字符串
}
(3)常见转义
public static void main(String[] args) {
System.out.println("使用空格无法达到对齐的效果");
System.out.println("abc def");
System.out.println("ab def");
System.out.println("a def");
System.out.println("使用\\t制表符可以达到对齐的效果");
System.out.println("abc\tdef");
System.out.println("ab\tdef");
System.out.println("a\tdef");
System.out.println("一个\\t制表符长度是8");
System.out.println("12345678def");
System.out.println("换行符 \\n");
System.out.println("abc\ndef");
System.out.println("单引号 \\'");
System.out.println("abc\'def");
System.out.println("双引号 \\\"");
System.out.println("abc\"def");
System.out.println("反斜杠本身 \\");
System.out.println("abc\\def");
}