Class对象获取的三种方式
Java反射操作都需要首先获取Class对象。获取Class对象的方式有三种:
- 公有属性class
- 方法getClass()
- Class.forName()
示例:
public class ReflectClassDemo { /** log4j */ private static final Logger LOGGER = Logger.getLogger(ReflectClassDemo.class); /** * <p>获取Class对象的三种方式</p> * @author*/ public static void main(String[] args) throws ClassNotFoundException { LOGGER.info("获取Class对象方式01:类的public属性class"); Class clazz1 = User.class; LOGGER.info(clazz1); System.out.println(); LOGGER.info("获取Class对象方式02:类的public方法getClass()"); User user = new User(); Class clazz2 = user.getClass(); LOGGER.info(clazz2); System.out.println(); LOGGER.info("获取Class对象方法03:Class.forName(需要抛出异常)"); Class clazz3 = Class.forName("pers.hanchao.reflect.common.User"); LOGGER.info(clazz3); } }
运行结果:
2018-02-24 13:59:06 INFO ReflectClassDemo:18 - 获取Class对象方式01:类的public属性class 2018-02-24 13:59:06 INFO ReflectClassDemo:20 - class pers.hanchao.reflect.common.User 2018-02-24 13:59:06 INFO ReflectClassDemo:23 - 获取Class对象方式02:类的public方法getClass() 2018-02-24 13:59:06 INFO ReflectClassDemo:26 - class pers.hanchao.reflect.common.User 2018-02-24 13:59:06 INFO ReflectClassDemo:29 - 获取Class对象方法03:Class.forName(需要抛出异常) 2018-02-24 13:59:06 INFO ReflectClassDemo:31 - class pers.hanchao.reflect.common.User
总结:
- 通过公有属性class获取Class对象:通过类获取,无需创建类对象。
- 通过方法getClass()获取Class对象:需要类的对象。常用于不知道类名但是能获取对象的情况下。
- 通过方法Class.forName()获取Class对象:需要类的全名,需抛出异常。常用于加载配置。
通过反射实例化对象的两种方式
除了通过new
实例化对象,我们还可以通过反射实例化对象对象,有两种方式:
- Class.newInstance()
- Constructor.newInstance()
示例:
public class CreateObjectDemo { private final static Logger LOGGER = Logger.getLogger(CreateObjectDemo.class); /** * <p>Title: 通过反射创建对象的两种方式</p> * @author*/ public static void main(String[] args) throws Exception { //通过new实例化对象 User user = new User(); LOGGER.info("通过new实例化对象:" + user.toString()); //通过反射实例化对象 Class userClass = User.class; //通过反射实例化对象01-Class.newInstance()(需要强制类型转换[无参构造]) User user1 = (User) userClass.newInstance(); LOGGER.info("通过反射实例化对象01-Class.newInstance()(需要强制类型转换[无参构造]):" + user1.toString());
//通过反射实例化对象02-Constructor.newInstance()(需要强制类型转换[可带参数]) Constructor constructor = userClass.getDeclaredConstructor(); User user2 = (User) constructor.newInstance(); LOGGER.info("通过反射实例化对象02-Constructor.newInstance()(需要强制类型转换[无参构造]):" + user2.toString());
Constructor constructor1 = userClass.getDeclaredConstructor(String.class,String.class); User user3 = (User) constructor1.newInstance("李四","000000"); LOGGER.info("通过反射实例化对象02-Constructor.newInstance()(需要强制类型转换[有参构造]):" + user3.toString()); } }
补充:通过构造函数去创建,调用Constructor对象的newInstance()方法创建对象。
Class c1=Class.forName("pers.pwz.cmdemo.Chinese"); Chinese chinese=(Chinese) c1.getConstructor().newInstance(); chinese.say();
运行结果:
2018-02-24 14:23:29 INFO CreateObjectDemo:22 - 通过new实例化对象:User{username='张三', password='123456'} 2018-02-24 14:23:29 INFO CreateObjectDemo:27 - 通过反射实例化对象01-Class.newInstance()(需要强制类型转换[无参构造]):User{username='张三', password='123456'} 2018-02-24 14:23:29 INFO CreateObjectDemo:31 - 通过反射实例化对象02-Constructor.newInstance()(需要强制类型转换[无参构造]):User{username='张三', password='123456'} 2018-02-24 14:23:29 INFO CreateObjectDemo:34 - 通过反射实例化对象02-Constructor.newInstance()(需要强制类型转换[有参构造]):User{username='李四', password='000000'}
总结:
- 通过Class.newInstance()进行对象实例化:实际调用的是无参数的构造函数进行实例化。
- 通过Constructor.newInstance()进行对象实例化:可以选择调用哪个构造函数进行实例化。
获取某个类的所有构造方法
public class ReflectTest1 { public static void main(String[] args) { Date date = new Date(); Class cc = date.getClass(); String className = cc.getName(); System.out.println(className); Constructor[] declaredConstructors = cc.getDeclaredConstructors(); for (Constructor constructor : declaredConstructors) { int modifiers = constructor.getModifiers(); System.out.print(Modifier.toString(modifiers) + " "); System.out.print(constructor.getName() + "("); Class[] paramTypes = constructor.getParameterTypes(); for (Class paramType : paramTypes) { System.out.print(paramType.getName() + " "); } System.out.println(")"); } } }
输出结果:
java.util.Date public java.util.Date(java.lang.String ) public java.util.Date(int int int int int int ) public java.util.Date(int int int int int ) public java.util.Date() public java.util.Date(long ) public java.util.Date(int int int )
获取某个类的所有属性信息
public class ReflectTest2 { public static void main(String[] args) { Date date = new Date(); Class cc = date.getClass(); String className = cc.getName(); System.out.println(className); Field[] fields = cc.getDeclaredFields(); for (Field field : fields) { String modifiers = Modifier.toString(field.getModifiers()); Class type = field.getType(); String name = field.getName(); System.out.println(modifiers + " " + type.getName() + " " + name); } } }
输出结果:
java.util.Date private static final sun.util.calendar.BaseCalendar gcal private static sun.util.calendar.BaseCalendar jcal private transient long fastTime private transient sun.util.calendar.BaseCalendar$Date cdate private static int defaultCenturyStart private static final long serialVersionUID private static final [Ljava.lang.String; wtb private static final [I ttb
补充:
1、获取所有非private类型的字段,包括父类的 Class c1=Class.forName("pers.pwz.cmdemo.Chinese"); Object object= c1.getConstructor().newInstance(); Field [] fields= c1.getFields();//或者所有的非private类型的字段,包括父类的 for(int i=0;i<fields.length;i++) { System.out.println(fields[i].getName()); } 2、获取当前类所有字段,包括私有的 Class c1=Class.forName("pers.pwz.cmdemo.Chinese"); Object object= c1.getConstructor().newInstance(); Field [] fields= c1.getDeclaredFields();//获取当前类中所有的字段 for(int i=0;i<fields.length;i++) { System.out.println(fields[i].getName()); } 3、获取父类的所有字段,包括私有的 Class c1=Class.forName("pers.pwz.cmdemo.Chinese"); Class c2= c1.getSuperclass(); //通过c2去获取父类中的私有字段 4、获取指定的字段 Class c1=Class.forName("pers.pwz.cmdemo.Chinese"); Object object= c1.getConstructor().newInstance(); Field field= c1.getDeclaredField("Age"); 5、使用 Class c1=Class.forName("pers.pwz.cmdemo.Chinese"); Object object1= c1.getConstructor().newInstance(); Field field= c1.getDeclaredField("Age"); field.setAccessible(true);//如果字段是私有的,需要先这是允许访问 //取值 Object object2=field.get(object1); System.out.println(object2); //赋值 field.set(object1, 21); //取值 Object object3=field.get(object1); System.out.println(object3);
获取某个类的所有方法信息
public class ReflectTest3 { public static void main(String[] args) { Date date = new Date(); Class cc = date.getClass(); Method[] methods = cc.getDeclaredMethods(); for (Method method : methods) { String modifiers = Modifier.toString(method.getModifiers()); Class returnType = method.getReturnType(); String name = method.getName(); Class[] parameterTypes = method.getParameterTypes(); Class[] exceptions = method.getExceptionTypes(); System.out.println(modifiers + " " + returnType + " " + name + "(" + Arrays.asList(parameterTypes) + ")throws" + Arrays.asList(exceptions)); } } }
输出结果:
public boolean after([class java.util.Date])throws[] public boolean before([class java.util.Date])throws[] public boolean equals([class java.lang.Object])throws[] public class java.lang.String toString([])throws[] public int hashCode([])throws[] public class java.lang.Object clone([])throws[] public volatile int compareTo([class java.lang.Object])throws[] public int compareTo([class java.util.Date])throws[] ······
补充:获取方法并执行
1、获取所有的非private方法 Class c1=Class.forName("pers.pwz.cmdemo.Chinese"); Object chinese= c1.getConstructor().newInstance(); Method[] methods1= c1.getMethods();//获取所有的非private方法,并且父类的方法也会获取 for(int i=0;i<methods1.length;i++) { Class[] parameters= methods1[i].getParameterTypes(); if(parameters.length<=0) { System.out.println(methods1[i].getName()+"()"); } else { String [] typeName=new String[parameters.length]; for(int j=0;j<parameters.length;j++) { typeName[j]=parameters[j].getSimpleName(); } System.out.println(methods1[i].getName()+"("+String.join(",",typeName)+")"); } } 2、获取所有的当前类定义的所有方法,包括私有方法 Method[] methods1= c1.getDeclaredMethods();//获取当前类定义的所有,包括私有方法 3、获取指定方法名的方法 Class c1=Class.forName("pers.pwz.cmdemo.Chinese"); Object chinese= c1.getConstructor().newInstance(); Method setNumber= c1.getDeclaredMethod("setNumber", Integer.class);//如果方法没有参数就可以不用写,另外需要注意参数类型和定义方法的参数类型保持一致 4、获取父类的私有方法 Class c1=Class.forName("pers.pwz.cmdemo.Chinese"); Class c2= c1.getSuperclass(); //通过c2去获取父类中的私有方法 5、执行方法 Class c1=Class.forName("pers.pwz.cmdemo.Chinese"); Object chinese= c1.getConstructor().newInstance(); Method setNumber= c1.getDeclaredMethod("setNumber", Integer.class);//如果方法没有参数就可以不用写,另外需要注意参数类型和定义方法的参数类型保持一致 setNumber.invoke(chinese, 1); //第一个参数是实例对象,后面的参数是调用方法所需的参数
动态代理
代理是基本的设计模式之一。
下面是一个用来展示代理结构的简单示例:
interface Interface { void doSomething(); void somethingElse(String arg); } class RealObject implements Interface { public void doSomething() { System.out.println("doSomething"); } public void somethingElse(String arg) { System.out.println("somethingElse " + arg); } } class SimpleProxy implements Interface { private Interface proxied; public SimpleProxy(Interface proxied) { this.proxied = proxied; } public void doSomething() { System.out.println("SimpleProxy doSomething"); proxied.doSomething(); } public void somethingElse(String arg) { System.out.println("SimpleProxy somethingElse " + arg); proxied.somethingElse(arg); } } class SimpleProxyDemo { public static void consumer(Interface iface) { iface.doSomething(); iface.somethingElse("bonobo"); } public static void main(String[] args) { consumer(new RealObject()); consumer(new SimpleProxy(new RealObject())); } }
输出结果:
doSomething
somethingElse bonobo
SimpleProxy doSomething
doSomething
SimpleProxy somethingElse bonobo
somethingElse bonobo
Java的动态代理比代理的思想更向前迈进了一步,因为它可以动态地创建代理并动态地处理对所代理方法的调用。在动态代理所做的所有调用都会被重定向到单一的调用处理器上。
import java.lang.reflect.*; class DynamicProxyHandler implements InvocationHandler { private Object proxied; public DynamicProxyHandler(Object proxied) { this.proxied = proxied; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("**** proxy: " + proxy.getClass() + ", method: " + method + ", args: " + args); if(args != null) for(Object arg : args) System.out.println(" " + arg); return method.invoke(proxied, args); } } class SimpleDynamicProxy { public static void consumer(Interface iface) { iface.doSomething(); iface.somethingElse("bonobo"); } public static void main(String[] args) { RealObject real = new RealObject(); consumer(real); // Insert a proxy and call again: Interface proxy = (Interface)Proxy.newProxyInstance( Interface.class.getClassLoader(), new Class[]{ Interface.class }, new DynamicProxyHandler(real)); consumer(proxy); } }
输出结果:
doSomething somethingElse bonobo **** proxy: class $Proxy0, method: public abstract void Interface.doSomething(), args: null doSomething **** proxy: class $Proxy0, method: public abstract void Interface.somethingElse(java.lang.String), args: [Ljava.lang.Object;@53bd815b bonobo somethingElse bonobo
通过调用静态方法Proxy.newProxyInstance()
可以创建动态代理,这个方法需要得到一个类加载器。
动态代理可以将所有调用重定向到调用处理器,因此通常会向调用处理器的构造器传递给一个“实际”对象的引用,从而使得调用处理器在执行其中任务时,可以将请求转发。
空对象
当你使用内置的null
表示缺少对象时,在每次使用引用时都必须测试其是否为null
,这显得枯燥,而且势必产生相当乏味的代码。问题在于null
除了在你试图用它执行任何操作来产生NullPointerException
之外,它自己没有其他任何行为。有时引入空对象的思想将会很有用,它可以接受传递给它的所代表的对象的消息,但是将返回表示为实际上并不存在任何“真实”对象的值。通过这种方式,你可以假设所有的对象都是有效的,而不必浪费编程精力去检查null
。
推荐阅读:java设计模式之空对象模式