java的反射机制与通过反射创建对象的四种方式
导读
java的反射在框架中运用十分广泛,最常用的有java的动态代理、注解==,所以掌握java的反射机制是十分必要的
目的:
1. 理解Class类并实例化Class对象
2. 运行时创建类对象并获取类的完整结构
3. 通过反射调用类的指定方法、指定属性
一、要想更好地理解反射,就要先知道java类的加载过程
每个类都会产生一个对应的Class对象,也就是保存在.class文件。所有类都是在对其第一次使用时,动态加载到JVM的,当程序创建一个对类的静态成员的引用时,就会加载这个类。Class对象仅在需要的时候才会加载,static修饰的属性和代码块初始化是在类加载时进行的。
1.类加载的三个步骤
加载:由类加载器完成,找到对应的字节码,创建一个Class对象
链接:验证类中的字节码,为静态域分配空间
初始化:如果该类有超类,则对其初始化,执行静态初始化器和静态初始化块
类的加载器小tpis:
类加载器的一个重要运用:获取配置文件里的属性值
/**
* 类加载器:ClassLoader
*/
@Test
public void classLoaderTest() throws ClassNotFoundException, IOException {
//1.引导类加载器
ClassLoader loader1 = ClassLoader.getSystemClassLoader();
System.out.println(loader1);//sun.misc.Launcher$AppClassLoader@18b4aac2
ClassLoader loader2 = loader1.getParent();
System.out.println(loader2);//sun.misc.Launcher$ExtClassLoader@6433a2
ClassLoader loader3 = loader2.getParent();
System.out.println(loader3);//null
//2.扩展类加载器(自定义类加载器)
Class clazz = Student.class;
ClassLoader loader4 = clazz.getClassLoader();
System.out.println(loader4);//sun.misc.Launcher$AppClassLoader@18b4aac2
//3.系统类加载器
String className = "java.lang.String";
Class clazz1 = Class.forName(className);
ClassLoader loader5 = clazz1.getClassLoader();
System.out.println(loader5);//null
//类加载器的一个重要运用如下:
ClassLoader loaderImportant = this.getClass().getClassLoader();
InputStream is = loaderImportant.getResourceAsStream("templates/jdbc.properties");
Properties pro = new Properties();
pro.load(is);
System.out.println(pro.getProperty("username"));//chengergou
System.out.println(pro.getProperty("password"));//chengergou123
}
Class小tpis:
- Class本身是一个类
- Class对象只能由系统创建对象
- 一个类在JVM中只会有一个Class实例
- 一个Calss对象对应的是一个加载在JVM中.class文件
- 每个类的实例都会记得由哪个Class实例生成
- 通过Class能够得到一个类中的完整结构
理解:与对象的抽取与封装进行对比
Car这个类是对所有汽车的静态特征进行描述与抽取,那么不管是宝马、大众还是其它汽车,只
要是Car都拥有了name这个属性。Car的行为是运行,那么不管什么Car,都有run()这个方法
class Car {
private String name;
public void run(){
System.out.println("我的功能是run"):
}
这就是对对象的共同特征与行为进行抽取从而封装成了类。
同理,对所有的class对象进行向上抽取与封装,那么他们都有(Filed)、Constructor(构造器)、Method(方法),你可以看做是这样的类:
class Class{
Filed field;
Constructor constructor;
Method method;
。。。
}
具体参考Class本类的结构:
二、什么是反射(Java Reflection)
Reflection(反射)是动态语言的关键,反射机制允许程序在执行期间借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性和方法。
三、反射的功能
- 在运行时判断任意一个对象所属的类
- 在运行时构造任意一个类的对象
- 在运行时判断任意一个类所具有的成员变量和方法
- 在运行时调用任意一个对象的成员变量和方法
- 生成动态代理
四、有了反射我们能做什么???
通常,我们创建对象的时候都是new出来,但现在我们是反过来知道类的信息,从而创建出对象;类的信息我们可以通过获取.class文件(字节码文件)来得到,包括类的属性、方法、构造器==
4.1 在没有反射前,是先创建类的对象,从而调用类的方法和属性
/**
* 在没有反射前,是先创建类的对象,从而调用类的方法和属性
*/
@Test
public void testNoRefleTest() {
//对象的属性我们都设置了public修饰的getter和setter,所以都能
// 调用和赋值
Student s=new Student();
s.setAge(18);
s.setName("程二狗");
s.setGender("男");
System.out.println(s);//Student{age=18, name='程二狗', gender='男'}
//只能调用public权限修饰的方法(我这儿是不同包下的)
String name=s.getPublicMessagePublicHaveParam("陈雪峰");
System.out.println(name);//陈雪峰, hello!
s.showMygenderPublicNoParam();//程二狗的性别为男
}
访问权限控制小tpis:
注意:本文中的代码都是写在不同包下的(也是根据实际情况开发而写),你可以依据以下权限的进行相应访问权限匹配,代码中注释”只能“都是针对不同包下来说的
Java 中一共有四种访问权限控制,其权限控制的大小情况是这样的:public > protected > default(包访问权限) > private
访问权限 本类 本包的类 子类 非子类的外包类
public 是 是 是 是
protected 是 是 是 否
default 是 是 否 否
private 是 否 否 否
4.2 通过反射能调用类的完整结构
Filed、Method、Constructor、Superclass、Interface、Annotation
- 实现的全部接口
- 所继承的父类
- 全部的构造器
- 全部的方法
4.2.1 为了方便测试,我们先创建出必要的类:
image.png
自定义注解类
package com.ergou.springboot.entity;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.*;
@Retention(RetentionPolicy.RUNTIME)
@Target({TYPE,METHOD,FIELD,PARAMETER,CONSTRUCTOR})
public @interface MyAnnotation {
String value();
}
注解小tpis:
注解的生命周期:@Retention元注解有三种取值:RetetionPolicy.SOURCE、RetetionPolicy.CLASS、RetetionPolicy.RUNTIME,分别对应:java源文件<class文件<内存中的字节码。
接口
package com.ergou.springboot.entity;
import java.io.Serializable;
public interface MyInterface extends Serializable {
}
Person类
package com.ergou.springboot.entity;
public class Person<T> {
public String job;
private String address;
public void breathing(){
System.out.println("是人都要呼吸!");
}
public Person() {
}
public String getJob() {
return job;
}
public void setJob(String job) {
this.job = job;
}
}
学生类
package com.ergou.springboot.entity;
@MyAnnotation(value = "我是类的注解")
public class Student extends Person<String> implements Comparable,MyInterface {
//私有属性private 修饰
private Integer age;
//默认属性defualt修饰
String name;
//公有属性
public String gender;
//无参构造器
//tips:创建类时,默认有一个无参构造器
public Student() {
super();
//验证谁调用了我
System.out.println("我是Student的无参构造,我被调用了。。。");
}
//有参构造器
public Student(Integer age, String name, String gender) {
this.age = age;
this.name = name;
this.gender = gender;
}
//私有方法,无参数
@MyAnnotation(value = "我是private方法的注解")
private void seeMyAgePrivateNoParam(){
System.out.println("我是程二狗,我今年18岁了");
}
//私有方法,有参数
private String getMyFriendPrivateHaveParam(Integer age,String name) throws ArrayIndexOutOfBoundsException{
return "我的朋友叫"+name+",他今年"+age+"岁了";
}
//公有方法,有参数
@MyAnnotation(value = "我是public方法的注解")
public String getPublicMessagePublicHaveParam(String name){
return name+", hello!";
}
//公有方法,无参数
public void showMygenderPublicNoParam() throws Exception{
System.out.println("程二狗的性别为男");
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
//重写方法
@Override
public String toString() {
return "Student{" +
"age=" + age +
", name='" + name + '\'' +
", gender='" + gender + '\'' +
'}';
}
@Override
public int compareTo(Object o) {
return 0;
}
}
测试代码
/**
* 有了反射后,可以通过Class去创建对象(字节码.class)
* java.lang.Class:是反射的源头
* 通过创建创建类,编译(java.exe)生成对应的.class文件,之后我们用java.exe
* (JVM类加载器完成的)此.class文件,此.class文件加载到内存以后,就是一个运行时类
* 存放在缓存区,那么这个运行时类本身就是一个Class的实例!
* 注意:每一个运行时类只加载一次
* 有了Clas类的实例以后,我们才可以进行如下操作:
* 1)创建对应类的运行时类
* 2)获取对应运行时类的完整结构(属性、方法、构造器、内部类、父类、所在的包、异常、注解。。。)
* 3)调用对应的运行时类的指定结构(属性、方法、构造)
*/
@Test
public void testHaveRefleTest() throws Exception {
//=====================1.获取Class的实例(字节码文件)四种方式================================================
//1.1 通过运行时类的对象,调用其getClass()方法,返回运行时类。
Student stuObject = new Student();
Class stuForObject = stuObject.getClass();
System.out.println(stuForObject);//class com.ergou.springboot.entity.Student
//1.2获取Class类型的对象
Class clazzforClass = Student.class;
System.out.println(clazzforClass);//class com.ergou.springboot.entity.Student
//1.3 通过Class的静态方法获取(在jdbc、框架阶段,这个方法用的最多)
String className = "com.ergou.springboot.entity.Student";
Class clazzForStaticMethod = Class.forName(className);
System.out.println(clazzForStaticMethod);//class com.ergou.springboot.entity.Student
//1.4 通过类的加载器获取
ClassLoader classLoader = this.getClass().getClassLoader();
//this 代表当前类的实例,也就是com.ergou.spirngboot.ExampleApplicationTests@1c33c17b
System.out.println(this);
Class clazzForClassLoader = classLoader.loadClass(className);
System.out.println(clazzForClassLoader);//class com.ergou.springboot.entity.Student
//=====================2.获取共有单个属性、共有多个属性、私有单个属性、全部属性=======================================
//2.通过无参构造器创建对象,怎么证明调用的是无参构造?我在无参构造器中添加一个输出
//调用无参构造方法的必要条件:1.类中必须要有空参构造方法 2.构造器的权限修饰符权限要足够
Student stu = (Student) clazzForStaticMethod.newInstance();//我是Student的无参构造,我被调用了。。。
System.out.println(stu);// Student{age=null, name='null', gender='null'}
//2.1 通过有参构造器创建对象 public修饰的getConstructor(),参数是属性类型的字节码文件, private修饰的用
// getDeclaredConstructor(),并开启权限 havePrivateParamStudent.setAccessible(true);参数是属性类型的字节码文件
Constructor haveParamStudent = clazzForStaticMethod.getConstructor(Integer.class,String.class,String.class);
Student student = (Student)haveParamStudent.newInstance(18,"程二狗","男");
System.out.println("我是有参public构造器构造的对象:"+student);
//我是public有参构造器构造的对象:Student{age=18, name='程二狗', gender='男'}
//把构造器的属性public 变成private 开启下面的代码
// Constructor havePrivateParamStudent = clazzForStaticMethod.getDeclaredConstructor(Integer.class,String.class,String.class);
// havePrivateParamStudent.setAccessible(true);
// Student student = (Student) havePrivateParamStudent.newInstance(18,"程二狗","男");
// System.out.println("我是有参private构造器构造的对象:"+student);
//我是有参private构造器构造的对象:Student{age=18, name='程二狗', gender='男'}
//======================3.通过反射调用运行是类的指定对象========================================================
//3.1 getField只能获取public修饰的属性,无需开启修改权限(单个属性)
Field publicFileld = clazzForStaticMethod.getField("gender");
//3.1.2 第一个参数:要赋值的对象,第二参数:“gender”这个属性需要赋什么值
publicFileld.set(stu, "男");
System.out.println(stu);//Student{age=null, name='null', gender='男'}
//3.1.3 getFields只能获取public修饰的属性(多个属性)
Field[] publicFilelds = clazzForStaticMethod.getFields();
for (Field fileld : publicFilelds) {
System.out.println(fileld);//tips:能够获取父类和子类的public修饰的属性,输出如下:
//public java.lang.String com.ergou.springboot.entity.Student.gender
//public java.lang.String com.ergou.springboot.entity.Person.job
}
//3.2 getDeclaredField 暴力反射,任何权限修饰的都可以获取
Field privateFileld = clazzForStaticMethod.getDeclaredField("age");
//3.2.1 必须开启可修改权限,才能赋值成功
privateFileld.setAccessible(true);
//3.2.2 第一个参数:要赋值的对象,第二参数:“age”这个属性需要赋什么值
privateFileld.set(stu, 20);
//3.2.2 getFields只能获取public修饰的属性(多个属性)
Field[] allFilelds = clazzForStaticMethod.getDeclaredFields();
for (Field allFileld : allFilelds) {//tips::能够获取父类和子类所有申明的属性,输出如下:
System.out.println(allFileld);
//public java.lang.String com.ergou.springboot.entity.Student.gender
//public java.lang.String com.ergou.springboot.entity.Person.job
//private java.lang.Integer com.ergou.springboot.entity.Student.age
//java.lang.String com.ergou.springboot.entity.Student.name
// public java.lang.String com.ergou.springboot.entity.Student.gender
}
for (Field allFileld : allFilelds) {
System.out.println(allFileld.getName());//age name gender 只能获取子类的属性名称,
//不能获取父类的属性名称
}
//=============== 4.通过反射调用运行时类的指定方法===============================================================
//4.1 公有方法,无参数
Method publicNoPramaMethod = clazzForStaticMethod.getMethod("showMygenderPublicNoParam");
publicNoPramaMethod.invoke(stu);//程二狗的性别为男
//4.2 公有方法,有参数
Method publicHavePramaMethod = clazzForStaticMethod.getMethod("getPublicMessagePublicHaveParam", String.class);
String mess = (String) publicHavePramaMethod.invoke(stu, "陈雪峰");
System.out.println(mess);//陈雪峰, hello!
//4.3 私有方法,无参数
Method privateNoPramaMethod = clazzForStaticMethod.getDeclaredMethod("seeMyAgePrivateNoParam");
privateNoPramaMethod.setAccessible(true);
privateNoPramaMethod.invoke(stu);//我是程二狗,我今年18岁了
publicNoPramaMethod.invoke(stu);//程二狗的性别为男
//4.4 私有方法,有参数
Method privateHavePramaMethod = clazzForStaticMethod.getDeclaredMethod("getMyFriendPrivateHaveParam",Integer.class, String.class);
privateHavePramaMethod.setAccessible(true);
String mess1 = (String) privateHavePramaMethod.invoke(stu, 21,"陈雪峰");
System.out.println(mess1);//我的朋友叫陈雪峰,他今年21岁了
}
4.2.2 获取注解、返回值类型、方法名、形参名、异常
/**
* 获取注解、返回值类型、方法名、形参名、异常
*/
@Test
public void reflectionTestOther(){
Class clazz = Student.class;
Method[] methods1 = clazz.getDeclaredMethods();
for (Method method : methods1) {
//1.获取注解
Annotation[] anono=method.getAnnotations();
for (Annotation annotation : anono) {
System.out.println(annotation);
//@com.ergou.springboot.entity.MyAnnotation(value=我是public方法的注解)
//@com.ergou.springboot.entity.MyAnnotation(value=我是private方法的注解)
}
//2.返回值类型
Class returnType = method.getReturnType();
System.out.println(returnType.getName()+"");
//java.lang.String
//int
//java.lang.String
//void
//java.lang.String
//void
//void
//void
//void
//java.lang.String
//java.lang.Integer
//java.lang.String
//3.获取方法名
System.out.println(method.getName());
//toString
//compareTo
//getName
//setName
//showMygenderPublicNoParam
//getPublicMessagePublicHaveParam
//setAge
//setGender
//seeMyAgePrivateNoParam
//getMyFriendPrivateHaveParam
//getGender
//getAge
//4.获取形参名
Class[] paramters=method.getParameterTypes();
for (int i = 0;i < paramters.length;++i) {
System.out.println(paramters[i]+"args_"+i);
//class java.lang.Objectargs_0
//class java.lang.Stringargs_0
//class java.lang.Integerargs_0
//class java.lang.Stringargs_0
//class java.lang.Stringargs_0
//class java.lang.Integerargs_0
//class java.lang.Stringargs_1
}
//5.获取异常
Class[] exps=method.getExceptionTypes();
for (Class exp : exps) {
System.out.println(exp);
//class java.lang.Exception
//class java.lang.ArrayIndexOutOfBoundsException
}
}
}
小结:
===================================以上是重点===========================================
扩展1:获取属性的各部分内容:1.权限修饰符 2.变量类型 3.变量名
/**
* 获取属性的各部分内容
* 1.权限修饰符 2.变量类型 3.变量名
*/
@Test
public void reflectionTestFiled() throws Exception {
Class clazz = Student.class;
Field[] field = clazz.getDeclaredFields();
for (Field f : field) {
//1.获取每个权限的修饰符
int i = f.getModifiers();
System.out.println(i);
System.out.println(Modifier.toString(i));
//2.获取属性类型
System.out.println(f.getType());
//3.获取变量名
System.out.println(f.getType().getName());
//输出如下
//----------第一次循环---------
//2
//private
//class java.lang.Integer
//java.lang.Integer
//----------第二次循环---------
//0
// (空格代表default)
// class java.lang.String
//java.lang.String
//--------第三次循环-----------
//1
// public
//class java.lang.String
//java.lang.String
}
}
扩展2:获取运行时类的方法
/**
* 获取运行时类的方法
*/
@Test
public void reflectionTestMethod(){
Class clazz = Student.class;
//***:getMethods() 获取运行时类及其父类中声明为public的方法
Method[] methods = clazz.getMethods();
for (Method method : methods) {
System.out.println(method);
}
System.out.println("============我是分割线==========");
//public java.lang.String com.ergou.springboot.entity.Student.toString()
//public int com.ergou.springboot.entity.Student.compareTo(java.lang.Object)
//public java.lang.String com.ergou.springboot.entity.Student.getName()
//public void com.ergou.springboot.entity.Student.setName(java.lang.String)
//public java.lang.String com.ergou.springboot.entity.Student.getPublicMessagePublicHaveParam(java.lang.String)
//public void com.ergou.springboot.entity.Student.showMygenderPublicNoParam()
//public void com.ergou.springboot.entity.Student.setGender(java.lang.String)
//public void com.ergou.springboot.entity.Student.setAge(java.lang.Integer)
//public java.lang.String com.ergou.springboot.entity.Student.getGender()
//public java.lang.Integer com.ergou.springboot.entity.Student.getAge()
//public void com.ergou.springboot.entity.Person.setJob(java.lang.String)
//public void com.ergou.springboot.entity.Person.breathing()
//public java.lang.String com.ergou.springboot.entity.Person.getJob()
//public final void java.lang.Object.wait() throws java.lang.InterruptedException
//public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
//public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
//public boolean java.lang.Object.equals(java.lang.Object)
//public native int java.lang.Object.hashCode()
//public final native java.lang.Class java.lang.Object.getClass()
//public final native void java.lang.Object.notify()
//public final native void java.lang.Object.notifyAll()
//***:getDeclaredMethods()获取运行时类本生声明的所有方法,不能获取父类的
Method[] methods1 = clazz.getDeclaredMethods();
for (Method method : methods1) {
System.out.println(method);
}
//public java.lang.String com.ergou.springboot.entity.Student.toString()
//public int com.ergou.springboot.entity.Student.compareTo(java.lang.Object)
//public java.lang.String com.ergou.springboot.entity.Student.getName()
//public void com.ergou.springboot.entity.Student.setName(java.lang.String)
//public java.lang.String com.ergou.springboot.entity.Student.getPublicMessagePublicHaveParam(java.lang.String)
//public void com.ergou.springboot.entity.Student.showMygenderPublicNoParam()
//public void com.ergou.springboot.entity.Student.setGender(java.lang.String)
//public void com.ergou.springboot.entity.Student.setAge(java.lang.Integer)
//private void com.ergou.springboot.entity.Student.seeMyAgePrivateNoParam()
//java.lang.String com.ergou.springboot.entity.Student.getMyFriendPrivateHaveParam(java.lang.Integer,java.lang.String)
//public java.lang.String com.ergou.springboot.entity.Student.getGender()
//public java.lang.Integer com.ergou.springboot.entity.Student.getAge()
}
扩展3:获取所有的构造器
/**
* 获取所有的构造器
*/
@Test
public void getAllConstructorTest(){
Class clazz=Student.class;
//clazz.getDeclaredConstructors() 只能获取本类的所有构造器
Constructor[] constructors = clazz.getDeclaredConstructors();
for (Constructor constructor : constructors) {
System.out.println(constructor);
//public com.ergou.springboot.entity.Student()
//private com.ergou.springboot.entity.Student(java.lang.Integer,java.lang.String,java.lang.String)
}
}
扩展4:获取运行时的父类、获取父类的泛型、获取实现的借口
/**
* 获取运行时的父类、获取父类的泛型、获取实现的借口
*/
@Test
public void getOthersTest() {
//获取运行时的父类
Class clazz=Student.class;
Type type = clazz.getGenericSuperclass();
System.out.println(type);//com.ergou.springboot.entity.Person<java.lang.String>
//获取父类的泛型
ParameterizedType param = (ParameterizedType)type;
Type[] types = param.getActualTypeArguments();
//先转型成一个Class类型,在调用其方法
System.out.println(((Class)types[0]).getName());// //java.lang.String
//获取实现的接口
Class[] interfaces = clazz.getInterfaces();
for (Class anInterface : interfaces) {
System.out.println(anInterface);
//interface java.lang.Comparable
//interface com.ergou.springboot.entity.MyInterface
}
//获取所在的包
Package pack = clazz.getPackage();
System.out.println(pack);//package com.ergou.springboot.entity
//获取注解(类上的注解)
Annotation[] annos = clazz.getAnnotations();
for (Annotation anno : annos) {
System.out.println(anno);//@com.ergou.springboot.entity.MyAnnotation(value=我是类的注解)
}
}
通过这一节的学习,想必你对java的反射机制有了更好的理解,下一节我会为大家带来Java反射的重要运用之一:动态代理
代码百度网盘:链接:https://pan.baidu.com/s/1vrMqoqufUXqs4NQh_XRPCw
提取码:dw93