类除了具有普通的特性之外,还具有一些高级特性,如包、内部类等。包在整个管理中起到了非常重要的作用,使用包可以有效地管理繁杂的类文件,解决类重名问题,当在类中配合与权限修饰符使用时,可以控制其他人对类成员的访问。同时在Java语言中一个更为有效的隐藏实现细节的技巧是使用内部类,通过使用内部类机制可以向上转型为被内部类实现的公共接口。由于在类中可以定义多个内部类,所以实现接口的方式也不止一个,只要将内部类中的方法设置为类最小范围的修饰权限即可将内部类的实现细节有效地隐藏。
抽象类
(概念)所谓抽象类就是只声明方法的存在而不去具体实现它的类。抽象类不能被实例化,也就是不能创建其对象。在定义抽象类时,要在class关键字前面加上abstract关键字。定义抽象类,语法格式:
abstract class 类名{
类体
}
在抽象类中创建的,没有实际意义的,必须要子类重写的方法称为抽象方法。抽象方法只有方法的声明,而没有方法的实现,用abstract关键字进行修饰。声明一个抽象方法的基本格式:
abstract<方法返回值类型>方法名(参数列表);
方法返回值类型:必选参数,用于指定方法的返回值类型,如果该方法没有返回值,可以使用关键字void进行标识。方法返回值的类型可以是任何Java数据类型。
方法名:必选参数,用于指定抽象方法中所需的参数。当存在多个参数时,各参数之间使用逗号分隔。方法的参数可以使任何Java数据类型。
public abstract void harverst(); 在抽象类中添加一个抽象方法。
注意:抽象方法不能使用private或static关键字尽心修饰。
包含一个或多个抽象方法的类必须被声明为抽象类。因为抽象方法没有定义方法的实现部分,如果不声明为抽象类,这个类将可以生成对象,这时当用户调用抽象方法时,程序就不知道如何处理了。
定义一个水果类fruit,该类为抽象类,并在该类中定义一个抽象方法,同时在其子类中铜鼓哦重写的方法(@override)实现该抽象方法。
内部类
(概念)如果在一个类中再定义一个类,就将在类中再定义的那个类成为内部类。内部类可分为成员内部类和局部内部类以及匿名内部类。
成员内部类
(用法)在一个类中使用内部类可以在内部类中直接存取其所在类的私有成员变量。语法格式为:
public class outerclass{
private class innerclass{
//...
}
}
在内部类中可以随意使用外部类的成员方法和成员变量,尽管这些类成员被修饰为private。尽管成员变量i以及成员方法g()都在外部类中被修饰为private,但在内部类中可以直接使用外部类中的类成员。
内部类的实例一定要绑定在外部类的实例上,如果在外部类中初始化一个内部类对象,那么内部类对象就会绑定在外部类对象上。内部类初始化方式与其他类初始化方式相同,都是使用new关键字。
外部类创建内部类实例时与其他类创建对象引用时相同。内部类可以访问它的外部类的成员,但内部类的成员只有在内部类的范围之内是可知的,不能被外部类使用。例如,如果将内部类的成员变量y再次赋值时,将会出错,但是如果使用内部类对象引用调用成员变量y即可。图中反映了内部类innerclass对象与外部类outerclass对象的关系。
注意:如果在外部类和非静态方法之外实例化内部类对象,需要使用“外部类.内部类”的形式指定该对象的类型。
在主方法中,如果不适用doit()方法返回内部类对象引用,可以直接使用内部类实例化内部类对象,但由于是在主方法中实例化内部类对象,必须在new操作符之前提供一个外部类的引用。
注意:在实例化内部类对象时,不能在new操作符之前使用外部类名称那种形式来实例化内部类对象,而应该使用外部类的对象来创建其内部类对象。内部类对象会依赖于外部类对象,除非已经存在一个外部类对象,否则类中不会出现内部类对象。
使用this关键字获取内部类与外部类的引用
如果外部类中定义的成员变量与内部类的成员变量名称相同,可以使用this关键字。
在类中如果内部类与外部类遇到成员变量重名的情况可以使用this关键字进行处理,例如在内部类中使用this.x语句可以调用内部类的成员变量x,而使用thesamename.this.x语句可以调用外部类的成员变量x,即使用外部类名称后跟一个点操作符和this关键字便可获取外部类的一个引用。
在内存中所有对象被放置在堆中,将方法以及方法中的形参或局部变量放置在栈中。在栈中的doit()方法执行内部类的对象,而内部类的对象与外部类的对象是相互依赖的,outer.this对象指向外部类对象。
局部内部类
(概念)局部内部类是指在类的方法中定义的内部类,它的作用范围也是在这个方法体内。
将内部类定义在sell()方法内部,但是有一点值得注意,内部类apple是sell()方法的一部分,并非selloutclass方法的一部分,所以在sell()方法的外部不能访问该内部类,但是该内部类可以访问当前代码块的常量以及此外部类的所有成员。
匿名内部类
(概念)在编写程序代码时,不一定要给内部类取一个名字,可以直接以对象名来代替。匿名内部类的所有实现代码都需要在大括号之间进行编写。
说明:在图形化编程的事件监控器代码中,会大量使用匿名内部类,这样可以大大简化代码,并增强代码的可读性。匿名内部类的语法格式如下:return new A(){……//内部类体};
其中,A表示对象名。由于匿名内部类没有名称,所以匿名内部类使用默认构造方法来生成匿名内部类的对象。在匿名内部类定义结束后,需要加分号标识,这个分号并不代表定义内部类结束的标识,而代表创建匿名内部类的引用表达式的标识。
注意:匿名内部类编译以后,会产生以“外部类名$序号”为名称的.class文件,序号以1~n排列,分别代表1~n个匿名内部类。
静态内部类
(概念)在内部类前添加修饰符static,这个内部类就变为静态内部类。一个静态内部类中可以声明static成员,但是在非静态内部类中不可以声明静态成员。静态内部类有一个最大的特点,就是不可以使用外部类的非静态成员,所以静态内部类在程序开发中比较少见。
普通的内部类对象隐式地在外部保存了一个引用,指向创建它的外部类对象,但如果内部类被定义static时,它应该具有更多的限制。静态内部类具有以下两个特点:
创建静态内部类对象,不需要其外部类的对象。不能从静态内部类的对象中访问非静态外部类的对象。
注意:在内部类doitInner()方法中调用成员变量x,由于Inner被修饰为static形式,而成员变量x却是非static类型的,所以在doitInner()方法中不能调用x变量。进行程序测试时,如果在每一个Java文件中都设置一个主方法,将出现很多额外的代码,而程序本身并不需要这些主方法,为了解决这个问题,可以将主方法写入静态内部类中。如果编译上述类,将生成一个名为staticInnerClass$Inner的独立类和一个staticInnerClass类,只要使用staticInnerClass$Inner就可以运行主方法中的内容,这样当测试完成需要将所有.class文件打包时,只要删除staticInnerClass$Inner独立类即可。
内部类的继承
(概念)内部类也和其他普通类一样可以被继承,但是继承内部类比继承普通类要复杂一些,需要设置专门的语法来完成。
在某个类继承内部类时,必须硬性给予这个类一个带参数的构造方法,并且该构造方法的参数为需要继承内部类的外部类的引用,同时在构造方法体重使用a.super语句,这样才为继承提供了必要的对象引用。
class类与Java反射
通过Java反射机制,可以在程序中访问已经装载到jvm中的Java对象的描述,实现访问、检测和修改描述Java对象本身信息的功能。Java反射机制的功能十分强大,在Java.lang.reflect包中提供了对该功能的支持。
众所周知,所有Java类均继承了object类,在object类中定义了一个getclass()方法,该方法返回一个类型为class的对象。代码如下:Class textFieldC = textField.getClass();//textField为JTextField类的对象
利用class类的对象textFieldC,可以访问用来返回该对象的textFiled对象的描述信息。(理解为引用?)
主要的描述信息如表:
注意:在通过getFields()和getMethods()方法依次获得权限为public的成员变量和方法时,还包含从超类中继承到的成员变量和方法;而通过getDeclaredFields()和getDeclaredMethods()方法只是获得在本类中定义的所有成员变量和方法。
访问构造方法
在通过下列一组方法访问构造方法时,将返回constructor类型的对象或数组。每个constructor对象代表一个构造方法,利用constructor对象可以操纵相应的构造方法。
如果是访问指定的构造方法,需要根据该构造方法的入口参数的类型来访问。例如访问一个入口参数类型依次为string和int型的构造方法,可以通过下面两种方式实现。
objectClass.getDeclaredConstructor(string.class,int.class);
objectClass.getDeclaredConstructor(new Class[]{string.class,int.class});
constructor类中提供的常用方法:
通过java.lang.reflect.modifier类可以解析出getModifiers()方法的返回值所表示的修饰符信息,在该类中提供了一些列用来解析的静态方法,既可以查看是否被指定的修饰符修饰,还可以以字符串的形式获得所有修饰符。该类常用的静态方法如表所示:
(举例)判断constructor对象所代表的构造方法是否被private修饰,以及字符串形式获得该构造方法的所有修饰符,代码:
int modifiers = constructor.getModifiers();
boolean isEmbellishByPrivate=Modifier.isPrivate(modifiers);
string embelishment = Modifier.toString(modifiers);
反射访问 ?
访问成员变量
(应用)在通过下列一组方法访问成员变量时,将返回Field类型的对象或数组。每个Field对象代表一个成员变量,利用Field对象可以操纵相应的成员变量。
getFields() getField(String name) getDeclaredFields() getDeclaredField(String name)
如果是访问指定的成员变量,可以通过该成员变量的名称来访问。(举例)访问一个名称为birthday的成员变量,代码如下:
object.getDeclaredField("birthday");
注意:1)在项目中创建一个MoreFields类,在该类中依次声明一个int、float、boolean和string型的成员变量,并将它们设置为不同的访问权限。2)通过反射访问MoreFields类中的所有成员变量,将成员变量的名称和类型信息输出到控制台,并分别将各个成员变量在修改前后的值输出到控制台。要访问权限为private的成员变量s,需要执行setAccessible()方法,并将入口参数设为true,否则不允许访问。
访问方法
在通过下列一组方法访问方法时,将返回method类型的对象或数组。每个method对象代表一个方法,利用method对象可以操纵相应的方法。
getMethods() getMethod(String name,Class<?>……parameterTypes) getDeclaredMethods() getDeclaredMethod(String name,Class<?>……parameterTypes)
如果是访问指定的方法,需要根据该方法的名称和入口参数的类型来访问。(举例)访问一个名称为print、入口参数类型为string和int型的方法,可以通过下面两种方式实现。
objectClass.getDeclaredMethod("print".String.class,int.class);
objectClass.getDeclaredMethod("print",new class[]{string.class,int.class});
Method类中提供的常用方法:
1)在项目中创建一个MoreMethod类,并编写4个典型的方法。2)通过反射访问MoreMethod类中的所有方法,将各个方法的名称、入口参数类型、返回值类型等信息输出到控制台,并执行部分方法。在反射中执行具有可变数量的参数的构造方法时,需要将入口参数定义为二维数组。
注意:在Java中,调用类的方法有两种方式:对于静态方法可以直接使用类名调用,对于非静态方法必须使用类的对象调用。反射机制提供了比较另类的调用方式,可以根据需要指定要调用的方法,而不必在编程时确定。调用的方法不仅只限于public的,还可以是private的。本范例将使用反射机制调用math类的静态方法sin()和string类的非静态方法equals().
小结:首先讲解了抽象类,之后分别讲解了内部类中的成员内部类、局部内部类、匿名内部类和静态内部类,最后讲解了反射。通过学习本章,读者可以对内部类有一个更深层次的了解,并且能够在别写程序时熟练使用内部类。
小结:首先讲解了抽象类,之后分别讲解了内部类中的成员内部类、局部内部类、匿名内部类和静态内部类,最后讲解了反射。通过学习,应对内部类有一个更深层次的了解,并且能够在编写程序时熟练应用内部类。