最近学习了Java反射知识,为了加强学到的知识,从今天开始培养自己写博客的习惯,记录自己学习的一些理解。
一、创建对象
说到反射,我们都知道新建一个对象最常用的方法就是new一个,还有两种方法,这两种都是通过反射实现的,首先写一个简单的类:
public class Stu {
private String name;
int age;
public String grade;
public Stu() {
}
public Stu(String name) {
this.name = name;
}
public Stu(int age) {
this.age = age;
}
private Stu(String name, int age) {
this.name = name;
this.age = age;
}
public String getGrade() {
return grade;
}
public void setGrade(String grade) {
this.grade = grade;
}
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;
}
public void makeToast() {
System.out.println("你好,我是" + name);
}
private void MyMethod() {
System.out.println("这是我的私有方法");
}
}
1、通过直接反射类名获取实例
Class<?> aClass = Class.forName("**.**.**.Stu");
Stu stu = (Stu) aClass.newInstance();
其中aClass.newInstance()和Stu.class.newInstance()效果是一样的。
拿到Stu类的Class对象,我们知道Java是面向对象编程的,万物皆对象,我通过描述目标的属性、构造、方法,最终具体为一个对象,其实我们写的Class本身也是一个对象,知道类名后我们通过对应的Class对象就可以直接创建一个实例。
2、通过反射获取构造器
Class<?> aClass = Class.forName("**.**.**.Stu");
Constructor<?> stuConstructor = aClass.getDeclaredConstructor();
stuConstructor.setAccessible(true);
Stu stu2 = (Stu) stuConstructor.newInstance();
通过getDeclaredConstructor()方法拿到的是默认的无参数构造,如果想要拿到定义好的有参构造器就要在getDeclaredConstructor()方法内传入相应的参数类型。比如上述Stu类,如果要获取name构造器
Constructor<?> stu2Constructor = aClass.getDeclaredConstructor(String.class);
stu2Constructor.setAccessible(true);
Stu stu2 = (Stu) stuConstructor.newInstance("小沙");
这里需要注意的是,我们在类里定义的是什么类型,getDeclaredConstructor()参数就要传入什么类型,传入的类型不支持转型。
Constructor<?> stuConstructor = aClass.getDeclaredConstructor(Integer.class);
stuConstructor.setAccessible(true);
Stu stu2 = (Stu) stuConstructor.newInstance(18);
System.out.println(stu2.getAge());
打印结果:
java.lang.NoSuchMethodException: **.**.**.Stu.<init>(java.lang.Integer)
at java.lang.Class.getConstructor0(Class.java:3082)
at java.lang.Class.getDeclaredConstructor(Class.java:2178)
两种创建方法的区别:
我们可以进入Class.newInstance()源码看看:
private volatile transient Constructor<T> cachedConstructor;
@CallerSensitive
public T newInstance() throws InstantiationException, IllegalAccessException{
.
.
.
Constructor<T> tmpConstructor = cachedConstructor;
.
.
.
try {
return tmpConstructor.newInstance((Object[])null);
} catch (InvocationTargetException e) {
Unsafe.getUnsafe().throwException(e.getTargetException());
// Not reached
return null;
}
}
源码里主要的就2句,通过已经存在的cachedConstructor赋值给一个构造器对象tmpConstructor,然后tmpConstructor调用newInstance()方法创建对象,看到这里就明白了,跟上文的第二种创建对象的方式是一样的,第一种方法创建对象时只是封装了一下第二种的方法。
二、构造器
获取构造器
我在Stu类新建一个私有构造器
还是拿刚才获取的aClass对象
Class<?> aClass = Class.forName("cn.enjoyedu.test.Stu");
Constructor<?>[] constructors = aClass.getConstructors();
for (Constructor cos:constructors) {
System.out.println("getConstructors---------------"+cos.toString());
}
Constructor<?>[] declaredConstructors = aClass.getDeclaredConstructors();
for (Constructor cos:declaredConstructors) {
System.out.println("getDeclaredConstructors---------------"+cos.toString());
}
打印结果:
从上图可以看出,使用getDeclaredConstructors()可以获取包括私有在内的全部构造器。同样的方法也适用于获取方法getDeclaredMethods()、获取属性getDeclaredFields()。
使用构造器
Constructor<?> classDeclaredConstructor
= aClass.getDeclaredConstructor(String.class, int.class);
classDeclaredConstructor.setAccessible(true);
Stu stu3 = (Stu) classDeclaredConstructor.newInstance("大刘", 28);
stu3.makeToast();
注意在获取私有构造器使用时要授予权限
.setAccessible(true)
否则会报错。
三、属性
我们修改一下Stu类的属性
private String name;
int age;
public String grade;
然后使用
Field[] fields = aClass.getFields();
for (Field df : fields) {
System.out.println("getFields---------------" + df.toString());
}
Field[] declaredFields = aClass.getDeclaredFields();
for (Field df : declaredFields) {
System.out.println("getDeclaredFields---------------" + df.toString());
}
打印结果
getFields()方法只能获取权限为‘public’的属性,getDeclaredFields()方法能获取包括私有的所有属性。
通过反射使用Field:
Stu stuField = (Stu) aClass.newInstance();
stuField.setGrade("八年级");
//如果字段是私有的 加上权限
Field grade = aClass.getDeclaredField("grade");
System.out.println(grade.getName()+"------"+grade.get(stuField));
打印结果
四、方法
首先我们给Stu类里增加一个私有方法
private void MyMethod(){
System.out.println("这是我的私有方法");
}
Method[] methods = aClass.getMethods();
for (Method method:methods ) {
System.out.println("getMethods-----------------------"+method.getName());
}
Method[] declaredMethods = aClass.getDeclaredMethods();
for (Method methodD:declaredMethods ) {
System.out.println("getDeclaredMethods---------------"+methodD.getName());
}
打印结果
可以看到使用getMethods()获取的方法里有好多是Stu类里没有的,这些都是继承的Object的方法,并且getMethods()也没有获取到我们定义的MyMethod()。
getDeclaredMethods()方法获取到了本类里定义的包括私有在内的所有方法。
然后就使用获取到的方法。
//反射获取到方法
Method setNameMethod = aClass.getDeclaredMethod("setName", String.class);
//获取对象实例 不需要转型
Object stuMethods = aClass.newInstance();
//执行方法设置属性
setNameMethod.invoke(stuMethods, "小岳岳");
//获取方法
setNameMethod = aClass.getDeclaredMethod("makeToast");
//执行方法
setNameMethod.invoke(stuMethods);
//获取私有方法
setNameMethod= aClass.getDeclaredMethod("MyMethod");
//授予权限
setNameMethod.setAccessible(true);
setNameMethod.invoke(stuMethods);
打印结果
上面是获取并执行的都是公有方法,在执行私有方法时要加一句
setAccessible(true)
授予权限。
以上就是刚学反射的笔记。