1.反射的介绍: 反射是被视为动态语言的关键,反射机制允许程序在执行期间借助Reflection ApI取得任何类相关的内部信息,并能直接任意操作对象的内部属性及方法
* 类加载完之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类 * 只有一个Class对象),这个对象包含了完整的类的结构信息。我们可以通过 * 这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构 * 所以我们形象的称之为:反射
* 正常方式:引入需要的包类名称-->通过new实例化-->取得实例化对象 * 反射方式:实例化对象-->getClass()方法-->得到完整的包类名名称
* 动态语言:运行时代码可以根据某些自身条件改变自身结构 * 主要动态语言:PHP python C# javaScript * 静态语言:运行时结构不不可变的语言就是静态语言 * 主要静态语言:java c c++ * * java不是动态语言,但java可以称为”准动态语言“。即java有一定的动态性, * 我们可以利用反射机制,字节码操作获得类似动态语言的特性。java的动态性让编程的时候更加灵活 * 关于java.lang.Class类的理解: * 1.类的加载过程: * 程序经过java.exe命令以后,会生成一个或多个字节码文件(.class拓展名文件)。 * 接着我们使用java.exe命令对某个字节码文件进行解释运行。相当于将某个字节码文件加载到内存中 * 此过程就叫做类的加载。加载到内存中的类,我们称为运行时类,此运行时类,就作为Class的一个实例 * 2.Class的实例就对应着一个运行时类 * 3.加载到内存中的运行时类,会缓存一定的时间。在此时间之内,我们可以通过不同的方式来获取此运行时类。
2.使用反射和没有使用反射的对比:
@Test
public void test(){
//反射之前,对Person的操作
//1.创建Person对象
Person p=new Person("cht",23);
//2.通过对象,调用其内部的属性方法
p.age=10;
System.out.println(p.toString());
p.show();
//在person类外部,不可以通过Person类对象调用其内部私有结构
//比如:name、showNation()以及私有构造器
}
//反射之后,对person的操作
@Test
public void test2() throws Exception{
Class<Person> personClass = Person.class;
//通过反射,创建Person类的对象
Constructor<Person> constructor = personClass.getConstructor(String.class, int.class);
Person hyj = constructor.newInstance("hyj", 22);
System.out.println(personClass.toString());
//2.通过反射,调用对象指定的属性,方法
//调用属性
Field a = personClass.getDeclaredField("age");
a.set(hyj,18);
System.out.println(hyj.toString());
//调用方法
Method shows = personClass.getDeclaredMethod("show");
shows.invoke(hyj);
System.out.println("***********************");
//通过反射,可以调用Person类中的私有结构,比如私有属性,方法,构造器
//调用私有构造器
Constructor<Person> declaredConstructor = personClass.getDeclaredConstructor(String.class);
declaredConstructor.setAccessible(true);
Person jerry = declaredConstructor.newInstance("jerry");
System.out.println(jerry);
//调用私有的属性
Field name = personClass.getDeclaredField("name");
name.setAccessible(true);
name.set(hyj,"漂亮妹妹");
System.out.println(hyj);
//调用私有方法
Method showNation = personClass.getDeclaredMethod("showNation", String.class);
showNation.setAccessible(true);
showNation.invoke(hyj,"中国");//相当于hyj.showNation("中国")
}
3.获取Class的实例的4种方式:
1.知道具体类的情况下可以使用:
2.通过对象实例instance.getClass()
获取:
3.通过 Class.forName()
传入类的路径获取通过 Class.forName()
传入类的路径获取
4.通过类加载器xxxClassLoader.loadClass()
传入类路径获取:
//获取Class的实例的方式
@Test
public void test3() throws ClassNotFoundException {
//方式一:调用运行时类的属性.class
Class<Person> pc = Person.class;
System.out.println(pc);
//方式二:通过运行时类的对象,调用getClass()
Person p1=new Person();
Class pc1=p1.getClass();
System.out.println(pc1);
//方式三:调用Class的静态方法:forName(String classPath)
Class pc2 = Class.forName("reflection.Person");
System.out.println(pc2);
System.out.println(pc==pc1);
System.out.println(pc==pc2);
//方式四:使用类加载器:ClassLoader
ClassLoader classLoader = ReflectionTest.class.getClassLoader();
Class<?> pc3 = classLoader.loadClass("reflection.Person");
System.out.println(pc3);
System.out.println(pc==pc3);
}
Person类:
/**
* @Author:阿土伯
* @Date: 2022/2/15 22:54
*/
public class Person {
private String name;
public int age;
public Person(){
System.out.println("此时调用的是运行时类的空参构造器 且该空参构造器的权限必须为public");
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
private Person(String name) {
this.name=name;
}
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 +
'}';
}
private String showNation(String nation){
System.out.println("我的国籍是"+nation);
return nation;
}
public void show(){
System.out.println("我是一个闽南人");
}
}
补充:只要数组的元素类型与维度一样,就是同一个Class
@Test
public void test4(){
Class c1=Object.class;
Class c2=Comparable.class;
Class c3=String[].class;
Class c4=int[][].class;
Class c5= ElementType.class;
Class c6=Override.class;
Class c7=int.class;
Class c8=void.class;
Class c9=Class.class;
int[] a=new int[10];
int[] b=new int[100];
Class c10=a.getClass();
Class c11=b.getClass();
//只要数组的元素类型与维度一样,就是同一个Class
System.out.println(c10==c11);
}
4.newInstance的使用
@Test
public void test1() throws InstantiationException, IllegalAccessException {
Class<Person> personClass=Person.class;
/*
newInstance():调用此方法,创建对应的运行时类,内部调用了运行时类的空参构造器
想要此方法正常的创建运行时类的对象,要求:
1.运行时类必须提供空参构造器
2.空参的构造器访问权限得够,通常设置为public
在javabean中要求提供一个public得空参构造器得原因:
1.便于通过反射,创建运行时类得对象
2.便于子类继承此运行时类时,默认调用super()时,保证父类有此构造器
*/
Person obj=personClass.newInstance();
System.out.println(obj);
}
5.ClassLoader的使用
@Test
public void test1() throws Exception{
Properties pros=new Properties();
//此时的文件默认在当前的module下
//读取配置文件的方式一:
// FileInputStream fis=new FileInputStream("jdbc.properties");
// FileInputStream fis=new FileInputStream("src\\jdbc1.properties");
// pros.load(fis);
//方式二:使用ClassLoader类加载器
//配置文件默认识别为:当前module的src下
ClassLoader classLoader=ClassLoaderTest.class.getClassLoader();
InputStream is=classLoader.getResourceAsStream("jdbc1.properties");
pros.load(is);
String user=pros.getProperty("name");
String password=pros.getProperty("password");
System.out.println("user="+user+",password="+password);
}
6.反射的应用场景
6.1框架中使用的动态代理依赖反射实现的
6.2当编译的时候还不确定实例化的对象时使用反射来动态获取
//体现反射的动态性 框架的时候大量使用反射
//编译的时候不确定要造哪一个类对象 后面通过反射来获取所要造的类对象
@Test
public void test2(){
for(int i=0;i<100;i++){
int num=new Random().nextInt(3);//0,1,2
String classPath="";
switch (num){
case 0:
classPath="java.util.Date";
break;
case 1:
classPath="java.lang.Object";
break;
case 2:
classPath="reflection.Person";
break;
}
try {
Object obj=getInstance(classPath);
System.out.println(obj);
} catch (Exception e) {
e.printStackTrace();
}
}
}
7.反射的优缺点:
优点 : 可以让咱们的代码更加灵活、为各种框架提供开箱即用的功能提供了便利
缺点 :让我们在运行时有了分析操作类的能力,这同样也增加了安全问题。比如可以无视泛型参数的安全检查(泛型参数的安全检查发生在编译时)。