今日任务

反射的概念
反射构造方法
反射成员变量
反射成员方法
反射类的构造信息
使用反射创建对象
反射的案例

1.反射的概念

反射(Reflection): 反射是一种Java编程语言的功能。

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

反射就是把Java类中的各种成分(属性、普通方法、构造方法)映射成一个个的java对象。例如,一个类有:成员变量,方法,构造方法,包等等信息,利用反射技术可以对一个类进行解剖,把各个组成部分映射成一个个对象。

Java基本的组成单元由类组成的,类(属性、普通方法、构造方法),要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象。

2.反射的作用

反射是Java中的高级特性,在各种Java框架中都需要使用反射。所以,就算你将来很长一段时间不使用反射,但你使用的框架都大量使用了反射,所以想深入学习框架,那么就一定要学习反射。
框架通常通过反射来识别一个对象的“类型信息”。当你传递给框架一个对象时,框架会通过反射来了解对象的真实类型(对象实体的类型,而不是引用的类型),这个类型有几个构造器,有什么样的属性,有什么样的方法。还可以通过反射调用构造器,调用方法,对属性进行读写操作。

3.反射常用类

Class类—可获取类和类的成员信息 
Field类—可访问类的属性
Method类—可调用类的方法
Constructor类—可调用类的构造方法
/**
* @author bruceliu
* @time 2019年3月22日上午9:56:33
* @Description OOP编程
*/
public class Demo1 {

public static void main(String[] args) {

Person p=new Person();

//类和对象的关系
//类是一类事务的统称,例如 人类 就是一个类 刘德华就是一个对象
//有共性的事务,可以被抽象出来 形成一个类!!
// 旺财 阿黄 小黑 -----> Dog类 (自定义的类)
// 菊花 梅花 桃花 -----> Flower类 (自定义的类)
// 10 100 250 10000 -----> Integer类 (JDK中的类)
// "哈哈" "Hello" "早上好" -----> String类 (JDK中的类) JDK中 SUN公司的人员把开发中常用类 都给我们定义好了 成千上万!
// age name address sex (属性名、属性访问修饰符 、默认值 , 类型) -----> Field类—----可访问类的属性
// show() say() .... (方法名 访问修饰符 返回值 参数 ) ------> Method类 可调用类的方法
// Person() Person(String name) (方法名 访问修饰符 参数 ) ------> Constructor类—可调用类的构造方法
// Person.class Dog.class Flower.class (都有属性 都有构造 class 名字) ------> Class类 可获取类和类的成员信息

}
}

4.使用反射的基本步骤

1.导入java.lang.reflect.*       
2.获得需要操作的类的Java.lang.Class对象
3.调用Class的方法获取Field、Method等对象
4.使用反射API进行操作(设置属性﹑调用方法)

5.反射的入口类Class

让我们从Class类开始了解反射!
JAVASE专题-反射机制_创建对象
每个加载到方法区中的class文件都对应一个Class类的对象,你可以把Class类的对象理解为硬盘上的class文件的对应体。
JAVASE专题-反射机制_成员变量_02
Class类——表示类的类

5.1 Class类是反射机制的起源和入口

每个类都有自己的Class对象,用于获取与类相关的各种信息
Class类:继承于Object,存储了一些类的字节码信息
JAVASE专题-反射机制_构造方法_03

阅读API的Class类得知,Class 没有公共构造方法。

5.2 获取Class的三种方式

JAVASE专题-反射机制_构造方法_04
方式一: 通过Object类中的getObject()方法

Person p = new Person();
Class c = p.getClass();

方式二: 通过 类名.class 获取到字节码文件对象(任意数据类型都具备一个class静态属性,看上去要比第一种方式简单)。

Class c2 = Person.class;

方式三: 通过Class类中的方法(将类名作为字符串传递给Class类中的静态方法forName即可)。

Class c3 = Class.forName("com.bruceliu.Person");

注意:第三种和前两种的区别
前两种你必须明确Person类型.
后面是指定这种类型的字符串就行.这种扩展更强.我不需要知道你的类.我只提供字符串,按照配置文件加载就可以了

