包装类 (Wrapper Class)
为了解决8种基本数据类型的变量不能当成Object类型变量来使用,Java提供了包装类概念。分别定义了它们相应的引用类型
基本数据类型 | 包 装 类 |
byte | Byte |
short | Short |
int | Integer |
long | Long |
char | Character |
float | Float |
double | Double |
boolean | Boolean |
在JDK1.5以前,把基本数据类型变成包装类实例,需要通过对应包装类的构造器来实现,8个包装类中,除了Character之外,还可以通过传入一个字符串参数来构建包装类对象。
基本数据类型 -- 通过new WrapperClass(primitive)创建 ---> 包装类对象
包装类对象 -- 通过WrapperClass.xxxValue()方法 ---> 基本数据类型
注意:上面的用法已经过时。
public static void main(String[] args) { // 直接将基本数据类型变量赋值给包装类变量 Integer i = 5; Object obj = true; // 直接将包装类对象赋值给基本数据类型变量 int it = i; if (obj instanceof Boolean) { // 先强制类型转换,再赋值 boolean b = (Boolean) obj; System.out.println(b); } }
public static void main(String[] args) { String intStr = "123"; // 将字符串转换成基本数据类型变量 int i = Integer.parseInt(intStr); int i2 = new Integer(intStr); System.out.println(i2); String floatStr = "3.1415"; float f = Float.parseFloat(floatStr); float f2 = new Float(floatStr); System.out.println(f2); // 将基本数据类型变量转化成字符串 String s = String.valueOf(f2); System.out.println(s); Integer a = new Integer(4); // 输出 true System.out.println(a > 3.0); }
public static void main(String[] args) { Integer a = -1; Integer b = -1; // 输出 true System.out.println("a和b自动装箱后是否相等: " + (a == b)); Integer e = 128; Integer f = 128; // 输出 false System.out.println("e和f自动装箱后是否相等: " + (e == f)); Integer c = new Integer(2); Integer d = new Integer(2); // 包装类的实例实际上是引用类型,只有两个指向同一个对象才是true // 输出 false System.out.println("c和d包装类的实例是否相等:" + (c == d)); }
JDK1.7为所有的包装类提供了一个静态的compare(xxx val1, xxx val2)方法,来比较两个基本类型值的大小。
处理对象
类成员
3.2 final修饰符
final成员变量
final修饰的成员变量必须由程序员显示地指定初始值。
final修饰的类变量、实例变量能指定初始值的地方如下:
类变量:必须在静态初始化中指定初始值或声明该类变量时指定初始值,而且只能在两个地方的其中之一指定。
实例变量:必须在非静态初始化块、声明该实例变量或构造器中指定初始值,而且只能在三个地方的其中之一指定。
final局部变量
系统不会对局部变量进行初始化,必须由我们现实初始化。因此使用final修饰局部变量,既可以在定义时指定初始值,也可以不指定初始值。
如果在定义时没有定义初始值,则在后面的代码中可以对final变量赋初始值,但只能一次,不能重复赋值。
如果final修饰的的局部变量在定义时已经定义初始值,则后面代码不可以进行赋值。
final修饰基本数据类型变量和引用类型变量的区别
当使用final修饰基本数据类型变量时,不能对基本数据类型变量重新赋值,因此基本数据类型变量不能被改变。
当对于引用变量时,它保存的是一个引用,final只能保证这个引用变量所引用的地址不会改变,即一直引用同一个对象,但这个对象完全可以发生改变。
可执行“宏替换”的final变量
对于final变量,不管是类变量、实例变量,还是局部变量,只要改变量满足以下三个条件,这个final变量就不再是一个变量,而是相当于一个直接量:
1. 使用final修饰符修饰
2. 在定义该final变量时指定了初始值
3. 该初始值可以在编译时就被确定下来。
综上,该变量本质上就是一个“宏变量”,编译器会把程序中所有用到该变量的地方直接替换成该变量的值。
还有一种情况,如果被赋值的表达式只是基本的算术表达式或字符串连接运算,没有访问普通变量、调用方法,Java编译器同样会将这种final变量当成“宏变量”处理。
例如: final int a = 5 + 2; final String s = "你" + "好";
注意: 对于final实例变量而言,只有在定义该变量时指定初始化值才会有"宏变量"的效果。
final方法
final修饰的方法不可被重写。但是还是可以被重载的。
例如,Java提供的Object类中的getClass()方法,是final方法,就不可以被重写。
final类
final修饰的类不可以有子类。例如java.lang.Math类。
不可变类
指创建该类的实例后,该实例的实例变量是不可改变的。 不可变(immutable)
Java提供的8个包装类和java.lang.String类都是不可变类,它们的实例的实例变量不可改变。
如果需要创建自定义的不可变类,可遵循如下规则:
使用private和final修饰符来修饰该类的成员变量
提供带参的构造器,用来根据传入参数来初始化类里的成员变量
仅为该类里的成员变量提供getter方法,不要提供setter方法,因为普通方法无法修改final修饰的成员变量。
如果有必要,重写Object类的hasCode()和equals()方法。equal()方法根据关键成员变量来作为两个对象是否相等的标准,此外,还应该保证两个用equals()方法判断是否相等的对象的hashCode()也相等。
缓存实例的不可变类
不可变类的实例状态不可改变,可以很方便的被多个对象所共享。如果程序进程使用相同的不可变类实例,可以考虑缓存这种不可变类的实例。
内部类
类是一个独立的单元,在某些情况下,会把一个类放在另一个类的内部定义,这个定义在其他类内部的类就叫做内部类。(也叫作嵌套类)。包含内部类的类也被称为外部类。
从JDK1.1开始引入。
内部类的作用:
1. 提供了更好的封装,可以把内部类隐藏在外部类之内,不允许同一个包中的其他类访问该类。
2. 内部类成员可以访问外部类的私有数据,因为内部类被当成其外部类成员,同一个类的成员之间可以相互访问。但外部类不能访问内部类的实现细节,例如内部类的成员变量。
3. 匿名内部类适合用于创建那些仅需要一次使用的类。
从语法角度,定义内部类和外部类大致相同,内部类除了定义在其他类内部,还有以下不同:
1. 内部类比外部类可以多使用三个修饰符:private、protected、static ------外部类不可以使用这三个修饰符。
2.非静态内部类不能拥有静态成员。
编译具有一个内部类的java文件,会产生两个class文件(注意内部类和外部类的数量)
非静态内部类
大部分时候,内部类都被作为成员内部类定义,而不是作为局部内部类。成员内部类是一种与成员变量、方法、构造器和初始化块相似的类成员;局部内部类和匿名内部类则不是类成员。
成员内部类分为两种:静态内部类和非静态内部类。使用static修饰的成员内部类是静态内部类,反之,则为非静态内部类。
静态内部类
用static修饰的内部类,这个类属于外部类本身。
外部类不能直接访问内部类的成员。使用静态内部类的类名作为调用者访问类成员;使用静态内部类对象作为调用者来访问实例成员。
局部内部类
放在方法里定义的内部类,叫做局部内部类。
匿名内部类
语法格式:
new 实现接口() | 父类构造器(实参列表)
{
// 匿名内部类的类体部分
}