深入java--类型信息(反射)

news/2024/5/19 3:31:09 标签: Java, 反射, 接口与类型信息

      本节讨论的是Java如何让我们知道如何在运行时知道对象和类的信息。只要方式有两种,一种是传统的RTTI(在运行时,识别一个对象的类型信息),它假定我们在编译时已经知道了所有的类型,另一种是反射机制,它允许我们在运行时发现和使用类型信息。

1Class相当于类的类型一样,无论何时,如果想在运行时使用类型信息,就必须先获得恰当对象的引用(参照第三条String 例子,也是RTTI的例子)。如果你有一个Class对象,还可以使用getSuperclass()方法来查询其直接基类,将返回你可以用来直接查询的Class对象(类),这样就能在运行时发现一个完整的类继承结构,还有可以调用getSimpleName()返回类名,getCanonicalName()返回一个完整的名称(含有包名),getInterfaces()返回一个Class类型的数组。getClass是获得Class对象,也就是一个类。

Class对象就是用来创建所有常规对象的,类是程序的一部分,每个类都有一个Class对象,(更确切的说是被保存在一个同名的.class文件中)。为了运行程序,JVM虚拟机会使用“类加载器”的子系统。原生的类加载器加载的是所谓的 可信类,包括JAVA API类,他们通常是从本地加载的。如果你有特殊的需求,需要以某种特殊的方式加载类,那么你有一种特殊的方式可以加载额外的类加载器。 构造器也是类的静态方法,虽然没有static修饰。

2 Java程序在它开始运行之前不是被全部加载的,它在必需是才会被加载,动态加载的使能行为,在静态加载语言c++是根本不可能做到的。类加载器首先会检查这个类的Class对象是否加载,如果没有,默认的类加载器会根据类名查找.class文件。某个附加类加载器可能会从数据库中查找字节码。一旦某个类的Class对象被载入类存,它就被用来创建这个类的所有对象。

3 jvm会执行静态代码段,你要记住一个概念,静态代码是和class绑定的,class装载成功就表示执行了你的静态代码了。而且以后不会再走这段静态代码了。

Class.forName(xxx.xx.xx) 返回的是一个类  注意类名要完整(包含包名.类名),加载时会加载静态初始化块和静态初始化器
Class.forName(xxx.xx.xx);的作用是要求JVM查找并加载指定的类,也就是说JVM会执行该类的静态代码段

动态加载和创建Class 对象,比如想根据用户输入的字符串来创建对象 
String str = 用户输入的字符串 
Class t = Class.forName(str); 
t.newInstance();

4 使用newInstance()方法时Class并不知道你确切的类型,但是无论如何都会正确的创建,这是实现虚拟构造器的一种途径。注意:使用newInstance创建的类必须拥有默认的构造器。

5 java还提供了一个方法来生成对Class对象的引用(即类字面常量) 例如FancyToy.class 这样做不仅简单而且更安全,因为它在编译时就受到检查(因此不需要放在try语句块中),并且根除了它对forName()方法的调用,所以也更高效。

类字面常量不仅可以用于普通类, 还可以用于数组 接口 基本类型 以及包装类

boolean.class等价于Boolean.TYPE

char.class等价于Character.TYPE

byte.class等价于Byte.TYPE

int.class等价于Integer.TYPE

short.class等价于Short.TYPE

long.class等价于Long.TYPE

float.class等价于Float.TYPE

double.class等价于Double.TYPE

void.class等价于boolean.class等价于Void.TYPE

有一点注意:当使用.class来创建class对象时,不会自动的初始化该class对象 注意是类.class  普通对象是 对象.getClass 比如 int.class User.class   u.getClass

6 为了在Class中使用泛型时放松限制,使用了通配符?,它也是泛型的一部分,表示 任何事物 例如:Class<?>=int.class 注意:Class<?>优于普通不加泛型的class 即便他们是等价的。为了创建一个Class引用,被限定在某种范围,或该类型的任何子类,需要将通配符和extends结合,创建一个范围。例如 Class<? extends User>=user.class

7对于newInstance()方法 加了泛型之后可能返回的不是确定类型 也可能返回的是确定类型

例如:

Class<FancyToy>  ftClass=FancyToy.class;

FancyToy fancyToy=ftClass.newInstance;//这里newInstance出来的就是确切的类型

