1. Class类
1.1. Class类说明
Class 类的实例表示正在运行的 Java 应用程序中的类和接口。枚举是一种类,注释是一种接口。每个数组属于被映射为 Class 对象的一个类,所有具有相同元素类型和维数的数组都共享该 Class 对象。基本的 Java 类型(boolean、byte、char、short、int、long、float 和 double)和关键字 void 也表示为 Class 对象。
Class 没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的。
在Java中,每个class都有一个相应的Class对象。也就是说,当我们编写一个类,编译完成后,在生成的.class文件中,就会产生一个Class对象,用于表示这个类的类型信息。比如说,当我们编写了Student这么一个类,编译完后,就会产生一个Student.class的字节码文件,用于表示Student类的类型信息。
1.2. 获取Class实例的四种方式
Ø 利用对象调用getClass()方法获取该对象的Class实例;
Ø 使用Class类的静态方法forName(),用类的名字获取一个Class实例;
Ø 运用.class的方式来获取Class实例;
Ø 对于基本数据类型的封装类,还可以采用.TYPE来获取相对应的基本数据类型的Class实例。
现在来编写一个类来实现这几种方式,
public class A { public static void main(String args[]){ Point p = new Point(); Class c1 = p.getClass(); System.out.println(c1.getName());
//由于forName这个静态方法会抛出一个异常,所以在调用时要么在类中也 //抛出一个异常,要么捕获异常 try{ Class c2 = Class.forName("Point"); System.out.println(c2.getName()); }catch(Exception e){ e.printStackTrace(); }
Class c3 = Point.class; System.out.println(c3.getName());
Class c4 = int.class; System.out.println(c4.getName());
Class c5 = Integer.class; System.out.println(c5.getName());
Class c6 = Integer.TYPE; System.out.println(c6.getName());
} }
class Point{ int x,y; }
编译结果: Point Point Point int java.lang.Integer int |
1.3. 关于JAVA对象加载说明
在运行期间,如果我们要产生某个类的对象,Java虚拟机(JVM)会检查该类型的Class对象是否已被加载。如果没有被加载,JVM会根据类的名称找到.class文件并加载它。一旦某个类的Class对象已被加载到内存,就可以用它来产生该类型的所有对象。
现在我们根据这段话说明描述来编写一个类,验证这段话。
public class A { public static void main(String args[]){ System.out.println("before class Loading......"); new Point(); System.out.println("after class Loading......"); try{ Class.forName("Line"); }catch(Exception e){ e.printStackTrace(); } } }
class Point{ //静态代码段,当类被加载时,就会被执行 static{ System.out.println("Point class Loading......"); } }
class Line{ static{ System.out.println("Line class Loading......"); } }
编译结果: before class Loading...... Point class Loading...... after class Loading...... Line class Loading...... |
1.4. newInstance()方法
newInstance()方法是Class类的实例方法,用于创建此Class对象所表示的类的一个新实例。
在创建对象时,都要调用类的构造函数。
和new关键字创建对象可以自由选择构造函数相比,newInstance()方法创建对象时,只能调用类中缺省的构造方法,也就是没有参数的构造函数。如果一个类的所有构造函数都有参数,那么就会出现异常。但用newInstance()方法创建对象有个最大好处,事先不知道类的名称的情况下可以创建类的对象,也就是说在代码中可以动态创建类的对象。
现在来编写一个利用newInstance()方法创建一个对象的实例:
public class A { public static void main(String args[]){ try{ Class c = Class.forName("Point"); Point p = (Point)c.newInstance(); p.output(); }catch(Exception e){ e.printStackTrace(); } } }
class Point{ int x,y; Point(int x, int y){ this.x = x; this.y = y; } public void output(){ System.out.println("x="+x+",y="+y); } } 编译结果: x=5,y=5 |
如果在Point类中只有带参构造函数,在编译时,会出现这样的异常:
java.lang.InstantiationException: Point at java.lang.Class.newInstance0(Class.java:340) at java.lang.Class.newInstance(Class.java:308) at A.main(A.java:5) |
1.5. 反射API
1.5.1. 反射API的作用
利用newInstance()方法创建对象类,将调用到类的缺省构造方法,如果不存在缺省构造方法时,就会出现异常,这时就要使用反射API来解决这个问题。
反射API的作用有:
Ø 检测一个对象的类
Ø 获取类的修饰符、属性、函数、构造函数及基类的信息
Ø 找出接口的常量及方法
Ø 在不知道类名的情况下创建对象,类名在运行的时候动态获取
Ø 设置或获取对象属性的值,即使不知道属性的名称,名称在运行的时候动态获取
Ø 调用对象的方法,即使不知道方法名称,名称在运行的时候动态获取
Ø 创建新的数组,其大小和元素数据类型都是未知的,在运行的时候动态获取
1.5.2. 反射API相关类和使用
Ø Constructor:提供关于类的单个构造方法的信息以及对它的访问权限
Ø Field:提供有关类或接口的单个字段的信息,以及对它的动态访问权限。反射的字段可能是一个类(静态)字段或实例字段。
Ø Method:提供关于类或接口上单独某个方法(以及如何访问该方法)的信息。所反映的方法可能是类方法或实例方法(包括抽象方法)。
Ø Modifier:提供了 static
方法和常量,对类和成员访问修饰符进行解码。
现在来使用这些类:
import java.lang.reflect.*; public class A { public static void main(String args[]){ try{ Class c = Class.forName("Point"); Constructor[] cons = c.getDeclaredConstructors(); for(int i=0; i System.out.println(cons[i]); } Method[] method = c.getDeclaredMethods(); for(int i=0; i System.out.println(method[i]); } Field[] field = c.getDeclaredFields(); for(int i=0; i System.out.println(field[i]); } }catch(Exception e){ e.printStackTrace(); } } }
class Point{ int x,y; Point(){ this.x = 5; this.y = 5; } Point(int x, int y){ this.x = x; this.y = y; } public void output(){ System.out.println("x="+x+",y="+y); } } 编译结果: Point() Point(int,int) public void Point.output() int Point.x int Point.y |
1.5.3. Constructor类getParameterTypes()方法使用说明
getParameterTypes()方法返回一组按照声明顺序Class
对象,这些对象表示此 Constructor
对象所表示构造方法的形参类型。如果底层构造方法不带任何参数,则返回一个长度为 0 的数组。
现在以一个实例来说明该方法的使用:
import java.lang.reflect.*; public class A { public static void main(String args[]){ try{ Class c = Class.forName("Point"); Constructor[] cons = c.getDeclaredConstructors(); for(int i=0; i Class[] parm = cons[i].getParameterTypes(); for(int j=0; j System.out.println(parm[i]); } } }catch(Exception e){ e.printStackTrace(); } } }
class Point{ int x,y; Point(int x, int y){ this.x = x; this.y = y; } public void output(){ System.out.println("x="+x+",y="+y); } } 编译结果: int int |
如果Point类的构造函数形参是两个Object类对象,那么编译结果:
class java.lang.Object class java.lang.Object |
1.5.4. 使用Constructor类的newInstance()方法创建对象
newInstance:使用此Constructor对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例。
现在来编写使用该方法的实例:
import java.lang.reflect.*; public class A { public static void main(String args[]){ try{ Class c = Class.forName("Point"); Constructor[] cons = c.getDeclaredConstructors(); Class[] params = cons[0].getParameterTypes(); //获取Point(int x, int y)构造函数参数 Object[] o = new Object[params.length]; for(int i=0; i if(params[i].isPrimitive()){ //params[i]是否是基本数据类型变量 o[i] = new Integer(i); } } Object obj = cons[0].newInstance(o); Method[] method = c.getDeclaredMethods(); method[0].invoke(obj, null); }catch(Exception e){ e.printStackTrace(); } } }
class Point{ int x,y; Point(int x, int y){ this.x = x; this.y = y; } public void output(){ System.out.println("x="+x+",y="+y); } } 编译结果: x=0,y=1 |
这样,使用Class类和反射API就可以动态创建类的对象,动态调用对象的方法。