Java基础知识 32
在写反射这篇文章之前,我们先思考一个问题,java中如何创建一个对象,有哪几种方式?
java中创建对象大概有这几种方式:
(1)使用new关键字:这是我们最常见的也是最简单的创建对象的方式。
(2)使用clone的方法:无论何时我们调用一个对象的clone方法,JVM就会创建一个新的对象,将前面的对象的内容全部拷贝进去。
(3)使用反序列化:当我们序列化和反序列化一个对象时,JVM会给我们创建一个单独的对象。
上述是Java中常见的创建对象的三种方式,还有另外一种方式,就是我们下面要讨论的反射。
反射概述:
(1)什么是反射?
反射就是把Java类中的各个部分,映射成一个个的Java对象,拿到这些对象后我们可以做一些事情。
通过反射机制,我们来剖析一个类的构成:
一个类的构成包括:构造方法,成员变量,成员方法。
通过反射来剖析这个类的三个构成部分,那么这三个构成,也会被看作是一种类型。
构造方法:constructor 成员变量:field 成员方法:Method
(2)反射能干什么?
一般来说反射是用来做框架的,或者说可以做一些抽象都比较高的底层代码,反射在日常的开发者用到的不多,但是必须得懂它,搞懂反射以后,可以帮助我们理解框架的一些原理。有一句很经典的话:反射是框架设计的灵魂。
1.反射构造方法
反射机制:Java反射机制是在运行状态中,对于任何一个类,都能够知道这个类的所有属性和方法,对于任意一个对象,都能够调用它的任意一个方法和属性,这种动态获取类的信息以及动态调用对象的方法的功能被称为Java语言的反射机制。要想解剖一个类。必须先要获取到该类的字节码文件对象,而解剖使用的就是Class类中的方法,所以先要获取到每一个字节码文件对应的class类型的对象。
我们要使用反射机制,那么就先要获取一个类的字节码文件对象(class字节码文件类型)
(1)获取一个类的字节码文件对象class对象的三种方式:
着重理解理解记忆forName(String className)
//方式1:就是通过Object类中的getClass()方法
Student student = new Student(); //Student.class------->字节码文件对象
Class<? extends Student> aClass = student.getClass();
Class<? extends Student> aClass1 = student.getClass();
System.out.println(aClass == aClass1);
System.out.println("---------------------------");
//方式2:通过Class中的静态方法 forName()方法来获取一个类的字节码文件对象
//权限定类名:包名+类名 就能确定一个类的唯一性
//使用反射方式来强制创建某个类或接口对应的java.long.Class对象
Class<?> aClass2 = Class.forName("org.westos.demo.Student");
Class<?> aClass3 = Class.forName("org.westos.demo.Student");
System.out.println(aClass2 == aClass3);
Class<?> aClass4 = Class.forName("org.westos.demo.Student");
System.out.println(aClass2 == aClass4);
System.out.println("---------------------------");
//方式3:针对每个类,都有一个静态的.class属性 来获取一个类的字节码文件对象
Class<Student> studentClass = Student.class;
Class<Object> objectClass = Object.class;
Class<String> stringClass1 = String.class;
Class<String> stringClass2 = String.class;
System.out.println(stringClass1 == stringClass2);
(2)通过反射获取私有和非私有的无参构造方法:
获取到所有的构造方法,私有的构造方法获取不到:getConstructors()
获取到该类中所有的构造方法 ,包括私有的构造方法:getDeclaredConstructors()
获取到单个(空参的)的构造方法对象,getConstructor()
//通过反射,来调用构造方法执行,创建出该类对象
//1.先获取该类的字节码文件对象
Class<?> aClass = Class.forName("org.westos.demo2.Student");//Student.class
//2.可以获取Student类的构造方法对象
//获取该类中所有的构造方法对象,私有的构造方法对象获取不到
Constructor<?>[] constructors = aClass.getConstructors();
for (Constructor<?> constructor : constructors) {
System.out.println(constructor);
}
System.out.println("------------------------");
//获取该类中所有的构造方法对象,包括私有的构造
Constructor<?>[] declaredConstructors = aClass.getDeclaredConstructors();
for (Constructor<?> declaredConstructor : declaredConstructors) {
System.out.println(declaredConstructor);
}
System.out.println("------------------------");
//获取单个的构造方法对象
//获取空参的构造方法对象
Constructor<?> constructor = aClass.getConstructor();
System.out.println(constructor);
System.out.println("------------------------");
//获取一个参数的构造方法对象,参数就是你构造方法的参数类型字节码类型
Constructor<?> constructor1 = aClass.getConstructor(String.class);
System.out.println(constructor1);
System.out.println("------------------------");
//获取私有的构造方法对象 getDeclaredConstructor()
Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(String.class, int.class);
System.out.println(declaredConstructor);
(3)通过反射获取私有和非私有的有参构造方法
获取到一个参数的构造方法对象,参数就是你构造方法的参数类型的字节码类型:getConstructor(String.class)
获取到私有的构造方法对象:getDeclaredConstructor(String.class,int.class)
//获取一个参数的构造方法对象,参数就是你构造方法的参数类型字节码类型
Constructor<?> constructor1 = aClass.getConstructor(String.class);
System.out.println(constructor1);
System.out.println("--------------------------------");
//获取私有的构造方法对象 getDeclaredConstructor()
Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(String.class, int.class);
System.out.println(declaredConstructor);
public class Mytest2 {
public static void main(String[] args) throws Exception {
//要创建一个类的对象,肯定要借助构造方法
//我们通过反射的方式来创造一个类的对象。
Class<?> aClass = Class.forName("org.westos.demo2.Student");
//获取空参的构造方法对象
Constructor<?> declaredConstructor = aClass.getDeclaredConstructor();
//通过构造方法对象中的方法,创建出Student类对象
Student o = (Student) declaredConstructor.newInstance();
System.out.println(o);
System.out.println("----------------------");
//使用反射的方式,通过有参构造来创建对象
Constructor<?> constructor = aClass.getConstructor(String.class);
//调整构造方法对象中的方法来创建student类的对象
Object obj = constructor.newInstance("李四");
System.out.println(obj);
System.out.println("----------------------");
//反射这种方式,即便你是私有构造,也能创建出该类的对象
//获取私有的构造方法对象
Constructor<?> declaredConstructor1 = aClass.getDeclaredConstructor(String.class, int.class);
//取消语法检查
declaredConstructor1.setAccessible(true);
Object obj1 = declaredConstructor1.newInstance("赵六", 26);
Student stu= (Student) obj1;
System.out.println(stu);
}
}
注意:在反射私有的构造函数时,用普通的aClass.getConstructor()会报错,因为它是私有的,所以提供了专门反射私有构造函数的方法aClass.getDeclaredConstructor(int.class)//读取私有的构造函数,用这个方法读取完还需要设置一下取消语法检测(也叫暴力反射)c.setAccessible(true)//暴力反射。
//反射这种方式,即便你是私有构造,也能创建出该类的对象
//获取私有的构造方法对象
Constructor<?> declaredConstructor1 = aClass.getDeclaredConstructor(String.class, int.class);
//取消语法检查
declaredConstructor1.setAccessible(true);//暴力反射
Object obj1 = declaredConstructor1.newInstance("赵六", 26);
Student stu= (Student) obj1;
System.out.println(stu);
public class Mytest {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//1.我们使用反射的方式,调用空参构造来创建对象的方式
Class<?> aClass = Class.forName("org.westos.demo3.Teacher");
//2.获取空参构造方法对象
Constructor<?> constructor = aClass.getConstructor();
//3.调用构造方法对象中的方法来创建该对象
Teacher teacher = (Teacher) constructor.newInstance();
System.out.println(teacher);
System.out.println("----------------------");
//方式2:如果只是想通过空参构造来创建对象,可以使用Class类里面的newInstance()方法直接创建对象
Teacher teacher1 = (Teacher) aClass.newInstance();
}
}
2.反射成员变量
如果通过反射来剖析一个类的构成,对于类中的成员变量,使用Field类型来描述成员变量。
获取类中的所有字段对象,但是私有的字段对象获取不到:getFields()
获取类中的所有字段对象,包括私有的字段对象:getDeclaredFields()
获取单个的非私有的字段对象,要获取哪个字段对象,可以将名字传入:getField()
获取单个的私有或非私有的字段对象,getDeclaredField()
public class Mytest {
public static void main(String[] args) throws Exception {
//如果通过反射来剖析一个类的构成,对于类中的成员变量,使用Field类型来描述成员变量
//1.要使用反射,第一步得先获得它的字节码文件
Class<?> aClass = Class.forName("org.westos.demo4.Dog");
//2.获取Dog类的所有字段对象,但是私有的字段对象获取不到
Field[] fields = aClass.getFields();
for (Field field : fields) {
System.out.println(field);
}
System.out.println("--------------------------");
//获取Dog类中的所有字段对象,包括私有的字段对象
Field[] declaredFields = aClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println(declaredField);
}
System.out.println("--------------------------");
//获取单个的非私有的字段对象,你要获取哪个字段对象,你可以将名字传入
Field Filename = aClass.getField("name");
System.out.println(Filename);
Field Fileage = aClass.getField("age");
System.out.println(Fileage);
System.out.println("--------------------------");
//获取单个的私有或者非私有的字段对象
Field Filesex = aClass.getDeclaredField("sex");
System.out.println(Filesex);
}
}
通过反射的方式,给类中的成员变量设置值以及获取值。
public class Mytest2 {
public static void main(String[] args) throws Exception {
//我们通过反射的方式,给类中的成员变量设置值,以及获取值
//1.要使用反射,得先获取该类的的字节码文件对象
Class<?> aClass = Class.forName("org.westos.demo4.Dog");
//2.给name字段设置,我们就要获取name字段对象
Field fieldname = aClass.getField("name");
//调用field类中的方法来设置值
//通过反射,来创建一个类的对象getDeclaredConstructor()
Constructor<?> declaredConstructor = aClass.getDeclaredConstructor();
declaredConstructor.setAccessible(true);
Object obj1 = declaredConstructor.newInstance();
//set()方法要两个参数,第一个参数就是你该类的对象,第二个就是你要设置的具体的值
fieldname.set(obj1,"小黑");
//获取name字段的值
Object obj2 = fieldname.get(obj1);
System.out.println(obj2);
System.out.println("-------------------------------");
Field fieldage = aClass.getField("age");
Constructor<?> declaredConstructor1 = aClass.getDeclaredConstructor();
declaredConstructor1.setAccessible(true);
Object obj3 = declaredConstructor1.newInstance();
fieldage.set(obj3,8);
//获取age字段的值
Object obj4 = fieldage.getInt(obj3);
System.out.println(obj4);
System.out.println("-------------------------------");
//给私有字段设置值
Field fieldsex = aClass.getDeclaredField("sex");
//对于私有的,我们可以取消语法检查
fieldsex.setAccessible(true);
fieldsex.setChar(obj1,'男');
//获取字段的值
char aChar = fieldsex.getChar(obj1);
System.out.println(aChar);
}
}
3.反射成员方法
对应类中的成员方法Java给我们提供了Method这个类来描述。
获取到类中的所有的非私有的成员方法,包括从父类继承下来的成员方法:getMethods()
获取到类中的所有的成员方法,包括私有的成员方法,但不包括继承父类的方法:getDeclaredMethods()
获取到单个的非私有方法对象:getMethod() 方法的参数:参数1:方法名,参数2:方法上形参数据类型.class
获取到单个的私有方法对象:getDeclaredMethod()
public class Mytest {
public static void main(String[] args) throws Exception {
//1.获取该类的字节码文件对象
Class<?> aClass = Class.forName("org.westos.demo.Student");
//2.获取到类中所有的成员方法对象,获取到非私有的方法对象,包括从父类继承下来的方法对象也能获得到
Method[] methods = aClass.getMethods();
for (Method method : methods) {
System.out.println(method);
}
System.out.println("----------------------");
//获取类中所有的成员方法对象,包括私有的,但是不要继承父类的方法对象
Method[] declaredMethods = aClass.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
System.out.println(declaredMethod);
}
System.out.println("----------------------");
//获取单个的非私有方法对象
Method heheMethod = aClass.getMethod("hehe");
System.out.println(heheMethod);
System.out.println("----------------------");
//获取有参的方法对象, getMethod()方法的参数1:方法名 参数2:方法上形参数据类型的.class
Method testMethod = aClass.getMethod("test", String.class, int.class);
System.out.println(testMethod);
System.out.println("----------------------");
//获取单个的私有方法对象
Method showMethod = aClass.getDeclaredMethod("show", String.class, int.class);
System.out.println(showMethod);
}
}
invoke(Object obj,Object…args):对带有指定参数的指定对象调用由此Method对象表示的底层方法。
public class Mytest2 {
public static void main(String[] args) throws Exception {
//我们用反射的方式,调用方法执行
Class<?> aClass = Class.forName("org.westos.demo.Student");
Method heheMethod = aClass.getMethod("hehe");
//Object invoke(Object obj, Object... args)
//对带有指定参数的指定对象调用由此 Method 对象表示的底层方法。
Constructor<?> declaredConstructor = aClass.getDeclaredConstructor();
declaredConstructor.setAccessible(true);
Object obj = declaredConstructor.newInstance();
//invoke(obj); //参数1 就是该类的对象
Object invoke = heheMethod.invoke(obj);
System.out.println("---------------------------");
//调用有参方法执行
Method hahaMethod = aClass.getMethod("haha", String.class);
hahaMethod.invoke(obj,"老师");
System.out.println("--------------------------");
Method testMethod = aClass.getMethod("test", String.class, int.class);
testMethod.invoke(obj,"张三",23);
System.out.println("--------------------------");
Method test2Method = aClass.getMethod("test2", String.class, int.class);
Object invoke1 = test2Method.invoke(obj, "李四", 24);
System.out.println(invoke1);
System.out.println("--------------------------");
//调用私有的方法执行
Method showMethod = aClass.getDeclaredMethod("show", String.class, int.class);
//取消私有的检查
showMethod.setAccessible(true);
showMethod.invoke(obj,"王五",25);
}
}
通过反射越过泛型检查
public class Mytest {
public static void main(String[] args) throws Exception {
//泛型,只在编译期有效,运行期就擦除了
ArrayList<String> list = new ArrayList<>();
list.add("AAA");
list.add("BBB");
list.add("CCC");
//list.add(123);
//我们可以通过反射机制,越过泛型检测
Class<? extends ArrayList> aClass = list.getClass();
//获取add方法对象
Method addMethod = aClass.getDeclaredMethod("add", Object.class);
//调用add方法执行
addMethod.invoke(list,"DDD");
addMethod.invoke(list,123);
addMethod.invoke(list,true);
System.out.println(list);
}
}
通过反射写一个通用的设置某个对象的某个属性为指定的值
测试类:
public class Mytest {
public static void main(String[] args) throws Exception {
//通过反射机制写一个通用的设置某个对象的某个属性为指定的值
Constructor<?> declaredConstructor = Class.forName("org.westos.demo4.Teacher").getDeclaredConstructor();
declaredConstructor.setAccessible(true);
Object obj = declaredConstructor.newInstance();
MyUtils.setProperty(obj,"name","张三");
Object name = MyUtils.getProperty(obj, "name");
System.out.println(name);
System.out.println("----------------------");
MyUtils.setProperty(obj,"age",30);
Object age = MyUtils.getProperty(obj, "age");
Integer num= (Integer) age;
System.out.println(num);
}
}
--------------------------------------------
工具包:
public class MyUtils {
public static void setProperty(Object obj,String propertyName,Object value) throws Exception {
//获取字节码对象
Class<?> aClass = obj.getClass();
Field declaredField = aClass.getDeclaredField(propertyName);
declaredField.setAccessible(true);
declaredField.set(obj,value);
}
public static Object getProperty(Object obj,String propertyName) throws Exception {
//获取字节码对象
Class<?> aClass = obj.getClass();
Field declaredField = aClass.getDeclaredField(propertyName);
declaredField.setAccessible(true);
Object value = declaredField.get(obj);
return value;
}
}
---------------------------------------------
具体类:
public class Teacher {
private Teacher(){
}
private String name;
private int age;
}