首页 > 编程语言 >[Java基础]Class对象

[Java基础]Class对象

时间:2024-07-19 19:18:22浏览次数:16  
标签:Java 对象 class Method 方法 Class

Class 类

[class对象通常存放在方法区]
在程序运行期间,Java运行时系统始终为所有对象维护一个运行时类型标识。这个信息会跟踪每个对象所属的类。虚拟机利用运行时类型信息选择要执行的正确的方法。不过,可以使用一个特殊的Java类访问这些信息。保存这些信息的类名为Class,这个名字有些让人困惑。

Object类中的getclass()方法将会返回一个class类型的实例。

Employee e;
Class cl=e.getClass();

就像 Employee对象描述一个特定员工的属性一样,(lass对象会描述一个特定类的属性。可能最常用的Class方法就是getName。这个方法将返回类的名字。例如,下面这条语句:System.out.println(e.getClass().getName()+""+ e.getName());如果e是一个员工,则会输出:Employee Harry Hacker如果e是经理,则会输出:Manager Harry Hacker如果类在一个包里,包的名字也作为类名的一部分:var generator = new Random();Class cl= generator.getClass();String name =cl.getName();//name is set to "iava.util.Random'还可以使用静态方法forName获得类名对应的class 对象String className ="java.util.Random";Class cl=Class.forName(className);如果类名保存在一个字符串中,这个字符串会在运行时变化,就可以使用这个方法。如果cassName是一个类名或接口名,这个方法可以正常执行。否则,forName方法将抛出一个检查型异常(checked exception)。无论何时使用这个方法,都应该提供一个异常处理器exception handler)。关于如何提供异常处理器,请参看下一节。

有三种方法可以获得class对象的实例

  • Class.forName(""),传入类名的全限定符
  • 每一个对象都有getClass()方法,例如a.getClass()
  • 直接使用类名.class,例如MyClass.Class

提示:在启动时,包含main方法的类被加载。它会加载所有需要的类。这些被加载的类又要加载它们需要的类,以此类推。对于一个大型的应用程序来说,这将会花费很长时间,用户会因此感到不耐烦。可以使用下面这个技巧给用户一种启动速度比较快的假象。不过,要确保包含 main方法的类没有显式地引用其他的类。首先,显示一个启动画面;然后,通过调用Class.forName手工地强制加载其他类。获得class类对象的第三种方法是一个很方便的快捷方式。如果T是任意的Java类型200Java核心技术卷基础知识或 void 关键字),T.class 将代表匹配的类对象。例如:Class cl1=Random.class;// if you import java.util.*;Class cl2=int.class:Class cl3 = Double[].class;请注意,一个lass对象实际上表示的是一个类型,这可能是类,也可能不是类。例如int不是类,但int.class 是一个(lass 类型的对象。注释:Class类实际上是一个泛型类。例如,Employee.class的类型是Class。我们没有深究这个问题的原因是:它将已经很抽象的概念变得更加复杂。在大多数实际问题中,可以忽略类型参数,而使用原始的(lass类。有关这个问题更详细的介绍请参看第8章。警告:鉴于历史原因,getName方法在应用于数组类型的时候会返回有些奇怪的名字:Double[].class.getName()返回"[Ljava.lang.Double;".int[].class.getName()返回"[I".虚拟机为每个类型管理一个唯一的Class对象。因此,可以利用=运算符实现两个类对象的比较。例如,if(e.getClass()==Employee.class)...如果e是一个Employee实例,这个测试将通过。与条件einstanceofEmployee不同,如果e是某个子类(如Manager)的实例,这个测试将失败。如果有一个class类型的对象,可以用它构造类的实例。调用getConstructor方法将得到-个Constructor类型的对象,然后使用newInstance方法来构造一个实例。例如:var className ="java.util.Random";//or any other name of a class with//a no-arg constructorClass cl=Class.forName(className);Object obj=cl.getConstructor().newInstance();如果这个类没有无参数的构造器,getconstructor方法会抛出一个异常。可以参见5.7.7节了解如何调用其他构造器。

