JavaReflection
Reflection (反射)是Java被视为动态语言的关键,反射机制允许程序在执行期借助于ReflectionAPI取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。
Class<?> aClass = Class.forName("java.lang.String");
加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。
正常方式:引入需要"包类"名称
- 通过new实例化
- 取得实例化的对象
反射方式:实例化的对象
- getClass()方法
- 取得完整的"包类"名称
Java反射机制的优缺点:
优点:可以实现动态的创建和编译,体现出很大的灵活性
缺点:对性能有影响。使用反射基本上是一种解释操作,我们可以告诉JVM,我们希望做什么并且它满足我们的要求。这类操作总是慢于 直接执行相同的操作。
反射相关的主要API:
java.lang.Class:代表一个类
java.lang.reflect.Method:代表类的方法
java.lang.reflect.Field:代表类的成员变量
java.lang.reflect.Constructor:代表类的构造器
........
一个类在内存中只有过一个class对象
一个类被加载后,类的整个结构都会被封装在Class对象中
Class类的创建方式:
//1.实例化对象的getClass()方法
Student student = new Student();
Class C1 = student.getClass();
//2.Class.forName
Class C2 = Class.forName("com.Reflection.Student");
//3.类名.class
Class C3 = Student.class;
//4.基本的内置包装类都有一个type属性
Class C4 = Integer.class;
class类的常用方法:
方法名 | 功能说明 |
static ClassforName(String name) | 返回指定类名name的Class对象 |
Object newInstance() | 调用缺省构造函数,返回Class对象的一个实例 |
getName() | 返回此Class对象所表示的实体 (类,接口,数组 类或void)的名称。 |
Class getSuperClass() | 返回当前Class对象的父类的Class对象 |
Class[] getinterfaces() | 获取当前Class对象的接口 |
ClassLoader getClassLoader() | 返回该类的类加载器 |
Constructorll getConstructors() | 返回一个包含某些Constructor对象的数组 |
Method getMothed(String name,Class.. T) | 返回一个Method对象,此对象的形参类型为 paramType |
Fieldi getDeclaredFields() | 返回Field对象的一个数组 |
哪些类型可以有Class对象?
class:外部类成员(成员内部类,静态内部类),局部内部类,匿名内部类
interface: 接口
[]:数组
enum: 枚举
annotation: 注解@interface
primitive type: 基本数据类型
void
//所有类型的Class对象
public class Test03 {
public static void main(String[] args) {
Class c1 = Object.class; //类
Class c2 = Comparable.class; //接口
Class c3 = String[].class; //一维数组
Class c4 = int[][].class; //二维数组
Class c5 = Override.class; //注解
Class c6 = ElementType.class; //枚举
Class c7 = Integer.class; //基本数据类型
Class c8 = void.class; //void
Class c9 = Class.class; //Class
System.out.println(c1);
System.out.println(c2);
System.out.println(c3);
System.out.println(c4);
System.out.println(c5);
System.out.println(c6);
System.out.println(c7);
System.out.println(c8);
System.out.println(c9);
int[] a = new int[10];
int[] b = new int[100];
//只要是类型相同的数据 它的Class对象也就相同
System.out.println(a.getClass().hashCode());
System.out.println(b.getClass().hashCode());
}
}
类的加载和ClassLoader的理解
加载:将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构然后生成一个代表这个类的java.lang.Class对象.
链接:将Java类的二进制代码合并到JVM的运行状态之中的过程。
正式为类变量 (static)分配内存并设置类变量默认初始值的阶段,这些内存都将在方法区中进行分配
初始化:执行类构造器<clinit>()方法的过程。
什么时候发生类的初始化
类的主动引用(一定会发生)
当虚拟机启动,先初始化main方法所在的类
new一个类的对象
调用类的静态成员(除了final常量)和静态方法
使用java.lang.reflect包的方法对类进行反射调用
* 当初始化一个类,如果其父类没有被初始化,则先会初始化它的父类
类的被动引用(一定不会发生)
当访问一个静态域时,只有真正声明这个域的类才会被初始化。如:当通过子类引用父类的静态变量,不会导致子类初始化
通过数组定义类引用,不会触发此类的初始化
引用常量不会触发此类的初始化(常量在链接阶段就存)入调用类的常量池中力
package com.Reflection;
//什么时候会发生类的初始化
public class Test04 {
static {
System.out.println("Test04 类被加载");
}
public static void main(String[] args) throws ClassNotFoundException {
//主动引用
// 1.new 一个实例对象
//Son s = new Son();
// 2.reflection
// Class.forName("com.Reflection.Son");
//被动引用
//1.当通过子类引用父类的静态变量,不会导致子类初始化
// System.out.println(Son.MF);
//2.数组定义的引用
Son[] sons = new Son[10];
//3.常量不会引起初始化
System.out.println(Son.MS);
}
}
class Father {
static final int MF = 1000;
static {
System.out.println("father类被加载");
}
}
class Son extends Father{
static final int MS = 100;
static {
System.out.println("Son类被加载");
}
}
类加载器
类加载的作用: 将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后在堆中生成一个代表这个类的java.lang.Class对象,
作为方法区中类数据的访问入口。
类缓存: 标准的JavaSE类加载器可以按要求查找类,但一旦某个类被加载到类加载器中,它将维持加载(缓存)一段时间。不过JVM垃圾回收机制可以回收这些Class对象
类加载器的作用:用来把类装进内存的
package com.Reflection;
//类加载器
public class Test05 {
public static void main(String[] args) throws ClassNotFoundException {
//获取系统类的加载器
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
System.out.println(systemClassLoader);
//获取系统类的加载的父类 -- 扩展类加载器
ClassLoader systemClassLoaderParent = systemClassLoader.getParent();
System.out.println(systemClassLoaderParent);
//扩展类加载器的父类 -- 根加载器
ClassLoader systemClassLoaderParentParent = systemClassLoaderParent.getParent();
System.out.println(systemClassLoaderParentParent);
//获取当前类是由哪个加载器加载的 -- 系统加载器
ClassLoader classLoader = Class.forName("com.Reflection.Test05").getClassLoader();
System.out.println(classLoader);
//测试内置类是由哪个加载器加载的 -- 根加载器
ClassLoader classLoader1 = Class.forName("java.lang.String").getClassLoader();
System.out.println(classLoader1);
//如何获得系统类加载器可以加载的路径
String property = System.getProperty("java.class.path");
System.out.println(property);
//双亲委派机制
/*
* 假如你编写的类的包名及类名和系统的内置类相同,在类加载的时候就会寻找有没有相同的类,如果存在相同的类那么系统就会加载系统的内置类
* 基于安全的
* */
}
}
运行时类的完整结构
public class Test06 {
public static void main(String[] args) {
//获取Student的Class的对象
Class aClass = new Student().getClass();
//获取包名和类名
System.out.println(aClass.getName()); // 包名+类名
System.out.println(aClass.getSimpleName()); // 类名
System.out.println("=================================");
//获取类的属性
Field[] fields = aClass.getFields(); //父类及其子类的public属性
for (Field field : fields) {
System.out.println(field);
}
fields = aClass.getDeclaredFields(); //本类全部属性
for (Field field : fields) {
System.out.println(field);
}
System.out.println("=================================");
//获取方法
Method[] methods = aClass.getMethods(); //获取本类及其父类的public方法
for (Method method : methods) {
System.out.println(method);
}
System.out.println("----------------------------");
methods = aClass.getDeclaredMethods(); //获取全部的方法
for (Method method : methods) {
System.out.println(method);
}
System.out.println("=================================");
//获取构造器
Constructor[] constructors = aClass.getConstructors(); //获取public
for (Constructor constructor : constructors) {
System.out.println(constructor);
}
constructors = aClass.getDeclaredConstructors(); //获取全部
for (Constructor constructor : constructors) {
System.out.println(constructor);
}
System.out.println("=================================");
}
}
创建类的对象:
(1)调用Class对象的newInstance()方法(实质上调用无参构造)
Class C1 = Class.forName("com.Reflection.User");
//构造一个对象
User user = (User) C1.newInstance();
System.out.println(user);
(2)通过构造器来使用有参构造创建方法
//通过指定构造器来进行有参构造
Constructor constructor = C1.getDeclaredConstructor(String.class, int.class, String.class, String.class);
User instance = (User) constructor.newInstance("zuo", 23, "0001", "aasdas");
System.out.println(instance);
(3)通过反射调用普通方法
//通过反射调用普通方法
User user = (User) C1.newInstance();
Method setName = C1.getDeclaredMethod("setName", String.class);
//invoke : 激活的意思
setName.invoke(user,"mmmmmm");
System.out.println(user.getName());
(4)通过反射操作属性
//通过反射操作属性
User user4 = (User)c1.newInstance();
Field name = cl.getDeclaredField( name:"name");
//不能直接操作私有属性,我们需要关闭程序的安全监测,属性或者方法的setAccessible(true)
name.setAccessible(true);
name.set(user4,"狂神2");
System.out.println(user4.getName());
setAccessible
Method和Field、Constructor对象都有setAccessible()方法。
setAccessible作用是启动和禁用访问安全检查的开关。
参数值为true则指示反射的对象在使用时应该取消Java语言访问检查
提高反射的效率。如果代码中必须用反射,而该句代码需要频繁的被调用,那么请设置为true。
使得原本无法访问的私有成员也可以访问
参数值为false则指示反射的对象应该实施Java语言访问检查
标签:反射,System,class,println,机制,加载,Class,out From: https://www.cnblogs.com/zuok/p/17062065.html