源文件(T.java)经过编译后生成类文件,实例化该类生成对象运行;java运行时系统为会所有的对象维护一个被称为“运行时”的类型标记,跟踪着每一个对象所属的类,保存着对象所属类的信息,而用于保存这些信息的类被称为Class。
每一个Class对象都是一个保存对象所属的类信息的类文件,每一个类文件的类型是Class。
获取一个对象T的类文件T.class的三种方法:
//方式一:对象的getClass()方法
Class cl = new Date().getClass();
//方式二:Class的静态方法forName(String)根据类名加载,若String不是类名或接口名,出现异常
Class cl = Class.forName("java.util.Date");
//方式三:类名.class
Class cl = Date.class;
Class对象的newInstance()方法可以创建Class对象所指类的实例
//无参对象
Class.forName("java.util.Date").newInstance();
注意:使用newInstance()要求Class对象所表示的类必须有空参数的构造函数,若没有空参构造,则可以获取构造函数的Constructor对象创建带参对象。
public class UserRef {
public static void main(String[] args) throws Exception {
//使用forName,传入类型名用.分隔包名
Class cl = (Class) Class.forName("com.common.User");
createInstance(cl);
}
//测试利用Class对象创建带参的实例
public static void createInstance(Class clazz) throws Exception{
//获取构造函数,注意参数类型可以使用int.class==Integer.TYPE,但不能使用Integer.class
Constructor cons = clazz.getConstructor(Integer.TYPE, String.class);
Object[] initargs = new Object[]{20, "hi"}; //初始化参数
User u = cons.newInstance(initargs);
System.out.println(u);
}
}
//User对象
package com.common;
public class User {
private int id;
private String name;
public User(int id, String name) {
super();
this.id = id;
this.name = name;
}
//setters and getters
}
基础数据类型与其包装类的关系:
基础类型.class == 包装类.TYPE,但不等于包装类.class
public void testBasicType(){
sop(Integer.TYPE == int.class); //true
sop(Integer.class == int.class); //false
sop(Double.TYPE == double.class); //true
sop(Double.class == double.class); //false
sop(Character.TYPE == char.class); //true
sop(Character.class == char.class); //false
sop(Boolean.TYPE == boolean.class); //true
sop(Boolean.class == boolean.class); //false
}
利用反射可以获取一个运行对象的所有属性:
实例:
//打印该类的所有构造函数
public static void printConstructors(Class cl){
//返回cl.class的所有构造函数
Constructor[] constructors = cl.getDeclaredConstructors();
for(Constructor c : constructors){
//获得构造函数类型
String name = c.getName();
//获得构造函数修饰符
String modifiers = Modifier.toString(c.getModifiers());
System.out.print(" " + modifiers + " " + name + " (");
//获得构造函数所有形参,也是class对象
Class[] types = c.getParameterTypes();
for(int j = 0; j < types.length; j++){
//列出所有形参
if(j > 0) System.out.print(" ,");
String tname = types[j].getName();
System.out.print(tname);
}
System.out.println(")");
}
}
//打印所有方法
public static void printMethods(Class cl) {
Method[] methods = cl.getDeclaredMethods();
for(Method m : methods){
//打印方法的修饰符、返回类型、方法名、参数
String mname = m.getName();
Class returntype = m.getReturnType();
String modifiers = Modifier.toString(m.getModifiers());
System.out.print(" "+modifiers+" "+ returntype.getName() + " " + mname + " (");
//参数
Class[] paramTypes = m.getParameterTypes();
for(int j = 0; j < paramTypes.length; j++){
//列出所有形参
if(j > 0) System.out.print(" ,");
String tname = paramTypes[j].getName();
System.out.print(tname);
}
System.out.println(")");
}
}
//打印所有域
public static void printFields(Class cl){
Field[] fields = cl.getDeclaredFields();
for(Field f : fields){
//打印域的修饰符、名称
String name = f.getName();
String modifiers = Modifier.toString(cl.getModifiers());
System.out.println("\t"+ modifiers + " "+ name);
}
}
通过反射可以获取类中的域和方法等属性,而且利用反射也可以查看该类对象的域值。
查看对象域值的关键方法是Field类的get方法;
Employee harry = new Employee("harry potter", 3500, 10, 1, 1988);
Class cl = harry.getClass();
Field f = cl.getDeclaredField("name");
f.setAccessible(true);
Object v = f.get(harry); //harry potter
f.set(harry, "john wick");
System.out.println(v);
Arrays.copyOf(数组变量,新长度)可以动态地扩展已经填满的数组。
Employee[] e = new Employee[100];
...
e = Arrays.copyOf(e, 200);
利用反射可以编写一个类似的扩展数组操作
//传入数组对象和长度
public static Object goodCopyOf(Object a, int newLength){
Class cl = a.getClass();
if(!cl.isArray()) return null; //非数组返回空
//获取数组元素类型
Class componentType = cl.getComponentType();
//根据长度扩展数组
int length = Array.getLength(a); //原长度
Object newArray = Array.newInstance(componentType, newLength);
//将原来数组数据复制到新数组数据
System.arraycopy(a, 0, newArray, 0, Math.min(newLength, length));
return newArray;
}
//使用
e = (Employee)goodCopyOf(e, 200);
注意使用了Array.newInstance(componentType, newLength)创建一个长度为newLength,数组元素类型为componentType的新数组。
Field对象的get方法可以查看域所在类的对象中的域值,Method也有一个invoke方法,允许调用封装在当前Method所在对象的该方法。签名:
Object invoke(Object obj, Object… args);
第一个参数obj需传入method所在对象,若方法是静态的,可以设为null
第二个参数args需要传入调用该方法所需的参数值,若方法为无参,可以不传。
实例:
public class MethodPointerTest {
public static void main(String[] args) throws Exception {
Class cl = MethodPointerTest.class; //当前类的类文件对象
Method square = cl.getMethod("square", double.class); //获取cl类中名称为square,参数类型为double的Method对象
//获取Math类中名称为sqrt,参数类型为double的Method对象
Method sqrt = Math.class.getMethod("sqrt", double.class);
printTableMethod(1, 10, 10, square);
printTableMethod(1, 10, 10, sqrt);
}
public static double square(double x){
return x * x;
}
public static void printTableMethod(double f, double t, int n, Method m) throws Exception{
//调用m方法,从m(f)到m(t)打印n个函数数据
double s = (t - f) / (n - 1);
for(double x = f; x <= t; x += s){
//invoke调用m方法,计算x
double y = (Double)m.invoke(null, x);
System.out.printf("%10.4f | %10.4f%n", x, y);
}
}
}
当在程序运行时,需要创建实现了某些接口的对象,但不清楚这些接口具体是什么(真正需要调用时才能确定这些接口),此时即可使用动态代理的方式生成实现具体接口的对象。方式:
Object proxy = Proxy.newProxyInstance(ClassLoader loader, Class>[] interfaces, InvocationHandler h)
第一个参数:类加载器,使用null作为默认的类加载器
第二个参数:需要实现的具体接口的Class对象集合
第三个参数:调用处理器,实现了InvocationHandler接口的对象。
调用处理器:不仅包装了需要代理的对象(构造方法传入),而且还说明了对接口中的方法的调用方式:一切对接口中的方法的调用都由调用处理器中的invoke方法完成。
具体流程:一个对象obj需要实现某些接口,但不确定,可以将obj传入调用处理器InvocationHandler,再由Proxy.newProxyInstance生成obj对象的代理对象proxy,对proxy中接口方法的调用都由invoke方法实现。常用于:对已实现了接口的对象完成接口方法的覆盖。
示例:动态代理生成一个实现了Comparable接口的Integer对象,并对Comparable接口的CompareTo方法覆盖,改写。
public class ProxyTest {
public static void main(String[] args) {
Object[] elements = new Object[1000];
for(int i = 0; i < elements.length; i++){
//需代理的对象
Integer value = i + 1;
//调用处理器
TraceHandler handler = new TraceHandler(value);
//生成代理对象
Object proxy = Proxy.newProxyInstance(null, new Class[]{Comparable.class}, handler);
//数组存储代理对象
elements[i] = proxy;
}
//随机取一个整数
Integer key = new Random().nextInt(elements.length) + 1;
//二分查找
int result = Arrays.binarySearch(elements, key);
if(result > 0)
System.out.println(elements[result]);
}
}
public class TraceHandler implements InvocationHandler{
private Object target; //需代理的对象
public TraceHandler(Object obj) {
//存储包装对象
this.target = obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// 对接口中方法的具体调用都由invoke实现
System.out.print(target);
System.out.print("."+method.getName()+" (");
//打印参数
if(args != null){
for(int i = 0; i < args.length; i++){
System.out.print(args[i]);
if(i < args.length - 1)
System.out.print(", ");
}
}
System.out.println(")");
//方法正常调用,注意具体实现是由需要代理的对象target完成
return method.invoke(target, args);
}
}