手动实现包装类来理解不变类/不变对象、最终变量、常量

  • 1、被 final 修饰的类就是所谓的 【最终类】 ( 或者称作 不可变类 / 不变类 )
    2、被 final 修饰的类是没有子类的
    3、被 final 修饰的变量被称作 【最终变量】 ( 可以是 类变量、实例变量 、局部变量 、参数 )
    4、被 final 修饰的 类变量 被称作【常量】
    public final class Decimal {
    // 被 final 修饰的 类变量 被称作【常量】
    public final static long MAX_VALUE = 0x7fffffffffffffffL ;
    // 常量名称中所有单词一律大写,多个单词之间使用 下划线 隔开
    public final static long MIN_VALUE = 0x8000000000000000L ;
    // 被 final 修饰的 实例变量 在初始化之后不能再次赋值
    private final long value ;
    {
    // System.out.println( “this.value : “ + this.value ); // 错误: 可能尚未初始化变量value
    }
    public Decimal ( long value ) {
    // System.out.println( “this.value : “ + this.value ); // 错误: 可能尚未初始化变量value
    this.value = value ;
    System.out.println( “this.value : “ + this.value );
    }
    public long getValue(){
    return this.value ;
    }
    public void setValue( long value ) {
    // this.value = value ; // 错误: 无法为最终变量value分配值
    }
    public static Decimal valueOf( long value ) {
    return new Decimal( value );
    }
    public static void main(String[] args) {
    // 在 堆内存中所创建的 Decimal 对象是不可变的 ( 因为其内部 实例变量的 值不可更改 ) 【不可变对象】
    Decimal d = new Decimal( 100L );
    System.out.println( d.getValue() );
    System.out.println( System.identityHashCode( d ) );
    System.out.println( “~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~” );
    // 但是 Decimal 类型的变量 d 的值 是可以改变的,也就是 d 可以引用不同的 Decimal 实例
    d = new Decimal( 200L );
    System.out.println( d.getValue() );
    System.out.println( System.identityHashCode( d ) );
    System.out.println( “~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~” );
    d = Decimal.valueOf( 300L );
    System.out.println( d.getValue() );
    System.out.println( System.identityHashCode( d ) );
    }
    }

获得 某种 基本数据类型 数值 对应的 包装类类型的实例 /
获得 某种 基本数据类型 数值 对应的 包装类类型的实例
在 Java 9 之前使用 包装类的 构造方法创建实例
从 Java 9 开始使用 valueOf 等方法来获取 实例
/
public class WrapperTest1 {
public static void main(String[] args) {
// Java 9 之前使用 构造方法创建 相应的实例
byte b = 100 ;
Byte first = new Byte( b); // 但是从 Java 9 开始 包装类的 构造方法均已废弃
System.out.println( first ); // first.toString()
short s = 9527 ;
Short second = new Short( s ); // 但是从 Java 9 开始 包装类的 构造方法均已废弃
System.out.println( second ); // second.toString()
// 从 Java 9 开始 建议使用 工厂方法来创建 包装类的实例
Integer third = Integer.valueOf( 999 ) ; // 将 int 类型的数值 封装到一个 Integer 实例中
System.out.println( third ); // third.toString()
Long fourth = Long.valueOf( 999L ) ; // 将 long 类型的数值 封装到一个 Long 实例中
System.out.println( fourth ); // fourth.toString()
System.out.println( “~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~” );
System.out.println( fourth.getClass() ); // 获得运行时类型
System.out.println( fourth.hashCode() ); // 获得哈希码值
System.out.println( fourth.toString() ); // 获取字符串表示形式
// 即使变量 fourth 指向的 对象对应的类 重写 hashCode 方法,
// 我们仍然可以通过 identityHashCode 来获取 由 Object 提供的 hashCode 方法所返回的值
System.out.println( System**.identityHashCode( fourth ) );
}
}

八种基本类型对应的包装类都可以通过valueOf方法将相应基本类型

