java基础-java反射
java里的class文件加载分为两种情况,一种就是类型是编译器已知的,这种文件的.class文件在编译的时候,编译器会把.class文件打开检查,但是注意不是加载哦,第二种就是我们可能是从别的地方获取到了一个引用,然后动态的把这个未知类型的引用的对象的.class文件加载进jvm虚拟机里。前者为RTTI,即Run- Time Type Identification 运行时类型识别;后者为“反射”。
要想理解反射的原理,首先要了解什么是类型信息。Java让我们在运行时识别对象和类的信息,主要有2种方式:一种是传统的RTTI,它假定我们在编译时已经知道了所有的类型信息;另一种是反射机制,它允许我们在运行时发现和使用类的信息。
Class加载
理解RTTI在Java中的工作原理,首先需要知道类型信息在运行时是如何表示的,这是由Class对象来完成的,它包含了与类有关的信息。Class对象就是用来创建所有“常规”对象的,Java使用Class对象来执行RTTI,即使你正在执行的是类似类型转换这样的操作。
每个类都会产生一个对应的Class对象,也就是保存在.class文件。所有类都是在对其第一次使用时,动态加载到JVM的,当程序创建一个对类的静态成员的引用时,就会加载这个类。Class对象仅在需要的时候才会加载,static初始化是在类加载时进行的。
Class类是一个非常重要的java基础类,每当装载一个新的类型的时候,java虚拟机都会在java堆中创建一个对应于新类型的Class实例,该实例就代表此类型,通过该Class实例我们就可以访问该类型的基本信息。方法区中会存储某个被装载类的类型信息,可以通过Class实例来访问这些信息。
Class加载过程请参看《jvm-类加载》
反射(Reflection)
什么是反射?
定义:主要是指程序可以访问、检测和修改它本身状态或行为的一种能力。
反射本身并不是一个新概念,它可能会使我们联想到光学中的反射概念,尽管计算机科学赋予了反射概念新的含义,但是,从现象上来说,它们确实有某些相通之处,这些有助于我们的理解。在计算机科学领域,反射是指一类应用,它们能够自描述和自控制。也就是说,这类应用通过采用某种机制来实现对自己行为的描述(self-representation)和监测(examination),并能根据自身行为的状态和结果,调整或修改应用所描述行为的状态和相关的语义。
JAVA反射机制
JAVA反射机制:“程序运行时,允许改变程序结构或变量类型,这种语言称为动态语言”。从这个观点看,Perl,Python,Ruby是动态语言,C++,Java,C#不是动态语言。但是JAVA有着一个非常突出的动态相关机制:Reflection,用在Java身上指的是我们可以于运行时加载、探知、使用编译期间完全未知的classes。换句话说,Java程序可以加载一个运行时才得知名称的class,获悉其完整构造(但不包括methods定义),并生成其对象实体、或对其fields设值、或唤起其methods。
Java 反射机制主要提供了以下功能:
- 在运行时构造任意一个类的对象。
- 在运行时判断任意一个类所具有的成员变量和方法。
- 在运行时调用任意一个对象的方法。
- 在运行时判断任意一个对象所属的类。
这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。
反射机制的优点与缺点
为什么要用反射机制?直接创建对象不就可以了吗,这就涉及到了动态与静态的概念,
静态编译:在编译时确定类型,绑定对象,即通过。
动态编译:运行时确定类型,绑定对象。动态编译最大限度发挥了java的灵活性,体现了多态的应用,有以降低类之间的藕合性。
Reflectio API
Reflection是Java被视为动态(或准动态)语言的一个关键性质。
这个机制允许程序在运行时透过Reflection APIs取得任何一个已知名称的class的内部信息。
包括其modifiers(诸如public、static等)、 superclass(例如Object)、实现了的 interfaces (例如Serializable)、也包括其fields和methods的所有信息,并可于运行时改变fields内容或调用methods。
Java Reflection API简介
在JDK中,主要由以下类来实现Java反射机制,这些类(除了第一个)都位于java.lang.reflect包中
- Class类:代表一个类,位于java.lang包下。(这个不属于反射类哦,下面4个才是反射)
- Field类:代表类的成员变量(成员变量也称为类的属性)。
- Method类:代表类的方法。
- Constructor类:代表类的构造方法。
- Array类:提供了动态创建数组,以及访问数组的元素的静态方法。
Class类
在java.lang.Object类中定义了getClass()方法,因此对于任意一个Java对象,都可以通过此方法获得对象的类型。
Class类是Reflection API 中的核心类,它有以下方法
- getName():这个类型的全限定名 。
- getSuperClass():这个类型的直接超类的全限定名
- isInterface();这个类型是类类型还是接口类型
- getTypeParamters();这个类型的访问修饰符
- getFields():获得类的public类型的属性。
- getDeclaredFields():获得类的所有属性。
- getMethods():获得类的public类型的方法。
- getDeclaredMethods():获得类的所有方法(包括私有的)。
- getMethod(String name, Class[] parameterTypes):获得类的特定方法,name参数指定方法的名字,parameterTypes参数指定方法的参数类型。
- getConstructors():获得类的public类型的构造方法。
- getConstructor(Class[] parameterTypes):获得类的特定构造方法,parameterTypes参数指定构造方法的参数类型。
- newInstance():通过类的不带参数的构造方法创建这个类的一个对象。
T.class与t.getClass的区别
在java中,无论是类(Class),枚举(enum),接口(interface),数组(array),注解(annotation),内置数据类型(Primitive type),void类型都包含一个特殊的实例对象,就是java.lang.Class类实例。
java.lang.Class类没有public的构造函数,java.lang.Class类实例只能有JVM自动产生,每次JVM加载Class的时候,JVM就会为其自动生成一个java.lang.Class类实例。
就是‘类型’.class
在java中,可以有两个方法获取类的Class实例。
1. Object类有个getClass()方法,可以获取类的java.lang.Class实例。
2. 每个类有个public的静态变量class。即使是int,long,void这些内置类型也是有class静态变量的,但数组是个比较特殊的,只能通过getClass()方法获取Class变量。如果对数组变量进行.class操作,则会有Unknown Class编译错误。
A a = new A();
if(a.getClass()==A.class) {
System.out.println("equal");
} else {
System.out.println("unequal");
}
输出结果:equal
一个是根据对象获取getClass,一个是根据类型获取的class。
在反射中我们并不知道类型,只能根据对象来getClass,如果知道一个类的类型则可以使用‘类’.class获取。
Field类
获得对象的所有属性:
- Field fields[]=classType.getDeclaredFields();
- Class 类的getDeclaredFields()方法返回类的所有属性,包括public、protected、默认和private访问级别的属性
Method类
Method类的invoke(Object obj,Object args[])方法接收的参数必须为对象,如果参数为基本类型数据,必须转换为相应的包装类型的对象。invoke()方法的返回值总是对象,如果实际被调用的方法的返回类型是基本类型数据,那么invoke()方法会把它转换为相应的包装类型的对象,再将其返回
生成对象
通过默认构造方法创建一个新对象:
- Object objectCopy = classType.getConstructor(new Class[]{}).newInstance(new Object[]{});
- 以上代码先调用Class类的getConstructor()方法获得一个Constructor 对象,它代表默认的构造方法,然后调用Constructor对象的newInstance()方法构造一个实例。
获取某个类或某个对象所对应的Class对象的常用的3种方式:
- 使用Class类的静态方法forName:Class.forName(“java.lang.String”);
- 使用类的.class语法:String.class; 知道类型的情况下,使用“类”.class
- 使用对象的getClass()方法:String s = “aa”; Class
反射实例
测试类:
package com.test.spring.reflect;
public class PersonDome {
private Integer id;
private String name;
public Integer age;
public static final String NAME_ADDRESS = "beijing";
public static String getStatic;
private static String getPriStatic;
public void getAge() {
System.out.println("getAge:80.....");
}
public static void getAddress() {
System.out.println("static void getAddress bejing.....");
}
public static String s = getString();
private static String getString() {
System.out.println("给静态变量赋值的静态方法执行:loading");
return "ss";
}
static{
getAddress();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
}
主方法:
private static void classFromLoad() throws ClassNotFoundException, InstantiationException, IllegalAccessException {
Class c = Class.forName("com.test.spring.reflect.PersonDome");
if (c == null) {
System.out.println("c is null");
return;
}
/**
*java中创建对象,最常见的方式就是使用new关键字创建。但new并不是唯一的方式。
java.lang.Class有个newInstance方法,可以生成新的对象。但使用newInstance方法生成对象,必须有默认构造函数,或者public,protected的无参构造函数。
如果类只有private构造函数,则用newInstance时,会抛出IllegalAccessException异常;
如果类只有有参构造函数,则会抛出InstantiationException异常。
*/
Object o = c.newInstance();
try {
//返回o的构造方法
System.out.println("---返回o的构造方法---");
Constructor constructor = o.getClass().getConstructor();
System.out.println(constructor.getDeclaringClass());
//返回A类的所有为public 声明的构造方法
System.out.println("------返回A类的所有为public 声明的构造方法-----");
Constructor[] cons = o.getClass().getConstructors();
System.out.println(Arrays.asList(cons));
//返回A类所有的构造方法,包括private
System.out.println("-----返回A类所有的构造方法,包括private--------");
Constructor[] cons2 = o.getClass().getDeclaredConstructors();
System.out.println(Arrays.asList(cons2));
//返回类的getAge 方法
System.out.println("------返回类的getAge 方法-----invoke");
Method m = o.getClass().getMethod("getAge");
//执行
m.invoke(o.getClass().newInstance(), null);
//返回A类所有的public 方法
System.out.println("---------返回A类所有的public 方法-------");
Method[] ms = o.getClass().getMethods();
System.out.println(Arrays.asList(ms));
//返回A类所有的方法,包括private
System.out.println("-------返回A类所有的方法,包括private--------");
Method[] allMs = o.getClass().getDeclaredMethods();
System.out.println(Arrays.asList(allMs));
//返回A类的public字段
System.out.println("------返回A类的public字段---------");
Field field = o.getClass().getField("age");
System.out.println(field + "," + field.get(o.getClass().newInstance()));
Field[] fields = o.getClass().getDeclaredFields();
System.out.println(Arrays.asList(fields));
} catch (Exception e) {
e.printStackTrace();
}
}
解说:假设com.test.spring.reflect.PersonDome这个类,并没有在我们的项目中,而是在数据库中,或者是我们网络请求过来的,然后我们根据他来生成类的字节码文件class。
这个时候我们拿到了class字节码文件,但是我们并不知道这个class里面有些什么,接下来使用c.newInstance()生成一个对象,根据生成的对象获取这个类的所有信息。这也就是反射的原由了。
反射的第一种理解:
重温一下类的加载过程:在《jvm-类加载》文章中已经写类的加载过程了。
程序启动时:
加载-》验证-》准备-》解析-》初始化-》使用-》卸载。
网上看到的是:
RTTI和反射之间的真正区别只在于:
RTTI,编译器在编译时打开和检查.class文件
反射,运行时打开和检查.class文件
但是现在好了,程序已经启动了,在网络中的某个类,我们还没有开始进行类加载呢,当我们在程序运行时,这个时候才开始进行类加载-》… 调用了反射包里面的api,检查这个类的信息,这个是不是就反过来了。
本来是加载-》使用-》运行;现在是运行-》加载-》使用。
类的加载使用过程反过来了,先使用后加载,这个只是从字面意思上说明好想是反过来了,我们叫它反射,暂时可以这么理解,但是要知道,反射机制的作用是”可以访问、检测和修改它本身状态或行为的一种能力“。
反射的第二种理解:
先通过forName获取到类的字节码,然后通过Object o = c.newInstance()创建一个对象出来。
通过o这个对象,我们可以获取到类的所有信息,获取这些信息后你可以做任何修改,或者监控。
对象来自于类的实例化,现在通过对象反过来可以获取类的所有信息,就好像鸡生了蛋,蛋又孵化出了鸡一样。这个过程称为反射。恩,这个我感觉比较理解更准确一点,符合反射的定义与反射的机制。
Array实例
private static void testArray(){
String[] aa = {"aa","bb"};
Class c1 = aa.getClass();
System.out.println("是否是数值:"+c1.isArray());
System.out.println("数组组件类型的 Class:"+c1.getComponentType());
//初始化数值
String[] arr = (String[]) Array.newInstance(c1.getComponentType(), 2);
//数组设值
Array.set(arr, 0, "111");
Array.set(arr, 1, "222");
//访问
System.out.println(arr[0]);
System.out.println(Array.get(arr, 1));
}
class.forName与ClassLoad的区别
比较对比
1.使用class.forName加载
private static void testClassFrom() {
try {
Class<?> c = Class.forName("com.test.spring.reflect.PersonDome");
System.out.println("forName=" + c.getName());
System.out.println("----------start-forName----newInstance");
c.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
}
给静态变量赋值的静态方法执行:loading
static void getAddress bejing.....
forName=com.test.spring.reflect.PersonDome
----------start-forName----newInstance
解说:com.test.spring.reflect.PersonDome类请参看反射里面的例子。
使用Class.forName时,在加载类的时候会执行类的static区域。
可以使用 Class.forName(“com.test.spring.reflect.PersonDome”, false, Thread.currentThread().getContextClassLoader());
通过第二个参数设置为false,则可以控制类static区域不执行。
从源码中可以看出,Class.forName是通过ClassLoad来实现的,所以严格来说他们没啥区别,只不过Class.forName封装了一下ClassLoad,做了一些自己的事情而已。
注意:**java中创建对象,最常见的方式就是使用new关键字创建。但new并不是唯一的方式。
java.lang.Class有个newInstance方法,可以生成新的对象。但使用newInstance方法生成对象,必须有默认构造函数,或者public,protected的无参构造函数。如果类只有private构造函数,则用newInstance时,会抛出IllegalAccessException异常;如果类只有有参构造函数,则会抛出InstantiationException异常。**
2.使用ClassLoad加载
private static void testCLassLoad() {
ClassLoader loader = ClassLoader.getSystemClassLoader();
try {
Class<?> c = loader.loadClass("com.test.spring.reflect.PersonDome");
System.out.println("loadClass=" + c.getName());
System.out.println("----------start-loadClass----newInstance");
c.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
}
loadClass=com.test.spring.reflect.PersonDome
----------start-loadClass----newInstance
给静态变量赋值的静态方法执行:loading
static void getAddress bejing.....
可以看到,使用ClassLoader加载类的时候,并不会执行类的static区域,而是在实例化时才会执行static区域。
源码分析
1.Class.forName源码
public static Class<?> forName(String className)
throws ClassNotFoundException {
Class<?> caller = Reflection.getCallerClass();
return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
}
private static native Class<?> forName0(String name, boolean initialize,
ClassLoader loader,
Class<?> caller)
throws ClassNotFoundException;
可以看到forName最后还是调用的ClassLoad去加载类,只不过forName0的第二个参数为true,就是加载类的时候执行static区域。
2.ClassLoad源码
public Class<?> loadClass(String name) throws ClassNotFoundException {
return loadClass(name, false);
}
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// First, check if the class has already been loaded
Class<?> c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
long t1 = System.nanoTime();
c = findClass(name);
// this is the defining class loader; record the stats
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
protected final void resolveClass(Class<?> c) {
resolveClass0(c);
}
当第二个参数为true时会执行resolveClass方法。
resolveClass可以不完全地(不带解析)装入类,也可以完全地(带解析)装入类。当编写我们自己的loadClass时,可以调用resolveClass,这取决于loadClass的resolve参数的值.
注意:我在测试的过程中犯的错
public static void main(String args[]){
testClassFrom();
System.out.println();
System.out.println("--------load------");
testCLassLoad();
}
testClassFrom;testCLassLoad一起执行会发现,PersonDome这个类的静态方法区只会有一个能打印出来.
因为:他们两个都是使用同一个类加载,而静态方法,变量属于类变量,类方法,只会被加载一次。不管new多少个对象,他们都是一个块公共区。
http://blog.csdn.net/xyang81/article/details/7292380ClassLoad加载原理。
http://blog.csdn.net/qq_27093465/article/details/52262340参考
http://blog.csdn.net/shuanghujushi/article/details/51635990参考,还行
加载数据库驱动
Class.forNmae(“com.mysql.jdbc.Driver”);
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
public Driver() throws SQLException {
}
static {
try {
DriverManager.registerDriver(new Driver());
} catch (SQLException var1) {
throw new RuntimeException("Can\'t register driver!");
}
}
}
可以看出,使用Class.forName时,就会执行了static区域,然后就实例化了Driver了。
参考文档
https://baike.baidu.com/item/JAVA%E5%8F%8D%E5%B0%84%E6%9C%BA%E5%88%B6/6015990?fr=aladdin
http://blog.csdn.net/BuddyUU/article/details/52458241
http://blog.csdn.net/qianzhiyong111/article/details/7321097
https://zhidao.baidu.com/question/87612466.html反射是什么(参看下面的其他答案,最佳答案是瞎扯)
http://blog.csdn.net/zhu_tianwei/article/details/43269441反射原理
http://china-jianchen.iteye.com/blog/728774
http://www.cnblogs.com/luoxn28/p/5686794.html