Java高级特性——反射机制(第一篇)

——何为动态语言,何为静态语言?(学习反射知识前,需要了解动态语言和静态语言)

动态语言

>是一类在运行时可以改变其结构的语言,例如新的函数、对象、甚至是代码可以被引进,已有的函数可以被删除或者是其他结构上的改变,通俗的说就是代码在运行时可以根据某些自身条件改变自身的结构。

>主要的动态语言有:Object-C、C#、JavaScript、Python等。

动态语言JavaScript举例:

1 function f(){
2 
3 //此时的x为一个字符串类型
4 var x="var a=1;var b=2;alert(a+b)";
5 
6 //通过eval函数,执行x语句,此时的x值为运算后的值
7 eval(x);
8 }

通过上面代码可以了解到,本身的x在没有运行的时候是一个字符串类型,通过eval函数运行后,改变其本身原有的结构,就变成了一个整型。所以JavaScript语言拥有动态性,能够在运行时改变其本身原有的结构。

静态语言

>与动态语言相对应,运行时结构不可该变的语言。如Java、C++、C等。

>尽管Java不属于动态语言,但是可以称之为“准动态语言”,即Java具有一定的动态性,我们可以通过Java的反射机制获得类似动态语言的特性。Java的动态性让编程更加的灵活。

了解了什么是静态语言,什么是动态语言,下面开始了解Java的反射机制。

——什么是反射(Reflection)?

>reflection(反射)是Java被视为动态语言的关键。反射机制允许在执行期间借助Reflection API获取任何类的内部信息,并且能操作任意对象的内部属性及其方法。

Tip:类的内部信息:类名、方法、字段、属性、构造器等。

Tip:反射的强大之处:用private修饰的方法也能够通过反射获取到。

>加载完类后,在堆内存的方法区中就产生一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的这个类的信息,我们可以通过这个类看到类的结构的信息。这个对象就像一面镜子,透过这个镜子我们可以看到类的结构,所以我们形象的称之为“ 反射(Reflection)”。

比如获取一个Class对象,通过这个对象,我们可以看到这个类所有的结构信息,这就是反射(下面是获取Class对象一种方法,先了解):

Class<?> c = Class.forName("java.lang.String");

 正常方式和反射方式的流程图:

 

 

反射机制提供的功能:

>在运行时判断任何一个对象所属的类

>在运行时构造任意一个类的对象

>在运行时判断任意一个类所具有的成员变量和方法

>在运行时获取泛型信息

>在运行时调运任何一个类的方法以及属性等

>在运行时处理注解

>生成动态代理

>>>等等

反射的优缺点:

优点:可以实现动态创建对象和编译,体现出很大的灵活性

缺点:对性能有影响,使用放射基本上属于一种解释操作,我们可以告诉JVM,做什么并且它要满足我们什么要求,这类操作总是慢于直接执行相同的操作。

 ——如何获取反射对象?

获取反射对象,最主要的是了解反射主要的API:

>java.lang.Class:代表一个类

>java.lang.reflect.Method:代表类的方法

>java.lang.reflect.Filed:代表类的成员变量

>java.lang.reflect.Constructer:代表类的构造器

>>>等等

通过反射获取类的Class对象,例如:

package test;

public class Test{   
    //什么叫反射
    public static void main(String[] args) throws ClassNotFoundException {
        //通过反射获取类的class对象
        Class c = Class.forName("test.Test");
        
        System.out.println(c);
    }
    
    
}

 

打印的结果为:class test.Test 

我们前边说一个类在内存中只对应一个Class对象,下面案例测试(只修改上一个案例中的main方法):

 1     //什么叫反射
 2     public static void main(String[] args) throws ClassNotFoundException {
 3         //通过反射获取类的Class对象
 4         Class<?> c = Class.forName("test.Test");    
 5         Class<?> c1 = Class.forName("test.Test");
 6         Class<?> c2 = Class.forName("test.Test");
 7         
 8         //一个类在内存中只能有一个Class对象
 9         //一个类被加载后,类的整个结构都会被封装在Class对象中
10         
11         System.out.println(c.hashCode() == c1.hashCode());
12         System.out.println(c.hashCode() == c2.hashCode());
13         System.out.println(c1.hashCode() == c2.hashCode());
14 
15     }

 

打印结果为:true true  true ,可以看到获取到的Class对象均为堆内存方法区中的同一个Class对象。

Class类详解:

在Object类中定义了public final Class getClass()方法,此方法被所有的子类继承。这个方法的返回值是一个Class类型,此类(Class类)是Java反射的源头,实际上所谓的反射从程序的运行结果来看也很好理解, 即:可以通过对象反射求出类的名称。

>Class类本身也是一个类

>Class对象由系统建立对象

>一个加载的类在内存中只有一个Class对象

>一个Class对象对应的是一个加载到JVM的.class文件

>每个类的实例都会记得自己由那个Class实例生成

>通过Class可以完整的得到一个类中所有被加载的结构

>Class类是Reflection(反射)的根源,针对任何你想动态加载和运行的类,唯有先获得相应的Class对象

class对象常用的方法:

 

 

 如何获取Class实例:

> 若已知具体的类,通过类的class属性获取,该方法最为安全可靠,程序的性能最高,例如:Class c =  Test.class;

>若知某个类的实例,调用该实例的getClass()方法获取Class对象 例如:Class c = new Test().getClass();

>已知一个类的全类名,且该类在路径下, 可以通过Class的静态方法forName获取,可能抛出ClassNotFoundException

例如:Class c = Class.forName("test.Test");

>内置的基本数据类型可以直接用类名.Type  例如:String.Type

>还可以通过ClassLoader (后边介绍)

案例(修改上述案例的main方法):

 

   //什么叫反射
    public static void main(String[] args) throws ClassNotFoundException {
        
        //通过Class的forName方法获取
        Class c1 = Class.forName("test.Test");
        
        //通过类名.Class获取
        Class c2 = Test.class;
        
        //通过对象.getClass获取
        Class c3 = new Test().getClass();
            
        System.out.println(c1+"\r\n"+c2+"\r\n"+c3);
        
        //获取内置基本数据类型的Class对象,通过类型.TYPE获取
        
        System.out.println(Integer.TYPE);
            
        //获取父类Class对象,通过子类的Class对象.getSuperClass方法获取
        
        System.out.println(c1.getSuperclass());
        
    }
    

 

 

 

打印的结果为:(可以看到几种方式都能获取到Class对象)

 

 

哪些类可以有Class对象:

>Class:外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类

>interface:接口

>[]:数组

>enum:枚举类型

>annotation:注解

>primitive type:基本数据类型

>void:空

举例:

package test;

import java.lang.annotation.ElementType;

public class Test{
    
    //所有类的Class对象
    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.TYPE; //基本数据类型
        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);
        
        //只要元素类型与维度一样,就是同一个Class
        int[] a=new int[10];
        int[] b=new int[20];
        
        System.out.println(a.getClass().hashCode() == b.getClass().hashCode());
    }
    
}

 

打印结果为:

 

 Tip:对于数组来说,只要元素的类型与维度一样,就是同一个Class对象。

未经允许,禁止转载,转载请联系QQ:493116703

posted @ 2020-08-10 09:56  Hgqin  阅读(264)  评论(0编辑  收藏  举报