• thinking in java学习笔记:14章 类型信息


    14.2 Class 对象

    https://github.com/zhaojiatao/javase

    1、什么是Class对象,Class对象是用来做什么的?

    Class对象是java程序用来创建类的所有常规对象用的;每个类都有一个Class对象;

    2、Class对象是如何创建的?

    当程序创建第一个对类的静态成员(static修饰的成员以及构造方法)的引用时,就会加载这个类。类加载器首先检查这个类的

    Class对象是否已经加载;如果尚未加载,默认的类加载器就会查找.class文件。在加载字节码后会执行安全验证,之后会根

    据字节码在内存中创建这个类的Class对象;

    3、除了jvm的类加载器会获取某个类的Class对象之外,我们自己如何获取某个类的Class对象的引用?

    这个例子会介绍第一种方法:使用Class类的forName()方法,获取类的Class对象的引用;这个方法的副作用是,如果jvm

    还未加载这个类的Class对象的话就加载这个类;在类加载的过程中,会初始化static成员;

    注意,传递给forName方法的名字,必须是全限定名;

    此外,还可以使用类字面常量的方法来生成对类Class对象的引用;

     1 package thinkingInJava.chapter_14_classInfo;
     2 
     3 
     4 /**
     5  * @author zhaojiatao
     6  * @date 2018/9/9
     7  *
     8  * 1、什么是Class对象,Class对象是用来做什么的?
     9  *   Class对象是java程序用来创建类的所有常规对象用的;每个类都有一个Class对象;
    10  * 2、Class对象是如何创建的?
    11  *   当程序创建第一个对类的静态成员(static修饰的成员以及构造方法)的引用时,就会加载这个类。类加载器首先检查这个类的
    12  *   Class对象是否已经加载;如果尚未加载,默认的类加载器就会查找.class文件。在加载字节码后会执行安全验证,之后会根
    13  *   据字节码在内存中创建这个类的Class对象;
    14  * 3、除了jvm的类加载器会获取某个类的Class对象之外,我们自己如何获取某个类的Class对象的引用?
    15  *    这个例子会介绍第一种方法:使用Class类的forName()方法,获取类的Class对象的引用;这个方法的副作用是,如果jvm
    16  *    还又有加载这个类的Class对象的话就加载这个类;在类加载的过程中,会初始化static成员;
    17  *    注意,传递给forName方法的名字,必须是全限定名;
    18  *    此外,还可以使用类字面常量的方法来生成对类Class对象的引用;
    19  *
    20  *
    21  */
    22 interface HasBatteries{}
    23 
    24 interface Waterproof{}
    25 
    26 interface Shoots{}
    27 
    28 class Toy{
    29     Toy(){}
    30     Toy(int i){}
    31 }
    32 
    33 class FancyToy extends Toy implements HasBatteries,Waterproof,Shoots{
    34     FancyToy(){
    35         super(1);
    36     }
    37 }
    38 
    39 public class Test2 {
    40 
    41     //通过Class对象,可以得到Class对象的多有信息
    42     static void printInfo(Class cc){
    43         System.out.println("Class name:"+cc.getName()+"is interface?["+cc.isInterface()+"]");
    44         System.out.println("Simple name:"+cc.getSimpleName());
    45         System.out.println("Canonical name:"+cc.getCanonicalName());
    46     }
    47 
    48 
    49     public static void main(String[] args) {
    50         Class c=null;
    51 
    52         try{
    53             //注意,必须使用全限定名
    54             c=Class.forName("thinkingInJava.chapter_14_classInfo.FancyToy");
    55         }catch (ClassNotFoundException e){
    56             System.out.println("Can't find Test2");
    57             System.exit(1);
    58         }
    59 
    60         printInfo(c);
    61 
    62         for(Class face:c.getInterfaces()){
    63             printInfo(face);
    64         }
    65 
    66         Class up=c.getSuperclass();
    67         Object obj=null;
    68         try{
    69             //使用newInstance来创建的类,必须带有默认构造器
    70             obj=up.newInstance();
    71         }catch(InstantiationException e){
    72             System.out.println("Cannot instantiate");
    73             System.exit(1);
    74         }catch (IllegalAccessException e){
    75             System.out.println("Cannot access");
    76             System.exit(1);
    77         }
    78         printInfo(obj.getClass());
    79 
    80         System.out.println(obj instanceof Toy);
    81 
    82     }
    83 
    84 
    85 }
    View Code

    14.2.1 字面类常量

    除了使用class的forName方法获取类的Class对象的引用外,还可以使用类字面常量;

    这种方式不仅简单,且更安全,因为会在类编译期就进行安全检查

    注意,使用.class的方式,获取类的Class对象的引用,不会像forName()那样初始化类的Class对象

    .class的方式仅仅是获取了Class对象的引用,只有到了类真正被初始化结束后,Class对象才真正被初始化;即:

    只有在对静态方法或非常数静态域进行首次引用时才执行初始化;

     1 package thinkingInJava.chapter_14_classInfo;
     2 
     3 import java.util.Random;
     4 
     5 /**
     6  * @author zhaojiatao
     7  * @date 2018/9/10
     8  *
     9  * 除了使用class的forName方法获取类的Class对象的引用外,还可以使用类字面常量;
    10  * 这种方式不仅简单,且更安全,因为会在类编译期就进行安全检查;
    11  * 注意,使用.class的方式,获取类的Class对象的引用,不会像forName()那样初始化类的Class对象;
    12  * .class的方式仅仅是获取了Class对象的引用,只有到了类真正被初始化结束后,Class对象才真正被初始化;即:
    13  * 只有在对静态方法或非常数静态域进行首次引用时才执行初始化;
    14  *
    15  */
    16 public class Test2_1 {
    17     public static Random rand=new Random(47);
    18 
    19     public static void main(String[] args) throws ClassNotFoundException {
    20         Class initable=Initable.class;
    21         System.out.println("这个时候仅仅是获取到了Initable类的Class对象引用,Class对象还没初始化");
    22         //对于static final值是编译期常量,则该值无需对类初始化就可以被读取;
    23         System.out.println(Initable.staticFinal);
    24         //非编译期常量,即使被static和final修饰,也必须先初始化类Class对象,才能读取;
    25         System.out.println(Initable.staticFinal2);
    26 
    27         //如果一个static域不是final的,那么在对它访问时,总是要求在它被读取之前,先进行链接(为这个域分配存储空间)
    28         //以及初始化(初始化该存储空间)
    29         System.out.println(Initable2.staticNonFinal);
    30 
    31         //使用forName方法,就会初始化类
    32         Class initable3=Class.forName("thinkingInJava.chapter_14_classInfo.Initable3");
    33         System.out.println(Initable3.staticNonFinal);
    34 
    35 
    36 
    37     }
    38 
    39 }
    40 
    41 
    42 class Initable{
    43     static final int staticFinal=47;
    44     static final int staticFinal2=(int)(1+Math.random()*(10-1+1));
    45     static {
    46         System.out.println("Initializing Initable");
    47     }
    48 }
    49 
    50 class Initable2{
    51     static int staticNonFinal=147;
    52     static {
    53         System.out.println("Initializing Initable2");
    54     }
    55 }
    56 
    57 class Initable3{
    58     static int staticNonFinal=74;
    59     static{
    60         System.out.println("Initializing Initable3");
    61     }
    62 }
    View Code

      

    14.2.2 泛化

    学习范型在Class引用的使用过程的应用

      1 package thinkingInJava.chapter_14_classInfo;
      2 
      3 import org.junit.Test;
      4 
      5 import java.util.ArrayList;
      6 import java.util.List;
      7 
      8 /**
      9  * @author zhaojiatao
     10  * @date 2018/9/11
     11  *
     12  *
     13  * 学习范型在Class引用的使用过程的应用
     14  *
     15  *
     16  */
     17 public class Test2_2 {
     18 
     19 
     20     @Test
     21     public void Test01() {
     22         Class intClass=int.class;
     23         Class<Integer> genericIntClass=int.class;
     24         genericIntClass=Integer.class;
     25         //如果将genericIntClass这个Class引用赋值给double.class的话,会由于类型检查失败,是无法编译;
     26         //genericIntClass=double.class;
     27         //普通类的引用可以赋值为任何其他的Class对象;
     28         intClass = double.class;
     29     }
     30 
     31 
     32     //可以使用通配符?代替上例中的<Integer>,
     33     @Test
     34     public void Test02() {
     35         Class<?> intClass=int.class;
     36         intClass = double.class;
     37     }
     38 
     39     //如果我像创建一个Class对象的引用,并指定这个Class对象的类型为指定类型或其子类型,则需要使用? extend XXX
     40     @Test
     41     public void Test03() {
     42         Class<? extends Number> bounded=int.class;
     43         bounded = double.class;
     44         bounded = Number.class;
     45     }
     46 
     47     //至此,可以得出结论,使用范型语法的目的是为了提供编译期检查;
     48 
     49     @Test
     50     public void Test04(){
     51         FilledList<CountedInteger> fl=new FilledList<CountedInteger>(CountedInteger.class);
     52         System.out.println(fl.create(15));
     53     }
     54 
     55 
     56 
     57     @Test
     58     public void Test05() throws IllegalAccessException, InstantiationException {
     59         Class<A> a=A.class;
     60         A aa=a.newInstance();
     61         //注意,这里如果不写成这样会报错
     62         Class<? super A> b=a.getSuperclass();
     63 
     64         //注意:b.newInstance返回的不是精确值,而是Object;
     65         Object bb=b.newInstance();
     66 
     67     }
     68 
     69 
     70 
     71 }
     72 
     73 class CountedInteger{
     74     private static long counter;
     75     private final long id=counter++;
     76     public String toString(){
     77         return  Long.toString(id);
     78     }
     79 }
     80 
     81 
     82 //注意,由于这个类中使用了type.newInstance()方法,所以,必须保证T传进来的类有默认构造方法;
     83 class FilledList<T>{
     84     private Class<T> type;
     85     public FilledList(Class<T> type){
     86         this.type=type;
     87     }
     88 
     89     public List<T> create(int nElements){
     90         List<T> result=new ArrayList<T>();
     91         try{
     92             for(int i=0;i<nElements;i++){
     93                 //注意,当在type上使用范型,newInstance()方法将产生确定的类型;
     94                 result.add(type.newInstance());
     95             }
     96         }catch (Exception e){
     97             e.printStackTrace();
     98         }
     99         return result;
    100     }
    101 
    102 }
    103 
    104 
    105 
    106 class A{
    107 
    108 }
    109 
    110 class B extends A{
    111 
    112 }
    View Code

    14.3 类型转换前先做检查

    在类型转换之前先做检查,如果贸然强制转换,可能会抛出ClassCastException异常;

    学习使用instance of 和isInstance()进行类型检查;

    主要区别就是instance of是编译器进行类型检查;

    而 isInstance方法是运行期,动态进行类型检查,可用于反射、泛型中;

     1 package thinkingInJava.chapter_14_classInfo;
     2 
     3 /**
     4  * @author zhaojiatao
     5  * @date 2018/9/11
     6  *
     7  * 在类型转换之前先做检查,如果贸然强制转换,可能会抛出ClassCastException异常;
     8  * 学习使用instance of 和isInstance()进行类型检查;
     9  * 主要区别就是instance of是编译器进行类型检查;
    10  * 而 isInstance方法是运行期,动态进行类型检查,可用于反射、泛型中;
    11  *
    12  */
    13 
    14 public class Test3 {
    15     public static boolean DynamicEqual(Object fatherObj,Object sonObj){
    16          return fatherObj.getClass().isInstance(sonObj); // pass
    17         // return sonObj.getClass().isInstance(fatherObj);
    18         // return sonObj instanceof Father; // pass
    19         // return sonObj instanceof (fatherObj.getClass()); //error
    20     }
    21 
    22     public static void main(String[] args){
    23         //instance of 编译器类型检查
    24         Father father = new Father();
    25         Son son = new Son();
    26 
    27         System.out.println(son instanceof Son); // true
    28         System.out.println(son instanceof Father); // true
    29         System.out.println(son instanceof Object); // true
    30         System.out.println(null instanceof Object); // false
    31         System.out.println();
    32 
    33         //运行时动态类型检查(括号里的是子类)
    34         System.out.println(Son.class.isInstance(son)); // true
    35         //很明显是错误的,但编译是可以通过的
    36         System.out.println(Integer.class.isInstance(son));//false
    37         System.out.println(Father.class.isInstance(son)); // true
    38         System.out.println(Object.class.isInstance(son)); // true
    39         System.out.println(Object.class.isInstance(null)); // false
    40         System.out.println();
    41 
    42 
    43         //different using
    44         System.out.println(DynamicEqual(father, son));
    45     }
    46 
    47 
    48 }
    49 
    50 class Father{}
    51 
    52 class Son extends Father{}
    View Code

    14.4注册工厂

    工厂设计模式:将对象的创建工作交给类自己去完成;工厂方法可以被多态地调用,从而为你创建恰当类型的对象。

      1 package thinkingInJava.chapter_14_classInfo;
      2 
      3 import java.util.ArrayList;
      4 import java.util.Collections;
      5 import java.util.List;
      6 import java.util.Random;
      7 
      8 /**
      9  * @author zhaojiatao
     10  * @date 2018/9/12
     11  * 工厂方法设计模式:
     12  * 将对象的创建工作交给类自己去完成;工厂方法可以被多态地调用,从而为你创建恰当类型的对象。
     13  */
     14 public class Test4 {
     15 
     16     public static void main(String[] args) {
     17         for(int i=0;i<10;i++){
     18             System.out.println(Part.createRandom());
     19         }
     20     }
     21 
     22 }
     23 
     24 interface Factory<T>{
     25     T create();
     26 }
     27 
     28 
     29 class Part{
     30     public String toString(){
     31         return getClass().getSimpleName();
     32     }
     33 
     34     static List<Factory<? extends Part>> partFactories=new ArrayList<>();
     35 
     36     static {
     37         /*partFactories.add(new FuelFilter.Factory());
     38         partFactories.add(new AirFilter.Factory());
     39         partFactories.add(new CabinAirFilter.Factory());
     40         partFactories.add(new OilFilter.Factory());
     41 
     42         partFactories.add(new FanBelt.Factory());
     43         partFactories.add(new GeneratorBelt.Factory());
     44         partFactories.add(new PowerSteeringBelt.Factory());*/
     45         Collections.addAll(partFactories,new FuelFilter.Factory(),new AirFilter.Factory(),new CabinAirFilter.Factory(),
     46         new OilFilter.Factory(),new FanBelt.Factory(),new GeneratorBelt.Factory(),new PowerSteeringBelt.Factory()
     47         );
     48 
     49     }
     50 
     51     private static Random rand=new Random(47);
     52     public static Part createRandom(){
     53         int n =rand.nextInt(partFactories.size());
     54         return partFactories.get(n).create();
     55     }
     56 
     57 
     58 }
     59 
     60 
     61 class Filter extends Part{}
     62 
     63 class FuelFilter extends Filter{
     64     public static class Factory implements thinkingInJava.chapter_14_classInfo.Factory<FuelFilter>{
     65         @Override
     66         public FuelFilter create() {
     67             return new FuelFilter();
     68         }
     69     }
     70 }
     71 
     72 class AirFilter extends Filter{
     73     public static class Factory implements thinkingInJava.chapter_14_classInfo.Factory<AirFilter>{
     74         @Override
     75         public AirFilter create() {
     76             return new AirFilter();
     77         }
     78     }
     79 }
     80 
     81 class CabinAirFilter extends Filter{
     82     public static class Factory implements thinkingInJava.chapter_14_classInfo.Factory<CabinAirFilter>{
     83         @Override
     84         public CabinAirFilter create() {
     85             return new CabinAirFilter();
     86         }
     87     }
     88 }
     89 
     90 class OilFilter extends Filter{
     91     public static class Factory implements thinkingInJava.chapter_14_classInfo.Factory<OilFilter>{
     92         @Override
     93         public OilFilter create() {
     94             return new OilFilter();
     95         }
     96     }
     97 }
     98 
     99 
    100 class Belt extends Part{
    101 
    102 }
    103 
    104 class FanBelt extends Belt{
    105     public static class Factory implements thinkingInJava.chapter_14_classInfo.Factory<FanBelt>{
    106         @Override
    107         public FanBelt create() {
    108             return new FanBelt();
    109         }
    110     }
    111 }
    112 
    113 class GeneratorBelt extends Belt{
    114     public static class Factory implements thinkingInJava.chapter_14_classInfo.Factory<GeneratorBelt>{
    115         @Override
    116         public GeneratorBelt create() {
    117             return new GeneratorBelt();
    118         }
    119     }
    120 }
    121 
    122 class PowerSteeringBelt extends Belt{
    123     public static class Factory implements thinkingInJava.chapter_14_classInfo.Factory<PowerSteeringBelt>{
    124         @Override
    125         public PowerSteeringBelt create() {
    126             return new PowerSteeringBelt();
    127         }
    128     }
    129 }
    View Code

     14.5 instanceof 与 Class的等价性

      这一章节,其实主要就是讲解instanceof、isInstance()、class对象== 三者的不同;

     instanceof和isInstance()是一组,结果是相同的,区别在前文已经说过,前者需要在编译器进行类型检查,后者只在运行时进行类型检查;

       而比较class对象的==和equals一组,结果也是相同的,应为equals是比较两个class对象的内存地址是否一致;

      但综合起来看,这两组的结果是不一样的。instance和isInstance()考虑了继承的情况,而后一组没有;

     1 package thinkingInJava.chapter_14_classInfo;
     2 
     3 /**
     4  * @author zhaojiatao
     5  * @date 2018/9/12
     6  */
     7 public class Test5 {
     8     public static void main(String[] args) {
     9         FamilyVsExactType.test(new Base());
    10         FamilyVsExactType.test(new Derived());
    11     }
    12 }
    13 
    14 
    15 class Base{}
    16 class Derived extends Base{}
    17 
    18 class FamilyVsExactType {
    19     static void test(Object x){
    20         System.out.println("Testing x of type "+x.getClass());
    21         System.out.println("x instanceof Base "+(x instanceof Base));
    22         System.out.println("x instanceof Derived "+Derived.class.isInstance(x));
    23         System.out.println("Base.isInstance(x) "+Base.class.isInstance(x));
    24         System.out.println("Derived.isInstance(x) "+Derived.class.isInstance(x));
    25         System.out.println("x.getClass()==Base.class "+(x.getClass()==Base.class));
    26         System.out.println("x.getClass()==Derived.class "+(x.getClass()==Derived.class));
    27         System.out.println("x.getClass().equals(Base.class) "+x.getClass().equals(Base.class));
    28         System.out.println("x.getClass().equals(Derived.class) "+x.getClass().equals(Derived.class));
    29     }
    30 }
    View Code

    14.6 反射与内省 

    14.6.1Java反射机制的适用场景及其利与弊(引用:https://blog.csdn.net/zolalad/article/details/29370565)

    一、反射的适用场景是什么?

    1).Java的反射机制在做基础框架的时候非常有用,有一句话这么说来着:反射机制是很多Java框架的基石。而一般应用层面很少用,不过这种东西,现在很多开源框架基本都已经给你封装好了,自己基本用不着写。典型的除了Hibernate之外,还有Spring也用到很多反射机制。经典的就是在xml文件或者properties里面写好了配置,然后在Java类里面解析xml或properties里面的内容,得到一个字符串,然后用反射机制,根据这个字符串获得某个类的Class实例,这样就可以动态配置一些东西,不用每一次都要在代码里面去new或者做其他的事情,以后要改的话直接改配置文件,代码维护起来就很方便了,同时有时候要适应某些需求,Java类里面不一定能直接调用另外的方法,这时候也可以通过反射机制来实现。
    总的来说,自己写的很少,具体什么时候要用那要看需求,反射机制无非就是根据一个String来得到你要的实体对象,然后调用它原来的东西。但是如果是要自己写框架的话,那就会用得比较多了。

    2)当你做一个软件可以安装插件的功能,你连插件的类型名称都不知道,你怎么实例化这个对象呢?因为程序是支持插件的(第三方的),在开发的时候并不知道 。所以无法在代码中 New出来 ,但反射可以,通过反射,动态加载程序集,然后读出类,检查标记之后再实例化对象,就可以获得正确的类实例。

    3)在编码阶段不知道那个类名,要在运行期从配置文件读取类名, 这时候就没有办法硬编码new ClassName(),而必须用到反射才能创建这个对象.反射的目的就是为了扩展未知的应用。比如你写了一个程序,这个程序定义了一些接口,只要实现了这些接口的dll都可以作为插件来插入到这个程序中。那么怎么实现呢?就可以通过反射来实现。就是把dll加载进内存,然后通过反射的方式来调用dll中的方法。很多工厂模式就是使用的反射。 

    二、程序员在自己的业务开发中应该尽量的远离反射

    反射:在流行的库如Spring和Hibernate中,反射自然有其用武之地。不过内省业务代码在很多时候都不是一件好事,原因有很多,一般情况下我总是建议大家不要使用反射。

    首先是代码可读性与工具支持。打开熟悉的IDE,寻找你的Java代码的内部依赖,很容易吧。现在,使用反射来替换掉你的代码然后再试一下,结果如何呢?如果通过反射来修改已经封装好的对象状态,那么结果将会变得更加不可控。请看看如下示例代码:

    如果这样做就无法得到编译期的安全保证。就像上面这个示例一样,你会发现如果getDeclaredField()方法调用的参数输错了,那么只有在运行期才能发现。要知道的是,寻找运行期Bug的难度要远远超过编译期的Bug。

    最后还要谈谈代价问题。JIT对反射的优化程度是不同的,有些优化时间会更长一些,而有些甚至是无法应用优化。因此,有时反射的性能损失可以达到几个数量级的差别。不过在典型的业务应用中,你可能不会注意到这个代价。

    总结一下,我觉得在业务代码中唯一合理(直接)使用反射的场景是通过AOP。除此之外,你最好远离反射这一特性。

    三、性能分析

    反射机制是一种程序自我分析的能力。用于获取一个类的类变量,构造函数,方法,修饰符。

    优点:运行期类型的判断,动态类加载,动态代理使用反射。

    缺点:性能是一个问题,反射相当于一系列解释操作,通知jvm要做的事情,性能比直接的java代码要慢很多。

    14.6.2反射的一些常用操作:参考:https://blog.csdn.net/sinat_38259539/article/details/71799078

      1 package thinkingInJava.chapter_14_classInfo;
      2 
      3 import org.junit.Test;
      4 
      5 import java.io.InputStream;
      6 import java.lang.reflect.Constructor;
      7 import java.lang.reflect.Field;
      8 import java.lang.reflect.InvocationTargetException;
      9 import java.lang.reflect.Method;
     10 import java.util.ArrayList;
     11 import java.util.Properties;
     12 
     13 /**
     14  * @author zhaojiatao
     15  * @date 2018/9/13
     16  *
     17  * 反射的应用场景:https://blog.csdn.net/zolalad/article/details/29370565
     18  * 反射的一些常用操作:参考:https://blog.csdn.net/sinat_38259539/article/details/71799078
     19  *
     20  */
     21 class Student {
     22 
     23     //---------------构造方法-------------------
     24     //(默认的构造方法)
     25     Student(String str){
     26         System.out.println("(默认)的构造方法 s = " + str);
     27     }
     28 
     29     //无参构造方法
     30     public Student(){
     31         System.out.println("调用了公有、无参构造方法执行了。。。");
     32     }
     33 
     34     //有一个参数的构造方法
     35     public Student(char name){
     36         System.out.println("姓名:" + name);
     37     }
     38 
     39     //有多个参数的构造方法
     40     public Student(String name ,int age){
     41         System.out.println("姓名:"+name+"年龄:"+ age);//这的执行效率有问题,以后解决。
     42     }
     43 
     44     //受保护的构造方法
     45     protected Student(boolean n){
     46         System.out.println("受保护的构造方法 n = " + n);
     47     }
     48 
     49     //私有构造方法
     50     private Student(int age){
     51         System.out.println("私有的构造方法   年龄:"+ age);
     52     }
     53 
     54 
     55     //**********字段*************//
     56     public String name;
     57     protected int age;
     58     char sex;
     59     private String phoneNum;
     60 
     61 
     62 
     63 
     64     //**************成员方法***************//
     65     public void show1(String s){
     66         System.out.println("调用了:公有的,String参数的show1(): s = " + s);
     67     }
     68     protected void show2(){
     69         System.out.println("调用了:受保护的,无参的show2()");
     70     }
     71     void show3(){
     72         System.out.println("调用了:默认的,无参的show3()");
     73     }
     74     private String show4(int age){
     75         System.out.println("调用了,私有的,并且有返回值的,int参数的show4(): age = " + age);
     76         return "abcd";
     77     }
     78 
     79 
     80 
     81     @Override
     82     public String toString() {
     83         return "Student [name=" + name + ", age=" + age + ", sex=" + sex
     84                 + ", phoneNum=" + phoneNum + "]";
     85     }
     86 
     87 
     88     public static void main(String[] args) {
     89         System.out.println("main方法执行了。。。");
     90     }
     91 
     92     public void show(){
     93         System.out.println("is show()");
     94     }
     95 
     96 
     97 
     98 
     99 }
    100 
    101 
    102 
    103 
    104 public class Test6 {
    105     /*
    106      * 通过Class对象可以获取某个类中的:构造方法、成员变量、成员方法;并访问成员;
    107      *
    108      * 1.获取构造方法:
    109      *         1).批量的方法:
    110      *             public Constructor[] getConstructors():所有"公有的"构造方法
    111                 public Constructor[] getDeclaredConstructors():获取所有的构造方法(包括私有、受保护、默认、公有)
    112 
    113      *         2).获取单个的方法,并调用:
    114      *             public Constructor getConstructor(Class... parameterTypes):获取单个的"公有的"构造方法:
    115      *             public Constructor getDeclaredConstructor(Class... parameterTypes):获取"某个构造方法"可以是私有的,或受保护、默认、公有;
    116      *
    117      *             调用构造方法:
    118      *             Constructor-->newInstance(Object... initargs)
    119      *
    120      * 2.设置字段的值:
    121      *         Field --> public void set(Object obj,Object value):
    122      *         参数说明:
    123      *         1.obj:要设置的字段所在的对象;
    124      *         2.value:要为字段设置的值;
    125      *
    126      *
    127      */
    128 
    129 
    130 
    131     //通过反射获取构造方法并使用
    132     @Test
    133     public void test01() {
    134 
    135         //1.加载Class对象
    136         Class clazz = null;
    137         try {
    138             clazz = Class.forName("thinkingInJava.chapter_14_classInfo.Student");
    139         } catch (ClassNotFoundException e) {
    140             e.printStackTrace();
    141         }
    142 
    143         //2.获取所有公有构造方法
    144             System.out.println("**********************所有公有构造方法*********************************");
    145             //Returns an array containing Constructor objects reflecting all the public constructors of the class represented by this Class object.
    146             Constructor[] conArray = clazz.getConstructors();
    147             for(Constructor c : conArray){
    148                 System.out.println(c);
    149             }
    150 
    151             System.out.println("************所有的构造方法(包括:私有、受保护、默认、公有)***************");
    152             //Returns an array of Constructor objects reflecting all the constructors declared by the class represented by this Class object.
    153             conArray = clazz.getDeclaredConstructors();
    154             for(Constructor c : conArray){
    155                 System.out.println(c);
    156             }
    157 
    158             System.out.println("*****************获取公有、无参的构造方法*******************************");
    159             Constructor con = null;
    160             try {
    161                 con = clazz.getConstructor(null);
    162             } catch (NoSuchMethodException e) {
    163                 e.printStackTrace();
    164             }
    165             //1>、因为是无参的构造方法所以类型是一个null,不写也可以:这里需要的是一个参数的类型,切记是类型
    166             //2>、返回的是描述这个无参构造函数的类对象。
    167 
    168             System.out.println("con = " + con);
    169             //调用构造方法
    170             Object obj = null;
    171             try {
    172                 obj = con.newInstance();
    173             } catch (InstantiationException e) {
    174                 e.printStackTrace();
    175             } catch (IllegalAccessException e) {
    176                 e.printStackTrace();
    177             } catch (InvocationTargetException e) {
    178                 e.printStackTrace();
    179             }
    180             System.out.println("obj = " + obj);
    181             Student stu = (Student)obj;
    182             System.out.println("使用无参构造方法创建的Student对象的实例:stu="+stu.toString());
    183 
    184             System.out.println("******************获取私有构造方法,并调用*******************************");
    185             try {
    186                 con = clazz.getDeclaredConstructor(char.class);
    187             } catch (NoSuchMethodException e) {
    188                 e.printStackTrace();
    189             }
    190             System.out.println(con);
    191             //调用构造方法
    192             con.setAccessible(true);//暴力访问(忽略掉访问修饰符)
    193             try {
    194                 obj = con.newInstance('男');
    195             } catch (InstantiationException e) {
    196                 e.printStackTrace();
    197             } catch (IllegalAccessException e) {
    198                 e.printStackTrace();
    199             } catch (InvocationTargetException e) {
    200                 e.printStackTrace();
    201             }
    202 
    203             System.out.println("obj = " + obj);
    204             stu = (Student)obj;
    205             System.out.println("使用私有构造方法创建的Student对象的实例:stu="+stu.toString());
    206 
    207     }
    208 
    209 
    210 
    211 
    212     //获取成员变量并调用
    213     @Test
    214     public void test02() throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException, IllegalAccessException,
    215             InvocationTargetException, InstantiationException {
    216         //1.获取Class对象
    217         Class stuClass = Class.forName("thinkingInJava.chapter_14_classInfo.Student");
    218         //2.获取字段
    219         System.out.println("************获取所有公有的字段********************");
    220         Field[] fieldArray = stuClass.getFields();
    221         for(Field f : fieldArray){
    222             System.out.println(f);
    223         }
    224         System.out.println("************获取所有的字段(包括私有、受保护、默认的)********************");
    225         fieldArray = stuClass.getDeclaredFields();
    226         for(Field f : fieldArray){
    227             System.out.println(f);
    228         }
    229         System.out.println("*************获取公有字段xx并调用***********************************");
    230         Field f = stuClass.getField("name");
    231         System.out.println(f);
    232         //获取一个对象
    233         Object obj = stuClass.getConstructor().newInstance();//产生Student对象--》Student stu = new Student();
    234         //为字段设置值
    235         f.set(obj, "刘德华");//为Student对象中的name属性赋值--》stu.name = "刘德华"
    236         //验证
    237         Student stu = (Student)obj;
    238         System.out.println("验证姓名:" + stu.name);
    239 
    240 
    241         System.out.println("**************获取私有字段****并调用********************************");
    242         f = stuClass.getDeclaredField("phoneNum");
    243         System.out.println(f);
    244         f.setAccessible(true);//暴力反射,解除私有限定
    245         f.set(obj, "18888889999");
    246         System.out.println("验证电话:" + stu);
    247 
    248     }
    249 
    250 
    251 
    252     //获取成员方法并调用
    253     @Test
    254     public void test03() throws Exception {
    255 
    256         //1.获取Class对象
    257         Class stuClass = Class.forName("thinkingInJava.chapter_14_classInfo.Student");
    258         //2.获取所有公有方法
    259         System.out.println("***************获取所有的”公有“方法*******************");
    260         stuClass.getMethods();
    261         Method[] methodArray = stuClass.getMethods();
    262         for(Method m : methodArray){
    263             System.out.println(m);
    264         }
    265         System.out.println("***************获取所有的方法,包括私有的*******************");
    266         methodArray = stuClass.getDeclaredMethods();
    267         for(Method m : methodArray){
    268             System.out.println(m);
    269         }
    270         System.out.println("***************获取公有的show1()方法*******************");
    271         Method m = stuClass.getMethod("show1", String.class);
    272         System.out.println(m);
    273         //实例化一个Student对象
    274         Object obj = stuClass.getConstructor().newInstance();
    275         m.invoke(obj, "刘德华");
    276 
    277         System.out.println("***************获取私有的show4()方法******************");
    278         m = stuClass.getDeclaredMethod("show4", int.class);
    279         System.out.println(m);
    280         m.setAccessible(true);//解除私有限定
    281         Object result = m.invoke(obj, 20);//需要两个参数,一个是要调用的对象(获取有反射),一个是实参
    282         System.out.println("返回值:" + result);
    283 
    284 
    285 
    286 
    287     }
    288 
    289 
    290     //反射main方法
    291     @Test
    292     public void test04(){
    293         try {
    294             //1、获取Student对象的字节码
    295             Class clazz = Class.forName("thinkingInJava.chapter_14_classInfo.Student");
    296 
    297             //2、获取main方法
    298             Method methodMain = clazz.getMethod("main", String[].class);//第一个参数:方法名称,第二个参数:方法形参的类型,
    299             //3、调用main方法
    300             // methodMain.invoke(null, new String[]{"a","b","c"});
    301             //第一个参数,对象类型,因为方法是static静态的,所以为null可以,第二个参数是String数组,这里要注意在jdk1.4时是数组,jdk1.5之后是可变参数
    302             //这里拆的时候将  new String[]{"a","b","c"} 拆成3个对象。。。所以需要将它强转。
    303             methodMain.invoke(null, (Object)new String[]{"a","b","c"});//方式一
    304             //methodMain.invoke(null, new Object[]{new String[]{"a","b","c"}});//方式二
    305 
    306         } catch (Exception e) {
    307             e.printStackTrace();
    308         }
    309 
    310     }
    311 
    312 
    313 
    314     //反射方法的其它使用之---通过反射运行配置文件内容
    315     @Test
    316     public void test05() throws Exception {
    317         Properties pro = new Properties();
    318         {//此方式要求   配置文件在 src 文件夹 内
    319 
    320             //类名.class.getClassLoader().getResourceAsStream("文件名")
    321             InputStream inStream = this.getClass().getClassLoader().getResourceAsStream("pro.properties");
    322             pro.load(inStream);
    323             inStream.close();
    324         }
    325 
    326         //通过反射获取Class对象
    327         Class stuClass = Class.forName(pro.getProperty("className"));//"cn.fanshe.Student"
    328         //2获取show()方法
    329         Method m = stuClass.getMethod(pro.getProperty("methodName"));//show
    330         //3.调用show()方法
    331         m.invoke(stuClass.getConstructor().newInstance());
    332 
    333     }
    334 
    335 
    336 
    337     //反射方法的其它使用之---通过反射越过泛型检查
    338     @Test
    339     public void test06() throws Exception{
    340         ArrayList<String> strList = new ArrayList<String>();
    341         strList.add("aaa");
    342         strList.add("bbb");
    343 
    344         //    strList.add(100);
    345         //获取ArrayList的Class对象,反向的调用add()方法,添加数据
    346         Class listClass = strList.getClass(); //得到 strList 对象的字节码 对象
    347         //获取add()方法
    348         Method m = listClass.getMethod("add", Object.class);
    349         //调用add()方法
    350         m.invoke(strList, 100);
    351 
    352         //遍历集合
    353         for(Object obj : strList){
    354             System.out.println(obj);
    355         }
    356 
    357     }
    358 
    359 
    360 
    361 
    362 
    363 }
    View Code

    14.6.3 内省(参考:http://www.cnblogs.com/peida/archive/2013/06/03/3090842.html)

    内省(Introspector) 是Java 语言对 JavaBean 类属性、事件的一种缺省处理方法。

      JavaBean是一种特殊的类,主要用于传递数据信息,这种类中的方法主要用于访问私有的字段,且方法名符合某种命名规则。如果在两个模块之间传递信息,可以将信息封装进JavaBean中,这种对象称为“值对象”(Value Object),或“VO”。方法比较少。这些信息储存在类的私有变量中,通过set()、get()获得。

    什么时候会用到内省?

    在框架设计的时候使用比较多,像mybatis中类与表的映射关系的类的属性与表的列一一对应,框架在赋值和获取值得时候会调用对应的setter和getter方法;

    例如spring 中初始化bean的时候取药用到反射的方式实例化bean,在set值的时候会根据变量名获得settter方法,把配置好的值set进去;

      1 package thinkingInJava.chapter_14_classInfo;
      2 
      3 import org.apache.commons.beanutils.BeanUtils;
      4 import org.apache.commons.beanutils.PropertyUtils;
      5 import org.junit.Test;
      6 
      7 import java.beans.BeanInfo;
      8 import java.beans.IntrospectionException;
      9 import java.beans.Introspector;
     10 import java.beans.PropertyDescriptor;
     11 import java.lang.reflect.InvocationTargetException;
     12 import java.lang.reflect.Method;
     13 
     14 /**
     15  * @author zhaojiatao
     16  * @date 2018/9/13
     17  *
     18  * 内省:
     19  * http://www.cnblogs.com/peida/archive/2013/06/03/3090842.html
     20  * https://blog.csdn.net/u010445297/article/details/60967146
     21  *
     22  */
     23 public class Test6_Introspector {
     24 
     25     public static void main(String[] args) {
     26         UserInfo userInfo=new UserInfo();
     27         userInfo.setUserName("peida");
     28         try {
     29             BeanInfoUtil.getProperty(userInfo, "userName");
     30 
     31             BeanInfoUtil.setProperty(userInfo, "userName");
     32 
     33             BeanInfoUtil.getProperty(userInfo, "userName");
     34 
     35             BeanInfoUtil.setPropertyByIntrospector(userInfo, "userName");
     36 
     37             BeanInfoUtil.getPropertyByIntrospector(userInfo, "userName");
     38 
     39             BeanInfoUtil.setProperty(userInfo, "age");
     40             //说明:BeanInfoUtil.setProperty(userInfo, "age");报错是应为age属性是int数据类型,
     41             //而setProperty方法里面默认给age属性赋的值是String类型。所以会爆出argument type mismatch参数类型不匹配的错误信息。
     42 
     43         } catch (Exception e) {
     44             // TODO Auto-generated catch block
     45             e.printStackTrace();
     46         }
     47 
     48     }
     49 
     50 
     51 
     52     @Test
     53     public void testBeanUtils(){
     54         UserInfo userInfo=new UserInfo();
     55         try {
     56             BeanUtils.setProperty(userInfo, "userName", "peida");
     57 
     58             System.out.println("set userName:"+userInfo.getUserName());
     59 
     60             System.out.println("get userName:"+BeanUtils.getProperty(userInfo, "userName"));
     61 
     62             BeanUtils.setProperty(userInfo, "age", 18);
     63             System.out.println("set age:"+userInfo.getAge());
     64 
     65             System.out.println("get age:"+BeanUtils.getProperty(userInfo, "age"));
     66 
     67             System.out.println("get userName type:"+BeanUtils.getProperty(userInfo, "userName").getClass().getName());
     68             System.out.println("get age type:"+BeanUtils.getProperty(userInfo, "age").getClass().getName());
     69 
     70             PropertyUtils.setProperty(userInfo, "age", 8);
     71             System.out.println(PropertyUtils.getProperty(userInfo, "age"));
     72 
     73             System.out.println(PropertyUtils.getProperty(userInfo, "age").getClass().getName());
     74 
     75             PropertyUtils.setProperty(userInfo, "age", "8");
     76         }
     77         catch (IllegalAccessException e) {
     78             e.printStackTrace();
     79         }
     80         catch (InvocationTargetException e) {
     81             e.printStackTrace();
     82         }
     83         catch (NoSuchMethodException e) {
     84             e.printStackTrace();
     85         }
     86     }
     87 
     88 
     89 
     90 }
     91 
     92 
     93 class BeanInfoUtil {
     94 
     95     public static void setProperty(UserInfo userInfo,String userName){
     96         PropertyDescriptor propDesc= null;
     97         try {
     98             propDesc = new PropertyDescriptor(userName,UserInfo.class);
     99         } catch (IntrospectionException e) {
    100             e.printStackTrace();
    101         }
    102         Method methodSetUserName=propDesc.getWriteMethod();
    103         try {
    104             methodSetUserName.invoke(userInfo, "wong");
    105         } catch (IllegalAccessException e) {
    106             e.printStackTrace();
    107         } catch (InvocationTargetException e) {
    108             e.printStackTrace();
    109         }
    110         System.out.println("set userName:"+userInfo.getUserName());
    111     }
    112 
    113     public static void getProperty(UserInfo userInfo,String userName){
    114         PropertyDescriptor proDescriptor = null;
    115         try {
    116             proDescriptor = new PropertyDescriptor(userName,UserInfo.class);
    117         } catch (IntrospectionException e) {
    118             e.printStackTrace();
    119         }
    120         Method methodGetUserName=proDescriptor.getReadMethod();
    121         Object objUserName= null;
    122         try {
    123             objUserName = methodGetUserName.invoke(userInfo);
    124         } catch (IllegalAccessException e) {
    125             e.printStackTrace();
    126         } catch (InvocationTargetException e) {
    127             e.printStackTrace();
    128         }
    129         System.out.println("get userName:"+objUserName.toString());
    130     }
    131 
    132     public static void setPropertyByIntrospector(UserInfo userInfo,String userName){
    133         BeanInfo beanInfo= null;
    134         try {
    135             beanInfo = Introspector.getBeanInfo(UserInfo.class);
    136         } catch (IntrospectionException e) {
    137             e.printStackTrace();
    138         }
    139         PropertyDescriptor[] proDescrtptors=beanInfo.getPropertyDescriptors();
    140         if(proDescrtptors!=null&&proDescrtptors.length>0){
    141             for(PropertyDescriptor propDesc:proDescrtptors){
    142                 if(propDesc.getName().equals(userName)){
    143                     Method methodSetUserName=propDesc.getWriteMethod();
    144                     try {
    145                         methodSetUserName.invoke(userInfo, "alan");
    146                     } catch (IllegalAccessException e) {
    147                         e.printStackTrace();
    148                     } catch (InvocationTargetException e) {
    149                         e.printStackTrace();
    150                     }
    151                     System.out.println("set userName:"+userInfo.getUserName());
    152                     break;
    153                 }
    154             }
    155         }
    156     }
    157 
    158     public static void getPropertyByIntrospector(UserInfo userInfo,String userName){
    159         BeanInfo beanInfo= null;
    160         try {
    161             beanInfo = Introspector.getBeanInfo(UserInfo.class);
    162         } catch (IntrospectionException e) {
    163             e.printStackTrace();
    164         }
    165         PropertyDescriptor[] proDescrtptors=beanInfo.getPropertyDescriptors();
    166         if(proDescrtptors!=null&&proDescrtptors.length>0){
    167             for(PropertyDescriptor propDesc:proDescrtptors){
    168                 if(propDesc.getName().equals(userName)){
    169                     Method methodGetUserName=propDesc.getReadMethod();
    170                     Object objUserName= null;
    171                     try {
    172                         objUserName = methodGetUserName.invoke(userInfo);
    173                     } catch (IllegalAccessException e) {
    174                         e.printStackTrace();
    175                     } catch (InvocationTargetException e) {
    176                         e.printStackTrace();
    177                     }
    178                     System.out.println("get userName:"+objUserName.toString());
    179                     break;
    180                 }
    181             }
    182         }
    183     }
    184 }
    185 
    186 
    187 
    188 class UserInfo {
    189 
    190     private long userId;
    191     private String userName;
    192     private int age;
    193     private String emailAddress;
    194 
    195     public long getUserId() {
    196         return userId;
    197     }
    198     public void setUserId(long userId) {
    199         this.userId = userId;
    200     }
    201     public String getUserName() {
    202         return userName;
    203     }
    204     public void setUserName(String userName) {
    205         this.userName = userName;
    206     }
    207     public int getAge() {
    208         return age;
    209     }
    210     public void setAge(int age) {
    211         this.age = age;
    212     }
    213     public String getEmailAddress() {
    214         return emailAddress;
    215     }
    216     public void setEmailAddress(String emailAddress) {
    217         this.emailAddress = emailAddress;
    218     }
    219 
    220 }
    View Code

    14.7 jdk动态代理(参考:https://www.cnblogs.com/xiaoluo501395377/p/3383130.html)

    jdk的动态代理依赖接口,有局限性;底层是通过反射,执行method;

  • 相关阅读:
    UNIX时间戳/日期转换
    慎用date获取未来时间
    lnmp集成开发环境安装pdo_dblib扩展
    elementary OS下netbeans中文乱码的问题
    一个轻量级javascript框架的设计模式
    debian清空消息通知
    一道数组方面的笔试题
    模拟post提交
    P2970 [USACO09DEC]自私的放牧Selfish Grazing
    P1063 能量项链
  • 原文地址:https://www.cnblogs.com/zhaojiatao/p/9631824.html
Copyright © 2020-2023  润新知