一、String类、StringTokenizer类
1.实验要求
String、StringTokenizer:能用正则表达式来分离字符串,能否统计一篇短文中单词?能否分离出C程序中的单词?
注:该问题回答参考于博客String、StringBuffer与StringBuilder之间区别
2.程序与测试
程序一
代码:
package com.dreamchaser.work3; public class String_test { public static void main(String[] args) { String s="Hooray! It's snowing! It's time to make a snowman.James runs out. He makes a big pile of snow. He puts a big snowball on top. He adds a scarf and a hat. He adds an orange for the nose. He adds coal for the eyes and buttons.In the evening, James opens the door. What does he see? The snowman is moving! James invites him in. The snowman has never been inside a house. He says hello to the cat. He plays with paper towels.A moment later, the snowman takes James's hand and goes out.They go up, up, up into the air! They are flying! What a wonderful night!The next morning, James jumps out of bed. He runs to the door.He wants to thank the snowman. But he's gone. "; System.out.println("这篇短文共有"+s.split("[^a-zA-Z]").length+"个单词!"); }
运行结果:
代码解读:
利用正则表达式将字符串分割为所需的数组,然后统计其长度即可知道单词的个数。
程序二
代码:
package com.dreamchaser.work3; public class String_test1 { public static void main(String[] args) { //代码1 String sa = new String("Hello world"); String sb = new String("Hello world"); System.out.println(sa == sb); // false //代码2 String sc = "Hello world"; String sd = "Hello world"; System.out.println(sc == sd); // true } }
运行结果:
代码解读:
代码1中局部变量sa,sb中存储的是JVM在堆中new出来的两个String对象的内存地址。虽然这两个String对象的值(char[]存放的字符序列)都是"Hello
world"。 因此"=="比较的是两个不同的堆地址。代码2中局部变量sc,sd中存储的也是地址,但却都是常量池中"Hello
world"指向的堆的唯一的那个拘留字符串对象的地址 。自然相等了。
程序三
代码:
package com.dreamchaser.work3; public class String_test2 { public static void main(String[] args) { //代码1 String sa = "ab"; String sb = "cd"; String sab = sa + sb; String s = "abcd"; System.out.println(sab == s); // false //代码2 String sc = "ab" + "cd"; String sd = "abcd"; System.out.println(sc == sd); //true } }
运行结果:
代码解读:
代码1中局部变量sa,sb存储的是堆中两个拘留字符串对象的地址。而当执行sa+sb时,JVM首先会在堆中创建一个StringBuilder类,同时用sa指向的拘留字符串对象完成初始化,然后调用append方法完成对sb所指向的拘留字符串的合并操作,接着调用StringBuilder的toString()方法在堆中创建一个String对象,最后将刚生成的String对象的堆地址存放在局部变量sab中。而局部变量s存储的是常量池中"abcd"所对应的拘留字符串对象的地址。
sab与s地址当然不一样了。这里要注意了,代码1的堆中实际上有五个字符串对象:三个拘留字符串对象、一个String对象和一个StringBuilder对象。
代码2中"ab"+“cd"会直接在编译期就合并成常量"abcd”,
因此相同字面值常量"abcd"所对应的是同一个拘留字符串对象,自然地址也就相同。
程序四
代码:
package com.dreamchaser.work3; import java.util.StringTokenizer; public class StringTokenizer_test { public static void main(String[] args) { String s="Hooray! It's snowing! It's time to make a snowman.James runs out. He makes a big pile of snow. He puts a big snowball on top. He adds a scarf and a hat. He adds an orange for the nose. He adds coal for the eyes and buttons.In the evening, James opens the door. What does he see? The snowman is moving! James invites him in. The snowman has never been inside a house. He says hello to the cat. He plays with paper towels.A moment later, the snowman takes James's hand and goes out.They go up, up, up into the air! They are flying! What a wonderful night!The next morning, James jumps out of bed. He runs to the door.He wants to thank the snowman. But he's gone. "; StringTokenizer stringTokenizer=new StringTokenizer(s,"[^a-zA-Z]"); System.out.println("这篇短文共有"+stringTokenizer.countTokens()+"个单词!"); } }
运行结果:
代码解读:
利用StringTokenizer来统计一篇文章的单词个数
二、StringTokenizer类
1.实验要求
利用StringTokenizer来统计一篇文章的单词个数
2.程序与测试
程序一
代码:
package com.dreamchaser.work3; public class StringBuffer_test { public static void main(String[] args) { StringBuffer stringBuffer=new StringBuffer(); StringBuffer stringBuffer1; StringBuffer stringBuffer2; stringBuffer.append("I"); stringBuffer.append(" have"); stringBuffer1=stringBuffer; stringBuffer.append(" a"); stringBuffer.append(" dream!"); stringBuffer2=stringBuffer; System.out.println(stringBuffer); System.out.println("两者指针是否是一个"); if (stringBuffer1==stringBuffer2){ System.out.println("是一个!"); }else{ System.out.println("不是一个!"); } } }
运行结果:
代码解读:
使用StringBuffer的常用方法,同时测试了对象指针问题。
程序二
代码:
//String public final class String { private final char value[]; public String(String original) { // 把原字符串original切分成字符数组并赋给value[]; } } //StringBuffer public final class StringBuffer extends AbstractStringBuilder { char value[]; //继承了父类AbstractStringBuilder中的value[] public StringBuffer(String str) { super(str.length() + 16); //继承父类的构造器,并创建一个大小为str.length()+16的value[]数组 append(str); //将str切分成字符序列并加入到value[]中 }
代码解读:
至于两者的区别,简单地说,就是一个变量和常量的关系。
StringBuffer对象的内容可以修改;而字符串对象一旦产生后就不可以被修改,重新赋值其实是两个对象。
很显然,String和StringBuffer中的value[]都用于存储字符序列。但是,
(1)String中的是常量(final)数组,只能被赋值一次。 比如:new
String(“abc”)使得value[]={‘a’,‘b’,‘c’}(查看jdk String就是这么实现的),之后这个String对象中的value[]再也不能改变了。这也正是大家常说的,String是不可变的原因 。
(2)StringBuffer中的value[]就是一个很普通的数组,而且可以通过append()方法将新字符串加入value[]末尾。这样也就改变了value[]的内容和大小了。
总结:讨论String和StringBuffer可不可变。本质上是指对象中的value[]字符数组可不可变,而不是对象引用可不可变。
三、Math类、BigInteger类和Random类
1.实验要求
了解熟悉Math、BigInteger和Random的用法
2.程序与测试
程序一
代码:
package com.dreamchaser.work3; public class Math_test { public static void main(String[] args) { System.out.println(); // Math.sqrt()//计算平方根 Math.cbrt()//计算立方根 Math.hypot(x,y)//计算 (x的平方+y的平方)的平方根 System.out.println( "Math.sqrt(16)----:" + Math.sqrt(16));//4.0 System.out.println( "Math.cbrt(8)----:" + Math.cbrt(8));//2.0 System.out.println( "Math.hypot(3,4)----:" + Math.hypot(3, 4));//5.0 // Math.pow(a,b)//计算a的b次方 Math.exp(x)//计算e^x的值 System.out.println( "------------------------------------------"); System.out.println( "Math.pow(3,2)----:" + Math.pow(3, 2));//9.0 System.out.println( "Math.exp(3)----:" + Math.exp(3));//20.085536923187668 //Math.max();//计算最大值 Math.min();//计算最小值 System.out.println( "------------------------------------------"); System.out.println( "Math.max(2.3,4.5)----:" + Math.max(7, 15));//15 System.out.println( "Math.min(2.3,4.5)----:" + Math.min(2.3, 4.5));//2.3 //Math.abs求绝对值 System.out.println( "------------------------------------------"); System.out.println( "Math.abs(-10.4)----:" + Math.abs(-10.4));//10.4 System.out.println( "Math.abs(10.1)----:" + Math.abs(10.1));//10.1 //Math.ceil天花板的意思,就是返回大的值 System.out.println( "------------------------------------------"); System.out.println( "Math.ceil(-10.1)----:" + Math.ceil(-10.1));//-10.0 System.out.println( "Math.ceil(10.7)----:" + Math.ceil(10.7));//11.0 System.out.println( "Math.ceil(-0.7)----:" + Math.ceil(-0.7));//-0.0 System.out.println( "Math.ceil(0.0)----:" + Math.ceil(0.0));//0.0 System.out.println( "Math.ceil(-0.0)----:" + Math.ceil(-0.0));//-0.0 System.out.println( "Math.ceil(-1.7)----:" + Math.ceil(-1.7));//-1.0 //Math.floor地板的意思,就是返回小的值 System.out.println( "------------------------------------------"); System.out.println( "Math.floor(-10.1)----:" + Math.floor(-10.1));//-11.0 System.out.println( "Math.floor(10.7)----:" + Math.floor(10.7));//10.0 System.out.println( "Math.floor(-0.7)----:" + Math.floor(-0.7));//-1.0 System.out.println( "Math.floor(0.0)----:" + Math.floor(0.0));//0.0 System.out.println( "Math.floor(-0.0)----:" + Math.floor(-0.0));//-0.0 //Math.random 取得一个大于或者等于0.0小于不等于1.0的随机数[0,1) System.out.println( "------------------------------------------"); System.out.println( "Math.random()----:" + Math.random());//输出[0,1)间的随机数 0.8979626325354049 System.out.println( "Math.random()*100----:" + Math.random() * 100);//输出[0,100)间的随机数 32.783762836248144 // Math.rint 四舍五入 返回double值 System.out.println( "------------------------------------------"); System.out.println( "Math.rint(10.1)----:" + Math.rint(10.1));//10.0 System.out.println( "Math.rint(10.7)----:" + Math.rint(10.7));//11.0 System.out.println( "Math.rint(-10.5)----:" + Math.rint(-10.5));//-10.0 System.out.println( "Math.rint(-10.51)----:" + Math.rint(-10.51));//-11.0 System.out.println( "Math.rint(-10.2)----:" + Math.rint(-10.2));//-10.0 System.out.println( "Math.rint(9)----:" + Math.rint(9));//9.0 //Math.round 四舍五入 float时返回int值,double时返回long值 System.out.println( "------------------------------------------"); System.out.println( "Math.round(10.1)----:" + Math.round(10.1));//10 System.out.println( "Math.round(10.7)----:" + Math.round(10.7));//11 System.out.println( "Math.round(-10.5)----:" + Math.round(-10.5));//-10 System.out.println( "Math.round(-10.51)----:" + Math.round(-10.51));//-11 System.out.println( "Math.round(-10.2)----:" + Math.round(-10.2));//-10 System.out.println( "Math.round(9)----:" + Math.round(9));//9 //Math.nextUp(a) 返回比a大一点点的浮点数 System.out.println( "------------------------------------------"); System.out.println( "Math.nextUp(1.2)----:" + Math.nextUp(1.2));//1.2000000000000002 //Math.nextDown(a) 返回比a小一点点的浮点数 System.out.println( "------------------------------------------"); System.out.println( "Math.nextDown(1.2)----:" + Math.nextDown(1.2));//1.1999999999999997 //Math.nextAfter(a,b) 返回(a,b)或(b,a)间与a相邻的浮点数 b可以比a小 System.out.println( "------------------------------------------"); System.out.println( "Math.nextAfter(1.2, 2.7)----:" + Math.nextAfter(1.2, 2.7));//1.2000000000000002 System.out.println( "Math.nextAfter(1.2, -1)----:" + Math.nextAfter(1.2, -1));//1.1999999999999997 } }
运行结果:
代码解读:
Math:Java的Math类封装了很多与数学有关的属性和方法。
程序二
代码:
package com.dreamchaser.work3; import java.math.BigInteger; public class BigInteger_test { public static void main(String[] args) { BigInteger bigInteger1=new BigInteger("123456789987654321"); BigInteger bigInteger2=new BigInteger("321456789132456891323"); System.out.println("相加结果:"+bigInteger2.add(bigInteger1)); System.out.println("相减结果:"+bigInteger2.subtract(bigInteger1)); System.out.println("相乘结果"+bigInteger2.multiply(bigInteger1)); System.out.println("相除结果:"+bigInteger2.divide(bigInteger1)); } }
运行结果:
代码解读:
BigInteger类用于大数字的运算
程序三
代码:
package com.dreamchaser.work3; import java.util.Random; public class Random_test { public static void main(String[] args) { Random random = new Random(); System.out.println("nextInt():" + random.nextInt()); //随机生成一个整数,这个整数的范围就是int类型的范围-2^31~2^31-1 System.out.println("nextLong():" + random.nextLong()); //随机生成long类型范围的整数 System.out.println("nextFloat():" + random.nextFloat()); //随机生成[0, 1.0)区间的小数 System.out.println("nextDouble():" + random.nextDouble()); //随机生成[0, 1.0)区间的小数 byte[] byteArr = new byte[10]; random.nextBytes(byteArr); //随机生成byte,并存放在定义的数组中,生成的个数等于定义的数组的个数 for (int i = 0; i < byteArr.length; i++) { System.out.println(byteArr[i]); } /** * random.nextInt(n) * 随机生成一个正整数,整数范围[0,n) * 如果想生成其他范围的数据,可以在此基础上进行加减 * * 例如: * 1. 想生成范围在[0,n]的整数 * random.nextInt(n+1) * 2. 想生成范围在[m,n]的整数, n > m * random.nextInt(n-m+1) + m * random.nextInt() % (n-m) + m * 3. 想生成范围在(m,n)的整数 * random.nextInt(n-m+1) + m -1 * random.nextInt() % (n-m) + m - 1 * ...... 主要是依靠简单的加减法 */ System.out.println("nextInt(10):" + random.nextInt(10)); //随机生成一个整数,整数范围[0,10) for (int i = 0; i < 20; i++) { //[3,15) //这里有坑,需要注意,如果前面用了+号,应该要把计算结果整体用括号括起来,不然它会把+号解释为字符串拼接 System.out.println("我生成了一个[3,15)区间的数,它是:" + (random.nextInt(12) + 3)); } } }
运行结果:
代码解读:
Random类位于java.util包下,此类的实例用于生成伪随机数流。之所以称之为伪随机,是因为真正意义上的随机数(或者称为随机事件)在某次产生过程中是按照实验过程表现的分布概率随机产生的,其结果是不可预测,不可见的。而计算机中的随机函数是按照一定的算法模拟产生的,其结果是确定的,可见的。我们认为这样产生的数据不是真正意义上的随机数,因而称之为伪随机。
详情请看Java Random介绍
四、Class类
1.实验要求
Class类:重点p210例20的反射机制创建对象
2.程序与测试
程序一
代码:
package com.dreamchaser.work3; import com.dreamchaser.Tax; public class Class_test { public static void main(String[] args) { Tax tax=new Tax(); try { Class c1=Class.forName("com.dreamchaser.Tax"); Class c2=Tax.class; Class c3=tax.getClass(); if (c1==c2){ System.out.println("c1和c2指向同一个Class!"); }else{ System.out.println("c1和c2不指向同一个!"); } if (c3==c2){ System.out.println("c3和c2指向同一个Class!"); }else{ System.out.println("c3和c2不指向同一个!"); } try { Tax tax1= (Tax) c1.newInstance(); System.out.println(tax1); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } catch (ClassNotFoundException e) { e.printStackTrace(); } } }
运行结果:
代码解读:
通过程序可知,分别通过三种方式得到的Class其实是指向同一个地址,由此可知,该Class只会存在一个,在类编译的时候便已经产生。可以通过Class类的newInstance方法来创建对象。
在java世界里,一切皆对象。从某种意义上来说,java有两种对象:实例对象和Class对象。每个类的运行时的类型信息就是用Class对象表示的。它包含了与类有关的信息。其实我们的实例对象就通过Class对象来创建的。Java使用Class对象执行其RTTI(运行时类型识别,Run-Time Type Identification),多态是基于RTTI实现的。
每一个类都有一个Class对象,每当编译一个新类就产生一个Class对象,基本类型 (boolean, byte, char, short, int, long, float, and
double)有Class对象,数组有Class对象,就连关键字void也有Class对象(void.class)。Class对象对应着java.lang.Class类,如果说类是对象抽象和集合的话,那么Class类就是对类的抽象和集合。
Class类没有公共的构造方法,Class对象是在类加载的时候由Java虚拟机以及通过调用类加载器中的 defineClass方法自动构造的,因此不能显式地声明一个Class对象。一个类被加载到内存并供我们使用需要经历如下三个阶段:
加载,这是由类加载器(ClassLoader)执行的。通过一个类的全限定名来获取其定义的二进制字节流(Class字节码),将这个字节流所代表的静态存储结构转化为方法去的运行时数据接口,根据字节码在java堆中生成一个代表这个类的java.lang.Class对象。
链接,在链接阶段将验证Class文件中的字节流包含的信息是否符合当前虚拟机的要求,为静态域分配存储空间并设置类变量的初始值(默认的零值),并且如果必需的话,将常量池中的符号引用转化为直接引用。
初始化,到了此阶段,才真正开始执行类中定义的java程序代码。用于执行该类的静态初始器和静态初始块,如果该类有父类的话,则优先对其父类进行初始化。
所有的类都是在对其第一次使用时,动态加载到JVM中的(懒加载)。当程序创建第一个对类的静态成员的引用时,就会加载这个类。使用new创建类对象的时候也会被当作对类的静态成员的引用。因此java程序程序在它开始运行之前并非被完全加载,其各个类都是在必需时才加载的。这一点与许多传统语言都不同。动态加载使能的行为,在诸如C++这样的静态加载语言中是很难或者根本不可能复制的。
在类加载阶段,类加载器首先检查这个类的Class对象是否已经被加载。如果尚未加载,默认的类加载器就会根据类的全限定名查找.class文件。在这个类的字节码被加载时,它们会接受验证,以确保其没有被破坏,并且不包含不良java代码。一旦某个类的Class对象被载入内存,我们就可以它来创建这个类的所有对象。
五、Pattern类与Matcher类
1.实验要求
了解Pattern类与Matcher类的使用
2.程序与测试
程序一
代码:
package com.dreamchaser.work3; import java.util.regex.Matcher; import java.util.regex.Pattern; public class Pattern_Matcher_test { private static Pattern p=Pattern.compile("([a-z]+)(\\d+)"); private static Pattern p1=Pattern.compile("\\d+"); public static void main(String[] args) { Matcher m=p.matcher("aaa2223bb"); m.find(); //匹配aaa2223 m.groupCount(); //返回2,因为有2组 m.start(1); //返回0 返回第一组匹配到的子字符串在字符串中的索引号 m.start(2); //返回3 m.end(1); //返回3 返回第一组匹配到的子字符串的最后一个字符在字符串中的索引位置. m.end(2); //返回7 m.group(1); //返回aaa,返回第一组匹配到的子字符串 m.group(2); //返回2223,返回第二组匹配到的子字符串 Matcher m1=p1.matcher("我的QQ是:456456 我的电话是:0532214 我的邮箱是:aaa123@aaa.com"); while(m1.find()) { System.out.println(m1.group()); } } }
运行结果:
代码解读:
Pattern类用于创建一个正则表达式,也可以说创建一个匹配模式,它的构造方法是私有的,不可以直接创建,但可以通过Pattern.complie(String regex)简单工厂方法创建一个正则表达式,pattern() 返回正则表达式的字符串形式,其实就是返回Pattern.complile(String regex)的regex参数。
Pattern.matcher(CharSequence input)返回一个Matcher对象. Matcher类的构造方法也是私有的,不能随意创建,只能通过Pattern.matcher(CharSequence input)方法得到该类的实例.
Pattern类只能做一些简单的匹配操作,要想得到更强更便捷的正则匹配操作,那就需要将Pattern与Matcher一起合作.
Matcher类提供了对正则表达式的分组支持,以及对正则表达式的多次匹配支持.
详情可参考详解Pattern类和Matcher类
六、Vector类
1.实验要求
向量类Vector:虽然教材中没列出,但可参考数据结构案例做:A∪B,(A-B) ∪(B-A);两有序表的合并…
2.程序与测试
程序一
代码:
package com.dreamchaser.work3; import java.util.Enumeration; import java.util.Vector; public class Vector_test { public static void main(String[] args) { // initial size is 3, increment is 2 Vector v = new Vector(3, 2); System.out.println("Initial size: " + v.size()); System.out.println("Initial capacity: " + v.capacity()); v.addElement(new Integer(1)); v.addElement(new Integer(2)); v.addElement(new Integer(3)); v.addElement(new Integer(4)); System.out.println("Capacity after four additions: " + v.capacity()); v.addElement(new Double(5.45)); System.out.println("Current capacity: " + v.capacity()); v.addElement(new Double(6.08)); v.addElement(new Integer(7)); System.out.println("Current capacity: " + v.capacity()); v.addElement(new Float(9.4)); v.addElement(new Integer(10)); System.out.println("Current capacity: " + v.capacity()); v.addElement(new Integer(11)); v.addElement(new Integer(12)); System.out.println("First element: " + (Integer)v.firstElement()); System.out.println("Last element: " + (Integer)v.lastElement()); if(v.contains(new Integer(3))) { System.out.println("Vector contains 3."); } // enumerate the elements in the vector. Enumeration vEnum = v.elements(); System.out.println("\nElements in vector:"); while(vEnum.hasMoreElements()) { System.out.print(vEnum.nextElement() + " "); } System.out.println(); } }
运行结果:
代码解读:
Vector 类实现了一个动态数组。和 ArrayList 很相似,但是两者是不同的:
Vector 是同步访问的。
Vector 包含了许多传统的方法,这些方法不属于集合框架。
Vector 主要用在事先不知道数组的大小,或者只是需要一个可以改变大小的数组的情况。
总结
本文大致测试整理了一些常用系统类的常用方法,并附上部分原理(有很多资料是网上找的,地址一般都附在那个模块后面。当然这个翻阅别人优秀博客的过程也受益良多,尤其是String和Class的部分,以前不懂的地方看完后便豁然开朗)。
建议大家也能有翻阅别人的优秀博客,然后自己整理总结的过程,这真的能学到很多。