1、八种基本数据类型对应的包装类类型
byte ——-> Byte
short ——> Short
int ———> Integer
long ——-> Long
float ——> Float
double —> Double
boolean —> Boolean
char ——-> Character
2、八种基本类型对应的包装类都有一个将相应的基本类型数据封装成该类对象的 valueOf 方法
public static Xxx valueOf( xxx value )
*/

public class WrapperTest2 {
public static void main(String[] args) {
// valueOf 接受一个与之对应的基本数据类型的数值做参数,返回一个当前类型的对象
Byte b = Byte.valueOf( (byte) 100 ); // byte —-> Byte
System.out.println( b + “ , “ + b.getClass().getName() );
Short s = Short.valueOf( (short)1000 ); // short —-> Short
System.out.println( s + “ , “ + s.getClass().getName() );
Integer i = Integer.valueOf( 9527 ); // int —-> Integer
System.out.println( i + “ , “ + i.getClass().getName() );
Long o = Long.valueOf( 123456789L ); // long —-> Long
System.out.println( o + “ , “ + o.getClass().getName() );
Float f = Float.valueOf( 123.456789F ); // float —> Float
System.out.println( f + “ , “ + f.getClass().getName() );
Double d = Double.valueOf( 3.14 ); // double —-> Double
System.out.println( d + “ , “ + d.getClass().getName() );
Boolean z = Boolean.valueOf( true ); // boolean —-> Boolean
System.out.println( z + “ , “ + z.getClass().getName() );
Character c = Character.valueOf( ‘汉’ ); // char ——> Character
System.out.println( c + “ , “ + c.getClass().getName() );
}
}

除了 Character 外,八种基本类型对应的包装类都有一个将字符串解析

除了 Character 外,八种基本类型对应的包装类都有一个将字符串解析并封装成该类对象的 valueOf 方法
public static Xxx valueOf( String value )

public class WrapperTest3 {
public static void main(String[] args) {
// valueOf 接受一个与之对应的基本数据类型的数值做参数,返回一个当前类型的对象
Byte b = Byte.valueOf( “100” );
System.out.println( b + “ , “ + b.getClass().getName() );
Short s = Short.valueOf( “1000” );
System.out.println( s + “ , “ + s.getClass().getName() );
Integer i = Integer.valueOf( “9527” );
System.out.println( i + “ , “ + i.getClass().getName() );
Long o = Long.valueOf( “123456789” );
System.out.println( o + “ , “ + o.getClass().getName() );
Float f = Float.valueOf( “123.456789F” );
System.out.println( f + “ , “ + f.getClass().getName() );
Double d = Double.valueOf( “3.14” );
System.out.println( d + “ , “ + d.getClass().getName() );
Boolean z = Boolean.valueOf( “true” );
System.out.println( z + “ , “ + z.getClass().getName() );
// Character c = Character.valueOf( “汉” ); // Character 没有定义 valueOf( String ) 方法
}
}

将字符串形式表示的值解析为基本数据类型的数值

/*
1、基本数据类型的数值 被包装 到 一个相应的包装类类型的实例中
Java 9 之前 可以使用 包装类的构造方法实现
Java 9 开始 需要使用 valueOf 方法 【 public static 包装类类型 valueOf( 基本类型 value ) 】

2、将 用字符串形式 表示的值解析为 基本类型的数值 后,
再将 基本类型的数值 封装到一个相应的包装类类型的实例中
public static Xxx valueOf( String s )
注: Characher 类中没有 valueOf( String ) 方法

3、将 字符串形式 表示的值解析为 基本类型的数值

八种基本数据类型对应的包装类中,除了 Character 类之外,都有一个可以将字符串按照十进制解析其相应基本类型数值的方法

byte Byte.parseByte( String s )
short Short.parseShort( String s )
int Integer.parseInt( String s )
long Long.parseLong( String s )
float Float.parseFloat( String s )
double Double.parseDouble( String s )
boolean Boolean.parseBoolean( String s )

对于整数类型来说,其相应的包装类中可以将 字符串形式 表示的值 按照 指定的 radix 解析为十进制整数
byte Byte.parseByte( String s , int radix )
short Short.parseShort( String s , int radix )
int Integer.parseInt( String s , int radix )
long Long.parseLong( String s , int radix )

4、通常在 Java 语言中使用的 radix 的范围是 [ 2 , 36 ]
Character.MIN_RADIX 、Character.MAX_RADIX
*/

