注解 + 反射基础

注解

大量培训机构课程资源,需要加V:【blwxzy_188】

一、注解概述

注解是:代码里的特殊标记,可以在编译,运行,类加载时被读取,并执行相应的处理

在JavaSE中,注解的使用目的比较简单,例如标记过时的功能,忽略警告等。在JavaEE/Android 中注解占据了更要的角色,例如用来配置应用程序的任何切面,代替JavaEE旧版中所遗留的繁冗代码和XML配置等。

1.1、注解的写法

@XXX(一些信息)

1.2、注解可以放置在哪里

类上面,方法上面,构造方法上面,属性上面,参数前面

1.3、注解的作用

  1. 用来充当注释的作用,相当于一个文字的说明 比如:这个方法过时了(@Deprecated:用于表示所修饰的元素(类, 方法等)已过时,但是还能用。通常是因为所修饰的结构危险或存在更好的选择)
  2. 用来做代码的检测(验证) 比如:@Override
  3. 可以携带一些信息(内容)(重点)一般都是自定义注解

注解中可以携带信息,也可以不携带

信息不能随便写 信息的类型只能是:基本数据类型,String类型,枚举类型,注解类型(注解里面再放个注解),数组类型:数组的内部需要是以上几个类型

二、Java中定义好的注解

以下的三个注解是JDK内置的三个注解:

  1. @Deprecated 说明方法已经过时

image-20201016211324310

​ 方法上面标注这个注解,说明方法已经过时。

  1. @Override 用来做代码检测,检测此方法是否是个重写的方法

image-20201016211654002