基本数据类型获取Class的方式还有2中方式:
JAVASE专题-反射机制_创建对象_05
代码演示

/**
* @author bruceliu
* @Description
*/
public class ReflectDemo {

public static void main(String[] args) throws Exception {

// 1: 通过Object类中的getObject()方法
// Person p1 = new Person();
// Class c1 = p1.getClass();
// System.out.println("c1 = "+ c1);

// 2: 通过 类名.class 获取到字节码文件对象
// Class c2 = Person.class;
// System.out.println("c2 = "+ c2);

// 3: 反射中的方法
Class c3 = Class.forName("com.bruceliu.demo8.Person");// 包名.类名
System.out.println("c3 = " + c3);
}
}

Person类

/**
* @author bruceliu
* @Description
*/
public class Person {

// 成员变量
public String name;
public int age;
private String address;

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 String getAddress() {
return address;
}

public void setAddress(String address) {
this.address = address;
}

@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + ", address=" + address + "]";
}
}

6.反射类的属性信息

在反射机制中,把类中的成员变量使用类Field表示。可通过Class类中提供的方法获取成员变量:
返回一个成员变量

public Field getField(String name) 获取指定的 public修饰的变量
public Field getDeclaredField(String name) 获取指定的任意变量(包括私有)

返回多个成员变量

public Field[] getFields() 获取所有public 修饰的变量
public Field[] getDeclaredFields() 获取所有的 变量 (包含私有)
  • 示例:获取一个任意一个类中到底有多少属性?
    JAVASE专题-反射机制_创建对象_06
    JAVASE专题-反射机制_创建对象_07
    JAVASE专题-反射机制_创建对象_08
    默认的是 0表示!
//反射类的属性信息
public static void main(String[] args) {

Class c=Student.class;

Field[] filds = c.getDeclaredFields(); //获取属性数组

for (Field f : filds) {
int modifiers = f.getModifiers(); //获取属性的访问修饰符
Class type = f.getType(); //属性的类型
String name = f.getName(); //得到类的属性名
System.out.println(modifiers+"--"+type.getName()+"----"+name);
}
}

暴力反射类中的属性:
JAVASE专题-反射机制_成员变量_09

public static void main(String[] args) throws Exception {

Class c = Class.forName("com.bruceliu.demo7.Student");
Student s = (Student) c.newInstance(); // 创建一个对象,默认调用无参数的构造方法!

Field f1 = c.getDeclaredField("age"); // 私有的属性
f1.setAccessible(true); // 暴力反射 私有的属性 true

f1.setInt(s, 18); // 修改属性的值
int age = f1.getInt(s);

Field f2 = c.getDeclaredField("name");
f2.setAccessible(true); // 暴力反射 私有的属性 true
f2.set(s,"小张");

System.out.println(s);

}

7.反射类的方法信息

在反射机制中,把类中的成员方法使用类Method表示。可通过Class类中提供的方法获取成员方法:
返回获取一个方法:

获取public 修饰的方法
public Method getMethod(String name, Class<?>... parameterTypes)

获取任意的方法,包含私有的
public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
参数1: name 要查找的方法名称;
参数2: parameterTypes 该方法的参数类型

返回获取多个方法:

public Method[] getMethods() 获取本类与父类中所有public 修饰的方法
public Method[] getDeclaredMethods() 获取本类中所有的方法(包含私有的)
public static void main(String[] args) throws Exception {

Class c = Class.forName("com.baidu.demo.Student");
Method[] methods = c.getDeclaredMethods(); //获取类所有的方法
System.out.println("方法个数:"+methods.length);
for (Method m : methods) {

int modifiers = m.getModifiers(); //修饰符
Class t = m.getReturnType();
String type = t.getName(); //返回值类型
String name = m.getName(); //方法名
Class[] t1 = m.getParameterTypes();
System.out.println(modifiers+"--"+type+"--"+name);
for (Class c1 : t1) {
String parameterType= c1.getName(); //方法参数,类型 参数有多个
System.out.println("参数类型:"+parameterType);
}
Parameter[] parameters = m.getParameters(); //JDK1.7无法调用!
for (Parameter p : parameters) {
String parameterName = p.getName(); //参数名字
System.out.println("参数名字:"+parameterName);
}
System.out.println("====================================");
}

}

