1 基本数据类型的包装类
我们前面学习的八种基本数据类型并不是对象,为了将基本类型数据和对象之间实现互 相转化,JDK 为每一个基本数据类型提供了相应的包装类。
1.1 包装类基本知识
Java 是面向对象的语言,但并不是“纯面向对象”的,因为我们经常用到的基本数据 类型就不是对象。但是我们在实际应用中经常需要将基本数据转化成对象,以便于操作。 比如:将基本数据类型存储到 Object[ ]数组或集合中的操作等等。
为了解决这个不足,Java 在设计类时为每个基本数据类型设计了一个对应的类进行代 表,这样八个和基本数据类型对应的类统称为包装类(Wrapper Class)。
包装类均位于 java.lang 包,八种包装类和基本数据类型的对应关系如表所示:
在这八个类名中,除了 Integer 和 Character 类以外,其它六个类的类名和基本数据 类型一致,只是类名的第一个字母大写而已。
在这八个类中,除了 Character 和 Boolean 以外,其他的都是“数字型”,“数字 型”都是 java.lang.Number 的子类。
Number 类是抽象类,因此它的抽象方法,所有子类都需要提供实现。Number 类提供了 抽象方法:intValue()、longValue()、floatValue()、doubleValue(),意味着所有的“数 字型”包装类都可以互相转型。
下面我们通过一个简单的示例认识一下包装类。
初识包装类,代码如下:
的内存分析,如图所示:
1.2 包装类的用途
对于包装类来说,这些类的用途主要包含两种:
1.作为和基本数据类型对应的类型存在,方便涉及到对象的操作,如 Object[ ]、集合等 的操作。
2.包含每种基本数据类型的相关属性如最大值、最小值等,以及相关的操作方法(这些操 作方法的作用是在基本数据类型、包装类对象、字符串之间提供相互之间的转化!)
包装类的使用的代码如下:
执行的结果如图所示:
1.3 自动装箱和拆箱
自动装箱和拆箱就是将基本数据类型和包装类之间进行自动的互相转换。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()等)。
自动装箱与拆箱的功能事实上是编译器来帮的忙,编译器在编译时依据您所编写的语 法,决定是否进行装箱或拆箱动作。
自动装箱的代码如下:
自动拆箱的代码如下:
所以自动装箱与拆箱的功能是所谓的“编译器蜜糖(Compiler Sugar)”,虽然使用这 个功能很方便,但在程序运行阶段您得了解 Java 的语义。例如示例如下所示的程序是可以 通过编译的:
执行的结果为如图所示:
示例代码的运行结果之所以会出现空指针异常,是因为示例中的代码相当于如下代码:
null 表示 i 没有指向任何对象的实体,但作为对象名称是合法的(不管这个对象名称存 是否指向了某个对象的实体)。由于实际上 i 并没有指向任何对象的实体,所以也就不可能 操作 intValue()方法,这样上面的写法在运行时就会出现 NullPointerException 错误。
自动装箱与拆箱,代码如下:
1.4 包装类的缓存问题
整型、char类型所对应的包装类,在自动装箱时,对于-128~127之间的值会进行缓存 处理,其目的是提高效率。
缓存处理的原理为:如果数据在-128~127这个区间,那么在类加载时就已经为该区间 的每个数值创建了对象,并将这256个对象存放到一个名为cache的数组中。每当自动装箱 过程发生时(或者手动调用valueOf()时),就会先判断数据是否在该区间,如果在则直接 获取数组中对应的包装类对象的引用,如果不在该区间,则会通过new调用包装类的构造方法来创建对象。
下面我们以Integer类为例,看一看Java为我们提供的源码,加深对缓存技术的理解,代码如下:
这段代码中我们需要解释下面几个问题:
1.IntegerCache类为Integer类的一个静态内部类,仅供Integer类使用。
2. 一般情况下 IntegerCache.low为-128,IntegerCache.high为127。
IntegerCache.cache为内部类的一个静态属性,代码如下:
由上面的源码我们可以看到,静态代码块的目的就是初始化数组cache的,这个过程会 在类加载时完成。
包装类的缓存测试代码如下:
执行的结果如图所示:
内存分析如图所示:
1.5 自定义一个简单的包装类
演示的代码如下:
2 字符串相关类
String 类、StringBuilder 类、StringBuffer 类是三个字符串相关类。String 类是的对 象代表不可变的字符序列,StringBuilder 类和 StringBuffer 类代表可变字符序列。关于这 三个类详细的用法,在笔试面试以及实际开发中经常用到,我们必须掌握好它们。
2.1 String 类源码分析
String 类对象代表不可变的 Unicode 字符序列,因此我们可以将 String 对象称为“不 可变对象”。 那什么叫做“不可变对象”呢?指的是对象内部的成员变量的值无法再改变。 我们打开 String 类的源码,如图所示:
我们发现字符串内容全部存储到 value[ ]数组中,而变量 value 是 final 类型的,也就 是常量(即只能被赋值一次)。 这就是“不可变对象”的典型定义方式。
我们发现在前面学习 String 的某些方法,比如:substring()是对字符串的截取操作, 但本质是读取原字符串内容生成了新的字符串。测试代码如下:
执行结果如图所示:
在遇到字符串常量之间的拼接时,编译器会做出优化,即在编译期间就会完成字符串的 拼接。因此,在使用==进行 String 对象之间的比较时,我们需要特别注意。
字符串常量拼接时的优化代码如下:
执行的结果为如图所示:
String 类常用的方法有:
1.String 类的下述方法能创建并返回一个新的 String 对象: concat()、 replace()、 substring()、 toLowerCase()、 toUpperCase()、trim()。
2.提 供 查 找 功 能 的 有 关 方 法 : endsWith() 、 startsWith() 、 indexOf() 、 lastIndexOf()。
3.提供比较功能的方法: equals()、equalsIgnoreCase()、compareTo()。 其它方法: charAt() 、length()。
2.2 StringBuffer 和 StringBuilder
StringBuffer 和 StringBuilder 非常类似,均代表可变的字符序列。 这两个类都是抽 象类 AbstractStringBuilder 的子类,方法几乎一模一样。我们打开 AbstractStringBuilder 的源码。
AbstractStringBuilder 部分源码如下:
显然,内部也是一个字符数组,但这个字符数组没有用 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 类含义类似的方法:
StringBuffer/StringBuilder 基本用法的代码如下:
执行的结果如图所示:
2.3 不可变和可变字符序列使用陷阱
· String 使用的陷阱
String 一经初始化后,就不会再改变其内容了。对 String 字符串的操作实际上是对其 副本(原始拷贝)的操作,原来的字符串一点都没有改变。
比如: String s =“a”; 创建了一个字符串。
s = s+“b”; 实际上原来的"a"字符串对象已经丢弃了,现在又产生了另一个字符串 s+“b”(也就是"ab")。 如果多次执行这些改变串内容的操作,会导致大量副本字符串对象 存留在内存中,降低效率。如果这样的操作放到循环中,会极大影响程序的时间和空间性能, 甚至会造成服务器的崩溃。
相反,StringBuilder 和 StringBuffer 类是对原字符串本身操作的,可以对字符串进行 修改而不产生副本拷贝或者产生少量的副本。因此可以在循环中使用。
String和StringBuilder在字符串频繁修改时的效率测试代码如下:
执行的结果为如图所示:
3 时间处理相关类
“时间如流水,一去不复返”,时间是一维的。所以,我们需要一把刻度尺来表达和度 量时间。在计算机世界,我们把 1970 年 1 月 1 日 00:00:00 定为基准时间,每个度量单 位是毫秒(1 秒的千分之一),如图所示:
我们用 long 类型的变量来表示时间,从基准时间往前几亿年,往后几亿年都能表示。 如果想获得现在时刻的“时刻数值”,可以使用:
这个“时刻数值”是所有时间类的核心值,年月日都是根据这个“数值”计算出来的。 我们工作学习涉及的时间相关类有如下这些:
3.1 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)。
Date类的使用代码如下:
执行的结果为如图所示:
查看 API 文档大家可以看到其实 Date 类中的很多方法都已经过时了。JDK1.1 之前的 Date 包含了:日期操作、字符串转化成时间对象等操作。JDK1.1 之后,日期操作一般使用 Calendar 类,而字符串的转化使用 DateFormat 类。
3.2 DateFormat 类和 SimpleDateFormat 类
·DateFormat 类的作用 把时间对象转化成指定格式的字符串。反之,把指定格式的字符串转化成时间对象。 DateFormat 是一个抽象类,一般使用它的的子类 SimpleDateFormat 类来实现。
DateFormat 类和 SimpleDateFormat 类的使用代码如下:
代码中的格式化字符的具体含义见表:
时间java哪些基础类格式字符也可以为我们提供其他的便利。比如:获得当前时间是今年的第几天。代码如下:
执行的结果为如图所示:
3.3 Calendar 日历类
Calendar类是一个抽象类,为我们提供了关于日期计算的相关功能,比如:年、月、 日、时、分、秒的展示和计算。
GregorianCalendar是Calendar的一个具体子类,提供了世界上大多数国家/地区 使用的标准日历系统。
GregorianCalendar类和Calendar类的使用代码如下:
执行的结果如图所示:
4 其他常用类
4.1 Math 类
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) 角度->弧度
Math 类的常用方法代码如下:
执行的结果如图所示:
4.2 Random 类
Math 类中虽然为我们提供了产生随机数的方法 Math.random(),但是通常我们需要 的随机数范围并不是[0, 1)之间的 double 类型的数据,这就需要对其进行一些复杂的运算。 如果使用 Math.random()计算过于复杂的话,我们可以使用例外一种方式得到随机数,即 Random 类,这个类是专门用来生成随机数的,并且 Math.random()底层调用的就是 Random 的 nextDouble()方法。
Random 类的常用方法代码如下:
执行的结果为如图所示:
4.3 File 类
File类用来代表文件和目录。
4.3.1 File类的基本用法
java.io.File 类:代表文件和目录。 在开发中,读取文件、生成文件、删除文件、修改 文件的属性时经常会用到本类。
File 类的常见构造方法:public File(String pathname)
以 pathname 为路径创建 File 对象,如果 pathname 是相对路径,则默认的当前路径 在系统属性 user.dir 中存储。
使用File类创建文件的代码如下:
通过File对象可以访问文件的属性:
使用File类访问文件或目录属性的代码如下:
执行的结果为如图所示:
通过 File 对象创建空文件或目录(在该对象所指的文件或目录不存在的情况下)如表所示:
使用mkdirs创建目录的代码如下:
执行的结果如图所示:
使用mkdirs创建目录的代码如下:
执行的结果如图所示:
4.3.2 递归遍历目录结构和树状展现
本节结合前面给大家讲的递归算法,展示目录结构。大家可以先建立一个目录,下面增 加几个子文件夹或者文件,用于测试。
演示的代码如下:
执行的结果为如图所示:
4.4 枚举
JDK1.5 引入了枚举类型。枚举类型的定义包括枚举声明和枚举体。格式如下:
枚举体就是放置一些常量。我们可以写出我们的第一个枚举类型,代码如下:
所有的枚举类型隐性地继承自 java.lang.Enum。枚举实质上还是类!而每个被枚举的 成员实质就是一个枚举类型的实例,他们默认都是 public static final 修饰的。可以直接通 过枚举类型名使用它们。
枚举的使用代码如下:
版权声明:
本文来源网络,所有图片文章版权属于原作者,如有侵权,联系删除。
本文网址:https://www.bianchenghao6.com/h6javajc/26261.html