举例说明
1. 实例类
class Person {
private String name = "liming";
public int age = 2;
public Person(){
}
public Person(int age, String name){
this.age = age;
this.name = name;
}
public void sayHello(){
System.out.println("hi!....");
}
public int getAgeDay(int a)
return a * 365;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
2. 创建实例类的对象
方式一:new + 构造器
方式二:要创建Xxx类的对象,可以考虑:Xxx、Xxxs、XxxFactory、XxxBuilder类中查看是否有静态方法的存在。可以调用其静态方法,创建Xxx对象。
方式三:通过反射
- 实例
//方式一:①Class对象调用newInstance()方法
Class<Person> clazz = Person.class;
Person obj = clazz.newInstance();
System.out.println(obj);
//方式二:②通过反射获取到构造器之后,构造器通过newInstance()方法就可以生成初始化的类对象
Constructor constructors = null;
//获取构造器
try {
constructors = clazz2.getConstructor(new Class[]{int.class,String.class});
} catch (SecurityException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
//获取类对象
try {
Person person3 = (Person) constructors.newInstance(10,"ahong");
System.out.println("我"+person3.age + "岁了!我叫" + person3.getName()+ "");
} catch (IllegalArgumentException e) {
// 参数不合法异常
e.printStackTrace();
} catch (InstantiationException e) {
// 没有这个构造器,实例化异常
e.printStackTrace();
} catch (IllegalAccessException e) {
// 类型转换异常
e.printStackTrace();
} catch (InvocationTargetException e) {
// 当被调用的方法的内部抛出了异常而没有被捕获时,将由此异常接收
e.printStackTrace();
}
- 说明
-
newInstance():调用此方法,创建对应的运行时类的对象。内部调用了运行时类的空参的构造器。
-
要想此方法正常的创建运行时类的对象,要求:
2.1运行时类必须提供空参的构造器
2.2空参的构造器的访问权限得够。通常,设置为public。 -
在javabean中要求提供一个public的空参构造器。原因:
3.1便于通过反射,创建运行时类的对象
3.2便于子类继承此运行时类时,默认调用super()时,保证父类此构造器
1. 不用反射
- 调用实例类
public class TestReflection {
public static void main(String[] args) {
//方法一:常规调用
Person person = new Person();
int myAge = person.age;
String myNameString = person.getName();
int day = person.getAgeDay(myAge);
System.out.println("我是" + myNameString + myAge + "岁" + "出生" + day +"天了!");
person.sayHello();
System.out.println("-------------------------------------------");
}
}
2. 使用反射
- 我们可以通过反射,获取对应的运行时类中所有的属性、方法、构造器、父类、接口、父类的泛型、包、注解、异常等。。。。
- setAccessible(true)方法,这是针对私有变量而言,public和protected等都不需要。这个方法是允许通过反射访问类的私有结构。
- 获取Class对象
//方式一;调用运行时类的属性:.class
Class<Person> clazz1 = Person.class;
//方式二:通过运行时类的对象,调用getClass()
Class clazz2 = person.getClass();
//方式三:调用Class的静态方法:forName(String classPath)
try {
Class clazz3 = Class.forName("Person");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
//方式四:使用类的加载器:ClassLoader (了解)
ClassLoader classLoader = TestiBATIS.class.getClassLoader();
//在非static的方法下可以用ClassLoader classLoader = this.getClass().getClassLoader();得到加载器
try {
Class clazz4 = classLoader.loadClass("Person");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
- 获取运行时类的父类,使用Class对象调用getSuperclass()方法
Class superClass = clazz1.getSuperclass();
System.out.println(superClass);
- 获取接口,使用Class对象调用getInterfaces()方法
//一个类可以实现多个接口,所以getInterfaces()方法返回的是Class[]数组getInterfaces()只返回指定类实现的接口,不会返父类实现的接口。
Class[] interfaceClasss = clazz1.getInterfaces();
- 调用指定的属性
//1.getDeclaredField(String fieldName):获取运行时类中指定变量名的属性
Person per = clazz1.newInstance();
Field name = clazz1.getDeclaredField("name");
//2.保证当前属性是可访问的
name.setAccessible(true);
//3.获取、设置指定对象的此属性值
name.set(per, "Li");
System.out.println(name.get(per));
- 调用指定的构造器
1.获取指定的构造器 getDeclaredConstructor():参数:指明构造器的参数列表
Constructor constructor = clazz.getDeclaredConstructor(int.class, String.class);
//2.保证此构造器是可访问的
constructor.setAccessible(true);
//3.调用此构造器创建运行时类的对象
Person per = (Person) constructor.newInstance("Tom");
System.out.println(per);
- 调用指定的方法
- invoke()方法
- java反射提供invoke()方法,在运行时根据业务需要调用相应的方法,这种情况在运行时非常常见,只要通过反射获取到方法名之后,就可以调用对应的方法
- invoke方法有两个参数,第一个参数是要调用方法的对象,第二个参数是调用方法要传入的参数。如果有多个参数,则用数组。如果调用的是static方法,invoke()方法第一个参数就用null代替:
//1.获取指定的某个方法getDeclaredMethod():参数1 :指明获取的方法的名称 参数2:指明获取的方法的形参列表
Method dayMethod = clazz.getDeclaredMethod("day", int.class);
//2.保证当前方法是可访问的
dayMethod.setAccessible(true);
//①获取方法参数
Class[] parameterTypes = dayMethod.getParameterTypes();
//②获取方法返回类型
Class returnType = dayMethod.getReturnType();
//3. 调用方法的invoke():参数1:方法的调用者 参数2:给方法形参赋值的实参
//invoke()的返回值即为对应类中调用的方法的返回值。
//执行此方法
int day1 = (Integer)dayMethod.invoke(per, 1);
System.out.println(day1);
精简版
//二.带参无返回值
Method sayNameAgeMehtod = clazz.getDeclaredMethod("sayNameAge", int.class, String.class);
sayNameAgeMehtod.setAccessible(true);
System.out.println(sayNameAgeMehtod.invoke(per, 2, "kkkk"));
3. 反射的应用
- 使用反射可以在运行时检查和调用类声明的成员方法,可以用来检测某个类是否有getter和setter方法。getter和setter是java bean必须有的方法。 getter和setter方法有下面的一些规律: getter方法以get为前缀,无参,有返回值 setter方法以set为前缀,有一个参数,返回值可有可无, 下面的代码提供了检测一个类是否有getter和setter方法
public static void printGettersSetters(Class aClass){
Method[] methods = aClass.getMethods();
for(Method method : methods){
if(isGetter(method)) System.out.println("getter: " + method);
if(isSetter(method)) System.out.println("setter: " + method);
}
}
public static boolean isGetter(Method method){
if(!method.getName().startsWith("get")) return false;
if(method.getParameterTypes().length != 0) return false;
if(void.class.equals(method.getReturnType()) return false;
return true;
}
public static boolean isSetter(Method method){
if(!method.getName().startsWith("set")) return false;
if(method.getParameterTypes().length != 1) return false;
return true;
}