反射调用方法:
获取成员方法,步骤如下:

1. 获取Class对象
2. 获取构造方法
3. 通过构造方法,创建对象
4. 获取指定的方法
5. 执行找到的方法
public Object invoke(Object obj, Object... args)
执行指定对象obj中,当前Method对象所代表的方法,方法要传入的参数通过args指定。
public class Test1 {
public static void main(String[] args) throws Exception {
T t = new T();
Class c = t.getClass();
Method method = c.getDeclaredMethod("show1", new Class[] { String.class });
method.setAccessible(true);
method.invoke(t, "test private");

Method method1=Class.forName("com.bruceliu.demo1.T").getDeclaredMethod("show2", new Class[]{String.class,Integer.class});
method1.setAccessible(true);
method1.invoke(T.class, "test static private",808000);

Method method2=t.getClass().getDeclaredMethod("show3",new Class[]{String.class});
//method2.setAccessible(true);
method2.invoke(t, "test public");
}
}

class T {
public void show3(String s) {
System.out.println("公共普通方法");
}

private static void show2(String s, Integer i) {
System.out.println("静态方法,参数是: " + s + " " + i);
}

private void show1(String s) {
System.out.println("你好,参数是: " + s);
}
}

8.反射类的构造方法信息

在反射机制中,把类中的成员(构造方法、成员方法、成员变量)都封装成了对应的类进行表示。其中,构造方法使用类Constructor表示。可通过Class类中提供的方法获取构造方法:
返回一个构造方法

public Constructor<T> getConstructor(Class<?>... parameterTypes) 获取public修饰, 指定参数类型所对应的构造方法
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) 获取指定参数类型所对应的构造方法(包含私有的)

返回多个构造方法

public Constructor<?>[] getConstructors() 获取所有的public 修饰的构造方法
public Constructor<?>[] getDeclaredConstructors() 获取所有的构造方法(包含私有的)
public static void main(String[] args) throws Exception {

Class c = Class.forName("com.baidu.demo.Student");

Constructor[] constructors = c.getDeclaredConstructors(); //获取构造方法集合
System.out.println("构造方法个数:"+constructors.length);

for (Constructor con : constructors) {
int modifiers = con.getModifiers(); //访问修饰符
String name = con.getName(); //方法名
Class[] t = con.getParameterTypes();
System.out.println(modifiers+"--"+name);
for (Class t1 : t) {
String pname = t1.getName(); //参数类型
System.out.println("参数类型:"+pname);
}
Parameter[] parameters = con.getParameters();
for (Parameter p : parameters) {
String pname = p.getName(); //参数名
System.out.println("参数名:"+pname);
}
System.out.println("-------------------------");
}
}

9.使用反射创建对象

获取构造方法,步骤如下:

1. 获取到Class对象
2. 获取指定的构造方法
3. 通过构造方法类Constructor中的方法,创建对象
public T newInstance(Object... initargs)

AccessibleObject 类是 Field、Method 和 Constructor 对象的父类。它提供了将反射的对象标记为在使用时取消默认 Java 语言访问控制检查的能力。

对于公共成员、默认(打包)访问成员、受保护成员和私有成员,在分别使用 Field、Method 或 Constructor 对象来设置或获取字段、调用方法,或者创建和初始化类的新实例的时候,会执行访问检查。常用方法如下:

public void setAccessible(boolean flag) throws SecurityException 
参数值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。参数值为 false 则指示反射的对象应该实施 Java 语言访问检查。

获取私有构造方法,步骤如下:

1. 获取到Class对象
2. 获取指定的构造方法
3. 暴力访问, 通过setAccessible(boolean flag)方法
4. 通过构造方法类Constructor中的方法,创建对象
public T newInstance(Object... initargs)

创建对象不仅仅使用new的这种方式,反射也可以创建对象!!!

