概述
文章目录
- Java反射
- 1.反射的基础原理
- 1.1 Class类
- 2.2 类加载机制
- 2. 反射初识
- 2.1 Class类能搞清楚类的各种信息
- 2.2 用反射构建对象
- 2.3 用反射来修改字段
- 2.4. 使用反射来调用方法
- 3. 反射执行的内部流程
- 总结
Java反射
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
1.反射的基础原理
1.1 Class类
Class类也是类的一种,注意其与class关键字是完全不同的。 Class类是java虚拟机加载类字节码后产生的类的类型信息,这个Class对象保存在同名.class的字节码文件中,在内存中有且只有一个与之对应的Class对象来描述其类型信息。无论创建多少个实例对象,其依据的都是用一个Class对象。 Class对象只能有JVM创建和加载 Class类的对象作用是运行时提供或获得某个对象的类型信息,这点对于反射技术很重要
public final class Class<T> implements java.io.Serializable,
GenericDeclaration,
Type,
AnnotatedElement {
//。。
private static native void registerNatives();
static {
registerNatives();
}
private Class(ClassLoader loader) {
classLoader = loader;
}
@CallerSensitive
public static Class<?> forName(String className)
throws ClassNotFoundException {
Class<?> caller = Reflection.getCallerClass();
return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
}
Class类的实例表示java应用运行时的类(class ans enum)或接口(interface and annotation)(每个java类运行时都在JVM里表现为一个class对象,可通过类名.class、类型.getClass()、Class.forName(“类名”)等方法获取class对象)
public static void main(String[] args) throws ClassNotFoundException {
Class<Main> mainClass = Main.class;
Main m = new Main();
Class aClass = m.getClass();
Class<?> aClass1 = Class.forName("com.reflection.clazz.Main");
System.out.println(mainClass == aClass);
System.out.println(aClass == aClass1);
// System.out.println(m.getClass().getClass());
}
运行结果
true
true
如果使用接口
public static void main(String[] args) throws ClassNotFoundException {
Class F1Class = F1.class;
F1 c = new C1();
Class C1Class = c.getClass();
Class<?> F1Class1 = Class.forName("com.reflection.clazz.F1");
Class<?> C1lass11 = Class.forName("com.reflection.clazz.C1");
System.out.println(F1Class == F1Class1);
System.out.println(C1Class == C1lass11);
System.out.println(F1Class == C1Class);
}
运行结果:
true
true
false
2.2 类加载机制
类全生命周期如下图
类加载流程如下图
从下图看的很清楚,每个字节码文件加载后对应一个Class对象,这个对象存储了类的基本结构,这个读写可以和堆内的普通对象一起完成复杂的操作。
2. 反射初识
2.1 Class类能搞清楚类的各种信息
先定义一个User类
public class User extends AbstractUser implements Person, Serializable {
private int id;
private String name;
public User() {
}
public User(int id, String name) {
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + ''' +
'}';
}
}
这个User类继承了接口和抽象类
public interface Person {
}
public class AbstractUser {
public int age;
}
经过Class的反射,可以看到:
public class Main3 {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
// 获取类名称
System.out.println(User.class.getName());
System.out.println(User.class.getSimpleName());
System.out.println(User.class.getCanonicalName());
//获取接口信息
System.out.println(" is interface :" + User.class.isInterface());
for (Class infs : User.class.getInterfaces()) {
System.out.println(" impl interfaces :" + infs);
}
//父类
System.out.println("Super class : " + User.class.getSuperclass());
//获取属性
for (Field field : User.class.getFields()) {
System.out.println(" all field :" + field.getName());
}
//获取公开属性
for (Field field : User.class.getDeclaredFields()) {
System.out.println(" all DeclaredField :" + field.getName());
}
//获取公开方法
for (Method declaredMethod : User.class.getDeclaredMethods()) {
System.out.println(" all DeclaredMethods :" + declaredMethod.getName());
}
//获取公开方法
for (Constructor constructor : User.class.getDeclaredConstructors()) {
System.out.println(constructor.getName() + " 方法需要的参数如下:");
for (Parameter parameter : constructor.getParameters()) {
System.out.println(constructor.getName() + "t" + parameter.getType() + ":" + parameter.getName());
}
}
}
}
运行结果:
com.reflection.clazz.pojo.User
User
com.reflection.clazz.pojo.User
is interface :false
impl interfaces :interface com.reflection.clazz.pojo.Person
impl interfaces :interface java.io.Serializable
Super class : class com.reflection.clazz.pojo.AbstractUser
all field :age
all DeclaredField :id
all DeclaredField :name
all DeclaredMethods :toString
all DeclaredMethods :getName
all DeclaredMethods :getId
all DeclaredMethods :setName
all DeclaredMethods :setId
com.reflection.clazz.pojo.User 方法需要的参数如下:
com.reflection.clazz.pojo.User 方法需要的参数如下:
com.reflection.clazz.pojo.User int:arg0
com.reflection.clazz.pojo.User class java.lang.String:arg1
2.2 用反射构建对象
可以通过参数类型找出指定的构造方法,生成所要的对象。私有方法也可以更改其权限来创建对象。
public class Main4 {
public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
//使用默认构造方法
Apple apple = Apple.class.newInstance();
System.out.println(apple);
//使用Apple(String color, String area)来构造
Constructor<Apple> constructor = Apple.class.getConstructor(String.class, String.class);
Apple apple1 = constructor.newInstance("红色", "阿克苏");
System.out.println(apple1);
//调用私有构造方法 private Apple(String area)
Constructor<Apple> constructor1 = Apple.class.getDeclaredConstructor(String.class);
constructor1.setAccessible(true);
Apple apple2 = constructor1.newInstance("山东");
System.out.println(apple2);
}
}
运行结果:
Apple{color='null', area='null', weight=0.0, price=0.0}
Apple{color='红色', area='阿克苏', weight=0.0, price=0.0}
Apple{color='null', area='山东', weight=0.0, price=0.0}
2.3 用反射来修改字段
可以看到私有字段的属性值也可以被反射方式修改;不过final字段的修改并不成功。
class Box{
private final String color ="蓝色";
private int len;
private int width;
private int height;
private double weight;
@Override
public String toString() {
return "Box{" +
"color='" + color + ''' +
", len=" + len +
", width=" + width +
", height=" + height +
", weight=" + weight +
'}';
}
}
public class Main5 {
public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchFieldException {
Box box = Box.class.newInstance();
Field field = Box.class.getDeclaredField("width");
Field field2 = Box.class.getDeclaredField("color");
field.setAccessible(true);
field2.setAccessible(true);
field.set(box,20);
field2.set(box,"灰色");
System.out.println(box);
}
}
运行结果
Box{color='蓝色', len=0, width=20, height=0, weight=0.0}
2.4. 使用反射来调用方法
通过反射能够获取类所继承实现、以及私有的各类方法,并且能够进行调用
interface Fly{
void fly();
}
class Animal {
public void live() {
System.out.println(" living ");
}
}
class Bird extends Animal implements Fly{
@Override
public void fly() {
System.out.println("flying");
}
public static void eat(String food){
System.out.println("eating some "+food);
}
private int layeggs(int num) {
System.out.println(" lay "+ num+ " eggs , hope born baby birds");
return new Random().nextInt(num);
}
}
public class Main6 {
public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchFieldException, NoSuchMethodException, InvocationTargetException {
Bird bird = Bird.class.newInstance();
Method[] methods = Bird.class.getDeclaredMethods();
for(Method method:methods){
System.out.println("Bird类有这个方法:"+method.getName()+",其参数个数:"+method.getParameterCount());
}
//调用一个私有方法试试
Method method = Bird.class.getDeclaredMethod("layeggs", int.class);
method.setAccessible(true);
Integer ret = (Integer) method.invoke(bird, 42);
System.out.println(ret);
//调用一个静态方法
Method staticmethod = Bird.class.getDeclaredMethod("eat",String.class);
staticmethod.invoke(null,"谷子");
}
}
运行结果:
Bird类有这个方法:layeggs,其参数个数:1
Bird类有这个方法:eat,其参数个数:1
Bird类有这个方法:fly,其参数个数:0
lay 42 eggs , hope born baby birds
35
eating some 谷子
3. 反射执行的内部流程
反射类及反射方法的获取,都是通过从列表中搜寻查找匹配的方法,所以查找性能会随类的大小方法多少而变化; 每个类都会有一个与之对应的Class实例,从而每个类都可以获取method反射方法,并作用到其他实例身上; 反射也是考虑了线程安全的,放心使用; 反射使用软引用relectionData缓存class信息,避免每次重新从jvm获取带来的开销; 反射调用多次生成新代理Accessor, 而通过字节码生存的则考虑了卸载功能,所以会使用独立的类加载器; 当找到需要的方法,都会copy一份出来,而不是使用原来的实例,从而保证数据隔离; 调度反射方法,最终是由jvm执行invoke0()执行
总结
“反射”机制,它允许我们在运行时发现和使用类的信息。掌握反射是进阶程序员的必经之路。
java基础 系列在github上有一个开源项目,主要是本系列博客的demo代码。https://github.com/forestnlp/javabasic
如果您对软件开发、机器学习、深度学习有兴趣请关注本博客,将持续推出Java、软件架构、深度学习相关专栏。
您的支持是对我最大的鼓励。
最后
以上就是完美画笔为你收集整理的Java反射浅析一二Java反射总结的全部内容,希望文章能够帮你解决Java反射浅析一二Java反射总结所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复