public class WrapperTest4 {
public static void main( String[] args ) {
final String x = “101”;
// Byte.parseByte( “10010” ) // NumberFormatException: Value out of range. Value:”10010” Radix:10
byte b1 = Byte.parseByte( x ); // String ====> byte
System.out.println( b1 );
byte b2 = Byte.parseByte( x , 2 ) ; // String ====> byte
System.out.println( b2 );
short s1 = Short.parseShort( x ); // String ====> byte
System.out.println( s1 );
short s2 = Short.parseShort( x , 16 ) ; // String ====> byte
System.out.println( s2 );
int i1 = Integer.parseInt( x ); // String ====> byte
System.out.println( i1 );
int i2 = Integer.parseInt( x , 20 ) ; // String ====> byte
System.out.println( i2 );
// 在 Long.parseLong( String ) 方法的参数中,是否可以使用 “100010L” 形式 ?
long o1 = Long.parseLong( x ); // String ====> byte
System.out.println( o1 );
long o2 = Long.parseLong( x , 36 ) ; // String ====> byte
System.out.println( o2 );
System.out.println( “~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~” );
float f1 = Float.parseFloat( “3.14F” );
System.out.println( f1 );
float f2 = Float.parseFloat( “3.14” );
System.out.println( f2 );
double d1 = Double.parseDouble( “3.14D” );
System.out.println( d1 );
double d2 = Double.parseDouble( “3.14” );
System.out.println( d2 );
System.out.println( “~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~” );
boolean z1 = Boolean.parseBoolean( “tRue” );
System.out.println( z1 ); // true
boolean z2 = Boolean.parseBoolean( “ true “ ); // 注意最后多个空格
System.out.println( z2 ); // false
boolean z3 = Boolean.parseBoolean( “” );
System.out.println( z3 ); // false
// System.out.println( “~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~” );
// Character 类中没有 parseChar( String ) 方法
// char ch = Character.parseChar( “A” ); // 错误: 找不到符号
}
}

将 基本数据类型的数值 转换为 字符形式 表示

/*
1、将 基本数据类型的数值 转换为 字符形式 表示
2、这里仅测试 Integer 类中的 toString 方法,其它包装类中的方法请自行测试
3、除了 Integer 类之外,其它包装类中是否有 toBinaryString 、toOctalString 、toHexString 等方法 ?
*/
public class WrapperTest5 {
public static void main( String[] args ) {
final int x = 9527 ;
String s = x + “” ; // 【近”串”者”串”】
System.out.println( s + “ , “ + s.getClass().getName() );
String ss = Integer.toString( x ); // 返回 整数的 十进制 字符串 形式
System.out.println( ss + “ , “ + ss.getClass().getName() );
String binary = Integer.toString( x, 2 ) ; // 返回 整数的 二进制 字符串 形式
System.out.println( binary + “ , “ + binary.getClass().getName() );
String hex = Integer.toString( x, 16 ) ; // 返回 整数的 十六进制 字符串 形式
System.out.println( hex + “ , “ + hex.getClass().getName() );
System.out.println( “~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~” );
String b = Integer.toBinaryString( x ); // 返回 整数的 二进制 字符串 形式
System.out.println( b );
String o = Integer.toOctalString( x ); // 返回 整数的 八进制 字符串 形式
System.out.println( o );
String h = Integer.toHexString( x ); // 返回 整数的 十六进制 字符串 形式
System.out.println( h );
}
}

理解基本数据类型的数值 封装到 其相应包装类类型的实例中后,即可

