注解
大量培训机构课程资源,需要加V:【blwxzy_188】
一、注解概述
注解是:代码里的特殊标记,可以在编译,运行,类加载时被读取,并执行相应的处理
在JavaSE中,注解的使用目的比较简单,例如标记过时的功能,忽略警告等。在JavaEE/Android 中注解占据了更要的角色,例如用来配置应用程序的任何切面,代替JavaEE旧版中所遗留的繁冗代码和XML配置等。
1.1、注解的写法
@XXX(一些信息)
1.2、注解可以放置在哪里
类上面,方法上面,构造方法上面,属性上面,参数前面
1.3、注解的作用
- 用来充当注释的作用,相当于一个文字的说明 比如:这个方法过时了(@Deprecated:用于表示所修饰的元素(类, 方法等)已过时,但是还能用。通常是因为所修饰的结构危险或存在更好的选择)
- 用来做代码的检测(验证) 比如:@Override
- 可以携带一些信息(内容)(重点)一般都是自定义注解
注解中可以携带信息,也可以不携带
信息不能随便写 信息的类型只能是:基本数据类型,String类型,枚举类型,注解类型(注解里面再放个注解),数组类型:数组的内部需要是以上几个类型
二、Java中定义好的注解
以下的三个注解是JDK内置的三个注解:
- @Deprecated 说明方法已经过时
方法上面标注这个注解,说明方法已经过时。
- @Override 用来做代码检测,检测此方法是否是个重写的方法
主要是用来检查,子类方法是不是重写的父类的方法
-
@SupperssWarnings({“信息”}) 去掉程序的警告(建议不要使用)
可以携带一些信息,这个信息是一个String[],可以往括号里填入unused,告诉我们变量定义后,未被使用
加上@SuppressWarnings ( "unused" )
注解后,str不再是浅灰色,变亮了。
三、元注解
3.1、概念
修饰注解的注解,对当前的注解进行解释说明的注解
3.2、四种元注解
常用:
@Target:描述当前的注解可以放置在哪里写的(类上面,方法上面,属性上面,参数前面)
@Retention 描述当前的注解是存在什么作用域中的
源代码文件—>编译—>字节码文件---->加载—>内存
SOURCE CLASS RUNTIME
不常用:
@Inherited 描述当前这个注解是否能被子类对象继承
@Document 描述当前这个注解是否能被生成文档形式,是否能被文档所记录
四、自定义注解
自己描述一个注解类型
步骤
- 通过一个
@interface
来定义一个注解类型(发现写法与接口非常相似,可以利用接口的特点来记忆) - 注解类里可以描述public static final 的属性(比较少见)
- 注解类里可以描述public abstract的方法,但是注解里的方法必须要有返回值,返回值类型与上面提到过的类型要一致
- 我们自己定义的注解,如果想要拿来使用,光定义不够,还需要很多细致的说明,需要给注解类加上元注解
@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 {
}
当自己使用自己描述的注解时产生的问题
- 我们在注解里面描述了一个方法,方法没有参数,但是有返回值,在使用注解时,却让我们传参数
理解为:注解的方法是将我们传递给他的参数搬运给了别人,所以需要有返回值类型,可以加入default指定成员的默认值
-
使用别人写好的注解的时候,好像不用写方法名,而我们自己定义的 方法必须写方法名才能使用
原因:如果我们自己定义的注解,而且注解里面只有一个方法的话,是当方法名叫value的话,就可以省略,如果不止一个方法,则必须把方法名写上
-
如何解析注解内携带的信息,注解的应用场景,查看反射的应用2.6小节,利用反射获取注解
自定义注解有什么用?自定义注解需要配上注解的信息处理流程(采用反射)采用意义
五、JDK8中的注解新特性
1.可重复注解
比如在修饰同一个类时,可以声明两次
注意:
1.需要额外定义一个新的注解,新定义的 注解的成员使用注解数组元素
2.新的注解的元注解和可重复注解的元注解一致(除了@Repeatable(MyAnnotations.class))
2.参数注解
可以修饰数据类型(参数)
注意:
1.需要在自定义注解中的target元注解上加上ElementType.TYPE_USE
反射
一、概述
记住一句话:反射就是一面镜子,从镜子里面取东西就行
官方解释
- 反射是动态语言的关键,反射机制允许程序在执行期间借助Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性和方法。
- 类加载完之后,在堆内存的方法区中会产生一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像是一面镜子,透过这个镜子,我们就可以看到整个类的结构,我们形象的称之为:反射
自己理解
- Class类就代表一个类的字节码文件
- 反射就是把java类中的各个部分(类本身,属性,方法,构造方法,注解)都一一映射成一个Java对象
框架 = 反射 + 注解 + 设计模式
动态语言
类在运行时可以改变其结构的语言:例如新的函数、对象、甚至代码可以被引进,已有的函数可以被删除或者是其他的结构上的变化。就是在代码运行时可以根据某些条件改变自身结构
静态语言
与动态语言相对应,运行时,结构不可变的语言,就是静态语言。如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来将字节码文件加载到内存中,此过程我们叫类加载。
加载到内存中的类我们称之为运行时类。
什么时候会发生类初始化?
-
类的主动引用(一定会发生类的初始化)
- 当JVM启动时,会先初始化main方法所在的类
- new一个对象的时候
- 调用类的静态成员(除了final常量)和静态方法
- 使用java.lang.reflect包的方法对类进行反射调用
- 当初始化一个类时,如果他的父类没有被初始化,则会先初始化他的父类
1.2、ClassLoader类加载器
程序加载过程
类加载器的作用:
- 将.class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后在堆中生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口。
- 标准的javaSE类加载器可以按照要求查找类,单一旦某个类被加载到类加载器中,它将维持(加载)一段时间。不过JVM垃圾回收机制可以回收这些Class对象。
类加载器(ClassLoader)分类
类加载器的作用就是用来把类装载进内存的。
@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资源包下
注意:解决中文乱码问题,需要进行以下配置
二、反射的应用
2.1、理解Class类
Object类是所有类的父类,在Object类中定义了一个方法:
public final Class getClass() --> 返回的是一个Class类的类型
以上方法的返回值就是一个Class类型,此类是java反射的源头,从程序的运行结果来看:反射就是通过对象(或者具体的类)反射求出类的名称,修饰符列表,继承实现关系等等。
- 类的实例化对象照镜子后可以得到的信息:某个类的属性、方法、构造器、继承实现关系、注解(这个对象的结构有什么,就能获取什么)。
注意
- 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对象,能做什么事?
-
创建类的对象
/** * 6.通过class来创建对象 newInstance () * 是通过调用默认的无参构造方法来进行初始化的 * 要求:1.类必须有一个无参数的构造器 * 2.类的构造器的访问权限需要足够 */ Person p = (Person)clazz.newInstance (); System.out.println ( p );
-
获取运行时类的完整结构
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对象的参数信息。
//如何操作方法,让他执行 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、获取注解
获取注解的步骤
- 获取Class
- 获取Class内的成员 属性 方法 构造方法
Annotation a = 成员.getAnnotation(注解类型.class);
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 );
}
}