第六章常用对象API
6.1 String类
6.1.1 创建String对象
1、创建方式:
⑴、String s ="aaa";
privatestaticvoid stringDemo_1() {
String s = "aaa";
String s2 = "aaa";
//ture的原因:他们同处常量池中,常量池的特点是池中没有就创建,如果有的话,就直接使用。
Sop(s==s2);
}
⑵、String s1 = newString("aaa");
privatestaticvoid stringDemo_2(){
//创建一个字符串,并把它存储在字符串常量池中
Strings = "aaa";
//在内存中创建两个对象一个new对象一个字符串对象
Strings1 = new String("aaa");
//false的原因:俩个变量处在不同的空间中,
Sop(s==s1);
Sop(s.equals(s1));
//ture的原因:String类中的equals复写Objects中的equals建立string类中自己的字符串对象的判断依据,其实比较的是字符串的内容
}
⑶、s与s1的区别(面试题):s在内存中只有一个对象,s2在内存中有两个对象。
2、特点:字符串对象一旦初始化就不会被改变
{
String str = "aaa";
str = "bbb";
System.out.println(str);
}
打印结果是:bbb,这并不意味着字符串对象改变了,只是str指向bbb对象罢了。
3、构造方法
String类的构造方法可以将数组变成字符串,但只能变俩种byte和char
⑴、将字节数组转成字符串
publicstaticvoid stringConstructor1() {
Strings = new String();
//等效于String s = "";但不等于Strings = null
------------------------------------------------
//将字节数组转成字符串
byte[] arr = {66,67,68,69};
String s1 = new String(arr);
Sop(s1);
String s2= new String(arr,1,3);
Sop(s2);
}
⑵、将字符数组转成字符串
publicstaticvoidstringConstructor2() {
char[] ch = {'h','u','f','e','i'};
Sop(ch);
String s2= new String(ch,2,3);
Sop(s2);
}
6.1.2 常见功能
1、获取
⑴、获取字符串中字符的长度:int length();与数组中的length是不一样的,数组中的length是指数组的属性,在这是一个方法。
Strings = "dfakjfkjfiwe";
Sop("length="+s.length());
⑵、根据位置获取字符: char charAt(int index)
String s = "dfakjfkjfiwe";
Sop("第五个位置是:"+s.charAt(4)); //注意StringIndexOutOfBoundsException
⑶、根据字符获取在字符串中第一次出现的位置(重点)
①、根据字符或数字获取位置:int indexOf(int ch);
String s = "dfakjfkjfiwe";
Sop("j是指针式:"+s.indexOf("j"));
②、从指定的位置开始索引,查找第一次出现的位置:int indexOf(int ch,int fromIndex);
String s = "dfakjfkjfiwe";
Sop("j是指针式:"+s.indexOf("j",5));
③、根据字符串获取在字符串中第一次出现的位置
intindexOf(String str);
int indexOf(String str,int fromIndex);
int lastIndexOf(int ch);
int lastIndexOf(int ch,int fromIndex);
int lastIndexOf(String str);
int lastIndexOf(String str,int fromIndex);
String s = "dfakjfkjfiwe";
Sop("lastIndexOf:"+s.lastIndexOf('a'));
//indexOf()的功能有俩:一是根据字符获取在字符串中第一次出现的位置,二是判断字符是否存在,我们可以根据-1,来判断字符或者字符串是否存在
⑷获取字符串中的一部分,也称子串
StirngsubString(int beginIndex,int endIndex);包含起始,不包含结束
StirngsubString(int beginIndex)
String s = "dfakjfkjfiwe";
Sop("subString:"+s.substring(2,6));
2、转换
⑴、将字符串变成字符串数组(字符串切割): String[]split(String regex);涉及正则表达式
String s = "dfakjfkjfiwe";
String[] str = s.split("j");
for(String st : str) {
Sop(st);
}
⑵、将字符串变成字符数组:char[] toCharArray();
String s = "dfakjfkjfiwe";
char[] ch = s.toCharArray();
for (int i = 0; i < ch.length; i++) {
Sop(ch[i]);
}
⑶、将字符串转成字节数组:byte[] getBytes();
byte[] b = s.getBytes();
for (int i = 0; i < b.length; i++) {
Sop(b[i]);
}
⑷、将字符串中的字母转成大小写:String toUpperCase(),String toLowerCase()
⑸、 将字符串中的内容替换:
①、Stringreplace(char oldch, char newch);
②、String replace(String s1,String s2);
⑹、去除字符串两端的空格去掉:String trim();
⑺、将字符串进行连接:String concat();
⑻、将基础数据类型转换成字符串:String valueOf(),valueOf()是静态方法
3、判断
⑴、两个字符内容是否相同
①、booleanequals(Object obj);
②、boolean equalsIgnoreCase(String str);
String s = " dfakjfkjfiwe ";
Sop(s.trim().equals("DFAKJFKJFIWE".toLowerCase()));
Sop(s.trim().equalsIgnoreCase("DFAKJFKJFIWE"));
⑵、字符串中是否包含指定字符串:boolean contains(Stringstr)
⑶、字符串是否以指定的字符串开头、结束
boolean startsWith(string);
boolean endsWith(string);5
String str = "JudgeFunction.java";
Sop(str.startsWith("Judge"));
Sop(str.endsWith(".java"));
Sop(str.contains("Function"));//字符串是否包含指定字符
4、比较:int compareTo():按字典顺序比较。返回结果之所以是int,因为返回的结果有三种形式:正数、负数或0;
Sop("adf".compareTo("asdf"));
//基本数据类型用比较运算符比较,对象要用方法比较
5、intern方法
当调用 intern 方法时,如果池已经包含一个等于此 String 对象的字符串(equals(Object) 方法确定),则返回池中的字符串。否则,将此 String 对象添加到池中,并返回此 String 对象的引用。
publicclassInternFunction {
publicstaticvoid main(String[] args) {
String s1 =new String("aaa");
String s2 = s1.intern();
Sop(s1==s2);
}
}
6.1.3StringBuffer类
1、StringBuffer:就是字符串缓冲区,用于存储数据,其实是对数组的封装
2、特点
⑴、长度可变,其原理是当长度不够用时,重新创建一个更长的数组,将原来的数据遍历,放在新的数组中,然后在他的后面加入新的数据
⑵、可以存储不同类型数据
⑶、最终要转成字符串进行使用
⑷、可以对字符串进行修改
3、功能:
⑴、添加:append(data),尾部追加,除了byte、short,其他都能添加
privatestaticvoid bufferMethodDemo_1() {
//创建缓冲区对象
StringBuffer sb =new StringBuffer();
//构造一个其中不带字符的字符串缓冲区,其初始容量为 16 个字符。
StringBuffer s =sb.append(5).append(6);//方法调用链
s.insert(1,"dfa");//
Sop(sb);
Sop(s);
}
⑵、删除: delete(start,end)//name.delete(1,name.length())清空缓冲区
deleteCharAt(int index)//删除指定位置的元素
⑶、插入:insert(index,data)
⑷、查找:charAt(index)
indexOf(String)
lastIndexOf(String)
⑸、修改: StringBuffer replace(start,end,String)
void setCharAt(index,char)
void setLength(length)
StringBuffer reverse()
6.1.4StringBuilder类
Jdk1.5以后出现了功能和StringBuffer一样的,不同的是StringBuffer是同步的,而StringBuilder是线程不同步的。
StringBuffer之所以是同步的,是因为
class StringBuffer {
Object lock = new Object();
public StringBuffer append(int x){
synchronized(lock){
}
returnnull;
}
public StringBuffer delete(int x){
synchronized(lock){
}
returnnull;
}
将StringBuilder
的实例用于多个线程是不安全的。如果需要这样的同步,则建议使用StringBuffer
。它的出现是为了提高效率。Jdk升级的目的:简化书写,提高效率,增加安全性
6.1.5String与StringBuilder
或
StringBuffer的区别
publicclassStringBuilderDemo {
publicstaticvoidmain(String[] args) {
int[] arr = {3,3,4,1,6,5};
Strings = arrayToString(arr);
//这俩个方法的区别是上面的每遍历一遍就会在字符串常量池中增加一个成员,效率低
//下面的的是不断的向缓冲区装,然后一次性输出
Strings2 = arrayToString_2(arr);
Sop(s);
Sop(s2);
}
publicstaticString arrayToString_2(int[]arr) {
StringBuildersb = new StringBuilder();
sb.append("[");
for (int i = 0; i < arr.length; i++) {
if(i!=arr.length-1)
sb.append(arr[i]+", ");
else
sb.append(arr[arr.length-1]+"]");
}
return sb.toString();
}
publicstaticString arrayToString(int[] arr) {
Stringstr = "[";
for (int i = 0; i < arr.length; i++) {
if(i!=arr.length-1){
str+=arr[i]+", ";
}else
str+= arr[arr.length-1]+"]";
}
return str;
}
}
6.1.6 基本数据类型包装类
1、由来:为了方便操作基本数据类型值,将其封装成了对象,在对象中定义了属性和行为丰富了该数据的操作。用于描述该对象的类就称为基本数据类型包装类
2、基本类型与对应的包装类
byte Byte shortShort int Integer Long Long float Float
double Double char Character boolean Boolean
例:int num = 1;
Interger i= new Interger(num);//将基本数据类型转换成包装类
3、应用:基本数据类型和字符串之间的转换
⑴、基本类型 -à 字符串
①、基本类型数据+””
②、用String类中的静态方法valueOf(基本类型数值);
③、基本数据类型.toString(基本数据类型)
⑵、字符串 -à 基本类型
①、使用包装类中的静态方法xxx parseXxx(“xxx类型的字符串”);只有character没有parse方法,因为他封装的就是字符。
②、如果字符串被Integer进行对象的封装,可以使用intValue()将该对象转换成基本数据类型值,即intValue()方法是非静态的,这是它与parseInt()方法的区别。
③、用Integer类中的静态方法valueOf(基本类型数值);
4、常见方法
⑴parseInt:将字符串参数作为有符号的十进制整数进行解析
例:Sop(Integer.parseInt(“213”)+1);
5、进制转换
⑴十进制——>其他进制
Integer.toBinaryString(int);
Integer.toOctalString(int);
Integer.toHexString(int);
Integer.toString(int,想要转换的进制);//自定义的进制转换
⑵其他进制——>十进制
Integer. parseInt(”String”,radix);
int x = Integer.parseInt("3C",16);
6、自动装箱拆箱
⑴自动装箱
例:int num = 4;
Integeri = 4;
⑵自动拆箱
例:int num = 4;
num =num + 5;
Integer i = 4;
i = i + 5
面试题:
Integer x =127;
Integer y =127;
Sop(x==y);//true
Sop(x.equals(y));//true
--------------------------------------------------------------
Integer x = 128;
Integer y =128;
Sop(x==y);false
Sop(x.equals(y));true
结果不一样的原因:jdk1.5以后,自动装箱,如果装箱的是一个字节,那么该数据会被共享,而不会开辟新的空间。
目标九:2013/10/20
6.2 泛型
1、简介
Generics 或Generic type :泛型 jdk1.5出现的的安全机制,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法。
Generics允许对类型进行抽象(abstract over types)。最常见的例子是集合类型(Containertypes),Collection的类树中任意一个即是。
下面是那种典型用法:
ListmyIntList = new LinkedList();// 1
myIntList.add(new Integer(0));// 2
Integerx = (Integer) myIntList.iterator().next();// 3
第3行的类型转换有些烦人。通常情况下,程序员知道一个特定的list里边放的是什么类型的数据。但是,这个类型转换是必须的(essential)。编译器只能保证iterator返回的是Object类型。为了保证对Integer类型变量赋值的类型安全,必须进行类型转换。
当然,这个类型转换不仅仅带来了混乱,它还可能产生一个运行时错误(runtime error),因为程序员可能会犯错。
程序员如何才能明确表示他们的意图,把一个list中的内容限制为一个特定的数据类型呢?这是generics背后的核心思想。这是上面程序片断的一个泛型版本:
List<Integer> myIntList = newLinkedList<Integer>(); // 1
myIntList.add(new Integer(0)); // 2s
Integer x = myIntList.iterator().next();// 3
2、List<E>类定义和List<Integer>类引用中涉及的术语:
①、整个称为List<E>泛型类型
②、E称为类型变量或类型参数
③、整个List<Integer>称为参数化的类型
④、List<Integer>中Integer称为类型参数的实际或实例类型参数
⑤、List<Integer>中的<>念typeof,即Listtypeof Integer
⑥、List称为原始类型
3、泛型和子类继承
下面的代码片断合法么?
List<String> ls = new ArrayList<String>(); //1
List<Object> lo = ls; //2
第1行当然合法,但是这个问题的狡猾之处在于第2行。
这产生一个问题:
一个String的List是一个Object的List么?大多数人的直觉是回答:“当然!”。
好,在看下面的几行:
lo.add(new Object()); // 3
String s = ls.get(0); // 4: 试图把Object赋值给String
这里,我们使用lo指向ls。我们通过lo来访问ls,一个String的list。我们可以插入任意对象进去。结果是ls中保存的不再是String。当我们试图从中取出元素的时候,会得到意外的结果。
Java编译器当然会阻止这种情况的发生。第2行会导致一个编译错误。
总之,如果Foo是Bar的一个子类型(子类或者子接口),而G是某种泛型声明,那么G<Foo>是G<Bar>的子类型并不成立!!
4、好处
⑴、将运行时期ClassCastException异常转换到编译时期
⑵、避免了强制转换的麻烦
5、应用
当操作的引用数据类型不确定时,就使用<>,将要操作的引用数据类型传入即可。其实<>就是一个用于接收具体引用数据类型的参数范围。
参数化类型与原始类型的兼容性:两者可以互相引用,但编译器会报告警告。
参数化类型不考虑类型参数的继承关系。例如Vector<String> v = new Vector<Object>();Vector<Object> v = new Vector<String>();这两者都是错误的。
思考:下面代码会报错吗?
Vector v = newVector<String>();
Vector<Object>v1 = v;
答案:不会,编译器会顺序执行,参数化类型与原始类型是兼容的。不要把两行代码看成一句代码。
6、泛型的擦出和补偿
⑴、在程序中,只要带有<>的类或接口,就要明确传入的具体引用数据类型。泛型技术是给编译器使用的技术,用于编译时期,确保了类型的安全。运行时,将泛型去掉,生成的class文件中是不带泛型的,这个称之为泛型的擦出。
其原因是运行时,虚拟机会启动类加载器,只在编译时期进行检查,如果检查完了,类是安全的,那么就可以把泛型去掉,因为类型已经统一了,但类加载器没有升级,只能按原来的规则运行。
⑵、泛型的补偿:在运行时,通过获取元素类型进行转换动作,不用使用者再强制转换了。
⑶、利用反射证明
publicclass GenericsTest {
publicstaticvoidmain(String[] args) throws Exception {
//如何做到al变动,下面相关的al也跟着变。先选中al,然后点右键,选中重构,再选中rename
ArrayList<String> al = new ArrayList<String>();
al.add("aadjf");
al.add("aadjf");
al.add("aadjf");
//以此证明泛型的擦除,
al.getClass().getMethod("add",Object.class).invoke(al,1);
Iterator it = al.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
}
}
7、什么时候用?当类中操作的引用数据类型不确定时,就使用泛型来表示
8、泛型类:
9、泛型方法:
⑴、将泛型定义在方法上
public <Q> void show(Q string) {
Sop("show:" + string);
}
⑵、静态方法的泛型
当方法静态时,不能像上面的println方法一样访问类上定义的泛型,如果静态方法要使用泛型,只能在方法上定义泛型。
错误代码
publicstatic void print1n(W str) {
Sop("print" + str);
}
正确代码
publicstatic<E> void print1n(E str) {
Sop("print" + str);
}
注意,我们并没有传送真实类型参数(actual type argument)给一个泛型方法。编译器根据实参为我们推断类型参数的值。它通常推断出能使调用类型正确的最明确的类型参数
10、泛型接口
publicclass GenericDemo4 {
publicstaticvoidmain(String[] args) {
InterImpl i = new InterImpl();
i.show("yaner");
InterImpl2<Integer> i2 = new InterImpl2<Integer>();
i2.show(22);
}
}
// 泛型接口:将泛型定义在接口上
interface Inter<T> {
publicvoid show(T t);
}
class InterImpl implements Inter<String> {
publicvoid show(String t) {
Sop("My wife'name is " + t);
}
}
class InterImpl2<S> implements Inter<S> {
publicvoid show(S t) {
Sop("Her age is " + t);
}
}
11、通配符
⑴、由来
考虑写一个例程来打印一个集合(Collection)中的所有元素。下面是在老的语言中你可能写的代码:
void printCollection(Collection c) {
Iterator i = c.iterator();
for (int k = 0; k < c.size(); k++) {
System.out.println(i.next());
}
}
下面是一个使用泛型的幼稚的尝试(使用了新的循环语法):
void printCollection(Collection<Object>c) {
for (Object e : c) {
System.out.println(e);
}
}
问题是新版本的用处比老版本小多了。老版本的代码可以使用任何类型的collection作为参数,而新版本则只能使用Collection<Object>,我们刚才阐述了,它不是所有类型的collections的父类。
那么什么是各种collections的父类呢?它写作: Collection<?>(发音为:"collection ofunknown"),就是,一个集合,它的元素类型可以匹配任何类型。显然,它被称为通配符。我们可以写:
void printCollection(Collection<?> c) {
for (Object e : c) {
System.out.println(e);
}
}
现在,我们可以使用任何类型的collection来调用它。注意,我们仍然可以读取c中的元素,其类型是Object。这永远是安全的,因为不管collection的真实类型是什么,它包含的都是objects。但是将任意元素加入到其中不是类型安全的:
Collection<?> c = new ArrayList<String>();
c.add(new Object()); // 编译时错误
因为我们不知道c的元素类型,我们不能向其中添加对象。
add方法有类型参数E作为集合的元素类型。我们传给add的任何参数都必须是一个未知类型的子类。因为我们不知道那是什么类型,所以我们无法传任何东西进去。唯一的例外是null,它是所有类型的成员。
另一方面,我们可以调用get()方法并使用其返回值。返回值是一个未知的类型,但是我们知道,它总是一个Object,因此把get的返回值赋值给一个Object类型的对象或者放在任何希望是Object类型的地方是安全的。
⑵、通配符使用上限
①、上限的体现:?extends E:接收的是E类型或其子类的对象,上限。之所以这样设计, 提高了扩展性,
②、上限的应用:一般在定义存储元素时,用上限
⑶、通配符使用下限
①、下限的体现?super S;接收的是S类型或其父类的对象,下限。
②、下限的应用;通常对集合中的元素进行取出操作时,可以下限。
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
publicclass GenericAdvanceDemo2 {
//需求:输入自己定义的泛型。
publicstaticvoidmain(String[] args) {
ArrayList<Student> arr = new ArrayList<Student>();
arr.add(new Student("yaner", 23));
arr.add(new Student("hufei", 24));
LinkedList<Worker> arr2 = new LinkedList<Worker>();
arr2.add(new Worker("zhangsan", 11));
arr2.add(new Worker("lisi", 41));
iteratorCollection(arr);
iteratorCollection(arr2);
}
publicstaticvoiditeratorCollection(Collection<? extendsPerson> arr){
//Collection<Person>加入直接改成Person会出现错误,因为左右泛型不匹配,即只能接受Person对象。那该怎么办?将Person改为?extends Person,即设定泛型的上限定其实 “?”就是“? extends Object”
Iterator<? extends Person> it = arr.iterator();
while (it.hasNext()) {
Person p = it.next();
Sop(p.getName() + ":" + p.getAge());
}
}
}