主要是用来检查,子类方法是不是重写的父类的方法

  1. @SupperssWarnings({“信息”}) 去掉程序的警告(建议不要使用

    可以携带一些信息,这个信息是一个String[],可以往括号里填入unused,告诉我们变量定义后,未被使用

image-20201016211821416

加上@SuppressWarnings ( "unused" )注解后,str不再是浅灰色,变亮了。

三、元注解

3.1、概念

修饰注解的注解,对当前的注解进行解释说明的注解

3.2、四种元注解

常用:

@Target:描述当前的注解可以放置在哪里写的(类上面,方法上面,属性上面,参数前面)

@Retention 描述当前的注解是存在什么作用域中的

​ 源代码文件—>编译—>字节码文件---->加载—>内存

​ SOURCE CLASS RUNTIME

不常用:

@Inherited 描述当前这个注解是否能被子类对象继承

@Document 描述当前这个注解是否能被生成文档形式,是否能被文档所记录

四、自定义注解

自己描述一个注解类型

步骤

  1. 通过一个@interface来定义一个注解类型(发现写法与接口非常相似,可以利用接口的特点来记忆)
  2. 注解类里可以描述public static final 的属性(比较少见)
  3. 注解类里可以描述public abstract的方法,但是注解里的方法必须要有返回值,返回值类型与上面提到过的类型要一致
  4. 我们自己定义的注解,如果想要拿来使用,光定义不够,还需要很多细致的说明,需要给注解类加上元注解
@Target ({ElementType.TYPE,ElementType.FIELD,ElementType.METHOD,ElementType.CONSTRUCTOR})
@Retention ( RetentionPolicy.RUNTIME )
@Inherited
public @interface MyAnnotation {

     //注解方法要求必须要有返回值,类型是我们总结的那些
     String value() default "king";

}

@MyAnnotation ( test="abc" )
public class Person extends Animal {
}

当自己使用自己描述的注解时产生的问题

  1. 我们在注解里面描述了一个方法,方法没有参数,但是有返回值,在使用注解时,却让我们传参数

​ 理解为:注解的方法是将我们传递给他的参数搬运给了别人,所以需要有返回值类型,可以加入default指定成员的默认值

  1. 使用别人写好的注解的时候,好像不用写方法名,而我们自己定义的 方法必须写方法名才能使用

    原因:如果我们自己定义的注解,而且注解里面只有一个方法的话,是当方法名叫value的话,就可以省略,如果不止一个方法,则必须把方法名写上

  2. 如何解析注解内携带的信息,注解的应用场景,查看反射的应用2.6小节,利用反射获取注解

自定义注解有什么用?自定义注解需要配上注解的信息处理流程(采用反射)采用意义

五、JDK8中的注解新特性

1.可重复注解

比如在修饰同一个类时,可以声明两次

image-20201016225206576

注意:

1.需要额外定义一个新的注解,新定义的 注解的成员使用注解数组元素

2.新的注解的元注解和可重复注解的元注解一致(除了@Repeatable(MyAnnotations.class))

2.参数注解

可以修饰数据类型(参数)

image-20201016225446728

注意:

​ 1.需要在自定义注解中的target元注解上加上ElementType.TYPE_USE

反射

一、概述

记住一句话:反射就是一面镜子,从镜子里面取东西就行

官方解释

  • 反射是动态语言的关键,反射机制允许程序在执行期间借助Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性和方法。
  • 类加载完之后,在堆内存的方法区中会产生一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像是一面镜子,透过这个镜子,我们就可以看到整个类的结构,我们形象的称之为:反射

自己理解

  • Class类就代表一个类的字节码文件
  • 反射就是把java类中的各个部分(类本身,属性,方法,构造方法,注解)都一一映射成一个Java对象

框架 = 反射 + 注解 + 设计模式

image-20201014140823785

image-20201014141237807

image-20201014213408767

动态语言

类在运行时可以改变其结构的语言:例如新的函数、对象、甚至代码可以被引进,已有的函数可以被删除或者是其他的结构上的变化。就是在代码运行时可以根据某些条件改变自身结构

静态语言

与动态语言相对应,运行时,结构不可变的语言,就是静态语言。如Java,C、C++。

java可以称之为准动态语言,有一定的动态性,利用反射机制可以获取类的结构,类中的属性,构造方法,方法,可以修改属性的值,或者给属性赋值,或者给利用反射来创建对象实例,这就是动态性。

哪些类型可以有Class对象?

(1)class: 外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类

(2)interface:接口

(3)[]:数组

(4)enum:枚举

(5)annotation:注解@interface

(6)primitive type:基本数据类型

(7)void

Class c1 = Object.class;
Class c2 = Comparable.class;
Class c3 = String[].class;
Class c4 = int[][].class;
Class c5 = ElementType.class;
Class c6 = Override.class;
Class c7 = int.class;
Class c8 = void.class;
Class c9 = Class.class;
int[] a = new int[10];
int[] b = new int[100];
Class c10 = a.getClass();
Class c11 = b.getClass();
// 只要元素数组类型与维度一样,就是同一个Class
System.out.println(c10 == c11);

由此可见,Java里万物皆对象。

1.1、类加载

类加载的过程:

程序会先调用javac.exe来执行Java源文件生成.class的字节码文件,然后会调用java.exe来将字节码文件加载到内存中,此过程我们叫类加载。

加载到内存中的类我们称之为运行时类。

image-20201015230705918

什么时候会发生类初始化?

  • 类的主动引用(一定会发生类的初始化)

    • 当JVM启动时,会先初始化main方法所在的类
    • new一个对象的时候
    • 调用类的静态成员(除了final常量)和静态方法
    • 使用java.lang.reflect包的方法对类进行反射调用
    • 当初始化一个类时,如果他的父类没有被初始化,则会先初始化他的父类

1.2、ClassLoader类加载器

程序加载过程

image-20201015231228275

类加载器的作用:

  • 将.class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后在堆中生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口。
  • 标准的javaSE类加载器可以按照要求查找类,单一旦某个类被加载到类加载器中,它将维持(加载)一段时间。不过JVM垃圾回收机制可以回收这些Class对象。

类加载器(ClassLoader)分类

类加载器的作用就是用来把类装载进内存的。

image-20201015232822100

  @Test
    public void test1() {

        //查看自定义普通类的类加载器,发现普通类加载器使用的是系统类加载器换句话说:普通类加载时使用系统类加载器
        ClassLoader classLoader = ClassLoaderTest.class.getClassLoader ( );
        System.out.println ( classLoader );//sun.misc.Launcher$AppClassLoader@18b4aac2

        //调用系统类加载器的getParent():得到的是扩展类加载器
        ClassLoader classLoader1 = classLoader.getParent ();
        System.out.println ( classLoader1 );//sun.misc.Launcher$ExtClassLoader@8807e25

        //调用扩展类加载器的getParent():得到的是null,因为引导类加载器获取不到,是用C++编写的
        //引导类加载器主要是用来加载Java的核心类库
        ClassLoader classLoader2 = classLoader1.getParent ( );
        System.out.println ( classLoader2 );//null

        //String类的类加载器就是引导类加载器
        ClassLoader classLoader3 = String.class.getClassLoader ( );
        System.out.println ( classLoader3 );//null

    }

ClassLoader的应用

读取Properties的文件

方式一:

  @Test
    public void test2() throws Exception {
        //读取properties配置文件方式有一:
        Properties pro = new Properties ( );
        //注意:这里的 路径默认是在moudle(模块)下
        FileInputStream fis = new FileInputStream ( "jdbc.properties" );
        pro.load ( fis );

        String user = pro.getProperty ( "user" );
        String passwd = pro.getProperty ( "passwd" );
        System.out.println ("user = " + user + ",passwd = " + passwd );

    }

注意:使用第一种方式,配置文件的默认位置是在moudle模块下

方式二:

    @Test
    public void test3() throws IOException {
        //方式二:读取properties配置文件
        Properties pros = new Properties ( );

        //获取本类的一个系统类加载器
        ClassLoader classLoader = ClassLoaderTest.class.getClassLoader ( );
        //使用获取到的系统类加载器调用getResourceAsStream();方法获得一个输入流
        //此方法的properties的配置文件路径默认为当前moudle的src下
        InputStream fis = classLoader.getResourceAsStream ( "jdbc1.properties" );
        pros.load ( fis );
        
        
        String user = pros.getProperty ( "user" );
        String passwd = pros.getProperty ( "passwd" );
        System.out.println ("user = " + user + ",passwd = " + passwd );

    }

注意:使用第二种方式,配置文件的默认位置是在moudle模块下的src下

​ 如果使用maven的话,则默认的配置文件 路径在resources资源包下

注意:解决中文乱码问题,需要进行以下配置

image-20201015235547201

二、反射的应用

2.1、理解Class类

Object类是所有类的父类,在Object类中定义了一个方法:

public final Class getClass() --> 返回的是一个Class类的类型

以上方法的返回值就是一个Class类型,此类是java反射的源头,从程序的运行结果来看:反射就是通过对象(或者具体的类)反射求出类的名称,修饰符列表,继承实现关系等等。

image-20201014215649232

  • 类的实例化对象照镜子后可以得到的信息:某个类的属性、方法、构造器、继承实现关系、注解(这个对象的结构有什么,就能获取什么)。

注意

  • Class本身也是一个类
  • Class对象不能手动new,只能由系统建立Class对象
  • 一个加载的类在JVM中只会有一个Class实例
  • 一个Class对象对应的是一个加载到JVM中的一个.class文件
  • 每个类的实例都会记得自己是由哪个Class实例所生成
  • 通过Class可以完整的得到一个类中的所有被加载的结构
  • Class类是Reflection的根源,针对任何你想动态加载、运行的类,唯有先获得相应的Class对象

2.2、获取Class

加载到内存中的类,会缓存一定的时间,我们可以通过不同的方式来获取此运行时类。

获取Class的实例,三种方法:

方式一:已知具体的类,通过类的class属性获取,该方法最为安全可靠,程序性能高

 Class<Person> clazz = Person.class;

方式二:已知某个类的实例对象,调用该实例的getClass()方法获取Class对象

Person person = new Person ( );
Class clazz = person.getClass ( );

方式三:通过Class.forName("完整的类名")来获取Class对象,可能抛出ClassNotFoundException异常,运用的较多

Class clazz = Class.forName ( "com.pojo.Person" );

注意:

  • 一个Class实例对应着一个运行时类
  • 以上三种方法获取的Class的实例的方式,获取到的都是同一个运行时类
2.2.1、class中的常用方法
方法名返回值类型作用
getModifiers ();int获得修饰符列表
getName ( );String获得类名
getName ();Package获得类所在的包类型
getSuperclass ( );Class获得当前类的父类
getInterfaces ( );Class[]获得当前类的所有实现的接口

拿到Class对象,能做什么事?

  1. 创建类的对象

    /**
     * 6.通过class来创建对象 newInstance ()
     *  是通过调用默认的无参构造方法来进行初始化的
     *  要求:1.类必须有一个无参数的构造器
     * 		  2.类的构造器的访问权限需要足够			
     */
    Person p = (Person)clazz.newInstance ();
    System.out.println ( p );
    
  2. 获取运行时类的完整结构

    ​ 2.1、获得修饰符列表

       //1.通过一个class对象,来操作Person.class类文件 方式一
                Class clazz = Class.forName ( "com.pojo.Person" );
                //类有自己的结构  有权限修饰符  特征修饰符  类名字 继承关系 实现的接口
               /* 类中常用的方法:
                    1.获取类的修饰符的,包含特征符 和 权限符
                        getModifiers();返回值是一个int类型
                        每一个修饰符 用一个整数来进行表示
                        0开始-----》 0 1 2 4 8 16 32 64 128 256 512
                        0--默认不写
                        1--public
                        2--private
                        4--protected
                        8--static
                        16--final
                        32--synchronized
                        64--volatile
                        128--transient
                        256--native
                        512--interface
                        1024--abstract
                        如果有多个修饰符会自动求和
                    */
                int modifiers = clazz.getModifiers ();
                System.out.println ( modifiers );//1
    

    2.2、获取类的名字

     /**
                 * 2.获取类名字
                 *   getName();//获取类全名(包名+类名)
                 *   getSimpleName();//获取简单类名
                 */
                String name = clazz.getName ( );
                String simpleName = clazz.getSimpleName ( );
                System.out.println ( name );//com.pojo.Person
                System.out.println ( simpleName );//Person
    

​ 2.3、获取类所在的包名

/**
             * 3.获取类所在的包名
             *   getPackage().getName ();
             */
            Package clazzPackage = clazz.getPackage ( );
            System.out.println ( clazzPackage.getName () );

​ 2.4、获取当前类的父类

     ```java

Class superclass = clazz.getSuperclass ( );
System.out.println ( superclass.getName () );
```

	  2.5、**获取当前类的所有父接口**   
@Test
public void test3(){
    ArrayList<String> list = new ArrayList<> ( );
    Class clazz = ArrayList.class;
    //1.获取当前clazz的所有父接口:getInterfaces()
    Class[] interfaces = clazz.getInterfaces ( );
    for ( Class anInterface : interfaces ) {
        System.out.println ( anInterface );
    }
}

2.3、获取Field

调用Class中的方法获得类的属性

方法名返回值作用局限
getField ( 属性名 );Field根据方法名获得属性1.知道属性的名字 2.属性必须是公有的,私有的获取不到
getFields ( );Field[]获取所有的属性(包括继承下来的)1.只能获取公有的
getDeclaredField ( 属性名);Field获取本类中对应的属性(继承的获取不了)1.本类中的属性,包括私有的和共有的 2.继承的获取不了
getDeclaredFields();Field获取本类中所有的属性1.本类中的所有属性 ,包括共有的和私有的 2.继承的获取不了
2.3.1、Field中的常用方法

Field类的常用方法

获取属性的修饰符列表

Class<Person> clazz = Person.class;
//getField ()方法的局限:1.知道属性的名字是什么 2.属性是共有的,私有的获取不到
Field nameField = clazz.getField ( "name" );
//获取到属性之后,可以获取属性自己的结构 赋值 取值
// 获得属性的修饰符 getModifiers ( )
int modifiers = nameField.getModifiers ();
System.out.println ( modifiers );

获取属性的类型的Class类型

//获取属性的类型  Class类型 getType ( )
Class type = nameField.getType ( );
System.out.println ( type.getName ( ) );

获取属性的名字

//获取属性的名字
System.out.println ( nameField.getName ( ) );

操作属性,往属性里存值

set()方法 传递的参数为属性所属的类的实例化对象 ,值

//操作属性,向里面存值 属性.赋值(哪个对象,值)
//以前是直接有对象,可以通过对象.的方式,现在是没对象,只有属性,所以,得知道这个属性是哪个对象的
Person person = clazz.newInstance ( );
nameField.set ( person,"李寒衣" );
System.out.println ( person );

操作属性,从属性里取值

//操作属性,从里面取值
//以前:对象.属性
//现在:值 = 属性.取值(哪个对象)
String name = (String) nameField.get ( person );
System.out.println ( name );

获得私有属性并赋值和取值

Person person = clazz.newInstance ( );
//clazz来获取类中的私有属性
/**
 * getDeclaredField ();
 * getDeclaredFields();
 * 如上两个方法都能获取公有和私有的属性,但是只能获取本类中的属性,继承来的获取不了
 */
Field name = clazz.getDeclaredField ( "name" );

//表示私有的属性可以直接被操作  打开权限
name.setAccessible ( true );

name.set ( person,"百里东君" );

String value = (String)name.get ( person );
System.out.println ( value );
2.3.2、反射操作属性的小案例

要求

  • 通过反射修改给定的String的值

实现

@Test
public void test6() throws NoSuchFieldException, IllegalAccessException {
    String str = "abc";
    System.out.println ( str );
    //反射的技术可以获取私有属性,并且可以操作私有属性
    //1.获取String类对应的那个Class
    Class<? extends String> clazz = str.getClass ( );

    //2.通过clazz获取类中的value属性
    Field field = clazz.getDeclaredField ( "value" );

    //3.设置私有属性可以被操作
    field.setAccessible ( true );

    //4.获取value属性的值
    // private final cahr[] value ;
    char[] temp = (char[])field.get ( str );

    temp[0] = '司';
    temp[1] = '空';
    temp[2] = '长';

    System.out.println ( temp );

}

2.4、获取Method

调用Class中的方法获得类中的方法

方法名返回值作用局限
getMethod ( 方法名,参数对应的Class类型 );Method获取类中对应的方法获取公有的方法 (自己的或者继承的) ,私有的获取不了
getMethods ( );Method[]获取类中的所有方法获取的是公有的所有的方法,包括自己的和继承来的
getDeclaredMethod ( 私有的方法名);Method获取类中对应的私有的方法获取的可以是私有的,也可以是公有的,只能是自己的,继承的获取不了
getDeclaredMethods ( );Method[]获取类中的所有的私有方法所有方法,包括公有的和私有的,继承的获取不了

Method常用方法

//1.获取Person对应的Class
Class<Person> clazz = Person.class;

//2.通过clazz获取其中的方法
//getMethod( ); 传进去的是方法名,方法参数类型对应的Class来寻找
Method method = clazz.getMethod ( "eat",String.class );

//获取方法的修饰符
int modifiers = method.getModifiers ( );

//获取返回值数据类型
Class<?> returnType = method.getReturnType ( );

//获取方法名
String methodName = method.getName ( );

//获取方法参数列表的类型
Class<?>[] parameterTypes = method.getParameterTypes ( );

//获取方法抛出异常的类型
Class<?>[] exceptionTypes = method.getExceptionTypes ( );

操作共有方法来执行

1.通过Class类的**getMethod(String name,Class…parameterTypes)**方法取得一个Method对象,并设置此方法操作时所需要的参数类型。

2.之后使用**Object invoke(Object obj, Object[] args)**进行调用,并向方法中传递要设置的obj对象的参数信息。

image-20201014233021830

//如何操作方法,让他执行 getMethod()获取公有的方法 (自己的或者继承的) 传进去的值,方法名 (Class...)参数对应的类型
Class<Person> clazz = Person.class;
Person person = clazz.newInstance ( );
Method method = clazz.getMethod ( "eat" , String.class );
//invoke();方法是用来执行方法的 传进去的是一个对象(要执行的对象) 执行参数需要的所有参数
//有返回值就需要接收
String invoke = (String) method.invoke ( person , "测试参数" );
System.out.println ( invoke );

Object invoke(Object obj, Object … args)

说明:

1.Object 对应原方法的返回值,若原方法无返回值,此时返回null

2.若原方法若为静态方法,此时形参Object obj可为null

3.若原方法形参列表为空,则Object[] args为null

4.若原方法声明为private,则需要在调用此invoke()方法前,显式调用方法对象的setAccessible(true)方法,将可访问private的方法。

操作私有的方法来执行

Class<Person> clazz = Person.class;
Person person = clazz.newInstance ( );

//获取私有的方法 获取一个方法 只能获取自己的 传值 :方法名,参数类型class..
//getDeclaredMethods  获取自己的全部方法
Method testPrivate = clazz.getDeclaredMethod ( "testPrivate" );

//打开私有的权限
testPrivate.setAccessible ( true );

//运行方法
testPrivate.invoke ( person );

2.5、获取Constructor

调用Class中的方法获得类中的构造方法 对比Method 构造方法是特殊的Method

clazz.getConstructor (); //获得无参的共有的构造方法  可以传递参数
clazz.getConstructors ();//获得所有公有的构造方法
clazz.getDeclaredConstructor ();//获得私有的
clazz.getDeclaredConstructors ();//获得所有的包括私有的和公有的

Constructor类中的常用方法

//Constructor类中的常用方法
//同上,方法名都一致
constructor.getModifiers ();
constructor.getParameterTypes ();
constructor.getName ();
constructor.getExceptionTypes ();

调用无参数的构造方法来实例化对象

Class<Person> clazz = Person.class;


//找寻无参数的构造方法 传进去的值 (传参数对应的类型)
Constructor<Person> constructor = clazz.getConstructor ( );

//执行构造方法 newInstance
Person p = constructor.newInstance ();
System.out.println ( p );

调用有参数的构造方法来实例化对象

//找带参数的构造方法   参数为构造方法的参数对应的Class类型
Constructor<Person> constructor1 = clazz.getConstructor ( String.class , Integer.class );

//传进去的是参数值
Person person = constructor1.newInstance ( "百里东君" , 12 );
System.out.println ( person );

2.6、获取注解

获取注解的步骤

  1. 获取Class
  2. 获取Class内的成员 属性 方法 构造方法
  3. Annotation a = 成员.getAnnotation(注解类型.class);
  4. a.invoke("value")执行方法获取返回结果

案例

MyAnnotation注解:

@Target ( {ElementType.FIELD,ElementType.METHOD,ElementType.TYPE,ElementType.CONSTRUCTOR})
@Retention ( RetentionPolicy.RUNTIME)
public @interface MyAnnotation {

    String[] value() default "king";//默认方法
    
}

Person类:

public class Person {

    private String name;
    private Integer age;
    private String sex;

    @MyAnnotation({"萧楚河","20","男"})
    public Person (){}

    public String getName ( ) {
        return name;
    }

    public void setName ( String name ) {
        this.name = name;
    }

    public Integer getAge ( ) {
        return age;
    }

    public void setAge ( Integer age ) {
        this.age = age;
    }

    public String getSex ( ) {
        return sex;
    }

    public void setSex ( String sex ) {
        this.sex = sex;
    }

    @Override
    public String toString ( ) {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", sex='" + sex + '\'' +
                '}';
    }

    //利用反射实现IOC的思想,对象由别人来控制创建 创建同时自动注入属性信息
    //自动注入属性信息--->Scanner输入进来的 存入文件里 产生一个注解 携带这些信息


    @MyAnnotation({"李寒衣","18","女"})
    private String name1;

    @MyAnnotation({"百里东君","20","男"})
    public void eat(){}
}

测试方法:获取注解的信息

public class TestMain {
    public static void main ( String[] args ) throws Exception {
        //解析Person类中 属性上面的注解信息  需要用到反射技术

        //1.获取Person对应的Class
        Class<Person> clazz = Person.class;

        //2.通过clazz去获取里面的属性
        Field fieldname = clazz.getDeclaredField ( "name1" );


      //3.通过属性去获取上面的注解对象
        MyAnnotation a = fieldname.getAnnotation ( MyAnnotation.class );
        /* 正常的对象调用执行过程
        //4.利用a对象执行一下value()的方法 帮我们搬运过来
        String[] value = a.value ();
        for ( String s : value ) {
            System.out.println ( s );
        }*/

        //4.利用反射执行a中的value方法
        Class<? extends MyAnnotation> aClass = a.getClass ( );
        //5.通过aClass对象获取里面的value方法
        Method method = aClass.getMethod ( "value" );
        //6.执行value方法 获取传递的信息
        String[] values = (String[]) method.invoke ( a );

        for ( String value : values ) {
            System.out.println ( value );
        }

    }

    //获取Person类中方法上面的注解信息
    @Test
    public void test1() throws Exception {
        Class<?> clazz = Class.forName ( "com.king3.Person" );
        Method eat = clazz.getMethod ( "eat" );
        MyAnnotation a = eat.getAnnotation ( MyAnnotation.class );
        Class<? extends MyAnnotation> aClass = a.getClass ( );
        Method method = aClass.getMethod ( "value" );
        String[] ss = (String[]) method.invoke ( a );
        for ( String s : ss ) {
            System.out.println ( s );
        }
    }

  • Method和Field、Constructor对象都有setAccessible()方法。
  • setAccessible启动和禁用访问安全检查的开关。
  • 参数值为true则指示反射的对象在使用时应该取消Java语言访问检查。
  • 提高反射的效率。如果代码中必须用反射,而该句代码需要频繁的被调用,那么请设置为true。
  • 使得原本无法访问的私有成员也可以访问
  • 参数值为false则指示反射的对象应该实施Java语言访问检查。

三、反射的小应用

3.1、需求

设计一个方法,控制对象的创建。类似Spring框架的IOC(控制反转)

3.2、分析

  • 是否需要参数:String 类全名
  • 是否需要返回值: Object

3.3、实现

3.3.1、版本一:利用Scanner输入

pojo类

public class Person {

    private String name;
    private String age;
    private Integer id;


    public Person ( ) {
    }

    public Person ( String name , String age , Integer id ) {
        this.name = name;
        this.age = age;
        this.id = id;
    }

    public Integer getId ( ) {
        return id;
    }

    public void setId ( Integer id ) {
        this.id = id;
    }

    public String getName ( ) {
        return name;
    }

    public void setName ( String name ) {
        this.name = name;
    }

    public String getAge ( ) {
        return age;
    }

    public void setAge ( String age ) {
        this.age = age;
    }

    @Override
    public String toString ( ) {
        return "Person{" +
                "name='" + name + '\'' +
                ", age='" + age + '\'' +
                ", id=" + id +
                '}';
    }
}

MySpringIoc类

public class MySpringIoc {
    /**
     * 设计一个方法,控制对象的创建
     *   是否需要参数:String 类全名
     *   是否需要返回值? Object
     */
    public Object getBean(String className){
        Object obj = null;
        Scanner input = new Scanner ( System.in );
        System.out.println ("请给" + className + "类的对象赋值" );
        try {
            //1.获取方法传递进来的参数对象的类Class
            Class<?> clazz = Class.forName ( className );

            //2.通过clazz创建一个对象
             obj = clazz.newInstance ( );

             //3.做一个DI依赖注入  注入对象中所有的属性值
             //找到一个对象中的所有set方法 给属性赋值
            //自己通过拼接字符串的方式处理名字
            //   通过clazz寻找到类中的所有私有属性---》获取每一个属性的名字--》set属性
            Field[] fields = clazz.getDeclaredFields ( );

            for ( Field field : fields ) {
                //获取属性名字
                String fieldName = field.getName ( );
                //手动拼串, 拼接属性对应的set方法名  setTitle

                //将属性的首字母变大写
                String firstLetter = fieldName.substring ( 0 , 1 ).toUpperCase ( );
                String otherLetter = fieldName.substring ( 1 );

                //拼串
                StringBuilder setMethodName = new StringBuilder ( "set" );
                setMethodName.append ( firstLetter );
                setMethodName.append ( otherLetter );

                //获取field对应的属性类型 --> 找寻set方法时候传递参数
                Class<?> fieldType = field.getType ( );


                //通过处理好的set方法名找寻类中的set方法
                Method setmethod = clazz.getMethod ( setMethodName.toString ( ) , fieldType );

                //找到了setMethod一执行 属性就赋值成功
                System.out.println ("请给" + fieldName + "属性提供值:" );
                String value = input.nextLine ( );

                //当值的类型不是string类型的时候就会出错 Integer Float
                //将所有的String类型的值,转换成属性类型的值
                //包装类有七个都含有带string的构造方法
                //new Integer(String) new Float(String)
                //可以利用包装类带string的构造方法处理  属性类型对应的带String参数的构造方法

                Constructor<?> fieldTypeConstructor = fieldType.getConstructor ( String.class );

                setmethod.invoke (obj,fieldTypeConstructor.newInstance ( value ));

            }

        } catch (Exception e) {
            e.printStackTrace ( );
        }

        return obj;
    }
    
}

测试类

public class Main {
    public static void main ( String[] args ) {
        //创建一个Person类型的对象  将对象的控制权交由别人处理
        Demo demo = new Demo ( );
        Person bean = (Person)demo.getBean ( "com.king1.Person" );
        System.out.println ( bean );

    }
}
3.3.2、版本2:使用注解

pojo类

public class Person {

    private String name;
    private Integer age;
    private String sex;

    @MyAnnotation({"萧楚河","20","男"})
    public Person (){}

    public String getName ( ) {
        return name;
    }

    public void setName ( String name ) {
        this.name = name;
    }

    public Integer getAge ( ) {
        return age;
    }

    public void setAge ( Integer age ) {
        this.age = age;
    }

    public String getSex ( ) {
        return sex;
    }

    public void setSex ( String sex ) {
        this.sex = sex;
    }

    @Override
    public String toString ( ) {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", sex='" + sex + '\'' +
                '}';
    }
}    
public class User  {
    private String username;
    private String password;
    private Float balance;

    @MyAnnotation({"司空长风","123465","12345.125"})
    public User () {}

    public String getUsername ( ) {
        return username;
    }

    public void setUsername ( String username ) {
        this.username = username;
    }

    public String getPassword ( ) {
        return password;
    }

    public void setPassword ( String password ) {
        this.password = password;
    }

    public Float getBalance ( ) {
        return balance;
    }

    public void setBalance ( Float balance ) {
        this.balance = balance;
    }

    @Override
    public String toString ( ) {
        return "User{" +
                "username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", balance=" + balance +
                '}';
    }
}

MySpringIoc类

public class MySpring  {
    //设计一个方法给一个类名字返回一个对象,对象内的属性值存在着
    public Object getBean(String className){
        //用变量来接收最终的变量
        Object obj = null;
        try {
            //1.通过传递的className来获取对应的类Class 要求的参数是一个类全名
            Class<?> clazz = Class.forName ( className );
            //2.通过clazz创建一个空的对象
            Constructor<?> constructor = clazz.getConstructor ();
            obj = constructor.newInstance ();

            //3.创建对象以后,将对象内所有的属性自动赋值DI 把值存入注解里 或者存到文件里去 代码包装起来是不能修改的 但文件可以
            /*
            值--->文件
              好处在于代码包装起来,.jar不能修改 文件还可以修改
              不好的在于开发的时候 源代码和配置文件不在一起 读取/修改比较麻烦
            值--->注解
              好处在于开发的时候方便 源代码和注解在一起 读取/调整时候比较容易
              不好在于代码包装起来后 注解内携带的信息不能修改
             */

            //4.获取属性的值---->当前类的无参数构造方法之上的
            MyAnnotation a = constructor.getAnnotation ( MyAnnotation.class );

            //5.获取a注解对象内携带的信息 这个信息是我们person对象所有的属性值
            Class<? extends MyAnnotation> aClass = a.getClass ( );
            Method amethod = aClass.getMethod ( "value" );
            String[] values = (String[]) amethod.invoke (a);
            //6.将values中的每一个值对应的赋值赋给属性
            //  找寻属性对应的set方法赋值
            //获取全部的属性
            Field[] fields = clazz.getDeclaredFields ();
            for ( int i = 0 ; i < fields.length ; i++) {
                //获取属性的名字
                String fieldName = fields[i].getName ( );

                //处理set方法的字符串
                String firstLetter = fieldName.substring ( 0 , 1 ).toUpperCase ( );
                String otherLetter = fieldName.substring ( 1 );
                StringBuilder setMethodName = new StringBuilder ( "set" );
                setMethodName.append ( firstLetter );
                setMethodName.append ( otherLetter );

                //通过处理好的set方法名字找到对应的set方法
                Method setMethod = clazz.getMethod ( setMethodName.toString ( ) , fields[i].getType ( ) );
                //每次循环执行找到的set方法给对应的属性赋值
                //需要将注解内读取到的String类型的值 转换成属性类型对应的值
                setMethod.invoke ( obj,fields[i].getType ().getConstructor ( String.class ).newInstance ( values[i] ));

            }


        } catch (Exception e) {
            e.printStackTrace ( );
        }

        return obj;
    }

}

测试类

public class SpringMain {
    public static void main ( String[] args ) {
        //获取一个Person类型的对象 不用自己处理 跟别人要 MySpring
        //对象的创建权力反转(IOC) 赋值(自动DI) 别人处理
        MySpring mySpring = new MySpring ();
        Person p = (Person) mySpring.getBean ( "com.king3.Person" );
        System.out.println ( p );

        User user = (User) mySpring.getBean ( "com.king3.User" );
        System.out.println ( user );
        
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蚂蚁爱学习

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值