Java系列(十五)__Java常用类库(1)
在每一个JVM进程之中都会存在有一个Runtime类的实例化对象,此类对象将由JVM为用户提供好。但是为了保持只有一个Runtime类的实例化对象,所以Runtime类所采用的是一个单例设计模式,构造方法被私有化了。
既然此处是单例设计模式,那么一定会存在有一个static方法可以取得本类的实例化对象:
· 取得Runtime类对象:public static Runtime getRuntime();
当取得了Runtime类对象之后就可以通过如下的三个方法取得一些内存信息:
· 最大可用内存量:public long maxMemory();
· 可用内存量:public long totalMemory();
· 空闲内存量:public long freeMemory()。
清理垃圾空间:public void gc()。
范例:观察内存信息
package cn.mldn.demo; public class TestDemo { public static void main(String[] args) { Runtime run = Runtime.getRuntime() ; // 单例设计 System.out.println("1、MAX = " + run.maxMemory()); System.out.println("1、TOTAL = " + run.totalMemory()); System.out.println("1、FREE = " + run.freeMemory()); String str = "" ; for (int x = 0; x < 30000; x++) { str += x ; // 垃圾产生 } System.out.println("2、MAX = " + run.maxMemory()); System.out.println("2、TOTAL = " + run.totalMemory()); System.out.println("2、FREE = " + run.freeMemory()); run.gc(); System.out.println("3、MAX = " + run.maxMemory()); System.out.println("3、TOTAL = " + run.totalMemory()); System.out.println("3、FREE = " + run.freeMemory()); } } |
面试题:请问什么是GC?如何操作?
· GC是垃圾收集器(Garbage Collector),是负责进行垃圾空间清理的线程;
· 在Java之中可以由JVM自动调用GC,或者是由用户调用Runtime类中的gc()方法手工清理垃圾。
对于System类应该是很熟悉的,至少已经接触过两类方法:输出、数组排序,但是对于之前给出的数组排序方法名称并不标准:public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length)。此方法基本上不用。
在System类里面有一个方法是取得当前的日期时间:public static long currentTimeMillis()。此时取得的数据只是一个数值,并不是传统意义的年、月、日。
范例:观察currentTimeMillis()方法
package cn.mldn.demo; public class TestDemo { public static void main(String[] args) throws Exception { // 方便处理 long start = System.currentTimeMillis() ; String str = "" ; for (int x = 0; x < 30000; x++) { str += x ; // 垃圾产生 } long end = System.currentTimeMillis() ; System.out.println("本操作所花费的时间:" + (end - start)); } } |
在System类之中存在如下的一个方法:public static void gc()。但是此操作并不是一个新的操作,通过System类调用gc()就等价于调用了Runtime类的gc()方法,这两个gc()没有任何的区别。
思考?所谓的垃圾指的就是没有任何的栈内存所指向堆内存空间,但是在堆内存变为垃圾之前,一定会经历创建、使用的两个过程,最终的过程才成为垃圾。那么现在的疑问:一个对象产生的时候发现可以通过构造方法做一些对象产生的处理,但是一旦对象没用了,连个说最后一句话的机会都没有。
那么如果希望在对象被回收前可以进行一些处理操作,则可以覆写Object类之中的finliaze()方法:
· 方法:protected void finalize() throws Throwable;
虽然此时抛出了异常信息,但是不管抛出什么样的信息,最终都不会影响程序的执行。
package cn.mldn.demo; class Person { public Person() { System.out.println("啊从出生了。"); } @Override protected void finalize() throws Throwable { System.out.println("啊从因为得罪李老师,结果自杀了。"); throw new Exception("老子死的好嗨皮啊!") ; } } public class TestDemo { public static void main(String[] args) throws Exception { // 方便处理 Person per = new Person() ; per = null ; System.gc() ; } } |
面试题:请解释final、finally、finalize的区别?
· final:关键字,可以定义不能被继承的父类、定义不能被覆写的方法、常量;
· finally:关键字,异常处理的统一出口,不管是否有异常都执行;
· finalize:方法(protected void finalize() throws Throwable),Object类方法,当对象被清理前执行的收尾操作。
在讲解StringBuffer类之前先来回顾一下String类特点:
· 一个字符串常量就是String类的匿名对象;
· String类对象有两种实例化方式:
|- 方式一:直接赋值,可以自动入池,只开辟一块内存空间;
|- 方式二:构造方法实例化,会开辟两块空间,其中一块将成为垃圾,不会自动入池,可以使用intern()方法手工入池;
· 字符串内容一旦声明则不可改变。
正是因为字符串内容一旦声明不可改变,所以如果在频繁修改字符串内容的程序上是不能够使用String的,那么为此在Java之中又提供有一个StringBuffer类,而String和StringBuffer类最大的特征在于:String不可改变内容,而StringBuffer可以改变。在String类对象之中使用“+”可以实现字符串连接操作,但是在StringBuffer类之中只能够使用append()方法实现字符串连接:public StringBuffer append(数据类型 b)。既然返回的是StringBuffer就可以一直使用append()连接。
范例:观察程序
package cn.mldn.demo; public class TestDemo { public static void main(String[] args) throws Exception { // 方便处理 StringBuffer buf = new StringBuffer() ; buf.append("Hello ").append("World ").append("!!!") ; fun(buf) ; System.out.println(buf); } public static void fun(StringBuffer temp) { temp.append("\n").append("Hello MLDN .") ; } } |
此时在fun()方法之中对StringBuffer类对象所做的修改可以影响原始对象,所以就可以得出结论:StringBuffer的内容可以进行修改。
到此已经学习过了两个表示字符串的类:String、StringBuffer,那么这两个类有什么联系呢?
String类定义: |
StringBuffer类定义: |
public final class String extends Object implements Serializable, Comparable, CharSequence |
public final class StringBuffer extends Object implements Serializable, CharSequence |
发现String和StringBuffer两个类都是CharSequence接口的子类。那么就表示,如果见到某些方法上的参数或返回值类型是CharSequence,就应该立刻想到是字符串。
范例:利用CharSequence接收
package cn.mldn.demo; public class TestDemo { public static void main(String[] args) throws Exception { // 方便处理 StringBuffer buf = new StringBuffer() ; buf.append("Hello ").append("World ").append("!!!") ; CharSequence csA = buf ; // StringBuffer实现CharSequence CharSequence csB = "HELLO" ; // String实现了CharSequence System.out.println(csA); System.out.println(csB); } } |
但是虽然String和StringBuffer都是CharSequence接口的子类,可是这两个类的对象并不可能直接进行转型。但是在实际的工作之中毕竟直接使用String和StringBuffer这两子类居多。那么这两个类对象之间也可以进行转型。
1、 将String变为StringBuffer;
· 利用StringBuffer类的构造方法:public StringBuffer(String str)。
· 利用StringBuffer类的append()方法:public StringBuffer append(String str)。
package cn.mldn.demo; public class TestDemo { public static void main(String[] args) throws Exception { // 方便处理 StringBuffer bufA = new StringBuffer("Hello") ; // String变为StringBuffer StringBuffer bufB = new StringBuffer() ; bufB.append("World") ; System.out.println(bufA); System.out.println(bufB); } } |
2、 将StringBuffer变为String
· 所有类对象都支持变为String方法:public String toString();
package cn.mldn.demo; public class TestDemo { public static void main(String[] args) throws Exception { // 方便处理 StringBuffer bufA = new StringBuffer("Hello") ; // String变为StringBuffer String str = bufA.toString() ; // StringBuffer变为String System.out.println(str); } } |
除了以上的基本特征之外,在StringBuffer类里面还提供有一些String类所没有的方法。
范例:执行数据插入
· 方法:public StringBuffer insert(int offset, 数据类型 l)。
package cn.mldn.demo; public class TestDemo { public static void main(String[] args) throws Exception { // 方便处理 StringBuffer buf = new StringBuffer("Hello") ; // String变为StringBuffer buf.insert(0, "你好 ") ; System.out.println(buf); } } |
范例:删除部分数据
· 方法:public StringBuffer delete(int start, int end);
package cn.mldn.demo; public class TestDemo { public static void main(String[] args) throws Exception { // 方便处理 StringBuffer buf = new StringBuffer("Hello World") ; buf.delete(5, 11) ; System.out.println(buf); } } |
范例:字符串反转
· 方法:public StringBuffer reverse()。
package cn.mldn.demo; public class TestDemo { public static void main(String[] args) throws Exception { // 方便处理 StringBuffer buf = new StringBuffer("Hello World") ; System.out.println(buf.reverse()); } } |
面试题:请解释String和StringBuffer的区别?
· String和StringBuffer都是CharSequence接口的子类,其中String的内容不可以修改,而StringBuffer的内容可以修改;
· StringBuffer类提供了与String类互补的操作方法,例如:append()、insert()、reverse()。
工作之中使用最多的一定是String类,只有在频繁修改的时候才使用StringBuffer类。
在JDK 1.5之后Java增加了一个StringBuilder类,此类定义如下:
public final class StringBuilder extends Object implements Serializable, CharSequence |
但是通过方法组成可以发现,StringBuilder和StringBuffer类的操作方法可以说是完全一样的。
范例:使用StringBuilder
package cn.mldn.demo; public class TestDemo { public static void main(String[] args) throws Exception { // 方便处理 StringBuilder buf = new StringBuilder("Hello World") ; System.out.println(buf.reverse()); } } |
范例:比较一下String、StringBuffer、StringBuilder的区别
package cn.mldn.demo; public class TestDemo { public static void main(String[] args) throws Exception { testString() ; testStringBuffer() ; testStringBuilder() ; } public static void testString() { // 方便处理 long start = System.currentTimeMillis() ; String str = "" ; for (int x = 0 ; x < 20000 ; x ++) { str += x ; } long end = System.currentTimeMillis() ; System.out.println("1、String花费:" + (end - start)); } public static void testStringBuffer() { // 方便处理 long start = System.currentTimeMillis() ; StringBuffer str = new StringBuffer() ; for (int x = 0 ; x < 200000 ; x ++) { str.append(x) ; } long end = System.currentTimeMillis() ; System.out.println("2、StringBuffer花费:" + (end - start)); } public static void testStringBuilder() { // 方便处理 long start = System.currentTimeMillis() ; StringBuilder str = new StringBuilder() ; for (int x = 0 ; x < 200000 ; x ++) { str.append(x) ; } long end = System.currentTimeMillis() ; System.out.println("2、StringBuffer花费:" + (end - start)); } } |
可以发现,如果在频繁修改的情况下,String的性能是明显不足的,而StringBuffer和StringBuilder明显是高效的,但是以上的代码是在单线程情况下,如果是多线程共同操作,那么StringBuilder会更快,因为StringBuilder采用的是异步处理,而StringBuffer采用的是同步处理的,但是StringBuffer数据安全。
面试题:请解释String、StringBuffer、StringBuilder的区别?
· String不适合于字符串的频繁修改,而StringBuffer和StringBuilder适合于频繁修改,而且速度要远远高于String;
· StringBuffer是在JDK 1.0的时候引入的,而StringBuilder是在JDK 1.5的时候引入的,而且StringBuffer和StringBuilder继承结构相同,方法的组成也相同;
· StringBuffer中的所有方法都使用synchronized进行标记,都是同步方法,性能相对较低,而StringBuilder采用的异步处理,性能相对较高,但是StringBuffer在多线程操作时的数据安全性要高于StringBuilder。
日期是一个重要的单位,在Java之中如果用户要想取得当前的系统日期时间,一个最简单的方式就是直接输出java.util.Date类对象,而这个Date类的构造方法:。
· 无参构造:public Date();
· 有参构造:public Date(long date)。
范例:操作Date
package cn.mldn.demo; import java.util.Date; public class TestDemo { public static void main(String[] args) throws Exception { Date date = new Date() ; System.out.println(date); } } |
Mon Sep 01 10:36:20 CST 2014 |
所以现在可以发现,虽然Date可以取得当前的日期时间,只不过感觉上别扭了点,而且有些同学是读不出来的。
提示:关于long和Date的互相转型
long数据类型一直强调的是:可以保存文件大小或者是日期时间数据,而这一特性在Date类上是很好的体现:
· 构造方法:public Date(long date);
· 返回long数据:public long getTime()。
范例:互相转换
package cn.mldn.demo; import java.util.Date; public class TestDemo { public static void main(String[] args) throws Exception { Date date = new Date(System.currentTimeMillis()) ; System.out.println(date); System.out.println(date.getTime()); } } |
那么为了得到良好的体验,必须要想办法格式化Date类的数据显示结果。(Oracle的TO_CHAR()函数可以将日期或数字格式化为字符串),Java为此提供了一个专门的处理类:java.text.SimpleDateFormat类。
那么在SimpleDateFormat类之中主要使用如下几个操作方法:
· 构造方法:public SimpleDateFormat(String pattern);
· 将Date格式化为String:public final String format(Date date);
· 将String格式化为Date:public Date parse(String source) throws ParseException。
但是在实例化SimpleDateFormat类的时候需要传入一些日期时间的标记,而所有的标记字母都在SimpleDateFormat类中定义:年(yyyy)、月(MM)、日(dd)、时(HH)、分(mm)、秒(ss)、毫秒(SSS)。
范例:将Date格式化为String
package cn.mldn.demo; import java.text.SimpleDateFormat; import java.util.Date; public class TestDemo { public static void main(String[] args) throws Exception { Date date = new Date() ; SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS") ; String str = sdf.format(date) ; System.out.println(str); } } |
范例:将字符串转换为Date
package cn.mldn.demo; import java.text.SimpleDateFormat; import java.util.Date; public class TestDemo { public static void main(String[] args) throws Exception { String str = "2011-11-11 11:11:11.111" ; SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS") ; Date date = sdf.parse(str) ; System.out.println(date); } } |
那么到此为止已经清楚了基本数据类型、包装类、String、Date之间的转换:
· 基本数据类型 è 包装类:自动装箱和自动拆箱;
· 基本数据类型 è String:使用valueOf()方法;
· long è Date:Date类的构造方法;
· Date è long:getTime()方法;
· String è 基本类型:包装类;
· String çè Date:java.text.SimpleDateFormat类、format()、parse()。
java.util.Random类的主要功能是用于产生随机数。
范例:观察代码
package cn.mldn.demo; import java.util.Random; public class TestDemo { public static void main(String[] args) throws Exception { Random rand = new Random(); for (int x = 0; x < 10; x++) { System.out.print(rand.nextInt(100) + "、"); } } } |
这一个类意义不大,有些了解就够了。
克隆指的就是对象的复制操作,在Object类里面提供有一个clone()方法:
· clone()方法:protected Object clone() throws CloneNotSupportedException;
要被克隆的对象所在的类需要去实现一个Cloneable接口。但是此接口里面没有包含任何的方法,所以此接口属于一个标识接口,表示一种能力。
package cn.mldn.demo; class Person implements Cloneable { private String name ; private int age ; public Person(String name,int age) { this.name = name ; this.age = age ; } @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); // 调用父类的对象克隆方法 } public void setName(String name) { this.name = name; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "姓名:" + this.name + ",年龄:" + this.age ; } } public class TestDemo { public static void main(String[] args) throws Exception { Person perA = new Person("张三",20) ; Person perB = (Person) perA.clone() ; // 克隆 perB.setAge(90); System.out.println(perA); System.out.println(perB); } } |
一定要知道这使用的是一个标识接口,表示能力。
Math是一个数学操作类,在Math类之中提供有大量的数学操作方法。但是在这个类里面只强调一个round()方法:public static long round(double a)。
范例:使用四舍五入
package cn.mldn.demo; public class TestDemo { public static void main(String[] args) throws Exception { System.out.println(Math.round(19.5)); } } |
面试题:请说出“Math.round(13.5)”、“Math.round(-13.5)”、“Math.round(-13.51)”结果是什么?
· Math.round(13.5):14;
· Math.round(-13.5):-13;
· Math.round(-13.51):-14。