一、包装类
包装类的分类 (Wrapper)
- 针对八种基本数据类型相应的引用类型--包装类
- 有了类的特点,就可以调用类中的方法。
基本数据类型
包装类
boolean
Boolean
char
Character
double
Double
int
Integer
byte
Byte
short
Short
float
Float
long
Long
后六个的父类都为Number。
(1)我们查看Boolean的源码:
我们进入类图如下所示:
Boolean实现了Serializable和Comparable接口。
(2)我们查看Character的源码:
我们查看他的类图:
Character类实现了Serializable和Comparable。
(3)我们查看Byte类:
我们进行查看他的类图:
我们查看剩下的类图:
Float\Double\Intger\Byte\Short\Long 都继承自Number类,实现了Comparable,同时也实现了Serializable接口。
包装类和基本数据的转换
演示 包装类和基本数据类型的相互转换,这里以int和Integer演示。
(1)jdk5前的手动装箱和拆箱方式,装箱:基本类型 ->包装类型,反之,拆箱:包装类型 -->基本类型
(2)jdk5 以后(含jdk5)的自定装箱和拆箱方式
(3)自动装箱底层调用的是valueOf方法,比如Integer.valueOf( )
(4 )其它包装类的用法类似。
我们查看示例如下所示:
package com.ypl.wrapper; public class Integer01 { public static void main(String[] args) { //演示int <-->Integer的装箱和拆箱 //装箱: int -->Integer //拆箱: Integer -->int //jdk5前是手动装箱和拆箱 //手动装箱 int ->Integer int n1=100; //方式1: Integer integer = new Integer(n1); //方式2: Integer integer1 = Integer.valueOf(n1); //手动拆箱 Integer -->int int i = integer.intValue(); //jdk5后,就可以自动装箱和自动拆箱 int n2=200; //自动装箱 int ->Integer Integer integer2=n2; //底层使用的是Integer.valueOf(n2)。 //我们进行debug,我们进入底层发现源码如下所示: /** * public static Integer valueOf(int i) { * if (i >= IntegerCache.low && i <= IntegerCache.high) * return IntegerCache.cache[i + (-IntegerCache.low)]; * return new Integer(i); * } */ //自动拆箱 Integer -->int int n3=integer2; //底层仍然使用的是intValue()方法 //我们继续进行debug,我们进入底层发现源码如下所示: /** * public int intValue() { * return value; * } */ } }
课堂测试题:
下面的代码是否正确,为什么?
Double d=100d; //ok,自动装箱 Double.valueOf(100d); Float f=1.5f; //ok,自动装箱 Float.valueOf(1.5f);
如下两个题目输出结果相同吗?各是什么?
Object obj1=true?new Integer(1):new Double(2.0); //三元运算符【是一个整体】 如果为真,则返回第一个值,如果为假,则返回第二个值。 System.out.println(obj1);//什么? 输出1.0,Double会提升优先级。
Object obj2; if(true) obj2=new Integer(1); else obj2=new Double(2.0); System.out.println(obj2); 输出什么? 1,分别计算,是独立的,不会提示优先级
我们在编译软件上实际编码如下:
package com.ypl.wrapper; public class WrapperExercise01 { public static void main(String[] args) { Double d=100d; //ok,自动装箱 Double.valueOf(100d); Float f=1.5f; //ok,自动装箱 Float.valueOf(1.5f); Object obj1=true?new Integer(1):new Double(2.0); //三元运算符【是一个整体】 如果为真,则返回第一个值,如果为假,则返回第二个值。 System.out.println(obj1);//什么? 输出1.0,Double会提升优先级。 Object obj2; if(true) obj2=new Integer(1); else obj2=new Double(2.0); System.out.println(obj2); } }
运行之后如下所示:
包装类型和String类型的相互转换
我们示例代码如下所示:
package com.ypl.wrapper; public class WrapperVSString { public static void main(String[] args) { //案例演示,以Integer和String转换为例,其他类似: //包装类(Integer) -->String //1.自动装箱 //2.toString方法 //3.valueOf方法 Integer i= 100; //自动装箱 //方式1,i本身没有变化 String str1=i+""; //方式2 String str2 = i.toString(); //方式3 String str3 = String.valueOf(i); /* public static String valueOf(Object obj) { return (obj == null) ? "null" : obj.toString(); } valueOf底层为Object对象,而Integer本身就是从Object过来的。 */ //String -->包装类(Integer) //1.Integer的parseInt方法 //2.Integer的构造器的形式 String str4="123450"; Integer i2 = Integer.parseInt(str4); //使用到自动装箱 /* 我们查看parseInt的底层代码: public static int parseInt(String s) throws NumberFormatException { return parseInt(s,10); } */ Integer i3 = new Integer(str4); //构造器 /* 我们查看构造器的源码如下所示: public Integer(String s) throws NumberFormatException { this.value = parseInt(s, 10); } */ System.out.println("ok----"); } }
我们运行之后如下所示:
我们发现没有问题。
char [ ] 和String类型的相互转换
package com.ypl.String; /** * String与 char[]之间的转换 * String -->char[]:调用String的toCharArray() * char --->String:调用String的构造器 */ public class StringChar { public static void main(String[] args) { String str1="abc123"; //题目:a21cb3,可以将字符串变成数组,然后利用数组的方法进行反转 char[] charArray = str1.toCharArray(); for(int i=0;i<charArray.length;i++){ System.out.println(charArray[i]); } char[] arr=new char[]{'h','e','l','l','o'}; String s = new String(arr); System.out.println(s); } }
运行之后如下所示:
字节数组和String类型的相互转换
package com.ypl.String; import java.io.UnsupportedEncodingException; import java.util.Arrays; /** * String与byte[ ]之间的转换 * 编码:String -->byte[]:调用String的getBytes() * 解码:byte[] -->String:调用String的构造器 * * * * 编码:字符串-->字节(看得懂--->看不懂的二进制数据) * 解码:编码的逆过程,字节--->字符串(看不懂的二进制数据-->看得懂) *说明:解码时,要求解码使用的字符集必须与编码时使用的字符集一致,否则会出现乱码 */ public class StringByte { public static void main(String[] args) { String str1="abc123中国"; byte[] bytes = str1.getBytes(); //使用默认的字符集,进行编码 System.out.println(Arrays.toString(bytes)); byte[] gbks = new byte[0]; try { gbks = str1.getBytes("gbk");//使用gbk字符集进行编码 } catch (UnsupportedEncodingException e) { e.printStackTrace(); } System.out.println(gbks); String s = new String(bytes); //使用默认的字符集,进行解码 System.out.println(s); String s1 = new String(gbks); System.out.println(s1);//出现乱码。原因:编码集和解码集不一致 try { String gbk = new String(gbks, "gbk"); System.out.println(gbk); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } }
运行之后如下所示:
包装类(Integer类和Character类)的常用方法
我们发现Integer有很多种方法:
我们选择常见的方法如下所示:
Integer.MIN_VALUE(返回最小值)
Integer.MAX_VALUE(返回最大值)
我们发现Character的方法如下所示:
Character.isDigit(判断是不是数字)
Character.isLetter(判断是不是字母)
Character.isUpperCase(判断是不是大写)
Character.isLowerCase(判断是不是小写)
Character.isWhitespace(判断是不是空格)
Character.toUpperCase(转成大写)
Character.toLowerCase(转成小写)
我们的代码示例如下所示:
package com.ypl.wrapper; public class WrapperMethod { public static void main(String[] args) { System.out.println(Integer.MIN_VALUE);//返回最小值 System.out.println(Integer.MAX_VALUE);//返回最大值 System.out.println(Character.isDigit('a')); //判断是不是数字 System.out.println(Character.isLetter('a'));//判断是不是字母 System.out.println(Character.isUpperCase('a'));//判断是不是大写 System.out.println(Character.isLowerCase('a'));//判断是不是小写 System.out.println(Character.isWhitespace('a'));//判断是不是空格 System.out.println(Character.toUpperCase('a'));//转成大写 System.out.println(Character.toLowerCase('A'));//转成小写 } }
我们运行之后如下所示:
Integer类面试题1:
我们的代码如下所示:
package com.ypl.wrapper; public class method1 { public static void main(String[] args) { Integer i = new Integer(1); Integer j = new Integer(1); System.out.println(i==j); //False,不是同一个对象 //所以,这里主要是看范围 -128 ~127就是直接返回 Integer m=1; //底层 Integer.valueOf(1); ->阅读源码 Integer n=1; //底层 Integer.valueOf(1); ->阅读源码 /** * 1.如果i在IntegerCache.low(-128)~IntegerCache.high(127),包括-128和127,就直接从数组返回 * 2.如果不在-128~127,就直接new Integer(i) * public static Integer valueOf(int i) { * if (i >= IntegerCache.low && i <= IntegerCache.high) * return IntegerCache.cache[i + (-IntegerCache.low)]; * return new Integer(i); * } * 我们继续追加的时候,我们发现 * private static class IntegerCache { * static final int low = -128; * static final int high; * static final Integer cache[]; * * This method will always cache values in the range -128 to 127, inclusive, * and may cache other values outside of this range. */ System.out.println(m==n); //返回True. //所以,这里主要是看范围 -128 ~127就是直接返回 //否则,就new Integer(xx); Integer x=128;//底层 Integer.valueOf(128); Integer y=128;//底层 Integer.valueOf(128); /** * public static Integer valueOf(int i) { * if (i >= IntegerCache.low && i <= IntegerCache.high) * return IntegerCache.cache[i + (-IntegerCache.low)]; * return new Integer(i); * } */ System.out.println(x==y); //128不在范围内,因此就返回false。 Integer a=127;//底层 Integer.valueOf(127); Integer b=127;//底层 Integer.valueOf(127); //所以,这里主要是看范围 -128 ~127就是直接返回,包括-128和127 /** * public static Integer valueOf(int i) { * if (i >= IntegerCache.low && i <= IntegerCache.high) * return IntegerCache.cache[i + (-IntegerCache.low)]; * return new Integer(i); * } */ System.out.println(a==b); //127在范围内,因此就返回true。 } }
我们运行之后如下所示:
我们可以更加详细的查看我们的源码,我们进行debug,如下所示:
我们追加之后,我们发现如下所示:
我们发现 IntegerCache.cache里面东西已经创建好了,而且最低为-128,最高为127。
下来我们继续看待一道Integer类面试题:
我们的代码如下所示:
package com.ypl.wrapper; public class WrapperExercise03 { public static void main(String[] args) { //示例一 Integer i1 = new Integer(127); Integer i2 = new Integer(127); System.out.println(i1==i2); //返回false //示例二 Integer i3 = new Integer(128); Integer i4 = new Integer(128); System.out.println(i3==i4); //返回false //示例三,取值范围在-128~127,直接返回 Integer i5=127; //底层Integer.valuesOf(127) Integer i6=127; //底层Integer.valueOf(127) System.out.println(i5==i6); //返回true //示例四,超出直接返回的取值范围 ~128-127。 Integer i7=128; Integer i8=128; System.out.println(i7==i8); //返回false //示例五 Integer i9=127; //底层Integer.valuesOf(127) Integer i10 = new Integer(127); /** * public static Integer valueOf(int i) { * if (i >= IntegerCache.low && i <= IntegerCache.high) * return IntegerCache.cache[i + (-IntegerCache.low)]; * return new Integer(i); * } */ System.out.println(i9==i10); //返回false。 //示例六,只要有基本数据类型,判断的是值是否相等。 Integer i11=127; int i12=127; System.out.println(i11==i12); //返回true,会自动装箱 //示例七,只要有基本数据类型,判断的是值是否相等。 Integer i13=128; int i14=128; System.out.println(i13==i14); //返回true. } }
我们运行之后如下所示:
二、String类
String类的理解和创建对象
(1)String对象用于保存字符串,也就是一组字符序列,String是一个final类,代表不可变的字符序列,是不能被继承的类。
(2)字符串常量对象是用双引号括起的字符序列。例如:“你好”、“12.97”、“boy"等
(3)字符串的字符使用Unicode字符编码,一个字符(不区分字母还是汉字)占两个字节
char占两个字节
(4)String类较常用构造器(其他看手册):
//this.value=new char[0];
String s1=new String( );
//this.value=original.value;
String s2=new String(String original);
//this.value=Arrays.copyof(value,value.length);
String s3=new String(char[ ] a);
String s4=new String(char[ ] a,int startIndex(从哪里开始),int count(多少个字符))
(5)字符串的不可变性:
package com.ypl.String;
/**
* String的使用
*/
public class StringTest {
/*
String:字符串,使用一对" "引起来表示。
1.String声明为final的,不可被继承
2.String实现了Serializable接口:表示字符串是支持序列化的。
实现了Comparable<String>接口:表示String可以比较大小
3.String内部定义了final char[] value用于存储字符串数据
4.String:代表不可变的字符序列。简称:不可变性。
体现:(1)当对字符串重新赋值时,需要重写指定内存区域赋值,不能使用原有的value进行赋值。
(2)当对现有的字符串进行连接操作时,也需要重新指定内存区域赋值,不能在原有的value进行赋值
(3)当调用String的replace()方法修改指定字符或字符串时,也需要重新指定内存区域赋值。
我们在通过String进行字面量赋值的时候,如果方法区里面的常量池没有这个数,则进行创建,然后地址值赋值给栈,如果要重新赋值,则
查看方法区里面的常量池是否存在,如果没有,即继续创建,同时新创建的变量的地址指向栈空间,原来的被取消,如果长时间未得到栈的调用,
被垃圾回收机制回收
5.通过字面量的方式(区别于new)给一个字符串赋值,此时的字符串值声明在字符串常量池中。
6.字符串常量池中是不会存储相同内容的字符串的。
*/
public static void main(String[] args) {
String s1="abc"; //字面量的定义方式
String s2="abc"; //保存在常量池里面的,以地址进行存储
s1="hello";
System.out.println(s1==s2);//两个对象用==比较的是地址值
System.out.println(s1);//hello
System.out.println(s2);//abc
System.out.println("******************");
String s3="abc";
s3+="def";
System.out.println(s3);//abcdef
System.out.println(s2);
System.out.println("******************");
String s4="abc";
String s5 = s4.replace('a', 'm');
System.out.println(s4);
System.out.println(s5);
}
}
运行之后如下所示:
我们设计如下代码:
public class String01 {
public static void main(String[] args) {
//String对象用于保存字符串,也就是一组字符序列
String name="jack";
}
}
我们进入String的底层源码:
我们查看他的类图:
String实现了Serializable这个接口,说明String可以串行化,String对象是可以在网络上传输的。
String实现了Comparable这个接口,说明String对象可以相互比较大小。
String实现了CharSequence这个接口,实现字符序列。
我们查看String的构造器:
我们的设计如下所示:
package com.ypl.String;
public class String01 {
public static void main(String[] args) {
//1.String对象用于保存字符串,也就是一组字符序列
//2."jack"字符串常量,双引号括起的字符序列
//3.字符串的字符使用Unicode字符编码,一个字符(不区分字母还是汉字)占两个字节,char占两个字节
//4.String类有很多构造器,构造器的重载
//常用的有
//String s1=new String( );
//String s2=new String(String original);
//String s3=new String(char[] a);
//String s4=new String(char[] a,int startIndex,int count)
//String s5=new String(byte[] b)
//5.String类实现了接口Serializable(String可以串行化:可以在网络传输)
//6.String类实现了接口Comparable(String对象可以比较大小,利用compareTo方法。
//7.String类是final类,不能被其他的类继承
/**
* 8.String有属性 private final char value[];用于存放字符串内容
* 我们的字符串常量真正存储在String源码里面的:
* private final char value[];
* 在底层仍然是一个char数组。字符串本质仍然是一个char数组。
*/
//9.一定要注意:value是一个final类型,不可以修改(不是字符不可以修改,而是里面的地址不可以修改,即指向存储的地址不可以修改,
// 而所指向的地址存储的值是可以修改的):即value不能指向新的地址,但是单个字符内容是可以变化的。
final char value[] ={'a','b','c'};
char[] v2={'t','o','m'};
value[0]='H';
value=v2;
String name="jack";
}
}
为了更清晰的了解:
一定要注意:value是一个final类型,不可以修改(不是字符不可以修改,而是里面的地址不可以修改,即指向存储的地址不可以修改, // 而所指向的地址存储的值是可以修改的):即value不能指向新的地址,但是单个字符内容是可以变化的。
我们设计代码如下:
final char value[] ={'a','b','c'};
char[] v2={'t','o','m'};
value[0]='H';
value=v2;
//value=v2;不可以修改value地址
这样子是有错误的:
不能给value再分配一个新的变量
当我们修改代码如下所示时:
char value[] ={'a','b','c'};
char[] v2={'t','o','m'};
value[0]='H';
value=v2;
我们如下所示:
我们拿掉final发现是可以的了,final是为了约束不能指向一个新的空间。单独的是可以允许的。
创建String对象的两种方式:
(1)方式一:直接赋值String s="rgflfbc"
先从常量池查看是否有"rgflfbc"数据空间,如果有,直接指向;如果没有则重新创建,然后指向。s最终指向的是常量池的空间地址。
jvm的内存:String的内存布局
(2) 方式二:调用构造器 String s=new String("rgflfbc");
先在堆中创建空间,里面维护了value属性,指向常量池的rgflfbc空间。如果常量池没有"rgflfbc",重新创建,如果有,直接通过value指向。最终指向的是堆中的空间地址。
我们进行如下测试:
测试题1:
我们的代码如下所示:
package com.ypl.String;
public class StringExercise01 {
public static void main(String[] args) {
String a="abc";
String b="abc";
System.out.println(a.equals(b)); //true
System.out.println(a==b); //true
}
}
我们运行之后如下所示:
测试题2:
package com.ypl.String;
public class StringExercise01 {
public static void main(String[] args) {
String a1 =new String("abc");
String a2 =new String("abc") ;
System.out.println(a1.equals(a2)); //true
System.out.println(a1==a2); //false
}
}
运行之后如下所示:
测试题3:
我们的代码设计如下所示:
package com.ypl.String;
public class StringExercise02 {
public static void main(String[] args) {
String a="rgf"; //a指向常量池的"rgf"
String b=new String("rgf"); //b指向堆中的对象 value
System.out.println(a.equals(b)); //true
System.out.println(a==b); //false,a指向常量池,b指向堆中的对象 value。
System.out.println(a==b.intern());//查看intern方法,在API里面查看,返回true,在常量池里面查看是否有b对象的字符串,如果有则返回。
System.out.println(b==b.intern());b指向堆,即堆中的对象value,b.intern返回常量池的地址。
}
}
b.intern( )方法最终返回的是常量池的地址(对象)
我们运行之后如下所示:
我们通过菜鸟教程找到了中文版的API,intern方法的概述如下所示:‘
调用intern方法时,如果常量池已包含等于此String方法确定的String
对象的字符串(用equals(Object)方法确定),则返回常量池中的字符串。 否则,将此String
对象添加到池中,并返回对此String
对象的引用。
测试题4:
我们设计的代码如下所示:
package com.ypl.String;
public class StringExercise03 {
public static void main(String[] args) {
String s1="rgfedu";//指向常量池"rgfedu"
String s2="java";//指向常量池"java"
String s4="java";//指向常量池"java"
String s3=new String("java");//指向堆中对象
System.out.println(s2==s3);//false,s2指向常量池,s3指向堆
System.out.println(s2==s4);//true,都指向常量池
System.out.println(s2.equals(s3));//true,常量池里面的值相等
System.out.println(s1==s2 );//false,常量池里面的值不相等
}
}
我们运行之后如下所示:
测试题5:
我们设计的代码如下所示:
package com.ypl.String;
public class StringExercise04 {
public static void main(String[] args) {
Person p1=new Person();
p1.name="rgfedu";
Person p2 = new Person();
p2.name="rgfedu";
System.out.println(p1.name.equals(p2.name)); //true,两个值相等
System.out.println(p1.name== p2.name);//true,指向两个同一样的常量,p1.name和p2.name不是new的,而是直接给的常量值
//对象的属性都放在了堆里面。而name作为属性,他的常量值是放在常量池里面的,属性值为常量值的地址值
System.out.println(p1.name=="rgfedu");//true,与常量池里面的一样。
String s1 = new String("bcde");
String s2 = new String("bcde");
System.out.println(s1==s2);//都是new出来的,即堆里面的value指向常量池里面的同一个值。但是堆的地址不同,即为false.
}
}
class Person{
String name;
int age;
public Person() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
我们运行之后如下所示:
字符串的特性:
(1)String是一个final类,代表不可变的字符序列
(2)字符串是不可变的。一个字符串对象一旦被分配,其内容是不可变的。
我们思考如下问题:
(1)以下语句创建了几个对象?
String s1="hello";
s1="haha";
由于字符串是不可变得,所以我们刚开始创建的指向hello对象的s1,在创建haha对象之后,s1不再指向hello,而是指向haha。所以是创建了两个对象。
(2)
String a="hello"+"abc";
创建了几个对象?
String a="hello"+"abc"; //==>优化等价 String a="helloabc"
创建了一个对象。
分析:
1.编译器很聪明,做一个优化,判断创建的常量池对象,是否有引用指向
2.String a="hello"+"abc";==>String a="helloabc"
(3)
String a="hello"; //创建a对象
String b="abc"; //创建b对象
String c=a+b;
创建了几个对象?
我们通过具体的debug来进行查看:
1.
2. 我们step into追进去之后如下所示:
我们发现我们进入了StringBuilder的构造器里面了,第一步为先创建了一个StringBuilder sb=StringBuilder();sb是在堆中。
3.执行sb.append("hello");其中append是在原来字符串的基础上追加的
3.我们跳出去再继续追进去如下所示:
执行sb.append("abc");
4.我们再跳出去再追进去:
调用sb.toString()方法,
5.此时c的内容为helloabc了。
最后其实是c指向堆中的对象(String)value[ ]-->池中的”helloabc"
一共有三个对象,如上所示。
我们进行测试如下:
package com.ypl.String; public class StringExercise05 { public static void main(String[] args) { String a="hello"; //创建a对象 String b="abc"; //创建b对象 String d="helloabc"; String c=a+b; System.out.println(c==d); } }
运行之后如下所示:
重要规则:String ci="ab"+"cd";常量相加,看的是池。
String c1=a+b;变量相加,是在堆中。
测试如下所示:
package com.ypl.String; /** * String的实例化方式: * 方式一:通过字面量定义的方式 * 方式二:通过new +构造器的方式 * 常量与常量的拼接结果在常量池。且常量池中不会存在相同内容的常量 * 只要其中有一个是变量,结果就在堆中 * 如果拼接的结果调用intern()方法,返回值就在常量池中。 */ public class String02 { public static void main(String[] args) { String s1="javaEE"; String s2="hadoop"; String s3="javaEEhadoop"; //常量池值指向s3 String s4="javaEE"+"hadoop";// //变量名参与了此时就不在常量池了,而是在堆空间中。 String s5=s1+"hadoop"; String s6="javaEE"+s2; String s7=s1+s2; System.out.println(s3==s4);//true System.out.println(s3==s5);//false System.out.println(s3==s6);//false System.out.println(s5==s6);//false System.out.println(s3==s7);//false System.out.println(s5==s7);//false System.out.println(s6==s7);//false String s8 = s5.intern(); //返回值得到的s8使用的常量值中已经存在的”javaEEhadoop“ System.out.println(s3==s8);//true String s9 = s6.intern(); //返回值得到的s8使用的常量值中已经存在的”javaEEhadoop“ System.out.println(s3==s9);//true String s10 = s7.intern(); //返回值得到的s8使用的常量值中已经存在的”javaEEhadoop“ System.out.println(s3==s10);//true String ss1="javaEEhadoop"; String ss2="javaEE"; String ss3=ss2+"hadoop"; System.out.println(ss1==ss3); //false final String ss4="javaEE"; //加final时为常量,加常量时则在常量池里面(ss4:常量) String ss5=ss4+"hadoop"; System.out.println(ss1==ss5); //true } }
运行如下所示:
(4)
package com.ypl.String;
public class StringExercise06 {
public static void main(String[] args) {
String s1="rgfedu"; //s1指向常量池中的"rgfedu"。
String s2="java"; //s2指向常量池中的"java"。
String s5="rgfedujava"; //s5指向常量池中的"rgfedujava"。
String s6=(s1+s2).intern(); //s6指向池中的"rgfedujava"。
//调用intern方法时,如果池已包含等于此String方法确定的String对象的字符串(用equals(Object)方法确定),
// 则返回池中的字符串。否则,将此String对象添加到池中,并返回对此String对象的引用。
System.out.println(s5==s6); //true
System.out.println(s5.equals(s6)); //true
}
}
我们运行之后如下所示:
(5) 下列程序运行的结果是什么,
我们的代码如下所示:
package com.ypl.String;
public class StringExercise07 {
String str=new String("rgf");
final char[] ch={'j','a','v','a'};
public void change(String str,char ch[]) {
str="java";
ch[0]='h';
}
public static void main(String[] args) {
StringExercise07 ex=new StringExercise07();
ex.change(ex.str,ex.ch);
System.out.print(ex.str+" and "); //rgf
System.out.println(ex.ch);//hava
}
}
我们运行之后如下所示:
我们了解一些JVM的基本知识:
三种JVM:
Sun公司的HotSpot
BEA公司的JRockit
IBM公司的J9 VM
堆内示意图:
伊甸区 |
幸存0区 |
幸存1区 |
养老区 |
永久存储区 |
一个JVM实例只存在一个堆内存,堆内存的大小是可以调节的。类加载器读取了类文件后,需要把类、方法、常变量放到堆内存中,保存所有引用类型的真实信息,以方便执行器执行,堆内存分为三部分:
新生区、养老区、永久存储区(方法区)
虽然JVM规范将方法区描述为堆的一个逻辑部分,但它却还有一个别名叫做Non-Heap(非堆),目的就是要和堆分开。
JDK1.6的时候,常量池在方法区,JDK1.7的时候,常量池放在了堆里面。JDK1.8的时候,常量池又拿回到方法区了。
(6)
String类的常用方法:
String类是保存字符串常量的。每次更新都需要重新开辟空间,(创建对象,重新指向)效率极低,因此java设计者还提供了StringBuilder和StringBuffer来增强String的功能,并提高效率。
equals //区分大小写,判断内容是否相等
equalsIgnoreCase //忽略大小写的判断内容是否相等
length //获取字符的个数,字符串的长度
indexOf //获取字符在字符串中第1次出现的索引,索引从0开始,如果找不到,返回-1
lastIndexOf //获取字符在字符串中最后一次出现的索引,索引从0开始,如找不到,返回-1
substring //截取指定范围的子串
trim //去前后空格
charAt:获取某索引处的字符,注意不能使用Str[index]这种方式。
isEmpty():判断是否是空字符串
我们设计的代码如下所示:
package com.ypl.String;
public class StringMethod01 {
public static void main(String[] args) {
//1.equals,比较内容是否相等,区分大小写
String str1="hello";
String str2="Hello";
System.out.println(str1.equals(str2)); //false
//2.equalsIgnoreCase 忽略大小写的判断内容是否相等
String str3="hello";
String str4="Hello";
System.out.println(str3.equalsIgnoreCase(str4)); //true
String username="Rgf";
if("rgf".equalsIgnoreCase(username)){
System.out.println("Success!");
}else {
System.out.println("Failure");
}
//3.length 获取字符的个数,字符串的长度
System.out.println("rgf".length());//3
//4.indexOf获取字符在字符串对象中第一次出现的索引,索引从0开始,如果找不到,返回-1.
String s1="wer@ter";
int index=s1.indexOf('r');
System.out.println(index);//2
//5.lastIndexOf获取字符在字符串中最后一次出现的索引,索引从0开始,如果找不到,返回-1.
String s2="wer@ter";
int inde=s2.lastIndexOf('r');
System.out.println(inde);//6
//6.substring 截取指定范围的子串
String name="hello,rgf";
//从索引0开始截取2前面的内容
System.out.println(name.substring(0,2));
//从索引6开始截取后面所有的内容
System.out.println(name.substring(6)); //截取后面的字符
//7.charAt:获取某索引处的字符,注意不能使用Str[index]这种方式。
System.out.println(s1.charAt(0));
System.out.println(s1.charAt(6));
//8.isEmpty:判断是否是空字符串
/**
* public boolean isEmpty() {
* return value.length == 0;
* }
*/
System.out.println(s1.isEmpty());
//9.trim:返回字符串的副本,忽略前导空白和尾部空白
String s2=" he llo world ";
String s3 = s2.trim();
System.out.println(s2);
System.out.println(s3);
}
}
我们运行之后如下所示:
我们设计的代码如下所示:
//1.indexOf获取字符在字符串对象中第一次出现的索引,索引从0开始,如果找不到,返回-1.
String s1="wer@ter";
System.out.println("weIndexOf="+s1.indexOf("we")); //0
我们运行之后如下所示:
其他常用方法:
toUpperCase //转换成大写
toLowerCase //转换成小写
concat //将指定字符串连接到此字符串的结尾,等价于用“+”。
replace 替换字符串中的字符
split 分割字符串,对于某些分割字符,我们需要转义比如 | \\等
compareTo //比较两个字符串的大小
toCharArray //转换成字符数组
format //格式字符串,%s字符串 %c字符 %d 整型 %.2f浮点型
我们设计的代码如下所示:
package com.ypl.String;
public class StringMethod02 {
public static void main(String[] args) {
//1.toUpperCase ,转换成大写
String s="heLLo";
System.out.println(s.toUpperCase()); //HELLO
//2.toLowerCase,转换成小写
System.out.println(s.toLowerCase()); //hello
// 3.concat拼接字符串
String s1="宝玉";
s1=s1.concat("林黛玉").concat("薛宝钗").concat("together");
System.out.println(s1);
//4.replace替换字符串中的字符 ,replaceAll:替换所有
String s2="宝玉 and 薛宝钗 薛宝钗 薛宝钗";
//s2.replace()方法执行后,返回的结果才是替换过的。
//注意对s1没有任何影响
s2=s2.replace("薛宝钗","林黛玉");
System.out.println(s2);
//5.split分割字符串,对于某些分割字符,我们需要转义比如 | \\等
String poem="锄禾日当午,汗滴河下土,谁知盘中餐,粒粒皆辛苦";
//以,为标准对这首诗进行分割,就返回一个数组。
String[] split=poem.split(",");
System.out.println("==这首诗的内容是==");
for(int i=0;i<split.length;i++){
System.out.println(split[i]);
}
//在对字符串进行分割的时候,如果有特殊字符,需要加入转义符\ ,如下\\\\,\转义符 \ \转义符 \.
String poem1="E:\\aaa\\bbb";
String[] split1 = poem1.split("\\\\");
for (int j=0;j<split1.length;j++){
System.out.println(split1[j]);
}
//6.toCharArray 转换成字符数组
s="happy";
char[] chs = s.toCharArray();
for (int i=0;i<chs.length;i++){
System.out.println(chs[i]);
}
//7.compareTo 比较两个字符串的大小,如果前者大,则返回正数,后者大,则返回负数,如果相等,返回0
//(1)如果长度相同,并且每个字符也相同,就返回0.
//(2)如果长度相同或者长度不相同,但是在进行比较时可以区分大小,就返回的是
// if(c1!=c2){
//return c1-c2;
//}
//即从不相同的字符开始相减。
//(4)如果长度不同,如果前面的部分都相同,就返回str1.len-str2.len
String a="jchn";
String b="jack";
System.out.println(a.compareTo(b)); //返回值是'c'-'a'=2的值
//我们进行查看源码如下所示:
/*
public int compareTo(String anotherString) {
int len1 = value.length;
int len2 = anotherString.value.length;
int lim = Math.min(len1, len2);
char v1[] = value;
char v2[] = anotherString.value;
int k = 0;
while (k < lim) {
char c1 = v1[k];
char c2 = v2[k];
if (c1 != c2) {
return c1 - c2;
}
k++;
}
return len1 - len2;
}
*/
//8.format格式字符串
//占位符有:
// %s 字符串 %c 字符 %d 整型 %.2f浮点型
String name="john";
int age=10;
double score=98.3/3;
char gender='男';
//将所有信息都拼接在一个字符串
String info="我的姓名是"+name+"年龄是"+age+",成绩是"+score+"性别是"+gender+"。希望大家喜欢我";
System.out.println(info);
//(1)%s,%d,%.2f,%c称为占位符。
//(2)这些占位符由后面的变量来替换
//(3)%s表示后面由字符串来进行替换
//(4)%d是整数来替换
//(5)%.2f表示使用小数来替换,替换后只会保留小数点两位,并且进行四舍五入的处理。
//(6)%c使用char类型来替换
String info2=String.format("我的姓名是%s年龄是%d,成绩是%.2f性别是%c.希望大家喜欢我!",name,age,score,gender);
System.out.println("info="+info2);
}
}
我们运行之后如下所示:
我们继续演示其他方法:
package com.ypl.String;
public class StringMethod00 {
public static void main(String[] args) {
/**
* endsWith:测试此字符串是否以指定的后缀结束
* startsWith:测试此字符串是否以指定的前缀结束
* startsWith:测试此字符串从指定索引开始的子字符串是否以指定前缀开始
* contains:当且仅当此字符串包含指定的char值序列时,返回true
* matches:告知此字符串是否匹配给定的正则表达式。
*/
String str1="helloworld";
boolean b1 = str1.endsWith("ld");
System.out.println(b1);
boolean b2 = str1.startsWith("He");
System.out.println(b2);
boolean b3 = str1.startsWith("ll", 2);
System.out.println(b3);
String str2="wo";
System.out.println(str1.contains(str2));
String str3="hellorworld";
System.out.println(str3.lastIndexOf("or"));
System.out.println(str3.lastIndexOf("or",5));
//什么情况下,indexof(str)和lastIndexof(str)返回值相同?
//情况一:存在唯一的一个str,情况二:不存在str.
String str4="12345";
//判断str4字符串中是否全部有数字组成,即有1-n个数字组成
boolean matches = str4.matches("\\d+");
System.out.println(matches);
}
}
运行之后如下所示:
Java比较器
在java中经常会涉及到对象数组的排序问题,那么就涉及到对象之间的比较问题
java实现对象排序的方式有两种:
自然排序:java.lang.Comparable
定制排序:java.util.Comparator
自然排序:
package com.ypl.data_; import org.junit.jupiter.api.Test; import java.util.Arrays; /** * 一、说明:Java中的对象,正常情况下,只能进行比较操作:==或!=,不能使用>或<的。 * 但是在开发场景中,我们需要对多个对象进行排序,言外之意,就需要比较对象的大小。 * 如何实现?使用两个接口中的任何一个:Comparable 或 Comparator * 二、Comparable接口的使用 * * */ public class CompareTest { /* Comparable接口的使用举例:自然排序 1.像String、包装类等实现了Comparable接口,重写了compareTo(obj)方法的,给出了比较两个对象大小的方式 2.像String、包装类重写重写了compareTo(obj)方法以后,进行了从小到大的排列。 3.重写compareTo(obj)的规则: 如果当前对象this大于形参对象obj,则返回正整数, 如果当前对象this小于形参对象obj,则返回负整数 如果当前对象this等于形参对象obj,则返回零 4.对于自定义类来说,如果需要排序,我们可以让自定义类实现Comparable接口,重写compareTo(obj), 在compareTo(obj)方法中指明如何排序。 源码如下所示: public int compareTo(String anotherString) { int len1 = value.length; int len2 = anotherString.value.length; int lim = Math.min(len1, len2); char v1[] = value; char v2[] = anotherString.value; int k = 0; while (k < lim) { char c1 = v1[k]; char c2 = v2[k]; if (c1 != c2) { return c1 - c2; } k++; } return len1 - len2; } */ public static void main(String[] args) { String[] arr=new String[]{"AA","CC","KK","MM","GG","JJ","DD"}; Arrays.sort(arr); //String实现了Comparable接口,实现该接口要重写compareTo方法,sort底层为compareTo方法。 System.out.println(Arrays.toString(arr)); } @Test public void test2(){ Goods[] arr = new Goods[5]; arr[0]=new Goods("lenovoMouse",34); arr[1]=new Goods("dellMouse",43); arr[2]=new Goods("XiaomiMouse",12); arr[3]=new Goods("huaweiMouse",65); arr[4]=new Goods("MicrosoftMouse",43); Arrays.sort(arr); System.out.println(Arrays.toString(arr)); } }
package com.ypl.data_; /** * 商品类 */ public class Goods implements Comparable{ private String name; private double price; public Goods() { } public Goods(String name, double price) { this.name = name; this.price = price; } public String getName() { return name; } public void setName(String name) { this.name = name; } public double getPrice() { return price; } public void setPrice(double price) { this.price = price; } @Override public String toString() { return "Goods{" + "name='" + name + '\'' + ", price=" + price + '}'; } //指明商品比较大小的方式:按照价格从低到高排序,再按照产品名称从高到低排序 @Override public int compareTo(Object o) { if(o instanceof Goods){ Goods goods=(Goods) o; //方式一: if(this.price>goods.price){ return 1; }else if(this.price<goods.price){ return -1; }else { // return 0; return this.name.compareTo(goods.name); } //方式二: // return Double.compare(this.price,goods.price); } throw new RuntimeException("传入的数据类型不一致!"); } }
运行之后如下所示:
![]()
常见算法题目:
1.模拟一个trim方法,去除字符串两端的空格:
2.将一个字符串进行反转。将字符串中指定部分进行反转。比如“abcdefg"反转为”abfedcg"
3.获取一个字符串在另一个字符串中出现的次数。
比如:获取“ab"在”abkkcadkabkebfkabkskab"中出现的次数(indexof、contence|substring)
4.获取两个字符串中最大相同子串。比如:
str1="abcwerthelloyuiodef",str2="cvhellobnm"
提示:将短的那个串进行长度依次递减的子串与较长的串比较
5.对字符串中字符进行自然顺序排序
提示:
(1)字符串变成字符数组
(2)对数组排序,选择,冒泡,Arrays.sort();
(3)将排序后的数组变成字符串。