Java面向对象(二)

包装类 (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()方法 ---> 基本数据类型

  注意:上面的用法已经过时。

JDK 1.5 提供了自动装箱 和 自动拆箱 功能。
自动装箱:就是可以把一个基本数据类变量直接赋值给对应的包装类变量,或者赋值给Object变量(Object是所有类的父类,子类对象可以直接赋值给父类)
自动拆箱:允许把包装类对象直接赋值给一个对象的基本数据类型变量。
注意:进行自动装箱和自动拆箱时必须注意类型匹配。
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);
        }
    }
包装类可以实现基本数据类型和字符串之间的转换:
    基本数据类型  -- 通过String.valueOf(primitive)转换 --->  String对象   (另一种方法:把基本数据类型变量和""进行连接运算,系统会自动把基本类型变量装换成字符串)
    String对象  --- 通过 WrapperClass.parseXxx()方法或利用包装类Xxx(String s)的构造器 ----> 基本数据类型
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);
    }
注意:虽然包装类型的变量是引用类型,但包装类的实例可以与数值类型的值进行比较,取出包装类实例的值来比较。
 
特例:两个int类型的数组自动装箱成Integer实例,如果它的范围在-128~127之间,永远是引用cache数组中的同一个数组元素,所以相等;
        如果不在-128~127之间的整数自动封装成Integer实例,系统总是重新创建一个Integer实例,因此它们是不相等的。
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)方法,来比较两个基本类型值的大小。

处理对象

    Java对象都是Object类的实例,都可直接调用该类中的方法,这些方法提供了处理Java对象的通用方法。
    2.1  toString()
    Object类提供的toString() 方法总是返回该对象实现类的 ”类名 + @hashCode“ 值。hashCode是8位十六进制数字。
    如果用户需要自定义类能“自我描述”的功能,就必须重写Object类的toString()方法。
    2.2  == 和 equals 方法
    可以参考另一篇文章:http://www.cnblogs.com/FocusIN/p/6746759.html

类成员

    static关键字修饰的成员就是类成员
    在java类里只能包含 成员变量、方法、构造器、初始化块、内部类(包括接口、枚举)5种成员。
    其中static可以修饰 成员变量、方法、初始化块、内部类(包括接口、枚举),以static修饰的成员就是类成员。
  类变量既可以通过类来访问,也可以通过类的对象来访问。当使用类的对象来访问类变量时,系统会在底层转换为通过该类来访问类变量。
    3.1 单例类
        如果一个类始终只能创建一个实例,则这个类被称为单例类。可以参考设计模式中的单例模式:http://www.cnblogs.com/FocusIN/p/6618811.html

3.2 final修饰符

final关键字可以修改 类、变量、方法,final关键字有点类似C#里面的sealed关键字,用于表示它修饰的类、方法和变量不可改变。

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 实现接口() | 父类构造器(实参列表)

  {

    // 匿名内部类的类体部分

  }

posted @ 2017-01-22 11:27  无涯的风  阅读(271)  评论(0编辑  收藏  举报