/*
1、Byte 、Short 、Integer 、Long 、Float 、Double 都继承了 Number 类
2、将 基本数据类型的数值 封装到 其相应包装类类型的实例中后,可以将这个数值当做一个对象来对待
/

理解自动装箱(auto-boxing)

/*
1、JDK 1.5 老特性 自动装箱 ( auto-boxing )
所谓自动装箱,就是将 基本类型 的数值 “自动包装” 到一个 包装类类型的实例中
2、将 基本类型的值 赋值给 包装类类型的 引用变量 时,就会发生 自动装箱 ( auto-boxing )
*/

public class AutoBoxingTest {
public static void main(String[] args) {
int x = 100 ; // 基本类型 的变量中直接存储数值本身
System.out.println( “x : “ + x );
// 将 基本类型的值 赋值给 包装类类型的 引用变量 时,就会发生 自动装箱 ( auto-boxing )
Integer o = x ; // 引用类型 的变量中存储的是 堆内存 中某个对象的 地址
System.out.println( “o : “ + o );
System.out.println( System.identityHashCode( o ) );
System.out.println( Integer.toHexString( System.identityHashCode( o ) ) );
System.out.println( “~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~” );
Integer n = Integer.valueOf( x ); // 手动装箱
System.out.println( “n : “ + n );
System.out.println( “= = = = = = = = = = = = = = =” );
Object object = 100L ; // auto-boxing
System.out.println( “object : “ + object );
System.out.println( “运行时类型 : “ + object.getClass().getName() );
System.out.println( “= = = = = = = = = = = = = = =” );
// JVM 会为引用类型的数组的每个元素赋予的默认值都是 null
Object[] array = new Object[ 5 ] ;
for (int i = 0; i < array.length; i++) {
System.out.print( array[ i ] );
System.out.print( i < array.length - 1 ? “ , “ : “\n” );
}
array[ 0 ] = 100 ; // auto-boxing
array[ 1 ] = 200D ; // auto-boxing
array[ 2 ] = 300F ; // auto-boxing
array[ 3 ] = 400L ; // auto-boxing
array[ 4 ] = 500 ; // auto-boxing
for (int i = 0; i < array.length; i++) {
Object t = array[ i ] ;
System.out.println( t + “ , “ + t.getClass().getName() );
}
}
}

理解自动拆箱 ( auto-unboxing )

/*
1、JDK 1.5 老特性 自动拆箱 ( auto-unboxing )
所谓自动拆箱,就是将 包装类类型的实例中所封装的 基本类型的值 取出来,仍然以基本类型数值的方式来运算
2、当将一个包装类类型的引用变量的值 “赋值” 给一个基本数据类型的变量时,会发生 自动拆箱
3、用一个包装类类型的引用变量 参与 运算时,会发生自动拆箱
/

public class AutoUnboxingTest {
public static void main(String[] args) {
Integer x = Integer.valueOf( 9527 );
// 当将一个包装类类型的引用变量的值 “赋值” 给一个基本数据类型的变量时,会发生 自动拆箱 ( auto-boxing )
int i = x ; // auto-unboxing
System.out.println( i );
int n = x.intValue() ; // JDK 1.5 之前需要 手动拆箱
System.out.println( n );
Long ox = Long.valueOf( 10000L );
long o = ox ; // 这里对应的 手动拆箱 代码是 ox.longValue()
System.out.println( o );
System.out.println( “~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~” );
Double d = Double.valueOf( 3.14 );
// 使用 包装类 类型的引用变量 参与数学运算时,会首先自动拆箱,然后再运算
double u = d + 1 ; // auto-unboxing
System.out.println( u );
System.out.println( “~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~” );
Integer y = Integer.valueOf( 999 );
System.out.println( “y : “ + y );
System.out.println( Integer.toHexString( System.identityHashCode( y ) ) );
y++ ; // 1、auto-unboxing ( 999 ) 2、++ ( 999 —> 1000 ) 3、auto-boxing ( 1000 )
System.out.println( “y : “ + y );
System.out.println( Integer.toHexString( System.identityHashCode( y ) ) );
}
}