反射
类的加载
类的加载时机
- 创建类的实例。
- 调用类的静态变量,或者为静态变量赋值。
- 调用类的静态方法。
- 使用反射方式加载某个类,并生成Class对象。
- 初始化某个类的子类。
- 直接使用java.exe命令来运行某个主类。
类加载器
我们用到的类都是由类加载器负责加载到内存的。
三种类加载器:
启动类加载器(Bootstrap ClassLoader)
扩展类加载器(Extension ClassLoader)
应用程序类加载器(Application ClassLoader)
双亲委派机制
当某个类加载器需要加载一个类时,它并不是直接去加载,而是把加载的需要交给父类加载器,最终到达启动类加载器。
双亲委派机制的优点
双亲委派机制保证类只加载一次
反射概述
学习的各种框架的设计和底层原理:Spring、SpringMVC、Mybatis等
反射的引入
通过反射技术对象类进行了解剖得到了类的所有成员。
反射的概念
反射中,万物皆对象
Class对象:某个字节码文件
Field对象:某个成员变量
Method对象:某个成员方法
Constructor:某个构造方法
newInstance:创建对象
invoke:调用/执行
调用语法:
正常语法是:对象名.成员方法名(参数)
反射语法是:成员对象名.invoke(对象名,参数)
使用反射操作类成员的前提
反射的应用
Class对象的获取方式
三种获取方式
通过类名.class获得。
通过对象名.getClass()方法获得 。
通过Class类的静态方法获得: static Class forName(“类全名”) * 每一个类的Class对象都只有一个。
Class类常用方法
Class aClass = Dog.class;
//获得类全名
String name = aClass.getName();
//获得类名
String simpleName = aClass.getSimpleName();
//创建class对象所代表的那个类的对象,底层实际上使用Dog的无参构造
Dog o = (Dog) aClass.newInstance();
反射——操作构造方法(4种方式)
- Constructor getConstructor(Class… parameterTypes) //获得单个构造【只能获得public修饰的构造方法】
//获取单个无参构造方法
Constructor constructor = aClass.getConstructor();
//获取单个有参构造
Constructor constructor1 = aClass.getConstructor(int.class, String.class);
- Constructor getDeclaredConstructor(Class… parameterTypes) //获取单个构造【可以是public、protected、(默认)、private修饰符的构造方法】
//获取单个任意修饰符的构造【public、protected、(默认)、private】
Constructor constructor2 = aClass.getDeclaredConstructor(int.class, String.class);
- Constructor[] getConstructors() //获得类中的所有构造方法对象,只能获得public
//第一步是获取class对象
Dog dd = new Dog();
Class aClass = dd.getClass();
//获取单个无参构造方法
Constructor constructor = aClass.getConstructor();
//使用构造创建对象
Dog dog = (Dog) constructor.newInstance();
System.out.println(dog);
//获取单个有参构造
Constructor constructor1 = aClass.getConstructor(int.class, String.class);
//使用构造创建对象【如果是私有构造不能直接使用,因为没有权限】
Dog dog1 = (Dog) constructor1.newInstance(10, "小明");
System.out.println(dog1);
//如果是私有的,需要设置暴力权限
constructor1.setAccessible(true);
Dog dog2 = (Dog) constructor1.newInstance(10, "小明");
System.out.println(dog2);
- Constructor[] getDeclaredConstructors() //获得类中的所有构造方法对象 ,可以是public、protected、(默认)、private修饰符的构造方法。
//获取所有【public】构造
Constructor[] constructors = aClass.getConstructors();
System.out.println(Arrays.toString(constructors));
//获取任意修饰符的构造
Annotation[] declaredAnnotations = aClass.getDeclaredAnnotations();
for (Annotation declaredAnnotation : declaredAnnotations) {
System.out.println(declaredAnnotation);
}
Constructor的类概述
Class类张与Constructor相关的方法
Constructor对象常用方法
反射——操作成员方法与调用
public class GetMemo {
public static void main(String[] args) throws NoSuchMethodException {
//1、第一步是获取class对象
Dog dd = new Dog();
Class aClass = dd.getClass();
//2、获取单个成员方法【方法中没有参数】
aClass.getMethod("eat");
aClass.getMethod("eat",String.class,String.class);
//3、获取任意修饰的
aClass.getDeclaredMethod("eat",String.class);
//4、获取所有的成员方法【public且包括父类继承的】
Method[] methods = aClass.getMethods();
for (Method method : methods) {
System.out.println(method);
}
//5、获取所有的是私有的成员方法【任意修饰符】不包含父类继承的
Method[] methodss = aClass.getDeclaredMethods();
for (Method method : methodss) {
System.out.println(method);
}
}
}
成员对象名.invoke(对象名,参数)
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
//1、第一步是获取class对象
Dog dd = new Dog();
Class aClass = dd.getClass();
//2、获取单个成员方法【方法中没有参数】
Method eat = aClass.getMethod("eat");
//【使用成员方法】
eat.invoke(dd);
Method eat1 = aClass.getMethod("eat", String.class, String.class);
//【使用成员方法】
eat1.invoke(dd, "饭", "水");
//3、获取任意修饰的
Method eat3 = aClass.getDeclaredMethod("eat", String.class);
//设置暴力访问权限
eat3.setAccessible(true);
eat3.invoke("eat", "饭", "水");
反射——操作构造方法与调用
public class ConstructorDemo01 {
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//第一步是获取class对象
Dog dd = new Dog();
Class aClass = dd.getClass();
//获取单个无参构造方法
Constructor constructor = aClass.getConstructor();
//使用构造创建对象
Dog dog = (Dog) constructor.newInstance();
System.out.println(dog);
//获取单个有参构造
Constructor constructor1 = aClass.getConstructor(int.class, String.class);
//使用构造创建对象【如果是私有构造不能直接使用,因为没有权限】
Dog dog1 = (Dog) constructor1.newInstance(10, "小明");
System.out.println(dog1);
//如果是私有的,需要设置暴力权限
constructor1.setAccessible(true);
Dog dog2 = (Dog) constructor1.newInstance(10, "小明");
System.out.println(dog2);
//获取单个任意修饰符的构造【public、protected、(默认)、private】
Constructor constructor2 = aClass.getDeclaredConstructor(int.class, String.class);
//获取所有【public】构造
Constructor[] constructors = aClass.getConstructors();
System.out.println(Arrays.toString(constructors));
//获取任意修饰符的构造
Annotation[] declaredAnnotations = aClass.getDeclaredAnnotations();
for (Annotation declaredAnnotation : declaredAnnotations) {
System.out.println(declaredAnnotation);
}
}
}
Method类的概述
注解
JDK1.5的新特性。(注解、泛型、增强for、可变参数)
注解相当于是一种标记,是类的组成部分,可以携带一些额外的信息。注解主要是给编译器看或JVM看的,用于完成某些功能。
注解的作用
1、给程序带入一些参数
2、编译检查
3、给框架使用,作为配置文件
常见注解
1、@author:用来标识作者名,eclipse开发工具默认的是系统用户名。
2、 @version:用于标识对象的版本号,适用范围:文件、类、方法。
3、 @Override :用来修饰方法声明,告诉编译器该方法是重写父类中的方法,如果父类不存在该方
法,则编译失败。
4、@Test:用于单元测试。
自定义注解
public @interface 注解名{ }
如:定义一个名为Student的注解 public @interface Student { }
给自定义注解添加属性
public @interface 注解名{
//注解内部只有属性,没有别的
格式1:数据类型 属性名();
格式2:数据类型 属性名() default 默认值;
}
注意:
只能是三大类型(1、八大基本类型 2、String,class,注解类型,枚举类 3、以上12种具体数据类型的数组)
案例实现
@Retention(RetentionPolicy.RUNTIME)//时间周期
public @interface Book {
//包含属性:String value() 书名
//包含属性:double price() 价格,默认值为 100
//包含属性:String[] authors() 多位作者
String value();
double price() default 100;
String[] authors();
}
public class demo {
@Book(value = "西游记", authors = {"你", "我"})
public void show() {
}
}
public class Test {
public static void main(String[] args) throws NoSuchMethodException {
//获得字节码文件
Class<demo> demoClass = demo.class;
Method show = demoClass.getMethod("show");
if (show.isAnnotationPresent(Book.class)) {
System.out.println("有该注解");
Book annotation = show.getAnnotation(Book.class);
String value = annotation.value();
double price = annotation.price();
String[] authors = annotation.authors();
System.out.println("书名:" + value);
System.out.println("价钱:" + price);
System.out.println("作者:" + Arrays.toString(authors));
}
}
}
练习
需求:
- 定义一个Person类,包含私有属性name、age,getXxx和setXxx方法和有参满参构造方法。
- 使用反射的方式创建一个实例、调用构造函数初始化name、age。使用反射方式调用setName方法对姓名进行设置,不使用setAge方法直接使用反射方式对age。
person.java
public class Person {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
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;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
PersonTest.java
public class PersonTest {
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//获取class对象
Class<Person> aClass = Person.class;
Constructor<? extends Person> constructor = aClass.getConstructor(String.class, int.class);
Person person1 = constructor.newInstance(null, 20);
//使用成员方法
Method setName = aClass.getMethod("setName", String.class);
setName.invoke(person1, "张三");
//Method setAge = personClass.getMethod("setAge", int.class);
//setAge.invoke(person1,20);
System.out.println(person1);
}
}
需求:
- 定义一个类,在类中定义一个成员方法 show ,方法功能是:打印一个字符串。
- 使用反射机制创建该类的对象,并调用该对象的 show 方法。
demo01 .java
public class demo01 {
public void print() {
System.out.println("打印字符串");
}
}
Test .java
public class Test {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
//先获取class对象
demo01 dd = new demo01();
Class aClass = demo01.class;
//获取单个成员方法
Method print = aClass.getMethod("print");
//使用成员方法
print.invoke(dd);
}
}