Java是面向对象的语言,但并不是“纯面向对象”的,因为我们经常用到的基本数据类型就不是对象。但是我们在实际应用中经常需要将基本数据转化成对象,以便于操作。比如:将基本数据类型存储到Object[]数组或集合中的操作等等。
为了解决这个不足,Java在设计类时为每个基本数据类型设计了一个对应的类进行代表,这样八个和基本数据类型对应的类统称为包装类(Wrapper Class)。
包装类均位于java.lang包
在这八个类名中,除了Integer和Character类以外,其它六个类的类名和基本数据类型一致,只是类名的第一个字母大写而已。
在这八个类中,除了Character和Boolean以外,其他的都是“数字型”,“数字型”都是java.lang.Number的子类。Number类是抽象类,因此它的抽象方法,所有子类都需要提供实现。Number类提供了抽象方法:intValue()、longValue()、floatValue()、doubleValue(),意味着所有的“数字型”包装类都可以互相转型
对于包装类来说,这些类的用途主要包含两种:
1. 作为和基本数据类型对应的类型存在,方便涉及到对象的操作,如Object[]、集合等的操作。
2. 包含每种基本数据类型的相关属性如最大值、最小值等,以及相关的操作方法(这些操作方法的作用是在基本数据类型、包装类对象、字符串之间提供相互之间的转化!)。
自动装箱和拆箱就是将基本数据类型和包装类之间进行自动的互相转换。JDK1.5后,Java引入了自动装箱(autoboxing)/拆箱(unboxing)。
自动装箱:
基本类型的数据处于需要对象的环境中时,会自动转为“对象”。
我们以Integer为例:在JDK1.5以前,这样的代码 Integer i = 5 是错误的,必须要通过Integer i = new Integer(5) 这样的语句来实现基本数据类型转换成包装类的过程;而在JDK1.5以后,Java提供了自动装箱的功能,因此只需Integer i = 5这样的语句就能实现基本数据类型转换成包装类,这是因为JVM为我们执行了Integer i = Integer.valueOf(5)这样的操作,这就是Java的自动装箱。
自动拆箱:
每当需要一个值时,对象会自动转成基本数据类型,没必要再去显式调用intValue()、doubleValue()等转型方法。
如 Integer i = 5;int j = i; 这样的过程就是自动拆箱。
我们可以用一句话总结自动装箱/拆箱:
自动装箱过程是通过调用包装类的valueOf()方法实现的,而自动拆箱过程是通过调用包装类的 xxxValue()方法实现的(xxx代表对应的基本数据类型,如intValue()、doubleValue()等)。
自动装箱与拆箱的功能事实上是编译器来帮的忙,编译器在编译时依据您所编写的语法,决定是否进行装箱或拆箱动作
整型、char类型所对应的包装类,在自动装箱时,对于-128~127之间的值会进行缓存处理,其目的是提高效率。
缓存处理的原理为:如果数据在-128~127这个区间,那么在类加载时就已经为该区间的每个数值创建了对象,并将这256个对象存放到一个名为cache的数组中。每当自动装箱过程发生时(或者手动调用valueOf()时),就会先判断数据是否在该区间,如果在则直接获取数组中对应的包装类对象的引用,如果不在该区间,则会通过new调用包装类的构造方法来创建对象。
String 类对象代表不可变的Unicode字符序列,因此我们可以将String对象称为“不可变对象”。那什么叫做“不可变对象”呢?指的是对象内部的成员变量的值无法再改变。
StringBuffer和StringBuilder
显然,内部也是一个字符数组,但这个字符数组没有用final修饰,随时可以修改。因此,StringBuilder和StringBuffer称之为“可变字符序列”。那两者有什么区别呢?
1. StringBuffer JDK1.0版本提供的类,线程安全,做线程同步检查, 效率较低。
2. StringBuilder JDK1.5版本提供的类,线程不安全,不做线程同步检查,因此效率较高。建议采用该类。
· 常用方法列表:
1. 重载的public StringBuilder append(…)方法
可以为该StringBuilder 对象添加字符序列,仍然返回自身对象。
2. 方法 public StringBuilder delete(int start,int end)
可以删除从start开始到end-1为止的一段字符序列,仍然返回自身对象。
3. 方法 public StringBuilder deleteCharAt(int index)
移除此序列指定位置上的 char,仍然返回自身对象。
4. 重载的public StringBuilder insert(…)方法
可以为该StringBuilder 对象在指定位置插入字符序列,仍然返回自身对象。
5. 方法 public StringBuilder reverse()
用于将字符序列逆序,仍然返回自身对象。
6. 方法 public String toString() 返回此序列中数据的字符串表示形式。
7. 和 String 类含义类似的方法:
package 面向对象;
/**
* 测试s't'ring builder和buffer
* @author luckly
*
*/
public class TestBuilder {
public static void main(String[] args) {
//stringbuilder 线程不安全。但是小效率高stringbuffer线程安全效率低
StringBuilder sb=new StringBuilder("abcdefg");
System.out.println(Integer.toHexString(sb.hashCode()));
System.out.println(sb);
sb.setCharAt(2, 'M');
System.out.println(Integer.toHexString(sb.hashCode()));
System.out.println(sb);
}
}
package 面向对象;
/**
* 测试s't'ring builder和buffer
*
* @author luckly
*
*/
public class TestBuilder2 {
public static void main(String[] args) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 26; i++) {
sb.append((char) ('a' + i));
}
System.out.println(sb);
sb.reverse();// 倒序
System.out.println(sb);
sb.setCharAt(2, 'W');
System.out.println(sb);
sb.insert(0, '5');
System.out.println(sb);
sb.insert(1, '我').insert(2, '爱').insert(3, '你');
System.out.println(sb);
sb.delete(20, 23);
}
}
package 面向对象;
/**
* 测试s't'ring builder和buffer
*
* @author luckly
*
*/
public class TestBuilder3 {
public static void main(String[] args) {
/** 使用String进行字符串的拼接 */
String str8 = "";
// 本质上使用StringBuilder拼接, 但是每次循环都会生成一个StringBuilder对象
long num1 = Runtime.getRuntime().freeMemory();// 获取系统剩余内存空间
long time1 = System.currentTimeMillis();// 获取系统的当前时间
for (int i = 0; i < 5000; i++) {
str8 = str8 + i;// 相当于产生了10000个对象
}
long num2 = Runtime.getRuntime().freeMemory();
long time2 = System.currentTimeMillis();
System.out.println("String占用内存 : " + (num1 - num2));
System.out.println("String占用时间 : " + (time2 - time1));
/** 使用StringBuilder进行字符串的拼接 */
StringBuilder sb1 = new StringBuilder("");
long num3 = Runtime.getRuntime().freeMemory();
long time3 = System.currentTimeMillis();
for (int i = 0; i < 5000; i++) {
sb1.append(i);
}
long num4 = Runtime.getRuntime().freeMemory();
long time4 = System.currentTimeMillis();
System.out.println("StringBuilder占用内存 : " + (num3 - num4));
System.out.println("StringBuilder占用时间 : " + (time4 - time3));
}
}
不可变和可变字符序列使用陷阱
String使用的陷阱
String一经初始化后,就不会再改变其内容了。对String字符串的操作实际上是对其副本(原始拷贝)的操作,原来的字符串一点都没有改变。比如:
String s ="a"; 创建了一个字符串
s = s+"b"; 实际上原来的"a"字符串对象已经丢弃了,现在又产生了另一个字符串s+"b"(也就是"ab")。如果多次执行这些改变串内容的操作,会导致大量副本字符串对象存留在内存中,降低效率。如果这样的操作放到循环中,会极大影响程序的时间和空间性能,甚至会造成服务器的崩溃。
相反,StringBuilder和StringBuffer类是对原字符串本身操作的,可以对字符串进行修改而不产生副本拷贝或者产生少量的副本。因此可以在循环中使用。
1. String:不可变字符序列。
2. StringBuffer:可变字符序列,并且线程安全,但是效率低。
3. StringBuilder:可变字符序列,线程不安全,但是效率高(一般用它)。
Date时间类(java.util.Date)
在标准Java类库中包含一个Date类。它的对象表示一个特定的瞬间,精确到毫秒。
1. Date() 分配一个Date对象,并初始化此对象为系统当前的日期和时间,可以精确到毫秒)。
2. Date(long date) 分配 Date 对象并初始化此对象,以表示自从标准基准时间(称为“历元(epoch)”,即 1970 年 1 月 1 日 00:00:00 GMT)以来的指定毫秒数。
3. boolean after(Date when) 测试此日期是否在指定日期之后。
4. booleanbefore(Date when) 测试此日期是否在指定日期之前。
5. boolean equals(Object obj) 比较两个日期的相等性。
6. long getTime() 返回自 1970 年 1 月 1 日 00:00:00 GMT 以来此 Date 对象表示的毫秒数。
7. String toString() 把此 Date 对象转换为以下形式的 String:
dow mon dd hh:mm:ss zzz yyyy 其中:dow 是一周中的某一天 (Sun、 Mon、Tue、Wed、 Thu、 Fri、 Sat)。
package 面向对象;
import java.util.Date;
public class TestDate {
public static void main(String[] args) {
Date d=new Date(2000);
System.out.println(d);
System.out.println(d.getTime());
Date d2=new Date();
System.out.println(d2.getTime());
System.out.println(d2.after(d));
Date d3=new Date(2025-1900,3,10);
System.out.println(d3);
System.out.println(Math.ceil(3.2));
System.out.println(Math.floor(3.2));
System.out.println(Math.round(3.2));
System.out.println(Math.round(3.8));
//绝对值、开方、a的b次幂等操作
System.out.println(Math.abs(-45));
System.out.println(Math.sqrt(64));
System.out.println(Math.pow(5, 2));
System.out.println(Math.pow(2, 5));
//Math类中常用的常量
System.out.println(Math.PI);
System.out.println(Math.E);
//随机数
System.out.println(Math.random());// [0,1)
}
}
package 面向对象;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* 测试时间对象和字符串的转换
* @author luckly
*
*/
public class TestDateFomat {
public static void main(String[] args) throws ParseException {
DateFormat df=new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
String str=df.format(new Date(400000));
System.out.println(str);
// 将符合指定格式的字符串转成成时间对象.字符串格式需要和指定格式一致。
DateFormat df2=new SimpleDateFormat("yyyy年MM月dd日 hh点mm分ss秒");
Date date=df2.parse("1983年10月10日 10点35分32秒");
System.out.println(date);
DateFormat df3=new SimpleDateFormat("D");
String str3=df3.format(new Date());
System.out.println(str3 );
}
}
Calendar日历类
Calendar 类是一个抽象类,为我们提供了关于日期计算的相关功能,比如:年、月、日、时、分、秒的展示和计算。
GregorianCalendar 是 Calendar 的一个具体子类,提供了世界上大多数国家/地区使用的标准日历系统。
菜鸟雷区
注意月份的表示,一月是0,二月是1,以此类推,12月是11。 因为大多数人习惯于使用单词而不是使用数字来表示月份,这样程序也许更易读,父类Calendar使用常量来表示月份:JANUARY、FEBRUARY等等。
package 面向对象;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Scanner;
/**
* 可视化日期
* 可视化日历第二个版本
* @author luckly
*
*/
public class TestCalendar2 {
public static void main(String[] args) throws ParseException {
System.out.println("请输入日期(格式2010-01-10)");
Scanner scanner=new Scanner(System.in);
String str=scanner.nextLine();
DateFormat df=new SimpleDateFormat("yyyy-MM-dd");
Date date=df.parse(str);
Calendar c=new GregorianCalendar();
c.setTime(date);
int day=c.get(Calendar.DAY_OF_MONTH);
int days=c.getActualMaximum(Calendar.DATE);
System.out.println("日\t一\t二\t三\t四\t五\t六");
c.set(Calendar.DAY_OF_MONTH, 1);
for(int i=0; i<c.get(Calendar.DAY_OF_WEEK)-1;i++) {
System.out.print("\t");
}
for (int i = 0; i < days; i++) {
if(day==c.get(Calendar.DAY_OF_MONTH)) {
System.out.print(c.get(Calendar.DAY_OF_MONTH)+"*\t");
}else {
System.out.print(c.get(Calendar.DAY_OF_MONTH)+"\t");
}
if(c.get(Calendar.DAY_OF_WEEK)==Calendar.SATURDAY) {
System.out.println();
}
c.add(Calendar.DAY_OF_MONTH, 1);
}
}
}
package 面向对象;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Scanner;
/**
* 可视化日期
* 可视化日历第二个版本
* @author luckly
*
*/
public class TestCalendar2 {
public static void main(String[] args) throws ParseException {
System.out.println("请输入日期(格式2010-01-10)");
Scanner scanner=new Scanner(System.in);
String str=scanner.nextLine();
DateFormat df=new SimpleDateFormat("yyyy-MM-dd");
Date date=df.parse(str);
Calendar c=new GregorianCalendar();
c.setTime(date);
int day=c.get(Calendar.DAY_OF_MONTH);
int days=c.getActualMaximum(Calendar.DATE);
System.out.println("日\t一\t二\t三\t四\t五\t六");
c.set(Calendar.DAY_OF_MONTH, 1);
for(int i=0; i<c.get(Calendar.DAY_OF_WEEK)-1;i++) {
System.out.print("\t");
}
for (int i = 0; i < days; i++) {
if(day==c.get(Calendar.DAY_OF_MONTH)) {
System.out.print(c.get(Calendar.DAY_OF_MONTH)+"*\t");
}else {
System.out.print(c.get(Calendar.DAY_OF_MONTH)+"\t");
}
if(c.get(Calendar.DAY_OF_WEEK)==Calendar.SATURDAY) {
System.out.println();
}
c.add(Calendar.DAY_OF_MONTH, 1);
}
}
}
java.io.File类:代表文件和目录。 在开发中,读取文件、生成文件、删除文件、修改文件的属性时经常会用到本类。
File类的常见构造方法:public File(String pathname)
以pathname为路径创建File对象,如果pathname是相对路径,则默认的当前路径在系统属性user.dir中存储,
import java.io.File;
public class TestFile6 {
public static void main(String[] args) {
File f = new File("d:/电影");
printFile(f, 0);
}
/**
* 打印文件信息
* @param file 文件名称
* @param level 层次数(实际就是:第几次递归调用)
*/
static void printFile(File file, int level) {
//输出层次数
for (int i = 0; i < level; i++) {
System.out.print("-");
}
//输出文件名
System.out.println(file.getName());
//如果file是目录,则获取子文件列表,并对每个子文件进行相同的操作
if (file.isDirectory()) {
File[] files = file.listFiles();
for (File temp : files) {
//递归调用该方法:注意等+1
printFile(temp, level + 1);
}
}
}
}
import java.util.Random;
public class TestEnum {
public static void main(String[] args) {
// 枚举遍历
for (Week k : Week.values()) {
System.out.println(k);
}
// switch语句中使用枚举
int a = new Random().nextInt(4); // 生成0,1,2,3的随机数
switch (Season.values()[a]) {
case SPRING:
System.out.println("春天");
break;
case SUMMER:
System.out.println("夏天");
break;
case AUTUMN:
System.out.println("秋天");
break;
case WINDTER:
System.out.println("冬天");
break;
}
}
}
/**季节*/
enum Season {
SPRING, SUMMER, AUTUMN, WINDTER
}
/**星期*/
enum Week {
星期一, 星期二, 星期三, 星期四, 星期五, 星期六, 星期日
}
总结
1. 每一个基本数据类型对应一个包装类。
2. 包装类的用途:
作为和基本数据类型对应的引用类型存在,方便涉及到对象的操作。
包含每种基本数据类型的相关属性如最大值、最小值以及相关的操作方法。
3. JDK1.5后在Java中引入自动装箱和拆箱。
4. 字符串相关类String、StringBuffer与StringBuilder
String:不可变字符序列。
StringBuffer:可变字符序列,并且线程安全,但是效率低。
StringBuilder:可变字符序列,线程不安全,但是效率高(一般用它)。
日期与时间类Date、DateFormat、SimpleDateFormat、Calendar、GregorianCalendar。
5. Math类的常用方法
pow(double a,double b)
max(double a,double b)
min(double a,double b)
random()
long round(double a)
6. 与操作文件相关的File类。
7. 当需要定义一组常量时,使用枚举类型