反射
- 反射机制的概述
- ClassLoader的简单概述
- 获取Class类并创建实例的四种方法
- 获取运行时类的完整结构
- 调用运行时类 的指定结构
反射机制的概述
反射(Reflection)是被视为动态语言的关键,那么何为动态呢有动态就有静态,静态是指在编译期就知道要创建哪个对象比如== new Person();== 这是我们手动去创建的,动态呢是指在类被加载在内存中后才知道要创建什么对象可以体现为动态性。
Java中万事万物皆对象,为何这么说呢,我们平时写了许多类这些类可以创建对象。但是一个类中有静态属性静态方法我们不要创建对象就可以通过类名进行调用,那是不是说明类也是对象呢。
我们定义的每个类以及JDK提供好的类每个类都有修饰符 返回值 类名 构造器 属性以及方法等,既然每个类都有那么我们为何不将这些共性抽取出来呢,所以java中提供了一个类Class每个类被加载进内存后JVM会创建唯一一个Class的类对象这个对象包含了这个类的完整信息。
ClassLoader的简单概述
当程序主动使用某个类时,如果该类还未被加载到内存中,则系统会通过如下三个步骤来对该类进行初始化。
在类加载前肯定需要将.java文件编译后生成.class文件。
加载:
将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时的数据结构,然后生成一个代表这个类的Java.lang.class对象。
链接:
将java类的二进制代码合并到JVM的运行状态之中的过程。
- 验证:确保加载的类信息符合JBM规范,比如检查类安不安全。
- 准备:正式未类变量(static)分配内存并设置类变量的默认初始值的阶段(如int 为0 boolean 为false).
- 解析::虚拟机常量池内的符号引用(常量名)替换为直接引用(地址)的过程。
初始化:
- 执行类构造器<clinit()>方法的过程。类构造器()方法是由编译期自动收集类中所有类变量的赋值动作和静态代码块中的语句合并产生的。(类构造器是构造类信息的,不是构造该类对象的构造器)。
public class Reflection {
public static void main(String[] args) {
System.out.println(Person.status);
}
}
class Person{
//先执行 status=01
static {
status=1;
}
//后执行 status=0
public static int status=0;
}
上面的代码执行结果为0,因为在类加载到内存的初始化阶段会对静态变量静态代码块进行真正意义上的赋值。与代码的先后顺序有关,后面的赋值会覆盖前面的数据。
- 当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先触发其父类的初始化与创建类的对象相似会调用父类的构造方法进行初始化。
- 虚拟机会保证一个类的()方法在多线程环境中被正确加锁和同步。
获取Class类并创建实例的四种方法
//方法一:会抛出两个异常
// 1非法进入 比如构造器私有会触发
// 2实例化异常 如没有提供空参的构造方法
Class<Person> pclass = Person.class;
Person person = pclass.newInstance();
System.out.println(person);
//方法二: 这种方法比较鸡肋 有对象再获取类对象再创建对象明显有点多余
Person p2 = new Person();
Class p2Class = p2.getClass();
p2Class.newInstance();
//方法三:ClassNotFoundException 字符串中可能有误找不到该类
//这种方法常用,比如在Spring框架中都是通过配置 class的全类名来创建对象的
Class fClass = Class.forName("com.syz.reflection.Person");
Object o = fClass.newInstance();
//方法四:使用类加载器,首先我们要获取类加载器,可以获取当前类的加载器是谁然后通过
//该加载器加载我们要加载的类
ClassLoader loader = Reflection.class.getClassLoader();
Class<?> loadClass = loader.loadClass("com.syz.reflection.Person");
Object o1 = loadClass.newInstance();
扩展一下类加载器
//1.获取一个系统类加载器
• ClassLoader classloader = ClassLoader.getSystemClassLoader();
• System.out.println(classloader);
• //2.获取系统类加载器的父类加载器,即扩展类加载器
• classloader = classloader.getParent();
• System.out.println(classloader);
• //3.获取扩展类加载器的父类加载器,即引导类加载器
• classloader = classloader.getParent();
• System.out.println(classloader);
• //4.测试当前类由哪个类加载器进行加载
• classloader = Class.forName("exer2.ClassloaderDemo").getClassLoader();
• System.out.println(classloader);
15.3 类的加载与ClassLoader的理解
• //5.测试JDK提供的Object类由哪个类加载器加载
• classloader =
• Class.forName("java.lang.Object").getClassLoader();
• System.out.println(classloader);
• //*6.关于类加载器的一个主要方法:getResourceAsStream(String str):获取类路
径下的指定文件的输入流
• InputStream in = null;
• in = this.getClass().getClassLoader().getResourceAsStream("exer2\\test.properties");
• System.out.println(in);
获取获取运行时类的完整结构
可以获取 Field,Methid,Constructor、Superclass、Interface、Annotation
- 实现的全部接口
interface Car<T>{
public static final int a=1;
void show();
}
class Bus implements Car<String>{
@Override
public void show() {
}
}
//测试方法
@Test
public void TestGetInterface(){
Class<Bus> busClass = Bus.class;
//获取所有实现的接口
Class<?>[] interfaces = busClass.getInterfaces();
for (Class c:interfaces){
System.out.println(c.getName());
}
System.out.println();
//获取所有带泛型的接口
Type[] genericInterfaces = busClass.getGenericInterfaces();
for (Type t:genericInterfaces){
System.out.println(t.getTypeName());
}
}
- 所继承的父类
@Test
public void TestGetFather(){
Class<Bus> busClass = Bus.class;
//获取父类的类对象
Class<? super Bus> superclass = busClass.getSuperclass();
//获取父类类型的类对象
Type genericSuperclass = busClass.getGenericSuperclass();
}
- 全部的构造器
class Train{
public int width;
int height;
private String name;
protected String begin;
private String end;
Train(){
}
private Train(String name){
this.name=name;
}
Train(int width,int height){
this.width=width;
this.height=height;
}
}
@Test
public void getConstructors(){
Class<Train> busClass = Train.class;
//获取本类的public的构造方法,不包括父类
Constructor<?>[] constructors = busClass.getConstructors();
System.out.println();
//获取本类所有声明的构造方法包括私有的方法,不包括父类
Constructor<?>[] declaredConstructors = busClass.getDeclaredConstructors();
}
- 全部的方法
@Test
public void getMethod(){
Class<Train> trainClass = Train.class;
//获取本类及父类的所有公共的方法
Method[] methods = trainClass.getMethods();
//获取本类所有声明的方法 包括私有的方法
Method[] declaredMethods = trainClass.getDeclaredMethods();
}
- 全部的Field
@Test
public void getField(){
Class<Train> trainClass = Train.class;
//获取本类及父类的所有公共的方法
Field[] fields = trainClass.getFields();
//获取本类所有声明的方法 包括私有的方法
Field[] declaredFields = trainClass.getDeclaredFields();
}
调用运行时类 的指定结构
- 调用属性和调用方法一样
Class<Train> trainClass = Train.class;
Train train = trainClass.newInstance();
//此方法可以获取public的方法 需指定方法名和参数列表
Method getLong = trainClass.getMethod("getLong");
//调用时需传入一个对象,因为每个对象的调用方法后产生的效果不同
int longs = (int) getLong.invoke(train);
System.out.println(longs);
//获取其他的方法如private,需声明参数的Class对象
Method sayHallo = trainClass.getDeclaredMethod("SayHallo",String.class);
//如果是私有必须设置
sayHallo.setAccessible(true);
//调用时需传入实际参数
sayHallo.invoke(train,"aaaaa");
setAccessibles说明
Method和Field、Constructor对象都有setAccessible()方法。
- setAccessible启动和禁用访问安全检查的开关。
- 参数值为true则指示反射的对象在使用时应该取消Java语言访问检查。
- 提高反射的效率。如果代码中必须用反射,而该句代码需要频繁的被
调用,那么请设置为true。 - 使得原本无法访问的私有成员也可以访问
- 参数值为false则指示反射的对象应该实施Java语言访问检查。