Class<? extends FancyToy> up=ftClass.getSuperclass;

这里泛型不能直接写FancyToy的父类 要模糊的写  这样也导致了newInstance出来不知道是一个什么类型 不是精确地

Object obj=up.newInstance();

8 反射机制在Java中是用来支持其他跳特性的,例如对象序列化和JavaBean(MVC中的数据库层model)

javaBean在MVC设计模型中是model,又称模型层,在一般的程序中,我们称它为数据层,就是用来设置数据的属性和一些行为,
然后我会提供获取属性和设置属性的get/set方法
9重要P334thinking in java RTTI与反射的真正区别在于:对RTTI来说,编译器在编译时打开和检查.class文件(
当你创建好一个java类的时候,他会编译一次,之后就是在你每次修改这个文件的时候,单击保存或是CTR+S的时候都会编译一次,这是eclipse为自动编译的
),换句话说我们可以用普通的方式调用对象的所有方法;而对于反射的方式, .class文件在编译的时候是不可以获取的,所以是在运行时打开和检查.class文件。(对运行时的理解看第三条的代码String例子)。对于想要运行时运算的动机看第三条的代码String例子和334页的分布式计算,把许多Java对象分布在各处,分为许多的小的计算单元,分布到空闲的机器上运行。

10通常不会直接使用反射工具,但是反射在你要创建更加动态的代码的时候会很有用。

11 一个类,你不写任何构造方法时,会自动存在一个无参构造方法
但是如果你已经写了有参的构造方法了,那么那个默认存在的无参构造方法就不存在了
你如果还需要一个无参构造方法,那就要自己写一个
那为什么一般都需要一个无参构造方法呢?
1)、子类构造方法需要调用父类构造方法,而默认情况下是隐式调用父类无参构造方法,如果父类没有无参构造方法,那就要显式调用一个有参构造方法
2)、反射需要它,反射生成对象时,是调用的无参构造方法,如果没有无参构造方法,就不能反射 newInstance 生成一个该类的对象

10 如果在多态中,B extends A  ,A a=new B() 其中B实现A   a中的方法只有实现的也就是公共的方法,a是一个B.class 也就是B的类型,但是如果想要调用B中特有的方法,必须对a向下转型(强转)才可以调用B特有的方法,

注意:default和private的类及时在import导入包的时候也不能在另外一个包中命名,假设C设一个default的类,在另一个包中C c=...这样是不对的,即使import了,所以此时没办法强制转换,因为连(C)都写不出来(提示不可见),这时候把对应的类改为public就可以或者还有另外一种方法就是通过反射,压制java的安全性检查,在另一个包中来调用C(即使C不是公开的类)中的任何方法,包括private方法。 即使import了也不能使得default的类在其包中被使用,包可以规范命名,还可以使得在不用包使用相同的名字,import进来只是使用其public和protected的部分类,相当于把一系列的类给导入进来的作用。

下面举例:

结构截图:


package typeinfo.interfacea;

public interface A
{
    /*
     * thinking in java page346
     */
    void f();
}

package typeinfo.packageaccess;

import typeinfo.interfacea.*;

class C implements A
{

    @Override
    public void f()
    {
        System.out.println("public C.f()");
    }
    public void g()
    {
        System.out.println("public C.g()");
    }
    void u()
    {
        System.out.println("package C.u()");
    }
    protected void v()
    {
        System.out.println("protected C.v()");
    }
    private void w()
    {
        System.out.println("private C.w()");
    }
    
}

package typeinfo.packageaccess;

import typeinfo.interfacea.*;

public class HiddenC
{
    public static A makeA()
    {
        return new C();
    }
}

package type;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import typeinfo.interfacea.A;
import typeinfo.packageaccess.HiddenC;

public class Test
{
    
