Java基础之反射总结
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。
Java是一门面向对象语言,万物皆对象。对象是由类new出来的。例如我们有一个Dog类:
public class Dog{ }
创建一个Dog对象:Dog dog = new Dog();这个dog就是一个对象。
既然万物皆对象,那么Dog这个类是什么类的对象呢?答案就是Class类的对象。在Java中所有类都是Class类的对象。官方叫做类的ClassType:类类型。
类类型的获取有三种方式:
Class c1 = Dog.class;
Class c2 = dog.getClass();
Class c3 = Class.forName("Dog");
通过类的类类型,可以创建类的实例对象: Dog dog = Dog.class.newInstance();
Class的常用操作:
package com.shtel.test.reflection; import java.lang.reflect.Array; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; //Class 测试 public class ClassTest { //定义私有构造器 @SuppressWarnings("unused") private ClassTest() { } //定义一个有参构造器 public ClassTest(String name) { this.name=name; System.out.println("执行有参构造器"); } //定义两个私有属性,一个公共属性 private String name = "rose"; private Number age; public String getName() { return name; } public void setName(String name) { this.name = name; } public Number getAge() { return age; } public void setAge(Number age) { this.age = age; } public String hobby; //定义一个无参的info方法 public void info() { System.out.println("执行无参的info方法"); } //定义一个有参的info方法 public void info(String str) { System.out.println("执行有参的info方法;参数="+str); } //定义一个私有方法 private void pri() { System.out.println("执行私有方法"); } //定义测试内部类 class Inner{} public static void main(String[] args) throws Exception { //获取ClassTest的类类型 Class<ClassTest> clazz = ClassTest.class; //获取ClassTest的全部构造器 Constructor[] decConstructors = clazz.getDeclaredConstructors(); System.out.println("ClassTest全部构造器:"); for(Constructor ctr : decConstructors) { System.out.println(ctr); } //获取ClassTest的全部public构造器 Constructor[] constructors = clazz.getConstructors(); System.out.println("ClassTest全部public构造器:"); for(Constructor ctr : constructors) { System.out.println(ctr); } //获取ClassTest全部public方法 Method[] methods = clazz.getMethods(); System.out.println("ClassTest全部public方法:"); for(Method mth : methods) { System.out.println(mth); } //获取指定放方法 System.out.println("获取指定方法:"+clazz.getMethod("info", String.class)); //获取ClassTest全部内部类 Class<?>[] inners = clazz.getDeclaredClasses(); System.out.println("ClassTest全部内部类:"); for(Class<?> inner : inners) { System.out.println(inner); } //获取ClassTest全部public属性 Field[] fileds = clazz.getFields(); System.out.println("ClassTest全部public属性:"); for(Field f : fileds) { System.out.println(f); } //获取ClassTest全部public属性 Field[] decFileds = clazz.getDeclaredFields(); System.out.println("ClassTest全部属性:"); for(Field f : decFileds) { System.out.println(f); } //通过Class创建对象 ClassTest ct = clazz.newInstance(); //指定构造器创建实例对象 ClassTest ct1 = clazz.getConstructor(String.class).newInstance("lucy"); System.out.println(ct.name); System.out.println(ct1.name); //通过Class调用方法 Method info = clazz.getMethod("info", String.class); info.invoke(ct1, "hello"); //通过Class调用私有方法 Method pri = clazz.getDeclaredMethod("pri"); //取消访问权限检查 pri.setAccessible(true); pri.invoke(ct); //动态创建数组(java.lang.reflect包下还提供一个Array类,用来动态的创建数组操作数组) Object arr = Array.newInstance(String.class, 10); Array.set(arr, 0, "零"); Array.set(arr, 1, "一"); System.out.println(Array.get(arr, 0) +"\n"+ Array.get(arr, 1)); } } 类的静态加载和动态加载: 编译时刻加载类称为静态加载,运行时刻加载类称为动态加载,使用new方法新建实例即为静态加载类,在编译时候就要加载全部类。 例子: public class CreateDog(){ public static void main(String[] args) { //参数传入金毛,就创建金毛,调用对应bark方法 if(args[0].equals("JinMao")) { new JinMao().bark(); } //参数传入哈士奇,就创建哈士奇,调用对应bark方法 if(args[0].equals("HaShiQi")) { new HaShiQi().bark(); } } } //金毛类 public class JinMao(){ public void brak(){ System.out.println("JinMao !"); } }
当我们编译CreateDog类的时候会报错,找不到HaShiQi这个类,因为这个类还没有写。这就是静态加载,要在编译时刻加载所有用到的类。假如某个项目有100个功能,每个功能都有一个类。那就必须这个一百个类都正常存在,那么这个程序才能使用。但是我们往往使用的就是其中一个功能,由于其他类的缺少,这个功能也不能使用。这就是静态加载的弊端。
为了解决这个问题,我们可以使用动态加载,将上面的例子修改如下:
public interface Dog { void bark(); } public Class JinMao implements Dog(){ public void bark(){ System.out.println("JinMao !"); } } public class CreateDog(){ public static void main(String[] args) { try{ Dog dog = (Dog)Class.forName(args[0]).newInstance(); dog.bark(); } catch(Exception e){ e.printStackTrace(); } } }
这是再来编译程序就不会出错。当我们传入参数是JinMao的时候,就会调用JinMao.bark()方法。当我门传入参数是HaShiQi的时候会报ClassNotFoundException(因为没写HaShiQi类)。倘若我只想听金毛的叫声,这个程序是可以的,不会因为没有哈士奇而不能听金毛的叫声。这就解决了静态加载的弊端。
通过类类型可以获取这个类的所有的方法和属性:
public static void getClassMethodMessage(Object obj){ Class<? extends Object> c = obj.getClass(); Method[] methods = c.getMethods(); for(int i=0;i<methods.length;i++){ //获取类里的方法名称 System.out.print(methods[i].getName()+"("); //获取方法的参数数组 Class<?>[] paramType = methods[i].getParameterTypes(); for(Class<?> c1 : paramType){ System.out.print(c1.getName()+","); } System.out.print(")"); System.out.println(); } } public static void getClassFiledMessage(Object obj){ Class<? extends Object> c = obj.getClass(); //获取类属性 Field[] fileds = c.getFields(); for(Field f:fileds){ System.out.println(f.getName()); } }
通过反射调用方法:
public class ReflectTest { public static void main(String[] args) { try { Class<Dog> c = Dog.class; Dog dog = (Dog)c.newInstance(); Method eat = c.getMethod("eat",String.class); eat.invoke(dog,"大骨头"); } catch (Exception e) { e.printStackTrace(); } } } public class Dog { private String DogName; public String getDogName() { return DogName; } public void setDogName(String dogName) { DogName = dogName; } public void brak(){ System.out.println("狗叫了"); } public void eat(String food){ System.out.println("我正在吃"+food); } }