码迷,mamicode.com
首页 > 其他好文 > 详细

反射基础

时间:2020-03-19 13:23:21      阅读:71      评论:0      收藏:0      [点我收藏+]

标签:字符   ant   转型   用处   接口   vps   如何   ring   new t   

Java反射机制

其实如果没有一个具体的应用的话我们很难体会到反射的好处

 

一、 认识反射机制:

1.反射机制的概念

Java编程开发之所以会存在有反射机制,最为重要的原因是可以使Java编写代码更加的灵活,而这种灵活如果要想彻底的领悟,那么也需要通过大量的苦练才可以得到,当你已经可以熟练使用反射之后,那么就可以设计出更加结构性强,且可重用性更高

的程序代码,在Java里面存在有很多的开发框架,而之所以可以提供大量开发框架,主要的原因也在于反射机制。

Java的反射机制指的是通过“反方向”的操作来实现类的相关处理,那么既然要有“反”则一定会有“正”,按照传统的开发的行为模式来讲,如果要想进行一个类的操作,那么是需要根据类进行对象的实例化,随后再通过实例化对象实现类中方法的调用

处理,于是现在给出如下代码。

范例:观察传统的类的使用行为

package com. yootk demo;

class Book {

public void read(){

System.out.print1n("认真学习李兴华老师出版的《ava从入门到项目实战》")

}

}

public class YootkDemo{

public static void main(string[ args) throws Exception{

Book book= new Book();//1,首先获取B00K类的实例化对象

book.read();//通过实例化对象进行方法调用。

}

}

 

程序执行结果:认真学习李兴华老师出版的《Java从入门到项目实战》

 

以上的处理操作是一种正向的处理行为,但是如果是反向操作,则就意味着可以根据实例化对象获取相关的信息来源,在Java里面所有的类实际上都属于 Object子类,那么在 Object类中就提供有一个重要的方法,这个方法可以获取“反”的信息:,其实就是得到这个类的Class对象而已

根据来源找到出处

我们现在玩的不是对象层次了,而是比对象更高的层次

 

 

public final Class<?> getClass()

这个方法可以通过类的实例化对象进行调用,并且会返回有一个 Class类的对象的实例

2.继承结构:

 

平时看java源代码的时候,如果碰到泛型的话,我想? T K V E这些是经常出现的,但是有时想不起来代表什么意思,今天整理下: 
? 表示不确定的java类型。
T  表示java类型。
K V 分别代表java键值中的Key Value。
E 代表Element。

3.Class类的重要方法

+getName():String

+getsimpleName(): String

+getTypeName():String

 

 

package com. yootk .demo;

class Book {

public void read(){

System.out.print1n("认真学习李兴华老师出版的《ava从入门到项目实战》")

}

}

public class YootkDemo{

public static void main(string[] args)throws Exception {

Book book= new Book();//1,首先获取Book类的实例化对象

System. out. println(book.getc1ass(). getName());∥/对象所属类的完整名称

System. out. println( book. getclass(). getsimpleName());∥/对象所属类的简化名称(不包含包名称)

System. out. println( book. getc1ass(). getTypeName());//获得对象所属类的类型

}

}

程序执行结果

com. yootk demo Book

Book

com. youk demo. Book

 

 

通过以上的程序就发现,除了对象的正向处理操作之外,那么还可以通过 getclass方法来获取一个类所对应的完整的信息的结构,而这就是反射的开始。