    public static void main(String[] args)
    {
        //这是在typeinfo.packageaccess.HiddenC里面调用new C()
        A a=HiddenC.makeA();
        a.f();
        /*
                              这个能成功是因为A时public ,C重写了f方法 动态链接到C的public方法
          a中只有f方法,还没有向下转型,转型之后才有的其他方法
        */
        System.out.println(a.getClass().getName());//a是C类型 但是此时a自己不能C自己特有的方法 只能调用
                                                   //必须向下转型(此时不允许 因为不能在包的外部命名C,default), 只能调用父接口共有的方法(可用)
        /*
         * 导包之后即使原本是default的类仍然是无法可见的
         * 只有public protected才行  包作用一是规范,方便整合    二是在不同包内部可以有相同的类的名字、
         * 此处C是default 在包外是不可见 即使导入包了之后也是,不能在package typeinfo.packageaccess
         * 不能在包的外部命名C
         */
//        if(a instanceof C)
//
//        {
//            C c=(C)a;
//            c.g();
//        }
        //此时可以使用反射解决这个不能向下转型从而调用C自己方法的问题,我们可以通过反射来压制java的安全性检查
        callHiddenMethods(a,"g");
        callHiddenMethods(a,"f");
        callHiddenMethods(a,"v");
        callHiddenMethods(a,"w");

    }
    static void callHiddenMethods(Object object,String methodName)
    {
        try
        {
            Method m=object.getClass().getDeclaredMethod(methodName);
            m.setAccessible(true);
            m.invoke(object);
        }
        catch (IllegalAccessException e)
        {
            e.printStackTrace();
        }
        catch (IllegalArgumentException e)
        {
            e.printStackTrace();
        }
        catch (InvocationTargetException e)
        {
            e.printStackTrace();
        }
        catch (NoSuchMethodException e)
        {
            e.printStackTrace();
        }
        catch (SecurityException e)
        {
            e.printStackTrace();
        }
        
    }

输出:

public C.f()
typeinfo.packageaccess.C
public C.g()
public C.f()
protected C.v()
private C.w()








http://www.niftyadmin.cn/n/916679.html

相关文章

resource

&#xfeff;&#xfeff;linux采用struct resource结构体来描述一个挂接在cpu总线上的设备资源(32位cpu的总线地址范围是0~4G /usr/src/linux-2.6.21.5/include/linux/ioport.h struct resource { resource_size_t start; resource_size_t end; const char …

js 中特殊形势的函数-匿名函数的应用

javascript中的匿名函数&#xff0c;那什么叫做匿名函数&#xff1f; 匿名函数就是没有函数名称&#xff1b;演示代码: 1 <script> 2 function(x,y){ 3 return xy //这个就是一个匿名函数&#xff0c;没有函数名&#xff0c;但是这个函数不能调用。如何调用…

深入java--String(正则表达式)

1 尽量用StringBuilder 不要用String,因为编译器底层会自动帮你用StringBuilder 2 如果想要打印出对象的内存地址&#xff0c;不要在toString用this&#xff0c;这样会产生无限的迭代 public class A {public String toString(){return "A:"this;} }因为编译器看到了…

3.x版本内核中platform_device的生成

转自http://blog.csdn.net/mcgrady_tracy/article/details/42777969 内核版本&#xff1a;Linux-3.18.2 在3.x版本内核中platform_device不再静态定义&#xff0c;而是通过device tree来动态生成&#xff0c;例如&#xff08;arch/arm/mach-s3c24xx/mach-sc2416-dt.c&#xf…

Java中的String.format

package junit.test;import java.util.Date; import java.util.Locale;import org.junit.Test;public class StringFormat {/* String.format()用法1、转换符 %s: 字符串类型&#xff0c;如&#xff1a;"ljq" %b: 布尔类型&#xff0c;如&#xff1a;true %d: 整数类…

深入java--正则表达式

转载地址&#xff1a;http://www.jb51.net/tools/zhengze.html 跳过目录 本文目标如何使用本教程正则表达式到底是什么东西&#xff1f;入门测试正则表达式元字符字符转义重复字符类分枝条件反义分组后向引用零宽断言负向零宽断言注释贪婪与懒惰处理选项平衡组/递归匹配还有…

adf和FB

ADF(Atomic Display Framework)是Google新推出的一个关于Display驱动的框架。 linux 标准的显示框架是基于framebuffer的。

深入java--注解

Java注解1、什么是java注解注解&#xff0c;顾名思义&#xff0c;注解,就是对某一事物进行添加注释说明&#xff0c;会存放一些信息&#xff0c;这些信息可能对以后某个时段来说是很有用处的。Java注解又叫java标注&#xff0c;java提供了一套机制&#xff0c;使得我们可以对方…