`
段箭*残箫
  • 浏览: 53872 次
  • 性别: Icon_minigender_1
  • 来自: 成都
社区版块
存档分类

使用new和反射产生java内部类的实例化对象

阅读更多

前两天看到一道面试题,是关于内部类的知识,觉得很有意思。

这道题是这样的:

根据注释填写(1),(2),(3)处的代码

public class Test{

       public static void main(String[] args){

              // 初始化Bean1

              (1)

              bean1.I++;

              // 初始化Bean2

              (2)

              bean2.J++;

              //初始化Bean3

              (3)

              bean3.k++;

       }

 

       class Bean1{

              public int I = 0;

       }

 

       static class Bean2{

              public int J = 0;

       }

}

public class Bean{

       public class Bean3{

              public int k = 0;

       }

}

 

这其实就是实例化内部类对象的题。

从上面的题可以看出,Bean1为非静态内部类,Bean2为静态内部类,而Bean3则不是在Test类内部了,而是在一个外部类Bean的内部(是不是很拗口),呵呵。现通过new和反射来详细讲解其产生原理。

1.new

我们知道,内部类分为两种,一种是静态内部类,一种是非静态内部类。前者不用产生外部类的实例化对象即可产生内部类的实例化对象,后者必须先产生外部类的实例化对象,才能产生内部类的实例化对象。

 

实例化静态内部类对象的模板是:  外部类类名.内部类类名 xxx = new 外部类类名.内部类类名()

实例化非静态内部类对象的模板是:外部类类名.内部类类名 xxx = 外部类对象名.new 内部类类名()

 

1>>实例化非静态内部类Bean1

java代码 :

Test test = new Test();
   Test.Bean1 b1 = test.new Bean1();
   b1.I++;

总共3行代码,是不是很简单呢。

2>>实例化静态内部类Bean2

java代码:

 Test.Bean2 b2 = new Test.Bean2();
   b2.j++;

3>>实例化非静态内部类Bean3

Bean bean = new Bean();
    Bean.Bean3 b3 =  bean.new Bean3();
    System.out.println(b3.k++);

 

总结:通过new方式产生内部类的实例化对象实现起来比较简单,也很容易理解,如果要深层次了解其产生,请用下面讲解的方式,反射。

2.反射

1>>反射产生非静态内部类Bean1的实例化对象

java代码:

try {
    Class<?> cla2 = Class.forName("com.lovo.nio.Test$Bean1");
   } catch (ClassNotFoundException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   }

解析:我们知道,内部类的class文件是以"外部类$内部类.class"的形式存在的,所以获取Class对象的时候,必须使用forName("包名+外部类$内部类")的形式才能得到Class对象

得到Class对象cla2后,肯定有人会说用下面的方法得到Bean1的实例化对象:

Bean1 b6 = (Bean1)cla2.newInstance();
    运行上述代码,出现异常:InstantiationException,查看Java API文档,下面引用其原话:

当应用程序试图使用 Class 类中的 newInstance 方法创建一个类的实例,而指定的类对象无法被实例化时,抛出该异常。实例化失败有很多原因,包括但不仅限于以下原因:

  • 类对象表示一个抽象类、接口、数组类、基本类型、void
  • 类没有非 null 构造方法

在这里的原因是:Bean1的构造方法不公开,意思就是说,他的构造方法不是public的,不能通过newInstance()方式产生他的实例化对象。

那么我们是否可以查看其是什么访问修饰符的构造方法呢?答案是可以的,可以通过以下方式得到:

Constructor<?>[] c = cla2.getDeclaredConstructors();
    int i = c[0].getModifiers();
    //得到访问修饰符
    String str = Modifier.toString(i);
    System.out.println(str+" aaaaaaaaa");      //注意:此处的aaaaaaaaaaa仅表示有这个输出

 运行以上代码,输出“   aaaaaaaaa”,可以看出并没有输出str,实际上已经输出了,就是default,default在Java中可以省略不写的。现在该明白了吧!~

那要如何才能实例化他的内部类对象呢,刚才我们说过,要实例化非静态的内部类对象,必须先实例化外部类的对象,可是我们任然没有实例化外部类的对象。我们查看JAVA PAI文档,发现Constructor类有一个方法,newInstance(Object... initargs),于是我们想到下面这种方式来构建:

 //反射产生非静态内部类Bean1的实例化对象
   try {
    Class<?> cla2 = Class.forName("com.lovo.nio.Test$Bean1");
//    Bean1 b6 = (Bean1)cla2.newInstance();
//    System.out.println(b6);
    Constructor<?>[] c = cla2.getDeclaredConstructors();
    int i = c[0].getModifiers();
    //得到访问修饰符
    String str = Modifier.toString(i);
    System.out.println(str+" aaaaaaaaa");
    Bean1 bean1 = (Bean1)c[0].newInstance(new Test());
    System.out.println(bean1.I++);
   } catch (ClassNotFoundException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   } catch (IllegalArgumentException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   } catch (InstantiationException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   } catch (IllegalAccessException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   } catch (InvocationTargetException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   }

   运行以上代码,正常。

 2>>反射产生静态内部类Bean2的实例化对象

 Java代码:

//反射产生静态内部类Bean2的实例化对象
   try {
    // 初始化Bean2 
          Class<?> cla = null;
    cla = Class.forName("com.lovo.nio.Test$Bean2");
    Bean2 bean2 = (Bean2)cla.newInstance();
    System.out.println(bean2.j++);
   } catch (ClassNotFoundException e1) {
    // TODO Auto-generated catch block
    e1.printStackTrace();
   } catch (InstantiationException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   } catch (IllegalAccessException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   }
   解析:

 首先来看看static的相关知识:

static内部类意味着:  
        (1)   为创建一个static内部类的对象,我们不需要一个外部类对象。  
        (2)   不能从static内部类的一个对象中访问一个外部类对象.  
          倘若为了创建内部类的对象而不需要创建外部类的一个对象,那么可将所有东西都设为static。为了能正常工作,同时也必须将内部类设为static。  
          此外,也可考虑用一个static内部类容纳自己的测试代码。
 

在这里我们同样可以使用上面的方法获取他的构造方法的修饰符,也是default的,(静态内部类是有构造方法的,而且是无参的构造方法).

java代码:

try {
    
    // 初始化Bean2 
          Class<?> cla = null;
    cla = Class.forName("com.lovo.nio.Test$Bean2");
    Bean2 bean2 = (Bean2)cla.newInstance();
    Constructor<?>[] cs = cla.getDeclaredConstructors();
//    Modifier.toString(cs[0]);
    System.out.println("******************");

    System.err.println(cs.length);
    System.out.println(cs[0].getModifiers());
    System.out.println("******************");

    System.out.println(bean2.j++);
   } catch (ClassNotFoundException e1) {
    // TODO Auto-generated catch block
    e1.printStackTrace();
   } catch (InstantiationException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   } catch (IllegalAccessException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   }

运行以上代码,红色部分代码输出:

******************
1

******************

可以看出,有一个默认的(方法修饰符没写的,不是没有方法修饰符)构造方法。

如果我们要使用反射产生Bean2的实例化对象的话,只能用getDeclaredConstructors()方法。如上面的代码所示。

 3>>反射产生 外部类的内部类Bean3 的实例化对象

分析:要产生外部类的内部类的实例化对象,则要先产生外部类的实例化对象。再通过getClass()方法得到外部类的实例化对象的Class对象,再通过getClasses()方法得到外部类的所有公共类和接口,包括内部类。

Java代码:

try {
      Class<?> c3 = bean.getClass();
      Class<?>[] cl = c3.getClasses();
//      Bean3 b4 = (Bean3)c3.newInstance();
//      System.out.println(b4);
      //使用反射产生Bean3实例化对象
      Constructor<?>[] cc = cl[0].getDeclaredConstructors();
      //获取构造方法的个数,用以判定其构造方法是否是公共的,如果直接通过c3.newInstance()方法来实例化Bean3的话,则会包异常:java.lang.ClassCastException
      System.out.println(cc.length);
      Bean3 bb = (Bean3)cc[0].newInstance(new Bean());
      System.out.println(bb.k++);
     } catch (IllegalArgumentException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
     } catch (InstantiationException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
     } catch (IllegalAccessException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
     } catch (InvocationTargetException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
     }

 

最后关于反射的一点点总结:

1.Java的反射是Java类的“自审”,可以通过反射探知类的结构,比如,获得类的属性,方法,修饰符,返回类型。是在程序运行时动态的生成的,那么有人会问,既然可以的到类的属性,方法等,那么是否可以增加或者删除它的属性或者方法呢?答案是否定的,因为Java是静态语言,不是动态语言,那么就不能修改类的属性,方法,只能探知类的属性,方法等。

2.既然可以通过反射得到类的属性及其父类的属性,方法等以及实现的接口的相关内容,那么是否可以通过接口得到他的实现类的属性,方法呢?答案是否定的,因为在子类中有个关键字implements,是通过他得到类所实现的接口,但是如果要通过接口得到子类的相关内容,是行不通的,因为在接口中没有任何与实现类相关联的代码(比如关键字)。

不知道小弟所说是否正确,请大家指正,谢谢!

分享到:
评论
1 楼 somefuture 2016-08-23  
Object object = new Object() {
String args = "";
int a = 0;
};如果是对于这样的代码,后面怎么拿到里面的属性呢?

相关推荐

    java 通过反射获取枚举类,及枚举类的值,枚举类枚举实例名

    在Java编程语言中,反射(Reflection)是一种强大的工具,它允许程序在运行时检查和操作类、接口、字段和方法等对象。枚举(Enumeration)是Java中的一个特殊类类型,用于定义一组常量。本项目"test-enum-demo-...

    java反射机制学习(二):通过反射获取构造方法并实例化对象

    在`GetClassInstanceDemo01.java`、`GetClassInstanceDemo02.java`和`GetClassInstanceDemo03.java`中,可能展示了不同情况下的构造方法获取和对象实例化,比如带有参数的构造方法或处理异常的情况。 例如,如果`...

    java反射经典实例

    Java反射是Java编程语言中的一个强大特性,它允许运行中的Java程序对自身进行检查并且可以直接操作程序的内部属性。在本“Java反射经典实例”中,我们将深入探讨这个主题,了解如何利用反射来访问和修改私有属性以及...

    利用反射生成泛型类对象

    对于框架设计者来说,便捷的代码,是很重要的一部分。 反射和泛型是一种重要的解决途径。 此代码是一个生成泛型对象的类。...希望能帮助那些为查找泛型构造器、非静态内部泛型类的对象生成而烦恼的童鞋。

    反射私有内部类的例子

    反射是Java语言提供的一种能够在运行时分析类和对象的能力。它允许程序获取类、接口、方法和字段的信息,并可以在运行时创建和操作这些实体。反射的主要应用场景包括框架开发、动态代理、单元测试等。 #### 三、...

    JAVA反射机制的入门代码

    2. **创建对象**:使用`Class`对象的`newInstance()`方法创建类的新实例,前提是该类有无参构造函数。如果类没有公共构造函数,可以使用`Constructor`类的`getDeclaredConstructor()`和`newInstance()`方法。 3. **...

    讲解Java中如何构造内部类对象以及访问对象

    这时,`getConstructors()` 将返回空数组,因为非静态内部类需要一个外部类的引用才能实例化。在这种情况下,我们需要使用 `getDeclaredConstructors()` 方法来获取所有声明的构造器,包括私有的: ```java ...

    java反射实例代码

    3. **实例化对象**: `Class`对象提供了`newInstance()`方法,用于创建类的新实例。但是,这个方法要求类必须有一个无参数的公共构造器。如果类没有这样的构造器,或者构造器需要参数,我们需要使用`Constructor`对象...

    反射和动态实例化

    动态实例化是反射的一个关键应用,它使得我们能够在运行时根据需要创建对象,而无需在编译时就确定具体类。这在处理插件系统、框架设计和元数据驱动的应用程序中非常有用。 首先,让我们深入理解反射的基本概念。...

    JAVA反射机制-Class类-Class对象的获取.pdf

    ### JAVA反射机制——Class类与Class对象的获取 #### 概述 在Java语言中,反射是一种强大的功能,允许程序在运行时动态地检查和修改自身的行为。这种能力是通过`java.lang.Class`类实现的,它提供了封装类或接口...

    Java 反射机制 代码的实例

    Java反射机制是Java编程语言中的一个强大特性,它允许程序在运行时检查和操作类、接口、对象等的内部结构。通过反射,开发者可以动态地获取类的信息并调用其方法,创建对象,访问私有成员,甚至改变类的行为。在深入...

    JAVA_内部类_反射机制

    总之,Java的内部类提供了灵活的类设计能力,特别是对于继承和多态的需求,而反射机制则为Java代码提供了运行时的强大灵活性,允许程序在运行时动态地操作类和对象。两者结合使用,可以解决许多复杂的编程问题,但也...

    java由类的完整路径利用反射给接口注入对象

    如果我们知道接口的实现类,可以通过反射API的`getDeclaredConstructor()`和`newInstance()`方法来创建一个实例,其中`getDeclaredConstructor()`用于获取指定的构造器,`newInstance()`则用来实例化对象。...

    java的类反射

    Java的类反射是Java语言中一个强大的特性,它允许程序在运行时检查并操作类、接口、字段和方法的信息。这种动态类型的能力使得Java代码能够更加灵活,可以处理未知类型的对象,实现元编程,以及在运行时创建和访问类...

    java反射机制 字符串——java对象

    这一过程涉及到了对`java.lang.reflect`包中类的使用,如`Class.forName()`用于获取类的Class对象,`Constructor.newInstance()`用于创建对象,以及`Method.invoke()`用于调用方法。 总的来说,Java反射机制和字符...

    java 反射实例,大量注释,简单易懂

    Java反射是Java编程语言中的一个重要特性,它允许程序在运行时检查类、接口、字段和方法的信息,并且能够在运行时创建和访问这些类的对象。在Java中,反射提供了对类对象的元数据(metadata)的访问,使得我们可以...

    Java让泛型实例化的方法

    然而,在 Java 中使用泛型时,会出现一个问题:如何实例化泛型对象?在 Java 中,泛型擦除机制使得编译器无法确定泛型所对应的真实类型,因此无法直接实例化泛型对象。下面将详细介绍 Java 让泛型实例化的方法。 ...

    Java反射实例源码

    Java反射是Java编程语言中的一个强大工具,它允许运行中的Java程序对自身进行检查并且可以直接操作程序的内部属性。在Java中,反射机制的核心类是`java.lang.Class`,`java.lang.reflect.Method`,`java.lang....

    利用反射,根据类得完全限定名获得该类得实例

    ### 反射技术在Java中的应用:根据类的完全限定名获取其实例 #### 引言 在Java编程中,反射是一种强大的工具,允许程序在运行时检查和修改其结构和行为。通过反射,我们可以根据类的完全限定名(Fully Qualified ...

Global site tag (gtag.js) - Google Analytics