八种基本数据类型并不是对象,为了将基本类型数据和对象之间实现互相转化,JDK 为每一个基本数据类型提供了相应的包装类。
Java 是面向对象的语言,但并不是“纯面向对象”的,因为我们经常用到的基本数据类型就不是对象。但是我们在实际应用中经常需要将基本数据转化成对象,以便于操作。比如:将基本数据类型存储到 Object[ ]数组或集合中的操作等等。
为了解决这个不足,Java 在设计类时为每个基本数据类型设计了一个对应的类进行代表,这样八个和基本数据类型对应的类统称为包装类(Wrapper Class)。
包装类均位于 java.lang 包,八种包装类和基本数据类型的对应关系如表所示:
基本数据类型对应的包装类 | |
基本数据类型 | 包装类 |
byte | Byte |
boolean | Boolean |
short | Short |
char | Character |
int | Integer |
long | Long |
float | Float |
double | Double |
在这八个类名中,除了 Integer 和 Character 类以外,其它六个类的类名和基本数据类型一致,只是类名的第一个字母大写而已。
在这八个类中,除了 Character 和 Boolean 以外,其他的都是“数字型”,“数字型”都是 java.lang.Number 的子类。
Number 类是抽象类,因此它的抽象方法,所有子类都需要提供实现。Number 类提供了抽象方法:intValue()、longValue()、floatValue()、doubleValue(),意味着所有的“数字型”包装类都可以互相转型。
示例:
public class WrapperClassTest{
public static void main(String[] args){
Integer i=new Integer(10);
Integer j=new Integer(50);
}
}
内存分析图:
对于包装类来说,这些类的用途主要包含两种:
1. 作为和基本数据类型对应的类型存在,方便涉及到对象的操作,如 Object[ ]、集合等的操作。
2. 包含每种基本数据类型的相关属性如最大值、最小值等,以及相关的操作方法(这些操作方法的作用是在基本数据类型、包装类对象、字符串之间提供相互之间的转化!)。
包装类的使用:
package cn.pxy.test;
public class Test {
/**测试Integer的用法,其他包装类与Integer类似*/
void testInteger(){
//基本类型转化成Integer对象
Integer int1=new Integer(10);//已经废弃,不推荐使用
Integer int2=Integer.valueOf(20);//官方推荐这种写法
//Integer对象转化神int
int a=int1.intValue();
//字符串转化成Integer对象
Integer int3=Integer.parseInt("334");
Integer int4=new Integer("999");
//Integer对象转化成字符串
String str1=int3.toString();
//一些常见int类型相关的常量
System.out.println("int能表示的最大整数:"+Integer.MAX_VALUE);
}
public static void main(String[] args) {
Test test=new Test();
test.testInteger();
}
}
运行结果:
自动装箱和拆箱就是将基本数据类型和包装类之间进行自动的互相转换。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 的自动装箱。
/*示例*/
Integer i=100;//自动装箱
//相当于编译器自动作以下的语法编译
Integer i=Integer.valueOf(100);//调用的是valueOf(100),而不是new Integer(100)
自动拆箱:
每当需要一个值时,对象会自动转成基本数据类型,没必要再去显式调用 intValue()、doubleValue()等转型方法。如 Integer i = 5;int j = i; 这样的过程就是自动拆箱。
/*示例*/
Integer i=100;
int j=i;//自动拆箱
//相当于编译器自动作以下的语法编译
int j=i.intValue();
我们可以用一句话总结自动装箱/拆箱:
自动装箱过程是通过调用包装类的 valueOf()方法实现的,而自动拆箱过程是通过调用包装类的 xxxValue()方法实现的(xxx 代表对应的基本数据类型,如 intValue()、doubleValue()等)。
自动装箱与拆箱的功能事实上是编译器来帮的忙,编译器在编译时依据您所编写的语法,决定是否进行装箱或拆箱动作。
所以自动装箱与拆箱的功能是所谓的“编译器蜜糖(Compiler Sugar)”,虽然使用这个功能很方便,但在程序运行阶段您得了解 Java 的语义。
//示例
package cn.pxy.test;
public class Test2 {
/**
* 测试自动装箱和拆箱
*/
public static void main(String[] args) {
Integer b=23;//自动装箱
int a=new Integer(20);//自动拆箱
//下面问题需要注意
Integer c=null;
int d=c;//可以通过编译,但此处会出现空指针异常,因为此处其实就是:c.intValue()
}
}
整型、char类型所对应的包装类,在自动装箱时,对于-128~127之间的值会进行缓存处理,其目的是提高效率。
缓存处理的原理为:如果数据在-128~127这个区间,那么在类加载时就已经为该区间的每个数值创建了对象,并将这256个对象存放到一个名为cache的数组中。每当自动装箱过程发生时(或者手动调用valueOf()时),就会先判断数据是否在该区间,如果在则直接获取数组中对应的包装类对象的引用,如果不在该区间,则会通过new调用包装类的构造方法来创建对象。
下面我们以Integer类为例,看一看Java为我们提供的源码,加深对缓存技术的理解:
Integer类相关源码:
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
这段代码中我们需要解释下面几个问题:
1. IntegerCache类为Integer类的一个静态内部类,仅供Integer类使用。
2. 一般情况下 IntegerCache.low为-128,IntegerCache.high为127,
IntegerCache.cache为内部类的一个静态属性。
IntegerCache类相关源码:
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer[] cache;
static Integer[] archivedCache;
static {
// high value may be configured by property
int h = 127;
String integerCacheHighPropValue =
VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
try {
h = Math.max(parseInt(integerCacheHighPropValue), 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(h, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {
// If the property cannot be parsed into an int, ignore it.
}
}
high = h;
// Load IntegerCache.archivedCache from archive, if possible
VM.initializeFromArchive(IntegerCache.class);
int size = (high - low) + 1;
// Use the archived cache if it exists and is large enough
if (archivedCache == null || size > archivedCache.length) {
Integer[] c = new Integer[size];
int j = low;
for(int i = 0; i < c.length; i++) {
c[i] = new Integer(j++);
}
archivedCache = c;
}
cache = archivedCache;
// range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;
}
private IntegerCache() {}
}
由上面的源码我们可以看到,静态代码块的目的就是初始化数组cache的,这个过程会在类加载时完成。
包装类的缓存测试:
package cn.pxy.test;
public class Test2 {
public static void main(String[] args) {
Integer in1=-128;
Integer in2=-128;
System.out.println(in1==in2);//true,因为在缓存范围内
System.out.println(in1.equals(in2));//true
Integer in3=1234;
Integer in4=1234;
System.out.println(in3==in4);//false,因为1234不在缓存范围内
System.out.println(in3.equals(in4));//true
}
}
执行结果:
内存分析如图:
package cn.pxy.test;
/**
* 定义一个简单的包装类
* @author 胖咸鱼
*
*/
public class MyInteger {
private int value;
private static MyInteger[] cache=new MyInteger[256];
public static final int LOW=-128;
public static final int HIGH=127;
static {
//[-128,127]
for(int i=MyInteger.LOW;i<=HIGH;i++) {
//-128,0;-127,1;-126,2;
cache[i+128]=new MyInteger(i);
}
}
public static MyInteger valueOf(int i) {
if(i>=LOW&&i<=HIGH) {
return cache[i+128];
}
return new MyInteger(i);
}
public String toString() {
return this.value+";
}
public int intValue() {
return value;
}
private MyInteger(int i) {
this.value=i;
}
public static void main(String[] args) {
MyInteger m=MyInteger.valueOf(30);
System.out.println(m);
}
}
运行结果:
String 类、StringBuilder 类、StringBuffer 类是三个字符串相关类。String 类是的对象代表不可变的字符序列,StringBuilder 类和 StringBuffer 类代表可变字符序列。关于这三个类详细的用法,在笔试面试以及实际开发中经常用到,我们必须掌握好它们。
String 类对象代表不可变的 Unicode 字符序列,因此我们可以将 String 对象称为“不可变对象”。 那什么叫做“不可变对象”呢?指的是对象内部的成员变量的值无法再改变。我们打开 String 类的源码,如图所示:
我们发现字符串内容全部存储到 value[ ]数组中,而变量 value 是 final 类型的,也就是常量(即只能被赋值一次)。 这就是“不可变对象”的典型定义方式。
我们发现String 的某些方法,比如:substring()是对字符串的截取操作,但本质是读取原字符串内容生成了新的字符串。测试代码如下:
package cn.pxy.test;
public class TestString1 {
public static void main(String[] args) {
String s1=new String("abcdef");
String s2=s1.substring(2,4);
//打印:ab199863
System.out.println(Integer.toHexString(s1.hashCode()));
//打印:c61,显然s1、s2不是同一个对象
System.out.println(Integer.toHexString(s2.hashCode()));
}
}
运行结果:
在遇到字符串常量之间的拼接时,编译器会做出优化,即在编译期间就会完成字符串的拼接。因此,在使用==进行 String 对象之间的比较时,我们需要特别注意:
//字符串拼接时的优化
package cn.pxy.test;
public class TestString2 {
public static void main(String[] args) {
//编译器做了优化,直接在编译的时候将字符串进行拼接
String str1="hello"+"java";//相当于str1="hello java"
String str2="hellojava";
System.out.println(str1==str2);//true
String str3="hello";
String str4="java";
//编译的时候不知道变量中存储的是什么,所以没办法在编译的时候优化
String str5=str3+str4;
System.out.println(str2==str5);//false
}
}
运行结果:
StringBuffer 和 StringBuilder 非常类似,均代表可变的字符序列。 这两个类都是抽象类 AbstractStringBuilder 的子类,方法几乎一模一样。打开 AbstractStringBuilder的部分源码如下:
abstract class AbstractStringBuilder implements Appendable, CharSequence {
/**
* The value is used for character storage.
*/
char value[ ];
//以下代码省略
}
显然,内部也是一个字符数组,但这个字符数组没有用 final 修饰,随时可以修改。因此,StringBuilder 和 StringBuffer 称之为“可变字符序列”。
那两者有什么区别:
*StringBuffer JDK1.0 版本提供的类,线程安全,做线程同步检查, 效率较低。
*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 类含义类似的方法:
public int indexOf(String str)
public int indexOf(String str,int fromIndex)
public String substring(int start)
public String substring(int start,int end)
public int length()
char charAt(int index)
StringBuffer/StringBuilder 基本用法:
package cn.pxy.test;
public class TestStringBufferAndBuilder {
public static void main(String[] args) {
/**StringBuilder*/
StringBuilder sb=new StringBuilder();
for(int i=0;i<7;i++) {
sb.append((char)('a'+i));//追加单个字符
}
System.out.println(sb.toString());//转换成String输出
sb.append(",I can sing my abc!");//追加字符串
System.out.println(sb.toString());
/**StringBuffer,下面的方法同样适用StringBuilder*/
StringBuffer sb2=new StringBuffer("胖咸鱼先生说");
sb2.insert(0, "爱").insert(0, "我");//插入字符串
System.out.println(sb2);
sb2.delete(0, 2);//删除子字符串
System.out.println(sb2);
sb2.deleteCharAt(0).deleteCharAt(0);//删除某个字符
System.out.println(sb2.charAt(0));//获取某个字符
System.out.println(sb2.reverse());//字符串逆序
}
}
运行结果:
String 使用的陷阱:
String 一经初始化后,就不会再改变其内容了。对 String 字符串的操作实际上是对其副本(原始拷贝)的操作,原来的字符串一点都没有改变。比如:String s =”a”; 创建了一个字符串s = s+”b”; 实际上原来的”a”字符串对象已经丢弃了,现在又产生了另一个字符串s+”b”(也就是”ab”)。 如果多次执行这些改变串内容的操作,会导致大量副本字符串对象存留在内存中,降低效率。如果这样的操作放到循环中,会极大影响程序的时间和空间性能,甚至会造成服务器的崩溃。
相反,StringBuilder 和 StringBuffer 类是对原字符串本身操作的,可以对字符串进行修改而不产生副本拷贝或者产生少量的副本。因此可以在循环中使用。
示例:String和StringBuilder在字符串频繁修改时的效率测试
package cn.pxy.test;
public class Test {
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;//相当于产生了5000个对象
}
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));
}
}
运行结果:
时间是一维的,我们需要一把刻度尺来表达和度量时间。在计算机世界,我们把 1970 年 1 月 1 日 00:00:00 定为基准时间,每个度量单位是毫秒(1 秒的千分之一)。
我们用 long 类型的变量来表示时间,从基准时间往前几亿年,往后几亿年都能表示。如果想获得现在时刻的“时刻数值”,可以使用:
long now = System.currentTimeMillis();//代表当前时刻的毫秒数
这个“时刻数值”是所有时间类的核心值,年月日都是根据这个“数值”计算出来的。我们工作学习涉及的时间相关类有如下这些:
在标准 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 cn.pxy.test;
import java.util.Date;
public class Test {
public static void main(String[] args) {
long nowNum=System.currentTimeMillis();//当前时刻对应的毫秒数
Date d=new Date();//没有传参,代表当前时刻的对象
System.out.println(d.getTime());//返回时间对应的毫秒数
Date d2=new Date(1000L*3600*24*365*150);//距离1970年150年
System.out.println(d2);
}
}
运行结果:
JDK1.1 之前的Date 包含了:日期操作、字符串转化成时间对象等操作。JDK1.1 之后,日期操作一般使用Calendar 类,而字符串的转化使用 DateFormat 类。
DateFormat 类的作用:
把时间对象转化成指定格式的字符串。反之,把指定格式的字符串转化成时间对象。DateFormat 是一个抽象类,一般使用它的的子类 SimpleDateFormat 类来实现。
示例:DateFormat类和SimpleDateFormat类的使用
package cn.pxy.test;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class TestDateFormat {
public static void main(String[] args) throws ParseException {
//new出SimpleDateFormat对象
SimpleDateFormat s1=new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
SimpleDateFormat s2=new SimpleDateFormat("yyyy-MM-dd");
//将时间对象转换成字符串
String daytime=s1.format(new Date());
System.out.println(daytime);
System.out.println(s2.format(new Date()));
System.out.println(new SimpleDateFormat("hh:mm:ss").format(new Date()));
//将符合指定格式的字符串转化成时间对象,字符串格式需要和指定格式一致
String time="2027-10-07";
Date date=s2.parse(time);//字符串转化为时间对象
System.out.println("date1:"+date);
time="2027-10-07 20:15:30";
date=s1.parse(time);
System.out.println("date2:"+date);
}
}
运行结果:
代码中格式化字符的含义 | |||
字母 | 日期或时间元素 | 表示 | 示例 |
G | Era标志符 | Text | AD |
y | 年 | Year | 1996;96 |
M | 年中的月份 | Month | July;Jul;07 |
w | 年中的周数 | Number | 27 |
W | 月中的周数 | Number | 2 |
D | 年中的天数 | Number | 189 |
d | 月中的天数 | Number | 10 |
F | 月中的星期 | Number | 2 |
E | 星期中的天数 | Test | Tuesday;Tue |
a | Am/pm标记 | Test | PM |
H | 一天中的小时数(0-23) | Number | 0 |
k | 一天中的小时数(1-24) | Number | 24 |
K | am/pm中的小时数(0-11) | Number | 0 |
h | am/pm中的小时数(1-12) | Number | 12 |
m | 小时中的分钟数 | Number | 30 |
s | 分钟中的秒数 | Number | 55 |
S | 毫秒数 | Number | 978 |
z | 时区 | General time zone | Pacific Standard Time; PST;GMT-08:00 |
Z | 时区 | RFC 822 time zone | 0800 |
时间格式字符也可以为我们提供其他的便利。比如:获得当前时间是今年的第几天。代码如下:
package cn.pxy.test;
import java.text.SimpleDateFormat;
import java.util.Date;
public class TestDateFormat {
public static void main(String[] args) {
SimpleDateFormat s1=new SimpleDateFormat("D");
SimpleDateFormat s2=new SimpleDateFormat("yyyy-MM-dd");
System.out.println("今天是:"+s2.format(new Date()));
String daytime=s1.format(new Date());
System.out.println("今天是一年中的第几天:"+daytime);
}
}
运行结果:
Calendar 类是一个抽象类,为我们提供了关于日期计算的相关功能,比如:年、月、日、时、分、秒的展示和计算。
GregorianCalendar 是 Calendar 的一个具体子类,提供了世界上大多数国家/地区使用的标准日历系统。
注:月份的表示,一月是 0,二月是 1,以此类推,12 月是 11。 因为大多数人习惯于使用单词而不是使用数字来表示月份,这样程序也许更易读,父类 Calendar 使用常量来表示月份:JANUARY、FEBRUARY 等等。
示例:GregorianCalendar 类和 Calendar 类的使用
package cn.pxy.test;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
public class TestCalendar {
public static void main(String[] args) {
//得到相关日期元素
GregorianCalendar calendar=new GregorianCalendar(2999,10,9,22,10,50);
int year=calendar.get(Calendar.YEAR);//打印2999
int month=calendar.get(Calendar.MONTH);//打印10
int day=calendar.get(Calendar.DAY_OF_MONTH);//打印9
int day2=calendar.get(Calendar.DATE);//打印9
//日:Calendar.DAY_OF_MONTH与Calendar.DATE同义
int date=calendar.get(Calendar.DAY_OF_WEEK);//打印7
//星期几,这里是:1-7.周日是1,周一是2.。。。周六是7
System.out.println(year);
System.out.println(month);
System.out.println(day);
System.out.println(day2);
System.out.println(date);
//设置日期
GregorianCalendar calendar2=new GregorianCalendar();
calendar2.set(Calendar.YEAR, 2999);
calendar2.set(Calendar.MONTH, Calendar.FEBRUARY);
calendar2.set(Calendar.DATE, 3);
calendar2.set(Calendar.HOUR_OF_DAY, 10);
calendar2.set(Calendar.MINUTE, 20);
calendar2.set(Calendar.SECOND, 23);
printCalendar(calendar2);
//计算日期
GregorianCalendar calendar3=new GregorianCalendar(2999,10,9,22,10,50);
calendar3.add(Calendar.MONTH, -7);//月份减7
calendar3.add(Calendar.DATE, 7);//增加7天
printCalendar(calendar3);
//日历对象和时间对象转换
Date d=calendar3.getTime();
GregorianCalendar calendar4=new GregorianCalendar();
calendar4.setTime(new Date());
}
static void printCalendar(Calendar calendar) {
int year=calendar.get(Calendar.YEAR);
int month=calendar.get(Calendar.MONTH)+1;
int day=calendar.get(Calendar.DAY_OF_MONTH);
int date=calendar.get(Calendar.DAY_OF_WEEK)-1;
String week="+((date==0)#"日": date );
int hour=calendar.get(Calendar.HOUR);
int minute=calendar.get(Calendar.MINUTE);
int second=calendar.get(Calendar.SECOND);
System.out.printf("%d年%d月%d日,星期%s%d:%d:%dn",year,month,day,week+" ",hour,minute,second);
}
}
运行结果:
java.lang.Math 提供了一系列静态方法用于科学计算;其方法的参数和返回值类型一般为 double 型。如果需要更加强大的数学运算能力,计算高等数学中的相关内容,可以使用 apache commons 下面的 Math 类库。
Math 类的常用方法:
1.abs 绝对值
2.acos,asin,atan,cos,sin,tan 三角函数
3.sqrt 平方根
4.pow(double a, double b) a 的 b 次幂
5.max(double a, double b) 取大值
6.min(double a, double b) 取小值
7.ceil(double a) 大于 a 的最小整数
8.floor(double a) 小于 a 的最大整数
9.random() 返回 0.0 到 1.0 的随机数
10.long round(double a) double 型的数据 a 转换为 long 型(四舍五入)
11.toDegrees(double angrad) 弧度->角度
12.toRadians(double angdeg) 角度->弧度
使用示例:
package cn.pxy.test;
public class TestMath {
public static void main(String[] args) {
//取整相关操作
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)
}
}
运行结果:
Math 类中虽然为我们提供了产生随机数的方法 Math.random(),但是通常我们需要的随机数范围并不是[0, 1)之间的 double 类型的数据,这就需要对其进行一些复杂的运算。如果使用 Math.random()计算过于复杂的话,我们可以使用例外一种方式得到随机数,即Random 类,这个类是专门用来生成随机数的,并且 Math.random()底层调用的就是Random 的 nextDouble()方法。
package cn.pxy.test;
import java.util.Random;
public class TestRandom {
public static void main(String[] args) {
Random rand=new Random();
//随机生成【0,1)之间的double类型的数据
System.out.println(rand.nextDouble());
//随机生成int类型允许范围之内的整型数据
System.out.println(rand.nextInt());
//随机生成【0,1)之间的float类型的数据
System.out.println(rand.nextFloat());
//随机生成false或者true
System.out.println(rand.nextBoolean());
//随机生成【0,10)之间的int类型的数据
System.out.println(rand.nextInt(10));
//随机生成【20,30)之间的int类型的数据
System.out.println(20+rand.nextInt(10));
//随机生成【20,30)之间的int类型的数据(此方法计算比较复杂)
System.out.println(20+(int)(rand.nextDouble()*10));
}
}
执行结果:
File 类用来代表文件和目录。
File 类的基本用法
java.io.File 类:代表文件和目录。 在开发中,读取文件、生成文件、删除文件、修改文件的属性时经常会用到本类。
File 类的常见构造方法:public File(String pathname)
以 pathname 为路径创建 File 对象,如果 pathname 是相对路径,则默认的当前路径在系统属性 user.dir 中存储,如示例:
package cn.pxy.test;
import java.io.File;
import java.io.IOException;
public class TestFile1 {
public static void main(String[] args) throws IOException {
System.out.println(System.getProperty("user.dir"));//获取项目根路径
File f=new File("a.txt");//相对路径,默认放到user.dir目录下面
f.createNewFile();//创建文件
File f2=new File("d:/b.txt");//绝对路径
f2.createNewFile();
}
}
创建结果:
通过 File 对象可以访问文件的属性:
File 类访问属性的方法列表 | |
方法 | 说明 |
public boolean exists() | 判断 File 是否存在 |
public boolean isDirectory() | 判断 File 是否是目录 |
public boolean isFile() | 判断 File 是否是文件 |
public long lastModified() | 返回 File 最后修改时间 |
public long length() | 返回 File 大小 |
public String getName() | 返回文件名 |
public String getPath() | 返回文件的目录路径 |
示例:
package cn.pxy.test;
import java.io.File;
import java.io.IOException;
import java.util.Date;
public class TestFile1 {
public static void main(String[] args) throws IOException {
File f = new File("d:/b.txt");
f.createNewFile();
System.out.println("File 是否存在:"+f.exists());
System.out.println("File 是否是目录:"+f.isDirectory());
System.out.println("File 是否是文件:"+f.isFile());
System.out.println("File 最后修改时间:"+new Date(f.lastModified()));
System.out.println("File 的大小:"+f.length());
System.out.println("File 的文件名:"+f.getName());
System.out.println("File 的目录路径:"+f.getPath());
}
}
运行结果:
通过 File 对象创建空文件或目录(在该对象所指的文件或目录不存在的情况下)
File 类创建文件或目录的方法列表 | |
方法 | 说明 |
createNewFile() | 创建新的File |
delete() | 删除File对应的文件 |
mkdir() | 创建一个目录;中间某个目录缺失,则创建失败 |
mkdirs() | 创建多个目录;中间某个目录缺失,则创建该缺失目录 |
示例:
//mkdir
package cn.pxy.test;
import java.io.File;
import java.io.IOException;
public class TestFile1 {
public static void main(String[] args) throws IOException{
File f = new File("d:/c.txt");
f.createNewFile();
f.delete();
File f2=new File("d:/电影/华语/大陆");
boolean flag=f2.mkdir();//目录结构中有一个不存在,则不会创建整个目录树
System.out.println(flag);//创建失败
}
}
//mkdirs
package cn.pxy.test;
import java.io.File;
import java.io.IOException;
public class TestFile1 {
public static void main(String[] args) throws IOException{
File f = new File("d:/c.txt");
f.createNewFile(); // 会在 d 盘下面生成 c.txt 文件
f.delete(); // 将该文件或目录从硬盘上删除
File f2 = new File("d:/动漫/日本/宫崎骏");
boolean flag = f2.mkdirs();//目录结构中有一个不存在也没关系;创建整个目录 树
System.out.println(flag);//创建成功
}
}
递归遍历目录结构和树状展现
在电影目录下增加几个子文件夹或者文件,用于测试。
package cn.pxy.test;
import java.io.File;
import java.io.IOException;
public class TestFile1 {
public static void main(String[] args) throws IOException{
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) {
//递归调用该方法
printFile(temp,level+1);
}
}
}
}
运行结果:
JDK1.5 引入了枚举类型。枚举类型的定义包括枚举声明和枚举体。格式如下:
enum 枚举名 {
枚举体(常量列表)
}
例如:
enum Season {
SPRING, SUMMER, AUTUMN, WINDER
}
所有的枚举类型隐性地继承自 java.lang.Enum。枚举实质上还是类!而每个被枚举的成员实质就是一个枚举类型的实例,他们默认都是 public static final 修饰的。可以直接通过枚举类型名使用它们。
注:当你需要定义一组常量时,可以使用枚举类型。但尽量不要使用枚举的高级特性,事实上高级特性都可以使用普通类来实现,没有必要引入枚举,增加程序的复杂性!
//枚举的使用
package cn.pxy.test;
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 {
星期一, 星期二, 星期三, 星期四, 星期五, 星期六, 星期日
}
运行结果: