@[反射最全内容,持续更新,通俗易懂,史上最强]
反射的存在原因
当你要利用配置文件(Properties)给定的信息对相关类进行操作时,你会你怎么做?
假设给定的配置文件内容为:
classfullpath=com.hspedu.refleciton.Cat
method=cry
让你执行他的方法
我们的思路是不是首先要读取配置文件
Properties properties=new Properties();
properties.load(new FileInputStream("src/re.properties"));
//获取需要操作的类名
String classfullpath = properties.get("classfullpath").toString();
//获取需要操作的方法名字
String method = properties.get("method").toString();
但是这样你只是读取到了一个字符串,没法创建对象,总不能
new classfullpath() 把,这很明显是错误的。
所以在这个需求上,你就需要利用反射的知识进行解决。
反射基础知识
Java程序在计算机中有三个阶段
第一:编译阶段
即通过 javac编译 形成字节码文件
第二:加载阶段
通过类加载器(ClassLoader)体现反射,形成Class类对象,存放于方法区里,Class类对象里面包括 成员变量 Field[] fields、成员方法 Method [] ms、构造器 Contractor [] cons。
类的加载阶段又细分为三个过程:
①加载(Loading):他将.class文件读入内存,并位置创建一个java.lang.Class对象(类对象),这个过程由类加载器完成
②连接(Linking):连接由分为三个小部分
a:验证(verification):主要是安全校验
b:准备(preparation):对静态变量进行默认初始化,注意是默认初始化,一般基本数据类型赋值为0,引用数据类型赋值为null
c:解析(Resolution):Java虚拟机将常量池中的符号引用替换成直接引用。(思考一下,在类还未加载时,还没有地址,所以常量的地址是相对的,解析完成后,有了地址,常量的位置就是绝对的了),还有就是将.class文件的二进制数据合并到JRE中。
③初始化(Initialization):将静态代码块和静态变量写入内存
第三阶段:运行阶段
当我们new 了一个对象后,我们可以通过这个对象知道他是属于哪个Class对象,得到Class对象后,我们就可以利用这个Class对象操作这个类的方法、属性等等。
动态加载和静态加载的区别
这里验证不要用集成编译环境,因为太智能了,他会一下找到错误,想要切身体会,可以用记事本写下面的代码,用cmd运行一下:
静态加载:
import java.util.Scanner;
public class classload {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
int flag=sc.nextInt();
switch (flag){
case 1:
Person p=new Person();
break;
case 2:
System.out.println("尚硅谷");
break;
}
}
}
上述代码你在用cmd运行时会发现,他连编译都通过不了,因为我们没有Person这个实体类,也就是说,在编译时,他就会加载相关的类,如果没有就会报错,依赖性太强。
动态加载:
import java.util.Scanner;
public class classload {
public static void main(String[] args) throws Exception{
Scanner sc=new Scanner(System.in);
int flag=sc.nextInt();
switch (flag){
case 1:
Class<?> clazz = Class.forName("com.hspedu.reflection.Person");
Object object = clazz.newInstance();
break;
case 2:
System.out.println("韩顺平");
break;
}
}
}
上述代码在用cmd运行时,你会发现他可以通过编译,虽然这个java文件里也没有写Person类,只有选1时,进行加载才会报错,这就是反射的动态加载机制。
得到Class对象的六种方式
//1.Class.forName
Class clazz1=Class.forName("com.hspedu.Car");//()中的内容一般通过读取配置文件内容获取
System.out.println(clazz1);
//2.类名.class 应用场景:参数传递
Class clazz2=Car.class;
System.out.println(clazz2);//前面利用构造器中传参
//3.对象.getClass(),应用场景,有对象实例
Car car = new Car();
Class<? extends Car> clazz3 = car.getClass();
System.out.println(clazz3);
//4.通过类的加载器来获取Class对象
//(1)先得到类加载器
ClassLoader classLoader = car.getClass().getClassLoader();
//(2)通过类加载器得到Class对象
Class<?> clazz4 = classLoader.loadClass("com.hspedu.Car");
System.out.println(clazz4);
上述四种为最重要的
//5.基本数据类型(int,double......)
Class<Integer> integerClass = int.class;
Class<Double> doubleClass = double.class;
//6.包装类(Integer、Double.....)
Class<Integer> type = Integer.TYPE;
Class<Double> type1 = Double.TYPE;
//这里给大家抛出一个问题
System.out.println(tpye==integerclass)//true or false
反射机制创建实例
Class.forName(),()中写你准备加载的类的相对路径。
比如说你在com.hspedu.refleciton包下写了Cat,则()中写com.hspedu.refleciton.Cat
Class clazz=Class.forName(com.hspedu.refleciton.Cat);
clazz就是我们获取的Class类型的对象,简称类对象
Object obj=clazz.newInstance();
clazz通过newInstance()方法获取类com.hspedu.refleciton.Cat的对象实例,需要注意的是,这里我们只能利用空参构造器进行初始化。
- 构造器有关内容
当我们通过public有参构造获取类对象实例时
public User(String name){
this.name=name;
}
Class<?> clazz = Class.forName("com.hspedu.refleciton.classload.User");
Constructor<?> constructor = clazz.getConstructor(String.class);
Object object1 = constructor.newInstance("尚硅谷");
当我们通过非public有参构造获取类对象实例时
private User(int age, String name) {
this.age = age;
this.name = name;
}
Constructor<?> declaredConstructor = clazz.getDeclaredConstructor(int.class, String.class);//getdeclaredConstructor得到类中所有构造器实例
declaredConstructor.setAccessible(true);//暴力破解,使反射可以访问private的构造器
User object2 = (User) declaredConstructor.newInstance(24, "lsp");
构造器实例有两个常用方法:
1.getModifiers:
以int形式返回修饰符
默认修饰符:0 public:1 private:2 protected:4
static :8 final: 16
如果是 public static 则为 9
2.getReturnType()是获得返回值类型对应的Class对象
- 属性(字段)有关内容
Class<?> clazz = Class.forName("com.hspedu.refleciton.classload.Student");
Object object = clazz.newInstance();//o 的运行类型就是Student
System.out.println(object.getClass());//Student
Field age = clazz.getField("age");
age.set(object,12);//修改字段内容
Field name = clazz.getDeclaredField("name");
name.setAccessible(true);//这里name是私有的且被static修饰
name.set(null,"李白");//因为name是static修饰的,所以可以用null也可以用object
System.out.println(object);
System.out.println(name.get(object));//获取字段值
-
方法有关内容
clazz通过getMethod()方法获取类com.hspedu.refleciton.Cat的方法实例(这里体现了Java的万事万物皆对象),我们把Cat中的方法,看成一个方法对象。
然后我们通过 方法对象 去调用方法
method1.invoke(obj);//这里就体现出反射和传统方法的区别了
如果method1方法对象所对应的方法有参数,则写成
method1,invoke(obj,实参,实参…)
传统方法是:对象名.方法();
反射机制是:方法对象.invoke(对象名,实参,实参…);
Class<?> clazz = Class.forName("com.hspedu.refleciton.classload.Boss");
Object object = clazz.newInstance();
Method hi = clazz.getMethod("hi", String.class);//获取方法名为hi,参数类型为String的方法
hi.invoke(object, "啥大椿");//给object类中的hi方法的参数赋值为 啥大椿
Method say = clazz.getDeclaredMethod("say", int.class, String.class, char.class);
say.setAccessible(true);
//对于反射来讲,如果方法有返回值,统一返回Objec,但是其运行是类型为say方法的返回值类型
Object invoke = say.invoke(null, 11, "夏洛", '男');//null可以替换成 Object
System.out.println(invoke);
System.out.println(invoke.getClass());//String
//方法实例有两个特有的方法:
// 1.getReturnType()是获得返回值类型对应的Class对象
// 2.getParameterTypes()是获得参数数组,每一个参数的类型对应的Class对象
反射常用的Utils
Class<?> clazz1 = Class.forName("com.hspedu.refleciton.classload.Person");
//getName() :获取全类名
String name = clazz1.getName();
//getSimpleName :获取简单类名
String simpleName = clazz1.getSimpleName();
//获取所有public 修饰的属性,包含本类以及父类
Field[] fields = clazz1.getFields();
//获取本类的所有属性
Field[] declaredFields = clazz1.getDeclaredFields();
//获取本来及父类的所有方法---->父类包括 父类的父类
Method[] methods = clazz1.getMethods();
//获取本类中的所有方法
Method[] declaredMethods = clazz1.getDeclaredMethods();
//获取包含public修饰的构造器,不包括父类的
Constructor<?>[] constructors = clazz1.getConstructors();
//获取本类所有的构造器
Constructor<?>[] declaredConstructors = clazz1.getDeclaredConstructors();
//返回包信息
Package aPackage = clazz1.getPackage();
//返回父类的全路径
Class<?> superclass = clazz1.getSuperclass();
//返回接口信息
Class<?>[] interfaces = superclass.getInterfaces();
//返回注解信息
Annotation[] annotations = clazz1.getAnnotations();
反射优化
通过反射调用方法,比传统方法耗时多的多,可以关掉反射检查来减少一部分时间
public class Reflection02 {
public static void main(String[] args) throws Exception {
m1();//4
m2();//1610
m3();//772
}
//传统方法类调用方法所用时间
public static void m1(){
Cat cat=new Cat();
long start=System.currentTimeMillis();
for (int i = 0; i < 900000000; i++) {
cat.hi();
}
long end=System.currentTimeMillis();
System.out.println(end-start);
}
//利用反射机制调用方法所用时间
public static void m2() throws Exception {
Class clazz=Class.forName("com.hspedu.refleciton.Cat");
Object object = clazz.newInstance();
Method hi = clazz.getMethod("hi");
long start=System.currentTimeMillis();
for (int i = 0; i < 900000000; i++) {
hi.invoke(object);
}
long end=System.currentTimeMillis();
System.out.println(end-start);
}
//反射优化后所用时间
public static void m3() throws Exception {
Class clazz=Class.forName("com.hspedu.refleciton.Cat");
Object object = clazz.newInstance();
Method hi = clazz.getMethod("hi");
hi.setAccessible(true);//在反射调用是,关闭访问检查!
long start=System.currentTimeMillis();
for (int i = 0; i < 900000000; i++) {
hi.invoke(object);
}
long end=System.currentTimeMillis();
System.out.println(end-start);
}
}
练习1:
通过反射修改私有成员变量 com.hspedu.homework Homework01.java
1.定义PrivateTest类,有私有name属性并且属性值为hellokitty
2.提供getName的公有方法
3.创建PrivateTest的类,利用Class类得到私有的name属性,修改私有的name属性值,并调用getName()的方法打印name属性值
class PrivateTest{
private String name="helloKitty";
public String getName(){
return name;
}
}
练习2:
利用反射和File完成以下功能 Homework02.java
1.利用Class类的forName方法得到File类的class 对象
2.在控制台打印File类的所有构造器
3.通过newInstance的方法创建File对象,并创建E:\mynew.txt文件
提示:创建文件的正常写法如下:
File file = new File(“d:\aa.txt”);
file.createNewFile();
练习1代码:
这里只是其中的一种方式:
import java.lang.reflect.Field;
public class Homework01 {
public static void main(String[] args) throws Exception {
Class<?> clazz = Class.forName("com.hspedu.refleciton.homework.PrivateTest");
Object object = clazz.newInstance();
Field name = clazz.getDeclaredField("name");
name.setAccessible(true);
name.set(object,"GoSiLin");
Object object1 = name.get(object);
System.out.println(object1);
}
}
class PrivateTest{
private String name="helloKitty";
public String getName(){
return name;
}
}
练习2代码:
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
public class Homework02 {
public static void main(String[] args) throws Exception{
Class<?> fileClazz = Class.forName("java.io.File");
Constructor<?>[] declaredConstructors = fileClazz.getDeclaredConstructors();
for(Constructor<?> d:declaredConstructors){
System.out.println(d);
}
Constructor<?> declaredConstructor = fileClazz.getDeclaredConstructor(String.class);
String filePath="f:\\aa.txt";
Object file= declaredConstructor.newInstance(filePath);
Method createNewFile = fileClazz.getMethod("createNewFile");
createNewFile.invoke(file);
}
}