       严格来讲所有的反射之中最为核心的话题就是 Class

二、 Class类对象实例化

在整个的反射处理机制之中, Class类是整个反射处理操作的源头所在,如果现在可以获取 Class类的对象,那么就可以进行所有的更加深层次的反射操作(之前仅仅是根据实例化对象的 Class获取了类的基本名称),在Java的处理机制之中,实际上会有如下的三种方式可以获取 Class类的实例化对象

方式一:Object类中提供有 getClass()方法

由于在 Object类中提供有 getClass()方法,所以任意的实例化对象都可以通过此方法来获取 Class类的对象实例

范例:观察不同类的对象实例获取的 Class对象信息

package com. yootk .demo;

class Book {

public void read(){

System.out.print1n("认真学习李兴华老师出版的《Java从入门到项目实战》")

}

}

 

public class YootkDemo{

public static void main(string[ args) throws Exception{

printclass(book. getclass());

printclass(new java util. Date(). getclass():

}

}

 

 

public static void printclass(Class<?> clazz){

System, out printin("【当前操作的类型】"+c1azz. getName())

}

 

程序执行结果

【当前操作的类型】 com. yootk demo.Book

【当前操作的类型】 java util. Date

 

方式二:“类名称.class”的形式

在Java处理过程之中,可以直接使用“类名称.class”的形式直接在没有产生类实例化对象的时候获取 Class类的实例

package com. yootk demo;

class Book{

public c ass YootkDemo{

public static void main(String[l args) throws Exception {

C1ass<?>c1azzA=Book,c1ass;//属于Java的原生语法支持

C1ass<?>c1azzB=java.uti1.Date.c1ass;//属于Java的原生语法支持

System. out. println(clazzA);

System. out. println(clazzB);

}

    }

 

 

程序执行结果:。

 interface com. yootk demo. IBook

class java util. Date

此吋是直接进行Class类对象的实例出,所以这个时候的信息输出会直接通过 toString()方法来获取相关的对象完整信息

  一旦我们见到xxx.class的时候我们要注意到他返回的是一个Class类对象

 

 

方式三:Class.forName()

在Class类的内部提供有一个根据“类名称”字符串实现类反射加载的处理方法

public static Class<?> forName(String className)throws ClassNotFoundException

在之前获取 Class类对象的情况下都必须获取类本身对应的程序包,但是如果使用了“ Class. forNameo”方法进行 Class类对实例化获取的时候,就可以直接将类名称以字符串的形式定义在程序之中。

 

 

 

范例:通过 for Nameo方法来获取Clas类的对象

package com. yootk demo;

 

public  c1ass YootkDemo{

public static void main(String[ args) throws Exception {

c1ass?>c1azz=C1ass.forName("java,uti1l.Date");//根据类名称实现信息加载

System. out. println(clazz)

}

程序执行结果:. class java util. Date

这个时候就通过字符串实现了类的加载,但是需要明确的是,以上的这几点处理语法在整个项目的实际开发过程之中全部都有可能使用到,不可能做一个优先级区分。

 

三、用反射实例化类对象

现在通过了一系列的分析口经可以得到三种实例化Class类对象的方法,但是不理解的是,为什么我们要分析这三种方式,或者是为什么要获取 Class类的实例化对象呢?

在Java之中如果要想产生一个类的实例化对象,那么一点你给要通过关键字new进行构造方法的调用,随后再通过该对象进行具体的类的结构操作,可是除了这种关键字new之外,如果此时已经获得了Class类的对象实例,那么就可以通过 Class类的如下方法来实现类对象的实例化处理

@Deprecated(since=9)

public T newInstanceO throws Instantiation Exception, IllegalAccess Exception

之所以在 newlnstance()方法上面追加有一个“@ Deprecated”不是说这个方法有问题,而是说这个方法描述的含义不清晰,但是在JK1.8及以前的版本肯定还是使用 newInstance()方法完成。但是是在JDK1.9之后推荐的做法变更为

clazz. getDeclaredConstructor(). newInstance()

 

 

 

传统的实例化

package com. yootk demo

class book{

public Book{

System. out. println("【Book】实例化新的Bok类对象")

}

}

public class yootkdemo{

public static void main(string[] args)throws Exception {

c1ass<?> book1Clazz=C1ass. forName("com. yootk. demobook");实例化Book类的对象

bookclazz. newInstance();//在JK1.8的时候依然可以正常使用

}

}

程序执行结果:

【Book】实例化新的Book类对象

通过以上两个代码的对比可以发现,使用关键字new或者使用反射机制中提供的 newInstanceo两个方法都可以实现指定类对象实例化处理,这样就意味着从此之后可以不局限于关键字“new”的使用了,但是在JK1.9之后传统所使用的 newInstance()方法已经变为了一个不推荐使用的方法了,所以以上的代码可以进行如下的变更。

 

 


JDK1.9推荐的实例化

package com. yootk demo

class book{

public Book{

System. out. println("【Book】实例化新的Bok类对象")

}

}

public class yootkdemo{

public static void main(string[] args)throws Exception {

c1ass<?> book1Clazz=C1ass. forName("com. yootk. demobook");实例化Book类的对象

bookclazz.getDeclaredConstructor(). newInstance();

//在JK1.9之后的做法

}

}

程序执行结果:

【Book】实例化新的Book类对象

 

在JDK1.9之后如果要想通过Class类对象获取其他类的实例,那么就需要进行方法的更换,但是有另外一点需要注意的是,当通过 Class类对象获取指定类实例的时候, newInstance()方法所返回的数据类型为 Object,那么这个时候就需要进行一些对象的

向下转型处理(对象的向下会存在有安全隐患)这个安全隐患是什么:

 

范例:通过反射获取指定类的对象实例


JDK1.9推荐的实例化

package com. yootk demo

class book{

public read{

System. out. println("认真学习李兴华老师出版的《Java从入门到项目实战》")

}

}

public class yootkdemo{

public static void main(string[] args)throws Exception {

c1ass<?> book1Clazz=C1ass. forName("com. yootk. demobook");实例化Book类的对象

Book book = (book)bookclazz.getDeclaredConstructor(). newInstance();

//在JK1.9之后的做法

 

book.read();

}

}

程序执行结果:

"认真学习李兴华老师出版的《Java从入门到项目实战》"

但是需要注意的是,虽然以上的操作可以通过向下转型获取指定类型的对象实例,但是这种操作的代码是存在有设计上问题的,之所以使用反射很大的程度上是不希望进行完整类信息的导入(为什么我们不希望导入包呢),但是如果仅仅是按照如上的方式进行处理,那么如果真的有些其他包的类,则依然会出现导入包的情况。

范例:观察反射实例化对象强制转型所带来的问题

 

package com. yootk demo;

import java.util.Date;

public class yootkdemo{

public static void main(string[] args)throws Exception {

c1ass<?> clazz=C1ass. forName("java.util.Date ");实例化Book类的对象

Date date =(Date)clazz.getDeclaredConstructor (). newInstance ();

System. out. println(date. getTime)

 

}

}

程序执行结果:1582166868404

我们这里进行一个强制的向下转型的话就会出现导入Date包,那么既然导入了Date包那么我为什么不直接new一个Date类的实例呢,这样显然更快,更便捷

 

当有了反射机制的支持之后,那么就可以得到第二种对象实例化的方案,而这种方案之中主要依靠的是 Class完成。

 

如果两种方式都要明确 的导入所需要的包的话,很明显这不是反射的优势,所以在下面我们还是要逐步的分析我们到底能用反射能干什么,但是至少反射给了我们第二种实例化对象的方式。我们要是能吃透第二种选择创建实例化对象的时候我们就能自定义我们的开发框架了,

 

 

四.反射与工厂设计模式

经过了分析之后就发现通过反射可以获得类的实例化对象,但是现在就需要去思考为什么要提供反射的机制来获取实例化对象,或者说如果直接使用关键字new有什么问题吗?如果要想回答这个问题最佳的做法是通过工厂设计模式来进行分析。

范例:传统的工厂设计模式

 

 

所用的工厂设计模式都有一个公共的interface或者是一个抽象类

所有的工厂都继承一个公共的父类(接口、抽象类)

package com. yootk demo;

interface IBook{//接口

public void read();

}

class MathBook implements IBook{

public void read(){

System.out. printin("【 MatcHbook】认真学习大学数学课程(线性代数、概率统计、离散数学).");

}

}

class ProgramBook implements IBook

public void read(){

System.out. print1n("【 ProgramBook】认真学习李兴华老师出版的《ava从入门到项目实战》.");

}

}

 

class Factory{

private Factory(){};//工厂的构造方法私有化,只对外提供一个getInstance实例的方法,这个好像单例模式啊,但是这个确实看着不是单例模式啊

public static IBook getInstance(string className){

if ("math". equalsIgnoreCase(className)){

return new MathBook();

}

else if  ( “program".equalsIgnoreCase(className)){

     return new ProgramBook();

}else{

    return null;

}

 

 

 

public class YootkDemo

/李兴华编程训练营: youth.ke.qq,com

public static void main(string[ args) throws Exception {

    IBook book= Factory getInstance( className: "program");

book.read();

}

 

 

 

 

 

工厂设计模式的好处是什么:工厂设计模式在我们最终进行使用的时候我们所有的操作的子类。最终结果都是由我们的Factory最终结果都是由我们的Factory来进行包办的,如果把所有的子类进行隐藏的话,

那么我们的当前客户端获取对象的话就是使用的当前的给定的处理机制:由我们的Factory包办所有子类对象的返回,客户端(就是我们的测试类)在最终进行处理的时候是基于我们的Factoty类对象,而在整个设计的时候,我们的Factory类对根我们的接口有关,Factory类会去帮我们匹配所有的操作的子类,这就是我们现在能见到的工厂

 

以上的程序为一个最为传统的静态工厂设计模式,利用 Factory类可以对外部隐藏所有book接口的子类,这样的优势就在于避免不同层的类与具体接口子类耦合。

但是如果说此时 IBook接口里面会提供有10W个子类呢?那么请问,这个时候的 Factory类还能写吗?如果你有耐心会在Factory. getlnstance()方法里面加入10W个判断,如果真的加入了10W个判断,那么程序的执行时间复杂度一定会飙高。

所以这种传统的静态工厂类是不可能满足现实的项日开发要求的,最佳的做法要采用动态工厂类,反射机制就可以登场了

在使用反射操作的时候只需要根据字符串(类名称)获取 Class类的实例化对象之后就可以直接反射实例化对象处理,这样的操作最适合于完成工厂设计的改良

 

 

 

所用的工厂设计模式都有一个公共的interface或者是一个抽象类

所有的工厂都继承一个公共的父类(接口、抽象类)

package com. yootk demo;

interface IBook{//接口

public void read();

}

class MathBook implements IBook{

public void read(){

System.out. printin("【 MatcHbook】认真学习大学数学课程(线性代数、概率统计、离散数学).");

}

}

class ProgramBook implements IBook

public void read(){

System.out. print1n("【 ProgramBook】认真学习李兴华老师出版的《ava从入门到项目实战》.");

}

}

 

class Factory{

private Factory(){};//工厂的构造方法私有化,只对外提供一个getInstance实例的方法,这个好像单例模式啊,但是这个确实看着不是单例模式啊

public static IBook getInstance(string className){

   private Factory(){

}

public static getInstance(String className){

   try{

      Object obj = Class.forName(className);

      if(obj instanceof iBoook){

         return (iBook)obj;

}else{

   return null

}

   }catch(Exception e){

     return null;

}

}

}

 

public class YootkDemo

/李兴华编程训练营: youth.ke.qq,com

public static void main(string[ args) throws Exception {

    IBook book= Factory getInstance( className: "com.yootk.demo.program");

book.read();

}

 

【 ProgramBook】认真学习李兴华老师出版的《Java从入门到项目实战》

工厂类不与我们的子类产生直接的联系

那么紧接着我们在传递参数的时候就要传完整的包名+类名,在我们的动态工厂模式下面,我们的工厂类就没有严重的耦合问题了,工厂类完全变成了一种独立的操作模式

 

 

通过我们的两种工厂的实现模式我们就可以深刻的体会到使用反射机制的好处

五、反射与单例设计模式

单例设计模式也是在Jaⅶa开发中比较常见的一种设计模型,这种设计模型的最重要的特点在于:在一个JM进程之中某一个类只允许提供有唯一的一个实例化对象,同时单例设计模式也分为两种:饿汉式、懒汉式,在面试中最为常见的问题就是懒汉式的单例设计模式,因为这里面与多线程息息相关。

范例:观察懒汉式单例设计模式所存在的问题

package com. yootk demo

c1ass singleton{∥/单例设计类

private static singleton instance;//不进行对象实例化

private singleton(){//构造方法私有化

System. out.printin("【"+ Thread. currentthread() getName()+"】实例化 singleton类的对象。");

}

}

public string tostring(){

return"【VIP】李兴华编程训练营: yootkke.qq.com”;

}

pub1ic static singleton getInstance(){//获收 Singleton类实例化对象

if( instance==nu11){//现在还没有实例化

instance= new singleton();//对象实例化

}

  return instance;

   }

}

 

public class YootkEmo{

public static void main(string[] args) throws Exception{

for(intx=0;Ⅹ<18;ⅹ++){

Singleton singleton= singleton. getInstance();获取实例化对象

System. out. printIn(singleton);

}

}

执行结果

【main】实例化 Singleton类的对象    //我们的主线程在执行实例化对象的操作

【vIP】李兴华编程训练营:yootk.ke.qq.com

【vIP】李兴华编程训练营: yootk.ke.qq,com

【vIP】李兴华编程训练营: yootk.ke.qq,com

【vIP】李兴华编程训练营:yootk.ke.q.com

【vIP】李兴华编程训练营: yootk,ke.qq,com

【vIP】李兴华编程训练营: yootk ke.qq,com

【vIP】李兴华编程训练营: yootk.ke.qq,com

【VIP】李兴华编程训练营:yootk.ke.qq,com

【ⅥIP】李兴华编程训练营: yootk,ke.qq,com

【vIP】李兴华编程训练营:yootk.ke,qq.com

问题来了:我们当前的操作是一个单线程的操作,那么如果是一个多线程的操作呢会发生什么样的情况呢??

public class YootkEmo{

public static void main(string[] args) throws Exception{

for(intx=0;Ⅹ<18;ⅹ++){

new Thread(()-->{

 Singleton singleton= singleton. getInstance();获取实例化对象

System. out. printIn(singleton);

},name:”单例线程-”+x).start();//开启多个线程,执行的先后顺序很短暂,CPU太快了

}

}

执行结果:

【单例操作线程-9】实例化 Singleton类的对象。

【VP】李兴华编程训练营:yootk.ke.qq.com

【单例操作线程-6】实例化 Singleton类的对象。

【VIP】李兴华编程训练营: yootk ke go,com

【单例操作线程-3】实例化 Singleton类的对象。

【VP】李兴华编程训练营:yootk.ke.qq.com

【单例操作线程-5】实例化 Singleton类的对象。

  我们在多线程的环境中会发现有多个实例化的对象,单例模式失效了

按照最初的设计来讲,此时采用的应该属于单例设计模式,所以 Singleton类的对象实例应该始终只保持一个,可是最终执行后的结果会发现构造方法被重复调了多次,每当调用一次构造方法就意味产生了一个新的 Singleton类的实例化对象。

当我们将结构图的操作代码放在多线程的环境来讲:

在整个的程序过程中,对于当前对象的获取的操作结构来讲,我们会有多少个线程同时进入到当前方法的内部呢:

getInstance()方法是多个线程共享的方法

多个线程的环境下,我们的判断条件来不及判断,那么这些线程就会通过我们的判断条件,创建我们的Instance实例对象

多线程的环境下我们的线程一拥而上,此时的Instance还是null,那么就会出现线程同步的问题:我们的构造单例对象的方法就会失效

 

 

然此时出现有线程不同步的问题,那么所需要解决的方式一定是围绕着线程的同步,那么一旦要想采用同步的方式进行处理,传统的风格应该在方法中加入同步操作。

范例:直接通过同步来解决问题

pub1ic static  synchronized singleton getInstance(){//获收 Singleton类实例化对象

if( instance==nu11){//现在还没有实例化

instance= new singleton();//对象实例化

}

  return instance;

   }

 

程厅执行结果

【单例操作线程-0】实例化 Singleton类的对象

【ⅤP】李兴华編程训练营:yootk.kc.qq.com

【VIP】李兴华编程训练营

缺点:

  因为此时仅仅是为 Singleton类对象实例化的时候才需要同步一次,这个时候发现不仅仅是对象实例化,同时所有的调用也都需要同步,最终的形式就会影响到程序的执行性能。

我们减少对象的产生的目的其实就是了减少我们的频繁的new操作创建对象,我获得对象的操作绝对是没有同步的意义的,在整个过程中真正由必要进行同步操作的话其实是这一部分

我们只需要对这个操作结构进行同步,而不需要对我们的方法进行同步,所有的操作线程都采用并发的形式来进入到我们的这个方法中,但是我们在执行判断的时候我们需要进行一个同步处理:

 

 

 

范例:懒汉式单例设计模式的正确使用姿势范例:懒汉式单例设计模式的正确使用姿势

pub1ic singleton getInstance(){//获收 Singleton类实例化对象

if( instance==nu11){//现在还没有实例化

synchronized(Singleton.class){

   if(instance == null){

       instance= new singleton();//对象实例化

}

}

}

  return instance;

   }

为了保证性能我们还要在

   private static Singleton instance中加上volatle关键字

   private static volatle Singleton instanc

 

 

多个线程的环境下面可能由任何一个线程来实例化我们的单例对象,但是实例化的单例对象有且只有一个

在以后实际面试过程之中,懒汉式的单例设计模式一定是被重点关注到的技术性问题,因为这里面牵扯到同步正确处理的方式,如果在方法上同步,则所有的操作都会同步,所以此时需要进行局部的同步处理

 

 反射学完之后我们就可以改进很多的设计模式

 

 

六、反射机制与类的结构操作

       反射机制除了可以通过 Class类的方式获取一个类的实例化对象之外,其最大的特点还可以实现整个类结构的剖析,例如:某一个类对应的父类、它所实现的父接口、类中的构造方法、成员属性或者普通方法等等。

6.1获取类结构信息

如果要进行一个类的定义,那么在这个类中往往会存在有程序所处的包、一个类所继承的父类或者是相关的实现接口,那么这些信息都可以通过 Class类直接获取,在 Class中提供有如下的几个方法

No

方法名

类型

描述

1

public Package getPackage()

普通

获取指定类的所在包

2

public Class<?>getSuperClass()

普通

获取实现父类(不包含爷爷类)

3

public Class<?>[] getInterfaces()

普通

获取实现的全部接口

4

publix String getPackageName();

普通

获取类所在的包的名称

AnnoationElement 这个interface里面含有获取注解信息的一些方法

我们的Package类实现了AnnoationElement这个接口,我们在学习反射的时候里面有一项重要的功能就是注解元素的获取

 

按照我们目前的程序结构来说:我们只能获取到我们的类的基础的继承结构

 

 

 

如果要想获取这些类的结构信息,那么最佳的做法肯定是要有一组相关的程序类存在,那么如果要想有这样的程序类存在的话,自己来手工定义

 

 

 

范例:定义要使用的基础类结构

interface IBook {}

interface ISpec {}

abstract class AbstractPrint {}

class Book extends AbstractPrint implements IBook, ISpec {}

 

public class Yoot kemo{

public static void main(string[]args) throws Exception {

Class<?> clazz= Class forName(com. yootk demo Book")j

System. out. println(c1az. getPackage();∥返回Package对象的实例 Package类

System. out. println(clazz. getPackageName());返回包名 String类

 }

}

 

 

程序执行结果:·

package com. yootk demo

com. yootk demo

 

 

一个类可以继承一个父并且同时实现若个接口,此时就可以利用继承关系的逻辑获取这些相关的信息

范例:获取继承父类和实现接口

interface IBook {}

interface ISpec {}

abstract class AbstractPrint {}

class Book extends AbstractPrint implements IBook, ISpec {}

 

pubIIc class Yootk{

public static void main(string args)throws Exception{

Class<?> clazz =Class forName("com. yootk demo Book");

Class<?> superclazz=c1azz.getsuperclass();//获取父类

System.out, print1n("【继承父类】"+ superc1azz, getName())

System.out, print1n("【继承父类】"+ superc1az, getsuperc1ass(). getName()

Class<?> inters = c1azz.getInterfaces();//获取实现的接口;

for(Class<?> temp:inters){

  System. out.print1n("【实现接口】"+temp. getName();

}

}

}

【继承父类】com.yootk.demo. AbstractPrint

【继承父类】java.1ang. object

【实现接口】com.yootk.demo. IBook

【实现接口】com. yootk.demo. ISpec

在整个的程序之中利用class这种处理结构,就可以持续进行当前类继承关系的剖析,这样的操作的结构(就是我们的main方法)是指上就是对已有类执行反射的处理过程。之所以现在可以实现这样的功能,主要的原因是针对于生成的二进制字节码文件进行的处理。

七、反射调用构造方法:

在一个类中会存在有若干个构造方法的信息,那么这样就在 Class类里面可以基于反射机来获取一个类中全部已经存在的构造方法,具体的操作方法如下

 

 

No

方法名

类型

描述

1

public Constructor<T> getConstructor(Class<?>…parameter Types) throws NoSuchMethodException

普通

根据指定参数类型获取指定构造方法对象。,

只可以获得public访问权限定义的构造方法

2

public Constructor<?>[] getConstructors() throws SecurityExceptioon

普通

获取类中的全部构造方法只可以获得public访问权限定义的构造方法

 

3

public Constructor[T]  getDeclaredConstructor

(Class<?>..parameters) throws

NoSuchMethodException,SecurityException

普通

获取类中指定参数类型构造方法对象,可以获得类中全部的构造方法,不区分权限

4

public Constructor<?>[] getDeclaredConstructors() throws SecurityExceptioon

普通

获取全部的构造方法可以获得类中全部的构造方法,不区分权限

 

可以发现在 Class类中对于构造方法信息的获取分为了两组的方法名称,那么这两组的名称一定是有区别的。

范例:获取构造方法

package com. yootk demo;

class Book{

//Book类中的构造方法使用了不同的访间权限

protected Book (string title) t

Book(string title, string author);

private Book(string title, String author, double price);

}

pub1ic class yootkdemo{

public static void main(string [] args) throws Exception{

Class<?> clazz =Class. forName("com. yootk demo Book")

System. out. println ("-------------- getconstructors()获取构造-----“);

for (Constructor<?> constructor: clazz .getconstructors()){

System. out println("【1-构造信息】"+ constructor);

}

for (Constructor<?> constructor: clazz. getDeclaredConstructors ()){

   System. out println("【2-构造信息】"+ constructor);

}

}

}

 

结果:

-----------------getConstructors()获取构造---------------------------------

【1-构造信息】 public com. yootk,demo.Book()

---------------- getDeclaredConstructors()获取构造-------------------

【1-构造信息】 private com. yootk. demo, book(java.lang. string,java.1ang. string, double)

【1-构造信息】com, yootk, demo, book(java.1ang. string,java.lang. string)

【1-构造信息】 protected com. yootk demo Book(java.1ang. string)

【1-构造信息】 public com, yootk, demo, book()

 

当通过 Class类对象实例获取 Constructor类对象之后,会返回 java. lang reflect.Constructor类的对象实例,而这个类就属于Java中的反射操作类。

 

 

在Java反射机制之中,每一个 Constrcutor类对象实例实际上都会描述一个对应的构造方法信息,于是可以使用 Constructor类中的如下方法进行构造方法的处理。

No

方法名

类型

描述

1

public  String getName()

普通

获取构造方法的名称

2

public int getParamCount()

普通

获取方法中的参数个数

3

public TypeVariable<Consrtuctor<T>>[]

普通

获取构造方法参数的类型

4

public T newInstance(Object…initargs)throws

Instantiation Exception, IllegalAccess Exception

IllegalArgumentException,

InvocationTargetException

普通

调用构造方法进行对象的反射实例化

获取构造方法的主要目的是进行指定有参构造的对象实例化处理操作,所以这里面最为重要的方法就是 newInstance()

范例:调用类中的有参构造方法进行类对象的实例化

 

 

 

 

 

import java. lang reflect .Constructor

class book{

private string author

private double price;

public Book(string titler, string author, double price){

this. title= title

this. author= author

this price= price

}

}

//本类中的无参构造、 setter、 getter方法,略,

public string tostring (){

return"【Book】图书名称:"+this.tit1e+"、图书作者:"+this. author+"、图书价格:"+this. price

}

 

public class YootkDemo{

public static void main(stringl args) throws Exception{

class<?>clazz class .forName(" com. yootk demo Book””);

Constructor<?> constructor clazz getDeclaredConstructor(string class, String class, double class);

object obj= constructor. newInstance( initargs:"Java从入门到项目实战","李兴华",99,8)}//反射对象实例化

System. out. println(obj)

}

}

 

程序执行结果:.【Book】图书名称:Java从入门到项目实战、图书作者:李兴华、图书价格:99.8

JDK1.9之后 Class类中的 newInstance()方法不再推荐使用,主要是newInstanc()方法需要我们被实例化的类要有一个无参数的构造方法,并且给出了构造方法的调用形式:

clazz.getDeclaredConstructor().newInstance();

获得指定类的无参构造,并且调用 Constructor类中的 newInstance()方法实现类对象实例化(调用的为无参构造方法)

 

在使用反射的时候我们都提倡使用无参构造来使用

八、反射调用方法

在一个类中除了构造之外还会存在有许多类中提供的方法,那么在这种情况下,所有的方法信息也是可以通过 Class类的对象反射获取的,使用如下的方法获取即可

No

方法名

类型

描述

1

public Method getMethod(String name,Class<?>… parameterTypes) throws NoSuchMethodException.Security Exception

普通

获取类中的public访问权限定义的指定方法

2

public Method[] getMethods() throws SerurityException

普通

获取类中所有定义的public方法

3

public Method getDeclaredMethod(String name,Class<?>…parameterTypes) throws NoSuchMethodException,SerurityException

普通

获取本类中的指定参数的方法,不区分访问控制权限

 

不能获取父类中定义的方法,即使你继承了

4

public Method[] getDeclaredMethods()throws SecurityException;

普通

获取本类中所有的方法(不区分访问控制权限)

不能获取父类中定义的方法,

 

 

很显然Declared是声明的意思,那么在哪儿声明呢,显然是在我们的本类的声明才行

 

 

范例:

package com. yootk demo

import java. lang reflect method

class book{

public void read() throws RuntimeException{}

}

 

public class YootkDemo{

public static void main(String [] args){

Class<?> clazz =Class. forName(“com. yootk demo Book”);

Method[]  methods = clazz. getMethods);//获取类中所有定义的public修饰的方法

for(Method method methods){

   System. out. printin(method);

}

}

}

public final native void java. lang Object. wait(long )throws java. lang InterruptedException

public final void java. lang Object. wait(long, int )throws java.lang Interrupted Exception

public final void java. lang Object wait() throws java. lang InterruptedException

public boolean java. lang Object. equals(java lang Object)

public java. lang String java. lang Object toString()

public native int java.lang Object. hashCode()

public final native java. lang Class java lang Object. getclass()

public final native void java. lang Object notify()

public final native void java. lang Object. notifyAll()

 

在Java反射机制运行过程之中,每一个方法都通过 Method类的对象实例来进行包装,这种情况下如果要想进行方法更深入层次的调用,就需要去研究 Method类的重要组成

 

Constructor类和Method类都是属于Java.lang.reflect包下面的程序类,Class类只整个反射的钥匙和基石

 

 

 

在Method类中有如下几个重要的方法来供我们使用:

No

方法名

类型

描述

1

public Class<?>getReturnType()

普通

获取方法的返回值类型

2

public Type[] getGenericParameterTypes()

普通

获取方法的参数类型

3

public Object invoke(Object obj,Object ..args) throws IlleagleAccessException.

IlleagleArgumentException.

InvocationTargetException

 

普通

方法的调用

4

public abstract int getModifiers()

普通

方法的修饰符

下面首先来研究一下如何可以根据反射获取类中的全部的方法的信息内容,在之前进行方法内容输出的时候所采用的方式是由 Method类提供的 toString方法完成的,但是现在希望可以自己实现所有方法信息的获取

范例:获取类中的方法信息

在程序设计之中,所有方法前所追加的修饰符( public、 private、 static、 synchronized、 abstract等)在程序里面都会有相应的

权限编码存在,如果要想将这些编码转换为可以读懂的信息需要通过java. lang reflect Modifier完成。

package com. yootk demo

import java. lang reflect method

class book{

public void read() throws RuntimeException{}

}

 

public class YootkDemo{

public static void main(String [] args){

Class<?> clazz =Class. forName(“com. yootk demo Book”);

Method[]  methods = clazz. getMethods);//获取类中所有定义的public修饰的方法

for(Method method : methods){

   System. out. print(Modifier.toString(method.getModifiers()));

   //获取返回值类型

   System.out.print(method.getGenericReturnType().getTypeName());

   //获取方法的名称

   System.out.print(method.getName()+”(”);

Type[] parameterTypes = methods.getGenericParameterTypes();

for(int I = 0; i<parameterTypes.length;i++){

  System.out.print(parameterTypes[i].getName()+”  arg”+i);

   if(i<parameterTypes.length-1){

      System.out.print(“,”);

}

}

System.out.print(“)”);

 

Type[] exceptions = method.getGenericExceptionTypes();

if(exceptions.length>0){

  System.out.print(“throws exceptions”);

for(int I = 0; i<exceptions.length;i++){

   System.out.print(exceptions[i].getName());

   if(i<parameterTypes.length-1){

      System.out.print(“,”);

}

       }

}

 

 

System.out.println();

}

}

}

这个处理操作我们就返回了这个类的全部方法

我们在使用IDE开发工具的提示功能的时候就是基于这样的原理

在实际的开发中我们使用Method类最大的用途并不是进行方法结构的剖析,Method方法的最大缺陷就是无法获得具体的参数名,而我们最大的用途是实现方法的反射调用

 

 

 

 

 

范例:反射的方法调用

 

package com. yootk demo;

class Book{

private string title ;

public void setTitle(string title){

this title= title;

}

public string getTitle{

return title

}

}

public class YootkDemo{

public static void main(string[ ] args) throws Exception{

string fieldName=" title";//明确给出了成员的性名称

String fieldvalue="Java从入门到项目实战";//成员属性的内容

class<?> clazz =Class. forName("com. yootk demo Book");

//1、如果要想通过B0Ok类

object object=clazz. getDeclaredconstructor(). newinstance();/反射对象实例化

//2、要找到指定调用的 setter、 getter方法

Method setMethod = clazz.getMethod(name:”set”+initCap(fildName),String.class);

// 3、利用反射结合对象实例(不是具体的B0Ok类,而是0bject类型同时传入所需要的参数,得到的是Method类型的对象

setMethod.invoke(object,filedValue);

 

//获得getter方法的实例,传入我们的方法名,getter方法没有参数传入

Method getMethod = clazz.getMethod(name:”get”+fileName);

//传入我们的实例对象,表示我们调用的是哪一个对象的方法。以及我们要传递的参数,其实没有那么神奇,很多看着高深的手段其实他的实现逻辑和简单的实现逻辑是一样的

Object value = getMethod.invoke(object);

System.out.println(vlaue);

 

 

}

public static String initCap(String str){

if (str = =nul||””. equals(str)){

return str

}

if(str.length()==1){

return str. toUppercase();

}

else{

return str.substring(0,1).toUppercase()+ str .substring(1)

}

}

}

}

执行结果:Java从入门到项目实战

之所以使用如上的形式代替掉传统的关键字new以及明确的“对象。方法()”调用形式,本质上来讲就是为了进行解耦和设计。

 

九:反射调用成员属性:

类中除了提供有构造还有方法之外,最为重要的概念就是属性,因为在不同的对象里面所保存的内容就属于属性的信息,属性严格来讲在Java中称为成员,所以如果要想获得所有成员的信息,就需要通过 Class类的对象来完成

No

方法名

类型

描述

1

public Filed[] getFileds[] throws SecurityException

普通

获取所有的继承而来的public成员

2

public Filed getFiled(String name) throws NoSuchFiledException,SecurityException

普通

获取一个指定名称的成员

3

public Filed[] getDeclaredFileds()throws SecurityException

普通

获取本类中的全部成员

4

public Filed getDeclaredFiled(String name)throws NoScuhFiledException ,SecurityException

普通

获取本类中指定名称的成员对象

 

 

 

 

 

 

 

 

对于成员来讲我们一定分为本类成员、父类成员、以及接口中的常量成员等信息。那么接下来我们就来演示如何获取类中的成员数据:

public class StringUtil{

   public static String initCap(String str){

if (str = =nul||””. equals(str)){

return str

}

if(str.length()==1){

return str. toUppercase();

}

else{

return str.substring(0,1).toUppercase()+ str .substring(1)

}

}

}

}

 

package com. yootk demo

interface IBook{

public static final String FLAG="沐言优拓:ww,yootk.com”;

}

Abstract class AbstractBook implements IBook{

protected  String type="编程教育类图书

private String company=”沐言科技软件教育学院”

}

class ProgramBook extends AbstractBook{}

private string tit1e;//图书名称

}

 

public class Yoot{

public static main(string[ ] args) throws Exception{

C1ass?>c1azz= Class. forName("com. yootk.demo. ProgramBook");//获取操作类

System. out. printin(“------------getFields())执行结果--------“);

for( Field field:c1azz. getFields()){获取继承而来的public的成员,而不是我们本类中的

System.out. println("【1-成员】"+ field);

}

System. out. printin(“------------getDeclaredFields()执行结果------------”);

for( Field field:c1azz. getDeclaredFields()){//获取操作类(本类)定义的成员

System.out. println("【2-成员】"+ field);

 }

}

}

 

---------------getFields()执行结果---------------

【1-成员】 public static final java.1ang. string com. yootk.demo. IBook FLAG

------getDeclaredFields()执行结果----------------

【2成员】 private java.1ang. string com. yootk.demo. ProgramBook. tit1e

在实际项目开发过程之中,如果使用反射进行处理的时候,一般来讲都会采用“ getDeclaredFields()、getDeclaredField()”方式来获取本类的操作属性(即便属性使用了 private封装也可以返回)所有的成员在Java中都使用 Field类型来进行描述,来观察Field类中的值

Fild类中的常用方法

No

方法名

类型

描述

1

public Objcet get(Object obj )throws IlleagleArgumentException.IlleagleAccessException

普通

获取指定成员的内容

2

public void set(Object obj, Object value) IlleagleArgumentException.IlleagleAccessException

普通

设置成员的属性内容

3

public String getName()

普通

获取成员名称

4

public Class<?>getType()

普通

获取成员类型

5

public void setAccessible(boolean flag)

 

设置封装的可见性

 

 

 

 

 

如果在一个类的对象里面要进行成员的操作,那么一定要首先获取本类的实例化对象而后才可以进行,在 Field类中就直接提供有set()方法设置属性,get()方法获取属性的操作

 

范例:通过 Field实现属性的直接操作

import java.lang.reflect.Filed

class Book{

  private String title;

}

 

public class YootkDemo{

  public static void main(String [] args){

Class clazz= Class.forName(“com.Yootk.demo.Book”);

Object obj =  clazz.getDeclaredConstructor().getInstance();  

Filed filed = clazz.getDeclaredFiled(“title”);

fild.setAccessible(true);

fild.set(obj,”李兴华老师的Java入门到项目实战”);

}

 

我们一定要分清Class对象能做什么。还有就是我们获取到了Class对象之后如何和获取我们的Constructor对象。如何获取我们的Method对象,如何获取我们的Fild类型的对象,这些类型或者是对象里面各自有什么样的方法是要注意的,这些一定要注意

封装都是骗人的。我们在反射中可以轻而易举的的取消掉我们的封装

以上的程序是直接实现了成员属性的反射调用,在进行调用处理的时候由于Book类中的ttle属性使用了 private封装,那么就必须通过 setAccessibleO方法取消掉封装,但是如果从实际的开发来讲,对于属性的操作一般还是建议通过 setter, getter方法。Fild类只是表示我们具有这样的能力,但是我们并不推荐这么使用

 

十、Unsafe工具类(一般来讲不建议使用)

java. langreflect本身所描述的是一种反射的基本操作功能,除了这个基本的功能之外,在JDK里面还提供有一个比较特殊的反射类: sun. mIsc. Unsafe(按照Java开发的原则来讲,所有以“sun”开头的包一般都不建议调用,因为这些包都会与操作系统的底层有关,可以直接通过C代码进行操作),其中 Unsafe类可以实现在没有实例化对象的情况下进行类中方法的调用,在这个类中提供有如下两个重要的结构:

 

 

构造方法私有化

private UnSafe()

内部常量

private static final Unsafe theUnsafe = new Unsafe()

一般在单例设计模式之中,如果类中的构造方法被私有化了,往往会提供有一个 static方法获取本类对象,但是 Unsafe类没有这样的处理方法,这个时候可以考虑通过反射机制来获取到内部提供的theUnsafe常量(这个常量实际上是一个对象)

package com.yootk.demo

import sun.misc.Unsafe;

 

import java.lang.reflect.Field;

public class YootkDeomo{

   pubic static void main(String [] args ) throws Exception{

Fild UnsfeField =   sun.misc.Unsafe.class.getDeclaredField(“theUnsafe”);

System.out.println(UnsafeField);

}

}

 

程序执行结果: private static final sun. misc. Unsafe sun. misc. Unsafe. the Unsafe

      

获 Unsafe类的对象实例最为重要的目的是可以绕过JM的管理机制来实现一些类的调用处理,例如:传统的开发过程之中只要调用类中的普通方法就必须有实例化对象存在,但是如果使用了 UnSafe类,这个机制就可以被打破。

 

 

范例:通过 Unsafe类绕过JVM的对象管理机制实现方法调用

package com.yootk.demo

import sun.misc.Unsafe

 

import java.lang.reflect.Field;

class Singleton{

   private Singleton singleton = null;

   private singleton(){

  System.out.println(“singleton对象实例化”);

}

 

public void print(){

   System.out.println(“李兴华编程训练营”);

}

 

public static getInstance(){

if(singleton == null) {

sysnchronnized(Singleton.class){

         if(singleton== null){

           singleton = new Singleton();

}

    }

  }

  return singleton;’

}

 

 

public class UnsafeDemo{

   pubic static void main(String [] args ) throws Exception{

Field unsafeField =   sun.misc.Unsafe.class.getDeclaredField(“theUnsafe”);

     unsafeField.setAccessible(true);//取消封装

     //这是获取unsafe类的实例对象,我不理解这种反射方式

      Unsafe unsafe = (Unsafe) unsafeField.get(null);

System.out.println(UnsafeField);//获取unsafe对象

 

//获取对象的实例

Singleton  singleton = (Singleton)unsafe.allocateInstance(Singleton.class);

singleton.print();

 

}

 

李兴华编程训练营yootk ke.qq.com

 

但是输出显示我们并没有调用类中的构造方法啊,真的很奇怪

 

解释:我们通过unsafe绕过了整个Java的运行机制,直接实现了类中方法的调用

通过一系列的分析可以发现利用 Unsafe类的对象实例可以直接绕过JVM运行机制,从而直接实现指定类的方法调用,并且连实例化对象的操作全部省略了。

 

反射基础

标签:字符   ant   转型   用处   接口   vps   如何   ring   new t   

原文地址:https://www.cnblogs.com/zrcblog/p/12523591.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!