//反射创建对象
public static void main(String[] args) throws Exception {

Class c = Student.class;
Student s1 = (Student)c.newInstance(); //默认调用无参数的构造方法创建对象
s1.setName("德华");
System.out.println(s1);


// 获取到第二个构造方法
Constructor con = c.getDeclaredConstructor(new Class[] { int.class, String.class });
con.setAccessible(true);
// 构造方法创建对象
Student s2 = (Student) con.newInstance(new Object[] { 18, "凤姐" });
System.out.println(s2);
}
}

public static void main(String[] args) throws Exception {

Class c = Class.forName("com.baidu.demo.Student");
Student s1 = (Student) c.newInstance(); // 调用无参数的构造方法 创建对象
System.out.println(s1);

// 有参数的构造方法
Constructor con = c.getDeclaredConstructor(new Class[] { int.class, String.class, String.class });
con.setAccessible(true);// 暴力访问构造方法
Student s2 = (Student) con.newInstance(new Object[] { 18, "凤姐", "你好" }); // 调用带参数的构造方法创建对象
System.out.println(s2);
System.out.println(s2.getAge());
System.out.println(s2.getName());
System.out.println(s2.getSex());
}

10.使用反射案例

JAVASE专题-反射机制_构造方法_10

11.反射总结

如何获取.Class文件对象

1, 通过Object类 getClass()方法获取 Class对象 
2, 通过类名.class 方式 获取 Class对象
3, 通过反射的方式, Class.forName(String classname) 获取Class对象
public static Class<?> forName(String className)throws ClassNotFoundException
返回与带有给定字符串名的类或接口相关联的 Class 对象

通过反射, 获取类中的构造方法,并完成对象的创建

获取指定的构造方法
public Constructor<T> getConstructor(Class<?>... parameterTypes)
获取指定的public修饰的构造方法
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
获取指定的构造方法,包含私有的

获取所有的构造方法
public Constructor<?>[] getConstructors() 获取所有的public 修饰的构造方法
public Constructor<?>[] getDeclaredConstructors() 获取所有的构造方法,包含私有的

通过反射, 获取类中的构造方法,并完成对象的创建

1,获取字节码文件对象
2,通过字节码文件对象 ,获取到指定的构造方法
getConstructor(参数);
3,通过构造方法,创建对象
public T newInstance(Object... initargs)

私有构造方法,创建对象

1.获取字节码文件对象
2.通过字节码文件对象 ,获取到指定的构造方法
getDeclaredConstructor (参数);
3.暴力访问
con.setAccessible(true);
4.通过构造方法,创建对象
public T newInstance(Object... initargs)

通过反射,获取Class文件中的方法

获取指定的方法
public Method getMethod(String name, Class<?>... parameterTypes)

获取指定的public方法
public Method getDeclaredMethod(String name, Class<?>... parameterTypes)

获取指定的任意方法,包含私有的
获取所有的方法
public Method[] getMethods() 获取本类与父类中所有public 修饰的方法
public Method[] getDeclaredMethods()获取本类中所有的方法,包含私有的

通过反射,调用方法

1.获取Class对象
2,获取构造方法,创建对象
3,获取指定的public方法
4.执行方法
public Object invoke(Object obj, Object... args)

私有方法的调用:

1,获取Class对象
2,获取构造方法,创建对象
3,获取指定的private方法
4,开启暴力访问
m5.setAccessible(true);
5,执行方法
public Object invoke(Object obj, Object... args)

通过反射,获取成员变量(字段)

获取指定的成员变量
public Field getField(String name) 获取public修饰的成员变量
public Field getDeclaredField(String name) 获取任意的成员变量,包含私有
获取所有的成员变量
public Field[] getFields() 获取所有public修饰的成员变量
public Field[] getDeclaredFields() 获取司所有的成员变量,包含私有

通过反射,获取成员 变量,并赋值使用

1,获取字节码文件对象
2,获取构造方法,创建对象
3,获取指定的成员变量
4,对成员变量赋值\获取值操作
public void set(Object obj, Object value) 赋值
public Object get(Object obj) 获取值

私有成员变量的使用

1,获取字节码文件对象
2,获取构造方法,创建对象
3,获取指定的成员变量
4,开启暴力访问
5,对成员变量赋值\获取值操作
public void set(Object obj, Object value) 赋值
public Object get(Object obj) 获取值