反射
一、反射的概念
1.什么是反射
Java中的反射值得是程序在运行状态中
1.对于一个给定的类(class)对象,可以获取到这个类(class)对象的所有的属性和方法
2.对于给定的一个对象(new XXXClassName<?extends Object>),都能够调用它的任意一个属性和方法,这种动态获取类的内容以及动态调用对象的方法和属性的机制,就叫做Java的反射机制。
Java反射的优缺点:
优点:
1.增加程序的灵活性,避免将固有得到逻辑程序写死在代码里面
2.代码简洁,可读性增强,可提高代码的复用率
缺点:
1.相较于直接调用在量大的情况下反射性能下降厉害
2.内部暴露和安全隐患
2.Class的组成
一个Class实例包含的内容又哪些呢?
我们在程序运行的时候获取到Class类型,我们要根据Class类型来获取相关的内容
二、Class的操作
1.怎么获取类对象
自定义的Person类:
package com.wedu.reflectionDemo;
/**
* Person类
*/
public class Person {
private Integer id;
private String color;
private Integer age;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
测试类:
package com.wedu.reflectionDemo;
public class ReflectionDemo1 {
/**
* 获取Class对象的方式
* @param args
*/
public static void main(String[] args) throws ClassNotFoundException {
//获取Person对应的Class对象
System.out.println("-------获取Person对应的Class对象---------");
Class<Person> class1 = Person.class;
System.out.println(class1);
Class class2 = Class.forName("com.wedu.reflectionDemo.Person");
System.out.println(class2);
//还可以通过Person对象中的getClass方法获取
System.out.println("------通过Person对象中的getClass方法获取--------");
Person p = new Person();
Class<? extends Person> class3 = p.getClass();
System.out.println(class3);
//系统提供的一些类型获取Class对象
System.out.println("------系统提供的一些类型获取Class对象------");
Class<String> stringClass = String.class;
Class<Integer> integerClass = int.class;
Class<int[]> aClass = int[].class;
System.out.println(stringClass);
System.out.println(integerClass);
System.out.println(aClass);
Class<double[]> aClass1 = double[].class;
System.out.println(aClass1);
//获取包装类对应的Class对象
System.out.println("------获取包装类对应的Class对象----------");
Class<Integer> integerClass1 = Integer.class;
Class<Integer> type = Integer.TYPE;
System.out.println(integerClass1);
System.out.println(type);
//我要获取void 没回返回结果的 Class类型
System.out.println("----------获取void 没回返回结果的 Class类型----------");
Class<Void> type1 = Void.TYPE;
Class<Void> voidClass = Void.class;
System.out.println(type1);
System.out.println(voidClass);
}
}
输出结果:
-------获取Person对应的Class对象---------
class com.wedu.reflectionDemo.Person
class com.wedu.reflectionDemo.Person
------通过Person对象中的getClass方法获取--------
class com.wedu.reflectionDemo.Person
------系统提供的一些类型获取Class对象------
class java.lang.String
int
class [I
class [D
------获取包装类对应的Class对象----------
class java.lang.Integer
int
----------获取void 没回返回结果的 Class类型----------
void
class java.lang.Void
2. Class对象中的常用方法
2.1 getName()
获取当前类型的全类路径名称
2.2 newInstance()
通过无参构造方法获取对应的实例对象,如果无参构造方法被覆盖的话,会抛出NoSuchMethodException: com.wedu.reflectionDemo.Student.<init>()
异常
2.3 getSuperClass()
获取当前类型的的父类,如果没有显示继承的父类,那么返回的是Object
2.4 getInterface()
获取当前类型实现的所有接口
定义两个接口让Student类实现:
接口1:
public interface InterfaceDemo1 {
}
接口2:
public interface InterfaceDemo2 {
}
注意:
Student类中加了两个构造器:
public Student(Integer stuNum) {
this.stuNum = stuNum;
}
public Student() {
}
案例代码:
package com.wedu.reflectionDemo;
import java.util.Arrays;
public class ReflectionDemo2 {
/**
*反射中的常用方法
* @param args
*/
public static void main(String[] args) throws InstantiationException, IllegalAccessException {
//获取一个Student的类对象
Class<Student> studentClass = Student.class;
//getName() 完整包名+类名
System.out.println("----getName() 完整包名+类名--------");
System.out.println(studentClass.getName());
//通过类型获取一个Student对象 newInstance() 调用的是无参的构造方法
//如果没有无参构造方法那么会抛 NoSuchMethodException: com.wedu.reflectionDemo.Student.<init>()
System.out.println("---------通过类型获取一个Student对象 newInstance()----------");
Student student = studentClass.newInstance();
System.out.println(student);
//获取当前类对象的父类对象
System.out.println("----获取当前类对象的父类对象------------");
Class<? super Student> superclass = studentClass.getSuperclass();
System.out.println(superclass);
System.out.println(superclass.getSuperclass());
//获取当前类型实现的接口
System.out.println("--------获取当前类型实现的接口-----------");
Class<?>[] interfaces = studentClass.getInterfaces();
System.out.println(Arrays.toString(interfaces));
}
}
输出结果:
----getName() 完整包名+类名--------
com.wedu.reflectionDemo.Student
---------通过类型获取一个Student对象 newInstance()----------
com.wedu.reflectionDemo.Student@1d81eb93
----获取当前类对象的父类对象------------
class com.wedu.reflectionDemo.Person
class java.lang.Object
--------获取当前类型实现的接口-----------
[interface com.wedu.reflectionDemo.InterfaceDemo1, interface com.wedu.reflectionDemo.InterfaceDemo2]
2.5 获取属性
Field getField(String name)
//返回一个 Field对象,它反映此表示的类或接口的指定公共成员字段 类对象。
Field[] getFields()
//返回包含一个数组 Field对象反射由此表示的类或接口的所有可访问的公共字段 类对象。
Field getDeclaredField(String name)
//返回一个 Field对象,它反映此表示的类或接口的指定已声明字段 类对象。
Field[] getDeclaredFields()
//返回的数组 Field对象反映此表示的类或接口声明的所有字段 类对象。
package com.wedu.reflectionDemo;
import java.lang.reflect.Field;
public class ReflectionDemo3 {
/**
* 反射获取属性的方法
* @param args
*/
public static void main(String[] args) {
Class<Student> studentClass = Student.class;
//1.获取相关的属性方法 Class中的每一个属性会被封装为一个Field对象
//getFields() 获取当前类型和父类中的所有的public权限的字段
System.out.println("--------getFields()-----------");
Field[] fields = studentClass.getFields();
for (Field field:fields) {
System.out.println(field.getName());
}
//getDeclareFields() 表示的获取当前类中的所有的 属性, 包括私有的
System.out.println("-----------getDeclareFields()------------");
Field[] declaredFields = studentClass.getDeclaredFields();
for (Field field:declaredFields) {
System.out.println(field.getName());
}
}
}
输出结果:
--------getFields()-----------
gender
id
-----------getDeclareFields()------------
stuNum
schoolName
className
gender
Student类:
package com.wedu.reflectionDemo;
public class Student extends Person implements InterfaceDemo1,InterfaceDemo2{
private Integer stuNum;
String schoolName;
protected String className;
public String gender;
public String say(){
System.out.println("say……");
return "hello……";
}
void sleep(){
System.out.println("sleep……");
}
protected void run(){
System.out.println("gogogogogogogogogoog……");
}
private void eat(){
System.out.println("eat …… food");
}
public Student(Integer stuNum) {
this.stuNum = stuNum;
}
public Student() {
}
}
针对属性的操作:
package com.wedu.reflectionDemo;
import java.lang.reflect.Field;
public class ReflectionDemo4 {
/**
* 反射获取属性的方法之后操作属性
*
* @param args
*/
public static void main(String[] args) throws Exception {
Class<Student> studentClass = Student.class;
//获取Student对象
Student student = studentClass.newInstance();
student.gender = "男";
//通过类型获取gender属性
Field gender = studentClass.getDeclaredField("gender");
System.out.println(gender.getName());
//获取对应的访问权限修饰符 1表示 public, 2表示 private,4表示 protected
System.out.println(gender.getModifiers());
//获取或设置属性的信息
System.out.println(gender.get(student));
//修改属性值
gender.set(student,"女");
System.out.println(gender.get(student));
//操作私有属性
Field stuNum = studentClass.getDeclaredField("stuNum");
//在反射中是不允许直接操作私有属性的,如果一定要操作必须放开权限
stuNum.setAccessible(true);//允许对私有属性的操作
System.out.println(stuNum.get(student));
//修改私有属性的信息
stuNum.set(student,1001);
System.out.println(stuNum.get(student));
//养成好的习惯,操作属性完成后,把权限关闭
stuNum.setAccessible(false);
}
}
输出结果:
gender
1
男
女
null
1001
2.6获取方法
Method getMethod(String name, 类<?>... parameterTypes)
//返回一个 方法对象,它反映此表示的类或接口的指定公共成员方法 类对象。
Method[] getMethods()
//返回包含一个数组 方法对象反射由此表示的类或接口的所有公共方法 类对象,包括那些由类或接口和那些从超类和超接口继承的声明。
Method getDeclaredMethod(String name, 类<?>... parameterTypes)
//返回一个 方法对象,它反映此表示的类或接口的指定声明的方法 类对象。
Method[] getDeclaredMethods()
//返回包含一个数组 方法对象反射的类或接口的所有声明的方法,通过此表示 类对象,包括公共,保护,默认(包)访问和私有方法,但不包括继承的方法。
接着对自定义Student类中的say()方法做了修改,看笔记时请注意
public String say(String name){
System.out.println("say……"+name);
return "hello……";
}
Mehod的操作:
注意say方法又做了修改:
public String say(String name) throws RuntimeException{
System.out.println("say……"+name);
return "hello……";
}
案例代码:
package com.wedu.reflectionDemo;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Arrays;
public class ReflectionDemo6 {
/**
* Method对象
*
* @param args
*/
public static void main(String[] args) throws Exception {
Class<Student> studentClass = Student.class;
//获取特定的方法 say
Method say = studentClass.getDeclaredMethod("say", String.class);
//方法的调用 方法属于普通方法,那么我们要通过对象类调用
Student student = new Student();
//第一个参数 要调用哪个对象的的say方法 第二个参数 是一个 参数列表 调用方法的实际参数
Object obj = say.invoke(student, "bobo");
System.out.println(obj);
//私有方法的调用 eat
Method eat = studentClass.getDeclaredMethod("eat");
//调用私有的方法,我们需要放开权限
eat.setAccessible(true);
Object obj1 = eat.invoke(student);
System.out.println(obj1);
//调用完成后记得收回权限
eat.setAccessible(false);
//获取方法的返回值类型
System.out.println(say.getReturnType());
System.out.println(eat.getReturnType());
//获取方法显示抛出的异常
Class<?>[] exceptionTypes = say.getExceptionTypes();
System.out.println(Arrays.toString(exceptionTypes));
//获取方法声明的参数
Parameter[] parameters = say.getParameters();
for (Parameter parameter : parameters) {
//Parameter封装的是 属性的对象
System.out.println(parameter.getName() + " " + parameter.getModifiers() + " " + parameter.toString());
}
}
}
输出结果:
say……bobo
hello……
eat …… food
null
class java.lang.String
void
[class java.lang.RuntimeException]
arg0 0 java.lang.String arg0
2.7 获取构造方法
方法:
Constructor<T> getConstructor(类<?>... parameterTypes)
//返回一个 Constructor对象,该对象反映 Constructor对象表示的类的指定的公共 类函数。
Constructor<?>[] getConstructors()
//返回包含一个数组 Constructor对象反射由此表示的类的所有公共构造 类对象。
Constructor<T> getDeclaredConstructor(类<?>... parameterypes)
//返回一个 Constructor对象,该对象反映 Constructor对象表示的类或接口的指定 类函数。
Constructor<?>[] getDeclaredConstructors()
//返回一个反映 Constructor对象表示的类声明的所有 Constructor对象的数组 类 。
注意:Student类中新增了私有构造器
private Student(Integer stuNum, String schoolName, String className, String gender) {
this.stuNum = stuNum;
this.schoolName = schoolName;
this.className = className;
this.gender = gender;
}
代码:
import java.lang.reflect.Constructor;
import java.util.Arrays;
public class ReflectionDemo7 {
/**
*反射中的构造方法
* @param args
*/
public static void main(String[] args) {
Class<Student> studentClass = Student.class;
//获取对应的构造器 getConstructors() 获取当前类中所有的public构造器
Constructor<?>[] constructors = studentClass.getConstructors();
for (Constructor constructor:constructors) {
System.out.println(constructor.getName()+" "+ Arrays.toString(constructor.getParameters()));
}
System.out.println("--------------------------");
//获取当前类中所有的 构造方法 包括private 修饰的
Constructor<?>[] declaredConstructors = studentClass.getDeclaredConstructors();
for (Constructor constructor:declaredConstructors) {
System.out.println(constructor.getName()+" "+ Arrays.toString(constructor.getParameters()));
}
输出结果:
com.wedu.reflectionDemo.Student []
com.wedu.reflectionDemo.Student [java.lang.Integer arg0]
--------------------------
com.wedu.reflectionDemo.Student []
com.wedu.reflectionDemo.Student [java.lang.Integer arg0]
com.wedu.reflectionDemo.Student [java.lang.Integer arg0, java.lang.String arg1, java.lang.String arg2, java.lang.String arg3]
constructor操作
注意:
测试通过私有的构造器来使用构造器的操作的时候可以把一个构造方法改成私有的,这里改的是一个参数的构造器,如下所示:
private Student(Integer stuNum) {
this.stuNum = stuNum;
}
案例代码:
package com.wedu.reflectionDemo;
import java.lang.reflect.Constructor;
import java.util.Arrays;
public class ReflectionDemo8 {
/**
* 反射 构造器的操作
* @param args
*/
public static void main(String[] args) throws Exception {
Class<Student> studentClass = Student.class;
//获取一个构造器
Constructor<Student> c = studentClass.getDeclaredConstructor(Integer.class);
System.out.println(c.getName());
System.out.println(c.getModifiers());
System.out.println(c.getParameterCount());
/*//通过构造器创建实例对象
Student s = c.newInstance(1009);
System.out.println(s);
//获取构造方法显示抛出的异常
Class<?>[] exceptionTypes = c.getExceptionTypes();
System.out.println(Arrays.toString(exceptionTypes));*/
//操作private修饰的构造方法 我们同样需要需要放开权限
c.setAccessible(true);
//通过私有构造器创建实例对象
Student s = c.newInstance(1009);
//操作完成后同样需要关闭权限
c.setAccessible(false);
System.out.println(s);
//获取构造方法显示抛出的异常
Class<?>[] exceptionTypes = c.getExceptionTypes();
System.out.println(Arrays.toString(exceptionTypes));
}
}
输出结果:
com.wedu.reflectionDemo.Student
2
1
com.wedu.reflectionDemo.Student@1d81eb93
[]
2.8静态属性和静态方法操作
首先在我们自定义的Student类中定义静态属性和静态方法,然后再对他们操作,添加如下:
public static String s1="aaa";
public static void fun1(){
System.out.println("fun...");
}
案例代码:
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class ReflectionDemo9 {
/**
* 反射 静态属性和静态方法的操作
* @param args
*/
public static void main(String[] args) throws Exception {
Class<Student> studentClass = Student.class;
//静态属性的操作
Field s1 = studentClass.getDeclaredField("s1");
System.out.println(s1.get("s1"));
s1.set(null,"hahahaha");
System.out.println(s1.get("s1"));
//静态方法的调用
Method fun1 = studentClass.getDeclaredMethod("fun1");
fun1.invoke(null);
}
输出结果:
aaa
hahahaha
fun...
静态代码块的执行:
首先在Student类中给一个静态代码块:
static {
System.out.println("static code ……");
}
测试代码:
public class ReflectionDemo10 {
/**
* 反射 静态代码块的执行
* @param args
*/
public static void main(String[] args) throws Exception {
//.class的方式是不会触发类加载的
Class<Student> studentClass = Student.class;
//Class.forName 会显示的加载类
Class<?> aClass = Class.forName("com.wedu.reflectionDemo.Student");
//对象.getClass() 先要获取对象,n肯定执行力静态代码块了
}
输出结果:
static code ……
三、单例的漏洞
我们之前介绍过单例的使用,保证一个类只有一个实例存在,但是我们讲了反射后发现,私有的构造器也能够获取到,进而可以创建出很多个实例对象,这显然和我们的而期望不一致,那么针对这个漏洞,我们应该怎么办呢?
Bug还原
单例代码
public class SingletonTest {
//声明一个静态的单例属性
private static SingletonTest instance;
//私有化构造器
private SingletonTest(){
}
//在对外提供一个静态的公有的方法来获取单例对象
public static SingletonTest getInstance(){
if (instance==null){
synchronized (SingletonTest.class){
if (instance==null){
instance = new SingletonTest();
}
}
}
return instance;
}
}
测试代码:
package com.wedu.reflectionDemo;
import java.lang.reflect.Constructor;
public class ReflectionDemo11 {
/**
*
* @param args
*/
public static void main(String[] args) throws Exception {
SingletonTest i1 = SingletonTest.getInstance();
System.out.println(i1);
SingletonTest i2 = SingletonTest.getInstance();
System.out.println(i2);
//通过反射的方式来获取对象 获取私有的构造器
Constructor<SingletonTest> c = SingletonTest.class.getDeclaredConstructor();
c.setAccessible(true);//放开权限 破坏了单例的设计
SingletonTest i3 = c.newInstance();
SingletonTest i4 = c.newInstance();
c.setAccessible(false); //关闭权限
System.out.println(i3);
System.out.println(i4);
}
}
输出结果:
com.wedu.reflectionDemo.SingletonTest@b4c966a//正常获取
com.wedu.reflectionDemo.SingletonTest@b4c966a//正常获取
com.wedu.reflectionDemo.SingletonTest@2f4d3709//反射获取
com.wedu.reflectionDemo.SingletonTest@4e50df2e//反射获取
我们发现造成这个bug的根本原因是private的构造方法多次执行了,那么我们只需要在私有构造方法中添加逻辑即可
如果要创建多个实例就会抛异常,从而不成功