注释:有一个已经废弃的(lass.toInstance方法,它也可以用无参数构造器构造一个实例。不过,如果构造器抛出一个检查型异常,这个异常将不做任何检查重新抛出。这违反了编译时异常检查的原则。与之不同,(onstructor.newInstance会把所有构造器异常包装到一个InvocationTargetException中。C++注释:newInstance方法相当于C++中的虚拟构造器概念。不过,C++中的虚拟构造器不是一个语言特性,而是需要一个专业库支持的习惯用法。(lass 类类似于C++中的type info类,getClass方法则等价于typeid运算符。不过,Java的Class比type info功能更全面。C++的 type info只能给出表示类型名的一个字符串,而不能创建那个类型的新对象。第5章继死201srl java.lang.class 10static Class forName(String className)返回一个 Class 对象,表示名为 className 的类。Constructor getConstructor(Class...parameterTypes)1.1生成一个对象,描述有指定参数类型的构造器。参见5.7.7节更多地了解如何提供参数类型。java.lang,reflect .Constructor 110bject newInstance(0bject... params)将params传递到构造器,来构造这个构造器声明类的一个新实例。参见5.7.7节更多地了解如何提供参数。java.lang.Throwable 1.0void printStackTrace()将 Throwable 对象和堆栈轨迹打印到标准错误流。

利用反射分析类的能力

下面简要介绍反射机制最重要的内容--检查类的结构。在java.lang.reflect包中有三个类Field、Method和Constructor分别用于描述类的字段、方法和构造器。这三个类都有一个叫做 getName的方法,用来返回字段、方法或构造器的名称.Java核心技术 卷204基础知识Field类有一个getType方法,用来返回描述字段类型的一个对象,这个对象的类型同样是Class。Method和Constructor类有报告参数类型的方法,Method类还有一个报告返回类型的方法。这三个类都有一个名为getModifiers的方法,它将返回一个整数,用不同的 0/1位描述所使用的修饰符,如public和static。另外,还可以利用java.lang.reflect包中Modifier类的静态方法分析 getModifiers返回的这个整数。例如,可以使用Modifier类中的isPublic、isPrivate 或isFinal 判断方法或构造器是public、private还是final。我们需要做的就是在 getModifiers 返回的整数上调用Modifier类中适当的方法,另外,还可以利用Modifier.tostring方法将修饰符打印出来。Class类中的getFields、getMethods和getConstructors方法将分别返回这个类支持的公共字段、方法和构造器的数组,其中包括超类的公共成员。class类的getDeclareFieldsgetDeclareMethods和getDeclaredConstructors方法将分别返回类中声明的全部字段、方法和构造器的数组,其中包括私有成员、包成员和受保护成员,但不包括超类的成员。

使用反射在运行时分析对象

从前面一节中,我们已经知道如何查看任意对象数据字段字段的名字和类型:获得对应的 Class 对象。在这个Class对象上调用getDeclaredFields。本节将进一步查看字段的具体内容。当然,在编写程序时,如果知道想要查看的字段名和类型,查看对象中指定字段的内容是一件很容易的事情。而利用反射机制可以查看在编译如isAbstract方法就是检查modifiers值中对应修饰符abstract的二进制位。--译者注日第5章 继死209时还不知道的对象字段。要做到这一点,关键方法是Field类中的get方法。如果f是一个Field类型的对象(例如,通过 getDeclaredFields 得到的对象),obj是某个包含f字段的类的对象,f.get(obj)将返回一个对象,其值为oj的当前字段值。这样说起来显得有点抽象,下面来看一个程序。var harry=new Employee("Harry Hacker",50000,10,1,1989);Class cl=harry.getClass();//the class obiect representing EmployeeField f= cl.getDeclaredField("name");// the name field of the Employee classObject v=f.get(harry);// the value of the name field of the harry object, i.e.//the String object "Harry Hacker'当然,不仅可以获得值,也可以设置值。调用f.set(obj,value)将把对象obj的f表示的字段设置为新值。实际上,这段代码存在一个问题。由于name是一个私有字段,所以get和set方法会抛出一个IllegalAccessException。只能对可以访问的字段使用get和set方法。Java安全机制允许查看一个对象有哪些字段,但是除非拥有访问权限,否则不允许读写那些字段的值。反射机制的默认行为受限于Java的访问控制。不过,可以调用Field、Method或Constructor对象的setAccessible方法覆盖Java的访问控制。例如f.setAccessible(true);//now OK to call f.get(harry)setAccessible方法是Accessible0bject类中的一个方法,它是Field、Method和Constructor类的公共超类。这个特性是为调试、持久存储和类似机制提供的。本节稍后将利用它编写一个通用的 toString 方法。如果不允许访问,setAccessible调用会抛出一个异常。访问可以被模块系统(见卷I的第9章)或安全管理器(卷Ⅱ的第10章)拒绝。安全管理器并不常用。不过,在Java9中,由于Java API是模块化的,每个程序都包含模块。

使用反射编写泛型数组代码

调用任意方法和构造器

在C和C++中,可以通过一个函数指针执行任意函数。从表面上看,Java 没有提供方法指针,也就是说,Java没有提供途径将一个方法的存储地址传给另外一个方法,以便第二个方法以后调用。事实上,Java的设计者曾说过:方法指针是很危险的,而且很容易出错。他们认为 Java 的接口(interface)和lambda表达式(将在下一章讨论)是一种更好的解决方案。不过,反射机制允许你调用任意的方法。回想一下,可以用Field类的 get方法查看一个对象的字段。与之类似,Method类有一个invoke 方法,允许你调用包装在当前Method对象中的方法。invoke方法的签名是:0bject invoke(0bject obj, 0bject... args)第一个参数是隐式参数,其余的对象提供了显式参数。对于静态方法,第一个参数可以忽略,即可以将它设置为null。例如,假设用m表示Employee类的getName方法,下面这条语句显示了如何调用这个方法:String n=(String)ml.invoke(harry);如果返回类型是基本类型,invoke方法会返回其包装器类型。例如,假设m2表示Employee类的 getsalary方法,那么返回的对象实际上是一个Double,必须相应地完成强制类型转换。可以使用自动拆箱将它转换为一个double:double s=(Double)m2.invoke(harry);如何得到 Method对象呢?当然,可以调用 getDeclareMethods方法,然后搜索返回的 Method对象数组,直到发现想要的方法为止。也可以调用Class类的etMethod方法得到想要的方法

它与 getField方法类似。getfield方法根据表示字段名的字符串,返回一个Field 对象。不过有可能存在若干个同名的方法,因此要准确地得到想要的那个方法必须格外小心。有鉴于还必须提供想要的方法的参数类型。getMethod的签名是:此,Method getMethod(String name, Class... parameterTypes)例如,下面说明了如何获得 Employee类的getName方法和raisesalary方法的方法指针Method m1=Employee.class.getMethod("getName");Method m2=Employee.class.getMethod("raiseSalary",double.class);可以使用类似的方法调用任意的构造器。将构造器的参数类型提供给assgetConstructor方法,并把参数值提供给Constructor.newInstance方法:Class cl=Random.class;//or any other class with a constructor that//accepts a long parameterConstructor cons=cl.getConstructor(long.class);0bject obj= cons.newInstance(42L);到此为止,我们已经了解了使用 Method对象的规则。下面来看如何具体使用。程序清单5-18中的程序会打印一个数学函数(如 Math.sqrt或Math.sin)的值的表格。打印的结果如下所示:

public static native double java.lang.Math.sqrt(double)1.00001.00002.00001.41421.73213.00002.00004.00002.23615.00002.44956.00007.00002.64582.82848.00003.00009.000010.00003.1623

当然,打印表格的代码与具体要打印表格的数学函数无关。

double dx=(to-from)/(n-1);for(double x=from;x<=to;x+= dx)double y=(Double)f.invoke(null,x);System.out.printf("%10.4f%10.4f%n",x,y);

在这里,f是一个Method类型的对象。由于正在调用的方法是一个静态方法,所以invoke的第一个参数是 null。要打印 Math.sqrt 函数的值表格,需要将f设置为:Math.class.getMethod("sqrt",double.class)这是Math类的一个方法,名为sqrt,有一个double类型的参数。

这个例子清楚地表明,利用method对象可以实现C语言中函数指针(或C#中的委托)所能完成的所有操作。同C中一样,这种编程风格不是很简便,而且总是很容易出错。如果在调用方法的时候提供了错误的参数会发生什么?invoke方法将会抛出一个异常。另外,invoke的参数和返回值必须是0biect类型。这就意味着必须来回进行多次强制类型转换。这样一来,编译器会丧失检查代码的机会,以至于等到测试阶段才会发现错误,而这个时候查找和修正错误会麻烦得多。不仅如此,使用反射获得方法指针的代码要比直接调用方法的代码慢得多。
有鉴于此,建议仅在绝对必要的时候才在你自己的程序中使用Method对象。通常更好的做法是使用接口以及Java8引人的lambda表达式(第6章中介绍)。特别要强调:我们建议Java开发者不要使用回调函数的Method对象。可以使用回调的接口,这样不仅代码的执行速度更快,也更易于维护。

标签:Java,对象,class,Method,方法,Class
From: https://www.cnblogs.com/DCFV/p/18312229

相关文章

  • java基础学习:序列化之 - kryo
    文章目录一、介绍二、特点三、使用方式四、应用场景五、注意事项一、介绍Kryo是一个快速且高效的Java序列化框架,它主要用于将Java对象转换为字节流以便存储或传输,同时能够将字节流反序列化为原始Java对象。Kryo相比Java自带的序列化机制具有更高的性能和更小的序列化......
  • java基础学习:序列化之 - ObjectMapper
    文章目录一、介绍二、主要功能三、使用方法官网:一、介绍ObjectMapper是Jackson库中的一个核心类,用于在Java对象和JSON数据之间进行转换。Jackson是一个流行的Java库,用于处理JSON数据。它提供了灵活的方式来序列化和反序列化Java对象,即将Java对象转换......
  • 对JAVA的HashMap的深入理解
    今天我们来从源码层面分析JAVA的HashMap底层实现原理,我们还是先从HashMap的构造方法来分析。我们发现HashMap有四个构造方法,首先还是来分析它的无参构造方法,源码如下:这个方法比较简单定义了一个数据成员loadFactor的值,设置为0.75。我们再来看第二个方法HashMap(int)发......
  • 使用 JavaScript 检测大写锁定键(Detect Caps Lock with JavaScript)(转)
    原文地址:DetectCapsLockwithJavaScript-使用JavaScript检测大写锁定ByDavidWalshonFebruary6,2024作者:大卫·沃尔什,2024年2月6日Anyoneiscapableofhavingtheircapslockkeyonatanygiventimewithoutrealizingso.Userscaneasilyspotunwan......
  • JavaScript 基础知识 Day01
    一、计算机基础知识1、计算机数据存储单位位(Bit):1bit可以保存一个0或者1(最小的存储单位)字节(Byte):1B=8b千字节(KB):1KB=1024B兆字节(MB):1MB=1024KB吉字节(GB):1GB=1024MB太字节(TB):1TB=1024GB2、关于JavaScript 它是在1952年2月由网景开......
  • 深入理解Java中的泛型与类型安全
    深入理解Java中的泛型与类型安全大家好,我是微赚淘客系统3.0的小编,是个冬天不穿秋裤,天冷也要风度的程序猿!今天我们将深入探讨Java中的泛型和类型安全。泛型是Java的一个强大特性,它使得代码更加通用、灵活,同时保持了类型安全。1.泛型概述1.1什么是泛型泛型允许我们在定义类、......
  • Java中的内存管理与调优策略
    Java中的内存管理与调优策略大家好,我是微赚淘客系统3.0的小编,是个冬天不穿秋裤,天冷也要风度的程序猿!今天我们将深入探讨Java中的内存管理与调优策略。Java的内存管理涉及多个方面,包括垃圾回收、堆和非堆内存的配置,以及性能优化。通过这些策略,我们可以显著提高应用程序的性能和稳......
  • 使用Java和RabbitMQ构建消息队列系统
    使用Java和RabbitMQ构建消息队列系统大家好,我是微赚淘客系统3.0的小编,是个冬天不穿秋裤,天冷也要风度的程序猿!今天我们将深入探讨如何使用Java和RabbitMQ构建一个高效的消息队列系统。RabbitMQ是一个开源的消息中间件,支持多种消息协议,能够帮助我们实现异步处理和解耦。1.Rabbit......
  • 基于Java和MySQL的数据库优化技术
    基于Java和MySQL的数据库优化技术大家好,我是微赚淘客系统3.0的小编,是个冬天不穿秋裤,天冷也要风度的程序猿!今天我们将探讨如何基于Java和MySQL进行数据库优化,提升系统的性能和稳定性。我们将从查询优化、索引使用、事务管理以及连接池配置几个方面来介绍具体的优化技术。1.查询......
  • Java中的线程池管理与并发性能优化
    Java中的线程池管理与并发性能优化大家好,我是微赚淘客系统3.0的小编,是个冬天不穿秋裤,天冷也要风度的程序猿!今天我们将探讨如何在Java中有效管理线程池,以及如何通过优化并发性能提升应用的效率。线程池是管理线程的一个重要工具,能够提高系统的并发处理能力,并减少线程创建和销毁的......